基于Linux系统的USBHOST驱动程序设计与实现[设计]摘要:本文介绍了基于Linux系统的USB控制器驱动设计的通常技巧。首先介绍USB制器驱动的总体结构,而后逐一剖析了各模块的功能和设计要点,最后给出了基于Philips的ISP1161芯片驱动的设计实例。关键词:LinuxUSBHOST驱动ISP1161序言随着嵌入式系统应用的日渐广泛,Linux系统凭着其强悍、稳定、免费以及对设备的广泛支持等特性使其在嵌入式系统领域迅速得到了应用。Linux系统对设备的友好支持来历已久,随着USB插口的广泛使用,Linux系统的USB子系统也得到不断的发展。虽然LinuxUSB设备(device)和主机(HC,HostControll)提供统一的编程插口linux usb驱动开发实例,但相对于USB设备,主机结构更复杂,开发难度更大。本文通过剖析Linux系统的USB主机控制器(HC)编程框架,给出了USB控制器驱动程序的总体结构,并通过作者编撰的ISP1161的实际程序说明各模块的设计要点。Linux系统中USB驱动结构Linux中的USB子系统核心模块为USBCore模块,它为USB驱动(device和HC)提供了一个用于访问和控制USB硬件的统一插口。
如图1所示:应用程序发出的USB恳求块(urb)经过USB设备驱动和USBcore后抵达USB主机控制器(HC),主机控制器解析urb后将数据发送到指定的USB设备上来。[图片]USB驱动结构具体说,主机控制器驱动程序实现的功能包括以下几点:实现不同USB传输类型的调度工作。USB数据的实际传输工作。实现虚拟根HUB的功能。USB设备之间是通过USBHUB链接在一起的,主机控制器和USB设备之间通常通过根HUB相连,如图2所示。一般主机控制器芯片提供与根HUB相关的状态查询和控制单元。当有设备插入时,在枚举过程中,主机控制器驱动通过查询和控制单元应答设备来伪装成一个HUBlinux usb驱动开发实例,所以一般称此HUB为虚拟根HUB。除上述功能以外,主机控制器驱动还要调用Linux提供的反弹函数,协同操作系统完成urb的维护和传输。Linux-usb主机驱动结构主机控制器驱动程序按功能结构界定以下模块:URB_READY用于解析、维护和缓存urb;USB_SCHEDULE用于USB传输调度;USB_COMM完成USB实际通讯;USB_VRH实现主机控制器根HUB功能。[图片]主机控制器拓扑结构3.1URB_READY模块USB核心传送的urb恳求抵达主机控制器后,驱动要缓存各urb便于旁边的传输。
驱动程序为USB控制器的每位活动端点都构建一个urb等待队列,收到的urb根据端点的不同缓存到不同的等待队列中,而后再进行调度传输。因为各端点都可以独立完成通讯工作,互不影响,达到了最大的并行处理速率。3.2USB_SCHEDULE模块驱动程序在每位USB传输帧中都要安排调度。各传输类型占用的带宽要符合USB合同规定,这是由控制器芯片和驱动协同保证的。为便捷调度,USB_SCHEDULE模块构建了4个调度队列,分别对应四种USB传输类型。调度模块首先扫描每位端点的urb缓冲队列,之后把每位队列的当前urb根据传输类型的不同链接到4种调度队列中。当帧中断到来时,驱动程序扫描调度队列,将队列中的urb数据传送出去。4个调度队列的优先级是根据USB规范中传输类型的带宽决定的。3.3USB_COMM模块USB_COMM模块完成USB设备之间的通讯。大多数主机控制芯片内部都带有数据通讯缓存区,驱动程序只要依照芯片的要求在系统显存中组织好数据,再通过USB主机控制器插口传送到其内部缓存区中即可通讯。随后,驱动程序要将传输状态写回到urb并将其传送给USB核心,对于读数据的urb恳求linux培训,还要将返回的数据写回到urb中,便于退还给USB核心。
3.4USB_VRH模块USB_VRH模块主要实现虚拟根HUB的功能,使主机控制器在枚举时注册为一个HUB设备,但数据的传输还是通过主机控制器来完成。ISP1161主机控制器驱动Philips公司的ISP1161芯片支持OHCI标准,它是一个符合USB2.0全速规范的单片主机控制器和设备控制器。ISP1161可以仅作为主机控制器或设备控制器使用,也可以同时作为主机和设备控制器使用。ISP1161支持储存器扩充,所以对于没有集成USB主机控制器的微处理器,可以使用ISP1161来降低系统对USBHOST的支持。4.1ISP1161主机控制器驱动的总体结构Linux系统中,USB主机控制器驱动程序插口函数中最重要的实现是hci_submit_urb()函数,该函数拿来处理USB核心发送出来的urb恳求,所以该函数的实现直接影响系统的性能。如前所述,在本驱动中hci_submit_urb()函数仅将下层发送出来的urb缓存到端点队列中,最后的urb通讯响应是在中断函数中调度完成的。驱动的整体结构如图3所示:[图片]ISP1161驱动结构说明图中等待队列为URB_READY模块实现部份;激活队列为USB_SCHEDULE模块实现部份;为USB_COMM模块实现部份。
图中也给出了联接各模块的插口函数。驱动程序有两条并行的执行线路linux重启命令,为驱动次序执行线路,负责把urb缓存;则是在帧中断服务程序中完成的,主要拿来调度不同种类的数据传输。4.2ISP1161驱动URB_READY模块实现该模块中实现了Linux驱动插口函数hci_submit_urb(),经过一些简单判定后,调用qu_queue_urb()函数来将urb缓存到各端点的等待队列中。对于空的端点队列,urb毋须缓存,可以直接放置在激活队列(调度队列)中。qu_queue_urb()函数的伪代码如下qu_queue_urb(urbinted(EdList_head[ed])list_add(urbelsequ_queue_active_urb(urb);当urb抵达图中处,表示完成此端点的一次数据通讯,驱动须要在该端点的缓存队列中找到下一个urb,加入到激活队列中,这是通过调用qu_return_urb()函数来完成的。4.3ISP1161驱动USB_SCHEDULE模块实现调度功能是在帧中断服务程序中完成的,USB规范中规定一帧中可以有多个事务处理,即多次数据通讯,所以在一帧中要合理安排各传输类型的urb。
中断服务程序的伪代码如下:voidhc_interrupt(intirq,void__hci,structpt_regsints_uP=READ_REG16(HcuPInterrupt)bstat=READ_REG16(HcBufferStatus);(ATLInt|SOFITLInt)READ_REGn16(HcATLBufferPort,buff_len代码的(1)部份表示USB控制器读数据时的处理方法:程序将数据读出后,调用sh_done_list函数来进行后续处理。(2)部份为帧调度处理:程序判定缓冲区可用后,调用sh_schedule_trans()函数将激活队列(调度队列)中的urb数据写入到ISP1161的通讯缓冲区中,进行实际的USB通讯。4.4ISP1161驱动USB_COMM模块实现该模块为驱动提供实际的USB通讯的功能,所以实现方法与具体的USBhost芯片相关。具体到ISP1161芯片,只要将需传输的数据根据PTD格式在显存中组织好后,再传送到芯片内部缓冲区中就可通讯。对于返回的数据和状态信息,也要从缓冲区读出并解析,再填写到urb结构中传回给USB核心,图3中的sh_done_list()函数完成上述功能。
推论本文给出了Linux系统下USBhost驱动的设计方式。该方式采用模块设计,各模块互相独立,模块之间通过特定的插口函数联接,这样的结构保持了驱动的最大可移植性。对于不同的USBhost芯片,只需改写USB_COMM模块,该驱动就可以挺好的在Linux系统上工作了。参考文献ProgrammingGuideLinuxUSBDeviceDrivers.pdf,2000linux2.4.18内核源码,2001ISP1161A1-03.pdf,2004作者简介张卓亮,大唐微电子公司工程师,主要从事嵌入式系统的设计与研究。
文章评论