0x00 前言
最近需要把usb做成串口,所以来仔细学一下linux下usb的驱动
0x01描述符类型
1. usb设备描述符
/* USB_DT_DEVICE: Device descriptor */struct usb_device_descriptor {__u8 bLength;__u8 bDescriptorType;__le16 bcdUSB;__u8 bDeviceClass;__u8 bDeviceSubClass;__u8 bDeviceProtocol;__u8 bMaxPacketSize0;__le16 idVendor;__le16 idProduct;__le16 bcdDevice;__u8 iManufacturer;__u8 iProduct;__u8 iSerialNumber;__u8 bNumConfigurations;} __attribute__ ((packed));

bNumConfigurations:usb中有几个配值,对应几个配值描述符
2. 配置描述符
struct usb_config_descriptor {__u8 bLength;__u8 bDescriptorType;__le16 wTotalLength;__u8 bNumInterfaces;__u8 bConfigurationValue;__u8 iConfiguration;__u8 bmAttributes;__u8 bMaxPower;} __attribute__ ((packed));
3. 接口描述符
usb中有一个接口就是一个功能
/* USB_DT_INTERFACE: Interface descriptor */struct usb_interface_descriptor {__u8 bLength;__u8 bDescriptorType;__u8 bInterfaceNumber;__u8 bAlternateSetting;__u8 bNumEndpoints;__u8 bInterfaceClass;__u8 bInterfaceSubClass;__u8 bInterfaceProtocol;__u8 iInterface;} __attribute__ ((packed));

bNumEndpoints:g每个不同的功能就用不同的端点来实现
4. 端点描述符
/* USB_DT_ENDPOINT: Endpoint descriptor */struct usb_endpoint_descriptor {__u8 bLength; //描述的长度__u8 bDescriptorType; //描述类型,对于端点就是USB_DT_ENDPOINT__u8 bEndpointAddress; //bit 0 ~ 3位为端点地址//bit 7 表示方向,输入还是输出__u8 bmAttributes; //bit 1 和 0 为传输类型 Transfer Type//00 表示控制, 01 表示等时,//10 表示批量, 11 表示中断__le16 wMaxPacketSize; //端点一次可以处理的最大字节数__u8 bInterval; //希望主机轮询自己的时间间隔/* NOTE: these two are _only_ in audio endpoints. *//* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */__u8 bRefresh;__u8 bSynchAddress;} __attribute__ ((packed));
5.之间的关系
0x02 端点 endpoint
- usb通信最基本的形式
2. 端点只能往一个方向传输数据,IN / OUT (端口0除外)从usb端口0读取信息
3.存在usb设备端
4. 主机和端点之间的数据传输通过管道

0x03 USB传输模式
/* USB_DT_ENDPOINT: Endpoint descriptor */struct usb_endpoint_descriptor {__u8 bLength; //描述的长度__u8 bDescriptorType; //描述类型,对于端点就是USB_DT_ENDPOINT__u8 bEndpointAddress; //bit 0 ~ 3位为端点地址//bit 8 表示方向,输入还是输出__u8 bmAttributes; //bit 1 和 0 为传输类型 Transfer Type//00 表示控制, 01 表示等时,//10 表示批量, 11 表示中断__le16 wMaxPacketSize; //端点一次可以处理的最大字节数__u8 bInterval; //希望主机轮询自己的时间间隔/* NOTE: these two are _only_ in audio endpoints. *//* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */__u8 bRefresh;__u8 bSynchAddress;} __attribute__ ((packed));

__u8 bmAttributes; //bit 1 和 0 为传输类型 Transfer Type
//00 表示控制, 01 表示等时,
//10 表示批量, 11 表示中断
类似 U盘:就适合用批量, 类似鼠标就适合中断
1.控制传输
2. 中断传输
__u8 bInterval; //希望主机轮询自己的时间间隔
无法自己向输入子系统上报中断,需要主机主动访问
3. 批量传输
大容量数据传输, 没有固定的传输速率 usb打印机,扫描仪,大容量储存设备
4. 等时传输
大容量数据传输,实时性高,不保证到达, 音频视频,usb摄像头
0x04 USB请求块 urb
1. urb的结构
主机——》设备 从主机的角度去考虑
struct urb {/* private: usb core and host controller only fields in the urb */struct kref kref; /* reference count of the URB */int unlinked; /* unlink error code */void *hcpriv; /* private data for host controller */atomic_t use_count; /* concurrent submissions counter */atomic_t reject; /* submissions will fail *//* public: documented fields in the urb that can be used by drivers */struct list_head urb_list; /* list head for use by the urb's* current owner */struct list_head anchor_list; /* the URB may be anchored */struct usb_anchor *anchor;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 */struct scatterlist *sg; /* (in) scatter gather buffer list */int num_mapped_sgs; /* (internal) mapped sg entries */int num_sgs; /* (in) number of entries in the sg list */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 */};

第11个成员pipe 就是上文,主控制器与端点的传输通道

第15个成员void *transfer_buffer;
如果的out端点,主机——>设备 transfer_buffer就是要穿的数据
第16个 dma_addr_t transfer_dma; 支持dma操作的用
int interval;轮询的时间间隔
context 保存主机—》设备的上下文信息
complete: 完成的回调函数
2. urb的使用
1. 分配urb
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
2. 初始化urb
static inline void usb_fill_control_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,unsigned char *setup_packet,void *transfer_buffer,int buffer_length,usb_complete_t complete_fn,void *context)static inline void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,unsigned char *setup_packet,void *transfer_buffer,int buffer_length,usb_complete_t complete_fn,void *context)static inline void usb_fill_bulk_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,unsigned char *setup_packet,void *transfer_buffer,int buffer_length,usb_complete_t complete_fn,void *context)

