12.1、USB驱动——描述符、URB、管道

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

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

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

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

 

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

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

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

    设备描述符

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

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

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

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

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

    配置所包含的接口

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

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

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

    接口描述符

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

    端点

[cpp] view plain copy
 
  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.   
  8.     unsigned char *extra;   /* Extra descriptors */  
  9.     int extralen;  
  10.     int enabled;  
  11. };  

    端点描述符

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

 

二、描述符的获取过程

1、usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);

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

2、usb_configure_device

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

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

三、URB

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

 

[cpp] view plain copy
 
  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的初始化工作。

[cpp] view plain copy
 
  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 的接口函数:

[cpp] view plain copy
 
  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)

五、传输模式

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

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

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

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

posted on 2018-03-17 11:09  拉风摊主  阅读(984)  评论(0编辑  收藏  举报

导航