0x00 前言

最近需要把usb做成串口,所以来仔细学一下linux下usb的驱动

0x01描述符类型

/driver/usb/ch9.h

1. usb设备描述符

  1. /* USB_DT_DEVICE: Device descriptor */
  2. struct usb_device_descriptor {
  3. __u8 bLength;
  4. __u8 bDescriptorType;
  5. __le16 bcdUSB;
  6. __u8 bDeviceClass;
  7. __u8 bDeviceSubClass;
  8. __u8 bDeviceProtocol;
  9. __u8 bMaxPacketSize0;
  10. __le16 idVendor;
  11. __le16 idProduct;
  12. __le16 bcdDevice;
  13. __u8 iManufacturer;
  14. __u8 iProduct;
  15. __u8 iSerialNumber;
  16. __u8 bNumConfigurations;
  17. } __attribute__ ((packed));

image.gif
bNumConfigurations:usb中有几个配值,对应几个配值描述符

2. 配置描述符

  1. struct usb_config_descriptor {
  2. __u8 bLength;
  3. __u8 bDescriptorType;
  4. __le16 wTotalLength;
  5. __u8 bNumInterfaces;
  6. __u8 bConfigurationValue;
  7. __u8 iConfiguration;
  8. __u8 bmAttributes;
  9. __u8 bMaxPower;
  10. } __attribute__ ((packed));

image.gif
bNumInterfaces: 接口描述符的个数

3. 接口描述符

usb中有一个接口就是一个功能

  1. /* USB_DT_INTERFACE: Interface descriptor */
  2. struct usb_interface_descriptor {
  3. __u8 bLength;
  4. __u8 bDescriptorType;
  5. __u8 bInterfaceNumber;
  6. __u8 bAlternateSetting;
  7. __u8 bNumEndpoints;
  8. __u8 bInterfaceClass;
  9. __u8 bInterfaceSubClass;
  10. __u8 bInterfaceProtocol;
  11. __u8 iInterface;
  12. } __attribute__ ((packed));

image.gif
bNumEndpoints:g每个不同的功能就用不同的端点来实现

4. 端点描述符

  1. /* USB_DT_ENDPOINT: Endpoint descriptor */
  2. struct usb_endpoint_descriptor {
  3. __u8 bLength; //描述的长度
  4. __u8 bDescriptorType; //描述类型,对于端点就是USB_DT_ENDPOINT
  5. __u8 bEndpointAddress; //bit 0 ~ 3位为端点地址
  6. //bit 7 表示方向,输入还是输出
  7. __u8 bmAttributes; //bit 1 和 0 为传输类型 Transfer Type
  8. //00 表示控制, 01 表示等时,
  9. //10 表示批量, 11 表示中断
  10. __le16 wMaxPacketSize; //端点一次可以处理的最大字节数
  11. __u8 bInterval; //希望主机轮询自己的时间间隔
  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));

image.gif

5.之间的关系

0x02 端点 endpoint

  1. usb通信最基本的形式
    2. 端点只能往一个方向传输数据,IN / OUT (端口0除外)

    从usb端口0读取信息

3.存在usb设备端
4. 主机和端点之间的数据传输通过管道
linux USB设备驱动 - 图5image.gif

0x03 USB传输模式

  1. /* USB_DT_ENDPOINT: Endpoint descriptor */
  2. struct usb_endpoint_descriptor {
  3. __u8 bLength; //描述的长度
  4. __u8 bDescriptorType; //描述类型,对于端点就是USB_DT_ENDPOINT
  5. __u8 bEndpointAddress; //bit 0 ~ 3位为端点地址
  6. //bit 8 表示方向,输入还是输出
  7. __u8 bmAttributes; //bit 1 和 0 为传输类型 Transfer Type
  8. //00 表示控制, 01 表示等时,
  9. //10 表示批量, 11 表示中断
  10. __le16 wMaxPacketSize; //端点一次可以处理的最大字节数
  11. __u8 bInterval; //希望主机轮询自己的时间间隔
  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));

image.gif

