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

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 结构体中,包含了一个设备描述符和这个设备支持的多个配置。

 
  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. };

    设备描述符

 
  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));

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

 
  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];

  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 就是它们全部的长度。

 
  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));

    配置所包含的接口

 
  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. };

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

 
  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. };

    接口描述符

 
  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));

    端点

 
  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. };

    端点描述符

 
  1. struct usb_endpoint_descriptor {

  2. __u8 bLength; //描述符长度

  3. __u8 bDescriptorType; //描述符类型

  4.  
  5. __u8 bEndpointAddress; //端点地址:0~3位为端点号,第7位为传输方向

  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);

 
  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. }

 
  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

 
  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.  
  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 文件中定义。

 

 
  1. struct urb {

  2. struct kref kref; /* reference count of the URB */

  3. ...

  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. ...

  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)

五、传输模式

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

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值