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