Linux Kernel USB 子系统(1)

| 分类 Kernel  | 标签 linux  kernel  usb 

这里的排版很乱,不知道怎么搞的,这里的好一些: http://yangyingchao.github.io/posts/26968.html

学习一下 Linux 下的 USB 相关的一些知识,做个记录。  

1 USB 结构和 USB 基础

1.1 USB 的结构

USB 总线有且仅有一个总控制器 (root controller),其他的设备通过 hub 连接到总线上, 形成了一个树形结构,最多可连接 127 个设备。 该结构如下图所示:

./images/usb_structer.png

USB 结构

刚才提到,终端设备必要时需要通过 Hub 连接到 root controller 上, 为了让 USB 设备的驱动对这个结构有一个统一的描述,内核将总控制器模拟成了一个虚拟 hub , 这样在驱动开发中可以不用区分总控制器和普通 hub, 简化了驱动开发过程。

此外,尽管 USB 子系统的各个设备在物理上是一个树形结构,但对于 USB 设备的驱动来讲, 驱动并不需要关心这种树形结构:无论一个设备是直接连到总控制器上或是通过 hub 连接, 该设备都会由内核分配一个唯一的标识符(设备 ID),驱动和内核通过这个 ID 来指代这个设备, 从而从逻辑上看,可以认为这些设备都是直接连到了总控制器上,如下图所示:

./images/usb_structer_logical.png

逻辑上的 USB 结构

1.2 相关术语

当在 USB 下谈到设备 (device) 时,我们需要多加注意, 区分下面几个术语: 设备 (device)、功能 (function)、配置 (configration) 、接口 (interfaces) 和端点 (End Points)。

  • device 可以是连接到 USB 总线上的任何东西,比如摄像头、U 盘等等。 而每一个设备,又可能由多个功能单元组成,从而提供多个功能 (function) 。 每个功能单元,都可能有各自的驱动。
  • 每个设备都提供了一套或者多套配置 (Configuration) 。配置决定了设备的总体特性,例如, 一个设备可能提供了两套配置:一个用于使用外部电源时,另外一个用于由 USB 总线供电时。
  • 每个配置由若干接口 (interfaces) 组成,每个接口都可以提供不同的设置 (Setting) 选项。 以摄像头为例,一个摄像头可能提供三个接口:一个是使能麦克,一个使能摄像头,或者两个都使能。 使用不同的接口,可能会导致设备占用的带宽的不同。
  • 每个接口都通过驱动来控制若干端点 (EndPoints)。

1.3 USB 设备的分类

USB 设备根据其用于,可以分为若干类。 Linux 内核中,按照这些分类来组织驱动,可参考 drivers/usb/Readme :

  • core/ : USB 子系统的核心代码,包括 usbfs 文件以及 hub 类 (class) 的驱动
  • host/ : USB host controller 驱动,包括 UHCI, OHCI, EHCI 等。
  • gadget/ : USB 外部控制器驱动, 以及与外部控制器交互的若干部件的驱动。
  • image/ : 图像相关驱动,如扫描仪、数码相机等。
  • ../input/ : 输入相关驱动,如键盘、鼠标,触摸屏等。
  • ../media/ : 多媒体驱动,如摄像头等。
  • ../net/ : 网卡驱动。
  • serial/ : USB/串口转接器的驱动。
  • storage/: 存储相关,如 U 盘。

1.4 USB 传输模式

