让我们用一个”pipe“的设备驱动去结束简单字符设备吧(这里所说的pipe并非标准的pipe只是模拟了一个从一端写入从另一端写入的设备)
测试代码1 测试代码2
#define MAX_SIMPLE_LEN 1024 //buffer 数据长度 struct simple_dev{ char *data; //指向数据的头部 char *datard; //读指针 char *datawr; //写指针 char *dataend; //指向缓冲区的结尾 wait_queue_head_t inq; //读取等待队列头 wait_queue_head_t outq; //写入等待队列头 struct cdev cdev; //字符设备结构 struct semaphore semp; //结构体信号量 };
D("alloc simple_dev struct \n"); char7_dev = kmalloc(DEV_COUNT*sizeof(struct simple_dev), GFP_KERNEL); if( char7_dev == NULL) { printk(KERN_ERR "kmalloc simple_dev err no memory"); unregister_chrdev_region(dev, DEV_COUNT); return -ENOMEM; }
使用kmalloc 给设备结构他分配内存,因为系统可能因为暂时不能分配到内存(虽然很少见),所以在分配到内存后需要检测是否分配成功,在检测到成功分配内存后还需要将所分配的内存区清零,以免以后出现奇怪的错误难以调试(一定要记住)
memset(char7_dev, 0, DEV_COUNT*sizeof(struct simple_dev));
在为自定义结构他申请好内存后,我们需要为每个结构体内的buffer申请内存,使用相同的方法,分配并清零
for( index = 0 ; index < DEV_COUNT ; ++index ) { //char7_dev[index].count = 0 if(( char7_dev[index].data = kmalloc(MAX_SIMPLE_LEN, GFP_KERNEL) )!= NULL ) { memset(char7_dev[index].data, 0, MAX_SIMPLE_LEN); D("kmalloc the data space OK! \n"); } else{ for( --index ; index >= 0 ; --index ) { kfree(char7_dev[index].data); } printk(KERN_ERR "kmalloc simple_dev data number err no memory"); kfree(char7_dev); unregister_chrdev_region(dev, DEV_COUNT); return -ENOMEM; } }
这里需要注意的一点是 在申请某个buffer缓冲区失败时候,需要将已经成功申请的内存释放掉(else中做了这个工作)
在申请好设备及各设备buffer内存后,我们需要对结构中的一些变量进行初始化,
将data指向buffer起始位置, datawr,datard 初始化也指向起始位置(表示空buffer),dataend 指向buffer末尾一个无效位置,用于判断读写位置是否合法
for( index = 0 ; index < DEV_COUNT ; ++index ) { /*init the data ptr*/ char7_dev[index].datard = char7_dev[index].data; char7_dev[index].datawr = char7_dev[index].data; char7_dev[index].dataend = char7_dev[index].data + MAX_SIMPLE_LEN; /*init semaphore, waitqueue_head and so on*/ sema_init(&(char7_dev[index].semp), 1); init_waitqueue_head(&(char7_dev[index].inq)); init_waitqueue_head(&(char7_dev[index].outq)); }
for( index = 0 ; index < DEV_COUNT ; ++index ) { cdev_init(&(char7_dev[index].cdev), &simple_fops); char7_dev[index].cdev.owner = THIS_MODULE; err = cdev_add(&(char7_dev[index].cdev), dev, 1); if(err < 0) { printk(KERN_ERR "add cdev err \n"); goto error1; } else { D( "add %d char dev OK!\n", index+1); } }
struct file_operations simple_fops={ .owner = THIS_MODULE, .open = simple_open, .release = simple_close, .read = simple_read, .write = simple_write, .llseek = simple_llseek, // .ioctl = simple_ioctl, .poll = simple_poll, .mmap = simple_mmap, };
因为2.6.35之后文件操作已经没有ioctl方法,所以不在介绍了
if(down_interruptible(&dev->semp) < 0) { printk(KERN_ERR "[%s]get the mutex lock error %d, %s", current->comm, __LINE__, __func__); return -ERESTARTSYS; } else { D("have get the mutex %d\n", __LINE__); }
if(dev->datawr == dev->datard) //empty buffer { while(dev->datawr == dev->datard)//循环检测是否buffer中是否已经有数据 { up(&dev->semp);//释放信号量 if(filp->f_flags & O_NONBLOCK) //检测用户是否是非阻塞打开 { D("set NONBLOCK mask %d\n", __LINE__); return -EAGAIN; } D("[%s] reading going to sleep!", current->comm); /*将当调用进程加到写等待队列*/ if(wait_event_interruptible(dev->inq, dev->datard != dev->datawr)) { return -ERESTARTSYS; } if(down_interruptible(&dev->semp) < 0)//wait_wvent_interrupt返回,获取信号量 { printk(KERN_ERR "[%s]get the mutex lock error %d, %s", current->comm, __LINE__, __func__); return -ERESTARTSYS; } else { D("have get the mutex %d\n", __LINE__); } } }
看到这里你可能已经知道上边的流程图有一些错误(up 和 down 操作应该在while循环中去做,而不是在整个if 中)由于时间问题上图就不做修改了
如果读指针在写指针之后(datard > datawr)则buffer中的数据就从读位置到buffer结尾,又buffer开头转到写位置
if(dev->datawr < dev->datard) { data_remain = (dev->dataend - dev->datard) + (dev->datawr-dev->data); } else { data_remain = dev->datawr - dev->datard; }如果有一些不理解 你可以参照下边的示意图,应该很容易就理解上述计算buffer内数据长度的
判断数据长度的合法性并计算能够写入用户空间的长度
if(data_remain < 0) { printk(KERN_ERR "the remain data we calculate is wrong check! %d \n", __LINE__); } else if(count > data_remain) { WAR("the data is less than the user want to read\n"); D("we can only copy %d bytes to user\n", data_remain); count = data_remain; } else { }
1、当读取操作不会读到buffer尾部时候,直接将数据copy给用户,调整读指针, 唤醒睡眠在写队列上的进程,释放信号量,相用户返回已经读取的数据长度
if(( dev->datawr > dev->datard ) || (dev->datard + count <= dev->dataend)) { err = copy_to_user(userstr, dev->datard, count); if(err != 0) { printk(KERN_ERR "an error occured when copy data to user:%d\n", __LINE__); up(&dev->semp); return err; } else { D("data copy to user OK\n"); dev->datard = dev->datard + count; if(dev->datard == dataend) dev->datard = dev->data; wake_up_interruptible(&dev->outq); up(&dev->semp); return count; } }
2、如果读到buffer末尾还需要绕回来从数据头部再读取
则先读取read 指针到buffer末尾的数据
然后再从头部读取相应长度的数据
同样在成功读取后需要唤醒写等待队列, 调整读指针, 释放信号量
else { data_remain= (dev->dataend -dev->datard ); /*读取从当前位置到buffer结尾的数据长度*/ err = copy_to_user(userstr, dev->datard+1, data_remain); if(err != 0) { printk(KERN_ERR "an error occured when copy data to user:%d\n", __LINE__); up(&dev->semp); return err; } else { D("data copy to user OK\n"); // up(&dev->semp); } /*从buffer头部读取剩余的长度*/ err = copy_to_user(userstr+data_remain, dev->data, count-data_remain); if(err != 0) { printk(KERN_ERR "an error occured when copy data to user:%d\n", __LINE__); up(&dev->semp); return err; } else { D("data copy to user OK\n"); dev->datard = dev->data+(count-data_remain); wake_up_interruptible(&dev->outq); up(&dev->semp); return count; } }
与读操作类似
检测buffer是否已经满需要检测下即将写入的地址是否有效(是否已经到了尾部位置),如果数据已经写到buffer的结尾则需要调整写
if (dev->datawr+1 == dev->dataend)//即将写入的位置是buffer尾部 next_ptr = dev->data; //调整写入指针的指向 else next_ptr = dev->datawr + 1;
if( next_ptr == dev->datard ) { while(next_ptr == dev->datard) { up(&dev->semp); if(filp->f_flags & O_NONBLOCK) { D("set NONBLOCK mask %d\n", __LINE__); return -EAGAIN; } D("[%s] writing going to sleep!", current->comm); if(wait_event_interruptible(dev->outq, next_ptr != dev->datard)) { return -ERESTARTSYS; } if(down_interruptible(&dev->semp) < 0) { printk(KERN_ERR "[%s]get the mutex lock error %d, %s", current->comm, __LINE__, __func__); return -ERESTARTSYS; } else { D("have get the mutex %d\n", __LINE__); } } }
if(dev->datawr >= dev->datard) { remain_space = (dev->dataend - dev->datawr-1) + (dev->datard - dev->data); } else { remain_space = dev->datard - dev->datawr - 1; }
if( (dev->datawr < dev->datard) || (dev->datawr + count < dev->dataend) ) { err = copy_from_user(dev->datawr, userstr, count); if(err != 0) { printk(KERN_ERR "error occured when copy data from user %d\n", __LINE__); up(&dev->semp); return err; } else { D("data copy from user OK\n"); dev->datawr = dev->datawr + count ; wake_up_interruptible(&dev->inq); up(&dev->semp); return count; } } else { remain_space = dev->dataend - dev->datawr ; err = copy_from_user(dev->datawr, userstr, remain_space); if(err != 0) { printk(KERN_ERR "error occured when copy data from user %d\n", __LINE__); up(&dev->semp); return err; } else { D("copy part of the data from user\n"); } err = copy_from_user(dev->data, userstr+remain_space, count-remain_space); if(err != 0) { printk(KERN_ERR "error occured when copy data from user %d\n", __LINE__); up(&dev->semp); return err; } else{ D("data copy from user OK\n"); dev->datawr = dev->data + (count-remain_space); wake_up_interruptible(&dev->inq); up(&dev->semp); return count; } }
poll_wait(filp, &dev->inq, wait); poll_wait(filp, &dev->outq, wait);
if(dev->datard != dev->datawr) { mask |= POLLIN | POLLRDNORM; //can be read } if(dev->datawr+1 == dev->dataend) next_ptr = dev->data; else next_ptr = dev->datawr+1; if(next_ptr != dev->datard) { mask |= POLLOUT | POLLWRNORM; //can be write }