Orion
Barry Importing existing Orion kernel d41a53c (3 years, 3 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;
+}
+