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