网上查找Linux硬盘驱动的话能找到好多相关信息,但是具体代码都没有。经过一番努力,实现了硬盘读写的代码,特来分享一下。可能因为硬盘控制器更新的原因,一些新式的笔记本上使用这些代码会失败。不过在VmWare上是可以用的。这个项目的git地址:git://code.csdn.net/hanjianqiao/dingus.git。在commit为“Add harddisk”的版本直接编译出的镜像便是下面这个。
上图中密密麻麻的16进制数据是从硬盘第一个扇区取出来的,这些数据是在之前就已经写入了硬盘的。最后的55 AA是大家都熟悉的可启动扇区标记。
本来想用一张图来表达硬盘驱动的过程,可是实在没想出来这个图怎么画比较好,就用纯文字了:
写硬盘:
1、向硬盘控制端口输出控制数据,然后发送写命令0x30
2、等待硬盘控制器就绪(可用轮询方式),进行下一步
3、依次向数据端口0x1f0写256字(双字节)数据
4、一轮询方式后中断方式等待硬盘写数据完成
读硬盘:
1、等待硬盘控制器就绪(可用轮询方式),进行下一步
2、向硬盘控制端口输出控制数据,然后发送写命令0x20
3、等待硬盘中断产生
4、硬盘中断产生时,连续从数据端口读256个字。
这里的硬盘控制器就像是售货员一样,当你买东西的时候,你要先告诉她要什么样的商品。然后当售货员取道你想要的东西时就会触发一个中断:这是你要的商品,然后你就能得到你想要的东西。当你退货(相当于写扇区)的时候,你要给售货员说你要退货,然后把东西交给他(把数据写入缓冲),然后售货员把退回的货物放到存储的地方之后触发一个中断,你就知道货已成功退掉了。
下面上代码:
void init_harddisk(){ unsigned char cw; struct GATE_DESCRIPTOR *idt = (struct GATE_DESCRIPTOR *) ADR_IDT; cw = io_in8(PIC1_IMR); cw = cw & 0xbf; io_out8(PIC1_IMR, cw); set_gatedesc(idt + 0x2e, (int) asm_inthandler2e, 2 * 8, AR_INTGATE32); return; }
这段代码和前面的鼠标驱动,键盘驱动的初始化差不多,都是使能中断并配置中断响应函数。
static int controller_ready (void) { int retries = 10000; while (--retries && (io_in8(0x1f7) & 0xc0) != 0x40); return (retries); // 返回等待循环的次数。 }
这个相当于延时函数。
接着就是主要的函数了:
void readHardDisk(unsigned char nsector, unsigned char sector, unsigned int cyl, unsigned char current){ struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO; int i; i = controller_ready(); if(i == 0){ putfonts8_asc(binfo->vram, binfo->scrnx, 400, 400, COL8_00FF00, "Read Busy"); putHex(binfo->vram, binfo->scrnx, 480, 400, COL8_00FF00, io_in8(0x1f7)); putHex(binfo->vram, binfo->scrnx, 520, 400, COL8_00FF00, io_in8(0x1f1)); return; } io_out8(0x3f6, 0x00); io_out8(0x1f2, nsector); io_out8(0x1f3, sector); io_out8(0x1f4, cyl); io_out8(0x1f5, cyl>>8); io_out8(0x1f6, current | 0xa0); io_out8(0x1f7, 0x20); } void writeHardDisk(unsigned char nsector, unsigned char sector, unsigned int cyl, unsigned char current, short* buf){ struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO; int i; i = controller_ready(); if(i == 0){ putfonts8_asc(binfo->vram, binfo->scrnx, 400, 400, COL8_00FF00, "Busy"); return; } io_out8(0x1f2, nsector); io_out8(0x1f3, sector); io_out8(0x1f4, cyl); io_out8(0x1f5, cyl>>8); io_out8(0x1f6, current | 0xa0); io_out8(0x1f7, 0x30); i = controller_ready(); if(i == 0){ putfonts8_asc(binfo->vram, binfo->scrnx, 400, 400, COL8_00FF00, "Busy in writing"); return; } for(i = 0; i < 256; i++){ io_out16(0x1f0, buf[i]); } } void inthandler2e(int *esp){ struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO; int i; short hd_data[256]; for(i = 0; i < 256; i++){ hd_data[i] = io_in16(0x1f0); } //*((unsigned char *)hd_data) = io_in8(0x1f7); putHexs(binfo->vram, binfo->scrnx, 0, 0, COL8_00FF00, (unsigned char *)hd_data, 512); io_out8(PIC1_OCW2, 0x66); io_out8(PIC0_OCW2, 0x62); return; }
可能看到0x1f0大家会莫名其妙,下面就是对这些端口的介绍。想查看更详细的说名可以参照赵炯老师写的《Linux内核完全注释》这本书。
1、向硬盘控制端口输出控制数据,然后发送写命令0x30
2、等待硬盘控制器就绪(可用轮询方式),进行下一步
Dingus 硬盘驱动(简)