xref: /xv6-public/ide.c (revision c24ac5d7)
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