// Simple PIO-based (non-DMA) IDE driver code. #include "types.h" #include "param.h" #include "mmu.h" #include "proc.h" #include "defs.h" #include "x86.h" #include "traps.h" #include "spinlock.h" #include "buf.h" #define IDE_BSY 0x80 #define IDE_DRDY 0x40 #define IDE_DF 0x20 #define IDE_ERR 0x01 #define IDE_CMD_READ 0x20 #define IDE_CMD_WRITE 0x30 // ide_queue points to the buf now being read/written to the disk. // ide_queue->qnext points to the next buf to be processed. // You must hold ide_lock while manipulating queue. static struct spinlock ide_lock; static struct buf *ide_queue; static int disk_1_present; static int ide_probe_disk1(void); static void ide_start_request(); //PAGEBREAK: 10 // Wait for IDE disk to become ready. static int ide_wait_ready(int check_error) { int r; while(((r = inb(0x1F7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY) ; if(check_error && (r & (IDE_DF|IDE_ERR)) != 0) return -1; return 0; } void ide_init(void) { initlock(&ide_lock, "ide"); irq_enable(IRQ_IDE); ioapic_enable(IRQ_IDE, ncpu - 1); ide_wait_ready(0); disk_1_present = ide_probe_disk1(); } // Probe to see if disk 1 exists (we assume disk 0 exists). static int ide_probe_disk1(void) { int r, x; // wait for Device 0 to be ready ide_wait_ready(0); // switch to Device 1 outb(0x1F6, 0xE0 | (1<<4)); // check for Device 1 to be ready for a while for(x = 0; x < 1000 && (r = inb(0x1F7)) == 0; x++) ; // switch back to Device 0 outb(0x1F6, 0xE0 | (0<<4)); return x < 1000; } // Interrupt handler - wake up the request that just finished. void ide_intr(void) { acquire(&ide_lock); if(ide_queue){ if((ide_queue->flags & B_WRITE) == 0) if(ide_wait_ready(1) >= 0) insl(0x1F0, ide_queue->data, 512/4); ide_queue->done = 1; wakeup(ide_queue); ide_queue = ide_queue->qnext; ide_start_request(); } else { cprintf("stray ide interrupt\n"); } release(&ide_lock); } // Start the next request in the queue. // Caller must hold ide_lock. static void ide_start_request (void) { if(ide_queue){ ide_wait_ready(0); outb(0x3f6, 0); // generate interrupt outb(0x1F2, 1); // number of sectors outb(0x1F3, ide_queue->sector & 0xFF); outb(0x1F4, (ide_queue->sector >> 8) & 0xFF); outb(0x1F5, (ide_queue->sector >> 16) & 0xFF); outb(0x1F6, 0xE0 | ((ide_queue->dev & 1)<<4) | ((ide_queue->sector>>24)&0x0F)); if(ide_queue->flags & B_WRITE){ outb(0x1F7, IDE_CMD_WRITE); outsl(0x1F0, ide_queue->data, 512/4); } else { outb(0x1F7, IDE_CMD_READ); } } } //PAGEBREAK: 30 // Queue up a disk operation and wait for it to finish. // b must have B_BUSY set. void ide_rw(struct buf *b) { struct buf **pp; if((b->dev & 0xff) && !disk_1_present) panic("ide disk 1 not present"); acquire(&ide_lock); b->done = 0; b->qnext = 0; // append b to ide_queue pp = &ide_queue; while(*pp) pp = &(*pp)->qnext; *pp = b; if(ide_queue == b) ide_start_request(); while(!b->done) sleep(b, &ide_lock); release(&ide_lock); }