libusb的嵌入式移植

linux對usb已有了比較完善的支持,但是看了一下原理還有代碼,還是覺得一頭霧水!有人推薦libusb,在網上搜了一下資料,嗯,感覺確實簡單多了!

下面先介紹一下libusb:

Linux 平臺上的usb驅動開發,主要有內核驅動的開發和基於libusb的無驅設計。

1、爲什麼要開發libusb

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

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

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

花了些功夫寫了個新產品的驅動,挺有成就感啊,代碼質量也是相當的有水準啊。正當你沉醉在你的代碼中時,客服不斷的郵件來了,“客戶需要2.6.5內核的驅動,config文件我已經發你了” “客戶需要雙核的2.6.18-smp 的驅動” “客戶的平臺是自己定製的是2.6.12-xxx “   你恨不得把驅動的源代碼給客戶,這樣省得編譯了。你的一部分工作時間編譯內核,定製驅動

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

 

2、如何使用libusb進行開發

libusb是基於用戶空間的usb庫。libusb 設計了一系列的外部API 爲應用程序所調用,通過這些API應用程序可以操作硬件,從libusb的源代碼可以看出,這些API 調用了內核的底層接口,和kernel driver中所用到的函數所實現的功能差不多,只是libusb更加接近USB 規範。使得libusb的使用也比開發內核驅動相對容易的多。

 

2.0 一些重要的數據結構

struct usb_dev_handle {
int fd;
struct usb_bus *bus;
struct usb_device *device;
int config;
int interface;
int altsetting;
void *impl_info;
};

struct usb_device {
struct usb_device *next, *prev;
char filename[PATH_MAX + 1];
struct usb_bus *bus;
struct usb_device_descriptor descriptor;
struct usb_config_descriptor *config;
void *dev; /* Darwin support */
};
struct usb_bus {
struct usb_bus *next, *prev;
char dirname[PATH_MAX + 1];
struct usb_device *devices;
};

 

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.husbi.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);

 

3 移植

到網站http://sourceforge.net/project/showfiles.php?group_id=1674下載最新的libusb版本

現在是0.1.12
解壓到/usr/local/libusb,分成兩個版本一個是libusb-arm 一個是libusb-pc。

pc版本直接configure ;make就可以了。(其實原來的操作系統/usr/lib中都會自帶有libusb的庫文件,版本可能和我們的會有不同,有興趣可以看看)

交叉編譯arm版本

#./configure --host=arm-linux

#make
make時可能會出現“treat warning as error”之類的錯誤信息。在Makefile裏,去掉-Werror的編譯選項就可以了。另外在一個tests文件夾的也會報uppercase的錯誤,無關緊要,把它註釋掉就可以了。

在 .libs這個隱藏文件夾中,有編譯好的libusb庫文件

libusb-0.1.so.4.4.4 libusb.a libusbpp.a libusbpp-0.1.so.4.4.4

把libusb-arm整個目錄複製到/usr/nfs (這是我的arm板nfs掛載的目錄)

我們在編程時,記得要在編譯選項里加入libusb的頭文件和庫文件
LIBUSB=/usr/local/libusb/libusb-arm

-I$(LIBUSB) -L$(LIBUSB)/.libs -lusb

 

我寫了一個小的測試程序,後面附上。

 

程序編譯好後,就可以下載到arm板上了。

arm開發板的內核首先要能夠支持USB,usb-core、hub、usbdevfs、OHCI等等

設置好環境變量,修改/etc/profile文件增加

export LD_LIBRARY_PATH=/mnt/libusb-arm/.libs:$LD_LIBRARY_PATH

運行測試程序

# usbtest-arm
bus/device idVendor/idProduct
*****************************

見 鬼了。不行!在測試程序第一層for循環都沒運行。應該是bus沒有找到。重新查看libusb的源代碼。發現在linux.c文件中的 usb_os_init(void)是通過環境變量USB_DEVFS_PATH,目錄/dev/bus/usb和/proc/bus/usb開始搜尋 usb-bus的。而我的開發板上沒有/dev/bus,有/proc/bus/usb但是裏面沒有任何文件。再查看PC上的系統,使用2.4內核的 Redhat9.0 ,也是沒有/dev/bus,但是/proc/bus/usb下有001/ 002/ devices drivers四個文件;使用2.6內核的FC6,

