diff options
Diffstat (limited to 'devices/ide.c')
| -rw-r--r-- | devices/ide.c | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/devices/ide.c b/devices/ide.c new file mode 100644 index 0000000..2cc0292 --- /dev/null +++ b/devices/ide.c | |||
| @@ -0,0 +1,527 @@ | |||
| 1 | #include "devices/ide.h" | ||
| 2 | #include <ctype.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include "devices/block.h" | ||
| 7 | #include "devices/partition.h" | ||
| 8 | #include "devices/timer.h" | ||
| 9 | #include "threads/io.h" | ||
| 10 | #include "threads/interrupt.h" | ||
| 11 | #include "threads/synch.h" | ||
| 12 | |||
| 13 | /* The code in this file is an interface to an ATA (IDE) | ||
| 14 | controller. It attempts to comply to [ATA-3]. */ | ||
| 15 | |||
| 16 | /* ATA command block port addresses. */ | ||
| 17 | #define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0) /* Data. */ | ||
| 18 | #define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1) /* Error. */ | ||
| 19 | #define reg_nsect(CHANNEL) ((CHANNEL)->reg_base + 2) /* Sector Count. */ | ||
| 20 | #define reg_lbal(CHANNEL) ((CHANNEL)->reg_base + 3) /* LBA 0:7. */ | ||
| 21 | #define reg_lbam(CHANNEL) ((CHANNEL)->reg_base + 4) /* LBA 15:8. */ | ||
| 22 | #define reg_lbah(CHANNEL) ((CHANNEL)->reg_base + 5) /* LBA 23:16. */ | ||
| 23 | #define reg_device(CHANNEL) ((CHANNEL)->reg_base + 6) /* Device/LBA 27:24. */ | ||
| 24 | #define reg_status(CHANNEL) ((CHANNEL)->reg_base + 7) /* Status (r/o). */ | ||
| 25 | #define reg_command(CHANNEL) reg_status (CHANNEL) /* Command (w/o). */ | ||
| 26 | |||
| 27 | /* ATA control block port addresses. | ||
| 28 | (If we supported non-legacy ATA controllers this would not be | ||
| 29 | flexible enough, but it's fine for what we do.) */ | ||
| 30 | #define reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206) /* Control (w/o). */ | ||
| 31 | #define reg_alt_status(CHANNEL) reg_ctl (CHANNEL) /* Alt Status (r/o). */ | ||
| 32 | |||
| 33 | /* Alternate Status Register bits. */ | ||
| 34 | #define STA_BSY 0x80 /* Busy. */ | ||
| 35 | #define STA_DRDY 0x40 /* Device Ready. */ | ||
| 36 | #define STA_DRQ 0x08 /* Data Request. */ | ||
| 37 | |||
| 38 | /* Control Register bits. */ | ||
| 39 | #define CTL_SRST 0x04 /* Software Reset. */ | ||
| 40 | |||
| 41 | /* Device Register bits. */ | ||
| 42 | #define DEV_MBS 0xa0 /* Must be set. */ | ||
| 43 | #define DEV_LBA 0x40 /* Linear based addressing. */ | ||
| 44 | #define DEV_DEV 0x10 /* Select device: 0=master, 1=slave. */ | ||
| 45 | |||
| 46 | /* Commands. | ||
| 47 | Many more are defined but this is the small subset that we | ||
| 48 | use. */ | ||
| 49 | #define CMD_IDENTIFY_DEVICE 0xec /* IDENTIFY DEVICE. */ | ||
| 50 | #define CMD_READ_SECTOR_RETRY 0x20 /* READ SECTOR with retries. */ | ||
| 51 | #define CMD_WRITE_SECTOR_RETRY 0x30 /* WRITE SECTOR with retries. */ | ||
| 52 | |||
| 53 | /* An ATA device. */ | ||
| 54 | struct ata_disk | ||
| 55 | { | ||
| 56 | char name[8]; /* Name, e.g. "hda". */ | ||
| 57 | struct channel *channel; /* Channel that disk is attached to. */ | ||
| 58 | int dev_no; /* Device 0 or 1 for master or slave. */ | ||
| 59 | bool is_ata; /* Is device an ATA disk? */ | ||
| 60 | }; | ||
| 61 | |||
| 62 | /* An ATA channel (aka controller). | ||
| 63 | Each channel can control up to two disks. */ | ||
| 64 | struct channel | ||
| 65 | { | ||
| 66 | char name[8]; /* Name, e.g. "ide0". */ | ||
| 67 | uint16_t reg_base; /* Base I/O port. */ | ||
| 68 | uint8_t irq; /* Interrupt in use. */ | ||
| 69 | |||
| 70 | struct lock lock; /* Must acquire to access the controller. */ | ||
| 71 | bool expecting_interrupt; /* True if an interrupt is expected, false if | ||
| 72 | any interrupt would be spurious. */ | ||
| 73 | struct semaphore completion_wait; /* Up'd by interrupt handler. */ | ||
| 74 | |||
| 75 | struct ata_disk devices[2]; /* The devices on this channel. */ | ||
| 76 | }; | ||
| 77 | |||
| 78 | /* We support the two "legacy" ATA channels found in a standard PC. */ | ||
| 79 | #define CHANNEL_CNT 2 | ||
| 80 | static struct channel channels[CHANNEL_CNT]; | ||
| 81 | |||
| 82 | static struct block_operations ide_operations; | ||
| 83 | |||
| 84 | static void reset_channel (struct channel *); | ||
| 85 | static bool check_device_type (struct ata_disk *); | ||
| 86 | static void identify_ata_device (struct ata_disk *); | ||
| 87 | |||
| 88 | static void select_sector (struct ata_disk *, block_sector_t); | ||
| 89 | static void issue_pio_command (struct channel *, uint8_t command); | ||
| 90 | static void input_sector (struct channel *, void *); | ||
| 91 | static void output_sector (struct channel *, const void *); | ||
| 92 | |||
| 93 | static void wait_until_idle (const struct ata_disk *); | ||
| 94 | static bool wait_while_busy (const struct ata_disk *); | ||
| 95 | static void select_device (const struct ata_disk *); | ||
| 96 | static void select_device_wait (const struct ata_disk *); | ||
| 97 | |||
| 98 | static void interrupt_handler (struct intr_frame *); | ||
| 99 | |||
| 100 | /* Initialize the disk subsystem and detect disks. */ | ||
| 101 | void | ||
| 102 | ide_init (void) | ||
| 103 | { | ||
| 104 | size_t chan_no; | ||
| 105 | |||
| 106 | for (chan_no = 0; chan_no < CHANNEL_CNT; chan_no++) | ||
| 107 | { | ||
| 108 | struct channel *c = &channels[chan_no]; | ||
| 109 | int dev_no; | ||
| 110 | |||
| 111 | /* Initialize channel. */ | ||
| 112 | snprintf (c->name, sizeof c->name, "ide%zu", chan_no); | ||
| 113 | switch (chan_no) | ||
| 114 | { | ||
| 115 | case 0: | ||
| 116 | c->reg_base = 0x1f0; | ||
| 117 | c->irq = 14 + 0x20; | ||
| 118 | break; | ||
| 119 | case 1: | ||
| 120 | c->reg_base = 0x170; | ||
| 121 | c->irq = 15 + 0x20; | ||
| 122 | break; | ||
| 123 | default: | ||
| 124 | NOT_REACHED (); | ||
| 125 | } | ||
| 126 | lock_init (&c->lock); | ||
| 127 | c->expecting_interrupt = false; | ||
| 128 | sema_init (&c->completion_wait, 0); | ||
| 129 | |||
| 130 | /* Initialize devices. */ | ||
| 131 | for (dev_no = 0; dev_no < 2; dev_no++) | ||
| 132 | { | ||
| 133 | struct ata_disk *d = &c->devices[dev_no]; | ||
| 134 | snprintf (d->name, sizeof d->name, | ||
| 135 | "hd%c", 'a' + chan_no * 2 + dev_no); | ||
| 136 | d->channel = c; | ||
| 137 | d->dev_no = dev_no; | ||
| 138 | d->is_ata = false; | ||
| 139 | } | ||
| 140 | |||
| 141 | /* Register interrupt handler. */ | ||
| 142 | intr_register_ext (c->irq, interrupt_handler, c->name); | ||
| 143 | |||
| 144 | /* Reset hardware. */ | ||
| 145 | reset_channel (c); | ||
| 146 | |||
| 147 | /* Distinguish ATA hard disks from other devices. */ | ||
| 148 | if (check_device_type (&c->devices[0])) | ||
| 149 | check_device_type (&c->devices[1]); | ||
| 150 | |||
| 151 | /* Read hard disk identity information. */ | ||
| 152 | for (dev_no = 0; dev_no < 2; dev_no++) | ||
| 153 | if (c->devices[dev_no].is_ata) | ||
| 154 | identify_ata_device (&c->devices[dev_no]); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | /* Disk detection and identification. */ | ||
| 159 | |||
| 160 | static char *descramble_ata_string (char *, int size); | ||
| 161 | |||
| 162 | /* Resets an ATA channel and waits for any devices present on it | ||
| 163 | to finish the reset. */ | ||
| 164 | static void | ||
| 165 | reset_channel (struct channel *c) | ||
| 166 | { | ||
| 167 | bool present[2]; | ||
| 168 | int dev_no; | ||
| 169 | |||
| 170 | /* The ATA reset sequence depends on which devices are present, | ||
| 171 | so we start by detecting device presence. */ | ||
| 172 | for (dev_no = 0; dev_no < 2; dev_no++) | ||
| 173 | { | ||
| 174 | struct ata_disk *d = &c->devices[dev_no]; | ||
| 175 | |||
| 176 | select_device (d); | ||
| 177 | |||
| 178 | outb (reg_nsect (c), 0x55); | ||
| 179 | outb (reg_lbal (c), 0xaa); | ||
| 180 | |||
| 181 | outb (reg_nsect (c), 0xaa); | ||
| 182 | outb (reg_lbal (c), 0x55); | ||
| 183 | |||
| 184 | outb (reg_nsect (c), 0x55); | ||
| 185 | outb (reg_lbal (c), 0xaa); | ||
| 186 | |||
| 187 | present[dev_no] = (inb (reg_nsect (c)) == 0x55 | ||
| 188 | && inb (reg_lbal (c)) == 0xaa); | ||
| 189 | } | ||
| 190 | |||
| 191 | /* Issue soft reset sequence, which selects device 0 as a side effect. | ||
| 192 | Also enable interrupts. */ | ||
| 193 | outb (reg_ctl (c), 0); | ||
| 194 | timer_usleep (10); | ||
| 195 | outb (reg_ctl (c), CTL_SRST); | ||
| 196 | timer_usleep (10); | ||
| 197 | outb (reg_ctl (c), 0); | ||
| 198 | |||
| 199 | timer_msleep (150); | ||
| 200 | |||
| 201 | /* Wait for device 0 to clear BSY. */ | ||
| 202 | if (present[0]) | ||
| 203 | { | ||
| 204 | select_device (&c->devices[0]); | ||
| 205 | wait_while_busy (&c->devices[0]); | ||
| 206 | } | ||
| 207 | |||
| 208 | /* Wait for device 1 to clear BSY. */ | ||
| 209 | if (present[1]) | ||
| 210 | { | ||
| 211 | int i; | ||
| 212 | |||
| 213 | select_device (&c->devices[1]); | ||
| 214 | for (i = 0; i < 3000; i++) | ||
| 215 | { | ||
| 216 | if (inb (reg_nsect (c)) == 1 && inb (reg_lbal (c)) == 1) | ||
| 217 | break; | ||
| 218 | timer_msleep (10); | ||
| 219 | } | ||
| 220 | wait_while_busy (&c->devices[1]); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | /* Checks whether device D is an ATA disk and sets D's is_ata | ||
| 225 | member appropriately. If D is device 0 (master), returns true | ||
| 226 | if it's possible that a slave (device 1) exists on this | ||
| 227 | channel. If D is device 1 (slave), the return value is not | ||
| 228 | meaningful. */ | ||
| 229 | static bool | ||
| 230 | check_device_type (struct ata_disk *d) | ||
| 231 | { | ||
| 232 | struct channel *c = d->channel; | ||
| 233 | uint8_t error, lbam, lbah, status; | ||
| 234 | |||
| 235 | select_device (d); | ||
| 236 | |||
| 237 | error = inb (reg_error (c)); | ||
| 238 | lbam = inb (reg_lbam (c)); | ||
| 239 | lbah = inb (reg_lbah (c)); | ||
| 240 | status = inb (reg_status (c)); | ||
| 241 | |||
| 242 | if ((error != 1 && (error != 0x81 || d->dev_no == 1)) | ||
| 243 | || (status & STA_DRDY) == 0 | ||
| 244 | || (status & STA_BSY) != 0) | ||
| 245 | { | ||
| 246 | d->is_ata = false; | ||
| 247 | return error != 0x81; | ||
| 248 | } | ||
| 249 | else | ||
| 250 | { | ||
| 251 | d->is_ata = (lbam == 0 && lbah == 0) || (lbam == 0x3c && lbah == 0xc3); | ||
| 252 | return true; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | /* Sends an IDENTIFY DEVICE command to disk D and reads the | ||
| 257 | response. Registers the disk with the block device | ||
| 258 | layer. */ | ||
| 259 | static void | ||
| 260 | identify_ata_device (struct ata_disk *d) | ||
| 261 | { | ||
| 262 | struct channel *c = d->channel; | ||
| 263 | char id[BLOCK_SECTOR_SIZE]; | ||
| 264 | block_sector_t capacity; | ||
| 265 | char *model, *serial; | ||
| 266 | char extra_info[128]; | ||
| 267 | struct block *block; | ||
| 268 | |||
| 269 | ASSERT (d->is_ata); | ||
| 270 | |||
| 271 | /* Send the IDENTIFY DEVICE command, wait for an interrupt | ||
| 272 | indicating the device's response is ready, and read the data | ||
| 273 | into our buffer. */ | ||
| 274 | select_device_wait (d); | ||
| 275 | issue_pio_command (c, CMD_IDENTIFY_DEVICE); | ||
| 276 | sema_down (&c->completion_wait); | ||
| 277 | if (!wait_while_busy (d)) | ||
| 278 | { | ||
| 279 | d->is_ata = false; | ||
| 280 | return; | ||
| 281 | } | ||
| 282 | input_sector (c, id); | ||
| 283 | |||
| 284 | /* Calculate capacity. | ||
| 285 | Read model name and serial number. */ | ||
| 286 | capacity = *(uint32_t *) &id[60 * 2]; | ||
| 287 | model = descramble_ata_string (&id[10 * 2], 20); | ||
| 288 | serial = descramble_ata_string (&id[27 * 2], 40); | ||
| 289 | snprintf (extra_info, sizeof extra_info, | ||
| 290 | "model \"%s\", serial \"%s\"", model, serial); | ||
| 291 | |||
| 292 | /* Disable access to IDE disks over 1 GB, which are likely | ||
| 293 | physical IDE disks rather than virtual ones. If we don't | ||
| 294 | allow access to those, we're less likely to scribble on | ||
| 295 | someone's important data. You can disable this check by | ||
| 296 | hand if you really want to do so. */ | ||
| 297 | if (capacity >= 1024 * 1024 * 1024 / BLOCK_SECTOR_SIZE) | ||
| 298 | { | ||
| 299 | printf ("%s: ignoring ", d->name); | ||
| 300 | print_human_readable_size (capacity * 512); | ||
| 301 | printf ("disk for safety\n"); | ||
| 302 | d->is_ata = false; | ||
| 303 | return; | ||
| 304 | } | ||
| 305 | |||
| 306 | /* Register. */ | ||
| 307 | block = block_register (d->name, BLOCK_RAW, extra_info, capacity, | ||
| 308 | &ide_operations, d); | ||
| 309 | partition_scan (block); | ||
| 310 | } | ||
| 311 | |||
| 312 | /* Translates STRING, which consists of SIZE bytes in a funky | ||
| 313 | format, into a null-terminated string in-place. Drops | ||
| 314 | trailing whitespace and null bytes. Returns STRING. */ | ||
| 315 | static char * | ||
| 316 | descramble_ata_string (char *string, int size) | ||
| 317 | { | ||
| 318 | int i; | ||
| 319 | |||
| 320 | /* Swap all pairs of bytes. */ | ||
| 321 | for (i = 0; i + 1 < size; i += 2) | ||
| 322 | { | ||
| 323 | char tmp = string[i]; | ||
| 324 | string[i] = string[i + 1]; | ||
| 325 | string[i + 1] = tmp; | ||
| 326 | } | ||
| 327 | |||
| 328 | /* Find the last non-white, non-null character. */ | ||
| 329 | for (size--; size > 0; size--) | ||
| 330 | { | ||
| 331 | int c = string[size - 1]; | ||
| 332 | if (c != '\0' && !isspace (c)) | ||
| 333 | break; | ||
| 334 | } | ||
| 335 | string[size] = '\0'; | ||
| 336 | |||
| 337 | return string; | ||
| 338 | } | ||
| 339 | |||
| 340 | /* Reads sector SEC_NO from disk D into BUFFER, which must have | ||
| 341 | room for BLOCK_SECTOR_SIZE bytes. | ||
| 342 | Internally synchronizes accesses to disks, so external | ||
| 343 | per-disk locking is unneeded. */ | ||
| 344 | static void | ||
| 345 | ide_read (void *d_, block_sector_t sec_no, void *buffer) | ||
| 346 | { | ||
| 347 | struct ata_disk *d = d_; | ||
| 348 | struct channel *c = d->channel; | ||
| 349 | lock_acquire (&c->lock); | ||
| 350 | select_sector (d, sec_no); | ||
| 351 | issue_pio_command (c, CMD_READ_SECTOR_RETRY); | ||
| 352 | sema_down (&c->completion_wait); | ||
| 353 | if (!wait_while_busy (d)) | ||
| 354 | PANIC ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no); | ||
| 355 | input_sector (c, buffer); | ||
| 356 | lock_release (&c->lock); | ||
| 357 | } | ||
| 358 | |||
| 359 | /* Write sector SEC_NO to disk D from BUFFER, which must contain | ||
| 360 | BLOCK_SECTOR_SIZE bytes. Returns after the disk has | ||
| 361 | acknowledged receiving the data. | ||
| 362 | Internally synchronizes accesses to disks, so external | ||
| 363 | per-disk locking is unneeded. */ | ||
| 364 | static void | ||
| 365 | ide_write (void *d_, block_sector_t sec_no, const void *buffer) | ||
| 366 | { | ||
| 367 | struct ata_disk *d = d_; | ||
| 368 | struct channel *c = d->channel; | ||
| 369 | lock_acquire (&c->lock); | ||
| 370 | select_sector (d, sec_no); | ||
| 371 | issue_pio_command (c, CMD_WRITE_SECTOR_RETRY); | ||
| 372 | if (!wait_while_busy (d)) | ||
| 373 | PANIC ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no); | ||
| 374 | output_sector (c, buffer); | ||
| 375 | sema_down (&c->completion_wait); | ||
| 376 | lock_release (&c->lock); | ||
| 377 | } | ||
| 378 | |||
| 379 | static struct block_operations ide_operations = | ||
| 380 | { | ||
| 381 | ide_read, | ||
| 382 | ide_write | ||
| 383 | }; | ||
| 384 | |||
| 385 | /* Selects device D, waiting for it to become ready, and then | ||
| 386 | writes SEC_NO to the disk's sector selection registers. (We | ||
| 387 | use LBA mode.) */ | ||
| 388 | static void | ||
| 389 | select_sector (struct ata_disk *d, block_sector_t sec_no) | ||
| 390 | { | ||
| 391 | struct channel *c = d->channel; | ||
| 392 | |||
| 393 | ASSERT (sec_no < (1UL << 28)); | ||
| 394 | |||
| 395 | select_device_wait (d); | ||
| 396 | outb (reg_nsect (c), 1); | ||
| 397 | outb (reg_lbal (c), sec_no); | ||
| 398 | outb (reg_lbam (c), sec_no >> 8); | ||
| 399 | outb (reg_lbah (c), (sec_no >> 16)); | ||
| 400 | outb (reg_device (c), | ||
| 401 | DEV_MBS | DEV_LBA | (d->dev_no == 1 ? DEV_DEV : 0) | (sec_no >> 24)); | ||
| 402 | } | ||
| 403 | |||
| 404 | /* Writes COMMAND to channel C and prepares for receiving a | ||
| 405 | completion interrupt. */ | ||
| 406 | static void | ||
| 407 | issue_pio_command (struct channel *c, uint8_t command) | ||
| 408 | { | ||
| 409 | /* Interrupts must be enabled or our semaphore will never be | ||
| 410 | up'd by the completion handler. */ | ||
| 411 | ASSERT (intr_get_level () == INTR_ON); | ||
| 412 | |||
| 413 | c->expecting_interrupt = true; | ||
| 414 | outb (reg_command (c), command); | ||
| 415 | } | ||
| 416 | |||
| 417 | /* Reads a sector from channel C's data register in PIO mode into | ||
| 418 | SECTOR, which must have room for BLOCK_SECTOR_SIZE bytes. */ | ||
| 419 | static void | ||
| 420 | input_sector (struct channel *c, void *sector) | ||
| 421 | { | ||
| 422 | insw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2); | ||
| 423 | } | ||
| 424 | |||
| 425 | /* Writes SECTOR to channel C's data register in PIO mode. | ||
| 426 | SECTOR must contain BLOCK_SECTOR_SIZE bytes. */ | ||
| 427 | static void | ||
| 428 | output_sector (struct channel *c, const void *sector) | ||
| 429 | { | ||
| 430 | outsw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2); | ||
| 431 | } | ||
| 432 | |||
| 433 | /* Low-level ATA primitives. */ | ||
| 434 | |||
| 435 | /* Wait up to 10 seconds for the controller to become idle, that | ||
| 436 | is, for the BSY and DRQ bits to clear in the status register. | ||
| 437 | |||
| 438 | As a side effect, reading the status register clears any | ||
| 439 | pending interrupt. */ | ||
| 440 | static void | ||
| 441 | wait_until_idle (const struct ata_disk *d) | ||
| 442 | { | ||
| 443 | int i; | ||
| 444 | |||
| 445 | for (i = 0; i < 1000; i++) | ||
| 446 | { | ||
| 447 | if ((inb (reg_status (d->channel)) & (STA_BSY | STA_DRQ)) == 0) | ||
| 448 | return; | ||
| 449 | timer_usleep (10); | ||
| 450 | } | ||
| 451 | |||
| 452 | printf ("%s: idle timeout\n", d->name); | ||
| 453 | } | ||
| 454 | |||
| 455 | /* Wait up to 30 seconds for disk D to clear BSY, | ||
| 456 | and then return the status of the DRQ bit. | ||
| 457 | The ATA standards say that a disk may take as long as that to | ||
| 458 | complete its reset. */ | ||
| 459 | static bool | ||
| 460 | wait_while_busy (const struct ata_disk *d) | ||
| 461 | { | ||
| 462 | struct channel *c = d->channel; | ||
| 463 | int i; | ||
| 464 | |||
| 465 | for (i = 0; i < 3000; i++) | ||
| 466 | { | ||
| 467 | if (i == 700) | ||
| 468 | printf ("%s: busy, waiting...", d->name); | ||
| 469 | if (!(inb (reg_alt_status (c)) & STA_BSY)) | ||
| 470 | { | ||
| 471 | if (i >= 700) | ||
| 472 | printf ("ok\n"); | ||
| 473 | return (inb (reg_alt_status (c)) & STA_DRQ) != 0; | ||
| 474 | } | ||
| 475 | timer_msleep (10); | ||
| 476 | } | ||
| 477 | |||
| 478 | printf ("failed\n"); | ||
| 479 | return false; | ||
| 480 | } | ||
| 481 | |||
| 482 | /* Program D's channel so that D is now the selected disk. */ | ||
| 483 | static void | ||
| 484 | select_device (const struct ata_disk *d) | ||
| 485 | { | ||
| 486 | struct channel *c = d->channel; | ||
| 487 | uint8_t dev = DEV_MBS; | ||
| 488 | if (d->dev_no == 1) | ||
| 489 | dev |= DEV_DEV; | ||
| 490 | outb (reg_device (c), dev); | ||
| 491 | inb (reg_alt_status (c)); | ||
| 492 | timer_nsleep (400); | ||
| 493 | } | ||
| 494 | |||
| 495 | /* Select disk D in its channel, as select_device(), but wait for | ||
| 496 | the channel to become idle before and after. */ | ||
| 497 | static void | ||
| 498 | select_device_wait (const struct ata_disk *d) | ||
| 499 | { | ||
| 500 | wait_until_idle (d); | ||
| 501 | select_device (d); | ||
| 502 | wait_until_idle (d); | ||
| 503 | } | ||
| 504 | |||
| 505 | /* ATA interrupt handler. */ | ||
| 506 | static void | ||
| 507 | interrupt_handler (struct intr_frame *f) | ||
| 508 | { | ||
| 509 | struct channel *c; | ||
| 510 | |||
| 511 | for (c = channels; c < channels + CHANNEL_CNT; c++) | ||
| 512 | if (f->vec_no == c->irq) | ||
| 513 | { | ||
| 514 | if (c->expecting_interrupt) | ||
| 515 | { | ||
| 516 | inb (reg_status (c)); /* Acknowledge interrupt. */ | ||
| 517 | sema_up (&c->completion_wait); /* Wake up waiter. */ | ||
| 518 | } | ||
| 519 | else | ||
| 520 | printf ("%s: unexpected interrupt\n", c->name); | ||
| 521 | return; | ||
| 522 | } | ||
| 523 | |||
| 524 | NOT_REACHED (); | ||
| 525 | } | ||
| 526 | |||
| 527 | |||
