大家常说,一个设备通常有多个配置,配置通常有多个接口,接口通常有多个端点。接口代表逻辑上的设备,比如声卡分为 录音和播放。访问设备时,访问的是某个接口(逻辑设备)。除了端点0之外,每个端点只支持一个传输方向,一种性质的传输传输数据时,读写某个端点,端点是数据通道。

有一个设备,如支持视频和音频的一个播放器。那么,对于上面提到的4个描述符,对它们设置的时候,它们分别对于哪一个描述符呢?

从我现在的理解来看,这样一个设备对应一个设备描述符,支持视频的功能对应一个接口描述符,支持音频功能的对应一个接口描述符。为了支持视频,在下层有多个端口同时工作为提供视频数据传输的支持,所以有多个端点描述符。

本文首先分析设备、配置、接口、设置、端点之间的关系,然后根据 2440-ochi 驱动程序,分析一个设备注册到内核时,它的这些描述符的获取过程。

一、设备、配置、接口、设置、端点之间的关系

在内核中,一个 USB 设备,无论是 hub 还是普通的USB鼠标等等,它们都使用一个 usb_device 结构体来描述,在 usb_device 结构体中,包含了一个设备描述符和这个设备支持的多个配置。

  1. struct usb_device {
  2. ...
  3. struct device dev;
  4. struct usb_device_descriptor descriptor;    // 设备描述符
  5. struct usb_host_config *config;     // 支持的配置
  6. struct usb_host_config *actconfig;  // 当前的配置
  7. ...
  8. };

设备描述符

  1. struct usb_device_descriptor {
  2. __u8  bLength;          // 描述符长度
  3. __u8  bDescriptorType;  //描述符类型
  4. __le16 bcdUSB;      //USB版本号
  5. __u8  bDeviceClass; //USB分配的设备类
  6. __u8  bDeviceSubClass;  //USB分配的子类
  7. __u8  bDeviceProtocol;  //USB分配的协议
  8. __u8  bMaxPacketSize0;  //endpoint0最大包大小
  9. __le16 idVendor;    //厂商编号
  10. __le16 idProduct;   //产品编号
  11. __le16 bcdDevice;   //设备出厂编号
  12. __u8  iManufacturer;    //描述厂商字符串的索引
  13. __u8  iProduct;         //描述产品字符串的索引
  14. __u8  iSerialNumber;    //描述设备序列号字符串的索引
  15. __u8  bNumConfigurations;   //可能的配置数量
  16. } __attribute__ ((packed));

设备所包含的配置,配置里包含一个配置描述符,以及该配置所拥有的接口。

  1. struct usb_host_config {
  2. struct usb_config_descriptor    desc;   // 配置描述符
  3. char *string;
  4. 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的个数
  5. struct usb_interface *interface[USB_MAXINTERFACES]; // 接口
  6. struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
  7. unsigned char *extra;
  8. int extralen;
  9. };

配置描述符,注意它的 wTotalLength ,我们通常将一个配置以及它所包含的接口,接口所包含的端点所有的描述符一次性都获取到,wTotalLength 就是它们全部的长度。

  1. struct usb_config_descriptor {
  2. __u8  bLength;  //描述符长度
  3. __u8  bDescriptorType;  //描述符类型编号
  4. __le16 wTotalLength;    //请求配置所返回的所有数据的大小,当前配置的所有描述符包括所包含的接口、端点描述符
  5. __u8  bNumInterfaces;   //配置所支持的接口数
  6. __u8  bConfigurationValue;  //Set_Configuration 命令需要的参数值
  7. __u8  iConfiguration;   //描述该配置的字符串的索引值
  8. __u8  bmAttributes; //供电模式选择
  9. __u8  bMaxPower;    //设备从总线提取的最大电流
  10. } __attribute__ ((packed));

配置所包含的接口

  1. struct usb_interface {
  2. struct usb_host_interface *altsetting;  // 一个接口可能有多个设置(一个接口多种功能),也就是这些接口所包含的端点凑起来可能有多种功能
  3. struct usb_host_interface *cur_altsetting;  // 当前的设置
  4. unsigned num_altsetting;    /* number of alternate settings */
  5. struct usb_interface_assoc_descriptor *intf_assoc;
  6. int minor;          /* minor number this interface is
  7. * bound to */
  8. enum usb_interface_condition condition;     /* state of binding */
  9. unsigned is_active:1;       /* the interface is not suspended */
  10. unsigned sysfs_files_created:1; /* the sysfs attributes exist */
  11. unsigned ep_devs_created:1; /* endpoint "devices" exist */
  12. unsigned unregistering:1;   /* unregistration is in progress */
  13. unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
  14. unsigned needs_altsetting0:1;   /* switch to altsetting 0 is pending */
  15. unsigned needs_binding:1;   /* needs delayed unbind/rebind */
  16. unsigned reset_running:1;
  17. struct device dev;      /* interface specific device info */
  18. struct device *usb_dev;
  19. atomic_t pm_usage_cnt;      /* usage counter for autosuspend */
  20. struct work_struct reset_ws;    /* for resets in atomic context */
  21. };

