大家常说,一个设备通常有多个配置,配置通常有多个接口,接口通常有多个端点。接口代表逻辑上的设备,比如声卡分为 录音和播放。访问设备时,访问的是某个接口(逻辑设备)。除了端点0之外,每个端点只支持一个传输方向,一种性质的传输传输数据时,读写某个端点,端点是数据通道。
本文首先分析设备、配置、接口、设置、端点之间的关系,然后根据 2440-ochi 驱动程序,分析一个设备注册到内核时,它的这些描述符的获取过程。
一、设备、配置、接口、设置、端点之间的关系
在内核中,一个 USB 设备,无论是 hub 还是普通的USB鼠标等等,它们都使用一个 usb_device 结构体来描述,在 usb_device 结构体中,包含了一个设备描述符和这个设备支持的多个配置。
struct usb_device {
...
struct device dev;
struct usb_device_descriptor descriptor; // 设备描述符
struct usb_host_config *config; // 支持的配置
struct usb_host_config *actconfig; // 当前的配置
...
};
设备描述符
struct usb_device_descriptor {
__u8 bLength; // 描述符长度
__u8 bDescriptorType; //描述符类型
__le16 bcdUSB; //USB版本号
__u8 bDeviceClass; //USB分配的设备类
__u8 bDeviceSubClass; //USB分配的子类
__u8 bDeviceProtocol; //USB分配的协议
__u8 bMaxPacketSize0; //endpoint0最大包大小
__le16 idVendor; //厂商编号
__le16 idProduct; //产品编号
__le16 bcdDevice; //设备出厂编号
__u8 iManufacturer; //描述厂商字符串的索引
__u8 iProduct; //描述产品字符串的索引
__u8 iSerialNumber; //描述设备序列号字符串的索引
__u8 bNumConfigurations; //可能的配置数量
} __attribute__ ((packed));
设备所包含的配置,配置里包含一个配置描述符,以及该配置所拥有的接口。
struct usb_host_config {
struct usb_config_descriptor desc; // 配置描述符
char *string;
struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];
struct usb_interface *interface[USB_MAXINTERFACES]; // 接口
struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
unsigned char *extra;
int extralen;
};
配置描述符,注意它的 wTotalLength ,我们通常将一个配置以及它所包含的接口,接口所包含的端点所有的描述符一次性都获取到,wTotalLength 就是它们全部的长度。
struct usb_config_descriptor {
__u8 bLength; //描述符长度
__u8 bDescriptorType; //描述符类型编号
__le16 wTotalLength; //请求配置所返回的所有数据的大小,当前配置的所有描述符包括所包含的接口、端点描述符
__u8 bNumInterfaces; //配置所支持的接口数
__u8 bConfigurationValue; //Set_Configuration 命令需要的参数值
__u8 iConfiguration; //描述该配置的字符串的索引值
__u8 bmAttributes; //供电模式选择
__u8 bMaxPower; //设备从总线提取的最大电流
} __attribute__ ((packed));
配置所包含的接口
struct usb_interface {
struct usb_host_interface *altsetting; // 一个接口可能有多个设置(一个接口多种功能),也就是这些接口所包含的端点凑起来可能有多种功能
struct usb_host_interface *cur_altsetting; // 当前的设置
unsigned num_altsetting; /* number of alternate settings */
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 is_active:1; /* the interface is not suspended */
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 reset_running:1;
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 */
};
接口当前的设置,里边包含了接口描述符和该接口所拥有的端点
struct usb_host_interface {
struct usb_interface_descriptor desc; // 接口描述符
struct usb_host_endpoint *endpoint;
char *string; /* iInterface string, if present */
unsigned char *extra; /* Extra descriptors */
int extralen;
};
接口描述符
struct usb_interface_descriptor {
__u8 bLength; //描述符长度
__u8 bDescriptorType; //描述符类型
__u8 bInterfaceNumber; //接口的编号
__u8 bAlternateSetting; //备用的接口描述符编号
__u8 bNumEndpoints; //该接口使用的端点数,不包括端点0
__u8 bInterfaceClass; //接口类型
__u8 bInterfaceSubClass; //接口子类型
__u8 bInterfaceProtocol; //接口所遵循的协议
__u8 iInterface; //描述该接口的字符串的索引值
} __attribute__ ((packed));
端点
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc; // 端点描述符
struct list_head urb_list; // 该端点的 urb 队列
void *hcpriv;
struct ep_device *ep_dev; /* For sysfs info */
struct usb_host_ss_ep_comp *ss_ep_comp; /* For SS devices */
unsigned char *extra; /* Extra descriptors */
int extralen;
int enabled;
};
端点描述符
struct usb_endpoint_descriptor {
__u8 bLength; //描述符长度
__u8 bDescriptorType; //描述符类型
__u8 bEndpointAddress; //端点地址:0~3位为端点号,第7位为传输方向
__u8 bmAttributes; // 端点属性 bit 0-1 00控制 01 同步 02批量 03 中断
__le16 wMaxPacketSize; //本端点接收或发送的最大信息包的大小
__u8 bInterval; //轮询数据断端点的时间间隔
//批量传送的端点,以及控制传送的端点,此域忽略
//对于中断传输的端点,此域的范围为1~255
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
1、usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
{
struct usb_device_descriptor *desc;
int ret;
if (size > sizeof(*desc))
return -EINVAL;
desc = kmalloc(sizeof(*desc), GFP_NOIO);
if (!desc)
return -ENOMEM;
ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
if (ret >= 0)
memcpy(&dev->descriptor, desc, size);
kfree(desc);
return ret;
}
int usb_get_descriptor(struct usb_device *dev, unsigned char type,
unsigned char index, void *buf, int size)
{
int i;
int result;
memset(buf, 0, size); /* Make sure we parse really received data */
for (i = 0; i < 3; ++i) {
/* retry on length 0 or error; some devices are flakey */
result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
(type << 8) + index, 0, buf, size,
USB_CTRL_GET_TIMEOUT);
if (result <= 0 && result != -ETIMEDOUT)
continue;
if (result > 1 && ((u8 *)buf)[1] != type) {
result = -ENODATA;
continue;
}
break;
}
return result;
}
2、usb_configure_device
static int usb_configure_device(struct usb_device *udev)
{
usb_get_configuration(udev);
}
int usb_get_configuration(struct usb_device *dev)
{
struct device *ddev = &dev->dev;
int ncfg = dev->descriptor.bNumConfigurations;
int result = 0;
unsigned int cfgno, length;
unsigned char *buffer;
unsigned char *bigbuffer;
struct usb_config_descriptor *desc;
cfgno = 0;
if (ncfg > USB_MAXCONFIG) {
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
}
length = ncfg * sizeof(struct usb_host_config);
dev->config = kzalloc(length, GFP_KERNEL);
length = ncfg * sizeof(char *);
dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
desc = (struct usb_config_descriptor *)buffer;
result = 0;
for (; cfgno < ncfg; cfgno++) {
/* We grab just the first descriptor so we know how long the whole configuration is */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, USB_DT_CONFIG_SIZE);
/* 长度为当前配置所有描述符的长度 */
length = max((int) le16_to_cpu(desc->wTotalLength), USB_DT_CONFIG_SIZE);
/* Now that we know the length, get the whole thing */
bigbuffer = kmalloc(length, GFP_KERNEL);
/* 获取描述符 */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);
dev->rawdescriptors[cfgno] = bigbuffer;
/* 解析配置描述符 */
result = usb_parse_configuration(&dev->dev, cfgno, &dev->config[cfgno], bigbuffer, length);
}
result = 0;
return result;
}
至此,所有的描述符获取完毕。三、URB
URB(USB Request Block,USB请求块)是USB数据传机制使用的核心数据结构。URB供USB协议栈使用。URB在include/linux/usb.h 文件中定义。
struct urb {
struct kref kref; /* reference count of the URB */
...
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 */
...
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 使用分三步,分配内存,初始化,提交。URB的内存分配是调用 usb_alloc_urb()方法来分配,该函数分配内存并将其至零,之后初始化URB相关的kobject和用于保护的URB自旋锁。USB核心提供下列辅助函数来完成URB的初始化工作。
usb_fill_[int|control|bulk]_urb(
struct urb * urb, // URB pointer
struct usb_device * dev, // USB device structure
unsigned int pipe, // pipe encoding
void * transfer_buffer, // Buffer for I/O
int buffer_length, // I/O Buffer length
usb_complete_t complete_fn, // Callback routine
void * context, // For usb by completion_fn
int interval // For int URBS only
)
complete_fn 是回调函数,回调函数在URB提交过程后被调用,负责检查提交状态、释放传输输出缓冲区等。为了提交URB以便进行数据传输,需要调用 usb_submit_urb()函数。该函数异步提交URB。
USB 核心也提供了公布提交 URB 的接口函数:
usb_[interrupt|control|bulk]_msg(
struct usb_device * usb_dev,
unsigned int pipe,
void * data,
int len,
int * actual_length,
int timeout
)
创建一个URB 之后提交,如果没有成功则会一直等待。该函数不需要传递回调函数地址,一个通用的完成回调函数将会实现此功能。也不需要另外创建和初始化,因为这个函数在没有增加任何开销的情况下连这些都已经做了。
URB 的任务完成以后,usb_free_urb()函数释放该实例。usb_unlink_urb()取消一个等待处理的URB.
四、管道
管道包含以下几部分:
断点地址;
数据传输方向;
数据传输模式:控制模式、中断模式、批量模式、实时模式;
管道是URB的重要成员,为USB数据传输提供地址信息。USB核心提供现成的宏来创建管道。
usb_[rcv|snd][ctrl|int|bulk|isoc]pipe(struct usb_device *usb_dev,_u8 endpointAddress)
五、传输模式
控制传输模式:用来传送外设和主机之间的控制、状态、配置等信息
批量传输模式:传输大量延时要求不高的数据
中断传输模式:传输数据量小,但是对传输延时要求较高的的情况,比如键盘
实时传输模式:传输实时数据,传输速率要预先可知