Linux之 USB驱动框架-USB总线核心和主控驱动(4)

news2024/9/20 22:51:33

一、USB设备描述符

一个USB设备描述符中可以有多个配置描述符,即USB设备可以有多种配置;一个配置描述符中可以有多个接口描述符,即USB设备可以支持多种功能(接口);一个接口描述符中可以有多个端点描述符。
一设备至少要包含设备描述符、配置描述符和接口描述符,如果USB设备没有端点描述符,则它仅仅用默认管道与主机进行数据传输。
接口,表示逻辑上的设备,比如USB声卡可以分为接口1-录音设备,接口2-播放设备。
访问设备时,即访问某个接口,接口表示逻辑设备。
传输数据时,即读写某个端口,端口是数据通道。

1.1 设备描述符

/* USB_DT_DEVICE: Device descriptor */
struct usb_device_descriptor {
    __u8  bLength; //该结构体大小
    __u8  bDescriptorType; //描述符类型 (此处应为0x01,即设备描述符)
 
    __le16 bcdUSB; //usb版本号 200 -> USB2.0
    __u8  bDeviceClass; //设备类 
    __u8  bDeviceSubClass; //设备类子类
    __u8  bDeviceProtocol; //设备协议,以上三点都是USB官方定义
    __u8  bMaxPacketSize0; //端点0最大包大小 (只能为8,16,32,64)
    __le16 idVendor; //厂家id
    __le16 idProduct; //产品id
    __le16 bcdDevice; //设备出厂编号
    __u8  iManufacturer; //描述厂商信息的字符串描述符的索引值
    __u8  iProduct; //描述产品信息的字串描述符的索引值
    __u8  iSerialNumber; //描述设备序列号信息的字串描述符的索引值 
    __u8  bNumConfigurations; //可能的配置描述符的数目
} __attribute__ ((packed));
 

1.2 配置描述符

 struct usb_config_descriptor {
    __u8  bLength; //该结构体大小
    __u8  bDescriptorType;//描述符类型(本结构体中固定为0x02)  
 
    __le16 wTotalLength; //该配置下,信息的总长度(包括配置,接口,端点和设备类及厂商定义的描述符)
    __u8  bNumInterfaces; //接口的个数
    __u8  bConfigurationValue; //Set_Configuration命令所需要的参数值,用来选定此配置
    __u8  iConfiguration; //描述该配置的字符串描述的索引值 
    __u8  bmAttributes;//供电模式的选择  
    __u8  bMaxPower;//设备从总线提取的最大电流
} __attribute__ ((packed));

1.3 接口描述符

 struct usb_interface_descriptor {
    __u8  bLength;        //该结构体大小
    __u8  bDescriptorType;//接口描述符的类型编号(0x04)
 
    __u8  bInterfaceNumber;  //该接口的编号  
    __u8  bAlternateSetting; //备用的接口描述符编号  
    __u8  bNumEndpoints; //该接口使用的端点数,不包括端点0  
    __u8  bInterfaceClass; //接口类
    __u8  bInterfaceSubClass; //子类
    __u8  bInterfaceProtocol; //协议
    __u8  iInterface;//描述此接口的字串描述表的索引值  
} __attribute__ ((packed));

 配置描述符中包含了一个或多个接口描述符,这里的“接口”并不是指物理存在的接口,在这里把它称之为“功能”更易理解些,例如一个设备既有录音的功能又有扬声器的功能,则这个设备至少就有两个“接口”。

1.4 端点描述符

/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
    __u8  bLength;        //端点描述符字节数大小(7个字节)
    __u8  bDescriptorType;//端点描述符类型编号(0x05) 
 
    __u8  bEndpointAddress; //此描述表所描述的端点的地址、方向 : 
                            // bit3~bit0:端点号,bit6~bit4:保留,
                            // bit7:方向,如果是控制端点则忽略,0-输出端点(主机到设备)1-输入端点(设备到主机)
    __u8  bmAttributes; // 端点特性,bit1~bit0 表示传输类型,其他位保留
                        // 00-控制传输  01-实时传输   10-批量传输 11-中断传输
    __le16 wMaxPacketSize;  //端点收、发的最大包大小
    __u8  bInterval; // 中断传输模式中主机查询端点的时间间隔。
                     // 对于实时传输的端点此域必需为1,表示周期为1ms。对于中断传输的端点此域值的范围为1ms到255ms
 
    /* NOTE:  these two are _only_ in audio endpoints. */
    /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
    __u8  bRefresh;
    __u8  bSynchAddress;
} __attribute__ ((packed));
 

 端点是设备与主机之间进行数据传输的逻辑接口,除配置使用的端点0(控制端点,一般一个设备只有一个控制端点)为双向端口外,其它均为单向。端点描述符描述了数据的传输类型、传输方向、数据包大小和端点号(也可称为端点地址)等。

