ioctl方法能够很多完成read和write方法无法做到的任务,通常可以用来配置驱动程序的参数,对实现复杂的设备操作非常有利,因此运用非常广泛。sucll设备也实现了ioctl方法。
int scull_ioctl(struct file *filp,unsigned int cmd, unsigned long arg)
ioctl的命令由32bit组成,如下图所示
最高的2位是保留位,用来指示访问模式,也就是数据传输方向,防止越界写入用户空间。低16位的幻数和序数唯一地标记了命令,中间14位是用户空间传递参数的大小,限制在16kB-1的大小。内核提供了很多宏来构建各种各样的命令,定义在。其中还提供了很多解码用的宏,驱动程序通过检查幻数和序数可以判断传递过来的命令是否可以处理。接下来判断数据传输方向,并用access_ok()函数验证用户空间地址可读或者可写,access_ok() 是对内核而言的,数据传输方向于传入的参数相反,比如,解码cmd后得到的方向是写,这个方向是从用户空间写数据到内核空间,那么对内核而言,就是从用户空间读数据了。根据命令的幻数和序数定义了不同的功能,驱动程序主要通过调用put_user向用户空间拷贝数据,调用get_user()从用户空间拷贝数据,capable()用于验证用户空间程序用户的权限,防止越权操作。
现在可以在用户空间写一个程序,调用ioctl()依次对scull进行设置,通知,获取,查询,交换,切换等操作.程序如下:
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<fcntl.h>
- #include<errno.h>
- #include<string.h>
- #include<sys/ioctl.h>
- #include "ioctlscull.h"
- int main(int argc,char **argv)
- {
- int ret,cmd,fd,quantum,qset;
-
- fd=open("/dev/scull0",O_RDWR);
- if(fd<0)
- printf("can not open scull0\n");
- if(argc>=2)
- {
- quantum=atoi(argv[1]);
- ret=ioctl(fd,SCULL_IOCSQUANTUM,&quantum); //这里传入的是实参
- if(ret<0)
- {
- printf("set quantum ioctl error:%s\n",strerror(errno));
- exit(0);
- }
- if(argc==3)
- {
- qset=atoi(argv[2]);
- ret=ioctl(fd,SCULL_IOCSQSET,&qset);
- if(ret<0)
- {
- printf("set qset ioctl error:%s\n",strerror(errno));
- exit(0);
- }
- }
- }
-
- ret=ioctl(fd,SCULL_IOCQQUANTUM);
- if(ret<0)
- {
- printf("read quantum ioctl error:%s\n",strerror(errno));
- exit(0);
- }
- else
- printf("%d\n",ret);
-
- ret=ioctl(fd,SCULL_IOCQQSET);
- if(ret<0)
- {
- printf("read qset ioctl error:%s\n",strerror(errno));
- exit(0);
- }
- else
- printf("%d\n",ret);
-
- close(fd);
- return 0;
- }
其中scull.h定义了ioctl的命令
- #ifndef _IOCTLSCULL_H
- #define _IOCTLSCULL_H
- #define SCULL_IOCSQUANTUM 0x40046b01
- #define SCULL_IOCSQSET 0x40046b02
- #define SCULL_IOCTQUANTUM 0x6b03
- #define SCULL_IOCTQSET 0x6b04
- #define SCULL_IOCGQUANTUM 0x80046b05
- #define SCULL_IOCGQSET 0x80046b06
- #define SCULL_IOCQQUANTUM 0x6b07
- #define SCULL_IOCQQSET 0x6b08
- #endif
用sudo运行程序,结果如下,可以对QUANTUM 和QSET的值进行设置;
[jqzeng@garden ioctlScull]$ sudo ./ioctlScull
1000
4000
[jqzeng@garden ioctlScull]$ sudo ./ioctlScull 1234
1234
4000
[jqzeng@garden ioctlScull]$ sudo ./ioctlScull 1234 456789
1234
456789
阅读(7364) | 评论(0) | 转发(4) |