Libusb测试USB device(2)
因为测试的比较混乱,我就按照我的问题导向来描述我的过程吧
一. 提示libusb_context对象为NULL的错误:
在简化的过程中,我没有加入dev_handler判断为空就进行了具体的工作;
1 if( !dev_handler ) 2 libusb_open(dev_obj, &dev_handler); 3 if( dev_handler ) 4 // do your job
二. 如果不知道是什么原因(比如说ioctl之类的错误,那么可能是安装错误了驱动:
我使用了zadig-2.7.exe,来安装:
如果想要测试HID设备,安装成WinUSB会比较合适;
如果想要其他的usb设备安装成libusb会比较合适
如果想要测试等时传输(isoch)则libusbk会更合适一些(虽然我用的是WinUSB好像也可以)
三. 对于Windows中USB的驱动的了解也比较重要:
1 libusb_bullk_transfer 是libusb提供的函数; 2 do_syun_bulk_transfer 执行具体的工作 3 // 在do_sync_bulk中 4 { 5 alloc_transfer(); 6 fill_bulk_transfer(); 7 submit_transfer(); 8 sync_transfer_wait_for_completion(); 9 } 10 // 其中submit又是重点: 11 submit_transfer(){ 12 ref_device(); 13 mutex_lock & transfer_lock; 14 set_itransfer_obj; 15 add_to_flying_list 16 usbi_backend->submit_transfer() 17 } 18 // 其中ubsi_backend表示的是具体的实现的
四. 对于device发送的Descriptor解析页非常重要:
其中我认为最重要的就是对断点endpoint descriptor属性的理解 :
关系这个断电支持什么样的传输方式,已经最多穿多少(对于Isoch来说非常重要)
1 static void print_endpoint(const struct libusb_endpoint_descriptor* endpoint) 2 { 3 int i, ret; 4 printf("\t\t\tEndpoint:\n"); 5 // 可以看出endpoint是OUT(host->device)还是IN(device->host) 6 printf("\t\t\t\tbEndpointAddress: %02xh\n", endpoint->bEndpointAddress); 7 // 可以看出支持的操作:01:中断;02:bulk;03:isoch 8 printf("\t\t\t\tbmAttributes: %02xh\n", endpoint->bmAttributes); 9 printf("\t\t\t\twMaxPacketSize: %u\n", endpoint->wMaxPacketSize); 10 printf("\t\t\t\tTransfer Interval %u\n", endpoint->bInterval); 11 }
五. 关于等时传输isoch(困扰了我最久的一块儿)
Step1: 根据 微软的官方文档 了解等是传输断点(endpoint)中具体的信息;
获得不连续数据包大小MaximumPacketSize |
使用libusb提供的libusb_get_max_iso_packet_size方法就可以了 全速设备:这是1帧发送的数据; 对于高速设备,这个是1微帧发送的字节数; 好像已经固定了:Full是1024;High是3072;Super是45000 |
确认帧包含多少微帧 | 根据断点提供的bInterval来计算:2^(Interval-1) |
验证 |
需要连续包个数必须是每个帧数据包数的倍数; |
分配结构体 |
也就是alloc_transfer |
指定详细信息 | 也就是给transfer结构体添加缓冲区数据 |
最诡异的就是这个Interval:
根据Interval就可以计算出来轮询周期Polling Period = 2^(Interval-1); 轮询周期表示每轮询周期个总线间隔才会传输一次;
所以Interval == 4的时候就和Full Speed差不多;
Step2:实践:使用等时传输函数:
首先上代码:
int do_sync_transfer() { int endpoint_num; int test_type; int interface_num = USB_INTERFACE; int ret; int packet_size; int transfer_length; int numberOfPackets; unsigned char* buff; int temp; int trans_time; printf("INFO: Enter the test type\n\t0: ISOC IN; 1: ISOC OUT\n"); scanf_s("%d", &test_type); switch (test_type) { case 0: endpoint_num = ISOC_IN_ENDPOINT; dir = 1; break; case 1: endpoint_num = ISOC_OUT_ENDPOINT; dir = 0; break; default:printf("ERROR: Wrong test type\n");return; } if ((ret = libusb_clear_halt(g_handler, endpoint_num)) < 0) { printf("ERROR: clear halt failed\n\tReason: %s\n", libusb_strerror(ret)); return -1; } printf("INFO: clear halt succeed\n"); int number0fPacketPerFrame; // new ONE: // 计算缓冲区大小和长度最重要的地方: // 根据Interval 计算Polling Period // 缓冲区大小 = 最大不连续包大小 * 8/ Polling Period int win_interval = (ISOC_INTERVAL - 1); int min_buffer_size = MAX_BYTES_PER_INTERVAL * 8 / win_interval; buff = (unsigned char*)malloc(sizeof(unsigned char) * 3072); int temp_time = min_buffer_size / 16; for (int i = 0; i < temp_time; i++) memset(buff + (i * 16), temp_time, 16); libusb_set_debug(NULL, 4); if (endpoint_num == ISOC_OUT_ENDPOINT) { struct libusb_transfer* xfer; // 标识发送开始 // 也是很重要的:设置异步的检测状态函数: // 注意使用detach分离 is_send_over = 0; pthread_t my_thread; pthread_create(&my_thread, NULL, handle_isoc_transfer, g_ctx); pthread_detach(my_thread); for (trans_time = 0; trans_time < 15; trans_time++) { printf("TIME: %d\n", trans_time); printf("Enter Send transfer length:\n"); scanf_s("%d", &transfer_length); numberOfPackets = ceil(transfer_length / min_buffer_size); printf("The number of packets is : %d\n", numberOfPackets); if (numberOfPackets % number0fPacketPerFrame != 0) { printf("Number of Packet should be a multiple of numberOf packet per frame\n"); return; } for (int i = 0; i < numberOfPackets; i++) { int temp_translen = (transfer_length >= min_buffer_size) ? min_buffer_size : transfer_length; transfer_length -= temp_translen; xfer = libusb_alloc_transfer(number0fPacketPerFrame); if (!xfer) { printf("ERROR: no memory for transfer\n"); libusb_set_debug(NULL, 0); return -1; } libusb_fill_iso_transfer(xfer, g_handler, endpoint_num, buff, transfer_length, number0fPacketPerFrame, isoc_callback_send,NULL, 200); xfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; libusb_set_iso_packet_lengths(xfer, temp_translen); ret = libusb_submit_transfer(xfer); if (ret < 0) { printf("ERROR: submit\n"); libusb_free_transfer(xfer); libusb_set_debug(NULL, 0); return -1; } Sleep(500); libusb_free_transfer(xfer); } } is_send_over = 1; } else { int receive_time; is_receive_over = 0; pthread_t my_thread; pthread_create(&my_thread, NULL, handle_isoc_transfer, g_ctx); pthread_detach(my_thread); for (trans_time = 0; trans_time < 15; trans_time++) { printf("TIME: %d\n", trans_time); printf("Enter Receive Time:\n"); scanf_s("%d", &receive_time); for (int i = 0; i < receive_time; i++) { printf("Receive No.%d", i); if ((ret = libusb_clear_halt(g_handler, endpoint_num)) < 0) { printf("ERROR: clear halt failed\n\tReason: %s\n", libusb_strerror(ret)); return -1; } printf("INFO: clear halt succeed\n"); struct libusb_transfer* xfer; libusb_fill_iso_transfer( xfer, g_handler, endpoint_num, buff, min_buffer_size, number0fPacketPerFrame, isoc_callback_receive, NULL, 200); xfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; libusb_set_iso_packet_lengths(xfer, min_buffer_size); ret = libusb_submit_transfer(xfer); if (ret < 0) { printf("ERROR: submit\n"); libusb_free_transfer(xfer); libusb_set_debug(NULL, 0); return -1; } Sleep(200); libusb_free_transfer(xfer); } } is_receive_over = 1; } libusb_set_debug(NULL, 0); return 0; }
然后对应的异步处理函数代码如下:
1 static int LIBUSB_CALL usb_arrived_callback(libusb_context*, libusb_device*, libusb_hotplug_event, void*); 2 // leave : left 3 static int LIBUSB_CALL usb_left_callback(libusb_context*, libusb_device*, libusb_hotplug_event, void*); 4 // Just get one handler (default by dev) 5 void get_handle_and_device_by_vandp(uint16_t, uint16_t, libusb_device**, libusb_device_handle**); 6 int print_all_device(); 7 8 void* handle_isoc_transfer(void* param) { 9 if (param == NULL) { 10 printf("PARAM Wrong\n"); 11 return; 12 } 13 libusb_context* ctx = (libusb_context*)param; 14 while (1) { 15 if (is_send_over == 1) { 16 printf("This time already done"); 17 break; 18 } 19 libusb_handle_events(ctx); 20 } 21 } 22 23 static void LIBUSB_CALL isoc_callback_send(struct libusb_transfer* xfer) 24 { 25 printf("INFO : in isoc_callback send\n"); 26 if (xfer == NULL) 27 return; 28 printf("INFO : the transfer status is :%d\n", xfer->status); 29 switch(xfer->status) 30 { 31 case LIBUSB_TRANSFER_COMPLETED: 32 printf("INFO: transter complete\nDatas are:\n"); 33 printf("The length : %d\n", xfer->actual_length); 34 break; 35 case LIBUSB_TRANSFER_ERROR: 36 printf("ERROR: transfer error\n"); 37 break; 38 case LIBUSB_TRANSFER_TIMED_OUT: 39 printf("ERROR: transfer timeout\n"); 40 break; 41 case LIBUSB_TRANSFER_CANCELLED: 42 printf("ERROR: transfer is cancelled\n"); 43 break; 44 case LIBUSB_TRANSFER_STALL: 45 printf("ERROR: transfer stall\n"); 46 break; 47 case LIBUSB_TRANSFER_NO_DEVICE: 48 printf("ERROR: no device detected\n"); 49 break; 50 case LIBUSB_TRANSFER_OVERFLOW: 51 printf("ERROR: transfer overflowed\n"); 52 break; 53 default: 54 printf("ERROR: UNKNOWN status\n"); 55 break; 56 } 57 } 58 59 static void LIBUSB_CALL isoc_callback_receive(struct libusb_transfer* xfer) { 60 if (xfer == NULL) 61 return; 62 printf("INFO: enter : %s and status is : %d\n", __func__, xfer->status); 63 switch (xfer->status) 64 { 65 case LIBUSB_TRANSFER_COMPLETED: 66 printf("INFO: transfer completed\n"); 67 break; 68 case LIBUSB_TRANSFER_ERROR: 69 printf("ERROR: transfer error\n"); 70 break; 71 case LIBUSB_TRANSFER_TIMED_OUT: 72 printf("ERROR: transfer timeout\n"); 73 break; 74 case LIBUSB_TRANSFER_CANCELLED: 75 printf("ERROR: transfer is cancelled\n"); 76 break; 77 case LIBUSB_TRANSFER_STALL: 78 printf("ERROR: transfer stall\n"); 79 break; 80 case LIBUSB_TRANSFER_NO_DEVICE: 81 printf("ERROR: no device detected\n"); 82 break; 83 case LIBUSB_TRANSFER_OVERFLOW: 84 printf("ERROR: transfer overflowed\n"); 85 break; 86 default: 87 printf("ERROR: UNKNOWN status\n"); 88 break; 89 } 90 }
总是不确定到底该发送多少个数据才能被接收:
终于找到了windows_winusb.c中的winusbx_submit_iso_transfer函数:
1 // For high-speed and SuperSpeed device, the interval is 2**(bInterval-1). 2 if (transfer->dev_handle->dev->speed >= LIBUSB_SPEED_HIGH) 3 interval = (1 << (pipe_info_ex.Interval - 1)); 4 if (transfer->dev_handle->dev->speed >= LIBUSB_SPEED_HIGH) {// Microframes (125us) 5 printf("--------INFO: %d\n", (pipe_info_ex.MaximumBytesPerInterval * 8) / interval); 6 iso_transfer_size_multiple = (pipe_info_ex.MaximumBytesPerInterval * 8) / interval; 7 } 8 else // Normal Frames (1ms) 9 iso_transfer_size_multiple = pipe_info_ex.MaximumBytesPerInterval / interval; 10 if (transfer->length % iso_transfer_size_multiple != 0) { 11 usbi_err(TRANSFER_CTX(transfer), "length of isoch buffer must be a multiple of the MaximumBytesPerInterval * 8 / Interval"); 12 return LIBUSB_ERROR_INVALID_PARAM; 13 }
可见:里面对interval也进行了计算:
在使用等时传输读的时候需要将buffer的长度设置为size_multiple;但是写的时候并不需要;
最后终于测试成功了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律