Kernel-USB 驱动架构

1.USB设备识别流程

基于Amlogic代码梳理,插入USB设备后,硬件中断的调用堆栈

07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.253922@0] wth usb_hcd_giveback_urb enter
07-22 16:13:04.972     0     0 W <KERNEL|(0)swapper/0      >: [  136.253933@0] CPU: 0 PID: 0 Comm: swapper/0 Tainted: P           O    4.9.113 #41
07-22 16:13:04.972     0     0 W <KERNEL|(0)swapper/0      >: [  136.253936@0] Hardware name: Generic DT based system
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.253952@0] [c1677ba4+  16][<c020e274>] show_stack+0x20/0x24
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.253963@0] [c1677bc4+  32][<c057b2ec>] dump_stack+0x90/0xac
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.253973@0] [c1677be4+  32][<c06e4fc4>] usb_hcd_giveback_urb+0x34/0x134
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.253983@0] [c1677c14+  48][<c06e5178>] usb_hcd_poll_rh_status+0xb4/0x148
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.253992@0] [c1677cb4+ 160][<c0714574>] xhci_irq+0x858/0x1acc
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254001@0] [c1677cc4+  16][<c06e4b1c>] usb_hcd_irq+0x34/0x48
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254013@0] [c1677d14+  80][<c0299db4>] __handle_irq_event_percpu+0xc4/0x2ac
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254024@0] [c1677d34+  32][<c0299fc8>] handle_irq_event_percpu+0x2c/0x68
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254031@0] [c1677d54+  32][<c029a04c>] handle_irq_event+0x48/0x6c
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254039@0] [c1677d74+  32][<c029da98>] handle_fasteoi_irq+0xe0/0x1b0
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254048@0] [c1677d84+  16][<c0298e78>] generic_handle_irq+0x34/0x44
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254055@0] [c1677db4+  48][<c0299478>] __handle_domain_irq+0x8c/0xfc
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254063@0] [c1677ddc+  40][<c02015d0>] gic_handle_irq+0x48/0x84
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254071@0] [c1401d2c+   0][<c020efa4>] __irq_svc+0x84/0xc4
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254083@0] [c1401d2c+  56][<c07b92bc>] cpuidle_enter_state+0x178/0x484
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254092@0] [c1401d3c+  16][<c07b9618>] cpuidle_enter+0x24/0x28
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254099@0] [c1401d4c+  16][<c0282140>] call_cpuidle+0x34/0x50
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254107@0] [c1401d7c+  48][<c0282474>] cpu_startup_entry+0x17c/0x264
07-22 16:13:04.972     0     0 I <KERNEL|(0)swapper/0      >: [  136.254118@0] [c1401d94+  24][<c0e24200>] rest_init+0x98/0x9c

GIC中断调用usb_hcd_irq,在根据usb协议类型调用对应的Host Controller Driver的irq回调函数。

static const struct hc_driver xhci_hc_driver = {
        .description =                "xhci-hcd",
        .product_desc =                "xHCI Host Controller",
        .hcd_priv_size =        sizeof(struct xhci_hcd),

        /*
         * generic hardware linkage
         */
        .irq =                        xhci_irq,
        .flags =                HCD_MEMORY | HCD_USB3 | HCD_SHARED,

        /*
         * basic lifecycle operations
         */
        .reset =                NULL, /* set in xhci_init_driver() */
        .start =                xhci_run,
        .stop =                        xhci_stop,
        .shutdown =                xhci_shutdown,

        /*
         * managing i/o requests and associated device resources
         */
        .urb_enqueue =                xhci_urb_enqueue,
        .urb_dequeue =                xhci_urb_dequeue,
        .alloc_dev =                xhci_alloc_dev,
        .free_dev =                xhci_free_dev,
        .alloc_streams =        xhci_alloc_streams,
        .free_streams =                xhci_free_streams,
        .add_endpoint =                xhci_add_endpoint,
        .drop_endpoint =        xhci_drop_endpoint,
        .endpoint_reset =        xhci_endpoint_reset,
        .check_bandwidth =        xhci_check_bandwidth,
        .reset_bandwidth =        xhci_reset_bandwidth,
        .address_device =        xhci_address_device,
        .enable_device =        xhci_enable_device,
        .update_hub_device =        xhci_update_hub_device,
        .reset_device =                xhci_discover_or_reset_device,

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

        /*
         * root hub support
         */
        .hub_control =                xhci_hub_control,
        .hub_status_data =        xhci_hub_status_data,
        .bus_suspend =                xhci_bus_suspend,
        .bus_resume =                xhci_bus_resume,
        ...
}

