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
31static struct spinlock idelock;
32static struct buf *idequeue;
33
34static int havedisk1;
35static void idestart(struct buf*);
36
37// Wait for IDE disk to become ready.
38static int
39idewait(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
50void
51ideinit(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.
73static void
74idestart(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.
103void
104ideintr(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.
137void
138iderw(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