LinuxUSB

LinuxUSB子系统架构

主机侧与设备侧驱动

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

USB驱动结构可以从主机侧和设备侧分别来看。而从两个角度所包含的内容也共同构成了USB子系统的实现。

USB驱动总体结构

设备、配置、接口、端点

设备、配置、接口和端点时USB设备的逻辑结构中所包含的四个层次。其逻辑结构如下所示。

USB逻辑结构

  • 设备通常有一个或多个配置
  • 配置通常有一个或多个接口
  • 接口通常有一个或多个设置
  • 接口有零个或多个端点

设备描述符

设备描述符是linux内核中关于USB设备的表示,它抽象了USB设备的通用信息,如供应商ID、产品ID和修订ID,支持的设备类、子类和使用的协议以及默认端点的最大包大小等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* USB_DT_DEVICE: Device descriptor */
struct usb_device_descriptor {
__u8 bLength;
__u8 bDescriptorType;

__le16 bcdUSB;
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
__u8 bMaxPacketSize0;
__le16 idVendor;
__le16 idProduct;
__le16 bcdDevice;
__u8 iManufacturer;
__u8 iProduct;
__u8 iSerialNumber;
__u8 bNumConfigurations;
} __attribute__ ((packed));

配置描述符

配置描述符是linux内核关于USB逻辑结构中配置的抽象,其中定义了配置中的接口数、支持的挂起和恢复能力以及功率要求等信息。

1
2
3
4
5
6
7
8
9
10
11
struct usb_config_descriptor {
__u8 bLength;
__u8 bDescriptorType;

__le16 wTotalLength;
__u8 bNumInterfaces;
__u8 bConfigurationValue;
__u8 iConfiguration;
__u8 bmAttributes;
__u8 bMaxPower;
} __attribute__ ((packed));

接口描述符

接口描述符是linux内核关于接口的抽象。

1
2
3
4
5
6
7
8
9
10
11
12
struct usb_interface_descriptor {
__u8 bLength;
__u8 bDescriptorType;

__u8 bInterfaceNumber;
__u8 bAlternateSetting;
__u8 bNumEndpoints;
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 iInterface;
} __attribute__ ((packed));

端点描述符

端点描述符是linux内核关于端点的抽象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;

__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));

USB主机控制器驱动

USB主机控制器有:OHCI(Open Host Controller Interface)、UHCI(Universal Host Controller Interface)、EHCI(Enhanced Host Controller Interface)和xHCI(eXtensible Host Controller Interface)。OHCI驱动程序用来为非PC系统上一集带有SiS和Ali芯片组的PC主板上的USB芯片提供支持。UHCI驱动程序多用来为大多数其它PC主板上的USB芯片提供支持。EHCI由USB2.0规范所提出,它兼容与OHCI和UHCI。xHCI是Intel开发的一个USB主机控制器接口,它目前主要面向USB3.0,同时也支持USB2.0及以下设备。

关键数据结构

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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 */
struct urb *status_urb; /* the current status urb */
#ifdef CONFIG_PM
struct work_struct wakeup_work; /* for remote wakeup */
#endif
struct work_struct died_work; /* for when the device dies */

/*
* hardware info/state
*/
const struct hc_driver *driver; /* hw-specific hooks */

struct usb_phy *usb_phy;
struct usb_phy_roothub *phy_roothub;

unsigned long flags;

enum usb_dev_authorize_policy dev_policy;
...
/* 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则具体描述一个主机控制器驱动,抽象主机控制器的具体操作和功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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;

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

int (*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup);

/* called after entering D0 (etc), before resuming the hub */
int (*pci_resume)(struct usb_hcd *hcd, bool hibernated);

/* cleanly make HCD stop writing memory and doing 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 */
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);
...
/* hw synch, freeing endpoint resources that urb_dequeue can't */
void (*endpoint_disable)(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 *);

...
};

主机控制器驱动实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

