在进行 usb 传输的过程中,往往需要大容量传输。无外乎批量传输。典型的批量传输协议如下:
cdc-acm :usb 转串口。win10 以下需要 pc 驱动。
mass-stroage:U 盘设备。
通常仅仅只需要批量传输,而不想引入复杂的协议,这个时候自定义接口进行批量传输就比较合适了,需要注意的是自定义接口需要安装pc驱动, host 并不支持自定义的接口。
描述符布局
- 设备描述符:bDeviceClass 改为0xFF
- 配置描述符:常规配置
- 接口描述符:bInterfaceClass 改为 0xFF
Inerface Descriptor Endpoint Descruptor(in) Endpoint Descruptor(out)
描述符介绍
这里只介绍接口描述符和端点描述符,并且是独立的接口设备。
自定义接口:关联两个端点
static struct usb_interface_descriptor intf_desc = { .bLength = sizeof(intf_desc), .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 0x02, .bInterfaceClass = 0xFF, .bInterfaceSubClass = 0x0, .bInterfaceProtocol = 0x0, };
端点描述符:in/out 端点
static struct usb_endpoint_descriptor hs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }; static struct usb_endpoint_descriptor hs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), };
整体接口描述符:
static struct usb_descriptor_header *hs_intf_descs[] = { //(struct usb_descriptor_header *) &iad_desc, (struct usb_descriptor_header *) &intf_desc, (struct usb_descriptor_header *) &hs_bulk_out_desc, (struct usb_descriptor_header *) &hs_bulk_in_desc, NULL, };
接口描述符如下:
复合设备
有的时候我们需要在已有的设备功能上,添加一个接口自定义功能。这个时候就需要添加复合设备。复合设备独立接口设备唯一区别在于接口描述符和设备描述符不同。
- 设备描述符:按照杂项设备处理
- 自定义接口,添加一个iad 关联
IAD Descriptor Inerface Descriptor Endpoint Descruptor(in) Endpoint Descruptor(out)
IAD 描述符:
static struct usb_interface_assoc_descriptor iad_desc = { .bLength = sizeof(iad_desc), .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, .bFirstInterface = 0, // 动态修改 .bInterfaceCount = 1, .bFunctionClass = 0xFF, // vendor .bFunctionSubClass = 0x00, .bFunctionProtocol = 0x00, .iFunction = 0, };
整体接口描述符:
static struct usb_descriptor_header *hs_intf_descs[] = { (struct usb_descriptor_header *) &iad_desc, (struct usb_descriptor_header *) &intf_desc, (struct usb_descriptor_header *) &hs_bulk_out_desc, (struct usb_descriptor_header *) &hs_bulk_in_desc, NULL, };
自定义接口描述符如下:
驱动适配
对于自定义接口,需要按照pc 驱动,否则 windows 端无法识别。
使用 zadig-2.5.exe 按照对应接口的设备驱动。这里是接口2
不同的驱动对应不同的系统调用。
winUSB:对应 libusb(1.0) 系统调用
libusbK:对应libusbK 系统调用
libusb-win32:对应libusb-0.1(libusb-win32) 系统调用
有关libusb、libusbK、libusb-win32 之间的关系本文不做过多介绍,这里只是版本的差异。
主机应用
以libusb 为例介绍。
控制传输
int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, uint16_t wLength, unsigned int timeout);
控制写:
... libusb_device_handle* handle = dev->handle; int status = libusb_control_transfer( handle, /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_INTERFACE, /* bRequest */ bRequest, /* wValue */ 0, /* wIndex */ 0, /* Data */ (unsigned char *)data, /* wLength */ len, ms);
控制读:
... libusb_device_handle* handle = dev->handle; int status = libusb_control_transfer( handle, /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_INTERFACE, /* bRequest */ bRequest, /* wValue */ 0, /* wIndex */ 0, /* Data */ (unsigned char*)(*data), /* wLength */ len, ms); return status;
批量传输
int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, unsigned char endpoint, unsigned char *data, int length, int *actual_length, unsigned int timeout);
批量读:
int bulk_read(struct bulkusbdev* dev, void* buffer, size_t len, int ms) { int size, errcode; libusb_device_handle* handle = dev->handle; uint8_t endpoint_in = dev->endpoint_in; errcode = libusb_bulk_transfer(handle, endpoint_in,(unsigned char*) buffer, len, &size, ms); if (errcode < 0) { printf("read: %s\n", libusb_strerror((enum libusb_error)errcode)); return -1; } return size; }
批量写:
int bulk_write(struct bulkusbdev* dev, void* buffer, int len, int ms) { int size, errcode; libusb_device_handle* handle = dev->handle; uint8_t endpoint_out = dev->endpoint_out; errcode = libusb_bulk_transfer(handle, endpoint_out, (unsigned char*)buffer, len, &size, ms); if (errcode<0) { printf("write: %s\n", libusb_strerror((enum libusb_error)errcode)); return -1; } return size; }
总结
本文简单介绍了,USB 设备自定义批量传输的设备描述符实现,有关不同的平台,设备驱动实现方式不一。掌握了描述符,基本就掌握了设备实现的核心。参考对应平台做相关移植即可。