除了描述符中描述的端点外,每个设备必须要有一个默认的控制型端点,地址为0,它的数据传输为双向,而且没有专门的描述符,只是在设备描述符中定义了它的最大包长度。主机通过此端点向设备发送命令,获得设备的各种描述符的信息,并通过它来配置设备。

1.5 字符描述符

struct usb_string_descriptor {
    __u8  bLength;  // 此描述表的字节数(bString域的数值N+2)
    __u8  bDescriptorType; // 字串描述表类型(此处应为0x03)

    __le16 wData[1];        /* UTF-16LE encoded */  
} __attribute__ ((packed));
 

1.6 人机接口描述符

USB 设备中有一大类就是 HID 设备,即 Human Interface Devices,人机接口设备。这类设备包括鼠标、键盘等,主要用于人与计算机进行交互。 它是 USB 协议最早支持的一种设备类。 HID 设备可以作为低速、全速、高速设备用。由于 HID 设备要求用户输入能得到及时响应,故其传输方式通常采用中断方式。
在 USB 协议中, HID 设备的定义放置在接口描述符中, USB 的设备描述符和配置描述符中不包含 HID 设备的信息。因此,对于某些特定的 HID 设备,可以定义多个接口,只有其中一个接口为 HID 设备类即可。

1.7 USB描述符的类型值

二、USB总线驱动程序

2.1 usb core

初始化内核USB总线及提供USB相关API,为设备驱动和HCD的交互提供桥梁。

usb_init

static int __init usb_init(void)
{
        int retval;
        if (usb_disabled()) {
                pr_info("%s: USB support disabled\n", usbcore_name);
                return 0;
        }
        usb_init_pool_max();

        usb_debugfs_init();

        usb_acpi_register();
        retval = bus_register(&usb_bus_type);         -----------------(1)
        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();
        if (retval)
                goto major_init_failed;
        retval = usb_register(&usbfs_driver);   ---------------------------(2)
        if (retval)
                goto driver_register_failed;
        retval = usb_devio_init();
        if (retval)
                goto usb_devio_init_failed;
        retval = usb_hub_init();                 -----------------------------------(3)        
        if (retval)
                goto hub_init_failed;
        retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);-------(4)
        if (!retval)
                goto out;

        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:
        return retval;
}
 

 (1)USB是基于总线-驱动-设备模型的框架,其初始化阶段一个重点任务就是完成USB总线的创建。usb_bus_type提供了驱动和设备匹配的匹配函数,后面注册设备和驱动时会调用到。

usb_bus_type

struct bus_type usb_bus_type = {
        .name =         "usb",
        .match =        usb_device_match,    //调用这个匹配函数
        .uevent =       usb_uevent,
        .need_parent_lock =     true,
};
 

 使用bus_register接口注册USB总线,会创建出两条链表用来分别存放向USB总线注册的设备和驱动。如下:

int bus_register(struct bus_type *bus)
{
      。。。。

        INIT_LIST_HEAD(&priv->interfaces);
        __mutex_init(&priv->mutex, "subsys mutex", key);
        klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); //设备链表
        klist_init(&priv->klist_drivers, NULL, NULL);   // 驱动链表

        retval = add_probe_files(bus);
        if (retval)
                goto bus_probe_files_fail;

        retval = bus_add_groups(bus, bus->bus_groups);
 。。。。。。。
}
EXPORT_SYMBOL_GPL(bus_register);
 

 另外一篇具体分析bus_register的具体做了哪些事情。

(2)usbfs_driver  是usb 接口驱动,注册接口驱动。一个设备可以有多个接口,每个接口对应着不同的功能。在usb总线注册USB接口驱动,该驱动被放在usb总线的驱动链表中。

usbfs_driver 

struct usb_driver usbfs_driver = {
        .name =         "usbfs",
        .probe =        driver_probe,
        .disconnect =   driver_disconnect,
        .suspend =      driver_suspend,
        .resume =       driver_resume,
        .supports_autosuspend = 1,
};
 

(3)初始化 usb hub, 初始化一个USB设备集线器,用来检测USB设备的连接和断开。

usb_hub_init()

int usb_hub_init(void)
{
        if (usb_register(&hub_driver) < 0) {  -----------------------(1)
                printk(KERN_ERR "%s: can't register hub driver\n",
                        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); --------(2)
        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\n", usbcore_name);

        return -1;
}
 

(3.1)注册 hub_driver.

hu_driver 

static struct usb_driver hub_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,
};
 

(3.2) 创建一个工作队列usb_hub_wq, 用来处理USB设备的断开、连接等事件。在内核低版本中,是创建了内核线程,khubd_task = kthread_run(hub_thread, NULL, "khubd"); 而在内核高版本是使用的工作队列。

 接着向usb 总线注册一个hub设备,触发 hub_probe,把hub event 压入hub_wq 工作队列

