root/kernel/blk_drv/floppy.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. floppy_deselect
  2. floppy_change
  3. setup_DMA
  4. output_byte
  5. result
  6. bad_flp_intr
  7. rw_interrupt
  8. setup_rw_floppy
  9. seek_interrupt
  10. transfer
  11. recal_interrupt
  12. unexpected_floppy_interrupt
  13. recalibrate_floppy
  14. reset_interrupt
  15. reset_floppy
  16. floppy_on_interrupt
  17. do_fd_request
  18. floppy_init

   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 
  30 /*
  31  * 28.02.92 - made track-buffering routines, based on the routines written
  32  * by entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
  33  */
  34 
  35 #include <linux/sched.h>
  36 #include <linux/fs.h>
  37 #include <linux/kernel.h>
  38 #include <linux/fdreg.h>
  39 #include <asm/system.h>
  40 #include <asm/io.h>
  41 #include <asm/segment.h>
  42 
  43 #define MAJOR_NR 2
  44 #include "blk.h"
  45 
  46 unsigned int changed_floppies = 0;
  47 
  48 static int recalibrate = 0;
  49 static int reset = 0;
  50 static int seek = 0;
  51 
  52 extern unsigned char current_DOR;
  53 
  54 #define immoutb_p(val,port) \
  55 __asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port))
  56 
  57 #define TYPE(x) ((x)>>2)
  58 #define DRIVE(x) ((x)&0x03)
  59 /*
  60  * Note that MAX_ERRORS=X doesn't imply that we retry every bad read
  61  * max X times - some types of errors increase the errorcount by 2 or
  62  * even 3, so we might actually retry only X/2 times before giving up.
  63  */
  64 #define MAX_ERRORS 12
  65 
  66 /*
  67  * globals used by 'result()'
  68  */
  69 #define MAX_REPLIES 7
  70 static unsigned char reply_buffer[MAX_REPLIES];
  71 #define ST0 (reply_buffer[0])
  72 #define ST1 (reply_buffer[1])
  73 #define ST2 (reply_buffer[2])
  74 #define ST3 (reply_buffer[3])
  75 
  76 /*
  77  * This struct defines the different floppy types. Unlike minix
  78  * linux doesn't have a "search for right type"-type, as the code
  79  * for that is convoluted and weird. I've got enough problems with
  80  * this driver as it is.
  81  *
  82  * The 'stretch' tells if the tracks need to be boubled for some
  83  * types (ie 360kB diskette in 1.2MB drive etc). Others should
  84  * be self-explanatory.
  85  */
  86 struct floppy_struct {
  87         unsigned int size, sect, head, track, stretch;
  88         unsigned char gap,rate,spec1;
  89 };
  90 
  91 static struct floppy_struct floppy_type[] = {
  92         {    0, 0,0, 0,0,0x00,0x00,0x00 },      /* no testing */
  93         {  720, 9,2,40,0,0x2A,0x02,0xDF },      /* 360kB PC diskettes */
  94         { 2400,15,2,80,0,0x1B,0x00,0xDF },      /* 1.2 MB AT-diskettes */
  95         {  720, 9,2,40,1,0x2A,0x02,0xDF },      /* 360kB in 720kB drive */
  96         { 1440, 9,2,80,0,0x2A,0x02,0xDF },      /* 3.5" 720kB diskette */
  97         {  720, 9,2,40,1,0x23,0x01,0xDF },      /* 360kB in 1.2MB drive */
  98         { 1440, 9,2,80,0,0x23,0x01,0xDF },      /* 720kB in 1.2MB drive */
  99         { 2880,18,2,80,0,0x1B,0x00,0xCF },      /* 1.44MB diskette */
 100 };
 101 
 102 /*
 103  * Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps
 104  * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
 105  * H is head unload time (1=16ms, 2=32ms, etc)
 106  *
 107  * Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc)
 108  * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA).
 109  */
 110 
 111 extern void floppy_interrupt(void);
 112 extern char tmp_floppy_area[1024];
 113 extern char floppy_track_buffer[512*2*18];
 114 
 115 /*
 116  * These are global variables, as that's the easiest way to give
 117  * information to interrupts. They are the data used for the current
 118  * request.
 119  */
 120 #define NO_TRACK 255
 121 
 122 static int read_track = 0;      /* flag to indicate if we want to read all track */
 123 static int buffer_track = -1;
 124 static int buffer_drive = -1;
 125 static int cur_spec1 = -1;
 126 static int cur_rate = -1;
 127 static struct floppy_struct * floppy = floppy_type;
 128 static unsigned char current_drive = 255;
 129 static unsigned char sector = 0;
 130 static unsigned char head = 0;
 131 static unsigned char track = 0;
 132 static unsigned char seek_track = 0;
 133 static unsigned char current_track = NO_TRACK;
 134 static unsigned char command = 0;
 135 unsigned char selected = 0;
 136 struct task_struct * wait_on_floppy_select = NULL;
 137 
 138 void floppy_deselect(unsigned int nr)
     /* [previous][next][first][last][top][bottom][index][help] */
 139 {
 140         if (nr != (current_DOR & 3))
 141                 printk("floppy_deselect: drive not selected\n\r");
 142         selected = 0;
 143         wake_up(&wait_on_floppy_select);
 144 }
 145 
 146 /*
 147  * floppy-change is never called from an interrupt, so we can relax a bit
 148  * here, sleep etc. Note that floppy-on tries to set current_DOR to point
 149  * to the desired drive, but it will probably not survive the sleep if
 150  * several floppies are used at the same time: thus the loop.
 151  */
 152 int floppy_change(struct buffer_head * bh)
     /* [previous][next][first][last][top][bottom][index][help] */
 153 {
 154         unsigned int mask = 1 << (bh->b_dev & 0x03);
 155 
 156         if (MAJOR(bh->b_dev) != 2) {
 157                 printk("floppy_changed: not a floppy\n");
 158                 return 0;
 159         }
 160         if (changed_floppies & mask) {
 161                 changed_floppies &= ~mask;
 162                 recalibrate = 1;
 163                 return 1;
 164         }
 165         if (!bh)
 166                 return 0;
 167         if (bh->b_dirt)
 168                 ll_rw_block(WRITE,bh);
 169         else {
 170                 buffer_track = -1;
 171                 bh->b_uptodate = 0;
 172                 ll_rw_block(READ,bh);
 173         }
 174         cli();
 175         while (bh->b_lock)
 176                 sleep_on(&bh->b_wait);
 177         sti();
 178         if (changed_floppies & mask) {
 179                 changed_floppies &= ~mask;
 180                 recalibrate = 1;
 181                 return 1;
 182         }       
 183         return 0;
 184 }
 185 
 186 #define copy_buffer(from,to) \
 187 __asm__("cld ; rep ; movsl" \
 188         ::"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \
 189         :"cx","di","si")
 190 
 191 static void setup_DMA(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 192 {
 193         unsigned long addr = (long) CURRENT->buffer;
 194         unsigned long count = 1024;
 195 
 196         cli();
 197         if (read_track) {
 198 /* mark buffer-track bad, in case all this fails.. */
 199                 buffer_drive = buffer_track = -1;
 200                 count = floppy->sect*2*512;
 201                 addr = (long) floppy_track_buffer;
 202         } else if (addr >= 0x100000) {
 203                 addr = (long) tmp_floppy_area;
 204                 if (command == FD_WRITE)
 205                         copy_buffer(CURRENT->buffer,tmp_floppy_area);
 206         }
 207 /* mask DMA 2 */
 208         immoutb_p(4|2,10);
 209 /* output command byte. I don't know why, but everyone (minix, */
 210 /* sanches & canton) output this twice, first to 12 then to 11 */
 211         __asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t"
 212         "outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:"::
 213         "a" ((char) ((command == FD_READ)?DMA_READ:DMA_WRITE)));
 214 /* 8 low bits of addr */
 215         immoutb_p(addr,4);
 216         addr >>= 8;
 217 /* bits 8-15 of addr */
 218         immoutb_p(addr,4);
 219         addr >>= 8;
 220 /* bits 16-19 of addr */
 221         immoutb_p(addr,0x81);
 222 /* low 8 bits of count-1 */
 223         count--;
 224         immoutb_p(count,5);
 225         count >>= 8;
 226 /* high 8 bits of count-1 */
 227         immoutb_p(count,5);
 228 /* activate DMA 2 */
 229         immoutb_p(0|2,10);
 230         sti();
 231 }
 232 
 233 static void output_byte(char byte)
     /* [previous][next][first][last][top][bottom][index][help] */
 234 {
 235         int counter;
 236         unsigned char status;
 237 
 238         if (reset)
 239                 return;
 240         for(counter = 0 ; counter < 10000 ; counter++) {
 241                 status = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR);
 242                 if (status == STATUS_READY) {
 243                         outb(byte,FD_DATA);
 244                         return;
 245                 }
 246         }
 247         current_track = NO_TRACK;
 248         reset = 1;
 249         printk("Unable to send byte to FDC\n\r");
 250 }
 251 
 252 static int result(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 253 {
 254         int i = 0, counter, status;
 255 
 256         if (reset)
 257                 return -1;
 258         for (counter = 0 ; counter < 10000 ; counter++) {
 259                 status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY);
 260                 if (status == STATUS_READY)
 261                         return i;
 262                 if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
 263                         if (i >= MAX_REPLIES)
 264                                 break;
 265                         reply_buffer[i++] = inb_p(FD_DATA);
 266                 }
 267         }
 268         reset = 1;
 269         current_track = NO_TRACK;
 270         printk("Getstatus times out\n\r");
 271         return -1;
 272 }
 273 
 274 static void bad_flp_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 275 {
 276         current_track = NO_TRACK;
 277         CURRENT->errors++;
 278         if (CURRENT->errors > MAX_ERRORS) {
 279                 floppy_deselect(current_drive);
 280                 end_request(0);
 281         }
 282         if (CURRENT->errors > MAX_ERRORS/2)
 283                 reset = 1;
 284         else
 285                 recalibrate = 1;
 286 }       
 287 
 288 /*
 289  * Ok, this interrupt is called after a DMA read/write has succeeded,
 290  * so we check the results, and copy any buffers.
 291  */
 292 static void rw_interrupt(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 293 {
 294         char * buffer_area;
 295 
 296         if (result() != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) || (ST2 & 0x73)) {
 297                 if (ST1 & 0x02) {
 298                         printk("Drive %d is write protected\n\r",current_drive);
 299                         floppy_deselect(current_drive);
 300                         end_request(0);
 301                 } else
 302                         bad_flp_intr();
 303                 do_fd_request();
 304                 return;
 305         }
 306         if (read_track) {
 307                 buffer_track = seek_track;
 308                 buffer_drive = current_drive;
 309                 buffer_area = floppy_track_buffer +
 310                         ((sector-1 + head*floppy->sect)<<9);
 311                 copy_buffer(buffer_area,CURRENT->buffer);
 312         } else if (command == FD_READ &&
 313                 (unsigned long)(CURRENT->buffer) >= 0x100000)
 314                 copy_buffer(tmp_floppy_area,CURRENT->buffer);
 315         floppy_deselect(current_drive);
 316         end_request(1);
 317         do_fd_request();
 318 }
 319 
 320 /*
 321  * We try to read tracks, but if we get too many errors, we
 322  * go back to reading just one sector at a time.
 323  *
 324  * This means we should be able to read a sector even if there
 325  * are other bad sectors on this track.
 326  */
 327 inline void setup_rw_floppy(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 328 {
 329         setup_DMA();
 330         do_floppy = rw_interrupt;
 331         output_byte(command);
 332         if (read_track) {
 333                 output_byte(current_drive);
 334                 output_byte(track);
 335                 output_byte(0);
 336                 output_byte(1);
 337         } else {
 338                 output_byte(head<<2 | current_drive);
 339                 output_byte(track);
 340                 output_byte(head);
 341                 output_byte(sector);
 342         }
 343         output_byte(2);         /* sector size = 512 */
 344         output_byte(floppy->sect);
 345         output_byte(floppy->gap);
 346         output_byte(0xFF);      /* sector size (0xff when n!=0 ?) */
 347         if (reset)
 348                 do_fd_request();
 349 }
 350 
 351 /*
 352  * This is the routine called after every seek (or recalibrate) interrupt
 353  * from the floppy controller. Note that the "unexpected interrupt" routine
 354  * also does a recalibrate, but doesn't come here.
 355  */
 356 static void seek_interrupt(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 357 {
 358 /* sense drive status */
 359         output_byte(FD_SENSEI);
 360         if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) {
 361                 recalibrate = 1;
 362                 bad_flp_intr();
 363                 do_fd_request();
 364                 return;
 365         }
 366         current_track = ST1;
 367         setup_rw_floppy();
 368 }
 369 
 370 /*
 371  * This routine is called when everything should be correctly set up
 372  * for the transfer (ie floppy motor is on and the correct floppy is
 373  * selected).
 374  */
 375 static void transfer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 376 {
 377         read_track = (command == FD_READ) && (CURRENT->errors < 4);
 378         if (cur_spec1 != floppy->spec1) {
 379                 cur_spec1 = floppy->spec1;
 380                 output_byte(FD_SPECIFY);
 381                 output_byte(cur_spec1);         /* hut etc */
 382                 output_byte(6);                 /* Head load time =6ms, DMA */
 383         }
 384         if (cur_rate != floppy->rate)
 385                 outb_p(cur_rate = floppy->rate,FD_DCR);
 386         if (reset) {
 387                 do_fd_request();
 388                 return;
 389         }
 390         if (!seek) {
 391                 setup_rw_floppy();
 392                 return;
 393         }
 394         do_floppy = seek_interrupt;
 395         output_byte(FD_SEEK);
 396         if (read_track)
 397                 output_byte(current_drive);
 398         else
 399                 output_byte((head<<2) | current_drive);
 400         output_byte(seek_track);
 401         if (reset)
 402                 do_fd_request();
 403 }
 404 
 405 /*
 406  * Special case - used after a unexpected interrupt (or reset)
 407  */
 408 static void recal_interrupt(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 409 {
 410         output_byte(FD_SENSEI);
 411         current_track = NO_TRACK;
 412         if (result()!=2 || (ST0 & 0xE0) == 0x60)
 413                 reset = 1;
 414         do_fd_request();
 415 }
 416 
 417 void unexpected_floppy_interrupt(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 418 {
 419         current_track = NO_TRACK;
 420         output_byte(FD_SENSEI);
 421         if (result()!=2 || (ST0 & 0xE0) == 0x60)
 422                 reset = 1;
 423         else
 424                 recalibrate = 1;
 425 }
 426 
 427 static void recalibrate_floppy(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 428 {
 429         recalibrate = 0;
 430         current_track = 0;
 431         do_floppy = recal_interrupt;
 432         output_byte(FD_RECALIBRATE);
 433         output_byte(head<<2 | current_drive);
 434         if (reset)
 435                 do_fd_request();
 436 }
 437 
 438 static void reset_interrupt(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 439 {
 440         output_byte(FD_SENSEI);
 441         (void) result();
 442         output_byte(FD_SPECIFY);
 443         output_byte(cur_spec1);         /* hut etc */
 444         output_byte(6);                 /* Head load time =6ms, DMA */
 445         do_fd_request();
 446 }
 447 
 448 /*
 449  * reset is done by pulling bit 2 of DOR low for a while.
 450  */
 451 static void reset_floppy(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 452 {
 453         int i;
 454 
 455         do_floppy = reset_interrupt;
 456         reset = 0;
 457         current_track = NO_TRACK;
 458         cur_spec1 = -1;
 459         cur_rate = -1;
 460         recalibrate = 1;
 461         printk("Reset-floppy called\n\r");
 462         cli();
 463         outb_p(current_DOR & ~0x04,FD_DOR);
 464         for (i=0 ; i<1000 ; i++)
 465                 __asm__("nop");
 466         outb(current_DOR,FD_DOR);
 467         sti();
 468 }
 469 
 470 static void floppy_on_interrupt(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 471 {
 472         if (inb(FD_DIR) & 0x80) {
 473                 changed_floppies |= 1<<current_drive;
 474                 buffer_track = -1;
 475         }
 476         if (reset) {
 477                 reset_floppy();
 478                 return;
 479         }
 480         if (recalibrate) {
 481                 recalibrate_floppy();
 482                 return;
 483         }
 484 /* We cannot do a floppy-select, as that might sleep. We just force it */
 485         selected = 1;
 486         if (current_drive != (current_DOR & 3)) {
 487                 seek = 1;
 488                 current_track = NO_TRACK;
 489                 current_DOR &= 0xFC;
 490                 current_DOR |= current_drive;
 491                 outb(current_DOR,FD_DOR);
 492                 add_timer(2,&transfer);
 493         } else
 494                 transfer();
 495 }
 496 
 497 void do_fd_request(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 498 {
 499         unsigned int block;
 500         char * buffer_area;
 501 
 502         INIT_REQUEST;
 503         seek = 0;
 504         floppy = (MINOR(CURRENT->dev)>>2) + floppy_type;
 505         if (current_drive != CURRENT_DEV)
 506                 current_track = NO_TRACK;
 507         current_drive = CURRENT_DEV;
 508         block = CURRENT->sector;
 509         if (block+2 > floppy->size) {
 510                 end_request(0);
 511                 goto repeat;
 512         }
 513         sector = block % floppy->sect;
 514         block /= floppy->sect;
 515         head = block % floppy->head;
 516         track = block / floppy->head;
 517         seek_track = track << floppy->stretch;
 518         if (CURRENT->cmd == READ)
 519                 command = FD_READ;
 520         else if (CURRENT->cmd == WRITE)
 521                 command = FD_WRITE;
 522         else {
 523                 printk("do_fd_request: unknown command\n");
 524                 end_request(0);
 525                 goto repeat;
 526         }
 527         if ((seek_track == buffer_track) &&
 528          (current_drive == buffer_drive)) {
 529                 buffer_area = floppy_track_buffer +
 530                         ((sector + head*floppy->sect)<<9);
 531                 if (command == FD_READ) {
 532                         copy_buffer(buffer_area,CURRENT->buffer);
 533                         end_request(1);
 534                         goto repeat;
 535                 } else
 536                         copy_buffer(CURRENT->buffer,buffer_area);
 537         }
 538         if (seek_track != current_track)
 539                 seek = 1;
 540         sector++;
 541         add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt);
 542 }
 543 
 544 static int floppy_sizes[] ={
 545            0,   0,   0,   0,
 546          360, 360 ,360, 360,
 547         1200,1200,1200,1200,
 548          360, 360, 360, 360,
 549          720, 720, 720, 720,
 550          360, 360, 360, 360,
 551          720, 720, 720, 720,
 552         1440,1440,1440,1440
 553 };
 554 
 555 void floppy_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 556 {
 557         outb(current_DOR,FD_DOR);
 558         blk_size[MAJOR_NR] = floppy_sizes;
 559         blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
 560         set_intr_gate(0x26,&floppy_interrupt);
 561         outb(inb_p(0x21)&~0x40,0x21);
 562 }

/* [previous][next][first][last][top][bottom][index][help] */