Nucleus
Barry Driver core e4c4dfe (3 years, 2 months ago)
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)
+{
+
+}