-bash-3.1$ ls -l /dev/bus/usb/
total 0
drwxr-xr-x 2 root root 60 Feb 27 20:09 001
drwxr-xr-x 2 root root 60 Feb 27 20:09 002
drwxr-xr-x 2 root root 60 Feb 27 20:09 003
drwxr-xr-x 2 root root 80 Feb 27 20:09 004

-bash-3.1$ ls -l /proc/bus/usb/
total 0
dr-xr-xr-x 2 root root 0 Feb 28 04:09 001
dr-xr-xr-x 2 root root 0 Feb 28 04:09 002
dr-xr-xr-x 2 root root 0 Feb 28 04:09 003
dr-xr-xr-x 2 root root 0 Feb 28 04:09 004
-r--r--r-- 1 root root 0 Feb 28 04:09 devices

看來是和我的arm板上的內核配置或是環境設置有關,libusb應該沒問題。

本來想試着設置USB_DEVFS_PATH,可是usb設備都不清楚在哪裏找。我試着插入u盤,usb鼠標,它們在/dev中的位置都不同的,設置一個統一的查找路徑USB_DEVFS_PATH行不通。

後來,繼續在libusb的maillist,linux-usb的官網上找線索。終於看到一個文檔中說:

/*************************************************************************************************/

The USB device filesystem is a dynamically generated filesystem, similar to the /proc filesystem. This filesystem can be mounted just about anywhere, however it is customarily mounted on /proc/bus/usb, which is an entry node created by the USB code, intended to be used as a mount point for this system. Mounting in other locations may break user space utilities, but should not affect the kernel support.

You need to select "Preliminary USB Device Filesystem" to make this work. You also need to enable general /proc support, and to have it mounted (normally automatic).

To mount the filesystem, you need to be root. Use the mount command: mount -t usbdevfs none /proc/bus/usb. Note that the none keyword is arbitrary - you can use anything, and some people prefer to use usbdevfs, as it makes the mount output look better.

If you do not want to have to mount the filesystem each time you reboot the system, you can add the following to /etc/fstab after the /proc entry:

none /proc/bus/usb usbdevfs defaults 0 0
This has the same effect as the mount command.

After you have mounted the filesystem, the contents of /proc/bus/usb should look something like:

dr-xr-xr-x 1 root root 0 Jan 26 10:40 001 -r--r--r-- 1 root root 0 Jan 26 10:40 devices -r--r--r-- 1 root root 0 Jan 26 10:40 drivers
. You may have more than one numbered directory entry if your machine has more than one universal serial bus controller.

/*************************************************************************************************/

試着掛載了usbdevfs文件系統,插入u盤
# usbtest-arm
bus/device idVendor/idProduct
hello,dell 1 !
hello,dell 2 !
001/001 0000/0000
hello,dell 2 !
001/002 0471/0000
*****************************

OK!成功了!雖然繞了不小的彎子,但最後還是搞定。看來,我的知識還是太不紮實了,以後還需要多多努力啊!

/*************************************************************************************************/

附1:

/* testlibusb.c */

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

int main(void) 

    struct usb_bus *bus; 
    struct usb_device *dev;

    usb_init(); 
    usb_find_busses(); 
    usb_find_devices();

    printf("bus/device idVendor/idProduct\n");

    for (bus = usb_busses; bus; bus = bus->next) { 
       printf("hello,dell 1 !\n");
       for (dev = bus->devices; dev; dev = dev->next) {
            printf("hello,dell 2 !\n");  
            printf("%s/%s %04X/%04X\n", 
                    bus->dirname, 
                    dev->filename, 
                    dev->descriptor.idVendor,
                    dev->descriptor.idProduct);

           if (!dev->config) { 
                printf(" Couldn't retrieve descriptors\n"); 
                continue; 
           }
       } 
   }
   printf("*****************************\n");
}

/*************************************************************************************************/


轉載:http://blog.chinaunix.net/uid-25885064-id-3036812.html

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