xhci驱动初始化会进行hcd驱动的注册。

void xhci_init_driver(struct hc_driver *drv,
                      const struct xhci_driver_overrides *over)
{
        *drv = xhci_hc_driver;

        if (over) {
                drv->hcd_priv_size += over->extra_priv_size;
                if (over->reset)
                        drv->reset = over->reset;
                if (over->start)
                        drv->start = over->start;
        }
}

hub_probe时调用hub_configure(),将hub_irq注册为urb的complete回调函数。

static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint)
{
        ...
        usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
                hub, endpoint->bInterval);
        ...
}

static inline void usb_fill_int_urb(struct urb *urb,
                                    struct usb_device *dev,
                                    unsigned int pipe,
                                    void *transfer_buffer,
                                    int buffer_length,
                                    usb_complete_t complete_fn,
                                    void *context,
                                    int interval)
{
        urb->dev = dev;
        urb->pipe = pipe;
        urb->transfer_buffer = transfer_buffer;
        urb->transfer_buffer_length = buffer_length;
        urb->complete = complete_fn;
        urb->context = context;

        if (dev->speed == USB_SPEED_HIGH || dev->speed >= USB_SPEED_SUPER) {
                /* make sure interval is within allowed range */
                interval = clamp(interval, 1, 16);

                urb->interval = 1 << (interval - 1);
        } else {
                urb->interval = interval;
        }

        urb->start_frame = -1;
}
        

USB中断的响应流程,调用栈:

07-22 15:58:32.665  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316825@0] wth kick_hub_wq enter
07-22 15:58:32.665  3945  3945 W <KERNEL|(0)dex2oat        >: [   19.316836@0] CPU: 0 PID: 3945 Comm: dex2oat Tainted: P           O    4.9.113 #40
07-22 15:58:32.665  3945  3945 W <KERNEL|(0)dex2oat        >: [   19.316838@0] Hardware name: Generic DT based system
07-22 15:58:32.665  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316856@0] [bc347bf4+  16][<c020e274>] show_stack+0x20/0x24
07-22 15:58:32.665  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316867@0] [bc347c14+  32][<c057b2ec>] dump_stack+0x90/0xac
07-22 15:58:32.665  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316875@0] [bc347c34+  32][<c06de4a4>] kick_hub_wq+0x2c/0x10c
07-22 15:58:32.665  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316883@0] [bc347c4c+  24][<c06de608>] hub_irq+0x84/0xfc
07-22 15:58:32.665  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316892@0] [bc347c6c+  32][<c06e4e60>] __usb_hcd_giveback_urb+0x88/0xe0
07-22 15:58:32.665  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316900@0] [bc347c9c+  48][<c06e4f54>] usb_giveback_urb_bh+0x9c/0xd8
07-22 15:58:32.665  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316909@0] [bc347ccc+  48][<c02306e0>] tasklet_hi_action+0xc4/0x170
07-22 15:58:32.666  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316918@0] [bc347d2c+  96][<c0201874>] __do_softirq+0x13c/0x3e0
07-22 15:58:32.666  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316926@0] [bc347d3c+  16][<c0230078>] irq_exit+0xe0/0x11c
07-22 15:58:32.666  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316937@0] [bc347d6c+  48][<c029947c>] __handle_domain_irq+0x90/0xfc
07-22 15:58:32.666  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316945@0] [bc347d94+  40][<c02015d0>] gic_handle_irq+0x48/0x84
07-22 15:58:32.666  3945  3945 I <KERNEL|(0)dex2oat        >: [   19.316952@0] [00000000+   0][<c020f434>] __irq_usr+0x54/0x80

