Linux字符设备驱动框架笔记(2)

Linux下各个进程都有自己独立的进程空间,即使是将内核的数据映射到用户进程,该数据的PID也会自动转变为该用户进程的PID,由于这种机制的存在,我们不能直接将数据从内核空间和用户空间进行拷贝,而需要专门的拷贝数据函数/宏:

long copy_from_user(void *to, const void __user * from, unsigned long n) long copy_to_user(void __user *to, const void *from, unsigned long n)

这两个函数可以将内核空间的数据拷贝到回调该函数的用户进程的用户进程空间,有了这两个函数,内核中的read,write就可以实现内核空间和用户空间的数据拷贝。

ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset) { long ret = 0; size = size > MAX_KBUF?MAX_KBUF:size; if(copy_to_user(user_buf, kbuf,size) return -EAGAIN; } return 0; } 实现ioctl

ioctl是Linux专门为用户层控制设备设计的系统调用接口,这个接口具有极大的灵活性,我们的设备打算让用户通过哪些命令实现哪些功能,都可以通过它来实现,ioctl在操作方法集中对应的函数指针是long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);,其中的命令和参数完全由驱动指定,Linux建议如图所示的方式定义ioctl()命令

设备类型 序列号 方向 数据尺寸 8bit 8bit 2bit 13/14bit

这里,设备类型字段为一个幻数,可以是0~0xff之间的数,内核中的"ioctl-number.txt"给出了一个推荐的和已经被使用的幻数(但是已经好久没人维护了),新设备驱动定义幻数的时候要避免与其冲突。命令码的方向字段为2bit,表示数据的传输方向,可能的值是:_IOC_NONE,_IOC_READ,_IOC_WRITE和_IOC_READ|_IOC_WRITE。命令码的数据字段表示涉及的用户数据的大小,这个成员的宽度依赖于体系结构,通常是13或14位。内核还定义了_IO(),_IOR(),_IOW(),_IOWR()这4个宏来辅助生成这种格式的命令。这几个宏的作用是根据传入的type(设备类型字段),nr(序列号字段)和size(数据长度字段)和宏名��行的方向字段移位组合生成命令码。内核中还预定义了一些I/O控制命令,如果某设备驱动中包含了与预定义命令一样的命令码,这些命令会被当做预定义命令被内核处理而不是被设备驱动处理,有如下4种:

FIOCLEX:即file ioctl close on exec 对文件设置专用的标志,通知内核当exec()系统带哦用发生时自动关闭打开的文件

FIONCLEX:即file ioctl not close on exec,清除由FIOCLEX设置的标志

FIOQSIZE:获得一个文件或目录的大小,当用于设备文件时,返回一个ENOTTY错误

FIONBIO:即file ioctl non-blocking I/O 这个调用修改flip->f_flags中的O_NONBLOCK标志

我们可以将驱动设计的命令包含在一个头文件中,记录用户程序和驱动程序的命令约定,下面是一个简单的例子

//mycmd.h ... #include <asm/ioctl.h> #define CMDT 'A' #define KARG_SIZE 36 struct karg{ int kval; char kbuf[KARG_SIZE]; }; #define CMD_OFF _IO(CMDT,0) #define CMD_ON _IO(CMDT,1) #define CMD_R _IOR(CMDT,2,struct karg) #define CMD_W _IOW(CMDT,3,struct karg) ... //chrdev.c static long demo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { static struct karg karg = { .kval = 0, .kbuf = {0}, }; struct karg *usr_arg; switch(cmd){ case CMD_ON: /* 开灯 */ break; case CMD_OFF: /* 关灯 */ break; case CMD_R: if(_IOC_SIZE(cmd) != sizeof(karg)){ return -EINVAL; } usr_arg = (struct karg *)arg; if(copy_to_user(usr_arg, &karg, sizeof(karg))){ return -EAGAIN; } break; case CMD_W: if(_IOC_SIZE(cmd) != sizeof(karg)){ return -EINVAL; } usr_arg = (struct karg *)arg; if(copy_from_user(&karg, usr_arg, sizeof(karg))){ return -EAGAIN; } break; default: ; }; return 0; } 创建设备文件

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/6f1f9428db418ddcebd0e73a7baea360.html