三种初始化: 控制 中断 批量(不适合等时传输)
看一下中断
static inline void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,void *transfer_buffer,int buffer_length,usb_complete_t complete_fn,void *context,int interval){urb->dev = dev;urb->pipe = pipe;urb->transfer_buffer = transfer_buffer;urb->transfer_buffer_length = buffer_length;urb->complete = complete_fn;urb->context = context;if (dev->speed == USB_SPEED_HIGH || dev->speed >= USB_SPEED_SUPER) {/* make sure interval is within allowed range */interval = clamp(interval, 1, 16);urb->interval = 1 << (interval - 1);} else {urb->interval = interval;}urb->start_frame = -1;}
3. 提交urb
提交给主控制器,由主控制器发送给usb设备
1. 异步提交
只管发,不管成功
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);

是urb_fill_xxx_urb传入的回调函数
2. 同步提交
extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,__u8 request, __u8 requesttype, __u16 value, __u16 index,void *data, __u16 size, int timeout);extern int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,void *data, int len, int *actual_length, int timeout);extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,void *data, int len, int *actual_length,int timeout);
0x05 usb驱动数据结构
struct usb_device {int devnum; //usb总线上的地址enum usb_device_state state;enum usb_device_speed speed;struct usb_device *parent;struct usb_device_descriptor descriptor;//usb设备描述符struct usb_host_config *config; //包含了配置描述符(>=1)struct usb_host_config *actconfig;struct usb_host_endpoint *ep_in[16]; //支持的端点struct usb_host_endpoint *ep_out[16];int maxchild;};
0x06 管道
每个端点通过管道和usb主控制器连接
1.端点地址
2. 数据传输方向(IN / OUT)
3. 数据传输模式
0x07 usb鼠标驱动分析
- 热插拔—-》专门的线程去检测hub ——》调用一个通用的usb设备驱动—-》获取描述符信息—>为接口注册设备(我们需要做的是usb接口驱动)
2. 详解probestatic int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)//接口 //匹配的ID{struct usb_device *dev = interface_to_usbdev(intf); //usb设备struct usb_host_interface *interface;//usb接口描述符(包含端点)struct usb_endpoint_descriptor *endpoint; //端点结构体struct usb_mouse *mouse;struct input_dev *input_dev;int pipe, maxp;int error = -ENOMEM;interface = intf->cur_altsetting; //接口if (interface->desc.bNumEndpoints != 1) //判断描述符中端点数为1个return -ENODEV;endpoint = &interface->endpoint[0].desc; //得到端点描述符if (!usb_endpoint_is_int_in(endpoint)) //判断是否为中断——in端点return -ENODEV;pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //得到一个pipemaxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //约定一次传输数据大小mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);//申请一个usb_mouseinput_dev = input_allocate_device(); //申请输入设备if (!mouse || !input_dev)goto fail1;mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);//为数据申请8字节if (!mouse->data)goto fail1;mouse->irq = usb_alloc_urb(0, GFP_KERNEL); //申请一个urbif (!mouse->irq)goto fail2;mouse->usbdev = dev;mouse->dev = input_dev;if (dev->manufacturer)strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));//为mouse继续初始化if (dev->product) {if (dev->manufacturer)strlcat(mouse->name, " ", sizeof(mouse->name));strlcat(mouse->name, dev->product, sizeof(mouse->name));}if (!strlen(mouse->name))snprintf(mouse->name, sizeof(mouse->name),"USB HIDBP Mouse %04x:%04x",le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));usb_make_path(dev, mouse->phys, sizeof(mouse->phys));strlcat(mouse->phys, "/input0", sizeof(mouse->phys));input_dev->name = mouse->name;input_dev->phys = mouse->phys;usb_to_input_id(dev, &input_dev->id);input_dev->dev.parent = &intf->dev;// 输入子系统,配置input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |BIT_MASK(BTN_EXTRA);input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);input_set_drvdata(input_dev, mouse);input_dev->open = usb_mouse_open;input_dev->close = usb_mouse_close;usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data, //给urb赋值(maxp > 8 ? 8 : maxp), //最大长度8usb_mouse_irq, mouse, endpoint->bInterval);//中断的回调函数mouse->irq->transfer_dma = mouse->data_dma;mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;error = input_register_device(mouse->dev); //注册input设备if (error)goto fail3;usb_set_intfdata(intf, mouse);return 0;fail3:usb_free_urb(mouse->irq);fail2:usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);fail1:input_free_device(input_dev);kfree(mouse);return error;}

