12.1、USB驱动——描述符、URB、管道
大家常说,一个设备通常有多个配置,配置通常有多个接口,接口通常有多个端点。接口代表逻辑上的设备,比如声卡分为 录音和播放。访问设备时,访问的是某个接口(逻辑设备)。除了端点0之外,每个端点只支持一个传输方向,一种性质的传输传输数据时,读写某个端点,端点是数据通道。
有一个设备,如支持视频和音频的一个播放器。那么,对于上面提到的4个描述符,对它们设置的时候,它们分别对于哪一个描述符呢?
从我现在的理解来看,这样一个设备对应一个设备描述符,支持视频的功能对应一个接口描述符,支持音频功能的对应一个接口描述符。为了支持视频,在下层有多个端口同时工作为提供视频数据传输的支持,所以有多个端点描述符。
本文首先分析设备、配置、接口、设置、端点之间的关系,然后根据 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]; //描述了device中的VideoVontrol和videoStreaming有多少个等信息,可以见博客第三期摄像头学习的第26篇blog图中的IAD(interface_assoc_descriptor)描述符,UVC设备仅关系包含该数组的第一个,usb_interface_assoc_descriptor 中的bInterfaceCount表示VC和VS的个数
- 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位为传输方向 ,端点地址中的端点号和端点方向,可以唯一确定一个管道pipe
- __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);
- <span style="white-space:pre"> </span>/* 获取描述符 */
- 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 */
- <span style="white-space:pre;"> </span>...
- 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 */
- <span style="white-space:pre;"> </span>...
- 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)
五、传输模式
控制传输模式:用来传送外设和主机之间的控制、状态、配置等信息
批量传输模式:传输大量延时要求不高的数据
中断传输模式:传输数据量小,但是对传输延时要求较高的的情况,比如键盘
实时传输模式:传输实时数据,传输速率要预先可知