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