[freenos] r336 committed - ATAController supports reading sectors from disk....

  • From: codesite-noreply@xxxxxxxxxx
  • To: freenos@xxxxxxxxxxxxx
  • Date: Thu, 03 Sep 2009 22:59:43 +0000

Revision: 336
Author: nieklinnenbank
Date: Thu Sep  3 15:52:22 2009
Log: ATAController supports reading sectors from disk.
Currently it is able to detect the first disk on the first ATA
bus using an IDENTIFY command. Applications may read bytes from
the disk using /dev/ata0. Additionally, the previous IDEController has been
renamed to ATAController.

http://code.google.com/p/freenos/source/detail?r=336

Added:
 /trunk/srv/ata
 /trunk/srv/ata/ATAController.cpp
 /trunk/srv/ata/ATAController.h
 /trunk/srv/ata/SConscript
Deleted:
 /trunk/srv/ide
Modified:
 /trunk/etc/rc
 /trunk/srv/SConscript

=======================================
--- /dev/null
+++ /trunk/srv/ata/ATAController.cpp    Thu Sep  3 15:52:22 2009
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2009 Niek Linnenbank
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <DeviceServer.h>
+#include "ATAController.h"
+#include <Error.h>
+#include <Types.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+int main(int argc, char **argv)
+{
+    DeviceServer server("ata", CharacterDeviceFile);
+
+    /* Open the system log. */
+    openlog("ATA", LOG_PID | LOG_CONS, LOG_USER);
+
+    /*
+     * Start serving requests.
+     */
+    server.add(new ATAController);
+    return server.run();
+}
+
+ATAController::ATAController()
+{
+}
+
+Error ATAController::initialize()
+{
+    ATADrive *drive;
+
+    /*
+     * Request ATA Control I/O port.
+     */
+    ProcessCtl(SELF, AllowIO, ATA_BASE_CTL0);
+
+    /*
+     * Request ATA Command I/O ports.
+     */
+    for (Size i = 0; i <= ATA_REG_CMD; i++)
+    {
+       ProcessCtl(SELF, AllowIO, ATA_BASE_CMD0 + i);
+    }
+    /* Detect ATA Controller. */
+    if (inb(ATA_BASE_CMD0 + ATA_REG_STATUS) == 0xff)
+    {
+       exit(EXIT_FAILURE);
+    }
+    pollReady(true);
+
+    /* Attempt to detect first drive. */
+    outb(ATA_BASE_CMD0 + ATA_REG_SELECT, ATA_SEL_MASTER);
+    pollReady(true);
+    outb(ATA_BASE_CMD0 + ATA_REG_CMD,    ATA_CMD_IDENTIFY);
+
+    switch (inb(ATA_BASE_CMD0 + ATA_REG_STATUS))
+    {
+       case 0:
+           syslog(LOG_INFO, "No ATA drive(s) detected");
+           break;
+
+       default:
+           /* Wait until the command completed. */
+           pollReady();
+
+           /* Allocate a new drive. */
+           drive = new ATADrive;
+           drives.insertTail(drive);
+
+           /* Read IDENTIFY data. */
+           for (int i = 0; i < 256; i++)
+           {
+               ((u16 *) &drive->identity)[i] = inw(ATA_BASE_CMD0 + 
ATA_REG_DATA);
+           }
+           /* Fixup ASCII bytes. */
+           IDENTIFY_TEXT_SWAP(drive->identity.firmware, 8);
+           IDENTIFY_TEXT_SWAP(drive->identity.serial, 20);
+           IDENTIFY_TEXT_SWAP(drive->identity.model, 40);
+
+           /* Print out information. */
+           syslog(LOG_INFO, "ATA drive detected: SERIAL=%20s FIRMWARE=%8s "
+                            "MODEL=%40s MAJOR=%x MINOR=%x SECTORS=%x",
+                             drive->identity.serial,
+                             drive->identity.firmware,
+                             drive->identity.model,
+                             drive->identity.majorRevision,
+                             drive->identity.minorRevision,
+                             drive->identity.sectors28);
+           break;
+    }
+    return ESUCCESS;
+}
+
+Error ATAController::read(s8 *buffer, Size size, Size offset)
+{
+    u16 sectors = (size / 512), word;
+    u32 lba     = (offset / 512);
+    Size result = 0;
+
+    /* Verify LBA. */
+    if (lba > drives.head()->identity.sectors28)
+    {
+       return EIO;
+    }
+    /* Perform ATA Read Command. */
+    outb(ATA_BASE_CMD0 + ATA_REG_SELECT, ATA_SEL_MASTER_28);
+    outb(ATA_BASE_CMD0 + ATA_REG_COUNT,  sectors);
+    outb(ATA_BASE_CMD0 + ATA_REG_ADDR0,  (lba) & 0xff);
+    outb(ATA_BASE_CMD0 + ATA_REG_ADDR1,  (lba >> 8) & 0xff);
+    outb(ATA_BASE_CMD0 + ATA_REG_ADDR2,  (lba >> 16) & 0xff);
+    outb(ATA_BASE_CMD0 + ATA_REG_CMD, ATA_CMD_READ);
+
+    /*
+     * Read out all requested sectors.
+     */
+    while(result < size)
+    {
+       /* Poll the status register. */
+       pollReady(true);
+
+        /* Read out bytes. */
+       for (int i = 0; i < 256; i++)
+       {
+           word = inw(ATA_BASE_CMD0 + ATA_REG_DATA);
+           buffer[(i * 2)]     = (u8)  (word & 0xff);
+           buffer[(i * 2) + 1] = (u8) ((word & 0xff00) >> 8);
+       }
+       result += 512;
+    }
+    return result;
+}
+
+Error ATAController::interrupt(Size vector)
+{
+    syslog(LOG_INFO, "ATA interrupted on IRQ %u", vector);
+    return ESUCCESS;
+}
+
+void ATAController::pollReady(bool noData)
+{
+    while (true)
+    {
+       u8 status = inb(ATA_BASE_CMD0 + ATA_REG_STATUS);
+
+       if (!(status & ATA_STATUS_BUSY) &&
+            (status & ATA_STATUS_DATA || noData))
+       {
+           break;
+       }
+    }
+}
=======================================
--- /dev/null
+++ /trunk/srv/ata/ATAController.h      Thu Sep  3 15:52:22 2009
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2009 Niek Linnenbank
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ATA_ATACONTROLLER_H
+#define __ATA_ATACONTROLLER_H
+
+#include <List.h>
+#include <Device.h>
+
+/**
+ * @name ATA I/O Bases.
+ * @{
+ */
+
+/** @brief First ATA Bus Command I/O Base. */
+#define ATA_BASE_CMD0  0x1f0
+
+/** @brief Second ATA Bus Command I/O Base. */
+#define ATA_BASE_CMD1  0x170
+
+/** @brief First ATA Bus Control I/O Base. */
+#define ATA_BASE_CTL0  0x3f6
+
+/** @brief Second ATA Bus Control I/O Base. */
+#define ATA_BASE_CTL1  0x376
+
+/**
+ * @}
+ */
+
+/**
+ * @name ATA Command Registers.
+ * @see http://wiki.osdev.org/ATA_PIO_Mode#Registers
+ * @{
+ */
+
+/**
+ * @brief Data port.
+ * Read and write PIO data bytes on this port.
+ */
+#define ATA_REG_DATA   0
+
+/**
+ * @brief Features and Error info.
+ * Mostly used with ATAPI.
+ */
+#define ATA_REG_ERROR  1
+
+/**
+ * @brief Sector Count.
+ * Number of sectors to read or write (0 = special value).
+ */
+#define ATA_REG_COUNT  2
+
+/**
+ * @brief Partial Disk Sector address.
+ * This register is CHS, LBA28 and LBA48 specific.
+ */
+#define ATA_REG_ADDR0  3
+
+/**
+ * @brief Partial Disk Sector address.
+ */
+#define ATA_REG_ADDR1  4
+
+/**
+ * @brief Partial Disk Sector address.
+ */
+#define ATA_REG_ADDR2  5
+
+/**
+ * @brief Drive Select bit, Flag bits, Extra address bits.
+ */
+#define ATA_REG_SELECT 6
+
+/**
+ * @brief Command port and Regular Status port.
+ * Used to write commands and read status.
+ */
+#define ATA_REG_CMD    7
+
+/**
+ * @brief Regular Status port.
+ * This is the same register as the command register.
+ * @see ATA_REG_CMD
+ */
+#define ATA_REG_STATUS 7
+
+/**
+ * @}
+ */
+
+/**
+ * @name ATA Status Registers.
+ * @{
+ */
+
+/**
+ * @brief Error flag (when set).
+ * Send a new command to clear it (or nuke it with a Software Reset).
+ */
+#define ATA_STATUS_ERROR 0x01
+
+/**
+ * @brief Drive data ready for transfer.
+ * Set when the drive has PIO data to transfer, or is ready to accept PIO data.
+ */
+#define ATA_STATUS_DATA         0x08
+
+/**
+ * @brief Drive is preparing to accept or send data.
+ * Wait until this bit clears. If it never clears, do a Software Reset.
+ * Technically, when BSY is set, the other bits in the Status bytes are meaningless.
+ */
+#define ATA_STATUS_BUSY  0x80
+
+/**
+ * @}
+ */
+
+/**
+ * @name ATA Control Registers.
+ * @see http://wiki.osdev.org/ATA_PIO_Mode#Device_Control_Register_.2F_Alternate_Status
+ * @{
+ */
+
+/**
+ * @brief Software Reset.
+ * Set this to reset all ATA drives on a bus, if one is misbehaving.
+ */
+#define ATA_REG_RESET 0x4
+
+/**
+ * @brief Interrupt Disable.
+ * Set this to stop the current device from sending interrupts.
+ */
+#define ATA_REG_INTR  0x2
+
+/**
+ * @}
+ */
+
+/**
+ * @name ATA Device Selector Flags.
+ * @{
+ */
+
+/** @brief Master Drive in Legacy mode. */
+#define ATA_SEL_MASTER         0xa0
+
+/** @brief Master Drive in 28-bit LBA mode. */
+#define ATA_SEL_MASTER_28      0xe0
+
+/** @brief Master Drive in 48-bit LBA mode. */
+#define ATA_SEL_MASTER_48      0x40
+
+/**
+ * @}
+ */
+
+/**
+ * @name ATA Commands.
+ * @{
+ */
+
+/** @brief Identifies an ATA device, if any. */
+#define ATA_CMD_IDENTIFY 0xec
+
+/** @brief Reads sectors from an ATA device. */
+#define ATA_CMD_READ    0x20
+
+/**
+ * @}
+ */
+
+/**
+ * @brief Swap ASCII bytes from IDENTIFY.
+ * This macro is needed to swap ASCII bytes in
+ * the IDENTIFY result buffer, because IDENTIFY returns
+ * 256 little-endian words.
+ */
+#define IDENTIFY_TEXT_SWAP(field,size) \
+    \
+    ({ \
+       u8 tmp; \
+        \
+        for (int i = 0; i < (size); i+=2) \
+        { \
+           tmp = (field)[i]; \
+           (field)[i]   = (field)[i+1]; \
+           (field)[i+1] = tmp; \
+       } \
+    })
+
+/**
+ * @brief IDENTIFY data presentation.
+ */
+typedef struct IdentifyData
+{
+    u16 type;
+    u16 reserved1[9];
+    u8  serial[20];
+    u16 reserved2[3];
+    u8  firmware[8];
+    u8  model[40];
+    u16 maxTransfer;
+    u16 trustedFeatures;
+    u16 capabilities[2];
+    u16 reserved3[8];
+    u32 sectors28;
+    u16 reserved4[18];
+    u16 majorRevision;
+    u16 minorRevision;
+    u16 supported[6];
+    u16 reserved5[12];
+    u64 sectors48;
+    u16 reserved6[2];
+    u16 sectorSize;
+}
+IdentifyData;
+
+/**
+ * @brief Represents a Drive on the ATA bus.
+ */
+typedef struct ATADrive
+{
+    /** Bytes returned from IDENTIFY. */
+    IdentifyData identity;
+
+    /** Number of sectors. */
+    Size sectors;
+}
+ATADrive;
+
+/**
+ * @brief AT Attachment (ATA) Host Controller Device.
+ */
+class ATAController : public Device
+{
+    public:
+
+       /**
+        * @brief Constructor function.
+         */
+       ATAController();
+
+       /**
+        * @brief Configures the ATA controller.
+        * @return Error result code.
+        */
+       Error initialize();
+
+        /**
+         * Read bytes from a drive attached to the ATA controller.
+         * @param buffer Buffer to store bytes to read.
+         * @param size Number of bytes to read.
+         * @param offset Offset in the device.
+         * @return Number of bytes on success and an error code on failure.
+         */
+       Error read(s8 *buffer, Size size, Size offset);
+
+       /**
+        * @brief Process ATA interrupts.
+        * @param vector Interrupt number.
+        * @return Error result code.
+        */
+       Error interrupt(Size vector);
+
+    private:
+
+       /**
+        * @brief Polls the Regular Status register.
+        * @param noData Don't wait for the ATA_STATUS_DATA flag to set.
+        */
+       void pollReady(bool noData = false);
+
+       /** @brief Drives detected on the ATA bus. */
+       List<ATADrive> drives;
+};
+
+#endif /* __ATA_ATACONTROLLER_H */
=======================================
--- /dev/null
+++ /trunk/srv/ata/SConscript   Thu Sep  3 15:52:22 2009
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2009 Niek Linnenbank
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+from build import *
+
+env = Prepare(target, [ 'libcrt', 'liballoc', 'libposix', 'libexec', 'libc' ],
+                     [ 'log', 'filesystem', 'process', 'memory', 'pci'])
+
+env.Program('server', [ 'ATAController.cpp' ],
+                        LIBS = env['LIBS'], LIBPATH = env['LIBPATH'])
=======================================
--- /trunk/etc/rc       Tue Aug 18 14:00:08 2009
+++ /trunk/etc/rc       Thu Sep  3 15:52:22 2009
@@ -2,6 +2,7 @@
 /srv/video/vga/server
 /srv/terminal/server
 /srv/log/server
+/srv/ata/server
 /srv/filesystem/proc/server
 /srv/serial/server
 /srv/pci/server
=======================================
--- /trunk/srv/SConscript       Sun Aug 30 12:54:47 2009
+++ /trunk/srv/SConscript       Thu Sep  3 15:52:22 2009
@@ -15,5 +15,5 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #

-SConscript(dirs = [ 'log', 'idle', 'memory', 'pci', 'usb', 'ide', 'video', 'input', +SConscript(dirs = [ 'log', 'idle', 'memory', 'pci', 'usb', 'ata', 'video', 'input',
                    'process', 'serial', 'terminal', 'time', 'filesystem' ])

Other related posts:

  • » [freenos] r336 committed - ATAController supports reading sectors from disk.... - codesite-noreply