Linux设备驱动工程师之路——高级字符设备驱动程序


高级字符设备驱动在简单字符驱动的基础上添加ioctl方法、阻塞非阻塞读写、poll方法、和自动创建设备文件的功能。 

一、重要知识点 

         1.ioctl

ioctl命令:使用4个字段定义一个ioctl命令,包括

type: 幻数,一般使用一个字符定义,在内核中唯一。

number: 序数。

direction: 数据传输方向,当不涉及数据传输时,此字段无效。

size: 所涉及用户数据的大小,当不涉及数据传输时,此字段无效。

_IOC_NONE

_IOC_READ

_IOC_WRITE

         “方向”字段的可能值。“读”和“写”是不同的位,可以用“OR”在一起指定读写。

_IOC(dir, type, size)

_IO(type,nr)

_IOR(type, nr, size)

_IOW(type, nr, size)

         用于生产ioctl命令的宏

_IOC_DIR(cmd)

_IOC_TYPE(cmd)

_IOC_NR(cmd)

_IOC_SIZE(cmd)

         用于解码ioctl命令的宏

 intaccess_ok(int type, const void *addr, unsigned long size)

         这个函数验证指向用户空间的指针是否可用,如果允许访问,access_ok返回非0值。

int put_user(datum, ptr)

int get_user(local, ptr)

int __put_user(datum, ptr)

int __get_user(local, ptr)

         用于向(或从)用户空间保存(或获取)单个数据项的宏。传送的字节数目由sizeof(*ptr)决定。前两个要先调用access_ok,后两个(__put_user和__get_user)则假设access_ok已经被调用过了。

 

         2.阻塞型I/O

typedef struct {/*…..*/} wait_queue_head_t

void init_waitqueue_head(wait_queue_head_t*queue)

DECLARE_WAIT_QUEUE_HEAD(queue)

         预先定义的Linux内核等待队列类型。wait_queue_head_t类型必须显示地初始化,初始化方法可以在运行时调用init_waitqueue_head,或在编译时DECLARE_WAIT_QUEUE_HEAD。

