Page 1 of 1

[Solved] ATA PIO Not Causing IRQ

Posted: Wed Jan 06, 2021 6:44 am
by LukeyTheKid
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

Code: Select all

// Initialize disk
  detect_and_init();
  // reset_controller();

  select_drive(MASTER_DRIVE);
  read_pio(1, 10, 0);
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:

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();
 // }
}
Before I make a read call, I first select the drive:

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;
}
(Edit: Here are the relevant constant definitions)

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;

Re: ATA PIO Not Causing IRQ

Posted: Wed Jan 06, 2021 12:55 pm
by rdos
LukeyTheKid wrote: I have read that IRQs are not generally used for PIO mode, but they should still be generated, right?
Why wouldn't IRQs be used for PIO? I use PIO and IRQs for ATA, and it works on the systems I've tested on.

The big issue with IRQs and ATA is how to find the correct IRQ, so how do you know which IRQ your ATA device use?

Re: ATA PIO Not Causing IRQ

Posted: Thu Jan 07, 2021 7:04 am
by LukeyTheKid
Why wouldn't IRQs be used for PIO? I use PIO and IRQs for ATA, and it works on the systems I've tested on.
I am still just getting my bearings with ATA; I don't mean that IRQs *can't* be used, just that it seems like the examples I've found all use polling
The big issue with IRQs and ATA is how to find the correct IRQ, so how do you know which IRQ your ATA device use?
I put breakpoints in every single one of my ISRs, and none of them were hit, so I assumed that no interrupt was being generated at all. Also, the wiki mentions that the IRQ for the primary bus should be 14, which is what I was looking for at first before throwing a hail mary on the full set of ISRs.

Re: ATA PIO Not Causing IRQ

Posted: Thu Jan 07, 2021 7:43 am
by LukeyTheKid
Argh. Only IRQs 0 and 1 were unmasked, that'll do it...Unmasked IRQ2 for the slave PIC and 14 for the ATA, and got an interrupt to fire successfully.

Thanks rdos for the reply; that got me re-examining some assumptions around my IRQ setup.

Re: ATA PIO Not Causing IRQ

Posted: Mon Jan 18, 2021 11:41 am
by SpaceAcoustics
LukeyTheKid wrote:Argh. Only IRQs 0 and 1 were unmasked, that'll do it...Unmasked IRQ2 for the slave PIC and 14 for the ATA, and got an interrupt to fire successfully.

Thanks rdos for the reply; that got me re-examining some assumptions around my IRQ setup.
I once debugged similar situation for a few hours.