学习了驱动程序的规划,感觉在学习驱动的一起学习linux内核,也是很不错的进程哦,做了几个试验,该做一些总结,只要不断的作总结才干形象深入。
我的渠道是虚拟机,fedora14,内核版别为2.6.38.1.其间较之前的版别存在较大的不同,详细的完成现已在上一次总结中给出了。今日首要总结的是ioctl和阻塞读写函数的完成。
一、ioctl函数的完成
首要阐明在2.6.36今后ioctl函数现已不再存在了,而是用unlocked_ioctl和compat_ioctl两个函数完成曾经版别的ioctl函数。一起在参数方面也发生了必定程度的改动,去除了本来ioctl中的struct inode参数,一起改动了回来值。
可是驱动规划进程中存在的问题改动并不是很大,同样在运用程序规划中咱们仍是选用ioctl完成拜访,而并不是unlocked_ioctl函数,因而咱们还能够称之为ioctl函数的完成。
ioctl函数的完成首要是用来完成详细的硬件操控,选用相应的指令操控硬件的详细操作,这样就能使得硬件的操作不再是单调的读写操作。使得硬件的运用愈加的便利。
ioctl函数完成首要包含两个部分,首要是指令的界说,然后才是ioctl函数的完成,指令的界说是选用必定的规矩。
ioctl的指令首要用于运用程序经过该指令操作详细的硬件设备,完成详细的操作,在驱动中首要是对指令进行解析,经过switch-case句子完成不同指令的操控,从而完成不同的硬件操作。
ioctl函数的指令界说办法:
int (*unlocked_ioctl)(struct file*filp,unsigned int cmd,unsigned long arg)
尽管其间没有指针的参数,可是一般选用arg传递指针参数。cmd是一个指令。每一个指令由一个整形数据构成(32bits),将一个指令分红四部分,每一部分完成详细的装备,设备类型(幻数)8bits,方向2bits,序号8bits,数据巨细13/14bits。指令的完成实质上便是经过简略的移位操作,将各个部分组合起来罢了。
一个指令的散布的大约状况如下:
|—方向位(31-30)|—-数据长度(29-16)—————-|———设备类型(15-8)——|———-序号(7-0)———-|
|—————————————————————————————————————————————-|
其间方向位首要是表明对设备的操作,比方读设备,写设备等操作以及读写设备等都具有必定的方向,2个bits只要4种方向。
数据长度表明每一次操作(读、写)数据的巨细,一般罢了每一个指令对应的数据巨细都是一个固定的值,不会常常改动,14bits阐明能够挑选的数据长度最大为16k。
设备类型类似于主设备号(因为8bits,刚好组成一个字节,因而常常选用字符作为幻数,表明某一类设备的指令),用来差异不同的指令类型,也便是特定的设备类型对应特定的设备。序号首要是这一类指令中的详细某一个,类似于次设备号(256个指令),也便是一个设备支撑的指令多达256个。
一起在内核中也存在详细的宏用来界说指令以及解析指令。
可是大部分的宏都仅仅界说详细的方向,其他的都需求规划者界说。
首要的宏如下:
#include
_IO(type,nr) 表明界说一个没有方向的指令,
_IOR(type,nr,size) 表明界说一个类型为type,序号为nr,数据巨细为size的读指令
_IOW(type,nr,size) 表明界说一个类型为type,序号为nr,数据巨细为size的写指令
_IOWR(type,nr,size) 表明界说一个类型为type,序号为nr,数据巨细为size的写读指令
一般的type可选用某一个字母或许数字作为设备指令类型。
是实践运用中一般选用如下的办法界说一个详细的指令:
//头文件
#include
/*界说一系列的指令*/
/*幻数,首要用于表明类型*/
#define MAGIC_NUM k
/*打印指令*/
#define MEMDEV_PRINTF _IO(MAGIC_NUM,1)
/*从设备读一个int数据*/
#define MEMDEV_READ _IOR(MAGIC_NUM,2,int)
/*往设备写一个int数据*/
#define MEMDEV_WRITE _IOW(MAGIC_NUM,3,int)
/*最大的序列号*/
#define MEM_MAX_CMD 3
还有对指令进行解析的宏,用来确认详细指令的四个部分(方向,巨细,类型,序号)详细如下所示:
/*确认指令的方向*/
_IOC_DIR(nr)
/*确认指令的类型*/
_IOC_TYPE(nr)
/*确认指令的序号*/
_IOC_NR(nr)
/*确认指令的巨细*/
_IOC_SIZE(nr)
上面的几个宏能够用来指令,完成指令正确性的查看。
ioctl的完成进程首要包含如下的进程:
1、指令的检测
2、指针参数的检测
3、指令的操控switch-case句子
1、指令的检测首要包含类型的查看,数据巨细,序号的检测,经过结合上面的指令解析宏能够快速的确认。
/*查看类型,幻数是否正确*/
if(_IOC_TYPE(cmd)!=MAGIC_NUM)
return -EINVAL;
/*检测指令序号是否大于答应的最大序号*/
if(_IOC_NR(cmd)> MEM_MAX_CMD)
return -EINVAL;
2、首要是指针参数的检测。指针参数首要是因为内核空间和用户空间的差异性导致的,因而需求来自用户空间指针的有效性。运用copy_from_user,copy_to_user,get_user,put_user之类的函数时,因为函数会完成指针参量的检测,因而能够省掉,可是选用__get_user(),__put_user()之类的函数时必定要进行检测。详细的检测办法如下所示:
if(_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE,(void *)args,_IOC_SIZE(cmd));
else if(_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ,(void *)args,_IOC_SIZE(cmd));
if(err)/*回来过错*/
return -EFAULT;
当方向是读时,阐明是从设备读数据到用户空间,因而要检测用户空间的指针是否可写,选用VERIFY_WRITE,而当方向是写时,阐明是往设备中写数据,因而需求检测用户空间中的指针的可读性VERIFY_READ。查看一般选用access_ok()完成检测,第一个参数为读写,第二个为检测的指针,第三个为数据的巨细。
3、命名的操控:
指令的操控首要是选用switch和case相结合完成的,这于window编程中的检测各种音讯的完成方法是相同的。
/*依据指令履行相应的操作*/
switch(cmd)
{
case MEMDEV_PRINTF:
printk(“<--------CMD MEMDEV_PRINTF Done------------>“);
…
break;
case MEMDEV_READ:
ioarg = &mem_devp->data;
…
ret = __put_user(ioarg,(int *)args);
ioarg = 0;
…
break;
case MEMDEV_WRITE:
…
ret = __get_user(ioarg,(int *)args);
printk(“<--------CMD MEMDEV_WRITE Done ioarg = %d--------->“,ioarg);
ioarg = 0;
…
break;
default:
ret = -EINVAL;
printk(“<-------INVAL CMD--------->“);
break;
}
这仅仅根本的框架结构,实践中依据详细的状况进行修正。这样就完成了根本的指令操控。
声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/dianyuan/kaiguan/317800.html