1、 NAND Flash驱动程序框架
FAT文件系统下的NAND Flash驱动程序采用了分层结构。驱动程序的上层是Flash抽象层,是物理操作无关层,该层对NAND Flash的操作进行抽象,并采用一定的策略平衡了NAND Flash的擦写。NAND Flash驱动程序的结构如图5.5所示。
File System(FAT)Flash DriverFAL(Flash Abstraction Layer)FMD(Flash Media Driver)Flash Hardware 图5.5 FAT下NAND Flash驱动结构
在图中: File System
即文件系统。在这里,采用的是FAT文件系统。FAT文件系统是一种采用链式分配方式的文件系统。并没有对NAND Flash的特点优化,因此需要在下层的驱动程序做优化。 Flash Driver
即NAND Flash驱动程序。对上层的文件系统提供以DSK为前缀的流驱动接口。该层驱动程序本身分为两层:FAL层、FMD层。 (1)、FAL层
即Flash Abstraction Layer,Flash抽象层。该层主要提供三个功能: A、将物理的Flash抽象成统一的接口提供给上层的文件系统。
B、将逻辑扇区地址转换成物理扇区地址。上层的FAT文件系统使用的是逻辑扇区地址,并不是真正的物理扇区,其转换由FAL实现。
C、对Flash实现损耗平衡(\"Wear-level\")。为了避免反复的擦写Flash的同一个块,需要一种策略来减少反复的擦写块。 (2)、FMD层
即Flash Media Driver,Flash介质驱动层。该层实现FAL层的请求,对Flash物理扇区进行操作。
Flash Hardware
即NAND Flash物理芯片。 2、FAL层(Flash Abstraction Layer)
1) 函数接口定义
FAL层对上的函数接口也就是整个NAND Flash驱动程序的对外接口,由于NAND Flash是块设备,Windows CE中块设备采用的是流驱动接口,流驱动接口是一个标准的统一接口,只是各个驱动的前缀不同,在这里NAND Flash函数接口的前缀为“DSK”,这个前缀也使得Windows CE将“DSKxx:”的文件名看作为设备,使得我们能够通过Windows CE标准的Win32 API,如CreateFile、DeviceIOControl等来对设备进行打开、读写等操作。
具体的函数接口定义如下:
DWORD DSK_Init(DWORD dwContext); BOOL DSK_Deinit(DWORD dwContext); DWORD DSK_Open(DWORD dwData, DWORD dwAccess, DWORD dwShareMode); BOOL DSK_Close(DWORD Handle); DWORD DSK_Read(DWORD Handle, LPVOID pBuffer, DWORD dwNumBytes); DWORD DSK_Write(DWORD Handle, LPCVOID pBuffer, DWORD dwInBytes); DWORD DSK_Seek(DWORD Handle, long lDistance, DWORD dwMoveMethod); BOOL DSK_IOControl(DWORD Handle, DWORD dwIoControlCode, PBYTE pInBuf, DWORD nInBufSize, PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned); VOID DSK_PowerUp(VOID); VOID DSK_PowerDown(VOID); 这里,实际使用的函数只有DSK_Init、DSK_Deinit、DSK_IOControl、DSK_PowerUp、DSK_PowerDown。其他函数均为空函数。
DSK_Init
NAND Flash驱动程序初始化函数,系统启动时加载驱动程序时会通过设备管理器调用此函数对NAND Flash进行初始化。 DSK_Deinit
NAND Flash驱动程序卸载函数,在卸载驱动程序的时候调用此函数。 DSK_IOControl
NAND Flash驱动程序的I/O函数,所有对设备进行的读写擦等操作都是由该函数实现的,也是上层主要使用的函数,而DSK_Read、DSK_Write、DSK_Seek均为空函数,不起任何作用。 DSK_PowerUp
NAND Flash驱动程序电源管理函数。在系统唤醒时调用此函数对Flash进行电源管理。此函数是通过调用FMD层的电源管理函数实现的。 DSK_PowerDown
NAND Flash驱动程序电源管理函数。在系统睡眠时调用此函数对Flash进行电源管理。此函数是通过调用FMD层的电源管理函数实现的。
2) 数据结构定义 DISK_INFO
DISK_INFO结构包含了有关磁盘设备的扇区数等物理信息,系统通过该结构来了解设备具体物理细节,该结构是通过DISK_IOCTL_GETINFO获取的。 typedef struct _DISK_INFO { DWORD di_total_sectors; DWORD di_bytes_per_sect; DWORD di_cylinders; DWORD di_heads; DWORD di_sectors; DWORD di_flags; } DISK_INFO, *PDISK_INFO;
结构成员描述如下:
di_total_sectors:设备扇区总数,一般情况下,块设备总是以扇区为单位进行操作。 di_bytes_per_sect:每个扇区的字节数。
di_cylinders:如果设备支持CHS寻址模式,则表示柱面数。
di_heads:如果设备支持CHS寻址模式,则表示每个柱面磁头数。 di_sectors:如果设备支持CHS寻址模式,则表示每个磁道扇区数。
由于NAND Flash没有磁头、柱面等概念,因此不使用CHS寻址模式,该模式是磁盘所特有的。
di_flags:标志位,其可能的值如下: Flag DISK_INFO_FLAG_MBR DISK_INFO_FLAG_CHS_UNCERTAIN DISK_INFO_FLAG_UNFORMATTED DISK_INFO_FLAG_PAGEABLE Description 设备使用MBR 设备不支持CHS寻址模式 设备需要格式化 设备支持请求页 SG_BUF
SG_BUF用于读写IOCTLs使用的缓冲,在使用DSK_IOControl函数对NAND Flash进行读写时,需传入该结构来表示要写入或读出的数据缓冲。 typedef struct _SG_BUF { PUCHAR sb_buf; DWORD sb_len; } SG_BUF, *PSG_BUF; 结构成员描述如下: sb_buf:缓冲指针。 sb_len:缓冲长度。
SG_REQ
该结构包含了上层读写的请求的具体要求,如从什么地址开始读写多大的数据。此结构的参数sr_sglist 即指向上面所示的SG_BUF结构。FAT文件系统使用SG_REQ请求驱动程序进行读写。 typedef struct _SG_REQ { DWORD sr_start; DWORD sr_num_sec; DWORD sr_num_sg; DWORD sr_status; PFN_REQDONE sr_callback; SG_BUF sr_sglist[1]; } SG_REQ, *PSG_REQ;
结构成员描述如下:
sr_start:读写请求的起始扇区号。 sr_num_sec:读写的扇区数。
sr_num_sg:指向的SG_BUF的个数。 sr_status:返回的请求状态。
sr_callback:请求结束的回调函数
sr_sglist:起始SG_BUF的指针。
3) FAL层具体功能
FAL层由类FAL、类MappingTable、类SectorMgr、类Compactor组成。整个FAL层的功能是由三个功能部分组成的。如图5.6所示。
FALBuildupMappingInfo()MappingTable*m_pMapSectorMgr*m_pSectorMgrWriteToMedia()Compactor*m_pCompactorCompactorMappingTableReadFromMedia()SectorMgrDeleteSectors()
图5.6 FAL类结构
(1)、类FAL
类FAL有三个成员变量,分别为其三个功能部分的对象。这三个部分分别为: 逻辑扇区到物理扇区映射表 扇区管理器 垃圾块回收器
类FAL的功能就是通过这三个功能部分的协作实现逻辑扇区到物理扇区的映射,并平衡NAND Flash擦写的次数。
类FAL提供的功能包括了读、写、删除和建立映射表函数。下面是示意代码: class Fal { public: //flash读函数 BOOL ReadFromMedia(PSG_REQ pSG_req, BOOL fDoMap); //flash写函数 BOOL WriteToMedia(PSG_REQ pSG_req, BOOL fDoMap); //flash删除函数 BOOL DeleteSectors(DWORD dwStartLogSector, DWORD dwNumSectors) ; protected: //flash扫描并建立映射表函数 virtual BOOL BuildupMappingInfo() ; public: MappingTable* m_pMap; //逻辑扇区到物理扇区映射表 SectorMgr* m_pSectorMgr; //扇区管理器 Compactor* m_pCompactor; //垃圾块回收器 };
(2)、类MappingTable
类MappingTable提供给FAL相关的映射功能。所有的映射数据都放在一张表中,这张表保存在类MappingTable的成员变量m_pDynamicLUT[MASTER_TABLE_SIZE]中。其映射表结构如图5.7。
m_pDynamicLUT12LS0LS1LS3LSKNULLNMaster TableNULLSecondary Tables 图5.7 FAL层映射表结构
该表是一张二级映射表,主映射表(Master Table)共有256项,因此m_pDynamicLUT是一个大小为256的数组,数组的成员是一个指向二级映射表(Secondary Tables)的指针。二级映射表每一项存放了该逻辑扇区地址相对应的物理扇区地址。类MappingTable的示意代码如下: class MappingTable { public: //查找映射表获取逻辑扇区地址的物理扇区地址 PBYTE GetPhysicalSectorAddr(SECTOR_ADDR logicalSectorAddr, PSECTOR_ADDR pPhysicalSectorAddr); //将物理扇区地址填入映射到指定的逻辑扇区地址 PBYTE MapLogicalSector(SECTOR_ADDR logicalSectorAddr, SECTOR_ADDR physicalSectorAddr, PSECTOR_ADDR pExistingPhysicalSectorAddr); private: //扇区映射表 PUCHAR m_pDynamicLUT[MASTER_TABLE_SIZE]; }; (3)、类SectorMgr
类SectorMgr主要用于管理空闲(free)扇区、脏(dirty)扇区信息。其内部具体结构如图5.8所示。
SectorMgrAddSectorsToListUnmarkSectorsAsFreeGetNextFreeSectorMarkSectorsAsDirtyUnmarkSectorsAsDirtySMListm_listsm_lists[Free]Compactor*m_pCompactorSMListAddSectorsCompactorStartCompactorDirtyListDirtyListm_dirtyListAddDirtySectorsRemoveDirtySectors 图5.8 扇区管理器结构
为了维护这些信息,SectorMgr使用了一种简单而有效的压缩算法,即Consecutive Item Compression (CIC)。这种算法其实是一种仅仅记录连续数据的结点链数据结构。即每个结点记录连续数据的起始地址和结尾地址,并包含下一结点的指针。
例如,有一串数据,0,1,2,3,4,5,6,7,8,12,13,14,15,16,17,18,
20,21,400,401,402...1082,23576…,则记录这串数据的结构如图5.9所示。
firstNum = 0lastNum = 8firstNum = 12lastNum = 21firstNum = 400lastNum = 1082 图5.9 CIC压缩结构
从图中可看出,只需要三个结点即可描述所需要维护的庞大数据,所以这是一种相当有效的数据结构。而这种数据结构之所以能够很好的工作,是由于FAL层的垃圾块回收器(Compactor)的协作。垃圾块回收器(Compactor)总是将Flash中的块以整块的方式进行回收,即使得块中的扇区以连续的方式得到释放。其示意代码如下:
class SectorMgr { public:
//加入空闲扇区链表
BOOL AddSectorsToList (ListType listType,
SECTOR_ADDR startingSectorAddr, DWORD dwNumSectors);
//去除空闲扇区
BOOL UnmarkSectorsAsFree(SECTOR_ADDR startingPhysicalSectorAddr,
DWORD dwNumSectors);
//获取下一个起始空闲扇区
BOOL GetNextFreeSector(PSECTOR_ADDR pPhysicalSectorAddr, BOOL bCritical); //将扇区标记为脏
BOOL MarkSectorsAsDirty(SECTOR_ADDR startingPhysicalSectorAddr,
DWORD dwNumSectors);
//去除脏扇区
BOOL UnmarkSectorsAsDirty(SECTOR_ADDR startingPhysicalSectorAddr,
DWORD dwNumSectors);
private:
//空闲扇区链表 SMList m_lists[NUM_LIST_TYPES]; //脏扇区表 DirtyList m_dirtyList; //垃圾回收器 Compactor* m_pCompactor; }; (4)、类Compactor
类Compactor,即垃圾回收器,用于将NAND Flash上的块回收加入空闲链表。其内部结构如图5.10所示。
CompactorStartCompactorm_hStartCompactingm_hCompactionCompleteSectorMgrCompactorThreadSectorMgr*m_pSectorMgrGetNextCompactionBlockCompactBlockMappingTable*m_pMapMappingTable 图5.10 垃圾回收器结构
如何选择块则由一定的策略而定,如最少脏页块。如果回收块中有有效数据,则将有效数据复制到新的空闲扇区中,并重新对其进行映射。垃圾回收器的启动时机则由SectorMgr的GetNextFreeSector()函数中决定垃圾回收的时机,这个时机是在Flash中可用的扇区低于一定的阈值或者低于Flash中的脏扇区数的时候。也就是说,当系统企图获得空闲扇区的时候,发现Flash中的空闲扇区低于预期了,则开始垃圾回收来获取更多的空闲扇区。其示意代码如下。 class Compactor { public: //开始进行垃圾块回收 BOOL StartCompactor(DWORD dwPriority, DWORD dwFreeSectorsNeeded, BOOL bCritical); //实际回收动作在此函数完成 DWORD CompactBlock(BLOCK_ID blockID, SECTOR_ADDR treatAsDirtySectorAddr); //根据某种策略获取下一个回收的块 BLOCK_ID GetNextCompactionBlock(BLOCK_ID blockID); //回收块线程,等待开始命令,然后进行动作 DWORD CompactorThread(); private: //通知开始回收的事件 HANDLE m_hStartCompacting; //通知回收完成的事件 HANDLE m_hCompactionComplete; SectorMgr* m_pSectorMgr; MappingTable* m_pMap; }; 3、FMD层(Flash Media Driver)
FMD层是对NAND Flash进行实际的I/O动作的驱动层。该程序与FAL层链接即生成完整的NAND Flash的驱动程序。除此之外,该层程序也可与Bootloader相链接,使得Bootloader能够支持对NAND Flash进行烧写。
1) 数据结构定义 FMDInterface
该结构用于将FMD层的函数接口传给FAL层,FAL层使用这些函数指针对NAND Flash进行实际的操作。 typedef struct _FMDInterface { DWORD cbSize; PFN_INIT pInit; PFN_DEINIT pDeInit; PFN_GETINFO pGetInfo; PFN_GETBLOCKSTATUS pGetBlockStatus; PFN_SETBLOCKSTATUS pSetBlockStatus; PFN_READSECTOR pReadSector; PFN_WRITESECTOR pWriteSector; PFN_ERASEBLOCK pEraseBlock; PFN_POWERUP pPowerUp; PFN_POWERDON pPowerDown; PFN_OEMIOCONTROL pOEMIoControl; } FMDInterface, *PFMDInterface;
结构成员描述如下:
cbSize:该数据结构的大小。
pInit:指向FMD_Init函数的指针。
pDeInit:指向FMD_Deinit函数的指针。 pGetInfo:指向FMD_GetInfo函数的指针。
pGetBlockStatus:指向FMD_GetBlockStatus函数的指针。 pSetBlockStatus:指向FMD_SetBlockStatus函数的指针。 pReadSector:指向FMD_ReadSector函数的指针。 pWriteSector:指向FMD_WriteSector函数的指针。 pEraseBlock:指向FMD_EarseBlock函数的指针。
pPowerUp:指向FMD_PowerUp函数的指针。
pPowerDown:指向FMD_PowerDown函数的指针。
pOEMIoControl:指向FMD_OEMIoControl函数的指针。
在FAL层的DSK_Init()函数(驱动加载时的初始化函数)中,FAL层会调用GetFMDInterface()函数获取FMD层的这些接口函数指针,之后FAL层中对NAND Flash的操作都是通过调用这些函数指针来实现。代码如下所示: DWORD DSK_Init() { PDEVICE pDevice = g_pDevice; … //获取FMD函数指针 GetFMDInterface( ); … } VOID GetFMDInterface( ) { FMD.cbSize = sizeof(FMDInterface); FMD.pInit = FMD_Init; FMD.pDeInit = FMD_Deinit; FMD.pGetInfo = FMD_GetInfo; FMD.pGetBlockStatus = FMD_GetBlockStatus; FMD.pSetBlockStatus = FMD_SetBlockStatus; FMD.pReadSector = FMD_ReadSector; FMD.pWriteSector = FMD_WriteSector; FMD.pEraseBlock = FMD_EraseBlock; FMD.pPowerUp = FMD_PowerUp; FMD.pPowerDown = FMD_PowerDown; }
FlashInfo
该数据结构保存了有关Flash存储器的相关信息。 typedef struct _FlashInfo { FLASH_TYPE flashType; DWORD dwNumBlocks; DWORD dwBytesPerBlock; WORD wSectorsPerBlock; WORD wDataBytesPerSector; }FlashInfo, *PFlashInfo;
结构成员描述如下:
flashType:Flash的类型,NAND或者NOR Flash。 dwNumBlocks:Flash存储器物理块个数。 dwBytesPerBlock:每个块的字节数。 wSectorsPerBlock:每个块的扇区数。
wDataBytesPerSector:每个扇区的字节数。 该数据结构用于FMD层向FAL层返回有关Flash的物理信息。FAL层通过调用FMD层的FMD_GetInfo()函数来获取信息。 SectorInfo
该数据结构保存了有关Flash存储器上每个扇区相关信息。 typedef struct _SectorInfo { DWORD dwReserved1; BYTE bOEMReserved; BYTE bBadBlock; WORD wReserved2; }SectorInfo, *PSectorInfo;
结构成员描述如下: dwReserved1:该部分保留给FAL层使用,FAL层将填写逻辑扇区号。 bOEMReserved:该部分保留给OEM使用,可用于定于读写属性。 bBadBlock:该字节是由Flash芯片坏块标志定义。
wReserved2:该部分保留给FAL层使用,FAL层将填写标志位来防止掉电错误。 FAL层通过FMD_ReadSector、FMD_WriteSector函数来获取或写入扇区信息。
stPXANandFlash(FMD层内部数据结构)
该数据结构用于保存有关操作NAND Flash的上下文信息,该结构是FMD层内部使用的数据结构。 typedef struct _ stPXANandFlash { Volatile GPIO_REGS* v_pGPIORegs; PBYTE pbBassAdd; DWORD dwPagePerBlock; DWORD dwSectorsPerChip; DWORD dwPagesPerChip; DWORD dwBlocksPerChip; DWORD dwDeviceId; FlashInfo Info; } stPXANandFlash, * PstPXANandFlash;
结构成员描述如下:
v_pGPIORegs:GPIO口控制寄存器。
pbBassAdd:PXA255的Static Bank 1地址空间的起始地址,即nCS[1]地址空间。 dwPagePerBlock:Flash每个块的页数。
dwSectorsPerChip:每个Flash存储芯片的扇区数。 dwPagesPerChip:每个Flash存储芯片的页数。 dwBlocksPerChip:每个Flash存储芯片的块数。 dwDeviceId:NAND Flash的厂商ID。
Info:FlashInfo结构的信息,用于返回给FAL层。
2) 函数接口定义
由上面的数据结构可知,FMD层所需要提供的函数接口如下所示。 函数名称 FMD_Init FMD_Deinit FMD_GetInfo FMD_GetBlockStatus FMD_SetBlockStatus FMD_ReadSector 功能描述 初始化Flash,由DSK_Init函数调用。 卸载Flash,由DSK_Init函数调用。 获取Flash信息 获取指定块的状态 设置指定块的状态 读指定的扇区的数据 FMD_WriteSector FMD_EraseBlock FMD_PowerUp FMD_PowerDown FMD_OEMIoControl 将数据写如指定扇区 擦除指定的块 上电操作 掉电操作 可定义自己的IoControl功能。 3) NAND Flash的初始化 NAND Flash的初始化是在FMD_Init函数中实现的。 系统启动时,设备管理器(Device Manager),即device.exe,它将枚举系统注册表,查找所有[HKEY_LOCAL_MACHINE\\Drivers\\BuiltIn]键下的Dll文件名。NAND Flash驱动程序在注册表中的设置如下所示:
[HKEY_LOCAL_MACHINE\\Drivers\\BuiltIn\\NandFls] \"Profile\" = \"NandFls\" ;存储管理器下NAND Flash的注册表位置 \"Prefix\" = \"DSK\" ;设备命名,也即流接口的前缀 \"Dll\" = \"NandFls.dll\" ;加载的动态链接库 \"Index\" = DWORD:1 ;设备命名中的索引,用于区分相同前缀的不同设备 \"Order\" = DWORD:0 ;加载的顺序 \"IClass\"=\"{A4E7EDDA-E575-4252-9D6B-4195D48BB865}\" ;块设备的标识 设备管理器调用ActivateDeviceEx函数,此函数根据驱动名称加载动态连接库,ActivateDeviceEx函数在内部调用NAND Flash驱动程序的初始化函数,该初始化函数名称由\"Prefix\"键值决定[19],在这里初始化函数为DSK_Init。DSK_Init在函数随即调用FMD_Init函数对NAND Flash进行初时化。
4) NAND Flash的I/O操作的实现
在NAND Flash被纳入存储管理器中后(纳入过程参见前面3.3.3节“存储管理器”),应用程序便可以通过WIN32 API进行文件操作了。一次完整的IO操作需经过存储管理器的各个层。首先到达过滤层(如果有的话),之后到达的是文件系统层,对参数进行格式化,将文件名转换为块设备驱动程序可以识别的设备地址,然后经过分区驱动交由NAND Flash驱动程序进行IO操作,最后通过调用NAND Flash的DSK_IOControl函数实现实际的读写操作。如图5.12所示。
应用程序Win32 API文件系统FATUDFS分区驱动程序分区驱动程序块设备驱动程序硬件
图 5.12 NAND Flashde I/O操作调用过程
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- baomayou.com 版权所有 赣ICP备2024042794号-6
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务