|
|
1.1 ! root 1: /* ! 2: * linux/kernel/floppy.c ! 3: * ! 4: * (C) 1991 Linus Torvalds ! 5: */ ! 6: ! 7: /* ! 8: * As with hd.c, all routines within this file can (and will) be called ! 9: * by interrupts, so extreme caution is needed. A hardware interrupt ! 10: * handler may not sleep, or a kernel panic will happen. Thus I cannot ! 11: * call "floppy-on" directly, but have to set a special timer interrupt ! 12: * etc. ! 13: * ! 14: * Also, I'm not certain this works on more than 1 floppy. Bugs may ! 15: * abund. ! 16: */ ! 17: ! 18: #include <linux/sched.h> ! 19: #include <linux/fs.h> ! 20: #include <linux/kernel.h> ! 21: #include <linux/fdreg.h> ! 22: #include <asm/system.h> ! 23: #include <asm/io.h> ! 24: #include <asm/segment.h> ! 25: ! 26: #define MAJOR_NR 2 ! 27: #include "blk.h" ! 28: ! 29: static void reset_floppy(void); ! 30: static void seek_interrupt(void); ! 31: static void rw_interrupt(void); ! 32: ! 33: extern unsigned char current_DOR; ! 34: extern unsigned char selected; ! 35: ! 36: #define immoutb_p(val,port) \ ! 37: __asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port)) ! 38: ! 39: #define TYPE(x) ((x)>>2) ! 40: #define DRIVE(x) ((x)&0x03) ! 41: /* ! 42: * Note that MAX_ERRORS=10 doesn't imply that we retry every bad read ! 43: * max 10 times - some types of errors increase the errorcount by 2, ! 44: * so we might actually retry only 6-7 times before giving up. ! 45: */ ! 46: #define MAX_ERRORS 10 ! 47: ! 48: /* ! 49: * globals used by 'result()' ! 50: */ ! 51: #define MAX_REPLIES 7 ! 52: static unsigned char reply_buffer[MAX_REPLIES]; ! 53: #define ST0 (reply_buffer[0]) ! 54: #define ST1 (reply_buffer[1]) ! 55: #define ST2 (reply_buffer[2]) ! 56: #define ST3 (reply_buffer[3]) ! 57: ! 58: /* ! 59: * This struct defines the different floppy types. Unlike minix ! 60: * linux doesn't have a "search for right type"-type, as the code ! 61: * for that is convoluted and weird. ! 62: * ! 63: * The 'stretch' tells if the tracks need to be boubled for some ! 64: * types (ie 360kB diskette in 1.2MB drive etc). Others should ! 65: * be self-explanatory. ! 66: */ ! 67: static struct floppy_struct { ! 68: int size, sect, head, track, stretch; ! 69: unsigned char gap,rate,spec1; ! 70: } floppy_type[] = { ! 71: { 0, 0,0, 0,0,0x00,0x00,0x00 }, /* no testing */ ! 72: { 720, 9,2,40,0,0x2A,0x02,0xDF }, /* 360kB PC diskettes */ ! 73: { 2400,15,2,80,0,0x1B,0x00,0xDF }, /* 1.2 MB AT-diskettes */ ! 74: { 720, 9,2,40,1,0x2A,0x02,0xDF }, /* 360kB in 720kB drive */ ! 75: { 1440, 9,2,80,0,0x2A,0x02,0xDF }, /* 3.5" 720kB diskette */ ! 76: { 720, 9,2,40,1,0x23,0x01,0xDF }, /* 360kB in 1.2MB drive */ ! 77: { 1440, 9,2,80,0,0x23,0x01,0xDF }, /* 720kB in 1.2MB drive */ ! 78: { 2880,18,2,80,0,0x1B,0x00,0xCF }, /* 1.44MB diskette */ ! 79: }; ! 80: /* ! 81: * Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps ! 82: * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc), ! 83: * H is head unload time (1=16ms, 2=32ms, etc) ! 84: * ! 85: * Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc) ! 86: * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA). ! 87: */ ! 88: ! 89: extern void floppy_interrupt(void); ! 90: extern char tmp_floppy_area[1024]; ! 91: ! 92: /* ! 93: * These are global variables, as that's the easiest way to give ! 94: * information to interrupts. They are the data used for the current ! 95: * request. ! 96: */ ! 97: static int cur_spec1 = -1; ! 98: static int cur_rate = -1; ! 99: static struct floppy_struct * floppy = floppy_type; ! 100: static unsigned char current_drive = 0; ! 101: static unsigned char sector = 0; ! 102: static unsigned char head = 0; ! 103: static unsigned char track = 0; ! 104: static unsigned char seek_track = 0; ! 105: static unsigned char command = 0; ! 106: ! 107: /* ! 108: * floppy-change is never called from an interrupt, so we can relax a bit ! 109: * here. ! 110: */ ! 111: int floppy_change(unsigned int nr) ! 112: { ! 113: floppy_on(nr); ! 114: floppy_select(nr); ! 115: if (inb(FD_DIR) & 0x80) { ! 116: floppy_off(nr); ! 117: return 1; ! 118: } ! 119: floppy_off(nr); ! 120: return 0; ! 121: } ! 122: ! 123: #define copy_buffer(from,to) \ ! 124: __asm__("cld ; rep ; movsl" \ ! 125: ::"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \ ! 126: :"cx","di","si") ! 127: ! 128: static void setup_DMA(void) ! 129: { ! 130: long addr = (long) CURRENT->buffer; ! 131: ! 132: if (addr >= 0x100000) { ! 133: addr = (long) tmp_floppy_area; ! 134: if (command == FD_WRITE) ! 135: copy_buffer(CURRENT->buffer,tmp_floppy_area); ! 136: } ! 137: /* mask DMA 2 */ ! 138: immoutb_p(4|2,10); ! 139: /* output command byte. I don't know why, but everyone (minix, */ ! 140: /* sanches & canton) output this twice, first to 12 then to 11 */ ! 141: __asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t" ! 142: "outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:":: ! 143: "a" ((char) ((command == FD_READ)?DMA_READ:DMA_WRITE))); ! 144: /* 8 low bits of addr */ ! 145: immoutb_p(addr,4); ! 146: addr >>= 8; ! 147: /* bits 8-15 of addr */ ! 148: immoutb_p(addr,4); ! 149: addr >>= 8; ! 150: /* bits 16-19 of addr */ ! 151: immoutb_p(addr,0x81); ! 152: /* low 8 bits of count-1 (1024-1=0x3ff) */ ! 153: immoutb_p(0xff,5); ! 154: /* high 8 bits of count-1 */ ! 155: immoutb_p(3,5); ! 156: /* activate DMA 2 */ ! 157: immoutb_p(0|2,10); ! 158: } ! 159: ! 160: static void output_byte(char byte) ! 161: { ! 162: int counter; ! 163: unsigned char status; ! 164: ! 165: for(counter = 0 ; counter < 10000 ; counter++) { ! 166: status = inb(FD_STATUS) & (STATUS_READY | STATUS_DIR); ! 167: if (status == STATUS_READY) { ! 168: outb(byte,FD_DATA); ! 169: return; ! 170: } ! 171: } ! 172: printk("Unable to send byte to FDC\n\r"); ! 173: } ! 174: ! 175: static int result(void) ! 176: { ! 177: int i = 0, counter, status; ! 178: ! 179: for (counter = 0 ; counter < 10000 ; counter++) { ! 180: status = inb(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY); ! 181: if (status == STATUS_READY) ! 182: return i; ! 183: if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) { ! 184: if (i >= MAX_REPLIES) ! 185: break; ! 186: reply_buffer[i++] = inb(FD_DATA); ! 187: } ! 188: } ! 189: printk("Getstatus times out\n\r"); ! 190: return -1; ! 191: } ! 192: ! 193: /* ! 194: * This is the routine called after every seek (or recalibrate) interrupt ! 195: * from the floppy controller. Note that the "unexpected interrupt" routine ! 196: * also does a recalibrate, but doesn't come here. ! 197: */ ! 198: static void seek_interrupt(void) ! 199: { ! 200: /* sense drive status */ ! 201: output_byte(FD_SENSEI); ! 202: if (result() != 2 || (ST0 & 0xF8) != 0x20) { ! 203: CURRENT->errors++; ! 204: if (CURRENT->errors > MAX_ERRORS) { ! 205: floppy_deselect(current_drive); ! 206: end_request(0); ! 207: reset_floppy(); ! 208: return; ! 209: } ! 210: output_byte(FD_RECALIBRATE); ! 211: output_byte(head<<2 | current_drive); ! 212: return; ! 213: } ! 214: /* are we on the right track? */ ! 215: if (ST1 != seek_track) { ! 216: CURRENT->errors++; ! 217: if (CURRENT->errors > MAX_ERRORS) { ! 218: floppy_deselect(current_drive); ! 219: end_request(0); ! 220: reset_floppy(); ! 221: return; ! 222: } ! 223: output_byte(FD_SEEK); ! 224: output_byte(head<<2 | current_drive); ! 225: output_byte(seek_track); ! 226: return; ! 227: } ! 228: /* yes - set up DMA and read/write command */ ! 229: setup_DMA(); ! 230: do_floppy = rw_interrupt; ! 231: output_byte(command); ! 232: output_byte(head<<2 | current_drive); ! 233: output_byte(track); ! 234: output_byte(head); ! 235: output_byte(sector); ! 236: output_byte(2); /* sector size = 512 */ ! 237: output_byte(floppy->sect); ! 238: output_byte(floppy->gap); ! 239: output_byte(0xFF); /* sector size (0xff when n!=0 ?) */ ! 240: } ! 241: ! 242: /* ! 243: * Ok, this interrupt is called after a DMA read/write has succeeded, ! 244: * so we check the results, and copy any buffers. ! 245: */ ! 246: static void rw_interrupt(void) ! 247: { ! 248: if (result() != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) || ! 249: (ST2 & 0x73)) { ! 250: CURRENT->errors++; ! 251: if (CURRENT->errors > MAX_ERRORS || (ST1 & 0x02)) { ! 252: if (ST1 & 0x02) ! 253: printk("Drive %d is write protected\n\r", ! 254: current_drive); ! 255: floppy_deselect(current_drive); ! 256: end_request(0); ! 257: do_fd_request(); ! 258: return; ! 259: } ! 260: do_floppy = seek_interrupt; ! 261: output_byte(FD_RECALIBRATE); ! 262: output_byte(head<<2 | current_drive); ! 263: return; ! 264: } ! 265: if (command == FD_READ && (long)(CURRENT->buffer) >= 0x100000) ! 266: copy_buffer(tmp_floppy_area,CURRENT->buffer); ! 267: floppy_deselect(current_drive); ! 268: end_request(1); ! 269: do_fd_request(); ! 270: } ! 271: ! 272: /* ! 273: * This routine is called when everything should be correctly set up ! 274: * for the transfer (ie floppy motor is on and the correct floppy is ! 275: * selected). ! 276: */ ! 277: static void transfer(void) ! 278: { ! 279: if (cur_spec1 != floppy->spec1) { ! 280: cur_spec1 = floppy->spec1; ! 281: output_byte(FD_SPECIFY); ! 282: output_byte(cur_spec1); /* hut etc */ ! 283: output_byte(6); /* Head load time =6ms, DMA */ ! 284: } ! 285: if (cur_rate != floppy->rate) ! 286: outb_p(cur_rate = floppy->rate,FD_DCR); ! 287: do_floppy = seek_interrupt; ! 288: if (seek_track) { ! 289: output_byte(FD_SEEK); ! 290: output_byte(head<<2 | current_drive); ! 291: output_byte(seek_track); ! 292: } else { ! 293: output_byte(FD_RECALIBRATE); ! 294: output_byte(head<<2 | current_drive); ! 295: } ! 296: } ! 297: ! 298: /* ! 299: * Special case - used after a unexpected interrupt (or reset) ! 300: */ ! 301: static void recal_interrupt(void) ! 302: { ! 303: do_floppy = NULL; ! 304: output_byte(FD_SENSEI); ! 305: if (result()!=2 || (ST0 & 0xE0) == 0x60) { ! 306: reset_floppy(); ! 307: return; ! 308: } ! 309: do_fd_request(); ! 310: } ! 311: ! 312: void unexpected_floppy_interrupt(void) ! 313: { ! 314: output_byte(FD_SENSEI); ! 315: if (result()!=2 || (ST0 & 0xE0) == 0x60) { ! 316: reset_floppy(); ! 317: return; ! 318: } ! 319: do_floppy = recal_interrupt; ! 320: output_byte(FD_RECALIBRATE); ! 321: output_byte(head<<2 | current_drive); ! 322: } ! 323: ! 324: static void reset_interrupt(void) ! 325: { ! 326: output_byte(FD_SENSEI); ! 327: (void) result(); ! 328: do_floppy = recal_interrupt; ! 329: output_byte(FD_RECALIBRATE); ! 330: output_byte(head<<2 | current_drive); ! 331: } ! 332: ! 333: static void reset_floppy(void) ! 334: { ! 335: printk("Reset-floppy called\n\r"); ! 336: do_floppy = reset_interrupt; ! 337: outb_p(0,FD_DOR); ! 338: outb(current_DOR,FD_DOR); ! 339: } ! 340: ! 341: static void floppy_on_interrupt(void) ! 342: { ! 343: /* We cannot do a floppy-select, as that might sleep. We just force it */ ! 344: selected = 1; ! 345: current_DOR &= 0xFC; ! 346: current_DOR |= current_drive; ! 347: transfer(); ! 348: } ! 349: ! 350: void do_fd_request(void) ! 351: { ! 352: unsigned int block; ! 353: ! 354: INIT_REQUEST; ! 355: floppy = (MINOR(CURRENT->dev)>>2) + floppy_type; ! 356: current_drive = CURRENT_DEV; ! 357: block = CURRENT->sector; ! 358: if (block+2 > floppy->size) { ! 359: end_request(0); ! 360: goto repeat; ! 361: } ! 362: sector = block % floppy->sect; ! 363: block /= floppy->sect; ! 364: head = block % floppy->head; ! 365: track = block / floppy->head; ! 366: seek_track = track << floppy->stretch; ! 367: sector++; ! 368: if (CURRENT->cmd == READ) ! 369: command = FD_READ; ! 370: else if (CURRENT->cmd == WRITE) ! 371: command = FD_WRITE; ! 372: else ! 373: panic("do_fd_request: unknown command"); ! 374: add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt); ! 375: } ! 376: ! 377: void floppy_init(void) ! 378: { ! 379: blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; ! 380: set_intr_gate(0x26,&floppy_interrupt); ! 381: outb(inb_p(0x21)&~0x40,0x21); ! 382: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.