hub_probe ->    
    hub_configure ->
        usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
                hub, endpoint->bInterval);
        // usb_fill_int_urb 接口创建了一个中断类型的 USB请求控制块
            hub_irq -> 
                kick_hub_wq(hub) ->
                    queue_work(hub_wq, &hub->events); //hub events 压入hub_wq 队列
    

这个工作队列的处理函数是:hub_event 

hub_probe->INIT_WORK(&hub->events, hub_event);

hub_event

static void hub_event(struct work_struct *work)
{
        struct usb_device *hdev;
        struct usb_interface *intf;
        struct usb_hub *hub;
        struct device *hub_dev;
        u16 hubstatus;
        u16 hubchange;
        int i, ret;

        hub = container_of(work, struct usb_hub, events);
        hdev = hub->hdev;
        hub_dev = hub->intfdev;
        intf = to_usb_interface(hub_dev);

        kcov_remote_start_usb((u64)hdev->bus->busnum);

        dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
                        hdev->state, hdev->maxchild,
                        /* NOTE: expects max 15 ports... */
                        (u16) hub->change_bits[0],
                        (u16) hub->event_bits[0]);

        /* Lock the device, then check to see if we were
         * disconnected while waiting for the lock to succeed. */
        usb_lock_device(hdev);
        if (unlikely(hub->disconnected))
                goto out_hdev_lock;

        /* If the hub has died, clean up after it */
        if (hdev->state == USB_STATE_NOTATTACHED) {
                hub->error = -ENODEV;
                hub_quiesce(hub, HUB_DISCONNECT);
                goto out_hdev_lock;
        }

        /* Autoresume */
        ret = usb_autopm_get_interface(intf);
        if (ret) {
                dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
                goto out_hdev_lock;
        }

        /* If this is an inactive hub, do nothing */
        if (hub->quiescing)
                goto out_autopm;

        if (hub->error) {
                dev_dbg(hub_dev, "resetting for error %d\n", hub->error);

                ret = usb_reset_device(hdev);
                if (ret) {
                        dev_dbg(hub_dev, "error resetting hub: %d\n", ret);
                        goto out_autopm;
                }

                hub->nerrors = 0;
                hub->error = 0;
        }

        /* deal with port status changes */    //hub 的端口变化处理
        for (i = 1; i <= hdev->maxchild; i++) {
                struct usb_port *port_dev = hub->ports[i - 1];

                if (test_bit(i, hub->event_bits)
                                || test_bit(i, hub->change_bits)
                                || test_bit(i, hub->wakeup_bits)) {
                        /*
                         * The get_noresume and barrier ensure that if
                         * the port was in the process of resuming, we
                         * flush that work and keep the port active for
                         * the duration of the port_event().  However,
                         * if the port is runtime pm suspended
                         * (powered-off), we leave it in that state, run
                         * an abbreviated port_event(), and move on.
                         */
                        pm_runtime_get_noresume(&port_dev->dev);
                        pm_runtime_barrier(&port_dev->dev);
                        usb_lock_port(port_dev);
                        port_event(hub, i);
                        usb_unlock_port(port_dev);
                        pm_runtime_put_sync(&port_dev->dev);
                }
        }

        /* deal with hub status changes */    
        if (test_and_clear_bit(0, hub->event_bits) == 0)
                ;       /* do nothing */
        else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
                dev_err(hub_dev, "get_hub_status failed\n");
        else {
                if (hubchange & HUB_CHANGE_LOCAL_POWER) {
                        dev_dbg(hub_dev, "power change\n");
                        clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
                        if (hubstatus & HUB_STATUS_LOCAL_POWER)
                                /* FIXME: Is this always true? */
                                hub->limited_power = 1;
                        else
                                hub->limited_power = 0;
                }
                if (hubchange & HUB_CHANGE_OVERCURRENT) {
                        u16 status = 0;
                        u16 unused;

                        dev_dbg(hub_dev, "over-current change\n");
                        clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
                        msleep(500);    /* Cool down */
                        hub_power_on(hub, true);
                        hub_hub_status(hub, &status, &unused);
                        if (status & HUB_STATUS_OVERCURRENT)
                                dev_err(hub_dev, "over-current condition\n");
                }
        }

out_autopm:
        /* Balance the usb_autopm_get_interface() above */
        usb_autopm_put_interface_no_suspend(intf);
out_hdev_lock:
        usb_unlock_device(hdev);

        /* Balance the stuff in kick_hub_wq() and allow autosuspend */
        usb_autopm_put_interface(intf);
        kref_put(&hub->kref, hub_release);

        kcov_remote_stop();
}
 

(4) usb_register_device_driver(&usb_generic_driver, THIS_MODULE),向usb总线驱动链表中,注册 usb 设备驱动 usb_generic_driver。

usb_generic_driver

//drivers/usb/core/generic.c 

