网上查找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、等待硬盘控制器就绪(可用轮询方式),进行下一步
原文地址:http://blog.csdn.net/hanjianqiao/article/details/37743415