static const struct hc_driver ehci_hc_driver = {
.description = hcd_name,
.product_desc = "EHCI Host Controller",
.hcd_priv_size = sizeof(struct ehci_hcd),

/*
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,

/*
* basic lifecycle operations
*/
.reset = ehci_setup,
.start = ehci_run,
.stop = ehci_stop,
.shutdown = ehci_shutdown,

/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
.endpoint_reset = ehci_endpoint_reset,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,

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

/*
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.get_resuming_ports = ehci_get_resuming_ports,

/*
* device support
*/
.free_dev = ehci_remove_device,
};

void ehci_init_driver(struct hc_driver *drv,
const struct ehci_driver_overrides *over)
{
/* Copy the generic table to drv and then apply the overrides */
*drv = ehci_hc_driver;

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

static int __init ehci_hcd_init(void)
{
int retval = 0;

if (usb_disabled())
return -ENODEV;

printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name);
set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
printk(KERN_WARNING "Warning! ehci_hcd should always be loaded"
" before uhci_hcd and ohci_hcd, not after\n");

pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd sitd %zd\n",
hcd_name,
sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
sizeof(struct ehci_itd), sizeof(struct ehci_sitd));

retval = platform_driver_register(&PLATFORM_DRIVER);
if (retval < 0)
goto clean0;

return retval;

platform_driver_unregister(&PLATFORM_DRIVER);
clean0:

clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
return retval;
}
module_init(ehci_hcd_init);

static void __exit ehci_hcd_cleanup(void)
{
platform_driver_unregister(&PLATFORM_DRIVER);
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
}
module_exit(ehci_hcd_cleanup);

USB设备驱动

USB设备驱动的结构

Linux内核实现了积累通用的USB设备驱动,有如下几类:

  • 音频设备类
  • 通信设备类
  • HID(人机接口)设备类
  • 显示设备类
  • 海量存储设备类
  • 电源设备类
  • 打印设备类
  • 集线器设备类

通常对于这些通用linux设备不需要再编写驱动,而需要编写的是特定厂商、特定芯片的驱动。

内核为各类USB设备分配了相应的主设备号,详见USB主设备号。次设备号为0~15,用于标识不同设备。在debugfs下,/sys/kernel/debug/usb/devices包含了USB的设备信息。

在内核中,使用usb_driver结构体来描述一个USB设备驱动,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct usb_driver {
const char *name;

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

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

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

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

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

const struct usb_device_id *id_table;
const struct attribute_group **dev_groups;

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;
};

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

URB请求块

USB驱动中作为通信核心的是URB(USB Request Block)。URB作为USB设备驱动中用来描述与USB设备通信所用的基本载体和核心数据结构,非常类似于网络设备驱动中的sk_buff结构体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
struct urb {
/* private: usb core and host controller only fields in the urb */
struct kref kref; /* reference count of the URB */
int unlinked; /* unlink error code */
void *hcpriv; /* private data for host controller */
atomic_t use_count; /* concurrent submissions counter */
atomic_t reject; /* submissions will fail */

/* 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 */
};

URB的处理流程

  • 被一个USB设备驱动创建

    创建urb结构体的接口为:

    1
    struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
  • 初始化URB

    对于一个urb结构的初始化,内核提供了如下接口

    ```
    /* 初始化一个控制urb */
    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 */
    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 */
    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 */
    void usb_init_urb(struct urb *urb);
    ```

  • 提交给USB核心

    在完成前两步后我们得到了一个可用的URB,这样便可以提交给USB核心了。

    1
    int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
  • 提交由USB核心指定的USB主机控制器驱动

  • 被USB主机控制器处理,进行一次到USB设备的传输

    这两步由USB核心和主机控制器完成。

  • 当URB完成,USB主机控制器驱动通知USB设备驱动

    当URB生命结束时,在URB的完成回调中通过URB结构体的status成员可以获知其原因,并进行相应的错误处理。