__u8 bmAttributes; //bit 1 和 0 为传输类型 Transfer Type
//00 表示控制, 01 表示等时,
//10 表示批量, 11 表示中断
类似 U盘:就适合用批量, 类似鼠标就适合中断

1.控制传输

获取/配值设备
端口0

2. 中断传输

__u8 bInterval; //希望主机轮询自己的时间间隔
无法自己向输入子系统上报中断,需要主机主动访问

3. 批量传输

大容量数据传输, 没有固定的传输速率 usb打印机,扫描仪,大容量储存设备

4. 等时传输

大容量数据传输,实时性高,不保证到达, 音频视频,usb摄像头

0x04 USB请求块 urb

1. urb的结构

主机——》设备 从主机的角度去考虑

  1. struct urb {
  2. /* private: usb core and host controller only fields in the urb */
  3. struct kref kref; /* reference count of the URB */
  4. int unlinked; /* unlink error code */
  5. void *hcpriv; /* private data for host controller */
  6. atomic_t use_count; /* concurrent submissions counter */
  7. atomic_t reject; /* submissions will fail */
  8. /* public: documented fields in the urb that can be used by drivers */
  9. struct list_head urb_list; /* list head for use by the urb's
  10. * current owner */
  11. struct list_head anchor_list; /* the URB may be anchored */
  12. struct usb_anchor *anchor;
  13. struct usb_device *dev; /* (in) pointer to associated device */
  14. struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */
  15. unsigned int pipe; /* (in) pipe information */
  16. unsigned int stream_id; /* (in) stream ID */
  17. int status; /* (return) non-ISO status */
  18. unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
  19. void *transfer_buffer; /* (in) associated data buffer */
  20. dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
  21. struct scatterlist *sg; /* (in) scatter gather buffer list */
  22. int num_mapped_sgs; /* (internal) mapped sg entries */
  23. int num_sgs; /* (in) number of entries in the sg list */
  24. u32 transfer_buffer_length; /* (in) data buffer length */
  25. u32 actual_length; /* (return) actual transfer length */
  26. unsigned char *setup_packet; /* (in) setup packet (control only) */
  27. dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
  28. int start_frame; /* (modify) start frame (ISO) */
  29. int number_of_packets; /* (in) number of ISO packets */
  30. int interval; /* (modify) transfer interval
  31. * (INT/ISO) */
  32. int error_count; /* (return) number of ISO errors */
  33. void *context; /* (in) context for completion */
  34. usb_complete_t complete; /* (in) completion routine */
  35. struct usb_iso_packet_descriptor iso_frame_desc[0];
  36. /* (in) ISO ONLY */
  37. };

image.gif
第11个成员pipe 就是上文,主控制器与端点的传输通道
linux USB设备驱动 - 图9image.gif

第15个成员void *transfer_buffer;
如果的out端点,主机——>设备 transfer_buffer就是要穿的数据
第16个 dma_addr_t transfer_dma; 支持dma操作的用
int interval;轮询的时间间隔
context 保存主机—》设备的上下文信息
complete: 完成的回调函数

2. urb的使用

1. 分配urb

  1. struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);

image.gif

2. 初始化urb

  1. static inline void usb_fill_control_urb(struct urb *urb,
  2. struct usb_device *dev,
  3. unsigned int pipe,
  4. unsigned char *setup_packet,
  5. void *transfer_buffer,
  6. int buffer_length,
  7. usb_complete_t complete_fn,
  8. void *context)
  9. static inline void usb_fill_int_urb(struct urb *urb,
  10. struct usb_device *dev,
  11. unsigned int pipe,
  12. unsigned char *setup_packet,
  13. void *transfer_buffer,
  14. int buffer_length,
  15. usb_complete_t complete_fn,
  16. void *context)
  17. static inline void usb_fill_bulk_urb(struct urb *urb,
  18. struct usb_device *dev,
  19. unsigned int pipe,
  20. unsigned char *setup_packet,
  21. void *transfer_buffer,
  22. int buffer_length,
  23. usb_complete_t complete_fn,
  24. void *context)