通过tasklet执行底半部函数usb_giveback_urb_bh调度

static void usb_giveback_urb_bh(unsigned long param)
{
        struct giveback_urb_bh *bh = (struct giveback_urb_bh *)param;
        struct list_head local_list;

        spin_lock_irq(&bh->lock);
        bh->running = true;
 restart:
        list_replace_init(&bh->head, &local_list);
        spin_unlock_irq(&bh->lock);

        while (!list_empty(&local_list)) {
                struct urb *urb;

                urb = list_entry(local_list.next, struct urb, urb_list);
                list_del_init(&urb->urb_list);
                bh->completing_ep = urb->ep;
                __usb_hcd_giveback_urb(urb);
                bh->completing_ep = NULL;
        }

        /* check if there are new URBs to giveback */
        spin_lock_irq(&bh->lock);
        if (!list_empty(&bh->head))
                goto restart;
        bh->running = false;
        spin_unlock_irq(&bh->lock);
}

__usb_hcd_giveback_urb中会执行urb->complete回调函数

- static void __usb_hcd_giveback_urb(struct urb *urb)
- {
-         struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
-         struct usb_anchor *anchor = urb->anchor;
-         int status = urb->unlinked;
-         /* pass ownership to the completion handler */
-         urb->status = status;
- 
-         /*
-          * We disable local IRQs here avoid possible deadlock because
-          * drivers may call spin_lock() to hold lock which might be
-          * acquired in one hard interrupt handler.
-          *
-          * The local_irq_save()/local_irq_restore() around complete()
-          * will be removed if current USB drivers have been cleaned up
-          * and no one may trigger the above deadlock situation when
-          * running complete() in tasklet.
-          */
- 
- #ifndef CONFIG_AMLOGIC_USB
-         local_irq_save(flags);
- #endif
-         urb->complete(urb);
- #ifndef CONFIG_AMLOGIC_USB
-         local_irq_restore(flags);
- #endif
- 
-         usb_anchor_resume_wakeups(anchor);
-         atomic_dec(&urb->use_count);
-         if (unlikely(atomic_read(&urb->reject)))
-                 wake_up(&usb_kill_urb_queue);
-         usb_put_urb(urb);
- 
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
{
        struct giveback_urb_bh *bh;
        bool running, high_prio_bh;
    
        spin_lock(&bh->lock);
        list_add_tail(&urb->urb_list, &bh->head);
        running = bh->running;
        spin_unlock(&bh->lock);

        if (running)
                ;
        else if (high_prio_bh)
                tasklet_hi_schedule(&bh->bh);
        else
                tasklet_schedule(&bh->bh);
}


static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
{
        spin_lock_init(&bh->lock);
        INIT_LIST_HEAD(&bh->head);
        tasklet_init(&bh->bh, usb_giveback_urb_bh, (unsigned long)bh);
}
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
{
        struct giveback_urb_bh *bh;
        bool running, high_prio_bh;
    
        spin_lock(&bh->lock);
        list_add_tail(&urb->urb_list, &bh->head);
        running = bh->running;
        spin_unlock(&bh->lock);

        if (running)
                ;
        else if (high_prio_bh)
                tasklet_hi_schedule(&bh->bh);
        else
                tasklet_schedule(&bh->bh);
}


static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
{
        spin_lock_init(&bh->lock);
        INIT_LIST_HEAD(&bh->head);
        tasklet_init(&bh->bh, usb_giveback_urb_bh, (unsigned long)bh);
}

hub驱动probe函数INIT_WORK(&hub->events, hub_event);

跳转到hub_event

static void hub_event(struct work_struct *work)
{
        ...
        /* deal with port status changes */
        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);
                }
        }
        ...
}

接下来的调用顺序

