Nucleus
Barry Driver core e4c4dfe (3 years, 2 months ago)
diff --git a/drivers/storage/ide.c b/drivers/storage/ide.c
new file mode 100644
index 0000000..0cc9253
--- /dev/null
+++ b/drivers/storage/ide.c
@@ -0,0 +1,222 @@
+/*
+ * 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 <stdint.h>
+#include <io.h>
+#include <nucleus/pci.h>
+#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]);
+ }
+}