Linux Kernel USB 子系统(1)
这里的排版很乱,不知道怎么搞的,这里的好一些: http://tubocurarine.is-programmer.com/posts/26968.html
学习一下 Linux 下的 USB 相关的一些知识,做个记录。
Table of Contents
1 USB 结构和 USB 基础
1.1 USB 的结构
USB 总线有且仅有一个总控制器 (root controller),其他的设备通过 hub 连接到总线上, 形成了一个树形结构,最多可连接 127 个设备。 该结构如下图所示:
USB 结构
刚才提到,终端设备必要时需要通过 Hub 连接到 root controller 上, 为了让 USB 设备的驱动对这个结构有一个统一的描述,内核将总控制器模拟成了一个虚拟 hub , 这样在驱动开发中可以不用区分总控制器和普通 hub, 简化了驱动开发过程。
此外,尽管 USB 子系统的各个设备在物理上是一个树形结构,但对于 USB 设备的驱动来讲, 驱动并不需要关心这种树形结构:无论一个设备是直接连到总控制器上或是通过 hub 连接, 该设备都会由内核分配一个唯一的标识符(设备 ID),驱动和内核通过这个 ID 来指代这个设备, 从而从逻辑上看,可以认为这些设备都是直接连到了总控制器上,如下图所示:
逻辑上的 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 子系统主要任务包括:
- 注册和管理设备驱动。
- 为 USB 设备寻找驱动,并初始化和配置设备。
- 在内核中表现设备的树形结构。
- 与设备交互。
在 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 会在为设备找到驱动或者为驱动找到了设备后调用。