I am currently developing an ATA driver and I ran into some problems when trying it on real hardware.
My test bench is a very old laptop that has a PCI IDE controller with 2 channels:
- The first one (I/O ports 0x1f0 and 0x3f6) is controlling the hard drive as master
- The second one (I/O ports 0x170 and 0x376) is controlling the optical drive as master
However, during the detection phase of my driver, I noticed that there is a weird thing after sending the IDENTIFY command to the slave drive of the first channel.
It looks like even though there is no drive, the status byte is not set to zero after 400ns.
My driver therefore thinks there is a drive, and waits for the BSY bit to be cleared, and then for the DRQ bit to be set.
I searched the whole day for information about this, I tried adding wait time, etc... But nothing solved my problem.
Does anybody have any information about this?
Here is my code:
Code: Select all
static void ata_identify(ata_channel_t *channel, int drive) {
ata_identify_t identify;
outb(channel->commandIoBase + ATA_CMDREG_DRIVE_HEAD, 0xa0 | ((drive & 1) << 4));
ata_wait(channel); // Unnecessary?
outb(channel->commandIoBase + ATA_CMDREG_SECTOR_COUNT, 0);
outb(channel->commandIoBase + ATA_CMDREG_LBA_0_7, 0);
outb(channel->commandIoBase + ATA_CMDREG_LBA_8_15, 0);
outb(channel->commandIoBase + ATA_CMDREG_LBA_16_23, 0);
outb(channel->commandIoBase + ATA_CMDREG_COMMAND, ATA_COMMAND_IDENTIFY);
ata_wait(channel); // Unnecessary?
if(!inb(channel->commandIoBase + ATA_CMDREG_STATUS)) {
printf("ata: no %s drive detected on this channel\n", drive ? "slave" : "master");
return;
} else {
printf("ata: detected drive\n");
}
printf("ata: waiting for BUSY bit to clear...\n");
ata_waitBsyEnd(channel);
printf("ata: BUSY bit cleared\n");
if(inb(channel->commandIoBase + ATA_CMDREG_STATUS) & ATA_STATUS_ERROR) {
uint8_t firstByte = inb(channel->commandIoBase + ATA_CMDREG_CYLINDER_LOW);
uint8_t secondByte = inb(channel->commandIoBase + ATA_CMDREG_CYLINDER_HIGH);
if(firstByte == 0x00 && secondByte == 0x00) {
printf("ata: detected PATA device\n");
} else if(firstByte == 0x14 && secondByte == 0xeb) {
printf("ata: detected PATAPI device\n");
} else if(firstByte == 0x3c && secondByte == 0xc3) {
printf("ata: detected SATA device\n");
} else if(firstByte == 0x69 && secondByte == 0x96) {
printf("ata: detected SATAPI device\n");
} else {
printf("ata: unknown device identification: %#02x %#02x\n", firstByte, secondByte);
}
return;
}
printf("ata: waiting for DRQ bit to be set...\n");
ata_waitDrq(channel);
printf("ata: DRQ bit is set.\n");
ata_receive(channel, &identify);
printf("ata: device identification: %.40s\n", identify.modelNumber);
}
static void ata_waitBsyEnd(ata_channel_t *channel) {
while(inb(channel->commandIoBase + ATA_CMDREG_STATUS) & ATA_STATUS_BUSY);
}
static void ata_waitDrq(ata_channel_t *channel) {
while(!(inb(channel->commandIoBase + ATA_CMDREG_STATUS) & ATA_STATUS_DATA_REQUEST_READY));
}
static void ata_receive(ata_channel_t *channel, void *buffer) {
for(int i = 0; i < 256; i++) {
uint16_t tmp = inw(channel->commandIoBase + ATA_CMDREG_DATA);
((uint16_t *)buffer)[i] = (tmp >> 8) | (tmp << 8);
}
}
static void ata_wait(ata_channel_t *channel) {
for(int i = 0; i < 4; i++) {
inb(channel->commandIoBase + ATA_CMDREG_STATUS);
}
}