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