Storage (ATA Driver)¶
Responsibility¶
- Provide low-level access to ATA (IDE) disks in 28-bit LBA mode.
- Support basic operations:
- Device identification (
Identify). - Sector reads (
Read28). - Sector writes (
Write28). - Cache flush (
Flush). - Intended to be used as the hardware backend for future filesystem layers.
Hardware Model¶
AdvancedTechnologyAttachment represents a single ATA channel endpoint (primary/secondary, master/slave).
I/O Ports¶
Constructed with a base port and master flag:
AdvancedTechnologyAttachment::AdvancedTechnologyAttachment(uint16_t portBase, bool master)
: dataPort(portBase),
errorPort(portBase + 0x01),
sectorCountPort(portBase + 0x02),
lbaLowPort(portBase + 0x03),
lbaMidPort(portBase + 0x04),
lbaHiPort(portBase + 0x05),
devicePort(portBase + 0x06),
commandPort(portBase + 0x07),
controlPort(portBase + 0x206)
{
bytesPerSector = 512;
this->master = master;
}
- Standard ATA register layout:
dataPort– 16‑bit data register.errorPort– error/status details.sectorCountPort– sector count for transfer.lbaLowPort,lbaMidPort,lbaHiPort– LBA address bytes.devicePort– device/head, plus upper LBA bits and master/slave select.commandPort– command/status.-
controlPort– device control (e.g., interrupts, 400 ns delays). -
Default:
bytesPerSector = 512.masterindicates whether this object is pointing at the master or slave device on the channel.
Device Identification¶
Process¶
- Select device:
- Basic presence check:
devicePort.Write(0xA0);
uint8_t status = commandPort.Read();
if (status == 0xFF)
return false; // no device attached
- Prepare IDENTIFY:
devicePort.Write(master ? 0xA0 : 0xB0);
sectorCountPort.Write(0);
lbaLowPort.Write(0);
lbaMidPort.Write(0);
lbaHiPort.Write(0);
commandPort.Write(0xEC); // IDENTIFY
- Check status:
- Detect ATAPI:
uint8_t lbaMid = lbaMidPort.Read();
uint8_t lbaHi = lbaHiPort.Read();
if (lbaMid != 0 || lbaHi != 0) {
printf("ATAPI device detected\n");
return false;
}
- Poll until not busy (BSY cleared) and no error:
while (((status & 0x80) == 0x80) && ((status & 0x01) != 0x01)) {
status = commandPort.Read();
}
if (status & 0x01) {
printf("ATA ERROR IN IDENTIFY\n");
return false;
}
- Read and discard 256 words of identification data:
- Returns
trueif a valid ATA device is found and successfully identified,falseotherwise.
Reading Sectors (LBA28)¶
Preconditions and validation¶
- Sector must fit in 28 bits:
countmust not exceed one sector:
Command setup¶
- Select device and upper LBA bits:
- 400 ns delay (roughly):
- Clear error, set sector count, disable interrupts:
- Set LBA low/mid/high bytes:
lbaLowPort.Write(sector & 0xFF);
lbaMidPort.Write((sector >> 8) & 0xFF);
lbaHiPort.Write((sector >> 16) & 0xFF);
- Issue READ SECTORS command:
Polling for readiness¶
- Initial status:
- Wait for BSY to clear:
while (((status & 0x80) == 0x80) && ((status & 0x01) != 0x01)) {
status = commandPort.Read();
}
if (status & 0x01) {
printf("ERROR in ATA READ (BSY)\n");
uint8_t error = errorPort.Read();
printf("Error code: 0x");
printByte(error);
printf("\n");
return;
}
- Wait for DRQ to set:
while (!(status & 0x08) && !(status & 0x01)) {
status = commandPort.Read();
}
if (status & 0x01) {
printf("ERROR in ATA READ (DRQ)\n");
return;
}
Data transfer¶
- Reads
countbytes intodata, 16 bits at a time:
printf("Reading ATA: ");
for (uint16_t i = 0; i < count; i += 2) {
uint16_t wdata = dataPort.Read();
data[i] = wdata & 0xFF;
if (i + 1 < count)
data[i + 1] = (wdata >> 8) & 0xFF;
// Debug print
char text; [github](https://github.com/pac-ac/osakaOS/issues)
text = data[i];
text = (i + 1 < count) ? data[i + 1] : '\0'; [github](https://github.com/pac-ac/osakaOS)
text = '\0'; [github](https://github.com/pac-ac/osakaOS/actions)
printf(text);
}
printf(" || DONE\n");
- Drain the rest of the sector (if
count< 512):
- Final 400 ns delay:
Writing Sectors (LBA28)¶
Preconditions and validation¶
- Same sector and count checks as
Read28:
Command setup¶
- Matches read but with WRITE command:
devicePort.Write((master ? 0xE0 : 0xF0) | ((sector & 0x0F000000) >> 24));
for (int i = 0; i < 15; i++)
controlPort.Read();
errorPort.Write(0);
sectorCountPort.Write(1);
controlPort.Write(0x02); // disable IRQs
lbaLowPort.Write(sector & 0xFF);
lbaMidPort.Write((sector >> 8) & 0xFF);
lbaHiPort.Write((sector >> 16) & 0xFF);
commandPort.Write(0x30); // WRITE SECTORS
- Polling for BSY cleared and DRQ set uses the same pattern as
Read28, with appropriate error messages.
Data transfer¶
- Writes
countbytes fromdata, two bytes at a time:
printf("Writing ATA: ");
for (uint16_t i = 0; i < count; i += 2) {
uint16_t wdata = data[i];
if (i + 1 < count)
wdata |= ((uint16_t)data[i + 1]) << 8;
// Debug print
char text; [github](https://github.com/pac-ac/osakaOS/issues)
text = wdata & 0xFF;
text = (wdata >> 8) & 0xFF; [github](https://github.com/pac-ac/osakaOS)
text = '\0'; [github](https://github.com/pac-ac/osakaOS/actions)
printf(text);
dataPort.Write(wdata);
}
printf(" || DONE\n");
- Fills the remainder of the 512‑byte sector with zeros:
Completion and error checking¶
- Waits for the device to finish writing:
- Checks for error:
if (status & 0x01) {
printf("ERROR after writing data\n");
uint8_t error = errorPort.Read();
printf("Error code: 0x");
printByte(error);
printf("\n");
return;
}
- Final 400 ns delay:
Flush Cache¶
Behavior¶
- Sends FLUSH CACHE command:
- If status is 0, returns immediately (no pending work):
- Otherwise, waits for BSY to clear or error:
while (((status & 0x80) == 0x80) && ((status & 0x01) != 0x01)) {
status = commandPort.Read();
}
if (status & 0x01) {
printf("ERROR IN ATA FLUSH\n");
uint8_t error = errorPort.Read();
printf("Error code: 0x");
printByte(error);
printf("\n");
return;
}
Usage and Integration¶
- A typical setup in
kernelMain(currently commented in your code) looks like:
// primary ATA master on 0x1F0 (standard IDE base for primary channel)
AdvancedTechnologyAttachment ata0m(0x1F0, true);
if (ata0m.Identify())
printf("ATA PRIMARY MASTER FOUND\n");
- Once identified, clients can:
uint8_t buffer;
ata0m.Read28(sectorNumber, buffer, 512);
// modify buffer
ata0m.Write28(sectorNumber, buffer, 512);
ata0m.Flush();
- This driver is meant to serve as a low-level backend for block‑oriented abstractions:
- A future block device API (e.g.,
BlockDeviceinterface). - Filesystem implementations (FAT, ext2, custom).
Invariants and Assumptions¶
- 28‑bit LBA mode only:
- Valid sector range is 0–(2²⁸−1); higher bits are rejected.
- Sector size is fixed at 512 bytes (
bytesPerSector). - Only single‑sector operations (
sectorCountPort = 1) are currently supported. - All operations are synchronous and blocking:
- Driver busy‑waits (polls status) and does not use interrupts for completion.
- Debug
printfoutput inRead28/Write28: - Intended for development; may be removed or gated behind a debug flag for production.
- Error handling:
- On errors, driver logs the error code from
errorPortbut does not attempt recovery or retries.
Open Questions / TODO¶
- Add multi‑sector read/write support for improved performance.
- Wrap this driver in a higher‑level block device interface with:
- Bounds checking.
- Caching.
- Better error propagation (return codes instead of only
printf). - Add support for secondary ATA channel (e.g.,
portBase = 0x170) and slave devices. - Introduce non‑blocking or interrupt-driven I/O paths for multitasking environments.
- Consider abstracting the debug output away from the driver (e.g., via a logging interface). ```