Linux下PCI设备驱动开发详解 下载本文

wake_up_interruptible(&schar_poll_read); }

MSG(\

//return the data written retun count; }

非阻塞性读操作

提供数据服务的驱动程序必须区分阻塞和非阻塞两种打开方式。如果没有足够的数据满足一个请求,我们通常的做法就是使进程进入休眠状态,而一旦有了足 够数据,就再把它唤醒。但如果设备是以非阻塞方式打开 的,我们就不能像刚才那样做了; 我们必须尽可能多的供应数据,而进程在没有数据可读的时候会立刻返回而不是进入休眠状态。 给schar_read函数加上下面阴影部分的代码就实现了非阻塞读操作了;

static ssize_t schar_read(struct file *file, char *buf, size-t count, loft_t *offset) ...

while(count> schar_pool){

//if the device is opened non blocking satisfy what we can of the request and don't put the process to sleep.

if (file->f_flags & O_NONBLOCK){ if(schar_pool>0){

file->f_pos += schar_pool;

if(copy_to_user(buf,schar_buffer,schar_pool)) return -EFAULT;

count = schar_length; schar_pool = 0; return count; }else{

return -EAGAIN; }

MSG(\%u to sleep\\n\ //go to sleep, but wake up on signals interruptible_sleep_on(&schar_wq); if(signal_pending(current)){

MSG(\ //tell vfs about the signal return -EINTR; } } } ... }

schar_read检查f_flag以确定设备当前是以什么方式打开的。如果应用程序请求的数据比我们数据池里的多,就先把里面有的返回给应用程序,等数据池空了时再返回一个“-EAGAIN”错误。 这就暗示读数据进程应该过一会再来试试自己的请求。

查找操作

Schar设备没有自己的查找函数,如果你在register_chrdev函数操作时llseek注册为NULL,那么就表明查找操作将调用内核里的缺 省实现来完成。 内核版本的查找操作可以在fs/read_write.c文件里找到,它的名字是default_llseek,这个调用提供了SEEK_SET、 SEEK_CUR和 SEEK_END三项功能。 这些与llseek用在一起的宏定义的作用是修改file->f_pos的值,即当前文件流里将被读取的下一个字节的位置。如果你想自己定义查找函 数,就必须编写相应的代码。 下面是一个典型的用法示范:

loff_t own_llseek(struct file *file, loff_t offset, int mode) {

switch(mode){

case 0: //seek_set file->f_pos = offset; return file->f_pos; case 1: //seek_SUR file->f_pos += offset; return file->f_pos; case 2: //seek_END return -EINVAL; default:

return -EINVAL; //cannot happen

如果设备没有查找操作在概念的话,就必须定义llseek来阻止查找,可以简单的返回一个\它的意思“查找操作非法”。

loff_t own_llseek(struct file *file, loff_t offset, int mode) {

//illegal seek return -ESPIPE; }

文件操作ioctl:I/O控制

有时候,也需要读取一个运行中驱动程序的参数是有用的,有些驱动程序它们在使用中没有间歇,也不可能把它们从系统里拿下来,重新配置,编译和运行它也就无从谈起。 而ioctl就是驱动程序里让我们能够在它运行时设置或者检索其有关设置情况的入口点。

内核里每个设备都有对应有一个独一无二(基本如此,但也两块硬盘这样的例外)的ioctl基地址和一级命令。举个例子,SCSI主控制器的 ioctl基地址是0X22,并且整个子范围0x00~0xff也都分配给了它。因为大多数设备并不需要支持多达256个命令,所以只用到了子的一小部 分。 16位的基地址构成ioctl命令的上半部分,而16位的设备命令就构成了ioctl命令的下半部分,因此,SCSI主控制器的第一个命令就是 0X2200.ioctl基地址都写在Documentation/ioctl-number.txt文档里,你可以自己仔细查看,查找一个没有使用的基地址。 如果你感兴趣的话,可以在asm/loctl.h文件里查找。

Linux可以区分四种类型的ioctl调用,它们是:直接命令、读、写、读和写。这就是由模块里标识符的写法定义。如下:

_IO(base, command) 定义了中选中命令。 在发出ioctl调用时没有需要传递进出应用程序的数据。

一个_IO类的ioctl调用将返回一个正整数(也就是说它不会被 解释为一个错误)

_IOR(base, command, size) 一个从应用程序角度来看的读操作ioctl命令。size是需要传回给应用程序的参

数的长度

_IOW(base,command,size) 一个应用程序角度看的写操作的ioctl命令。size是从应用程序传来的参数的长

_IOWR(base, command,size) 一个读写操作的ioctl命令。size是来回传递的参数的长度

此外,还有几个用来对待发送命令的合法性进行检查的宏定义,内核方面的编码机制把这个数据

域划分为方向,长度和命令几个部分。这些信息如下:

_IOC_DIR(command) command命令的方向。根据上面介绍的命令类型,这个方向可以是 _IOC_NONE,_IOC_WRITE or _IOC_READ。对_IOWR类的命令,这个方向值

是_IOC_WRITE与_IOC_READ的逻辑或者结果 _IOC_TYPE(command) 这个数据项的ioctl基地址部分,schar的是0xbb _IOC_NR(command) 这个数据项的设备命令部分 _IOC_SIZE(command) 参数的长度--如果有参数的话

ioctl函数本身是在模块注册时提供给内核的file_operations结构里的定义。我们把schar设备支持的ioctl命令定义在schar.h文件里 #define SCHAR_IOCTL_BASE 0xbb

#define SCHAR_TOGGLE_DEBUG _IO(SCHAR_IOCTL_BASE,0)

#define SCHAR_GET_POOL _IOR(SCHAR_IOCTL_BASE,1 , unsigned long) #define SCHAR_EX_TIMER_DELAY _IOWR(SCHAR_IOCTL_BASE,1,unsigned long)

选择基地址远离一切有用的设备,这样的话,就不会引起冲突。 在Schar里我们使用了三种类型的命令。SCHAR_TOGGLE_DEBUG切换打印或不打印调试信息, 因为它是一个_IO类型的ioctl命令,所以不带参数。SCHAR_GET_POOL返回数据池里现有数据的字节长度, SCHAR_EX_TIMER_DELAY设置定时器的每次延时的“jiffies”(内核时基)个数并返回原来的设置值。“jiffies”是内核用来 测量时间的一个基本单位。

schar_ioctl函数几乎完全由一个switch语句构成,设备支持的ioctl命令就在这个语句里得到处理。如下:

static int schar_ioctl(struct inode *inode, struct file *file, unsigned int cmd , unsigned long arg) {

//make sure that the command is really one of schar's if(_IOC_TYPE(cmd) !=SCHAR_IOCTL_BASE) return -ENOTTY;

switch(cmd)

case SCHAR_TOGGLE_DEBUG:{ schar_debug = !schar_debug; return 0; }