全部博文(668)
分类:
2008-10-07 21:54:24
1 引言
近年来,形式多样的嵌入式数字化产品已经开始成为继PC机之后的一大信息处理工具。这些产品大都需要数据的转储和备份功能。Flash存储卡由于其存储密度高、易于编程和再编程、读写速度快、成本低等特征而成为嵌入式系统中一种比较理想的存储设备。CompactFlash存储卡(简称CF卡)是目前中应用最为广泛的flash存储卡。目前对CF卡的大多数应用,都是用一个PCMCIA控制器来实现对CF卡的操作。然而在中,硬件设计应该尽可能简单,从而降低成本,以至许多嵌入式Linux系统中没有PCMCIA控制器而仍然利用CF卡作为存储设备。此时CF卡是如何工作的了?这就是本文主要探讨的问题。
2 CF卡的硬件特性
CF卡通过一个50引脚的连接器与主机相连。在CF卡内,有一个集成的片上控制器,这个控制器管理着接口协议、数据存储和恢复、错误处理和诊断、电源管理和时钟控制。当主机正确地驱动CF卡后,用户就可以像使用磁盘一样方便地利用CF卡存储数据。
一般地,CF卡分为3种寻址访问模式,每一种模式在系统上反映为如何来访问其地址空间。通过对CF卡的属性存储空间进行配置,可以得到CF卡的不同的寻址访问模式:
(1) I/O模式:通过访问CF卡的内部IO寄存器所指向的16字节数据块,实现对CF卡的通用存储空间进行寻址访问,一般利用系统提供的中断进行读写事务处理。
(2) Memory模式:将CPU与CF卡的地址线和数据线进行直连,通过访问其通用存储空间的16字节数据块来实现对CF卡的寻址访问。
(3) True IDE模式(与大多数磁盘驱动器兼容):将标准的PC-ATA磁盘I/O寻址空间1F0h-1F7h、3F6h-3F7h (primary)或者 170h-177h、 376h-377h (secondary)作为寻址端口, 同时使用中断IRQ14 (或者其他可用的IRQ)进行事务处理。
其中,CF卡控制器在Memory模式下能够最快地响应操作命令。该模式较好地满足了的实时性要求,而且在此模式下CF卡插槽结构简单,设计成本低,因此本文选择Memory寻址模式进行研究。
3 系统设计
本文以ARM系列的EP7312[2]为模板,用Linux作为操作系统,开发了用CF卡作为其存储设备的。该系统用一个50针的简单插槽,将CF卡的地址线和数据线与CPU直接相连,从而省去了PCMCIA控制器。CF卡的驱动程序主要是改造Linux中原有的IDE程序,不使用中断事务处理方式,让CF卡在Memory模式下进行8位寻址。同时,该系统用一个后台程序来管理CF卡的热插拔事务。图1给出了系统中CF卡的驱动程序结构图。
3.1 CF卡的配置
在对CF卡进行存储操作之前,驱动要对它进行必要的配置。
首先,驱动在系统中设置传输CF卡信号的GPIO,并为其分配好寻址用的IO端口。在Memory模式下,CF卡的IO端口即为其通用存储空间的16字节数据块。
其次,对CF卡的属性存储空间进行配置。CF卡有4个8位属性寄存器:属性选项寄存器:(BASE + 00H)、卡配置和状态寄存器:(BASE + 02H)、针替换寄存器:(BASE + 04h)、套接字拷贝寄存器:(BASE+ 06h)。它们的基址(BASE)为属性存储空间中的200H。由于CF卡在系统中使用Memory模式,且没有用到中断,故将属性选项寄存器配置为0x00。其他属性寄存器一般保持默认值,配置状态寄存器、针替换寄存器、套接字拷贝寄存器的默认值分别为0x80、0x2e、0x00。为保证能够在Memory方式下正确地使用CF卡,驱动要对这些属性寄存器的值进行验证,若这些值与默认值不匹配,则要将其值设置为默认值。
3.2 CF卡的底层驱动
要实现CF卡的存储备份功能,则要求CF卡必须能够正确地接收并响应系统对它的I/O请求;这主要通过CF卡的底层驱动来实现。
3.2.1 发布ATA命令
首先,驱动要正确地接收系统对CF卡的I/O请求,进而向CF卡发布ATA命令。CF卡的ATA命令集分为3类。每类命令都要求CF卡在接收到命令时处于不忙(BSY=0)状态且有数据请求(DRQ=1)时才能处理CF卡缓冲区中的数据流,如读(0x20)、写(0x30)、擦除(0xc0)、格式化(0x50)等命令。
对CF卡发布ATA命令,首先要填写CF卡的命令控制块。在发布读写命令时,要注意CF卡的寻址方式。CF卡支持C/H/S(Cylinder/Head/Sector)和LBA(Logical Block Addressing)寻址模式。
由于CF卡是一种大容量的存储设备(市场上已经有容量为4GB的CF卡),因此,这里用LBA寻址模式来访问CF卡。寻址模式的选择通过ATA寄存器组中的Drive/Head寄存器来配置。根据I/O请求得到CF卡的块号block,利用该块号来计算CF卡的LBA地址。取block的低28位,依次对应CF卡的柱面低位寄存器、柱面高位寄存器、扇区寄存器、设备/磁头寄存器,如图2所示。将这些参数值和ATA命令值按8位方式写入相应寄存器中,即完成了一次ATA命令发布。
CF卡的其他ATA命令发布方式与读写命令类似,这里就不再讨论。
3.2.2 命令响应
在填写ATA命令控制块后,CF卡要对这些命令做出响应,如读写命令要求从CF卡内读写数据等。如何处理CF卡的这些命令响应是关键。将CF卡中通用存储空间的I/O端口注册于IDE管理程序,利用IDE的磁盘管理程序来管理CF卡的命令请求。但是,IDE管理是利用中断来响应对各个设备的命令请求的,而CF卡在Memory方式下发送完ATA命令后没有产生中断,也就是说在硬件上CF卡的Memory方式没有可利用的中断。这就要求采用不同于通用IDE管理的命令响应处理方式。
可以利用轮询的方法来处理命令响应。在发送完ATA命令后,IDE管理程序设置相应于该命令的处理函数,并立即进入命令该处理函数。在各个命令处理函数内部,通过定时轮询的方式查看CF卡的状态寄存器。若CF卡的状态与命令状态匹配,则进行相应的数据处理。如:发送的是读命令且探测到CF卡的状态位中有数据请求且CF卡不忙,就立即按8位方式进行操作,将CF卡的数据寄存器上的数据串行化输出到系统缓冲区中。若CF卡的状态与命令状态不匹配,则继续等待;超时则进入出错处理。
3.2.3 出错处理
在发布完ATA命令后,由于受外部环境(如CPU负荷高、自然条件恶劣等)的影响,以及CF卡内在电气特性的某种不稳定性,都可能造成CF卡没有接收到该命令或者接收命令出错。尽管发生出错的概率很低,但驱动仍应该应对这种偶然情况进行处理。一方面,IDE管理程序应该返回到对CF卡发送命令之前的状态,以便重新发送该ATA命令。这一点在IDE管理程序中已经实现。另一方面,CF卡本身要恢复到一般正常状态,以便重新接收ATA命令。这又可以有两种方法。一种是通过IDE对CF卡发送 Reset 信号,这要求CF卡从硬件上支持。另一种是纯软件方式。CF卡的属性选项寄存器有一个SRESET位,通过读写该寄存器,可以达到让CF卡实现软重置的目的。但有一点需要注意的是,在将该寄存器中SRESET置位后,等待一段时间后仍应该将其恢复到初始状态,这样才能达到软重置的目的。为了节约硬件成本,这里采用软重置的方法。
3.2.4 文件组织
在系统中将CF卡作为块设备进行注册。由于DOS文件系统的通用性,采用VFAT(16位的DOS)文件系统对CF卡上的内容进行组织。驱动为文件系统提供应用接口[3],即标准的IDE文件操作,包括打开、释放、IO控制等,它们面向文件系统对CF卡的数据请求。
3.3 对热插拔事件的管理
嵌入式存储设备的一大特色就是能够做到即插即用。系统只有准确、及时地响应热插拔事件,才能方便快捷地实现存储卡的存储备份功能。在Linux中,系统层和应用层都要对热插拔事件进行处理。在系统层,一方面要探测CF卡的热插拔事件,分配或释放系统资源,并驱动CF卡;另一方面,要将此事件准确及时地通知给应用层。应用层则根据热插拔事件作相应的处理,给用户提供相应于CF卡的方便、快捷的用户接口。
因此,下面分别在操作系统层和应用层进行设计来实现热插拔管理。
3.3.1 操作系统层
为方便CF卡的热插拔管理,在操作系统层注册一个字符型设备CF_MGR文件。
1)探测热插拔事件
探测CF卡的热插拔事件,可以利用中断或者定时器。中断固然能够实时地检测CF卡的热插拔事件,但它需要硬件上的支持。结合嵌入式硬件尽量简单的特点,这里用定时器技术来探测CF的热插拔事件。
系统定时检查两个信号量,利用这两个信号的值来判断CF卡的插入拔出事件。一个是CF卡注册标志(REG_FLAG),初始值为0。当CF卡插入并被驱动后,REG_FLAG变为1;当CF卡拔出并被卸载后,REG_FLAG变为0。另一个信号值(DETECT_FLAG)反映连接在CF卡插槽上的CD1、CD2信号值。这是从硬件上来实现的:当CF卡在插槽中时,DETECT_FLAG为0;而当CF卡不在插槽中时,DETECT_FLAG为1。通过REG_FLAG和DETECT_FLAG的值的组合,管理设备来判断所发生的CF卡的热插拔事件及其状态,如表1。
2)与应用层通信接口
Linux进程可以转入休眠状态以等待某个特定事件,当该事件发生时这些进程能够被再次唤醒。内核实现这一功能的技术要点是把等待队列(wait queue)和每一个事件联系起来。CF_MGR管理文件初始化一个等待队列,以备将CF卡的热插拔事件通知给其他进程。当系统定时检查出热插拔事件时,就唤醒该等待队列。内核遍历相应队列,唤醒休眠的任务让它投入运行状态。
管理设备通过一系列文件操作跟应用层通信,如open、release、read、poll、ioctl等。其中,read用来将要处理的CF卡的热插拔事件传递给应用层,write反馈应用层处理完的事件,而poll将管理设备的等待信号插入等待队列,以便发生热插拔事件时唤醒该等待队列,并返回该设备可读的信息。
3.3.2 应用层
良好的用户接口是嵌入式存储设备能否便于使用的关键。在应用层,一方面程序要及时监聴CF卡所发生的热插拔事件,另一方面,在没有任何事件发生的情况之下应该停止程序的运行,让出抢占的系统资源,进入阻塞状态,让程序最小程度的占用CPU 资源。为达到此目的,采用Linux中的Select阻塞机制。
首先将CF卡系统层的管理设备的文件描述符加入到Select文件描述符集合中,然后阻塞这些文件描述符。当发生热插拔事件时,系统层唤醒poll加入到等待队列中的等待信号,并将该设备文件可读的信息传给应用层。然后应用层通过read函数得到CF卡的热插拔事件,调用应用层事件处理器,并将该事件通过write反馈到系统层。这样,就完成了一个Select循环。应用层通过Select阻塞方式可以及时地探测到CF卡的热插拔事件。
应用层事件处理器负责将CF卡的当前的状态信息提供给用户,并给用户进行方便、快捷的存储管理提供接口。一方面,当发生CF卡插入事件时,将CF卡挂接到指定目录下;另一方面,当发生CF卡拔出事件时,将文件系统设置到一个安全的状态,并释放所用到的CF资源。
4 结论
通过以上系统设计,CF卡在此中实现了如下功能:支持VFAT文件系统,让CF卡在各种系统中通用,真正实现转储功能;根据硬件设计要求,实现memory方式访问;根据已有的总线宽度,按8位方式对数据进行访问;能够方便地对CF卡进行热插拔。