/* * This file is the driver for the PCI IDE devices. It handles detecting drives * and accessing them through the IDE interface. The ATA and ATAPI drivers * handle reading and writing to the devices individually through this. * This driver will access the drives in the best available way, presenting a * uniform interface for any other code to use. It also adds a device node to * the devfs mount for every device it finds, to provide access for the rest of * the OS easily. */ #include #include #include #include "ide.h" struct IDEChannelRegs ideChannels[2]; struct IDEDevice ideDevices[4]; uint8_t ideBuf[2048] = {0}; int ideIrqInvoked = 0; /* Read identification space */ static void ide_read_buffer(uint8_t channel, void *buffer, uint32_t dwords) { struct IDEChannelRegs *c = &ideChannels[channel]; insl(c->base, buffer, dwords); } /* Initialise an IDE drive */ static void ide_drive_init(uint32_t bar0, uint32_t bar1, uint32_t bar2, uint32_t bar3, uint32_t bar4) { ideChannels[IDE_PRIMARY].base = bar0 & ~3; ideChannels[IDE_PRIMARY].ctrl = bar1 & ~3; ideChannels[IDE_PRIMARY].busmaster = bar4 & ~3; ideChannels[IDE_SECONDARY].base = bar2 & ~3; ideChannels[IDE_SECONDARY].ctrl = bar3 & ~3; ideChannels[IDE_SECONDARY].busmaster = (bar4 & ~3) + 8; /* Disable IRQs */ ide_write(IDE_PRIMARY, IDE_REG_CONTROL, 2); ide_write(IDE_SECONDARY, IDE_REG_CONTROL, 2); /* Iterate all possible devices */ uint32_t count = 0; uint8_t channel, drive, err, type, status; struct IDEDevice *dev; for (count = 0; count < 4; count++) { dev = &ideDevices[count]; dev->reserved = 0; channel = count / 2; drive = count % 2; type = IDE_ATA; err = 0; ide_write(channel, IDE_REG_HDDEVSEL, 0xA0 | (drive << 4)); ide_write(channel, IDE_REG_COMMAND, IDE_CMD_IDENTIFY); if (ide_read(channel, IDE_REG_STATUS) == 0) continue; /* Wait for response */ while (1) { status = ide_read(channel, IDE_REG_STATUS); if ((status & IDE_SR_ERROR)) { err = 1; break; } if (!(status & IDE_SR_BUSY) && (status & IDE_SR_DATA_READY)) break; } /* Check type */ if (err) { uint8_t cl = ide_read(channel, IDE_REG_LBA1); uint8_t ch = ide_read(channel, IDE_REG_LBA2); if (cl == 0x14 && ch == 0xEB) type = IDE_ATAPI; else if (cl == 0x69 && ch == 0x96) type = IDE_ATAPI; else continue; ide_write(channel, IDE_REG_COMMAND, IDE_CMD_IDENTIFY_PACKET); } /* Fill in IDE Device structure */ ide_read_buffer(channel, ideBuf, 128); uint32_t size; uint16_t sig, cap, set; sig = *(uint16_t *) (ideBuf + IDE_IDENT_DEVICETYPE); cap = *(uint16_t *) (ideBuf + IDE_IDENT_CAPABILITIES); set = *(uint16_t *) (ideBuf + IDE_IDENT_COMMANDSETS); if (set & (1 << 26)) size = *(uint32_t *) (ideBuf + IDE_IDENT_MAX_LBA_EXT); else size = *(uint32_t *) (ideBuf + IDE_IDENT_MAX_LBA); dev->reserved = 1; dev->type = type; dev->channel = channel; dev->drive = drive; dev->signature = sig; dev->capabilities = cap; dev->commandSets = set; dev->size = size; uint32_t k; for (k = 0; k < 40; k += 2) { dev->model[k] = ideBuf[IDE_IDENT_MODEL + k + 1]; dev->model[k + 1] = ideBuf[IDE_IDENT_MODEL + k]; } dev->model[40] = 0; } } /* Read channel */ uint8_t ide_read(uint8_t channel, uint8_t reg) { uint8_t result; struct IDEChannelRegs *c = &ideChannels[channel]; if (reg > 0x07 && reg < 0x0C) ide_write(channel, IDE_REG_CONTROL, 0x80 | c->noint); if (reg < 0x08) result = inb(c->base + reg - 0); else if (reg < 0x0C) result = inb(c->base + reg - 6); else if (reg < 0x0E) result = inb(c->ctrl + reg - 10); else if (reg < 0x16) result = inb(c->busmaster + reg - 14); if (reg > 0x07 && reg < 0x0C) ide_write(channel, IDE_REG_CONTROL, c->noint); return result; } /* Write channel */ void ide_write(uint8_t channel, uint8_t reg, uint8_t data) { struct IDEChannelRegs *c = &ideChannels[channel]; if (reg > 0x07 && reg < 0x0C) ide_write(channel, IDE_REG_CONTROL, 0x80 | c->noint); if (reg < 0x08) outb(c->base + reg - 0, data); else if (reg < 0x0C) outb(c->base + reg - 6, data); else if (reg < 0x0E) outb(c->ctrl + reg - 10, data); else if (reg < 0x16) outb(c->busmaster + reg - 14, data); if (reg > 0x07 && reg < 0x0C) ide_write(channel, IDE_REG_CONTROL, c->noint); } /* Poll for status after command */ uint8_t ide_poll_status(uint8_t channel, uint32_t check) { uint8_t i, state; /* Wait for BUSY */ for (i = 0; i < 4; i++) ide_read(channel, IDE_REG_ALTSTATUS); /* Wait for BUSY to clear */ while (ide_read(channel, IDE_REG_STATUS) & IDE_SR_BUSY); /* Check the status */ if (check) { state = ide_read(channel, IDE_REG_STATUS); if (state & IDE_SR_ERROR) return ERROR_GENERIC; if (state & IDE_SR_WRITE_FAULT) return ERROR_WRITE_FAULT; if ((state & IDE_SR_DATA_READY) == 0) return ERROR_NOT_READY; } return ERROR_NONE; } /* Initialise a PCI IDE controller */ void pci_init_ide(uint8_t bus, uint8_t dev, uint8_t func) { uint8_t class, subclass, progif, header; class = pci_read_byte(bus, dev, func, 11); subclass = pci_read_byte(bus, dev, func, 10); progif = pci_read_byte(bus, dev, func, 9); header = pci_read_byte(bus, dev, func, 14); /* Parallel IDE defaults */ uint32_t bar[5] = {0x1F0, 0x3F6, 0x170, 0x376, 0x00}; /* Primary Native */ if (progif & (1 << 0)) { bar[0] = pci_read_dword(bus, dev, func, 0x10); bar[1] = pci_read_dword(bus, dev, func, 0x14); } /* Secondary Native */ if (progif & (1 << 2)) { bar[2] = pci_read_dword(bus, dev, func, 0x18); bar[3] = pci_read_dword(bus, dev, func, 0x1C); } /* Bus Master */ if (progif & (1 << 7)) bar[4] = pci_read_dword(bus, dev, func, 0x20); ide_drive_init(bar[0], bar[1], bar[2], bar[3], bar[4]); uint8_t i; for (i = 0; i < 4; i++) { if (!ideDevices[i].reserved) continue; if (ideDevices[i].type == IDE_ATA) ata_init(&ideDevices[i]); // if (ideDevices[i].type == IDE_ATAPI) // atapi_init(&ideDevices[i]); } }