I think you have misunderstood where the IRQ should fire. The IRQ only fires *after* a sector has been read or written, a whole sector.
The IRQ won't fire after you have written the parameter registers, only after you have written 256 16-bit words.
Also, the IRQ will not fire immediately after the last word written, it will take a few nS before it does.
And don't write the Flush Cache command until after the whole sector has been written, in fact at the moment, until you
get a working read/write routine, I would forget about flushing the cache.
Here is what I would do (please see my notes afterward):
Code: Select all
Ports::outportb(io_port + 6, 0xE0 | (slavebit << 4) | ((addr >> 24) & 0x0F));
Ports::inportb(io_port + ATA_REG_STATUS));
Ports::outportb(io_port + 1, 0x00);
Ports::outportb(io_port + 2, 0x01);
Ports::outportb(io_port + 3, (uint8_t)addr);
Ports::outportb(io_port + 4, (uint8_t)(addr >> 8));
Ports::outportb(io_port + 5, (uint8_t)(addr >> 16));
Ports::outportb(io_port + 7, 0x30);
if (Ports::inportb(io_port + ATA_REG_STATUS))
{
// for ( int i = 0 ; ; i++) // why increment 'i'? Unneeded instruction
for (;;)
{
uint8_t status = Ports::inportb(io_port+ATA_REG_STATUS);
if ( status & ATA_SR_DRQ ) break;
else if(status & ATA_SR_ERR)
{
video->setPosition(30,5);
video->putString("Error on Write Sector",COLOR_BLUE,COLOR_LIGHT_BROWN);
return;
}
}
}
for (int idx = 0; idx < 256; idx++)
{
uint16_t writeword = p_buffer[idx * 2] | (p_buffer[idx * 2 + 1] << 8);
Ports::outportw(io_port, writeword);
}
// here is where the IRQ should fire.
Here are some notes about the ATA interface.
- - You should only select the drive once. For example, don't select the same drive again for the next sector. Only write to the DRV_HEAD register if the value to write is different from the last time. The ATA controller takes a considerable amount of time to 'select' itself. (400ns is a long time, really)
- You should read the status register after selecting the drive, giving it enough time to actually select itself.
- If you cast your p_buffer from a byte buffer to a word buffer, you can do something like:Code: Select all
uint16_t *ptr = (uint16_t *) p_buffer;
for (int idx = 0; idx < 256; idx++)
Ports::outportw(io_port, *ptr++);
Here is a condensed version of my "transfer a sector" function from my book:
Code: Select all
// select drive
// (reads the status register to clear any pending interrupts)
ata_select_drv(ata->cntrlr->base, ata->drv, ATA_DH_ISLBA, (bit8u) ((bit64u) (lba & (bit64u) 0x00000F000000) >> 24));
// wait for the controller to not be busy (i.e: wait for it to be ready)
if (ata_wait(ata, ATA_STATUS_RDY, wait)) {
outportb(ata->cntrlr->base + ATA_FEATURES, (features & 0xFF));
outportb(ata->cntrlr->base + ATA_SECTOR_COUNT, ((buflen / 512) & 0xFF));
outportb(ata->cntrlr->base + ATA_LBA_LOW_BYTE, (bit8u) (((bit32u) lba & 0x000000FF) >> 0));
outportb(ata->cntrlr->base + ATA_LBA_MID_BYTE, (bit8u) (((bit32u) lba & 0x0000FF00) >> 8));
outportb(ata->cntrlr->base + ATA_LBA_HIGH_BYTE, (bit8u) (((bit32u) lba & 0x00FF0000) >> 16));
} else
return FALSE;
// send the command
outportb(ata->cntrlr->base + ATA_COMMAND, command);
// wait for the drive to be ready for the transfer
if (ata_wait(ata, ATA_STATUS_DRQ, wait)) {
if (ttype == ATA_TRNS_TYPE_PIO) {
// All sector transfers on an ATA call are 512 byte sectors...
bit32u addr = (bit32u) buf;
while (buflen > 0) {
if (ata->dword_io) {
bit32u *ptr = (bit32u *) addr;
j = ((buflen > 512) ? 512 : buflen) / sizeof(bit32u);
for (i=0; i<j; i++) {
if (dir == ATA_DIR_RECV)
*ptr++ = inportl(ata->cntrlr->base + ATA_DATA);
else
outportl(ata->cntrlr->base + ATA_DATA, *ptr++);
}
} else {
bit16u *ptr = (bit16u *) addr;
j = ((buflen > 512) ? 512 : buflen) / sizeof(bit16u);
for (i=0; i<j; i++) {
if (dir == ATA_DIR_RECV)
*ptr++ = inportw(ata->cntrlr->base + ATA_DATA);
else
outportw(ata->cntrlr->base + ATA_DATA, *ptr++);
}
}
buflen -= 512;
addr += 512;
// wait for the drive to be ready for the next transfer
if ((buflen > 0) && !ata_wait(ata, ATA_STATUS_DRQ, wait))
return FALSE;
}
} else {
// is DMA, do the DMA code
// not included for this post.
}
}
Please note that this is a stripped down version and allows transfers less than 512 bytes, since the same code is used to send 12- and 16-byte packets.
Hope this helps.
Ben
http://www.fysnet.net/media_storage_devices.htm