diff options
Diffstat (limited to 'src/kernel/blk_drv')
-rw-r--r-- | src/kernel/blk_drv/Makefile | 62 | ||||
-rw-r--r-- | src/kernel/blk_drv/blk.h | 140 | ||||
-rw-r--r-- | src/kernel/blk_drv/floppy.c | 462 | ||||
-rw-r--r-- | src/kernel/blk_drv/hd.c | 349 | ||||
-rw-r--r-- | src/kernel/blk_drv/ll_rw_blk.c | 165 | ||||
-rw-r--r-- | src/kernel/blk_drv/ramdisk.c | 125 |
6 files changed, 1303 insertions, 0 deletions
diff --git a/src/kernel/blk_drv/Makefile b/src/kernel/blk_drv/Makefile new file mode 100644 index 0000000..766d2fd --- /dev/null +++ b/src/kernel/blk_drv/Makefile | |||
@@ -0,0 +1,62 @@ | |||
1 | # | ||
2 | # Makefile for the FREAX-kernel block device drivers. | ||
3 | # | ||
4 | # Note! Dependencies are done automagically by 'make dep', which also | ||
5 | # removes any old dependencies. DON'T put your own dependencies here | ||
6 | # unless it's something special (ie not a .c file). | ||
7 | # | ||
8 | |||
9 | include ../../Makefile.header | ||
10 | |||
11 | CFLAGS += -I../../include | ||
12 | CPP += -I../../include | ||
13 | |||
14 | .c.s: | ||
15 | @$(CC) $(CFLAGS) \ | ||
16 | -S -o $*.s $< | ||
17 | .s.o: | ||
18 | @$(AS) -o $*.o $< | ||
19 | .c.o: | ||
20 | @$(CC) $(CFLAGS) \ | ||
21 | -c -o $*.o $< | ||
22 | |||
23 | OBJS = ll_rw_blk.o floppy.o hd.o ramdisk.o | ||
24 | |||
25 | blk_drv.a: $(OBJS) | ||
26 | @$(AR) rcs blk_drv.a $(OBJS) | ||
27 | @sync | ||
28 | |||
29 | clean: | ||
30 | @rm -f core *.o *.a tmp_make | ||
31 | @for i in *.c;do rm -f `basename $$i .c`.s;done | ||
32 | |||
33 | dep: | ||
34 | @sed '/\#\#\# Dependencies/q' < Makefile > tmp_make | ||
35 | @(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ | ||
36 | $(CPP) -M $$i;done) >> tmp_make | ||
37 | @cp tmp_make Makefile | ||
38 | |||
39 | ### Dependencies: | ||
40 | floppy.s floppy.o: floppy.c ../../include/linux/sched.h ../../include/linux/head.h \ | ||
41 | ../../include/linux/fs.h ../../include/sys/types.h \ | ||
42 | ../../include/linux/mm.h ../../include/signal.h \ | ||
43 | ../../include/linux/kernel.h ../../include/linux/fdreg.h \ | ||
44 | ../../include/asm/system.h ../../include/asm/io.h \ | ||
45 | ../../include/asm/segment.h blk.h | ||
46 | hd.s hd.o: hd.c ../../include/linux/config.h ../../include/linux/sched.h \ | ||
47 | ../../include/linux/head.h ../../include/linux/fs.h \ | ||
48 | ../../include/sys/types.h ../../include/linux/mm.h \ | ||
49 | ../../include/signal.h ../../include/linux/kernel.h \ | ||
50 | ../../include/linux/hdreg.h ../../include/asm/system.h \ | ||
51 | ../../include/asm/io.h ../../include/asm/segment.h blk.h | ||
52 | ll_rw_blk.s ll_rw_blk.o: ll_rw_blk.c ../../include/errno.h \ | ||
53 | ../../include/linux/sched.h ../../include/linux/head.h \ | ||
54 | ../../include/linux/fs.h ../../include/sys/types.h \ | ||
55 | ../../include/linux/mm.h ../../include/signal.h \ | ||
56 | ../../include/linux/kernel.h ../../include/asm/system.h blk.h | ||
57 | ramdisk.s ramdisk.o: ramdisk.c ../../include/string.h ../../include/linux/config.h \ | ||
58 | ../../include/linux/sched.h ../../include/linux/head.h \ | ||
59 | ../../include/linux/fs.h ../../include/sys/types.h \ | ||
60 | ../../include/linux/mm.h ../../include/signal.h \ | ||
61 | ../../include/linux/kernel.h ../../include/asm/system.h \ | ||
62 | ../../include/asm/segment.h ../../include/asm/memory.h blk.h | ||
diff --git a/src/kernel/blk_drv/blk.h b/src/kernel/blk_drv/blk.h new file mode 100644 index 0000000..b272173 --- /dev/null +++ b/src/kernel/blk_drv/blk.h | |||
@@ -0,0 +1,140 @@ | |||
1 | #ifndef _BLK_H | ||
2 | #define _BLK_H | ||
3 | |||
4 | #define NR_BLK_DEV 7 | ||
5 | /* | ||
6 | * NR_REQUEST is the number of entries in the request-queue. | ||
7 | * NOTE that writes may use only the low 2/3 of these: reads | ||
8 | * take precedence. | ||
9 | * | ||
10 | * 32 seems to be a reasonable number: enough to get some benefit | ||
11 | * from the elevator-mechanism, but not so much as to lock a lot of | ||
12 | * buffers when they are in the queue. 64 seems to be too many (easily | ||
13 | * long pauses in reading when heavy writing/syncing is going on) | ||
14 | */ | ||
15 | #define NR_REQUEST 32 | ||
16 | |||
17 | /* | ||
18 | * Ok, this is an expanded form so that we can use the same | ||
19 | * request for paging requests when that is implemented. In | ||
20 | * paging, 'bh' is NULL, and 'waiting' is used to wait for | ||
21 | * read/write completion. | ||
22 | */ | ||
23 | struct request { | ||
24 | int dev; /* -1 if no request */ | ||
25 | int cmd; /* READ or WRITE */ | ||
26 | int errors; | ||
27 | unsigned long sector; | ||
28 | unsigned long nr_sectors; | ||
29 | char * buffer; | ||
30 | struct task_struct * waiting; | ||
31 | struct buffer_head * bh; | ||
32 | struct request * next; | ||
33 | }; | ||
34 | |||
35 | /* | ||
36 | * This is used in the elevator algorithm: Note that | ||
37 | * reads always go before writes. This is natural: reads | ||
38 | * are much more time-critical than writes. | ||
39 | */ | ||
40 | #define IN_ORDER(s1,s2) \ | ||
41 | ((s1)->cmd<(s2)->cmd || ((s1)->cmd==(s2)->cmd && \ | ||
42 | ((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \ | ||
43 | (s1)->sector < (s2)->sector)))) | ||
44 | |||
45 | struct blk_dev_struct { | ||
46 | void (*request_fn)(void); | ||
47 | struct request * current_request; | ||
48 | }; | ||
49 | |||
50 | extern struct blk_dev_struct blk_dev[NR_BLK_DEV]; | ||
51 | extern struct request request[NR_REQUEST]; | ||
52 | extern struct task_struct * wait_for_request; | ||
53 | |||
54 | #ifdef MAJOR_NR | ||
55 | |||
56 | /* | ||
57 | * Add entries as needed. Currently the only block devices | ||
58 | * supported are hard-disks and floppies. | ||
59 | */ | ||
60 | |||
61 | #if (MAJOR_NR == 1) | ||
62 | /* ram disk */ | ||
63 | #define DEVICE_NAME "ramdisk" | ||
64 | #define DEVICE_REQUEST do_rd_request | ||
65 | #define DEVICE_NR(device) ((device) & 7) | ||
66 | #define DEVICE_ON(device) | ||
67 | #define DEVICE_OFF(device) | ||
68 | |||
69 | #elif (MAJOR_NR == 2) | ||
70 | /* floppy */ | ||
71 | #define DEVICE_NAME "floppy" | ||
72 | #define DEVICE_INTR do_floppy | ||
73 | #define DEVICE_REQUEST do_fd_request | ||
74 | #define DEVICE_NR(device) ((device) & 3) | ||
75 | #define DEVICE_ON(device) floppy_on(DEVICE_NR(device)) | ||
76 | #define DEVICE_OFF(device) floppy_off(DEVICE_NR(device)) | ||
77 | |||
78 | #elif (MAJOR_NR == 3) | ||
79 | /* harddisk */ | ||
80 | #define DEVICE_NAME "harddisk" | ||
81 | #define DEVICE_INTR do_hd | ||
82 | #define DEVICE_REQUEST do_hd_request | ||
83 | #define DEVICE_NR(device) (MINOR(device)/5) | ||
84 | #define DEVICE_ON(device) | ||
85 | #define DEVICE_OFF(device) | ||
86 | |||
87 | #else | ||
88 | /* unknown blk device */ | ||
89 | #error "unknown blk device" | ||
90 | |||
91 | #endif | ||
92 | |||
93 | #define CURRENT (blk_dev[MAJOR_NR].current_request) | ||
94 | #define CURRENT_DEV DEVICE_NR(CURRENT->dev) | ||
95 | |||
96 | #ifdef DEVICE_INTR | ||
97 | void (*DEVICE_INTR)(void) = NULL; | ||
98 | #endif | ||
99 | static void (DEVICE_REQUEST)(void); | ||
100 | |||
101 | static inline void unlock_buffer(struct buffer_head * bh) | ||
102 | { | ||
103 | if (!bh->b_lock) | ||
104 | printk(DEVICE_NAME ": free buffer being unlocked\n"); | ||
105 | bh->b_lock=0; | ||
106 | wake_up(&bh->b_wait); | ||
107 | } | ||
108 | |||
109 | static inline void end_request(int uptodate) | ||
110 | { | ||
111 | DEVICE_OFF(CURRENT->dev); | ||
112 | if (CURRENT->bh) { | ||
113 | CURRENT->bh->b_uptodate = uptodate; | ||
114 | unlock_buffer(CURRENT->bh); | ||
115 | } | ||
116 | if (!uptodate) { | ||
117 | printk(DEVICE_NAME " I/O error\n\r"); | ||
118 | printk("dev %04x, block %d\n\r",CURRENT->dev, | ||
119 | CURRENT->bh->b_blocknr); | ||
120 | } | ||
121 | wake_up(&CURRENT->waiting); | ||
122 | wake_up(&wait_for_request); | ||
123 | CURRENT->dev = -1; | ||
124 | CURRENT = CURRENT->next; | ||
125 | } | ||
126 | |||
127 | #define INIT_REQUEST \ | ||
128 | repeat: \ | ||
129 | if (!CURRENT) \ | ||
130 | return; \ | ||
131 | if (MAJOR(CURRENT->dev) != MAJOR_NR) \ | ||
132 | panic(DEVICE_NAME ": request list destroyed"); \ | ||
133 | if (CURRENT->bh) { \ | ||
134 | if (!CURRENT->bh->b_lock) \ | ||
135 | panic(DEVICE_NAME ": block not locked"); \ | ||
136 | } | ||
137 | |||
138 | #endif | ||
139 | |||
140 | #endif | ||
diff --git a/src/kernel/blk_drv/floppy.c b/src/kernel/blk_drv/floppy.c new file mode 100644 index 0000000..f497906 --- /dev/null +++ b/src/kernel/blk_drv/floppy.c | |||
@@ -0,0 +1,462 @@ | |||
1 | /* | ||
2 | * linux/kernel/floppy.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * 02.12.91 - Changed to static variables to indicate need for reset | ||
9 | * and recalibrate. This makes some things easier (output_byte reset | ||
10 | * checking etc), and means less interrupt jumping in case of errors, | ||
11 | * so the code is hopefully easier to understand. | ||
12 | */ | ||
13 | |||
14 | /* | ||
15 | * This file is certainly a mess. I've tried my best to get it working, | ||
16 | * but I don't like programming floppies, and I have only one anyway. | ||
17 | * Urgel. I should check for more errors, and do more graceful error | ||
18 | * recovery. Seems there are problems with several drives. I've tried to | ||
19 | * correct them. No promises. | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * As with hd.c, all routines within this file can (and will) be called | ||
24 | * by interrupts, so extreme caution is needed. A hardware interrupt | ||
25 | * handler may not sleep, or a kernel panic will happen. Thus I cannot | ||
26 | * call "floppy-on" directly, but have to set a special timer interrupt | ||
27 | * etc. | ||
28 | * | ||
29 | * Also, I'm not certain this works on more than 1 floppy. Bugs may | ||
30 | * abund. | ||
31 | */ | ||
32 | |||
33 | #include <linux/sched.h> | ||
34 | #include <linux/fs.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/fdreg.h> | ||
37 | #include <asm/system.h> | ||
38 | #include <asm/io.h> | ||
39 | #include <asm/segment.h> | ||
40 | |||
41 | #define MAJOR_NR 2 | ||
42 | #include "blk.h" | ||
43 | |||
44 | static int recalibrate = 0; | ||
45 | static int reset = 0; | ||
46 | static int seek = 0; | ||
47 | |||
48 | extern unsigned char current_DOR; | ||
49 | |||
50 | #define immoutb_p(val,port) \ | ||
51 | __asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port)) | ||
52 | |||
53 | #define TYPE(x) ((x)>>2) | ||
54 | #define DRIVE(x) ((x)&0x03) | ||
55 | /* | ||
56 | * Note that MAX_ERRORS=8 doesn't imply that we retry every bad read | ||
57 | * max 8 times - some types of errors increase the errorcount by 2, | ||
58 | * so we might actually retry only 5-6 times before giving up. | ||
59 | */ | ||
60 | #define MAX_ERRORS 8 | ||
61 | |||
62 | /* | ||
63 | * globals used by 'result()' | ||
64 | */ | ||
65 | #define MAX_REPLIES 7 | ||
66 | static unsigned char reply_buffer[MAX_REPLIES]; | ||
67 | #define ST0 (reply_buffer[0]) | ||
68 | #define ST1 (reply_buffer[1]) | ||
69 | #define ST2 (reply_buffer[2]) | ||
70 | #define ST3 (reply_buffer[3]) | ||
71 | |||
72 | /* | ||
73 | * This struct defines the different floppy types. Unlike minix | ||
74 | * linux doesn't have a "search for right type"-type, as the code | ||
75 | * for that is convoluted and weird. I've got enough problems with | ||
76 | * this driver as it is. | ||
77 | * | ||
78 | * The 'stretch' tells if the tracks need to be boubled for some | ||
79 | * types (ie 360kB diskette in 1.2MB drive etc). Others should | ||
80 | * be self-explanatory. | ||
81 | */ | ||
82 | static struct floppy_struct { | ||
83 | unsigned int size, sect, head, track, stretch; | ||
84 | unsigned char gap,rate,spec1; | ||
85 | } floppy_type[] = { | ||
86 | { 0, 0,0, 0,0,0x00,0x00,0x00 }, /* no testing */ | ||
87 | { 720, 9,2,40,0,0x2A,0x02,0xDF }, /* 360kB PC diskettes */ | ||
88 | { 2400,15,2,80,0,0x1B,0x00,0xDF }, /* 1.2 MB AT-diskettes */ | ||
89 | { 720, 9,2,40,1,0x2A,0x02,0xDF }, /* 360kB in 720kB drive */ | ||
90 | { 1440, 9,2,80,0,0x2A,0x02,0xDF }, /* 3.5" 720kB diskette */ | ||
91 | { 720, 9,2,40,1,0x23,0x01,0xDF }, /* 360kB in 1.2MB drive */ | ||
92 | { 1440, 9,2,80,0,0x23,0x01,0xDF }, /* 720kB in 1.2MB drive */ | ||
93 | { 2880,18,2,80,0,0x1B,0x00,0xCF }, /* 1.44MB diskette */ | ||
94 | }; | ||
95 | /* | ||
96 | * Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps | ||
97 | * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc), | ||
98 | * H is head unload time (1=16ms, 2=32ms, etc) | ||
99 | * | ||
100 | * Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc) | ||
101 | * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA). | ||
102 | */ | ||
103 | |||
104 | extern void floppy_interrupt(void); | ||
105 | extern char tmp_floppy_area[1024]; | ||
106 | |||
107 | /* | ||
108 | * These are global variables, as that's the easiest way to give | ||
109 | * information to interrupts. They are the data used for the current | ||
110 | * request. | ||
111 | */ | ||
112 | static int cur_spec1 = -1; | ||
113 | static int cur_rate = -1; | ||
114 | static struct floppy_struct * floppy = floppy_type; | ||
115 | static unsigned char current_drive = 0; | ||
116 | static unsigned char sector = 0; | ||
117 | static unsigned char head = 0; | ||
118 | static unsigned char track = 0; | ||
119 | static unsigned char seek_track = 0; | ||
120 | static unsigned char current_track = 255; | ||
121 | static unsigned char command = 0; | ||
122 | unsigned char selected = 0; | ||
123 | struct task_struct * wait_on_floppy_select = NULL; | ||
124 | |||
125 | void floppy_deselect(unsigned int nr) | ||
126 | { | ||
127 | if (nr != (current_DOR & 3)) | ||
128 | printk("floppy_deselect: drive not selected\n\r"); | ||
129 | selected = 0; | ||
130 | wake_up(&wait_on_floppy_select); | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * floppy-change is never called from an interrupt, so we can relax a bit | ||
135 | * here, sleep etc. Note that floppy-on tries to set current_DOR to point | ||
136 | * to the desired drive, but it will probably not survive the sleep if | ||
137 | * several floppies are used at the same time: thus the loop. | ||
138 | */ | ||
139 | int floppy_change(unsigned int nr) | ||
140 | { | ||
141 | repeat: | ||
142 | floppy_on(nr); | ||
143 | while ((current_DOR & 3) != nr && selected) | ||
144 | interruptible_sleep_on(&wait_on_floppy_select); | ||
145 | if ((current_DOR & 3) != nr) | ||
146 | goto repeat; | ||
147 | if (inb(FD_DIR) & 0x80) { | ||
148 | floppy_off(nr); | ||
149 | return 1; | ||
150 | } | ||
151 | floppy_off(nr); | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | #define copy_buffer(from,to) \ | ||
156 | __asm__("cld ; rep ; movsl" \ | ||
157 | ::"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \ | ||
158 | ) | ||
159 | |||
160 | static void setup_DMA(void) | ||
161 | { | ||
162 | long addr = (long) CURRENT->buffer; | ||
163 | |||
164 | cli(); | ||
165 | if (addr >= 0x100000) { | ||
166 | addr = (long) tmp_floppy_area; | ||
167 | if (command == FD_WRITE) | ||
168 | copy_buffer(CURRENT->buffer,tmp_floppy_area); | ||
169 | } | ||
170 | /* mask DMA 2 */ | ||
171 | immoutb_p(4|2,10); | ||
172 | /* output command byte. I don't know why, but everyone (minix, */ | ||
173 | /* sanches & canton) output this twice, first to 12 then to 11 */ | ||
174 | __asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t" | ||
175 | "outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:":: | ||
176 | "a" ((char) ((command == FD_READ)?DMA_READ:DMA_WRITE))); | ||
177 | /* 8 low bits of addr */ | ||
178 | immoutb_p(addr,4); | ||
179 | addr >>= 8; | ||
180 | /* bits 8-15 of addr */ | ||
181 | immoutb_p(addr,4); | ||
182 | addr >>= 8; | ||
183 | /* bits 16-19 of addr */ | ||
184 | immoutb_p(addr,0x81); | ||
185 | /* low 8 bits of count-1 (1024-1=0x3ff) */ | ||
186 | immoutb_p(0xff,5); | ||
187 | /* high 8 bits of count-1 */ | ||
188 | immoutb_p(3,5); | ||
189 | /* activate DMA 2 */ | ||
190 | immoutb_p(0|2,10); | ||
191 | sti(); | ||
192 | } | ||
193 | |||
194 | static void output_byte(char byte) | ||
195 | { | ||
196 | int counter; | ||
197 | unsigned char status; | ||
198 | |||
199 | if (reset) | ||
200 | return; | ||
201 | for(counter = 0 ; counter < 10000 ; counter++) { | ||
202 | status = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR); | ||
203 | if (status == STATUS_READY) { | ||
204 | outb(byte,FD_DATA); | ||
205 | return; | ||
206 | } | ||
207 | } | ||
208 | reset = 1; | ||
209 | printk("Unable to send byte to FDC\n\r"); | ||
210 | } | ||
211 | |||
212 | static int result(void) | ||
213 | { | ||
214 | int i = 0, counter, status; | ||
215 | |||
216 | if (reset) | ||
217 | return -1; | ||
218 | for (counter = 0 ; counter < 10000 ; counter++) { | ||
219 | status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY); | ||
220 | if (status == STATUS_READY) | ||
221 | return i; | ||
222 | if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) { | ||
223 | if (i >= MAX_REPLIES) | ||
224 | break; | ||
225 | reply_buffer[i++] = inb_p(FD_DATA); | ||
226 | } | ||
227 | } | ||
228 | reset = 1; | ||
229 | printk("Getstatus times out\n\r"); | ||
230 | return -1; | ||
231 | } | ||
232 | |||
233 | static void bad_flp_intr(void) | ||
234 | { | ||
235 | CURRENT->errors++; | ||
236 | if (CURRENT->errors > MAX_ERRORS) { | ||
237 | floppy_deselect(current_drive); | ||
238 | end_request(0); | ||
239 | } | ||
240 | if (CURRENT->errors > MAX_ERRORS/2) | ||
241 | reset = 1; | ||
242 | else | ||
243 | recalibrate = 1; | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * Ok, this interrupt is called after a DMA read/write has succeeded, | ||
248 | * so we check the results, and copy any buffers. | ||
249 | */ | ||
250 | static void rw_interrupt(void) | ||
251 | { | ||
252 | if (result() != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) || (ST2 & 0x73)) { | ||
253 | if (ST1 & 0x02) { | ||
254 | printk("Drive %d is write protected\n\r",current_drive); | ||
255 | floppy_deselect(current_drive); | ||
256 | end_request(0); | ||
257 | } else | ||
258 | bad_flp_intr(); | ||
259 | do_fd_request(); | ||
260 | return; | ||
261 | } | ||
262 | if (command == FD_READ && (unsigned long)(CURRENT->buffer) >= 0x100000) | ||
263 | copy_buffer(tmp_floppy_area,CURRENT->buffer); | ||
264 | floppy_deselect(current_drive); | ||
265 | end_request(1); | ||
266 | do_fd_request(); | ||
267 | } | ||
268 | |||
269 | static inline void setup_rw_floppy(void) | ||
270 | { | ||
271 | setup_DMA(); | ||
272 | do_floppy = rw_interrupt; | ||
273 | output_byte(command); | ||
274 | output_byte(head<<2 | current_drive); | ||
275 | output_byte(track); | ||
276 | output_byte(head); | ||
277 | output_byte(sector); | ||
278 | output_byte(2); /* sector size = 512 */ | ||
279 | output_byte(floppy->sect); | ||
280 | output_byte(floppy->gap); | ||
281 | output_byte(0xFF); /* sector size (0xff when n!=0 ?) */ | ||
282 | if (reset) | ||
283 | do_fd_request(); | ||
284 | } | ||
285 | |||
286 | /* | ||
287 | * This is the routine called after every seek (or recalibrate) interrupt | ||
288 | * from the floppy controller. Note that the "unexpected interrupt" routine | ||
289 | * also does a recalibrate, but doesn't come here. | ||
290 | */ | ||
291 | static void seek_interrupt(void) | ||
292 | { | ||
293 | /* sense drive status */ | ||
294 | output_byte(FD_SENSEI); | ||
295 | if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) { | ||
296 | bad_flp_intr(); | ||
297 | do_fd_request(); | ||
298 | return; | ||
299 | } | ||
300 | current_track = ST1; | ||
301 | setup_rw_floppy(); | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * This routine is called when everything should be correctly set up | ||
306 | * for the transfer (ie floppy motor is on and the correct floppy is | ||
307 | * selected). | ||
308 | */ | ||
309 | static void transfer(void) | ||
310 | { | ||
311 | if (cur_spec1 != floppy->spec1) { | ||
312 | cur_spec1 = floppy->spec1; | ||
313 | output_byte(FD_SPECIFY); | ||
314 | output_byte(cur_spec1); /* hut etc */ | ||
315 | output_byte(6); /* Head load time =6ms, DMA */ | ||
316 | } | ||
317 | if (cur_rate != floppy->rate) | ||
318 | outb_p(cur_rate = floppy->rate,FD_DCR); | ||
319 | if (reset) { | ||
320 | do_fd_request(); | ||
321 | return; | ||
322 | } | ||
323 | if (!seek) { | ||
324 | setup_rw_floppy(); | ||
325 | return; | ||
326 | } | ||
327 | do_floppy = seek_interrupt; | ||
328 | if (seek_track) { | ||
329 | output_byte(FD_SEEK); | ||
330 | output_byte(head<<2 | current_drive); | ||
331 | output_byte(seek_track); | ||
332 | } else { | ||
333 | output_byte(FD_RECALIBRATE); | ||
334 | output_byte(head<<2 | current_drive); | ||
335 | } | ||
336 | if (reset) | ||
337 | do_fd_request(); | ||
338 | } | ||
339 | |||
340 | /* | ||
341 | * Special case - used after a unexpected interrupt (or reset) | ||
342 | */ | ||
343 | static void recal_interrupt(void) | ||
344 | { | ||
345 | output_byte(FD_SENSEI); | ||
346 | if (result()!=2 || (ST0 & 0xE0) == 0x60) | ||
347 | reset = 1; | ||
348 | else | ||
349 | recalibrate = 0; | ||
350 | do_fd_request(); | ||
351 | } | ||
352 | |||
353 | void unexpected_floppy_interrupt(void) | ||
354 | { | ||
355 | output_byte(FD_SENSEI); | ||
356 | if (result()!=2 || (ST0 & 0xE0) == 0x60) | ||
357 | reset = 1; | ||
358 | else | ||
359 | recalibrate = 1; | ||
360 | } | ||
361 | |||
362 | static void recalibrate_floppy(void) | ||
363 | { | ||
364 | recalibrate = 0; | ||
365 | current_track = 0; | ||
366 | do_floppy = recal_interrupt; | ||
367 | output_byte(FD_RECALIBRATE); | ||
368 | output_byte(head<<2 | current_drive); | ||
369 | if (reset) | ||
370 | do_fd_request(); | ||
371 | } | ||
372 | |||
373 | static void reset_interrupt(void) | ||
374 | { | ||
375 | output_byte(FD_SENSEI); | ||
376 | (void) result(); | ||
377 | output_byte(FD_SPECIFY); | ||
378 | output_byte(cur_spec1); /* hut etc */ | ||
379 | output_byte(6); /* Head load time =6ms, DMA */ | ||
380 | do_fd_request(); | ||
381 | } | ||
382 | |||
383 | /* | ||
384 | * reset is done by pulling bit 2 of DOR low for a while. | ||
385 | */ | ||
386 | static void reset_floppy(void) | ||
387 | { | ||
388 | int i; | ||
389 | |||
390 | reset = 0; | ||
391 | cur_spec1 = -1; | ||
392 | cur_rate = -1; | ||
393 | recalibrate = 1; | ||
394 | printk("Reset-floppy called\n\r"); | ||
395 | cli(); | ||
396 | do_floppy = reset_interrupt; | ||
397 | outb_p(current_DOR & ~0x04,FD_DOR); | ||
398 | for (i=0 ; i<100 ; i++) | ||
399 | __asm__("nop"); | ||
400 | outb(current_DOR,FD_DOR); | ||
401 | sti(); | ||
402 | } | ||
403 | |||
404 | static void floppy_on_interrupt(void) | ||
405 | { | ||
406 | /* We cannot do a floppy-select, as that might sleep. We just force it */ | ||
407 | selected = 1; | ||
408 | if (current_drive != (current_DOR & 3)) { | ||
409 | current_DOR &= 0xFC; | ||
410 | current_DOR |= current_drive; | ||
411 | outb(current_DOR,FD_DOR); | ||
412 | add_timer(2,&transfer); | ||
413 | } else | ||
414 | transfer(); | ||
415 | } | ||
416 | |||
417 | void do_fd_request(void) | ||
418 | { | ||
419 | unsigned int block; | ||
420 | |||
421 | seek = 0; | ||
422 | if (reset) { | ||
423 | reset_floppy(); | ||
424 | return; | ||
425 | } | ||
426 | if (recalibrate) { | ||
427 | recalibrate_floppy(); | ||
428 | return; | ||
429 | } | ||
430 | INIT_REQUEST; | ||
431 | floppy = (MINOR(CURRENT->dev)>>2) + floppy_type; | ||
432 | if (current_drive != CURRENT_DEV) | ||
433 | seek = 1; | ||
434 | current_drive = CURRENT_DEV; | ||
435 | block = CURRENT->sector; | ||
436 | if (block+2 > floppy->size) { | ||
437 | end_request(0); | ||
438 | goto repeat; | ||
439 | } | ||
440 | sector = block % floppy->sect; | ||
441 | block /= floppy->sect; | ||
442 | head = block % floppy->head; | ||
443 | track = block / floppy->head; | ||
444 | seek_track = track << floppy->stretch; | ||
445 | if (seek_track != current_track) | ||
446 | seek = 1; | ||
447 | sector++; | ||
448 | if (CURRENT->cmd == READ) | ||
449 | command = FD_READ; | ||
450 | else if (CURRENT->cmd == WRITE) | ||
451 | command = FD_WRITE; | ||
452 | else | ||
453 | panic("do_fd_request: unknown command"); | ||
454 | add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt); | ||
455 | } | ||
456 | |||
457 | void floppy_init(void) | ||
458 | { | ||
459 | blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; | ||
460 | set_trap_gate(0x26,&floppy_interrupt); | ||
461 | outb(inb_p(0x21)&~0x40,0x21); | ||
462 | } | ||
diff --git a/src/kernel/blk_drv/hd.c b/src/kernel/blk_drv/hd.c new file mode 100644 index 0000000..d5e1aa9 --- /dev/null +++ b/src/kernel/blk_drv/hd.c | |||
@@ -0,0 +1,349 @@ | |||
1 | /* | ||
2 | * linux/kernel/hd.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This is the low-level hd interrupt support. It traverses the | ||
9 | * request-list, using interrupts to jump between functions. As | ||
10 | * all the functions are called within interrupts, we may not | ||
11 | * sleep. Special care is recommended. | ||
12 | * | ||
13 | * modified by Drew Eckhardt to check nr of hd's from the CMOS. | ||
14 | */ | ||
15 | |||
16 | #include <linux/config.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/hdreg.h> | ||
21 | #include <asm/system.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/segment.h> | ||
24 | |||
25 | #define MAJOR_NR 3 | ||
26 | #include "blk.h" | ||
27 | |||
28 | #define CMOS_READ(addr) ({ \ | ||
29 | outb_p(0x80|addr,0x70); \ | ||
30 | inb_p(0x71); \ | ||
31 | }) | ||
32 | |||
33 | /* Max read/write errors/sector */ | ||
34 | #define MAX_ERRORS 7 | ||
35 | #define MAX_HD 2 | ||
36 | |||
37 | static void recal_intr(void); | ||
38 | |||
39 | static int recalibrate = 0; | ||
40 | static int reset = 0; | ||
41 | |||
42 | /* | ||
43 | * This struct defines the HD's and their types. | ||
44 | */ | ||
45 | struct hd_i_struct { | ||
46 | int head,sect,cyl,wpcom,lzone,ctl; | ||
47 | }; | ||
48 | #ifdef HD_TYPE | ||
49 | struct hd_i_struct hd_info[] = { HD_TYPE }; | ||
50 | #define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct))) | ||
51 | #else | ||
52 | struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} }; | ||
53 | static int NR_HD = 0; | ||
54 | #endif | ||
55 | |||
56 | static struct hd_struct { | ||
57 | long start_sect; | ||
58 | long nr_sects; | ||
59 | } hd[5*MAX_HD]={{0,0},}; | ||
60 | |||
61 | #define port_read(port,buf,nr) \ | ||
62 | __asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr)) | ||
63 | |||
64 | #define port_write(port,buf,nr) \ | ||
65 | __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr)) | ||
66 | |||
67 | extern void hd_interrupt(void); | ||
68 | extern void rd_load(void); | ||
69 | |||
70 | /* This may be used only once, enforced by 'static int callable' */ | ||
71 | int sys_setup(void * BIOS) | ||
72 | { | ||
73 | static int callable = 1; | ||
74 | int i,drive; | ||
75 | unsigned char cmos_disks; | ||
76 | struct partition *p; | ||
77 | struct buffer_head * bh; | ||
78 | |||
79 | if (!callable) | ||
80 | return -1; | ||
81 | callable = 0; | ||
82 | #ifndef HD_TYPE | ||
83 | for (drive=0 ; drive<2 ; drive++) { | ||
84 | hd_info[drive].cyl = *(unsigned short *) BIOS; | ||
85 | hd_info[drive].head = *(unsigned char *) (2+BIOS); | ||
86 | hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); | ||
87 | hd_info[drive].ctl = *(unsigned char *) (8+BIOS); | ||
88 | hd_info[drive].lzone = *(unsigned short *) (12+BIOS); | ||
89 | hd_info[drive].sect = *(unsigned char *) (14+BIOS); | ||
90 | BIOS += 16; | ||
91 | } | ||
92 | if (hd_info[1].cyl) | ||
93 | NR_HD=2; | ||
94 | else | ||
95 | NR_HD=1; | ||
96 | #endif | ||
97 | for (i=0 ; i<NR_HD ; i++) { | ||
98 | hd[i*5].start_sect = 0; | ||
99 | hd[i*5].nr_sects = hd_info[i].head* | ||
100 | hd_info[i].sect*hd_info[i].cyl; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | We querry CMOS about hard disks : it could be that | ||
105 | we have a SCSI/ESDI/etc controller that is BIOS | ||
106 | compatable with ST-506, and thus showing up in our | ||
107 | BIOS table, but not register compatable, and therefore | ||
108 | not present in CMOS. | ||
109 | |||
110 | Furthurmore, we will assume that our ST-506 drives | ||
111 | <if any> are the primary drives in the system, and | ||
112 | the ones reflected as drive 1 or 2. | ||
113 | |||
114 | The first drive is stored in the high nibble of CMOS | ||
115 | byte 0x12, the second in the low nibble. This will be | ||
116 | either a 4 bit drive type or 0xf indicating use byte 0x19 | ||
117 | for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. | ||
118 | |||
119 | Needless to say, a non-zero value means we have | ||
120 | an AT controller hard disk for that drive. | ||
121 | |||
122 | |||
123 | */ | ||
124 | |||
125 | if ((cmos_disks = CMOS_READ(0x12)) & 0xf0) | ||
126 | if (cmos_disks & 0x0f) | ||
127 | NR_HD = 2; | ||
128 | else | ||
129 | NR_HD = 1; | ||
130 | else | ||
131 | NR_HD = 0; | ||
132 | for (i = NR_HD ; i < 2 ; i++) { | ||
133 | hd[i*5].start_sect = 0; | ||
134 | hd[i*5].nr_sects = 0; | ||
135 | } | ||
136 | for (drive=0 ; drive<NR_HD ; drive++) { | ||
137 | if (!(bh = bread(0x300 + drive*5,0))) { | ||
138 | printk("Unable to read partition table of drive %d\n\r", | ||
139 | drive); | ||
140 | panic(""); | ||
141 | } | ||
142 | if (bh->b_data[510] != 0x55 || (unsigned char) | ||
143 | bh->b_data[511] != 0xAA) { | ||
144 | printk("Bad partition table on drive %d\n\r",drive); | ||
145 | panic(""); | ||
146 | } | ||
147 | p = 0x1BE + (void *)bh->b_data; | ||
148 | for (i=1;i<5;i++,p++) { | ||
149 | hd[i+5*drive].start_sect = p->start_sect; | ||
150 | hd[i+5*drive].nr_sects = p->nr_sects; | ||
151 | } | ||
152 | brelse(bh); | ||
153 | } | ||
154 | if (NR_HD) | ||
155 | printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":""); | ||
156 | rd_load(); | ||
157 | mount_root(); | ||
158 | return (0); | ||
159 | } | ||
160 | |||
161 | static int controller_ready(void) | ||
162 | { | ||
163 | int retries=100000; | ||
164 | |||
165 | while (--retries && (inb_p(HD_STATUS)&0x80)); | ||
166 | return (retries); | ||
167 | } | ||
168 | |||
169 | static int win_result(void) | ||
170 | { | ||
171 | int i=inb_p(HD_STATUS); | ||
172 | |||
173 | if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)) | ||
174 | == (READY_STAT | SEEK_STAT)) | ||
175 | return(0); /* ok */ | ||
176 | if (i&1) i=inb(HD_ERROR); | ||
177 | return (1); | ||
178 | } | ||
179 | |||
180 | static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, | ||
181 | unsigned int head,unsigned int cyl,unsigned int cmd, | ||
182 | void (*intr_addr)(void)) | ||
183 | { | ||
184 | register int port asm("dx"); | ||
185 | |||
186 | if (drive>1 || head>15) | ||
187 | panic("Trying to write bad sector"); | ||
188 | if (!controller_ready()) | ||
189 | panic("HD controller not ready"); | ||
190 | do_hd = intr_addr; | ||
191 | outb_p(hd_info[drive].ctl,HD_CMD); | ||
192 | port=HD_DATA; | ||
193 | outb_p(hd_info[drive].wpcom>>2,++port); | ||
194 | outb_p(nsect,++port); | ||
195 | outb_p(sect,++port); | ||
196 | outb_p(cyl,++port); | ||
197 | outb_p(cyl>>8,++port); | ||
198 | outb_p(0xA0|(drive<<4)|head,++port); | ||
199 | outb(cmd,++port); | ||
200 | } | ||
201 | |||
202 | static int drive_busy(void) | ||
203 | { | ||
204 | unsigned int i; | ||
205 | |||
206 | for (i = 0; i < 10000; i++) | ||
207 | if (READY_STAT == (inb_p(HD_STATUS) & (BUSY_STAT|READY_STAT))) | ||
208 | break; | ||
209 | i = inb(HD_STATUS); | ||
210 | i &= BUSY_STAT | READY_STAT | SEEK_STAT; | ||
211 | if (i == (READY_STAT | SEEK_STAT)) | ||
212 | return(0); | ||
213 | printk("HD controller times out\n\r"); | ||
214 | return(1); | ||
215 | } | ||
216 | |||
217 | static void reset_controller(void) | ||
218 | { | ||
219 | int i; | ||
220 | |||
221 | outb(4,HD_CMD); | ||
222 | for(i = 0; i < 100; i++) nop(); | ||
223 | outb(hd_info[0].ctl & 0x0f ,HD_CMD); | ||
224 | if (drive_busy()) | ||
225 | printk("HD-controller still busy\n\r"); | ||
226 | if ((i = inb(HD_ERROR)) != 1) | ||
227 | printk("HD-controller reset failed: %02x\n\r",i); | ||
228 | } | ||
229 | |||
230 | static void reset_hd(int nr) | ||
231 | { | ||
232 | reset_controller(); | ||
233 | hd_out(nr,hd_info[nr].sect,hd_info[nr].sect,hd_info[nr].head-1, | ||
234 | hd_info[nr].cyl,WIN_SPECIFY,&recal_intr); | ||
235 | } | ||
236 | |||
237 | void unexpected_hd_interrupt(void) | ||
238 | { | ||
239 | printk("Unexpected HD interrupt\n\r"); | ||
240 | } | ||
241 | |||
242 | static void bad_rw_intr(void) | ||
243 | { | ||
244 | if (++CURRENT->errors >= MAX_ERRORS) | ||
245 | end_request(0); | ||
246 | if (CURRENT->errors > MAX_ERRORS/2) | ||
247 | reset = 1; | ||
248 | } | ||
249 | |||
250 | static void read_intr(void) | ||
251 | { | ||
252 | if (win_result()) { | ||
253 | bad_rw_intr(); | ||
254 | do_hd_request(); | ||
255 | return; | ||
256 | } | ||
257 | port_read(HD_DATA,CURRENT->buffer,256); | ||
258 | CURRENT->errors = 0; | ||
259 | CURRENT->buffer += 512; | ||
260 | CURRENT->sector++; | ||
261 | if (--CURRENT->nr_sectors) { | ||
262 | do_hd = &read_intr; | ||
263 | return; | ||
264 | } | ||
265 | end_request(1); | ||
266 | do_hd_request(); | ||
267 | } | ||
268 | |||
269 | static void write_intr(void) | ||
270 | { | ||
271 | if (win_result()) { | ||
272 | bad_rw_intr(); | ||
273 | do_hd_request(); | ||
274 | return; | ||
275 | } | ||
276 | if (--CURRENT->nr_sectors) { | ||
277 | CURRENT->sector++; | ||
278 | CURRENT->buffer += 512; | ||
279 | do_hd = &write_intr; | ||
280 | port_write(HD_DATA,CURRENT->buffer,256); | ||
281 | return; | ||
282 | } | ||
283 | end_request(1); | ||
284 | do_hd_request(); | ||
285 | } | ||
286 | |||
287 | static void recal_intr(void) | ||
288 | { | ||
289 | if (win_result()) | ||
290 | bad_rw_intr(); | ||
291 | do_hd_request(); | ||
292 | } | ||
293 | |||
294 | void do_hd_request(void) | ||
295 | { | ||
296 | int i,r = 0; | ||
297 | unsigned int block,dev; | ||
298 | unsigned int sec,head,cyl; | ||
299 | unsigned int nsect; | ||
300 | |||
301 | INIT_REQUEST; | ||
302 | dev = MINOR(CURRENT->dev); | ||
303 | block = CURRENT->sector; | ||
304 | if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) { | ||
305 | end_request(0); | ||
306 | goto repeat; | ||
307 | } | ||
308 | block += hd[dev].start_sect; | ||
309 | dev /= 5; | ||
310 | __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0), | ||
311 | "r" (hd_info[dev].sect)); | ||
312 | __asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0), | ||
313 | "r" (hd_info[dev].head)); | ||
314 | sec++; | ||
315 | nsect = CURRENT->nr_sectors; | ||
316 | if (reset) { | ||
317 | reset = 0; | ||
318 | recalibrate = 1; | ||
319 | reset_hd(CURRENT_DEV); | ||
320 | return; | ||
321 | } | ||
322 | if (recalibrate) { | ||
323 | recalibrate = 0; | ||
324 | hd_out(dev,hd_info[CURRENT_DEV].sect,0,0,0, | ||
325 | WIN_RESTORE,&recal_intr); | ||
326 | return; | ||
327 | } | ||
328 | if (CURRENT->cmd == WRITE) { | ||
329 | hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr); | ||
330 | for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++) | ||
331 | /* nothing */ ; | ||
332 | if (!r) { | ||
333 | bad_rw_intr(); | ||
334 | goto repeat; | ||
335 | } | ||
336 | port_write(HD_DATA,CURRENT->buffer,256); | ||
337 | } else if (CURRENT->cmd == READ) { | ||
338 | hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr); | ||
339 | } else | ||
340 | panic("unknown hd-command"); | ||
341 | } | ||
342 | |||
343 | void hd_init(void) | ||
344 | { | ||
345 | blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; | ||
346 | set_intr_gate(0x2E,&hd_interrupt); | ||
347 | outb_p(inb_p(0x21)&0xfb,0x21); | ||
348 | outb(inb_p(0xA1)&0xbf,0xA1); | ||
349 | } | ||
diff --git a/src/kernel/blk_drv/ll_rw_blk.c b/src/kernel/blk_drv/ll_rw_blk.c new file mode 100644 index 0000000..8931a81 --- /dev/null +++ b/src/kernel/blk_drv/ll_rw_blk.c | |||
@@ -0,0 +1,165 @@ | |||
1 | /* | ||
2 | * linux/kernel/blk_dev/ll_rw.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This handles all read/write requests to block devices | ||
9 | */ | ||
10 | #include <errno.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <asm/system.h> | ||
14 | |||
15 | #include "blk.h" | ||
16 | |||
17 | /* | ||
18 | * The request-struct contains all necessary data | ||
19 | * to load a nr of sectors into memory | ||
20 | */ | ||
21 | struct request request[NR_REQUEST]; | ||
22 | |||
23 | /* | ||
24 | * used to wait on when there are no free requests | ||
25 | */ | ||
26 | struct task_struct * wait_for_request = NULL; | ||
27 | |||
28 | /* blk_dev_struct is: | ||
29 | * do_request-address | ||
30 | * next-request | ||
31 | */ | ||
32 | struct blk_dev_struct blk_dev[NR_BLK_DEV] = { | ||
33 | { NULL, NULL }, /* no_dev */ | ||
34 | { NULL, NULL }, /* dev mem */ | ||
35 | { NULL, NULL }, /* dev fd */ | ||
36 | { NULL, NULL }, /* dev hd */ | ||
37 | { NULL, NULL }, /* dev ttyx */ | ||
38 | { NULL, NULL }, /* dev tty */ | ||
39 | { NULL, NULL } /* dev lp */ | ||
40 | }; | ||
41 | |||
42 | static inline void lock_buffer(struct buffer_head * bh) | ||
43 | { | ||
44 | cli(); | ||
45 | while (bh->b_lock) | ||
46 | sleep_on(&bh->b_wait); | ||
47 | bh->b_lock=1; | ||
48 | sti(); | ||
49 | } | ||
50 | |||
51 | static inline void unlock_buffer(struct buffer_head * bh) | ||
52 | { | ||
53 | if (!bh->b_lock) | ||
54 | printk("ll_rw_block.c: buffer not locked\n\r"); | ||
55 | bh->b_lock = 0; | ||
56 | wake_up(&bh->b_wait); | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * add-request adds a request to the linked list. | ||
61 | * It disables interrupts so that it can muck with the | ||
62 | * request-lists in peace. | ||
63 | */ | ||
64 | static void add_request(struct blk_dev_struct * dev, struct request * req) | ||
65 | { | ||
66 | struct request * tmp; | ||
67 | |||
68 | req->next = NULL; | ||
69 | cli(); | ||
70 | if (req->bh) | ||
71 | req->bh->b_dirt = 0; | ||
72 | if (!(tmp = dev->current_request)) { | ||
73 | dev->current_request = req; | ||
74 | sti(); | ||
75 | (dev->request_fn)(); | ||
76 | return; | ||
77 | } | ||
78 | for ( ; tmp->next ; tmp=tmp->next) | ||
79 | if ((IN_ORDER(tmp,req) || | ||
80 | !IN_ORDER(tmp,tmp->next)) && | ||
81 | IN_ORDER(req,tmp->next)) | ||
82 | break; | ||
83 | req->next=tmp->next; | ||
84 | tmp->next=req; | ||
85 | sti(); | ||
86 | } | ||
87 | |||
88 | static void make_request(int major,int rw, struct buffer_head * bh) | ||
89 | { | ||
90 | struct request * req; | ||
91 | int rw_ahead; | ||
92 | |||
93 | /* WRITEA/READA is special case - it is not really needed, so if the */ | ||
94 | /* buffer is locked, we just forget about it, else it's a normal read */ | ||
95 | if ((rw_ahead = (rw == READA || rw == WRITEA))) { | ||
96 | if (bh->b_lock) | ||
97 | return; | ||
98 | if (rw == READA) | ||
99 | rw = READ; | ||
100 | else | ||
101 | rw = WRITE; | ||
102 | } | ||
103 | if (rw!=READ && rw!=WRITE) | ||
104 | panic("Bad block dev command, must be R/W/RA/WA"); | ||
105 | lock_buffer(bh); | ||
106 | if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) { | ||
107 | unlock_buffer(bh); | ||
108 | return; | ||
109 | } | ||
110 | repeat: | ||
111 | /* we don't allow the write-requests to fill up the queue completely: | ||
112 | * we want some room for reads: they take precedence. The last third | ||
113 | * of the requests are only for reads. | ||
114 | */ | ||
115 | if (rw == READ) | ||
116 | req = request+NR_REQUEST; | ||
117 | else | ||
118 | req = request+((NR_REQUEST*2)/3); | ||
119 | /* find an empty request */ | ||
120 | while (--req >= request) | ||
121 | if (req->dev<0) | ||
122 | break; | ||
123 | /* if none found, sleep on new requests: check for rw_ahead */ | ||
124 | if (req < request) { | ||
125 | if (rw_ahead) { | ||
126 | unlock_buffer(bh); | ||
127 | return; | ||
128 | } | ||
129 | sleep_on(&wait_for_request); | ||
130 | goto repeat; | ||
131 | } | ||
132 | /* fill up the request-info, and add it to the queue */ | ||
133 | req->dev = bh->b_dev; | ||
134 | req->cmd = rw; | ||
135 | req->errors=0; | ||
136 | req->sector = bh->b_blocknr<<1; | ||
137 | req->nr_sectors = 2; | ||
138 | req->buffer = bh->b_data; | ||
139 | req->waiting = NULL; | ||
140 | req->bh = bh; | ||
141 | req->next = NULL; | ||
142 | add_request(major+blk_dev,req); | ||
143 | } | ||
144 | |||
145 | void ll_rw_block(int rw, struct buffer_head * bh) | ||
146 | { | ||
147 | unsigned int major; | ||
148 | |||
149 | if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV || | ||
150 | !(blk_dev[major].request_fn)) { | ||
151 | printk("Trying to read nonexistent block-device\n\r"); | ||
152 | return; | ||
153 | } | ||
154 | make_request(major,rw,bh); | ||
155 | } | ||
156 | |||
157 | void blk_dev_init(void) | ||
158 | { | ||
159 | int i; | ||
160 | |||
161 | for (i=0 ; i<NR_REQUEST ; i++) { | ||
162 | request[i].dev = -1; | ||
163 | request[i].next = NULL; | ||
164 | } | ||
165 | } | ||
diff --git a/src/kernel/blk_drv/ramdisk.c b/src/kernel/blk_drv/ramdisk.c new file mode 100644 index 0000000..dc99f7c --- /dev/null +++ b/src/kernel/blk_drv/ramdisk.c | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * linux/kernel/blk_drv/ramdisk.c | ||
3 | * | ||
4 | * Written by Theodore Ts'o, 12/2/91 | ||
5 | */ | ||
6 | |||
7 | #include <string.h> | ||
8 | |||
9 | #include <linux/config.h> | ||
10 | #include <linux/sched.h> | ||
11 | #include <linux/fs.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <asm/system.h> | ||
14 | #include <asm/segment.h> | ||
15 | #include <asm/memory.h> | ||
16 | |||
17 | #define MAJOR_NR 1 | ||
18 | #include "blk.h" | ||
19 | |||
20 | char *rd_start; | ||
21 | int rd_length = 0; | ||
22 | |||
23 | void do_rd_request(void) | ||
24 | { | ||
25 | int len; | ||
26 | char *addr; | ||
27 | |||
28 | INIT_REQUEST; | ||
29 | addr = rd_start + (CURRENT->sector << 9); | ||
30 | len = CURRENT->nr_sectors << 9; | ||
31 | if ((MINOR(CURRENT->dev) != 1) || (addr+len > rd_start+rd_length)) { | ||
32 | end_request(0); | ||
33 | goto repeat; | ||
34 | } | ||
35 | if (CURRENT-> cmd == WRITE) { | ||
36 | (void ) memcpy(addr, | ||
37 | CURRENT->buffer, | ||
38 | len); | ||
39 | } else if (CURRENT->cmd == READ) { | ||
40 | (void) memcpy(CURRENT->buffer, | ||
41 | addr, | ||
42 | len); | ||
43 | } else | ||
44 | panic("unknown ramdisk-command"); | ||
45 | end_request(1); | ||
46 | goto repeat; | ||
47 | } | ||
48 | |||
49 | /* | ||
50 | * Returns amount of memory which needs to be reserved. | ||
51 | */ | ||
52 | long rd_init(long mem_start, int length) | ||
53 | { | ||
54 | int i; | ||
55 | char *cp; | ||
56 | |||
57 | blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; | ||
58 | rd_start = (char *) mem_start; | ||
59 | rd_length = length; | ||
60 | cp = rd_start; | ||
61 | for (i=0; i < length; i++) | ||
62 | *cp++ = '\0'; | ||
63 | return(length); | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * If the root device is the ram disk, try to load it. | ||
68 | * In order to do this, the root device is originally set to the | ||
69 | * floppy, and we later change it to be ram disk. | ||
70 | */ | ||
71 | void rd_load(void) | ||
72 | { | ||
73 | struct buffer_head *bh; | ||
74 | struct super_block s; | ||
75 | int block = 256; /* Start at block 256 */ | ||
76 | int i = 1; | ||
77 | int nblocks; | ||
78 | char *cp; /* Move pointer */ | ||
79 | |||
80 | if (!rd_length) | ||
81 | return; | ||
82 | printk("Ram disk: %d bytes, starting at 0x%x\n", rd_length, | ||
83 | (int) rd_start); | ||
84 | if (MAJOR(ROOT_DEV) != 2) | ||
85 | return; | ||
86 | bh = breada(ROOT_DEV,block+1,block,block+2,-1); | ||
87 | if (!bh) { | ||
88 | printk("Disk error while looking for ramdisk!\n"); | ||
89 | return; | ||
90 | } | ||
91 | *((struct d_super_block *) &s) = *((struct d_super_block *) bh->b_data); | ||
92 | brelse(bh); | ||
93 | if (s.s_magic != SUPER_MAGIC) | ||
94 | /* No ram disk image present, assume normal floppy boot */ | ||
95 | return; | ||
96 | nblocks = s.s_nzones << s.s_log_zone_size; | ||
97 | if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) { | ||
98 | printk("Ram disk image too big! (%d blocks, %d avail)\n", | ||
99 | nblocks, rd_length >> BLOCK_SIZE_BITS); | ||
100 | return; | ||
101 | } | ||
102 | printk("Loading %d bytes into ram disk... 0000k", | ||
103 | nblocks << BLOCK_SIZE_BITS); | ||
104 | cp = rd_start; | ||
105 | while (nblocks) { | ||
106 | if (nblocks > 2) | ||
107 | bh = breada(ROOT_DEV, block, block+1, block+2, -1); | ||
108 | else | ||
109 | bh = bread(ROOT_DEV, block); | ||
110 | if (!bh) { | ||
111 | printk("I/O error on block %d, aborting load\n", | ||
112 | block); | ||
113 | return; | ||
114 | } | ||
115 | (void) memcpy(cp, bh->b_data, BLOCK_SIZE); | ||
116 | brelse(bh); | ||
117 | printk("\010\010\010\010\010%4dk",i); | ||
118 | cp += BLOCK_SIZE; | ||
119 | block++; | ||
120 | nblocks--; | ||
121 | i++; | ||
122 | } | ||
123 | printk("\010\010\010\010\010done \n"); | ||
124 | ROOT_DEV=0x0101; | ||
125 | } | ||