还是没有接触到怎么控制硬件,但是在书里看到了一个挺巧妙的环形缓冲区实现。
此环形缓冲区实际为一个大小为bufsize的一维数组,有一个rp的读指针,一个wp的写指针。
在数据满时写进程会等待读进程读取数据,数据为空时读进程会等待写进程写入数据。
在上次代码上改的,所以名字还是ioctldemo
ioctldemo.c
#include <linux/module.h> #include <linux/init.h> #include <linux/stat.h> #include <linux/types.h> #include <linux/kdev_t.h> #include <linux/fs.h> #include <linux/moduleparam.h> #include <linux/cdev.h> #include <linux/wait.h> #include <linux/slab.h> #include <linux/semaphore.h> #include <asm-generic/uaccess.h> #include <asm-generic/ioctl.h> #include <asm-generic/current.h> #define IOCTLDEMO_MAJOR 0 #define MODULE_NAME "ioctldemo" #define DEMO_MAGIC 'm' #define DEMO_SIZE int #define DEMO_NR_MAX 1 #define MY_IOCTL_READ _IOR(DEMO_MAGIC,1,DEMO_SIZE); static int ioctldemo_major = IOCTLDEMO_MAJOR; void ioctldemo_exit(void); int ioctldemo_init(void); long my_unlocked_ioctl(struct file*, unsigned int, unsigned long); int my_cdev_open(struct inode*, struct file*); int my_cdev_release(struct inode*,struct file*); ssize_t my_cdev_read (struct file*, char __user*, size_t, loff_t *); ssize_t my_cdev_write(struct file*, const char __user*, size_t, loff_t *); MODULE_LICENSE("Dual BSD/GPL"); module_param(ioctldemo_major,int,S_IRUGO); module_init(ioctldemo_init); module_exit(ioctldemo_exit); struct dev_driver { wait_queue_head_t inq,outq; //read and write queues char *buffer, *end; //begin of buff, end of buff int bufsize;//为了方便取余,必须为2的n次幂 char *rp,*wp; struct semaphore sem; struct cdev my_cdev; }my_driver; static struct file_operations cdev_ops = { .owner = THIS_MODULE, .open = my_cdev_open, .release = my_cdev_release, .read = my_cdev_read, .write = my_cdev_write, }; int __init ioctldemo_init(void) { int ret; dev_t devno; printk(KERN_NOTICE "=== ioctldemo_init start\n"); devno = MKDEV(ioctldemo_major,); if(ioctldemo_major) { printk(KERN_NOTICE "=== ioctldemo_init try register\n"); ret = register_chrdev_region(devno,,MODULE_NAME); }else { printk(KERN_NOTICE "=== ioctldemo_init auto register\n"); ret = alloc_chrdev_region(&devno,,,MODULE_NAME); ioctldemo_major = MAJOR(devno); } ) { printk(KERN_NOTICE "=== ioctldemo_init register fail\n"); return ret; } cdev_init(&my_driver.my_cdev,&cdev_ops); my_driver.my_cdev.owner = THIS_MODULE; ret = cdev_add(&my_driver.my_cdev,MKDEV(ioctldemo_major,),); ) { printk(KERN_NOTICE "=== ioctldemo_init add cdev fail\n"); return ret; } //init buffer my_driver.bufsize= <<; my_driver.buffer = (char*)kmalloc(my_driver.bufsize,GFP_KERNEL); my_driver.end = my_driver.buffer + my_driver.bufsize; my_driver.rp = my_driver.wp = my_driver.buffer; printk(KERN_DEBUG "ioctldemo buf->%p, end->%p",my_driver.buffer,my_driver.end); //init semaphore sema_init(&my_driver.sem,); //init wait queue init_waitqueue_head(&my_driver.inq); init_waitqueue_head(&my_driver.outq); printk(KERN_NOTICE "=== ioctldemo_init finish\n"); ; } void __exit ioctldemo_exit(void) { printk (KERN_NOTICE "=== ioctldemo_exit"); kfree(my_driver.buffer); cdev_del(&my_driver.my_cdev); unregister_chrdev_region(MKDEV(ioctldemo_major,),); } int my_cdev_open(struct inode *node, struct file *filp) { ; } int my_cdev_release(struct inode *node, struct file *filp) { ; } /*一个环形缓冲区的不覆盖读写实现。 *对于 read函数: 写指针和读指针重合时视为数据为空,等待输入数据。每次读取最多读到缓冲区数组尾部。 *对于write函数: 每次写入最多写入到(读指针-1)位置或缓冲区数组尾部,如果写指针在读指针的前一项(相对环形来说)视为队列已满,等待读取数据。 */ ssize_t my_cdev_read (struct file *filp, char __user *buf, size_t count, loff_t *pos) { if(down_interruptible(&my_driver.sem)) return -ERESTARTSYS; while(my_driver.rp == my_driver.wp) { up(&my_driver.sem); if(filp->f_flags == O_NONBLOCK) return -EAGAIN; printk(KERN_NOTICE "%s :reading, go to sleep\n",current->comm); if(wait_event_interruptible(my_driver.inq,(my_driver.rp != my_driver.wp))) return -ERESTARTSYS; if(down_interruptible(&my_driver.sem)) return -ERESTARTSYS; printk(KERN_DEBUG "%s :read awoken from waiting,rp:%p, rp:%p\n",current->comm,my_driver.rp,my_driver.wp); } if(my_driver.wp > my_driver.rp) count = min(count,(size_t)(my_driver.wp - my_driver.rp)); else count = min(count,(size_t)(my_driver.end - my_driver.rp)); if(copy_to_user(buf,my_driver.rp,count)) { up(&my_driver.sem); return -EFAULT; } my_driver.rp += count; if(my_driver.rp == my_driver.end) { my_driver.rp = my_driver.buffer; printk(KERN_DEBUG "ioctldemo convert rp"); } up(&my_driver.sem); wake_up_interruptible(&my_driver.outq); printk(KERN_NOTICE "%s did read %ld bytes\n",current->comm,count); return count; } //get max space that is free now size_t my_cdev_getfree(struct dev_driver *drp) { if(drp->rp == drp->wp) ; )) - ; } ssize_t my_cdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos) { if(down_interruptible(&my_driver.sem)) return -ERESTARTSYS; ) { up(&my_driver.sem); if(filp->f_flags == O_NONBLOCK) return -EAGAIN; printk(KERN_NOTICE "%s :write go to sleep\n",current->comm); ))) return -ERESTARTSYS; if(down_interruptible(&my_driver.sem)) return -ERESTARTSYS; printk(KERN_NOTICE "%s :write awoken from sleep\n",current->comm); } count = min(count,my_cdev_getfree(&my_driver)); if(my_driver.wp >= my_driver.rp) count = min(count,(size_t)(my_driver.end - my_driver.wp)); else// writer pointer has wrapped, fill up to rp-1 count = min(count,(size_t)(my_driver.rp - my_driver.wp -)); if(copy_from_user(my_driver.wp,buf,count)) { up(&my_driver.sem); return -EFAULT; } my_driver.wp += count; if(my_driver.wp == my_driver.end) { my_driver.wp = my_driver.buffer; printk(KERN_DEBUG "ioctldemo convert wp"); } up(&my_driver.sem); wake_up_interruptible(&my_driver.inq); printk(KERN_NOTICE "%s did write %ld bytes\n",current->comm,count); return count; }
编译,安装,卸载脚本见
测试:
例如我把驱动挂载到了/dev/ioctldemo
可以执行以下命令
cat /dev/ioctldemo
此时缓冲区因为没有数据,所以等待输入。
再打开另外一个终端,输入命令
ls > /dev/ioctldemo
可以看到之前的窗口显示出来了数据。