usb总线系统介绍

一、特性和运作模式

1. USB标准的四个版本:

(1)USB1.0版本:

          USB总线协议的第一个版本。

(2)USB1.1版本:

          此版本普及了USB总线协议,大多数硬件都采用了该版本的标准。

(3)USB2.0版本:

          此版本提升了USB总线的最大传输数率,由USB1.1的12Mb/s提高到了480Mb/s。

(4)USB3.0版本:

          此版本进一步提高了USB总线的传输速率,并且支持OTG功能。


2. USB标准的四种不同的传输模式:

(1)控制传输(control transfer)

          控制传输涉及传输所需的控制信息,用于设备的初始配置。此类通信必须安全可靠,但只需要较窄的带宽。其

   中通过预定义的令牌传输各种控制命令,USB标准定义了令牌的符号名称和语义,如GET_STATUS、

   SET_INTERFACE等。在内核源代码这些令牌都在<usb.h>中声明为预处理器常数,其前缀为USQ_REQ_,以防止

   名称冲突。标准强制要求了一个命令的最小集合,所有设备都必须支持这些命令。但厂商可以随意添加其他特定于

   设备的命令,厂商提供的驱动程序必须能够理解/使用这些命令。

(2)块传输(bulk transfer)

          块传输按数据包发送数据,可以占据总线的全部带宽。在这种模式下,数据传输的安全性由总线保证。换句话

   说,发送的数据总是原样到达其目的地。扫描仪或大容量存储器之类的设备会使用这种模式。

(3)中断传输(interrupt transfer)

          中断传输类似于块传输,但按一定的周期重复。驱动程序可以自由地定义周期长度(在一定的限度内)。网卡

   和类似设备会优先选择使用这种传输模式。

(4)同步传输(isochronous transfer)

          同步传输具有特殊作用,它是能够使用固定的预定义带宽的唯一方法(尽管不可靠)。在某些方面,这种模式

   可以与网卡的数据报技术类比。在需要确保连续数据流,而能够容忍偶尔数据丢失的情况下,该传输模式是最适用

   的。使用这种模式的一个主要的例子就是网络摄像头,该设备通过USB总线发送视频数据。



二、驱动程序的管理

1. 内核中按两个层次实现USB总线系统

(1)宿主机适配器的驱动程序必须是可用的。该适配器必须为USB链提供链接选项,并承担与终端设备的电子通

   信。适配器自身必须连接到另一个系统总线(当前,有3种不同宿主机适配器类型,分别称之为OHCI、EHCI、

   UHCI,这些涵盖了市售的所有控制器类型)。

(2)设备驱动程序与各个USB设备通信,并将设备的功能导出到内核的其他部分,进而到用户空间。这些驱动程序

   与宿主机控制器通过一种标准化接口交互,因而控制器类型与USB驱动程序是不相关的。任何其他方法显然都是不

   切实际的,因为需要为每个USB设备开发与宿主机控制器相关的驱动程序。


2. USB子系统的四项主要任务

(1)注册和管理现存的设备驱动程序

(2)为USB设备查找适当的驱动程序,以及初始化和配置

(3)在内核内存中表示设备树

(4)与设备通信(交换数据)


3. 数据结构

usb_driver是USB设备驱动程序和内核其余部分(特别是USB层)之间协作的起始点。

<usb.h>

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 soft_unbind:1;
};

name字段用于日常管理。name是驱动程序的名称,在内核中必须是唯一的(通常使用模块的文件名)。

在这里,通常嵌入的driver对象隐藏在另一个结构体中。

<usb.h>

struct usbdrv_wrap {
        struct device_driver driver;
        int for_devices;
};

       上面这个数据结构使得可以区分接口驱动程序(for_devices为0)和设备驱动程序。

函数指针probe和disconnect很重要,二者与id_table共同构成了USB子系统热插拔能力的支柱。在宿主机适配器检测

到新设备插入时,即发起一个探测过程,以查找适当的设备驱动程序。

       内核接下来遍历设备树的所有结点,确定是否有驱动程序与该设备相关。当然,这里预先假定了该设备尚未分配驱动程序。如果已经分配了驱动程序,则跳过该设备。

       内核首先扫描驱动程序支持的所有设备列表,即id_table。在找到设备和表项的匹配之后,则调用特定于驱动程

序的probe函数,执行进一步的检查和初始化工作。如果设备ID和驱动程序提供的列表之间无法匹配,则内核不会调

用probe,而是跳到下一个驱动程序。


ID表由以下结构的几个实例组成,该结构通过几个ID描述了USB设备:

<mod_devicetable.h>

struct usb_device_id {
        /*  针对哪些字段进行匹配  */
        __u16           match_flags;

        /*  用于特定产品的匹配,范围包含边界在内 */
        __u16           idVendor;
        __u16           idProduct;
        __u16           bcdDevice_lo;
        __u16           bcdDevice_hi;

        /*  用于设备类别的匹配  */
        __u8            bDeviceClass;
        __u8            bDeviceSubClass;
        __u8            bDeviceProtocol;

        /*  用于接口类别的匹配 */
        __u8            bInterfaceClass;
        __u8            bInterfaceSubClass;
        __u8            bInterfaceProtocol;

        /* not matched against */
        kernel_ulong_t  driver_info;
};

       match_flags用于指定将该结构的哪些字段与设备数据比较,为此定义了各种预处理器常数。

例如,USB_DEVICE_ID_MATCH_VENDOR表示检查idVendor字段,USB_DEVICE_ID_MATCH_DEV_PROTOCOL

