大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE稳定放心使用
ioctl是设备驱动程序中对设备的I/O通道进行管理的函数
。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数如下:
int ioctl(int fd, ind cmd, …);
其中fd是用户程序打开设备时使用open函数返回的文件标示符,cmd是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,这个参数的有无和cmd的意义相关。
ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数来控制设备的I/O通道。
简单介绍一下函数:
int (*ioctl) (struct inode * node, struct file *filp, unsigned int cmd, unsigned long arg);
参数:
1)inode和file:ioctl的操作有可能是要修改文件的属性,或者访问硬件。要修改
文件属性的话,就要用到这两个结构体了,所以这里传来了它们的指针。
2)cmd:命令,接下来要长篇大论地说。
3)arg:参数,接下来也要长篇大论。
返回值:
1)如果传入的非法命令,ioctl返回错误号-EINVAL。
2)内核中的驱动函数返回值都有一个默认的方法,只要是正数,内核就会傻乎乎的认为这是正确的返回,并把它传给应用层,如果是负值,内核就会认为它是错误号了。
Ioctl里面多个不同的命令,那就要看它函数的实现来决定返回值了。打个比方,如果ioctl里面有一个类似read的函数,那返回值也就可以像read一样返回。
当然,不返回也是可以的。
ioctl如何实现
在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是应用程序自己的事情。
在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径。
如果有两个不同的设备,但它们的ioctl的cmd(命令码)却一样的,哪天有谁不小心打开错了,并且调用ioctl,这样就完蛋了。因为这个文件里面同样有cmd对应实现,故,我们可以自己生成未使用的命令码。
所以在Linux核心中是这样定义一个命令码的。
一个cmd被分为了4个段,每一段都有各自的意义,cmd的定义在<linux/ioctl.h>。注:但实际上<linux/ioctl.h>中只是包含了<asm/ioctl.h>,这说明了这是跟平台相关的,ARM的定义在<arch/arm/include/asm/ioctl.h>,但这文件也是包含别的文件<asm-generic/ioctl.h>,千找万找,终于找到了。
在<asm-generic/ioctl.h>中,cmd拆分如下:
全部都在
<asm-generic/ioctl.h>
和
ioctl-number.txt
这两个文档有说明
http:/…./linux/include/asm-generic/ioctl.h
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
____________________________________
| 设备类型 | 序列号 | 方向 |数据尺寸|
|———-|——–|——|——–|
| 8 bit | 8 bit |2 bit |8~14 bit|
|———-|——–|——|——–|
这样一来,一个命令就变成了一个整数形式的命令码;但是命令码非常的不直观,所以Linux Kernel中提供了一些宏。这些宏可根据便于理解的字符串生成命令码,或者是从命令码得到一些用户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送方向和数据传输尺寸。
幻数:说得再好听的名字也只不过是个0~0xff的数,占8bit(_IOC_TYPEBITS)。这个数是用来区分不同的驱动的,像设备号申请的时候一样,内核有一个文档给出一些推荐的或者已经被使用的幻数。在内核文件中定义如下:
Ioctl-number.txt (f:\sourceproject\linux-kernel\linux-3.14.26-g2489c02\documentation\ioctl)
点击(此处)折叠或打开
- Code Seq#(hex) Include File Comments
- ========================================================
- 0x00 00–1F linux/fs.h
- 0x00 00–1F scsi/scsi_ioctl.h
- 0x00 00–1F linux/fb.h
- 0x00 00–1F linux/wavefront.h
- 0x02 all linux/fd.h
- 0x03 all linux/hdreg.h
- 0x04 D2–DC linux/umsdos_fs.h Dead since 2.6.11, but don‘t reuse these.
- 0x06 all linux/lp.h
- 0x09 all linux/raid/md_u.h
- 0x10 00–0F drivers/char/s390/vmcp.h
- 0x10 10–1F arch/s390/include/uapi/sclp_ctl.h
- 0x10 20–2F arch/s390/include/uapi/asm/hypfs.h
- 0x12 all linux/fs.h
- linux/blkpg.h
- 0x1b all InfiniBand Subsystem <http://infiniband.sourceforge.net/>
- 0x20 all drivers/cdrom/cm206.h
- 0x22 all scsi/sg.h
- ‘#‘ 00–3F IEEE 1394 Subsystem Block for the entire subsystem
- ‘$‘ 00–0F linux/perf_counter.h, linux/perf_event.h
- …………………
- ………………..
四、CMD参数如何得出
cmd参数在用户程序端由一些宏根据设备类型、序列号、传送方向、数据尺寸等生成,这个整数通过系统调用传递到内核中的驱动程序,再由驱动程序使用解码宏从这个整数中得到设备的类型、序列号、传送方向、数据尺寸等信息,然后通过switch{case}结构进行相应的操作。
Linux内核已经提供了相应的宏来自动生成ioctl命令码:
_IO(type,nr) //无数据传输 |
上面的命令已经定义了方向,我们要传的是幻数
(type)
、序号
(nr)
和大小
(size)
。在这里
szie
的参数只需要填参数的类型,如
int
,上面的命令就会帮你检测类型的正确然后赋值
sizeof(int)
。
相对的,Linux内核也提供了相应的宏来从ioctl命令号种
解码
相应的域值:
_IOC_DIR(nr) //从命令中提取方向 |
例:
/*include_cmd.hpp*/
#define LED_IOC_MAGIC 0x13 //定义幻数
#define LED_MAX_NR 3 //定义命令的最大序数
#define
LED_GPRS_MAGIC _IO(LED_IOC_MAGIC,0x00) //0x00
用”
宏+幻数“来自动生成ioctl命令码
#define LED_WIFI
_MAGIC _IO(LED_IOC_MAGIC,0x01) //0x00
#define LED_BT
_MAGIC _IO(LED_IOC_MAGIC,0x02) //0x00
/*test.cpp*/
fd = open();
ioctl(fd,LED_GPRS_MAGIC,0);
ioctl(fd,LED_GPRS_MAGIC,1);
ioctl(fd,LED_WIFI_MAGIC ,0);
ioctl(fd,LED_WIFI_MAGIC ,1);
/*test_ioctl.c*/
int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, unsigned long arg)
{
if(_IOC_TYPE(cmd) !=LED_IOC_MAGIC ) return -EINVAL; //提取出幻数做检验
if(_IOC_NR(cmd) > LED_MAX_NR ) return -EINVAL; //提取命令序数
switch(cmd){
case LED_GPRS_MAGIC:
if(arg==0){
//……….
}else if(arg ==1){
//……….
}
break;
case LED_WIFI_MAGIC:
//……….
break;
}
}
如果arg是一个整数,可以直接使用;
如果是指针,我们必须确保这个用户地址是有效的,因此,使用之前需要进行正确检查。
内部有检查的,
不需要检测的:
- copy_from_user
- copy_to_user
- get_user
- put_user
需要检测
的:
- __get_user
- __put_user
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/180730.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...