之所以称之为高级字符驱动程序操作,是lddr3的作者相对于第三章而言的。若想把功能搞强大,必然就得学更多的。
ioctl是一个牛逼的东东,之所以牛逼,在于所谓的随心所欲,导致了不是很好啊掌握。首先说什么是ioctl呢,in/out control,输入输出控制,从英语的字面上还是比较好理解。用比拟的方法来讲解或许更好:你是一个美丽的姑娘,然后呢,我就想追你,怎么办呢,鲜花,邂逅,要号等方法。这里,有个对象,就是你,我们想采取方式,比如鲜花,邂逅,要号等,而这些方式都是你自己选择的。而对于每一种方法,有个参数,比如鲜花的数量,邂逅的有个地点,要号可以选择qq,手机号等。
简言之就是:对某个对象采取(参数)措施。其实这就是ioctl。下面我们引出它的原型。
用户空间为:
int ioctl(int fd,unsigned long cmd,...);
驱动空间中为:
int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
虽然看起来形式不一样,但是本质是一样的,innode和filp显然和fd对应,arg与...对应。
讲用户空间的即可,fd为文件,即对象,正如上面的姑娘,然后cmd就是命令,这是我们选择的,正如上面采取追女孩的方法,参数就不解释了。对象和参数都没有问题,唯一我们不清楚的就是cmd这个命令了。下面具体讲解下。
正如电影里面的一样,每一个命令就如一个命令执行者,他的有个ID,4个字段。
1.type,叫幻数(8位),其实就理解为主设备号就好了,多用一个字母表示。
2.number,序数(8位),理解为次设备号。
3.direction,数据传输方向,注意:你应该从应用程序的角度去看这个方向。先列出来。
_IOC_NONE(没有数据传输), _IOC_READ, _IOC_WRITE, 和 _IOC_READ|_IOC_WRITE (数据在2个方向被传送).
你把你的眼睛放在应用程序上,懂吧,这样说吧,你在家里,叫你买东西,你就得出去买,叫你卖庄稼,你就把家你的庄稼拿出去卖。就是这样的,你家住在应用程序,_IOC_READ就是叫你到驱动程序中读取数据,懂了吧。_IOC_WRITE当然就把自己家应用程序的写到驱动程序中。
4.size,数据大小(13或者14位)
实践中编号多半都是用一些构造编号的宏来搞定的_IOC_WRITE,包含在 中,如_IO(type,nr)(给没有参数的命令), _IOR(type, nre, datatype)(给从驱动中读数据的), _IO(type,nr,datatype)(给写数据), 和 _IOWR(type,nr,datatype)(给双向传送).
编号我们也是不可能记得住的,我们可以用宏的方式让其值给有意义的方法,就如你叫13号球员,我必须和记得数字,但是你给它取个名字叫张三,用的是很不是叫13而是叫张三就可以了。此处的张三其实也就是上面说的cmd,但是,只是名字和编号对应,具体的功能我们还得在内核空间中自己去勾画。具体如何做到的呢,下面我们来具体简单实例来说明。
比如我的需求是给quantum赋值为10,这样我们需要写个赋值命令
SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,1,int),我们怎么知道赋值成功没有呢,于是再写个读quantum的命令SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,2),应用空间中,我们就写为如下的scull_test.c
- #include <unistd.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdlib.h>
- #include <linux/ioctl.h>
- #define SCULL_IOC_MAGIC 'k'
- #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int)
- #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7)
- int main()
- {
- int sculltest;
- int code;
-
- if ((sculltest = open("/dev/scull",O_RDWR )) < 0) {
- printf("open error! \n");
- exit(1);
- }
- printf("open scull ! \n");
- code = 10;
- if ( ioctl( sculltest , SCULL_IOCSQUANTUM , &code ) < 0) {
- printf("ioctl SCULL_IOCSQUANTUM error! \n");
- exit(1);
- }
- printf("scull_quantum=%d \n" , ioctl( sculltest , SCULL_IOCQQUANTUM , NULL ) );
- close(sculltest);
- printf("close scull ! \n");
- printf("\n");
- exit(0);
- }
上面的已经很好理解了,先利用工具构造好命令的ID,然后在下面的主函数中运用。这里是写好了,这毕竟是写驱动,于是在驱动中我们得写个ioctl的命令处理函数,正如写write和read函数一样,我们在写驱动的scull.c中加入如下函数scull_ioctl()
- int scull_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
- {
- int err = 0, tmp;
- int retval = 0;
- if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
- if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
- if (_IOC_DIR(cmd) & _IOC_READ)
- err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
- else if (_IOC_DIR(cmd) & _IOC_WRITE)
- err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
- if (err) return -EFAULT;
- switch(cmd) {
- case SCULL_IOCSQUANTUM: if (! capable (CAP_SYS_ADMIN))
- return -EPERM;
- retval = __get_user(scull_quantum, (int __user *)arg);
- break;
- case SCULL_IOCQQUANTUM: return scull_quantum;
- default:
- return -ENOTTY;
- }
- return retval;
- }
暂时只讲下面的switch(cmd),当你带着你的ID来指认自己的孩子的时候,switch让你顺路找到孩子,就是这么简单的。
ioctl基本就讲完了,整体的样子就描述出来了(难点是要把用户空间于驱动内核空间区别开来)。还有一些细节问题,下面再慢慢讲。
1.switch的返回值,这个简单,书上也写了,自己看。
2.ioctl参数验证,参数进入内核就如你进大门,你如果是整数,就代表你是住这里的,随便近,如果你是指针,那就代表你是外来者,勇猛用access_ok来拷问(中声明)。
int access_ok(int type, const void *addr, unsigned long size);
type只能为VERIFY_READ 或者 VERIFY_WRITE, 依据这个要进行的动作是否是读用户空间内存区或者写它。addr和size较好理解。举个例子就是上面代码中的
access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
3.权能和受限操作。
给你了权限你才可以做权限内的事,这个大家都懂的,我们用captable函数实现(定义在)
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
这段代码取之上面那一大段中,就是看你有CAP_SYS_ADMIN(还有其他的,书上都写好了的)这样的权限没有,没有当然就悲剧去谈后世。
最后加上代码,是别人写好的的比较完善的,我就不用自己写了。
ioctl_and_llseek.rar
这个别人写好的代码中,驱动的makefile多半是不能用的,因为作者是采用他的环境写的,下面添加段makefile的代码,然更多的情况和环境可以用。
- ifneq ($(KERNELRELEASE),)
-
-
obj-m:=scull.o
-
-
else
-
-
KERNELDIR:=/lib/modules/$(shell uname -r)/build
-
-
PWD:=$(shell pwd)
-
-
default:
-
-
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
-
-
clean:
-
-
rm -rf *.o *.mod.c *.mod.o *.ko *.symvers *.order
-
-
endif
阅读(645) | 评论(0) | 转发(0) |