USB 标准规定了四种不同的传输模式:控制传输 (Control transfer), 批量传输 (Bulk transfer), 中断传输 (Interrupt transfer) 和等时传输 (Isochronous transfer)。

  • 控制传输:

    控制传输用来在对设备进行配置的时候,传输控制信息, 占用的带宽较小。 该过程中控制信息通过预先定义的命令 (Commands) 来表示,这些命令包括 GET_STATUS, SET_INTERFACE 等等, 这些命令可以在 USB 标准中查到。 Linux 内核代码中,这些命令都是预先定义的,以 USB_REQ_ 开头的变量, 位于 include/usb/ch9.h 中。 例如:

    /*  * Standard requests, for the bRequest field of a SETUP packet.  *   * These are qualified by the bRequestType field, so that for example   * TYPE_CLASS or TYPE_VENDOR specific feature flags could be retrieved   * by a GET_STATUS request.   */ #define USB_REQ_GET_STATUS      0x00 #define USB_REQ_CLEAR_FEATURE       0x01 #define USB_REQ_SET_FEATURE     0x03 #define USB_REQ_SET_ADDRESS     0x05 #define USB_REQ_GET_DESCRIPTOR      0x06 #define USB_REQ_SET_DESCRIPTOR      0x07 

    USB Standard 中仅定义了所有设备都需要支持的的命令的最小集, 在此之外,厂商还可以根据自己的设备来自行添加所需的命令。

  • 批量传输 (Bulk transfer:)

    批量传输一般用于数据传输,该传输可能会占用所有的带宽。 这种操作下,数据的传输必需在由总线保证的安全情况下进行, 换句话说, 一块数据必需不变地传输到目的设备上。 U 盘和扫描仪等设备都大量的使用批量传输。

  • 中断传输 (Interrupt transfer)

    中断传输与批量传输类似,但是它会定期的发生。 两次中断传输的间隔,在一定的范围内,可以由驱动自行设置。

  • 等时传输 (Isochronous transfer)

    等时传输是几种传输模式中比较特殊的一种。 这种模式下,数据的传输采用了一个固定的、预定义的带宽。 该传输适用于需要持续的数据流,同时可以容忍偶尔的数据丢失的情况下, 比如摄像头。

2 USB 子系统

2.1 USB 子系统任务及其双层结构

USB 子系统主要任务包括:

  1. 注册和管理设备驱动。
  2. 为 USB 设备寻找驱动,并初始化和配置设备。
  3. 在内核中表现设备的树形结构。
  4. 与设备交互。

在 Linux 内核中, 该完成这些任务的子系统由两层构成。

  • Host Controller 驱动

    即主控制器驱动。 控制器向 USB 链提供连接选项,并向终端设备提供电路上的通讯。 控制器本身则必需连接到其他系统总线上。 当前, USB 控制器主要可分为 OHCI, EHCI 和 UHCI 几类。

  • 终端设备驱动

    设备驱动负责与各个 USB 设备打交道,并将该设备的功能提供给内核的其他部分。 设备驱动通过标准化的接口来与 host controller 通讯,从而分离了控制器类型和设备驱动。

2.2 相关数据结构

这些数据结构围绕 USB 总线展开,两端分别是 USB 设备和 USB 驱动。

2.2.1 USB 总线定义

对内核来讲, USB 总线也是一个设备, 内核中通过 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 sg_tablesize;      /* 0 or largest number of sg list entries */        int devnum_next;        /* Next open device number in                       * round-robin allocation */        struct usb_devmap devmap;   /* device address allocation map */      struct usb_device *root_hub;    /* Root hub */      struct usb_bus *hs_companion;   /* Companion EHCI bus, if any */      struct list_head bus_list;  /* list of busses */        int bandwidth_allocated;    /* on this bus: how much of the time                       * reserved for periodic (intr/iso)                       * requests is used, on average?                       * Units: microseconds/frame.                       * Limits: Full/low speed reserve 90%,                       * while high speed reserves 80%.                       */      int bandwidth_int_reqs;     /* number of Interrupt requests */      int bandwidth_isoc_reqs;    /* number of Isoc. requests */    #ifdef CONFIG_USB_DEVICEFS      struct dentry *usbfs_dentry;    /* usbfs dentry entry for the bus */  #endif    #if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)      struct mon_bus *mon_bus;    /* non-null when associated */      int monitored;          /* non-zero when monitored */  #endif  };  

其中:

  • busnum & bus_name:

    用于在全局中唯一标识该总线:前者为用于注册是使用的 Number , 后者为描述性的字符串。

  • controller:

    由于总线也是一个设备, usb_bus 中通过做为通用设备模型的 controller 来表明构成该总线的设备实例。

  • bus_list:

    一个表头,用于将该 usb_bus 添加到全局的链表中,便于管理。

  • root_hub:

    root_hub 实际上是一个虚拟出来的实例(参考第一小节),用于表示树形结构的根。

  • devmap:

    一个 bitmap , 用于记录该总线上已经分配了哪些 USB Number, 还有剩余。

