USB 热插拔¶
Linux 热插拔¶
在像 USB(和 Cardbus PCI)这样的热插拔总线中,最终用户在电源开启的情况下将设备插入总线。在大多数情况下,用户希望设备能够立即使用。这意味着系统必须执行许多操作,包括
找到一个可以处理该设备的驱动程序。这可能涉及加载内核模块;较新的驱动程序可以使用 module-init-tools 将其设备(和类)支持发布到用户实用程序。
将驱动程序绑定到该设备。总线框架使用设备驱动程序的 probe() 例程来执行此操作。
告诉其他子系统配置新设备。可能需要启用打印队列,启动网络,安装磁盘分区等等。在某些情况下,这些将是特定于驱动程序的操作。
这涉及到内核模式和用户模式操作的混合。使设备立即可用意味着任何用户模式操作都不能等待管理员来执行它们:内核必须触发它们,要么是被动地(触发某些监控守护程序调用辅助程序),要么是主动地(直接调用此类用户模式辅助程序)。
那些触发的操作必须支持系统的管理策略;这些程序在这里被称为“策略代理”。通常,它们涉及分派到更熟悉的管理工具的 shell 脚本。
由于其中一些操作依赖于只有在动态链接驱动程序时才可用的驱动程序信息(元数据),因此当您配置高度模块化的系统时,您将获得最佳的热插拔效果。
内核热插拔助手 (/sbin/hotplug
)¶
有一个内核参数:/proc/sys/kernel/hotplug
,它通常保存路径名 /sbin/hotplug
。该参数命名一个内核可能在不同时间调用的程序。
/sbin/hotplug 程序可以由任何子系统调用,作为其对配置更改的反应的一部分,从该子系统中的一个线程调用。只需要一个参数:被通知某些内核事件的子系统的名称。该名称用作进一步事件分派的第一个键;任何其他参数和环境变量由进行该调用的子系统指定。
热插拔软件和其他资源可在以下网址获取
邮件列表信息也可在该网站获取。
USB 策略代理¶
当 USB 设备添加到系统或从系统中移除时,USB 子系统当前会调用 /sbin/hotplug
。调用由内核 hub 工作队列 [hub_wq] 完成,或者作为根 hub 初始化的一部分(由 init、modprobe、kapmd 等完成)。其唯一的命令行参数是字符串“usb”,并且它传递以下环境变量
ACTION |
|
PRODUCT |
USB 供应商、产品和版本代码(十六进制) |
TYPE |
设备类代码(十进制) |
INTERFACE |
接口 0 类代码(十进制) |
如果配置了 “usbdevfs”,还会传递 DEVICE 和 DEVFS。DEVICE 是设备的路径名,对于具有多个和/或备用接口(使驱动程序选择复杂化)的设备很有用。根据设计,USB 热插拔独立于 usbdevfs
:您可以完成 USB 设备设置的大部分基本部分,而无需使用该文件系统,也无需运行用户模式守护程序来检测系统配置的更改。
当前可用的策略代理实现可以加载模块的驱动程序,并可以调用特定于驱动程序的设置脚本。最新的利用了 USB module-init-tools 支持。以后的代理可能会卸载驱动程序。
USB Modutils 支持¶
当前版本的 module-init-tools 将创建一个 modules.usbmap
文件,其中包含来自每个驱动程序的 MODULE_DEVICE_TABLE
中的条目。各种用户模式策略代理可以使用这些文件来确保在启动时或稍后加载所有正确的驱动程序模块。
有关此类表条目的完整信息,请参阅 linux/usb.h
;或查看现有驱动程序。每个表条目描述一个或多个标准,用于将驱动程序与设备或设备类匹配。特定标准由“match_flags”中设置的位标识,并与字段值配对。您可以直接或使用诸如此类的宏构造标准,并使用 driver_info 存储更多信息
USB_DEVICE (vendorId, productId)
... matching devices with specified vendor and product ids
USB_DEVICE_VER (vendorId, productId, lo, hi)
... like USB_DEVICE with lo <= productversion <= hi
USB_INTERFACE_INFO (class, subclass, protocol)
... matching specified interface class info
USB_DEVICE_INFO (class, subclass, protocol)
... matching specified device class info
一个简短的示例,对于支持多个特定 USB 设备及其怪癖的驱动程序,可能具有如下的 MODULE_DEVICE_TABLE
static const struct usb_device_id mydriver_id_table[] = {
{ USB_DEVICE (0x9999, 0xaaaa), driver_info: QUIRK_X },
{ USB_DEVICE (0xbbbb, 0x8888), driver_info: QUIRK_Y|QUIRK_Z },
...
{ } /* end with an all-zeroes entry */
};
MODULE_DEVICE_TABLE(usb, mydriver_id_table);
大多数 USB 设备驱动程序都应将这些表传递给 USB 子系统以及模块管理子系统。但并非全部:某些驱动程序框架使用分层在 USB 之上的接口进行连接,因此它们不需要这样的 struct usb_driver
。
直接连接到 USB 子系统的驱动程序应声明如下
static struct usb_driver mydriver = {
.name = "mydriver",
.id_table = mydriver_id_table,
.probe = my_probe,
.disconnect = my_disconnect,
/*
if using the usb chardev framework:
.minor = MY_USB_MINOR_START,
.fops = my_file_ops,
if exposing any operations through usbdevfs:
.ioctl = my_ioctl,
*/
};
当 USB 子系统知道驱动程序的设备 ID 表时,它会在选择要探测的驱动程序时使用。执行新设备处理的线程会检查驱动程序的设备 ID 条目(来自 MODULE_DEVICE_TABLE
)与设备的接口和设备描述符。只有在匹配时才会调用 probe()
,并且 probe()
的第三个参数将是匹配的条目。
如果您没有为驱动程序提供 id_table
,那么可能会为每个新设备探测您的驱动程序;probe()
的第三个参数将为 NULL
。