接口当前的设置,里边包含了接口描述符和该接口所拥有的端点

  1. struct usb_host_interface {
  2. struct usb_interface_descriptor desc;   // 接口描述符
  3. struct usb_host_endpoint *endpoint;
  4. char *string;       /* iInterface string, if present */
  5. unsigned char *extra;   /* Extra descriptors */
  6. int extralen;
  7. };

接口描述符

  1. struct usb_interface_descriptor {
  2. __u8  bLength;          //描述符长度
  3. __u8  bDescriptorType;  //描述符类型
  4. __u8  bInterfaceNumber; //接口的编号
  5. __u8  bAlternateSetting;    //备用的接口描述符编号
  6. __u8  bNumEndpoints;    //该接口使用的端点数,不包括端点0
  7. __u8  bInterfaceClass;  //接口类型
  8. __u8  bInterfaceSubClass;   //接口子类型
  9. __u8  bInterfaceProtocol;   //接口所遵循的协议
  10. __u8  iInterface;   //描述该接口的字符串的索引值
  11. } __attribute__ ((packed));

端点

  1. struct usb_host_endpoint {
  2. struct usb_endpoint_descriptor  desc;   // 端点描述符
  3. struct list_head        urb_list;   // 该端点的 urb 队列
  4. void                *hcpriv;
  5. struct ep_device        *ep_dev;    /* For sysfs info */
  6. struct usb_host_ss_ep_comp  *ss_ep_comp;    /* For SS devices */
  7. unsigned char *extra;   /* Extra descriptors */
  8. int extralen;
  9. int enabled;
  10. };

端点描述符

  1. struct usb_endpoint_descriptor {
  2. __u8  bLength;  //描述符长度
  3. __u8  bDescriptorType;  //描述符类型
  4. __u8  bEndpointAddress; //端点地址:0~3位为端点号,第7位为传输方向 ,端点地址中的端点号和端点方向,可以唯一确定一个管道pipe
  5. __u8  bmAttributes;     // 端点属性 bit 0-1 00控制 01 同步 02批量 03 中断
  6. __le16 wMaxPacketSize;  //本端点接收或发送的最大信息包的大小
  7. __u8  bInterval;    //轮询数据断端点的时间间隔
  8. //批量传送的端点,以及控制传送的端点,此域忽略
  9. //对于中断传输的端点,此域的范围为1~255
  10. /* NOTE:  these two are _only_ in audio endpoints. */
  11. /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
  12. __u8  bRefresh;
  13. __u8  bSynchAddress;
  14. } __attribute__ ((packed));

二、描述符的获取过程

1、usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);

  1. int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
  2. {
  3. struct usb_device_descriptor *desc;
  4. int ret;
  5. if (size > sizeof(*desc))
  6. return -EINVAL;
  7. desc = kmalloc(sizeof(*desc), GFP_NOIO);
  8. if (!desc)
  9. return -ENOMEM;
  10. ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
  11. if (ret >= 0)
  12. memcpy(&dev->descriptor, desc, size);
  13. kfree(desc);
  14. return ret;
  15. }
  1. int usb_get_descriptor(struct usb_device *dev, unsigned char type,
  2. unsigned char index, void *buf, int size)
  3. {
  4. int i;
  5. int result;
  6. memset(buf, 0, size);   /* Make sure we parse really received data */
  7. for (i = 0; i < 3; ++i) {
  8. /* retry on length 0 or error; some devices are flakey */
  9. result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
  10. USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
  11. (type << 8) + index, 0, buf, size,
  12. USB_CTRL_GET_TIMEOUT);
  13. if (result <= 0 && result != -ETIMEDOUT)
  14. continue;
  15. if (result > 1 && ((u8 *)buf)[1] != type) {
  16. result = -ENODATA;
  17. continue;
  18. }
  19. break;
  20. }
  21. return result;
  22. }

