在为 ioctl 编写代码之前, 你需要选择对应命令的数字. 许多程序员的第一个本能的反 应是选择一组小数从0或1 开始, 并且从此开始向上. 但是, 有充分的理由不这样做. ioctl 命令数字应当在这个系统是唯一的, 为了阻止向错误的设备发出正确的命令而引起 的错误. 这样的不匹配不会不可能发生, 并且一个程序可能发现它自己试图改变一个非串 口输入系统的波特率, 例如一个 FIFO 或者一个音频设备. 如果这样的 ioctl 号是唯一 的, 这个应用程序得到一个 EINVAL 错误而不是继续做不应当做的事情.

为帮助程序员创建唯一的 ioctl 命令代码, 这些编码已被划分为几个位段. Linux 的第 一个版本使用 16-位数: 高 8 位是关联这个设备的"魔"数, 低 8 位是一个顺序号, 在设 备内唯一. 这样做是因为 Linus 是"无能"的(他自己的话); 一个更好的位段划分仅在后 来被设想. 不幸的是, 许多驱动仍然使用老传统. 它们不得不: 改变命令编码会破坏大量 的二进制程序,并且这不是内核开发者愿意见到的.

根据 Linux 内核惯例来为你的驱动选择 ioctl 号, 你应当首先检查 include/asm/ioctl.h 和 Documentation/ioctl-number.txt. 这个头文件定义你将使用 的位段: type(魔数), 序号, 传输方向, 和参数大小. ioctl-number.txt 文件列举了在 内核中使用的魔数,[20]20 因此你将可选择你自己的魔数并且避免交叠. 这个文本文件也列 举了为什么应当使用惯例的原因.

定义 ioctl 命令号的正确方法使用 4 个位段, 它们有下列的含义. 这个列表中介绍的新 符号定义在 <linux/ioctl.h>.

但是, 这个文件的维护在后来有些少见了.

魔数. 只是选择一个数(在参考了 ioctl-number.txt 之后)并且使用它在整个驱动 中. 这个成员是 8 位宽(_IOC_TYPEBITS).

number

序(顺序)号. 它是 8 位(_IOC_NRBITS)宽. direction

数据传送的方向,如果这个特殊的命令涉及数据传送.
可能的值是 _IOC_NONE(没有 数据传输), _IOC_READ, _IOC_WRITE, 和 _IOC_READ|_IOC_WRITE (数据在 2 个方
向被传送). 数据传送是从应用程序的观点来看待的; _IOC_READ 意思是从设备读, 因此设备必须写到用户空间. 注意这个成员是一个位掩码, 因此
_IOC_READ 和

_IOC_WRITE 可使用一个逻辑 AND 操作来抽取.

size

涉及到的用户数据的大小. 这个成员的宽度是依赖体系的, 但是常常是 13 或者 14 位. 你可为你的特定体系在宏 _IOC_SIZEBITS 中找到它的值. 你使用这个 size 成员不是强制的
- 内核不检查它 -- 但是它是一个好主意. 正确使用这个成
员可帮助检测用户空间程序的错误并使你实现向后兼容, 如果你曾需要改变相关数 据项的大小. 如果你需要更大的数据结构, 但是, 你可忽略这个 size 成员. 我们 很快见到如何使用这个成员.

头文件
<asm/ioctl.h>, 它包含在 <linux/ioctl.h> 中, 定义宏来帮助建立命令号, 如 下:
_IO(type,nr)(给没有参数的命令), _IOR(type, nre, datatype)(给从驱动中读数据 的),
_IOW(type,nr,datatype)(给写数据), 和 _IOWR(type,nr,datatype)(给双向传送). type 和 number 成员作为参数被传递,
并且 size 成员通过应用 sizeof 到 datatype 参数而得到.

这个头文件还定义宏, 可被用在你的驱动中来解码这个号: _IOC_DIR(nr),

_IOC_TYPE(nr), _IOC_NR(nr), 和 _IOC_SIZE(nr). 我们不进入任何这些宏的细节,
因为 头文件是清楚的, 并且在本节稍后有例子代码展示.

