自己动手写驱动程序

来源:互联网 发布:linux https 编辑:程序博客网 时间:2024/05/28 15:36
一、编码思维导图


二、编写代码
memdev.c
  1. #include <linux/module.h>
  2. #include <linux/init.h>
  3. #include <linux/cdev.h>
  4. #include <linux/fs.h>
  5. #include <asm/uaccess.h>

  6. struct cdev mdev;                                 //1.静态分配cdev
  7. dev_t devno;

  8. int dev1_regs[5];
  9. int dev2_regs[5];

  10. loff_t mem_lseek(struct file *filep, loff_t offset, int whence)
  11. {
  12.     loff_t new_pos = 0;

  13.     switch(whence) {
  14.         case SEEK_SET:
  15.             new_pos = 0 + offset;
  16.             break;
  17.         case SEEK_CUR:
  18.             new_pos = filep->f_pos + offset;
  19.             break;
  20.         case SEEK_END:
  21.             new_pos = 5*sizeof(int) + offset;
  22.             break;
  23.         default:
  24.             break;
  25.     }

  26.     filep->f_pos = new_pos;
  27.     return new_pos;
  28. }

  29. size_t mem_read(struct file *filep, char __user *buf, size_t size, loff_t *ppos)
  30. {
  31.     int *reg_base = filep->private_data;

  32.     copy_to_user(buf, reg_base+(*ppos), size);

  33.     filep->f_pos += size;

  34.     return size;
  35. }

  36. size_t mem_write(struct file *filep, const char __user *buf, size_t size, loff_t *ppos)
  37. {
  38.     int *reg_base = filep->private_data;

  39.     copy_from_user(reg_base+(*ppos), buf, size);

  40.     filep->f_pos += size;

  41.     return size;
  42. }

  43. size_t mem_open(struct inode *node, struct file *filep)
  44. {
  45.     int num = MINOR(node->i_rdev);
  46.     if(num == 0)
  47.         filep->private_data = dev1_regs;
  48.     else if(num == 1)
  49.         filep->private_data = dev2_regs;

  50.     return 0;
  51. }

  52. int mem_close (struct inode *node, struct file *filep)
  53. {
  54.     return 0;
  55. }

  56. struct file_operations memfops =
  57. {
  58.     .llseek = mem_lseek,
  59.     .open = mem_open,
  60.     .write = mem_write,
  61.     .read = mem_read,
  62.     .release = mem_close,
  63. };

  64. int memdev_init()
  65. {
  66.     cdev_init(&mdev, &memfops);                                  //2.初始化cdev

  67.     alloc_chrdev_region(&devno, 0, 2, "memdev");                 //3.动态分配设备号到devno
  68.     cdev_add(&mdev, devno, 2);                                   //4.注册cdev
  69.     return 0;
  70. }

  71. void memdev_exit()
  72. {
  73.     cdev_del(&mdev);                                              //5.设备注销
  74.     unregister_chrdev_region(devno, 2);                           //6.设备号注销
  75. }

  76. module_init(memdev_init);                    //初始化入口
  77. module_exit(memdev_exit);                    //模块退出

三、函数学习
设备初始化:cdev_init
原型:void cdev_init(struct cdev *cdev, const struct file_operations *fops);
cdev:待初始化的cdev结构
fops:设备对应的操作函数集

动态分配设备号:alloc_chrdev_region
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);
dev:分配后设备号的地址
baseminor:起始地设备序号
count:分配的个数
name:分配后的设备别名

设备注册:cdev_add
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
p:待添加的字符设备结构
dev:设备号
count:该类设备的设备个数

注销设备:cdev_del
void cdev_del(struct cdev *p);
p:待注销的设备结构

释放设备号:unregister_chrdev_region
void unregister_chrdev_region(dev_t from, unsigned count);
from:待释放设备号
count:释放的设备个数

