From: Ben Pfaff Date: Sun, 29 Aug 2004 21:34:16 +0000 (+0000) Subject: Implement read/write support. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=71f6a8a847a1a6f528475c05f923727ebe75403d;p=pintos-anon Implement read/write support. --- diff --git a/src/devices/disk.c b/src/devices/disk.c index a742ee4..cc7e9a7 100644 --- a/src/devices/disk.c +++ b/src/devices/disk.c @@ -39,7 +39,11 @@ #define DEV_DEV 0x10 /* Select device: 0=master, 1=slave. */ /* Commands. */ -#define CMD_IDENTIFY 0xec /* IDENTIFY DEVICE. */ +#define CMD_IDENTIFY 0xec /* IDENTIFY DEVICE. */ +#define CMD_READ_SECTOR_RETRY 0x20 /* READ SECTOR with retries. */ +#define CMD_READ_SECTOR_NORETRY 0x21 /* READ SECTOR without retries. */ +#define CMD_WRITE_SECTOR_RETRY 0x30 /* WRITE SECTOR with retries. */ +#define CMD_WRITE_SECTOR_NORETRY 0x31 /* WRITE SECTOR without retries. */ struct disk { @@ -67,19 +71,7 @@ struct channel static struct channel channels[2]; static void -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_status (c)); - timer_nsleep (400); -} - -static void -wait_idle (const struct disk *d) +wait_until_idle (const struct disk *d) { int i; @@ -93,17 +85,9 @@ wait_idle (const struct disk *d) printk ("%s: idle timeout\n", d->name); } -static void -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 (const struct disk *d) +wait_while_busy (const struct disk *d) { struct channel *c = d->channel; int i; @@ -125,6 +109,26 @@ busy_wait (const struct disk *d) return false; } +static void +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_status (c)); + timer_nsleep (400); +} + +static void +select_device_wait (const struct disk *d) +{ + wait_until_idle (d); + select_device (d); + wait_until_idle (d); +} + static void reset_channel (struct channel *c) { @@ -166,7 +170,7 @@ reset_channel (struct channel *c) if (present[0]) { select_device (&c->dev[0]); - busy_wait (&c->dev[0]); + wait_while_busy (&c->dev[0]); } /* Wait for device 1 to clear BSY. */ @@ -181,12 +185,12 @@ reset_channel (struct channel *c) break; timer_msleep (10); } - busy_wait (&c->dev[1]); + wait_while_busy (&c->dev[1]); } } static bool -check_ata_device (struct disk *d) +check_device_type (struct disk *d) { struct channel *c = d->channel; uint8_t error, lbam, lbah; @@ -250,6 +254,41 @@ input_sector (struct channel *c, void *sector) } } +static bool +output_sector (struct channel *c, const void *sector) +{ + uint8_t status; + + ASSERT (sector != NULL); + + status = inb (reg_status (c)); + if (status & STA_DRQ) + { + /* Command was successful. Write data into SECTOR. */ + outsw (reg_data (c), sector, DISK_SECTOR_SIZE / 2); + return true; + } + else + { + /* Command failed. */ + return false; + } +} + +static void +printk_ata_string (char *string, size_t word_cnt) +{ + int last; + int i; + + for (last = word_cnt * 2 - 1; last >= 0; last--) + if (string[last ^ 1] != ' ') + break; + + for (i = 0; i <= last; i++) + printk ("%c", string[i ^ 1]); +} + static void identify_ata_device (struct disk *d) { @@ -259,7 +298,7 @@ identify_ata_device (struct disk *d) select_device_wait (d); execute_command (d, CMD_IDENTIFY); - busy_wait (d); + wait_while_busy (d); if (!input_sector (d->channel, id)) { @@ -267,9 +306,13 @@ identify_ata_device (struct disk *d) return; } - d->capacity = id[57] | ((uint32_t) id[58] << 16); - printk ("%s: detected %"PRDSNu" sector (%d MB) disk\n", + d->capacity = id[60] | ((uint32_t) id[61] << 16); + printk ("%s: detected %'"PRDSNu" sector (%d MB) disk: ", d->name, d->capacity, d->capacity * DISK_SECTOR_SIZE / 1024 / 1024); + printk_ata_string ((char *) &id[27], 20); + printk (" "); + printk_ata_string ((char *) &id[10], 10); + printk ("\n"); } static void @@ -281,7 +324,13 @@ interrupt_handler (struct intr_frame *f) if (f->vec_no == c->irq) { if (c->expecting_interrupt) - sema_up (&c->completion_wait); + { + /* Acknowledge interrupt. */ + inb (reg_status (c)); + + /* Wake up waiter. */ + sema_up (&c->completion_wait); + } else printk ("%s: unexpected interrupt\n", c->name); return; @@ -336,11 +385,11 @@ disk_init (void) /* Reset hardware. */ reset_channel (c); - /* Detect ATA devices. */ - if (check_ata_device (&c->dev[0])) - check_ata_device (&c->dev[1]); + /* Distinguish ATA hard disks from other devices. */ + if (check_device_type (&c->dev[0])) + check_device_type (&c->dev[1]); - /* Detect device properties. */ + /* Read hard disk identity information. */ for (device = 0; device < 2; device++) if (c->dev[device].is_ata) identify_ata_device (&c->dev[device]); @@ -350,10 +399,60 @@ disk_init (void) struct disk * disk_get (int idx) { + struct disk *d; + ASSERT (idx >= 0 && idx < 4); - return &channels[idx / 2].dev[idx % 2]; + d = &channels[idx / 2].dev[idx % 2]; + return d->is_ata ? d : NULL; +} + +disk_sector_no +disk_size (struct disk *d) +{ + ASSERT (d != NULL); + + return d->capacity; +} + +static void +select_sector (struct disk *d, disk_sector_no sec_no) +{ + struct channel *c = d->channel; + + ASSERT (sec_no < d->capacity); + ASSERT (sec_no < (1UL << 28)); + + select_device_wait (d); + outb (reg_nsect (c), 1); + outb (reg_lbal (c), sec_no); + outb (reg_lbam (c), sec_no >> 8); + outb (reg_lbah (c), (sec_no >> 16)); + outb (reg_device (c), + DEV_MBS | DEV_LBA | (d->device == 1 ? DEV_DEV : 0) | (sec_no >> 24)); +} + +void +disk_read (struct disk *d, disk_sector_no sec_no, void *buffer) +{ + ASSERT (d != NULL); + ASSERT (buffer != NULL); + + select_sector (d, sec_no); + execute_command (d, CMD_READ_SECTOR_RETRY); + wait_while_busy (d); + if (!input_sector (d->channel, buffer)) + panic ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no); } -disk_sector_no disk_size (struct disk *); -void disk_read (struct disk *, disk_sector_no, void *); -void disk_write (struct disk *, disk_sector_no, const void *); +void +disk_write (struct disk *d, disk_sector_no sec_no, const void *buffer) +{ + ASSERT (d != NULL); + ASSERT (buffer != NULL); + + select_sector (d, sec_no); + execute_command (d, CMD_WRITE_SECTOR_RETRY); + wait_while_busy (d); + if (!output_sector (d->channel, buffer)) + panic ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no); +}