时隔一年再次开始撰写博客,这一年的时间经历了很多,现在终于稳定下来。以后很长一段时间都能够稳定的学习和更新。时间将会聚焦于USB和PCIe的开发进行,能和大家共同进步真的很高兴。
本篇为USB系列的LibUSB使用指南的第一篇。
USB系列主要围绕USB的知识、协议、开发总结、使用说明等进行。
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下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的设备和一些(>=1)准备传输的端点。注意:第六第七步的顺序千万不能倒过来,否则你的libusb_clear_halt会报错LIBUSB_ERROR_NOT_FOUND (ret=-5)。而且在Windows开发中,LibUSB中的detach_kernel系列的函数都是多余的,人家Windows的原理就不需要这些操作。
博主使用的是libusb_bulk_transfer传输的,中间出了点小问题,简要说一下:
main.c
#include
#include
#include
#include "log.h"
#include
#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
#include
#include "log.h"
#include
#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
#ifndef _USB_DEVICE_OPT_
#define _USB_DEVICE_OPT_
#include
#include
#include "log.h"
#include
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!