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