USB基础知识2

USB基础知识2

相关术语

  • #include <linux/usb.h>
    所有和 USB 相关的头文件. 它必须被所有的 USB 设备驱动包含。
  • struct usb_driver
    描述 USB 驱动的结构。
  • struct usb_device_id
    描述这个驱动支持的 USB 设备的结构
  • int usb_register(struct usb_driver *d)
    用来从 USB 核心注册和注销一个 USB 驱动的函数。
  • struct usb_device *interface_to_usbdev(struct usb_interface *intf)
    struct usb_interface 获取控制 struct usb_device *
  • struct usb_device
    控制完整 USB 设备的结构
  • struct usb_interface
    主 USB 设备结构, 所有的 USB 驱动用来和 USB 核心通讯的
  • void usb_set_intfdata(struct usb_interface *intf, void *data)
    void *usb_get_intfdata(struct usb_interface *intf)
    设置和获取在 struct usb_interface 中的私有数据指针部分的函数。
  • struct usb_class_driver
    描述 USB 驱动的一个结构, 这个驱动要使用 USB 主编号来和用户空间程序通讯。
  • int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
    void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver)
    用来注册和注销一个特定 struct usb_interface * 结构到 struct usb_class_driver 结构的函数。
  • struct urb
    描述一个 USB 数据传输的结构。
  • struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
    void usb_free_urb(struct urb *urb)
    用来创建和销毁一个 struct usb urb*的函数。
  • int usb_submit_urb(struct urb *urb, int mem_flags)
    int usb_kill_urb(struct urb *urb)
    int usb_unlink_urb(struct urb *urb)
    用来启动和停止一个 USB 数据传输的函数
  • 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, void *context, int interval)
    void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete, void *context)
    void usb_fill_control_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, unsigned char *setup_packet, void *transfer_buffer, int buffer_ length, usb_complete_t complete, void *context)
    用来在被提交给 USB 核心之前初始化一个 struct urb 的函数。
  • int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
    int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout)
    用来发送和接受 USB 数据的函数, 不必使用一个 struct urb

1. Linux USB驱动层次

USB 驱动位于不同的内核子系统(块, 网络, 字符, 等等)和硬件控制器之
间. USB 核心提供了一个接口给 USB 驱动用来存取和控制 USB 硬件, 而不必担心出现在系统中的不同的 USB 硬件控制器.。
在这里插入图片描述

1.1 主机侧与设备侧USB驱动

USB 采用树形拓扑结构,主机侧和设备侧的 USB 控制器分别称为主机控制器(Host Controller)和 USB 设备控制器(UDC),每条总线上只有一个主机控制器,负责协调主机和设备间的通信,而设备不能主动向主机发送任何消息。

在 Linux 系统中,USB 驱动可以从两个角度去观察,一个角度是主机侧,一个角度是设备侧。

Linux USB驱动总体结构
从主机侧的观念去看,在 Linux 驱动中,USB 驱动处于最底层的是USB 主机控制器硬件,在其之上运行的是 USB 主机控制器驱动,主机控制器之上为 USB 核心层,再上层为 USB 设备驱动层(插入主机上的 U 盘、鼠标、USB 转串口等设备驱动)。因此,在主机侧的层次结构中,要实现的 USB 驱动包括两类:USB 主机控制器驱动和 USB 设备驱动,前者控制插入其中的 USB 设备,后者控制 USB 设备如何与主机通信。Linux 内核 USB 核心负责 USB 驱动管理和协议处理的主要工作。主机控制器驱动和设备驱动之间的 USB 核心非常重要,其功能包括:通过定义一些数据结构、宏和功能函数,向上为设备驱动提供编程接口,向下为 USB 主机控制器驱动提供编程接口;通过全局变量维护整个系统的 USB 设备信息;完成设备热插拔控制、总线数据传输控制等。

从Device侧看,Linux 内核中 USB 设备侧驱动程序分为 3 个层次:UDC 驱动程序、Gadget API 和 Gadget 驱动程序。UDC 驱动程序直接访问硬件,控制 USB 设备和主机间的底层通信,向上层提供与硬件相关操作的回调函数。当前 Gadget API 是 UDC 驱动程序回调函数的简单包装。Gadget 驱动程序具体控制 USB 设备功能的实现,使设备表现出“网络连接”、“打印机” 或“USB Mass Storage”等特性,它使用 Gadget API 控制 UDC 实现上述功能。Gadget API 把下层的 UDC 驱动程序和上层的 Gadget 驱动程序隔离开,使得在 Linux 系统中编写 USB 设备侧驱动程序时能够把功能的实现和底层通信分离。

1.2 设备、配置、接口、端点

USB 设备的逻辑组织中,包含设备、配置、接口和端点 4 个层次。

在这里插入图片描述

  • 一个USB设备通常有一个或多个配置,但在同一时刻只能有一个配置;
  • 一个配置通常有一个或多个接口;
  • 一个接口通常有一个或多个端点;
  • 接口有零或多个端点。

每个 USB 设备都提供了不同级别的配置信息,可以包含一个或多个配置,不同的配置使设备表现出不同的功能组合(在探测/连接期间需从其中选定一个),配置由多个接口组成。

在 USB 协议中,接口由多个端点组成,代表一个基本的功能,是 USB 设备驱动程序控制的对象,一个功能复杂的 USB 设备可以具有多个接口。每个配置中可以有多个接口,而设备接口是端点的汇集(collection)。例如,USB 扬声器可以包含一个音频接口以及对旋钮和按钮的接口。一个配置中的所有接口可以同时有效,并可被不同的驱动程序连接。每个接口可以有备用接口,以提供不同质量的服务参数。

端点是 USB 通信的最基本形式,每一个 USB 设备接口在主机看来就是一个端点的集合。主机只能通过端点与设备进行通信,以使用设备的功能。在 USB 系统中每一个端点都有惟一的地址,这是由设备地址和端点号给出的(枚举时候分配的地址)。每个端点都有一定的属性,其中包括传输方式、总线访问频率、带宽、端点号和数据包的最大容量等。一个 USB 端点只能在一个方向承载数据,或者从主机到设备(称为输出端点),或者从设备到主机(称为输入端点),因此端点可看作一个单向的管道。端点 0 通常为控制端点,用于设备初始化参数等。只要设备连接到 USB 上并且上电端点 0 就可以被访问。端点 1、2 等一般用作数据端点,存放主机与设备间往来的数据。

1.3 USB描述符

在这里插入图片描述

对应上述USB设备的构成,USB采用描述符来描述USB设备的属性,在USB协议的第九章(chaper 9)中,有对USB描述符的详细说明,在Linux驱动的以下文件中,定义了USB描述符的结构体,文件名直接命名为ch9.h。

USB描述符的总体功能如下:

  • 其实所有的描述符都是USB设备用来描述自己属性及用途的,所以必须在设备端实现对应的描述符,主机会在枚举此设备的时候根据设备实现的描述符去确定设备到底是一个什么样的设备、设备需要的总线资源、和设备的通讯方式等等。
  • 每一个USB设备只有一个设备描述符,主要向主机说明设备类型、端点0最大包长、设备版本、配置数量等等。
  • 每一个USB设备至少有一个或者多个配置描述符,但是主机同一时间只能选择某一种配置,标准配置描述符主要向主机描述当前配置下的设备属性、所需电流、支持的接口数、配置描述符集合长度等等。
  • 主机在获取配置描述符集合的时候会先获取一次标准配置描述符,然后根据里面的配置描述符集合长度属性值获取配置描述符集合的所有描述符信息,配置描述符集合有标准配置描述符、接口描述符、端点描述符、HID描述符。
  • 每一个USB配置下至少有一个或者多个接口描述符,接口描述符主要说明设备类型、此接口下使用的端点数(不包括0号号端点),一个接口就是实现一种功能,实现这种功能可能需要端点0就够了,可能还需要其它的端点配合。
  • 每一个USB接口下至少有0个或者多个端点描述符,端点描述符用来描述符端点的各种属性。
  • 端点是实现USB设备功能的物理缓冲区实体,USB主机和设备是通过端点进行数据交互的。
  • 一个USB设备有一个或多个配置描述符。每个配置有一个或多个接口,每个接口有零个或多个端点。
  • 字符串描述符就是用字符串描述一个设备的一些属性,描述的属性包括设备厂商名字、产品名字、产品序列号、各个配置名字、各个接口名字。
  • HID描述符只有HID设备才会存在。
  • HID设备至少有一个报告描述符。
  • 报告描述符主要作用就是描述主机和HID设备交互的数据,向主机说明这些数据中哪些位是用来做什么用的。
1.3.1 USB 描述符类型

USB规范定义了不同的描述符,如设备描述符为1,配置描述符为2,字符串描述符为3,接口描述符为4,端点描述符为5…,,他们的类别编号定义如下:

  • USB2.0规范定义的描述符类型

    描述符类型
    DEVICE:设备描述符1
    CONFIGURATION:配置描述符2
    STRING:字符串描述符3
    INTERFACE:接口描述符4
    ENDPOINT:端点描述符5
    DEVICE_QUALIFIER:限定描述符6
    OTHER_SPEED_CONFIGURATION:其它速率配置描述符7
    INTERFACE_POWER:接口功率描述符8
    HID:HID描述符0x21
    REPORT_DESCIPTOR:报表描述符0x22
  • USB3.2规范定义描述符类型

描述符类型
DEVICE1
CONFIGURATION2
STRING3
INTERFACE4
ENDPOINT5
Reserved6
Reserved7
INTERFACE_POWER18
OTG9
DEBUG10
INTERFACE_ASSOCIATION11
BOS15
DEVICE CAPABILITY16
SUPERSPEED_USB_ENDPOINT_COMPANION48
SUPERSPEEDPLUS_ISOCHRONOUS_ENDPOINT_COMPANION49
  • 描述符总结
    • 接口描述符:只要记录接口编号,接口的端点数,接口所使用的类子类协议。
    • 端点描述符:端点号,方向,端点的传输类型,最大包长度,查询时间间隔。
    • 字符串描述符:提供一些方便人们查阅的信息不是必须的。
1.3.2 USB设备描述符布局

USB设备通过配置描述符,接口描述符,端点描述符等描述符实现设备功能。

USB设备灵活多变的角色转换是通过USB的配置描述符来实现的,但一个USB设备有多个配置描述符时,就表明了这个USB设备支持多角的转换,不过一次只能有一个USB配置描述符有效。当设备在接入PC时,主机端发现设备存在多配置功能时,会弹窗让用户选择一种工作状态。

一个USB配置描述符可以有多个USB接口描述符。在USB规范定义中,一个接口代表着该USB设备当前支持的一种功能,其实现是通过其接口描述符下的端点描述符中的端点来完成。通常对设备功能的操作,就是主机通过端点描述符进行数据交互。

在一般的情况下,这种设备功能的函数接口与端点操作一一对应,但是USB也定义了一种复杂设备,这种复杂设备是需要多个接口联合来实现某一种设备功能,所以将多个接口联合接来的描述又叫接口关联描述符。如UVC摄像头设备,UAC音频设备这种复杂设备,其都是通过接口关联描述来整合该USB设备的音视频流功能。

  • 简单的USB设备功能
    一般一个接口代表着该设备的一个独立功能。
    在这里插入图片描述
  • 复杂USB设备功能
    复杂设备功能是由多接口通过接口关联描述符组合完成USB设备的功能。
    在这里插入图片描述
    在上述设备中,配置0具有两个接口: Interface 0 和 Interface 1。 接口0具有三个替代设置(alternate setting 接口)。 在任何给定时间,只有一个备用设置处于活动状态。 请注意,替代设置0不使用终结点,而备用设置1和2使用终结点1。 通常,视频摄像机使用 同步端点 进行流式处理。 对于这种类型的端点,在使用端点时,会在总线上保留带宽。 当照相机未流式传输视频时,客户端驱动程序可以选择备用设置0来节省带宽。 当网络摄像机是流式传输视频时,客户端驱动程序可以切换为备用设置1或备用设置2,这将提供更高的质量级别并消耗增加的总线带宽。 接口1具有两个替代设置。 类似于接口0,备用设置0不使用终结点。 替换设置1定义为使用终结点1。

端点不能在配置中的两个接口之间共享。 设备使用端点地址来确定数据传输或端点操作的目标端点,如管道重置。 所有这些操作都由主机启动。

  • USB设备描述符关系
    在这里插入图片描述
1.3.1 USB设备描述符usb_device_descriptor

关于设备的通用信息,如供应商 ID、产品 ID 和修订 ID,支持的设备类、子类和适用的协议以及默认端点的最大包大小等。在 Linux 内核中,USB 设备用usb_device 结构体来描述,USB 设备描述符定义为 usb_device_descriptor 结构体

/* USB_DT_DEVICE: Device descriptor */

struct usb_device_descriptor {

__u8 bLength; //该描述符结构体大小(18字节)

__u8 bDescriptorType; //描述符类型(本结构体中固定为0x01)

__le16 bcdUSB; //USB 版本号

__u8 bDeviceClass; //设备类代码(由USB官方分配)

__u8 bDeviceSubClass; //子类代码(由USB官方分配)

__u8 bDeviceProtocol; //设备协议代码(由USB官方分配)

__u8 bMaxPacketSize0; //端点0的最大包大小(有效大小为8,16,32,64)

__le16 idVendor; //生产厂商编号(由USB官方分配)

__le16 idProduct; //产品编号(制造厂商分配)

__le16 bcdDevice; //设备出厂编号

__u8 iManufacturer; //设备厂商字符串索引

__u8 iProduct; //产品描述字符串索引

__u8 iSerialNumber; //设备序列号字符串索引

__u8 bNumConfigurations; //当前速度下能支持的配置数量

} __attribute__ ((packed));
1.3.2 USB配置描述符结构体usb_config_descriptor

配置描述符定义了设备的配置信息,一个设备可以有多个配置描述符。
大部分的USB设备只有一个配置描这符,设备存在多个配置描述符时,配置描述符指定设备的供电方式、最大功耗是多少、它拥有的接口数量。因此,可以有两种配置,一种用于设备由总线供电时,另一种用于设备供电时。由于这是接口描述符的“头”,因此使一种配置使用与另一种配置不同的传输模式也是可行的。

一旦主机检查了所有配置,主机将发送具有非零值的 SetConfiguration 命令,该值与其中一个配置的bConfigurationValue 匹配。这用于选择所需的配置。
在这里插入图片描述

读取配置描述符时,它会返回整个配置层次结构,其中包括所有相关的接口和端点描述符。

wTotalLength字段反映配置描述符层次结构中的字节数。

配置描述符在USB设备的枚举过程中,需要获取两次:
第一次只获取配置描这符的基本长度9字节,获取后从wTotalLength字节中解析出配置描述符的总长度,然后再次获取全部的描述符。

配置描述数据中不仅包括配置描述符本身,也包括其下的接口描述符和端点描述符。

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.3 USB接口描述符结构体usb_interface_descriptor

接口描述符一般是按设备的运行状态分类,这样设备可以在不同的接口中切换来实现设备运行状态的切换,如在设备在运行状态下使用正常的接口描述符,在停止状态下使用该接口描述符的备用接口描述符。

struct usb_interface_descriptor {

__u8 bLength; //该描述符结构大小

__u8 bDescriptorType; //接口描述符的类型编号(0x04)

__u8 bInterfaceNumber; //接口描述符的类型编号(0x04)

__u8 bAlternateSetting; //接口描述符的类型编号(0x04)

__u8 bNumEndpoints; //该接口使用的端点数,不包括端点0

__u8 bInterfaceClass; //接口类型

__u8 bInterfaceSubClass; //接口子类型

__u8 bInterfaceProtocol; //接口遵循的协议

__u8 iInterface; //描述该接口的字符串索引值

} __attribute__ ((packed));
1.3.4 USB 接口关联描述符usb_interface_assoc_descriptor

对于复合USB设备的接口描述符,可以在每个类(Class)要合并的接口描述符之前加一个接口关联描述符(Interface Association Descriptor,IAD),其作用就是把多个接口定义成一个类设备,即多个接口作用于一个设备。

/* USB_DT_INTERFACE_ASSOCIATION: groups interfaces */
struct usb_interface_assoc_descriptor {
	__u8  bLength;
	__u8  bDescriptorType;

	__u8  bFirstInterface;
	__u8  bInterfaceCount;
	__u8  bFunctionClass;
	__u8  bFunctionSubClass;
	__u8  bFunctionProtocol;
	__u8  iFunction;
} __attribute__ ((packed));

#define USB_DT_INTERFACE_ASSOCIATION_SIZE	8
偏移量字段长度描符
0bLength1接口关联描述符的长度
1bDescriptorType1描述符类型为接口关联描述符
2bFirstInterface1该接口关联描述符所关联的第一个接口号
3bInterfaceCount1该接口关联描述符所拥有的连续接口数
4bFunctionClass1接口关联描述符的功能所实现的USB类
5bFunctionSubClass1接口关联描述符所实现的USB子类
6bFunctionProtocol1接口关联描述符所实现的USB协议
7iFunction1功能的字符串索引
  • 接口关联描述符与其它描述符的关系图
    在这里插入图片描述
1.3.5 USB端点描述符结构体usb_endpoint_descriptor

USB设备中的每个端点都有自己的端点描述符,由接口描述符中的bNumEndpoint决定其数量。
端点地址、方向和类型,支持的最大包大小,如果是中断类型的端点则还包括轮询频率。在 Linux 内核中,USB 端点使用 usb_host_endpoint 结构体来描述,USB端点描述符定义为usb_endpoint_descriptor 结构体

