块设备读写和控制
在初始化块设备数据结构的时候,有几个回调函数需要初始化:
pdev->BLKD_pfuncBlkRd = vdisk_read; pdev->BLKD_pfuncBlkWrt = vdisk_write; pdev->BLKD_pfuncBlkIoctl = vdisk_ioctl; pdev->BLKD_pfuncBlkReset = vdisk_reset; pdev->BLKD_pfuncBlkStatusChk = vdisk_status_chk;
其中的reset和status_chk这两个函数在此驱动中是空函数,但是这两个函数一定要有,不然块设备创建和使用会有问题:
static int vdisk_reset (PLW_BLK_DEV dev) { return (ERROR_NONE); } static int vdisk_status_chk (PLW_BLK_DEV dev) { return (ERROR_NONE); }
块设备最重要的两个操作就和读和写,可以说这两个函数是整个驱动中的核心操作函数,下面我们以读函数为例,看下读函数的原型:
int vdisk_read (PLW_BLK_DEV dev, void *buf, int blk_start, int blk_num);
- dev表示块设备指针,标明操作的是哪一个块设备。
- buf表示上层要传输数据的缓冲区地址。
- blk_start表示要传输的数据的起始扇区号。
- blk_num表示要传输的扇区个数。
我们知道数据传输三要素:源位置、目标位置、数据大小。对于读函数而言,源位置就是buf地址,目标位置就是blk_start,数据大小就是blk_num。
根据起始扇区号和单个扇区大小就可以计算出目标位置在虚拟磁盘中的位置:
static int vdisk_read (PLW_BLK_DEV dev, void *buf, int blk_start, int blk_num) { //计算要读取的虚拟磁盘起始位置 void *addr = vdisk_start + blk_start * dev->BLKD_ulBytesPerSector; //读取虚拟磁盘数据 memcpy(buf, addr, blk_num * dev->BLKD_ulBytesPerSector); return (ERROR_NONE); }
由于是虚拟磁盘,数据传输就是一个简单的memcpy,在实际的磁盘驱动中一般就是发起DMA传输,然后程序阻塞等待中断完成传输来唤醒。
写函数的参数和读函数一样,只不过源位置变成了blk_start,目标位置变为了buf:
static int vdisk_write (PLW_BLK_DEV dev, void *buf, int blk_start, int blk_num) { //计算要读取的虚拟磁盘起始位置 void *addr = vdisk_start + blk_start * dev->BLKD_ulBytesPerSector; //写入虚拟磁盘数据 memcpy(addr, buf, blk_num * dev->BLKD_ulBytesPerSector); return (ERROR_NONE); }
除了读写函数之外,块设备还需要实现一个ioctl函数,用来处理各种文件系统相关的命令,但是在本驱动中,我们只需要实现获取扇区大小和扇区总数这两个命令:
static int vdisk_ioctl (PLW_BLK_DEV dev, int cmd, long arg) { switch (cmd) { /* * 必须要支持的命令 */ case FIOSYNC: case FIODATASYNC: case FIOSYNCMETA: case FIOFLUSH: /* 将缓存写入磁盘 */ case FIOUNMOUNT: /* 卸载卷 */ case FIODISKINIT: /* 初始化磁盘 */ case FIODISKCHANGE: /* 磁盘媒质发生变化 */ break; case FIOTRIM: /* AHCI_TRIM_EN */ break; /* * 低级格式化 */ case FIODISKFORMAT: /* 格式化卷 */ return (PX_ERROR); /* 不支持低级格式化 */ /* * FatFs 扩展命令 */ case LW_BLKD_CTRL_POWER: case LW_BLKD_CTRL_LOCK: case LW_BLKD_CTRL_EJECT: break; case LW_BLKD_GET_SECSIZE: case LW_BLKD_GET_BLKSIZE: *((LONG *)arg) = (LONG)dev->BLKD_ulBytesPerSector; break; case LW_BLKD_GET_SECNUM: *((ULONG *)arg) = (ULONG)dev->BLKD_ulNSector; break; case FIOWTIMEOUT: case FIORTIMEOUT: break; default: _ErrorHandle(ENOSYS); return (PX_ERROR); } return (ERROR_NONE); }
评论