struct usb_device_driver usb_generic_driver = {
        .name = "usb",
        .match = usb_generic_driver_match,
        .probe = usb_generic_driver_probe,
        .disconnect = usb_generic_driver_disconnect,
#ifdef  CONFIG_PM
        .suspend = usb_generic_driver_suspend,
        .resume = usb_generic_driver_resume,
#endif
        .supports_autosuspend = 1,
};
 

 2.2 注册USB设备驱动

路径: drivers/usb/core/driver.c 

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;
        new_udriver->drvwrap.driver.name = new_udriver->name;
        new_udriver->drvwrap.driver.bus = &usb_bus_type;

//usb 设备匹配,先会调用usb_probe_device,然后在该接口中调用driver的probe
        new_udriver->drvwrap.driver.probe = usb_probe_device;
        new_udriver->drvwrap.driver.remove = usb_unbind_device;
        new_udriver->drvwrap.driver.owner = owner;
        new_udriver->drvwrap.driver.dev_groups = new_udriver->dev_groups;

        retval = driver_register(&new_udriver->drvwrap.driver);

        if (!retval) {
                pr_info("%s: registered new device driver %s\n",
                        usbcore_name, new_udriver->name);
                /*
                 * Check whether any device could be better served with
                 * this new driver
                 */
                bus_for_each_dev(&usb_bus_type, NULL, new_udriver,
                                 __usb_bus_reprobe_drivers);
        } else {
                pr_err("%s: error %d registering device driver %s\n",
                        usbcore_name, retval, new_udriver->name);
        }

        return retval;
}
EXPORT_SYMBOL_GPL(usb_register_device_driver);
 

 usb_register_device_driver 注册一个通用USB设备驱动,而不是USB接口驱动。

2.3 usb_register 和 usb_register_device_driver 区别

usb_register 注册一个USB接口驱动,一个设备可以有多个接口,一个接口表示一种功能。比如USB声卡设备,有两个接口,一个播放接口,一个录音接口。

//drivers/usb/core/driver.c

#define usb_register(driver) \
        usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
 

int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
                        const char *mod_name)
{
        int retval = 0;

        if (usb_disabled())
                return -ENODEV;

        new_driver->drvwrap.for_devices = 0;
        new_driver->drvwrap.driver.name = new_driver->name;
        new_driver->drvwrap.driver.bus = &usb_bus_type;

// 对应的usb接口“设备”被匹配时,首先会调用usb_probe_interface,然后在该接口中调用driver的probe
        new_driver->drvwrap.driver.probe = usb_probe_interface;
        new_driver->drvwrap.driver.remove = usb_unbind_interface;
        new_driver->drvwrap.driver.owner = owner;
        new_driver->drvwrap.driver.mod_name = mod_name;
        new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups;
        spin_lock_init(&new_driver->dynids.lock);
        INIT_LIST_HEAD(&new_driver->dynids.list);

        retval = driver_register(&new_driver->drvwrap.driver);
        if (retval)
                goto out;

        retval = usb_create_newid_files(new_driver);
        if (retval)
                goto out_newid;

        pr_info("%s: registered new interface driver %s\n",
                        usbcore_name, new_driver->name);

out:
        return retval;

out_newid:
        driver_unregister(&new_driver->drvwrap.driver);

        pr_err("%s: error %d registering interface driver %s\n",
                usbcore_name, retval, new_driver->name);
        goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);
 

总结:USB core注册了一个USB总线,并向USB总线中注册了三个驱动,分别是USB接口驱动、HUB驱动、USB设备驱动。其中在注册HUB驱动前创建了一个hub_wq 工作队列,用来处理hub上USB设备事件,比如插入和拔出;在HUB驱动的probe函数中,创建了一个urb并为其注册了一个timer中断处理函数hub_irq,hub wq 处理USB设备事件。

三、USB主机控制器驱动(HCD)

usb 主机控制器驱动 注册,有的使用platform_dirver 注册进系统,有的使用 pci_driver 注册进系统。具体使用哪种方式注册进系统,是根据usb控制器的硬件连接方式决定的。

我的PC机的XHCI驱动,就是pci_driver 方式

3.1下面分析以 platform 总线注册的OHCI主控驱动。

 usb 主机控制器设备

// arch/arm/mach-s3c/mach-smdk2440.c

MACHINE_START(S3C2440, "SMDK2440")
        /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
        .atag_offset    = 0x100,

        .init_irq       = s3c2440_init_irq,
        .map_io         = smdk2440_map_io,
        .init_machine   = smdk2440_machine_init,
        .init_time      = smdk2440_init_time,
MACHINE_END

static void __init smdk2440_machine_init(void)
{
        s3c24xx_fb_set_platdata(&smdk2440_fb_info);
        s3c_i2c0_set_platdata(NULL);
        /* Configure the I2S pins (GPE0...GPE4) in correct mode */
        s3c_gpio_cfgall_range(S3C2410_GPE(0), 5, S3C_GPIO_SFN(2),
                              S3C_GPIO_PULL_NONE);
        platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
        smdk_machine_init();
}

