So, i rewrited the device init code using the Wiki code, but as soon as i enable the device interrupts using the ATA_REG_CONTROL and issue the ATA DMA read command i start to receiving endless count of interrupts from IDE controller.
Here is my new version of device init code:
Code:
bool ata_device_init(ata_device_t *dev,int id) {
unsigned char err = 0,status;
ide_write(dev,ATA_REG_CONTROL,2);
ide_write(dev,ATA_REG_HDDEVSEL,0xA0 | (id << 4));
ata_io_wait(dev);
ide_write(dev,ATA_REG_COMMAND,ATA_CMD_IDENTIFY);
ata_io_wait(dev);
if (ide_read(dev,ATA_REG_STATUS) == 0) return false;
kprintf("Waiting device to clear BSY state...");
while(1) {
status = ide_read(dev,ATA_REG_STATUS);
if ((status & ATA_SR_ERR)) {
// Looks like, we send wrong command?
uint8_t t1 = ide_read(dev,ATA_REG_LBA1);
uint8_t t2 = ide_read(dev,ATA_REG_LBA2);
if (t1 == 0x14 && t2 == 0xEB) {
// fault caused by WRONG command.
kprintf("ATAPI device fault(normal,ignore)\r\n");
dev->type = IDE_ATAPI;
break;
} else {
kprintf("First device fault received 0x%x 0x%x\r\n",t1,t2);
return false;
}
}
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break;
}
kprintf("cleared\r\n");
if (dev->type == IDE_ATAPI) {
// Selected by first fault handler.
goto atapiDetect;
}
uint8_t ch = ide_read(dev,ATA_REG_LBA1);
uint8_t cl = ide_read(dev,ATA_REG_LBA2);
if (cl == 0x14 && ch == 0xEB) {
atapiDetect:
kprintf("ATAPI device detected!\r\n");
dev->type = IDE_ATAPI;
ide_write(dev,ATA_REG_COMMAND,ATA_CMD_IDENTIFY_PACKET);
ata_io_wait(dev);
// Repeat process
while(1) {
status = ide_read(dev,ATA_REG_STATUS);
if ((status & ATA_SR_ERR)) {
kprintf("Device fault fallback!\r\n");
return false;
}
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break;
}
} else if (cl == 0x69 && ch == 0x96) {
kprintf("ATA device!\r\n");
} else if (cl == 0x0 && ch == 0) {
kprintf("We skiped something?!\r\n");
} else {
kprintf("Unknown type\r\n");
return false;
}
uint16_t *identf = (uint16_t *)&dev->identify;
for (int i = 0; i < 256; i++) {
identf[i] = inw(dev->base);
}
kprintf("readed\n");
uint8_t *ptr = (uint8_t *)&dev->identify.model;
for (int i = 0; i < 39; i+=2) {
uint8_t tmp = ptr[i+1];
ptr[i+1] = ptr[i];
ptr[i] = tmp;
}
kprintf("ATA: Detected device name: %s, type: %s\r\n",dev->identify.model,dev->type == IDE_ATAPI ? "ATAPI" : "IDE");
ata_create_device(dev->type == IDE_ATA,dev);
dev->type = IDE_ATA;
return true;
}
The DMA reading code itself:
Code:
static void ata_vdev_readBlock(vfs_node_t *node,int blockNo,int how, void *buf) {
ata_device_t *dev = node->device;
if (dev->type == IDE_ATAPI) {
kprintf("Currently doesn't supported!\n");
return;
}
void *dd = buf;
int doCopy = false;
uint64_t lba = blockNo;
uint16_t sectors = how/512;
//kprintf("Read %d sectors at %d\r\n",sectors,lba);
if (sectors == 0) sectors = 1;
if (sectors > 255) {
kprintf("ATA: Maximum count of sectors that able to read per one command is 255!\n");
sectors = 254;
}
if (blockNo == 0) {
goto readActual;
}
kprintf("%s: called\r\n",__func__);
// Before issusing real READ DMA command, check if we don't readed the sectors previously
readActual:
ide_write(dev,ATA_REG_CONTROL,0);
kprintf("%s: actual reading begin\r\n",__func__);
// DMA support!
outb(dev->bar4,0x00);
outl(dev->bar4 + 0x04, dev->dma_prdt_phys);
outb(dev->bar4 + 0x2, inb(dev->bar4 + 0x02) | 0x04 | 0x02);
outb(dev->bar4, 0x08);
while (1) {
uint8_t status = inb(dev->base + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY)) break;
}
int cache_sectors = sectors;
outb(dev->base+ATA_REG_HDDEVSEL,0x40);
ata_io_wait(dev);
outb(dev->base + ATA_REG_FEATURES, 0x01);
outb(dev->base+ATA_REG_SECCOUNT0,(cache_sectors >> 8) & 0xFF);
outb(dev->base+ATA_REG_LBA0, (lba & 0xff000000) >> 24);
outb(dev->base+ATA_REG_LBA1, (lba & 0xff000000) >> 32);
outb(dev->base+ATA_REG_LBA2, (lba & 0xff000000) >> 48);
outb(dev->base+ATA_REG_SECCOUNT0,cache_sectors & 0xFF);
outb(dev->base+ATA_REG_LBA0,lba & 0xFF);
outb(dev->base+ATA_REG_LBA1,(lba >> 8) & 0xFF);
outb(dev->base+ATA_REG_LBA2,(lba >> 16) & 0xFF);
uint8_t i;
// convert buffer
uint8_t *bu = (uint8_t *)buf;
while (1) {
uint8_t status = inb(dev->base + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) break;
}
outb(dev->base + ATA_REG_COMMAND, ATA_CMD_READ_DMA_EXT);
ata_io_wait(dev);
outb(dev->bar4, 0x08 | 0x01);
inter_ata->busy = 1;
arch_sti();
/*while (1) {
int status = inb(dev->bar4 + 0x02);
int dstatus = inb(dev->base + ATA_REG_STATUS);
if (!(status & 0x04)) {
continue;
}
if (!(dstatus & ATA_SR_BSY)) {
break;
}
}
*/
while(inter_ata->busy);
/* Inform device we are done. */
outb(dev->bar4 + 0x2, inb(dev->bar4 + 0x02) | 0x04 | 0x02);
if (doCopy) {
memcpy(dd,buf,how);
kfree(buf);
}
if (sectors > 255) {
kprintf("ATA: Sectors count are bigger that 0xFF, repeat process\n");
sectors-=255;
ata_vdev_readBlock(node,blockNo+255,sectors*512,bu);
}
// Dump the PDRT
if (how > 8192) {
kprintf("WARRNING: DMA support only 8 sectors :). So we copy only 8 sectors\r\n");
}
if (buf == NULL) {
//kprintf("Zero buffer!!!\r\n");
dev->latestSector = blockNo;
return;
}
memcpy(bu,dev->dma_start,(how > 8192) ? 8192 : how);
kprintf("%s: ended\r\n",__func__);
}
Current version of device block reading code is for testing, so for example we don't read the real count of cache sectors.
Also i notice that the bug/problem shows only if system boots in Legacy Mode using the EFI legacy boot or by real legacy BIOS.
EDIT: Rewrite some parts of code like in the wiki, nothing changed.