Basic disk detection stuff.
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 29 Aug 2004 19:18:25 +0000 (19:18 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 29 Aug 2004 19:18:25 +0000 (19:18 +0000)
src/Makefile.inc
src/devices/disk.c
src/devices/disk.h
src/threads/init.c

index 6ffa3504a2e612be4ba5e4fec206f49baa82268d..a76c4a9cc3ad429345be1eb9a9451af6da509d37 100644 (file)
@@ -31,6 +31,7 @@ DEVICES_SRC  = timer.c                # Timer device.
 DEVICES_SRC += kbd.c           # Keyboard device.
 DEVICES_SRC += vga.c           # Video device.
 DEVICES_SRC += serial.c                # Serial port device.
+DEVICES_SRC += disk.c          # IDE disk device.
 
 # Library code.
 LIB_SRC  = debug.c             # Debug helpers.
index b40a8e8f4ab407a6d6d366bcd763d16cc880ebf5..a742ee42d0cbbebd45e865410eeb42f791c5edb8 100644 (file)
@@ -1,4 +1,11 @@
 #include "disk.h"
+#include <stdbool.h>
+#include "debug.h"
+#include "io.h"
+#include "interrupt.h"
+#include "lib.h"
+#include "synch.h"
+#include "timer.h"
 
 #define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0)
 #define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1)
 #define reg_status(CHANNEL) ((CHANNEL)->reg_base + 7)
 #define reg_command(CHANNEL) reg_status (CHANNEL)
 #define reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206)
-#define reg_alt(CHANNEL) reg_ctl (CHANNEL)
+#define reg_alt_status(CHANNEL) reg_ctl (CHANNEL)
 
 /* Alternate Status Register bits. */
-#define ALT_BSY 0x80
-#define ALT_DRDY 0x40
-#define ALT_DF 0x20
-#define ALT_DSC 0x10
-#define ALT_DRQ 0x08
-#define ALT_CORR 0x04
-#define ALT_IDX 0x02
-#define ALT_ERR 0x01
+#define STA_BSY 0x80
+#define STA_DRDY 0x40
+#define STA_DF 0x20
+#define STA_DSC 0x10
+#define STA_DRQ 0x08
+#define STA_CORR 0x04
+#define STA_IDX 0x02
+#define STA_ERR 0x01
 
 /* Control Register bits. */
 #define CTL_SRST 0x04
@@ -36,7 +43,7 @@
 
 struct disk 
   {
-    const char *name;
+    char name[8];
     struct channel *channel;
     int device;
 
@@ -46,72 +53,75 @@ struct disk
 
 struct channel 
   {
-    const char *name;
+    char name[8];
     uint16_t reg_base;
-    int irq;
+    uint8_t irq;
+
+    bool expecting_interrupt;
+    struct semaphore completion_wait;
+
     struct disk dev[2];
   };
 
 #define CHANNEL_CNT (sizeof channels / sizeof *channels)
-static struct channel channels[2] = {
-  {"ide0", 0x1f0, 14},
-  {"ide1", 0x170, 15},
-};
+static struct channel channels[2];
 
 static void
-select_device (struct device *d) 
+select_device (const struct disk *d)
 {
   struct channel *c = d->channel;
   uint8_t dev = DEV_MBS;
   if (d->device == 1)
     dev |= DEV_DEV;
   outb (reg_device (c), dev);
-  inb (reg_alt (c));
-  ndelay (400);
+  inb (reg_alt_status (c));
+  timer_nsleep (400);
 }
 
 static void
-wait_idle (struct device *d) 
+wait_idle (const struct disk *d) 
 {
   int i;
 
   for(i = 0; i < 1000; i++) 
     {
-      if ((inb (reg_status (d->channel)) & (STA_BUSY | STA_DRQ)) == 0)
+      if ((inb (reg_status (d->channel)) & (STA_BSY | STA_DRQ)) == 0)
         return;
-      udelay (10);
+      timer_usleep (10);
     }
 
   printk ("%s: idle timeout\n", d->name);
 }
 
 static void
-select_device_wait (struct device *d) 
+select_device_wait (const struct disk *d) 
 {
   wait_idle (d);
   select_device (d);
   wait_idle (d);
 }
 
+/* Wait up to 30 seconds for disk D to clear BSY. */
 static bool
-busy_wait (struct channel *c
+busy_wait (const struct disk *d
 {
+  struct channel *c = d->channel;
   int i;
   
   for (i = 0; i < 3000; i++)
     {
       if (i == 700)
-        printk ("%s: busy, waiting...");
-      if (!(inb (reg_alt (c)) & ALT_BSY)) 
+        printk ("%s: busy, waiting...", d->name);
+      if (!(inb (reg_alt_status (c)) & STA_BSY)) 
         {
           if (i >= 700)
             printk ("ok\n");
           return true; 
         }
-      mdelay (10);
+      timer_msleep (10);
     }
 
-  printk ("failed\n", c->name);
+  printk ("failed\n");
   return false;
 }
 
@@ -119,13 +129,14 @@ static void
 reset_channel (struct channel *c) 
 {
   bool present[2];
+  int device;
 
   /* The ATA reset sequence depends on which devices are present,
      so we start by detecting device presence. */
   for (device = 0; device < 2; device++)
     {
       struct disk *d = &c->dev[device];
-      
+
       select_device (d);
 
       outb (reg_nsect (c), 0x55);
@@ -144,38 +155,40 @@ reset_channel (struct channel *c)
   /* Issue soft reset sequence, which selects device 0 as a side effect.
      Also enable interrupts. */
   outb (reg_ctl (c), 0);
-  delay (1);
+  timer_usleep (10);
   outb (reg_ctl (c), CTL_SRST);
-  delay (1);
+  timer_usleep (10);
   outb (reg_ctl (c), 0);
-  delay (150);
+
+  timer_msleep (150);
 
   /* Wait for device 0 to clear BSY. */
   if (present[0]) 
     {
-      select_device (c->dev[0]);
-      busy_wait (c); 
+      select_device (&c->dev[0]);
+      busy_wait (&c->dev[0]); 
     }
 
   /* Wait for device 1 to clear BSY. */
   if (present[1])
     {
-      select_device (c->dev[1]);
+      int i;
+
+      select_device (&c->dev[1]);
       for (i = 0; i < 3000; i++) 
         {
           if (inb (reg_nsect (c)) == 1 && inb (reg_lbal (c)) == 1)
             break;
-          delay (10);
+          timer_msleep (10);
         }
-      busy_wait (c);
+      busy_wait (&c->dev[1]);
     }
 }
 
 static bool
-check_ata_device (const struct device *d) 
+check_ata_device (struct disk *d) 
 {
   struct channel *c = d->channel;
-  bool maybe_slave;     /* If D is a master, could a slave exist? */
   uint8_t error, lbam, lbah;
 
   select_device (d);
@@ -205,49 +218,121 @@ execute_command (struct disk *d, uint8_t command)
      up'd by the completion handler. */
   ASSERT (intr_get_level () == IF_ON);
 
+  /* Atomically note that we expect an interrupt and send the
+     command to the device. */
   intr_disable ();
-  sema_init (&completion_sema, 0, "disk");
-  intr_enable ();
-  
+  c->expecting_interrupt = true;
   outb (reg_command (c), command);
+  intr_enable ();
 
-  sema_down ();
+  /* Wait for the command to complete. */
+  sema_down (&c->completion_wait);
 }
 
-void
+static bool
+input_sector (struct channel *c, void *sector) 
+{
+  uint8_t status;
+
+  ASSERT (sector != NULL);
+
+  status = inb (reg_status (c));
+  if (status & STA_DRQ) 
+    {
+      /* Command was successful.  Read data into SECTOR. */
+      insw (reg_data (c), sector, DISK_SECTOR_SIZE / 2);
+      return true; 
+    }
+  else 
+    {
+      /* Command failed. */
+      return false; 
+    }
+}
+
+static void
 identify_ata_device (struct disk *d) 
 {
-  struct channel *c = d->channel;
+  uint16_t id[DISK_SECTOR_SIZE / 2];
 
   ASSERT (d->is_ata);
 
   select_device_wait (d);
-  execute_command (CMD_IDENTIFY);
-  
-  
+  execute_command (d, CMD_IDENTIFY);
+  busy_wait (d);
+
+  if (!input_sector (d->channel, id)) 
+    {
+      d->is_ata = false;
+      return;
+    }
 
+  d->capacity = id[57] | ((uint32_t) id[58] << 16);
+  printk ("%s: detected %"PRDSNu" sector (%d MB) disk\n",
+          d->name, d->capacity, d->capacity * DISK_SECTOR_SIZE / 1024 / 1024);
 }
 
-void
-disk_init (void
+static void
+interrupt_handler (struct intr_frame *f
 {
   struct channel *c;
 
   for (c = channels; c < channels + CHANNEL_CNT; c++)
+    if (f->vec_no == c->irq)
+      {
+        if (c->expecting_interrupt) 
+          sema_up (&c->completion_wait);
+        else
+          printk ("%s: unexpected interrupt\n", c->name);
+        return;
+      }
+
+  NOT_REACHED ();
+}
+
+void
+disk_init (void) 
+{
+  size_t channel;
+
+  for (channel = 0; channel < CHANNEL_CNT; channel++)
     {
+      struct channel *c = &channels[channel];
       int device;
 
-      /* Register interrupt handler. */
-      register_irq (c);
-
-      /* Initialize device state. */
+      /* Initialize channel. */
+      snprintf (c->name, sizeof c->name, "ide%d", channel);
+      switch (channel) 
+        {
+        case 0:
+          c->reg_base = 0x1f0;
+          c->irq = 14 + 0x20;
+          break;
+        case 1:
+          c->reg_base = 0x170;
+          c->irq = 15 + 0x20;
+          break;
+        default:
+          NOT_REACHED ();
+        }
+      c->expecting_interrupt = false;
+      sema_init (&c->completion_wait, 0, c->name);
+      /* Initialize devices. */
       for (device = 0; device < 2; device++)
         {
           struct disk *d = &c->dev[device];
+          snprintf (d->name, sizeof d->name, "%s:%d", c->name, device);
           d->channel = c;
           d->device = device;
+
+          d->is_ata = false;
+          d->capacity = 0;
         }
 
+      /* Register interrupt handler. */
+      intr_register (c->irq, 0, IF_OFF, interrupt_handler, c->name);
+
       /* Reset hardware. */
       reset_channel (c);
 
@@ -266,7 +351,7 @@ struct disk *
 disk_get (int idx) 
 {
   ASSERT (idx >= 0 && idx < 4);
-  return &channel[idx / 2].dev[idx % 2];
+  return &channels[idx / 2].dev[idx % 2];
 }
 
 disk_sector_no disk_size (struct disk *);
index 9df00ffbf541a155d4a3a45aff25e5d0304311ab..5f89d1de1c3e1048da31484bcbea371f7f8b83e6 100644 (file)
@@ -1,11 +1,13 @@
 #ifndef HEADER_DISK_H
 #define HEADER_DISK_H 1
 
+#include <inttypes.h>
 #include <stdint.h>
 
 #define DISK_SECTOR_SIZE 512
 
 typedef uint32_t disk_sector_no;
+#define PRDSNu PRId32   /* For use with printk(). */
 
 void disk_init (void);
 struct disk *disk_get (int idx);
index 6da714e124a125bf1fee5e7ec63f28cc96d04364..54a98d256236eb54fa2ba1a515949285b10d8085 100644 (file)
@@ -18,6 +18,7 @@
 #include "vga.h"
 #ifdef FILESYS
 #include "filesys.h"
+#include "disk.h"
 #endif
 
 /* Size of kernel static code and data, in 4 kB pages. */
@@ -32,6 +33,7 @@ void power_off (void);
 static void
 main_thread (void *aux UNUSED) 
 {
+  disk_init ();
   thread_execute ("a.out");
 }