|
|
1.1 root 1: /*
2: * linux/kernel/floppy.c
3: *
4: * (C) 1991 Linus Torvalds
5: */
6:
7: /*
1.1.1.2 root 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: /*
1.1 root 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:
1.1.1.2 root 44: static int recalibrate = 0;
45: static int reset = 0;
46: static int seek = 0;
1.1 root 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: /*
1.1.1.2 root 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.
1.1 root 59: */
1.1.1.2 root 60: #define MAX_ERRORS 8
1.1 root 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
1.1.1.2 root 75: * for that is convoluted and weird. I've got enough problems with
76: * this driver as it is.
1.1 root 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 {
1.1.1.2 root 83: unsigned int size, sect, head, track, stretch;
1.1 root 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: };
1.1.1.3 ! root 95:
1.1 root 96: /*
97: * Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps
98: * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
99: * H is head unload time (1=16ms, 2=32ms, etc)
100: *
101: * Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc)
102: * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA).
103: */
104:
105: extern void floppy_interrupt(void);
106: extern char tmp_floppy_area[1024];
107:
108: /*
109: * These are global variables, as that's the easiest way to give
110: * information to interrupts. They are the data used for the current
111: * request.
112: */
113: static int cur_spec1 = -1;
114: static int cur_rate = -1;
115: static struct floppy_struct * floppy = floppy_type;
116: static unsigned char current_drive = 0;
117: static unsigned char sector = 0;
118: static unsigned char head = 0;
119: static unsigned char track = 0;
120: static unsigned char seek_track = 0;
1.1.1.2 root 121: static unsigned char current_track = 255;
1.1 root 122: static unsigned char command = 0;
1.1.1.2 root 123: unsigned char selected = 0;
124: struct task_struct * wait_on_floppy_select = NULL;
125:
126: void floppy_deselect(unsigned int nr)
127: {
128: if (nr != (current_DOR & 3))
129: printk("floppy_deselect: drive not selected\n\r");
130: selected = 0;
131: wake_up(&wait_on_floppy_select);
132: }
1.1 root 133:
134: /*
135: * floppy-change is never called from an interrupt, so we can relax a bit
1.1.1.2 root 136: * here, sleep etc. Note that floppy-on tries to set current_DOR to point
137: * to the desired drive, but it will probably not survive the sleep if
138: * several floppies are used at the same time: thus the loop.
1.1 root 139: */
140: int floppy_change(unsigned int nr)
141: {
1.1.1.2 root 142: repeat:
1.1 root 143: floppy_on(nr);
1.1.1.2 root 144: while ((current_DOR & 3) != nr && selected)
1.1.1.3 ! root 145: sleep_on(&wait_on_floppy_select);
1.1.1.2 root 146: if ((current_DOR & 3) != nr)
147: goto repeat;
1.1 root 148: if (inb(FD_DIR) & 0x80) {
149: floppy_off(nr);
150: return 1;
151: }
152: floppy_off(nr);
153: return 0;
154: }
155:
156: #define copy_buffer(from,to) \
157: __asm__("cld ; rep ; movsl" \
158: ::"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \
159: :"cx","di","si")
160:
161: static void setup_DMA(void)
162: {
163: long addr = (long) CURRENT->buffer;
164:
1.1.1.2 root 165: cli();
1.1 root 166: if (addr >= 0x100000) {
167: addr = (long) tmp_floppy_area;
168: if (command == FD_WRITE)
169: copy_buffer(CURRENT->buffer,tmp_floppy_area);
170: }
171: /* mask DMA 2 */
172: immoutb_p(4|2,10);
173: /* output command byte. I don't know why, but everyone (minix, */
174: /* sanches & canton) output this twice, first to 12 then to 11 */
175: __asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t"
176: "outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:"::
177: "a" ((char) ((command == FD_READ)?DMA_READ:DMA_WRITE)));
178: /* 8 low bits of addr */
179: immoutb_p(addr,4);
180: addr >>= 8;
181: /* bits 8-15 of addr */
182: immoutb_p(addr,4);
183: addr >>= 8;
184: /* bits 16-19 of addr */
185: immoutb_p(addr,0x81);
186: /* low 8 bits of count-1 (1024-1=0x3ff) */
187: immoutb_p(0xff,5);
188: /* high 8 bits of count-1 */
189: immoutb_p(3,5);
190: /* activate DMA 2 */
191: immoutb_p(0|2,10);
1.1.1.2 root 192: sti();
1.1 root 193: }
194:
195: static void output_byte(char byte)
196: {
197: int counter;
198: unsigned char status;
199:
1.1.1.2 root 200: if (reset)
201: return;
1.1 root 202: for(counter = 0 ; counter < 10000 ; counter++) {
1.1.1.2 root 203: status = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR);
1.1 root 204: if (status == STATUS_READY) {
205: outb(byte,FD_DATA);
206: return;
207: }
208: }
1.1.1.2 root 209: reset = 1;
1.1 root 210: printk("Unable to send byte to FDC\n\r");
211: }
212:
213: static int result(void)
214: {
215: int i = 0, counter, status;
216:
1.1.1.2 root 217: if (reset)
218: return -1;
1.1 root 219: for (counter = 0 ; counter < 10000 ; counter++) {
1.1.1.2 root 220: status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY);
1.1 root 221: if (status == STATUS_READY)
222: return i;
223: if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
224: if (i >= MAX_REPLIES)
225: break;
1.1.1.2 root 226: reply_buffer[i++] = inb_p(FD_DATA);
1.1 root 227: }
228: }
1.1.1.2 root 229: reset = 1;
1.1 root 230: printk("Getstatus times out\n\r");
231: return -1;
232: }
233:
1.1.1.2 root 234: static void bad_flp_intr(void)
235: {
236: CURRENT->errors++;
237: if (CURRENT->errors > MAX_ERRORS) {
238: floppy_deselect(current_drive);
239: end_request(0);
240: }
241: if (CURRENT->errors > MAX_ERRORS/2)
242: reset = 1;
243: else
244: recalibrate = 1;
245: }
246:
1.1 root 247: /*
1.1.1.2 root 248: * Ok, this interrupt is called after a DMA read/write has succeeded,
249: * so we check the results, and copy any buffers.
1.1 root 250: */
1.1.1.2 root 251: static void rw_interrupt(void)
1.1 root 252: {
1.1.1.2 root 253: if (result() != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) || (ST2 & 0x73)) {
254: if (ST1 & 0x02) {
255: printk("Drive %d is write protected\n\r",current_drive);
1.1 root 256: floppy_deselect(current_drive);
257: end_request(0);
1.1.1.2 root 258: } else
259: bad_flp_intr();
260: do_fd_request();
1.1 root 261: return;
262: }
1.1.1.2 root 263: if (command == FD_READ && (unsigned long)(CURRENT->buffer) >= 0x100000)
264: copy_buffer(tmp_floppy_area,CURRENT->buffer);
265: floppy_deselect(current_drive);
266: end_request(1);
267: do_fd_request();
268: }
269:
270: inline void setup_rw_floppy(void)
271: {
1.1 root 272: setup_DMA();
273: do_floppy = rw_interrupt;
274: output_byte(command);
275: output_byte(head<<2 | current_drive);
276: output_byte(track);
277: output_byte(head);
278: output_byte(sector);
279: output_byte(2); /* sector size = 512 */
280: output_byte(floppy->sect);
281: output_byte(floppy->gap);
282: output_byte(0xFF); /* sector size (0xff when n!=0 ?) */
1.1.1.2 root 283: if (reset)
284: do_fd_request();
1.1 root 285: }
286:
287: /*
1.1.1.2 root 288: * This is the routine called after every seek (or recalibrate) interrupt
289: * from the floppy controller. Note that the "unexpected interrupt" routine
290: * also does a recalibrate, but doesn't come here.
1.1 root 291: */
1.1.1.2 root 292: static void seek_interrupt(void)
1.1 root 293: {
1.1.1.2 root 294: /* sense drive status */
295: output_byte(FD_SENSEI);
296: if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) {
297: bad_flp_intr();
298: do_fd_request();
1.1 root 299: return;
300: }
1.1.1.2 root 301: current_track = ST1;
302: setup_rw_floppy();
1.1 root 303: }
304:
305: /*
306: * This routine is called when everything should be correctly set up
307: * for the transfer (ie floppy motor is on and the correct floppy is
308: * selected).
309: */
310: static void transfer(void)
311: {
312: if (cur_spec1 != floppy->spec1) {
313: cur_spec1 = floppy->spec1;
314: output_byte(FD_SPECIFY);
315: output_byte(cur_spec1); /* hut etc */
316: output_byte(6); /* Head load time =6ms, DMA */
317: }
318: if (cur_rate != floppy->rate)
319: outb_p(cur_rate = floppy->rate,FD_DCR);
1.1.1.2 root 320: if (reset) {
321: do_fd_request();
322: return;
323: }
324: if (!seek) {
325: setup_rw_floppy();
326: return;
327: }
1.1 root 328: do_floppy = seek_interrupt;
329: if (seek_track) {
330: output_byte(FD_SEEK);
331: output_byte(head<<2 | current_drive);
332: output_byte(seek_track);
333: } else {
334: output_byte(FD_RECALIBRATE);
335: output_byte(head<<2 | current_drive);
336: }
1.1.1.2 root 337: if (reset)
338: do_fd_request();
1.1 root 339: }
340:
341: /*
342: * Special case - used after a unexpected interrupt (or reset)
343: */
344: static void recal_interrupt(void)
345: {
346: output_byte(FD_SENSEI);
1.1.1.2 root 347: if (result()!=2 || (ST0 & 0xE0) == 0x60)
348: reset = 1;
349: else
350: recalibrate = 0;
1.1 root 351: do_fd_request();
352: }
353:
354: void unexpected_floppy_interrupt(void)
355: {
356: output_byte(FD_SENSEI);
1.1.1.2 root 357: if (result()!=2 || (ST0 & 0xE0) == 0x60)
358: reset = 1;
359: else
360: recalibrate = 1;
361: }
362:
363: static void recalibrate_floppy(void)
364: {
365: recalibrate = 0;
366: current_track = 0;
1.1 root 367: do_floppy = recal_interrupt;
368: output_byte(FD_RECALIBRATE);
369: output_byte(head<<2 | current_drive);
1.1.1.2 root 370: if (reset)
371: do_fd_request();
1.1 root 372: }
373:
374: static void reset_interrupt(void)
375: {
376: output_byte(FD_SENSEI);
377: (void) result();
1.1.1.2 root 378: output_byte(FD_SPECIFY);
379: output_byte(cur_spec1); /* hut etc */
380: output_byte(6); /* Head load time =6ms, DMA */
381: do_fd_request();
1.1 root 382: }
383:
1.1.1.2 root 384: /*
385: * reset is done by pulling bit 2 of DOR low for a while.
386: */
1.1 root 387: static void reset_floppy(void)
388: {
1.1.1.2 root 389: int i;
390:
391: reset = 0;
392: cur_spec1 = -1;
393: cur_rate = -1;
394: recalibrate = 1;
1.1 root 395: printk("Reset-floppy called\n\r");
1.1.1.2 root 396: cli();
1.1 root 397: do_floppy = reset_interrupt;
1.1.1.2 root 398: outb_p(current_DOR & ~0x04,FD_DOR);
399: for (i=0 ; i<100 ; i++)
400: __asm__("nop");
1.1 root 401: outb(current_DOR,FD_DOR);
1.1.1.2 root 402: sti();
1.1 root 403: }
404:
405: static void floppy_on_interrupt(void)
406: {
407: /* We cannot do a floppy-select, as that might sleep. We just force it */
408: selected = 1;
1.1.1.2 root 409: if (current_drive != (current_DOR & 3)) {
410: current_DOR &= 0xFC;
411: current_DOR |= current_drive;
412: outb(current_DOR,FD_DOR);
413: add_timer(2,&transfer);
414: } else
415: transfer();
1.1 root 416: }
417:
418: void do_fd_request(void)
419: {
420: unsigned int block;
421:
1.1.1.2 root 422: seek = 0;
423: if (reset) {
424: reset_floppy();
425: return;
426: }
427: if (recalibrate) {
428: recalibrate_floppy();
429: return;
430: }
1.1 root 431: INIT_REQUEST;
432: floppy = (MINOR(CURRENT->dev)>>2) + floppy_type;
1.1.1.2 root 433: if (current_drive != CURRENT_DEV)
434: seek = 1;
1.1 root 435: current_drive = CURRENT_DEV;
436: block = CURRENT->sector;
437: if (block+2 > floppy->size) {
438: end_request(0);
439: goto repeat;
440: }
441: sector = block % floppy->sect;
442: block /= floppy->sect;
443: head = block % floppy->head;
444: track = block / floppy->head;
445: seek_track = track << floppy->stretch;
1.1.1.2 root 446: if (seek_track != current_track)
447: seek = 1;
1.1 root 448: sector++;
449: if (CURRENT->cmd == READ)
450: command = FD_READ;
451: else if (CURRENT->cmd == WRITE)
452: command = FD_WRITE;
453: else
454: panic("do_fd_request: unknown command");
455: add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt);
456: }
457:
1.1.1.3 ! root 458: static int floppy_sizes[] ={
! 459: 0, 0, 0, 0,
! 460: 360, 360 ,360, 360,
! 461: 1200,1200,1200,1200,
! 462: 360, 360, 360, 360,
! 463: 720, 720, 720, 720,
! 464: 360, 360, 360, 360,
! 465: 720, 720, 720, 720,
! 466: 1440,1440,1440,1440
! 467: };
! 468:
1.1 root 469: void floppy_init(void)
470: {
1.1.1.3 ! root 471: blk_size[MAJOR_NR] = floppy_sizes;
1.1 root 472: blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
1.1.1.2 root 473: set_trap_gate(0x26,&floppy_interrupt);
1.1 root 474: outb(inb_p(0x21)&~0x40,0x21);
475: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.