2、usb_configure_device

  1. static int usb_configure_device(struct usb_device *udev)
  2. {
  3. usb_get_configuration(udev);
  4. }
  1. int usb_get_configuration(struct usb_device *dev)
  2. {
  3. struct device *ddev = &dev->dev;
  4. int ncfg = dev->descriptor.bNumConfigurations;
  5. int result = 0;
  6. unsigned int cfgno, length;
  7. unsigned char *buffer;
  8. unsigned char *bigbuffer;
  9. struct usb_config_descriptor *desc;
  10. cfgno = 0;
  11. if (ncfg > USB_MAXCONFIG) {
  12. dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
  13. }
  14. length = ncfg * sizeof(struct usb_host_config);
  15. dev->config = kzalloc(length, GFP_KERNEL);
  16. length = ncfg * sizeof(char *);
  17. dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
  18. buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
  19. desc = (struct usb_config_descriptor *)buffer;
  20. result = 0;
  21. for (; cfgno < ncfg; cfgno++) {
  22. /* We grab just the first descriptor so we know how long the whole configuration is */
  23. result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, USB_DT_CONFIG_SIZE);
  24. /* 长度为当前配置所有描述符的长度 */
  25. length = max((int) le16_to_cpu(desc->wTotalLength), USB_DT_CONFIG_SIZE);
  26. /* Now that we know the length, get the whole thing */
  27. bigbuffer = kmalloc(length, GFP_KERNEL);
  28. <span style="white-space:pre">        </span>/* 获取描述符 */
  29. result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);
  30. dev->rawdescriptors[cfgno] = bigbuffer;
  31. /* 解析配置描述符 */
  32. result = usb_parse_configuration(&dev->dev, cfgno, &dev->config[cfgno], bigbuffer, length);
  33. }
  34. result = 0;
  35. return result;
  36. }

至此,所有的描述符获取完毕。

三、URB

URB(USB Request Block,USB请求块)是USB数据传机制使用的核心数据结构。URB供USB协议栈使用。URB在include/linux/usb.h 文件中定义。

  1. struct urb {
  2. struct kref kref;       /* reference count of the URB */
  3. <span style="white-space:pre;"> </span>...
  4. struct usb_device *dev;     /* (in) pointer to associated device */
  5. struct usb_host_endpoint *ep;   /* (internal) pointer to endpoint */
  6. unsigned int pipe;      /* (in) pipe information */
  7. unsigned int stream_id;     /* (in) stream ID */
  8. int status;         /* (return) non-ISO status */
  9. unsigned int transfer_flags;    /* (in) URB_SHORT_NOT_OK | ...*/
  10. void *transfer_buffer;      /* (in) associated data buffer */
  11. dma_addr_t transfer_dma;    /* (in) dma addr for transfer_buffer */
  12. <span style="white-space:pre;"> </span>...
  13. u32 transfer_buffer_length; /* (in) data buffer length */
  14. u32 actual_length;      /* (return) actual transfer length */
  15. unsigned char *setup_packet;    /* (in) setup packet (control only) */
  16. dma_addr_t setup_dma;       /* (in) dma addr for setup_packet */
  17. int start_frame;        /* (modify) start frame (ISO) */
  18. int number_of_packets;      /* (in) number of ISO packets */
  19. int interval;           /* (modify) transfer interval
  20. * (INT/ISO) */
  21. int error_count;        /* (return) number of ISO errors */
  22. void *context;          /* (in) context for completion */
  23. usb_complete_t complete;    /* (in) completion routine */
  24. struct usb_iso_packet_descriptor iso_frame_desc[0];
  25. /* (in) ISO ONLY */
  26. };

URB 使用分三步,分配内存,初始化,提交。URB的内存分配是调用 usb_alloc_urb()方法来分配,该函数分配内存并将其至零,之后初始化URB相关的kobject和用于保护的URB自旋锁。USB核心提供下列辅助函数来完成URB的初始化工作。

  1. usb_fill_[int|control|bulk]_urb(
  2. struct urb * urb,       // URB pointer
  3. struct usb_device * dev,    // USB device structure
  4. unsigned int pipe,      // pipe encoding
  5. void * transfer_buffer,     // Buffer for I/O
  6. int buffer_length,      // I/O Buffer length
  7. usb_complete_t complete_fn,     // Callback routine
  8. void * context,         // For usb by completion_fn
  9. int interval            // For int URBS only
  10. )

complete_fn 是回调函数,回调函数在URB提交过程后被调用,负责检查提交状态、释放传输输出缓冲区等。为了提交URB以便进行数据传输,需要调用 usb_submit_urb()函数。该函数异步提交URB。

USB 核心也提供了公布提交 URB 的接口函数:

  1. usb_[interrupt|control|bulk]_msg(
  2. struct usb_device * usb_dev,
  3. unsigned int pipe,
  4. void * data,
  5. int len,
  6. int * actual_length,
  7. int timeout
  8. )

创建一个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)

五、传输模式

控制传输模式:用来传送外设和主机之间的控制、状态、配置等信息

批量传输模式:传输大量延时要求不高的数据

中断传输模式:传输数据量小,但是对传输延时要求较高的的情况,比如键盘