port_event()
    hub_port_connect_change()
        hub_port_connect()
            usb_alloc_dev() //创建usb设备

 2.USB驱动架构

参考总结 https://zhuanlan.zhihu.com/p/61079354

USB是一种主从结构的系统。主机叫做Host,从机叫做Device;一般情况下,PC作为USB host端,手机等数码设备作为USB device,OTG(On the Go)则使设备既可以是USB device也可以是USB host;通常,作为USB device的设备被称为Gadget。

所有支持USB通讯的设备端都有USB device程序,通常称它们为USB固件。在一些功能简单的设备里,用一些专用的可编程USB控制器就可以完成USB device功能。而在一些运行了操作系统的复杂的嵌入式系统中,要完成USB device程序,就会要求你不仅熟悉USB device控制器的操作,还要熟悉操作系统的驱动架构。

2.1 主机端

在主机端,有USB HCD和USBD两个接口层。

USB HCD的全称为主机控制器驱动(Host Controller Driver),它是对主机控制器硬件的一个抽象,提供与USB系统软件之间的软件接口。

从客户软件的角度看,USBD控制所有的USB设备,因此客户软件对设备的控制和所要发送的数据只要交给USBD就可以了。USBD为客户软件提供命令机制和管道机制。客户软件通过命令机制可以访问所有设备的0号端点且与默认管道通信,从而实现对设备的配置和其他一些基本的控制工作。管道机制允许客户和设备实现特定的通信功能。该默认管道描述了一条USBD和USB设备间通信的逻辑通道。

主机端各层有以下功能:

  1. 检测连接和移去的USB设备。
  2. 管理主机和USB设备间的数据流。
  3. 连接USB状态和活动统计。
  4. 控制主控制器和USB设备间的电气接口,包括能量供应。

USB核心(USBD)是整个USB驱动的核心部分,从上图可知

  • 一方面USBD对从USB主机控制器接收到的数据进行处理,并传递给上层的设备端驱动软件
  • 同时也接收来自上层的非USB格式数据流,进行相应的数据处理后传递给USB主机控制器驱动。

2.2 设备端

在设备端,Gadget API定义了一个通用的Gadget Driver的接口,Gadget Driver通过Gadget API与底层USB Device Controller Driver通信。其中Gadget API层屏蔽了底层硬件的不同,使Gadget Driver注重功能的实现,尽量与硬件无关。

2.3 硬件结构

集线器(USB Root Hub)端两条数据线(D+D-),都接有15K的下拉电阻,当无设备接入时,集线器数据线D+D-的电压为低电平。当设备接入时,由于设备的数据线上接有1.5K上拉电阻,使得1根数据线被拉高。集线器根据数据线被拉高得知有设备接入,并根据D+为高还是D-为高来判断所接入的设备是全速USB设备(D+为高)还是低速USB设备(D-为高).

3.USB-core代码先梳理

  1 #                                                                                                                                                                                                       
  2 # Makefile for USB Core files and filesystem
  3 #
  4 
  5 usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o
  6 usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
  7 usbcore-y += devio.o notify.o generic.o quirks.o devices.o
  8 usbcore-y += port.o
  9 
 10 usbcore-$(CONFIG_OF)        += of.o
 11 usbcore-$(CONFIG_PCI)       += hcd-pci.o
 12 usbcore-$(CONFIG_ACPI)      += usb-acpi.o
 13 
 14 obj-$(CONFIG_USB)       += usbcore.o
 15 
 16 obj-$(CONFIG_USB_LEDS_TRIGGER_USBPORT)  += ledtrig-usbport.o

3.1 USB Core

主要完成

  • bus_register(USB总线注册)
  • usb_major_init(注册usb主控器字符设备)
  • usb_register(注册usbfs驱动)
  • usb_hub_init(USB Hub初始化,注册hub驱动、创建内核守护线程来监测hub端口的状态变化)

等工作

常用的数据结构:

usb_deivce

