分类: LINUX
2008-11-09 11:37:22
关于IOCTL驱动的编写方法LDD这本书确实写的比较明白了,在这呢我就简单的做一个介绍。这里我主要描述自己编写IOCTL驱动时所遇到的问题及其原因。
驱动里的ioctl函数主要实现不用read,write函数的与用户空间的简单数据交互及无参数的命令控制。那么我们如何实现这几种功能的IOCTL函数呢?ioctl驱动中以SWITCH{case A,case B}结构以实现对不同命令的响应,首先我们要对我们要使用的“A”,“B”命令定义一个整个操作系统内唯一的标识,同时这个标识又能够表明我们的操作类型及传递参数的类型(如果需要的话)。linux系统的每一个命令号被分为多个位字段,这些位字段包括type,number,direction,size,分别表示幻数(与设备相关的一个字母,以避免与内核冲突),序数(命令编号),方向位(以用户空间为参照的读,写和无数据传输),size(传递参数类型)。内核中/include/asm/ioctl.h,/Documentation/ioctl-number.txt两个文件表明了我们应该如何定义ioctl命令编号及自定义的幻数,。
看起来构造IOTCL命令这么复杂,庆幸的是内核已经为我们编写了构造命令编号的宏命令。_IO(type,nr),_IOR(type,nr,datatype),_IOW(type,nr,datatype)。它们在
这里把我所写的部分代码贴出来:
驱动程序部分代码:
#define MOTOR_MAGIC 'k'
#define SET_PULSE _IOW(MOTOR_MAGIC, 1,int)
.
.
static int motor_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
{ int data=0;
int retval;
switch (cmd) {
case SET_PULSE:
if (copy_from_user(&data, (int *)arg, sizeof(int)))
return -EFAULT;
.
.
.
}
应用程序部分代码:
#define MOTOR_MAGIC 'k'
#define SET_PULSE _IOW(MOTOR_MAGIC, 1,int)
int pulse,fileno;
fileno = open("/tmp/usb",O_RDWR);
if (fileno == -1) {
printf("open device key error!\n");
return 0;
}
.
.
printf("Please input the pulse:\n"
scanf("%d",pulse);
if(ioctl(fileno,SET_PULSE,pulse)<0){
perror("ioctl error");
exit(1);
}
.
.
这里我们一定要注意的是data 和pulse的数据类型一定要与SET_PULSE _IOW(MOTOR_MAGIC, 1,int)中定义的int类型严格一致,即使是unsigned int 和int类型之间也是有很大差别的,我就是因为设定了unsigned int类型的data和pulse却使用SET_PULSE _IOW(MOTOR_MAGIC, 1,int)搞了我两三天才搞明白。 再有就是ioctl函数的声明一定要与 static int motor_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);相一致,我刚开始就是定义错了,也是搞了我好长时间才明白。因为内核非常的相信你,它不会检查你的IOTCL定义有没有问题,它只负责把你的IOCTL函数映射到驱动的ioctl操作上。其中inode和filp对应应用程序传递的文件描述符,这和传递给open方法的参数一样,参数cmd 由用户空间不经修改的传递给驱动程序,可选的arg参数无论用户空间传递的是指针还是值,它都以unsigned long的形式传递给驱动程序。
还有就是我在驱动中明明是用的是使用地址的copy_from_usr函数,但用户程序却只能传递值.搞了半天最后才知道,原来我的用户程序中的scanf(x),'x'没有加'&'符号,所以这时从终端把读到的数据写到了'x'所代表的地址处,也就是说现在'x'所代表的地址正是所读入的数据.呵呵,总结出的经验就是----->对库函数的参数类型一定要搞清楚.