一、導讀
本文將描述linux-usb子系統的核心,主要分析其核心的初始化流程,文中源碼基于內核版本:4.1.15。
linux usb子系統框架的核心位于/drivers/usb/core/目錄中,文件結構如下圖所示:

目錄中各文件功能大致如下:
二、USB核心的初始化
在linux內核的啟動階段USB模塊最早輸出日志信息如下:
(1)[0.475284]usbcore:registerednewinterfacedriverusbfs (2)[0.475348]usbcore:registerednewinterfacedriverhub (3)[0.475400]usbcore:registerednewdevicedriverusb
從上述輸出日志可知:第(1)行表示成功注冊USB文件系統,且在系統正常啟動后,會生成對應的/sys/bus/usb/目錄。第(2)行表示成功注冊USB HUB驅動。第(3)行表明成功注冊USB通用設備驅動,即usb_generic_driver。通常USB設備都是以設備的身份先與usb_generic_driver匹配,匹配成功后,會分裂出接口,當對接口調用 device_add()后,會觸發USB接口和USB驅動的匹配。
USB核心由/drivers/usb/core/usb.c文件描述。該文件以linux內核模塊的方式設計,使用subsys_initcall(usb_init);將usb核心模塊導出。其中,usb核心的初始化由usb_init()完成,實現如下:
staticint__initusb_init(void) { intretval; if(usb_disabled()){ pr_info("%s:USBsupportdisabled ",usbcore_name); return0; } usb_init_pool_max();//初始化pool_max參數 usb_debugfs_init();//初始化usb的調試文件系統debugfs usb_acpi_register();//初始化acpi retval=bus_register(&usb_bus_type);//向linux內核注冊usb總線類型 if(retval) gotobus_register_failed; //注冊usb通知器 retval=bus_register_notifier(&usb_bus_type,&usb_bus_nb); if(retval) gotobus_notifier_failed; //usb設備號初始化 retval=usb_major_init(); if(retval) gotomajor_init_failed; //注冊usbfs驅動程序 retval=usb_register(&usbfs_driver); if(retval) gotodriver_register_failed; //初始化usb的devio retval=usb_devio_init(); if(retval) gotousb_devio_init_failed; //初始化usb的hub retval=usb_hub_init(); if(retval) gotohub_init_failed; //注冊通用usb設備驅動 retval=usb_register_device_driver(&usb_generic_driver,THIS_MODULE); if(!retval) gotoout; usb_hub_cleanup(); hub_init_failed: usb_devio_cleanup(); usb_devio_init_failed: usb_deregister(&usbfs_driver); driver_register_failed: usb_major_cleanup(); major_init_failed: bus_unregister_notifier(&usb_bus_type,&usb_bus_nb); bus_notifier_failed: bus_unregister(&usb_bus_type); bus_register_failed: usb_acpi_unregister(); usb_debugfs_cleanup(); out: returnretval; }
從上述代碼可見,usb_init()中執行了如下操作:
(1)判斷linux內核是否開啟了對USB的支持,如果不支持則直接返回。
(2)初始化調試文件系統關于usb的目錄和文件:
staticintusb_debugfs_init(void)
{
//創建usb目錄
usb_debug_root=debugfs_create_dir("usb",NULL);
if(!usb_debug_root)
return-ENOENT;
//在usb目錄下創建devices文件
usb_debug_devices=debugfs_create_file("devices",0444,
usb_debug_root,NULL,
&usbfs_devices_fops);
if(!usb_debug_devices)
{
debugfs_remove(usb_debug_root);
usb_debug_root=NULL;
return-ENOENT;
}
return0;
}
(3)初始化usb與acpi相關的參數:

(4)向linux內核注冊usb總線類型,usb總線類型定義如下:
structbus_typeusb_bus_type={
.name="usb",
.match=usb_device_match,
.uevent=usb_uevent,
};
(5)注冊usb總線通知器
(6)初始化usb主設備號:
intusb_major_init(void)
{
interror;
//注冊usb字符設備
error=register_chrdev(USB_MAJOR,"usb",&usb_fops);
if(error)
printk(KERN_ERR"Unabletogetmajor%dforusbdevices
",
USB_MAJOR);
returnerror;
}
(7)注冊usbfs驅動程序,usbfs以usb驅動程序的形式進行定義:
structusb_driverusbfs_driver={
.name="usbfs",
.probe=driver_probe,
.disconnect=driver_disconnect,
.suspend=driver_suspend,
.resume=driver_resume,
};
(8)初始化usb的devio,實質上是初始化USB字符設備,devio用于USB設備與用戶空間進行通信:
int__initusb_devio_init(void)
{
intretval;
retval=register_chrdev_region(USB_DEVICE_DEV,USB_DEVICE_MAX,
"usb_device");
if(retval){
printk(KERN_ERR"Unabletoregisterminorsforusb_device
");
gotoout;
}
cdev_init(&usb_device_cdev,&usbdev_file_operations);
retval=cdev_add(&usb_device_cdev,USB_DEVICE_DEV,USB_DEVICE_MAX);
if(retval){
printk(KERN_ERR"Unabletogetusb_devicemajor%d
",
USB_DEVICE_MAJOR);
gotoerror_cdev;
}
//注冊usbdev_nb通知器
usb_register_notify(&usbdev_nb);
out:
returnretval;
error_cdev:
unregister_chrdev_region(USB_DEVICE_DEV,USB_DEVICE_MAX);
gotoout;
}
(9)初始化usb的hub,hub也是以USB驅動方式進行設計(/drivers/usb/core/hub.c):
staticstructusb_driverhub_driver={
.name="hub",
.probe=hub_probe,
.disconnect=hub_disconnect,
.suspend=hub_suspend,
.resume=hub_resume,
.reset_resume=hub_reset_resume,
.pre_reset=hub_pre_reset,
.post_reset=hub_post_reset,
.unlocked_ioctl=hub_ioctl,
.id_table=hub_id_table,
.supports_autosuspend=1,
};
intusb_hub_init(void)
{
if(usb_register(&hub_driver)0)?{
??printk(KERN_ERR?"%s:?can't?register?hub?driver
",
???usbcore_name);
??return?-1;
?}
?/*
??*?The?workqueue?needs?to?be?freezable?to?avoid?interfering?with
??*?USB-PERSIST?port?handover.?Otherwise?it?might?see?that?a?full-speed
??*?device?was?gone?before?the?EHCI?controller?had?handed?its?port
??*?over?to?the?companion?full-speed?controller.
??*/
?hub_wq?=?alloc_workqueue("usb_hub_wq",?WQ_FREEZABLE,?0);
?if?(hub_wq)
??return?0;
?/*?Fall?through?if?kernel_thread?failed?*/
?usb_deregister(&hub_driver);
?pr_err("%s:?can't?allocate?workqueue?for?usb?hub
",?usbcore_name);
?return?-1;
}
(10)注冊通用usb設備驅動usb_generic_driver,定義如下(/drivers/usb/core/generic.c):
structusb_device_driverusb_generic_driver={
.name="usb",
.probe=generic_probe,
.disconnect=generic_disconnect,
#ifdefCONFIG_PM
.suspend=generic_suspend,
.resume=generic_resume,
#endif
.supports_autosuspend=1,
};
?從前文描述可知:在linux啟動過程中或者是在插入USB設備后,通常USB設備都是以設備的身份先與usb_generic_driver匹配,這時候由.probe指向的generic_peobe()會執行,當匹配成功后,會分裂出接口,當對接口調用device_add()后,會觸發USB接口和USB接口驅動的匹配。
generic_probe()函數實現如下:
staticintgeneric_probe(structusb_device*udev)
{
interr,c;
/*Chooseandsettheconfiguration.Thisregisterstheinterfaces
*withthedrivercoreandletsinterfacedriversbindtothem.
*/
if(udev->authorized==0)
dev_err(&udev->dev,"Deviceisnotauthorizedforusage
");
else{
//選擇配置
c=usb_choose_configuration(udev);
if(c>=0){
//設置配置
err=usb_set_configuration(udev,c);
if(err&&err!=-ENODEV){
dev_err(&udev->dev,"can'tsetconfig#%d,error%d
",
c,err);
/*Thisneednotbefatal.Theusercantryto
*setotherconfigurations.*/
}
}
}
/*USBdevicestate==configured...usable*/
usb_notify_add_device(udev);//通知usb設備添加,對應注冊的回調函數會被執行。
return0;
}
三、總結
本文主要描述linux-usb核心的初始化,usb核心是usb子系統的內層,其他的usb模塊都基于這個內層再設計。除此之外,著重描述了usb_init()函數的具體執行步驟,如下圖所示:

審核編輯:劉清
-
USB接口
+關注
關注
9文章
715瀏覽量
59026 -
ACPI
+關注
關注
1文章
14瀏覽量
9279 -
LINUX內核
+關注
關注
1文章
321瀏覽量
23206
原文標題:Linux-USB驅動框架 | usb核心
文章出處:【微信號:嵌入式小生,微信公眾號:嵌入式小生】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
關于Linux設備驅動中input子系統的介紹
嵌入式技術:Linux驅動USB必須了解的四個描述符
輸入子系統的作用與框架
基于USB設備的Linux網絡驅動程序開發
基于Linux內核輸入子系統的驅動研究
Linux usb子系統:子系統架構
詳細了解Linux設備模型中的input子系統
linux-usb子系統的核心描述
評論