BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / commit / d41a53cbc7d055b1c00cf0a339dbed6925f4f02c / drivers / ide

// Related

Orion

Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
new file mode 100644
index 0000000..9e3b73a
--- /dev/null
+++ b/drivers/ide/ide.c
@@ -0,0 +1,685 @@
+/*
+ * This file is the driver for the PCI IDE devices, which handles access to
+ * ATA/ATAPI devices.  It detects the drives connected to the system, and
+ * handles any access to them - with the ability to do DMA if possible.  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, to provide access to the rest of the OS easily.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include "../drivers.h"
+#include "../pci.h"
+#include "../../mem/frame.h"
+#include "../../proc/proc.h"
+#include "../../vfs/vfs.h"
+#include "../../io.h"
+#include "../../screen.h"
+
+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_read(File *file, char *buf, size_t size, off_t offset);
+size_t ata_write(File *file, char *buf, size_t size, off_t offset);
+int ata_open(File *file);
+
+FileOps ideFileOps = {
+	.read = ata_read,
+	.write = ata_write,
+	.open = ata_open,
+};
+
+Driver ideDriver = {
+	.major = 2,
+	.ops = &ideFileOps,
+	.next = NULL,
+};
+
+page_t ataDmaArea;
+
+uint8_t ide_read(uint8_t chan, uint8_t reg);
+void ide_write(uint8_t chan, uint8_t reg, uint8_t data);
+
+/* Status */
+#define ATA_SR_BSY  0x80 /* Busy */
+#define ATA_SR_DRDY 0x40 /* Drive ready */
+#define ATA_SR_DF   0x20 /* Drive write fault */
+#define ATA_SR_DSC  0x10 /* Drive seek complete */
+#define ATA_SR_DRQ  0x08 /* Data request ready */
+#define ATA_SR_CORR 0x04 /* Corrected data */
+#define ATA_SR_IDX  0x02 /* Index */
+#define ATA_SR_ERR  0x01 /* Error */
+
+/* Error */
+#define ATA_ER_BBK  0x80 /* Bad block */
+#define ATA_ER_UNC  0x40 /* Uncorrectable data */
+#define ATA_ER_MC   0x20 /* Media changed */
+#define ATA_ER_IDNF 0x10 /* ID mark not found */
+#define ATA_ER_MCR  0x08 /* Media change request */
+#define ATA_ER_ABRT 0x04 /* Command aborted */
+#define ATA_ER_T0NF 0x02 /* Track 0 not found */
+#define ATA_ER_AMNF 0x01 /* No address mark */
+
+/* Command */
+#define ATA_CMD_READ_PIO        0x20
+#define ATA_CMD_READ_PIO_EXT    0x24
+#define ATA_CMD_READ_DMA        0xC8
+#define ATA_CMD_READ_DMA_EXT    0x25
+#define ATA_CMD_WRITE_PIO       0x30
+#define ATA_CMD_WRITE_PIO_EXT   0x34
+#define ATA_CMD_WRITE_DMA       0xCA
+#define ATA_CMD_WRITE_DMA_EXT   0x35
+#define ATA_CMD_CACHE_FLUSH     0xE7
+#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
+#define ATA_CMD_PACKET          0xA0
+#define ATA_CMD_IDENTIFY_PACKET 0xA1
+#define ATA_CMD_IDENTIFY        0xEC
+
+#define ATAPI_CMD_READ  0xA8
+#define ATAPI_CMD_EJECT 0x1B
+
+#define ATA_IDENT_DEVICETYPE     0
+#define ATA_IDENT_CYLINDERS      2
+#define ATA_IDENT_HEADS          6
+#define ATA_IDENT_SECTORS       12
+#define ATA_IDENT_SERIAL        20
+#define ATA_IDENT_MODEL         54
+#define ATA_IDENT_CAPABILITIES  98
+#define ATA_IDENT_FIELDVALID   106
+#define ATA_IDENT_MAX_LBA      120
+#define ATA_IDENT_COMMANDSETS  164
+#define ATA_IDENT_MAX_LBA_EXT  200
+
+#define IDE_ATA       0
+#define IDE_ATAPI     1
+#define ATA_MASTER    0
+#define ATA_SLAVE     1
+#define ATA_PRIMARY   0
+#define ATA_SECONDARY 1
+#define ATA_READ      0
+#define ATA_WRITE     1
+
+/* Registers */
+#define ATA_REG_DATA       0x0
+#define ATA_REG_ERROR      0x1
+#define ATA_REG_FEATURES   0x1
+#define ATA_REG_SECCOUNT0  0x2
+#define ATA_REG_LBA0       0x3
+#define ATA_REG_LBA1       0x4
+#define ATA_REG_LBA2       0x5
+#define ATA_REG_HDDEVSEL   0x6
+#define ATA_REG_COMMAND    0x7
+#define ATA_REG_STATUS     0x7
+#define ATA_REG_SECCOUNT1  0x8
+#define ATA_REG_LBA3       0x9
+#define ATA_REG_LBA4       0xA
+#define ATA_REG_LBA5       0xB
+#define ATA_REG_CONTROL    0xC
+#define ATA_REG_ALTSTATUS  0xC
+#define ATA_REG_DEVADDRESS 0xD
+
+struct IDEChanRegs {
+	uint16_t base;  /* IO base */
+	uint16_t ctrl;  /* Control base */
+	uint16_t bmide; /* Bus Master IDE */
+	uint8_t noint;  /* No interrupt */
+} channels[2];
+
+uint8_t ideBuf[2048] = {0};
+volatile unsigned static char ideIrqInvoked = 0;
+unsigned static char atapiPacket[12] = {0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+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;
+	uint8_t model[41];
+} ideDevices[4];
+
+/* Read channel */
+uint8_t
+ide_read(uint8_t chan, uint8_t reg)
+{
+	uint8_t result;
+	if (reg > 0x07 && reg < 0x0C)
+		ide_write(chan, ATA_REG_CONTROL, 0x80 | channels[chan].noint);
+	if (reg < 0x08)
+		result = inb(channels[chan].base + reg - 0x00);
+	else if (reg < 0x0C)
+		result = inb(channels[chan].base + reg - 0x06);
+	else if (reg < 0x0E)
+		result = inb(channels[chan].ctrl + reg - 0x0A);
+	else if (reg < 0x16)
+		result = inb(channels[chan].bmide + reg - 0x0E);
+	if (reg > 0x07 && reg < 0x0C)
+		ide_write(chan, ATA_REG_CONTROL, channels[chan].noint);
+	return result;
+}
+
+/* Write channel */
+void
+ide_write(uint8_t chan, uint8_t reg, uint8_t data)
+{
+	if (reg > 0x07 && reg < 0x0C)
+		ide_write(chan, ATA_REG_CONTROL, 0x80 | channels[chan].noint);
+	if (reg < 0x08)
+		outb(channels[chan].base + reg - 0x00, data);
+	else if (reg < 0x0C)
+		outb(channels[chan].base + reg - 0x06, data);
+	else if (reg < 0x0E)
+		outb(channels[chan].ctrl + reg - 0x0A, data);
+	else if (reg < 0x16)
+		outb(channels[chan].bmide + reg - 0x0E, data);
+	if (reg > 0x07 && reg < 0x0C)
+		ide_write(chan, ATA_REG_CONTROL, channels[chan].noint);
+}
+
+/* Read identification space */
+void
+ide_read_buffer(uint8_t chan, uint8_t reg, void *buffer, uint32_t quads)
+{
+	if (reg > 0x07 && reg < 0x0C)
+		ide_write(chan, ATA_REG_CONTROL, 0x80 | channels[chan].noint);
+	asm volatile("pushw %es; movw %ds, %ax; movw %ax, %es");
+	if (reg < 0x08)
+		insl(channels[chan].base + reg - 0x00, buffer, quads);
+	else if (reg < 0x0C)
+		insl(channels[chan].base + reg - 0x06, buffer, quads);
+	else if (reg < 0x0E)
+		insl(channels[chan].ctrl + reg - 0x0A, buffer, quads);
+	else if (reg < 0x16)
+		insl(channels[chan].bmide + reg - 0x0E, buffer, quads);
+	asm volatile("popw %es");
+	if (reg > 0x07 && reg < 0x0C)
+		ide_write(chan, ATA_REG_CONTROL, channels[chan].noint);
+}
+
+/* Poll for status after command */
+uint8_t
+ide_polling(uint8_t chan, uint32_t check)
+{
+	uint8_t i, state;
+	/* Wait for BSY to set */
+	for (i = 0; i < 4; i++)
+		ide_read(chan, ATA_REG_ALTSTATUS);
+	/* Wait for BSY to clear */
+	while (ide_read(chan, ATA_REG_STATUS) & ATA_SR_BSY);
+
+	if (check) {
+		state = ide_read(chan, ATA_REG_STATUS);
+		if (state & ATA_SR_ERR)
+			return 2;
+		if (state & ATA_SR_DF)
+			return 1;
+		if ((state & ATA_SR_DRQ) == 0)
+			return 3;
+	}
+
+	return 0;
+}
+
+/* Print an IDE error */
+uint8_t
+ide_print_error(uint32_t drive, uint8_t err)
+{
+	if (err == 0)
+		return err;
+
+	uint8_t st;
+	kprintf("IDE:");
+	if (err == 1) {
+		kprintf(" - Device fault");
+		err = 19;
+	} else if (err == 2) {
+		st = ide_read(ideDevices[drive].channel, ATA_REG_ERROR);
+		if (st & ATA_ER_AMNF) {
+			kprintf(" - No address mark found");
+			err = 7;
+		}
+		if (st & ATA_ER_T0NF) {
+			kprintf(" - No media or media error");
+			err = 3;
+		}
+		if (st & ATA_ER_ABRT) {
+			kprintf(" - Command aborted");
+			err = 20;
+		}
+		if (st & ATA_ER_MCR) {
+			kprintf(" - No media or media error");
+			err = 3;
+		}
+		if (st & ATA_ER_IDNF) {
+			kprintf(" - ID mark not found");
+			err = 21;
+		}
+		if (st & ATA_ER_MC) {
+			kprintf(" - No media or media error");
+			err = 3;
+		}
+		if (st & ATA_ER_UNC) {
+			kprintf(" - Uncorrectable data error");
+			err = 22;
+		}
+		if (st & ATA_ER_BBK) {
+			kprintf(" - Bad sectors");
+			err = 13;
+		}
+	} else if (err == 3) {
+		kprintf(" - Read nothing");
+		err = 23;
+	} else if (err == 4) {
+		kprintf(" - Write protected");
+		err = 8;
+	}
+	kprintf(" - [%s %s] %s",
+	        (char *[]){"Primary","Secondary"}[ideDevices[drive].channel],
+	        (char *[]){"Master","Slave"}[ideDevices[drive].drive],
+	        ideDevices[drive].model);
+	return err;
+}
+
+/* Initialise an IDE drive */
+void
+ide_drive_init(uint32_t bar0, uint32_t bar1, uint32_t bar2,
+               uint32_t bar3, uint32_t bar4)
+{
+	channels[ATA_PRIMARY].base = (bar0 & ~3) + 0x1F0 * (!bar0);
+	channels[ATA_PRIMARY].ctrl = (bar1 & ~3) + 0x3F6 * (!bar1);
+	channels[ATA_SECONDARY].base = (bar2 & ~3) + 0x170 * (!bar2);
+	channels[ATA_SECONDARY].ctrl = (bar3 & ~3) + 0x376 * (!bar3);
+	channels[ATA_PRIMARY].bmide = (bar4 & ~3) + 0;
+	channels[ATA_SECONDARY].bmide = (bar4 & ~3) + 8;
+	/* Disable IRQs */
+	ide_write(ATA_PRIMARY, ATA_REG_CONTROL, 2);
+	ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 2);
+
+	uint32_t count = 0;
+	uint8_t i, j, k, err, type, status;
+	for (i = 0; i < 2; i++)
+	for (j = 0; j < 2; j++) {
+		err = 0;
+		type = IDE_ATA;
+		ideDevices[count].reserved = 0;
+		ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4));
+		sleep(1);
+		ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
+		sleep(1);
+		if (ide_read(i, ATA_REG_STATUS) == 0)
+			continue;
+
+		while (1) {
+			status = ide_read(i, ATA_REG_STATUS);
+			if ((status & ATA_SR_ERR)) {
+				err = 1;
+				break;
+			}
+			if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ))
+				break;
+		}
+
+		if (err) {
+			uint8_t cl = ide_read(i, ATA_REG_LBA1);
+			uint8_t ch = ide_read(i, ATA_REG_LBA2);
+
+			if (cl == 0x14 && ch == 0xEB)
+				type = IDE_ATAPI;
+			else if (cl == 0x69 && ch == 0x96)
+				type = IDE_ATAPI;
+			else
+				continue;
+
+			ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
+			sleep(1);
+		}
+
+		ide_read_buffer(i, ATA_REG_DATA, ideBuf, 128);
+
+		uint16_t sig, cap, set;
+		sig = *((uint16_t *) (ideBuf + ATA_IDENT_DEVICETYPE));
+		cap = *((uint16_t *) (ideBuf + ATA_IDENT_CAPABILITIES));
+		set = *((uint16_t *) (ideBuf + ATA_IDENT_COMMANDSETS));
+		ideDevices[count].reserved = 1;
+		ideDevices[count].type = type;
+		ideDevices[count].channel = i;
+		ideDevices[count].drive = j;
+		ideDevices[count].signature = sig;
+		ideDevices[count].capabilities = cap;
+		ideDevices[count].commandSets = set;
+
+		uint32_t size;
+		if (ideDevices[count].commandSets & (1 << 26))
+			size = *((uint32_t *) (ideBuf + ATA_IDENT_MAX_LBA_EXT));
+		else
+			size = *((uint32_t *) (ideBuf + ATA_IDENT_MAX_LBA));
+		ideDevices[count].size = size;
+		for (k = 0; k < 40; k += 2) {
+			ideDevices[count].model[k] =
+				ideBuf[ATA_IDENT_MODEL + k + 1];
+			ideDevices[count].model[k + 1] =
+				ideBuf[ATA_IDENT_MODEL + k];
+		}
+		ideDevices[count].model[40] = 0;
+		count++;
+	}
+}
+
+/* Wait for an IRQ to arrive */
+void
+ide_wait_irq(void)
+{
+	while (!ideIrqInvoked);
+	ideIrqInvoked = 0;
+}
+
+/* Access an ATA drive */
+uint8_t
+ide_ata_access(uint8_t write, uint8_t drive, uint32_t lba, uint8_t sectors,
+               uint16_t selector, uint32_t edi)
+{
+	uint8_t lbaMode, dma, cmd;
+	uint8_t lbaIO[6];
+	uint32_t chan = ideDevices[drive].channel;
+	uint32_t slave = ideDevices[drive].drive;
+	uint32_t bus = channels[chan].base;
+	uint32_t words = 256;
+	uint16_t cyl, i;
+	uint8_t head, sect, err;
+
+	ide_write(chan, ATA_REG_CONTROL,
+	          channels[chan].noint = (ideIrqInvoked = 0) + 2);
+
+	if (lba >= 0x10000000) {
+		/* LBA48 */
+		lbaMode = 2;
+		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 (ideDevices[drive].capabilities & 0x200) {
+		/* LBA28 */
+		lbaMode = 1;
+		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 {
+		/* CHS */
+		lbaMode = 0;
+		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; /* XXX */
+
+	while (ide_read(chan, ATA_REG_STATUS) & ATA_SR_BSY);
+
+	if (lbaMode == 0)
+		ide_write(chan, ATA_REG_HDDEVSEL, 0xA0 | (slave << 4) | head);
+	else
+		ide_write(chan, ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | head);
+
+	if (lbaMode == 2) {
+		ide_write(chan, ATA_REG_SECCOUNT1, 0);
+		ide_write(chan, ATA_REG_LBA3, lbaIO[3]);
+		ide_write(chan, ATA_REG_LBA4, lbaIO[4]);
+		ide_write(chan, ATA_REG_LBA5, lbaIO[5]);
+	}
+	ide_write(chan, ATA_REG_SECCOUNT0, sectors);
+	ide_write(chan, ATA_REG_LBA0, lbaIO[0]);
+	ide_write(chan, ATA_REG_LBA1, lbaIO[1]);
+	ide_write(chan, ATA_REG_LBA2, lbaIO[2]);
+
+	if (lbaMode == 0 && !dma && !write) cmd = ATA_CMD_READ_PIO;
+	if (lbaMode == 1 && !dma && !write) cmd = ATA_CMD_READ_PIO;
+	if (lbaMode == 2 && !dma && !write) cmd = ATA_CMD_READ_PIO_EXT;
+	if (lbaMode == 0 &&  dma && !write) cmd = ATA_CMD_READ_DMA;
+	if (lbaMode == 1 &&  dma && !write) cmd = ATA_CMD_READ_DMA;
+	if (lbaMode == 2 &&  dma && !write) cmd = ATA_CMD_READ_DMA_EXT;
+	if (lbaMode == 0 && !dma &&  write) cmd = ATA_CMD_WRITE_PIO;
+	if (lbaMode == 1 && !dma &&  write) cmd = ATA_CMD_WRITE_PIO;
+	if (lbaMode == 2 && !dma &&  write) cmd = ATA_CMD_WRITE_PIO_EXT;
+	if (lbaMode == 0 &&  dma &&  write) cmd = ATA_CMD_WRITE_DMA;
+	if (lbaMode == 1 &&  dma &&  write) cmd = ATA_CMD_WRITE_DMA;
+	if (lbaMode == 2 &&  dma &&  write) cmd = ATA_CMD_WRITE_DMA_EXT;
+	ide_write(chan, ATA_REG_COMMAND, cmd);
+
+	if (dma) {
+		if (write) {
+			/* TODO */
+		} else {
+			/* TODO */
+		}
+	} else {
+		if (write) {
+			for (i = 0; i < sectors; i++) {
+				ide_polling(chan, 0);
+				outsw(bus, (void *) edi, words);
+			}
+			ide_write(chan, ATA_REG_COMMAND, (uint8_t []) {
+				ATA_CMD_CACHE_FLUSH,
+				ATA_CMD_CACHE_FLUSH,
+				ATA_CMD_CACHE_FLUSH_EXT,
+			}[lbaMode]);
+			ide_polling(chan, 0);
+		} else {
+			for (i = 0; i < sectors; i++) {
+				if (err = ide_polling(chan, 1))
+					return err;
+				insw(bus, (void *) edi, words);
+				edi += words * 2;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/* IDE IRQ handler */
+void
+ide_irq(InterruptFrame *frame)
+{
+	ideIrqInvoked = 1;
+}
+
+/* Initialise an IDE drive */
+void
+ide_init(uint8_t bus, uint8_t slot, uint8_t func)
+{
+	register_driver(&ideDriver);
+
+	uint8_t class, subclass, progif, header;
+	class = pci_read_byte(bus, slot, func, 11);
+	subclass = pci_read_byte(bus, slot, func, 10);
+	progif = pci_read_byte(bus, slot, func, 9);
+	header = pci_read_byte(bus, slot, func, 14);
+
+	uint8_t primaryNative = 0, primaryChange = 0,
+	        secondaryNative = 0, secondaryChange = 0,
+	        busMaster = 0;
+	if (progif & (1 << 0))
+		primaryNative = 1;
+	if (progif & (1 << 1))
+		primaryChange = 1;
+	if (progif & (1 << 2))
+		secondaryNative = 1;
+	if (progif & (1 << 3))
+		secondaryChange = 1;
+	if (progif & (1 << 7))
+		busMaster = 1;
+
+	uint32_t bar0 = 0x1F0, bar1 = 0x3F6,
+	         bar2 = 0x170, bar3 = 0x376,
+	         bar4 = 0x00;
+
+	if (primaryNative) {
+		bar0 = pci_read_dword(bus, slot, func, 0x10);
+		bar1 = pci_read_dword(bus, slot, func, 0x14);
+	}
+	if (secondaryNative) {
+		bar2 = pci_read_dword(bus, slot, func, 0x18);
+		bar3 = pci_read_dword(bus, slot, func, 0x1C);
+	}
+	if (busMaster)
+		bar4 = pci_read_dword(bus, slot, func, 0x20);
+
+	register_interrupt(14, ide_irq);
+	ataDmaArea = alloc_frames(1);
+
+	ide_drive_init(0x1F0, 0x3F6, 0x170, 0x376, 0x00);
+	uint8_t i, j;
+	char name[16];
+	for (i = 0; i < 4; i++) {
+		if (!ideDevices[i].reserved)
+			continue;
+		sprintf(name, "/dev/%cd%c",
+		        (char []){'h','c'}[ideDevices[i].type],
+		        (char []){'a','b'}[ideDevices[i].channel]);
+		mknod(name, S_IFBLK | 0600, MKDEV(ideDriver.major, i * 16));
+		kprintf("    %s drive [%s] %d MB - %s",
+		        (char *[]){"ATA", "ATAPI"}[ideDevices[i].type],
+		        name, ideDevices[i].size / 1024 / 2,
+		        ideDevices[i].model);
+		/* Attempt to read partition table */
+		if (ideDevices[i].type != 0)
+			continue;
+		ide_ata_access(0, i, 0, 0x02, 1, ataDmaArea);
+		struct partition *part;
+		for (j = 0; j < 4; j++) {
+			part = (void *) ataDmaArea + 446 + (j * 16);
+			if (!part->type)
+				continue;
+			sprintf(name, "/dev/%cd%c%d",
+			        (char []){'h','c'}[ideDevices[i].type],
+			        (char []){'a','b'}[ideDevices[i].channel],
+			        j + 1);
+			int asdf = mknod(name, S_IFBLK | 0600,
+			      MKDEV(ideDriver.major, (i * 16) + (j + 1)));
+			kprintf("      Partition %d: %#.8x - %#.8x",
+			        j+1, part->lba*512,
+			        (part->lba + part->size) * 512);
+		}
+	}
+}
+
+/* Read a drive */
+size_t
+ata_read(File *file, char *buf, size_t size, off_t offset)
+{
+	uint16_t min, bufOffset = offset % 512;
+	size_t count = 0;
+	uint16_t dev = MINOR(file->inode->dev) / 16,
+	         parti = MINOR(file->inode->dev) % 16;
+	uint32_t lba;
+	struct partition *part;
+	if (parti) {
+		ide_ata_access(0, dev, 0, 1, 0x02, ataDmaArea);
+		part = (void *) ataDmaArea + 446 + ((parti - 1) * 16);
+		if (offset > (part->lba + part->size) * 512)
+			return 0;
+		if (offset + size > (part->lba + part->size) * 512)
+			size = ((part->lba + part->size) * 512) - offset;
+	} else {
+		if (offset > ideDevices[dev].size * 512)
+			return 0;
+		if (offset + size > ideDevices[dev].size * 512)
+			size = (ideDevices[dev].size * 512) - offset;
+	}
+	while (size + bufOffset) {
+		min = (size > 0x1000) ? 0x1000 : size;
+		lba = (parti ? part->lba : 0) + ((offset + count) / 512);
+		ide_ata_access(0, dev, lba, 8, 0x02, ataDmaArea);
+		memcpy((void *) (buf + count),
+		       (void *) ataDmaArea + bufOffset,
+		       min);
+		size -= min;
+		count += min;
+		bufOffset = 0;
+	}
+	return count;
+}
+
+/* Write a drive */
+size_t
+ata_write(File *file, char *buf, size_t size, off_t offset)
+{
+	uint16_t min;
+	size_t count = 0;
+	uint16_t dev = MINOR(file->inode->dev) / 16,
+	         parti = MINOR(file->inode->dev) % 16;
+	uint32_t lba;
+	struct partition *part;
+	if (parti) {
+		ide_ata_access(0, dev, 0, 1, 0x02, ataDmaArea);
+		part = (void *) ataDmaArea + 446 + ((parti - 1) * 16);
+		if (offset > (part->lba + part->size) * 512)
+			return 0;
+		if (offset + size > (part->lba + part->size) * 512)
+			size = ((part->lba + part->size) * 512) - offset;
+	} else {
+		if (offset > ideDevices[dev].size * 512)
+			return 0;
+		if (offset + size > ideDevices[dev].size * 512)
+			size = (ideDevices[dev].size * 512) - offset;
+	}
+	while (size) {
+		min = (size > 0x1000) ? 0x1000 : size;
+		memcpy((void *) ataDmaArea,
+//		       (void *) (((uint32_t) (buf + count) / 512) * 512),
+		       (void *) (buf + count),
+		       min);
+		lba = (parti ? part->lba : 0) + ((offset + count) / 512);
+		ide_ata_access(1, dev, lba, 8, 0x02, ataDmaArea);
+		size -= min;
+		count += min;
+	}
+	return count;
+	/*
+	 * TODO: Currently, offset is sector-aligned, which we don't always
+	 *       want.  This should be fixed by basically checking the offset
+	 *       for this and reading into the dma buffer before we write to the
+	 *       adjusted offset in the buffer, this way the surrounding data
+	 *       isn't deleted by accident with bad alignment.
+	 */
+}
+
+/* Open a drive */
+int
+ata_open(File *file)
+{
+	if (ideDevices[MINOR(file->inode->dev)/16].reserved)
+		return 0;
+	if (ideDevices[MINOR(file->inode->dev)/16].type != 0)
+		return -EINVAL;
+	return -ENODEV;
+}
+