struct usb_endpoint_descriptor {

__u8 bLength; //端点描述符字节数大小(7个字节)

__u8 bDescriptorType; //端点描述符类型编号(0x05)

__u8 bEndpointAddress; //端点地址及输入输出属性

__u8 bmAttributes; //端点的传输类型属性

__le16 wMaxPacketSize; //端点收、发的最大包大小

__u8 bInterval; //主机查询端点的时间间隔

/* NOTE: these two are _only_ in audio endpoints. */

/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */

__u8 bRefresh; //声卡用到的变量

__u8 bSynchAddress;

} __attribute__ ((packed));
  • bmAttributes端点属性.Bit7-2,保留(同步有定义).BIt1-0:00控制,01同步,02批量,03中断.

    当为同步传输时,bEndpointType的bit3-2的值不同代表的含义不同:
    00:无同步
    01:异步
    10:适配
    11:同步
    BIT5:4
    00: 表示数据端点
    01:表示反馈端点Feedback endpoint
    10:表示隐式反馈数据端点 Implicit feedback Data endpoint
    11:保留

  • wMaxPacketSize : 本端点接收或发送的最大信息包大小.
    USB2.0时:

    对于同步端点,此值用于指示主机在调度中保留的总线时间,这是每(微)帧数据有效负载所需的时间,有效负载时间就是发送一帧数据需要占用的总线时间,在实际数据传输过程中,管道实际使用的带宽可能比保留的带宽少,大家想想,如果实际使用的带宽比保留的还多,那就丢数了;
    对于其类型的端点,bit10~bit0指定最大数据包大小(以字节为单位);
    bit12bit11对于高速传输的同步和中断端点有效:bit12bit11可指定每个微帧的额外通信次数,这里大家一定要知道是在高速传输中,当一个事务超时时,在一个微帧时间内重传的次数,如果设置为00b(None),则表示在一个微帧内只传输一个事务,不进行额外的超时重传,如果设置为01b,则表示在一个微帧内可以传输两次事务,有一次额外的重传机会,从下面可以看出,一个微帧最多可以有两次重传事务的机会,如果微帧结束了还是失败,就需要等到下一个微帧继续发送该事务;

    USB3.0时:wMaxPacketSize表示包的大小。对于bulk为1024,而对于同步传输,可以为0~1024或1024。

  • bInterval: 轮训数据传送端点的时间间隔.对于批量传送和控制传送的端点忽略.对于同步传送的端点,必须为1,对于中断传送的端点,范围为1-255.

    对于全速/高速同步端点,此值必须在1到16之间。bInterval值用作2的指数,例如bInterval为4,表示周期为8个单位;
    对于全速/低速中断端点,该字段的值可以是1到255,也就是主机多少ms给设备发一次数据请求;
    对于高速中断端点,使用bInterval值作为2的指数,例如bInterval为4表示周期为8。这个值必须在1到16之间;
    对于高速批量/控制输出端点,bInterval必须指定端点的最大NAK速率。值0表示端点永不NAK。其它值表示每个微帧的bInterval*125us时间最多1个NAK。这个值的范围必须在0到255之间;
    00 = None (1 transaction per microframe)
    01 = 1 additional (2 per microframe)
    10 = 2 additional (3 per microframe)
    11 = Reserved
    其它位默认为0,
    对于全速/低速批量/控制输出端点,此值无意义,可以任意指定。

1.3.6 USB字符串描述符usb_string_descriptor

在其他描述符中会为某些字段提供字符串索引,它们可被用来检索描述性字符串,可以以多种语言形式提供。字符串描述符是可选的,有的设备有,有的设备没有,如果不支持字符串描述符,其设备描述符,配置描述符,接口描述符内的所有字符串描述符索引都必须为0。字符串描述符对应于 usb_string_descriptor 结构体

 struct usb_string_descriptor {
 _ _u8 bLength; /* 描述符长度 */
 _ _u8 bDescriptorType; /* 描述符类型 */
 
 _ _le16 wData[1]; /* 以 UTF-16LE 编码 */
 } _ _attribute_ _ ((packed));

例如在主机上插入一个U盘,通过 lsusb -v 命令得到这个 U 盘相关的描述符,从中可以显示这个 U 盘包含了一个设备描述符、一个配置描述符、一个接口描述符以及批量输入和批量输出两个端点描述符。呈现出来的信息内容直接对应于 usb_device_descriptorusb_config_ descriptor usb_interface_descriptorusb_endpoint_descriptorusb_string_descriptor 结构体,如下所示:

root@user-PC:# lsusb -v 

Bus 002 Device 008: ID 0781:5591 SanDisk Corp. Ultra Flair
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               3.20
  bDeviceClass            0 
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         9
  idVendor           0x0781 SanDisk Corp.
  idProduct          0x5591 Ultra Flair
  bcdDevice            1.00
  iManufacturer           1  USB
  iProduct                2  SanDisk 3.2Gen1
  iSerial                 3 050167cd44537596472a2a652b5f25433401ec0adefce423260d173342730ff35503000000000000000000000090e08aff180e10915581072e2a30ee
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x002c
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              896mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         8 Mass Storage
      bInterfaceSubClass      6 SCSI
      bInterfaceProtocol     80 Bulk-Only
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0400  1x 1024 bytes
        bInterval               0
        bMaxBurst               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0400  1x 1024 bytes
        bInterval               0
        bMaxBurst              15
Binary Object Store Descriptor:
  bLength                 5
  bDescriptorType        15
  wTotalLength       0x0016
  bNumDeviceCaps          2
  USB 2.0 Extension Device Capability:
    bLength                 7
    bDescriptorType        16
    bDevCapabilityType      2
    bmAttributes   0x00000002
      HIRD Link Power Management (LPM) Supported
  SuperSpeed USB Device Capability:
    bLength                10
    bDescriptorType        16
    bDevCapabilityType      3
    bmAttributes         0x00
    wSpeedsSupported   0x000e
      Device can operate at Full Speed (12Mbps)
      Device can operate at High Speed (480Mbps)
      Device can operate at SuperSpeed (5Gbps)
    bFunctionalitySupport   1
      Lowest fully-functional device speed is Full Speed (12Mbps)
    bU1DevExitLat          10 micro seconds
    bU2DevExitLat         256 micro seconds
can't get debug descriptor: Resource temporarily unavailable
Device Status:     0x0000
  (Bus Powered)

2. USB主机控制器驱动

2.1 USB主机控制器驱动的整体结构

USB 主机控制器有 3 种规格:OHCI (Open Host Controller Interface)、UHCI (Universal Host
Controller Interface) 和 EHCI (Enhanced Host Controller Interface)。OHCI 驱动程序用来为非 PC 系统上以及带有 SiS 和 ALi 芯片组的 PC 主板上的 USB 芯片提供支持。UHCI 驱动程序多用来为大多数其他 PC 主板(包括 Intel 和 Via)上的 USB 芯片提供支持。EHCI 由 USB 2.0 规范所提出,它兼容于 OHCI 和 UHCI。UHCI 的硬件线路比 OHCI 简单,所以成本较低,但需要较复杂的驱动程序,CPU 负荷稍重。本节将重点介绍嵌入式系统中常用的 OHCI 主机控制器驱动。

2.1.1 主机控制器驱动usb_hcd

Linux 内核中,用 usb_hcd 结构体描述 USB 主机控制器驱动,它包含 USB 主机控制器的“家务”信息、硬件资源、状态描述和用于操作主机控制器的 hc_driver

struct usb_hcd {

	/*
	 * housekeeping
	 */
	struct usb_bus		self;		/* hcd is-a bus */
	struct kref		kref;		/* reference counter */

	const char		*product_desc;	/* product/vendor string */
	int			speed;		/* Speed for this roothub.
						 * May be different from
						 * hcd->driver->flags & HCD_MASK
						 */
	char			irq_descr[24];	/* driver + bus # */

	struct timer_list	rh_timer;	/* drives root-hub polling 驱动根 hub 的 polling*/
	struct urb		*status_urb;	/* the current status urb 目前的状态 urb*/
#ifdef CONFIG_PM
	struct work_struct	wakeup_work;	/* for remote wakeup */
#endif

	/*
	 * hardware info/state
	 * 硬件信息/状态
	 */
	const struct hc_driver	*driver;	/* hw-specific hooks 硬件特定的钩子函数 */

	/*
	 * OTG and some Host controllers need software interaction with phys;
	 * other external phys should be software-transparent
	 */
	struct usb_phy		*usb_phy;
	struct usb_phy_roothub	*phy_roothub;

	/* Flags that need to be manipulated atomically because they can
	 * change while the host controller is running.  Always use
	 * set_bit() or clear_bit() to change their values.
	 */
	unsigned long		flags;
#define HCD_FLAG_HW_ACCESSIBLE		0	/* at full power */
#define HCD_FLAG_POLL_RH		2	/* poll for rh status? */
#define HCD_FLAG_POLL_PENDING		3	/* status has changed? */
#define HCD_FLAG_WAKEUP_PENDING		4	/* root hub is resuming? */
#define HCD_FLAG_RH_RUNNING		5	/* root hub is running? */
#define HCD_FLAG_DEAD			6	/* controller has died? */
#define HCD_FLAG_INTF_AUTHORIZED	7	/* authorize interfaces? */
#define HCD_FLAG_DEV_AUTHORIZED		8	/* authorize devices? */

	/* The flags can be tested using these macros; they are likely to
	 * be slightly faster than test_bit().
	 */
#define HCD_HW_ACCESSIBLE(hcd)	((hcd)->flags & (1U << HCD_FLAG_HW_ACCESSIBLE))
#define HCD_POLL_RH(hcd)	((hcd)->flags & (1U << HCD_FLAG_POLL_RH))
#define HCD_POLL_PENDING(hcd)	((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING))
#define HCD_WAKEUP_PENDING(hcd)	((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING))
#define HCD_RH_RUNNING(hcd)	((hcd)->flags & (1U << HCD_FLAG_RH_RUNNING))
#define HCD_DEAD(hcd)		((hcd)->flags & (1U << HCD_FLAG_DEAD))

	/*
	 * Specifies if interfaces are authorized by default
	 * or they require explicit user space authorization; this bit is
	 * settable through /sys/class/usb_host/X/interface_authorized_default
	 */
#define HCD_INTF_AUTHORIZED(hcd) \
	((hcd)->flags & (1U << HCD_FLAG_INTF_AUTHORIZED))

	/*
	 * Specifies if devices are authorized by default
	 * or they require explicit user space authorization; this bit is
	 * settable through /sys/class/usb_host/X/authorized_default
	 */
#define HCD_DEV_AUTHORIZED(hcd) \
	((hcd)->flags & (1U << HCD_FLAG_DEV_AUTHORIZED))

	/* Flags that get set only during HCD registration or removal. */
	unsigned		rh_registered:1;/* is root hub registered? */
	unsigned		rh_pollable:1;	/* may we poll the root hub? */
	unsigned		msix_enabled:1;	/* driver has MSI-X enabled? */
	unsigned		msi_enabled:1;	/* driver has MSI enabled? */
	/*
	 * do not manage the PHY state in the HCD core, instead let the driver
	 * handle this (for example if the PHY can only be turned on after a
	 * specific event)
	 */
	unsigned		skip_phy_initialization:1;

	/* The next flag is a stopgap, to be removed when all the HCDs
	 * support the new root-hub polling mechanism. 
	 * 下一个标志的采用只是“权益之计”,当所有 HCDs 支持新的根 Hub 轮询机制后将移除*/
	unsigned		uses_new_polling:1;
	unsigned		wireless:1;	/* Wireless USB HCD 无线 USB HCD?*/
	unsigned		has_tt:1;	/* Integrated TT in root hub 根 hub 集成了 TT?*/
	unsigned		amd_resume_bug:1; /* AMD remote wakeup quirk */
	unsigned		can_do_streams:1; /* HC supports streams */
	unsigned		tpl_support:1; /* OTG & EH TPL support */
	unsigned		cant_recv_wakeups:1;
			/* wakeup requests from downstream aren't received */

	unsigned int		irq;		/* irq allocated */
	void __iomem		*regs;		/* device memory/io 设备内存或 I/O*/
	resource_size_t		rsrc_start;	/* memory/io resource start 内存或 I/O 资源开始位置*/
	resource_size_t		rsrc_len;	/* memory/io resource length 内存或 I/O 资源大小*/
	unsigned		power_budget;	/* in mA, 0 = no limit */

	struct giveback_urb_bh  high_prio_bh;
	struct giveback_urb_bh  low_prio_bh;

	/* bandwidth_mutex should be taken before adding or removing
	 * any new bus bandwidth constraints:
	 *   1. Before adding a configuration for a new device.
	 *   2. Before removing the configuration to put the device into
	 *      the addressed state.
	 *   3. Before selecting a different configuration.
	 *   4. Before selecting an alternate interface setting.
	 *
	 * bandwidth_mutex should be dropped after a successful control message
	 * to the device, or resetting the bandwidth after a failed attempt.
	 */
	struct mutex		*address0_mutex;
	struct mutex		*bandwidth_mutex;
	struct usb_hcd		*shared_hcd;
	struct usb_hcd		*primary_hcd;


#define HCD_BUFFER_POOLS	4
	struct dma_pool		*pool[HCD_BUFFER_POOLS];

	int			state;
#	define	__ACTIVE		0x01
#	define	__SUSPEND		0x04
#	define	__TRANSIENT		0x80

#	define	HC_STATE_HALT		0
#	define	HC_STATE_RUNNING	(__ACTIVE)
#	define	HC_STATE_QUIESCING	(__SUSPEND|__TRANSIENT|__ACTIVE)
#	define	HC_STATE_RESUMING	(__SUSPEND|__TRANSIENT)
#	define	HC_STATE_SUSPENDED	(__SUSPEND)

#define	HC_IS_RUNNING(state) ((state) & __ACTIVE)
#define	HC_IS_SUSPENDED(state) ((state) & __SUSPEND)

	/* more shared queuing code would be good; it should support
	 * smarter scheduling, handle transaction translators, etc;
	 * input size of periodic table to an interrupt scheduler.
	 * (ohci 32, uhci 1024, ehci 256/512/1024).
	 */

	/* The HC driver's private data is stored at the end of
	 * this structure.
	 * 主机控制器驱动的私有数据
	 */
	unsigned long hcd_priv[0]
			__attribute__ ((aligned(sizeof(s64))));
};

usb_hcd 中的 hc_driver 成员非常重要,它包含具体的用于操作主机控制器的钩子函数,其定
义:

struct hc_driver {
	const char	*description;	/* "ehci-hcd" etc */
	const char	*product_desc;	/* product/vendor string */
	size_t		hcd_priv_size;	/* size of private data */

	/* irq handler */
	irqreturn_t	(*irq) (struct usb_hcd *hcd);

	int	flags;
#define	HCD_MEMORY	0x0001		/* HC regs use memory (else I/O) */
#define	HCD_LOCAL_MEM	0x0002		/* HC needs local memory */
#define	HCD_SHARED	0x0004		/* Two (or more) usb_hcds share HW */
#define	HCD_USB11	0x0010		/* USB 1.1 */
#define	HCD_USB2	0x0020		/* USB 2.0 */
#define	HCD_USB25	0x0030		/* Wireless USB 1.0 (USB 2.5)*/
#define	HCD_USB3	0x0040		/* USB 3.0 */
#define	HCD_USB31	0x0050		/* USB 3.1 */
#define	HCD_USB32	0x0060		/* USB 3.2 */
#define	HCD_MASK	0x0070
#define	HCD_BH		0x0100		/* URB complete in BH context */

	/* called to init HCD and root hub */
	int	(*reset) (struct usb_hcd *hcd);
	int	(*start) (struct usb_hcd *hcd);

	/* NOTE:  these suspend/resume calls relate to the HC as
	 * a whole, not just the root hub; they're for PCI bus glue.
	 */
	/* called after suspending the hub, before entering D3 etc 
		挂起 Hub 后,进入 D3(etc)前被调用*/
	int	(*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup);

	/* called after entering D0 (etc), before resuming the hub 
		在进入 D0(etc)后,恢复 Hub 前调用*/
	int	(*pci_resume)(struct usb_hcd *hcd, bool hibernated);

	/* cleanly make HCD stop writing memory and doing I/O 
		使 HCD 停止写内存和进行 I/O 操作*/
	void	(*stop) (struct usb_hcd *hcd);

	/* shutdown HCD */
	void	(*shutdown) (struct usb_hcd *hcd);

	/* return current frame number 
		返回目前的帧数*/
	int	(*get_frame_number) (struct usb_hcd *hcd);

	/* manage i/o requests, device state 
		管理 I/O 请求和设备状态*/
	int	(*urb_enqueue)(struct usb_hcd *hcd,
				struct urb *urb, gfp_t mem_flags);
	int	(*urb_dequeue)(struct usb_hcd *hcd,
				struct urb *urb, int status);

	/*
	 * (optional) these hooks allow an HCD to override the default DMA
	 * mapping and unmapping routines.  In general, they shouldn't be
	 * necessary unless the host controller has special DMA requirements,
	 * such as alignment contraints.  If these are not specified, the
	 * general usb_hcd_(un)?map_urb_for_dma functions will be used instead
	 * (and it may be a good idea to call these functions in your HCD
	 * implementation)
	 */
	int	(*map_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb,
				   gfp_t mem_flags);
	void    (*unmap_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb);

	/* hw synch, freeing endpoint resources that urb_dequeue can't 
		释放 endpoint 资源*/
	void	(*endpoint_disable)(struct usb_hcd *hcd,
			struct usb_host_endpoint *ep);

	/* (optional) reset any endpoint state such as sequence number
	   and current window */
	void	(*endpoint_reset)(struct usb_hcd *hcd,
			struct usb_host_endpoint *ep);

	/* root hub support */
	int	(*hub_status_data) (struct usb_hcd *hcd, char *buf);
	int	(*hub_control) (struct usb_hcd *hcd,
				u16 typeReq, u16 wValue, u16 wIndex,
				char *buf, u16 wLength);
	int	(*bus_suspend)(struct usb_hcd *);
	int	(*bus_resume)(struct usb_hcd *);
	int	(*start_port_reset)(struct usb_hcd *, unsigned port_num);
	unsigned long	(*get_resuming_ports)(struct usb_hcd *);

		/* force handover of high-speed port to full-speed companion */
	void	(*relinquish_port)(struct usb_hcd *, int);
		/* has a port been handed over to a companion? */
	int	(*port_handed_over)(struct usb_hcd *, int);

		/* CLEAR_TT_BUFFER completion callback */
	void	(*clear_tt_buffer_complete)(struct usb_hcd *,
				struct usb_host_endpoint *);

	/* xHCI specific functions */
		/* Called by usb_alloc_dev to alloc HC device structures */
	int	(*alloc_dev)(struct usb_hcd *, struct usb_device *);
		/* Called by usb_disconnect to free HC device structures */
	void	(*free_dev)(struct usb_hcd *, struct usb_device *);
	/* Change a group of bulk endpoints to support multiple stream IDs */
	int	(*alloc_streams)(struct usb_hcd *hcd, struct usb_device *udev,
		struct usb_host_endpoint **eps, unsigned int num_eps,
		unsigned int num_streams, gfp_t mem_flags);
	/* Reverts a group of bulk endpoints back to not using stream IDs.
	 * Can fail if we run out of memory.
	 */
	int	(*free_streams)(struct usb_hcd *hcd, struct usb_device *udev,
		struct usb_host_endpoint **eps, unsigned int num_eps,
		gfp_t mem_flags);

