Chinaunix首页 | 论坛 | 博客
  • 博客访问: 477306
  • 博文数量: 56
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1957
  • 用 户 组: 普通用户
  • 注册时间: 2013-06-07 23:02
文章分类

全部博文(56)

文章存档

2016年(1)

2014年(7)

2013年(48)

分类: 嵌入式

2013-08-05 13:05:13

7.2.6 函数库sysTffs

前面两节分别描述了Flsocket结构和函数库Flsocket,这两个函数库都是用于对socket的管理,不依赖于具体的socket控制芯片,本节将分析函数库sysTffs,它实现了结构FLsocket中的各个函数接口。

sysTffs函数库支持两种形式,如果没有定义INCLUDE_PCMCIA系统直接给出了一种与硬件相关的一个系列接口函数,填充FLSocket结构的正式这一系列与硬件相关的函数(sysTffs.c文件库是以82365SL型号的SOCKET控制器为例实现的),其缺点是不具备通用性,如果计算机系统安装的是其他socket芯片,则要对sysTffs.c库进行修改。

如果定义了INCLUDE_PCMCIA,则填充FLSocket结构的一系列函数则是利用了PCMCIA_CTRL结构通用接口来实现的,因此对sysTffs.c库来说则完全是一个通用的库,由pcic.c文件负责完成通用结构PCMCIA_CTRL的具体实现,如果计算机系统的socket芯片要重写驱动的话,直接对底层的硬件实现文件pcic.c修改就可以了,它的有点是层次清晰移植方便。本节将从通用性出发,仅对定义了INCLUDE_PCMCIA的情况进行分析。如图7.6

7.6 函数库sysTffs的层次结构

从图7.6中可以看出,sysTffs文件库为FlSocket结构实现了一组不依赖于计算机硬件的函数接口,同时为了保证sysTffs.c库的通用性,sysTffs.c库并没有直接控制socket控制硬件,而是调用了一个通用的接口:PCMCIA_CTRL结构。PCMCIA_CTRL结构为上层及函数库sysTffs提供了一组函数接口,而这些函数接口则是由一个依赖于计算机硬件函数库pici.c来实现的。

对一般的计算机系统来说,socket的实现有两类,一类是常见的socket插槽,可以根据需要对flash卡进行插拔(称之为PcCard);第二类则是固化在计算机硬件电路板上去的,也就是始终存在于计算机系统内的(DOCdisk on chip)。函数库sysTffs实现了这两类socket的操作。如图7.7所示。

图7.7 函数库sysTffs实现了这两类socket的操作

下面具体分析sysTffs文件库的作用。

1. LOCAL void sysTffsInit (void)

这个函数主要完成DOC和两个socket的注册。注意这里一个socket控制芯片可以控制两个socket

这里还有一个需要注意的问题,就是计算sysTffsMsecLoopCount的数值,这个数值是一毫秒内执行KILL_TIME_FUNC函数的次数,这个函数可以用于对一个tick进行细分。它将一个tick细分为ix个更细的时间段。这个时间段可以提供精确的数据延时作用。

2. LOCAL FLStatus docRegister (void)

要理解这个函数首先必须弄清楚一个概念,就是函数中出现的volpVol变量,TrueFFS文件。

在文件flbase.h中有这么个宏定义:#define vol   (*pVol)。因此在函数docRegister中定义的FLSocket vol经过预处理后则变为FLSocket (*pVol),这是这个pVol则变成了一个指向FLSocket结构的指针,而vol则指的是FLSocket结构指针pVol指向的FLSocket结构变量。另一方面,在文件Flsocket.c文件中有如下定义:

unsigned noOfDrives = 0; /* No. of drives actually registered */

static FLSocket vols[DRIVES];

而在函数docRegister中有这么一句:

pVol = flSocketOf (noOfDrives),其中函数flSocketOf的实现是这样的:

return &vols[volNo]

这就说明了docRegister函数的注册过程。首先在文件FLSocket.c中定义了FLSocket结构数组,sysTffs.c文件中的函数docRegister则是对数组中的一个元素即其中一个FLSocket结构变量进行填写,这样该DOC的相关驱动信息就全部保存在FLSocket结构数组中的相关元素了。

另一方面,docRegister函数还要对文件tffsConfig.c中定义的变量LOCAL char * tffsSocket[DRIVES]进行填写(记录socket的名字),最终完成注册过程。

docRegister函数填写了FLSocket结构数组中下标为noOfDrives的元素,因此在填写完毕后要将noOfDrives++以方便其他函数的注册。

3. LOCAL unsigned docWindowBaseAddress

返回window在系统中的基地址。

函数WindowBaseAddress扫描系统中从DOC2_SCAN_ADRS_0DOC2_SCAN_ADRS_1(含)的地址最后识别出DiskOnChip 2000的内存窗口。

4. LOCAL FLBoolean docCardDetected(FLSocket vol)

因为DOC是直接焊接在计算机板上的,因此总是存在的,其返回值总为TRUE

5. LOCAL void docVccOn(FLSocket vol)

只要计算机启动后DOC始终处于加电的状态,因此这个函数不需要任何操作。

6. LOCAL void docVccOff(FLSocket vol)

只要计算机启动后DOC始终处于加电的状态,因此这个函数不需要任何操作,当然任何VccOff操作都是无效的。

7. LOCAL FLStatus docVppOn(FLSocket vol)

DOC来说Vpp总是处于有效的状态,因此这个函数不需要任何操作。

8. LOCAL void docVppOff(FLSocket vol)

DOC来说Vpp总是处于有效的状态,因此这个函数不需要任何操作,当然任何VppOff操作都是无效的。

9. LOCAL FLStatus docInitSocket(FLSocket vol)

DOC来说socket总是处于就绪的状态,不需要特别的初始化操作。

10. LOCAL void docSetWindow

DOC来说socket不需要特别的操作。

11. LOCAL void docSetMappingContext

DOC来说socket不需要特别的操作。

12. LOCAL FLBoolean docGetAndClearCardChangeIndicator

DOC来说socket的状态是不变的。

13. LOCAL FLBoolean docWriteProtected

DOC来说总返回FALSE。

14. LOCAL void docFreeSocket

DOC来说socket不需要特别的操作。

15. LOCAL FLStatus pcRegister

(

int socketNo,

unsigned int baseAddress

)

这个是socket的注册函数,和DOCRegister函数类似。它可以根据参数完成socket0或者socket1的注册。

socketNo0或者1表明socket0或者socket1,注意不是Flsocket数组结构的下标。

baseAddress:该参数表明了window基地址,如果该参数为0,则选择默认的基地址PC_BASE_ADRS_0和PC_BASE_ADRS_1;否则选择参数中指定的基地址。注意vol.window.baseAddress的数值是基地址左移12bit之后的结果。而baseAddressPC_BASE_ADRS_0PC_BASE_ADRS_1在转化为vol.window.baseAddress时都需要左移12bit

类似于docRegister函数,它也注册了FLSocket结构变量中相关元素。最后在tffsSocket[noOfDrives]中注册了驱动的名字:PC_SOCKET_NAME_0或者PC_SOCKET_NAME_1

数组变量char pcDriveNo[2]中分别记录了这两个socketFlsocket结构数组中的下标。

16. void flDelayMsecs

    (

    unsigned milliseconds

)

这个函数主要控制精确计时。

sysTffsMsecLoopCount是前面sysTffsInit函数中计算的1毫秒内循环执行KILL_TIME_FUNC的次数。这里需要特别说明的是tickGet ()这个语句,表面上看这个语句没什么作用,实际上是对前面计算sysTffsMsecLoopCount的一个补偿,因为在计算sysTffsMsecLoopCount的时候每个循环做了四件事情:

oldTick == tickGet()这个语句包含两部分,判断和tickGet()调用

iy = KILL_TIME_FUNC

ix++;

因此sysTffsMsecLoopCount实际是1毫秒内做这四件事情的次数。因此为了确保每个执行周期执行的时间都一样,那么他们每个周期所做的这些工作消耗的时间也必须都一样,为了确保函数中flDelayMsecs的第二级循环for (ix = 0; ix < sysTffsMsecLoopCount; ix++)执行同样的时间,在函数flDelayMsecs中也必须增加tickGet()这个函数。这样他也做了4件事:

ix < sysTffsMsecLoopCount判断

ix++

tickGet ()

iy = KILL_TIME_FUNC

这样将使得时间延时更加精确。

不过这里还是有个bug,就是

for (ix = 0; ix < milliseconds; ix++)

        for (ix = 0; ix < sysTffsMsecLoopCount; ix++)

这两个ix循环将引起变量混乱。而且开始定义的iz并没有使用。因此这里需要把其中一个循环变量改为iz

for (iz = 0; iz < milliseconds; iz++)

        for (ix = 0; ix < sysTffsMsecLoopCount; ix++)

17. LOCAL FLBoolean pcCardDetected

    (

    FLSocket vol

    )

pcmciaCtrl.card[vol.serialNo].detected变量保存的是card的存在状态,TRUE表明卡存在。该函数通过访问该变量了解卡的存在情况。

18. LOCAL void pcVccOn

    (

    FLSocket vol

    )

通过设置硬件寄存器的加电控制位通用接口给Vcc加电。

首先要通过(*pChip->flagGet) (vol.serialNo)函数获取当前socket 的状态,然后将其中有关Vcc的状态位进行修改(PC_VCC_5VPC_VCC_3V状态,同时置PC_PWR_AUTO为发现card时自动供电,但这里是供了5V的电,写驱动的时候需要注意这个电压,注意和pcic.c函数的衔接,如果pcic.c需要加的是3V,那么就要做一些修改),然后调用(*pChip->flagSet) (vol.serialNo, flag)设置状态。

供电需要一定的时间,因此退出前需要等待一段之间,直至发现供电状态正常(PC_READY | PC_POWERON)。

19. LOCAL void pcVccOff

    (

    FLSocket vol

    )

pcVccOn的逆过程,因为这里是为了关掉socket的电源,因此其他的设置都已经无关紧要了,因此这里可以采用最简单的处理方法,不必先通过(*pChip->flagGet) (vol.serialNo)函数获取当前socket 的状态,直接置PC_VCC_5VPC_VCC_3V控制位为0即可,当然为了保证下次的时候能够自动加电,还要设置PC_PWR_AUTO状态。

20. LOCAL FLStatus pcVppOn

    (

    FLSocket vol

    )

类似于pcVccOn,只是将供电改为PC_VPP_12V(注意这里还有个PC_VPP_5V,写驱动的时候可根据情况修改)。

21. LOCAL void pcVppOff

    (

    FLSocket vol

    )

类似于pcVccOff,只是清除了flagPC_VPP_12V位,并增加PC_VPP_5V位。也就是说把Vpp供电从12V修改位5V

22. LOCAL FLStatus pcInitSocket

    (

    FLSocket vol

    )

调用函数(*pChip->flagSet) (vol.serialNo, flag)设置flag= (PC_PWR_AUTO | PC_VCC_5V)进行5V自动供电。

注意这里只是socket的初始化,此时可能还没有插入flash卡,因此只需要进行简单的设置即可,进一步的设置要在发现flash之后进行

23. LOCAL void pcSetWindow

    (

    FLSocket vol

    )

设置当前window的硬件属性:Base addresssizespeed and bus width.。需要设置的参数保存在vol.window结构变量中。如果不能将windowsize设置成为参数vol.window.size要求的大小,则window就要设置成为一个更大的数值。

在任何情况下,vol.window.size都要在退出的时候保存window的实际大小。

memwin.window = window No,对一个socket来说,通常支持多个window,但是同时时刻只有一个window是起作用的,可以设置使得任何一个window起作用,TFFS则默认使用了1#window,也及即其PC_WINDOW定义为1memwin.flags保存的是总线宽度,8bit16bit,设置bus width的同时还要设置MAP_ACTIVE,即设置当前window生效。

memwin.extraws = PC_EXTRAWS,TFFS需要的等待状态。参见硬件说明。

memwin.start = vol.window.baseAddress << 12;设置窗口的开始地址,注意输入参数vol.window.baseAddress是以4K为单位的,而结构PCMCIA_MEMWIN中的start表示window开始的完整地址,低12bit0

memwin.stop = (vol.window.baseAddress << 12) + vol.window.size - 1;设置窗口的结束地址,窗口的最后一个有效字节的地址。

memwin.cardstart = 0; 这个将在函数pcSetMappingContext()中进行设置

最后调用函数(*pChip->memwinSet) (vol.serialNo, &memwin)执行设置。

注意这里的setWindow仅仅是设置了memWindow,也就是说对flash卡,通常并不使用IO的访问方式,而是mem的访问方式。

24. LOCAL void pcSetMappingContext

    (

    FLSocket vol,

    unsigned page

    )

实现过程类似于pcSetWindow,唯一不同的地方在于memwin.cardstart的设置。我们知道,socket的地址映射是将计算机系统的一段地址映射到flash卡的一段地址,对于计算机系统来说有个startstop,只有地址落在start≤sysAddr≤stop之间的地址,socket控制芯片才会响应。如果响应的话,对于一个系统地址,socket芯片就必须知道映射到flash卡的哪一段地址中去,而memwin.cardstart的作用呢,就是告诉socket控制芯片,计算机系统start≤sysAddr≤stop的这一段地址要映射到卡上地址为memwin.cardstart开始的这段内存中,当然了,其在卡内的size和计算机系统的window size也是相同的。有关更详细的memwin.cardstart的含义将在后面pcic.c文件中详细说明。

这里需要注意的是:输入参数vol.window.baseAddress指的是页地址。其单位以4k为单位,而函数(*pChip->memwinSet) 的输入参数中则指的是窗口第一个地址和最后一个地址。

25. LOCAL FLBoolean pcGetAndClearCardChangeIndicator

    (

    FLSocket vol

    )

从函数名字上看应该是获取socket中卡的存在情况是否变化(该状态保存在变量pcmciaCtrl.cardStatus[vol.SerialNo]PC_DETECT),如果有卡返回TRUE,否则返回FALSE

26. LOCAL FLBoolean pcWriteProtected

    (

    FLSocket vol

    )

通过调用函数(*pcmciaCtrl.status) (vol.serialNo)获取其中的PC_WRPROT状态位。

阅读(5189) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~