struct usb_device {
        int                devnum;
        char                devpath[16];
        u32                route;
        enum usb_device_state        state;
        enum usb_device_speed        speed;
        struct usb_tt        *tt;
        int                ttport;
        unsigned int toggle[2];
        struct usb_device *parent;
        struct usb_bus *bus;
        struct usb_host_endpoint ep0;
        struct device dev;
        struct usb_device_descriptor descriptor;
        struct usb_host_bos *bos;
        struct usb_host_config *config;
        ...
}

usb_bus

struct usb_bus {
        struct device *controller;        /* host/master side hardware */
        int busnum;                        /* Bus number (in order of reg) */
        const char *bus_name;                /* stable id (PCI slot_name etc) */
        u8 uses_dma;                        /* Does the host controller use DMA? */
        u8 uses_pio_for_control;        /*
                                         * Does the host controller use PIO
                                         * for control transfers?
                                         */
        u8 otg_port;                        /* 0, or number of OTG/HNP port */
        unsigned is_b_host:1;                /* true during some HNP roleswitches */
        unsigned b_hnp_enable:1;        /* OTG: did A-Host enable HNP? */
        unsigned no_stop_on_short:1;    /*
                                         * Quirk: some controllers don't stop
                                         * the ep queue on a short transfer
                                         * with the URB_SHORT_NOT_OK flag set.
                                         */
        unsigned no_sg_constraint:1;        /* no sg constraint */
        unsigned sg_tablesize;                /* 0 or largest number of sg list entries */

        int devnum_next;                /* Next open device number in
                                         * round-robin allocation */
        struct mutex devnum_next_mutex; /* devnum_next mutex */

        struct usb_devmap devmap;        /* device address allocation map */
        struct usb_device *root_hub;        /* Root hub */
        ...
}

usb_driver
保存客户驱动信息,包括驱动名称,以及驱动提供给USB内核使用的函数指针

struct usb_driver {
        const char *name;

        int (*probe) (struct usb_interface *intf,
                      const struct usb_device_id *id);

        void (*disconnect) (struct usb_interface *intf);

        int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
                        void *buf);

        int (*suspend) (struct usb_interface *intf, pm_message_t message);
        int (*resume) (struct usb_interface *intf);
        int (*reset_resume)(struct usb_interface *intf);

        int (*pre_reset)(struct usb_interface *intf);
        int (*post_reset)(struct usb_interface *intf);

        const struct usb_device_id *id_table;

        struct usb_dynids dynids;
        struct usbdrv_wrap drvwrap;
        unsigned int no_dynamic_id:1;
        unsigned int supports_autosuspend:1;
        unsigned int disable_hub_initiated_lpm:1;
        unsigned int soft_unbind:1;
};

urb 是进行USB通信的数据结构,USB core通过URB在USB设备类驱动和USB core、USB core和HCD间进行数据传输。

struct urb {
        /* private: usb core and host controller only fields in the urb */
        struct kref kref;                /* reference count of the URB */
        void *hcpriv;                        /* private data for host controller */
        atomic_t use_count;                /* concurrent submissions counter */
        atomic_t reject;                /* submissions will fail */
        int unlinked;                        /* unlink error code */