这里是一些 ioctl 命令如何在 scull 被定义的. 特别地, 这些命令设置和获得驱动的可 配置参数.

/* Use 'k' as magic number */

#define SCULL_IOC_MAGIC 'k'

/* Please use a different 8-bit number in your code */

#define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0)

/*

*  S means
"Set" through a ptr,

*  T means
"Tell" directly with the argument value

*  G means
"Get": reply by setting through a pointer

*  Q means
"Query": response is on the return value

*  X means
"eXchange": switch G and S atomically

*  H means
"sHift": switch T and Q atomically

*/

#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1,
int)

#define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int)

#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3)

#define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4)

#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5,
int)

#define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int)

#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7)

#define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8)

#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9,
int)

#define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int)

#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11)

#define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12)

#define SCULL_IOC_MAXNR 14 真正的源文件定义几个额外的这里没有出现的命令.

我们选择实现 2 种方法传递整数参数: 通过指针和通过明确的值(尽管, 由于一个已存在
的惯例, ioclt 应当通过指针交换值). 类似地, 2 种方法被用来返回一个整数值:通过指 针和通过设置返回值. 这个有效只要返回值是一个正的整数; 如同你现在所知道的, 在从 任何系统调用返回时, 一个正值被保留(如同我们在 read 和 write 中见到的), 而一个 负值被看作一个错误并且被用来在用户空间设置 errno.[21] 21

"exchange"和"shift"操作对于 scull 没有特别的用处. 我们实现"exchange"来显示驱动
如何结合独立的操作到单个的原子的操作, 并且"shift"来连接"tell"和"query". 有时需
要象这样的原子的测试-和-设置操作, 特别地, 当应用程序需要设置和释放锁.

命令的明确的序号没有特别的含义. 它只用来区分命令. 实际上, 你甚至可使用相同的序 号给一个读命令和一个写命令, 因为实际的 ioctl 号在"方向"位是不同的, 但是你没有 理由这样做. 我们选择在任何地方不使用命令的序号除了声明中, 因此我们不分配一个返 回值给它. 这就是为什么明确的号出现在之前给定的定义中. 这个例子展示了一个使用命
令号的方法, 但是你有自由不这样做.

除了少数几个预定义的命令(马上就讨论), ioctl 的 cmd 参数的值当前不被内核使用, 并且在将来也很不可能. 因此, 你可以, 如果你觉得懒, 避免前面展示的复杂的声明并明 确声明一组调整数字. 另一方面, 如果你做了, 你不会从使用这些位段中获益, 并且你会 遇到困难如果你曾提交你的代码来包含在主线内核中. 头文件<linux/kd.h> 是这个老式 方法的例子, 使用 16-位的调整值来定义 ioctl 命令. 那个源代码依靠调整数因为使用 那个时候遵循的惯例, 不是由于懒惰. 现在改变它可能导致无理由的不兼容.

