技术交流

好好学习,天天向上。

0%

第十三章——USB驱动程序

介绍

USB总线只提供通道,而不关心数据传输格式。USB设备有申请一定带宽的能力,但是由Host轮询时才发送数据,一个USB总线中只有一个Host。USB定义了几种专用设备的标准,只要设备满足这些标准就可以实现免驱,这些设备标准包括存储设备、键盘、鼠标、游戏杆、网络设备和调制解调器。USB驱动可以分为两类,一类是USB上位机驱动(USB device driver),一类是USB下位机驱动(USB gadget drivers)。USB驱动框架中,由USB核心(usb core)层实现核心功能,核心层工作在USB控制器之上,位于内核各大功能模块之下。

USB协议中,有这么几个概念

  • 配置

    配置是USB接口的集合,一个USB设备可以有多个配置,但一个时刻只能激活一个配置

  • 接口

    一个配置可以有多个接口,接口是端点的集合。一个接口就是一种功能,一种功能对应一个驱动程序。一个接口通过设定不同参数,可以有多个不同的“设置”,每个设置有独立的编号

  • 端点

    一种类似于单向管道的数据通路,一个接口可以没有、有一个、有多个端点

端点还可以按照下表细分为不同的类型

端点类型 传输模式 使用场景
控制端点 异步传输 管理面端点,每个USB设备“端点0”承担该功能
中断端点 周期传输 数据传输端点的一种,一般用于传输少量数据
批量端点 异步传输 数据传输端点的一种,一般用于传输大量数据,保证数据到达
等时端点 周期传输 数据传输端点地一种,用于可接受丢包的场景

内核数据结构

端点

struct usb_host_endpoint结构体用于管理USB端点

1
2
3
4
5
6
7
8
9
10
11
12
13
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc;
struct usb_ss_ep_comp_descriptor ss_ep_comp;
struct usb_ssp_isoc_ep_comp_descriptor ssp_isoc_ep_comp;
struct list_head urb_list;
void *hcpriv;
struct ep_device *ep_dev; /* For sysfs info */

unsigned char *extra; /* Extra descriptors */
int extralen;
int enabled;
int streams;
};

其内部成员struct usb_endpoint_descriptor包含真实的端点信息。

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

__u8 bEndpointAddress; // 端点地USB地址,还包含端点数据方向信息,可以结合USB_DIR_OUT和USB_DIR_IN来使用
__u8 bmAttributes; /* 端点类型,可以结合USB_ENDPOINT_XFERTYPE_MASK确定端点是下面哪一种类型
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3 */
__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));

接口

内核使用struct usb_interface结构体来描述USB接口。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
struct usb_interface {
struct usb_host_interface *altsetting; // 一个包含所有可用于该接口的“设置”的数组
struct usb_host_interface *cur_altsetting; /* the currently active alternate setting */
unsigned num_altsetting; /* number of alternate settings */

/* If there is an interface association descriptor then it will list
* the associated interfaces */
struct usb_interface_assoc_descriptor *intf_assoc;
int minor; /* minor number this interface is bound to */

// 下面这些字段驱动程序一般无需关心
enum usb_interface_condition condition; /* state of binding */
unsigned sysfs_files_created:1; /* the sysfs attributes exist */
unsigned ep_devs_created:1; /* endpoint "devices" exist */
unsigned unregistering:1; /* unregistration is in progress */
unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */
unsigned needs_binding:1; /* needs delayed unbind/rebind */
unsigned resetting_device:1; /* true: bandwidth alloc after reset */
unsigned authorized:1; /* used for interface authorization */

struct device dev; /* interface specific device info */
struct device *usb_dev;
atomic_t pm_usage_cnt; /* usage counter for autosuspend */
struct work_struct reset_ws; /* for resets in atomic context */
};

配置和USB_DEVICE

内核使用struct usb_host_confg表示配置,使用struct usb_device表示整个USB设备,可以在 include/linux/usb.h找到关于这两个结构体的描述。这两个结构体一般不需要USB驱动程序直接读写,驱动经常做的是将struct usb_host_confg转换为struct usb_interface(可以用interface_to_usbdev实现)。

urb

urb是USB驱动和USB设备进行通信的基本数据块。urb对应到端点,urb可以在多个端点间复用。urb完整生命周期经历如下流程:urb由驱动创建–>驱动提交给usb core–>usb core会将其交给USB主控制器驱动–>主控制器传输urb–>USB主控制器驱动通知usb设备驱动传输完成。根据端点类型不同,urb的类型也有不同的创建函数。在填装urb的时候需要添加回调函数,以获知urb发送完成消息。

实现USB驱动程序

USB驱动的工作逻辑和PCI驱动类似,先注册驱动自身,然后根据设备的制造商ID和设备ID判断是否成功匹配驱动。USB设备的ID信息在内核层面用struct usb_device_id表示。流程类似,填好表,构造好成员函数,最后向内核注册驱动。

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
static struct usb_device_id xxx_table [ ] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, skel_table); // 向内核热插拔机制注册设备信息

static struct usb_driver xxx_driver = {
.owner = THIS_MODULE,
.name = "xxx",
.id_table = xxx_table,
.probe = xxx_probe,
.disconnect = xxx_disconnect,
};
static int __init usb_xxx_init(void)
{
int result;
/* register this driver with the USB subsystem */
result = usb_register(&xxx_driver);
if (result)
err("usb_register failed. Error number %d", result);
return result;
}

static void __exit usb_xxx_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&xxx_driver);
}

不使用urb的USB传输

当驱动仅仅是需要传输一些简单的USB数据的时候,使用urb传输数据会显得比较麻烦。内核提供不使用urb的数据传输接口,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 数据传输接口
int usb_bulk_msg(
struct usb_device *usb_dev, // 目标USB设备指针
unsigned int pipe, // endpoint "pipe" to send the message to
void *data, // pointer to the data to send
int len, // length in bytes of the data to send
int *actual_length, // 指针入参式返回值,说明传输了的实际大小
int timeout // jiffies为单位的超时时长,0值则一直等待数据传输完成
);
// 和usb_bulk_msg类似,不过是用于发送控制信息的
int usb_control_msg(
struct usb_device *dev, // pointer to the usb device to send the message to
unsigned int pipe, // endpoint "pipe" to send the message to
__u8 request, // USB message request value
__u8 requesttype, // USB message request type value
__u16 value, // USB message value
__u16 index, // USB message index value
void *data, // pointer to the data to send
__u16 size, // length in bytes of the data to send
int timeout // jiffies为单位的超时时长,0值则一直等待数据传输完成
);