        /* public: documented fields in the urb that can be used by drivers */
        struct list_head urb_list;        /* list head for use by the urb's
                                         * current owner */
        struct list_head anchor_list;        /* the URB may be anchored */
        struct usb_anchor *anchor;
        struct usb_device *dev;                /* (in) pointer to associated device */
        struct usb_host_endpoint *ep;        /* (internal) pointer to endpoint */
        unsigned int pipe;                /* (in) pipe information */
        unsigned int stream_id;                /* (in) stream ID */
        int status;                        /* (return) non-ISO status */
        unsigned int transfer_flags;        /* (in) URB_SHORT_NOT_OK | ...*/
        void *transfer_buffer;                /* (in) associated data buffer */
        dma_addr_t transfer_dma;        /* (in) dma addr for transfer_buffer */
        struct scatterlist *sg;                /* (in) scatter gather buffer list */
        int num_mapped_sgs;                /* (internal) mapped sg entries */
        int num_sgs;                        /* (in) number of entries in the sg list */
        u32 transfer_buffer_length;        /* (in) data buffer length */
        u32 actual_length;                /* (return) actual transfer length */
        unsigned char *setup_packet;        /* (in) setup packet (control only) */
        dma_addr_t setup_dma;                /* (in) dma addr for setup_packet */
        int start_frame;                /* (modify) start frame (ISO) */
        int number_of_packets;                /* (in) number of ISO packets */
        int interval;                        /* (modify) transfer interval
                                         * (INT/ISO) */
        int error_count;                /* (return) number of ISO errors */
        void *context;                        /* (in) context for completion */
        usb_complete_t complete;        /* (in) completion routine */
        struct usb_iso_packet_descriptor iso_frame_desc[0];
                                        /* (in) ISO ONLY */
};

3.2 USB HCD

硬件主机控制器Host Controller之上运行的是HCD,是对主机控制器硬件的一个抽象,实现核心层与控制器之间的对话接口,USB HCD包含多种USB接口规范:

  • UHCI:Intel提供,通用主机控制接口,USB1.0/1.1;
  • OHCI:微软提供,开放主机控制接口,USB1.0/1.1(1.5 Mbit/s和12 Mbit/s);
  • EHCI:增强主机控制接口,USB2.0(480 Mbit/s);
  • XHCI:架构在设计上支持USB 1/2/3速度,包括USB 3 Gen1(5 Gbit/s)和USB 3 Gen2(10Gbit/s),且只需单个驱动程序堆栈;

3.3 USB设备驱动

USB设备是由一些配置(configuration)、接口(interface)和端点(endpoint)组成,,即一个USB设备可以含有一个或多个配置,在每个配置中可含有一个或多个接口,在每个接口中可含有若干个端点。一个USB设备驱动可能包含多个子驱动。一个USB设备子驱动程序对应一个USB接口,而非整个USB设备。

USB传输的对象为端点(endpoint),每一个端点都有传输类型,传输方向,除了端点0外,每一个端点只支持一个方向的数据传输,端点0用于控制传输,既能输出也能输入。输入(IN)、输出(OUT) "都是" 基于USB主机的立场说的。比如鼠标的数据是从鼠标传到PC机, 对应的端点称为"输入端点"。

USB的传输类型:

  • 控制传输:可靠,时间有保证,比如:USB设备的识别过程
  • 批量传输: 可靠, 时间没有保证, 比如:U盘
  • 中断传输:可靠,实时,比如:USB鼠标
  • 实时传输:不可靠,实时,比如:USB摄像头

针对不同类型的USB设备,需要实现特定的USB驱动程序。如HID(Human Interface Device), 属于人机交互类的设备,如USB鼠标,USB键盘等。该类设备必须遵循HID设计规范。

在Linux内核中,使用 struct usb_driver结构体来描述一个USB驱动,通过usb_register在USB驱动中注册进内核。

 */
struct usb_driver {
        const char *name;

        int (*probe) (struct usb_interface *intf,
                      const struct usb_device_id *id);

        void (*disconnect) (struct usb_interface *intf);

        int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
                        void *buf);

        int (*suspend) (struct usb_interface *intf, pm_message_t message);
        int (*resume) (struct usb_interface *intf);
        int (*reset_resume)(struct usb_interface *intf);

        int (*pre_reset)(struct usb_interface *intf);
        int (*post_reset)(struct usb_interface *intf);

        const struct usb_device_id *id_table;

        struct usb_dynids dynids;
        struct usbdrv_wrap drvwrap;
        unsigned int no_dynamic_id:1;
        unsigned int supports_autosuspend:1;
        unsigned int disable_hub_initiated_lpm:1;
        unsigned int soft_unbind:1;
};
#define        to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)

4.USB驱动中的urb(usb request block)数据结构

https://www.cnblogs.com/pang1567/p/3352999.html