	/* Bandwidth computation functions */
	/* Note that add_endpoint() can only be called once per endpoint before
	 * check_bandwidth() or reset_bandwidth() must be called.
	 * drop_endpoint() can only be called once per endpoint also.
	 * A call to xhci_drop_endpoint() followed by a call to
	 * xhci_add_endpoint() will add the endpoint to the schedule with
	 * possibly new parameters denoted by a different endpoint descriptor
	 * in usb_host_endpoint.  A call to xhci_add_endpoint() followed by a
	 * call to xhci_drop_endpoint() is not allowed.
	 */
		/* Allocate endpoint resources and add them to a new schedule */
	int	(*add_endpoint)(struct usb_hcd *, struct usb_device *,
				struct usb_host_endpoint *);
		/* Drop an endpoint from a new schedule */
	int	(*drop_endpoint)(struct usb_hcd *, struct usb_device *,
				 struct usb_host_endpoint *);
		/* Check that a new hardware configuration, set using
		 * endpoint_enable and endpoint_disable, does not exceed bus
		 * bandwidth.  This must be called before any set configuration
		 * or set interface requests are sent to the device.
		 */
	int	(*check_bandwidth)(struct usb_hcd *, struct usb_device *);
		/* Reset the device schedule to the last known good schedule,
		 * which was set from a previous successful call to
		 * check_bandwidth().  This reverts any add_endpoint() and
		 * drop_endpoint() calls since that last successful call.
		 * Used for when a check_bandwidth() call fails due to resource
		 * or bandwidth constraints.
		 */
	void	(*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
		/* Returns the hardware-chosen device address */
	int	(*address_device)(struct usb_hcd *, struct usb_device *udev);
		/* prepares the hardware to send commands to the device */
	int	(*enable_device)(struct usb_hcd *, struct usb_device *udev);
		/* Notifies the HCD after a hub descriptor is fetched.
		 * Will block.
		 */
	int	(*update_hub_device)(struct usb_hcd *, struct usb_device *hdev,
			struct usb_tt *tt, gfp_t mem_flags);
	int	(*reset_device)(struct usb_hcd *, struct usb_device *);
		/* Notifies the HCD after a device is connected and its
		 * address is set
		 */
	int	(*update_device)(struct usb_hcd *, struct usb_device *);
	int	(*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int);
	/* USB 3.0 Link Power Management */
		/* Returns the USB3 hub-encoded value for the U1/U2 timeout. */
	int	(*enable_usb3_lpm_timeout)(struct usb_hcd *,
			struct usb_device *, enum usb3_link_state state);
		/* The xHCI host controller can still fail the command to
		 * disable the LPM timeouts, so this can return an error code.
		 */
	int	(*disable_usb3_lpm_timeout)(struct usb_hcd *,
			struct usb_device *, enum usb3_link_state state);
	int	(*find_raw_port_number)(struct usb_hcd *, int);
	/* Call for power on/off the port if necessary */
	int	(*port_power)(struct usb_hcd *hcd, int portnum, bool enable);

};

其中int (*urb_enqueue)(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);中的urb_enqueue函数十分关键,实际上,上层通过 usb_submit_urb()提交 1 个 USB 请求后,该函数调用 usb_hcd_submit_urb(),并最终调用至 usb_hcd 的 driver 成员(hc_driver 类型)的 urb_enqueue()

在 Linux 内核中,使用如下函数来创建 HCD:

struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
		struct device *sysdev, struct device *dev, const char *bus_name,
		struct usb_hcd *primary_hcd)
{
	struct usb_hcd *hcd;

	hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
	if (!hcd)
		return NULL;
	if (primary_hcd == NULL) {
		hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex),
				GFP_KERNEL);
		if (!hcd->address0_mutex) {
			kfree(hcd);
			dev_dbg(dev, "hcd address0 mutex alloc failed\n");
			return NULL;
		}
		mutex_init(hcd->address0_mutex);
		hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
				GFP_KERNEL);
		if (!hcd->bandwidth_mutex) {
			kfree(hcd->address0_mutex);
			kfree(hcd);
			dev_dbg(dev, "hcd bandwidth mutex alloc failed\n");
			return NULL;
		}
		mutex_init(hcd->bandwidth_mutex);
		dev_set_drvdata(dev, hcd);
	} else {
		mutex_lock(&usb_port_peer_mutex);
		hcd->address0_mutex = primary_hcd->address0_mutex;
		hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;
		hcd->primary_hcd = primary_hcd;
		primary_hcd->primary_hcd = primary_hcd;
		hcd->shared_hcd = primary_hcd;
		primary_hcd->shared_hcd = hcd;
		mutex_unlock(&usb_port_peer_mutex);
	}

	kref_init(&hcd->kref);

	usb_bus_init(&hcd->self);
	hcd->self.controller = dev;
	hcd->self.sysdev = sysdev;
	hcd->self.bus_name = bus_name;
	hcd->self.uses_dma = (sysdev->dma_mask != NULL);

	timer_setup(&hcd->rh_timer, rh_timer_func, 0);
#ifdef CONFIG_PM
	INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif

	hcd->driver = driver;
	hcd->speed = driver->flags & HCD_MASK;
	hcd->product_desc = (driver->product_desc) ? driver->product_desc :
			"USB Host Controller";
	return hcd;
}
EXPORT_SYMBOL_GPL(__usb_create_hcd);

如下函数被用来增加和移除 HCD:

/**
 * usb_add_hcd - finish generic HCD structure initialization and register
 * @hcd: the usb_hcd structure to initialize
 * @irqnum: Interrupt line to allocate
 * @irqflags: Interrupt type flags
 *
 * Finish the remaining parts of generic HCD initialization: allocate the
 * buffers of consistent memory, register the bus, request the IRQ line,
 * and call the driver's reset() and start() routines.
 */
int usb_add_hcd(struct usb_hcd *hcd,
		unsigned int irqnum, unsigned long irqflags)
{
	int retval;
	struct usb_device *rhdev;

	if (!hcd->skip_phy_initialization && usb_hcd_is_primary_hcd(hcd)) {
		hcd->phy_roothub = usb_phy_roothub_alloc(hcd->self.sysdev);
		if (IS_ERR(hcd->phy_roothub))
			return PTR_ERR(hcd->phy_roothub);

		retval = usb_phy_roothub_init(hcd->phy_roothub);
		if (retval)
			return retval;

		retval = usb_phy_roothub_power_on(hcd->phy_roothub);
		if (retval)
			goto err_usb_phy_roothub_power_on;
	}

	dev_info(hcd->self.controller, "%s\n", hcd->product_desc);

	/* Keep old behaviour if authorized_default is not in [0, 1]. */
	if (authorized_default < 0 || authorized_default > 1) {
		if (hcd->wireless)
			clear_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
		else
			set_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
	} else {
		if (authorized_default)
			set_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
		else
			clear_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
	}
	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);

	/* per default all interfaces are authorized */
	set_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags);

	/* HC is in reset state, but accessible.  Now do the one-time init,
	 * bottom up so that hcds can customize the root hubs before hub_wq
	 * starts talking to them.  (Note, bus id is assigned early too.)
	 */
	retval = hcd_buffer_create(hcd);
	if (retval != 0) {
		dev_dbg(hcd->self.sysdev, "pool alloc failed\n");
		goto err_create_buf;
	}

	retval = usb_register_bus(&hcd->self);
	if (retval < 0)
		goto err_register_bus;

	rhdev = usb_alloc_dev(NULL, &hcd->self, 0);
	if (rhdev == NULL) {
		dev_err(hcd->self.sysdev, "unable to allocate root hub\n");
		retval = -ENOMEM;
		goto err_allocate_root_hub;
	}
	mutex_lock(&usb_port_peer_mutex);
	hcd->self.root_hub = rhdev;
	mutex_unlock(&usb_port_peer_mutex);

	rhdev->rx_lanes = 1;
	rhdev->tx_lanes = 1;

	switch (hcd->speed) {
	case HCD_USB11:
		rhdev->speed = USB_SPEED_FULL;
		break;
	case HCD_USB2:
		rhdev->speed = USB_SPEED_HIGH;
		break;
	case HCD_USB25:
		rhdev->speed = USB_SPEED_WIRELESS;
		break;
	case HCD_USB3:
		rhdev->speed = USB_SPEED_SUPER;
		break;
	case HCD_USB32:
		rhdev->rx_lanes = 2;
		rhdev->tx_lanes = 2;
		/* fall through */
	case HCD_USB31:
		rhdev->speed = USB_SPEED_SUPER_PLUS;
		break;
	default:
		retval = -EINVAL;
		goto err_set_rh_speed;
	}

	/* wakeup flag init defaults to "everything works" for root hubs,
	 * but drivers can override it in reset() if needed, along with
	 * recording the overall controller's system wakeup capability.
	 */
	device_set_wakeup_capable(&rhdev->dev, 1);

	/* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is
	 * registered.  But since the controller can die at any time,
	 * let's initialize the flag before touching the hardware.
	 */
	set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);

	/* "reset" is misnamed; its role is now one-time init. the controller
	 * should already have been reset (and boot firmware kicked off etc).
	 */
	if (hcd->driver->reset) {
		retval = hcd->driver->reset(hcd);
		if (retval < 0) {
			dev_err(hcd->self.controller, "can't setup: %d\n",
					retval);
			goto err_hcd_driver_setup;
		}
	}
	hcd->rh_pollable = 1;

	/* NOTE: root hub and controller capabilities may not be the same */
	if (device_can_wakeup(hcd->self.controller)
			&& device_can_wakeup(&hcd->self.root_hub->dev))
		dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");

	/* initialize tasklets */
	init_giveback_urb_bh(&hcd->high_prio_bh);
	init_giveback_urb_bh(&hcd->low_prio_bh);

	/* enable irqs just before we start the controller,
	 * if the BIOS provides legacy PCI irqs.
	 */
	if (usb_hcd_is_primary_hcd(hcd) && irqnum) {
		retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
		if (retval)
			goto err_request_irq;
	}

	hcd->state = HC_STATE_RUNNING;
	retval = hcd->driver->start(hcd);
	if (retval < 0) {
		dev_err(hcd->self.controller, "startup error %d\n", retval);
		goto err_hcd_driver_start;
	}

	/* starting here, usbcore will pay attention to this root hub */
	retval = register_root_hub(hcd);
	if (retval != 0)
		goto err_register_root_hub;

	retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group);
	if (retval < 0) {
		printk(KERN_ERR "Cannot register USB bus sysfs attributes: %d\n",
		       retval);
		goto error_create_attr_group;
	}
	if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
		usb_hcd_poll_rh_status(hcd);

	return retval;

error_create_attr_group:
	clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
	if (HC_IS_RUNNING(hcd->state))
		hcd->state = HC_STATE_QUIESCING;
	spin_lock_irq(&hcd_root_hub_lock);
	hcd->rh_registered = 0;
	spin_unlock_irq(&hcd_root_hub_lock);

#ifdef CONFIG_PM
	cancel_work_sync(&hcd->wakeup_work);
#endif
	mutex_lock(&usb_bus_idr_lock);
	usb_disconnect(&rhdev);		/* Sets rhdev to NULL */
	mutex_unlock(&usb_bus_idr_lock);
err_register_root_hub:
	hcd->rh_pollable = 0;
	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
	del_timer_sync(&hcd->rh_timer);
	hcd->driver->stop(hcd);
	hcd->state = HC_STATE_HALT;
	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
	del_timer_sync(&hcd->rh_timer);
err_hcd_driver_start:
	if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)
		free_irq(irqnum, hcd);
err_request_irq:
err_hcd_driver_setup:
err_set_rh_speed:
	usb_put_invalidate_rhdev(hcd);
err_allocate_root_hub:
	usb_deregister_bus(&hcd->self);
err_register_bus:
	hcd_buffer_destroy(hcd);
err_create_buf:
	usb_phy_roothub_power_off(hcd->phy_roothub);
err_usb_phy_roothub_power_on:
	usb_phy_roothub_exit(hcd->phy_roothub);

	return retval;
}
EXPORT_SYMBOL_GPL(usb_add_hcd);
/**
 * usb_remove_hcd - shutdown processing for generic HCDs
 * @hcd: the usb_hcd structure to remove
 * Context: !in_interrupt()
 *
 * Disconnects the root hub, then reverses the effects of usb_add_hcd(),
 * invoking the HCD's stop() method.
 */
void usb_remove_hcd(struct usb_hcd *hcd)
{
	struct usb_device *rhdev = hcd->self.root_hub;

	dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);

	usb_get_dev(rhdev);
	sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group);

	clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
	if (HC_IS_RUNNING (hcd->state))
		hcd->state = HC_STATE_QUIESCING;

	dev_dbg(hcd->self.controller, "roothub graceful disconnect\n");
	spin_lock_irq (&hcd_root_hub_lock);
	hcd->rh_registered = 0;
	spin_unlock_irq (&hcd_root_hub_lock);

#ifdef CONFIG_PM
	cancel_work_sync(&hcd->wakeup_work);
#endif

	mutex_lock(&usb_bus_idr_lock);
	usb_disconnect(&rhdev);		/* Sets rhdev to NULL */
	mutex_unlock(&usb_bus_idr_lock);

	/*
	 * tasklet_kill() isn't needed here because:
	 * - driver's disconnect() called from usb_disconnect() should
	 *   make sure its URBs are completed during the disconnect()
	 *   callback
	 *
	 * - it is too late to run complete() here since driver may have
	 *   been removed already now
	 */

	/* Prevent any more root-hub status calls from the timer.
	 * The HCD might still restart the timer (if a port status change
	 * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke
	 * the hub_status_data() callback.
	 */
	hcd->rh_pollable = 0;
	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
	del_timer_sync(&hcd->rh_timer);

	hcd->driver->stop(hcd);
	hcd->state = HC_STATE_HALT;

	/* In case the HCD restarted the timer, stop it again. */
	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
	del_timer_sync(&hcd->rh_timer);

	if (usb_hcd_is_primary_hcd(hcd)) {
		if (hcd->irq > 0)
			free_irq(hcd->irq, hcd);
	}

	usb_deregister_bus(&hcd->self);
	hcd_buffer_destroy(hcd);

	usb_phy_roothub_power_off(hcd->phy_roothub);
	usb_phy_roothub_exit(hcd->phy_roothub);

	usb_put_invalidate_rhdev(hcd);
	hcd->flags = 0;
}
EXPORT_SYMBOL_GPL(usb_remove_hcd);
2.1.2 OHCI 主机控制器驱动ohci_hcd

OHCI HCD 驱动属于 HCD 驱动的实例,它定义了一个 ohci_hcd 结构体,usb_hcd 结构体给出的 usb_hcd 结构体的私有数据,这个结构体的定义如下所示

struct ohci_hcd {
	spinlock_t		lock;

	/*
	 * I/O memory used to communicate with the HC (dma-consistent)
	 * 与主机控制器通信的 I/O 内存(DMA 一致) 
	 */
	struct ohci_regs __iomem *regs;

	/*
	 * main memory used to communicate with the HC (dma-consistent).
	 * hcd adds to schedule for a live hc any time, but removals finish
	 * only at the start of the next frame.
	 * 与主机控制器通信的主存(DMA 一致) 
	 */
	struct ohci_hcca	*hcca;
	dma_addr_t		hcca_dma;

	struct ed		*ed_rm_list;		/* to be removed 将被移除*/

	struct ed		*ed_bulktail;		/* last in bulk list 批量队列尾*/
	struct ed		*ed_controltail;	/* last in ctrl list 控制队列尾*/
	struct ed		*periodic [NUM_INTS];	/* shadow int_table int_table“影子” */

	void (*start_hnp)(struct ohci_hcd *ohci);

	/*
	 * memory management for queue data structures
	 * 队列数据的内存管理
	 */
	struct dma_pool		*td_cache;
	struct dma_pool		*ed_cache;
	struct td		*td_hash [TD_HASH_SIZE];
	struct td		*dl_start, *dl_end;	/* the done list */
	struct list_head	pending;
	struct list_head	eds_in_use;	/* all EDs with at least 1 TD */

	/*
	 * driver state
	 * driver 状态
	 */
	enum ohci_rh_state	rh_state;
	int			num_ports;
	int			load [NUM_INTS];
	u32			hc_control;	/* copy of hc control reg 主机控制器控制寄存器的复制*/
	unsigned long		next_statechange;	/* suspend/resume 挂起/恢复*/
	u32			fminterval;		/* saved register 被保存的寄存器*/
	unsigned		autostop:1;	/* rh auto stopping/stopped */
	unsigned		working:1;
	unsigned		restart_work:1;

	unsigned long		flags;		/* for HC bugs */
#define	OHCI_QUIRK_AMD756	0x01			/* erratum #4 */
#define	OHCI_QUIRK_SUPERIO	0x02			/* natsemi */
#define	OHCI_QUIRK_INITRESET	0x04			/* SiS, OPTi, ... */
#define	OHCI_QUIRK_BE_DESC	0x08			/* BE descriptors */
#define	OHCI_QUIRK_BE_MMIO	0x10			/* BE registers */
#define	OHCI_QUIRK_ZFMICRO	0x20			/* Compaq ZFMicro chipset*/
#define	OHCI_QUIRK_NEC		0x40			/* lost interrupts */
#define	OHCI_QUIRK_FRAME_NO	0x80			/* no big endian frame_no shift */
#define	OHCI_QUIRK_HUB_POWER	0x100			/* distrust firmware power/oc setup */
#define	OHCI_QUIRK_AMD_PLL	0x200			/* AMD PLL quirk*/
#define	OHCI_QUIRK_AMD_PREFETCH	0x400			/* pre-fetch for ISO transfer */
#define	OHCI_QUIRK_GLOBAL_SUSPEND	0x800		/* must suspend ports */
#define	OHCI_QUIRK_QEMU		0x1000			/* relax timing expectations */

	// there are also chip quirks/bugs in init logic

	unsigned		prev_frame_no;
	unsigned		wdh_cnt, prev_wdh_cnt;
	u32			prev_donehead;
	struct timer_list	io_watchdog;

	struct work_struct	nec_work;	/* Worker for NEC quirk */

	struct dentry		*debug_dir;

	/* platform-specific data -- must come last */
	unsigned long           priv[0] __aligned(sizeof(s64));

};

使用如下内联函数可实现 usb_hcdohci_hcd 的相互转换:

/* convert between an hcd pointer and the corresponding ohci_hcd */
static inline struct ohci_hcd *hcd_to_ohci (struct usb_hcd *hcd)
{
	return (struct ohci_hcd *) (hcd->hcd_priv);
}
static inline struct usb_hcd *ohci_to_hcd (const struct ohci_hcd *ohci)
{
	return container_of ((void *) ohci, struct usb_hcd, hcd_priv);
}

usb_hcd 得到 ohci_hcd 只是取得“私有”数据,而从 ohci_hcd 得到 usb_hcd 则是通过container_of()从结构体成员获得结构体指针。

使用如下函数可初始化 OHCI 主机控制器:

/*-------------------------------------------------------------------------*
 * HC functions
 *-------------------------------------------------------------------------*/

/* init memory, and kick BIOS/SMM off */

static int ohci_init (struct ohci_hcd *ohci)
{
	int ret;
	struct usb_hcd *hcd = ohci_to_hcd(ohci);

	/* Accept arbitrarily long scatter-gather lists */
	if (!(hcd->driver->flags & HCD_LOCAL_MEM))
		hcd->self.sg_tablesize = ~0;

	if (distrust_firmware)
		ohci->flags |= OHCI_QUIRK_HUB_POWER;

	ohci->rh_state = OHCI_RH_HALTED;
	ohci->regs = hcd->regs;

	/* REVISIT this BIOS handshake is now moved into PCI "quirks", and
	 * was never needed for most non-PCI systems ... remove the code?
	 */

#ifndef IR_DISABLE
	/* SMM owns the HC?  not for long! */
	if (!no_handshake && ohci_readl (ohci,
					&ohci->regs->control) & OHCI_CTRL_IR) {
		u32 temp;

		ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");

		/* this timeout is arbitrary.  we make it long, so systems
		 * depending on usb keyboards may be usable even if the
		 * BIOS/SMM code seems pretty broken.
		 */
		temp = 500;	/* arbitrary: five seconds */

		ohci_writel (ohci, OHCI_INTR_OC, &ohci->regs->intrenable);
		ohci_writel (ohci, OHCI_OCR, &ohci->regs->cmdstatus);
		while (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_IR) {
			msleep (10);
			if (--temp == 0) {
				ohci_err (ohci, "USB HC takeover failed!"
					"  (BIOS/SMM bug)\n");
				return -EBUSY;
			}
		}
		ohci_usb_reset (ohci);
	}
#endif

	/* Disable HC interrupts */
	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);

	/* flush the writes, and save key bits like RWC */
	if (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_RWC)
		ohci->hc_control |= OHCI_CTRL_RWC;

	/* Read the number of ports unless overridden */
	if (ohci->num_ports == 0)
		ohci->num_ports = roothub_a(ohci) & RH_A_NDP;

	if (ohci->hcca)
		return 0;

	timer_setup(&ohci->io_watchdog, io_watchdog_func, 0);
	ohci->prev_frame_no = IO_WATCHDOG_OFF;

	ohci->hcca = dma_alloc_coherent (hcd->self.controller,
			sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL);
	if (!ohci->hcca)
		return -ENOMEM;

	if ((ret = ohci_mem_init (ohci)) < 0)
		ohci_stop (hcd);
	else {
		create_debug_files (ohci);
	}

	return ret;
}
/* ohci_setup routine for generic controller initialization */

int ohci_setup(struct usb_hcd *hcd)
{
	struct ohci_hcd		*ohci = hcd_to_ohci(hcd);

	ohci_hcd_init(ohci);
	
	return ohci_init(ohci);
}
EXPORT_SYMBOL_GPL(ohci_setup);

如下函数分别用于开启、停止及复位 OHCI 控制器:

/* Start an OHCI controller, set the BUS operational
 * resets USB and controller
 * enable interrupts
 */
static int ohci_run (struct ohci_hcd *ohci)
{
	u32			mask, val;
	int			first = ohci->fminterval == 0;
	struct usb_hcd		*hcd = ohci_to_hcd(ohci);

	ohci->rh_state = OHCI_RH_HALTED;

	/* boot firmware should have set this up (5.1.1.3.1) */
	if (first) {

		val = ohci_readl (ohci, &ohci->regs->fminterval);
		ohci->fminterval = val & 0x3fff;
		if (ohci->fminterval != FI)
			ohci_dbg (ohci, "fminterval delta %d\n",
				ohci->fminterval - FI);
		ohci->fminterval |= FSMP (ohci->fminterval) << 16;
		/* also: power/overcurrent flags in roothub.a */
	}

	/* Reset USB nearly "by the book".  RemoteWakeupConnected has
	 * to be checked in case boot firmware (BIOS/SMM/...) has set up
	 * wakeup in a way the bus isn't aware of (e.g., legacy PCI PM).
	 * If the bus glue detected wakeup capability then it should
	 * already be enabled; if so we'll just enable it again.
	 */
	if ((ohci->hc_control & OHCI_CTRL_RWC) != 0)
		device_set_wakeup_capable(hcd->self.controller, 1);

	switch (ohci->hc_control & OHCI_CTRL_HCFS) {
	case OHCI_USB_OPER:
		val = 0;
		break;
	case OHCI_USB_SUSPEND:
	case OHCI_USB_RESUME:
		ohci->hc_control &= OHCI_CTRL_RWC;
		ohci->hc_control |= OHCI_USB_RESUME;
		val = 10 /* msec wait */;
		break;
	// case OHCI_USB_RESET:
	default:
		ohci->hc_control &= OHCI_CTRL_RWC;
		ohci->hc_control |= OHCI_USB_RESET;
		val = 50 /* msec wait */;
		break;
	}
	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
	// flush the writes
	(void) ohci_readl (ohci, &ohci->regs->control);
	msleep(val);

	memset (ohci->hcca, 0, sizeof (struct ohci_hcca));

	/* 2msec timelimit here means no irqs/preempt */
	spin_lock_irq (&ohci->lock);

retry:
	/* HC Reset requires max 10 us delay */
	ohci_writel (ohci, OHCI_HCR,  &ohci->regs->cmdstatus);
	val = 30;	/* ... allow extra time */
	while ((ohci_readl (ohci, &ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
		if (--val == 0) {
			spin_unlock_irq (&ohci->lock);
			ohci_err (ohci, "USB HC reset timed out!\n");
			return -1;
		}
		udelay (1);
	}

	/* now we're in the SUSPEND state ... must go OPERATIONAL
	 * within 2msec else HC enters RESUME
	 *
	 * ... but some hardware won't init fmInterval "by the book"
	 * (SiS, OPTi ...), so reset again instead.  SiS doesn't need
	 * this if we write fmInterval after we're OPERATIONAL.
	 * Unclear about ALi, ServerWorks, and others ... this could
	 * easily be a longstanding bug in chip init on Linux.
	 */
	if (ohci->flags & OHCI_QUIRK_INITRESET) {
		ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
		// flush those writes
		(void) ohci_readl (ohci, &ohci->regs->control);
	}

	/* Tell the controller where the control and bulk lists are
	 * The lists are empty now. */
	ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
	ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);

	/* a reset clears this */
	ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);

	periodic_reinit (ohci);

	/* some OHCI implementations are finicky about how they init.
	 * bogus values here mean not even enumeration could work.
	 */
	if ((ohci_readl (ohci, &ohci->regs->fminterval) & 0x3fff0000) == 0
			|| !ohci_readl (ohci, &ohci->regs->periodicstart)) {
		if (!(ohci->flags & OHCI_QUIRK_INITRESET)) {
			ohci->flags |= OHCI_QUIRK_INITRESET;
			ohci_dbg (ohci, "enabling initreset quirk\n");
			goto retry;
		}
		spin_unlock_irq (&ohci->lock);
		ohci_err (ohci, "init err (%08x %04x)\n",
			ohci_readl (ohci, &ohci->regs->fminterval),
			ohci_readl (ohci, &ohci->regs->periodicstart));
		return -EOVERFLOW;
	}

	/* use rhsc irqs after hub_wq is allocated */
	set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
	hcd->uses_new_polling = 1;

	/* start controller operations */
	ohci->hc_control &= OHCI_CTRL_RWC;
	ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
	ohci->rh_state = OHCI_RH_RUNNING;

	/* wake on ConnectStatusChange, matching external hubs */
	ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status);

	/* Choose the interrupts we care about now, others later on demand */
	mask = OHCI_INTR_INIT;
	ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
	ohci_writel (ohci, mask, &ohci->regs->intrenable);

	/* handle root hub init quirks ... */
	val = roothub_a (ohci);
	val &= ~(RH_A_PSM | RH_A_OCPM);
	if (ohci->flags & OHCI_QUIRK_SUPERIO) {
		/* NSC 87560 and maybe others */
		val |= RH_A_NOCP;
		val &= ~(RH_A_POTPGT | RH_A_NPS);
		ohci_writel (ohci, val, &ohci->regs->roothub.a);
	} else if ((ohci->flags & OHCI_QUIRK_AMD756) ||
			(ohci->flags & OHCI_QUIRK_HUB_POWER)) {
		/* hub power always on; required for AMD-756 and some
		 * Mac platforms.  ganged overcurrent reporting, if any.
		 */
		val |= RH_A_NPS;
		ohci_writel (ohci, val, &ohci->regs->roothub.a);
	}
	ohci_writel (ohci, RH_HS_LPSC, &ohci->regs->roothub.status);
	ohci_writel (ohci, (val & RH_A_NPS) ? 0 : RH_B_PPCM,
						&ohci->regs->roothub.b);
	// flush those writes
	(void) ohci_readl (ohci, &ohci->regs->control);

	ohci->next_statechange = jiffies + STATECHANGE_DELAY;
	spin_unlock_irq (&ohci->lock);

	// POTPGT delay is bits 24-31, in 2 ms units.
	mdelay ((val >> 23) & 0x1fe);

	ohci_dump(ohci);

	return 0;
}
/* ohci_start routine for generic controller start of all OHCI bus glue */
static int ohci_start(struct usb_hcd *hcd)
{
	struct ohci_hcd		*ohci = hcd_to_ohci(hcd);
	int	ret;

	ret = ohci_run(ohci);
	if (ret < 0) {
		ohci_err(ohci, "can't start\n");
		ohci_stop(hcd);
	}
	return ret;
}
static void ohci_stop (struct usb_hcd *hcd)
{
	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);

	ohci_dump(ohci);

	if (quirk_nec(ohci))
		flush_work(&ohci->nec_work);
	del_timer_sync(&ohci->io_watchdog);
	ohci->prev_frame_no = IO_WATCHDOG_OFF;

	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
	ohci_usb_reset(ohci);
	free_irq(hcd->irq, hcd);
	hcd->irq = 0;

	if (quirk_amdiso(ohci))
		usb_amd_dev_put();

	remove_debug_files (ohci);
	ohci_mem_cleanup (ohci);
	if (ohci->hcca) {
		dma_free_coherent (hcd->self.controller,
				sizeof *ohci->hcca,
				ohci->hcca, ohci->hcca_dma);
		ohci->hcca = NULL;
		ohci->hcca_dma = 0;
	}
}
static void ohci_usb_reset (struct ohci_hcd *ohci)
{
	ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
	ohci->hc_control &= OHCI_CTRL_RWC;
	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
	ohci->rh_state = OHCI_RH_HALTED;
}

3. USB设备驱动

3.1 USB 设备驱动整体结构usb_driver

这里所说的 USB 设备驱动指的是从主机角度观察,怎样访问被插入的 USB 设备,而不是指 USB 设备内部本身运行的固件程序。

一般的通用的 Linux 设备(如 U 盘、USB 鼠标、USB 键盘等)都不需要工程师再编写驱动,需要编写的是特定厂商、特定芯片的驱动,而且往往也可以参考内核中已提供的驱动的模板。

Linux 内核为各类 USB 设备分配了相应的设备号,如 ACM USB 调制解调器的主设备号为 166(默
认设备名/dev/ttyACMn)、USB 打印机的主设备号为 180,次设备号为 0~15(默认设备名 /dev/lpn )、USB 串口的主设备号为 188(默认设备名/dev/ttyUSBn)等,详见http://www.lanana.org/网站的设备列表。

其中usbview 可以以图形化的方式显示系统中的 USB 设备。
在这里插入图片描述

当然,在编译 Linux 内核时,应该包括“USB device filesystem”。usbfs 动态跟踪总线上插入和移除的设备,通过它可以查看系统中 USB 设备的信息,包括拓扑、带宽、设备描述符信息、产品 ID、字符串描述符、配置描述符、接口描述符、端点描述符等,但是比较遗憾,目前没有找到这个使用方法进行查看。

此外,在 sysfs 文件系统中,同样包含了 USB 相关信息的描述,但只限于接口级别。USB 设备和 USB 接口在 sysfs 中均表示为单独的 USB 设备,其目录命名规则如下:

根集线器-集线器端口号(-集线器端口号-...):配置.接口

下面讲解/sys/bus/usb 目录的树形结构实例,其中的多数文件都是到/sys/devices/sys/drivers 中相应文件的链接。

user@user-PC:~$ tree -L 2 /sys/bus/usb
/sys/bus/usb
├── devices
│   ├── 1-0:1.0 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-0:1.0
│   ├── 1-1 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1
│   ├── 1-11 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-11
│   ├── 1-1:1.0 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0
│   ├── 1-11:1.0 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-11/1-11:1.0
│   ├── 1-11:1.1 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-11/1-11:1.1
│   ├── 1-8 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-8
│   ├── 1-8:1.0 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0
│   ├── 2-0:1.0 -> ../../../devices/pci0000:00/0000:00:14.0/usb2/2-0:1.0
│   ├── 2-5 -> ../../../devices/pci0000:00/0000:00:14.0/usb2/2-5
│   ├── 2-5:1.0 -> ../../../devices/pci0000:00/0000:00:14.0/usb2/2-5/2-5:1.0
│   ├── 2-5.4 -> ../../../devices/pci0000:00/0000:00:14.0/usb2/2-5/2-5.4
│   ├── 2-5.4:1.0 -> ../../../devices/pci0000:00/0000:00:14.0/usb2/2-5/2-5.4/2-5.4:1.0
│   ├── usb1 -> ../../../devices/pci0000:00/0000:00:14.0/usb1
│   └── usb2 -> ../../../devices/pci0000:00/0000:00:14.0/usb2
├── drivers
│   ├── hub
│   ├── uas
│   ├── usb
│   ├── usbfs
│   ├── usbhid
│   ├── usbkbd
│   ├── usbmouse
│   └── usb-storage
├── drivers_autoprobe
├── drivers_probe
└── uevent

25 directories, 3 files

正如 tty_driveri2c_driver 等,在 Linux 内核中,使用 usb_driver 结构体描述一个 USB 设备驱动。

/**
 * struct usb_driver - identifies USB interface driver to usbcore
 * @name: The driver name should be unique among USB drivers,
 *	and should normally be the same as the module name.
 * @probe: Called to see if the driver is willing to manage a particular
 *	interface on a device.  If it is, probe returns zero and uses
 *	usb_set_intfdata() to associate driver-specific data with the
 *	interface.  It may also use usb_set_interface() to specify the
 *	appropriate altsetting.  If unwilling to manage the interface,
 *	return -ENODEV, if genuine IO errors occurred, an appropriate
 *	negative errno value.
 * @disconnect: Called when the interface is no longer accessible, usually
 *	because its device has been (or is being) disconnected or the
 *	driver module is being unloaded.
 * @unlocked_ioctl: Used for drivers that want to talk to userspace through
 *	the "usbfs" filesystem.  This lets devices provide ways to
 *	expose information to user space regardless of where they
 *	do (or don't) show up otherwise in the filesystem.
 * @suspend: Called when the device is going to be suspended by the
 *	system either from system sleep or runtime suspend context. The
 *	return value will be ignored in system sleep context, so do NOT
 *	try to continue using the device if suspend fails in this case.
 *	Instead, let the resume or reset-resume routine recover from
 *	the failure.
 * @resume: Called when the device is being resumed by the system.
 * @reset_resume: Called when the suspended device has been reset instead
 *	of being resumed.
 * @pre_reset: Called by usb_reset_device() when the device is about to be
 *	reset.  This routine must not return until the driver has no active
 *	URBs for the device, and no more URBs may be submitted until the
 *	post_reset method is called.
 * @post_reset: Called by usb_reset_device() after the device
 *	has been reset
 * @id_table: USB drivers use ID table to support hotplugging.
 *	Export this with MODULE_DEVICE_TABLE(usb,...).  This must be set
 *	or your driver's probe function will never get called.
 * @dynids: used internally to hold the list of dynamically added device
 *	ids for this driver.
 * @drvwrap: Driver-model core structure wrapper.
 * @no_dynamic_id: if set to 1, the USB core will not allow dynamic ids to be
 *	added to this driver by preventing the sysfs file from being created.
 * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend
 *	for interfaces bound to this driver.
 * @soft_unbind: if set to 1, the USB core will not kill URBs and disable
 *	endpoints before calling the driver's disconnect method.
 * @disable_hub_initiated_lpm: if set to 1, the USB core will not allow hubs
 *	to initiate lower power link state transitions when an idle timeout
 *	occurs.  Device-initiated USB 3.0 link PM will still be allowed.
 *
 * USB interface drivers must provide a name, probe() and disconnect()
 * methods, and an id_table.  Other driver fields are optional.
 *
 * The id_table is used in hotplugging.  It holds a set of descriptors,
 * and specialized data may be associated with each entry.  That table
 * is used by both user and kernel mode hotplugging support.
 *
 * The probe() and disconnect() methods are called in a context where
 * they can sleep, but they should avoid abusing the privilege.  Most
 * work to connect to a device should be done when the device is opened,
 * and undone at the last close.  The disconnect code needs to address
 * concurrency issues with respect to open() and close() methods, as
 * well as forcing all pending I/O requests to complete (by unlinking
 * them as necessary, and blocking until the unlinks complete).
 */
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);/* I/O 控制函数 */

	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;/* usb_device_id 表指针 */

	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)

在编写新的 USB 设备驱动时,主要应该完成的工作是 probe()disconnect()函数,即探测和断开函数,它们分别在设备被插入和拔出的时候被调用,用于初始化和释放软硬件资源。对usb_driver 的注册和注销通过这两个函数完成:

int usb_register(struct usb_driver *new_driver)
void usb_deregister(struct usb_driver *driver);

usb_driver 结构体中的 id_table成员描述了这个 USB 驱动所支持的 USB 设备列表,它指向一个 usb_device_id 数组,usb_device_id 结构体用于包含 USB 设备的制造商 ID、产品 ID、产品版本、
设备类、接口类等信息及其要匹配标志成员 match_flags(标明要与哪些成员匹配,包含 DEV_LO
DEV_HIDEV_CLASSDEV_SUBCLASSDEV_PROTOCOL、INT_CLASSINT_SUBCLASSINT_PROTOCOL)。可以借助下面一组宏来生成 usb_device_id 结构体的实例:

USB_DEVICE(vendor, product)

该宏根据制造商 ID 和产品 ID 生成一个 usb_device_id 结构体的实例,在数组中增加该元素将
意味着该驱动可支持匹配制造商 ID、产品 ID 的设备。

USB_DEVICE_VER(vendor, product, lo, hi)

该宏根据制造商 ID、产品 ID、产品版本的最小值和最大值生成一个 usb_device_id 结构体的实例,
在数组中增加该元素将意味着该驱动可支持匹配制造商 ID、产品 ID 和 lo~hi 范围内版本的设备。

USB_DEVICE_INFO(class, subclass, protocol)

该宏用于创建一个匹配设备指定类型的 usb_device_id 结构体实例。

USB_INTERFACE_INFO(class, subclass, protocol)

该宏用于创建一个匹配接口指定类型的 usb_device_id 结构体实例。

USB 核心检测到某个设备的属性和某个驱动程序的 usb_device_id 结构体所携带的信息一致时,这个驱动程序的 probe()函数就被执行。拔掉设备或者卸掉驱动模块后,USB 核心就执行disconnect()函数来响应这个动作。

usb_driver 本身只是起到了找到 USB 设备、管理 USB 设备连接和断开的作用

上述 usb_driver 结构体中的函数是 USB 设备驱动中 USB 相关的部分,而 USB 只是一个总线,真正的 USB 设备驱动的主体工作仍然是 USB 设备本身所属类型的驱动,如字符设备、tty 设备、块设备、输入设备等。因此 USB 设备驱动包含其作为总线上挂在设备的驱动和本身所属设备类型的驱动两部分。

platform_driver 类似,usb_driver 起到了“牵线”的作用,即在 probe()里注册相应的字符、tty 等设备,在 disconnect()注销相应的字符、tty 等设备,而原先对设备的注册和注销一般直接发生在模块加载和卸载函数中。

尽管 USB 本身所属设备驱动的结构与其不挂在 USB 总线上时完全相同,但是在访问方式上却发生了很大的变化,例如,对于 USB 接口的字符设备而言,尽管仍然是 write()read()ioctl()这些函数,但是在这些函数中,贯穿始终的是称为 URB 的 USB 请求块。

3.2 USB 请求块(URB)

3.2.1 urb结构体

linux 内核中的 USB 代码和所有的 USB 设备通讯所用的基本载体和核心数据结构称为 urb ( USB request block). 这个请求块用 struct urb 结构描述并且可在 include/linux/usb.h 中找到.

/**
 * struct urb - USB Request Block
 * @urb_list: For use by current owner of the URB.
 * @anchor_list: membership in the list of an anchor
 * @anchor: to anchor URBs to a common mooring
 * @ep: Points to the endpoint's data structure.  Will eventually
 *	replace @pipe.
 * @pipe: Holds endpoint number, direction, type, and more.
 *	Create these values with the eight macros available;
 *	usb_{snd,rcv}TYPEpipe(dev,endpoint), where the TYPE is "ctrl"
 *	(control), "bulk", "int" (interrupt), or "iso" (isochronous).
 *	For example usb_sndbulkpipe() or usb_rcvintpipe().  Endpoint
 *	numbers range from zero to fifteen.  Note that "in" endpoint two
 *	is a different endpoint (and pipe) from "out" endpoint two.
 *	The current configuration controls the existence, type, and
 *	maximum packet size of any given endpoint.
 * @stream_id: the endpoint's stream ID for bulk streams
 * @dev: Identifies the USB device to perform the request.
 * @status: This is read in non-iso completion functions to get the
 *	status of the particular request.  ISO requests only use it
 *	to tell whether the URB was unlinked; detailed status for
 *	each frame is in the fields of the iso_frame-desc.
 * @transfer_flags: A variety of flags may be used to affect how URB
 *	submission, unlinking, or operation are handled.  Different
 *	kinds of URB can use different flags.
 * @transfer_buffer:  This identifies the buffer to (or from) which the I/O
 *	request will be performed unless URB_NO_TRANSFER_DMA_MAP is set
 *	(however, do not leave garbage in transfer_buffer even then).
 *	This buffer must be suitable for DMA; allocate it with
 *	kmalloc() or equivalent.  For transfers to "in" endpoints, contents
 *	of this buffer will be modified.  This buffer is used for the data
 *	stage of control transfers.
 * @transfer_dma: When transfer_flags includes URB_NO_TRANSFER_DMA_MAP,
 *	the device driver is saying that it provided this DMA address,
 *	which the host controller driver should use in preference to the
 *	transfer_buffer.
 * @sg: scatter gather buffer list, the buffer size of each element in
 * 	the list (except the last) must be divisible by the endpoint's
 * 	max packet size if no_sg_constraint isn't set in 'struct usb_bus'
 * @num_mapped_sgs: (internal) number of mapped sg entries
 * @num_sgs: number of entries in the sg list
 * @transfer_buffer_length: How big is transfer_buffer.  The transfer may
 *	be broken up into chunks according to the current maximum packet
 *	size for the endpoint, which is a function of the configuration
 *	and is encoded in the pipe.  When the length is zero, neither
 *	transfer_buffer nor transfer_dma is used.
 * @actual_length: This is read in non-iso completion functions, and
 *	it tells how many bytes (out of transfer_buffer_length) were
 *	transferred.  It will normally be the same as requested, unless
 *	either an error was reported or a short read was performed.
 *	The URB_SHORT_NOT_OK transfer flag may be used to make such
 *	short reads be reported as errors.
 * @setup_packet: Only used for control transfers, this points to eight bytes
 *	of setup data.  Control transfers always start by sending this data
 *	to the device.  Then transfer_buffer is read or written, if needed.
 * @setup_dma: DMA pointer for the setup packet.  The caller must not use
 *	this field; setup_packet must point to a valid buffer.
 * @start_frame: Returns the initial frame for isochronous transfers.
 * @number_of_packets: Lists the number of ISO transfer buffers.
 * @interval: Specifies the polling interval for interrupt or isochronous
 *	transfers.  The units are frames (milliseconds) for full and low
 *	speed devices, and microframes (1/8 millisecond) for highspeed
 *	and SuperSpeed devices.
 * @error_count: Returns the number of ISO transfers that reported errors.
 * @context: For use in completion functions.  This normally points to
 *	request-specific driver context.
 * @complete: Completion handler. This URB is passed as the parameter to the
 *	completion function.  The completion function may then do what
 *	it likes with the URB, including resubmitting or freeing it.
 * @iso_frame_desc: Used to provide arrays of ISO transfer buffers and to
 *	collect the transfer status for each buffer.
 *
 * This structure identifies USB transfer requests.  URBs must be allocated by
 * calling usb_alloc_urb() and freed with a call to usb_free_urb().
 * Initialization may be done using various usb_fill_*_urb() functions.  URBs
 * are submitted using usb_submit_urb(), and pending requests may be canceled
 * using usb_unlink_urb() or usb_kill_urb().
 *
 * Data Transfer Buffers:
 *
 * Normally drivers provide I/O buffers allocated with kmalloc() or otherwise
 * taken from the general page pool.  That is provided by transfer_buffer
 * (control requests also use setup_packet), and host controller drivers
 * perform a dma mapping (and unmapping) for each buffer transferred.  Those
 * mapping operations can be expensive on some platforms (perhaps using a dma
 * bounce buffer or talking to an IOMMU),
 * although they're cheap on commodity x86 and ppc hardware.
 *
 * Alternatively, drivers may pass the URB_NO_TRANSFER_DMA_MAP transfer flag,
 * which tells the host controller driver that no such mapping is needed for
 * the transfer_buffer since
 * the device driver is DMA-aware.  For example, a device driver might
 * allocate a DMA buffer with usb_alloc_coherent() or call usb_buffer_map().
 * When this transfer flag is provided, host controller drivers will
 * attempt to use the dma address found in the transfer_dma
 * field rather than determining a dma address themselves.
 *
 * Note that transfer_buffer must still be set if the controller
 * does not support DMA (as indicated by bus.uses_dma) and when talking
 * to root hub. If you have to trasfer between highmem zone and the device
 * on such controller, create a bounce buffer or bail out with an error.
 * If transfer_buffer cannot be set (is in highmem) and the controller is DMA
 * capable, assign NULL to it, so that usbmon knows not to use the value.
 * The setup_packet must always be set, so it cannot be located in highmem.
 *
 * Initialization:
 *
 * All URBs submitted must initialize the dev, pipe, transfer_flags (may be
 * zero), and complete fields.  All URBs must also initialize
 * transfer_buffer and transfer_buffer_length.  They may provide the
 * URB_SHORT_NOT_OK transfer flag, indicating that short reads are
 * to be treated as errors; that flag is invalid for write requests.
 *
 * Bulk URBs may
 * use the URB_ZERO_PACKET transfer flag, indicating that bulk OUT transfers
 * should always terminate with a short packet, even if it means adding an
 * extra zero length packet.
 *
 * Control URBs must provide a valid pointer in the setup_packet field.
 * Unlike the transfer_buffer, the setup_packet may not be mapped for DMA
 * beforehand.
 *
 * Interrupt URBs must provide an interval, saying how often (in milliseconds
 * or, for highspeed devices, 125 microsecond units)
 * to poll for transfers.  After the URB has been submitted, the interval
 * field reflects how the transfer was actually scheduled.
 * The polling interval may be more frequent than requested.
 * For example, some controllers have a maximum interval of 32 milliseconds,
 * while others support intervals of up to 1024 milliseconds.
 * Isochronous URBs also have transfer intervals.  (Note that for isochronous
 * endpoints, as well as high speed interrupt endpoints, the encoding of
 * the transfer interval in the endpoint descriptor is logarithmic.
 * Device drivers must convert that value to linear units themselves.)
 *
 * If an isochronous endpoint queue isn't already running, the host
 * controller will schedule a new URB to start as soon as bandwidth
 * utilization allows.  If the queue is running then a new URB will be
 * scheduled to start in the first transfer slot following the end of the
 * preceding URB, if that slot has not already expired.  If the slot has
 * expired (which can happen when IRQ delivery is delayed for a long time),
 * the scheduling behavior depends on the URB_ISO_ASAP flag.  If the flag
 * is clear then the URB will be scheduled to start in the expired slot,
 * implying that some of its packets will not be transferred; if the flag
 * is set then the URB will be scheduled in the first unexpired slot,
 * breaking the queue's synchronization.  Upon URB completion, the
 * start_frame field will be set to the (micro)frame number in which the
 * transfer was scheduled.  Ranges for frame counter values are HC-specific
 * and can go from as low as 256 to as high as 65536 frames.
 *
 * Isochronous URBs have a different data transfer model, in part because
 * the quality of service is only "best effort".  Callers provide specially
 * allocated URBs, with number_of_packets worth of iso_frame_desc structures
 * at the end.  Each such packet is an individual ISO transfer.  Isochronous
 * URBs are normally queued, submitted by drivers to arrange that
 * transfers are at least double buffered, and then explicitly resubmitted
 * in completion handlers, so
 * that data (such as audio or video) streams at as constant a rate as the
 * host controller scheduler can support.
 *
 * Completion Callbacks:
 *
 * The completion callback is made in_interrupt(), and one of the first
 * things that a completion handler should do is check the status field.
 * The status field is provided for all URBs.  It is used to report
 * unlinked URBs, and status for all non-ISO transfers.  It should not
 * be examined before the URB is returned to the completion handler.
 *
 * The context field is normally used to link URBs back to the relevant
 * driver or request state.
 *
 * When the completion callback is invoked for non-isochronous URBs, the
 * actual_length field tells how many bytes were transferred.  This field
 * is updated even when the URB terminated with an error or was unlinked.
 *
 * ISO transfer status is reported in the status and actual_length fields
 * of the iso_frame_desc array, and the number of errors is reported in
 * error_count.  Completion callbacks for ISO transfers will normally
 * (re)submit URBs to ensure a constant transfer rate.
 *
 * Note that even fields marked "public" should not be touched by the driver
 * when the urb is owned by the hcd, that is, since the call to
 * usb_submit_urb() till the entry into the completion routine.
 */
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 */
};

struct urb 结构中和 USB 设备驱动有关的成员是:

  • struct usb_device *dev
    指向这个 urb 要发送到的 struct usb_device 的指针. 这个变量必须被 USB 驱动初始化, 在这个 urb 被发送到 USB 核心之前.。
  • unsigned int pipe
    端点消息, 给这个 urb 要被发送到的特定 struct usb_device. 这个变量必须被 USB 驱动初始化, 在这个urb被发送到 USB 核心之前。
    为设置这个结构的成员, 驱动使用下面的函数是适当的, 依据流动的方向. 注意每个端点只可是一个类型。
    • unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint) :指定一个控制 OUT 端点给特定的带有特定端点号的 USB 设备。
    • unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)
      指定一个控制 IN 端点给带有特定端点号的特定 USB 设备.
    • unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint)
      指定一个块 OUT 端点给带有特定端点号的特定 USB 设备
    • unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint)
      指定一个块 IN 端点给带有特定端点号的特定 USB 设备
    • unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint)
      指定一个中断 OUT 端点给带有特定端点号的特定 USB 设备
    • unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint)
      指定一个中断 IN 端点给带有特定端点号的特定 USB 设备
    • unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint)
      指定一个同步 OUT 端点给带有特定端点号的特定 USB 设备
    • unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint)
      指定一个同步 IN 端点给带有特定端点号的特定 USB 设备
  • unsigned int transfer_flags
    这个变量可被设置为不同位值, 根据这个 USB 驱动想这个 urb 发生什么. 可用的值是:
    • URB_SHORT_NOT_OK
      当置位, 它指出任何在一个 IN 端点上可能发生的短读, 应当被 USB 核心当作一个错误. 这个值只对从 USB 设备读的 urb 有用, 不是写 urbs.
    • URB_ISO_ASAP
      如果这个 urb 是同步的, 这个位可被置位如果驱动想这个 urb 被调度, 只要带宽允许它这样, 并且在此点设置这个 urb 中的 start_frame 变量. 如果对于同步 urb 这个位没有被置位, 驱动必须指定 start_frame 值并且必须能够正确恢复, 如果没有在那个时刻启动. 见下面的章节关于同步 urb 更多的消息.
    • URB_NO_TRANSFER_DMA_MAP
      应当被置位, 当 urb 包含一个要被发送的 DMA 缓冲. USB 核心使用这个被 transfer_dma 变量指向的缓冲, 不是被 transfer_buffer 变量指向的缓冲.
    • URB_NO_SETUP_DMA_MAP
      URB_NO_TRANSFER_DMA_MAP 位, 这个位用来控制有一个 DMA 缓冲已经建立的 urb. 如果它被置位, USB 核心使用这个被 setup_dma 变量而不是 setup_packet 变量指向的缓冲.
    • URB_ASYNC_UNLINK
      如果置位, 给这个 urb 的对 usb_unlink_urb 的调用几乎立刻返回, 并且这个 urb 在后面被解除连接. 否则, 这个函数等待直到 urb 完全被去链并且在返回前结束. 小心使用这个位, 因为它可有非常难于调试的同步问题.
    • URB_NO_FSBR
      只有 UHCI USB 主机控制器驱动使用, 并且告诉它不要试图做 Front Side Bus Reclamation 逻辑. 这个位通常应当不设置, 因为有 UHCI 主机控制器的机器创建了许多 CPU 负担, 并且 PCI 总线被等待设置了这个位的 urb 所饱和。
    • URB_ZERO_PACKET
      如果置位, 一个块 OUT urb 通过发送不包含数据的短报文而结束, 当数据对齐到一个端点报文边界. 这被一些坏掉的 USB 设备所需要(例如一些 USB 到 IR 的设备) 为了正确的工作…
    • URB_NO_INTERRUPT
      如果置位, 硬件当 urb 结束时可能不产生一个中断. 这个位应当小心使用并且只在排队多个到相同端点的 urb 时使用. USB 核心函数使用这个为了做 DMA 缓冲传送.
  • void *transfer_buffer
    指向用在发送数据到设备(对一个 OUT urb)或者从设备中获取数据(对于一个 IN urb)的缓冲的指针. 对主机控制器为了正确存取这个缓冲, 它必须被使用一个对 kmalloc 调用来创建, 不是在堆栈或者静态地. 对控制端点, 这个缓冲是给发送的数据阶段.
  • dma_addr_t transfer_dma
    用来使用 DMA 传送数据到 USB 设备的缓冲.
  • int transfer_buffer_length
    缓冲的长度, 被 transfer_buffer 或者 transfer_dma 变量指向(由于只有一个可被一个 urb 使用). 如果这是 0, 没有传送缓冲被 USB 核心所使用.

