📄 ch14s07.html
字号:
<dt><span class="term"><span>EV</span></span></dt><dd></dd><dt><span class="term"><span>KEY</span></span></dt><dd></dd><dt><span class="term"><span>REL</span></span></dt><dd></dd><dt><span class="term"><span>ABS</span></span></dt><dd></dd><dt><span class="term"><span>MSC</span></span></dt><dd></dd><dt><span class="term"><span>LED</span></span></dt><dd></dd><dt><span class="term"><span>SND</span></span></dt><dd></dd><dt><span class="term"><span>FF </span></span></dt><dd><p>这些都来自输入设备描述符并且被设置为合适的值如果特定的输入设备支持它.</p></dd></dl></div></div><div class="sect3" lang="zh-cn"><div class="titlepage"><div><div><h4 class="title"><a name="USB.sect3"></a>14.7.2.5. USB 总线</h4></div></div></div><p>任何在 USB 总线上的设备有参数 name 和 SUBSYSTEM 环境变量设置为 usb. USB 子系统也总是一直添加下列的环境变量:</p><div class="variablelist"><dl><dt><span class="term"><span>PRODUCT </span></span></dt><dd><p>一个字符串, idVendor/idProduct/bcdDevice 的格式, 来指定这些 USB 设备特定的成员.</p></dd><dt><span class="term"><span>TYPE </span></span></dt><dd><p>一个 bDeviceClass/bDeviceSubClass/bDeviceProtocol 格式的字符串, 指定这些 USB 设备特定的成员.</p></dd></dl></div><p>如果 bDeviceClass 成员设置为 0, 下列的环境变量也被设置:</p><div class="variablelist"><dl><dt><span class="term"><span>INTERFACE </span></span></dt><dd><p>一个 bInterfaceClass/bInterfaceSubClass/bInterfaceProtocol 格式的字符串, 指定这些 USB 设备特定成员.</p></dd></dl></div><p>如果这个内核建立选项, CONFIG_USB_DEVICEFS, 它选择 usbfs 文件系统来在内核中建立, 被选中, 下列环境变量也被设置:</p><div class="variablelist"><dl><dt><span class="term"><span>DEVICE </span></span></dt><dd><p>一个字符串, 在设备所在的 usbfs 文件系统中出现. 这个字串以 /proc/bus/usb/USB_BUS_NUMBER/USB_DEVICE_NUMBER 的格式, 其中 USB_BUS_NUMBER 是这个设备所在的 USB 总线的 3 个数, USB_DEVICE_NUMBER 是已由内核分配给 USB 设备的 3 位数.</p></dd></dl></div></div><div class="sect3" lang="zh-cn"><div class="titlepage"><div><div><h4 class="title"><a name="SCSI.sect3"></a>14.7.2.6. SCSI 总线</h4></div></div></div><p>所有的 SCSI 设备创建一个热插拔事件当 SCSI 设备从内核中创建或去除. /sbin/hotplug 调用有参数 name 和 SUBSYSTEM 环境变量设置为 scsi 给每个添加或去除自系统的 SCSI 设备. 没有额外的环境变量由 SCSI 系统添加, 但是它被在此提及因为有一个 SCSI 特定的用户空间脚本来决定什么 SCSI 驱动( 磁盘, 磁带, 通用, 等等)应当给这个特定 SCSI 设备加载.</p></div><div class="sect3" lang="zh-cn"><div class="titlepage"><div><div><h4 class="title"><a name="Laptopdockingstations.sect3"></a>14.7.2.7. 膝上电脑坞站</h4></div></div></div><p>如果一个支持即插即用的膝上电脑坞站被从运行中的 Linux 系统中添加或去除( 通过插入膝上电脑到坞站中, 或者去除它), 一个热插拔事件被产生. /sbin/hotplug 调用有参数 name 和 SUBSYSTEM 环境变量设为 dock. 没有其他的环境变量被设置.</p></div><div class="sect3" lang="zh-cn"><div class="titlepage"><div><div><h4 class="title"><a name="S390andzSeries.sect3"></a>14.7.2.8. S/390 和 zSeries</h4></div></div></div><p>在 S/390 体系中, 通道总线结构支持很广范围的硬件, 所有产生 /sbin/hotplug 事件当它们从 Linux 虚拟系统被添加或去除时的硬件. 这些设备都有 /sbin/hotplug 参数 name 和 SUBSYSTEM 环境变量设置为 dasd. 没有其他环境变量被设置.</p></div></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="Usingsbinhotplug.sect2"></a>14.7.3. 使用 /sbin/hotplug </h3></div></div></div><p>现在 Linux 内核在调用 /sbin/hotplug 为每个设备, 添加和删除自内核, 许多非常有用的工具在用户空间已被创建来利用这一点. 2 个最常用的工具是 Linux 热插拔脚本和 udev.</p><div class="sect3" lang="zh-cn"><div class="titlepage"><div><div><h4 class="title"><a name="Linuxhotplugscripts.sect3"></a>14.7.3.1. Linux 热插拔脚本</h4></div></div></div><p>Linux 热插拔脚本作为 /sbin/hotplug 调用的第一个用户而启动. 这些脚本查看内核设置的来描述刚刚发现的设备的不同的环境变量, 并接着试图发现一个匹配这个设备的内核模块.</p><p>如同前面描述的, 当一个驱动使用 MODULE_DEVICE_TABLE 宏, 程序 depmod 采用这个信息并创建位于 /lib/module/KERNEL_VERSION/modules.*map 的文件. 这个 * 是不同的, 根据驱动支持的总线类型. 当前, 模块 map 文件为使用设备的驱动而产生, 这些设备支持 PCI, USB, IEEE1394, INPUT, ISAPNP, 和 CCW 子系统.</p><p>热插拔脚本使用这些模块映射文本文件, 来决定试图加载什么模块来支持内核刚刚发现的设备. 它们加载所有的模块, 在第一次匹配时不停止, 为了使内核发现那个模块工作得最好. 这些脚本不加载任何模块当驱动被去除时. 如果它们要试图做这个, 它们可能偶然地关闭被同一个要被去除的驱动控制的设备.</p><p>注意, 现在 modprobe 程序能直接从模块中读 MODULE_DEVICE_TABLE 信息而不需要模块 map 文件, 热插拔脚本可能被删减为一个小的在 modprobe 程序周围的包装.</p></div><div class="sect3" lang="zh-cn"><div class="titlepage"><div><div><h4 class="title"><a name="udev.sect3"></a>14.7.3.2. udev 啥?</h4></div></div></div><p>在内核中创建统一的驱动模型的一个主要原因是允许用户空间动态管理 /dev 树. 这之前已使用 devfs 的实现在用户空间实现, 但是那个代码底线已慢慢消失, 由于缺少一个活跃的维护者以及一些无法修正的核心 bug. 许多内核开发者认识到如果所有的设备信息被输出给用户空间, 它可能进行所有的必要的 /dev 树的管理.</p><p>devfs 在它的设计中有一些非常基础的缺陷. 它需要每个设备驱动被修改来支持它, 并且它要求设备驱动来指定名子和在它所在的 /dev 树中的位置. 它也没有正确处理动态主次编号, 并且它不允许用户空间以简单方式覆盖设备的命名, 这样来强制设备命名策略于内核中而不是在用户空间. Linux 内核开发中非常厌恶使策略在内核中, 并且因为 devfs 命名策略不遵循 Linux 标准基础规格, 它确实困扰他们.</p><p>随着 Linux 内核开始安装到大型服务器, 许多用户遇到如何管理大量设备的问题. 超过 10,000 个单一设备的磁盘驱动阵列提出了非常困难的任务, 保证一个特定磁盘一直使用相同的名子命名, 不管它在磁盘阵列的哪里或者它什么时候被内核发现. 同样的问题也折磨着桌面用户, 想插入 2 个 USB 打印机到他们的系统, 并且接着发现它们没有办法保证已知为 /dev/lpt0 的打印机不会改变并分配给其他的打印机如果系统重启.</p><p>因此, udev 被创建. 它依靠所有通过 sysfs 输出给用户空间的设备信息, 并且依靠被 /sbin/hotplug 通知有设备添加或去除. 策略决策, 例如给一个设备什么名子, 可在用户空间指定, 内核之外. 这保证了命名策略被从内核中去除并且允许大量每个设备名子的灵活性.</p><p>对更多的关于如何使用 udev 和如何配置它的信息, 请看在你的发布中和 udev 软件包一起的文档.</p><p>所有的一个设备驱动需要做的, 为 udev 正确使用它, 是确保任何分配给一个驱动控制的设备的主次编号通过 sysfs 输出到用户空间. 对任何使用一个子系统来安排它一个主次编号的驱动, 这已经由子系统完成, 并且驱动不必做任何工作. 做这个的子系统的例子是 tty, misc, usb, input, scsi, block, i2c, network, 和 frame buffer 子系统. 如果你的驱动自己获得一个主次编号, 通过对 cdev_init 函数的调用或者更老的 register_chrdev 函数, 驱动需要被修改以便 udev 能够正确使用它.</p><p>udev 查找一个称为 dev 的文件在 sysfs 的 /class/ 树中, 为了决定分配什么主次编号给一个特定设备当它被内核通过 /sbin/hotplug 接口调用时. 一个设备驱动只要为每个它控制的设备创建这个文件. class_simple 接口常常是最易的做这个的方法.</p><p>如同" class_simple 接口"一节中提过的, 使用 class_simple 接口的第一步是调用 class_simple_create 函数来创建一个 struct class_simple.</p><pre class="programlisting">static struct class_simple *foo_class;...foo_class = class_simple_create(THIS_MODULE, "foo");if (IS_ERR(foo_class)) { printk(KERN_ERR "Error creating foo class.\n"); goto error;}</pre><p>这个代码创建一个目录在 sysfs 中 /sys/class/foo.</p><p>无论何时你的驱动发现一个新设备, 并且你如第 3 章描述的分配它一个次编号, 驱动应当调用 class_simple_device_add 函数:</p><pre class="programlisting">class_simple_device_add(foo_class, MKDEV(FOO_MAJOR, minor), NULL, "foo%d", minor); </pre><p>这个代码导致在 /sys/class/foo 创建一个子目录称为 fooN, 这里 N 是这个设备的次编号. 在这个目录里创建有一个文件, dev, 它恰好是 udev 为你的设备创建一个设备节点需要的.</p><p>当你的驱动从一个设备解除, 并且你放弃它所依附的次编号, 需要调用 class_simple_device_remove 来去除这个设备的 sysfs 入口.</p><pre class="programlisting">class_simple_device_remove(MKDEV(FOO_MAJOR, minor)); </pre><p>之后, 当你的整个驱动被关闭, 需要调用 class_simple_destroy 来去除你起初调用 class_simple_create 创建的 class.</p><pre class="programlisting">class_simple_destroy(foo_class); </pre><p>同样 class_simple_device_add 创建的 dev 文件包括主次编号, 由一个 : 隔开. 如果你的驱动不想使用 class_simple 接口因为你想提供其他在子系统的类目录中的文件, 使用 print_dev_t 函数来正确格式化特定设备的主次编号.</p></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch14s06.html">上一页</a> </td><td width="20%" align="center"><a accesskey="u" href="ch14.html">上一级</a></td><td width="40%" align="right"> <a accesskey="n" href="ch14s08.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">14.6. 集成起来 </td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top"> 14.8. 处理固件</td></tr></table></div></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -