PKUOS - Pintos
Pintos source browser for PKU Operating System course
|
#include "devices/ide.h"
#include <ctype.h>
#include <debug.h>
#include <stdbool.h>
#include <stdio.h>
#include "devices/block.h"
#include "devices/partition.h"
#include "devices/timer.h"
#include "threads/io.h"
#include "threads/interrupt.h"
#include "threads/synch.h"
Go to the source code of this file.
Data Structures | |
struct | ata_disk |
An ATA device. More... | |
struct | channel |
An ATA channel (aka controller). More... | |
Macros | |
#define | reg_data(CHANNEL) ((CHANNEL)->reg_base + 0) |
The code in this file is an interface to an ATA (IDE) controller. More... | |
#define | reg_error(CHANNEL) ((CHANNEL)->reg_base + 1) |
Error. More... | |
#define | reg_nsect(CHANNEL) ((CHANNEL)->reg_base + 2) |
Sector Count. More... | |
#define | reg_lbal(CHANNEL) ((CHANNEL)->reg_base + 3) |
LBA 0:7. More... | |
#define | reg_lbam(CHANNEL) ((CHANNEL)->reg_base + 4) |
LBA 15:8. More... | |
#define | reg_lbah(CHANNEL) ((CHANNEL)->reg_base + 5) |
LBA 23:16. More... | |
#define | reg_device(CHANNEL) ((CHANNEL)->reg_base + 6) |
Device/LBA 27:24. More... | |
#define | reg_status(CHANNEL) ((CHANNEL)->reg_base + 7) |
Status (r/o). More... | |
#define | reg_command(CHANNEL) reg_status (CHANNEL) |
Command (w/o). More... | |
#define | reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206) |
ATA control block port addresses. More... | |
#define | reg_alt_status(CHANNEL) reg_ctl (CHANNEL) |
Alt Status (r/o). More... | |
#define | STA_BSY 0x80 |
Alternate Status Register bits. More... | |
#define | STA_DRDY 0x40 |
Device Ready. More... | |
#define | STA_DRQ 0x08 |
Data Request. More... | |
#define | CTL_SRST 0x04 |
Control Register bits. More... | |
#define | DEV_MBS 0xa0 |
Device Register bits. More... | |
#define | DEV_LBA 0x40 |
Linear based addressing. More... | |
#define | DEV_DEV 0x10 |
Select device: 0=master, 1=slave. More... | |
#define | CMD_IDENTIFY_DEVICE 0xec |
Commands. More... | |
#define | CMD_READ_SECTOR_RETRY 0x20 |
READ SECTOR with retries. More... | |
#define | CMD_WRITE_SECTOR_RETRY 0x30 |
WRITE SECTOR with retries. More... | |
#define | CHANNEL_CNT 2 |
We support the two "legacy" ATA channels found in a standard PC. More... | |
Functions | |
static void | reset_channel (struct channel *c) |
Resets an ATA channel and waits for any devices present on it to finish the reset. More... | |
static bool | check_device_type (struct ata_disk *d) |
Checks whether device D is an ATA disk and sets D's is_ata member appropriately. More... | |
static void | identify_ata_device (struct ata_disk *d) |
Sends an IDENTIFY DEVICE command to disk D and reads the response. More... | |
static void | select_sector (struct ata_disk *d, block_sector_t sec_no) |
Selects device D, waiting for it to become ready, and then writes SEC_NO to the disk's sector selection registers. More... | |
static void | issue_pio_command (struct channel *c, uint8_t command) |
Writes COMMAND to channel C and prepares for receiving a completion interrupt. More... | |
static void | input_sector (struct channel *c, void *sector) |
Reads a sector from channel C's data register in PIO mode into SECTOR, which must have room for BLOCK_SECTOR_SIZE bytes. More... | |
static void | output_sector (struct channel *c, const void *sector) |
Writes SECTOR to channel C's data register in PIO mode. More... | |
static void | wait_until_idle (const struct ata_disk *d) |
Low-level ATA primitives. More... | |
static bool | wait_while_busy (const struct ata_disk *d) |
Wait up to 30 seconds for disk D to clear BSY, and then return the status of the DRQ bit. More... | |
static void | select_device (const struct ata_disk *d) |
Program D's channel so that D is now the selected disk. More... | |
static void | select_device_wait (const struct ata_disk *d) |
Select disk D in its channel, as select_device(), but wait for the channel to become idle before and after. More... | |
static void | interrupt_handler (struct intr_frame *f) |
ATA interrupt handler. More... | |
void | ide_init (void) |
Initialize the disk subsystem and detect disks. More... | |
static char * | descramble_ata_string (char *, int size) |
Disk detection and identification. More... | |
static void | ide_read (void *d_, block_sector_t sec_no, void *buffer) |
Reads sector SEC_NO from disk D into BUFFER, which must have room for BLOCK_SECTOR_SIZE bytes. More... | |
static void | ide_write (void *d_, block_sector_t sec_no, const void *buffer) |
Write sector SEC_NO to disk D from BUFFER, which must contain BLOCK_SECTOR_SIZE bytes. More... | |
Variables | |
static struct channel | channels [CHANNEL_CNT] |
static struct block_operations | ide_operations |
#define CHANNEL_CNT 2 |
#define CMD_IDENTIFY_DEVICE 0xec |
#define reg_alt_status | ( | CHANNEL | ) | reg_ctl (CHANNEL) |
#define reg_command | ( | CHANNEL | ) | reg_status (CHANNEL) |
#define reg_ctl | ( | CHANNEL | ) | ((CHANNEL)->reg_base + 0x206) |
#define reg_data | ( | CHANNEL | ) | ((CHANNEL)->reg_base + 0) |
#define reg_device | ( | CHANNEL | ) | ((CHANNEL)->reg_base + 6) |
#define reg_lbah | ( | CHANNEL | ) | ((CHANNEL)->reg_base + 5) |
#define reg_lbam | ( | CHANNEL | ) | ((CHANNEL)->reg_base + 4) |
#define reg_nsect | ( | CHANNEL | ) | ((CHANNEL)->reg_base + 2) |
#define reg_status | ( | CHANNEL | ) | ((CHANNEL)->reg_base + 7) |
Checks whether device D is an ATA disk and sets D's is_ata member appropriately.
If D is device 0 (master), returns true if it's possible that a slave (device 1) exists on this channel. If D is device 1 (slave), the return value is not meaningful.
Definition at line 230 of file ide.c.
References ata_disk::channel, ata_disk::dev_no, inb(), ata_disk::is_ata, reg_error, reg_lbah, reg_lbam, reg_status, select_device(), STA_BSY, and STA_DRDY.
Referenced by ide_init().
|
static |
Disk detection and identification.
Translates STRING, which consists of SIZE bytes in a funky format, into a null-terminated string in-place.
Drops trailing whitespace and null bytes. Returns STRING.
Definition at line 316 of file ide.c.
References isspace(), and block::size.
Referenced by identify_ata_device().
void ide_init | ( | void | ) |
Initialize the disk subsystem and detect disks.
Definition at line 102 of file ide.c.
References ata_disk::channel, CHANNEL_CNT, channels, check_device_type(), channel::completion_wait, ata_disk::dev_no, channel::devices, channel::expecting_interrupt, identify_ata_device(), interrupt_handler(), intr_register_ext(), channel::irq, ata_disk::is_ata, channel::lock, lock_init(), ata_disk::name, channel::name, NOT_REACHED, channel::reg_base, reset_channel(), sema_init(), and snprintf().
Referenced by pintos_init().
|
static |
Reads sector SEC_NO from disk D into BUFFER, which must have room for BLOCK_SECTOR_SIZE bytes.
Internally synchronizes accesses to disks, so external per-disk locking is unneeded.
Definition at line 345 of file ide.c.
References buffer, ata_disk::channel, CMD_READ_SECTOR_RETRY, channel::completion_wait, input_sector(), issue_pio_command(), channel::lock, lock_acquire(), lock_release(), ata_disk::name, PANIC, PRDSNu, select_sector(), sema_down(), and wait_while_busy().
|
static |
Write sector SEC_NO to disk D from BUFFER, which must contain BLOCK_SECTOR_SIZE bytes.
Returns after the disk has acknowledged receiving the data. Internally synchronizes accesses to disks, so external per-disk locking is unneeded.
Definition at line 365 of file ide.c.
References buffer, ata_disk::channel, CMD_WRITE_SECTOR_RETRY, channel::completion_wait, issue_pio_command(), channel::lock, lock_acquire(), lock_release(), ata_disk::name, output_sector(), PANIC, PRDSNu, select_sector(), sema_down(), and wait_while_busy().
|
static |
Sends an IDENTIFY DEVICE command to disk D and reads the response.
Registers the disk with the block device layer.
Definition at line 260 of file ide.c.
References ASSERT, BLOCK_RAW, block_register(), BLOCK_SECTOR_SIZE, ata_disk::channel, CMD_IDENTIFY_DEVICE, channel::completion_wait, descramble_ata_string(), ide_operations, input_sector(), ata_disk::is_ata, issue_pio_command(), ata_disk::name, partition_scan(), print_human_readable_size(), printf(), select_device_wait(), sema_down(), snprintf(), and wait_while_busy().
Referenced by ide_init().
|
static |
Reads a sector from channel C's data register in PIO mode into SECTOR, which must have room for BLOCK_SECTOR_SIZE bytes.
Definition at line 420 of file ide.c.
References BLOCK_SECTOR_SIZE, insw(), and reg_data.
Referenced by ide_read(), and identify_ata_device().
|
static |
ATA interrupt handler.
< Acknowledge interrupt.
< Wake up waiter.
Definition at line 507 of file ide.c.
References CHANNEL_CNT, channels, channel::completion_wait, channel::expecting_interrupt, inb(), channel::irq, channel::name, NOT_REACHED, printf(), reg_status, sema_up(), and intr_frame::vec_no.
Referenced by ide_init().
Writes COMMAND to channel C and prepares for receiving a completion interrupt.
Definition at line 407 of file ide.c.
References ASSERT, channel::expecting_interrupt, intr_get_level(), INTR_ON, outb(), and reg_command.
Referenced by ide_read(), ide_write(), and identify_ata_device().
|
static |
Writes SECTOR to channel C's data register in PIO mode.
SECTOR must contain BLOCK_SECTOR_SIZE bytes.
Definition at line 428 of file ide.c.
References BLOCK_SECTOR_SIZE, outsw(), and reg_data.
Referenced by ide_write().
|
static |
Resets an ATA channel and waits for any devices present on it to finish the reset.
Definition at line 165 of file ide.c.
References CTL_SRST, ata_disk::dev_no, channel::devices, inb(), outb(), reg_ctl, reg_lbal, reg_nsect, select_device(), timer_msleep(), timer_usleep(), and wait_while_busy().
Referenced by ide_init().
|
static |
Program D's channel so that D is now the selected disk.
Definition at line 484 of file ide.c.
References ata_disk::channel, DEV_DEV, DEV_MBS, ata_disk::dev_no, inb(), outb(), reg_alt_status, reg_device, and timer_nsleep().
Referenced by check_device_type(), reset_channel(), and select_device_wait().
|
static |
Select disk D in its channel, as select_device(), but wait for the channel to become idle before and after.
Definition at line 498 of file ide.c.
References select_device(), and wait_until_idle().
Referenced by identify_ata_device(), and select_sector().
|
static |
Selects device D, waiting for it to become ready, and then writes SEC_NO to the disk's sector selection registers.
(We use LBA mode.)
Definition at line 389 of file ide.c.
References ASSERT, ata_disk::channel, DEV_DEV, DEV_LBA, DEV_MBS, ata_disk::dev_no, outb(), reg_device, reg_lbah, reg_lbal, reg_lbam, reg_nsect, and select_device_wait().
Referenced by ide_read(), and ide_write().
|
static |
Low-level ATA primitives.
Wait up to 10 seconds for the controller to become idle, that is, for the BSY and DRQ bits to clear in the status register.
As a side effect, reading the status register clears any pending interrupt.
Definition at line 441 of file ide.c.
References ata_disk::channel, inb(), ata_disk::name, printf(), reg_status, STA_BSY, STA_DRQ, and timer_usleep().
Referenced by select_device_wait().
Wait up to 30 seconds for disk D to clear BSY, and then return the status of the DRQ bit.
The ATA standards say that a disk may take as long as that to complete its reset.
Definition at line 460 of file ide.c.
References ata_disk::channel, inb(), ata_disk::name, printf(), reg_alt_status, STA_BSY, STA_DRQ, and timer_msleep().
Referenced by ide_read(), ide_write(), identify_ata_device(), and reset_channel().
|
static |
Definition at line 80 of file ide.c.
Referenced by ide_init(), and interrupt_handler().
|
static |
Definition at line 82 of file ide.c.
Referenced by identify_ata_device().