static struct platform_device *smdk2440_devices[] __initdata = {
        &s3c_device_ohci,
        &s3c_device_lcd,
        &s3c_device_wdt,
        &s3c_device_i2c0,
        &s3c_device_iis,
};

// arch/arm/mach-s3c/devs.c
struct platform_device s3c_device_ohci = {
        .name           = "s3c2410-ohci",
        .id             = -1,
        .num_resources  = ARRAY_SIZE(s3c_usb_resource),
        .resource       = s3c_usb_resource,
        .dev            = {
                .dma_mask               = &samsung_device_dma_mask,
                .coherent_dma_mask      = DMA_BIT_MASK(32),
        }
};

};

usb 主机控制器 驱动

//  .config
CONFIG_ARCH_S3C2410=y

//  drivers/usb/host/ohci-s3c2410.c

static int __init ohci_s3c2410_init(void)
{
        if (usb_disabled())
                return -ENODEV;

        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
        ohci_init_driver(&ohci_s3c2410_hc_driver, NULL); --------------------(1)

        /*
         * The Samsung HW has some unusual quirks, which require
         * Sumsung-specific workarounds. We override certain hc_driver
         * functions here to achieve that. We explicitly do not enhance
         * ohci_driver_overrides to allow this more easily, since this
         * is an unusual case, and we don't want to encourage others to
         * override these functions by making it too easy.
         */

        ohci_s3c2410_hc_driver.hub_status_data  = ohci_s3c2410_hub_status_data;
        ohci_s3c2410_hc_driver.hub_control      = ohci_s3c2410_hub_control;

        return platform_driver_register(&ohci_hcd_s3c2410_driver);--------------(2)
}
module_init(ohci_s3c2410_init);


static struct platform_driver ohci_hcd_s3c2410_driver = {
        .probe          = ohci_hcd_s3c2410_probe,
        .remove         = ohci_hcd_s3c2410_remove,
        .shutdown       = usb_hcd_platform_shutdown,
        .driver         = {
                .name   = "s3c2410-ohci",
                .pm     = &ohci_hcd_s3c2410_pm_ops,
                .of_match_table = ohci_hcd_s3c2410_dt_ids,
        },
};
 

(1) 调用 ohci_init_driver 把ohci_hc_driver 主控驱动赋值给ohci_s3c2410_hc_driver。

下面来分析ohci_init_driver 

/*
 * Generic structure: This gets copied for platform drivers so that
 * individual entries can be overridden as needed.
 */

//ohci_hc_driver 是内核抽象出的通用驱动

static const struct hc_driver ohci_hc_driver = {
        .description =          hcd_name,
        .product_desc =         "OHCI Host Controller",
        .hcd_priv_size =        sizeof(struct ohci_hcd),

        /*
         * generic hardware linkage
        */
        .irq =                  ohci_irq,
        .flags =                HCD_MEMORY | HCD_DMA | HCD_USB11,

        /*
        * basic lifecycle operations
        */
        .reset =                ohci_setup,
        .start =                ohci_start,
        .stop =                 ohci_stop,
        .shutdown =             ohci_shutdown,

        /*
         * managing i/o requests and associated device resources
        */
        .urb_enqueue =          ohci_urb_enqueue,
        .urb_dequeue =          ohci_urb_dequeue,
        .endpoint_disable =     ohci_endpoint_disable,

        /*
        * scheduling support
        */
        .get_frame_number =     ohci_get_frame,

        /*
        * root hub support
        */
        .hub_status_data =      ohci_hub_status_data,
        .hub_control =          ohci_hub_control,
#ifdef CONFIG_PM
        .bus_suspend =          ohci_bus_suspend,
        .bus_resume =           ohci_bus_resume,
#endif
        .start_port_reset =     ohci_start_port_reset,
};
 

void ohci_init_driver(struct hc_driver *drv,
                const struct ohci_driver_overrides *over)
{
        /* Copy the generic table to drv and then apply the overrides */
        *drv = ohci_hc_driver;  //把ohci_hc_driver 主控驱动给注册进系统的主控设备驱动里

        if (over) {
                drv->product_desc = over->product_desc;
                drv->hcd_priv_size += over->extra_priv_size;
                if (over->reset)
                        drv->reset = over->reset;
        }
}
EXPORT_SYMBOL_GPL(ohci_init_driver);
 

 上面提到的ohci_hc_driver 通用驱动赋值给ohci_s3c2410_hc_driver 后,如何使用呢?

在platform 总线设备和驱动匹配后,会调用ohci_hcd_s3c2410_probe,然后

hcd = usb_create_hcd(&ohci_s3c2410_hc_driver, &dev->dev, "s3c24xx"); 把ohci_s3c2410_hc_driver 赋值到新创建hcd中, hcd->driver = ohci_s3c2410_hc_driver;

再然后usb_add_hcd 向系统增加hcd。

 (2)ohci_hcd_s3c2410_driver 驱动通过platform 注册进系统。

