本文從軟件角度分析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設備插入時,其設備驅動是動態加載的
審核編輯:陳陳
-
usb
+關注
關注
60文章
7773瀏覽量
262392 -
Linux
+關注
關注
87文章
11123瀏覽量
207919 -
熱插拔
+關注
關注
2文章
208瀏覽量
36311
原文標題:不放過linux-usb的熱插拔過程
文章出處:【微信號:嵌入式小生,微信公眾號:嵌入式小生】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論