微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > Linux驱动总结3

Linux驱动总结3

时间:12-01 来源:互联网 点击:

return kobject_name(&dev->

上面的命令解析宏可以快速的确定。

/*检查类型,幻数是否正确*/

if(_IOC_TYPE(cmd)!=MAGIC_NUM)

return -EINVAL;

/*检测命令序号是否大于允许的最大序号*/

if(_IOC_NR(cmd)> MEM_MAX_CMD)

return -EINVAL;

2、主要是指针参数的检测。指针参数主要是因为内核空间和用户空间的差异性导致的,因此需要来自用户空间指针的有效性。使用copy_from_user,copy_to_user,get_user,put_user之类的函数时,由于函数会实现指针参量的检测,因此可以省略,但是采用__get_user(),__put_user()之类的函数时一定要进行检测。具体的检测方法如下所示:

if(_IOC_DIR(cmd) & _IOC_READ)

err = !access_ok(VERIFY_WRITE,(void *)args,_IOC_SIZE(cmd));

else if(_IOC_DIR(cmd) & _IOC_WRITE)

err = !access_ok(VERIFY_READ,(void *)args,_IOC_SIZE(cmd));

if(err)/*返回错误*/

return -EFAULT;

当方向是读时,说明是从设备读数据到用户空间,因此要检测用户空间的指针是否可写,采用VERIFY_WRITE,而当方向是写时,说明是往设备中写数据,因此需要检测用户空间中的指针的可读性VERIFY_READ。检查通常采用access_ok()实现检测,第一个参数为读写,第二个为检测的指针,第三个为数据的大小。

3、命名的控制:

命令的控制主要是采用switch和case相结合实现的,这于window编程中的检测各种消息的实现方式是相同的。

/*根据命令执行相应的操作*/

switch(cmd)

{

case MEMDEV_PRINTF:

printk("<--------CMD MEMDEV_PRINTF Done------------>");

...

break;

case MEMDEV_READ:

ioarg = &mem_devp->data;

...

ret = __put_user(ioarg,(int *)args);

ioarg = 0;

...

break;

case MEMDEV_WRITE:

...

ret = __get_user(ioarg,(int *)args);

printk("<--------CMD MEMDEV_WRITE Done ioarg = %d--------->",ioarg);

ioarg = 0;

...

break;

default:

ret = -EINVAL;

printk("<-------INVAL CMD--------->");

break;

}

这只是基本的框架结构,实际中根据具体的情况进行修改。这样就实现了基本的命令控制。

文件操作支持的集合如下:

/*添加该模块的基本文件操作支持*/

static const struct file_operations mem_fops =

{

/*结尾不是分号,注意其中的差别*/

.owner = THIS_MODULE,

.llseek = mem_llseek,

.read = mem_read,

.write = mem_write,

.open = mem_open,

.release = mem_release,

/*添加新的操作支持*/

.unlocked_ioctl = mem_ioctl,

};

需要注意不是ioctl,而是unlocked_ioctl。

二、设备的堵塞读写方式实现,通常采用等待队列。

设备的堵塞读写方式,默认情况下的读写操作都是堵塞型的,具体的就是如果需要读数据,当设备中没有数据可读的时候应该等待设备中有设备再读,当往设备中写数据时,如果上一次的数据还没有被读完成,则不应该写入数据,就会导致进程的堵塞,等待数据可读写。但是在应用程序中也可以采用非堵塞型的方式进行读写。只要在打开文件的时候添加一个O_NONBLOCK,这样在不能读写的时候就会直接返回,而不会等待。

因此我们在实际设计驱动设备的同时需要考虑读写操作的堵塞方式。堵塞方式的设计主要是通过等待队列实现,通常是将等待队列(实质就是一个链表)的头作为设备数据结构的一部分。在设备初始化过程中初始化等待队列的头。最后在设备读写操作的实现添加相应的等待队列节点,并进行相应的控制。

等待队列的操作基本如下:

1、等待队列的头定义并初始化的过程如下:

方法一:

struct wait_queue_head_t mywaitqueue;

init_waitqueue_head(&mywaitqueue);

方法二:

DECLARE_WAIT_QUEUE_HEAD(mywaitqueue);

以上的两种都能实现定义和初始化等待队列头。

2、创建、移除一个等待队列的节点,并添加、移除相应的队列。

定义一个等待队列的节点:DECLARE_WAITQUEUE(wait,tsk)

其中tsk表示一个进程,可以采用current当前的进程。

添加到定义好的等待队列头中。

add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);

即:add_wait_queue(&mywaitqueue,&wait);

移除等待节点

remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);

即:remove_wait_queue(&mywaitqueue,&wait);

3、等待事件

wait_event(queue,

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top