在调试好sd卡的底层驱动后,需要加载文件系统,这样才能方便的进行文件读写。而sd卡驱动要调试到什么程度呢,首先sd卡的初始化要没问题,裸读和裸写没有问题,例如读写一个2048字节的数据读到的数据和写入的数据是一样的,此时就算成功了。
在vxworks中,每个设备都有一个节点,通过open,close,read,write这几个函数来操作设备节点。如何建立节点,这就是本日志的内容。文件系统和底层的sd卡驱动是独立的,它们有一些量作为中间纽带,也就是说,在sd卡驱动调试好之后需要向文件系统传递一些东西,有sd卡的读写接口函数。sd卡总的块的数量,以及每块的大小值(一般为512字节)
pDrvCtrl->card.card = (struct mmc *)mmcdev;
nblock = mmcdev->block_dev.lba;
pDrvCtrl->card.blockNumber = nblock;
pDrvCtrl->card.blockSize = mmcdev->block_dev.blksz;
pDrvCtrl->cardIns = TRUE;
pDrvCtrl->card.cardinsert = TRUE;
pDrvCtrl->card.blkRd = (FUNCPTR)mmcdev->block_dev.block_read;
pDrvCtrl->card.blkWt = (FUNCPTR)mmcdev->block_dev.block_write;
向这个pDrvCtrl结构体放入读写函数的地址,上层就通过这两个函数来对底层进行读写,同时告诉系统这个sd卡的大小为nblock块。在vxworks中,系统一般提供vxbus这种设备驱动模块,sd卡也就是注册到这当中作为一个模块。
xbd->bioMutex = semMCreate (SEM_Q_PRIORITY | SEM_INVERSION_SAFE);
if (xbd->bioMutex == NULL)
goto error;
xbd->bioReady = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
if (xbd->bioReady == NULL)
goto error;
xbd->xbdSemId = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
if (xbd->xbdSemId == NULL)
goto error;
if (!xbd->directModeFlag)
{
/* spawn service task */
xbd->svcTaskId = taskSpawn (SDMMC_XBD_SVC_TASK_NAME, SDMMC_XBD_SVC_TASK_PRI,
0, SDMMC_XBD_SVC_TASK_STACK,
(FUNCPTR)sdMmcXbdBioSvcTask, (int)xbd,
0, 0, 0, 0, 0, 0, 0, 0, 0);
if (xbd->svcTaskId == ERROR)
goto error;
}
/* register new event handler for base device synchronization */
rc = erfHandlerRegister (xbdEventCategory, xbdEventInstantiated,
sdMmcXbdCreateSyncHandler, xbd, 0);
if (rc != OK)
goto error;
/* attach XBD */
if (devName == NULL)
{
logMsg("please point the name for sd\n",0,0,0,0,0,0);
goto error;
}
else
{
strcpy ((char *)xbd->devName, devName);
}
rc = xbdAttach (&xbd->xbd, &sdMmcXbdFuncs, xbd->devName,xbd->blockSize,
xbd->blockNumber, &dev);
if (rc != OK)
goto error;
/* raise an insert event */
rc = erfEventRaise (xbdEventCategory, xbdEventPrimaryInsert, ERF_ASYNC_PROC,
(void *)dev, NULL);
if (rc != OK)
goto error;
xbd->xbdInserted = 1;
/* wait for FSM to send xbdEventInstantiated event */
rc = semTake (xbd->xbdSemId, WAIT_FOREVER);
该代码首先创建了3个系统信号量,然后开始注册sd卡设备。其中最重要的是xbdAttach函数,该函数以一个xbd结构体作为支点,放入几个参数,
xbd->devName,xbd->blockSize, xbd->blockNumber
分别为xbd的名字(该名字就是会出现到节点中的名字),xbd块大小,xbd块的数量。还有一个就是
sdMmcXbdFuncs
这是一个结构体地址
LOCAL struct xbd_funcs sdMmcXbdFuncs =
{
sdMmcXbdIoctl,
sdMmcXbdStrategy,
sdMmcXbdDump,
};
该结构注册了3个本地函数,其中最重要的是
sdMmcXbdIoctl
系统在节点操作时,会调用该函数,而在该函数中又会调用一个真正操作的子函数
LOCAL void sdMmcXbdExecBio
(
SDMMC_XBD_DEV * xbd,
struct bio * bio
)
{
unsigned blkSize;
sector_t blkNum;
unsigned nblocks;
unsigned size;
STATUS rc = ERROR;
blkSize = xbd->xbd.xbd_blocksize;
blkNum = xbd->xbd.xbd_nblocks;
/* check that all of this transaction fits in the disk */
size = bio->bio_bcount;
nblocks = size / blkSize;
/* if our starting block number is bad, done with error */
if (bio->bio_blkno >= blkNum)
{
bio->bio_bcount = 0;
bio_done (bio, ENOSPC);
return;
}
/* if we overrun the end of the disk, truncate the block number */
if (bio->bio_blkno + nblocks > blkNum)
nblocks = blkNum - bio->bio_blkno;
/* calculate the real size */
size = nblocks * blkSize;
bio->bio_bcount = size;
/* if we have less than 1 block, done it */
if (nblocks == 0)
{
bio->bio_bcount = 0;
bio_done (bio, 0);
return;
}
if (bio->bio_flags & BIO_READ)
{
rc = xbd->blkRd (xbd->card, bio->bio_blkno, nblocks, bio->bio_data);
}
else if (bio->bio_flags & BIO_WRITE)
{
rc = xbd->blkWt (xbd->card, bio->bio_blkno, nblocks, bio->bio_data);
}
if (rc == OK)
{
bio_done (bio, 0);
}
else
{
bio->bio_bcount = 0;
if (errno == S_ioLib_DISK_NOT_PRESENT)
bio_done (bio, ENXIO);
else
bio_done (bio, errno);
}
SDMMC_XBD_DBG (SDMMC_XBD_DBG_RW, "END blkNo (%d) numBlks (%d) buf (0x%08x)\n",
(int)bio->bio_blkno, (int)nblocks, (int)bio->bio_data,
0, 0, 0);
}
在代码中
rc = xbd->blkRd (xbd->card, bio->bio_blkno, nblocks, bio->bio_data);
rc = xbd->blkWt (xbd->card, bio->bio_blkno, nblocks, bio->bio_data);
这两句其实就是调用之前注册的读写接口函数。
还要注意的是xbdAttach函数一般是有操作顺序的,要erfHandlerRegister,xbdAttach,erfEventRaise这三个函数一起连用来完成。该代码执行时,系统会读写sd卡的相应块区域,读写的数据这是由文件系统决定的。
执行完以上函数后,在系统shell中就创建了一个节点,比如代码中给的/sd名字,在shell中输入devs命令,就会出现/sd:0的节点,这时用cd命令进入该节点,同时输入ls命令可以查看该目录的结构。在第一次使用时可以使用dosfsDiskFormat函数对该块设备进行格式化;同时应用程序中就可以使用open和read,write函数对该节点进行读写。