linux對usb已有了比較完善的支持,但是看了一下原理還有代碼,還是覺得一頭霧水!有人推薦libusb,在網上搜了一下資料,嗯,感覺確實簡單多了!
下面先介紹一下libusb:
Linux 平臺上的usb驅動開發(fā),主要有內核驅動的開發(fā)和基于libusb的無驅設計。
1、為什么要開發(fā)libusb
對于內核驅動的大部分設備,諸如帶usb接口的hid設備,linux本身已經自帶了相關的驅動,我們只要操作設備文件便可以完成對設備大部分的操作,而另外一些設備,諸如自己設計的硬件產品,這些驅動就需要我們驅動工程師開發(fā)出相關的驅動了。內核驅動有它的優(yōu)點,然而內核驅動在某些情況下會遇到如下的一些問題:
1?當使用我們產品的客戶有2.4內核的平臺,同時也有2.6內核的平臺,我們要設計的驅動是要兼容兩個平臺的,就連makefile?我們都要寫兩個。
2?當我們要把linux移植到嵌入平臺上,你會發(fā)現(xiàn)原先linux自?帶的驅動移過去還挺大的,我的內核當然是越小越好拉,這樣有必要么。這還不是最郁悶的地方,如果嵌入平臺是客戶的,客戶要購買你的產品,你突然發(fā)現(xiàn)客戶設?備里的系統(tǒng)和你的環(huán)境不一樣,它沒有你要的驅動了,你的程序運行不了,你會先想:“沒關系,我寫個內核驅動加載一下不就行了“。卻發(fā)現(xiàn)客戶連insmod加載模塊的工具都沒移植,那時你就看看老天,說聲我怎么那么倒霉啊,客戶可不想你動他花了n時間移植的內核哦
3?花了些功夫寫了個新產品的驅動,挺有成就感啊,代碼質量也是相當?shù)挠兴疁拾 U斈愠磷碓谀愕拇a中時,客服不斷的郵件來了,“客戶需要2.6.5內核的驅動,config文件我已經發(fā)你了”?“客戶需要雙核的?2.6.18-smp?的驅動”?“客戶的平臺是自己定制的是2.6.12-xxx “???你恨不得把驅動的源代碼給客戶,這樣省得編譯了。你的一部分工作時間編譯內核,定制驅動
有問題產生必然會有想辦法解決問題的人,?libusb的出現(xiàn)給我們帶來了某些方便,即節(jié)約了我們的時間,也降低了公司的成本。?所以在一些情況下,就可以考慮使用libusb的無驅設計了。
2、如何使用libusb進行開發(fā)
libusb是基于用戶空間的usb庫。libusb?設計了一系列的外部API?為應用程序所調用,通過這些API應用程序可以操作硬件,從libusb的源代碼可以看出,這些API?調用了內核的底層接口,和kernel driver中所用到的函數(shù)所實現(xiàn)的功能差不多,只是libusb更加接近USB?規(guī)范。使得libusb的使用也比開發(fā)內核驅動相對容易的多。
2.0 一些重要的數(shù)據結構
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?初始化設備接口
這些接口也可以稱為核心函數(shù),它們主要用來初始化并尋找相關設備。
usb_init
函數(shù)定義:?void usb_init(void);
從函數(shù)名稱可以看出這個函數(shù)是用來初始化相關數(shù)據的,這個函數(shù)大家只要記住必須調用就行了,而且是一開始就要調用的.?
usb_find_busses?
函數(shù)定義:?int usb_find_busses(void);
尋找系統(tǒng)上的usb總線,任何usb設備都通過usb總線和計算機總線通信。進而和其他設備通信。此函數(shù)返回總線數(shù)。
?usb_find_devices
函數(shù)定義:?int usb_find_devices(void);
尋找總線上的usb設備,這個函數(shù)必要在調用usb_find_busses()后使用。以上的三個函數(shù)都是一開始就要用到的,此函數(shù)返回設備數(shù)量。?
usb_get_busses?
函數(shù)定義:?struct usb_bus *usb_get_busses(void);
這個函數(shù)返回總線的列表,在高一些的版本中已經用不到了,這在下面的實例中會有講解
2.2?操作設備接口
usb_open
函數(shù)定義:?usb_dev_handle *usb_open(struct *usb_device dev);
打開要使用的設備,在對硬件進行操作前必須要調用usb_open?來打開設備,這里大家看到有兩個結構體?usb_dev_handle?和?usb_device?是我們在開發(fā)中經常碰到的,有必要把它們的結構看一看。在libusb?中的usb.h和usbi.h中有定義。
這里我們不妨理解為返回的?usb_dev_handle?指針是指向設備的句柄,而行參里輸入就是需要打開的設備。
usb_close
函數(shù)定義:?int usb_close(usb_dev_handle *dev);
與usb_open相對應,關閉設備,是必須調用的,?返回0成功,<0?失敗。
usb_set_configuration
函數(shù)定義:?int usb_set_configuration(usb_dev_handle *dev, int configuration);
設置當前設備使用的configuration,參數(shù)configuration?是要使用的configurtation descriptoes中的bConfigurationValue,?返回0成功,<0失敗(?一個設備可能包含多個configuration,比如同時支持高速和低速的設備就有對應的兩個configuration,詳細可查看usb標準)
usb_set_altinterface?
函數(shù)定義:?int usb_set_altinterface(usb_dev_handle *dev, int alternate);
和名字的意思一樣,此函數(shù)設置當前設備配置的interface descriptor,參數(shù)alternate是指interface descriptor中的bAlternateSetting。返回0成功,<0失敗
usb_resetep
函數(shù)定義:?int usb_resetep(usb_dev_handle *dev, unsigned int ep);
復位指定的endpoint,參數(shù)ep?是指bEndpointAddress,。這個函數(shù)不經常用,被下面的usb_clear_halt函數(shù)所替代。
usb_clear_halt
函數(shù)定義:?int usb_clear_halt (usb_dev_handle *dev, unsigned int ep);
復位指定的endpoint,參數(shù)ep?是指bEndpointAddress。這個函數(shù)用來替代usb_resetep
usb_reset?
函數(shù)定義:?int usb_reset(usb_dev_handle *dev);
這個函數(shù)現(xiàn)在基本不怎么用,不過這里我也講一下,和名字所起的意思一樣,這個函數(shù)reset設備,因為重啟設備后還是要重新打開設備,所以用usb_close就已經可以滿足要求了。
usb_claim_interface
函數(shù)定義:?int usb_claim_interface(usb_dev_handle *dev, int interface);
注冊與操作系統(tǒng)通信的接口,這個函數(shù)必須被調用,因為只有注冊接口,才能做相應的操作。Interface?指?bInterfaceNumber. (下面介紹的usb_release_interface?與之相對應,也是必須調用的函數(shù))
usb_release_interface?
函數(shù)定義:?int usb_release_interface(usb_dev_handle *dev, int interface);
注銷被usb_claim_interface函數(shù)調用后的接口,釋放資源,和usb_claim_interface對應使用。
2.3?控制傳輸接口
usb_control_msg
函數(shù)定義:int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout);
從默認的管道發(fā)送和接受控制數(shù)據
usb_get_string?
函數(shù)定義:?int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, size_t buflen);
usb_get_string_simple?
函數(shù)定義:??int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen);
usb_get_descriptor?
函數(shù)定義:?int usb_get_descriptor(usb_dev_handle *dev, unsigned char type, unsigned char index, void *buf, int size);
usb_get_descriptor_by_endpoint
函數(shù)定義:?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?
函數(shù)定義:?int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);
usb_interrupt_read?
函數(shù)定義:?int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);
2.5?中斷傳輸接口
usb_bulk_write?
函數(shù)定義:?int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);
usb_interrupt_read
函數(shù)定義:?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版本
現(xiàn)在是0.1.12
解壓到/usr/local/libusb,分成兩個版本一個是libusb-arm 一個是libusb-pc。
pc版本直接configure ;make就可以了。(其實原來的操作系統(tǒng)/usr/lib中都會自帶有l(wèi)ibusb的庫文件,版本可能和我們的會有不同,有興趣可以看看)
交叉編譯arm版本
#./configure --host=arm-linux
#make
make時可能會出現(xiàn)“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開發(fā)板的內核首先要能夠支持USB,usb-core、hub、usbdevfs、OHCI等等
設置好環(huán)境變量,修改/etc/profile文件增加
export LD_LIBRARY_PATH=/mnt/libusb-arm/.libs:$LD_LIBRARY_PATH
運行測試程序
# usbtest-arm
bus/device idVendor/idProduct
*****************************
見 鬼了。不行!在測試程序第一層for循環(huán)都沒運行。應該是bus沒有找到。重新查看libusb的源代碼。發(fā)現(xiàn)在linux.c文件中的 usb_os_init(void)是通過環(huán)境變量USB_DEVFS_PATH,目錄/dev/bus/usb和/proc/bus/usb開始搜尋 usb-bus的。而我的開發(fā)板上沒有/dev/bus,有/proc/bus/usb但是里面沒有任何文件。再查看PC上的系統(tǒng),使用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板上的內核配置或是環(huán)境設置有關,libusb應該沒問題。
本來想試著設置USB_DEVFS_PATH,可是usb設備都不清楚在哪里找。我試著插入u盤,usb鼠標,它們在/dev中的位置都不同的,設置一個統(tǒng)一的查找路徑USB_DEVFS_PATH行不通。
后來,繼續(xù)在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文件系統(tǒng),插入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!成功了!雖然繞了不小的彎子,但最后還是搞定??磥?,我的知識還是太不扎實了,以后還需要多多努力?。?/p>
/*************************************************************************************************/
附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");
}
/*************************************************************************************************/
?
評論