嵌入式Linux下的USB设备驱动技术

2012-04-14 08:54:10来源: 互联网

嵌入式Linux下的USB设备驱动技术

Linux以其稳定、高效、易定制、硬件支持广泛、源代码开放等特点,已在嵌入式领域迅速崛起,被国际上许多大型的跨国企业用作嵌入式产品的系统平台。

USB是Universal Serial Bus (通用串行总线)的缩写,是1995年由Microsoft、Compaq、IBM等公司联合制定的一种新的PC串行通信协议。它是一种快速、灵活的总线接口。与其它通信接口相比较,USB接口的最大特点是易于使用,这也是USB的主要设计目标。USB的成功得益于在USB标准中除定义了通信的物理层和电器层标准外。还定义了一套相对完整的软件协议堆栈。这使得多数USB设备都很容易在各种平台上工作。作为一种高速总线接口,USB适用于多种设备(如数码相机、MP3播放器、高速数据采集设备等)。另外,USB接口还支持热插拔,而且所有的配置过程都由系统自动完成,无须用户干预。

1 Linux下的USB设备驱动

在Linux内核的不断升级过程中,驱动程序的结构相对稳定。由于USB设备也是外围设备的一种,因此,它的驱动程序结构与普通设备的驱动程序相同。Linux系统的设备分为字符设备(CharDevice)和块设备(BlockDevice)。字符设备支持面向块字符的I/O操作,它不通过系统的快速缓存,而只支持顺序存取。块设备则支持面向块的I/O操作,所有块设备的I/O操作都通过在内核地址空间的I/O缓冲区进行,可以支持几乎任意长度和任意位置上的I/O请求。块设备与字符设备还有一点不同,就是块设备必须能够随机存取(RandomAccess),字符设备则没有这个要求。典型的字符设备包括鼠标、键盘、串行口等,而块设备主要包括硬盘软盘设备、CD-Rom等。由于USB设备主要都是通过快速串行通讯来读写数据,因此一般都可作为字符设备来进行处理。

2 Linux下的USB core

2.1 Linux中USB core与USB的结构关系

Linux操作系统中有一个叫做“USB core”的子系统,可提供支持USB设备驱动程序的API和USB主机控制器的驱动程序。同时提供有许多数据结构、宏定义和功能函数来对硬件或设备进行支持。在Linux下编写USB设备的驱动程序时,从严格意义上讲,就是使用这些USB core的子系统所定义的数据结构、宏和函数来编写数据的处理功能。在Linux下,core、host controller和driver三者之间的关系如图1所示。



2.2 USB core的初始化

USB core从USB子系统的初始化开始。USB子系统的初始化则在文件drivers/usb/core/usb.c里。其代码如下:

  subsys_initcall(usb_init);
  module_exit(usb_exit);

代码中的subsys_initcall是一个宏,相当于module_init,只不过因为这部分代码是核心,开发者通常把它看作一个子系统,而不仅仅是一个模块。因为USB core模块代表的不是某一个设备,而是所有USB设备赖以生存的模块。因此,在Linux中,像这样把一个类别的设备驱动归结为一个子系统(比如PCI子系统、scsi子系统等)。基本上,drivers/目录下面第一层的每个目录都可算作一个子系统,因为它们代表了一类设备。一般地,usb_init是真正的初始化函数,而usb_exit()则是整个USB子系统结束时的清理函数:



函数usb_init主要完成初始化和注册设备。

2.3 USB里的设备模型

Linux里一个很重要的概念是设备模型。对于驱动来说,设备的概念就是总线和与其相连的各种设备。在内核里,总线、设备、驱动也就是bus、device、driver是设备模型很重要的三个概念,它们都有自己专属的结构。在include/linux/devide.h里的定义为:

struct bus_type {……};
struct device {……);
struct device_driver {……};

每次出现一个设备都要向总线注册,每次出现一个驱动,也要向总线注册。系统初始化时,应扫描连接许多设备,并为每一个设备建立一个struct device的变量。每一次都应有一个驱动程序,并要准备一个struct device_driver结构的变量。还要把这些变量加入相应的链表(如把device插入devices链表,driver插入drivers链表)。这样,通过总线就能找到每一个设备和每一个驱动。然而,假如计算机里只有设备却没有对应的驱动,那么设备将无法工作。反过来,倘若只有驱动却没有设备,驱动也起不了任何作用。对于USB设备,它可以在计算机启动以后再插入或者拔出计算机。由于device可以在任何时刻出现,而driver也可以在任何时刻被加载,所以,每当一个struct device诞生时,它就会去BUS的drivers链表中寻找自己的另一半。如果找到了匹配的设备,就调用device_bind_driver,并绑定好。

Linux设备模型中的总线落实在USB子系统里就是usb_bus_type,它在usb_init函数中可用retval=bus_register(&usb_bus_type)语句注册,而在driver.c文件里的定义如下:



该函数的形参对应的就是总线两条链表里的设备和驱动。当总线上有新设备和驱动时,这个函数就会被调用。

3 USB驱动程序的描述符

一个设备可以有多个接口,一个接口可代表一个功能,因此,每个接口都对应着一个驱动。例如一个USB设备有两种功能,一个键盘,上面还带一个扬声器,这就是两个接口,就需要两个驱动程序,一个是键盘驱动程序,一个是音频流驱动程序。

一个驱动程序是否支持一个设备,要通过读取设备的描述符来判断。那么,什么是USB的描述符呢?USB的描述符是一个带有预定义格式的数据结构,里面保存有USB设备的各种属性和相关信息,可以通过向设备请求获得它们的描述符内容来深刻了解和感知一个USB设备。主要有四种USB描述符,分别为:接口描述符、端点描述符、设备描述符和配置描述符。

协议规定:一个USB设备必须支持这四大描述符,还有些描述符不是必须包含的,有些特殊设备用来描述设备的不同特性,但这四大描述符是一个都不能少的。USB设备里有一个eeprom,可用来存储设备本身信息,设备的描述符就存储在这里。

上述四个描述符分别放在了include/linux/usb.h文件中的struct usb_host_interface、structusb_host_endpoint、struct usb_device、struetusb_host_config里,而描述符结构体本身定义在include/linux/usb/ch9.h里.并分别用struct usb_interface_descriptor、struct usb_host_endpoint、structusb_device_descriptor和struct usb_config_descriptor来表示。描述符结构体的定义应完全按照USB协议对描述符的规定来定义。

4 USB接口驱动

4.1 接口结构

平时编写的USB驱动通常指的是写USB接口的驱动,一个接口对应一个接口驱动程序,需要以一个struct usb_driver结构的对象为中心,并以设备的接口提供的功能为基础,来进行USB驱动程序的编写。struct usb_driver结构体一般定义在include/linux/usb.h文件里。具体如下:

struct usb_driver{
const char*name;
int(*probe)  (struct usb_interface*intf,const
struct usb_device_jd*id);
void(*disconnect)  (struct usb_interface*intf);
int(*ioctl)  (struct usb_interface*intf,unsigned
int code,void*buf);
int  (*suspend)  (struct usb_interface*intf,
pm_message_t message);
int(*resume)  (struct usb_interface*intf);
void(*pre_reset)  (struct usb_interface*intf);
void(*post_reset)(struct usb_interface*intf);
const struct usb_device_id*id_table;
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
};

Name为驱动程序的名字,对应于/sys/bus/usb/drivers/下面的子目录名称。它只是彼此区别的一个代号,这里的名字在所有的USB驱动中必须是唯一的。probe用来看看这个USB驱动是否愿意接受某个接口的函数。Disconnect函数将在接口失去联系或使用rmmod卸载驱动将它和接口强行分开时被调用。Ioctl函数则用在驱动通过usbfs和用户空间进行交流时使用。Suspend、esume分别在设备被挂起和唤醒时使用。pre_reset、post_reset分别在设备将要复位(reset)和已经复位后使用。id_table的变量可用来判断是否支持某个设备接口。Dynids是支持动态id的。实际上,即使驱动已经加载了,也可以添加新的id给它。drvwrap是给USB core区分设备驱动和接口驱动用的。no_dynamic_id可以用来禁止动态id。supports_autosuspend可对autosuspend提供支持,如果设置为0,则不再允许绑定到这个驱动的接口autosuspend。

接口驱动

当insmod或modprobe驱动的时候,经过一个曲折的过程,就会调用相应USB驱动里的xxx_init函数,进而去调用usb_register (),以将相应的USB驱动提交给设备模型,添加到USB总线的驱动链表里。当rmmod驱动时,同样,在经过一个曲折的过程之后,再调用相应驱动里的xxx_cleanup函数,进而调用usb_deregister ()将相应的USB驱动从USB总线的驱动链表里删除。

5 结束语

本文介绍了Linux下USB core的工作原理,同时介绍了驱动USB必须了解的四个描述符。此外,还介绍了Linux下usb接口驱动的工作原理。本文介绍的方法能适应于Linux下各种不同的USB设备驱动程序的开发。

关键字:嵌入式  Linux  USB  设备驱动

编辑:神话 引用地址:http://www.eeworld.com.cn/mndz/2012/0414/article_15653.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。
论坛活动 E手掌握
微信扫一扫加关注
论坛活动 E手掌握
芯片资讯 锐利解读
微信扫一扫加关注
芯片资讯 锐利解读
推荐阅读
全部
嵌入式
Linux
USB
设备驱动

小广播

独家专题更多

TTI携TE传感器样片与你相见,一起传感未来
TTI携TE传感器样片与你相见,一起传感未来
TTI携TE传感器样片与你相见,一起传感未来
富士通铁电随机存储器FRAM主题展馆
富士通铁电随机存储器FRAM主题展馆
馆内包含了 纵览FRAM、独立FRAM存储器专区、FRAM内置LSI专区三大部分内容。 
走,跟Molex一起去看《中国电子消费品趋势》!
走,跟Molex一起去看《中国电子消费品趋势》!
 
电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2016 EEWORLD.com.cn, Inc. All rights reserved