主机控制器 设备和驱动匹配

//drivers/base/*

platform_driver_register->
    driver_register->
        bus_add_driver->
            driver_attach->
                bus_for_each_dev-> // 从平台总线的的设备链表中,取出每一项设备进行匹配
                    __driver_attach->

                        driver_match_device->// 此总线类型为平台总线,其存在match函数,即调用platform_match进行匹配

                        device_driver_attach->  //通过driver_match_device 驱动和设备匹配后,这个函数负责绑定,调用really_probe
                                driver_probe_device->
                                                    really_probe->

                                                                    drv-probe
 

static int __driver_attach(struct device *dev, void *data)
{
       ......

        ret = driver_match_device(drv, dev);
       ..........
        device_driver_attach(drv, dev);

        return 0;
}
 

                                           
    
// 平台总线                            
struct bus_type platform_bus_type = {
        .name           = "platform",
        .dev_groups     = platform_dev_groups,
        .match          = platform_match,
        .uevent         = platform_uevent,
        .dma_configure  = platform_dma_configure,
        .pm             = &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);
         

static int platform_match(struct device * dev, struct device_driver * drv)
{

      struct platform_device *pdev = to_platform_device(dev);
        struct platform_driver *pdrv = to_platform_driver(drv);

        /* When driver_override is set, only bind to the matching driver */
        if (pdev->driver_override)
                return !strcmp(pdev->driver_override, drv->name);

        /* Attempt an OF style match first */
        if (of_driver_match_device(dev, drv))
                return 1;

        /* Then try ACPI style match */
        if (acpi_driver_match_device(dev, drv))
                return 1;

        /* Then try to match against the id table */
        if (pdrv->id_table)
                return platform_match_id(pdrv->id_table, pdev) != NULL;

        /* fall-back to driver name match */
        return (strcmp(pdev->name, drv->name) == 0);   
    // 平台总线匹配设备和驱动的名称

}

// ohci 设备   name = "s3c2410-ohci"

struct platform_device s3c_device_ohci = {
        .name           = "s3c2410-ohci",
        .id             = -1,
        .num_resources  = ARRAY_SIZE(s3c_usb_resource),
        .resource       = s3c_usb_resource,
        .dev            = {
                .dma_mask               = &samsung_device_dma_mask,
                .coherent_dma_mask      = DMA_BIT_MASK(32),
        }
};

// ohci 驱动 name    = "s3c2410-ohci"

static struct platform_driver ohci_hcd_s3c2410_driver = {
        .probe          = ohci_hcd_s3c2410_probe,
        .remove         = ohci_hcd_s3c2410_remove,
        .shutdown       = usb_hcd_platform_shutdown,
        .driver         = {
                .name   = "s3c2410-ohci",
                .pm     = &ohci_hcd_s3c2410_pm_ops,
                .of_match_table = ohci_hcd_s3c2410_dt_ids,
        },
};
 

 匹配成功调用驱动的probe函数。

 driver_probe_device-> // 在此函数中匹配成功的话,就会去调用驱动的probe函数
    really_probe->
        drv->probe(dev)

usb 主机控制器驱动的probe 函数

 ohci_hcd_s3c2410_probe -> 
        usb_add_hcd ->     //向usb 增加主控驱动
            rhdev = usb_alloc_dev   //申请root hub 
            hcd->self.root_hub = rhdev
            register_root_hub -> 
                usb_new_device ->
                    device_add ->     
                        bus_attach_device ->
                            device_attach -> 
                                bus_for_each_drv -> // 从usb总线的的驱动链表中,取出每一项驱动进行匹配
                                    __device_attach ->
                                        driver_match_device->
                                            // 此总线类型为USB总线,其存在match函数,即调用usb_device_match进行匹配
                                        
                                        driver_probe_device-> // 在此函数中匹配成功的话,就会去调用驱动的probe函数
                                                    really_probe->
                                                        drv->probe(dev)

上面在注册设备时,调用的__driver_attach进行绑定,现在注册驱动,调用__device_attach进行绑定。

 接着分析:usb_device_match

static inline int is_usb_device(const struct device *dev)
{
    return dev->type == &usb_device_type;
}

/* Do the same for device drivers and interface drivers. */

static inline int is_usb_device_driver(struct device_driver *drv)
{
    // struct device_driver 中 struct usbdrv_wrap 中的for_devices变量为1,则为USB设备驱动
    // 上节USB Core中向USB总线注册的USB设备驱动中有将该变量设置为1(new_udriver->drvwrap.for_devices = 1;)
    return container_of(drv, struct usbdrv_wrap, driver)->
            for_devices;
}

