Linux USB 3.0驅動分析(四)—— USB Core分析


一.拓撲結構

·之所以要規定這個樹形拓撲結構是為了避免環形連接。

·一條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;
}


8.usb hub初始化
后續詳細分析


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博客



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM