姓名:薛绍宏 学号:19020100016 学院:电子工程学院
【嵌牛导读】本文介绍了嵌入式Linux基于libusb的USB驱动开发
【嵌牛鼻子】嵌入式Linux驱动程序开发
【嵌牛提问】嵌入式Linux的USB驱动如何基于libusb进行开发?
【嵌牛正文】
由于usb设备的普遍性及其多样性,大量的usb设备的驱动开发也就成为开发者做的最多的事情。Linux平台上,内核驱动的开发由于内核的复杂和版本问题,初学者难以入手,驱动程序也不易升级和维护。本文主要介绍Linux平台下使用libusb库基于usb文件系统的驱动开发,并将其应用到嵌入式系统中,可显著降低开发难度,提高工作效率
1.前言
Linux内核经过开发人员的不断努力,已经变得十分地完善和强大。其中,设备驱动占据了内核的很大一部分,内核都能够高效、稳定地驱动大部分设备。而另外一些设备,注入自己设计的硬件产品。这些驱动就需要驱动工程师开发出相关的内核设备驱动了。内核驱动有他的优点,然而在内核驱动开发过程中会遇到如下的一些问题:
对于不用的Linux内核版本,驱动程序也会有不同的版本。不利于升级和维护。
Linux自带的驱动程序开发,需要对内核的重新编译,而且开发出来的驱动模块比较大。
所以,如果我们能够找到一个为用户空间提供与内核版本无关的通用接口的方法,就能够不必重新改变编译好的内核,也不必为内核版本兼容性问题而头痛,只需在用户空间开发程序。这样就极大地提高了开发效率,缩短了开发周期。
USB协议是目前使用最广泛的外部总线协议,Linux对usb控制器、usb核心和常用的usb设备驱动有了比较完善的支持。但由于usb所支持的设备的多样性,一些特殊的usb设备的驱动还需要开发人员自行编写。而libusb为开发人员提供了在用户空间操作usb设备的API函数库,使usb驱动开发变得极为方便。
2.实现原理
2.1USB简介
usb(Universal Serial Bus)是用于将适用usb的外围设备连接到主机的外部总线结构。目前usb接口规范主要有:USB1.1(低速1.5Mb/s,全速12Mb/s)和USB2.0(高速480Mb/s),能够满足大部分外围设备的传输速度要求。usb同时又是一种通信协议,他支持主系统(host)和usb的外围设备(device)之间的数据传输。当前流行的USB Host规范有:OHCI、UHCI和EHCI。嵌入式系统主要是使用OHCI规范。
usb驱动分为usb主机端驱动程序和usb设备端驱动程序,本文重点分析usb主机端驱动程序的开发过程。USB主机(host)驱动结构如图:
开发人员主要关注usb设备驱动部分。usb设备的逻辑组织中,包含设备(device)、配置(config)、接口(interface)和端口(endpoint)四个层次:
每个usb设备可以有一个或多个配置,但一次只能选中其中一个。接口经捆绑为配置。接口代表了一个基本功能。
每个usb驱动程序控制一个接口,而一个接口有不同的设置(setting),但同一时间只能有一种活跃配置(cur_altsetting)。设备接口是端点的集合(collection),端点是usb通信的最基本形式。usb主机(host)通过端点与usb设备进行通信。
usb端点有以下四种传输方式:控制(Control),等待(iso)、中断(interrupt)、批量(bulk)。控制和批量端点用于异步的少量数据传输,而批量和等时端点是周期性的。控制和中断端点是用于少量数据传输,而批量和等时端点用于大量数据传输。
控制端点同城用于配置设备、获取设备信息、发送命令到设备、或者获取设备的状态报告,“端点0”为默认的控制端点,usb核心使用该端点在设备插入时进行设备的配置。批量端点它要求数据传输的准确性,但不保证固定时间内的完成。中断端点是例如usb键盘和鼠标所使用的的主要传输方式。libusb主要提供控制、批量和中断的传输方式的API函数。
2.2usb文件系统(usbfs)
Linux内核提供了usb文件系统,他需要编写内核时选中相关编译选项。并在系统启动运行时动态挂载。可在/etc/fstab文件中添加如下一行:
nonc /proc/bus/usb usbfs defaults 0 0
或者输入命令:
mount -t usbfs none /proc/bus/usb
usbfs动态跟踪总线上插入和移除的设备,通过它可以查看系统中usb的设备信息,包括拓扑、带宽、设备描述符、产品ID、配置描述符、接口描述符、端点描述符等。usbfs还允许用户空间的程序直接访问usb设备,这使得容易维护和调试。
usbfs也至此各种ioctl的调用。有了这些ioctl我们就可以编写用户空间的usb驱动程序,而libusb做的工作就是把这些ioctl开发成一个库。
3.libusb实现驱动开发
3.1libusb简介
libusb是一种高级别的API,它封装了低级别的内核与USB模块的交互,并提供了一系列适合在用户空间进行usb驱动开发的函数。libusb基于usb文件系统提供的usb接口,端点等信息,与usb设备进行通信。显然,只要开发平台上的内核支持usb文件系统,我们就可以利用libusb进行usb驱动开发。
libusb可以跨平台实现,开发的程序很容易在不同操作系统上一直。对于Linux操作系统,也很容易在不同的CPU架构间进行移植,而且不低担心内核版本造成的种种问题。相对于Linux内核驱动开发,libusb无疑是一种省时省力而又行之有效的开发工具。
libusb定义了struct usb_bus,struct usb_device 和usb_xxx_descriptor来对usb总线、设备、配置和端点等进行描述。
一般可通过libusb提供的一些初始化函数,在usb文件系统中查找先关的总线和设备,并保存在几个对应的数据结构中:
usb_init()函数检查环境变量USB_DEVFS_PATH,目录/dev/bus/usb或目录/proc/bus/usb,若指定路径下含有usb文件系统信息,则将路径保存在全局变量usb_path[PATH_MAX+1]中。
usb_find_busses()函数会根据usb_path[]找到系统中所有的usb bus,并组成链表,由全局结构指针usb_busses指向。
usb_find_devices()函数则遍历usb_busses指向的链表,寻找所有bus上的所有usb设备。每个bus上的所有devices列表由usb_bus结构成员struct usb_device *devices指向。
我们可用两层的for循环,遍历usb_busses指向的链表,找到与指定描述符相符的usb_device。如通常是通过IDVender和IDProduct查找。
usb_dev_handle是一个十分重要的句柄结构,如果要对usb设备、接口、端点等操作,都离不开它,结构如下:
struct usb_dev_handle{
int fd;
struct usb_bus *bus;
struct usb_device *device;
int config;
int interface;
int altsetting;
void *impl_info;
1
2
3
4
5
6
7
8
域fd为文件描述符,与文件系统关联:域bus和device则指向要处理的usb设备。
开发人员可以使用usb_open(),打开指定的usb设备Dev,并返回相应的句柄结构:
usb_dev_handle *dev_handle = usb_open(dev);
之后,dev_handle便贯穿于对应usb设备的操作过程中,直至最后调用usb_close()关闭指定的usb设备,释放该句柄。
开放人员可使用usb_set_configuration(),usb_setaltinterface()等函数对usb设备的配置,接口和端点等进行设定。然后调用usb_control_msg()进行控制传输,或usb_bulk_write(),usb_bulk_read()进行大批量的端点读写。
3.2开发实例
libusb要应用到嵌入式系统中,需要交叉编译;开发代码中要包含libusb的头文件usb.h,并在编译时,要是用-I和-L选项指定libusb的头文件和库文件的路径,和-lusb指定libusb.a静态库文件。
下面的伪代码在系统中按厂商号和产品号找到usb图像采集设备并读bulk端点到缓冲中,
int main(void)
{
//...一些初始化工作
dev_usbdev_probe();//找到usb设备
dev_hanle = usb_open(dev); //打开设备
//发送控制信息,请求传输
usb_control_msg(dev_handle,0x40,0xb5,2,0,buffer,sizeof(buffer),1000);
if(dev_handle){
//若请求成功,从端点6传输数据到image_buffer中
usb_bulk_read(dev_handle,6,&image_buffer[64],64,1000);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
4.总结
本文实现基础是usbfs允许用户空间的程序直接访问usb设备,这使得许多内核驱动程序可以迁移到用户空间,通过libusb库提供的API函数实现嵌入式Linux驱动开发的方法,使得开发人员无需被底层的usb协议所困,无需区分不同版本内核驱动开发,从而使得usb的驱动开发更容易维护和调试,从而可以提高开发效率并缩短开发周期。