-#include "disk.h"
+#include "devices/disk.h"
+#include <ctype.h>
+#include <debug.h>
#include <stdbool.h>
-#include "timer.h"
-#include "lib/debug.h"
-#include "lib/lib.h"
+#include <stdio.h>
+#include "devices/timer.h"
#include "threads/io.h"
#include "threads/interrupt.h"
#include "threads/synch.h"
+/* The code in this file is an interface to an ATA (IDE)
+ controller. It attempts to comply to [ATA-3]. */
+
/* ATA command block port addresses. */
#define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0) /* Data. */
#define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1) /* Error. */
/* Alternate Status Register bits. */
#define STA_BSY 0x80 /* Busy. */
+#define STA_DRDY 0x40 /* Device Ready. */
#define STA_DRQ 0x08 /* Data Request. */
/* Control Register bits. */
int dev_no; /* Device 0 or 1 for master or slave. */
bool is_ata; /* 1=This device is an ATA disk. */
- disk_sector_t capacity; /* Capacity in sectors (if is_ata is true). */
+ disk_sector_t capacity; /* Capacity in sectors (if is_ata). */
+
+ long long read_cnt; /* Number of sectors read. */
+ long long write_cnt; /* Number of sectors written. */
};
/* An ATA channel (aka controller).
int dev_no;
/* Initialize channel. */
- snprintf (c->name, sizeof c->name, "hd%d", chan_no);
+ snprintf (c->name, sizeof c->name, "hd%zu", chan_no);
switch (chan_no)
{
case 0:
default:
NOT_REACHED ();
}
- lock_init (&c->lock, c->name);
+ lock_init (&c->lock);
c->expecting_interrupt = false;
- sema_init (&c->completion_wait, 0, c->name);
+ sema_init (&c->completion_wait, 0);
/* Initialize devices. */
for (dev_no = 0; dev_no < 2; dev_no++)
d->is_ata = false;
d->capacity = 0;
+
+ d->read_cnt = d->write_cnt = 0;
}
/* Register interrupt handler. */
- intr_register (c->irq, 0, INTR_OFF, interrupt_handler, c->name);
+ intr_register_ext (c->irq, interrupt_handler, c->name);
/* Reset hardware. */
reset_channel (c);
}
}
+/* Prints disk statistics. */
+void
+disk_print_stats (void)
+{
+ int chan_no;
+
+ for (chan_no = 0; chan_no < CHANNEL_CNT; chan_no++)
+ {
+ int dev_no;
+
+ for (dev_no = 0; dev_no < 2; dev_no++)
+ {
+ struct disk *d = disk_get (chan_no, dev_no);
+ if (d != NULL && d->is_ata)
+ printf ("%s: %lld reads, %lld writes\n",
+ d->name, d->read_cnt, d->write_cnt);
+ }
+ }
+}
+
/* Returns the disk numbered DEV_NO--either 0 or 1 for master or
- slave, respectively--within the channel numbered CHAN_NO. */
+ slave, respectively--within the channel numbered CHAN_NO.
+
+ Pintos uses disks this way:
+ 0:0 - operating system kernel
+ 0:1 - file system
+ 1:0 - scratch
+ 1:1 - swap
+*/
struct disk *
disk_get (int chan_no, int dev_no)
{
if (!wait_while_busy (d))
PANIC ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no);
input_sector (c, buffer);
+ d->read_cnt++;
lock_release (&c->lock);
}
PANIC ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no);
output_sector (c, buffer);
sema_down (&c->completion_wait);
+ d->write_cnt++;
lock_release (&c->lock);
}
\f
/* Disk detection and identification. */
-static void printk_ata_string (char *string, size_t size);
+static void print_ata_string (char *string, size_t size);
/* Resets an ATA channel and waits for any devices present on it
to finish the reset. */
check_device_type (struct disk *d)
{
struct channel *c = d->channel;
- uint8_t error, lbam, lbah;
+ uint8_t error, lbam, lbah, status;
select_device (d);
error = inb (reg_error (c));
lbam = inb (reg_lbam (c));
lbah = inb (reg_lbah (c));
+ status = inb (reg_status (c));
- if (error != 1 && (error != 0x81 || d->dev_no == 1))
+ if ((error != 1 && (error != 0x81 || d->dev_no == 1))
+ || (status & STA_DRDY) == 0
+ || (status & STA_BSY) != 0)
{
d->is_ata = false;
return error != 0x81;
d->capacity = id[60] | ((uint32_t) id[61] << 16);
/* Print identification message. */
- printk ("%s: detected %'"PRDSNu" sector (", d->name, d->capacity);
+ printf ("%s: detected %'"PRDSNu" sector (", d->name, d->capacity);
if (d->capacity > 1024 / DISK_SECTOR_SIZE * 1024 * 1024)
- printk ("%"PRDSNu" GB",
+ printf ("%"PRDSNu" GB",
d->capacity / (1024 / DISK_SECTOR_SIZE * 1024 * 1024));
else if (d->capacity > 1024 / DISK_SECTOR_SIZE * 1024)
- printk ("%"PRDSNu" MB", d->capacity / (1024 / DISK_SECTOR_SIZE * 1024));
+ printf ("%"PRDSNu" MB", d->capacity / (1024 / DISK_SECTOR_SIZE * 1024));
else if (d->capacity > 1024 / DISK_SECTOR_SIZE)
- printk ("%"PRDSNu" kB", d->capacity / (1024 / DISK_SECTOR_SIZE));
+ printf ("%"PRDSNu" kB", d->capacity / (1024 / DISK_SECTOR_SIZE));
else
- printk ("%"PRDSNu" byte", d->capacity * DISK_SECTOR_SIZE);
- printk (") disk, model \"");
- printk_ata_string ((char *) &id[27], 40);
- printk ("\", serial \"");
- printk_ata_string ((char *) &id[10], 20);
- printk ("\"\n");
+ printf ("%"PRDSNu" byte", d->capacity * DISK_SECTOR_SIZE);
+ printf (") disk, model \"");
+ print_ata_string ((char *) &id[27], 40);
+ printf ("\", serial \"");
+ print_ata_string ((char *) &id[10], 20);
+ printf ("\"\n");
}
/* Prints STRING, which consists of SIZE bytes in a funky format:
each pair of bytes is in reverse order. Does not print
trailing whitespace and/or nulls. */
static void
-printk_ata_string (char *string, size_t size)
+print_ata_string (char *string, size_t size)
{
size_t i;
/* Print. */
for (i = 0; i < size; i++)
- printk ("%c", string[i ^ 1]);
+ printf ("%c", string[i ^ 1]);
}
\f
/* Selects device D, waiting for it to become ready, and then
timer_usleep (10);
}
- printk ("%s: idle timeout\n", d->name);
+ printf ("%s: idle timeout\n", d->name);
}
/* Wait up to 30 seconds for disk D to clear BSY,
for (i = 0; i < 3000; i++)
{
if (i == 700)
- printk ("%s: busy, waiting...", d->name);
+ printf ("%s: busy, waiting...", d->name);
if (!(inb (reg_alt_status (c)) & STA_BSY))
{
if (i >= 700)
- printk ("ok\n");
+ printf ("ok\n");
return (inb (reg_alt_status (c)) & STA_DRQ) != 0;
}
timer_msleep (10);
}
- printk ("failed\n");
+ printf ("failed\n");
return false;
}
sema_up (&c->completion_wait); /* Wake up waiter. */
}
else
- printk ("%s: unexpected interrupt\n", c->name);
+ printf ("%s: unexpected interrupt\n", c->name);
return;
}