ATA driver fails to read
Posted: Sun Aug 22, 2010 12:05 am
I am currently working on an ATA driver to read data in PIO mode. The driver is as simple as possible; it simply sends the appropriate values, then polls for data-ready and reads. The code is below.
In this context, the value d->io_base is 0x1f0, and the constants are:
As far as I can tell, this is semantically identical to the (presumably working) asm driver on the ATA_PIO_Mode page. However, when I run it, it hangs in the polling loop (DRQ is never set). An earlier version that used an interrupt that set a spinlock variable did the same (the interrupt never arrived). The use case calls the function using a stock ata_drive structure created by the function:
with only one segment and a segment number of 1. Is there any obvious problem that I am failing to see? (I have tested this in Qemu; Bochs page faults every time in unrelated code, and I have not tested it on hardware).
Code: Select all
s32int ata_read(struct ata_drive *d, u32int segment, u8int segnum, void *buf) {
u8int stat;
segment &= 0x0fffffff; // 28-bit LBA
outportb(d->io_base + ATA_PORT_SECTORS, segnum);
outportb(d->io_base + ATA_PORT_ADDR1, (u8int) segment);
outportb(d->io_base + ATA_PORT_ADDR2, (u8int) (segment >> 8));
outportb(d->io_base + ATA_PORT_ADDR3, (u8int) (segment >> 16));
outportb(d->io_base + ATA_PORT_DSELECT, 0xe0 | segment >> 24);
outportb(d->io_base + ATA_PORT_CMD, 0x20);
inportb(d->io_base + ATA_PORT_CMD); // 400 ns delay
inportb(d->io_ctrl + ATA_PORT_CMD);
inportb(d->io_ctrl + ATA_PORT_CMD);
inportb(d->io_ctrl + ATA_PORT_CMD);
u16int i = 0;
u8int j = 0;
u16int k = 0;
u16int *b = (u16int *) buf;
while (j < segnum) {
/* wait = 0; */
/* while (!wait); // spin on the wait lock */
while ((stat = inportb(d->io_ctrl))) {
if (stat & 0x80) {
continue;
}
if (stat & 0x21) {
printf("error: %x", stat);
return 1;
}
if (stat & 0x08) {
break;
}
}
for (i = 0; i < 256; i++,k++) {
b[k] = inportw(d->io_base + ATA_PORT_DATA);
}
}
return 0;
}
Code: Select all
#define ATA_PORT_DATA 0x0
#define ATA_PORT_ERRINF 0x1
#define ATA_PORT_SECTORS 0x2
#define ATA_PORT_ADDR1 0x3
#define ATA_PORT_ADDR2 0x4
#define ATA_PORT_ADDR3 0x5
#define ATA_PORT_DSELECT 0x6
#define ATA_PORT_CMD 0x7
Code: Select all
void ata_init(struct ata_drive **d) {
/* if (inportb(0x1f7) == 0xff) { // floating bus; no drives */
/* *d = 0; */
/* return; */
/* } */
*d = kmalloc(sizeof(struct ata_drive));
struct ata_drive *da = *d;
da[0].bus = 0;
da[0].port = 1;
da[0].func = 1;
da[0].io_base = 0x1f0;
da[0].io_ctrl = 0x3f6;
da[0].irq = IRQ14;
register_interrupt_handler(IRQ14, ata_interrupt_handler);
return;
}