目前linux的发行版本都支持usb设备的热插拔。usb转串口设备从硬件上来说也是一个即插即用的usb设备。插入或拔出的时候,都是通过usb的hub集线器检测。那么是如何检测并自动安装对应接口的驱动的呢?---------------目前网络上找到的资料都是零散的,关键是没有找到跟本文linux5.4.0内核相对应的(很多参考资料都是针对2.6内核,如参考书LDD3)。
下文就基于linux5.4.0内核,将接口事件检测到自动安装驱动的环节一步步写下来,如有不妥之处,欢迎大家指正。(我自己被怎么自动安装驱动部分困扰了很久!)
一、热插拔事件的触发
1、当usb设备插入集线器hub时,插入的设备将拉高D+或D-,此时端口变化发生了变化,从而进入hub_irq中断。
2、hub_irq中断触发hub的event_list,从而进入hub_thread线程;
3、hub_thread调用hub_events()。
4、由于端口的变化,hub_events()函数进入hub_port_connect_change()。
1)分配一个usb设备配并初始化udev:
udev = usb_alloc_dev(hdev, hdev_bus, port1);// struct usb_device *udev
2)初始化设备配置usb_new_device(udev);
(1)usb_configure_device(udev)->usb_get_configuration(udev);
得到设备的描述符(包括设备描述符、配置描述符、接口描述符等),并分析和提取配置、接口等信息赋值给udev结构相应字段。
(2)device_add(&udev->dev);------------将usb设备注册到设备模型中
int device_add(struct device *dev)
{
................................
kobject_uevent(&dev->kobj, KOBJ_ADD); //产生uevent事件,是linux设备模型中热插拔机制, 源码位于/usr/src/linux-5.4/lib/kobject_uevent.c
...............................
};
这里的kobject_uevent()是实现自动加载驱动的关键。
二、根据检测的接口信息、自动安装对应驱动
这里不得不提到Linux uevent机制。它是内核与用户空间的一种通信机制。当有新的设备加入的时候,将设备的信息发送消息到用户态。如设备驱动模型的usb热插拔uevent,发出kobject_uevent()事件获取相关的环境变量,并通知到用户空间。而用户态有一个Udev的守护进程监听这个信息,并执行一些操作,比如可以加载驱动程序。
Udev 守护进程直接从内核接收设备的插入、拔出、改变状态等事件,并到规则库中进行匹配,以实现设备的触发事件。
Udev同时为连接在系统上面的设备节点提供一个动态设备目录。设备插入或移出系统的时候,Udev就在 /dev目录下面创建或者删除设备节点文件。如插入本文的激光雷达,就在 /dev下面生成serial文件夹,serial中包含两个文件夹/dev/serial/by-id和/dev/serial/by-path。
Udev使用modalias方法来加载设备驱动程序,而位于/lib/modules/5.4.0/modules.alias 的modalias文件用于协助Udev加载设备驱动。
1、modules.alias 文件
那么modules.alias 是如何产生的呢?这与宏定义MODULE_DEVICE_TABLE()有关。
在linux的设备模型中, 每一个设备都有VENDOR ID, _PRODUCT ID等信息。而每一个设备驱动程序,也需要表明自己能为哪些VENDOR ID, _PRODUCT ID的设备提供服务。以下以PL2303为例,在pl2303.c中,有如下定义:
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID),
.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
......
}
MODULE_DEVICE_TABLE(usb, id_table);
在安装模块的时候,depmod程序根据id_table信息,把相关的模块信息添加到/lib/modules/uname-r/modules.alias文件中。
alias usb:v067Bp2303d*dc*dsc*dp*ic*isc*ip*in* pl2303
......
其中v代表Vendor ID,p代表PRODUCT ID,后面的*表示随意匹配。另外在/lib/modules/uname-r/modules.dep文件中还保存了模块之间的依赖关系(表示模块pl2303.ko依赖于模块usbserial.ko )。
kernel/drivers/usb/serial/pl2303.ko: kernel/drivers/usb/serial/usbserial.ko
2、udev的自动加载机制
udev 规则是定义在一个以 .rules 为扩展名的文件中,这些文件主要放在两个位置:
(1)/lib/udev/rules.d,这个目录用于存放系统安装的规则;
(2)/etc/udev/rules.d/, 这个目录存放本机规则,如激光雷达的自定义规则就放于此;
本文提到的serial驱动udev规则位于/lib/udev/rules.d/80-drivers.rules中。
如上所述kobject_uevent()把添加新设备的事件、以及相关信息(如设备的VendorID, ProductID等信息)通过netlink发送到用户态。在用户态的Udev进程得到信息后,根据/lib/udev/rules.d/80-drivers.rules的规则:
ENV{MODALIAS}=="?*", RUN{builtin}+="kmod load $env{MODALIAS}"
当有环境变量MODALIAS(就是modules.alias的信息格式)时,自动加载驱动程序。此时根据modules.alias和modules.dep文件,得知设备驱动模块为pl2303.ko,并且此模块依赖于 /usbserial.ko。如果此时usbserial.ko没有加载,就先加载usbserial.ko,接着再加载 pl2303.ko。
3、udevadm
udevadm是一个udev管理工具,可以获取内核发送的事件。输入命令:udevadm monitor --env。
valian@valian-TM1703:~$ udevadm monitor --env
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent
插入我的激光雷达后,可以获得以下uevent信息。这里可以看到MODALIAS信息,这里的MODALIAS就是modules.alias文件匹配的格式。
KERNEL[34996.845989] add /devices/pci0000:00/0000:00:14.0/usb1/1-4 (usb)
ACTION=add
BUSNUM=001
DEVNAME=/dev/bus/usb/001/011
DEVNUM=011
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4
DEVTYPE=usb_device
MAJOR=189
MINOR=10
PRODUCT=67b/2303/300
SEQNUM=4188
SUBSYSTEM=usb
TYPE=0/0/0
KERNEL[34996.847771] add /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0 (usb)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0
DEVTYPE=usb_interface
INTERFACE=255/0/0
MODALIAS=usb:v067Bp2303d0300dc00dsc00dp00icFFisc00ip00in00
PRODUCT=67b/2303/300
SEQNUM=4189
SUBSYSTEM=usb
TYPE=0/0/0
KERNEL[34996.848910] add /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/ttyUSB0 (usb-serial)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/ttyUSB0
SEQNUM=4190
SUBSYSTEM=usb-serial
KERNEL[34996.849138] add /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/ttyUSB0/tty/ttyUSB0 (tty)
ACTION=add
DEVNAME=/dev/ttyUSB0
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/ttyUSB0/tty/ttyUSB0
MAJOR=188
MINOR=0
SEQNUM=4191
SUBSYSTEM=tty
UDEV [34996.860612] add /devices/pci0000:00/0000:00:14.0/usb1/1-4 (usb)
ACTION=add
BUSNUM=001
DEVNAME=/dev/bus/usb/001/011
DEVNUM=011
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4
DEVTYPE=usb_device
DRIVER=usb
ID_BUS=usb
ID_MM_DEVICE_MANUAL_SCAN_ONLY=1
ID_MODEL=USB-Serial_Controller
ID_MODEL_ENC=USB-Serialx20Controller
ID_MODEL_FROM_DATABASE=PL2303 Serial Port
ID_MODEL_ID=2303
ID_REVISION=0300
ID_SERIAL=Prolific_Technology_Inc._USB-Serial_Controller
ID_USB_INTERFACES=:ff0000:
ID_VENDOR=Prolific_Technology_Inc.
ID_VENDOR_ENC=Prolificx20Technologyx20Inc.
ID_VENDOR_FROM_DATABASE=Prolific Technology, Inc.
ID_VENDOR_ID=067b
MAJOR=189
MINOR=10
PRODUCT=67b/2303/300
SEQNUM=4188
SUBSYSTEM=usb
TYPE=0/0/0
USEC_INITIALIZED=34996860449
UDEV [34997.866782] add /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0 (usb)
.MM_USBIFNUM=00
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0
DEVTYPE=usb_interface
DRIVER=pl2303
ID_MODEL_FROM_DATABASE=PL2303 Serial Port
ID_VENDOR_FROM_DATABASE=Prolific Technology, Inc.
INTERFACE=255/0/0
MODALIAS=usb:v067Bp2303d0300dc00dsc00dp00icFFisc00ip00in00
PRODUCT=67b/2303/300
SEQNUM=4189
SUBSYSTEM=usb
TYPE=0/0/0
USEC_INITIALIZED=34996862068
UDEV [34997.869061] add /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/ttyUSB0 (usb-serial)
.MM_USBIFNUM=00
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/ttyUSB0
DRIVER=pl2303
SEQNUM=4190
SUBSYSTEM=usb-serial
USEC_INITIALIZED=34997868911
UDEV [34997.873758] add /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/ttyUSB0/tty/ttyUSB0 (tty)
.ID_PORT=0
.MM_USBIFNUM=00
ACTION=add
DEVLINKS=/dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0 /dev/rplidar /dev/serial/by-path/pci-0000:00:14.0-usb-0:4:1.0-port0
DEVNAME=/dev/ttyUSB0
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/ttyUSB0/tty/ttyUSB0
ID_BUS=usb
ID_MM_CANDIDATE=1
ID_MODEL=USB-Serial_Controller
ID_MODEL_ENC=USB-Serialx20Controller
ID_MODEL_FROM_DATABASE=PL2303 Serial Port
ID_MODEL_ID=2303
ID_PATH=pci-0000:00:14.0-usb-0:4:1.0
ID_PATH_TAG=pci-0000_00_14_0-usb-0_4_1_0
ID_PCI_CLASS_FROM_DATABASE=Serial bus controller
ID_PCI_INTERFACE_FROM_DATABASE=XHCI
ID_PCI_SUBCLASS_FROM_DATABASE=USB controller
ID_REVISION=0300
ID_SERIAL=Prolific_Technology_Inc._USB-Serial_Controller
ID_TYPE=generic
ID_USB_DRIVER=pl2303
ID_USB_INTERFACES=:ff0000:
ID_USB_INTERFACE_NUM=00
ID_VENDOR=Prolific_Technology_Inc.
ID_VENDOR_ENC=Prolificx20Technologyx20Inc.
ID_VENDOR_FROM_DATABASE=Prolific Technology, Inc.
ID_VENDOR_ID=067b
MAJOR=188
MINOR=0
SEQNUM=4191
SUBSYSTEM=tty
TAGS=:systemd:
USEC_INITIALIZED=34997873660