• libusb


    一 libusb 介绍

       libusb 设计了一系列的外部API
    为应用程序所调用,通过这些API应用程序可以操作硬件,从libusb的源代码可以看出,这些API 调用了内核的底层接口,和kernel
    driver中所用到的函数所实现的功能差不多,只是libusb更加接近USB 规范。使得libusb的使用也比开发内核驱动相对容易的多。
    Libusb 的编译安装请查看Readme,这里不做详解

    二 libusb 的外部接口

    2.1 初始化设备接口

    这些接口也可以称为核心函数,它们主要用来初始化并寻找相关设备。

    usb_init
    函数定义: void usb_init(void);
    从函数名称可以看出这个函数是用来初始化相关数据的,这个函数大家只要记住必须调用就行了,而且是一开始就要调用的.

    usb_find_busses
    函数定义: int usb_find_busses(void);
    寻找系统上的usb总线,任何usb设备都通过usb总线和计算机总线通信。进而和其他设备通信。此函数返回总线数。

    usb_find_devices
    函数定义: int usb_find_devices(void);
    寻找总线上的usb设备,这个函数必要在调用usb_find_busses()后使用。以上的三个函数都是一开始就要用到的,此函数返回设备数量。

    usb_get_busses
    函数定义: struct usb_bus *usb_get_busses(void);
    这个函数返回总线的列表,在高一些的版本中已经用不到了,这在下面的实例中会有讲解


    2.2 操作设备接口

         usb_open
    函数定义: usb_dev_handle *usb_open(struct *usb_device dev);
    打开要使用的设备,在对硬件进行操作前必须要调用usb_open 来打开设备,这里大家看到有两个结构体usb_dev_handle 和
    usb_device 是我们在开发中经常碰到的,有必要把它们的结构看一看。在libusb 中的usb.h和usbi.h中有定义。
    这里我们不妨理解为返回的 usb_dev_handle 指针是指向设备的句柄,而行参里输入就是需要打开的设备。

        usb_close
        函数定义: int usb_close(usb_dev_handle *dev);
        与usb_open相对应,关闭设备,是必须调用的, 返回0成功,Libusb库的使用

    使用libusb之前你的linux系统必须装有usb文件系统,这里还介绍了使用hiddev设备文件来访问设备,目的在于不仅可以比较出usb的易用性,还提供了一个转化成libusb驱动的案例。
    3.1 find设备

    任何驱动第一步首先是寻找到要操作的设备,我们先来看看HID驱动是怎样寻找到设备的。我们假设寻找设备的函数Device_Find(注:代码只是为了方便解说,不保证代码的健全)


    int Device_Find()
    {
         char dir_str[100];   
         char hiddev[100];     
    DIR dir;                 


    memset (dir_str, 0 , sizeof(dir_str));
    memset (hiddev, 0 , sizeof(hiddev));

         
    dir=opendir("/dev/usb/hid");
         if(dir){
             
              sprintf(dir_str,"/dev/usb/hid/");
              closedir(dir);
         }else{
             
              sprintf(dir_str,"/dev/usb/");
         }

         
    for(i = 0; i
         
              sprintf(hiddev, "%shiddev%d", dir_str,i);

            
            fd = open(hiddev, O_RDWR);
            if(fd > 0) {

                
                ioctl(fd, HIDIOCGDEVINFO, &info);
         
                   
                 if(info.vendor== VENDOR_ID && info.product== PRODUCT_ID) {
                      
                        

                     device_num++;   
                }
                 close(fd);
            }
         }
         return device_num;         
    }


    我们再来看libusb是如何来寻找和初始化设备

    int Device_Find()
    {
    struct usb_bus              *busses;

         int                             device_num = 0;

         device_num = 0;        
         
         usb_init();            
         usb_find_busses();   
         usb_find_devices();
         
         
    busses = usb_get_busses();

         struct usb_bus         *bus;
         
         for (bus = busses; bus; bus = bus->next) {
              struct usb_device *dev;
             
              for (dev = bus->devices; dev; dev = dev->next) {

                
    if(dev->descriptor.idVendor==VENDOR_ID&& dev->descriptor.idProduct == PRODUCT_ID) {
                      
                        
                     device_num++;   
    }               
              }         
         }
         return device_num;         
    }


    注:在新版本的libusb中,usb_get_busses就可以不用了,这个函数是返回系统上的usb总线链表句柄
    这里我们直接用usb_busses变量,这个变量在usb.h中被定义为外部变量
    所以可以直接写成这样:
    struct usb_bus     *bus;
              for (bus = usb_busses; bus; bus = bus->next) {
                     struct usb_device *dev;
              for (dev = bus->devices; dev; dev = dev->next) {
                 

             }
    }

    3.2 打开设备

    假设我们定义的打开设备的函数名是device_open,


    int Device_Open()
    {
         int handle;
         
    handle = open(“hiddev0”, O_RDONLY);
    }

    int Device_Open()
    {

    struct usb_device*     udev;
    usb_dev_handle*          device_handle;

    device_handle = usb_open(udev);
    }


    3.3 读写设备和操作设备
    假设我们的设备使用控制传输方式,至于批处理传输和中断传输限于篇幅这里不介绍
    我们这里定义三个函数,Device_Write, Device_Read, Device_Report
    Device_Report 功能发送接收函数
    Device_Write 功能写数据
    Device_Read    功能读数据

    Device_Write和Device_Read调用Device_Report发送写的信息和读的信息,开发者根据发送的命令协议来设计,我们这里只简单实现发送数据的函数。

    假设我们要给设备发送72字节的数据,头8个字节是报告头,是我们定义的和设备相关的规则,后64位是数据。

    HID驱动的实现(这里只是用代码来有助理解,代码是伪代码)

    int Device_Report(int fd, unsigned char *buffer72)
    {
    int        ret;
    int       index;


         unsigned char send_data[72];
    unsigned char recv_data[72];
         struct hiddev_usage_ref uref;
         struct hiddev_report_info rinfo;
         ret = ioctl(fd, HIDIOCINITREPORT, 0);
         if( ret !=0) {
              return NOT_OPENED_DEVICE;
         }
         
         for(index = 0; index
             
         uref.report_type = HID_REPORT_TYPE_FEATURE;
         uref.report_id = HID_REPORT_ID_FIRST;
         uref.usage_index = index;
         uref.field_index = 0;
         uref.value = send_data[index];
         ioctl(fd, HIDIOCGUCODE, &uref);
         ret=ioctl(fd, HIDIOCSUSAGE, &uref);
         if(ret != 0 ){
                 return UNKNOWN_ERROR;
         }
    }

    rinfo.report_type = HID_REPORT_TYPE_FEATURE;
    rinfo.report_id = HID_REPORT_ID_FIRST;
    rinfo.num_fields = 1;
    ret=ioctl(fd, HIDIOCSREPORT, &rinfo);   
    if(ret != 0) {
              return WRITE_REPORT;
    }


    ret = ioctl(fd, HIDIOCINITREPORT, 0);
    for(index = 0; index
         uref.report_type = HID_REPORT_TYPE_FEATURE;
         uref.report_id = HID_REPORT_ID_FIRST;
         uref.usage_index = index;
         uref.field_index = 0;
         ioctl(fd, HIDIOCGUCODE, &uref);
         ret = ioctl(fd, HIDIOCGUSAGE, &uref);
         if(ret != 0 ) {
              return UNKNOWN_ERROR;
         }
         recv_data[index] = uref.value;
    }

    memcpy(buffer72, recv_data, 72);

    return SUCCESS;
    }

    libusb驱动的实现

    int Device_Report(int fd, unsigned char *buffer72)
    {
         
         usb_dev_handle* Device_handle;
         
         
         unsigned char    send_data[72];
         unsigned char    recv_data[72];
         
         int                send_len;
         int              recv_len;
         
         
         memset(send_data, 0 , sizeof(send_data));
         memset(recv_data, 0 , sizeof(recv_data));
         
         
         Device_handle = (usb_dev_handle*)(g_list[fd].device_handle);
         if (Device_handle == NULL) {
             return NOT_OPENED_DEVICE;
    }


    usb_claim_interface(Device_handle, 0);


    send_len = usb_control_msg(Device_handle,
    USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
                                    HID_REPORT_SET,
                                    0x300,
                                    0,
                                    send_data, 72, USB_TIMEOUT);


    if (send_len
             return WRITE_REPORT;
    }

    if (send_len != 72) {
              return send_len;
    }


    recv_len = usb_control_msg(Device_handle,
    USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
                                    HID_REPORT_GET,
                                    0x300,
                                       0,
                                    recv_data, 72, USB_TIMEOUT);
                                                         
         if (recv_len
             printf("failed to retrieve report from USB device! ");
             return READ_REPORT;
         }
         
         if (recv_len != 72) {
              return recv_len;
         }
         
         
         
         usb_release_interface(RY2_handle, 0);
         memcpy(buffer72, recv_data, 72);

    return SUCCESS;
    }


    3.4 关闭设备
    假设我们定义的关闭设备的函数名是Device_Close()


    int Device_Close()
    {
         int handle;
         
    handle = open(“hiddev0”, O_RDONLY);


    close( handle );
    }


    int Device_Close()
    {

    struct usb_device*     udev;
    usb_dev_handle*          device_handle;

    device_handle = usb_open(udev);


    usb_close(device_handle);
    }


    libusb的驱动框架
    前面我们看了些主要的libusb函数的使用,这里我们把前面的内容归纳下:
    一般的驱动应该都包含如下接口:
    Device_Find();
    Device_Open();
    Device_Write();
    Device_Read();
    Device_Close();

    具体代码如下:


    #include

    typedef struct
    {
         struct usb_device*     udev;
         usb_dev_handle*          device_handle;
         
    } device_descript;


    #define USB_TIMEOUT      10000


    #define VENDOR_ID     0xffff      
    #define PRODUCT_ID    0xffff


    #define DEVICE_MINOR 16
    int      g_num;
    device_descript g_list[ DEVICE_MINOR ];


    int Device_Find()
    {
         struct usb_bus         *bus;
         struct usb_device *dev;

         g_num = 0;
         usb_find_busses();
         usb_find_devices();
         
         
         for (bus = usb_busses; bus; bus = bus->next) {
              for (dev = bus->devices; dev; dev = dev->next) {
    if(dev->descriptor.idVendor==VENDOR_ID&& dev->descriptor.idProduct == PRODUCT_ID) {
                         
                          if (g_num
                           g_list[g_num].udev = dev;   
                           g_num ++;
                            }               
                  }         
              }
         }
         
         return g_num;
    }


    int Device_Open()
    {
         
         if(g_list[g_num].udev != NULL) {
              g_list[g_num].device_handle = usb_open(g_list[g_num].udev);
    }
    }


    int DeviceWite(int handle)
    {
         
    }

    int DeviceOpen(int handle)
    {
         
    }

    void Device_close(int handle)
    {
         
    }


    小结

        到此,使用libusb进行驱动开发介绍完了,通过对库所提供的API的使用可以体会到libusb的易用性。

    将request, requesttype, value, index, size加工成usb_ctrlrequest,然后调用usb_internal_control_msg()

    dev                     参数dev指向目标设备的usb_device数据结构
    pipe                    pipe是个32位无符号整数,其最高两位表示传输的类型(实时/中断/控制/批量),其余各位包括对方的端口号以及设备号,以及设备是否为全速(或者低度)。
    requesttype             requesttype其最高位表示传输的方向,最低5位则表明传输终极对象的类别(设备/接口/端口/其他)
    index, request, value   index则指明具体的单元,这就是终极的操作对象。针对这个操作对象,request说明了需要进行的具体操作,而value则是参数
    data, size              如果有更多的数据需要传递(读/写),则通过缓冲区data进行,其大小为size。这些都是从用户空间传下来的参数,而传输的目的正是要把这些信息发送给目标设备
    timeout                参数timeout表示愿意睡眠等待传输完成的时间
    ------------------------------------------------------
    int usb_control_msg(
        struct usb_device   *dev, unsigned int   pipe,
        __u8-request,__u8 requesttype,__u16   value,   
        __u16 index,   void *data,       __u16   size,
        int   timeout)
    {
        struct  usb_ctrlrequest *dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
        int ret;
        if (!dr)
            return -ENOMEM;
        dr->bRequestType= requesttype;
        dr->bRequest = request;
        dr->wValue = cpu_to_le16p(&value);
        dr->wIndex = cpu_to_le16p(&index);
        dr->wLength = cpu_to_le16p(&size);
        ret =  usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
        kfree(dr);
        return ret;
    }
    -----------------------------------
    QT USB技术(一)基础
    https://blog.51cto.com/u_11496263/1867516

    转载自:QT USB技术(一)基础_WZM3558862_51CTO博客

  • 相关阅读:
    Linux基础命令---arch
    JSON漫谈
    django中外键关联表的查询随笔
    <django中render_to_response的可选参数和使用方法>
    有趣的Redis:缓存被我写满了,该怎么办?
    2020全球C++及系统软件技术大会成功落下帷幕
    AWS 宣布创建 Elasticsearch 和 Kibana 分支
    Flutter开发指南之理论篇:Dart语法05(单线程模型,事件循环模型,Isolate)
    自定义注解!绝对是程序员装逼的利器!!
    Java8 Stream
  • 原文地址:https://www.cnblogs.com/tingtaishou/p/15355246.html
Copyright © 2020-2023  润新知