对于一个 OUT 端点, 如果这个端点最大的大小比这个变量指定的值小, 对这个 USB 设备的传送被分成更小的块为了正确的传送数据. 这种大的传送发生在连续的 USB 帧. 提交一个大块数据在一个 urb 中是非常快, 并且使 USB 主机控制器去划分为更小的快, 比以连续的顺序发送小缓冲.

  • unsigned char *setup_packet
    指向给一个控制 urb 的 setup 报文的指针. 它在位于传送缓冲中的数据之前被传送. 这个变量只对控制 urb 有效.

  • dma_addr_t setup_dma
    给控制 urb 的 setupt 报文的 DMA 缓冲. 在位于正常传送缓冲的数据之前被传送. 这个变量只对控制 urb 有效.

  • usb_complete_t complete
    指向完成处理者函数的指针, 它被 USB 核心调用当这个 urb 被完全传送或者当 urb 发生一个错误. 在这个函数中, USB 驱动可检查这个 urb, 释放它, 或者重新提交它给另一次传送.(见"completingUrbs: 完成回调处理者", 关于完成处理者的更多细节。

    • usb_complete_t 类型定义如此:
      typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
  • void *context
    指向数据点的指针, 它可被 USB 驱动设置. 它可在完成处理者中使用当 urb 被返回到驱动. 关于这个变量的细节见后续章节。

  • int actual_length
    当这个 urb 被完成, 这个变量被设置为数据的真实长度, 或者由这个 urb (对于 OUT urb)发送或者由这个 urb(对于 IN urb)接受. 对于 IN urb, 这个必须被用来替代 transfer_buffer_length 变量, 因为接收的数据可能比整个缓冲大小小.

  • int status
    当这个 urb 被结束, 或者开始由 USB 核心处理, 这个变量被设置为 urb 的当前状态. 一个 USB 驱动可安全存取这个变量的唯一时间是在 urb 完成处理者函数中(在"CompletingUrbs: 完成回调处理者"一节中描述). 这个限制是阻止竞争情况, 发生在这个 urb 被 USB 核心处理当中. 对于同步 urb, 在这个变量中的一个成功的值(0)只指示是否这个 urb 已被去链. 为获得在同步 urb 上的详细状态, 应当检查 iso_frame_desc 变量。

    这个变量的有效值包括:

    • 0 :这个 urb 传送是成功的.
    • -ENOENT :这个 urb 被对 usb_kill_urb 的调用停止.
    • -ECONNRESET :urb 被对 usb_unlink_urb 的调用去链, 并且 transfer_flags 变量被设置为
      URB_ASYNC_UNLINK
    • -EINPROGRESS :这个 urb 仍然在被 USB 主机控制器处理中. 如果你的驱动曾见到这个值, 它是一个你的驱动中的 bug.
    • -EPROTO :这个 urb 发生下面一个错误:
      • 一个 bitstuff 错误在传送中发生.
      • 硬件没有及时收到响应帧.
    • -EILSEQ :在这个 urb 传送中有一个 CRC 不匹配.
    • -EPIPE
      这个端点现在被停止. 如果这个包含的端点不是一个控制端点, 这个错误可被清除
      通过一个对函数 usb_clear_halt 的调用.
    • -ECOMM
      在传送中数据接收快于能被写入系统内存. 这个错误值只对 IN urb.
    • -ENOSR
      在传送中数据不能从系统内存中获取得足够快, 以便可跟上请求的 USB 数据速率.
      这个错误只对 OUT urb.
    • -EOVERFLOW :这个 urb 发生一个"babble"错误. 一个"babble"错误发生当端点接受数据多于端
      点的特定最大报文大小.
    • -EREMOTEIO :只发生在当 URB_SHORT_NOT_OK 标志被设置在 urb 的 transfer_flags 变量, 并且意味着 urb 请求的完整数量的数据没有收到.
    • -ENODEV :这个 USB 设备现在从系统中消失.
    • -EXDEV :只对同步 urb 发生, 并且意味着传送只部分完成. 为了决定传送什么, 驱动必须
      看单独的帧状态.
    • -EINVAL :这个 urb 发生了非常坏的事情. USB 内核文档描述了这个值意味着什么:
      ISO 疯了, 如果发生这个: 退出并回家.
      它也可发生, 如果一个参数在 urb 结构中被不正确地设置了, 或者如果在提交这个 urb 给 USB 核心的 usb_submit_urb 调用中, 有一个不正确的函数参数.
    • -ESHUTDOWN :这个 USB 主机控制器驱动有严重的错误; 它现在已被禁止, 或者设备和系统去掉连接, 并且这个 urb 在设备被去除后被提交. 它也可发生当这个设备的配置改变, 而这个 urb 被提交给设备. 通常, 错误值 -EPROTO, -EILSEQ, 和 -EOVERFLOW 指示设备的硬件问题, 设备固件, 或者连接设备到计算机的线缆.
  • int start_frame
    设置或返回同步传送要使用的初始帧号.

  • int interval
    urb 被轮询的间隔. 这只对中断或者同步 urb 有效. 这个值的单位依据设备速度而不同. 对于低速和高速的设备, 单位是帧, 它等同于毫秒. 对于设备, 单位是宏帧的设备, 它等同于 1/8 微秒单位. 这个值必须被 USB 驱动设置给同步或者中断 urb, 在这个 urb 被发送到 USB 核心之前.

  • int number_of_packets
    只对同步 urb 有效, 并且指定这个 urb 要处理的同步传送缓冲的编号. 这个值必须被 USB 驱动设置给同步 urb, 在这个 urb 发送给 USB 核心之前.

  • int error_count
    被 USB 核心设置, 只给同步 urb 在它们完成之后. 它指定报告任何类型错误的同步传送的号码.

  • struct usb_iso_packet_descriptor iso_frame_desc[0]
    只对同步 urb 有效. 这个变量是组成这个 urb 的一个 struct usb_iso_packet_descriptor 结构数组. 这个结构允许单个 urb 来一次定义多个同步传送. 它也用来收集每个单独传送的传送状态。
    结构usb_iso_packet_descriptor由下列成员组成:

    • unsigned int offset :报文数据所在的传送缓冲中的偏移(第一个字节从 0 开始).
    • unsigned int length :这个报文的传送缓冲的长度.
    • unsigned int actual_length :接收到给这个同步报文的传送缓冲的数据长度.
    • unsigned int status :这个报文的单独同步传送的状态. 它可采用同样的返回值如同主 struct urb 结构的状态变量.
3.2.2 urb处理流程

USB 设备中的每个端点都处理一个 urb 队列,在队列被清空之前,一个 urb 的典型生命周期如下。

3.2.2.1. 被一个 USB 设备驱动创建。
创建 urb 结构体的函数为:
	**
	 * usb_alloc_urb - creates a new urb for a USB driver to use
	 * @iso_packets: number of iso packets for this urb
	 * @mem_flags: the type of memory to allocate, see kmalloc() for a list of
	 *	valid options for this.
	 *
	 * Creates an urb for the USB driver to use, initializes a few internal
	 * structures, increments the usage counter, and returns a pointer to it.
	 *
	 * If the driver want to use this urb for interrupt, control, or bulk
	 * endpoints, pass '0' as the number of iso packets.
	 *
	 * The driver must call usb_free_urb() when it is finished with the urb.
	 *
	 * Return: A pointer to the new urb, or %NULL if no memory is available.
	 */
	struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
	{
		struct urb *urb;
	
		urb = kmalloc(sizeof(struct urb) +
			iso_packets * sizeof(struct usb_iso_packet_descriptor),
			mem_flags);
		if (!urb)
			return NULL;
		usb_init_urb(urb);
		return urb;
	}
	EXPORT_SYMBOL_GPL(usb_alloc_urb);

iso_packets 是这个 urb 应当包含的等时数据包的数目,若为 0 表示不创建等时数据包。
mem_flags 参数是分配内存的标志,和 kmalloc()函数的分配标志参数含义相同。如果分配成功,该函数返回一个 urb 结构体指针,否则返回 0。

urb 结构体在驱动中不能静态创建,因为这可能破坏 USB 核心给 urb 使用的引用计数方法。

usb_alloc_urb()的“反函数”为:

	/**
	 * usb_free_urb - frees the memory used by a urb when all users of it are finished
	 * @urb: pointer to the urb to free, may be NULL
	 *
	 * Must be called when a user of a urb is finished with it.  When the last user
	 * of the urb calls this function, the memory of the urb is freed.
	 *
	 * Note: The transfer buffer associated with the urb is not freed unless the
	 * URB_FREE_BUFFER transfer flag is set.
	 */
	void usb_free_urb(struct urb *urb)
	{
		if (urb)
			kref_put(&urb->kref, urb_destroy);
	}
	EXPORT_SYMBOL_GPL(usb_free_urb);

该函数用于释放由 usb_alloc_urb()分配的 urb 结构体, 在这个函数被调用之后, urb 结构消失, 驱动不能再存取它。

3.2.2.2. 初始化,被安排给一个特定 USB 设备的特定端点。
3.2.2.2.1. 中断urb
  • 对于中断 urb,使用 usb_fill_int_urb()函数来初始化 urb,如下所示:

    	/**
    	 * usb_fill_int_urb - macro to help initialize a interrupt urb
    	 * @urb: pointer to the urb to initialize.
    	 * @dev: pointer to the struct usb_device for this urb.
    	 * @pipe: the endpoint pipe
    	 * @transfer_buffer: pointer to the transfer buffer
    	 * @buffer_length: length of the transfer buffer
    	 * @complete_fn: pointer to the usb_complete_t function
    	 * @context: what to set the urb context to.
    	 * @interval: what to set the urb interval to, encoded like
    	 *	the endpoint descriptor's bInterval value.
    	 *
    	 * Initializes a interrupt urb with the proper information needed to submit
    	 * it to a device.
    	 *
    	 * Note that High Speed and SuperSpeed(+) interrupt endpoints use a logarithmic
    	 * encoding of the endpoint interval, and express polling intervals in
    	 * microframes (eight per millisecond) rather than in frames (one per
    	 * millisecond).
    	 *
    	 * Wireless USB also uses the logarithmic encoding, but specifies it in units of
    	 * 128us instead of 125us.  For Wireless USB devices, the interval is passed
    	 * through to the host controller, rather than being translated into microframe
    	 * units.
    	 */
    	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;
    	}
    

    urb 参数指向要被初始化的 urb 的指针;
    dev 指向这个 urb 要被发送到的 USB 设备;
    pipe 是这个 urb 要被发送到的 USB 设备的特定端点,这个值被创建, 使用前面提过的
    usb_sndintpipe 或者 usb_rcvintpipe 函数。
    transfer_buffer 是指向发送数据或接收数据的缓冲区的指针,和 urb 一样,它也不能是静态缓冲区,必须使用 kmalloc()来分配;
    buffer_lengthtransfer_buffer 指针所指向缓冲区的大小;
    complete 指针指向当这个 urb 完成时被调用的完成处理函数;
    context 是完成处理函数的“上下文”,指向数据块的指针, 它被添加到这个 urb 结构为以后被完成处理者函数获取。
    interval 是这个 urb 应当被调度的间隔。

    上述函数参数中的 pipe 使用 usb_sndintpipe()usb_rcvintpipe()创建。

3.2.2.2.2. 批量urb / 块 urb
  • 对于批量 urb,使用 usb_fill_bulk_urb()函数来初始化 urb,如下所示:

    	/**
    	 * usb_fill_bulk_urb - macro to help initialize a bulk urb
    	 * @urb: pointer to the urb to initialize.
    	 * @dev: pointer to the struct usb_device for this urb.
    	 * @pipe: the endpoint pipe
    	 * @transfer_buffer: pointer to the transfer buffer
    	 * @buffer_length: length of the transfer buffer
    	 * @complete_fn: pointer to the usb_complete_t function
    	 * @context: what to set the urb context to.
    	 *
    	 * Initializes a bulk urb with the proper information needed to submit it
    	 * to a device.
    	 */
    	static inline void usb_fill_bulk_urb(struct urb *urb,
    					     struct usb_device *dev,
    					     unsigned int pipe,
    					     void *transfer_buffer,
    					     int buffer_length,
    					     usb_complete_t complete_fn,
    					     void *context)
    	{
    		urb->dev = dev;
    		urb->pipe = pipe;
    		urb->transfer_buffer = transfer_buffer;
    		urb->transfer_buffer_length = buffer_length;
    		urb->complete = complete_fn;
    		urb->context = context;
    	}
    

    这个函数参数和 usb_fill_int_urb 函数的都相同. 但是, 没有 interval 参数因为 bulk urb 没有间隔值. 请注意这个 unsiged int pipe 变量必须被初始化用对 usb_sndbulkpipe 或者 usb_rcvbulkpipe 函数的调用。

    usb_fill_int_urb 函数不设置 urb 中的 transfer_flags 变量, 因此任何对这个成员的修改不得不由这个驱动自己完成.

3.2.2.2.3. 控制urb
  • 对于控制 urb,使用 usb_fill_control_urb() 函数来初始化 urb,如下所示:

    /**
    		 * usb_fill_control_urb - initializes a control urb
    		 * @urb: pointer to the urb to initialize.
    		 * @dev: pointer to the struct usb_device for this urb.
    		 * @pipe: the endpoint pipe
    		 * @setup_packet: pointer to the setup_packet buffer
    		 * @transfer_buffer: pointer to the transfer buffer
    		 * @buffer_length: length of the transfer buffer
    		 * @complete_fn: pointer to the usb_complete_t function
    		 * @context: what to set the urb context to.
    		 *
    		 * Initializes a control urb with the proper information needed to submit
    		 * it to a device.
    		 */
    		static inline void usb_fill_control_urb(struct urb *urb,
    							struct usb_device *dev,
    							unsigned int pipe,
    							unsigned char *setup_packet,
    							void *transfer_buffer,
    							int buffer_length,
    							usb_complete_t complete_fn,
    							void *context)
    		{
    			urb->dev = dev;
    			urb->pipe = pipe;
    			urb->setup_packet = setup_packet;
    			urb->transfer_buffer = transfer_buffer;
    			urb->transfer_buffer_length = buffer_length;
    			urb->complete = complete_fn;
    			urb->context = context;
    		}
    

    函数参数和 usb_fill_bulk_urb 函数都相同, 除了有个新参数, unsigned char *setup_packet, 它必须指向要发送给端点的 setup 报文数据. 还有, unsigned int pipe 变量必须被初始化, 使用对 usb_sndctrlpipe 或者 usb_rcvictrlpipe 函数的调用。

    usb_fill_control_urb 函数不设置 transfer_flags 变量在 urb 中, 因此任何对这个成员的修改必须游驱动自己完成. 大部分驱动不使用这个函数, 因为使用在"USB 传送不用 urb"一节中介绍的同步 API 调用更简单.

3.2.2.2.4. 等时urb / 同步 urb
  • 等时 urb 没有像中断、控制和批量 urb 的初始化函数,因此它们必须在驱动中"手动"初始化, 在它们可被提交给 USB 核心之前. 下面是一个如何正确初始化这类 urb 的例子. 它是从 konicawc.c 内核驱动中取得的, 它位于主内核源码树的 drivers/usb/media 目录.
3.2.2.3. 被 USB 设备驱动提交给 USB 核心

在完成第(1)、(2)步的创建和初始化 urb 后,urb 便可以提交给 USB 核心,通过usb_submit_urb()函数来完成,如下所示:

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

EXPORT_SYMBOL_GPL(usb_submit_urb);

urb 参数是指向 urb 的指针,mem_flags 参数与传递给 kmalloc()函数参数的意义相同,它用于
告知 USB 核心如何在此时分配内存缓冲区。

在提交 urb 到 USB 核心后,直到完成函数被调用之前,不要访问 urb 中的任何成员。

usb_submit_urb()在原子上下文和进程上下文中都可以被调用,mem_flags 变量需根据调用环
境进行相应的设置,如下所示。

  • GFP_ATOMIC:在中断处理函数、底半部、tasklet、定时器处理函数以及 urb 完成函数中,在调用者持有自旋锁或者读写锁时以及当驱动将 current→state 修改为非 TASK_RUNNING 时,应使用此标志。
    这个值应当被使用无论何时下面的是真:
    • 调用者处于一个 urb 完成处理者, 一个中断, 一个后半部, 一个 tasklet, 或者一个时钟回调。
    • 调用者持有一个自旋锁或者读写锁. 注意如果正持有一个旗标, 这个值不必要。
    • current->state 不是 TASK_RUNNING. 状态一直是 TASK_RUNNING 除非驱动已自己改变current状态。
  • GFP_NOIO:在存储设备的块 I/O 和错误处理路径中,应使用此标志;
  • GFP_KERNEL:如果没有任何理由使用 GFP_ATOMICGFP_NOIO,就使用 GFP_KERNEL

如果 usb_submit_urb()调用成功,即 urb 的控制权被移交给 USB 核心,该函数返回 0;否则,返回错误号。

3.2.2.4. 提交由 USB 核心指定的 USB 主机控制器驱动。
3.2.2.5. USB 主机控制器处理,进行一次到 USB 设备的传送。

第(4)~(5)步由 USB 核心和主机控制器完成,不受 USB 设备驱动的控制。

3.2.2.6. 当 urb 完成,USB 主机控制器驱动通知 USB 设备驱动

在如下 3 种情况下,urb 将结束,urb 完成函数将被调用。

  • urb 被成功发送给设备,并且设备返回正确的确认。如果 urb→status0,意味着对于一个输出 urb,数据被成功发送;对于一个输入 urb,请求的数据被成功收到。
  • 如果发送数据到设备或从设备接收数据时发生了错误,urb→status 将记录错误值。
  • urb 被从 USB 核心“去除连接”,这发生在驱动通过 usb_unlink_urb()usb_kill_urb() 函数取消 urb,或 urb 虽已提交,而 USB 设备被拔出的情况下。

usb_unlink_urb()usb_kill_urb() 这两个函数用于取消已提交的 urb,其参数为要被取消的 urb指针。对 usb_unlink_urb() 而言,如果 urb 结构体中的 URB_ASYNC_UNLINK(即异步 unlink)的标志被置位,则对该 urbusb_unlink_urb()调用将立即返回,具体的 unlink 动作将在后台进行。
否则,此函数一直等到 urb 被解开链接或结束时才返回。usb_kill_urb()会彻底终止 urb 的生命周期,它通常在设备的 disconnect() 函数中被调用。

urb 生命结束时(处理完成或被解除链接),通过 urb 结构体的 status 成员可以获知其原因,如 :
* 0 表示传输成功。
* -ENOENT 表示被 usb_kill_urb()杀死。
* -ECONNRESET 表示被 usb_unlink_urb() 杀死。
* -EPROTO 表示传输中发生了 bitstuff 错误或者硬件未能及时收到响应数据包。
* -ENODEV表示 USB 设备已被移除。
* -EXDEV 表示等时传输仅完成了一部分等。

对一些驱动, 应当用 usb_unlink_urb 函数来告知 USB 核心去停止 urb. 这个函数在返回到调用者之前不等待这个 urb 完全停止. 这对于在中断处理或者持有一个自旋锁时停止urb时是有用的, 因为等待一个 urb 完全停止需要 USB 核心有能力使调用进程睡眠. 为了正确工作这个函数要求URB_ASYNC_UNLINK 标志值被设置在正被要求停止的 urb 中.

对以上 urb 的处理步骤进行一个总结,下图给出了一个 urb 的整个处理流程,虚线框的usb_unlink_urb()usb_kill_urb()并非一定会发生,它只是在 urb 正在被 USB 核心和主机控制器处理时,被驱动程序取消的情况下才发生。

在这里插入图片描述

3.2.3 简单的批量与控制 urb / 无 urb 的 USB 传送

有时 USB 驱动程序只是从 USB 设备上接收或向 USB 设备发送一些简单的数据,这时候,没有必要将 urb 创建、初始化、提交、完成处理的整个流程走一遍,而可以使用两个更简单的函数,如下示。

3.2.3.1. usb_bulk_msg()