static int usb_mouse_open(struct input_dev *dev){struct usb_mouse *mouse = input_get_drvdata(dev);mouse->irq->dev = mouse->usbdev; //设置中断设备if (usb_submit_urb(mouse->irq, GFP_KERNEL)) //异步提交urbreturn -EIO;return 0;}

然后进行urb初始化时的回调函数(它并不是中断函数)
static void usb_mouse_irq(struct urb *urb){struct usb_mouse *mouse = urb->context; //哪个usb设备signed char *data = mouse->data; //设备会在此存放数据struct input_dev *dev = mouse->dev;int status;switch (urb->status) {case 0: /* success */break;case -ECONNRESET: /* unlink */case -ENOENT:case -ESHUTDOWN:return;/* -EPIPE: should clear the halt */default: /* error */goto resubmit;}//把数据提交给输入子系统input_report_key(dev, BTN_LEFT, data[0] & 0x01); //左按键input_report_key(dev, BTN_RIGHT, data[0] & 0x02); //右键input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); //中间键input_report_key(dev, BTN_SIDE, data[0] & 0x08);input_report_key(dev, BTN_EXTRA, data[0] & 0x10);input_report_rel(dev, REL_X, data[1]); //x坐标input_report_rel(dev, REL_Y, data[2]); //y坐标input_report_rel(dev, REL_WHEEL, data[3]); //滚轮input_sync(dev); //结束resubmit:status = usb_submit_urb (urb, GFP_ATOMIC); //继续提交,继续轮询if (status)dev_err(&mouse->usbdev->dev,"can't resubmit intr, %s-%s/input0, status %d\n",mouse->usbdev->bus->bus_name,mouse->usbdev->devpath, status);}