指示内核检查bDeviceProtocol字段。

       不仅在新设备添加到系统时,会建立驱动程序和设备之间的关联。在加载新驱动程序时,也会如此。起始点是

usb_register例程,在注册新USB驱动程序时必须调用它。



三、设备树的表示

下面的数据结构描述了USB设备树以及内核中各种设备的特征。

<usb.h>

struct usb_device {
        int             devnum;                                           /*  在USB总线上的地址  */

        char            devpath[16];                                   /*  用于消息中:/port/port/...  */

        enum usb_device_state   state;                        /*  已配置、未连接,等等  */

        enum usb_device_speed   speed;                    /*  high/full/low speed (or error)  */

        unsigned int toggle[2];                                       /*  每个比特位表示一个终点(0表示接入,1表示断开) */

        struct usb_device *parent;                                 /*  所在的集线器,如果为根结点,则为NULL  */

        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;                          /* 所有配置 */

        struct usb_host_config *actconfig;                      /* 当前活动配置  */

        struct usb_host_endpoint *ep_in[16];

        struct usb_host_endpoint *ep_out[16];


        char **rawdescriptors;

        unsigned short bus_mA;

        u8 portnum;                                                         /*  父结点端口号(从1开始)  */

        u8 level;

....
        /* 静态字符串,来自设备 */
        char *product;                                                     /*  iProduct字符串,如果有的话  */

        char *manufacturer;                                            /*  iManufacturer字符串,如果有的话  */

        char *serial;                                                         /*  iSerialNumber字符串,如果有的话  */

        struct list_head filelist;
#ifdef CONFIG_USB_DEVICE_CLASS
        struct device *usb_classdev;
#endif
#ifdef CONFIG_USB_DEVICEFS
        struct dentry *usbfs_dentry;
#endif

        int maxchild;
        struct usb_device **children;
....
};

结构体成员变量说明:

a. devnum保存了该设备的唯一编号(在整个USB树中全局唯一)。

    state和speed表示设备的状态(已连接、已配置,等等)和速度。

b. devpath指定了该设备在USB树的拓扑结构中的位置。

    从根结点移动到保存在各个数组项中的设备,必须遍历所有集线器的端口号。

c. parent指向该设备附接的集线器的数据结构,而bus指向总线对应的数据结构。

    两个字段提供了有关USB链拓扑结构的信息。

d. dev建立了与通用设备模型的关联。

e. descriptor将描述USB设备的特征数据集到一个数据结构中(包括厂商ID、产品ID、设备类别等信息)。

f. actconfig指向设备的当前配置,而congfig列出了所有可能的配置。

g. usbfs_entry用于连接到USB文件系统,通常装载在/proc/bus/usb中,提供从用户空间访问设备的入口。

h. product、manufacturer和serial指向一组ASCII字符串,分别是产品名称、生产商和设备序列号,

    这些都由硬件自身提供。

i. 如果当前设备是集线器,还有两个相关的成员:

    maxchild指定了集线器的端口数目(可以附接的设备数目),children是一个指针数组,包含了指向对应

    usb_device实例的指针。这两个成员定义了USB树的拓扑结构。


总线链表中的各个元素,由以下数据结构表示:

<usb.h>

struct usb_bus {
        struct device *controller;                 /*  主机端硬件控制器 */

        int busnum;                                     /*  总线编号(按注册顺序)*/

        const char *bus_name;                   /*  稳定的id(PCI slot_name等) */

        u8 uses_dma;                                  /*  主机控制器是否使用DMA  */
....
        struct usb_devmap devmap;            /*  设备地址分配位图  */

        struct usb_device *root_hub;           /*  根集线器  */

        struct list_head bus_list;                  /*  用作总线链表的链表元素  */

....
#ifdef CONFIG_USB_DEVICEFS
        struct dentry *usbfs_dentry;             /*  总线在usbfs中的dentry项  */
#endif
};

       该数据结构有两个成员可以唯一地标示该总线。busnum是一个整数,在总线注册时按顺序分配,bus_name是一

个指向短字符串的指针,其中保存了一个唯一的名称。controller是一个指向device实例的指针,对应于实现了该总线

的硬件设备。

       不仅设备会出现在上文提到的USB文件系统中,总线也一样。因而usb_bus还必须包含一个指向dentry实例的指

针,用于简历与该虚拟文件系统的必要的关联。

       数据结构中间有几个重要的成员,这些成员将各个可用的总线彼此连接起来,也连接了其上附接的设备。它们还

提供了一个到底层宿主机控制器的标准化连接,向USB层剩余的部分提供了控制器的抽象:

a. bus_list是一个链表元素,用于将所有usb_bus实例连接到一个链表中管理。

b. root_hub是一个指针,指向(虚拟)根集线器的数据结构,表示总线设备树的根结点。

c. devmap是一个位图,长度(最少)为128个比特位。它用于跟踪哪些USB编号已经分配,哪些仍然是空闲的。

    每个USB设备在插入时都分配了一个唯一的整数编号,标准规定一个总线上最多可附接128个设备。


       为与底层的硬件控制器通信,使用了USB请求块(USB request block, URB)。在与USB设备的所有可能形式的传输中,都使用URB交换数据。

drivers/usb/core/hcd.h

int usb_hcd_submit_urb(struct urb * urb, gfp_t mem_flags);

你可能感兴趣的:(linux,usb,hci,bus,roothub)