prothes 专注嵌入式的ARM linux
全部博文(197)
分类:
2012-05-05 16:46:00
原文地址:libusb的嵌入式移植 作者:txgc_wm
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 移植
到网站下载最新的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 |
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 |
/*************************************************************************************************/
试着挂载了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");
}
/*************************************************************************************************/
附2:参考网页:
利用Libusb API开发linux的设备驱动
、
摘自 http://tauruspdj.blog.163.com/blog/static/4312500620083343350520/