I'm trying to get an ATA PIO driver working. I have reviewed, among other resources, the wiki page, as well as these forums, but I'm having trouble getting 1) a big-picture perspective and 2) information that isn't confusing.
I sorted through all of it, and managed to produce a simple PIO ATA driver that implements the closest I could figure out. It can read a single sector perfectly, but it fails the second time it tries to read a single sector. It uses IRQs to be notified; the second time it tries, the IRQ never comes. The source is in my sig, if you care.
Although it would be nice if someone could pinpoint the problem from that, I feel like the larger problem is that I don't understand exactly what's happening at the system level. I would appreciate some clarification. As nearly as I can figure out:
In General:
-
Each disk controller chip can have up to two ATA buses ("channels"): ("primary" and "secondary"). Each ATA bus has up to two HDDs attached ("master" and "slave"). I inferred that from this--although the wording seemed contradictory? Each HDD may differ in IO ports, device control registers, and IRQs. By default (i.e. after BIOS):This suggested to me that devices should be grouped into buses: the disk controller has space for two ATA buses, and each ATA bus has space for two hard drives. On startup, you should set up the drives and select the one you want (you select one drive on a particular bus. If there are two buses . . . ?). Experimentally, I found that the primary master is selected by default.
Code: Select all
IO ports (range) Device Control Register/Alternate Status Port IRQ Primary Master [0x01F0,0x01F7] 0x03F6 14 Primary Slave [0x0170,0x0177] 0x0376 14 Secondary Master [0x01E8,0x01EF] 0x03E6 15 Secondary Slave [0x0168,0x016F] 0x0366 15
- By default, I have experimentally found that devices have interrupts enabled. While debugging, I initially tried disabling interrupts by setting the nIEN bit of the device control register (selecting the drive first, of course), but it didn't seem to work--I still received interrupts on reading. Since I ultimately want IRQs anyway, I ignored the issue.
- I understand that PIO is slower than DMA, but at this point I want to keep it as simple as possible. Similarly, for now I'm only using LBA 48 addressing, and only attempting to access the primary master drive.
- My driver does the following to read n sectors (I adapted largely from this):
- Make sure the drive you want is selected: at initialization, although it is apparently selected by default, I explicitly select the primary master (by sending 0x40 to port 0x01F6, although I doubt this is really correct).
- Send the sector count (n) and the LBA to 4 different ports. For the primary master: upper(n)=>0x01F2, byte4(LBA)=>0x01F3, byte5(LBA)=>0x01F4, byte6(LBA)=>0x01F5, lower(n)=>0x01F2, byte1(LBA)=>0x01F3, byte2(LBA)=>0x01F4, byte3(LBA)=>0x01F5, in that order.
- Send the READ SECTORS EXT command (0x24) to port 0x01F7 (for primary master).
- Wait for an IRQ.
- The IRQ handler reads in 512 bytes (256 words) from port 0x01F0 (for primary master) to the buffer.
- The IRQ handler sends an End Of Interrupt (EOI) to one or both PICs as applicable and then returns.
- If there is another sector we requested, then we go back to waiting for an IRQ again, repeating as necessary.
- I don't know exactly where the 400ns delays come in, but I saw reference to it in retrieving the status byte. Consequently, when getting the status byte, the IO port (0x03F6 for primary master) is read five times, and the last value is used. Other than that, there are no delays, which I feel is probably wrong.
- There's some error checking that happens. On initialization, the primary master drive confirms !ERR && !BSY && !DRQ. Additionally, before reading each sector, the IRQ handler checks for !ERR.
Thanks!
-G