驱动代码如下:
scull.c
#include
#include
#include
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#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