image.gif
三种初始化: 控制 中断 批量(不适合等时传输)
看一下中断

  1. static inline void usb_fill_int_urb(struct urb *urb,
  2. struct usb_device *dev,
  3. unsigned int pipe,
  4. void *transfer_buffer,
  5. int buffer_length,
  6. usb_complete_t complete_fn,
  7. void *context,
  8. int interval)
  9. {
  10. urb->dev = dev;
  11. urb->pipe = pipe;
  12. urb->transfer_buffer = transfer_buffer;
  13. urb->transfer_buffer_length = buffer_length;
  14. urb->complete = complete_fn;
  15. urb->context = context;
  16. if (dev->speed == USB_SPEED_HIGH || dev->speed >= USB_SPEED_SUPER) {
  17. /* make sure interval is within allowed range */
  18. interval = clamp(interval, 1, 16);
  19. urb->interval = 1 << (interval - 1);
  20. } else {
  21. urb->interval = interval;
  22. }
  23. urb->start_frame = -1;
  24. }

image.gif

3. 提交urb

提交给主控制器,由主控制器发送给usb设备
1. 异步提交
只管发,不管成功

  1. int usb_submit_urb(struct urb *urb, gfp_t mem_flags);

image.gif
是urb_fill_xxx_urb传入的回调函数
2. 同步提交

  1. extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,
  2. __u8 request, __u8 requesttype, __u16 value, __u16 index,
  3. void *data, __u16 size, int timeout);
  4. extern int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
  5. void *data, int len, int *actual_length, int timeout);
  6. extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
  7. void *data, int len, int *actual_length,
  8. int timeout);

image.gif
一直等urb传输成功

0x05 usb驱动数据结构

  1. struct usb_device {
  2. int devnum; //usb总线上的地址
  3. enum usb_device_state state;
  4. enum usb_device_speed speed;
  5. struct usb_device *parent;
  6. struct usb_device_descriptor descriptor;//usb设备描述符
  7. struct usb_host_config *config; //包含了配置描述符(>=1)
  8. struct usb_host_config *actconfig;
  9. struct usb_host_endpoint *ep_in[16]; //支持的端点
  10. struct usb_host_endpoint *ep_out[16];
  11. int maxchild;
  12. };

image.gif

0x06 管道

每个端点通过管道和usb主控制器连接
1.端点地址
2. 数据传输方向(IN / OUT)
3. 数据传输模式