2.2.2 Device Side

内核中通过 usb_device 来表示一个 USB 设备,其定义如下:

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_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;      u8 level;        unsigned can_submit:1;      unsigned persist_enabled:1;      unsigned have_langid:1;      unsigned authorized:1;      unsigned authenticated:1;      unsigned wusb:1;      int string_langid;        /* static strings from the device */      char *product;      char *manufacturer;      char *serial;        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[USB_MAXCHILDREN];        u32 quirks;      atomic_t urbnum;        unsigned long active_duration;    #ifdef CONFIG_PM      unsigned long connect_time;        unsigned do_remote_wakeup:1;      unsigned reset_resume:1;  #endif      struct wusb_dev *wusb_dev;      int slot_id;  };  

其中:

  • devnum:

    Device Number, 该设备号为该设备在 USB 总线上的地址, 在整个 USB 树形结构中是全局唯一的。

  • state & speed:

    表示设备的状态和速度,具体参考下面的定义:

    enum usb_device_speed {      USB_SPEED_UNKNOWN = 0,          /* enumerating */      USB_SPEED_LOW, USB_SPEED_FULL,      /* usb 1.1 */      USB_SPEED_HIGH,             /* usb 2.0 */      USB_SPEED_WIRELESS,         /* wireless (usb 2.5) */      USB_SPEED_SUPER,            /* usb 3.0 */  };    enum usb_device_state {      /* NOTATTACHED isn't in the USB spec, and this state acts       * the same as ATTACHED ... but it's clearer this way.       */      USB_STATE_NOTATTACHED = 0,        /* chapter 9 and authentication (wireless) device states */      USB_STATE_ATTACHED,      USB_STATE_POWERED,          /* wired */      USB_STATE_RECONNECTING,         /* auth */      USB_STATE_UNAUTHENTICATED,      /* auth */      USB_STATE_DEFAULT,          /* limited function */      USB_STATE_ADDRESS,      USB_STATE_CONFIGURED,           /* most functions */        USB_STATE_SUSPENDED        /* NOTE:  there are actually four different SUSPENDED       * states, returning to POWERED, DEFAULT, ADDRESS, or       * CONFIGURED respectively when SOF tokens flow again.       * At this level there's no difference between L1 and L2       * suspend states.  (L2 being original USB 1.1 suspend.)       */  };  
  • devpath:

    表示了该设备在 USB 树形结构中的位置。

  • parent:

    设备挂在的 hub 。

  • bus:

    表明了该设备位于那个总线上。 usb_bus 的介绍在前面小节。

其他的成员变量,可以参考该结构定义之前的注释。

2.2.3 Driver Side

USB 设备驱动通过结构 usb_driver 来表示。 usb_driver 结构是 USB 设备与 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 soft_unbind:1;  };  

其中:

  • name

    驱动名字,全局唯一,一般用于管理用途。

  • drvwrap

    该结构是 Linux 内核模型中通用驱动的一个包装,定义如下:

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

    其中,

    • driver 是通用驱动
    • for_devices 用于区分该驱动是接口驱动还是设备驱动:
      • for_devices = 0: 接口驱动
      • for_devices = 1: 设备驱动
  • id_table

    与 pci_driver 类似, USB 设备驱动通过 id_table 来表明该驱动所能驱动的设备。

    其定义如下:

    struct usb_device_id {      /* which fields to match against? */      __u16       match_flags;        /* Used for product specific matches; range is inclusive */      __u16       idVendor;      __u16       idProduct;      __u16       bcdDevice_lo;      __u16       bcdDevice_hi;        /* Used for device class matches */      __u8        bDeviceClass;      __u8        bDeviceSubClass;      __u8        bDeviceProtocol;        /* Used for interface class matches */      __u8        bInterfaceClass;      __u8        bInterfaceSubClass;      __u8        bInterfaceProtocol;        /* not matched against */      kernel_ulong_t  driver_info;  };  

    其中的 match_flags 是一个 flag , 用于说明在确定某一设备是否该由该驱动进行管理时, 应该比较的成员变量。

  • probe 与其他函数指针

    若干操作函数。其中, probe 会在为设备找到驱动或者为驱动找到了设备后调用。


上一篇     下一篇