https://blog.csdn.net/lizuobin2/article/details/51953702
USB传输数据时,就是打包成urb结构体来传输
如下图,在这棵树里,我们把树根比作主机控制器,树叶比作具体的USB设备,树干与树枝就是USB总线。树叶本身与树枝通过usb_driver连接,而树叶本书的驱动(读写、控制)则需要通过其树叶设备本身所属类设备驱动来完成。树根和树叶的“通信”依靠在树干和树枝里“流淌”的URB完成。
下图,主机(Host)上的客户软件(Client Software)通过缓冲区(Buffer)和一个USB逻辑设备(USB Logical Device)的一个接口(Interface)中的某一端点进行数据传输,客户软件的缓冲区和端点之间的通信构成了一个管道(Pipe)。
Linux中,用struct urb来和USB设备的端点进行通信。
大家常说,一个设备通常有多个配置,配置通常有多个接口,接口通常有多个端点。接口代表逻辑上的设备,比如声卡分为 录音和播放。访问设备时,访问的是某个接口(逻辑设备)。除了端点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);
-
<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 */
-
...
-
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)
五、传输模式
控制传输模式:用来传送外设和主机之间的控制、状态、配置等信息
批量传输模式:传输大量延时要求不高的数据
中断传输模式:传输数据量小,但是对传输延时要求较高的的情况,比如键盘
实时传输模式:传输实时数据,传输速率要预先可知