驱动代码如下:
scull.c
#include <linux/fs.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/gfp.h> #include <linux/poll.h> #include <asm/uaccess.h> #define SCULL_MAJOR 252 #define SCULL_NAME "scull" #define MAX_DATA 0x10 static int scull_major = SCULL_MAJOR; struct scull_dev { struct cdev cdev; unsigned char data[MAX_DATA]; struct semaphore sem; unsigned int current_len; wait_queue_head_t r_wait; wait_queue_head_t w_wait; }; MODULE_LICENSE("GPL"); MODULE_AUTHOR("BG2BKK"); struct scull_dev *scull_devp; int scull_open(struct inode *inode, struct file *filp) { struct scull_dev *dev = container_of(inode->i_cdev, struct scull_dev, cdev); filp->private_data = dev; printk(KERN_ALERT "open the scull device\n"); return 0; } int scull_release(struct inode *inode, struct file *filp) { printk(KERN_ALERT "close the scull device\n"); return 0; } ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_ops) { struct scull_dev *dev = filp->private_data; int ret = 0; DECLARE_WAITQUEUE(wait, current); down(&dev->sem); add_wait_queue(&dev->r_wait, &wait); while(dev->current_len == 0) { if(filp->f_flags & O_NONBLOCK) { ret = -EAGAIN; goto out; } __set_current_state(TASK_INTERRUPTIBLE); up(&dev->sem); schedule(); if(signal_pending(current)){ ret = -ERESTARTSYS; goto out2; } down(&dev->sem); } if(count > dev->current_len) count = dev->current_len; if(copy_to_user(buf, dev->data,count)){ ret = -EFAULT; goto out; } else { memcpy(dev->data, dev->data + count, dev->current_len - count); dev->current_len -= count; printk(KERN_ALERT "read %u bytes; current_len:%d\n",count, dev->current_len); wake_up_interruptible(&dev->w_wait); ret = count; } out: up(&dev->sem); out2: remove_wait_queue(&dev->w_wait, &wait); set_current_state(TASK_RUNNING); return ret; } ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_ops) { struct scull_dev *dev = filp->private_data; int ret = 0; DECLARE_WAITQUEUE(wait, current); down(&dev->sem); add_wait_queue(&dev->w_wait, &wait); while(dev->current_len == MAX_DATA){ if(filp->f_flags & O_NONBLOCK){ ret = -EAGAIN; goto out; } __set_current_state(TASK_INTERRUPTIBLE); up(&dev->sem); schedule(); if(signal_pending(current)){ ret = -ERESTARTSYS; goto out2; } down(&dev->sem); } if(count > MAX_DATA - dev->current_len){ count = MAX_DATA - dev->current_len; } if(copy_from_user(dev->data + dev->current_len, buf, count)){ ret = -EFAULT; goto out; } else { dev->current_len += count; printk(KERN_ALERT "written %d bytes, current_len:%d\n",count, dev->current_len); wake_up_interruptible(&dev->r_wait); ret = count; } out: up(&dev->sem); out2: remove_wait_queue(&dev->w_wait, &wait); set_current_state(TASK_RUNNING); return ret; } static unsigned int scull_poll(struct file *filp, poll_table *wait) { unsigned int mask = 0; struct scull_dev *dev = filp->private_data; down(&dev->sem); poll_wait(filp, &dev->r_wait, wait); poll_wait(filp, &dev->w_wait, wait); if(dev->current_len != 0) mask |= POLLIN | POLLRDNORM; if(dev->current_len != MAX_DATA) mask |= POLLOUT | POLLWRNORM; up(&dev->sem); return mask; } static const struct file_operations scull_fops = { .owner = THIS_MODULE, .open = scull_open, .release= scull_release, .write = scull_write, .read = scull_read, .poll = scull_poll, }; static void scull_setup_cdev(struct scull_dev *dev, int index) { int err, devno = MKDEV(scull_major, index); cdev_init(&dev->cdev, &scull_fops); dev->cdev.owner = THIS_MODULE; err = cdev_add(&dev->cdev, devno, 1); if(err) printk(KERN_NOTICE "ERROR %d adding scull_dev %d", err, index); } int scull_init(void) { int result; dev_t devno = MKDEV(scull_major,0); if(scull_major) result = register_chrdev_region(devno, 1, "scull"); else { result = alloc_chrdev_region(&devno, 0, 1, "scull"); scull_major = MAJOR(devno); } if(result < 0) return result; scull_devp = kmalloc(sizeof(struct scull_dev), GFP_KERNEL); if(!scull_devp){ result = -ENOMEM; goto fail_malloc; } memset(scull_devp, 0 , sizeof(struct scull_dev)); scull_setup_cdev(scull_devp, 0); init_MUTEX(&scull_devp->sem); init_waitqueue_head(&scull_devp->r_wait); init_waitqueue_head(&scull_devp->w_wait); printk(KERN_ALERT "init scull device\n"); return 0; fail_malloc: unregister_chrdev_region(devno, 1); return result; } void scull_cleanup(void) { cdev_del(&scull_devp->cdev); kfree(scull_devp); unregister_chrdev_region(MKDEV(scull_major, 0), 1); printk(KERN_ALERT "clean scull device\n"); } module_init(scull_init); module_exit(scull_cleanup);测试代码
/************************************************************************* *fileName: test.c *description: test the myscull.c *author: Hzc *create time: 2007-04-20 *modify info: - *************************************************************************/ #include <stdio.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> #define BUFFER_LEN 20 #define FIFO_CLEAR 0x01 #define device "/dev/scull" int main() { int num; fd_set rfds, wfds; int fd = open(device, O_RDONLY| O_NONBLOCK); if(fd != 0){ while(1){ FD_ZERO(&rfds); FD_ZERO(&wfds); FD_SET(fd, &rfds); FD_SET(fd, &wfds); select(fd+1, &rfds, &wfds, NULL, NULL); if(FD_ISSET(fd, &rfds)) printf("poll monitor: can be read\n"); if(FD_ISSET(fd, &wfds)) printf("poll monitor: can be written\n"); } } else{ printf("Device " device "open failer"); } }
KERNEL_DIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) obj-m := scull.o default: $(MAKE) -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules test: test.c gcc $< -o [email protected] -g clean: rm -rf *.o *.ko *~ *.order *.symvers *.markers *.mod.c
1 load_scull.sh
#!/bin/sh /sbin/insmod scull.ko mknod /dev/scull c 252 0
2 unload_scull.sh
#!/bin/sh /sbin/rmmod scull.ko rm /dev/scull -f
3 test.sh
#!/bin/sh make clean make make test sudo ./unload_scull sudo ./load_scull sudo ./test.o
在另一终端,以根用户模式,echo hello > /dev/scull,可以看到输出poll monitor: can be read和poll monitor: can be write
多echo hello > /dev/scull几次后,由于scull设备缓冲区仅16B,不再能写入,此时只输出poll monitor: can be read