ide.c (3221B)
1 // Simple PIO-based (non-DMA) IDE driver code. 2 3 #include "types.h" 4 #include "defs.h" 5 #include "param.h" 6 #include "memlayout.h" 7 #include "mmu.h" 8 #include "proc.h" 9 #include "x86.h" 10 #include "traps.h" 11 #include "spinlock.h" 12 #include "buf.h" 13 14 #define IDE_BSY 0x80 15 #define IDE_DRDY 0x40 16 #define IDE_DF 0x20 17 #define IDE_ERR 0x01 18 19 #define IDE_CMD_READ 0x20 20 #define IDE_CMD_WRITE 0x30 21 22 // idequeue points to the buf now being read/written to the disk. 23 // idequeue->qnext points to the next buf to be processed. 24 // You must hold idelock while manipulating queue. 25 26 static struct spinlock idelock; 27 static struct buf *idequeue; 28 29 static int havedisk1; 30 static void idestart(struct buf*); 31 32 // Wait for IDE disk to become ready. 33 static int 34 idewait(int checkerr) 35 { 36 int r; 37 38 while(((r = inb(0x1f7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY) 39 ; 40 if(checkerr && (r & (IDE_DF|IDE_ERR)) != 0) 41 return -1; 42 return 0; 43 } 44 45 void 46 ideinit(void) 47 { 48 int i; 49 50 initlock(&idelock, "ide"); 51 picenable(IRQ_IDE); 52 ioapicenable(IRQ_IDE, ncpu - 1); 53 idewait(0); 54 55 // Check if disk 1 is present 56 outb(0x1f6, 0xe0 | (1<<4)); 57 for(i=0; i<1000; i++){ 58 if(inb(0x1f7) != 0){ 59 havedisk1 = 1; 60 break; 61 } 62 } 63 64 // Switch back to disk 0. 65 outb(0x1f6, 0xe0 | (0<<4)); 66 } 67 68 // Start the request for b. Caller must hold idelock. 69 static void 70 idestart(struct buf *b) 71 { 72 if(b == 0) 73 panic("idestart"); 74 75 idewait(0); 76 outb(0x3f6, 0); // generate interrupt 77 outb(0x1f2, 1); // number of sectors 78 outb(0x1f3, b->sector & 0xff); 79 outb(0x1f4, (b->sector >> 8) & 0xff); 80 outb(0x1f5, (b->sector >> 16) & 0xff); 81 outb(0x1f6, 0xe0 | ((b->dev&1)<<4) | ((b->sector>>24)&0x0f)); 82 if(b->flags & B_DIRTY){ 83 outb(0x1f7, IDE_CMD_WRITE); 84 outsl(0x1f0, b->data, 512/4); 85 } else { 86 outb(0x1f7, IDE_CMD_READ); 87 } 88 } 89 90 // Interrupt handler. 91 void 92 ideintr(void) 93 { 94 struct buf *b; 95 96 // First queued buffer is the active request. 97 acquire(&idelock); 98 if((b = idequeue) == 0){ 99 release(&idelock); 100 // cprintf("spurious IDE interrupt\n"); 101 return; 102 } 103 idequeue = b->qnext; 104 105 // Read data if needed. 106 if(!(b->flags & B_DIRTY) && idewait(1) >= 0) 107 insl(0x1f0, b->data, 512/4); 108 109 // Wake process waiting for this buf. 110 b->flags |= B_VALID; 111 b->flags &= ~B_DIRTY; 112 wakeup(b); 113 114 // Start disk on next buf in queue. 115 if(idequeue != 0) 116 idestart(idequeue); 117 118 release(&idelock); 119 } 120 121 //PAGEBREAK! 122 // Sync buf with disk. 123 // If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. 124 // Else if B_VALID is not set, read buf from disk, set B_VALID. 125 void 126 iderw(struct buf *b) 127 { 128 struct buf **pp; 129 130 if(!(b->flags & B_BUSY)) 131 panic("iderw: buf not busy"); 132 if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) 133 panic("iderw: nothing to do"); 134 if(b->dev != 0 && !havedisk1) 135 panic("iderw: ide disk 1 not present"); 136 137 acquire(&idelock); //DOC:acquire-lock 138 139 // Append b to idequeue. 140 b->qnext = 0; 141 for(pp=&idequeue; *pp; pp=&(*pp)->qnext) //DOC:insert-queue 142 ; 143 *pp = b; 144 145 // Start disk if necessary. 146 if(idequeue == b) 147 idestart(b); 148 149 // Wait for request to finish. 150 while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){ 151 sleep(b, &idelock); 152 } 153 154 release(&idelock); 155 }