简单的批量和控制URB

有时USB驱动程序只是从USB设备上接收或向USB设备发送一些简单的数据,这时没必要按上面说的URB数据发送标准流程来处理,可使用如下这些简单接口:

1
2
3
4
5
6
7
8
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);
int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout);
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length,
int timeout);

USB设备驱动的骨架程序

在Linux的内核源码中,driver/usb/usb-skeleton.c文件为我们提供了一个最基础的USB驱动程序,即USB骨架程序,它可被看作一个简单的USB设备驱动实例。

USB UDC与Gadget驱动

UDC(USB设备控制器)驱动指的是作为其它USB主机控制器外设的USB硬件设备上底层硬件控制器的驱动,该硬件和驱动负责将一个USB设备依附于一个USB主机控制器上。

例如:某运行linux系统的手机作为PC的U盘时,手机中的底层USB控制器行使USB设备控制器的功能,这时候运行在底层的事UDC驱动,而手机要成为U盘,在UDC驱动上还有需要一个File Storage驱动(Function驱动)。

USB设备驱动调用USB核心的API,因此具体驱动与硬件无关。Function驱动调用通用的Gadget Function Api,因此Function驱动也与硬件无关。

UDC驱动和Function驱动都位于drivers/usb/gadget下。其中包含一些重要的Function驱动:

  • Ethernet over USB:该驱动模拟以太网口
  • File-Backed Storage Gadget:最常见的U盘功能实现
  • Serial Gadget: 包含Generic Serial实现和CDC ACM规范实现
  • Gadget MIDI: 暴露ALSA MIDI接口
  • USB Video Class Gadget驱动: 让Linux系统成为另一个系统的USB视频采集源
  • GadgetFS: 将Gadget API接口暴露给应用层,以便实现用户空间的驱动

关键结构体

在USB设备控制器驱动中,主要需要关心如下几个数据结构。

usb_gadget:描述一个USB设备控制器
usb_gadget_ops:UDC操作
usb_ep:描述一个端点
usb_ep_ops:描述端点操作
usb_function:描述一个Function
usb_request:在Gadget驱动中数据传输的单元

USB OTG驱动

USB OTG标准在完全兼容USB 2.0标准的基础上,它允许设备既可作为主机,也可作为外设操作,OTG新增了主机通信协议(HNP)和对话请求协议(SRP)。

在OTG中,初始主机设备称为A设备,外设称为B设备。可以通过电缆的连接方式决定初始角色。当OTG设备检测到接地的ID引脚时,表示默认的是A设备;而ID引脚浮空则表示B设备。系统一旦连接后,OTG的角色还可以更换,以采用新的HNP协议。而SRP允许B设备请求A设备打开VBUS电源并启动一次对话。一次OTG对话可通过A设备提供VBUS电源的时间来确定。

如下数据结构为描述OTG功能切换和协议的结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct usb_otg {
u8 default_a;

struct phy *phy;
/* old usb_phy interface */
struct usb_phy *usb_phy;
struct usb_bus *host;
struct usb_gadget *gadget;

enum usb_otg_state state;

/* bind/unbind the host controller */
int (*set_host)(struct usb_otg *otg, struct usb_bus *host);

/* bind/unbind the peripheral controller */
int (*set_peripheral)(struct usb_otg *otg,
struct usb_gadget *gadget);

/* effective for A-peripheral, ignored for B devices */
int (*set_vbus)(struct usb_otg *otg, bool enabled);

/* for B devices only: start session with A-Host */
int (*start_srp)(struct usb_otg *otg);

/* start or continue HNP role switch */
int (*start_hnp)(struct usb_otg *otg);

};

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 yxhlfx@163.com

文章标题:LinuxUSB

本文作者:红尘追风

发布时间:2016-08-11, 10:26:46

原始链接:http://www.micernel.com/2016/08/11/LinuxUSB/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录