diff options
author | dzwdz | 2022-12-26 21:31:39 +0100 |
---|---|---|
committer | dzwdz | 2022-12-26 21:31:39 +0100 |
commit | 2041919a2bb6cd8f4ef8ef6565962cfa82bb89f7 (patch) | |
tree | f3db482225fc303968ce25695548aad063a410de | |
parent | 17e78ada9bfd6f1cb99b3e0f7913baa73c29054f (diff) |
amd64/ata: refactor the ATA driver
-rw-r--r-- | src/kernel/arch/amd64/ata.c | 85 | ||||
-rw-r--r-- | src/kernel/arch/amd64/ata.h | 4 | ||||
-rw-r--r-- | src/kernel/arch/amd64/driver/pata.c | 16 |
3 files changed, 61 insertions, 44 deletions
diff --git a/src/kernel/arch/amd64/ata.c b/src/kernel/arch/amd64/ata.c index d88f890..fa55335 100644 --- a/src/kernel/arch/amd64/ata.c +++ b/src/kernel/arch/amd64/ata.c @@ -1,6 +1,7 @@ #include <kernel/arch/amd64/ata.h> #include <kernel/arch/amd64/port_io.h> #include <kernel/panic.h> +#include <kernel/util.h> #include <stdbool.h> static struct { @@ -24,41 +25,44 @@ enum { STATUS = 7, CTRL = 0x206, -}; // offsets +}; /* offsets */ -// get I/O port base for drive static uint16_t ata_iobase(int drive) { bool secondary = drive&2; return secondary ? 0x170 : 0x1F0; } static void ata_400ns(void) { - uint16_t base = ata_iobase(0); // doesn't matter + uint16_t base = ata_iobase(0); /* the drive doesn't matter. */ for (int i = 0; i < 4; i++) port_in8(base + STATUS); } static void ata_driveselect(int drive, int lba) { uint8_t v = 0xE0; - if (drive&1) // slave? - v |= 0x10; // set drive number bit + if (drive&1) v |= 0x10; /* slave? */ v |= (lba >> 24) & 0xf; port_out8(ata_iobase(drive) + DRV, v); } +static int ata_poll(int drive, int timeout) { + uint16_t iobase = ata_iobase(drive); + /* if timeout < 0, cycle forever */ + while (timeout < 0 || timeout--) { + uint8_t v = port_in8(iobase + STATUS); + if (v & 0x80) continue; /* BSY */ + if (v & 0x40) return 0; /* RDY */ + // TODO check for ERR + } + return -1; +} + static void ata_softreset(int drive) { uint16_t iobase = ata_iobase(drive); port_out8(iobase + CTRL, 4); port_out8(iobase + CTRL, 0); ata_400ns(); - - uint16_t timeout = 10000; - while (--timeout) { // TODO separate polling function - uint8_t v = port_in8(iobase + STATUS); - if (v & 0x80) continue; // still BSY, continue - if (v & 0x40) break; // RDY, break - // TODO check for ERR - } + ata_poll(drive, 10000); } static void ata_detecttype(int drive) { @@ -113,8 +117,9 @@ static bool ata_identify(int drive) { void ata_init(void) { for (int i = 0; i < 4; i++) { ata_detecttype(i); - if (ata_drives[i].type == DEV_PATA) + if (ata_drives[i].type == DEV_PATA) { ata_identify(i); + } } } @@ -123,32 +128,46 @@ bool ata_available(int drive) { } size_t ata_size(int drive) { - return ata_drives[drive].sectors * ATA_SECTOR; + return ata_drives[drive].sectors * 512; } -int ata_read(int drive, uint32_t lba, void *buf) { - if (ata_drives[drive].type != DEV_PATA) +int ata_read(int drive, void *buf, size_t len, size_t off) { + uint32_t lba, skip, cnt; + if (ata_drives[drive].type != DEV_PATA) { panic_unimplemented(); - int iobase = ata_iobase(drive); + } + + lba = off / 512; + skip = off % 512; + cnt = (len - skip + 511) / 512; + if (skip) cnt += 1; + int iobase = ata_iobase(drive); ata_driveselect(drive, lba); - port_out8(iobase + FEAT, 0); // supposedly pointless - port_out8(iobase + SCNT, 1); // sector count + port_out8(iobase + FEAT, 0); /* supposedly pointless */ + port_out8(iobase + SCNT, cnt); port_out8(iobase + LBAlo, lba); port_out8(iobase + LBAmid, lba >> 8); port_out8(iobase + LBAhi, lba >> 16); - port_out8(iobase + CMD, 0x20); // READ SECTORS - - for (;;) { // TODO separate polling function - uint8_t v = port_in8(iobase + STATUS); - if (v & 0x80) continue; // still BSY, continue - if (v & 0x40) break; // RDY, break - // TODO check for ERR + port_out8(iobase + CMD, 0x20); /* READ SECTORS */ + + for (uint32_t i = 0; i < cnt; i++) { + union { + uint16_t s; + char b[2]; + } d; + ata_poll(drive, -1); + for (int j = 0; j < 256; j++) { + d.s = port_in16(iobase); + for (int k = 0; k < 2; k++) { + size_t byte = i * 512 + j * 2 + k; + if (byte < skip) continue; + byte -= skip; + if (byte < len) { + ((char*)buf)[byte] = d.b[k]; + } + } + } } - - uint16_t *b = buf; - for (int i = 0; i < 256; i++) - b[i] = port_in16(iobase); - - return 512; + return len; } diff --git a/src/kernel/arch/amd64/ata.h b/src/kernel/arch/amd64/ata.h index 0c20281..c43ac23 100644 --- a/src/kernel/arch/amd64/ata.h +++ b/src/kernel/arch/amd64/ata.h @@ -3,9 +3,7 @@ #include <stddef.h> #include <stdint.h> -#define ATA_SECTOR 512 - void ata_init(void); bool ata_available(int drive); size_t ata_size(int drive); -int ata_read(int drive, uint32_t lba, void *buf); +int ata_read(int drive, void *buf, size_t len, size_t off); diff --git a/src/kernel/arch/amd64/driver/pata.c b/src/kernel/arch/amd64/driver/pata.c index 3118e86..77b654c 100644 --- a/src/kernel/arch/amd64/driver/pata.c +++ b/src/kernel/arch/amd64/driver/pata.c @@ -20,6 +20,8 @@ void pata_init(void) { static void accept(struct vfs_request *req) { int ret; long id = (long __force)req->id; + char wbuf[4096]; + size_t len; switch (req->type) { case VFSOP_OPEN: if (reqpathcmp(req, "/")) ret = root_id; @@ -28,6 +30,7 @@ static void accept(struct vfs_request *req) { else if (reqpathcmp(req, "/2")) ret = 2; else if (reqpathcmp(req, "/3")) ret = 3; else ret = -ENOENT; + // TODO don't allow opening nonexistent drives vfsreq_finish_short(req, ret); break; @@ -46,13 +49,9 @@ static void accept(struct vfs_request *req) { break; } fs_normslice(&req->offset, &req->output.len, ata_size(id), false); - - char buf[512]; /* stupid */ - uint32_t sector = req->offset / ATA_SECTOR; - size_t skip = (size_t)req->offset & (ATA_SECTOR - 1); - size_t len = min(req->output.len, ATA_SECTOR - skip); - ata_read(id, sector, buf); - virt_cpy_to(req->caller->pages, req->output.buf, buf + skip, len); + len = min(req->output.len, sizeof wbuf); + ata_read(id, wbuf, len, req->offset); + virt_cpy_to(req->caller->pages, req->output.buf, wbuf, len); vfsreq_finish_short(req, len); break; @@ -60,8 +59,9 @@ static void accept(struct vfs_request *req) { panic_unimplemented(); case VFSOP_GETSIZE: - if (id == root_id) + if (id == root_id) { panic_unimplemented(); + } vfsreq_finish_short(req, ata_size(id)); break; |