root/kernel/blk_drv/floppy.c

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

DEFINITIONS

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

   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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

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