简单驱动实现
本帖最后由 qixing1115 于 2013-07-03 17:49:51 编辑 源代码及编译好的模块在以下链接,如有不懂请请看readme文件(我是在linux下写的,包括代码,如果有乱码请在linux下用VI打开 )
http://download.csdn.net/detail/qixing1115/5694401
类似于内核fifo的源码,实现有open/read/write方法,内部有信号机制及阻塞、非阻塞模型的简单应用,可能会有一些BUG,可能只对初学者有些用吧,只供参考
信号机制
阻塞、非阻塞模型
private_data:一个驱动对应多个设备节点的相似设备
fifo采用的是循环队列
首先看下设备的结构体定义
typedef struct fifo_device_t
{
char *buffer;//内存空间
char *buffer_end;//指向内存的结尾
char *rp, *wp;//读/写指针
int buffer_size;//内存空间的大小
int nreader, nwriter;//读者/写者的个数(目前没有用到)
struct cdev cdev;//设备
struct semaphore sem;//信号号
wait_queue_head_t inq, outq;//等待队列头
}fifo_dev_t;
1:static int fifo_init(void)
功能:设备号申请、注册,为设备申请内存空间,主要代码如下:
devno1 = MKDEV(fifo_major, 0);
devno2 = MKDEV(fifo_major, 1);
ret = register_chrdev_region(devno1,devnr, devname);
//为设备申请内存空间,
//注意,1:这里并没有为buffer申请空间,为什么没有在这里申请???
//2:可不可以在这里申请??
//3:为什么申请了两倍的(fifo_dev_t)空间???
fifo_dev = kmalloc(2 * sizeof(fifo_dev_t), GFP_KERNEL);
if(fifo_dev == NULL)
goto err1;
memset(fifo_dev, 0,2 * sizeof(fifo_dev_t));
ret = cdev_setup(&(fifo_dev[0].cdev), devno1);
ret = cdev_setup(&(fifo_dev[1].cdev), devno2);
cdev_init(cdev, &fifo_ops);
cdev->owner = THIS_MODULE;
ret = cdev_add(cdev, devno, devnr);
//找到对应的设备, 原理可以看下container_of(宏)函数的具体实现
tmp_dev = container_of(inode->i_cdev, fifo_dev_t, cdev);
filp->private_data = tmp_dev;
//为buffer 申请空间并对结构体成员进行初始化
tmp_dev->buffer = kmalloc(tmp_dev->buffer_size, GFP_KERNEL);
//注意以下三条初始化是不可以放在fifo_init函数里的
init_MUTEX(&tmp_dev->sem);
init_waitqueue_head(&tmp_dev->inq);
init_waitqueue_head(&tmp_dev->outq);
fifo_dev_t *tmp_dev = filp->private_data;//这句不要忘 了
//这里获取信号量
if(down_interruptible(&tmp_dev->sem) != 0)
return -ERESTARTSYS;
//如果buffer为空的话不是进行读操作
while(fifo_isempty(tmp_dev))
{
up(&tmp_dev->sem);
//如果以非阻塞模式打开的话直接返回,
//如果以阻塞模式打开的话则睡眠
if((filp->f_flags & O_NONBLOCK) != 0)
return -EAGAIN;
else
{
if(wait_event_interruptible(tmp_dev->inq, !fifo_isempty(tmp_dev)) != 0)
return -ERESTARTSYS;
if(down_interruptible(&tmp_dev->sem) != 0)
return -ERESTARTSYS;
}
}
//把buffer 里的内容放在一个临时申请的空间里,我感觉这样操作起来简单一些
//如果你认为这样多余的话可以试着不用临时空间,
for(i = 0; i < n; i ++)
{
if(tmp_dev->rp > tmp_dev->buffer_end)
tmp_dev->rp = tmp_dev->buffer;
*(p + i) = *tmp_dev->rp;
tmp_dev->rp++;
}
//这时是真正的读取了
if(copy_to_user(buf, p, n))
{
up(&tmp_dev->sem);
return -EFAULT;
}
[解决办法]
= POLLOUT
[解决办法]
POLLWRNORM; /* writable */
return mask;
}