高级字符驱动程序操作--增加了async功能

/* How to use: * # echo "" > /var/log/messages //clear file * # insmod pipe.ko * # mknod /dev/myp c 250 0 * # make async_test * # ./async_test * Open a new terminal * # echo 1 > /dev/myp * return to the first terminal, * you can see: * Receive a signal from /dev/myp,signalnum:29 */ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> /* printk(), min() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/proc_fs.h> #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/fcntl.h> #include <linux/poll.h> #include <linux/cdev.h> #include <asm/uaccess.h> #include <linux/wait.h> /* wait_event_interruptible() */ //#include <asm/semaphore.h> /* down_interruptible() up() */ #include <linux/semaphore.h> #include <linux/sched.h> //error: ‘TASK_INTERRUPTIBLE’ undeclared #define MYDEBUG 1 //turn on debug info printing switch #define BUF_SIZE 1024 //pipe's buffer size /* ioctl */ #define MY_P_IOC_MAGIC 'p' //use 'p' as magic number #define MY_P_IOCRESET _IO(MY_P_IOC_MAGIC, 0)//clear content of device buffer #define MY_P_IOFILL _IOW(MY_P_IOC_MAGIC, 1, char)//fill device buffer with argument value #define MY_P_IOXCH _IOWR(MY_P_IOC_MAGIC,2, char*) //read out the charactor from buffer,and filled with new value #define MY_P_IOC_MAXNR 2 // print debug infomation #define print_debug_info() / do { / printk (KERN_NOTICE " %s called success /n", __func__);/ }while (0) struct my_pipe { wait_queue_head_t inq, outq; // read and write queue char* buffer, *end; //begin of buffer,end of buffer int buffersize; // sizeof buffer int nreaders, nwriters; // number of openings for r/w char* rp, *wp; // where to read, where to write struct fasync_struct *async_r_queue; //asynchronous reader queue struct fasync_struct *async_w_queue; // asynchronous writer queue struct semaphore sem;//mutual exclusion semaphore struct cdev cdev;// Char device structure }; int mydev_major, mydev_minor;//record the device major and minor number struct my_pipe * my_p_dev; // pointer to our device int my_p_buf_size = BUF_SIZE; // size of my_pipe's buffer //declaration of functions void my_p_clearup_module (void); int my_p_setup_cdev(struct my_pipe* my_p_dev, dev_t devno); int my_p_init_module (void); static int my_p_open(struct inode *inode, struct file *filp); static int my_p_release(struct inode *inode, struct file *filp); static ssize_t my_p_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); static ssize_t my_p_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos); int my_p_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); static unsigned int my_p_poll(struct file *filp, poll_table *wait); static int my_p_fasync(int fd, struct file *filp, int mode); struct file_operations my_p_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = my_p_read, .write = my_p_write, .poll = my_p_poll, .ioctl = my_p_ioctl, .open = my_p_open, .release = my_p_release, .fasync = my_p_fasync, }; int my_p_init_module (void) { int result = 0; dev_t dev; result = alloc_chrdev_region(&dev, 0/*minor*/, 1/*dev nums*/,"my_pipe"); mydev_major = MAJOR(dev);//store the major and minor number of our device mydev_minor = MINOR(dev); if (result < 0) { printk (KERN_WARNING "my_pipe: alloc major error!/n"); return result; } #ifdef MYDEBUG else { printk (KERN_WARNING "my_pipe: major is %d/n", mydev_major); } #endif // allocate device structure my_p_dev = kmalloc (sizeof(struct my_pipe), GFP_KERNEL); if (!my_p_dev) { printk (KERN_WARNING "my_pipe: alloc struct my_pipe error!/n"); goto fail; } //init our device items memset (my_p_dev, 0, sizeof(my_p_dev)); init_waitqueue_head(&my_p_dev->inq); init_waitqueue_head(&my_p_dev->outq); init_MUTEX(&my_p_dev->sem); my_p_dev->buffer = NULL;//This is very important if (my_p_setup_cdev(my_p_dev, dev) < 0) { printk (KERN_NOTICE "my_p_setup_cdev called error/n"); goto fail; } #ifdef MYDEBUG print_debug_info(); // current function success #endif return 0; fail: my_p_clearup_module(); return result; } int my_p_setup_cdev(struct my_pipe* my_p_dev, dev_t devno) { int result; cdev_init(&my_p_dev->cdev, &my_p_fops); my_p_dev->cdev.owner = THIS_MODULE; my_p_dev->cdev.ops = &my_p_fops; result = cdev_add (&my_p_dev->cdev, devno, 1); printk(KERN_NOTICE "result is %d/n", result); if (result < 0) { printk (KERN_NOTICE "my_p_dev setup error! /n"); return -EFAULT; } //#ifdef MYDEGUG print_debug_info();//success //#endif return result; } /* free the device mem and free the device struct */ void my_p_clearup_module (void) { dev_t devno = MKDEV(mydev_major, mydev_minor); //The queue memery has been freed by my_p_release cdev_del (&my_p_dev->cdev); // unregister charactor number region unregister_chrdev_region(devno, 1); //here we should free the device struct kfree (my_p_dev); //#ifdef MYDEGUG print_debug_info();//success //#endif } static int my_p_open(struct inode *inode, struct file *filp) { struct my_pipe *dev; dev = container_of(inode->i_cdev, struct my_pipe, cdev);//get and //save device's pointer filp->private_data = dev; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; if (!dev->buffer) { dev->buffer = kmalloc(my_p_buf_size, GFP_KERNEL);//allocate the buffer if(!dev->buffer){ up(&dev->sem); return -ENOMEM; } printk(KERN_NOTICE "mem allocate success/n"); // } dev->buffersize = my_p_buf_size; dev->end = dev->buffer + dev->buffersize; dev->rp = dev->buffer; dev->wp = dev->buffer; } if (filp->f_mode & FMODE_READ) dev->nreaders++; if (filp->f_mode & FMODE_WRITE) dev->nwriters++; up(&dev->sem); //#ifdef MYDEGUG print_debug_info();//success //#endif // return 0; return nonseekable_open(inode, filp);//don't want the seekable file descriptor } static int my_p_release(struct inode *inode, struct file *filp) { struct my_pipe *dev; dev = filp->private_data; // get device pointer /* remove this filp from the asynchronously notified filp's */ my_p_fasync(-1, filp, 0); down(&dev->sem); if (filp->f_mode & FMODE_READ) dev->nreaders--; if (filp->f_mode & FMODE_WRITE) dev->nwriters--; if ((dev->nreaders + dev->nwriters) == 0) { kfree(dev->buffer); dev->buffer = NULL; printk(KERN_NOTICE "mem release succes/n"); } up(&dev->sem); //#ifdef MYDEGUG print_debug_info();//success //#endif return 0; } /* Full return 1, or return 0 */ int is_full(struct my_pipe* dev) { char* p = NULL; p = dev->wp; p ++; if (p > dev->end) p = dev->buffer; if (p == dev->rp) return 1;//full return 0;//not full } static ssize_t my_p_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct my_pipe* dev = filp->private_data; int max_num = 0; printk(KERN_NOTICE "%s is entered/n", __func__); if (down_interruptible(&dev->sem)){ return -ERESTARTSYS; } while (is_full(dev) == 1) { up(&dev->sem); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(dev->inq,is_full(dev) != 0)) return -ERESTARTSYS; // signal: tell the fs layer to handle it /* otherwise loop, but first reacquire the lock */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; } //ok , now there is place to write to max_num = (int)(dev->rp - dev->wp -1 + my_p_buf_size) % my_p_buf_size; printk(KERN_NOTICE "max_num is %d/t count is %d", max_num, (int)count); count = min(count, (size_t)max_num); if (copy_from_user(dev->wp, buf, count)) { up(&dev->sem); printk(KERN_NOTICE "copy error in %s/n", __func__); return -EFAULT; } dev->wp += count; if (dev->wp == dev->end - 1)//pointer wrapted dev->wp = dev->buffer; up(&dev->sem); wake_up_interruptible(&dev->outq); // blocked in read() and select() //signal asynchronous readers if (dev->async_r_queue) { kill_fasync(&dev->async_r_queue, SIGIO, POLL_IN); } //#ifdef MYDEGUG print_debug_info();//success //#endif return count; } static ssize_t my_p_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct my_pipe* dev = filp->private_data; int max_num = 0; printk(KERN_NOTICE "%s is entered/n", __func__); if (down_interruptible(&dev->sem)){ return -ERESTARTSYS; } printk(KERN_NOTICE "%s before while/n", __func__); while (dev->rp == dev->wp) { // nothing to read up(&dev->sem); // release the lock if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(dev->outq, (dev->rp != dev->wp))) return -ERESTARTSYS; // signal: tell the fs layer to handle it /* otherwise loop, but first reacquire the lock */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; } printk(KERN_NOTICE "%s after while/n", __func__); /* ok, data is there, return something */ max_num = (dev->wp - dev->rp + my_p_buf_size) % my_p_buf_size; count = min((size_t)max_num, count); if (copy_to_user(buf, dev->rp, count)) { up(&dev->sem); return -EFAULT; } //update the read pointer dev->rp += count; if (dev->rp == dev->end - 1) dev->rp = dev->buffer; up(&dev->sem); /* finally, awake any writer */ wake_up_interruptible(&dev->inq); // blocked in write() and select() //signal asynchronous readers if (dev->async_w_queue) { kill_fasync(&dev->async_w_queue, SIGIO, POLL_OUT); } //#ifdef MYDEGUG print_debug_info();//success //#endif return count; } int my_p_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0; int retval = 0; struct my_pipe* dev = filp->private_data; char swap = '/0'; /* * extract the type and number bitfields, and don't decode * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() */ printk(KERN_NOTICE "%s called start.../n", __func__); if (_IOC_TYPE(cmd) != MY_P_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > MY_P_IOC_MAXNR) return -ENOTTY; if (_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); if (err) return -EFAULT; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; switch (cmd) { case MY_P_IOCRESET://clear content of device buffer if (dev->buffer) { dev->wp = dev->buffer; dev->rp = dev->buffer; } break; case MY_P_IOFILL://fill device buffer with argument value printk(KERN_NOTICE "%s called in switch/n", __func__); if (dev->buffer) { printk(KERN_NOTICE "parameter is %c/n", (char)arg); memset(dev->buffer, (char)arg, dev->buffersize - 1); dev->wp = dev->end - 1; dev->rp = dev->buffer; printk(KERN_NOTICE "dev->rp's content is %c /ndev->wp's content is %c /n", *dev->rp, *dev->wp); break; } retval = -EINVAL; break; case MY_P_IOXCH://read out the charactor from buffer,and filled with new value if (dev->buffer) { swap = *(dev->buffer); memset(dev->buffer, *((char*)arg), dev->buffersize -1); put_user(swap, (char*)arg); break; } retval = -EINVAL; break; default: retval = -ENOTTY; } up(&dev->sem); return retval; } static unsigned int my_p_poll(struct file *filp, poll_table *wait) { struct my_pipe *dev = filp->private_data; unsigned int mask = 0; /* * The buffer is circular; it is considered full * if "wp" is right behind "rp" and empty if the * two are equal. */ down(&dev->sem); /* add two queue too poll_table */ poll_wait(filp, &dev->inq, wait); poll_wait(filp, &dev->outq, wait); if (dev->rp != dev->wp) mask |= POLLIN | POLLRDNORM; /* readable */ if (!is_full(dev)) mask |= POLLOUT | POLLWRNORM; /* writable */ up(&dev->sem); return mask; } static int my_p_fasync(int fd, struct file *filp, int mode) { struct my_pipe* dev = filp->private_data; int retval = 0; //set dev->async_w_queue if ((retval = fasync_helper(fd, filp, mode, &dev->async_w_queue)) < 0) { return retval; } //set dev->async_r_queue if ((retval = fasync_helper(fd, filp, mode, &dev->async_r_queue)) < 0) { return retval; } print_debug_info();//print debug info return 0; } MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("[email protected]"); module_init(my_p_init_module); module_exit(my_p_clearup_module);

 

------------------用户空间测试程序----------------------------

 

//async_test.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> void input_handler(int signum) { printf("Receive a signal from /dev/myp,signalnum:%d/n",signum); } int main() { int fd, oflags; fd = open("/dev/myp", O_RDWR, S_IRUSR | S_IWUSR); if (fd < 0) { printf("/dev/myp open error/n"); exit(1); } //启动信号驱动机制 signal(SIGIO, input_handler); //set input_handler() to handle SIGIO signal fcntl(fd, F_SETOWN, getpid()); oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, oflags | FASYNC); while(1); }

你可能感兴趣的:(高级字符驱动程序操作--增加了async功能)