LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 7675|回复: 1

Linux设备驱动开发学习笔记

[复制链接]
发表于 2005-4-24 17:47:48 | 显示全部楼层 |阅读模式
只读了几章ldd,先总结一下

  1. Linux设备驱动开发学习笔记
  2. 内核版本:2.6.x

  3.   
  4.   Major and Minor Numbers
  5. 内核通过major号来识别设备,下面的命令列出的是系统上所连接的设备及其major number,第一列就是设备的major number.
  6. $ cat /proc/devices
  7. Character devices:
  8.   1 mem
  9.   4 /dev/vc/0
  10.   4 tty
  11.   5 /dev/tty
  12.   5 /dev/console
  13.   5 /dev/ptmx
  14.   7 vcs
  15. 10 misc
  16. 13 input
  17. 14 sound
  18. 116 alsa
  19. 128 ptm
  20. 136 pts
  21. 180 usb
  22. 195 nvidia
  23. 226 drm
  24. 254 devfs

  25. Block devices:
  26.   3 ide0
  27. 22 ide1
  28. $ls -l /dev
  29. ..............
  30. crw-------  1 root root 119,   0 Apr 24 11:34 vmnet0
  31. crw-------  1 root root 119,   1 Apr 24 11:34 vmnet1
  32. crw-------  1 root root 119,   2 Apr 24 11:34 vmnet2
  33. crw-------  1 root root 119,   3 Apr 24 11:34 vmnet3
  34. crw-------  1 root root 119,   4 Apr 24 11:34 vmnet4
  35. crw-------  1 root root 119,   5 Apr 24 11:34 vmnet5
  36. crw-------  1 root root 119,   6 Apr 24 11:34 vmnet6
  37. crw-------  1 root root 119,   7 Apr 24 11:34 vmnet7
  38. crw-------  1 root root 119,   8 Apr 24 11:34 vmnet8
  39. ..............
  40. 可以看到他们的major number 是119,但他们的minor number不同.分别是0~8.
  41. 内核只关心major number,而minor number 是由设备驱动来区别的.
  42. 内核内部,类型dev_t存储着设备号,且定义了一组宏来维护它.
  43.         MKDEV(int major,int minor);//return dev_t
  44.         MAJOR( dev_t dev);
  45.         MINOR (dev_t dev);
  46. 比如,我们用mknod建立一个新的设备文件
  47.         #mknod /dev/newchr c 50 0
  48. 建立/dev/newchr设备文件,类型是c(char,字符型),major number 是50,minor number 是0.mknod的用法可以用man来查看.
  49. 在内核内部,我们用上面的宏来维护:
  50.         dev_t mydev;
  51.         mydev=MKDEV(50,0);
  52. 我们也可以由mydev得到major 和minor number.
  53.         int major,minor;
  54.         major=MAJOR(mydev);
  55.         minor=MINOR(mydev);


  56.   注册设备号
  57. 我们定义好major和minor number 后就可以在内核中注册一个设备了.注册一个字符设备需要用到下面几个函数:
  58.         int register_chrdev_region(dev_t first,unsigned int count,
  59.                                                 char *name);
  60. first是你要注册的设备号范围的开始(其中minor号一般设置为0),count是你所申请的连续设备号的总数.name是设备的名称.它会在/proc/devices中出现.
  61.         int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,
  62.                                 unsigned int count,char *name);
  63. 这个函数是用来动态分配设备号的.有余开发者不知道所要用的major号是多少,便让内核动态分配一个.参数dev是个output-only参数.
  64.         void unregister_chrdev_region(dev_t first,unsigned int count);
  65. 一般在模块清除函数中调用.

  66. 重要的数据结构
  67. 正如你所想象的,注册只是第一步,后面还有更重要的部分,其中一个就是我们一开始提到的如何实现open/read/write/ioctl等用户接口.
  68. 首先看一下几个重要的数据结构:
  69.         file_operations,file,inode
  70. <linux/fs.h>中定义了这三个结构.
  71. struct file_operations {
  72.         struct module *owner;
  73.         loff_t (*llseek) (struct file *, loff_t, int);
  74.         ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
  75.         ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
  76.         ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
  77.         ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
  78.         int (*readdir) (struct file *, void *, filldir_t);
  79.         unsigned int (*poll) (struct file *, struct poll_table_struct *);
  80.         int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
  81.         int (*mmap) (struct file *, struct vm_area_struct *);
  82.         int (*open) (struct inode *, struct file *);
  83.         int (*flush) (struct file *);
  84.         int (*release) (struct inode *, struct file *);
  85.         int (*fsync) (struct file *, struct dentry *, int datasync);
  86.         int (*aio_fsync) (struct kiocb *, int datasync);
  87.         int (*fasync) (int, struct file *, int);
  88.         int (*lock) (struct file *, int, struct file_lock *);
  89.         ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
  90.         ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
  91.         ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
  92.         ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
  93.         unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
  94.         int (*check_flags)(int);
  95.         int (*dir_notify)(struct file *filp, unsigned long arg);
  96. }
  97. file_operations结构定义了一组函数指针,每一个打开的文件(用struct file表示)和他自己的一组函数(包含在一个叫f_op的域中,它指向一个struct file_operations结构)相联系.这些操作都是来实现系统调用的,所以才被命名为open,read,等等.对于那些不需要的功能(比如你的设备不需要write功能,即不需要向设备写数据),可以给write指针付NULL.

  98. struct file {
  99.         struct list_head        f_list;
  100.         struct dentry           *f_dentry;
  101.         struct vfsmount         *f_vfsmnt;
  102.         struct file_operations  *f_op;
  103.         atomic_t                f_count;
  104.         unsigned int            f_flags;
  105.         mode_t                  f_mode;
  106.         int                     f_error;
  107.         loff_t                  f_pos;
  108.         struct fown_struct      f_owner;
  109.         unsigned int            f_uid, f_gid;
  110.         struct file_ra_state    f_ra;

  111.         unsigned long           f_version;
  112.         void                    *f_security;

  113.         /* needed for tty driver, and maybe others */
  114.         void                    *private_data;

  115. #ifdef CONFIG_EPOLL
  116.         /* Used by fs/eventpoll.c to link all the hooks to this file */
  117.         struct list_head        f_ep_links;
  118.         spinlock_t              f_ep_lock;
  119. #endif /* #ifdef CONFIG_EPOLL */
  120.         struct address_space    *f_mapping;
  121. };
  122. 每个打开的文件对应一个struct file.它在被打开时由内核创建,并传给它所有可以操作该文件的函数,到文件被关闭时才被删除.

  123. The inode Structure.
  124. Inode结构是用来在内核内部表示文件的.同一个文件可以被打开好多次,所以可以对应很多struct file,但是只对应一个struct inode.该结构里面包含了很多信息,但是,驱动开发者只关心里面两个重要的域:
  125.         dev_t i_rdev;//含有真正的设备号
  126.         struct cdev *i_cdev;//struct cdev是内核内部表示字符设备的结构.

  127. 注册字符设备

  128. <linux/cdev.h>
  129. 可以有两种方法注册
  130.         struct cdev *my_cdev=cdev_alloc();
  131.         my_cdev->ops=&my_fops;

  132.         void cdev_init(struct cdev *cdev,struct file_operations *fops);

  133. 注册完后还要通知内核一声,通过调用
  134.         int cdev_add(struct cdev *dev,dev_t num,unsigned int count);
  135. count 一般是 1.

  136. 删除字符设备,调用
  137.         void cdev_del(struct cdev *dev);


  138. 说了那么多,现在可以来个例子了.
  139. #include <linux/kernel.h>
  140. #include <linux/fs.h>
  141. #include <linux/module.h>
  142. #include <asm/uaccess.h>
  143. #include <linux/cdev.h>
  144. #include <asm/uaccess.h>


  145. #define DP_MAJOR 50
  146. #define DP_MINOR 0
  147. static int char_read(struct file *filp,char __user *buffer,size_t,loff_t *);
  148. static int char_open(struct inode *,struct file *);
  149. static int char_write(struct file *filp,const char __user *buffer,size_t ,loff_t*);
  150. static int char_release(struct inode *,struct file *);
  151. static char *arr,*p;
  152. static int chropen;
  153. struct cdev *my_cdev;
  154. static int len;
  155. struct file_operations Fops = {
  156.         .read = char_read,
  157.         .write = char_write,
  158.         .open = char_open,
  159.         .release = char_release,        /* a.k.a. close */
  160. };
  161. static int __init char_init(void)
  162. {
  163.         printk(KERN_ALERT"Initing......\n");
  164.         dev_t dev;
  165.         dev=MKDEV(DP_MAJOR,DP_MINOR);
  166.         my_cdev = cdev_alloc( );
  167.         arr=kmalloc(1024,GFP_KERNEL);
  168.         if(arr==NULL){
  169.                 printk(KERN_ALERT"kmalloc error\n");
  170.         }
  171.         sprintf(arr,"Hello,Pid=%d\n",current->pid);
  172.         if(my_cdev==NULL){
  173.                 return -1;
  174.         }
  175.         if(register_chrdev_region(dev,10,"dpchr")<0){
  176.                 printk(KERN_ALERT"Register char dev error\n");
  177.                 return -1;
  178.         }
  179.         chropen=0;
  180.         len=0;
  181.         my_cdev->ops = &Fops;
  182.         cdev_init(my_cdev,&Fops);
  183.         cdev_add(my_cdev,dev,1);

  184.         return 0;
  185. }

  186. static int char_open(struct inode *inode,struct file *file)
  187. {
  188.         if(chropen==0)
  189.                 chropen++;
  190.         else{
  191.                 printk(KERN_ALERT"Another process open the char device\n");
  192.                 return -1;
  193.         }
  194.         p=arr;
  195.         try_module_get(THIS_MODULE);
  196.         return 0;
  197. }

  198. static int char_release(struct inode *inode,struct file *file)
  199. {
  200.         chropen--;
  201.         module_put(THIS_MODULE);
  202.         return 0;
  203. }

  204. static int char_read(struct file *filp,char __user *buffer,size_t length,loff_t *offset)
  205. {
  206.         int i=0;
  207.         if(*p=='\0')
  208.                 return 0;
  209.         while(length&&*p){
  210.                 put_user(*(p++),buffer++);
  211.                 length--;
  212.                 i++;
  213.         }
  214.         return i;
  215. }

  216. static int char_write(struct file *filp,const char __user  *buffer,size_t length,loff_t *offset)
  217. {

  218.         int i;
  219.         for(i=0;i<length&&i<1024;i++)
  220.                 get_user(p[i],buffer+i);

  221.         p[i]=0;
  222.         len=i;
  223.         return i;
  224. }
  225.        

  226. static void module_close()
  227. {
  228.         len=0;
  229.         printk(KERN_ALERT"Unloading..........\n");
  230.         kfree(arr);
  231.         unregister_chrdev_region(MKDEV(DP_MAJOR,DP_MINOR),10);
  232.         cdev_del(my_cdev);
  233. }

  234. module_init(char_init);
  235. module_exit(module_close);

  236. 需要注意的是,用户调用read/write时返回的值便是我们实现的函数(char_read,char_write)返回的值,所以我们不能随便的返回一个值(比如,0,用户的read/write返回0,所以会认为出错了,然后并没有出错,只是我们返回了一个错误的值而已.


  237. 参考资料
  238. 内核模块的编程http://www.tldp.org/LDP/lkmpg/2.6/html/index.html
  239. Linux Device Driver 3rd Edition
  240. Linux Kernel Development 2nd Edition
复制代码
发表于 2006-8-10 23:15:02 | 显示全部楼层
既然是两种字符设备注册方法
可以有两种方法注册
        struct cdev *my_cdev=cdev_alloc();
        my_cdev->ops=&my_fops;

        void cdev_init(struct cdev *cdev,struct file_operations *fops);
为什么例子两种注册方法都用了?
my_cdev = cdev_alloc( );
cdev_init(my_cdev,&Fops);
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表