The driver code is as follows:
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>#defineSCULL_MAJOR252#define SCULL_NAME"scull"#define MAX_DATA0x10staticint 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");structscull_dev *scull_devp;intscull_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;}intscull_release(struct inode *inode, struct file *filp){printk(KERN_ALERT "close the scull device\n");return 0;}ssize_tscull_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;}staticunsigned 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 structfile_operations scull_fops = {.owner=THIS_MODULE,.open=scull_open,.release=scull_release,.write=scull_write,.read=scull_read,.poll=scull_poll,};staticvoidscull_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);}intscull_init(void){int result;dev_tdevno = 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;}voidscull_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);
Test code
/************************************************************************* *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");}}
Makefile
KERNEL_DIR := /lib/modules/$(shell uname -r)/buildPWD:= $(shell pwd)obj-m := scull.odefault:$(MAKE) -C $(KERNEL_DIR) SUBDIRS=$(PWD) modulestest: test.cgcc $< -o $@.o -gclean:rm -rf *.o *.ko *~ *.order *.symvers *.markers *.mod.c
Test script
1 load_scull.sh
#!/bin/sh/sbin/insmod scull.komknod /dev/scull c 252 0
2 unload_scull.sh
#!/bin/sh/sbin/rmmod scull.korm /dev/scull -f
3 test. Sh
#!/bin/shmake cleanmake make testsudo ./unload_scullsudo ./load_scullsudo ./test.o
Run./test. O during the test. The terminal always outputs poll monitor: Can be write,
On another terminal, in the root user mode, Echo Hello>/dev/scull, you can see the output poll monitor: can be read and poll monitor: Can be write
After multiple echo Hello>/dev/scull times, because the scull device buffer is only 16B, it can no longer be written. At this time, only the poll monitor: can be read is output.