diff options
Diffstat (limited to 'src/kernel/ata.c')
-rw-r--r-- | src/kernel/ata.c | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/src/kernel/ata.c b/src/kernel/ata.c new file mode 100644 index 0000000..0e6cc4e --- /dev/null +++ b/src/kernel/ata.c @@ -0,0 +1,113 @@ +#include <stdint.h> +#include "panic.h" +#include "util.h" +#include "ata.h" + +enum { + AP_PRIMARY = 0x01f0, + AP_SECONDARY = 0x0170, + + AP_DATA = 0x0, + AP_ERROR = 0x1, + AP_FEAT = 0x1, + AP_COUNT = 0x2, + AP_SEC = 0x3, + AP_CYL_L = 0x4, + AP_CYL_H = 0x5, + AP_HD_DR = 0x6, + AP_LBA0 = 0x3, + AP_LBA1 = 0x4, + AP_LBA2 = 0x5, + AP_L3_DR = 0x6, + AP_STAT = 0x7, + AP_CMD = 0x7, + + APC_STAT = 0x0206, + APC_CTRL = 0x0206, + APC_ADDR = 0x0207 +}; + +enum { + ADR_BASE = 0xa0, + ADR_CASCD = 0x10, + ADR_LBA = 0x40 +}; + +enum { + AS_ERROR = 0x01, + AS_INDEX = 0x02, + AS_CORRECT = 0x04, + AS_DREADY = 0x08, + AS_SERVICE = 0x10, + AS_FAULT = 0x20, + AS_READY = 0x40, + AS_BUSY = 0x80 +}; + +enum { + AC_READ = 0x20, + AC_WRITE = 0x30, + AC_FLUSH = 0xe7 +}; + +void ata_ldelay() { + uint16_t i = 65535; + while (i--) + ; +} + +void ata_sdelay() { + uint8_t i = 255; + while (i--) + ; +} + +void ata_dpoll() { + uint8_t s; + while ((s = inb(AP_PRIMARY | AP_STAT) & (AS_BUSY | AS_DREADY)) != AS_DREADY) + if (s & (AS_ERROR | AS_FAULT)) + panic("ATA controller error or fault."); +} + +uint8_t read_sectors(uint16_t start, uint8_t count, void *buffer) { + outb(AP_PRIMARY | AP_L3_DR, ADR_BASE | ADR_LBA); + outb(AP_PRIMARY | AP_LBA2, 0); + outb(AP_PRIMARY | AP_LBA1, start >> 8); + outb(AP_PRIMARY | AP_LBA0, start & 0xff); + outb(AP_PRIMARY | AP_COUNT, count); + outb(AP_PRIMARY | AP_CMD, AC_READ); + uint8_t i = count - 1; + uint16_t *ptr = buffer; + do { + ata_dpoll(); + uint8_t j = 255; + do + *(ptr++) = inw(AP_PRIMARY | AP_DATA); + while (j--); + ata_ldelay(); + } while (i--); + return count;//TODO: return early if necessary +} + +uint8_t write_sectors(uint16_t start, uint8_t count, void *buffer) { + outb(AP_PRIMARY | AP_L3_DR, ADR_BASE | ADR_LBA); + outb(AP_PRIMARY | AP_LBA2, 0); + outb(AP_PRIMARY | AP_LBA1, start >> 8); + outb(AP_PRIMARY | AP_LBA0, start & 0xff); + outb(AP_PRIMARY | AP_COUNT, count); + outb(AP_PRIMARY | AP_CMD, AC_WRITE); + uint8_t i = 0; + uint16_t *ptr = buffer; + do { + ata_dpoll(); + uint8_t j = 0; + do { + ata_sdelay(); + outw(AP_PRIMARY | AP_DATA, *(ptr++)); + } + while (++j); + ata_ldelay(); + } while (++i != count); + outb(AP_PRIMARY | AP_CMD, AC_FLUSH); + return count;//TODO: return early if necessary +}
\ No newline at end of file |