--------------------------------------------------------
完善硬盘驱动程序
--------------------------------------------------------
上节我们的驱动程序只有一个简单的 identify 功能(基于的是 CHS 寻址),这节我们完善驱动程序,并且基于 LBA 寻址!
·枚举
#define CNT u.m3.m3i2
#define REQUEST u.m3.m3i2
#define PROC_NR u.m3.m3i3
#define DEVICE u.m3.m3i4
#define POSITION u.m3.m3l1
#define BUF u.m3.m3p2
—— POSITION 和 REQUEST 不同时使用!
· hd_ioctl 函数
PRIVATE void hd_ioctl(MESSAGE *p)
{
int device = p->DEVICE;
int drive = DRV_OF_DEV(device);
struct hd_info *hdi = &hd_info[drive];
if (p->REQUEST == DIOCTL_GET_GEO)
{
void *dst = va2la(p->PROC_NR, p->BUF);
void *src = va2la(TASK_HD,
device < MAX_PRIM ? &hdi->primary[device] : &hdi->logical[(device - MINOR_hd1a) % NR_SUB_PER_DRIVE]);
phys_copy(dst, src, sizeof(struct part_info));
}
else
assert(0);
}
——当请求者发送 p->type 等于 DEV_IOCTL 的时候,还要在 DEV_IOCTL 服务里进一步指明具体的服务(由 p->REQUEST 指明)!
·hd_rdwt
PRIVATE void hd_rdwt(MESSAGE *p)
{
int drive = DRV_OF_DEV(p->DEVICE);
u64 pos = p->POSITION;
assert((pos >> SECTOR_SIZE_SHIFT) < (1 << 31));
/**
* We only allow to R/W from a SECTOR boundary:
*/
assert((pos & 0x1FF) == 0);
// p->POSITION 求得扇区号
u32 sect_nr = (u32)(pos >> SECTOR_SIZE_SHIFT); /* pos / SECTOR_SIZE */
// 第几个逻辑扇区,这里是第 16 个
int logidx = (p->DEVICE - MINOR_hd1a) % NR_SUB_PER_DRIVE;
// 根据请求的扇区位于哪一个分区,加等于这个分区的起始 LBA
sect_nr += p->DEVICE < MAX_PRIM ? hd_info[drive].primary[p->DEVICE].base : hd_info[drive].logical[logidx].base;
struct hd_cmd cmd;
cmd.features = 0;
// 读取几个扇区
cmd.count = (p->CNT + SECTOR_SIZE - 1) / SECTOR_SIZE;
cmd.lba_low = sect_nr & 0xFF;
cmd.lba_mid = (sect_nr >> 8) & 0xFF;
cmd.lba_high = (sect_nr >> 16) & 0xFF;
cmd.device = MAKE_DEVICE_REG(1, drive, (sect_nr >> 24) & 0xF);
// 读/写 的行为在这里决定
cmd.command = (p->type == DEV_READ) ? ATA_READ : ATA_WRITE;
hd_cmd_out(&cmd);
int bytes_left = p->CNT;
// 请求者的缓冲区
void *la = (void *)va2la(p->PROC_NR, p->BUF);
while (bytes_left)
{
int bytes = min(SECTOR_SIZE, bytes_left);
if (p->type == DEV_READ)
{
interrupt_wait();
port_read(REG_DATA, hdbuf, SECTOR_SIZE);
// 这里 p->BUF 的地址是相对于请求者的段基址的,所以需要求出物理地址
// 但 hdbuf 并不需要!还有,这里可以直接读到 la 中,不需要额外拷贝!
phys_copy(la, (void *)va2la(TASK_HD, hdbuf), bytes);
}
else
{
if (!waitfor(STATUS_DRQ, STATUS_DRQ, HD_TIMEOUT))
panic("hd writing error.");
port_write(REG_DATA, la, bytes);
interrupt_wait();
}
bytes_left -= SECTOR_SIZE;
la += SECTOR_SIZE;
}
}
1,读和写都是 512 字节对齐的
2,因为满足 512 字节对齐的原因,所以在索取的时候和索取到了使用之前都要做处理!
·hd_close 函数
PRIVATE void hd_close(int device)
{
int drive = DRV_OF_DEV(device);
assert(drive == 0); /* only one drive */
hd_info[drive].open_cnt--;
}
·task_fs 函数
PUBLIC void task_fs()
{
printl("Task FS begins.\n");
/* open the device: hard disk */
MESSAGE driver_msg;
driver_msg.type = DEV_OPEN;
driver_msg.DEVICE = MINOR(ROOT_DEV);
assert(dd_map[MAJOR(ROOT_DEV)].driver_nr != INVALID_DRIVER);
send_recv(BOTH, dd_map[MAJOR(ROOT_DEV)].driver_nr, &driver_msg);
struct part_info _tempBuf;
driver_msg.type = DEV_IOCTL;
driver_msg.DEVICE = MINOR(ROOT_DEV);
driver_msg.REQUEST = DIOCTL_GET_GEO;
driver_msg.BUF = (void *)&_tempBuf;
send_recv(BOTH, dd_map[MAJOR(ROOT_DEV)].driver_nr, &driver_msg);
printf("Tinix's partition's Base address is [ 0X%d ]\n",_tempBuf.base);
printf("Tinix's partition's Size is [ 0x%d ]\n",_tempBuf.size);
spin("FS");
}
运行:
——有了 read/write 之后我们的驱动程序就算可以用了,详细驱动程序的建立过程,可以体会到微内核的魅力!