void wait_event((wait_queue_head_t q, intcondition)

int wait_event_interruptible(wait_queue_head_tq, int condition)

int wait_event_timeout(wait_queue_head_t q,int condition, int time)

int wait_event_interruptible_timeout(wait_queue_head_tq, int condition, int time)

         使进程在指定的队列上休眠,直到给定的condition值为真。

void wake_up(struct wait_queue **q)

void wake_up_interruptible(structwait_queue **q)

           这些函数唤醒休眠在队列q上的进程。_interruptible形式的函数只能唤醒可中断的进程。在实践中约定做法是在使用wait_event时用wake_up,而在使用wait_event_interruptible时使用wake_up_interruptible。

 

         3.poll方法

         poll方法分两步处理,第一步调用poll_wait指定等待队列,第二步返回是否可操作的掩码。

         POLLIN表示设备可读的掩码,POLLRDORM表示数据可读的掩码。POLLOUT表示设备可写的掩码,POLLWRNORM表示数据可读的掩码。一般同时返回POLLIN和POLLRDORM或者POLLOUT和POLLWRNORM。

 

    4.select系统调用

         原型为intselect(int mafdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set*restrict exceptfds, struct timeval *restrict tvptr)

         返回值:就绪的描述符数,若超时则返回0,若出错则返回-1

void FD_ISSET(int fd, fd_set *fdset)

void FD_CLR(int fd, fd_set *fdset)

void FD_SET(int fd, fd_set *fdset)

void FD_ZERO(fd_set *fdset)

         调用FD_ZERO将一个指定的fd_set变量的所有位设置为0。调用FD_SET设置一个fd_set变量指定位。调用FD_CLR则将一指定位清除。最后,调用FD_ISSET测试一指定位是否设置。 

         5.自动创建设备文件

struct class *class_create(struct module*owner, const char *name)

struct device *device_create(struct class*class, struct device *parent, dev_t devt, const char *fmt, ...)          

通过这两个函数可以专门用来创建一个字符设备文件节点,class_create 第一个参数指定所有者,第二参数指定类得名字。class_device_create第一个参数指定第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是设备名称。 

二、驱动代码

  1. #include <linux/module.h>  
  2. #include <linux/types.h>  
  3. #include <linux/fs.h>  
  4. #include <linux/errno.h>  
  5. #include <linux/mm.h>  
  6. #include <linux/sched.h>  
  7. #include <linux/init.h>  
  8. #include <linux/cdev.h>  
  9. #include <asm/io.h>  
  10. #include <asm/system.h>  
  11. #include <asm/uaccess.h>  
  12. #include <linux/ioctl.h>  
  13. #include <linux/wait.h>  
  14. #include <linux/poll.h>  
  15. #include <linux/device.h>  
  16.    
  17.    
  18. #define MEMDEV_MAJOR 251  
  19. #define MEMDEV_NUM 2  
  20. #define MEMDEV_SIZE 1024  
  21.    
  22. //定义设备IOCTL命令  
  23. #define MEMDEV_IOC_MAGIC 'k'  
  24. #define MEMDEV_IOC_NR 2  
  25.    
  26. #define MEMDEV_IOC_PRINT_IO(MEMDEV_IOC_MAGIC, 0)  
  27. #define MEMDEV_IOC_RD_IOR(MEMDEV_IOC_MAGIC, 1, int)  
  28. #define MEMDEV_IOC_WT_IOW(MEMDEV_IOC_MAGIC, 2, char)  
  29.    
  30. struct mem_dev  
  31. {  
  32.          unsignedint size;  
  33.          char*data;  
  34.          structsemaphore sem;  
  35.          wait_queue_head_t  inque;  
  36. };  
  37.    
  38.    
  39. static int mem_major = MEMDEV_MAJOR;  
  40.    
  41. struct cdev mem_cdev;  
  42. struct mem_dev *mem_devp;  
  43. bool havedata = false;  
  44.    
  45.    
  46. static int mem_open(struct inode *inode,struct file *filp)  
  47. {  
  48.          structmem_dev *dev;  
  49.          unsignedint num;  
  50.           
  51.          printk("mem_open.\n");  
  52.           
  53.          numMINOR(inode->i_rdev);//获得次设备号  
  54.          if(num> (MEMDEV_NUM -1))          //检查次设备号有效性  
  55.                    return-ENODEV;  
  56.                     
  57.          dev= &mem_devp[num];  
  58.          filp->private_datadev; //将设备结构保存为私有数据  
  59.           
  60.          return0;  
  61. }  
  62.    
  63. static int mem_release(struct inode *inode,struct file *filp)  
  64. {  
  65.          printk("mem_release.\n");  
  66.          return0;  
  67. }  
  68.    
  69. static ssize_t mem_read(struct file *filp,char __user *buf, size_t size, loff_t *ppos)  
  70. {  
  71.          intret = 0;  
  72.          structmem_dev *dev;  
  73.          unsignedlong p;  
  74.          unsignedlong count;  
  75.           
  76.          printk("mem_read.\n");  
  77.           
  78.          devfilp->private_data;//获得设备结构  
  79.          countsize;  
  80.          p= *ppos;  
  81.           
  82.          //检查偏移量和数据大小的有效性  
  83.          if(p> MEMDEV_SIZE)  
  84.                    return0;  
  85.          if(count> (MEMDEV_SIZE-p))  
  86.                    countMEMDEV_SIZE - p;  
  87.                     
  88.          if(down_interruptible(&dev->sem))//锁定互斥信号量  
  89.                    return-ERESTARTSYS;  
  90.                     
  91.          while(!havedata)  
  92.          {  
  93.                    up(&dev->sem);  
  94.                     
  95.                    if(filp->f_flags& O_NONBLOCK)  
  96.                             return-EAGAIN;  
  97.                    printk("readyto go sleep");  
  98.                    if(wait_event_interruptible(dev->inque,havedata))//等待数据  
  99.                             return-ERESTARTSYS;  
  100.                              
  101.                    if(down_interruptible(&dev->sem))  
  102.                             return-ERESTARTSYS;  
  103.          }  
  104.          //读取数据到用户空间  
  105.          if(copy_to_user(buf,dev->data+p, count)){  
  106.                    ret= -EFAULT;  
  107.                    printk("copyfrom user failed\n");  
  108.          }  
  109.          else{  
  110.                    *ppos+= count;  
  111.                    retcount;  
  112.                    printk("read%ld bytes from dev\n", count);  
  113.                     
  114.                    havedatafalse;//数据已经读出  
  115.                     
  116.          }  
  117.           
  118.          up(&dev->sem);//解锁互斥信号量  
  119.           
  120.          returnret;  
  121. }  
  122.    
  123. static ssize_t mem_write(struct file *filp,const char __user *buf, size_t size, loff_t *ppos)//注意:第二个参数和read方法不同  
  124. {  
  125.          intret = 0;  
  126.          structmem_dev *dev;  
  127.          unsignedlong p;  
  128.          unsignedlong count;  
  129.           
  130.          printk("mem_write.\n");  
  131.           
  132.          devfilp->private_data;  
  133.          countsize;  
  134.          p= *ppos;  
  135.           
  136.          if(p> MEMDEV_SIZE)  
  137.                    return0;  
  138.          if(count> (MEMDEV_SIZE-p))  
  139.                    countMEMDEV_SIZE - p;  
  140.                     
  141.          if(down_interruptible(&dev->sem))//锁定互斥信号量  
  142.                    return-ERESTARTSYS;  
  143.                     
  144.          if(copy_from_user(dev->data+p,buf, count)){  
  145.                    ret= -EFAULT;  
  146.                    printk("copyfrom user failed\n");  
  147.          }  
  148.          else{  
  149.                    *ppos+= count;  
  150.                    retcount;  
  151.                    printk("write%ld bytes to dev\n", count);  
  152.                     
  153.                    havedatatrue;  
  154.                    wake_up_interruptible(&dev->inque);//唤醒等待数据的队列  
  155.          }  
  156.           
  157.          up(&dev->sem);//解锁互斥信号量  
  158.           
  159.          returnret;  
  160. }  
  161.    
  162. static loff_t mem_llseek(struct file *filp,loff_t offset, int whence)  
  163. {  
  164.          intnewpos;  
  165.           
  166.          printk("mem_llseek.\n");  
  167.           
  168.          switch(whence)  
  169.          {  
  170.                    case0://从文件头开始  
  171.                             newposoffset;  
  172.                             break;  
  173.                     
  174.                    case1://从文件当前位置开始  
  175.                             newposfilp->f_pos + offset;  
  176.                             break;  
  177.                              
  178.                    case2://从文件末尾开始  
  179.                             newposMEMDEV_SIZE - 1 + offset;  
  180.                             break;  
  181.                              
  182.                    default:  
  183.                             return-EINVAL;  
  184.          }  
  185.           
  186.          if((newpos<0)|| (newpos>(MEMDEV_SIZE - 1)))  
  187.                    return-EINVAL;  
  188.                     
  189.          filp->f_posnewpos;  
  190.          returnnewpos;  
  191. }  
  192.    
  193. static int mem_ioctl(struct inode *inode,struct file *filp, unsigned int cmd, unsigned long arg)  
  194. {  
  195.          interr = 0ret = 0;  
  196.          intioarg = 0;  
  197.          charrdarg = '0';  
  198.           
  199.          //参数检查  
  200.           
  201.          if(_IOC_TYPE(cmd)!= MEMDEV_IOC_MAGIC)//参数类型检查  
  202.                    return-ENOTTY;  
  203.                     
  204.          if(_IOC_NR(cmd)> MEMDEV_IOC_NR)//参数命令号检查  
  205.                    return-ENOTTY;  
  206.                     
  207.          //用户空间指针有效性检查  
  208.          if(_IOC_DIR(cmd)& _IOC_READ)  
  209.                    err= !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));  
  210.          elseif(_IOC_DIR(cmd) & _IOC_WRITE)  
  211.                    err= !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));  
  212.          if(err)  
  213.                    return-ENOTTY;  
  214.                     
  215.          //根据命令执行操作  
  216.          switch(cmd)  
  217.          {  
  218.           case MEMDEV_IOC_PRINT:  
  219.                    printk("memdevioctl print excuting...\n");  
  220.                    break;  
  221.           
  222.          caseMEMDEV_IOC_RD:  
  223.                    ioarg1024;  
  224.                    ret  = __put_user(ioarg, (int *)arg);//用户空间向内核空间获得数据  
  225.                    printk("memdevioctl read excuting... \n");  
  226.                    break;  
  227.                     
  228.          caseMEMDEV_IOC_WT:  
  229.                    ret__get_user(rdarg, (char *)arg);//用户空间向内核空间传输数据  
  230.                    printk("memdevioctl write excuting... arg:%c\n", rdarg);  
  231.                    break;  
  232.                     
  233.          default:  
  234.                    return-ENOTTY;  
  235.                     
  236.          }  
  237.                     
  238.          returnret;  
  239. }  
  240.    
  241. static unsigned int mem_poll(struct file*filp, poll_table *wait)  
  242. {  
  243.          structmem_dev *dev;  
  244.          unsignedint mask = 0;  
  245.           
  246.          devfilp->private_data;  
  247.           
  248.          if(down_interruptible(&dev->sem))//锁定互斥信号量  
  249.                    return-ERESTARTSYS;  
  250.                    
  251.          poll_wait(filp,&dev->inque, wait);  
  252.          if(havedata)  
  253.                    mask|= POLLIN | POLLRDNORM;//返回可读掩码  
  254.                     
  255.          up(&dev->sem);//释放信号量  
  256.           
  257.          returnmask;  
  258. }  
  259.    
  260. static const struct file_operationsmem_fops = {  
  261.          .ownerTHIS_MODULE,  
  262.          .openmem_open,  
  263.          .writemem_write,  
  264.          .readmem_read,  
  265.          .releasemem_release,  
  266.          .llseekmem_llseek,  
  267.          .ioctlmem_ioctl,  
  268.          .pollmem_poll,  
  269. };  
  270.    
  271. static int __init memdev_init(void)  
  272. {  
  273.          intresult;  
  274.          interr;  
  275.          inti;  
  276.          structclass *memdev_class;  
  277.           
  278.          //申请设备号  
  279.          dev_tdevno = MKDEV(mem_major, 0);  
  280.           
  281.          if(mem_major)  
  282.                    resultregister_chrdev_region(devno, MEMDEV_NUM, "memdev");//注意静态申请的dev_t参数和动态dev_t参数的区别  
  283.          else{                                                                                                                          //静态直接传变量,动态传变量指针  
  284.                    resultalloc_chrdev_region(&devno, 0, MEMDEV_NUM, "memdev");  
  285.                    mem_majorMAJOR(devno);  
  286.          }  
  287.           
  288.          if(result< 0){  
  289.                    printk("can'tget major devno:%d\n", mem_major);  
  290.                    returnresult;  
  291.          }  
  292.           
  293.          //注册设备驱动  
  294.          cdev_init(&mem_cdev,&mem_fops);  
  295.          mem_cdev.ownerTHIS_MODULE;  
  296.           
  297.          errcdev_add(&mem_cdev, MKDEV(mem_major, 0), MEMDEV_NUM);//如果有N个设备就要添加N个设备号  
  298.          if(err)  
  299.                   printk("add cdev faild,err is%d\n", err);  
  300.                     
  301.          //分配设备内存  
  302.          mem_devpkmalloc(MEMDEV_NUM*(sizeof(struct mem_dev)), GFP_KERNEL);  
  303.          if(!mem_devp){  
  304.                     result = - ENOMEM;  
  305.                     goto fail_malloc;  
  306.          }  
  307.          memset(mem_devp,0, MEMDEV_NUM*(sizeof(struct mem_dev)));  
  308.           
  309.           
  310.          for(i=0;i<MEMDEV_NUM; i++){  
  311.                    mem_devp[i].sizeMEMDEV_SIZE;  
  312.                    mem_devp[i].datakmalloc(MEMDEV_SIZE, GFP_KERNEL);  
  313.                    memset(mem_devp[i].data,0, MEMDEV_SIZE);  
  314.                     
  315.                    init_MUTEX(&mem_devp[i].sem);//初始化互斥锁  
  316.                     
  317.                    //初始化等待队列  
  318.                    init_waitqueue_head(&mem_devp[i].inque);  
  319.          }  
  320.           
  321.          //自动创建设备文件  
  322.          memdev_classclass_create(THIS_MODULE, "memdev_driver");  
  323.          device_create(memdev_class,NULL, MKDEV(mem_major, 0), NULL, "memdev0");  
  324.           
  325.          returnresult;  
  326.           
  327. fail_malloc:  
  328.          unregister_chrdev_region(MKDEV(mem_major,0), MEMDEV_NUM);  
  329.          returnresult;  
  330. }  
  331.    
  332. static void memdev_exit(void)  
  333. {  
  334.          cdev_del(&mem_cdev);  
  335.          unregister_chrdev_region(MKDEV(mem_major,0), MEMDEV_NUM);//注意释放的设备号个数一定要和申请的设备号个数保存一致  
  336.                                                                                                                          //否则会导致设备号资源流失  
  337.          printk("memdev_exit\n");  
  338. }  
  339.    
  340. module_init(memdev_init);  
  341. module_exit(memdev_exit);  
  342.    
  343. MODULE_AUTHOR("Y-Kee");  
  344. MODULE_LICENSE("GPL");  

相关内容