Linux中USB驱动中,USB 代码和所有的 USB 设备通讯使用称为 urb 的东西( USB request block)

一个urb 用来发送或接受数据到或者从一个特定 USB 设备上的特定的 USB 端点, 以一种异步的方式.一个 USB 设备驱动可能分配许多 urb 给一个端点或者可能重用单个 urb 给多个不同的端点, 根据驱动的需要. 设备中的每个端点都处理一个 urb 队列, 以至于多个 urb 可被发送到相同的端点, 在队列清空之前. 一个 urb 的典型生命循环如下:

  1. 被一个 USB 设备驱动创建.
  2. 安排给一个特定 USB 设备的特定端点.
  3. 提交给 USB 核心, 被 USB 设备驱动.
  4. 提交给特定设备的被 USB 核心指定的 USB 主机控制器驱动, .
  5. 被 USB 主机控制器处理, 它做一个 USB 传送到设备.
  6. 当 urb 完成, USB 主机控制器驱动通知 USB 设备驱动.

urb 也可被提交这个 urb 的驱动在任何时间取消, 或者被 USB 核心如果设备被从系统中移出. urb 被动态创建并且包含一个内部引用计数, 使它们在这个 urb 的最后一个用户释放它时被自动释放。

5.USB Monitor工具

  1 #
  2 # Makefile for USB monitor
  3 #
  4 
  5 usbmon-y := mon_main.o mon_stat.o mon_text.o mon_bin.o
  6 
  7 obj-$(CONFIG_USB_MON)   += usbmon.o

usbmon以module形式加载

因此需要在defconfig中支持CONFIG_USB_MON=y

4.1 使用usbmon

  • 确认系统支持debugfs

  • 挂载 debugfs 文件系统
mount -t debugfs none_debugs /sys/kernel/debug
  • 确认内核加载了usbmon模块

目前4.14版本中,usbmon是直接打包到boot.img中

  • 抓取usb总线上的数据

查看连接的usb设备使用一下命令:

ab30a5:/sys/kernel/debug/usb/usbmon # lsusb
Bus 001 Device 001: ID 1d6b:0002
Bus 001 Device 018: ID 2207:0020
Bus 001 Device 016: ID 058f:6254
Bus 002 Device 001: ID 1d6b:0003
Bus 001 Device 017: ID 5448:152e



127|ab30a5:/ # cat /sys/kernel/debug/usb/devices

T:  Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=480  MxCh= 3
B:  Alloc=  0/800 us ( 0%), #Int=  0, #Iso=  5
D:  Ver= 2.00 Cls=09(hub  ) Sub=00 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=1d6b ProdID=0002 Rev= 4.09
S:  Manufacturer=Linux 4.9.113 xhci-hcd
S:  Product=xHCI Host Controller
S:  SerialNumber=xhci-hcd.1.auto
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=  0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   4 Ivl=256ms

T:  Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 16 Spd=480  MxCh= 4
D:  Ver= 2.00 Cls=09(hub  ) Sub=00 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=058f ProdID=6254 Rev= 1.00
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=100mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   1 Ivl=256ms

T:  Bus=01 Lev=02 Prnt=16 Port=02 Cnt=01 Dev#= 18 Spd=480  MxCh= 0
D:  Ver= 2.01 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=2207 ProdID=0020 Rev= 3.10
S:  Manufacturer=rockchip
S:  Product=UVC
S:  SerialNumber=2020
C:* #Ifs= 4 Cfg#= 1 Atr=80 MxPwr=500mA
A:  FirstIf#= 0 IfCount= 2 Cls=e0(wlcon) Sub=01 Prot=03
A:  FirstIf#= 2 IfCount= 2 Cls=0e(video) Sub=03 Prot=00
I:* If#= 0 Alt= 0 #EPs= 1 Cls=e0(wlcon) Sub=01 Prot=03 Driver=rndis_host
E:  Ad=82(I) Atr=03(Int.) MxPS=   8 Ivl=32ms
I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host
E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 2 Alt= 0 #EPs= 1 Cls=0e(video) Sub=01 Prot=00 Driver=uvcvideo
E:  Ad=83(I) Atr=03(Int.) MxPS=  16 Ivl=16ms
I:  If#= 3 Alt= 0 #EPs= 0 Cls=0e(video) Sub=02 Prot=00 Driver=uvcvideo
I:* If#= 3 Alt= 1 #EPs= 1 Cls=0e(video) Sub=02 Prot=00 Driver=uvcvideo
E:  Ad=84(I) Atr=05(Isoc) MxPS=3072 Ivl=125us

