• ★ USB 驅動程式「libusb 函式庫」的介紹及其使用範例 ★


    udo insmod /lib/modules/2.6.22-14-generic/kernel/drivers/usb/serial/usbserial.ko vendor=0x8086 product=0xd001
    同時插上
    ttyUSB0ttyUSB1(ch341),obm可以將dkb下載下去,但是自動重新啟動之後,就不能下載接下來的東西了,所以應該,需要close(ttyUSB0_handle);
    然後進行接下來的下載,分別呼叫兩次,不過應該自動關閉了,所以可能還是不能同時插上ttyUSB0和ttyUSB1。

    lsusb 顯示usb設備的vendor和product。

    比如:
    b074@gliethttp:~$ lsusb
    Bus 002 Device 001: ID 0000:0000 
    Bus 001 Device 116: ID 8086:d001 Intel Corp.
    Bus 001 Device 003: ID 413c:2105 Dell Computer Corp.
    Bus 001 Device 002: ID 0461:4d15 Primax Electronics, Ltd
    Bus 001 Device 001: ID 0000:0000

    其中Bus 001 Device 116: ID 8086:d001 Intel Corp. 就是vendor=0x8086和product=0xd001。
    可以使用dmesg來查看具體的是ttyUSB0還是ttyUSB1了

    pidof hello.exe
    pidof bash
    顯示行程的pid值

    傳輸速率:
    #define  B0    0000000        /* hang up */
    #define  B50    0000001
    #define  B75    0000002
    #define  B110    0000003
    #define  B134    0000004
    #define  B150    0000005
    #define  B200    0000006
    #define  B300    0000007
    #define  B600    0000010
    #define  B1200    0000011
    #define  B1800    0000012
    #define  B2400    0000013
    #define  B4800    0000014
    #define  B9600    0000015
    #define  B19200    0000016
    #define  B38400    0000017
    #define EXTA B19200
    #define EXTB B38400
    #define CSIZE    0000060
    #define   CS5    0000000
    #define   CS6    0000020
    #define   CS7    0000040
    #define   CS8    0000060
    #define CSTOPB    0000100
    #define CREAD    0000200
    #define PARENB    0000400
    #define PARODD    0001000
    #define HUPCL    0002000
    #define CLOCAL    0004000
    #define CBAUDEX 0010000
    #define    BOTHER 0010000
    #define    B57600 0010001
    #define   B115200 0010002
    #define   B230400 0010003
    #define   B460800 0010004 //有些CDMA使用該傳輸速率
    #define   B500000 0010005
    #define   B576000 0010006
    #define   B921600 0010007
    #define  B1000000 0010010
    #define  B1152000 0010011
    #define  B1500000 0010012
    #define  B2000000 0010013
    #define  B2500000 0010014
    #define  B3000000 0010015
    #define  B3500000 0010016
    #define  B4000000 0010017


    ★ Developing Linux Device Drivers using Libusb API ★
    Written by vikram_cvk - 2004-07-16 18:05

    Introduction
    We often come across a situation where a USB device which runs perfectly on Windows platform does not even get detected on Linux. Lack of support for USB devices is one of the reason why some people don't embrace Linux. Now there is a new API by name Libusb which helps the developers to develop USB device drivers on the fly!

    What is Libusb
    Libusb is a high-level language API which conceals low-level kernel interactions with the USB modules. It provides a set of function which are adequate to develop a device driver for a USB device from the Userspace.

    Libusb is not complex
    For any wannabe Linux Kernel programmers developing device driver as a Kernel module is a herculean task. Developing kernel modules requires fair degree of proficiency in 'C' language and also good idea of kernel subsystems, data structures etc. All these are enough to put-off a developer from venturing into Device Driver programming.Libusb has been designed to address this shortcoming. Simplified interface allows developers to develop USB drivers from the userspace . Libusb library functions provide high level abstraction to the Kernel structures and allows the developers to have access to these structures through the USBFS(USBfilesystem).

    Its Cross-platform
    Beauty of Libusb lies in its cross platform functionality. Driver written for one platform could be easily ported onto another platform with little or no changes, currently following operating systems are supported by Libusb.

    Linux
    FreeBSD
    Darwin
    OS X

    This HOWTO focuses on how Libusb can be used on Linux platform. For information about other platforms goto http://http://libusb.sourceforge.net/.


    LIBUSB ON LINUX
    Linux is the most popular platform for the Libusb API,the reason being growing popularity of Linux as a stable OS. On Linux Libusb makes of the USBFS file system. by default USBFS is automatically mounted when the system is booted.

    What is USBFS
    USBFS is a filesystem specifically designed for USB devices, by default this filesystem gets mounted when the system is booted and it can be found at /proc/bus/usb/. This filesystem consists of information about all the USB devices that are connected to the computer.Libusb makes use of this filesystem to interact with the USB devices.

    Following C program can be a stepping stone into the world of Libusb.This program can be used to gather all the technical/hardware details of a USB device connected to the computer ,ensure that some USB device is connected into the USB port.

    Details like Vendor-Id , Product-Id ,Endpoint addresses of a USB device is of paramount importance for a device driver developer.

    /*
     * testlibusb.c
     *
     *  Test suite program
     */

    #include <stdio.h>
    #include <string.h>
    #include <usb.h>

    int verbose = 0;

    void print_endpoint(struct usb_endpoint_descriptor *endpoint)
    {
      printf("      bEndpointAddress: %02xh/n", endpoint->bEndpointAddress);
      printf("      bmAttributes:     %02xh/n", endpoint->bmAttributes);
      printf("      wMaxPacketSize:   %d/n", endpoint->wMaxPacketSize);
      printf("      bInterval:        %d/n", endpoint->bInterval);
      printf("      bRefresh:         %d/n", endpoint->bRefresh);
      printf("      bSynchAddress:    %d/n", endpoint->bSynchAddress);
    }

    void print_altsetting(struct usb_interface_descriptor *interface)
    {
      int i;

      printf("    bInterfaceNumber:   %d/n", interface->bInterfaceNumber);
      printf("    bAlternateSetting:  %d/n", interface->bAlternateSetting);
      printf("    bNumEndpoints:      %d/n", interface->bNumEndpoints);
      printf("    bInterfaceClass:    %d/n", interface->bInterfaceClass);
      printf("    bInterfaceSubClass: %d/n", interface->bInterfaceSubClass);
      printf("    bInterfaceProtocol: %d/n", interface->bInterfaceProtocol);
      printf("    iInterface:         %d/n", interface->iInterface);

      for (i = 0; i < interface->bNumEndpoints; i++)
        print_endpoint(&interface->endpoint[i]);
    }

    void print_interface(struct usb_interface *interface)
    {
      int i;

      for (i = 0; i < interface->num_altsetting; i++)
        print_altsetting(&interface->altsetting[i]);
    }

    void print_configuration(struct usb_config_descriptor *config)
    {
      int i;

      printf("  wTotalLength:         %d/n", config->wTotalLength);
      printf("  bNumInterfaces:       %d/n", config->bNumInterfaces);
      printf("  bConfigurationValue:  %d/n", config->bConfigurationValue);
      printf("  iConfiguration:       %d/n", config->iConfiguration);
      printf("  bmAttributes:         %02xh/n", config->bmAttributes);
      printf("  MaxPower:             %d/n", config->MaxPower);

      for (i = 0; i < config->bNumInterfaces; i++)
        print_interface(&config->interface[i]);
    }

    int print_device(struct usb_device *dev, int level)
    {
      usb_dev_handle *udev;
      char description[256];
      char string[256];
      int ret, i;

      udev = usb_open(dev);
      if (udev) {
        if (dev->descriptor.iManufacturer) {
          ret = usb_get_string_simple(udev, dev->descriptor.iManufacturer, string, sizeof(string));
          if (ret > 0)
            snprintf(description, sizeof(description), "%s - ", string);
          else
            snprintf(description, sizeof(description), "%04X - ",
                     dev->descriptor.idVendor);
        } else
          snprintf(description, sizeof(description), "%04X - ",
                   dev->descriptor.idVendor);

        if (dev->descriptor.iProduct) {
          ret = usb_get_string_simple(udev, dev->descriptor.iProduct, string, sizeof(string));
          if (ret > 0)
            snprintf(description + strlen(description), sizeof(description) -
                     strlen(description), "%s", string);
          else
            snprintf(description + strlen(description), sizeof(description) -
                     strlen(description), "%04X", dev->descriptor.idProduct);
        } else
          snprintf(description + strlen(description), sizeof(description) -
                   strlen(description), "%04X", dev->descriptor.idProduct);

      } else
        snprintf(description, sizeof(description), "%04X - %04X",
                 dev->descriptor.idVendor, dev->descriptor.idProduct);

      printf("%.*sDev #%d: %s/n", level * 2, "                    ", dev->devnum,
             description);

      if (udev && verbose) {
        if (dev->descriptor.iSerialNumber) {
          ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, string, sizeof(string));
          if (ret > 0)
            printf("%.*s  - Serial Number: %s/n", level * 2,
                   "                    ", string);
        }
      }

      if (udev)
        usb_close(udev);

      if (verbose) {
        if (!dev->config) {
          printf("  Couldn't retrieve descriptors/n");
          return 0;
        }

        for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
          print_configuration(&dev->config[i]);
      } else {
        for (i = 0; i < dev->num_children; i++)
          print_device(dev->children[i], level + 1);
      }

      return 0;
    }

    int main(int argc, char *argv[])
    {
      struct usb_bus *bus;

      if (argc > 1 && !strcmp(argv[1], "-v"))
        verbose = 1;

      usb_init();

      usb_find_busses();
      usb_find_devices();

      for (bus = usb_busses; bus; bus = bus->next) {
        if (bus->root_dev && !verbose)
          print_device(bus->root_dev, 0);
        else {
          struct usb_device *dev;

          for (dev = bus->devices; dev; dev = dev->next)
            print_device(dev, 0);
        }
      }

      return 0;
    }



    The above program should be compiled as

    (root$)gcc -o usbdevice_details testlibusb.c -I/usr/local/include -L. -lnsl -lm -lc -L/usr/local/lib -lusb

    (root$)./usbdevice_details (enter)


    Following is the output of the above command ,its the listing of a USB pen drive connected to my system.

    The first line displays the bus-name/device-name & device-id/product-id and rest of the listing is self-descriptive.


    001/004 0EA0/2168
    - Manufacturer : USB
    - Product : Flash Disk
    - Serial Number: 4CE45C4E403EE53D
    wTotalLength: 39
    bNumInterfaces: 1
    bConfigurationValue: 1
    iConfiguration: 0
    bmAttributes: 80h
    MaxPower: 100
    bInterfaceNumber: 0
    bAlternateSetting: 0
    bNumEndpoints: 3
    bInterfaceClass: 8
    bInterfaceSubClass: 6
    bInterfaceProtocol: 80
    iInterface: 0
    bEndpointAddress: 81h
    bmAttributes: 02h
    wMaxPacketSize: 64
    bInterval: 0
    bRefresh: 0
    bSynchAddress: 0
    bEndpointAddress: 02h
    bmAttributes: 02h
    wMaxPacketSize: 64
    bInterval: 0
    bRefresh: 0
    bSynchAddress: 0
    bEndpointAddress: 83h
    bmAttributes: 03h
    wMaxPacketSize: 2
    bInterval: 1
    bRefresh: 0
    bSynchAddress: 0



    Before executing the above program download the current version of Libusb library from, http://http://libusb.sourceforge.net/. The above program can also be found under the tests directory of Libusb directory (after u install it)


    Now I will explain in brief some of the functions and attributes dealt in the above program.

    usb_init() - Used to initialize Libusb and establish connection with kernel structures .
    usb_find_busses() - Looks for all the USB busses on the computer.
    usb_find_devices() - Looks for all the USB devices connected to the computer.
    usb_open(dev) - Opens the device 'dev' which is given as argument to this function.
    usb_get_string_simple() - Used to extract the string descriptor of the device taken argument.

    Important attributes of USB devices useful in device driver coding

    Configuration and Endpoints are one of the two important descriptors of any USB device. These descriptors are defined using the ?struct usb_config_descriptor? and ?struct_usb_endpoint_descriptor? respectively .

    dev->descriptor.idVendor ? Reveals the Vendor-Id of the USB device connected to the system.

    dev->descriptor.idProduct - Reveals the Product-Id of the USB device connected to the system.

    dev->descriptor.iManufacturer - Reveals the name of the Manufacturer USB device connected to the system.

    EndpointAddress:Combination of endpoint address and endpoint direction on a USB device.

    InterfaceNumber : One of the several interfaces that is allocated to the connected USB device.

    AlternateSetting:This is part of the a single interface allocated to the USB device.


    Prerequisites for Libusb programming
    Linux system with Kernel 2.4 above series.
    Proficiency in C language.
    Good understanding of USB device internals.
    Idea about USBFS.

    Hope this HOWTO has enlightened you about Libusb API and I expect this HOWTO will give you a head start in your device driver programming endeavor .This HOWTO is just an introduction to Libusb ,for complete documentation please goto http://http://libusb.sourceforge.net/

    About Myself
    My name is Vikram C , I'm a linux freak and currently working as Linux developer in the city of Hyderabad India.You can reach me at vikram_147@hotmail.com / vikram@asrttechnologies.com

    //================================================

    驅動程式開發向來是核心開發中工作量最多的一塊,隨著USB設備的普及,大量的USB設備的驅動程式開發也成為驅動程式開發者手頭上做的最多的事情。本文主 要介紹 Linux平台下基於libusb的驅動程式開發,希望能夠給從事Linux驅動程式開發的朋友帶來些幫助,更希望能夠給其他平台上的無驅設計帶來些幫 助。文章是我在工作中使用libusb的一些總結,難免有錯誤,如有不當的地方,還請指正。

        Linux 平台上的usb驅動程式開發,主要有核心驅動程式的開發和基於libusb的無驅設計。

    對 於核心驅動程式的大部分設備,諸如帶usb介面的hid設備,linux本身已經包含了相關的驅動程式,我們只要操作設備檔案便可以完成對設備大部分的操 作,而另外一些設備,諸如自己設計的硬件產品,這些驅動程式就需要我們驅動程式工程師開發出相關的驅動程式了。核心驅動程式有它的優點,然而核心驅動程式 在某些情況下會遇到如下的一些問題:

    1 當使用我們產品的客戶有2.4核心的平台,同時也有2.6核心的平台,我們要設計的驅動程式是要兼容兩個平台的,就連makefile 我們都要寫兩個。

    2 當我們要把linux移植到嵌入平台上,你會發現原先linux包含的驅動程式移過去還挺大的,我的核心當然是越小越好拉,這樣有必要麼。這還不是最鬱悶 的地方,如果嵌入平台是客戶的,客戶要購買你的產品,你突然發現客戶設備裡的系統和你的環境不一樣,它沒有你要的驅動程式了,你的程序執行不了,你會先 想:「沒關系,我寫個核心驅動程式加載一下不就行了「。卻發現客戶連insmod加載模塊的工具都沒移植,那時你就看看老天,說聲我怎麼那麼倒霉啊,客戶 可不想你動他花了n時間移植的核心哦!

    3 花了些功夫寫了個新產品的驅動程式,挺有成就感啊,程式碼質量也是相當的有水準啊。正當你沉醉在你的程式碼中時,客服不斷的郵件來了,「客戶需要 2.6.5核心的驅動程式,config檔案我已經發你了」 「客戶需要雙核的 2.6.18-smp 的驅動程式」 「客戶的平台是自己客製化的是2.6.12-xxx 「   你恨不得把驅動程式的原始碼給客戶,這樣省得編譯了。你的一部分工作時間編譯核心,客製化驅動程式!

    有問題產生必然會有想辦法解決問題的人, libusb的出現給我們帶來了某些方便,即節約了我們的時間,也降低了公司的成本。 所以在一些情況下,就可以考慮使用libusb的無驅設計了。

        下面我們就來詳細討論一下libusb, 並以寫一個hid設備的驅動程式來講解如何運用libusb,至於文章中涉及的usb協議的知識,限於篇幅,就不詳細講解了,相關的可自行查看usb相關協議。


    一、 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成功,<0 失敗。

       usb_set_configuration

       函數定義: int usb_set_configuration(usb_dev_handle *dev, int configuration);

       設置當前設備使用的configuration,參數configuration 是你要使用的configurtation descriptoes中的bConfigurationValue, 返回0成功,<0失敗( 一個設備可能包含多個configuration,比如同時支持高速和低速的設備就有對應的兩個configuration,詳細可查看usb標準)

       usb_set_altinterface

       函數定義: int usb_set_altinterface(usb_dev_handle *dev, int alternate);

       和名字的意思一樣,此函數設置當前設備配置的interface descriptor,參數alternate是指interface descriptor中的bAlternateSetting。返回0成功,<0失敗

       usb_resetep

       函數定義: int usb_resetep(usb_dev_handle *dev, unsigned int ep);

       復位指定的endpoint,參數ep 是指bEndpointAddress,。這個函數不經常用,被下面介紹的usb_clear_halt函數所替代。

       usb_clear_halt

       函數定義: int usb_clear_halt (usb_dev_handle *dev, unsigned int ep);

       復位指定的endpoint,參數ep 是指bEndpointAddress。這個函數用來替代usb_resetep

       usb_reset

       函數定義: int usb_reset(usb_dev_handle *dev);

       這個函數現在基本不怎麼用,不過這裡我也講一下,和名字所起的意思一樣,這個函數reset設備,因為重啟設備後還是要重新打開設備,所以用usb_close就已經可以滿足要求了。

       usb_claim_interface

       函數定義: int usb_claim_interface(usb_dev_handle *dev, int interface);

       註冊與操作系統通訊的介面,這個函數必須被呼叫,因為只有註冊介面,才能做相應的操作。

    Interface 指 bInterfaceNumber. (下面介紹的usb_release_interface 與之相對應,也是必須呼叫的函數)

       usb_release_interface

       函數定義: int usb_release_interface(usb_dev_handle *dev, int interface);

       註銷被usb_claim_interface函數呼叫後的介面,釋放資源,和usb_claim_interface對應使用。

    2.3 控制傳輸介面

       usb_control_msg

       函數定義:int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout);

       從
    預設的管道發送和接受控制資料

       usb_get_string

       函數定義: int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, size_t buflen);

       usb_get_string_simple

    函數定義: int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen);

       usb_get_descriptor

       函數定義: int usb_get_descriptor(usb_dev_handle *dev, unsigned char type, unsigned char index, void *buf, int size);

       usb_get_descriptor_by_endpoint

       函數定義: int usb_get_descriptor_by_endpoint(usb_dev_handle *dev, int ep, unsigned char type, unsigned char index, void *buf, int size);

    2.4 批次傳輸介面

       usb_bulk_write

       函數定義: int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);

        usb_interrupt_read

    函數定義: int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);

    2.5 中斷傳輸介面

    usb_bulk_write

    函數定義: int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);

    usb_interrupt_read

    函數定義: int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);

    基本上libusb所經常用到的函數就有這些了,和usb協議確實很接近吧。下面我們實例在介紹一個應用。

    //===================================

    Libusb函式庫的使用
    使用libusb之前你的linux系統必須裝有usb檔案系統,這裡還介紹了使用hiddev設備檔案來訪問設備,目的在於不僅可以比較出usb的易用性,還提供了一個轉化成libusb驅動程式的案例。

    3.1 find設備
    任何驅動程式第一步首先是尋找到要操作的設備,我們先來看看HID驅動程式是怎樣尋找到設備的。我們假設尋找設備的函數Device_Find(註:程式碼只是為了方便解說,不保證程式碼的健全)

    /* 我們簡單看一下使用hid驅動程式尋找設備的實現,然後在看一下libusb是如何尋找設備的 */
    int Device_Find()
    {
        char dir_str[100];   /* 這個變數我們用來儲存設備檔案的目錄路徑 */
        char hiddev[100];    /* 這個變數用來儲存設備檔案的完整路徑*/
    DIR dir;              
    /* 申請的字串陣列清空,這個編程習慣要養成 */
    memset (dir_str, 0 , sizeof(dir_str));
    memset (hiddev, 0 , sizeof(hiddev));
        /* hiddev 的設備描述符不在/dev/usb/hid下面,就在/dev/usb 下面
    這裡我們使用opendir函數來檢驗目錄的有效性
    打開目錄返回的值儲存在變數dir裡,dir前面有聲明
    */
    dir=opendir("/dev/usb/hid");
        if(dir){
            /* 程序執行到這裡,說明存在 /dev/usb/hid 路徑的目錄 */
            sprintf(dir_str,"/dev/usb/hid/");
            closedir(dir);
        }else{
            /* 如果不存在hid目錄,那麼設備檔案就在/dev/usb下 */
            sprintf(dir_str,"/dev/usb/");
        }
        /* DEVICE_MINOR 是指設備數,HID一般是16個 */
    for(i = 0; i < DEVICE_MINOR; i++) {
        /* 獲得完整路徑的設備檔案名,一般hid設備檔案名是hiddev0 到 hiddev16 */
            sprintf(hiddev, "%shiddev%d", dir_str,i);
           /* 打開設備檔案,獲得檔案訊息處理函式 */
           fd = open(hiddev, O_RDWR);
           if(fd > 0) {
               /* 操作設備獲得設備訊息 */
              ioctl(fd, HIDIOCGDEVINFO, &info);
       
                  /* VENDOR_ID 和 PRODUCT_ID 是標識usb設備廠家和產品ID,驅動程式都需要這兩個參數來尋找設備,到此我們尋找到了設備 */
               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匯流排 */
        usb_find_devices(); /* 尋找usb匯流排上的usb設備 */
       
        /* 獲得系統匯流排鏈表的訊息處理函式 */
    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,

    /* 使用hid驅動程式打開設備 */
    int Device_Open()
    {
        int handle;
        /* 傳統HID驅動程式呼叫,通過open打開設備檔案就可 */
    handle = open(「hiddev0」, O_RDONLY);
    }
    /* 使用libusb打開驅動程式 */
    int Device_Open()
    {
    /* LIBUSB 驅動程式打開設備,這裡寫的是虛擬碼,不保證程式碼有用 */
    struct usb_device*    udev;
    usb_dev_handle*        device_handle;
    /* 當找到設備後,通過usb_open打開設備,這裡的函數就相當open 函數 */
    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; /* 儲存ioctl函數的返回值 */
    int      index;
        unsigned char send_data[72]; /* 發送的資料  */
    unsigned char recv_data[72]; /* 接收的資料 */
        struct hiddev_usage_ref uref; /* hid驅動程式定義的資料封包 */
        struct hiddev_report_info rinfo; /* hid驅動程式定義的 */
        memset(send_data, 0, sizeof(send_data));
    memset(recv_data, 0, sizeof(recv_data));
        memcpy(send_data, buffer72, 72);
       /* 這在發送資料之前必須呼叫的,初始化設備 */
        ret = ioctl(fd, HIDIOCINITREPORT, 0);
        if( ret !=0) {
            return NOT_OPENED_DEVICE;/* NOT_OPENED_DEVICE 屬於自己定義巨集 */
        }
        /* HID設備每次傳輸一個字組的資料封包 */
        for(index = 0; index < 72; 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 < 72; 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;
       
        /* save the data of send and receive */
        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));
       
        /* 這裡的g_list是全域的資料變數,裡面可以存儲相關設備的所需訊息,當然我們 也可以從函數形參中傳輸進來,設備的訊息在打開設備時初始化,我們將在後面的總結中詳細描述一下 */
        Device_handle = (usb_dev_handle*)(g_list[fd].device_handle);
        if (Device_handle == NULL) {
            return NOT_OPENED_DEVICE;
    }
    /* 這個函數前面已經說過,在操作設備前是必須呼叫的, 0是指用預設的設備 */
    usb_claim_interface(Device_handle, 0);
    /* 發送資料,所用到的巨集定義在usb.h可以找到,我列出來大家看一下
           #define USB_ENDPOINT_OUT       0x00
           #define USB_TYPE_CLASS     (0x01 << 5)
           #define USB_RECIP_INTERFACE 0x01
          
           #define HID_REPORT_SET       0x09 */
    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 < 0) {
            return WRITE_REPORT;
    }
    if (send_len != 72) {
            return send_len;
    }
    /* 接收資料
           #define USB_ENDPOINT_IN         0x80
           #define USB_TYPE_CLASS          (0x01 << 5)
           #define USB_RECIP_INTERFACE        0x01
           #define HID_REPORT_GET          0x01
        */
    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 < 0) {
            printf("failed to retrieve report from USB device!/n");
            return READ_REPORT;
        }
       
        if (recv_len != 72) {
            return recv_len;
        }
       
       
        /* 和usb_claim_interface對應 */
        usb_release_interface(RY2_handle, 0);
        memcpy(buffer72, recv_data, 72);
    return SUCCESS;
    }


    3.4 關閉設備
    假設我們定義的關閉設備的函數名是Device_Close()

    /* 使用hid驅動程式關閉設備 */
    int Device_Close()
    {
        int handle;
       
    handle = open(「hiddev0」, O_RDONLY);
    /* 傳統HID驅動程式呼叫,通過close()設備檔案就可 */
    close( handle );
    }
    /* 使用libusb關閉驅動程式 */
    int Device_Close()
    {
    /* LIBUSB 驅動程式打開設備,這裡寫的是虛擬碼,不保證程式碼有用 */
    struct usb_device*    udev;
    usb_dev_handle*        device_handle;
    device_handle = usb_open(udev);
    /* libusb函式庫使用usb_close關閉程序 */
    usb_close(device_handle);
    }


    libusb的驅動程式框架
    前面我們看了些主要的libusb函數的使用,這裡我們把前面的內容歸納下:
    一般的驅動程式應該都包含如下介面:

    Device_Find(); /* 尋找設備介面 */
    Device_Open(); /* 打開設備介面 */
    Device_Write(); /* 寫入設備介面 */
    Device_Read(); /* 讀取設備介面 */
    Device_Close(); /* 關閉設備介面 */


    具體程式碼如下:

    #include <usb.h>
    /* usb.h這個頭檔案是要包括的,裡面包含了必須要用到的資料結構 */
    /* 我們將一個設備的屬性用一個結構體來概括 */
    typedef struct
    {
        struct usb_device*    udev;
        usb_dev_handle*        device_handle;
        /* 這裡可以添加設備的其他屬性,這裡只列出每個設備要用到的屬性 */
    } device_descript;
    /* 用來設置傳輸資料的時間延遲 */
    #define USB_TIMEOUT     10000
    /* 廠家ID 和產品 ID */
    #define VENDOR_ID    0xffff    
    #define PRODUCT_ID   0xffff
    /* 這裡定義陣列來儲存設備的相關屬性,DEVICE_MINOR可以設置能夠同時操作的設備數量,用全域變數的目的在於方便儲存屬性 */
    #define DEVICE_MINOR 16
    int     g_num;
    device_descript g_list[ DEVICE_MINOR ];
    /* 我們寫個設備先找到設備,並把相關訊息儲存在 g_list 中 */
    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 < DEVICE_MINOR) {
                         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)
    {
        /* 呼叫usb_close */
    }


    小結
       到此,使用libusb進行驅動程式開發介紹完了,通過對函式庫所提供的API的使用可以體會到libusb的易用性。

    下載   
     
      svn co https://libusb.svn.sourceforge.net/svnroot/libusb libusb

  • 相关阅读:
    GIT学习实践笔记
    ubuntu 安装pygit2
    强化vim打造python的IDE
    Python Django学习和实践
    浏览器调试工具网页性能分析中的使用
    公司发版shell脚本重构
    Nightwatch+gulp
    Git
    JavaScript Unit Test with Qunit
    Unit Test Mocking Framework
  • 原文地址:https://www.cnblogs.com/leino11121/p/2381913.html
Copyright © 2020-2023  润新知