成就更好的自己
时隔一年再次开始撰写博客,这一年的时间经历了很多,现在终于稳定下来。以后很长一段时间都能够稳定的学习和更新。时间将会聚焦于USB和PCIe的开发进行,能和大家共同进步真的很高兴。
本篇为USB系列的LibUSB使用指南的第一篇。
USB系列主要围绕USB的知识、协议、开发总结、使用说明等进行。
LibUSB使用指南主要围绕LibUSB库的使用进行。
LibUSB中的描述符结构分析
LibUSB中的描述符结构主要分为一下几种层次:
设备描述符->配置描述符->接口描述符(备用接口描述符)->端点描述符
看到这篇博客的都应该知道上述描述符的包含关系和可能的存在数量,基础知识不再赘述,到时候会专门写对应的基础知识博客。
LibUSB中的描述符包含关系与结构如下图所示:
从图中可以看出是没有设备描述符libusb_device_descriptor的,因为设备描述符一般可以看做是以设备为对象的一种描述结构,因此每个设备只有一个而且地位特殊。
图中最高层是配置描述符libusb_config_descripto,使用libusb_get_config_descriptor函数传入索引可以获得该设备中的某一个配置描述符。配置描述符中有一项bNumInterfaces为接口描述符的个数,可通过该项获得在该配置描述符中接口描述符的个数。
在配置描述符中还有一项为libusb_interface *interface,该项就是接口描述符的结构体数组。这个接口描述符与我们广义上理解的接口描述符不同,这里的接口描述符是由备用接口描述符结构体数组和备用描述符个数组成的,也就是libusb_interface_descriptor *altsetting和num_altsetting这两项。
要注意的是bNumInterfaces所指是接口描述符数组的个数,num_altsetting所指的是每个接口描述符数组成员中的备用接口描述符的数量。因此实际的配置描述符是由备用接口描述符组成的一个二维数组,这个二维数组的主索引是bNumInterfaces,副索引是num_altsetting。
在备用接口描述符数组中的每个成员都携带着在该备用接口下所属的若干端点的描述符结构体。
整个上面的过程中只有这个备用接口的概念和广义理解上的不太一样,剩下各个描述符中的各个字段均与协议栈中我们所通识的一样。实际上,这些结构体的存在方式和使用LibUSB函数获取描述符的方式与USB设备的枚举过程和方式的有着本质上的关联(后期博客提到)。
下面就是以博主电脑上为例,输出能探测到设备的详细信息(太长了,部分截图):
Windows下的USB驱动问题
Windows下LibUSB产生的很多报错与驱动有着很大关系。
我先言简意骇的简要介绍一下在你的Windows中可能存在的各种USB驱动,我暂时驱动分为高级的功能性驱动和低级的设备性驱动:
高级的功能性驱动:比如插入MSD类设备、移动硬盘、蓝牙适配器、HID类设备、网卡等具有实际功能性意义的设备时,Windows底层会自动的识别该设备的类和功能,从而找到一款能够直接适配该设备功能的通用高级驱动,这样就实现了目前Windows下多数陌生设备的免驱。
低级的设备性驱动:当插入的设备极为特殊,Windows认不出来这个玩意是干嘛的(比如设备描述符和接口描述符中的类字段与子类字段全部为0xff,即厂家定义),这个时候仅仅只是能够进行设备的基础枚举操作和通信。此时能够支撑设备枚举和通信的驱动就是设备性驱动,比如libusb-win32或WinUSB等。
假如我现在插入一个U盘就开始调用LibUSB库进行Open操作,这样是一定会返回错误的,因为LibUSB本身不是驱动,实际上也是要调用驱动留出的系统调用接口进行数据和指令传递的(这里类比linux的特性,表述可能些许不合适)。当这个设备底层根本不是LibUSB需要的接口的时候,当然是无法正常调用。
此时需要将该设备的驱动换成合适的低级驱动(linux的优势就出来了),LibUSB库在Windows平台上是基于WinUSB底层驱动去开发的。因此最适合LibUSB开发的底层驱动当然是WinUSB。博主尝试过使用linusb-win32等其他的驱动,他们可以open设备但是在claim接口的时候会有掉设备的情况(一claim就听到Windows掉USB设备的声音,正常后不会这样),直接导致transfer的时候返回LIBUSB_ERROR_IO(-1)。因此,在进行Windows下的LibUSB开发之前先解决你的驱动问题。
驱动问题的解决方式是通过zadig工具进行驱动替换,首先插入你的设备,list一下获得设备名单,选择你的设备,选择winUSB驱动,replace即可。前提是要有网(公司的机器没有网,GG)。如图(博主使用的SMT32USBD虚拟为com):
注意,使用此种方式他只会根据选择设备的设备号进行定向更换,这样就产生了两个问题:第一,替换的效果只能适用同一VID和PID的设备。第二,被替换的设备从此以后只能使用这个低级驱动,原来高级驱动的功能将不能使用。若不小心将重要设备的驱动进行重置和降级,那就去zadig工具的官网上寻找答案吧(当时无知把移动硬盘驱动给降级了,人都裂开了)。
zadig官网与技术支持:
https://www.baidu.com/link?url=UuDz3G0f6UbanYtYLC8SIC6sDu-aTCuBlW1BkriYHqK&wd=&eqid=cd9600c2000f6899000000046358207b
Open_device过程
这一节咱们直接面向对象来说:
- libusb_open_device_with_vid_pid或者open_device:打开设备,得到一个libusb_device_handle,没啥说的,驱动没安好你就是开不了,气不气。
- libusb_get_device:由libusb_device_handle结构体反推libusb_device_descriptor结构体用的(open_device的不用)。
- libusb_get_device_descriptor:获取端点信息的第一步—获取设备描述符。
- libusb_get_config_descriptor:获取端点信息的第二步—获取配置描述符。、
- 使用上一步的信息,通过一系列if筛选,找到你想要建立通信的备用接口和备用接口中的端点地址。
- libusb_claim_interface和libusb_set_interface_alt_setting:声明接口和备用接口,传入参数为索引值,上一步得到的。驱动没安好你就是claim不了(ret=-5),气不气。
- libusb_clear_halt:清除端点和对应通道中的残存的数据,为下一次通信做好准备。
- 该free的free,该release的release。
到此为止,正常情况下你就应该得到一个已经open的设备和一些(>=1)准备传输的端点。注意:第六第七步的顺序千万不能倒过来,否则你的libusb_clear_halt会报错LIBUSB_ERROR_NOT_FOUND (ret=-5)。而且在Windows开发中,LibUSB中的detach_kernel系列的函数都是多余的,人家Windows的原理就不需要这些操作。
Transfer过程
博主使用的是libusb_bulk_transfer传输的,中间出了点小问题,简要说一下:
- libusb_bulk_transfer返回LIBUSB_ERROR_IO(-1)大概率是因为你的驱动没有装对,libusb0 (v2.6.0)的驱动能够open和claim不报错,但是就是在传输的时候报错。
- libusb_bulk_transfer返回LIBUSB_ERROR_NOT_FOUND (-5)大概率是因为你claim这一步没有正常claim。注意如果你的端点出自备用接口,记得claim备用接口(claim标准接口好像是默认claim该接口下索引为0的备用接口,保险起见不管备用接口索引是几,记得claim一下备用接口)。
贴一下程序(bulk)
main.c
#include <stdio.h>
#include <main.h>
#include <libusb.h>
#include "log.h"
#include <stdint.h>
#include "usb_device_opt.h"
const uint16_t VID = 0x1234;
const uint16_t PID = 0x1234;
//const uint16_t VID = 0x0bda;
//const uint16_t PID = 0x8179;
int main(void)
{
libusb_device **devs;
USB_MSD_ST msd = {NULL, NULL, 0x00, 0x00};
uint8_t err = 0;
int16_t dev_num;
Log_Init(3);
err = libusb_init(NULL);
if(err > 0)
{
LOG_ERROR("libusb_init() err with %d", err);
goto init_err;
}
//libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL , LIBUSB_LOG_LEVEL_INFO);
dev_num = libusb_get_device_list(NULL, &devs);
if(dev_num < 0)
{
LOG_ERROR("libusb_get_device_list() err with %d", err);
goto getlist_err;
}
USB_Dev_Scan_A_Print(dev_num, devs);
err = USB_MSD_Open(VID, PID, &msd);
if(err > 0)
{
USB_MSD_Close(&msd);
goto getlist_err;
}
uint8_t buff[512];
int size = 0;
memset(buff, 9, 512);
err = USB_MSD_Bulk_Write(&msd, buff, 64, &size, 1000);
if(err > 0)
{
USB_MSD_Close(&msd);
goto getlist_err;
}
else
{
LOG_INFO("Send OK!");
}
err = USB_MSD_Close(&msd);
getlist_err:
libusb_free_device_list(devs, 1);
init_err:
libusb_exit(NULL);
return 0;
}
usb_device_opt.c
#include <stdio.h>
#include <libusb.h>
#include "log.h"
#include <stdint.h>
#include "usb_device_opt.h"
uint8_t USB_Dev_Scan_A_Print(int16_t dev_num, libusb_device **devs)
{
struct libusb_device_descriptor usb_dev_desc;
struct libusb_config_descriptor *usb_cfg_desc;
uint8_t err = 0;
LOG_INFO("Dev_Num:%d", dev_num);
for(uint16_t i = 0;i < dev_num; i++)
{
err = libusb_get_device_descriptor(devs[i], &usb_dev_desc);
if(err > 0)
{
LOG_ERROR("libusb_get_device_descriptor() err with %d", err);
goto getdevdesc_err;
}
printf("|--[Vid:0x%04x, Pid:0x%04x]-[Class:0x%02x, SubClass:0x%02x]-[bus:%d, device:%d, port:%d]-[cfg_desc_num:%d]\n",
usb_dev_desc.idVendor, usb_dev_desc.idProduct, usb_dev_desc.bDeviceClass, usb_dev_desc.bDeviceSubClass,
libusb_get_bus_number(devs[i]), libusb_get_device_address(devs[i]), libusb_get_port_number(devs[i]), usb_dev_desc.bNumConfigurations);
//printf()
for(uint8_t j = 0;j < usb_dev_desc.bNumConfigurations; j++)
{
err = libusb_get_config_descriptor(devs[i], j, &usb_cfg_desc);
if(err > 0)
{
LOG_ERROR("libusb_get_config_descriptor(cfg_index:%d) err with %d", j, err);
goto getcfgdesc_err;
}
printf("| |--cfg_desc:%02d-[cfg_value:0x%01x]-[infc_desc_num:%02d]\n",
j, usb_cfg_desc->bConfigurationValue, usb_cfg_desc->bNumInterfaces);
for(uint8_t l = 0;l < usb_cfg_desc->bNumInterfaces; l++)
for(uint8_t n = 0;n < usb_cfg_desc->interface[l].num_altsetting; n++)
{
printf("| | |--intfc_desc: %02d:%02d-[Class:0x%02x, SubClass:0x%02x]-[ep_desc_num:%02d]\n",
l, n, usb_cfg_desc->interface[l].altsetting[n].bInterfaceClass, usb_cfg_desc->interface[l].altsetting[n].bInterfaceSubClass,
usb_cfg_desc->interface[l].altsetting[n].bNumEndpoints);
for(uint8_t m = 0;m < usb_cfg_desc->interface[l].altsetting[n].bNumEndpoints; m++)
{
printf("| | | |--ep_desc:%02d-[Add:0x%02x]-[Attr:0x%02x]-[MaxPkgLen:%02d]\n",
m, usb_cfg_desc->interface[l].altsetting[n].endpoint[m].bEndpointAddress,
usb_cfg_desc->interface[l].altsetting[n].endpoint[m].bmAttributes,
usb_cfg_desc->interface[l].altsetting[n].endpoint[m].wMaxPacketSize);
}
}
}
}
return 0;
getdevdesc_err:
return 0xff;
getcfgdesc_err:
return 0xff;
}
uint8_t USB_MSD_Open(uint16_t VID, uint16_t PID, USB_MSD_ST *msd)
{
struct libusb_device_descriptor usb_dev_desc;
struct libusb_config_descriptor *usb_cfg_desc;
uint8_t intfc_index = 0;
uint8_t err = 0;
msd->msd_handle = libusb_open_device_with_vid_pid(NULL, VID, PID);
if(msd->msd_handle == NULL)
{
LOG_ERROR("[0x%04x:0x%04x] MSD Open failed!", VID, PID);
goto opendev_err;
}
msd->msd_dev = libusb_get_device(msd->msd_handle);
if(msd->msd_dev == NULL)
{
LOG_ERROR("[0x%04x:0x%04x] get dev failed!", VID, PID);
goto getdev_err;
}
err = libusb_get_device_descriptor(msd->msd_dev, &usb_dev_desc);
if(err > 0)
{
LOG_ERROR("[0x%04x:0x%04x] get dev_desc failed err with %d", VID, PID, err);
goto getdevdesc_err;
}
err = libusb_get_config_descriptor(msd->msd_dev, 0, &usb_cfg_desc);
if(err > 0)
{
LOG_ERROR("[0x%04x:0x%04x] get cfg_desc failed err with %d", VID, PID, err);
goto getcfgdesc_err;
}
for(uint8_t m = 0;m < usb_cfg_desc->bNumInterfaces; m++)
{
for(uint8_t n = 0;n < usb_cfg_desc->interface[m].num_altsetting;n++)
{
if(usb_cfg_desc->interface[m].altsetting[n].bInterfaceClass == 0x0a && usb_cfg_desc->interface[m].altsetting[n].bInterfaceSubClass == 0x00)
{
for(uint8_t i = 0;i < usb_cfg_desc->interface[m].altsetting[n].bNumEndpoints;i++)
{
if((usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK)
{
if((usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN)
{
msd->endpoint_in = usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress;
}
if((usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT)
{
msd->endpoint_out = usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress;
}
}
}
if(msd->endpoint_in != 0x00 && msd->endpoint_out != 0x00)
{
intfc_index = m;
}
else
{
msd->endpoint_in = 0x00;
msd->endpoint_out = 0x00;
}
}
}
}
if(msd->endpoint_in == 0x00 || msd->endpoint_out == 0x00)
{
LOG_ERROR("[0x%04x:0x%04x] get ep_addr failed!", VID, PID);
goto getepaddr_err;
}
err = libusb_claim_interface(msd->msd_handle, 1);
err = libusb_set_interface_alt_setting(msd->msd_handle, 1, 0);
err = libusb_claim_interface(msd->msd_handle, 0);
err = libusb_set_interface_alt_setting(msd->msd_handle, 0, 0);
if(err > 0)
{
LOG_ERROR("[0x%04x:0x%04x] claim intfc failed err with %d", VID, PID, err);
goto claimintfc_err;
}
err = libusb_clear_halt(msd->msd_handle, msd->endpoint_out);
if(err > 0)
{
LOG_ERROR("[0x%04x:0x%04x] ep_out:%x clear halt failed err with %d", VID, PID, msd->endpoint_out, (int8_t)err);
goto epclrhalt_err;
}
err = libusb_clear_halt(msd->msd_handle, msd->endpoint_in);
if(err > 0)
{
LOG_ERROR("[0x%04x:0x%04x] ep_in:%x clear halt failed err with %d", VID, PID, msd->endpoint_in, (int8_t)err);
goto epclrhalt_err;
}
libusb_free_config_descriptor(usb_cfg_desc);
libusb_reset_device(msd->msd_handle);
LOG_INFO("[0x%04x:0x%04x]-[EP_IN:0x%02x, EP_OUT:0x%02x] Open success!", VID, PID, msd->endpoint_in, msd->endpoint_out);
return 0;
//ÓÐÎÊÌâ
claimintfc_err:
libusb_release_interface(msd->msd_handle, 1);
libusb_release_interface(msd->msd_handle, 0);
detachkernel_err:
epclrhalt_err:
getepaddr_err:
getcfgdesc_err:
getdevdesc_err:
getdev_err:
opendev_err:
libusb_free_config_descriptor(usb_cfg_desc);
libusb_close(msd->msd_handle);
return 0xff;
}
uint8_t USB_MSD_Close(USB_MSD_ST *msd)
{
if(msd->msd_handle != NULL)
{
libusb_release_interface(msd->msd_handle, 0);
libusb_close(msd->msd_handle);
}
return 0;
}
uint8_t USB_MSD_Bulk_Write(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms)
{
int err = 0;
err = libusb_bulk_transfer(msd->msd_handle, msd->endpoint_out, buffer, len, size, ms);
if (err < 0)
{
LOG_ERROR("Write:%d", err);
return -1;
}
return 0;
}
uint8_t USB_MSD_Bulk_Read(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms)
{
int err = 0;
err = libusb_bulk_transfer(msd->msd_handle, msd->endpoint_in, buffer, len, size, ms);
if (err < 0)
{
LOG_ERROR("Read:%d", err);
return -1;
}
return 0;
}
usb_device_opt.h文章来源:https://www.toymoban.com/news/detail-836972.html
#ifndef _USB_DEVICE_OPT_
#define _USB_DEVICE_OPT_
#include <stdio.h>
#include <libusb.h>
#include "log.h"
#include <stdint.h>
typedef struct USB_MSD
{
libusb_device* msd_dev;
libusb_device_handle* msd_handle;
uint8_t endpoint_in;
uint8_t endpoint_out;
} USB_MSD_ST;
extern uint8_t USB_Dev_Scan_A_Print(int16_t dev_num, libusb_device **devs);
extern uint8_t USB_MSD_Open(uint16_t VID, uint16_t PID, USB_MSD_ST *msd);
extern uint8_t USB_MSD_Bulk_Write(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms);
extern uint8_t USB_MSD_Bulk_Read(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms);
extern uint8_t USB_MSD_Close(USB_MSD_ST *msd);
#endif // _USB_DEVICE_OPT_
Good Night!文章来源地址https://www.toymoban.com/news/detail-836972.html
到了这里,关于USB系列-LibUSB使用指南(1)-Windows下的报错与踩坑的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!