linux對usb已有了比較完善的支持,但是看了一下原理還有代碼,還是覺得一頭霧水!有人推薦libusb,在網上搜了一下資料,嗯,感覺確實簡單多了!
下面先介紹一下libusb:
Linux 平臺上的usb驅動開發,主要有內核驅動的開發和基于libusb的無驅設計。
1、為什么要開發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的無驅設計了。
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.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);
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?nonekeyword 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 0This 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
#include
#include
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");
}
/*************************************************************************************************/
?
評論