• 基于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;
    }

    int match_with_endpoint(const struct libusb_interface_descriptor * interface, struct userDevice *user_device)
    {
        int i;
        int ret=0;
        printf("bNumEndpoints:%d ", 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应用示例,希望能够帮助到有需要的同学。 


  • 相关阅读:
    Spring Boot 2.4版本前后的分组配置变化及对多环境配置结构的影响
    Spring Boot 2.4 对多环境配置的支持更改
    Spring Boot 的2020最后一击:2.4.1、2.3.7、2.2.12 发布
    苹果M1芯片各种不支持,但居然可以刷朋友圈!你会买单吗?
    老板居然让我在Java项目中“造假”
    Spring Cloud正式移除Hystrix、Zuul等Netflix OSS组件
    为了Java微信支付V3开发包,我找出了微信支付文档至少六个错误
    IdentityServer4系列 | 支持数据持久化
    IdentityServer4系列 | 混合模式
    Gitlab Runner的分布式缓存实战
  • 原文地址:https://www.cnblogs.com/chorm590/p/13838336.html
Copyright © 2020-2023  润新知