基于libusb的通信实例
2020-11-23
关键字:libusb收发
1、开发前的准备工作
在据笔者的另一篇博文 libusb库在嵌入式Linux平台上的移植流程 准备好环境并编译完成后即可以基于libusb库来开发快速USB通信程序了。
2、使用libusb库功能的步骤
应用libusb库的功能之前要先对其初始化。
第一步是配置你要通信的USB设备的PID/VID以及类别信息。
第二步直接调用接口等待结果即可。
第三步则是寻找匹配的设备与端点。
第四步是调用接口建立通信信道。这里要注意的是libusb库在同一时刻只允许创建一个通信信道,当一个USB设备成功与一个应用程序透过libusb绑定通信后即无法二次绑定通信,直至当前绑定注销。
第五步则是自行编写的代码。
3、关键函数
使用libusb库通信的核心接口就是IO函数了。在 libusb.h 中提供了三个同步的通信接口分别用于传输控制、中断与块数据:
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);
中断数据同步传送接口原型如下:
int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, unsigned char endpoint, unsigned char *data, int length, int *actual_length, unsigned int timeout);
块数据同步传送接口原型如下:
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);
在笔者的实际开发过程中最常用到的是中断数据与块数据的传送,因此这里再额外提一下这两个接口的部分参数的含义。
这两个函数参数中的 endpoint 即是用于区分数据流动方向的,它们的数值如下:
块数据接收 0x83 块数据发送 0x02 中断数据接收 0x81 中断数据发送 0x01
另外,这两个函数中的 actual_length 是用于保存执行结果数据长度的。例如在发送时用于保存实际发送了多少字节,接收时保存实际接收到的字节数。
两个函数的最后一个参数 timeout 的值为0时表示持续阻塞直到有结果发生。正整数值则表示函数要阻塞多少毫秒。
4、实例
以下直接贴出一个最基本的示例程序的源码,希望可以给有需要的同学一个参考:
#include "libusb.h" #define BULK_RECV_EP 0x83 #define BULK_SEND_EP 0x02 #define INT_RECV_EP 0x81 #define INT_SEND_EP 0x01 #define TRANSFER_TIMEOUT 0 #define PID 0x2710 #define VID 0x2766 typedef struct { unsigned int pid; unsigned int vid; unsigned char bInterfaceClass; unsigned char bInterfaceSubClass; unsigned char bmAttributes; unsigned char bInEndpointAddress; unsigned char bOutEndpointAddress; unsigned char bInterfaceNumber; libusb_device *dev; libusb_device **devs; } usb_dev; static libusb_device_handle* usb_handle; static usb_dev udev; /* 根据PID与VID检查指定设备是否挂载。 */ static int get_device_descriptor(struct libusb_device_descriptor *dev_desc, usb_dev* user_device) { int rv = -2; int i = 0; libusb_device **devs; libusb_device *dev; rv = libusb_get_device_list(NULL, &devs); if (rv < 0) return rv; //遍历USB设备 while ((dev = devs[i++]) != NULL) { rv = libusb_get_device_descriptor(dev, dev_desc); if(rv == 0) { } } i = 0; while ((dev = devs[i++]) != NULL) { rv = libusb_get_device_descriptor(dev,dev_desc); if(rv < 0) { return -1; } if(dev_desc->idProduct == user_device->pid && dev_desc->idVendor == user_device->vid) { user_device->dev = dev; user_device->devs = devs; return 0; } } return -2; }
int match_with_endpoint(const struct libusb_interface_descriptor * interface, struct userDevice *user_device)
{
int i;
int ret=0;
printf("bNumEndpoints:%d\n", interface->bNumEndpoints);
for(i=0; i<interface->bNumEndpoints; i++)
{
if((interface->endpoint[i].bmAttributes&0x03)==user_device->bmAttributes) //transfer type :bulk ,control, interrupt
{
if(interface->endpoint[i].bEndpointAddress&0x80) //out endpoint & in endpoint
{
ret|=1;
user_device->bInEndpointAddress = interface->endpoint[i].bEndpointAddress;
}
else
{
ret|=2;
user_device->bOutEndpointAddress = interface->endpoint[i].bEndpointAddress;
}
}
}
if(ret==3)
{
return 1;
}
else
{
return 0;
}
}
static int get_device_endpoint(struct libusb_device_descriptor *dev_desc, usb_dev* user_device) { int rv = -2; int i,j,k; struct libusb_config_descriptor *conf_desc; unsigned char isFind = 0; for (i = 0; i < dev_desc->bNumConfigurations; i++) { if(user_device->dev != NULL) rv = libusb_get_config_descriptor(user_device->dev, i, &conf_desc); if(rv < 0) { return -1; } for (j = 0; j < conf_desc->bNumInterfaces; j++) { for (k=0; k < conf_desc->interface[j].num_altsetting; k++) { if(conf_desc->interface[j].altsetting[k].bInterfaceClass == user_device->bInterfaceClass) { if(match_with_endpoint(&(conf_desc->interface[j].altsetting[k] ), user_device)) { user_device->bInterfaceNumber = conf_desc->interface[j].altsetting[k].bInterfaceNumber; libusb_free_config_descriptor(conf_desc); rv = 0; return rv; } } } } } return -2; //don't find user device } int main() { usb_handle = NULL; struct libusb_device_descriptor udev_desc; //1. load user data. udev.pid = PID; udev.vid = VID; udev.bInterfaceClass = LIBUSB_CLASS_HID; udev.bInterfaceSubClass = LIBUSB_CLASS_HID; udev.bmAttributes = LIBUSB_TRANSFER_TYPE_INTERRUPT; udev.dev = NULL; //2. init libusb. int ret = libusb_init(NULL); if(ret < 0) { return -1; } //3. search for specified usb device. ret = get_device_descriptor(&udev_desc, &udev); if(ret < 0) { return -2; } ret = get_device_endpoint(&udev_desc, &udev); if(ret < 0) { return -3; } /*4.open device and start communication by usb*/ //open the usb device usb_handle = libusb_open_device_with_vid_pid(NULL, udev.vid, udev.pid); if(usb_handle == NULL) { return -4; } ret = libusb_claim_interface(usb_handle, udev.bInterfaceNumber); if(ret < 0) { ret = libusb_detach_kernel_driver(usb_handle, udev.bInterfaceNumber); if(ret < 0) { return -5; } ret = libusb_claim_interface(usb_handle, udev.bInterfaceNumber); if(ret < 0) { return -6; } } //5,通信。 //接收 int rlen; unsigned char buf[64]; int len = libusb_bulk_transfer(usb_handle, INT_RECV_EP, buf, len, &rlen, TRANSFER_TIMEOUT); //发送 int wlen; unsigned char data[64]; len = libusb_bulk_transfer(usb_handle, INT_SEND_EP, data, len, &wlen, TRANSFER_TIMEOUT); //6,注销 libusb_close(usb_handle); libusb_release_interface(usb_handle, udev.bInterfaceNumber); libusb_free_device_list(udev.devs, 1); libusb_exit(NULL); usb_handle = NULL; return 0; }
这份示例程序及依赖文件的目录结构如下图所示:
其编译命令如下:
gcc ddemo.c libusb-1.0.so.0.2.0
以上即是一个笔者实践过程中提炼出来的libusb应用示例,希望能够帮助到有需要的同学。