usb_bulk_msg()函数创建一个 USB 批量 urb 并将它发送到特定设备,这个函数是同步的,它一直等待 urb 完成后才返回。usb_bulk_msg()函数的原型为:

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
			 void *data, int len, int *actual_length,
			 int timeout);
  • usb_dev 参数为批量消息要发送的 USB 设备的指针。
  • pipe 为批量消息要发送到的 USB 设备的端点,这个块消息要发送到的 USB 设备的特定端点. 这个值被创建, 使用一个对 usb_sndbulkpipe 或者 usb_rcvbulkpipe 的调用。
  • data 参数为指向要发送或接收的数据缓冲区的指针,
    如果这是一个 OUT 端点, 指向要发送到设备的数据的指针。
    如果是一个IN端点,这是一个在被从设备读出后数据应当被放置的地方的指针。
  • len 参数为 data 参数所指向的缓冲区的长度。
  • actual_length 用于返回实际发送或接收的字节数。
  • timeout 是发送超时,以 jiffies 为单位,0 意味着永远等待。

如果函数调用成功,返回 0;否则,返回 1 个负的错误值,这错误号匹配之前在"urb 结构体"一节中描述的错误号. 如果成功, actual_length 参数包含被传送或从消息中获取的字节数。

eg:

/* do a blocking bulk read to get data from the device */ 
retval = usb_bulk_msg(dev->udev, 
 					usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), 
 					dev->bulk_in_buffer, 
 					min(dev->bulk_in_size, count), 
 					&count, HZ*10); 
/* if the read was successful, copy the data to user space */ 
if (!retval) { 
		if (copy_to_user(buffer, dev->bulk_in_buffer, count)) 
			 	retval = -EFAULT; 
		 else 
			   retval = count; 
} 

这个例子展示了一个简单的从一个 IN 端点的块读. 如果读取成功, 数据接着被拷贝到用
户空间. 这个典型地是在 USB 驱动的读函数中完成.

注意:
usb_bulk_msg 函数不能被从中断上下文调用, 或者持有一个自旋锁。
还有, 这个函数不能被任何其他函数取消, 因此当使用它时小心;
确认你的驱动的去连接知道足够多来等待调用结束, 在允许它自己被从内存中卸载之前。

3.2.3.2. usb_control_msg()函数

usb_control_msg() 函数与 usb_bulk_msg() 函数类似,不过它提供驱动发送和结束 USB 控制信息而非批量信息的能力,该函数的原型为:

int usb_control_msg(struct usb_device *dev, unsigned int pipe, _ _u8 request, 
_ _u8 requesttype, _ _u16 value, _ _u16 index, void *data, _ _u16 size, int timeout); 
  • dev 指向控制消息发往的 USB 设备。
  • pipe 是控制消息要发往的 USB 设备的端点,这个值在 usb_sndctrlpipe 或者 usb_rcvctrlpipe 函数中被创建。
  • request 是这个控制消息的 USB 请求值。
  • requesttype 是这个控制消息的 USB 请求类型。
  • value 是这个控制消息的 USB 消息值。
  • index 是这个控制消息的 USB 消息索引值。
  • data 指向要发送或接收的数据缓冲区,
    如果是一个 OUT 端点, 是一个指向要发送到设备的数据的指针。
    如果是一个 IN 端点, 是一个在被从设备读取后数据被放置的地方的指针。
  • sizedata 参数所指向的缓冲区的大小,
  • timeout 是发送超时,以 jiffies 为单位,0 意味着永远等待。

如果函数是成功的, 它返回被传送到或从这个设备的字节数. 如果它不成功, 它返回一个负错误码。

参数 requestrequesttypevalueindex 与 《USB 规范》中定义的 USB 控制消息直接对应, 对于更多的关于这些参数的有效值的信息和它们如何被使用, 见 《USB 规范》的第 9 章。

usb_bulk_msg()usb_control_msg()函数的使用要特别慎重,由于它们是同步的,因此不能在中断上下文和持有自旋锁的情况下使用。而且,该函数也不能被任何其他函数取消,因此,务必要使得驱动程序的 disconnect() 函数掌握足够的信息,以判断和等待该调用的结束。

3.2.3.3. 使用 USB 数据函数

USB 核心中的几个帮忙函数可用来从所有的 USB 设备中存取标准信息。这些函数不能从中断上下文或者持有自旋锁时调用。

函数 usb_get_descriptor 获取指定的 USB 描述符从特定的设备。 这个函数被定义为:

int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size); 

这个函数可被一个 USB 驱动用来从 struct usb_device 结构中, 获取任何还没有在 struct usb_devicestruct usb_interface 结构中出现的设备描述符, 例如声音描述符或者其他类的特定消息。
这个函数的参数是:

  • struct usb_device *usb_dev :指向应当从中获取描述符的 USB 设备的指针
  • unsigned char type :描述符类型. 这个类型在 USB 规范中描述, 并且是下列类型之一:
    USB_DT_DEVICE USB_DT_CONFIG USB_DT_STRING USB_DT_INTERFACE USB_DT_ENDPOINT
    USB_DT_DEVICE_QUALIFIER USB_DT_OTHER_SPEED_CONFIG USB_DT_INTERFACE_POWER USB_DT_OTG ``USB_DT_DEBUG USB_DT_INTERFACE_ASSOCIATION USB_DT_CS_DEVICE USB_DT_CS_CONFIG USB_DT_CS_STRING USB_DT_CS_INTERFACE USB_DT_CS_ENDPOINT
  • unsigned char index:应当从设备获取的描述符的数目。
  • void *buf :你拷贝描述符到的缓冲的指针。
  • int size:由 buf 变量指向的内存的大小。

如果这个函数成功, 它返回从设备读取的字节数, 否则, 它返回由它所调用的底层函数 usb_control_msg 所返回的一个负错误值。

usb_get_descripter 调用的一项最普遍的用法是从 USB 设备获取一个字符串. 因为这个是非常普遍, 有一个帮忙函数称为 usb_get_string

int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size); 

如果成功, 这个函数返回设备收到的给这个字符串的字节数. 否则, 它返回一个由这个函数调用的底层函数 usb_control_msg 返回的负错误值。

如果这个函数成功, 它返回一个以 UTF-16LE 格式编码的字符串(Unicode, 16 位每字符, 小端字节序)在 buf 参数指向的缓冲中. 因为这个格式不是非常有用, 有另一个函数, 称为 usb_string, 它返回一个从一个 USB 设备读来的字符串, 并且已经转换为一个 ISO 8859-1 格式字符串. 这个字符集是一个 8 位的 UICODE 的子集, 并且是最普遍的英文和其他西欧字符串格式. 因为这是 USB 设备的字符串的典型格式, 建议 usb_string 函数来替代 usb_get_string 函数。

3.3 探测和断开函数

在之前章节描述的 struct usb_driver 结构中, 驱动指定 2 个 USB 核心在合适的时候调用的函数. 探测函数被调用, 当设备被安装时, USB 核心认为这个驱动应当处理; 探测函数应当进行检查传递给它的关于设备的信息, 并且决定是否驱动真正合适那个设备. 去连接函数被调用当驱动应当不再控制设备, 由于某些理由, 并且可做清理。

探测和去连接函数回调都在 USB 集线器内核线程上下文中被调用, 因此它们中睡眠是合法的. 但是, 建议如果有可能大部分工作应当在设备被用户打开时完成. 为了保持 USB 探测时间为最小. 这是因为 USB 核心处理 USB 设备的添加和去除在一个线程中, 因此任何慢设备驱动可导致 USB 设备探测时间慢下来并且用户可注意到.

在探测函数回调中, USB 驱动应当初始化任何它可能使用来管理 USB 设备的本地结构.
它还应当保存任何它需要的关于设备的信息到本地结构, 因为在此时做这些通常更容易.

在 USB 设备驱动 usb_driver 结构体的探测函数中,应该完成如下工作。

  • 探测设备的端点地址、缓冲区大小,初始化任何可能用于控制 USB 设备的数据结构。

  • 把已初始化数据结构的指针保存到接口设备中。
    usb_set_intfdata() 函数可以设置 usb_interface 的私有数据,这个函数的原型为:

    void usb_set_intfdata (struct usb_interface *intf, void *data);
    

    这个函数的“反函数”用于得到 usb_interface 的私有数据,其原型为:

    void *usb_get_intfdata (struct usb_interface *intf);
    
  • 注册 USB 设备。
    如果是简单的字符设备,调用 usb_register_dev(),这个函数的原型为:

    int usb_register_dev(struct usb_interface *intf,
    			 struct usb_class_driver *class_driver);
    

    上述函数中第二个参数为usb_class_driver 结构体,这个结构体的定义为:

      struct usb_class_driver {
      char *name; /*sysfs 中用来描述设备名*/
      struct file_operations *fops;/*文件操作结构体指针*/
      int minor_base; /*开始次设备号*/
      };
    

    对于字符设备而言,usb_class_driver 结构体的 fops 成员中的 write()read()ioctl()等函数的地位完全等同于 file_operations 成员函数。
    如果是其他类型的设备,如 tty 设备,则调用对应设备的注册函数。
    在 USB 设备驱动 usb_driver 结构体的探测函数中,应该完成如下工作。

  • 释放所有为设备分配的资源。

  • 设置接口设备的数据指针为 NULL

  • 注销 USB 设备。

对于字符设备,可以直接调用 usb_register_dev() 函数的“反函数”,如下所示:

void usb_deregister_dev(struct usb_interface *intf,
 				struct usb_class_driver *class_driver);

对于其他类型的设备,如 tty 设备,则调用对应设备的注销函数。

3.4 USB骨架程序

Linux 内核源代码中的 driver/usb/usb-skeleton.c 文件为我们提供了一个最基础的 USB 驱动程序,即 USB 骨架程序,可被看做一个最简单的 USB 设备驱动实例。尽管具体 USB 设备驱动千差万别,但其骨架则万变不离其宗。

3.4.1 USB 骨架程序的 usb_driver 结构体

首先看看 USB 骨架程序的 usb_driver 结构体定义

static struct usb_driver skel_driver = {
	.name =		"skeleton",
	.probe =	skel_probe,
	.disconnect =	skel_disconnect,
	.suspend =	skel_suspend,
	.resume =	skel_resume,
	.pre_reset =	skel_pre_reset,
	.post_reset =	skel_post_reset,
	.id_table =	skel_table,
	.supports_autosuspend = 1,
};

module_usb_driver(skel_driver);
3.4.2 USB 骨架程序的 skel_table

从上述代码可以看出,它所支持的 USB 设备的列表数组为 skel_table[],其定义如

/* table of devices that work with this driver */
static const struct usb_device_id skel_table[] = {
	{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
	{ }					/* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, skel_table);
3.4.3 USB 骨架程序的模块加载与卸载函数

对上述 usb_driver 的注册和注销发生在 USB 骨架程序的模块加载与卸载函数内,如代码如下,,其分别调用了 usb_register()usb_deregister()

    static int __init usb_skel_init(void)
    {
            int result;

            /* register this driver with the USB subsystem */
            result = usb_register(&skel_driver);
            if (result < 0) {
                    err("usb_register failed for the "__FILE__ "driver."
                        "Error number %d", result);
                    return -1;
            }

            return 0;
    }
    module_init(usb_skel_init);

    static void __exit usb_skel_exit(void)
    {
            /* deregister this driver with the USB subsystem */
            usb_deregister(&skel_driver);
    }
    module_exit(usb_skel_exit);

3.4.4 USB 骨架程序的探测函数skel_probe

usb_driverprobe() 成员函数中,会根据 usb_interface 的成员寻找第一个批量输入和输出端点,
将端点地址、缓冲区等信息存入为 USB 骨架程序定义的 usb_skel 结构体,并将 usb_skel 实例的指针传入 usb_set_intfdata() 作为 USB 接口的私有数据,最后,它会注册 USB 设备

static int skel_probe(struct usb_interface *interface,
		      const struct usb_device_id *id)
{
	struct usb_skel *dev;
	struct usb_endpoint_descriptor *bulk_in, *bulk_out;
	int retval;

	/* allocate memory for our device state and initialize it */
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return -ENOMEM;

	kref_init(&dev->kref);
	sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
	mutex_init(&dev->io_mutex);
	spin_lock_init(&dev->err_lock);
	init_usb_anchor(&dev->submitted);
	init_waitqueue_head(&dev->bulk_in_wait);

	dev->udev = usb_get_dev(interface_to_usbdev(interface));
	dev->interface = usb_get_intf(interface);

	/* set up the endpoint information */
	/* use only the first bulk-in and bulk-out endpoints */
	retval = usb_find_common_endpoints(interface->cur_altsetting,
			&bulk_in, &bulk_out, NULL, NULL);
	if (retval) {
		dev_err(&interface->dev,
			"Could not find both bulk-in and bulk-out endpoints\n");
		goto error;
	}

	dev->bulk_in_size = usb_endpoint_maxp(bulk_in);
	dev->bulk_in_endpointAddr = bulk_in->bEndpointAddress;
	dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
	if (!dev->bulk_in_buffer) {
		retval = -ENOMEM;
		goto error;
	}
	dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!dev->bulk_in_urb) {
		retval = -ENOMEM;
		goto error;
	}

	dev->bulk_out_endpointAddr = bulk_out->bEndpointAddress;

	/* save our data pointer in this interface device */
	usb_set_intfdata(interface, dev);

	/* we can register the device now, as it is ready */
	retval = usb_register_dev(interface, &skel_class);
	if (retval) {
		/* something prevented us from registering this driver */
		dev_err(&interface->dev,
			"Not able to get a minor for this device.\n");
		usb_set_intfdata(interface, NULL);
		goto error;
	}

	/* let the user know what node this device is now attached to */
	dev_info(&interface->dev,
		 "USB Skeleton device now attached to USBSkel-%d",
		 interface->minor);
	return 0;

error:
	/* this frees allocated memory */
	kref_put(&dev->kref, skel_delete);

	return retval;
}
3.4.5 USB 骨架程序的自定义数据结构usb_skel

usb_skel结构体可以被看作一个私有数据结构体,应该根据具体的设备量身定制。

/* Structure to hold all of our device specific stuff */
struct usb_skel {
	struct usb_device	*udev;			/* the usb device for this device */
	struct usb_interface	*interface;		/* the interface for this device */
	struct semaphore	limit_sem;		/* limiting the number of writes in progress */
	struct usb_anchor	submitted;		/* in case we need to retract our submissions */
	struct urb		*bulk_in_urb;		/* the urb to read data with */
	unsigned char           *bulk_in_buffer;	/* the buffer to receive data */
	size_t			bulk_in_size;		/* the size of the receive buffer */
	size_t			bulk_in_filled;		/* number of bytes in the buffer */
	size_t			bulk_in_copied;		/* already copied to user space */
	__u8			bulk_in_endpointAddr;	/* the address of the bulk in endpoint */
	__u8			bulk_out_endpointAddr;	/* the address of the bulk out endpoint */
	int			errors;			/* the last request tanked */
	bool			ongoing_read;		/* a read is going on */
	spinlock_t		err_lock;		/* lock for errors */
	struct kref		kref;
	struct mutex		io_mutex;		/* synchronize I/O with disconnect */
	unsigned long		disconnected:1;
	wait_queue_head_t	bulk_in_wait;		/* to wait for an ongoing read */
};
#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
3.4.6 USB 骨架程序的断开函数skel_disconnect

USB 骨架程序的断开函数会完成探测函数相反的工作,即设置接口数据为 NULL,注销 USB设备.

static void skel_disconnect(struct usb_interface *interface)
{
	struct usb_skel *dev;
	int minor = interface->minor;

	dev = usb_get_intfdata(interface);
	usb_set_intfdata(interface, NULL);

	/* give back our minor */
	usb_deregister_dev(interface, &skel_class);

	/* prevent more I/O from starting */
	mutex_lock(&dev->io_mutex);
	dev->disconnected = 1;
	mutex_unlock(&dev->io_mutex);

	usb_kill_anchored_urbs(&dev->submitted);

	/* decrement our usage count */
	kref_put(&dev->kref, skel_delete);

	dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor);
}
3.4.7 USB 骨架程序的字符设备文件操作结构体skel_fops

usb_register_dev(interface, &skel_class)中第二个参数包含了字符设备的 file_operations 结构体指针,而这个结构体中的成员实现也是 USB 字符设备的另一个组成成分。 USB 骨架程序的字符设备文件操作 file_operations 结构体的定义如下。

static const struct file_operations skel_fops = {
	.owner =	THIS_MODULE,
	.read =		skel_read,
	.write =	skel_write,
	.open =		skel_open,
	.release =	skel_release,
	.flush =	skel_flush,
	.llseek =	noop_llseek,
};
3.4.8 USB 骨架程序的字符设备打开函数skel_open

由于只是一个象征性的骨架程序,open()成员函数的实现非常简单,它根据 usb_driver 和次设备号通过 usb_find_interface() 获得 USB 接口,之后通过 usb_get_intfdata() 获得接口的私有数据并赋予 file→private_data

static int skel_open(struct inode *inode, struct file *file)
{
	struct usb_skel *dev;
	struct usb_interface *interface;
	int subminor;
	int retval = 0;

	subminor = iminor(inode);

	interface = usb_find_interface(&skel_driver, subminor);
	if (!interface) {
		pr_err("%s - error, can't find device for minor %d\n",
			__func__, subminor);
		retval = -ENODEV;
		goto exit;
	}

	dev = usb_get_intfdata(interface);
	if (!dev) {
		retval = -ENODEV;
		goto exit;
	}

	retval = usb_autopm_get_interface(interface);
	if (retval)
		goto exit;

	/* increment our usage count for the device */
	kref_get(&dev->kref);

	/* save our object in the file's private structure */
	file->private_data = dev;

exit:
	return retval;
}

由于在 open() 函数中并没有申请任何软件和硬件资源,因此与 open() 函数对应的 release() 函数不用进行资源的释放,它进行减少在 open() 中增加的引用计数等工作。

3.4.9 USB 骨架程序的字符设备写函数skel_write

接下来要分析的是读写函数,前面已经提到,在访问 USB 设备的时候,贯穿于其中的“中枢神经”是 urb 结构体。

skel_write() 函数中进行的关于 urb 的操作与 3.2 小节的描述完全对应,即进行了 urb 的分配(调用 usb_alloc_urb())、初始化(调用 usb_fill_bulk_urb())和提交(调用 usb_submit_urb())的操作。