T:  Bus=01 Lev=02 Prnt=16 Port=03 Cnt=02 Dev#= 17 Spd=12   MxCh= 0
D:  Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=5448 ProdID=152e Rev= 0.01
S:  Manufacturer=SmartIC
S:  Product=SmartIC Audio Device
S:  SerialNumber=00142204022019000
C:* #Ifs= 3 Cfg#= 1 Atr=80 MxPwr=200mA
I:* If#= 0 Alt= 0 #EPs= 0 Cls=01(audio) Sub=01 Prot=00 Driver=snd-usb-audio
I:* If#= 1 Alt= 0 #EPs= 0 Cls=01(audio) Sub=02 Prot=00 Driver=snd-usb-audio
I:  If#= 1 Alt= 1 #EPs= 1 Cls=01(audio) Sub=02 Prot=00 Driver=snd-usb-audio
E:  Ad=81(I) Atr=05(Isoc) MxPS= 512 Ivl=1ms
I:* If#= 2 Alt= 0 #EPs= 2 Cls=03(HID  ) Sub=00 Prot=00 Driver=usbhid
E:  Ad=82(I) Atr=03(Int.) MxPS=  64 Ivl=1ms
E:  Ad=03(O) Atr=03(Int.) MxPS=  64 Ivl=1ms

T:  Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=5000 MxCh= 0
B:  Alloc=  0/800 us ( 0%), #Int=  0, #Iso=  0
D:  Ver= 3.00 Cls=09(hub  ) Sub=00 Prot=03 MxPS= 9 #Cfgs=  1
P:  Vendor=1d6b ProdID=0003 Rev= 4.09
S:  Manufacturer=Linux 4.9.113 xhci-hcd
S:  Product=xHCI Host Controller
S:  SerialNumber=xhci-hcd.1.auto
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=  0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=(none)
E:  Ad=81(I) Atr=03(Int.) MxPS=   4 Ivl=256ms
ab30a5:/ #

查看 /sys/kernel/debug/usb/usbmon 目录,发现该目录下有以下内容:0s、0u、1s、1t、1u、2s、2t、2u,其中1代表 bus1,2代表 bus2,0代表所有 usb 总线。

执行

cat /sys/kernel/debug/usb/usbmon/0u > /data/usb-bus.data

保存usb总线上的数据

4.2 usb数据包格式解析

usb 总线上传输的数据是以包为基本单位的,但是不能随意的使用包来传输数据,必须按照一定的关系把这些不同的包组织成事务(transaction)进行传输。

数据包类型可以分为

  • 令牌包

令牌包用来发起一次 usb 传输,因为 usb 是主从结构的拓扑结构,所有的数据传输都是由主机发起的,设备只能被动的响应,这就需要主机发送一个令牌包来通知哪个设备进行响应,如何响应。令牌包有 4 种,分别为输出(OUT)、输入(IN)、建立(SETUP)和帧起始(SOF)。

  • 数据包

顾名思义,数据包就是用来传输数据的,可以从主机到设备,也可以从设备到主机,方向由令牌包来指定。

  • 握手包

握手包的发送者一般为数据接收者,用来表示一个传输是否被对方确认。在传输正常的情况下,主机/设备会发送一个表示传输正确的 ACK 握手包

  • 特殊包

特殊包用在一些特殊的场合,这里就不介绍了

参考数据这块的解析:详解usbmon抓取的log各字段的含义 - Hengs - 博客园

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值