四、驱动访问模型
1.应用程序访问read
arm-linux-objdump -d read_mem

  1. 00008228 <main>:
  2.     8228: e92d4800 push {fp, lr}
  3.     822c: e28db004 add fp, sp, #4 ; 0x4
  4.     8230: e24dd008 sub sp, sp, #8 ; 0x8
  5.     8234: e3a03000 mov r3, #0 ; 0x0
  6.     8238: e50b3008 str r3, [fp, #-8]
  7.     823c: e3a03000 mov r3, #0 ; 0x0
  8.     8240: e50b300c str r3, [fp, #-12]
  9.     8244: e59f0044 ldr r0, [pc, #68] ; 8290 <main+0x68>
  10.     8248: e3a01002 mov r1, #2 ; 0x2
  11.     824c: eb0028a3 bl 124e0 <__libc_open>
  12.     8250: e1a03000 mov r3, r0
  13.     8254: e50b3008 str r3, [fp, #-8]
  14.     8258: e24b300c sub r3, fp, #12 ; 0xc
  15.     825c: e51b0008 ldr r0, [fp, #-8]
  16.     8260: e1a01003 mov r1, r3
  17.     8264: e3a02004 mov r2, #4 ; 0x4
  18.     8268: eb0028c0 bl 12570 <__libc_read>
>>libc_read:
  1. 00012570 <__libc_read>:
  2.    12570: e51fc028 ldr ip, [pc, #-40] ; 12550 <__libc_open+0x70>
  3.    12574: e79fc00c ldr ip, [pc, ip]
  4.    12578: e33c0000 teq ip, #0 ; 0x0
  5.    1257c: 1a000006 bne 1259c <__libc_read+0x2c>
  6.    12580: e1a0c007 mov ip, r7
  7.    12584: e3a07003 mov r7, #3 ; 0x3
  8.    12588: ef000000 svc 0x00000000
将3放入r7寄存器中
调用svc,进入内核空间
通过查表找到read并使用

在entry-common.S中
  1. ENTRY(sys_call_table)
  2. #include "calls.S"
  3. #undef ABI
  4. #undef OBSOLETE

  5. /*============================================================================
  6.  * Special system call wrappers
  7.  */
  8. @ r0 = syscall number
  9. @ r8 = syscall table
  10. sys_syscall:
  11.         bic    scno, r0, #__NR_OABI_SYSCALL_BASE
  12.         cmp    scno, #__NR_syscall - __NR_SYSCALL_BASE
  13.         cmpne    scno, #NR_syscalls    @ check range
  14.         stmloia    sp, {r5, r6}        @ shuffle args
  15.         movlo    r0, r1
  16.         movlo    r1, r2
  17.         movlo    r2, r3
  18.         movlo    r3, r4
  19.         ldrlo    pc, [tbl, scno, lsl #2]
  20.         b    sys_ni_syscall
  21. ENDPROC(sys_syscall)
利用3这个编号找到sys_read
>>>read_write.c
  1. ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
  2. {
  3.     ssize_t ret;

  4.     if (!(file->f_mode & FMODE_READ))
  5.         return -EBADF;
  6.     if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
  7.         return -EINVAL;
  8.     if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
  9.         return -EFAULT;

  10.     ret = rw_verify_area(READ, file, pos, count);
  11.     if (ret >= 0) {
  12.         count = ret;
  13.         if (file->f_op->read)
  14.             ret = file->f_op->read(file, buf, count, pos);                        //这里调用了file_operations的read
  15.         else
  16.             ret = do_sync_read(file, buf, count, pos);
  17.         if (ret > 0) {
  18.             fsnotify_access(file->f_path.dentry);
  19.             add_rchar(current, ret);
  20.         }
  21.         inc_syscr(current);
  22.     }

  23.     return ret;
  24. }





<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(38) | 评论(0) | 转发(0) |
0

上一篇:字符设备驱动模型

下一篇:LED驱动程序设计

相关热门文章
  • SHTML是什么_SSI有什么用...
  • 查看linux中某个端口(port)...
  • 卡尔曼滤波的原理说明...
  • shell中字符串操作
  • 关于java中的“错误:找不到或...
给主人留下些什么吧!~~