static int usb_device_match(struct device *dev, struct device_driver *drv)
{
    // USB设备 和 USB接口“设备”分开处理 
    /* devices and interfaces are handled separately */
    if (is_usb_device(dev)) {
        // 处理USB设备
        /* interface drivers never match devices */
        if (!is_usb_device_driver(drv))
            return 0;

        /* TODO: Add real matching code */
        return 1;

    } else {
        // 处理USB接口设备
        struct usb_interface *intf;
        struct usb_driver *usb_drv;
        const struct usb_device_id *id;

        /* device drivers never match interfaces */
        if (is_usb_device_driver(drv))
            return 0;

        intf = to_usb_interface(dev);
        usb_drv = to_usb_driver(drv);

        id = usb_match_id(intf, usb_drv->id_table);
        if (id)
            return 1;

        id = usb_match_dynamic_id(intf, usb_drv);
        if (id)
            return 1;
    }

    return 0;
}
 

 probe 向USB总线注册一个root hub 设备,从usb总线的的驱动链表中,取出每一项驱动进行匹配。在USB Core中已经向总线注册了三个驱动(USB设备驱动、USB接口驱动、USB hub驱动),根据条件匹配到USB设备驱动,则去调用USB设备驱动的probe函数。

usb 设备驱动的probe函数:

struct usb_device_driver usb_generic_driver = {
        .name = "usb",
        .match = usb_generic_driver_match,
        .probe = usb_generic_driver_probe,
        .disconnect = usb_generic_driver_disconnect,
#ifdef  CONFIG_PM
        .suspend = usb_generic_driver_suspend,
        .resume = usb_generic_driver_resume,
#endif
        .supports_autosuspend = 1,
};
 

 usb_generic_driver_probe(struct usb_device *udev) -> // 从上分析流程知udev为USB root hub设备 
                    usb_set_configuration ->
                                        device_add ->  // 创建USB接口设备,USB root hub接口设备被创建,device_add 又开始重复上面函数的流程

之后匹配到USB Core中注册的USB hub驱动,执行USB hub驱动的probe函数,该probe函数中,创建了一个urb并为其注册了一个中断处理函数hub_irq,hub_wq来处理USB设备事件(插入、拔出)。至此,系统启动初始化时关于USB的内容分析完成。USB Core和USB HCD的成功建立联系,为之后的USB设备驱动提供API。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1617352.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Bentley二次开发教程16-元素管理-巩固练习

该练习中的方法涉及到前两期的方法&#xff0c;主要步骤为&#xff1a; 使用拉伸实体功能创建梁与圆柱并进行变换对梁截面进行标注并进行变换对梁与圆柱执行布尔运算对实体进行材质附加 public static void CmdPracticeWork(string unparsed) {DgnFile dgnFile Session.Inst…

小程序AI智能名片S2B2C商城系统:实现分销模式的四大要件深度解析

在当前的电商领域&#xff0c;小程序AI智能名片S2B2C商城系统正以其独特的分销模式&#xff0c;引领着行业创新的风潮。这种模式的成功&#xff0c;离不开四大核心要件&#xff1a;商品、机制、平台和运营。接下来&#xff0c;我们将对这四大要件进行深度解析。 首先&#xff0…

【LeetCode热题100】【多维动态规划】最小路径和

题目链接&#xff1a;64. 最小路径和 - 力扣&#xff08;LeetCode&#xff09; 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 经典动态规…

2025年考研复习资料免费获取教程(内容持续更新)

文章目录 导文2025年政治考研复习资料获取2025年英语考研复习资料获取2025年英语一考研复习资料获取2025年英语二考研复习资料获取 2025年数学考研复习资料获取2025年数学一考研复习资料获取2025年数学二考研复习资料获取 导文 考研复习是每一位考研学子都必须经历的重要阶段&a…

重学java 19.面向对象 继承 上

走不出的那段阴霾&#xff0c;很多时候只不过是&#xff0c;我们把它当成了唯一 —— 24.4.22 面向对象整体知识导向&#xff1a; 知识梳理&#xff1a; 1.知道继承的好处 2.会使用继承 3.继承之后成员变量和成员方法的访问特点 4.方法的重写&#xff0c;知道方法重写的使用场景…

nginx配置https及wss

环境说明 服务器的是centos7 nginx版本nginx/1.20.1 springboot2.7.12 nginx安装教程点击这里 微信小程序wss配置 如果您的业务是开发微信小程序&#xff0c; 请先进行如下配置。 boot集成websocket maven <dependency><groupId>org.springframework.boot<…

【Elasticsearch】Elasticsearch 从入门到精通(一):基本介绍

《Elasticsearch 从入门到精通》共包含以下 2 2 2 篇文章&#xff1a; Elasticsearch 从入门到精通&#xff08;一&#xff09;&#xff1a;基本介绍Elasticsearch 从入门到精通&#xff08;二&#xff09;&#xff1a;基础使用 &#x1f60a; 如果您觉得这篇文章有用 ✔️ 的…

c语言应用,三子棋游戏设计