linux 选择 ioctl 命令的更多相关文章

  1. 使用Linux的alternatives命令替换选择软件的版本

    上周在安装搜索引擎Elasticsearch时,要求安装比较新的java 版本,我选择了java 1.8.0,安装java 成功后使用java -version 发现使用的版本仍旧是1.6.0, 查询 ...

  2. 关于构造IOCTL命令的学习心得

    在编写ioctl代码之前,需要选择对应不同命令的编号.为了防止对错误的设备使用正确的命令,命令号应该在系统范围内唯一,这种错误匹配并不是不会发生,程序可能发现自己正在试图对FIFO和audio等这类非 ...

  3. 构造IOCTL命令的学习心得-----_IO,…

    在编写ioctl代码之前,需要选择对应不同命令的编号.为了防止对错误的设备使用正确的命令,命令号应该在系统范围内唯一,这种错误匹配并不是不会发生,程序可能发现自己正在试图对FIFO和audio等这类非 ...

  4. 例解 Linux 下 Make 命令

    Linux 下 make 命令是系统管理员和程序员用的最频繁的命令之一.管理员用它通过命令行来编译和安装很多开源的工具,程序员用它来管理他们大型复杂的项目编译问题.本文我们将用一些实例来讨论 make ...

  5. 12个Linux进程管理命令介绍(转)

    12个Linux进程管理命令介绍 [日期:2015-06-02] 来源:Linux中国  作者:Linux [字体:大 中 小]   执行中的程序在称作进程.当程序以可执行文件存放在存储中,并且运行的 ...

  6. Linux下rz命令使用的实例详解

    Linux中rz命令和sz命令都可用于文件传输,而rz命令主要用于文件的上传,下面将通过几个实例来给大家详细介绍下Linux下rz命令的用法,一起来学习下吧. rz命令可以批量上传文件,当然也可上传单 ...

  7. linux 的 scp 命令 可以 在 linux 之间复制 文件 和 目录

    转自:http://blog.csdn.net/snlying/article/details/6184102 Linux系统中scp命令的用法. scp就是secure copy的简写,用于在lin ...

  8. 从零单排Linux – 1 – 简单命令

    从零单排Linux – 1 – 简单命令 Posted in: Linux 从零单排Linux – 1 一.Linux的简单命令: 1.忘记root密码: 读秒时按任意键进入 – e – ↓选择第二个 ...

  9. linux中screen命令的用法

    http://www.9usb.net/201002/linux-screen-mingling.html 作为linux服务器管理员,经常要使用ssh登陆到远程linux机器上做一些耗时的操作.也许 ...

随机推荐

  1. 【JZOJ2758】【SDOI2012】走迷宫(labyrinth)

    ╰( ̄▽ ̄)╭ Morenan 被困在了一个迷宫里. 迷宫可以视为 N 个点 M 条边的有向图,其中 Morena n处于起点 S , 迷宫的终点设为 T . 可惜的是 , Morenan 非常的脑小 ...

  2. sql标签和include标签的使用

    <sql>用来封装SQL语句, <include>用来调用<sql>封装的语句 代码片段: <sql id="select"> SE ...

  3. 微信小程序组件——详解wx:if elif else的用法

    背景 在学习微信小程序开发wxml页面时,需要使用if,else来判断组件是否进行展示,代码如下 <view wx:if="{{is_login==1}}">成功登录& ...

  4. mariadb配置文件优化参数

    mariadb数据库优化需要根据自己业务需求以及根据硬件配置来进行参数优化,下面是一些关于mariadb数据库参数优化的配置文件. 1 如下为128G内存32线程处理器的mariadb配置参数优化: ...

  5. iOS 适配iPhoneX上tableHeaderView发生了高度拉伸、UI出现的空白间距

    记录下前阵子遇到的一个问题,草稿箱里记录的有点潦草,讲下大概吧. 异常如下,粉色区域作为tableHeader放上去的(注意不是sectionHeader) header初始化之后一切正常,frame ...

  6. 【NS2】在linux下安装低版本GGC

    1.下载安装包,cd到文件所在目录 sudo dpkg -i gcc41-compat-4.1.2-ubuntu1210_i386.deb g++41-compat-4.1.2_i386.deb 2. ...

  7. JavaScript for循环 while循环

    循环可以将代码块执行指定的次数. JavaScript 循环 如果您希望一遍又一遍地运行相同的代码,并且每次的值都不同,那么使用循环是很方便的. 我们可以这样输出数组的值: 一般写法: documen ...

  8. 二维数组初始化 遍历 动态赋值 内存图 Day08

    package com.sxt.arraytest3; /* * 二维数组 */ public class TestArray { public static void main(String[] a ...

  9. redux之createStore方法底层封装模拟

    首先在看代码之前让我们一起回顾下redux的思想吧   首先redux就是一个MVC思想的框架,他总体是遵循数据的单向流动自顶向下流动 在我们仓库中有一个initState用来存储着我们的初始数据 另 ...

  10. 洛谷P2820 局域网

    #include<bits/stdc++.h> using namespace std; ; ; int n,k,sum,tot; struct node{ int cnt,fa; }f[ ...