0x07 usb鼠标驱动分析

  1. 热插拔—-》专门的线程去检测hub ——》调用一个通用的usb设备驱动—-》获取描述符信息—>为接口注册设备(我们需要做的是usb接口驱动)
    2. 详解probe
    1. static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
    2. //接口 //匹配的ID
    3. {
    4. struct usb_device *dev = interface_to_usbdev(intf); //usb设备
    5. struct usb_host_interface *interface;
    6. //usb接口描述符(包含端点)
    7. struct usb_endpoint_descriptor *endpoint; //端点结构体
    8. struct usb_mouse *mouse;
    9. struct input_dev *input_dev;
    10. int pipe, maxp;
    11. int error = -ENOMEM;
    12. interface = intf->cur_altsetting; //接口
    13. if (interface->desc.bNumEndpoints != 1) //判断描述符中端点数为1个
    14. return -ENODEV;
    15. endpoint = &interface->endpoint[0].desc; //得到端点描述符
    16. if (!usb_endpoint_is_int_in(endpoint)) //判断是否为中断——in端点
    17. return -ENODEV;
    18. pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //得到一个pipe
    19. maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //约定一次传输数据大小
    20. mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);//申请一个usb_mouse
    21. input_dev = input_allocate_device(); //申请输入设备
    22. if (!mouse || !input_dev)
    23. goto fail1;
    24. mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);
    25. //为数据申请8字节
    26. if (!mouse->data)
    27. goto fail1;
    28. mouse->irq = usb_alloc_urb(0, GFP_KERNEL); //申请一个urb
    29. if (!mouse->irq)
    30. goto fail2;
    31. mouse->usbdev = dev;
    32. mouse->dev = input_dev;
    33. if (dev->manufacturer)
    34. strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
    35. //为mouse继续初始化
    36. if (dev->product) {
    37. if (dev->manufacturer)
    38. strlcat(mouse->name, " ", sizeof(mouse->name));
    39. strlcat(mouse->name, dev->product, sizeof(mouse->name));
    40. }
    41. if (!strlen(mouse->name))
    42. snprintf(mouse->name, sizeof(mouse->name),
    43. "USB HIDBP Mouse %04x:%04x",
    44. le16_to_cpu(dev->descriptor.idVendor),
    45. le16_to_cpu(dev->descriptor.idProduct));
    46. usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
    47. strlcat(mouse->phys, "/input0", sizeof(mouse->phys));
    48. input_dev->name = mouse->name;
    49. input_dev->phys = mouse->phys;
    50. usb_to_input_id(dev, &input_dev->id);
    51. input_dev->dev.parent = &intf->dev;
    52. // 输入子系统,配置
    53. input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
    54. input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
    55. BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
    56. input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
    57. input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
    58. BIT_MASK(BTN_EXTRA);
    59. input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
    60. input_set_drvdata(input_dev, mouse);
    61. input_dev->open = usb_mouse_open;
    62. input_dev->close = usb_mouse_close;
    63. usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data, //给urb赋值
    64. (maxp > 8 ? 8 : maxp), //最大长度8
    65. usb_mouse_irq, mouse, endpoint->bInterval);
    66. //中断的回调函数
    67. mouse->irq->transfer_dma = mouse->data_dma;
    68. mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    69. error = input_register_device(mouse->dev); //注册input设备
    70. if (error)
    71. goto fail3;
    72. usb_set_intfdata(intf, mouse);
    73. return 0;
    74. fail3:
    75. usb_free_urb(mouse->irq);
    76. fail2:
    77. usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
    78. fail1:
    79. input_free_device(input_dev);
    80. kfree(mouse);
    81. return error;
    82. }
    image.gif
  1. static int usb_mouse_open(struct input_dev *dev)
  2. {
  3. struct usb_mouse *mouse = input_get_drvdata(dev);
  4. mouse->irq->dev = mouse->usbdev; //设置中断设备
  5. if (usb_submit_urb(mouse->irq, GFP_KERNEL)) //异步提交urb
  6. return -EIO;
  7. return 0;
  8. }

image.gif
然后进行urb初始化时的回调函数(它并不是中断函数)

  1. static void usb_mouse_irq(struct urb *urb)
  2. {
  3. struct usb_mouse *mouse = urb->context; //哪个usb设备
  4. signed char *data = mouse->data; //设备会在此存放数据
  5. struct input_dev *dev = mouse->dev;
  6. int status;
  7. switch (urb->status) {
  8. case 0: /* success */
  9. break;
  10. case -ECONNRESET: /* unlink */
  11. case -ENOENT:
  12. case -ESHUTDOWN:
  13. return;
  14. /* -EPIPE: should clear the halt */
  15. default: /* error */
  16. goto resubmit;
  17. }
  18. //把数据提交给输入子系统
  19. input_report_key(dev, BTN_LEFT, data[0] & 0x01); //左按键
  20. input_report_key(dev, BTN_RIGHT, data[0] & 0x02); //右键
  21. input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); //中间键
  22. input_report_key(dev, BTN_SIDE, data[0] & 0x08);
  23. input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
  24. input_report_rel(dev, REL_X, data[1]); //x坐标
  25. input_report_rel(dev, REL_Y, data[2]); //y坐标
  26. input_report_rel(dev, REL_WHEEL, data[3]); //滚轮
  27. input_sync(dev); //结束
  28. resubmit:
  29. status = usb_submit_urb (urb, GFP_ATOMIC); //继续提交,继续轮询
  30. if (status)
  31. dev_err(&mouse->usbdev->dev,
  32. "can't resubmit intr, %s-%s/input0, status %d\n",
  33. mouse->usbdev->bus->bus_name,
  34. mouse->usbdev->devpath, status);
  35. }

image.gif