The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/pc/devfloppy.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 #include        "u.h"
    2 #include        "../port/lib.h"
    3 #include        "mem.h"
    4 #include        "dat.h"
    5 #include        "fns.h"
    6 #include        "io.h"
    7 #include        "../port/error.h"
    8 
    9 #include        "floppy.h"
   10 
   11 /* Intel 82077A (8272A compatible) floppy controller */
   12 
   13 /* This module expects the following functions to be defined
   14  * elsewhere: 
   15  * 
   16  * inb()
   17  * outb()
   18  * floppyexec()
   19  * floppyeject() 
   20  * floppysetup0()
   21  * floppysetup1()
   22  * dmainit()
   23  * dmasetup()
   24  * dmaend()
   25  * 
   26  * On DMA systems, floppyexec() should be an empty function; 
   27  * on non-DMA systems, dmaend() should be an empty function; 
   28  * dmasetup() may enforce maximum transfer sizes. 
   29  */
   30 
   31 enum {
   32         /* file types */
   33         Qdir=           0, 
   34         Qdata=          (1<<2),
   35         Qctl=           (2<<2),
   36         Qmask=          (3<<2),
   37 
   38         DMAchan=        2,      /* floppy dma channel */
   39 };
   40 
   41 #define DPRINT if(floppydebug)print
   42 int floppydebug = 0;
   43 
   44 /*
   45  *  types of drive (from PC equipment byte)
   46  */
   47 enum
   48 {
   49         Tnone=          0,
   50         T360kb=         1,
   51         T1200kb=        2,
   52         T720kb=         3,
   53         T1440kb=        4,
   54 };
   55 
   56 FType floppytype[] =
   57 {
   58  { "3½HD",     T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54, 0, },
   59  { "3½DD",     T1440kb, 512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
   60  { "3½DD",     T720kb,  512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
   61  { "5¼HD",     T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, },
   62  { "5¼DD",     T1200kb, 512,  9, 2, 2, 40, 0x2A, 0x50, 1, },
   63  { "ATT3B1",    T1200kb, 512,  8, 2, 2, 48, 0x2A, 0x50, 1, },
   64  { "5¼DD",     T360kb,  512,  9, 2, 1, 40, 0x2A, 0x50, 2, },
   65 };
   66 
   67 /*
   68  *  bytes per sector encoding for the controller.
   69  *  - index for b2c is is (bytes per sector/128).
   70  *  - index for c2b is code from b2c
   71  */
   72 static int b2c[] =
   73 {
   74 [1]     0,
   75 [2]     1,
   76 [4]     2,
   77 [8]     3,
   78 };
   79 static int c2b[] =
   80 {
   81         128,
   82         256,
   83         512,
   84         1024,
   85 };
   86 
   87 FController     fl;
   88 
   89 #define MOTORBIT(i)     (1<<((i)+4))
   90 
   91 /*
   92  *  predeclared
   93  */
   94 static int      cmddone(void*);
   95 static void     floppyformat(FDrive*, Cmdbuf*);
   96 static void     floppykproc(void*);
   97 static void     floppypos(FDrive*,long);
   98 static int      floppyrecal(FDrive*);
   99 static int      floppyresult(void);
  100 static void     floppyrevive(void);
  101 static long     floppyseek(FDrive*, long);
  102 static int      floppysense(void);
  103 static void     floppywait(int);
  104 static long     floppyxfer(FDrive*, int, void*, long, long);
  105 
  106 Dirtab floppydir[]={
  107         ".",            {Qdir, 0, QTDIR},       0,      0550,
  108         "fd0disk",              {Qdata + 0},    0,      0660,
  109         "fd0ctl",               {Qctl + 0},     0,      0660,
  110         "fd1disk",              {Qdata + 1},    0,      0660,
  111         "fd1ctl",               {Qctl + 1},     0,      0660,
  112         "fd2disk",              {Qdata + 2},    0,      0660,
  113         "fd2ctl",               {Qctl + 2},     0,      0660,
  114         "fd3disk",              {Qdata + 3},    0,      0660,
  115         "fd3ctl",               {Qctl + 3},     0,      0660,
  116 };
  117 #define NFDIR   2       /* directory entries/drive */
  118 
  119 enum
  120 {
  121         CMdebug,
  122         CMnodebug,
  123         CMeject,
  124         CMformat,
  125         CMreset,
  126 };
  127 
  128 static Cmdtab floppyctlmsg[] =
  129 {
  130         CMdebug,        "debug",        1,
  131         CMnodebug,      "nodebug", 1,
  132         CMeject,        "eject",        1,
  133         CMformat,       "format",       0,
  134         CMreset,        "reset",        1,
  135 };
  136 
  137 static void
  138 fldump(void)
  139 {
  140         DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb),
  141                 inb(Pdor), inb(Pmsr), inb(Pdir));
  142 }
  143 
  144 /*
  145  *  set floppy drive to its default type
  146  */
  147 static void
  148 floppysetdef(FDrive *dp)
  149 {
  150         FType *t;
  151 
  152         for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++)
  153                 if(dp->dt == t->dt){
  154                         dp->t = t;
  155                         floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
  156                         break;
  157                 }
  158 }
  159 
  160 static void
  161 floppyreset(void)
  162 {
  163         FDrive *dp;
  164         FType *t;
  165         ulong maxtsize;
  166         
  167         floppysetup0(&fl);
  168         if(fl.ndrive == 0)
  169                 return;
  170 
  171         /*
  172          *  init dependent parameters
  173          */
  174         maxtsize = 0;
  175         for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
  176                 t->cap = t->bytes * t->heads * t->sectors * t->tracks;
  177                 t->bcode = b2c[t->bytes/128];
  178                 t->tsize = t->bytes * t->sectors;
  179                 if(maxtsize < t->tsize)
  180                         maxtsize = t->tsize;
  181         }
  182 
  183         /*
  184          * Should check if this fails. Can do so
  185          * if there is no space <= 16MB for the DMA
  186          * bounce buffer.
  187          */
  188         dmainit(DMAchan, maxtsize);
  189 
  190         /*
  191          *  allocate the drive storage
  192          */
  193         fl.d = xalloc(fl.ndrive*sizeof(FDrive));
  194         fl.selected = fl.d;
  195 
  196         /*
  197          *  stop the motors
  198          */
  199         fl.motor = 0;
  200         delay(10);
  201         outb(Pdor, fl.motor | Fintena | Fena);
  202         delay(10);
  203 
  204         /*
  205          *  init drives
  206          */
  207         for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
  208                 dp->dev = dp - fl.d;
  209                 dp->dt = T1440kb;
  210                 floppysetdef(dp);
  211                 dp->cyl = -1;                   /* because we don't know */
  212                 dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024);
  213                 dp->ccyl = -1;
  214                 dp->vers = 0;
  215         }
  216 
  217         /*
  218          *  first operation will recalibrate
  219          */
  220         fl.confused = 1;
  221 
  222         floppysetup1(&fl);
  223 }
  224 
  225 static Chan*
  226 floppyattach(char *spec)
  227 {
  228         static int kstarted;
  229 
  230         if(fl.ndrive == 0)
  231                 error(Enodev);
  232 
  233         if(kstarted == 0){
  234                 /*
  235                  *  watchdog to turn off the motors
  236                  */
  237                 kstarted = 1;
  238                 kproc("floppy", floppykproc, 0);
  239         }
  240         return devattach('f', spec);
  241 }
  242 
  243 static Walkqid*
  244 floppywalk(Chan *c, Chan *nc, char **name, int nname)
  245 {
  246         return devwalk(c, nc, name, nname, floppydir, 1+fl.ndrive*NFDIR, devgen);
  247 }
  248 
  249 static int
  250 floppystat(Chan *c, uchar *dp, int n)
  251 {
  252         return devstat(c, dp, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
  253 }
  254 
  255 static Chan*
  256 floppyopen(Chan *c, int omode)
  257 {
  258         return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen);
  259 }
  260 
  261 static void
  262 floppyclose(Chan *)
  263 {
  264 }
  265 
  266 static void
  267 islegal(ulong offset, long n, FDrive *dp)
  268 {
  269         if(offset % dp->t->bytes)
  270                 error(Ebadarg);
  271         if(n % dp->t->bytes)
  272                 error(Ebadarg);
  273 }
  274 
  275 /*
  276  *  check if the floppy has been replaced under foot.  cause
  277  *  an error if it has.
  278  *
  279  *  a seek and a read clears the condition.  this was determined
  280  *  experimentally, there has to be a better way.
  281  *
  282  *  if the read fails, cycle through the possible floppy
  283  *  density till one works or we've cycled through all
  284  *  possibilities for this drive.
  285  */
  286 static void
  287 changed(Chan *c, FDrive *dp)
  288 {
  289         ulong old;
  290         FType *start;
  291 
  292         /*
  293          *  if floppy has changed or first time through
  294          */
  295         if((inb(Pdir)&Fchange) || dp->vers == 0){
  296                 DPRINT("changed\n");
  297                 fldump();
  298                 dp->vers++;
  299                 start = dp->t;
  300                 dp->maxtries = 3;       /* limit it when we're probing */
  301 
  302                 /* floppyon will fail if there's a controller but no drive */
  303                 dp->confused = 1;       /* make floppyon recal */
  304                 if(floppyon(dp) < 0)
  305                         error(Eio);
  306 
  307                 /* seek to the first track */
  308                 floppyseek(dp, dp->t->heads*dp->t->tsize);
  309                 while(waserror()){
  310                         /*
  311                          *  if first attempt doesn't reset changed bit, there's
  312                          *  no floppy there
  313                          */
  314                         if(inb(Pdir)&Fchange)
  315                                 nexterror();
  316 
  317                         while(++dp->t){
  318                                 if(dp->t == &floppytype[nelem(floppytype)])
  319                                         dp->t = floppytype;
  320                                 if(dp->dt == dp->t->dt)
  321                                         break;
  322                         }
  323                         floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
  324 
  325                         /* floppyon will fail if there's a controller but no drive */
  326                         if(floppyon(dp) < 0)
  327                                 error(Eio);
  328 
  329                         DPRINT("changed: trying %s\n", dp->t->name);
  330                         fldump();
  331                         if(dp->t == start)
  332                                 nexterror();
  333                 }
  334 
  335                 /* if the read succeeds, we've got the density right */
  336                 floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize);
  337                 poperror();
  338                 dp->maxtries = 20;
  339         }
  340 
  341         old = c->qid.vers;
  342         c->qid.vers = dp->vers;
  343         if(old && old != dp->vers)
  344                 error(Eio);
  345 }
  346 
  347 static int
  348 readtrack(FDrive *dp, int cyl, int head)
  349 {
  350         int i, nn, sofar;
  351         ulong pos;
  352 
  353         nn = dp->t->tsize;
  354         if(dp->ccyl==cyl && dp->chead==head)
  355                 return nn;
  356         pos = (cyl*dp->t->heads+head) * nn;
  357         for(sofar = 0; sofar < nn; sofar += i){
  358                 dp->ccyl = -1;
  359                 i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar);
  360                 if(i <= 0)
  361                         return -1;
  362         }
  363         dp->ccyl = cyl;
  364         dp->chead = head;
  365         return nn;
  366 }
  367 
  368 static long
  369 floppyread(Chan *c, void *a, long n, vlong off)
  370 {
  371         FDrive *dp;
  372         long rv;
  373         int sec, head, cyl;
  374         long len;
  375         uchar *aa;
  376         ulong offset = off;
  377 
  378         if(c->qid.type & QTDIR)
  379                 return devdirread(c, a, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
  380 
  381         rv = 0;
  382         dp = &fl.d[c->qid.path & ~Qmask];
  383         switch ((int)(c->qid.path & Qmask)) {
  384         case Qdata:
  385                 islegal(offset, n, dp);
  386                 aa = a;
  387 
  388                 qlock(&fl);
  389                 if(waserror()){
  390                         qunlock(&fl);
  391                         nexterror();
  392                 }
  393                 floppyon(dp);
  394                 changed(c, dp);
  395                 for(rv = 0; rv < n; rv += len){
  396                         /*
  397                          *  all xfers come out of the track cache
  398                          */
  399                         dp->len = n - rv;
  400                         floppypos(dp, offset+rv);
  401                         cyl = dp->tcyl;
  402                         head = dp->thead;
  403                         len = dp->len;
  404                         sec = dp->tsec;
  405                         if(readtrack(dp, cyl, head) < 0)
  406                                 break;
  407                         memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len);
  408                 }
  409                 qunlock(&fl);
  410                 poperror();
  411 
  412                 break;
  413         case Qctl:
  414                 return readstr(offset, a, n, dp->t->name);
  415         default:
  416                 panic("floppyread: bad qid");
  417         }
  418 
  419         return rv;
  420 }
  421 
  422 static long
  423 floppywrite(Chan *c, void *a, long n, vlong off)
  424 {
  425         FDrive *dp;
  426         long rv, i;
  427         char *aa = a;
  428         Cmdbuf *cb;
  429         Cmdtab *ct;
  430         ulong offset = off;
  431 
  432         rv = 0;
  433         dp = &fl.d[c->qid.path & ~Qmask];
  434         switch ((int)(c->qid.path & Qmask)) {
  435         case Qdata:
  436                 islegal(offset, n, dp);
  437                 qlock(&fl);
  438                 if(waserror()){
  439                         qunlock(&fl);
  440                         nexterror();
  441                 }
  442                 floppyon(dp);
  443                 changed(c, dp);
  444                 for(rv = 0; rv < n; rv += i){
  445                         floppypos(dp, offset+rv);
  446                         if(dp->tcyl == dp->ccyl)
  447                                 dp->ccyl = -1;
  448                         i = floppyxfer(dp, Fwrite, aa+rv, offset+rv, n-rv);
  449                         if(i < 0)
  450                                 break;
  451                         if(i == 0)
  452                                 error(Eio);
  453                 }
  454                 qunlock(&fl);
  455                 poperror();
  456                 break;
  457         case Qctl:
  458                 rv = n;
  459                 cb = parsecmd(a, n);
  460                 if(waserror()){
  461                         free(cb);
  462                         nexterror();
  463                 }
  464                 qlock(&fl);
  465                 if(waserror()){
  466                         qunlock(&fl);
  467                         nexterror();
  468                 }
  469                 ct = lookupcmd(cb, floppyctlmsg, nelem(floppyctlmsg));
  470                 switch(ct->index){
  471                 case CMeject:
  472                         floppyeject(dp);
  473                         break;
  474                 case CMformat:
  475                         floppyformat(dp, cb);
  476                         break;
  477                 case CMreset:
  478                         fl.confused = 1;
  479                         floppyon(dp);
  480                         break;
  481                 case CMdebug:
  482                         floppydebug = 1;
  483                         break;
  484                 case CMnodebug:
  485                         floppydebug = 0;
  486                         break;
  487                 }
  488                 poperror();
  489                 qunlock(&fl);
  490                 poperror();
  491                 free(cb);
  492                 break;
  493         default:
  494                 panic("floppywrite: bad qid");
  495         }
  496 
  497         return rv;
  498 }
  499 
  500 static void
  501 floppykproc(void *)
  502 {
  503         FDrive *dp;
  504 
  505         while(waserror())
  506                 ;
  507         for(;;){
  508                 for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
  509                         if((fl.motor&MOTORBIT(dp->dev))
  510                         && TK2SEC(m->ticks - dp->lasttouched) > 5
  511                         && canqlock(&fl)){
  512                                 if(TK2SEC(m->ticks - dp->lasttouched) > 5)
  513                                         floppyoff(dp);
  514                                 qunlock(&fl);
  515                         }
  516                 }
  517                 tsleep(&up->sleep, return0, 0, 1000);
  518         }
  519 }
  520 
  521 /*
  522  *  start a floppy drive's motor.
  523  */
  524 static int
  525 floppyon(FDrive *dp)
  526 {
  527         int alreadyon;
  528         int tries;
  529 
  530         if(fl.confused)
  531                 floppyrevive();
  532 
  533         /* start motor and select drive */
  534         alreadyon = fl.motor & MOTORBIT(dp->dev);
  535         fl.motor |= MOTORBIT(dp->dev);
  536         outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
  537         if(!alreadyon){
  538                 /* wait for drive to spin up */
  539                 tsleep(&up->sleep, return0, 0, 750);
  540 
  541                 /* clear any pending interrupts */
  542                 floppysense();
  543         }
  544 
  545         /* set transfer rate */
  546         if(fl.rate != dp->t->rate){
  547                 fl.rate = dp->t->rate;
  548                 outb(Pdsr, fl.rate);
  549         }
  550 
  551         /* get drive to a known cylinder */
  552         if(dp->confused)
  553                 for(tries = 0; tries < 4; tries++)
  554                         if(floppyrecal(dp) >= 0)
  555                                 break;
  556         dp->lasttouched = m->ticks;
  557         fl.selected = dp;
  558 
  559         /* return -1 if this didn't work */
  560         if(dp->confused)
  561                 return -1;
  562         return 0;
  563 }
  564 
  565 /*
  566  *  stop the floppy if it hasn't been used in 5 seconds
  567  */
  568 static void
  569 floppyoff(FDrive *dp)
  570 {
  571         fl.motor &= ~MOTORBIT(dp->dev);
  572         outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
  573 }
  574 
  575 /*
  576  *  send a command to the floppy
  577  */
  578 static int
  579 floppycmd(void)
  580 {
  581         int i;
  582         int tries;
  583 
  584         fl.nstat = 0;
  585         for(i = 0; i < fl.ncmd; i++){
  586                 for(tries = 0; ; tries++){
  587                         if((inb(Pmsr)&(Ffrom|Fready)) == Fready)
  588                                 break;
  589                         if(tries > 1000){
  590                                 DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i);
  591                                 fldump();
  592 
  593                                 /* empty fifo, might have been a bad command */
  594                                 floppyresult();
  595                                 return -1;
  596                         }
  597                         microdelay(8);  /* for machine independence */
  598                 }
  599                 outb(Pfdata, fl.cmd[i]);
  600         }
  601         return 0;
  602 }
  603 
  604 /*
  605  *  get a command result from the floppy
  606  *
  607  *  when the controller goes ready waiting for a command
  608  *  (instead of sending results), we're done
  609  * 
  610  */
  611 static int
  612 floppyresult(void)
  613 {
  614         int i, s;
  615         int tries;
  616 
  617         /* get the result of the operation */
  618         for(i = 0; i < sizeof(fl.stat); i++){
  619                 /* wait for status byte */
  620                 for(tries = 0; ; tries++){
  621                         s = inb(Pmsr)&(Ffrom|Fready);
  622                         if(s == Fready){
  623                                 fl.nstat = i;
  624                                 return fl.nstat;
  625                         }
  626                         if(s == (Ffrom|Fready))
  627                                 break;
  628                         if(tries > 1000){
  629                                 DPRINT("floppyresult: %d stats\n", i);
  630                                 fldump();
  631                                 fl.confused = 1;
  632                                 return -1;
  633                         }
  634                         microdelay(8);  /* for machine independence */
  635                 }
  636                 fl.stat[i] = inb(Pfdata);
  637         }
  638         fl.nstat = sizeof(fl.stat);
  639         return fl.nstat;
  640 }
  641 
  642 /*
  643  *  calculate physical address of a logical byte offset into the disk
  644  *
  645  *  truncate dp->length if it crosses a track boundary
  646  */
  647 static void
  648 floppypos(FDrive *dp, long off)
  649 {
  650         int lsec;
  651         int ltrack;
  652         int end;
  653 
  654         lsec = off/dp->t->bytes;
  655         ltrack = lsec/dp->t->sectors;
  656         dp->tcyl = ltrack/dp->t->heads;
  657         dp->tsec = (lsec % dp->t->sectors) + 1;
  658         dp->thead = (lsec/dp->t->sectors) % dp->t->heads;
  659 
  660         /*
  661          *  can't read across track boundaries.
  662          *  if so, decrement the bytes to be read.
  663          */
  664         end = (ltrack+1)*dp->t->sectors*dp->t->bytes;
  665         if(off+dp->len > end)
  666                 dp->len = end - off;
  667 }
  668 
  669 /*
  670  *  get the interrupt cause from the floppy.
  671  */
  672 static int
  673 floppysense(void)
  674 {
  675         fl.ncmd = 0;
  676         fl.cmd[fl.ncmd++] = Fsense;
  677         if(floppycmd() < 0)
  678                 return -1;
  679         if(floppyresult() < 2){
  680                 DPRINT("can't read sense response\n");
  681                 fldump();
  682                 fl.confused = 1;
  683                 return -1;
  684         }
  685         return 0;
  686 }
  687 
  688 static int
  689 cmddone(void *)
  690 {
  691         return fl.ncmd == 0;
  692 }
  693 
  694 /*
  695  *  Wait for a floppy interrupt.  If none occurs in 5 seconds, we
  696  *  may have missed one.  This only happens on some portables which
  697  *  do power management behind our backs.  Call the interrupt
  698  *  routine to try to clear any conditions.
  699  */
  700 static void
  701 floppywait(int slow)
  702 {
  703         tsleep(&fl.r, cmddone, 0, slow ? 5000 : 1000);
  704         if(!cmddone(0)){
  705                 floppyintr(0);
  706                 fl.confused = 1;
  707         }
  708 }
  709 
  710 /*
  711  *  we've lost the floppy position, go to cylinder 0.
  712  */
  713 static int
  714 floppyrecal(FDrive *dp)
  715 {
  716         dp->ccyl = -1;
  717         dp->cyl = -1;
  718 
  719         fl.ncmd = 0;
  720         fl.cmd[fl.ncmd++] = Frecal;
  721         fl.cmd[fl.ncmd++] = dp->dev;
  722         if(floppycmd() < 0)
  723                 return -1;
  724         floppywait(1);
  725         if(fl.nstat < 2){
  726                 DPRINT("recalibrate: confused %ux\n", inb(Pmsr));
  727                 fl.confused = 1;
  728                 return -1;
  729         }
  730         if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
  731                 DPRINT("recalibrate: failed\n");
  732                 dp->confused = 1;
  733                 return -1;
  734         }
  735         dp->cyl = fl.stat[1];
  736         if(dp->cyl != 0){
  737                 DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl);
  738                 dp->cyl = -1;
  739                 dp->confused = 1;
  740                 return -1;
  741         }
  742 
  743         dp->confused = 0;
  744         return 0;
  745 }
  746 
  747 /*
  748  *  if the controller or a specific drive is in a confused state,
  749  *  reset it and get back to a known state
  750  */
  751 static void
  752 floppyrevive(void)
  753 {
  754         FDrive *dp;
  755 
  756         /*
  757          *  reset the controller if it's confused
  758          */
  759         if(fl.confused){
  760                 DPRINT("floppyrevive in\n");
  761                 fldump();
  762 
  763                 /* reset controller and turn all motors off */
  764                 splhi();
  765                 fl.ncmd = 1;
  766                 fl.cmd[0] = 0;
  767                 outb(Pdor, 0);
  768                 delay(10);
  769                 outb(Pdor, Fintena|Fena);
  770                 delay(10);
  771                 spllo();
  772                 fl.motor = 0;
  773                 fl.confused = 0;
  774                 floppywait(0);
  775 
  776                 /* mark all drives in an unknown state */
  777                 for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++)
  778                         dp->confused = 1;
  779 
  780                 /* set rate to a known value */
  781                 outb(Pdsr, 0);
  782                 fl.rate = 0;
  783 
  784                 DPRINT("floppyrevive out\n");
  785                 fldump();
  786         }
  787 }
  788 
  789 /*
  790  *  seek to the target cylinder
  791  *
  792  *      interrupt, no results
  793  */
  794 static long
  795 floppyseek(FDrive *dp, long off)
  796 {
  797         floppypos(dp, off);
  798         if(dp->cyl == dp->tcyl)
  799                 return dp->tcyl;
  800         dp->cyl = -1;
  801 
  802         fl.ncmd = 0;
  803         fl.cmd[fl.ncmd++] = Fseek;
  804         fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
  805         fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps;
  806         if(floppycmd() < 0)
  807                 return -1;
  808         floppywait(1);
  809         if(fl.nstat < 2){
  810                 DPRINT("seek: confused\n");
  811                 fl.confused = 1;
  812                 return -1;
  813         }
  814         if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
  815                 DPRINT("seek: failed\n");
  816                 dp->confused = 1;
  817                 return -1;
  818         }
  819 
  820         dp->cyl = dp->tcyl;
  821         return dp->tcyl;
  822 }
  823 
  824 /*
  825  *  read or write to floppy.  try up to three times.
  826  */
  827 static long
  828 floppyxfer(FDrive *dp, int cmd, void *a, long off, long n)
  829 {
  830         long offset;
  831         int tries;
  832 
  833         if(off >= dp->t->cap)
  834                 return 0;
  835         if(off + n > dp->t->cap)
  836                 n = dp->t->cap - off;
  837 
  838         /* retry on error (until it gets ridiculous) */
  839         tries = 0;
  840         while(waserror()){
  841                 if(tries++ >= dp->maxtries)
  842                         nexterror();
  843                 DPRINT("floppyxfer: retrying\n");
  844         }
  845 
  846         dp->len = n;
  847         if(floppyseek(dp, off) < 0){
  848                 DPRINT("xfer: seek failed\n");
  849                 dp->confused = 1;
  850                 error(Eio);
  851         }
  852 
  853         /*
  854          *  set up the dma (dp->len may be trimmed)
  855          */
  856         if(waserror()){
  857                 dmaend(DMAchan);
  858                 nexterror();
  859         }
  860         dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread);
  861         if(dp->len < 0)
  862                 error(Eio);
  863 
  864         /*
  865          *  start operation
  866          */
  867         fl.ncmd = 0;
  868         fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0);
  869         fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
  870         fl.cmd[fl.ncmd++] = dp->tcyl;
  871         fl.cmd[fl.ncmd++] = dp->thead;
  872         fl.cmd[fl.ncmd++] = dp->tsec;
  873         fl.cmd[fl.ncmd++] = dp->t->bcode;
  874         fl.cmd[fl.ncmd++] = dp->t->sectors;
  875         fl.cmd[fl.ncmd++] = dp->t->gpl;
  876         fl.cmd[fl.ncmd++] = 0xFF;
  877         if(floppycmd() < 0)
  878                 error(Eio);
  879 
  880         /* Poll ready bits and transfer data */
  881         floppyexec((char*)a, dp->len, cmd==Fread);
  882 
  883         /*
  884          *  give bus to DMA, floppyintr() will read result
  885          */
  886         floppywait(0);
  887         dmaend(DMAchan);
  888         poperror();
  889 
  890         /*
  891          *  check for errors
  892          */
  893         if(fl.nstat < 7){
  894                 DPRINT("xfer: confused\n");
  895                 fl.confused = 1;
  896                 error(Eio);
  897         }
  898         if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){
  899                 DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0],
  900                         fl.stat[1], fl.stat[2]);
  901                 DPRINT("offset %lud len %ld\n", off, dp->len);
  902                 if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){
  903                         DPRINT("DMA overrun: retry\n");
  904                 } else
  905                         dp->confused = 1;
  906                 error(Eio);
  907         }
  908 
  909         /*
  910          *  check for correct cylinder
  911          */
  912         offset = fl.stat[3] * dp->t->heads + fl.stat[4];
  913         offset = offset*dp->t->sectors + fl.stat[5] - 1;
  914         offset = offset * c2b[fl.stat[6]];
  915         if(offset != off+dp->len){
  916                 DPRINT("xfer: ends on wrong cyl\n");
  917                 dp->confused = 1;
  918                 error(Eio);
  919         }
  920         poperror();
  921 
  922         dp->lasttouched = m->ticks;
  923         return dp->len;
  924 }
  925 
  926 /*
  927  *  format a track
  928  */
  929 static void
  930 floppyformat(FDrive *dp, Cmdbuf *cb)
  931 {
  932         int cyl, h, sec;
  933         ulong track;
  934         uchar *buf, *bp;
  935         FType *t;
  936 
  937         /*
  938          *  set the type
  939          */
  940         if(cb->nf == 2){
  941                 for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
  942                         if(strcmp(cb->f[1], t->name)==0 && t->dt==dp->dt){
  943                                 dp->t = t;
  944                                 floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
  945                                 break;
  946                         }
  947                 }
  948                 if(t >= &floppytype[nelem(floppytype)])
  949                         error(Ebadarg);
  950         } else if(cb->nf == 1){
  951                 floppysetdef(dp);
  952                 t = dp->t;
  953         } else {
  954                 cmderror(cb, "invalid floppy format command");
  955                 SET(t);
  956         }
  957 
  958         /*
  959          *  buffer for per track info
  960          */
  961         buf = smalloc(t->sectors*4);
  962         if(waserror()){
  963                 free(buf);
  964                 nexterror();
  965         }
  966 
  967         /* force a recalibrate to cylinder 0 */
  968         dp->confused = 1;
  969         if(!waserror()){
  970                 floppyon(dp);
  971                 poperror();
  972         }
  973 
  974         /*
  975          *  format a track at time
  976          */
  977         for(track = 0; track < t->tracks*t->heads; track++){
  978                 cyl = track/t->heads;
  979                 h = track % t->heads;
  980 
  981                 /*
  982                  *  seek to track, ignore errors
  983                  */
  984                 floppyseek(dp, track*t->tsize);
  985                 dp->cyl = cyl;
  986                 dp->confused = 0;
  987 
  988                 /*
  989                  *  set up the dma (dp->len may be trimmed)
  990                  */
  991                 bp = buf;
  992                 for(sec = 1; sec <= t->sectors; sec++){
  993                         *bp++ = cyl;
  994                         *bp++ = h;
  995                         *bp++ = sec;
  996                         *bp++ = t->bcode;
  997                 }
  998                 if(waserror()){
  999                         dmaend(DMAchan);
 1000                         nexterror();
 1001                 }
 1002                 if(dmasetup(DMAchan, buf, bp-buf, 0) < 0)
 1003                         error(Eio);
 1004 
 1005                 /*
 1006                  *  start operation
 1007                  */
 1008                 fl.ncmd = 0;
 1009                 fl.cmd[fl.ncmd++] = Fformat;
 1010                 fl.cmd[fl.ncmd++] = (h<<2) | dp->dev;
 1011                 fl.cmd[fl.ncmd++] = t->bcode;
 1012                 fl.cmd[fl.ncmd++] = t->sectors;
 1013                 fl.cmd[fl.ncmd++] = t->fgpl;
 1014                 fl.cmd[fl.ncmd++] = 0x5a;
 1015                 if(floppycmd() < 0)
 1016                         error(Eio);
 1017 
 1018                 /* Poll ready bits and transfer data */
 1019                 floppyexec((char *)buf, bp-buf, 0);
 1020 
 1021                 /*
 1022                  *  give bus to DMA, floppyintr() will read result
 1023                  */
 1024                 floppywait(1);
 1025                 dmaend(DMAchan);
 1026                 poperror();
 1027 
 1028                 /*
 1029                  *  check for errors
 1030                  */
 1031                 if(fl.nstat < 7){
 1032                         DPRINT("format: confused\n");
 1033                         fl.confused = 1;
 1034                         error(Eio);
 1035                 }
 1036                 if((fl.stat[0]&Codemask)!=0 || fl.stat[1]|| fl.stat[2]){
 1037                         DPRINT("format: failed %ux %ux %ux\n",
 1038                                 fl.stat[0], fl.stat[1], fl.stat[2]);
 1039                         dp->confused = 1;
 1040                         error(Eio);
 1041                 }
 1042         }
 1043         free(buf);
 1044         dp->confused = 1;
 1045         poperror();
 1046 }
 1047 
 1048 static void
 1049 floppyintr(Ureg *)
 1050 {
 1051         switch(fl.cmd[0]&~Fmulti){
 1052         case Fread:
 1053         case Fwrite:
 1054         case Fformat:
 1055         case Fdumpreg: 
 1056                 floppyresult();
 1057                 break;
 1058         case Fseek:
 1059         case Frecal:
 1060         default:
 1061                 floppysense();  /* to clear interrupt */
 1062                 break;
 1063         }
 1064         fl.ncmd = 0;
 1065         wakeup(&fl.r);
 1066 }
 1067 
 1068 Dev floppydevtab = {
 1069         'f',
 1070         "floppy",
 1071 
 1072         floppyreset,
 1073         devinit,
 1074         devshutdown,
 1075         floppyattach,
 1076         floppywalk,
 1077         floppystat,
 1078         floppyopen,
 1079         devcreate,
 1080         floppyclose,
 1081         floppyread,
 1082         devbread,
 1083         floppywrite,
 1084         devbwrite,
 1085         devremove,
 1086         devwstat,
 1087 };

Cache object: 1f0d6bc68f1f43c9a8d8c5f509fb7264


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.