static ssize_t skel_write(struct file *file, const char *user_buffer,
			  size_t count, loff_t *ppos)
{
	struct usb_skel *dev;
	int retval = 0;
	struct urb *urb = NULL;
	char *buf = NULL;
	size_t writesize = min(count, (size_t)MAX_TRANSFER);

	dev = file->private_data;

	/* verify that we actually have some data to write */
	if (count == 0)
		goto exit;

	/*
	 * limit the number of URBs in flight to stop a user from using up all
	 * RAM
	 */
	if (!(file->f_flags & O_NONBLOCK)) {
		if (down_interruptible(&dev->limit_sem)) {
			retval = -ERESTARTSYS;
			goto exit;
		}
	} else {
		if (down_trylock(&dev->limit_sem)) {
			retval = -EAGAIN;
			goto exit;
		}
	}

	spin_lock_irq(&dev->err_lock);
	retval = dev->errors;
	if (retval < 0) {
		/* any error is reported once */
		dev->errors = 0;
		/* to preserve notifications about reset */
		retval = (retval == -EPIPE) ? retval : -EIO;
	}
	spin_unlock_irq(&dev->err_lock);
	if (retval < 0)
		goto error;

	/* create a urb, and a buffer for it, and copy the data to the urb */
	urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!urb) {
		retval = -ENOMEM;
		goto error;
	}

	buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL,
				 &urb->transfer_dma);
	if (!buf) {
		retval = -ENOMEM;
		goto error;
	}

	if (copy_from_user(buf, user_buffer, writesize)) {
		retval = -EFAULT;
		goto error;
	}

	/* this lock makes sure we don't submit URBs to gone devices */
	mutex_lock(&dev->io_mutex);
	if (dev->disconnected) {		/* disconnect() was called */
		mutex_unlock(&dev->io_mutex);
		retval = -ENODEV;
		goto error;
	}

	/* initialize the urb properly */
	usb_fill_bulk_urb(urb, dev->udev,
			  usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
			  buf, writesize, skel_write_bulk_callback, dev);
	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
	usb_anchor_urb(urb, &dev->submitted);

	/* send the data out the bulk port */
	retval = usb_submit_urb(urb, GFP_KERNEL);
	mutex_unlock(&dev->io_mutex);
	if (retval) {
		dev_err(&dev->interface->dev,
			"%s - failed submitting write urb, error %d\n",
			__func__, retval);
		goto error_unanchor;
	}

	/*
	 * release our reference to this urb, the USB core will eventually free
	 * it entirely
	 */
	usb_free_urb(urb);


	return writesize;

error_unanchor:
	usb_unanchor_urb(urb);
error:
	if (urb) {
		usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma);
		usb_free_urb(urb);
	}
	up(&dev->limit_sem);

exit:
	return retval;
}
3.4.10 USB 骨架程序的字符设备写操作完成函数skel_write_bulk_callback

写函数中发起的 urb 结束后,其完成函数 skel_write_bulk_callback() 将被调用,它会进行urb→status 的判断。

static void skel_write_bulk_callback(struct urb *urb)
{
	struct usb_skel *dev;
	unsigned long flags;

	dev = urb->context;

	/* sync/async unlink faults aren't errors */
	if (urb->status) {
		if (!(urb->status == -ENOENT ||
		    urb->status == -ECONNRESET ||
		    urb->status == -ESHUTDOWN))
			dev_err(&dev->interface->dev,
				"%s - nonzero write bulk status received: %d\n",
				__func__, urb->status);

		spin_lock_irqsave(&dev->err_lock, flags);
		dev->errors = urb->status;
		spin_unlock_irqrestore(&dev->err_lock, flags);
	}

	/* free up our allocated buffer */
	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
			  urb->transfer_buffer, urb->transfer_dma);
	up(&dev->limit_sem);
}
3.4.11 USB 骨架程序的字符设备读函数skel_read

USB 骨架程序的字符设备读函数并没有进行类似写函数的一系列针对 urb 的操作,而是简单
地调用 usb_bulk_msg() 发起一次同步 urb 传输操作

static void skel_read_bulk_callback(struct urb *urb)
{
	struct usb_skel *dev;
	unsigned long flags;

	dev = urb->context;

	spin_lock_irqsave(&dev->err_lock, flags);
	/* sync/async unlink faults aren't errors */
	if (urb->status) {
		if (!(urb->status == -ENOENT ||
		    urb->status == -ECONNRESET ||
		    urb->status == -ESHUTDOWN))
			dev_err(&dev->interface->dev,
				"%s - nonzero write bulk status received: %d\n",
				__func__, urb->status);

		dev->errors = urb->status;
	} else {
		dev->bulk_in_filled = urb->actual_length;
	}
	dev->ongoing_read = 0;
	spin_unlock_irqrestore(&dev->err_lock, flags);

	wake_up_interruptible(&dev->bulk_in_wait);
}

3.5 USB键盘驱动

在 Linux 系统中,键盘被认定为标准输入设备,对于一个 USB 键盘而言,其驱动主要由两部分组成:usb_driver 的成员函数以及输入设备驱动的 input_event 获取和报告。

3.5.1 USB 键盘设备驱动的模块加载与卸载函数及 usb_driver 结构体usb_kbd_init

在 USB 键盘设备驱动的模块加载和卸载函数中,将分别注册和注销对应于 USB 键盘的 usb_driver 结构体 usb_kbd_driver,为模块加载与卸载函数以及 usb_kbd_driver 结构体的定义如下所示

static const struct usb_device_id usb_kbd_id_table[] = {
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
		USB_INTERFACE_PROTOCOL_KEYBOARD) },
	{ }						/* Terminating entry */
};

MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);

static struct usb_driver usb_kbd_driver = {
	.name =		"usbkbd",
	.probe =	usb_kbd_probe,
	.disconnect =	usb_kbd_disconnect,
	.id_table =	usb_kbd_id_table,
};

module_usb_driver(usb_kbd_driver);
3.5.2 USB 键盘设备驱动的探测函数usb_kbd_probe

usb_driver 的探测函数中,将进行 input 设备的初始化和注册,USB 键盘要使用的中断 urb 和控制 urb 的初始化,并设置接口的私有数据。

static int usb_kbd_probe(struct usb_interface *iface,
			 const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev(iface);
	struct usb_host_interface *interface;
	struct usb_endpoint_descriptor *endpoint;
	struct usb_kbd *kbd;
	struct input_dev *input_dev;
	int i, pipe, maxp;
	int error = -ENOMEM;

	interface = iface->cur_altsetting;

	if (interface->desc.bNumEndpoints != 1)
		return -ENODEV;

	endpoint = &interface->endpoint[0].desc;
	if (!usb_endpoint_is_int_in(endpoint))
		return -ENODEV;

	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

	kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
	input_dev = input_allocate_device();/* 分配 input_dev 结构体 */
	if (!kbd || !input_dev)
		goto fail1;

	if (usb_kbd_alloc_mem(dev, kbd))
		goto fail2;

	kbd->usbdev = dev;
	kbd->dev = input_dev;
	spin_lock_init(&kbd->leds_lock);

	if (dev->manufacturer)
		strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));

	if (dev->product) {
		if (dev->manufacturer)
			strlcat(kbd->name, " ", sizeof(kbd->name));
		strlcat(kbd->name, dev->product, sizeof(kbd->name));
	}

	if (!strlen(kbd->name))
		snprintf(kbd->name, sizeof(kbd->name),
			 "USB HIDBP Keyboard %04x:%04x",
			 le16_to_cpu(dev->descriptor.idVendor),
			 le16_to_cpu(dev->descriptor.idProduct));

	usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
	strlcat(kbd->phys, "/input0", sizeof(kbd->phys));

	/* 输入设备初始化 */
	input_dev->name = kbd->name;
	input_dev->phys = kbd->phys;
	usb_to_input_id(dev, &input_dev->id);
	input_dev->dev.parent = &iface->dev;

	input_set_drvdata(input_dev, kbd);

	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
		BIT_MASK(EV_REP);
	input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
		BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |
		BIT_MASK(LED_KANA);

	for (i = 0; i < 255; i++)
		set_bit(usb_kbd_keycode[i], input_dev->keybit);
	clear_bit(0, input_dev->keybit);

	input_dev->event = usb_kbd_event;
	input_dev->open = usb_kbd_open;
	input_dev->close = usb_kbd_close;

	/* 初始化中断 urb */
	usb_fill_int_urb(kbd->irq, dev, pipe,
			 kbd->new, (maxp > 8 ? 8 : maxp),
			 usb_kbd_irq, kbd, endpoint->bInterval);
	kbd->irq->transfer_dma = kbd->new_dma;
	kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

	kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
	kbd->cr->bRequest = 0x09;
	kbd->cr->wValue = cpu_to_le16(0x200);
	kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
	kbd->cr->wLength = cpu_to_le16(1);

	/* 初始化控制 urb */
	usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
			     (void *) kbd->cr, kbd->leds, 1,
			     usb_kbd_led, kbd);
	kbd->led->transfer_dma = kbd->leds_dma;
	kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

	error = input_register_device(kbd->dev); /* 注册输入设备 */
	if (error)
		goto fail2;

	usb_set_intfdata(iface, kbd);/* 设置接口私有数据 */
	device_set_wakeup_enable(&dev->dev, 1);
	return 0;

fail2:	
	usb_kbd_free_mem(dev, kbd);
fail1:	
	input_free_device(input_dev);
	kfree(kbd);
	return error;
}
3.5.3 USB 键盘设备驱动的断开函数usb_kbd_disconnect

usb_driver 的断开函数中,将设置接口私有数据为 NULL、终止已提交的 urb 并注销输入
设备

static void usb_kbd_disconnect(struct usb_interface *intf)
{
	struct usb_kbd *kbd = usb_get_intfdata (intf);

	usb_set_intfdata(intf, NULL);/* 设置接口私有数据为 NULL */
	if (kbd) {
		usb_kill_urb(kbd->irq);/* 终止 urb */
		input_unregister_device(kbd->dev);/* 注销输入设备 */
		usb_kill_urb(kbd->led);
		usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
		kfree(kbd);
	}
}
3.5.4 USB 键盘设备驱动的中断 urb 完成函数usb_kbd_irq

键盘主要依赖于中断传输模式,在键盘中断 urb 的完成函数 usb_kbd_irq() 中(通过usb_kbd_probe中可以看出),将会通过 input_report_key() 报告按键事件,通过 input_sync()报告同步事件,

static void usb_kbd_irq(struct urb *urb)
{
	struct usb_kbd *kbd = urb->context;
	int i;

	switch (urb->status) {
	case 0:			/* success */
		break;
	case -ECONNRESET:	/* unlink */
	case -ENOENT:
	case -ESHUTDOWN:
		return;
	/* -EPIPE:  should clear the halt */
	default:		/* error */
		goto resubmit;
	}
	 /*获得键盘扫描码并报告按键事件*/
	for (i = 0; i < 8; i++)
		input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);

	for (i = 2; i < 8; i++) {

		if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
			if (usb_kbd_keycode[kbd->old[i]])
				input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
			else
				hid_info(urb->dev,
					 "Unknown key (scancode %#x) released.\n",
					 kbd->old[i]);
		}

		if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
			if (usb_kbd_keycode[kbd->new[i]])
				input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
			else
				hid_info(urb->dev,
					 "Unknown key (scancode %#x) pressed.\n",
					 kbd->new[i]);
		}
	}

	input_sync(kbd->dev);

	memcpy(kbd->old, kbd->new, 8);

resubmit:
	i = usb_submit_urb (urb, GFP_ATOMIC);
	if (i)
		hid_err(urb->dev, "can't resubmit intr, %s-%s/input0, status %d",
			kbd->usbdev->bus->bus_name,
			kbd->usbdev->devpath, i);
}

从 USB 键盘驱动的例子中,我们进一步看到了 usb_driver 本身只是起一个挂接总线的作用,而具体的设备类型的驱动仍然是工作的主体,例如键盘就是 input、USB 串口就是 tty,只是在这些设备底层进行硬件访问的时候,调用的都是 URB 相关的接口,URB 这套 USB 核心层 API 的存在,使我们无需关心底层 USB 主机控制器的具体细节,因此,USB 设备驱动也变得与平台无关,同样的驱动可应用于不同的 SoC。

4. USB UDC与gadget驱动

4.1 UDC与gadget驱动关键数据结构与API

这里的 USB 设备控制器(UDC)驱动指作为其他 USB 主机控制器外设的 USB 硬件设备上底层硬件控制器的驱动,该硬件和驱动负责将一个 USB 设备依附于一个 USB 主机控制器上。例如,当某运行 Linux 的手机作为 PC 的 U 盘时,手机中的底层 USB 控制器行使 USB 设备控制器的功能,这时候运行在底层的是 UDC 驱动,而手机要成为 U 盘,在 UDC 驱动之上仍然需要另外一个驱动,对于 USB 大容量存储器为 file storage 驱动,这一驱动称为 gadget 驱动。
在这里插入图片描述
从图 左边可以看出,USB 设备驱动调用 USB 核心的 API,因此具体驱动与 SoC 无关;同样,从图 右边可以看出,USB gadget 驱动调用通用的 gadget API,因此具体 gadget 驱动也变得与 SoC 无关。软件分层设计的好处再一次得到了深刻的体现。
本文目前只对主机侧的USB驱动进行探索,因此对UDC驱动的介绍偏少。

5. USB 和 sysfs

于单个 USB 物理设备的复杂性, 设备在 sysfs 中的表示也非常复杂. 物理 USB 设备(通过 struct usb_device 表示)和单个 USB 接口(由 struct usb_interface 表示)都作为单个设备出现在 sysfs 。 (这是因为这 2 个结构都包含一个 struct device 结构). 例如,下面的内容是其中一个设备的 sysfs 目录树:

usr@usr-PC:~$ tree -L 2 /sys/devices/pci0000\:00/0000:00:14.0/usb2/2-5
/sys/devices/pci0000:00/0000:00:14.0/usb2/2-5
├── 2-5:1.0
│   ├── 2-5-port1
│   ├── 2-5-port2
│   ├── 2-5-port3
│   ├── 2-5-port4
│   ├── authorized
│   ├── bAlternateSetting
│   ├── bInterfaceClass
│   ├── bInterfaceNumber
│   ├── bInterfaceProtocol
│   ├── bInterfaceSubClass
│   ├── bNumEndpoints
│   ├── driver -> ../../../../../../bus/usb/drivers/hub
│   ├── ep_81
│   ├── firmware_node -> ../../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4d/device:4e/device:5f
│   ├── interface
│   ├── modalias
│   ├── power
│   ├── subsystem -> ../../../../../../bus/usb
│   ├── supports_autosuspend
│   └── uevent
├── authorized
├── avoid_reset_quirk
├── bcdDevice
├── bConfigurationValue
├── bDeviceClass
├── bDeviceProtocol
├── bDeviceSubClass
├── bmAttributes
├── bMaxPacketSize0
├── bMaxPower
├── bNumConfigurations
├── bNumInterfaces
├── busnum
├── configuration
├── descriptors
├── dev
├── devnum
├── devpath
├── driver -> ../../../../../bus/usb/drivers/usb
├── ep_00
│   ├── bEndpointAddress
│   ├── bInterval
│   ├── bLength
│   ├── bmAttributes
│   ├── direction
│   ├── interval
│   ├── power
│   ├── type
│   ├── uevent
│   └── wMaxPacketSize
├── firmware_node -> ../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4d/device:4e/device:5f
├── idProduct
├── idVendor
├── ltm_capable
├── manufacturer
├── maxchild
├── port -> ../2-0:1.0/usb2-port5
├── power
│   ├── active_duration
│   ├── async
│   ├── autosuspend
│   ├── autosuspend_delay_ms
│   ├── connected_duration
│   ├── control
│   ├── level
│   ├── runtime_active_kids
│   ├── runtime_active_time
│   ├── runtime_enabled
│   ├── runtime_status
│   ├── runtime_suspended_time
│   ├── runtime_usage
│   ├── usb3_hardware_lpm_u1
│   ├── usb3_hardware_lpm_u2
│   ├── wakeup
│   ├── wakeup_abort_count
│   ├── wakeup_active
│   ├── wakeup_active_count
│   ├── wakeup_count
│   ├── wakeup_expire_count
│   ├── wakeup_last_time_ms
│   ├── wakeup_max_time_ms
│   └── wakeup_total_time_ms
├── product
├── quirks
├── removable
├── remove
├── rx_lanes
├── speed
├── subsystem -> ../../../../../bus/usb
├── tx_lanes
├── uevent
├── urbnum
└── version

17 directories, 77 files

结构 usb_device 在树中被表示在:

/sys/devices/pci0000\:00/0000:00:14.0/usb2/2-5

而这个设备驱动被绑定到接口:

/sys/devices/pci0000\:00/0000:00:14.0/usb2/2-5/2-5:1.0

内核识别USB设备方式为:
第一个 USB 设备是一个根集线器. 这是 USB 控制器, 常常包含在一个 PCI 设备中. 控制器的命名是由于它控制整个连接到它上面的 USB 总线. 控制器是一个 PCI 总线和 USB 总线之间的桥, 同时是总线上的第一个设备。

所有的根集线器被 USB 核心安排了一个唯一的号. 在我们的例子里, 根集线器称为 usb2, 因为它是注册到 USB 核心的第 2 个根集线器. 可包含在单个系统中在任何时间的根集线器的数目没有限制.

每个在 USB 总线上的设备采用根集线器的号作为它的名子的第一个数字. 紧跟着的是 - 字符和设备插入的端口号. 由于我们例子中的设备插在第一个端口, 一个 1 被添加到名子. 因此给主 USB 鼠标设备的名子是 2-1. 因为这个 USB 设备包含一个接口, 那使得树中的另一个设备被添加到 sysfs 路径. 到此点, USB 接口的命名方法是设备名:在我们的例子, 是 2-1 接着一个分号和 USB 配置名, 接着一个句点和接口名. 因此对这个例子, 设备名是 2-1:1.0 因为它是第一个配置并且有接口号 0.

总结, USB sysfs 设备命名方法是:

root_hub-hub_port:config.interface 

随着设备在 USB 树中进一步向下, 并且越来越多的 USB 集线器, 集线器端口号被添加到字符串中紧随着链中之前的集线器端口号. 对一个 2 层的树, 设备名看来:

root_hub-hub_port-hub_port:config.interface 

如同可在之前的 USB 设备和接口目录列表中见到的, 所有的 USB 特定信息可直接从 sysfs 获得(例如, idVendor, idProduct, 和 bMaxPower 信息).

参考

带你遨游USB世界
USB定义的各种描述符有那些功能
《Linux设备驱动程序(中文版第三版)》
《Linux设备驱动开发详解第2版-宋宝华》

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值