首页 > 代码库 > Dingus 硬盘驱动(简)

Dingus 硬盘驱动(简)

网上查找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、等待硬盘控制器就绪(可用轮询方式),进行下一步