xref: /xv6-public/ide.c (revision 4f14d8d1)
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
idewait(int checkerr)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
ideinit(void)51 ideinit(void)
52 {
53   int i;
54 
55   initlock(&idelock, "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
idestart(struct buf * b)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
ideintr(void)104 ideintr(void)
105 {
106   struct buf *b;
107 
108   // First queued buffer is the active request.
109   acquire(&idelock);
110 
111   if((b = idequeue) == 0){
112     release(&idelock);
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
iderw(struct buf * b)138 iderw(struct buf *b)
139 {
140   struct buf **pp;
141 
142   if(!holdingsleep(&b->lock))
143     panic("iderw: buf not locked");
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 
167   release(&idelock);
168 }
169