BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / commit / e4c4dfe6cc8f70e0d4919a434e62f1d061477add

// Related

Nucleus

Barry Driver core e4c4dfe (3 years, 2 months ago)
diff --git a/drivers/devices.c b/drivers/devices.c
new file mode 100644
index 0000000..19ec2e5
--- /dev/null
+++ b/drivers/devices.c
@@ -0,0 +1,63 @@
+/*
+ * This is the main file of the PCI driver core.  It contains the PCI Device
+ * database and controls device initialisation.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <nucleus/pci.h>
+#include <nucleus/panic.h>
+
+/* Structure for a Device */
+struct Device {
+	uint16_t vendor, device;
+	char *name;
+	void (*init)(uint8_t, uint8_t, uint8_t);
+};
+
+void pci_init_ide(uint8_t bus, uint8_t dev, uint8_t func);
+
+/* Device database */
+struct Device devices[] = {
+	/* Intel system devices */
+	{0x8086, 0x1237, "Intel i440FX Chipset", NULL},
+	{0x8086, 0x7000, "Intel PIIX3 PCI-to-ISA Bridge (Triton II)", NULL},
+	{0x8086, 0x7010, "Intel PIIX3 IDE Interface (Triton II)", pci_init_ide},
+	{0x8086, 0x7020, "Intel PIIX3 USB (Natoma/Triton II)", NULL},
+	{0x8086, 0x7113, "Intel PIIX4/4E/4M Power Management Controller", NULL},
+
+	/* Network devices */
+	{0x10EC, 0x8139, "Realtek RTL8139 10/100 NIC", NULL},
+	{0x8086, 0x100E, "Intel Pro 1000/MT NIC", NULL},
+
+	/* Virtual devices */
+	{0x1B36, 0x000D, "QEMU XHCI Host Adapter", NULL},
+	{0x1234, 0x1111, "QEMU/Bochs VBE Framebuffer", NULL},
+};
+
+/* Initialise a PCI device */
+void
+init_pci_device(int bus, int dev, int func)
+{
+	uint32_t d;
+	uint16_t vendor, device;
+
+	vendor = pci_read_word(bus, dev, func, 0);
+	device = pci_read_word(bus, dev, func, 2);
+
+	/* Lookup device details */
+	for (d = 0; d < sizeof(devices) / sizeof(struct Device); d++) {
+		if (devices[d].vendor != vendor
+		 || devices[d].device != device)
+			continue;
+		kprintf("PCI(%d,%d,%d) \"%s\"", bus, dev, func,
+		        devices[d].name);
+		if (devices[d].init)
+			devices[d].init(bus, dev, func);
+		return;
+	}
+
+	/* No match */
+	kprintf("PCI(%d,%d,%d) = %#.4x:%#.4x", bus, dev, func,
+	        vendor, device);
+}
diff --git a/drivers/storage/ata.c b/drivers/storage/ata.c
new file mode 100644
index 0000000..4d26ab2
--- /dev/null
+++ b/drivers/storage/ata.c
@@ -0,0 +1,209 @@
+/*
+ * This is the driver for ATA storage devices.  It controls access to the drives
+ * and contains the devfs functions.  It uses the IDE driver for access to the
+ * actual hardware.
+ */
+
+#include <stdint.h>
+#include <io.h>
+#include <nucleus/panic.h>
+#include <nucleus/vfs.h>
+#include "ide.h"
+
+/* Structure for a MBR Partition entry */
+struct Partition {
+	uint8_t drive;
+	uint8_t shead, ssect, scyl;
+	uint8_t type;
+	uint8_t ehead, esect, ecyl;
+	uint32_t lba;
+	uint32_t size;
+};
+
+size_t ata_dev_read(File *file, char *buf, size_t size, off_t offset);
+size_t ata_dev_write(File *file, char *buf, size_t size, off_t offset);
+
+FileOps ataFileOps = {
+	.read = ata_dev_read,
+	.write = ata_dev_write,
+};
+
+/* Access an ATA drive */
+static uint8_t
+ata_access(int write, struct IDEDevice *dev, uint32_t lba, uint8_t sectors,
+           void *buf)
+{
+	struct IDEChannelRegs *channel = &ideChannels[dev->channel];
+	uint8_t lbaMode, dma, cmd;
+	uint8_t lbaIO[6];
+	uint16_t cyl, i;
+	uint8_t head, sect, err;
+
+//	ideIrqInvoked = 0;
+	channel->noint = 2;
+	ide_write(dev->channel, IDE_REG_CONTROL, channel->noint);
+
+	/* Configure access mode */
+	if (lba >= 0x10000000) {
+		lbaMode = LBA_48;
+		lbaIO[0] = (lba & 0x000000FF) >> 0;
+		lbaIO[1] = (lba & 0x0000FF00) >> 8;
+		lbaIO[2] = (lba & 0x00FF0000) >> 16;
+		lbaIO[3] = (lba & 0xFF000000) >> 24;
+		lbaIO[4] = 0;
+		lbaIO[5] = 0;
+		head = 0;
+	} else if (dev->capabilities & 0x200) {
+		lbaMode = LBA_28;
+		lbaIO[0] = (lba & 0x000000FF) >> 0;
+		lbaIO[1] = (lba & 0x0000FF00) >> 8;
+		lbaIO[2] = (lba & 0x00FF0000) >> 16;
+		lbaIO[3] = 0;
+		lbaIO[4] = 0;
+		lbaIO[5] = 0;
+		head = (lba & 0x0F000000) >> 24;
+	} else {
+		lbaMode = LBA_CHS;
+		sect = (lba % 63) + 1;
+		cyl = (lba + 1 - sect) / (16 * 63);
+		lbaIO[0] = sect;
+		lbaIO[1] = (cyl >> 0) & 0xFF;
+		lbaIO[2] = (cyl >> 8) & 0xFF;
+		lbaIO[3] = 0;
+		lbaIO[4] = 0;
+		lbaIO[5] = 0;
+		head = (lba + 1 - sect) % (16 * 63) / 63;
+	}
+
+	dma = 0; //dev->busmaster;
+
+	/* Wait for device to become available */
+	while (ide_read(dev->channel, IDE_REG_STATUS) & IDE_SR_BUSY);
+
+	/* Select drive */
+	if (lbaMode == LBA_CHS)
+		ide_write(dev->channel, IDE_REG_HDDEVSEL,
+		          0xA0 | (dev->drive << 4) | head);
+	else
+		ide_write(dev->channel, IDE_REG_HDDEVSEL,
+		          0xE0 | (dev->drive << 4) | head);
+
+	/* Specify sectors to read */
+	if (lbaMode = LBA_48) {
+		ide_write(dev->channel, IDE_REG_SECCOUNT1, 0);
+		ide_write(dev->channel, IDE_REG_LBA3, lbaIO[3]);
+		ide_write(dev->channel, IDE_REG_LBA4, lbaIO[4]);
+		ide_write(dev->channel, IDE_REG_LBA5, lbaIO[5]);
+	}
+	ide_write(dev->channel, IDE_REG_SECCOUNT0, sectors);
+	ide_write(dev->channel, IDE_REG_LBA0, lbaIO[0]);
+	ide_write(dev->channel, IDE_REG_LBA1, lbaIO[1]);
+	ide_write(dev->channel, IDE_REG_LBA2, lbaIO[2]);
+
+	/* Send command */
+	if (lbaMode == LBA_CHS && !dma && !write) cmd = IDE_CMD_READ_PIO;
+	if (lbaMode == LBA_28  && !dma && !write) cmd = IDE_CMD_READ_PIO;
+	if (lbaMode == LBA_48  && !dma && !write) cmd = IDE_CMD_READ_PIO_EXT;
+	if (lbaMode == LBA_CHS &&  dma && !write) cmd = IDE_CMD_READ_DMA;
+	if (lbaMode == LBA_28  &&  dma && !write) cmd = IDE_CMD_READ_DMA;
+	if (lbaMode == LBA_48  &&  dma && !write) cmd = IDE_CMD_READ_DMA_EXT;
+	if (lbaMode == LBA_CHS && !dma &&  write) cmd = IDE_CMD_WRITE_PIO;
+	if (lbaMode == LBA_28  && !dma &&  write) cmd = IDE_CMD_WRITE_PIO;
+	if (lbaMode == LBA_48  && !dma &&  write) cmd = IDE_CMD_WRITE_PIO_EXT;
+	if (lbaMode == LBA_CHS &&  dma &&  write) cmd = IDE_CMD_WRITE_DMA;
+	if (lbaMode == LBA_28  &&  dma &&  write) cmd = IDE_CMD_WRITE_DMA;
+	if (lbaMode == LBA_48  &&  dma &&  write) cmd = IDE_CMD_WRITE_DMA_EXT;
+	ide_write(dev->channel, IDE_REG_COMMAND, cmd);
+
+	/* Read/Write data */
+	if (dma) {
+		/* Use DMA */
+		if (write) {
+			/* TODO */
+		} else {
+			/* TODO */
+		}
+	} else {
+		/* Use PIO */
+		if (write) {
+			for (i = 0; i < sectors; i++) {
+				ide_poll_status(dev->channel, 0);
+				outsw(channel->base, buf + (i * 512), 256);
+			}
+			ide_write(dev->channel, IDE_REG_COMMAND,
+			          (lbaMode == LBA_48)
+			          ? IDE_CMD_CACHE_FLUSH_EXT
+			          : IDE_CMD_CACHE_FLUSH);
+			ide_poll_status(dev->channel, 0);
+		} else {
+			for (i = 0; i < sectors; i++) {
+				if (err = ide_poll_status(dev->channel, 1))
+					return err;
+				insw(channel->base, buf + (i * 512), 256);
+			}
+		}
+	}
+
+	return 0;
+}
+
+/* Read from an ATA drive */
+static inline uint8_t
+ata_read(struct IDEDevice *dev, uint32_t lba, uint8_t sectors, void *buf)
+{
+	ata_access(0, dev, lba, sectors, buf);
+}
+
+/* Write to an ATA drive */
+static inline uint8_t
+ata_write(struct IDEDevice *dev, uint32_t lba, uint8_t sectors, void *buf)
+{
+	ata_access(1, dev, lba, sectors, buf);
+}
+
+/* Initialise an ATA drive */
+void
+ata_init(struct IDEDevice *dev)
+{
+	static int drive = 0;
+	static void *ataDmaArea = NULL;
+	if (!ataDmaArea)
+		ataDmaArea = (void *) alloc_frame();
+
+	/* Create device node */
+	char name[16];
+	sprintf(name, "/dev/hd%d", drive);
+//	mknod(name, S_IFBLK | 0600, MKDEV());
+	kprintf("  ATA drive [%s] (%d MB) %s", name,
+	        dev->size / 1024 / 2, dev->model);
+
+	/* Attempt to read partition table */
+	int i;
+	struct Partition *part = ataDmaArea + 446;
+	if (ata_read(dev, 0, 1, ataDmaArea))
+		return;
+	for (i = 0; i < 4; i++) {
+		if (!part[i].type)
+			continue;
+		sprintf(name, "/dev/hd%dp%d", drive, i);
+//		mknod(name, S_IFBLK | 0600, MKDEV(ideDriver.major, ());
+		kprintf("    Partition [%s] %#.8x - %#.8x", name,
+		        part[i].lba*512, (part[i].lba + part[i].size) * 512);
+	}
+
+	drive++;
+}
+
+/* Read from an ATA drive */
+size_t
+ata_dev_read(File *file, char *buf, size_t size, off_t offset)
+{
+	
+}
+
+/* Write to an ATA drive */
+size_t
+ata_dev_write(File *file, char *buf, size_t size, off_t offset)
+{
+	
+}
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]);
+	}
+}
diff --git a/drivers/storage/ide.h b/drivers/storage/ide.h
new file mode 100644
index 0000000..b59bf18
--- /dev/null
+++ b/drivers/storage/ide.h
@@ -0,0 +1,124 @@
+#ifndef DRIVERS_STORAGE_IDE_H
+#define DRIVERS_STORAGE_IDE_H
+
+/* IDE Types */
+enum IDEType {
+	IDE_ATA,
+	IDE_ATAPI,
+};
+
+/* LBA Modes */
+enum LBAMode {
+	LBA_CHS,
+	LBA_28,
+	LBA_48,
+};
+
+/* IDE Poll Response Errors */
+enum IDEPollError {
+	ERROR_NONE,
+	ERROR_GENERIC,
+	ERROR_WRITE_FAULT,
+	ERROR_NOT_READY,
+};
+
+/* IDE Status Responses */
+enum IDEStatus {
+	IDE_SR_BUSY = 0x80,
+	IDE_SR_READY = 0x40,
+	IDE_SR_WRITE_FAULT = 0x20,
+	IDE_SR_SEEK_COMPLETE = 0x10,
+	IDE_SR_DATA_READY = 0x08,
+	IDE_SR_CORRECTED_DATA = 0x04,
+	IDE_SR_INDEX = 0x02,
+	IDE_SR_ERROR = 0x01,
+};
+
+/* IDE Commands */
+enum IDECommand {
+	IDE_CMD_READ_PIO = 0x20,
+	IDE_CMD_READ_PIO_EXT = 0x24,
+	IDE_CMD_READ_DMA = 0xC8,
+	IDE_CMD_READ_DMA_EXT = 0x25,
+	IDE_CMD_WRITE_PIO = 0x30,
+	IDE_CMD_WRITE_PIO_EXT = 0x34,
+	IDE_CMD_WRITE_DMA = 0xCA,
+	IDE_CMD_WRITE_DMA_EXT = 0x35,
+	IDE_CMD_CACHE_FLUSH = 0xE7,
+	IDE_CMD_CACHE_FLUSH_EXT = 0xEA,
+	IDE_CMD_PACKET = 0xA0,
+	IDE_CMD_IDENTIFY_PACKET = 0xA1,
+	IDE_CMD_IDENTIFY = 0xEC,
+};
+
+/* IDE Identities */
+enum IDEIdentity {
+	IDE_IDENT_DEVICETYPE = 0,
+	IDE_IDENT_CYLINDERS = 2,
+	IDE_IDENT_HEADS = 6,
+	IDE_IDENT_SECTORS = 12,
+	IDE_IDENT_SERIAL = 20,
+	IDE_IDENT_MODEL = 54,
+	IDE_IDENT_CAPABILITIES = 98,
+	IDE_IDENT_FIELDVALID = 106,
+	IDE_IDENT_MAX_LBA = 120,
+	IDE_IDENT_COMMANDSETS = 164,
+	IDE_IDENT_MAX_LBA_EXT = 200,
+};
+
+/* IDE Registers */
+enum IDERegister {
+	IDE_REG_DATA = 0x00,
+	IDE_REG_ERROR = 0x01,
+	IDE_REG_FEATURES = 0x01,
+	IDE_REG_SECCOUNT0 = 0x02,
+	IDE_REG_LBA0 = 0x03,
+	IDE_REG_LBA1 = 0x04,
+	IDE_REG_LBA2 = 0x05,
+	IDE_REG_HDDEVSEL = 0x06,
+	IDE_REG_COMMAND = 0x07,
+	IDE_REG_STATUS = 0x07,
+	IDE_REG_SECCOUNT1 = 0x08,
+	IDE_REG_LBA3 = 0x09,
+	IDE_REG_LBA4 = 0x0A,
+	IDE_REG_LBA5 = 0x0B,
+	IDE_REG_CONTROL = 0x0C,
+	IDE_REG_ALTSTATUS = 0x0C,
+	IDE_REG_DEVADDRESS = 0x0D,
+};
+
+/* IDE Channels */
+enum IDEChannel {
+	IDE_PRIMARY,
+	IDE_SECONDARY,
+};
+struct IDEChannelRegs {
+	uint16_t base;
+	uint16_t ctrl;
+	uint16_t busmaster;
+	uint8_t noint;
+};
+
+/* Structure for an IDE Device */
+struct IDEDevice {
+	uint8_t reserved;
+	uint8_t channel;
+	uint8_t drive;
+	uint16_t type;
+	uint16_t signature;
+	uint16_t capabilities;
+	uint32_t commandSets;
+	uint32_t size;
+	char model[41];
+};
+
+extern struct IDEChannelRegs ideChannels[];
+extern struct IDEDevice ideDevices[];
+
+uint8_t ide_read(uint8_t channel, uint8_t reg);
+void ide_write(uint8_t channel, uint8_t reg, uint8_t data);
+uint8_t ide_poll_status(uint8_t channel, uint32_t check);
+
+void ata_init(struct IDEDevice *dev);
+
+#endif
diff --git a/kernel/pci/pci.h b/include/nucleus/pci.h
similarity index 81%
rename from kernel/pci/pci.h
rename to include/nucleus/pci.h
index b61dec3..3811908 100644
--- a/kernel/pci/pci.h
+++ b/include/nucleus/pci.h
@@ -1,5 +1,5 @@
-#ifndef KERNEL_PCI_H
-#define KERNEL_PCI_H
+#ifndef _NUCLEUS_PCI_H
+#define _NUCLEUS_PCI_H
 
 #include <stdint.h>
 
