#include <kernel/kernel.h> #include <kernel/driver.h> #include <kernel/cache.h> #include <stdlib.h> #include <wchar.h> #include <conio.h> #include <printf.h> #include <stdio.h> #include <os/blkdev.h> #include <errno.h> #include "ata.h" #include <kernel/debug.h> dword sysUpTime(); #define MAX_DRIVES 2 typedef struct atadrive_t atadrive_t; struct atadrive_t { device_t dev; word port; byte unit; word sectors, heads, cylinders, mult_max; wchar_t name[41]; dword total_sectors; }; typedef struct atactrl_t atactrl_t; struct atactrl_t { device_t dev; byte irq; atadrive_t* cur_drive; atadrive_t* drives[MAX_DRIVES]; }; typedef struct atapart_t atapart_t; struct atapart_t { device_t dev; device_t* drive; dword start_sector, total_sectors; }; bool ataWaitStatus(atadrive_t* ctx, word mask, word bits) { dword end; word stat; end = sysUpTime() + 2000; while (((stat = in(ctx->port + ATA_REG_STATUS)) & mask) != bits) { if (sysUpTime() >= end) { TRACE1("ATA: wait failed; stat = %x\n", stat); return false; } } return true; } void ataBlockToChs(atadrive_t* ctx, addr_t block, dword *cyl, dword *head, dword *sect) { *sect = block % ctx->sectors + 1; block /= ctx->sectors; *head = block % ctx->heads; block /= ctx->heads; *cyl = block; } size_t ataBlockRead(atadrive_t* ctx, addr_t start, size_t blocks, void* buffer) { unsigned short *buf; int cyl, head, sect; size_t i, read, per = min(blocks, ctx->mult_max); if (!ctx->heads || !ctx->sectors) return 0; buf = (unsigned short *) buffer; read = 0; while (read < blocks) { ataBlockToChs(ctx, start, &cyl, &head, §); TRACE4("%d = %d:%d:%d\t", start, cyl, head, sect); if (!ataWaitStatus(ctx, ATA_BUSY, 0)) return read; out(ctx->port + ATA_REG_DRVHD, ctx->unit); if (!ataWaitStatus(ctx, ATA_BUSY | ATA_READY, ATA_READY)) return read; out(ctx->port + ATA_REG_LOCYL, cyl & 0xff); out(ctx->port + ATA_REG_HICYL, (cyl >> 8) & 0xff); out(ctx->port + ATA_REG_SECTOR, sect); out(ctx->port + ATA_REG_DRVHD, head); out(ctx->port + ATA_REG_COUNT, per); out(ctx->port + ATA_REG_CMD, ATA_CMD_READ); if (!ataWaitStatus(ctx, ATA_BUSY | ATA_DRQ, ATA_DRQ)) return read; for (i = 0; i < 256 * per; i++) buf[i] = in16(ctx->port + ATA_REG_DATA); read += per; start += per; buf += per * 256; } return read; } bool ataStartRead(atadrive_t* ctx, addr_t start, size_t blocks) { int cyl, head, sect; size_t per = min(blocks, ctx->mult_max); atactrl_t* ctrl = (atactrl_t*) ctx->dev.config->parent; assert(ctrl != NULL); if (!ctx->heads || !ctx->sectors) return false; ataBlockToChs(ctx, start, &cyl, &head, §); TRACE4("%d = %d:%d:%d\t", start, cyl, head, sect); if (!ataWaitStatus(ctx, ATA_BUSY, 0)) return false; out(ctx->port + ATA_REG_DRVHD, ctx->unit); if (!ataWaitStatus(ctx, ATA_BUSY | ATA_READY, ATA_READY)) return false; out(ctx->port + ATA_REG_LOCYL, cyl & 0xff); out(ctx->port + ATA_REG_HICYL, (cyl >> 8) & 0xff); out(ctx->port + ATA_REG_SECTOR, sect); out(ctx->port + ATA_REG_DRVHD, head); out(ctx->port + ATA_REG_COUNT, per); out(ctx->port + ATA_REG_CMD, ATA_CMD_READ); return true; } size_t ataBlockWrite(atadrive_t* ctx, addr_t start, size_t blocks, const void* buffer) { return 0; } wchar_t *ataConvertName(const word* in_data, int off_start, int off_end) { static wchar_t ret_val[255]; int loop, loop1; for (loop = off_start, loop1 = 0; loop <= off_end; loop++) { ret_val [loop1++] = (char) (in_data [loop] / 256); /* Get High byte */ ret_val [loop1++] = (char) (in_data [loop] % 256); /* Get Low byte */ } for (loop1--; loop1 >= 0 && ret_val[loop1] == ' '; loop1--) ; ret_val[loop1 + 1] = '\0'; /* Make sure it ends in a NULL character */ return ret_val; } #pragma pack(push, 1) typedef struct partition_t partition_t; struct partition_t { byte bBoot; byte bStartHead; byte bStartSector; byte bStartCylinder; byte bSystem; byte bEndHead; byte bEndSector; byte bEndCylinder; dword dwStartSector; dword dwSectorCount; }; #pragma pack(pop) const wchar_t* part_type(int type) { switch (type) { case 0x00: return L"FDISK_TYPE_EMPTY"; case 0x01: return L"FDISK_TYPE_FAT12"; case 0x04: return L"FDISK_TYPE_FAT16_SMALL"; case 0x05: return L"FDISK_TYPE_EXTENDED"; case 0x06: return L"FDISK_TYPE_FAT16_BIG"; //case 0x07: case 0x0C: return L"FDISK_TYPE_FAT32"; case 0x0F: return L"FDISK_TYPE_NTFS"; case 0x82: return L"FDISK_TYPE_LINUX_SWAP"; case 0x83: return L"FDISK_TYPE_EXT2"; case 0xa5: return L"FDISK_TYPE_FREEBSD"; case 0xa6: return L"FDISK_TYPE_OPENBSD"; case 0xeb: return L"FDISK_TYPE_BFS"; default: return L"unknown"; } } void nsleep(dword ns) { } bool ataNextRequest(atadrive_t* ctx) { atactrl_t* ctrl; request_t *req, *next; assert(ctx->dev.config != NULL); ctrl = (atactrl_t*) ctx->dev.config->parent; assert(ctrl != NULL); if (ctx->dev.req_first == NULL) { ctrl->cur_drive = NULL; return true; // All requests are finished } req = ctx->dev.req_first; if (ctrl->cur_drive == NULL) { /* No requests are pending on the controller */ TRACE1("ata: starting request buf = %p\n", req->params.read.buffer); ctrl->cur_drive = ctx; req->user_length = req->params.read.length; req->params.read.length = 0; } else { assert(ctrl->cur_drive == ctx); /* There is a request on the controller, and it's ours */ if (req->params.read.length >= req->user_length) { TRACE0("ata: finished\n"); next = req->next; devFinishRequest(&ctx->dev, req); req = next; if (ctx->dev.req_first == NULL) { TRACE0("ata: this drive has finished\n"); ctrl->cur_drive = NULL; return true; } req = next; req->user_length = req->params.read.length; req->params.read.length = 0; } } if (req->params.read.length < req->user_length) { size_t toread = req->user_length - req->params.read.length; TRACE2("ata: pos = %d read %d\n", (size_t) (req->params.read.pos / 512), toread / 512); if (!ataStartRead(ctx, (size_t) (req->params.read.pos / 512), toread / 512)) wprintf(L"ata: ataStartRead failed\n"); /* Controller will generate an interrupt when the op has finished */ } return false; } bool ataRequest(device_t* dev, request_t* req) { atadrive_t* ctx = (atadrive_t*) dev; block_size_t* size; switch (req->code) { case DEV_REMOVE: hndFree(ctx); hndSignal(req->event, true); return true; case DEV_OPEN: case DEV_CLOSE: hndSignal(req->event, true); return true; case DEV_READ: case DEV_WRITE: TRACE0("ata: start request\n"); devStartRequest(dev, req); ataNextRequest(ctx); return true; case BLK_GETSIZE: size = (block_size_t*) req->params.buffered.buffer; size->block_size = 512; size->total_blocks = ctx->total_sectors; hndSignal(req->event, true); return true; default: wprintf(L"ataRequest: %c%c\n", req->code / 256, req->code % 256); } req->result = ENOTIMPL; return false; } bool ataPartitionRequest(device_t* dev, request_t* req) { atapart_t* ctx = (atapart_t*) dev; block_size_t* size; switch (req->code) { case DEV_REMOVE: hndFree(ctx); hndSignal(req->event, true); return true; case BLK_GETSIZE: size = (block_size_t*) req->params.buffered.buffer; size->block_size = 512; size->total_blocks = ctx->total_sectors; hndSignal(req->event, true); return true; case DEV_READ: case DEV_WRITE: req->params.read.pos += ctx->start_sector * 512; default: return ataRequest(ctx->drive, req); } } bool ataControllerRequest(device_t* dev, request_t* req) { atactrl_t* ctx = (atactrl_t*) dev; atadrive_t* drive; word *buf, per; int i; addr_t start; request_t* drvreq; size_t toread; switch (req->code) { case DEV_REMOVE: devRegisterIrq(dev, ctx->irq, false); hndFree(ctx); hndSignal(req->event, true); return true; case DEV_ISR: assert(req->params.isr.irq == ctx->irq); TRACE1("DEV_ISR: %d\t", req->params.isr.irq); drive = ctx->cur_drive; if (drive) { drvreq = drive->dev.req_first; if (drvreq) { toread = drvreq->user_length - drvreq->params.read.length; per = min(toread / 512, drive->mult_max); start = (size_t) (drvreq->params.read.length / 512); buf = (word*) (drvreq->params.read.buffer + start * 512); for (i = 0; i < 256 * per; i++) buf[i] = in16(drive->port + ATA_REG_DATA); drvreq->params.read.length += per * 512; drvreq->params.read.pos += per * 512; } while (ataNextRequest(drive)) { TRACE0("atactrl: Drive finished, looking for another\n"); ctx->cur_drive = NULL; drive = NULL; for (i = 0; i < MAX_DRIVES; i++) if (ctx->drives[i] && ctx->drives[i]->dev.req_first) { drive = ctx->drives[i]; break; } if (drive == NULL) { TRACE0("atactrl: No more drives; all finished!\n"); break; } } } return true; } req->result = ENOTIMPL; return false; } bool ataWaitTimeout(word port, byte mask, byte match, dword timeout) { dword end = sysUpTime() + timeout; byte stat; while (((stat = in(port)) & mask) != match) { if (sysUpTime() > end) { TRACE1("ata: wait failed, stat = 0x%02x\n", stat); return false; } } return true; } device_t* ataAddControllerDevice(driver_t* drv, const wchar_t* name, device_config_t* cfg) { word dd[256]; int dd_off, i, j; atadrive_t *drive; atapart_t *part; atactrl_t *ctrl; wchar_t str[10]; partition_t *parts; word port, irq; byte units[MAX_DRIVES] = { 0xA0, 0xB0 }; device_config_t *dcfg; device_t *cached; parts = (partition_t*) (dd + 0xdf); i = devFindResource(cfg, dresIo, 0); if (i > -1) port = cfg->resources[i].u.io.base; else port = 0x1F0; i = devFindResource(cfg, dresIrq, 0); if (i > -1) irq = cfg->resources[i].u.irq; else irq = 14; TRACE2("IDE controller: port %x irq %d\n", port, irq); /* soft reset both drives on this I/F (selects master) */ out(port + ATA_REG_DEVCTRL, 0x06); nsleep(400); /* release soft reset AND enable interrupts from drive */ out(port + ATA_REG_DEVCTRL, 0x00); nsleep(400); /* wait up to 2 seconds for status = BUSY=0 READY=1 DF=? DSC=? DRQ=? CORR=? IDX=? ERR=0 */ if (!ataWaitTimeout(port + 7, 0xC1, 0x40, 5000)) { wprintf(L"ata: no master detected\n"); return NULL; } ctrl = hndAlloc(sizeof(atactrl_t), NULL); ctrl->dev.driver = drv; ctrl->dev.request = ataControllerRequest; ctrl->dev.req_first = ctrl->dev.req_last = NULL; ctrl->dev.config = cfg; ctrl->irq = irq; ctrl->cur_drive = NULL; for (i = 0; i < countof(units); i++) /* Loop through drives on this controller */ { ctrl->drives[i] = NULL; TRACE0("Wait not busy..."); if (!ataWaitTimeout(port + 7, 0xff, 0x50, 2000)) continue; TRACE0("done\n"); out(port + 6, units[i]); /* Get first/second drive */ out(port + 7, 0xEC); /* Get drive info data */ TRACE0("Wait ready..."); if (!ataWaitTimeout(port + 7, 0xff, 0x58, 2000)) continue; TRACE0("done\n"); TRACE1("locyl = %x ", in(port + ATA_REG_LOCYL)); TRACE1("hicyl = %x\n", in(port + ATA_REG_HICYL)); for (dd_off = 0; dd_off != 256; dd_off++) /* Read "sector" */ dd[dd_off] = in16(port); drive = hndAlloc(sizeof(atadrive_t), NULL); drive->dev.driver = drv; drive->dev.request = ataRequest; drive->dev.req_first = drive->dev.req_last = NULL; drive->dev.config = NULL; drive->port = port; drive->unit = units[i]; drive->cylinders = dd[1]; drive->heads = dd[3]; drive->sectors = dd[6]; drive->total_sectors = drive->sectors * drive->heads * drive->cylinders; ctrl->drives[i] = drive; if (((dd[119] & 1) != 0) && (dd[118] != 0)) drive->mult_max = dd[94]; else drive->mult_max = 1; wcscpy(drive->name, ataConvertName(dd, 27, 46)); wcscat(drive->name, L" "); wcscat(drive->name, ataConvertName(dd, 10, 19)); swprintf(str, L"ide%d", i); dcfg = hndAlloc(sizeof(device_config_t), NULL); dcfg->parent = &ctrl->dev; dcfg->num_resources = 0; dcfg->resources = NULL; dcfg->device_id = dcfg->vendor_id = 0xffff; dcfg->subsystem = 0xffffffff; //cached = &drive->dev; devRegister(str, &drive->dev, dcfg); wprintf(L"Registered %s => %s [ ", str, drive->name); if (ataBlockRead(drive, 0, 1, dd)) { for (j = 0; j < 4; j++) { word c, h, s; c = parts[j].bStartCylinder | ((word) parts[j].bStartSector & 0xc0) << 2; h = parts[j].bStartHead; s = parts[j].bStartSector & 0x3f; if (parts[j].bSystem) { part = hndAlloc(sizeof(atapart_t), NULL); part->dev.driver = drv; part->dev.request = ataPartitionRequest; part->drive = &drive->dev; part->start_sector = parts[j].dwStartSector; part->total_sectors = parts[j].dwSectorCount; swprintf(str, L"ide%d%c", i, j + 'a'); dcfg = hndAlloc(sizeof(device_config_t), NULL); dcfg->parent = &ctrl->dev; dcfg->num_resources = 0; dcfg->resources = NULL; dcfg->device_id = dcfg->vendor_id = 0xffff; dcfg->subsystem = 0xffffffff; cached = ccInstallBlockCache(&part->dev, 512); devRegister(str, cached, dcfg); wprintf(L"%s ", part_type(parts[j].bSystem)); } } } _cputws(L"]\n"); } //wprintf(L"Finished detection!\n"); devRegisterIrq(&ctrl->dev, ctrl->irq, true); return &ctrl->dev; } bool STDCALL INIT_CODE drvInit(driver_t* drv) { drv->add_device = ataAddControllerDevice; return true; }