在c语言中&#xff0c;若是要实现三子棋游戏&#xff0c;需要我们对c语言的一维和二维数组的使用&#xff0c;函数的声明和调用&#xff0c;以及循环语句分支语句的结合等&#xff0c;这些知识的贯通。 首先&#xff0c;在设计游戏前我们要知道一下三子棋游戏的规则&#xff0…

NXopen c++快速分割体

通过对话框选择体以及分割该体的面&#xff0c;实现体的分割&#xff0c;相关资源请访问下载我的资源分享&#xff08;上传需要时间审核&#xff09; NXOpen::Session* theSession NXOpen::Session::GetSession();NXOpen::Part* workPart(theSession->Parts()->Work());…

【Linux学习】初始冯诺漫体系结构

文章目录 认识冯诺依曼系统 认识冯诺依曼系统 什么是冯诺依曼体系结构&#xff1f; 冯诺依曼体系结构是一种将程序指令和数据以二进制形式存放在主存储器中&#xff0c;由中央处理器统一控制和执行的计算机系统结构。冯诺依曼体系结构实现了程序的可编程性和硬件与软件的分离&…

mmdeploy框架转化模型

文章目录 1 从源码编译1.1 安装onnxruntime后端1.2 build mmdeploy1.3 install model converter 2 模型转换2.1 deploy用法2.2 示例 1 从源码编译 参考链接 reference2 git clone -b main https://github.com/open-mmlab/mmdeploy.git --recursive cd mmdeploy1.1 安装onnxru…

计算机缺少msvcp120.dll如何解决,7种详细的修复方法分享

msvcr120.dll文件是微软Visual C运行时库的一部分&#xff0c;版本号为12.0。这个DLL文件包含了许多用于支持在Windows上运行的应用程序的重要函数和组件。它是确保某些程序能够正确执行的关键组成部分&#xff0c;特别是那些使用C编写或依赖于某些Microsoft库的程序。 当用户…

C++ | Leetcode C++题解之第44题通配符匹配

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isMatch(string s, string p) {auto allStars [](const string& str, int left, int right) {for (int i left; i < right; i) {if (str[i] ! *) {return false;}}return true;};auto charMatch []…

4月18号总结

java学习 网络编程 1.网络分层 网络分层是将网络通信划分为不同的逻辑层次&#xff0c;每一层负责特定的功能&#xff0c;从而实现网络通信的模块化和标准化。常用的网络分层模型包括OSI&#xff08;开放系统互联&#xff09;模型和TCP/IP模型。 特点和作用&#xff1a; 分…

(C++) 树状数组

目录 一、介绍 二、一维树状数组 2.1 区间长度 2.2 前驱和后继 2.3 查询前缀和 2.4 点更新 三、一维数组的实现 3.1 区间长度函数 3.2 前缀和 3.3 插入/更新 3.4 封装成类 一、介绍 树状数组&#xff08;Binary Indexed Tree&#xff0c;BIT&#xff09;&#xff0c;又称为 …

火绒安全概述

页面简介&#xff1a; 火绒安全是一款集多种安全功能于一体的国产软件&#xff0c;旨在为用户提供全面的计算机保护。本页面将详细介绍火绒安全的核心功能和使用方式。 页面内容概览&#xff1a; 杀毒防护 实时监控&#xff1a;详细介绍火绒安全如何实时检测系统中的文件和程序…

Pyside6:Spacer的使用,布局中控件顶格添加

在普通的布局&#xff0c;如水平或纵向布局中&#xff0c;我们的控件都会保持中间状态&#xff0c;如下&#xff1a; 但有许多情况下 &#xff0c;我们需要将控件布局为如下界面&#xff1a; 在前端开发时&#xff0c;我们很容易通过flex来进行布局&#xff0c;修正它的居中或者…

Github首页美化(updating)

Github首页美化 https://github.com/QInzhengk一、新建仓库二、美化Github首页主页访问量统计仓库状态统计常用语言占比统计社交链接 界面展示 https://github.com/QInzhengk 一、新建仓库 对Github首页进行美化&#xff0c;需要新建一个仓库名和自己 Github 用户名相同的仓库…

【数据库】三、数据库SQL语言命令(基础从入门到入土)

【全文两万多字&#xff0c;涵盖大部分常见情况&#xff0c;建议点赞收藏】 目录 文章目录 目录安装SQL语言1.使用2.DATABASE查看所有库新建数据库修改数据库删除数据库连接数据库 3.TABLE创建表查看库所有表删除表查看表信息重命名表修改表字段&#xff08;列&#xff09;表中…

Java---ideaIU-2023.1专业版使用以及安装方法

介绍 JetBrains 是一家专注于创建智能开发工具的前沿软件公司,包括:行业中领头的 Java IDE – IntelliJ IDEA,以及 Kotlin 编程语言。旗下常用的软件有IntelliJ IDEA、PhpStorm、RubyMine、Rider、WebStorm、goland、CLion、Pycharm&#xff0c;本安装包集成以上8款软件&#…