ioctl

34 篇文章 1 订阅
32 篇文章 0 订阅
函数名: ioctl
功 能: 控制I/O设备
用 法: int ioctl(int handle, int cmd,[int *argdx, int argcx]);

include/asm/ioctl.h中定义的宏的注释:

#define   _IOC_NRBITS        8                               //序数(number)字段的字位宽度,8bits
#define   _IOC_TYPEBITS      8                               //幻数(type)字段的字位宽度,8bits
#define   _IOC_SIZEBITS      14                              //大小(size)字段的字位宽度,14bits
#define   _IOC_DIRBITS       2                               //方向(direction)字段的字位宽度,2bits

#define   _IOC_NRMASK       ((1 << _IOC_NRBITS)-1)    //序数字段的掩码,0x000000FF
#define   _IOC_TYPEMASK     ((1 << _IOC_TYPEBITS)-1)  //幻数字段的掩码,0x000000FF
#define   _IOC_SIZEMASK     ((1 << _IOC_SIZEBITS)-1)   //大小字段的掩码,0x00003FFF
#define   _IOC_DIRMASK      ((1 << _IOC_DIRBITS)-1)    //方向字段的掩码,0x00000003

#define   _IOC_NRSHIFT     0                                //序数字段在整个字段中的位移,0
#define   _IOC_TYPESHIFT   (_IOC_NRSHIFT+_IOC_NRBITS)       //幻数字段的位移,8
#define   _IOC_SIZESHIFT   (_IOC_TYPESHIFT+_IOC_TYPEBITS)   //大小字段的位移,16
#define   _IOC_DIRSHIFT    (_IOC_SIZESHIFT+_IOC_SIZEBITS)   //方向字段的位移,30

/*
* Direction bits.
*/

#define _IOC_NONE    0U     //没有数据传输
#define _IOC_WRITE   1U     //向设备写入数据,驱动程序必须从用户空间读入数据
#define _IOC_READ    2U     //从设备中读取数据,驱动程序必须向用户空间写入数据

#define _IOC(dir,type,nr,size) \
       (((dir)  << _IOC_DIRSHIFT) | \
        ((type) << _IOC_TYPESHIFT) | \
        ((nr)   << _IOC_NRSHIFT) | \
        ((size) << _IOC_SIZESHIFT))



/*
* used to create numbers
*/

//构造无参数的命令编号
#define _IO(type,nr)             _IOC(_IOC_NONE,(type),(nr),0)

//构造从驱动程序中读取数据的命令编号
#define _IOR(type,nr,size)     _IOC(_IOC_READ,(type),(nr),sizeof(size))

//用于向驱动程序写入数据命令
#define _IOW(type,nr,size)    _IOC(_IOC_WRITE,(type),(nr),sizeof(size))

//用于双向传输
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))



/*
*used to decode ioctl numbers..
*/

//从命令参数中解析出数据方向,即写进还是读出
#define _IOC_DIR(nr)          (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)

//从命令参数中解析出幻数type
#define _IOC_TYPE(nr)              (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)

//从命令参数中解析出序数number
#define _IOC_NR(nr)           (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)

//从命令参数中解析出用户数据大小
#define _IOC_SIZE(nr)         (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)


/* ...and for the drivers/sound files... */
#define IOC_IN            (_IOC_WRITE << _IOC_DIRSHIFT)
#define IOC_OUT           (_IOC_READ << _IOC_DIRSHIFT)
#define IOC_INOUT         ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
#define IOCSIZE_MASK      (_IOC_SIZEMASK << _IOC_SIZESHIFT)
#define IOCSIZE_SHIFT     (_IOC_SIZESHIFT)

程序例:

#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>

int main(void) {
..int stat;

/* use func 8 to determine if the default drive is removable */
..stat = ioctl(0, 8, 0, 0);
..if (!stat)
....printf("Drive %c is removable.\n", getdisk() + 'A');
..else
....printf("Drive %c is not removable.\n", getdisk() + 'A');
..return 0;
}

一、 什么是ioctl。
ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设

备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数如下:
int ioctl(int fd, ind cmd, …);       其中fd就是用户程序打开设备时使用open函数返回

的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,

一般最多一个,有或没有是和cmd的意义相关的。       ioctl函数是文件结构中的一个属性分

量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数

控制设备的I/O通道。
二、 ioctl的必要性
如果不用ioctl的话,也可以实现对设备I/O通道的控制,但那就是蛮拧了。例如,我们可以在

驱动程序中实现write的时候检查一下是否有特殊约定的数据流通过,如果有的话,那么后面就

跟着控制命令(一般在socket编程中常常这样做)。但是如果这样做的话,会      导致代码

分工不明,程序结构混乱,程序员自己也会头昏眼花的。       所以,我们就使用ioctl来实

现控制的功能。要记住,用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么

解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。 三、 ioctl如何实现
这是一个很麻烦的问题,我是能省则省。要说清楚它,没有四五千字是不行的,所以我这里是

