[Solved] ATA PIO Not Causing IRQ
Posted: Wed Jan 06, 2021 6:44 am
edit: For me, the issue was that only IRQs 0 and 1 were unmasked; the rest were masked. Unmasking IRQ14 (for ATA) and IRQ2 (for the slave PIC in general) fixed the issue, and now I am getting interrupts for my ATA read/writes.
---------------------------------------------------------------
Hi, I have been working on an ATA driver, and so far I've managed to perform detection / initialization successfully, and now I'm moving onto actual reads. I followed the wiki's ATA_PIO_Mode guide for 48 LBA, and when I send a read command I do not get an IRQ -- though I do get a DRQ flag when I poll, and I seem to be able to read/write. I looped from 0-255 and wrote to a sector, and then read back the same sector, and the data was correct.
I have read that IRQs are not generally used for PIO mode, but they should still be generated, right?
Interrupts are enabled (key presses are still working etc.), and I also tried resetting the controller after I first initialize it, with the nIEN bit cleared to see if that helped but still no joy. Once I have my bearings I am planning to move onto DMA, but for the moment I want to make sure that things just work in this basic approach first. It's possible that my drive select code is missing something as well, so if that's the issue then I'd definitely want that resolved before moving on.
One thing to mention is I just picked one random sector to read from (10), so I'm not sure if that may the issue. My read callsite is just
According to the IDENTIFY info, the drive is ATA-6, so I am going through that spec, but it's pretty dense so it will take some time...Thanks in advance for any help.
ATA code:
Header
Implementation
For reference, here is my read code:
Before I make a read call, I first select the drive:
(Edit: Here are the relevant constant definitions)
---------------------------------------------------------------
Hi, I have been working on an ATA driver, and so far I've managed to perform detection / initialization successfully, and now I'm moving onto actual reads. I followed the wiki's ATA_PIO_Mode guide for 48 LBA, and when I send a read command I do not get an IRQ -- though I do get a DRQ flag when I poll, and I seem to be able to read/write. I looped from 0-255 and wrote to a sector, and then read back the same sector, and the data was correct.
I have read that IRQs are not generally used for PIO mode, but they should still be generated, right?
Interrupts are enabled (key presses are still working etc.), and I also tried resetting the controller after I first initialize it, with the nIEN bit cleared to see if that helped but still no joy. Once I have my bearings I am planning to move onto DMA, but for the moment I want to make sure that things just work in this basic approach first. It's possible that my drive select code is missing something as well, so if that's the issue then I'd definitely want that resolved before moving on.
One thing to mention is I just picked one random sector to read from (10), so I'm not sure if that may the issue. My read callsite is just
Code: Select all
// Initialize disk
detect_and_init();
// reset_controller();
select_drive(MASTER_DRIVE);
read_pio(1, 10, 0);
ATA code:
Header
Implementation
For reference, here is my read code:
Code: Select all
void read_pio(uint16_t sector_count, uint32_t LBA_low4, uint16_t LBA_high2){
// Read LBA mode
// @TODO this assumes master drive
outb(PRIMARY_BUS_PORT_BASE + DRIVE_HEAD_REG_OFF, LBA_MODE);
// Sectorcount high byte
outb(PRIMARY_BUS_PORT_BASE + SECT_COUNT_REG_OFF, (sector_count >> 8) & 0xFF);
// LBA bytes 4, 5, 6
outb(PRIMARY_BUS_PORT_BASE + LBA_LO_REG_OFF, (LBA_low4 >> 24) & 0xFF);
outb(PRIMARY_BUS_PORT_BASE + LBA_MID_REG_OFF, (LBA_high2) & 0xFF);
outb(PRIMARY_BUS_PORT_BASE + LBA_HI_REG_OFF, (LBA_high2 >> 8) & 0xFF);
// Sectorcount low byte
outb(PRIMARY_BUS_PORT_BASE + SECT_COUNT_REG_OFF, (sector_count) & 0xFF);
// LBA bytes 1, 2, 3
outb(PRIMARY_BUS_PORT_BASE + LBA_LO_REG_OFF, (LBA_low4) & 0xFF);
outb(PRIMARY_BUS_PORT_BASE + LBA_MID_REG_OFF, (LBA_low4 >> 8) & 0xFF);
outb(PRIMARY_BUS_PORT_BASE + LBA_HI_REG_OFF, (LBA_low4 >> 16) & 0xFF);
// Read sectors command
outb(PRIMARY_BUS_PORT_BASE + CMD_REG_OFF, READ_SECTORS_EXT);
// Poll for DRQ -- when uncommented, this works, DRQ is set
// if (ata_wait(DRQ, ATA_WAIT)) {
// read_sectors();
// }
}
Code: Select all
// Returns status (1 = success, 0 = failure)
uint8_t select_drive(uint8_t drive){
uint8_t status;
// No matter which drive is requested, we first must check the
// status of the currently selected drive to make sure that it
// is not actively modifying status
status = inb(PRIMARY_BUS_PORT_BASE + STATUS_REG_OFF);
uint8_t mask = BSY | DRQ | ERR;
if (status & mask){
printf("Not ready to select drive\n");
return 0;
}
// Already selected, nothing to do
if (drive == CURRENT_DRIVE){
return 1;
}
// Select drive, then delay 400ns
outb(PRIMARY_BUS_PORT_BASE + DRIVE_HEAD_REG_OFF, drive);
for (int i = 0; i < 4; i++){
inb(PRIMARY_BUS_ALT_STATUS);
}
// Read the Status register to clear pending interrupts
// Ignore value
inb(PRIMARY_BUS_PORT_BASE + STATUS_REG_OFF);
// Read Status register one more time, use this to determine status
status = inb(PRIMARY_BUS_PORT_BASE + STATUS_REG_OFF);
if (status & mask){
printf("Drive select failed\n");
return 0;
}
CURRENT_DRIVE = drive;
return 1;
}
Code: Select all
// ----------------------------------------
// Controller I/O Port Registers
// ----------------------------------------
#define PRIMARY_BUS_PORT_BASE 0x1F0
#define PRIMARY_BUS_ALT_STATUS 0x3F6
#define MASTER_DRIVE 0xA0
// IO Port register offsets
#define DATA_REG_OFF 0x0
#define ERR_REG_OFF 0x1
#define FEATURE_REG_OFF 0x1
#define SECT_COUNT_REG_OFF 0x2
#define LBA_LO_REG_OFF 0x3
#define LBA_MID_REG_OFF 0x4
#define LBA_HI_REG_OFF 0x5
#define DRIVE_HEAD_REG_OFF 0x6
#define STATUS_REG_OFF 0x7
#define CMD_REG_OFF 0x7
// ----------------------------------------
// Command Port Commands
// ----------------------------------------
#define READ_SECTORS_EXT 0x24
// ----------------------------------------
// Status Register Bitflag Codes
// ----------------------------------------
#define ERR 0x1 // Indicates an error occurred. Send new cmd to clear
#define DRQ 0x8 // Set when drive has PIO dat to transfer, or is ready to accept PIO data
#define SRV 0x10 // Overlapped Mode Service Request
#define RDY 0x40 // Bit is clear when drive is spun down or after err. Set otherwise
#define BSY 0x80 // Indicates drive is preparing to send/receive data (wait for it to clear).
#define INVALID_ATA_STATUS 0xFF
// ----------------------------------------
// Read / Write Constants
// ----------------------------------------
#define LBA_MODE 0x40
// Polling mode, either once or immediate
#define ATA_ONCE 0x0
#define ATA_WAIT 0x1
extern uint8_t CURRENT_DRIVE;