实时传输模式:传输实时数据,传输速率要预先可知

12.1、USB驱动——描述符、URB、管道的更多相关文章

  1. (八) Usb摄像头描述符解析

    目录 Usb摄像头描述符解析 总结 参考资料 打印设备描述符 打印配置描述符 打印接口联合体描述符 打印接口描述符 打印当前设置的额外描述符 代码解析额外的描述符 打印端点描述符 title: Usb ...

  2. USB协议-USB的描述符及其之间的关系

    USB只是一个总线,只提供一个数据通路而已.USB总线驱动程序并不知道一个设备具体如何操作,有哪些行为.具体的一个设备实现什么功能,要由设备自己来决定.那么,USB主机是如何知道一个设备的功能以及行为 ...

  3. USB HID描述符【转】

    本文转载自: USB是个通用的总线,端口都是统一的.但是USB设备却各种各样,例如USB鼠标,USB键盘,U盘等等,那么USB主机是如何识别出不同的设备的呢?这就要依赖于描述符了.USB的描述符主要有 ...

  4. (转)USB的描述符及各种描述符之间的依赖关系

    全文链接:http://justmei.blog.163.com/blog/static/11609985320102421659260/?latestBlog 1 推荐 [原创] USB入门系列之七 ...

  5. 2.7 usb摄像头之usb摄像头描述符打印

    学习目标:参考lsusb源码,打印USB摄像头的设备描述符.配置描述符.接口联合描述符.端点描述符: 一.lsusb命令和源码 使用命令lsusb可以看看设备的id,并执行 # lsusb -v -d ...

  6. 2.6 USB摄像头驱动之USB描述符

    学习目标:分析USB摄像头驱动的描述符: 一.USB设备描述符 在usb设备驱动分析那一节,也用到了usb描述符. usb描述符可分为: USB设备描述符(usb_device_descriptor) ...

  7. usb驱动开发7之接口描述符

    前面struct usb_interface里表示接口设置的struct usb_host_interface被有意的飘过了,咱们在这节主要讲讲这个结构体,同样在include/linux/usb.h ...

  8. usb描述符简述(二)

    title: usb描述符简述 tags: linux date: 2018/12/18/ 18:25:23 toc: true --- usb描述符简述 转载自cnblog 具体描述符 https: ...

  9. USB HID设备报告描述符详解(转)

    转自:http://group.ednchina.com/93/198.aspx. 参考:USB HID usage table 概述:   报告在这里意思是数据传输(data transfer),而 ...

随机推荐

  1. Ansible学习记录六:Tower安装

    0.特别说明 1. 本文档没有特殊说明,均已root用户安装 2. 本文档中ftp传输文件的工具采用filezilla. 3. 本文档中的执行命令必须严格按照顺序而来. 4. 本文档中所用浏览器为Go ...

  2. init进程

    2.Linux下的三个特殊进程 Linux下有三个特殊的进程idle进程(PID=0),init进程(PID=1),和kthreadd(PID=2)idle进程由系统自动创建,运行在内核态idle进程 ...

  3. oracle on linux 巡检脚本-部分

    #!/bin/sh #ocpyang@126.com #Modified according to the actual situation mysql server IP and username ...

  4. 20.Node.js EventEmitter的方法和事件

    转自:http://www.runoob.com/nodejs/nodejs-tutorial.html EventEmitter 提供了多个属性,如 on 和 emit.on 函数用于绑定事件函数, ...

  5. JSON.parse和eval()的区别

    eval方法不检查给的字符串是否符合json的格式,parse会检查json语法格式. 比如一个json字符串data: { "a": 1, "b": &quo ...

  6. Jmeter使用_处理响应结果显示乱码

    1. 添加BeanShell PostProcessor 输入prev.setDataEncoding("utf-8"); 目的是修改响应数据编码格式为utf-8,保存

  7. caffe 在 windows 下的配置(scripts\build_win.cmd)

    官网配置文档见:GitHub - BVLC/caffe at windows 1. windows 设置 requirements: visual studio 2013/2015 CMake > ...

  8. 存储过程和SQL语句比较

    做为SQL存储过程和.NET的新手,下面的指导还是很有用的,自己这一段刚刚接触这些东西,搜集了一些相关的东西,能使新手较容易上手,当然啦,要精通和熟练应用,还是要看更多更深的资料的,高手请不要见笑.以 ...

  9. css实现水波纹效果

    1. HTML 代码: <div class="example"> <div class="dot"></div> < ...

  10. vue.js的基础与语法

    Vue的实例 创建第一个实例: {{}} 被称之为插值表达式.可以用来进行文本插值. <!DOCTYPE html> <html lang="en"> &l ...