不可能把它说得非常清楚了,不过如果有读者对用户程序怎么和驱动程序联系起来感兴趣的话

,可以看我前一阵子写的《write的奥秘》。读者只要把write换成ioctl,就知道用户程序的io

ctl是怎么和驱动程序中的ioctl实现联系在一起的了。
我这里说一个大概思路,因为我觉得《Linux设备驱动程序》这本书已经说 的非常清楚了,但

是得化一些时间来看。在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构

,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员

自己的事情,因为设备都是特定的,这里也没法说。关键在于怎么样组织命令码,因为在ioctl

中命令码是唯一联系用户程序命令和驱动程序支持的途径。命令码的组织是有一些讲究的,因

为我们一定要做到命令和设备是一一对应的,这样才不会将正确的命令发给错误的设备,或者

是把错误的命令发给正确的设备,或者是把错误的命令发给错误的设备。这些错误都会导致不

可预料的事情发生,而当程序员发现了这些奇怪的事情的时候,再来调试程序查找错误,那将

是非常困难的事情。所以在Linux核心中是这样定义一个命令码的:
| 设备类型 | 序列号 |方向  |数据尺寸|     
|----------|--------|------|--------|     
| 8 bit    |  8 bit |2 bit |8~14 bit|
|----------|--------|------|--------|
这样一来,一个命令就变成了一个整数形式的命令码。但是命令码非常的不直观,所以Linux

Kernel中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或者是从命令码得到一

些用户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送方向和数据

传输尺寸。这些宏我就不在这里解释了,具体的形式请读者察看Linux核心源代码中的和,文件

里给除了这些宏完整的定义。这里我只多说一个地方,那就是"幻数"。幻数是一个字母,数据

长度也是8,所以就用一个特定的字母来标明设备类型,这和用一 个数字是一样的,只是更加

利于记忆和理解。就是这样,再没有更复杂的了。更多的说了也没有,读者还是看一看源代码

吧,推荐各位阅读《Linux 设备驱动程序》所带源代码中的short一例,因为它比较短小,功能

比较简单,可以看明白ioctl的功能和细节。
四、 cmd参数如何得出
这里确实要说一说,cmd参数在用户程序端由一些宏根据设备类型、序列号、传送方向、数据尺

寸等生成,这个整数通过系统调用传递到内核中的驱动程序,再由驱动程序使用解码宏从这个

整数中得到设备的类型、序列号、传送方向、数据尺寸等信息,然后通过switch{case}结构进

行相应的操作。要透彻理解,只能是通过阅读源代码,我这篇文章实际上只是一个引子。Cmd参

数的组织还是比较复杂的,我认为要搞熟它还是得花不少时间的,但是这是值得的,驱动程序

中最难的是对中断的理解。
五、 小结
ioctl其实没有什么很难的东西需要理解,关键是理解cmd命令码是怎么在用户程序里生成并在

驱动程序里解析的,程序员最主要的工作量在switch{case}结构中,因为对设备的I/O控制都是

通过这一部分的代码实现的。
参考资料:      
1.《Linux设备驱动程序》,鲁宾尼著,中国电力出版社。

2.《write的奥秘》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ioctl是Linux系统中的一个系统调用,用于设备驱动程序和用户空间程序之间的通信。在GPIO驱动程序中,ioctl可以用于控制GPIO的输入输出状态。使用gpiolib库和ioctl命令可以实现GPIO的输入输出操作。 下面是一个使用ioctl控制GPIO的例子: ```c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/gpio.h> #define GPIO_NUM 17 int main(int argc, char *argv[]) { int fd; struct gpiochip_info chip_info; struct gpioline_info line_info; struct gpiohandle_request req; int ret; fd = open("/dev/gpiochip0", O_RDWR); if (fd < 0) { perror("open"); exit(1); } ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip_info); if (ret < 0) { perror("ioctl"); exit(1); } line_info.line_offset = GPIO_NUM; ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line_info); if (ret < 0) { perror("ioctl"); exit(1); } req.lines = 1; req.lineoffsets[0] = GPIO_NUM; req.default_values[0] = 0; req.consumer_label = "gpio_test"; ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req); if (ret < 0) { perror("ioctl"); exit(1); } while (1) { req.values[0] = 1; ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &req); if (ret < 0) { perror("ioctl"); exit(1); } sleep(2); req.values[0] = 0; ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &req); if (ret < 0) { perror("ioctl"); exit(1); } sleep(2); } close(fd); return 0; } ``` 该例子中使用了Linux内核提供的GPIO驱动程序,通过打开/dev/gpiochip0设备文件,使用ioctl命令控制GPIO的输入输出状态。具体来说,该例子中使用了GPIO_GET_CHIPINFO_IOCTL和GPIO_GET_LINEINFO_IOCTL命令获取GPIO芯片和GPIO线路的信息,使用GPIO_GET_LINEHANDLE_IOCTL命令获取GPIO线路的句柄,使用GPIOHANDLE_SET_LINE_VALUES_IOCTL命令设置GPIO线路的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值