linux设备驱动开发学习之旅--支持阻塞的设备驱动

时间:2021-09-15 23:35:11
[cpp] view plaincopylinux设备驱动开发学习之旅--支持阻塞的设备驱动linux设备驱动开发学习之旅--支持阻塞的设备驱动
  1. /** 
  2.  * Author:hasen 
  3.  * 参考 :《linux设备驱动开发详解》 
  4.  * 简介:android小菜鸟的linux 
  5.  *           设备驱动开发学习之旅 
  6.  * 主题:支持阻塞的设备驱动 
  7.  * Date:2014-11-05 
  8.  */  
  9.   
  10. /*重点已在注释前加上了@@,使用搜索"@@"可以查看重点代码部分*/  
  11.   
  12. /** 
  13.  * 现在实现这样一个设备,globalfifo,只有当FIFO中有数据的时候(即有进程把数据写入了FIFO 
  14.  * 而且没有被读进程读空),读进程才能把数据读出,而且读出的数据慧聪globalfifo的全局内存中 
  15.  * 拿掉;只有当FIFO非满时(即还有一些空间未被写,或写满后被读进程从这个FIFO中读出了数据), 
  16.  * 写进程才能往这个FIFO中写入数据。 
  17.  */  
  18.   
  19. /*globalfifo中,读FIFO将会唤醒写FIFO,写FIFO将会唤醒读FIFO, 
  20.      所以需要两个等待队列头, 分别对应读和写*/  
  21.   
  22. struct globalfifo_dev{  
  23.     struct cdev cdev ;/*cdev结构体*/  
  24.     unsigned int current_len ;/*当前fifo的有效长度*/  
  25.     unsigned char mem[GLOBALFIFO_SIZE] ;/*全局内存*/  
  26.     struct semaphore sem ;/*并发控制用的信号量*/  
  27.     wait_queue_head_t r_wait ;/*@@阻塞读用的等待队列头*/  
  28.     wait_queue_head_t w_wait ;/*@@阻塞写用的等待队列头*/  
  29. }  
  30.   
  31. globalfifo_dev *globalfifo_devp ;  
  32.   
  33. int gobalfifo_init(void)  
  34. {  
  35.     int ret ;   
  36.     dev_t devno = MKDEV(globalfifo_major,0) ;  
  37.     /*申请设备号*/  
  38.     if(globalfifo_major)  
  39.         ret = register_chrdev_region(devno,1,"globalfifo") ;  
  40.     else{  
  41.         ret = alloc_chrdev_region(&devno,0,1,"globalfifo") ;  
  42.         globalfifo_major = MAJOR(devno) ;  
  43.     }  
  44.     if(dev < 0)  
  45.         return ret ;  
  46.     /*动态申请设备结构体内存*/  
  47.     globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev),GFP_KERNEL) ;  
  48.     if(globalfifo_devp){ /*申请失败*/  
  49.         ret = -ENOMEM ;  
  50.         goto fail_malloc ;  
  51.     }  
  52.       
  53.     memset(globalfifo_devp,0,sizeof(struct globalfifo_dev)) ;  
  54.     globalfifo_setup_dev(globalfifo_devp ,0) ;  
  55.       
  56.     init_MUTEX(&globalfifo_devp->sem) ;/*初始化信号量*/  
  57.     init_waitqueue_head(&globalfifo_devp->r_wait) ;/*@@初始化读等待队列头*/  
  58.     init_waitqueue_head(&globalfifo_devp->w_wait) ;/*@@初始化写等待队列头*/  
  59.     return 0 ;  
  60.       
  61.     fail_malloc :unregister_chrdev_region(devno,1) ;  
  62.     return ret ;  
  63. }  
  64.   
  65. /*globalfifo读函数*/  
  66. static ssize_t globalfifo_read(struct file *filp,char __user *buf,  
  67.         size_t count ,loff_t *ppos)  
  68. {  
  69.     int ret ;   
  70.     struct globalfifo_dev *dev = filp->private_data ;/*获得设备结构体指针*/  
  71.     DECLARE_WAITQUEUE(wait,current) ; /*@@定义等待队列*/  
  72.       
  73.     down(&dev->sem) ; /*获得信号量*/  
  74.     add_wait_queue(&dev->r_wait,&wait) ;/*@@进入读等待队列头*/  
  75.       
  76.     /*@@等待FIFO非空*/  
  77.     while(dev->current_len == 0){  
  78.         if(filp->f_flags & O_NONBLOCK){  
  79.             ret = -EAGAIN ;  
  80.             goto out ;  
  81.         }  
  82.           
  83.         __set_current_state(TASK_INTERRUPTIBLE) ;/*@@改变进程状态为睡眠*/  
  84.         up(&dev->sem) ;  
  85.           
  86.         schedule() ;/*@@调度其他进程执行*/  
  87.         if(signal_pending(current)){ /*如果是信号唤醒的*/  
  88.             ret = -ERESTARTSYS ;  
  89.             goto out2 ;  
  90.         }  
  91.           
  92.         down(&dev->sem) ;  
  93.     }  
  94.       
  95.     /*拷贝到用户空间*/  
  96.     if(count >dev->current_len)  
  97.         count = dev->current_len ;  
  98.       
  99.     if(copy_to_user(buf,dev->mem,count)){  
  100.         ret = -EFAULT ;  
  101.         goto out ;  
  102.     }else{  
  103.         memcpy(dev->mem,dev->mem + count,dev->current_len - count);/*fifo数据前移*/  
  104.         dev->current_len -= count ; /*有效数据长度减少*/  
  105.         printk(KERN_INFO "read %d bytes,current_len:%d\n",count,dev->current_len) ;  
  106.           
  107.         wake_up_interruptible(&dev->w_wait) ;/*@@唤醒写等待程序*/  
  108.           
  109.         ret = count ;  
  110.     }  
  111.       
  112.     out :up(&dev->sem) ; /*释放信号量*/  
  113.     out2: remove_wait_queue(&dev->r_wait,&wait) ; /*@@移除等待队列*/  
  114.     set_current_state(TASK_RUNNING) ; /*@@切换当前进程状态*/  
  115.     return ret ;  
  116. }  
  117.   
  118. /*globalfifo写函数*/  
  119. static ssize_t globalfifo_write(static file *filp,const char __user *buf,  
  120.         size_t count,loff_t *ppos)  
  121. {  
  122.     struct globalfifo_dev *dev = filp->private_data ; /*获得设备结构体指针*/  
  123.     int ret ;  
  124.     DECLARE_WAITQUEUE(wait,current) ; /*@@定义等待队列*/  
  125.       
  126.     down(&dev->sem) ;/*获取信号量*/  
  127.     add_wait_queue(&dev->w_wait,&wait) ;/*@@进入写等待队列头*/  
  128.       
  129.     /*@@等待FIFO非满*/  
  130.     while(dev->current_len == GLOBALFIFO_SIZE){  
  131.         if(filp->f_flags & O_NONBLOCK){  
  132.             /*如果是非阻塞访问*/  
  133.             ret = -EAGAIN ;  
  134.             goto out ;  
  135.         }  
  136.         __set_current_state(TASK_INTERRUPTIBLE) ;/*@@改变进程状态为睡眠*/  
  137.         up(&dev->sem) ;  
  138.           
  139.         schedule() ;/*@@调度其他进程执行*/  
  140.         if(signal_pending(current)){  
  141.             /*如果是信号唤醒*/  
  142.             ret = -ERESTARTSYS ;  
  143.             goto out2;  
  144.         }  
  145.         down(&dev->sem) ; /*获得信号量*/  
  146.     }  
  147.     /*从用户空间拷贝到内核空间*/  
  148.     if(count > GLOBALFIFO_SIZE - dev->current_len)  
  149.         count = GLOBALFIFO_SIZE - dev->current_len ;  
  150.     if(copy_from_user(dev->mem + dev->current_len,buf,count)){  
  151.         ret = -EFAULT ;  
  152.         goto out ;  
  153.     }else{  
  154.         dev->current_len += count ;  
  155.         printk(KERN_INFO "write %d bytes ,current_len:%d\n",count,dev->current_len) ;  
  156.           
  157.         wake_up_interruptible(&dev->r_wait) ; /*@@唤醒读等待队列*/  
  158.           
  159.         ret = count ;  
  160.     }  
  161.     out : up(&dev->sem) ; /*释放信号量*/  
  162.     out2: remove_wait_queue(&dev->w_wait,&wait) ;/*@@移除等待队列*/  
  163.     set_current_state(TASK_RUNNING) ;/*改变进程状态*/  
  164.     return ret ;  
  165. }  


[plain] view plaincopy
  1. 在用户空间验证globalfifo的读写  
  2.   
  3. 1、在globalfifo所在文件夹下运行make命令编译得到globalfifo.ko。接着insmod模块:  
  4.       
  5.     hasen@hasen-pc$ sudo su  
  6.     hasen@hasen-pc# insmod globalfifo.ko  
  7.       
  8. 2、创建设备文件节点"/dev/globalfifo"   
  9.       
  10.     hasen@hasen-pc# mknod /dev/globalfifo c 249 0  
  11.       
  12. 3、启动两个进程,一个进程"cat /dev/globalfifo &"在后台执行,一个进程"echo 字符串 /dev/globalfifo"在前台执行  
  13.   
  14.     hasen@hasen-pc# cat /dev/globalfifo &  
  15.     hasen@hasen-pc# echo 'hello world!' /dev/globalfifo  
  16.       
  17. 会看到:每当echo进程向/dev/globalfifo写入一串数据,cat进程就立即将该串数据显示出来。