本文从软件角度分析linux内核USB子系统的热插拔过程,以实际分析思路和过程行文,基于linux内核版本:4.19.4,记录分析USB子系统时的所得。
导读
一、USB核心初始化
二、USB设备的枚举过程
(2-1)USB鼠标插入后日志分析
(2-2)USB鼠标移除/拔下后日志分析
三、 USB host控制器的初始化
四、USB设备插入后的硬件过程
五、USB设备插入后的软件过程
一、USB核心初始化
如果linux内核开启了对USB的支持,在内核启动过程中,首先会对USB核心进行初始化,该过程则会打印出如下日志信息:
(1)[ 0.476223] usbcore: registered new interface driver usbfs
(2)[ 0.476286] usbcore: registered new interface driver hub
(3)[ 0.476337] usbcore: registered new device driver usb
上述(1)、(2)两条信息是调用usb_register_driver()函数时打印出的,从而可以知道在linux内核启动过程中,usb核心会注册usbfs、hub接口驱动程序。从内核源码角度,以上hub接口驱动程序的注册过程是在usb_init()中调用usb_hub_init()完成的,除此之外还创建了hub_wq工作队列:
上述第(3)条信息是在usb_init()中调用:
usb_register_device_driver(&usb_generic_driver,THIS_MODULE);
当在成功注册usb设备驱动后打印出的日志信息。
二、USB设备的枚举过程
本小节以低速USB鼠标设备插入为例,查看linux内核对USB设备的枚举过程。
(2-1)USB鼠标插入后日志分析
我们都知道USB支持热插拔,在linux系统中,这个热插拔是怎样的一个过程,本小节将描述这个话题。
当USB设备接入系统时(热插拔探测过程),文本以插入一个USB鼠标为例,则会打印出类似下述的信息:
[ 3122.476846] usb 4-1: new low-speed USB device number 5 using ohci-platform
[ 3122.702408] usb 4-1: New USB device found, idVendor=17ef, idProduct=6019, bcdDevice= 1.00
[ 3122.702503] usb 4-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 3122.702537] usb 4-1: Product: Lenovo Optical USB Mouse
[ 3122.702568] usb 4-1: Manufacturer: PixArt
[ 3122.713317] input: PixArt Lenovo Optical USB Mouse as /devices/platform/fd8c0000.usb/usb4/4-1/4-1:1.0/00036019.0004/input/input8
[ 3122.771015] hid-generic 00036019.0004: input,hidraw0: USB HID v1.11 Mouse [PixArt Lenovo Optical USB Mouse] on usb-fd8c0000.usb-1/input0
[1058.921] event5 - PixArt Lenovo Optical USB Mouse: is tagged by udev as: Mouse
[1058.923] event5 - PixArt Lenovo Optical USB Mouse: device is a pointer
[1058.930] libinput: configuring device "PixArt Lenovo Optical USB Mouse".
[1058.937] associating input device event5 with output LVDS-1 (none by udev)
从上述内容可道,linux内核提示识别到了一个新的low-speedUSB(低速设备),并且得到了关于该USB鼠标相关的参数信息。然后input输入子系统打印出了两条信息:
[ 3122.713317] input: PixArt Lenovo Optical USB Mouse as /devices/platform/fd8c0000.usb/usb4/4-1/4-1:1.0/00036019.0004/input/input8
[ 3122.771015] hid-generic 00036019.0004: input,hidraw0: USB HID v1.11 Mouse [PixArt Lenovo Optical USB Mouse] on usb-fd8c0000.usb-1/input0
接着用户空间程序打印出了几条信息:
[1058.921] event5 - PixArt Lenovo Optical USB Mouse: is tagged by udev as: Mouse
[1058.923] event5 - PixArt Lenovo Optical USB Mouse: device is a pointer
[1058.930] libinput: configuring device "PixArt Lenovo Optical USB Mouse".
[1058.937] associating input device event5 with output LVDS-1 (none by udev)
至此,可以知道当USB鼠标插入usb接口后,linux内核会识别到usb设备插入并且打印出识别到的信息,然后会交给input输入子系统识别,因为鼠标是一个输入设备,应归属于输入子系统下,输入子系统会向用户空间暴露出操作接口。接着用户空间程序检测到用户的输入识别进而打印出相关的识别信息,从而完成整个识别过程。
从内核源码角度,usb内核识别阶段打印设备描述符信息的操作函数是announce_device(),实现如下:
static void announce_device(struct usb_device *udev)
{
u16 bcdDevice = le16_to_cpu(udev->descriptor.bcdDevice);
dev_info(&udev->dev,
"New USB device found, idVendor=%04x, idProduct=%04x, bcdDevice=%2x.%02x
",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
bcdDevice >> 8, bcdDevice & 0xff);
dev_info(&udev->dev,
"New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d
",
udev->descriptor.iManufacturer,
udev->descriptor.iProduct,
udev->descriptor.iSerialNumber);
show_string(udev, "Product", udev->product);
show_string(udev, "Manufacturer", udev->manufacturer);
show_string(udev, "SerialNumber", udev->serial);
}
#elsestatic inline void announce_device(struct usb_device *udev) { }
#endif
(2-2)USB鼠标移除/拔下后日志分析
当usb设备移除后,则会打印出类似以下的信息:
(1)[ 2791.684059] usb 4-1: USB disconnect, device number 4
(2)[1027.777] event5 - PixArt Lenovo Optical USB Mouse: device removed
第(1)条信息是linux内核usb核心识别到usb设备被移除/拔出后,打印出的信息,这个过程是内核空间的过程。
第(2)条信息是用户空间程序识别到usb鼠标设备被拔出后打印出的信息,这个过程是用户空间程序的操作过程。
三、 usb host控制器的初始化
一般情况下,处理器平台内部都集成了USB host主机控制器,linux内核中,对usb host控制器的初始化发生在内核启动阶段,从内核启动打印出的日志中,可以找到类似如下的信息(输出中加有标记信息):
[ 0.779954] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[ 0.779969] usb usb1: Product: EHCI Host Controller
[ 0.779978] usb usb1: Manufacturer: Linux 4.19.232 ehci_hcd
[ 0.779991] usb usb1: SerialNumber: fd800000.usb
[ 0.780002]
[ 0.780002] ================usb_new_device==============
[ 0.780432] ==================================enter hub_probe=============================
[ 0.780453] hub 1-0 USB hub found
[ 0.780493] hub 1-0 1 port detected
[ 0.783005] ehci-platform fd880000.usb: EHCI Host Controller
[ 0.783186] ehci-platform fd880000.usb: new USB bus registered, assigned bus number 2
[ 0.783503] ehci-platform fd880000.usb: irq 14, io mem 0xfd880000
[ 0.796390] ehci-platform fd880000.usb: USB 2.0 started, EHCI 1.00
[ 0.796606] usb usb2: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 4.19
[ 0.796626] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[ 0.796641] usb usb2: Product: EHCI Host Controller
[ 0.796655] usb usb2: Manufacturer: Linux 4.19.232 ehci_hcd
[ 0.796669] usb usb2: SerialNumber: fd880000.usb
[ 0.796680]
[ 0.796680] ================usb_new_device==============
[ 0.797080] ==================================enter hub_probe=============================
[ 0.797100] hub 2-0 USB hub found
[ 0.797142] hub 2-0 1 port detected
[ 0.797861] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[ 0.797892] ohci-platform: OHCI generic platform driver
[ 0.798121] ohci-platform fd840000.usb: Generic Platform OHCI controller
[ 0.798290] ohci-platform fd840000.usb: new USB bus registered, assigned bus number 3
[ 0.798541] ohci-platform fd840000.usb: irq 13, io mem 0xfd840000
[ 0.857203] usb usb3: New USB device found, idVendor=1d6b, idProduct=0001, bcdDevice= 4.19
[ 0.857224] usb usb3: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[ 0.857239] usb usb3: Product: Generic Platform OHCI controller
[ 0.857252] usb usb3: Manufacturer: Linux 4.19.232 ohci_hcd
[ 0.857265] usb usb3: SerialNumber: fd840000.usb
[ 0.857276]
[ 0.857276] ================usb_new_device==============
[ 0.857683] ==================================enter hub_probe=============================
[ 0.857703] hub 3-0 USB hub found
[ 0.857750] hub 3-0 1 port detected
[ 0.858206] ohci-platform fd8c0000.usb: Generic Platform OHCI controller
[ 0.858386] ohci-platform fd8c0000.usb: new USB bus registered, assigned bus number 4
[ 0.858624] ohci-platform fd8c0000.usb: irq 15, io mem 0xfd8c0000
[ 0.917200] usb usb4: New USB device found, idVendor=1d6b, idProduct=0001, bcdDevice= 4.19
[ 0.917220] usb usb4: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[ 0.917234] usb usb4: Product: Generic Platform OHCI controller
[ 0.917248] usb usb4: Manufacturer: Linux 4.19.232 ohci_hcd
[ 0.917260] usb usb4: SerialNumber: fd8c0000.usb
[ 0.917271]
[ 0.917271] ================usb_new_device==============
[ 0.917662] ==================================enter hub_probe=============================
[ 0.917683] hub 4-0 USB hub found
[ 0.917740] hub 4-0 1 port detected
[ 0.919175] xhci-hcd xhci-hcd.0.auto: xHCI Host Controller
[ 0.919378] xhci-hcd xhci-hcd.0.auto: new USB bus registered, assigned bus number 5
[ 0.919920] xhci-hcd xhci-hcd.0.auto: hcc params 0x0220fe64 hci version 0x110 quirks 0x0000011002010010
[ 0.919992] xhci-hcd xhci-hcd.0.auto: irq 81, io mem 0xfd000000
[ 0.920297] usb usb5: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 4.19
[ 0.920316] usb usb5: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[ 0.920331] usb usb5: Product: xHCI Host Controller
[ 0.920344] usb usb5: Manufacturer: Linux 4.19.232 xhci-hcd
[ 0.920357] usb usb5: SerialNumber: xhci-hcd.0.auto
[ 0.920369]
[ 0.920369] ================usb_new_device==============
[ 0.920786] ==================================enter hub_probe================
[ 0.920806] hub 5-0 USB hub found
[ 0.920849] hub 5-0 1 port detected
[ 0.921167] xhci-hcd xhci-hcd.0.auto: xHCI Host Controller
[ 0.921325] xhci-hcd xhci-hcd.0.auto: new USB bus registered, assignStarting syslogd: ed bus number 6
[ 0.921352] xhci-hcd xhci-hcd.0.auto: Host supports OK
USB 3.0 SuperSpeed
[ 0.921433] usb usb6: We don't know the alStarting klogd: gorithms for LPM for this host, disabling LPM.
[ 0.921559] usbOK
usb6: New USB device found, idVendor=1d6b, idProduct=0003, bcdDevice= 4.19
[ 0.921577]Populating /dev using udev: usb usb6: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[ 0.921593] usb usb6: Product: xHCI Host Controller
[ 0.921607] usb usb6: Manufacturer: Linux 4.19.232 xhci-hcd
[ 0.921620] usb usb6: SerialNumber: xhci-hcd.0.auto
[ 0.921630]
[ 0.921630] ================usb_new_device============
[ 0.922028] ====================enter hub_probe=============================
=============
[ 0.922047] hub 6-0 USB hub found
[ 0.922086] hub 6-0 1 port detected
在linux USB子系统中,需使用usb_register_bus()将USB host控制器注册到USB核心上,该函数由usb_add_hcd()调用,用于完成通用hcd结构的初始化和注册,并调用驱动程序的reset()和start()。
我们已经知道,在usb通信机制中,处理器内部一般都会集成host控制器,所以同样需要有对应的驱动程序去驱动。usb_add_hcd()函数则会在usb的主机控制器驱动程序的probe过程中被调用,内核中,几乎所有的usb host控制器驱动都是这样的写法。
在USB Host控制器驱动框架下,有一个重要的数据结构:struct usb_hcd,用于描述一个USB Host Controller Driver(简写HCD)。例如通用平台ohci驱动,驱动文件则是:ohci-platform.c,源码中有如下代码:
usb_create_hcd用于创建并初始化一个hcd结构描述符:
usb_add_hcd用于初始化hcd结构和注册hcd:
调用platform_get_irq()为设备分配中断号:
在usb_add_hcd()中会调用usb_hcd_request_irqs():
从上图可知,其会调用request_irq()这个重磅函数为ohci host控制器分配中断,中断处理函数为usb_hcd_irq ():
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
irqreturn_t rc;
if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
rc = IRQ_NONE;
else if (hcd->driver->irq(hcd) == IRQ_NONE)
rc = IRQ_NONE;
else
rc = IRQ_HANDLED;
return rc;
}
仔细分析上述代码,又回去了:在hcd中断处理函数中则会去调用执行hcd驱动程序指定的irq:
hcd->driver->irq(hcd)
那么对于ohci控制器来说,irq则是ohci_irq(),如下图所示:
综上,如果处理器内部usb host控制器符合ohci通用标准,则可以使用ohci-platform.c下的驱动程序,这意味着当ohci类的usb设备插入时,对应的中断处理函数会被调用执行(小生根据实际源码运行验证过,也确实如此)
在linux内核中,对于ehci和xhci的host控制器,同样有对应的通用驱动程序。只要处理器平台内部USB host控制器符合linux内核提供的通用驱动即可使用。
四、USB设备插入后的硬件过程
从linux内核源码反推硬件对插入的USB设备的识别,大概知道这个过程是由处理器内部的host控制器完成的。当插入USB设备后,将触发对应的usb host控制器的中断,中断进而又由中断控制器捕获,从而将中断信息由硬件转向linux内核的中断系统,从前面分析可知,USB host控制器驱动程序都为对应的控制器指定了中断处理函数,那么在中断处理函数中,则会读取控制器的寄存器参数值从而获取控制器状态,除此之外在中断处理函数中还做了很多事情(标准xhci控制器驱动程序的写法与ohci、ehci标准控制器驱动有很大不同,暂不过多深究!),例如调用usb_hcd_poll_rh_status(),但目的只有一个:激活刷新usb hub。
五、USB设备插入后的软件过程
当usb hub被激活刷新后,其后会调用到hub_event,后续的调用逻辑如下:
hub_events()->hub_port_connect_change()->hub_port_connect()->usb_new_device()
->device_add()->kobject_uevent()
usb_new_device()是创建usb新设备的核心函数,在linux内核启动过程中或者插入usb设备后,USB核心会调用这个函数。
kobject_uevent()用于向用户空间发送uevent事件,通知用户空间的侦听程序USB设备已经插入。
『笔者在浏览查询资料时,发现了这个问题,在此一并列出』
Q1:如果USB设备的驱动是在设备插入时动态加载的,那这个加载过程,处在这一个过程的哪一个位置?
A1:调用bus_probe_device(dev)完成!
综上,得出结论:在linux内核中,一个USB设备插入时,其设备驱动是动态加载的
审核编辑:陈陈
全部0条评论
快来发表一下你的评论吧 !