一.拓撲結構
·之所以要規定這個樹形拓撲結構是為了避免環形連接。
·一條USB總線有且只有一個USBHost,對應一個RootHub
·USB設備分為兩類,Hub和Functions,Hub通過端口Port連接更多USB設備,Functions即USB外接從設備。
·層次最多7層,且第7層不能有Hub,只能有functions。
·CompoundDevice-一個Hub上接多個設備組成一個小設備。
·CompositeDevice-一個USB外接設備具有多個復用功能。
二.USBCore
1.USB子系統結構
HCD(Host Controller Device): USB主控制器設備
協議里說,HCD提供主控制器驅動的硬件抽象,它只對USBCore一個負責,USBCore將用戶的請求映射到相關的HCD,用戶不能直接訪問HCD。換句話說,USBCore就是HCD與USB設備唯一的橋梁。
2.USB子系統的初始化
USBcore源碼位於./drivers/usb/core,其中的Makefile摘要如下,

usbcore這個模塊代表的不是某一個設備,而是所有USB設備賴以生存的模塊,它就是USB子系統。
在./drivers/usb/core/usb.c里實現了初始化,源代碼如下,
static int __init usb_init(void)
{
retval = usb_debugfs_init(); //1.usb debugfs初始化
if (retval)
goto out;
retval = bus_register(&usb_bus_type); //注冊USB總線,也就是USB子系統
if (retval)
goto bus_register_failed;
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb); //注冊內核通知鏈,用於設備和接口注冊的通知
if (retval)
goto bus_notifier_failed;
retval = usb_major_init(); //usb主設備號初始化
if (retval)
goto major_init_failed;
retval = usb_register(&usbfs_driver); //注冊usb文件系統
if (retval)
goto driver_register_failed;
retval = usb_devio_init(); //usb設備字符設備初始化
if (retval)
goto usb_devio_init_failed;
retval = usb_hub_init(); //usb hub初始化
if (retval)
goto hub_init_failed;
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE); //注冊一個USB設備(不是接口)驅動程序
if (!retval)
goto out;
}
usbcore注冊了USB總線,USB文件系統,USBHub以及USB的設備驅動usb generic driver等。
3. usb_debugfs_init --usb debugfs初始化
static int usb_debugfs_init(void)
{
usb_debug_root = debugfs_create_dir("usb", NULL); //創建"$(debugfs)/usb"
if (!usb_debug_root)
return -ENOENT;
//創建"$(debugfs)/usb/device",捆綁usbfs_devices_fops結構體,位置 /sys/kernel/debug/usb/device,打印所有總線下的設備
usb_debug_devices = debugfs_create_file("devices", 0444,usb_debug_root, NULL,&usbfs_devices_fops);
}
4.usb總線的注冊
struct bus_type usb_bus_type = {
.name = "usb", //總線名
.match = usb_device_match, //匹配方法,應該用於usb驅動id_table的匹配
.uevent = usb_uevent, //事件處理
};
5.注冊usb總線通知鏈
int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
{
return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
}
當總線添加刪除設備的時候會調用usb_bus_nb指定的notifier_cal方法,既usb_bus_notify
static int usb_bus_notify(struct notifier_block *nb, unsigned long action,void *data)
{
struct device *dev = data;
switch (action) {
case BUS_NOTIFY_ADD_DEVICE: //添加設備
if (dev->type == &usb_device_type) //usb設備
(void) usb_create_sysfs_dev_files(to_usb_device(dev)); //生成sysfs節點/sys/devices/platform/soc@0/38100000.usb/xhci-hcd.0.auto/usb1
else if (dev->type == &usb_if_device_type) //usb接口
(void) usb_create_sysfs_intf_files(to_usb_interface(dev));
break;
case BUS_NOTIFY_DEL_DEVICE: //刪除設備
if (dev->type == &usb_device_type) //usb設備
usb_remove_sysfs_dev_files(to_usb_device(dev));
else if (dev->type == &usb_if_device_type) //usb接口
usb_remove_sysfs_intf_files(to_usb_interface(dev));
break;
}
return 0;
}
6.初始化usb主控器字符設備,USB_MAJOR=180
int usb_major_init(void)
{
int error;
error = register_chrdev(USB_MAJOR, "usb", &usb_fops); //注冊usb控制器字符設備,捆綁usb_fops
if (error)
printk(KERN_ERR "Unable to get major %d for usb devices\n",USB_MAJOR);
return error;
}
捆綁usb_fops
static const struct file_operations usb_fops = {
.owner = THIS_MODULE,
.open = usb_open,
.llseek = noop_llseek,
};
7.注冊usbfs_driver
usb_register(&usbfs_driver); //通過usb_register()將usbfs驅動提交個設備模型,添加到USB總線的驅動鏈表里。
/* use a define to avoid include chaining to get THIS_MODULE & friends */
//其實是調用usb_register_driver,注冊一個usb接口驅動
#define usb_register(driver) \
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
struct usb_driver usbfs_driver = {
.name = "usbfs", //在/sys/bus/usb/drivers/usbfs
.probe = driver_probe,
.disconnect = driver_disconnect,
.suspend = driver_suspend,
.resume = driver_resume,
};
8.usb_devio_init USB_DEVICE_DEV=189
//用於應用程序直接訪問usb設備
int __init usb_devio_init(void)
{
int retval;
retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
"usb_device"); //分配設備號
if (retval) {
printk(KERN_ERR "Unable to register minors for usb_device\n");
goto out;
}
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 "Unable to get usb_device major %d\n",
USB_DEVICE_MAJOR);
goto error_cdev;
}
usb_register_notify(&usbdev_nb); //注冊設備通知鏈
}
usb注冊的通知鏈
void usb_register_notify(struct notifier_block *nb)
{
blocking_notifier_chain_register(&usb_notifier_list, nb);
}
usb通知鏈表頭為usb_notifier_list
在/drivers/usb/core/notify.c文件中,有四個函數()對usb_notifier_list中發送通知,這四個函數如下:
usb_notify_add_device //有設備添加
usb_notify_remove_device //有設備移除
usb_notify_add_bus //總線添加
usb_notify_remove_bus //總線移除
當這些事件發生后會調用usbdev_nb指定的notifier_cal方法,既usbdev_notify
static int usbdev_notify(struct notifier_block *self,
unsigned long action, void *dev)
{
switch (action) {
case USB_DEVICE_ADD:
break;
case USB_DEVICE_REMOVE:
usbdev_remove(dev);
break;
}
return NOTIFY_OK;
}
9. usb注冊通用設備驅動
int usb_register_device_driver(struct usb_device_driver *new_udriver,struct module *owner)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
new_udriver->drvwrap.for_devices = 1; //usb設備
new_udriver->drvwrap.driver.name = (char *) new_udriver->name; //設備名
new_udriver->drvwrap.driver.bus = &usb_bus_type; //總線類型
new_udriver->drvwrap.driver.probe = usb_probe_device; //probe方法
new_udriver->drvwrap.driver.remove = usb_unbind_device; //remove方法
new_udriver->drvwrap.driver.owner = owner; //模塊所有者
retval = driver_register(&new_udriver->drvwrap.driver); //注冊設備驅動
if (!retval) {
pr_info("%s: registered new device driver %s\n",usbcore_name, new_udriver->name);
usbfs_update_special();
} else {
printk(KERN_ERR "%s: error %d registering device driver %s\n",usbcore_name, retval, new_udriver->name);
}
return retval;
}
usb_device_driver結構體是usb_driver的簡化版本,這里注冊的是usb設備(非接口)驅動。
usb總線的match方法對usb設備和usb接口做了區分處理,針對usb設備,直接match的,(分析match時候再細化)
然后調用usb設備驅動的probe方法
static int usb_probe_device(struct device *dev)
{
struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
struct usb_device *udev = to_usb_device(dev);
int error = 0;
dev_dbg(dev, "%s\n", __func__);
if (!udriver->supports_autosuspend) //條件成立
error = usb_autoresume_device(udev);
if (!error)
error = udriver->probe(udev); //調用usb_device_driver的probe方法
return error;
}
接着調用usb_generic_driver的probe方法
struct usb_device_driver usb_generic_driver = {
.name = "usb",
.probe = generic_probe,
.disconnect = generic_disconnect,
#ifdef CONFIG_PM
.suspend = generic_suspend,
.resume = generic_resume,
#endif
.supports_autosuspend = 1,
};
對應到root hub,流程會轉入到generic_probe().代碼如下:
static int generic_probe(struct usb_device *udev)
{
/* Choose and set the configuration. This registers the interfaces
* with the driver core and lets interface drivers bind to them.
*/
if (udev->authorized == 0) //至於udev->authorized,在root hub的初始化中,是會將其初始化為1的.后面的邏輯就更簡單了.為root hub 選擇一個配置然后再設定這個配置.
dev_err(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev); //Usb2.0 spec上規定,對於hub設備,只能有一個config,一個interface,一個endpoint.實際上,在這里,對hub的選擇約束不大,反正就一個配置,不管怎么樣,選擇和設定都是這個配置.
if (c >= 0) {
err = usb_set_configuration(udev, c);
if (err && err != -ENODEV) {
dev_err(&udev->dev, "can't set config #%d, error %d\n",
c, err);
/* This need not be fatal. The user can try to
* set other configurations. */
}
}
}
/* USB device state == configured ... usable */
usb_notify_add_device(udev);
return 0;
}
三.USB總線
注冊USB總線通過bus_register(&usb_bus_type);
struct bus_type usb_bus_type={
.name="usb",
.match=usb_device_match,//這是個很重要的函數,用來匹配USB設備和驅動。
.uevent=usb_uevent,.pm=&usb_bus_pm_ops,
};
下面總結下USB設備和驅動匹配的全過程,
->step1-usb device driver
USB子系統初始化的時候就會注冊usb_generic_driver,它的結構體類型是usb_device_driver,它是USB世界里唯一的一個USB設備驅動,區別於struct usb_driver USB驅動。
·USB設備驅動(usb device driver)就只有一個,即usb_generice_driver這個對象,所有USB設備都要綁定到usb_generic_driver上,它的使命可以概括為:為USB設備選擇一個合適的配置,讓設備進入configured狀態。
·USB驅動(usb driver)就是USB設備的接口驅動程序,比如adb驅動程序,u盤驅動程序,鼠標驅動程序等等。
->step2-usb driver
Linux啟動時注冊USB驅動,在xxx_init()里通過usb_register()將USB驅動提交個設備模型,添加到USB總線的驅動鏈表里。
->step3-usb device
USB設備連接在Hub上,Hub檢測到有設備連接進來,為設備分配一個struct usb_device結構體對象,並將設備添加到USB總線的設備列表里。
->step4-usb interface
USB設備各個配置的詳細信息在USB core里的漫漫旅途中已經被獲取並存放在相關的幾個成員里。
usb_generic_driver得到了USB設備的詳細信息,然后把准備好的接口送給設備模型,Linux設備模型將接口添加到設備鏈表里,然后去輪詢USB總線另外一條驅動鏈表,針對每個找到的驅動去調用USB總線的match函數,完成匹配。
參考:
二、usb子系統初始化_jixianghao的博客-CSDN博客
USB在Linux里的結構框架是什么樣的?USB Core和Hub是什么?
Linux設備驅動之USB hub驅動_cosmoslhf的專欄-CSDN博客