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; } 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應用示例,希望能夠幫助到有需要的同學。