基於libusb的通信實例

2020-11-23

關鍵字:libusb收發


 

1、開發前的準備工作

 

在據筆者的另一篇博文  libusb庫在嵌入式Linux平臺上的移植流程   準備好環境並編譯完成後即可以基於libusb庫來開發快速USB通信程序了。

 
我們需要用到的編譯產物文件有二:
1、libusb.h
2、libusb-1.0.so.0.2.0
它們都可以在libusb源碼編譯目錄下找到。
 
準備好上面兩個文件後即可以開始寫自己的程序代碼了。

 

 

2、使用libusb庫功能的步驟

 

應用libusb庫的功能之前要先對其初始化。

 

初始化的步驟主要分爲五步:
1、配置用戶信息;
2、初始化庫;
3、尋找設備;
4、註冊並佔用設備;
5、通信。

 

第一步是配置你要通信的USB設備的PID/VID以及類別信息。

第二步直接調用接口等待結果即可。

第三步則是尋找匹配的設備與端點。

第四步是調用接口建立通信信道。這裏要注意的是libusb庫在同一時刻只允許創建一個通信信道,當一個USB設備成功與一個應用程序透過libusb綁定通信後即無法二次綁定通信,直至當前綁定註銷。

第五步則是自行編寫的代碼。

 

 

3、關鍵函數

 

使用libusb庫通信的核心接口就是IO函數了。在 libusb.h 中提供了三個同步的通信接口分別用於傳輸控制、中斷與塊數據:

1、libusb_control_transfer
2、libusb_interrupt_transfer
3、libusb_bulk_transfer
這三個函數均可用於讀寫,靠參數來控制數據的流動方向。
 
控制數據同步傳送接口原型如下:
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應用示例,希望能夠幫助到有需要的同學。 

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章