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