do not edit — generated by btf.
git.druid.rocksindexdruid520kaboomsrc/drivers/ata.nsc

src/drivers/ata.nsc


/*
 * ata/ide pio driver, primary bus, master drive, lba28. polling only
 * (no dma, no irq-driven i/o) -- matches tape-kernel's own approach,
 * which this is a clean-room rewrite of, not a port.
 *
 * a 512-byte sector is 64 8-byte words. reads/writes go through the
 * same packed-word technique as vga: *p is always a full 8-byte
 * load/store, no byte/halfword deref exists, so four consecutive
 * 16-bit port reads get packed into one u64 before a single *ptr
 * store (and symmetrically unpacked on write), rather than one store
 * per port word.
 */
 
void outb(u16 port, u8 val);
u8 inb(u16 port);
void outw(u16 port, u16 val);
u16 inw(u16 port);
 
/* status register bits (port 0x1f7 on read) */
u8
ata_status(void)
{
	return inb((u16)0x1f7);
}
 
i32
ata_wait_not_busy(void)
{
	i32 tries;
	tries = 100000;
	while(tries > 0)
	{
		if((ata_status() & (u8)0x80) == (u8)0)
		{
			return 1;
		}
		tries = tries - 1;
	}
	return 0; /* timed out -- no drive, or it's wedged */
}
 
i32
ata_wait_drq(void)
{
	i32 tries;
	u8 st;
	tries = 100000;
	while(tries > 0)
	{
		st = ata_status();
		if((st & (u8)0x01) != (u8)0)
		{
			return 0; /* err bit set */
		}
		if((st & (u8)0x08) != (u8)0)
		{
			return 1; /* drq set, data ready */
		}
		tries = tries - 1;
	}
	return 0;
}
 
void
ata_select_lba(u32 lba)
{
	u8 head;
	head = (u8)0xe0 | (u8)((lba >> (u32)24) & (u32)0x0f);
	outb((u16)0x1f6, head);
	outb((u16)0x1f2, (u8)1);            /* sector count = 1 */
	outb((u16)0x1f3, (u8)(lba & (u32)0xff));
	outb((u16)0x1f4, (u8)((lba >> (u32)8) & (u32)0xff));
	outb((u16)0x1f5, (u8)((lba >> (u32)16) & (u32)0xff));
}
 
/* reads one 512-byte sector at lba into buf (must have room for 512
 * bytes). returns 1 on success, 0 on timeout/error. */
global i32
ata_read_sector(u32 lba, ptr buf)
{
	ptr dst;
	u64 w0;
	u64 w1;
	u64 w2;
	u64 w3;
	u64 packed;
	i32 i;
 
	if(ata_wait_not_busy() == 0)
	{
		return 0;
	}
	ata_select_lba(lba);
	outb((u16)0x1f7, (u8)0x20); /* read sectors */
 
	if(ata_wait_not_busy() == 0)
	{
		return 0;
	}
	if(ata_wait_drq() == 0)
	{
		return 0;
	}
 
	dst = buf;
	i = 0;
	while(i < 64)
	{
		w0 = (u64)inw((u16)0x1f0);
		w1 = (u64)inw((u16)0x1f0);
		w2 = (u64)inw((u16)0x1f0);
		w3 = (u64)inw((u16)0x1f0);
		packed = w0 | (w1 << (u64)16) | (w2 << (u64)32) | (w3 << (u64)48);
		*dst = (i64)packed;
		dst = dst + 8;
		i = i + 1;
	}
	return 1;
}
 
/* writes one 512-byte sector at lba from buf. returns 1 on success, 0
 * on timeout/error. */
global i32
ata_write_sector(u32 lba, ptr buf)
{
	ptr src;
	u64 packed;
	u16 w0;
	u16 w1;
	u16 w2;
	u16 w3;
	i32 i;
 
	if(ata_wait_not_busy() == 0)
	{
		return 0;
	}
	ata_select_lba(lba);
	outb((u16)0x1f7, (u8)0x30); /* write sectors */
 
	if(ata_wait_not_busy() == 0)
	{
		return 0;
	}
	if(ata_wait_drq() == 0)
	{
		return 0;
	}
 
	src = buf;
	i = 0;
	while(i < 64)
	{
		packed = (u64)*src;
		w0 = (u16)(packed & (u64)0xffff);
		w1 = (u16)((packed >> (u64)16) & (u64)0xffff);
		w2 = (u16)((packed >> (u64)32) & (u64)0xffff);
		w3 = (u16)((packed >> (u64)48) & (u64)0xffff);
		outw((u16)0x1f0, w0);
		outw((u16)0x1f0, w1);
		outw((u16)0x1f0, w2);
		outw((u16)0x1f0, w3);
		src = src + 8;
		i = i + 1;
	}
 
	outb((u16)0x1f7, (u8)0xe7); /* cache flush */
	if(ata_wait_not_busy() == 0)
	{
		return 0;
	}
	return 1;
}
powered by btf.