@@ -10,6 +10,6 @@ void pci_write_byte(int bus, int dev, int func, int off, uint8_t value);
 void pci_write_word(int bus, int dev, int func, int off, uint16_t value);
 void pci_write_dword(int bus, int dev, int func, int off, uint32_t value);
 
-void init_pci(void);
+void init_pci_device(int bus, int dev, int func);
 
 #endif
diff --git a/kernel/acpi/acpi.h b/kernel/acpi/acpi.h
index 0c39b19..13d79a7 100644
--- a/kernel/acpi/acpi.h
+++ b/kernel/acpi/acpi.h
@@ -16,5 +16,6 @@ struct SDTHeader {
 
 void init_table(struct SDTHeader *table);
 void init_acpi(void *ebda);
+void init_pci(void);
 
 #endif
diff --git a/kernel/pci/pci.c b/kernel/acpi/pci.c
similarity index 77%
rename from kernel/pci/pci.c
rename to kernel/acpi/pci.c
index 408aeb8..b7c74c1 100644
--- a/kernel/pci/pci.c
+++ b/kernel/acpi/pci.c
@@ -1,13 +1,18 @@
 /*
- *
+ * This file controls the PCI interface.  It contains utility functions for
+ * accessing PCI configuration space.  It scans all devices connected to the
+ * system and tries to initialise them.
  */
 
 #include <stdint.h>
 #include <io.h>
-#include "pci.h"
+#include <nucleus/pci.h>
 
-const uint16_t CONFIG_ADDRESS = 0xCF8;
-const uint16_t CONFIG_DATA = 0xCFC;
+/* PCI Configuration Ports */
+enum PCIConfigPort {
+	CONFIG_ADDRESS = 0xCF8,
+	CONFIG_DATA = 0xCFC,
+};
 
 /* Make a PCI Address */
 static inline uint32_t
@@ -69,18 +74,11 @@ pci_write_dword(int bus, int dev, int func, int off, uint32_t value)
 void
 init_pci(void)
 {
-	uint16_t bus, slot, func;
-	uint16_t vendor, device;
-	uint8_t class, subclass;
-	uint32_t dev;
+	uint16_t bus, dev, func;
 	/* This is one massive iteration */
 	for (bus = 0; bus < 256; bus++)
-	for (slot = 0; slot < 32; slot++)
+	for (dev = 0; dev < 32; dev++)
 	for (func = 0; func < 8; func++)
-	if ((vendor = pci_read_word(bus, slot, func, 0)) != 0xFFFF) {
-		device = pci_read_word(bus, slot, func, 2);
-		/* do something */
-//		kprintf("PCI(%d,%d,%d) = %#.4x:%#.4x",
-//		        bus, slot, func, vendor, device);
-	}
+	if (pci_read_word(bus, dev, func, 0) != 0xFFFF)
+		init_pci_device(bus, dev, func);
 }
diff --git a/kernel/main.c b/kernel/main.c
index 0b45511..7d37563 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -13,7 +13,6 @@
 #include "multiboot.h"
 #include "desc.h"
 #include "acpi/acpi.h"
-#include "pci/pci.h"
 
 extern char _bss[], _end[];