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/i386/isa/ft.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 /*
    2  *  Copyright (c) 1993, 1994 Steve Gerakines
    3  *
    4  *  This is freely redistributable software.  You may do anything you
    5  *  wish with it, so long as the above notice stays intact.
    6  *
    7  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
    8  *  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    9  *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   10  *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
   11  *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   12  *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   13  *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   14  *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
   15  *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
   16  *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   17  *  POSSIBILITY OF SUCH DAMAGE.
   18  *
   19  *  ft.c - QIC-40/80 floppy tape driver
   20  * $FreeBSD: src/sys/i386/isa/ft.c,v 1.27.2.4 1999/09/05 08:12:38 peter Exp $
   21  *
   22  *  01/19/95 ++sg
   23  *  Cleaned up recalibrate/seek code at attach time for FreeBSD 2.x.
   24  *
   25  *  06/07/94 v0.9 ++sg
   26  *  Tape stuck on segment problem should be gone.  Re-wrote buffering
   27  *  scheme.  Added support for drives that do not automatically perform
   28  *  seek load point.  Can handle more wakeup types now and should correctly
   29  *  report most manufacturer names.  Fixed places where unit 0 was being
   30  *  sent to the fdc instead of the actual unit number.  Added ioctl support
   31  *  for an in-core badmap.
   32  *
   33  *  01/26/94 v0.3b - Jim Babb
   34  *  Got rid of the hard coded device selection.  Moved (some of) the
   35  *  static variables into a structure for support of multiple devices.
   36  *  ( still has a way to go for 2 controllers - but closer )
   37  *  Changed the interface with fd.c so we no longer 'steal' it's
   38  *  driver routine vectors.
   39  *
   40  *  10/30/93 v0.3
   41  *  Fixed a couple more bugs.  Reading was sometimes looping when an
   42  *  an error such as address-mark-missing was encountered.  Both
   43  *  reading and writing was having more backup-and-retries than was
   44  *  necessary.  Added support to get hardware info.  Updated for use
   45  *  with FreeBSD.
   46  *
   47  *  09/15/93 v0.2 pl01
   48  *  Fixed a bunch of bugs:  extra isa_dmadone() in async_write() (shouldn't
   49  *  matter), fixed double buffering in async_req(), changed tape_end() in
   50  *  set_fdcmode() to reduce unexpected interrupts, changed end of track
   51  *  processing in async_req(), protected more of ftreq_rw() with an
   52  *  splbio().  Changed some of the ftreq_*() functions so that they wait
   53  *  for inactivity and then go, instead of aborting immediately.
   54  *
   55  *  08/07/93 v0.2 release
   56  *  Shifted from ftstrat to ioctl support for I/O.  Streaming is now much
   57  *  more reliable.  Added internal support for error correction, QIC-40,
   58  *  and variable length tapes.  Random access of segments greatly
   59  *  improved.  Formatting and verification support is close but still
   60  *  incomplete.
   61  *
   62  *  06/03/93 v0.1 Alpha release
   63  *  Hopefully the last re-write.  Many bugs fixed, many remain.
   64  */
   65 
   66 #include "ft.h"
   67 #if NFT > 0
   68 #include "fd.h"
   69 
   70 #include <sys/param.h>
   71 #include <sys/systm.h>
   72 #include <sys/conf.h>
   73 #include <sys/disklabel.h>      /* temp. for dkunit() in fdc.h */
   74 #include <sys/file.h>
   75 #include <sys/proc.h>
   76 #include <sys/ioctl.h>
   77 #include <sys/malloc.h>
   78 #include <sys/buf.h>
   79 #include <sys/uio.h>
   80 #include <sys/ftape.h>
   81 
   82 #include <machine/clock.h>
   83 
   84 #include <i386/isa/isa_device.h>
   85 #ifdef PC98
   86 #include <pc98/pc98/fdreg.h>
   87 #else
   88 #include <i386/isa/fdreg.h>
   89 #include <i386/isa/rtc.h>
   90 #endif
   91 #include <i386/isa/fdc.h>
   92 #include <i386/isa/ftreg.h>
   93 
   94 extern int ftintr __P((ftu_t ftu));
   95 
   96 /* Enable or disable debugging messages. */
   97 #define FTDBGALL 0                      /* 1 if you want everything */
   98 /*#define DPRT(a) printf a              */
   99 #define DPRT(a)
  100 
  101 /* Constants private to the driver */
  102 #define FTPRI           (PRIBIO)        /* sleep priority */
  103 #define FTNBUFF         9               /* 8 for buffering, 1 for header */
  104 
  105 /* The following items are needed from the fd driver. */
  106 extern int in_fdc(int);                 /* read fdc registers */
  107 extern int out_fdc(int, int);           /* write fdc registers */
  108 
  109 extern int hz;                          /* system clock rate */
  110 
  111 /* Flags in isadev struct */
  112 #define FT_PROBE        0x1             /* allow for "dangerous" tape probes */
  113 
  114 /* Type of tape attached */
  115 /* use numbers that don't interfere with the possible floppy types */
  116 #define NO_TYPE 0               /* (same as NO_TYPE in fd.c) */
  117 
  118 /* F_TAPE_TYPE must match value in fd.c */
  119 #define F_TAPE_TYPE     0x020           /* bit for ft->types to indicate tape */
  120 #define FT_NONE         (F_TAPE_TYPE | 0)       /* no method required */
  121 #define FT_MOUNTAIN     (F_TAPE_TYPE | 1)       /* mountain */
  122 #define FT_COLORADO     (F_TAPE_TYPE | 2)       /* colorado */
  123 #define FT_INSIGHT      (F_TAPE_TYPE | 3)       /* insight */
  124 
  125 /* Mode FDC is currently in: tape or disk */
  126 enum { FDC_TAPE_MODE, FDC_DISK_MODE };
  127 
  128 /* Command we are awaiting completion of */
  129 enum { FTCMD_NONE, FTCMD_RESET, FTCMD_RECAL, FTCMD_SEEK, FTCMD_READID };
  130 
  131 /* Tape interrupt status of current request */
  132 enum { FTSTS_NONE, FTSTS_SNOOZE, FTSTS_INTERRUPT, FTSTS_TIMEOUT };
  133 
  134 /* Tape I/O status */
  135 enum {
  136         FTIO_READY,             /* No I/O activity */
  137         FTIO_READING,           /* Currently reading blocks */
  138         FTIO_RDAHEAD,           /* Currently reading ahead */
  139         FTIO_WRITING            /* Buffers are being written */
  140 };
  141 
  142 /* Current tape mode */
  143 enum {
  144         FTM_PRIMARY,            /* Primary mode */
  145         FTM_VERIFY,             /* Verify mode */
  146         FTM_FORMAT,             /* Format mode */
  147         FTM_DIAG1,              /* Diagnostic mode 1 */
  148         FTM_DIAG2               /* Diagnostic mode 2 */
  149 };
  150 
  151 /* Tape geometries table */
  152 static QIC_Geom ftgtbl[] = {
  153         { 0, 0, "Unformatted", "Unknown", 0,  0,     0,   0,     0 }, /* XXX */
  154         { 1, 1, "QIC-40",  "205/550",   20,  68,  2176, 128, 21760 },
  155         { 1, 2, "QIC-40",  "307.5/550", 20, 102,  3264, 128, 32640 },
  156         { 1, 3, "QIC-40",  "295/900",    0,   0,     0,   0,     0 }, /* ??? */
  157         { 1, 4, "QIC-40",  "1100/550",  20, 365, 11680, 128, 32512 },
  158         { 1, 5, "QIC-40",  "1100/900",   0,   0,     0,   0,     0 }, /* ??? */
  159         { 2, 1, "QIC-80",  "205/550",   28, 100,  3200, 128, 19200 },
  160         { 2, 2, "QIC-80",  "307.5/550", 28, 150,  4800, 128, 19200 },
  161         { 2, 3, "QIC-80",  "295/900",    0,   0,     0,   0,     0 }, /* ??? */
  162         { 2, 4, "QIC-80",  "1100/550",  28, 537, 17184, 128, 32512 },
  163         { 2, 5, "QIC-80",  "1100/900",   0,   0,     0,   0,     0 }, /* ??? */
  164         { 3, 1, "QIC-500", "205/550",    0,   0,     0,   0,     0 }, /* ??? */
  165         { 3, 2, "QIC-500", "307.5/550",  0,   0,     0,   0,     0 }, /* ??? */
  166         { 3, 3, "QIC-500", "295/900",    0,   0,     0,   0,     0 }, /* ??? */
  167         { 3, 4, "QIC-500", "1100/550",   0,   0,     0,   0,     0 }, /* ??? */
  168         { 3, 5, "QIC-500", "1100/900",   0,   0,     0,   0,     0 }  /* ??? */
  169 };
  170 #define NGEOM   (sizeof(ftgtbl) / sizeof(QIC_Geom))
  171 
  172 static QIC_Geom *ftg = NULL;                    /* Current tape's geometry */
  173 
  174 /*
  175  *  things relating to asynchronous commands
  176  */
  177 static int awr_state;           /* state of async write */
  178 static int ard_state;           /* state of async read */
  179 static int arq_state;           /* state of async request */
  180 static int async_retries;       /* retries, one per invocation */
  181 static int async_func;          /* function to perform */
  182 static int async_state;         /* state current function is at */
  183 static int async_arg0;          /* up to 3 arguments for async cmds */
  184 static int async_arg1;          /**/
  185 static int async_arg2;          /**/
  186 static int async_ret;           /* return value */
  187 static struct _astk {
  188         int over_func;
  189         int over_state;
  190         int over_retries;
  191         int over_arg0;
  192         int over_arg1;
  193         int over_arg2;
  194 } astk[10];
  195 static struct _astk *astk_ptr = &astk[0]; /* Pointer to stack position */
  196 
  197 /* List of valid async (interrupt driven) tape support functions. */
  198 enum {
  199         ACMD_NONE,              /* no command */
  200         ACMD_SEEK,              /* command seek */
  201         ACMD_STATUS,            /* report status */
  202         ACMD_STATE,             /* wait for state bits to be true */
  203         ACMD_SEEKSTS,           /* perform command and wait for status */
  204         ACMD_READID,            /* read id */
  205         ACMD_RUNBLK             /* ready tape for I/O on the given block */
  206 };
  207 
  208 /* Call another asyncronous command from within async_cmd(). */
  209 #define CALL_ACMD(r,f,a,b,c) \
  210                         astk_ptr->over_retries = async_retries; \
  211                         astk_ptr->over_func = async_func; \
  212                         astk_ptr->over_state = (r); \
  213                         astk_ptr->over_arg0 = async_arg0; \
  214                         astk_ptr->over_arg1 = async_arg1; \
  215                         astk_ptr->over_arg2 = async_arg2; \
  216                         async_func = (f); async_state = 0; async_retries = 0; \
  217                         async_arg0=(a); async_arg1=(b); async_arg2=(c); \
  218                         astk_ptr++; \
  219                         goto restate
  220 
  221 /* Perform an asyncronous command from outside async_cmd(). */
  222 #define ACMD_FUNC(r,f,a,b,c) over_async = (r); astk_ptr = &astk[0]; \
  223                         async_func = (f); async_state = 0; async_retries = 0; \
  224                         async_arg0=(a); async_arg1=(b); async_arg2=(c); \
  225                         async_cmd(ftu); \
  226                         return
  227 
  228 /* Various wait channels */
  229 static char *wc_buff_avail      = "bavail";
  230 static char *wc_buff_done       = "bdone";
  231 static char *wc_iosts_change    = "iochg";
  232 static char *wc_long_delay      = "ldelay";
  233 static char *wc_intr_wait       = "intrw";
  234 #define ftsleep(wc,to)  tsleep((caddr_t)(wc),FTPRI,(wc),(to))
  235 
  236 /***********************************************************************\
  237 * Per controller structure.                                             *
  238 \***********************************************************************/
  239 extern struct fdc_data fdc_data[NFDC];
  240 
  241 /***********************************************************************\
  242 * Per tape drive structure.                                             *
  243 \***********************************************************************/
  244 static struct ft_data {
  245         struct  fdc_data *fdc;  /* pointer to controller structure */
  246         int     ftsu;           /* this units number on this controller */
  247         int     type;           /* Drive type (Mountain, Colorado) */
  248 /*      QIC_Geom *ftg;  */      /* pointer to Current tape's geometry */
  249         int     flags;
  250         int     cmd_wait;       /* Command we are awaiting completion of */
  251         int     sts_wait;       /* Tape interrupt status of current request */
  252         int     io_sts;         /* Tape I/O status */
  253         int     mode;
  254         int     pcn;            /* present cylinder number */
  255         int     attaching;      /* true when ft is attaching */
  256         unsigned char *xptr;    /* pointer to buffer blk to xfer  */
  257         int     xcnt;           /* transfer count                 */
  258         int     xblk;           /* block number to transfer       */
  259         int     xseg;           /* segment being transferred      */
  260         SegReq *segh;           /* Current I/O request            */
  261         SegReq *segt;           /* Tail of queued I/O requests    */
  262         SegReq *doneh;          /* Completed I/O request queue    */
  263         SegReq *donet;          /* Completed I/O request tail     */
  264         SegReq *segfree;        /* Free segments                  */
  265         SegReq *hdr;            /* Current tape header            */
  266         int nsegq;              /* Segments on request queue      */
  267         int ndoneq;             /* Segments on completed queue    */
  268         int nfreelist;          /* Segments on free list          */
  269 
  270                                 /* the next 3 should be defines in 'flags' */
  271         int active;             /* TRUE if transfer is active     */
  272         int rdonly;             /* TRUE if tape is read-only      */
  273         int newcart;            /* TRUE if new cartridge detected */
  274         int laststs;            /* last reported status code      */
  275         int lastcfg;            /* last reported QIC config       */
  276         int lasterr;            /* last QIC error code            */
  277         int lastpos;            /* last known segment number      */
  278         int moving;             /* TRUE if tape is moving         */
  279         int rid[7];             /* read_id return values          */
  280 
  281 } *ft_data[NFT];
  282 
  283 /***********************************************************************\
  284 * Throughout this file the following conventions will be used:          *
  285 * ft is a pointer to the ft_data struct for the drive in question       *
  286 * fdc is a pointer to the fdc_data struct for the controller            *
  287 * ftu is the tape drive unit number                                     *
  288 * fdcu is the floppy controller unit number                             *
  289 * ftsu is the tape drive unit number on that controller. (sub-unit)     *
  290 \***********************************************************************/
  291 
  292 
  293 
  294 #define id_physid id_scsiid     /* this biotab field doubles as a field */
  295                                 /* for the physical unit number on the controller */
  296 
  297 int ftopen(dev_t, int);
  298 int ftclose(dev_t, int);
  299 int ftioctl(dev_t, int, caddr_t, int, struct proc *);
  300 int ftattach(struct isa_device *, struct isa_device *, int);
  301 static timeout_t ft_timeout;
  302 static void async_cmd(ftu_t);
  303 static void async_req(ftu_t, int);
  304 static void async_read(ftu_t, int);
  305 static void async_write(ftu_t, int);
  306 static void tape_start(ftu_t, int);
  307 static void tape_end(ftu_t);
  308 static void tape_inactive(ftu_t);
  309 static int tape_cmd(ftu_t, int);
  310 static int tape_status(ftu_t);
  311 static int qic_status(ftu_t, int, int);
  312 static int ftreq_rewind(ftu_t);
  313 static int ftreq_hwinfo(ftu_t, QIC_HWInfo *);
  314 
  315 /*****************************************************************************/
  316 
  317 
  318 /*
  319  *  Allocate a segment I/O buffer from the free list.
  320  */
  321 static SegReq *
  322 segio_alloc(ft_p ft)
  323 {
  324   SegReq *r;
  325 
  326   /* Grab first item from free list */
  327   if ((r = ft->segfree) != NULL) {
  328         ft->segfree = ft->segfree->next;
  329         ft->nfreelist--;
  330   }
  331   DPRT(("segio_alloc: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
  332   return(r);
  333 }
  334 
  335 
  336 /*
  337  *  Queue a segment I/O request.
  338  */
  339 static void
  340 segio_queue(ft_p ft, SegReq *sp)
  341 {
  342   /* Put request on in process queue. */
  343   if (ft->segt == NULL)
  344         ft->segh = sp;
  345   else
  346         ft->segt->next = sp;
  347   sp->next = NULL;
  348   ft->segt = sp;
  349   ft->nsegq++;
  350   DPRT(("segio_queue: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
  351 }
  352 
  353 
  354 /*
  355  *  Segment I/O completed, place on correct queue.
  356  */
  357 static void
  358 segio_done(ft_p ft, SegReq *sp)
  359 {
  360   /* First remove from current I/O queue */
  361   ft->segh = sp->next;
  362   if (ft->segh == NULL) ft->segt = NULL;
  363   ft->nsegq--;
  364 
  365   if (sp->reqtype == FTIO_WRITING) {
  366         /* Place on free list */
  367         sp->next = ft->segfree;
  368         ft->segfree = sp;
  369         ft->nfreelist++;
  370         wakeup((caddr_t)wc_buff_avail);
  371         DPRT(("segio_done: (w) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
  372   } else {
  373         /* Put on completed I/O queue */
  374         if (ft->donet == NULL)
  375                 ft->doneh = sp;
  376         else
  377                 ft->donet->next = sp;
  378         sp->next = NULL;
  379         ft->donet = sp;
  380         ft->ndoneq++;
  381         wakeup((caddr_t)wc_buff_done);
  382         DPRT(("segio_done: (r) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
  383   }
  384 }
  385 
  386 
  387 /*
  388  *  Take I/O request from finished queue to free queue.
  389  */
  390 static void
  391 segio_free(ft_p ft, SegReq *sp)
  392 {
  393   /* First remove from done queue */
  394   ft->doneh = sp->next;
  395   if (ft->doneh == NULL) ft->donet = NULL;
  396   ft->ndoneq--;
  397 
  398   /* Place on free list */
  399   sp->next = ft->segfree;
  400   ft->segfree = sp;
  401   ft->nfreelist++;
  402   wakeup((caddr_t)wc_buff_avail);
  403   DPRT(("segio_free: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
  404 }
  405 
  406 /*
  407  *  Probe/attach floppy tapes.
  408  */
  409 int
  410 ftattach(isadev, fdup, unithasfd)
  411         struct isa_device *isadev, *fdup;
  412         int unithasfd;
  413 {
  414   fdcu_t fdcu = isadev->id_unit;                /* fdc active unit */
  415   fdc_p fdc = fdc_data + fdcu;  /* pointer to controller structure */
  416   ftu_t ftu = fdup->id_unit;
  417   ft_p ft;
  418   ftsu_t ftsu = fdup->id_physid;
  419   QIC_HWInfo hw;
  420   char *manu;
  421 
  422   if (ftu >= NFT) return 0;
  423   ft = ft_data[ftu] = malloc(sizeof *ft, M_DEVBUF, M_NOWAIT);
  424   bzero(ft, sizeof *ft);
  425 
  426   /* Probe for tape */
  427   ft->attaching = 1;
  428   ft->type = NO_TYPE;
  429   ft->fdc = fdc;
  430   ft->ftsu = ftsu;
  431 
  432   /*
  433    *  FT_NONE - no method, just do it
  434    */
  435   tape_start(ftu, 0);
  436   if (tape_status(ftu) >= 0) {
  437         ft->type = FT_NONE;
  438         ftreq_hwinfo(ftu, &hw);
  439         goto out;
  440   }
  441 
  442   /*
  443    *  FT_COLORADO - colorado style
  444    */
  445   tape_start(ftu, 0);
  446   tape_cmd(ftu, QC_COL_ENABLE1);
  447   tape_cmd(ftu, QC_COL_ENABLE2 + ftu);
  448   if (tape_status(ftu) >= 0) {
  449         ft->type = FT_COLORADO;
  450         ftreq_hwinfo(ftu, &hw);
  451         tape_cmd(ftu, QC_COL_DISABLE);
  452         goto out;
  453   }
  454 
  455   /*
  456    *  FT_MOUNTAIN - mountain style
  457    */
  458   tape_start(ftu, 0);
  459   tape_cmd(ftu, QC_MTN_ENABLE1);
  460   tape_cmd(ftu, QC_MTN_ENABLE2);
  461   if (tape_status(ftu) >= 0) {
  462         ft->type = FT_MOUNTAIN;
  463         ftreq_hwinfo(ftu, &hw);
  464         tape_cmd(ftu, QC_MTN_DISABLE);
  465         goto out;
  466   }
  467 
  468   if(isadev->id_flags & FT_PROBE) {
  469     /*
  470      * Insight probe is dangerous, since it requires the motor being
  471      * enabled and therefore risks attached floppy disk drives to jam.
  472      * Probe only if explicitly requested by a flag 0x1 from config
  473      */
  474 
  475     /*
  476      *  FT_INSIGHT - insight style
  477      *
  478      *  Since insight requires turning the drive motor on, we will not
  479      *  perform this probe if a floppy drive was already found with the
  480      *  the given unit and controller.
  481      */
  482     if (unithasfd) goto out;
  483     tape_start(ftu, 1);
  484     if (tape_status(ftu) >= 0) {
  485         ft->type = FT_INSIGHT;
  486         ftreq_hwinfo(ftu, &hw);
  487         goto out;
  488     }
  489   }
  490 
  491 out:
  492   tape_end(ftu);
  493   if (ft->type != NO_TYPE) {
  494         fdc->flags |= FDC_HASFTAPE;
  495         switch(hw.hw_make) {
  496             case 0x0000:
  497                 if (ft->type == FT_COLORADO) {
  498                         manu = "Colorado";
  499                 } else if (ft->type == FT_INSIGHT) {
  500                         manu = "Insight";
  501                 } else if (ft->type == FT_MOUNTAIN && hw.hw_model == 0x05) {
  502                         manu = "Archive";
  503                 } else if (ft->type == FT_MOUNTAIN) {
  504                         manu = "Mountain";
  505                 } else {
  506                         manu = "Unknown";
  507                 }
  508                 break;
  509             case 0x0001:
  510                 manu = "Colorado";
  511                 break;
  512             case 0x0005:
  513                 if (hw.hw_model >= 0x09) {
  514                         manu = "Conner";
  515                 } else {
  516                         manu = "Archive";
  517                 }
  518                 break;
  519             case 0x0006:
  520                 manu = "Mountain";
  521                 break;
  522             case 0x0007:
  523                 manu = "Wangtek";
  524                 break;
  525             case 0x0222:
  526                 manu = "IOMega";
  527                 break;
  528             default:
  529                 manu = "Unknown";
  530                 break;
  531         }
  532         printf("ft%d: %s tape\n", fdup->id_unit, manu);
  533   }
  534   ft->attaching = 0;
  535   return(ft->type);
  536 }
  537 
  538 
  539 /*
  540  *  Perform common commands asynchronously.
  541  */
  542 static void
  543 async_cmd(ftu_t ftu) {
  544         ft_p    ft = ft_data[ftu];
  545         fdcu_t  fdcu = ft->fdc->fdcu;
  546         int cmd, i, st0, st3, pcn;
  547         static int bitn, retval, retpos, nbits, newcn;
  548         static int wanttrk, wantblk, wantdir;
  549         static int curtrk, curblk, curdir, curdiff;
  550         static int errcnt = 0;
  551 
  552 restate:
  553 #if FTDBGALL
  554   DPRT(("async_cmd state: func: %d  state: %d\n", async_func, async_state));
  555 #endif
  556   switch(async_func) {
  557      case ACMD_SEEK:
  558         /*
  559          *  Arguments:
  560          *     0 - command to perform
  561          */
  562         switch (async_state) {
  563             case 0:
  564                 cmd = async_arg0;
  565 #if FTDBGALL
  566                 DPRT(("===>async_seek cmd = %d\n", cmd));
  567 #endif
  568                 newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd;
  569                 async_state = 1;
  570                 i = 0;
  571                 if (out_fdc(fdcu, NE7CMD_SEEK) < 0) i = 1;
  572                 if (!i && out_fdc(fdcu, ftu) < 0) i = 1;
  573                 if (!i && out_fdc(fdcu, newcn) < 0) i = 1;
  574                 if (i) {
  575                         if (++async_retries >= 10) {
  576                                 DPRT(("ft%d: async_cmd command seek failed!!\n", ftu));
  577                                 goto complete;
  578                         }
  579                         DPRT(("ft%d: async_cmd command seek retry...\n",ftu));
  580                         async_state = 0;
  581                         goto restate;
  582                 }
  583                 break;
  584             case 1:
  585                 out_fdc(fdcu, NE7CMD_SENSEI);
  586                 st0 = in_fdc(fdcu);
  587                 pcn = in_fdc(fdcu);
  588                 if (st0 < 0 || pcn < 0 || newcn != pcn) {
  589                         if (++async_retries >= 10) {
  590                                 DPRT(("ft%d: async_cmd seek retries exceeded\n",ftu));
  591                                 goto complete;
  592                         }
  593                         DPRT(("ft%d: async_cmd command bad st0=$%02x pcn=$%02x\n",
  594                                                         ftu, st0, pcn));
  595                         async_state = 0;
  596                         timeout(ft_timeout, (caddr_t)ftu, hz/10);
  597                         break;
  598                 }
  599                 if (st0 & 0x20) {       /* seek done */
  600                         ft->pcn = pcn;
  601                 }
  602 #if FTDBGALL
  603                  else
  604                         DPRT(("ft%d: async_seek error st0 = $%02x pcn = %d\n",
  605                                                         ftu, st0, pcn));
  606 #endif
  607                 if (async_arg1) goto complete;
  608                 async_state = 2;
  609                 timeout(ft_timeout, (caddr_t)ftu, hz/50);
  610                 break;
  611             case 2:
  612                 goto complete;
  613                 /* NOTREACHED */
  614         }
  615         break;
  616 
  617      case ACMD_STATUS:
  618         /*
  619          *  Arguments:
  620          *     0 - command to issue report from
  621          *     1 - number of bits
  622          *  modifies: bitn, retval, st3
  623          */
  624         switch (async_state) {
  625             case 0:
  626                 bitn = 0;
  627                 retval = 0;
  628                 cmd = async_arg0;
  629                 nbits = async_arg1;
  630                 DPRT(("async_status got cmd = %d nbits = %d\n", cmd,nbits));
  631                 CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0);
  632                 /* NOTREACHED */
  633             case 1:
  634                 out_fdc(fdcu, NE7CMD_SENSED);
  635                 out_fdc(fdcu, ftu);
  636                 st3 = in_fdc(fdcu);
  637                 if (st3 < 0) {
  638                 DPRT(("ft%d: async_status timed out on bit %d r=$%02x\n",
  639                                         ftu,bitn,retval));
  640                         async_ret = -1;
  641                         goto complete;
  642                 }
  643                 if ((st3 & 0x10) != 0) retval |= (1 << bitn);
  644                 bitn++;
  645                 if (bitn >= (nbits+2)) {
  646                         if ((retval & 1) && (retval & (1 << (nbits+1)))) {
  647                                 async_ret = (retval & ~(1<<(nbits+1))) >> 1;
  648                                 if (async_arg0 == QC_STATUS && async_arg2 == 0 &&
  649                                    (async_ret & (QS_ERROR|QS_NEWCART))) {
  650                                         async_state = 2;
  651                                         goto restate;
  652                                 }
  653                         DPRT(("async status got $%04x ($%04x)\n", async_ret,retval));
  654                         } else {
  655                                 DPRT(("ft%d: async_status failed: retval=$%04x nbits=%d\n",
  656                                                 ftu, retval,nbits));
  657                                 async_ret = -2;
  658                         }
  659                         goto complete;
  660                 }
  661                 CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0);
  662                 /* NOTREACHED */
  663             case 2:
  664                 if (async_ret & QS_NEWCART) ft->newcart = 1;
  665                 CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1);
  666             case 3:
  667                 ft->lasterr = async_ret;
  668                 if ((ft->lasterr & QS_NEWCART) == 0 && ft->lasterr) {
  669                         DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
  670                                 ftu, ft->lasterr & 0xff, ft->lasterr >> 8));
  671                 }
  672                 cmd = async_arg0;
  673                 nbits = async_arg1;
  674                 CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1);
  675             case 4:
  676                 goto complete;
  677             case 5:
  678                 CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0);
  679             case 6:
  680                 CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0);
  681             case 7:
  682                 CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0);
  683             case 8:
  684                 cmd = async_arg0;
  685                 CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0);
  686         }
  687         break;
  688 
  689      case ACMD_STATE:
  690         /*
  691          *  Arguments:
  692          *     0 - status bits to check
  693          */
  694         switch(async_state) {
  695             case 0:
  696                 CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0);
  697             case 1:
  698                 if ((async_ret & async_arg0) != 0) goto complete;
  699                 async_state = 0;
  700                 if (++async_retries == 360) {   /* 90 secs. */
  701                         DPRT(("ft%d: acmd_state exceeded retry count\n", ftu));
  702                         goto complete;
  703                 }
  704                 timeout(ft_timeout, (caddr_t)ftu, hz/4);
  705                 break;
  706         }
  707         break;
  708 
  709      case ACMD_SEEKSTS:
  710         /*
  711          *  Arguments:
  712          *     0 - command to perform
  713          *     1 - status bits to check
  714          *     2 - (optional) seconds to wait until completion
  715          */
  716         switch(async_state) {
  717             case 0:
  718                 cmd = async_arg0;
  719                 async_retries = (async_arg2) ? (async_arg2 * 4) : 10;
  720                 CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0);
  721             case 1:
  722                 CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0);
  723             case 2:
  724                 if ((async_ret & async_arg1) != 0) goto complete;
  725                 if (--async_retries == 0) {
  726                         DPRT(("ft%d: acmd_seeksts retries exceeded\n", ftu));
  727                         goto complete;
  728                 }
  729                 async_state = 1;
  730                 timeout(ft_timeout, (caddr_t)ftu, hz/4);
  731                 break;
  732         }
  733         break;
  734 
  735      case ACMD_READID:
  736         /*
  737          *  Arguments: (none)
  738          */
  739         switch(async_state) {
  740             case 0:
  741                 if (!ft->moving) {
  742                         CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
  743                         /* NOTREACHED */
  744                 }
  745                 async_state = 1;
  746                 out_fdc(fdcu, 0x4a);            /* READ_ID */
  747                 out_fdc(fdcu, ftu);
  748                 break;
  749             case 1:
  750                 for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
  751                 async_ret = (ft->rid[3]*ftg->g_fdtrk) +
  752                             (ft->rid[4]*ftg->g_fdside) + ft->rid[5] - 1;
  753                 DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n",
  754                         ft->rid[0], ft->rid[1], ft->rid[2], ft->rid[3],
  755                         ft->rid[4], ft->rid[5], async_ret));
  756                 if ((ft->rid[0] & 0xc0) != 0 || async_ret < 0) {
  757                         /*
  758                          *  Method for retry:
  759                          *    errcnt == 1 regular retry
  760                          *              2 microstep head 1
  761                          *              3 microstep head 2
  762                          *              4 microstep head back to 0
  763                          *              5 fail
  764                          */
  765                         if (++errcnt >= 5) {
  766                                 DPRT(("ft%d: acmd_readid errcnt exceeded\n", fdcu));
  767                                 async_ret = -2;
  768                                 errcnt = 0;
  769                                 goto complete;
  770                         }
  771                         if (errcnt == 1) {
  772                                 ft->moving = 0;
  773                                 CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
  774                         } else {
  775                                 ft->moving = 0;
  776                                 CALL_ACMD(4, ACMD_SEEKSTS, QC_STPAUSE, QS_READY, 0);
  777                         }
  778                         DPRT(("readid retry %d...\n", errcnt));
  779                         async_state = 0;
  780                         goto restate;
  781                 }
  782                 if ((async_ret % ftg->g_blktrk) == (ftg->g_blktrk-1)) {
  783                         DPRT(("acmd_readid detected last block on track\n"));
  784                         retpos = async_ret;
  785                         CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0);
  786                         /* NOTREACHED */
  787                 }
  788                 ft->lastpos = async_ret;
  789                 errcnt = 0;
  790                 goto complete;
  791                 /* NOTREACHED */
  792             case 2:
  793                 CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0);
  794             case 3:
  795                 ft->moving = 0;
  796                 async_ret = retpos+1;
  797                 goto complete;
  798             case 4:
  799                 CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0);
  800             case 5:
  801                 ft->moving = 1;
  802                 async_state = 0;
  803                 timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
  804                 break;
  805         }
  806         break;
  807 
  808      case ACMD_RUNBLK:
  809         /*
  810          *  Arguments:
  811          *     0 - block number I/O will be performed on
  812          *
  813          *  modifies: curpos
  814          */
  815         switch (async_state) {
  816             case 0:
  817                 wanttrk = async_arg0 / ftg->g_blktrk;
  818                 wantblk = async_arg0 % ftg->g_blktrk;
  819                 wantdir = wanttrk & 1;
  820                 ft->moving = 0;
  821                 CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
  822             case 1:
  823                 curtrk = wanttrk;
  824                 curdir = curtrk & 1;
  825                 DPRT(("Changing to track %d\n", wanttrk));
  826                 CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0);
  827             case 2:
  828                 cmd = wanttrk+2;
  829                 CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0);
  830             case 3:
  831                 CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0);
  832             case 4:
  833                 ft->laststs = async_ret;
  834                 if (wantblk == 0) {
  835                         curblk = 0;
  836                         cmd = (wantdir) ? QC_SEEKEND : QC_SEEKSTART;
  837                         CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90);
  838                 }
  839                 if (ft->laststs & QS_BOT) {
  840                         DPRT(("Tape is at BOT\n"));
  841                         curblk = (wantdir) ? 4800 : 0;
  842                         async_state = 6;
  843                         goto restate;
  844                 }
  845                 if (ft->laststs & QS_EOT) {
  846                         DPRT(("Tape is at EOT\n"));
  847                         curblk = (wantdir) ? 0 : 4800;
  848                         async_state = 6;
  849                         goto restate;
  850                 }
  851                 CALL_ACMD(5, ACMD_READID, 0, 0, 0);
  852             case 5:
  853                 if (async_ret < 0) {
  854                         ft->moving = 0;
  855                         ft->lastpos = -2;
  856                         if (async_ret == -2) {
  857                                 CALL_ACMD(9, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
  858                         }
  859                         CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
  860                 }
  861                 curtrk = (async_ret+1) / ftg->g_blktrk;
  862                 curblk = (async_ret+1) % ftg->g_blktrk;
  863                 DPRT(("gotid: curtrk=%d wanttrk=%d curblk=%d wantblk=%d\n",
  864                         curtrk, wanttrk, curblk, wantblk));
  865                 if (curtrk != wanttrk) {        /* oops! */
  866                         DPRT(("oops!! wrong track!\n"));
  867                         CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
  868                 }
  869                 async_state = 6;
  870                 goto restate;
  871             case 6:
  872                 DPRT(("curtrk = %d nextblk = %d\n", curtrk, curblk));
  873                 if (curblk == wantblk) {
  874                         ft->lastpos = curblk - 1;
  875                         async_ret = ft->lastpos;
  876                         if (ft->moving) goto complete;
  877                         CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0);
  878                 }
  879                 if (curblk > wantblk) {         /* passed it */
  880                         ft->moving = 0;
  881                         CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
  882                 }
  883                 if ((wantblk - curblk) <= 256) {        /* approaching it */
  884                         CALL_ACMD(5, ACMD_READID, 0, 0, 0);
  885                 }
  886                 /* way up ahead */
  887                 ft->moving = 0;
  888                 CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
  889                 break;
  890             case 7:
  891                 ft->moving = 1;
  892                 CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0);
  893                 break;
  894             case 8:
  895                 async_state = 9;
  896                 timeout(ft_timeout, (caddr_t)ftu, hz/10);  /* XXX */
  897                 break;
  898             case 9:
  899                 goto complete;
  900             case 10:
  901                 curdiff = ((curblk - wantblk) / QCV_BLKSEG) + 2;
  902                 if (curdiff >= ftg->g_segtrk) curdiff = ftg->g_segtrk - 1;
  903                 DPRT(("pos %d past %d, reverse %d\n", curblk, wantblk, curdiff));
  904                 CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0);
  905             case 11:
  906                 DPRT(("reverse 1 done\n"));
  907                 CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0);
  908             case 12:
  909                 DPRT(("reverse 2 done\n"));
  910                 CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90);
  911             case 13:
  912                 CALL_ACMD(5, ACMD_READID, 0, 0, 0);
  913             case 14:
  914                 curdiff = ((wantblk - curblk) / QCV_BLKSEG) - 2;
  915                 if (curdiff < 0) curdiff = 0;
  916                 DPRT(("pos %d before %d, forward %d\n", curblk, wantblk, curdiff));
  917                 CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0);
  918             case 15:
  919                 DPRT(("forward 1 done\n"));
  920                 CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0);
  921             case 16:
  922                 DPRT(("forward 2 done\n"));
  923                 CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90);
  924         }
  925         break;
  926   }
  927 
  928   return;
  929 
  930 complete:
  931   if (astk_ptr != &astk[0]) {
  932         astk_ptr--;
  933         async_retries = astk_ptr->over_retries;
  934         async_func = astk_ptr->over_func;
  935         async_state = astk_ptr->over_state;
  936         async_arg0 = astk_ptr->over_arg0;
  937         async_arg1 = astk_ptr->over_arg1;
  938         async_arg2 = astk_ptr->over_arg2;
  939         goto restate;
  940   }
  941   async_func = ACMD_NONE;
  942   async_state = 0;
  943   switch (ft->io_sts) {
  944      case FTIO_READY:
  945         async_req(ftu, 2);
  946         break;
  947      case FTIO_READING:
  948      case FTIO_RDAHEAD:
  949         async_read(ftu, 2);
  950         break;
  951      case FTIO_WRITING:
  952         async_write(ftu, 2);
  953         break;
  954      default:
  955         DPRT(("ft%d: bad async_cmd ending I/O state!\n", ftu));
  956         break;
  957   }
  958 }
  959 
  960 
  961 /*
  962  *  Entry point for the async request processor.
  963  */
  964 static void
  965 async_req(ftu_t ftu, int from)
  966 {
  967   ft_p  ft = ft_data[ftu];
  968   SegReq *sp;
  969   static int over_async, lastreq;
  970   int cmd;
  971 
  972   if (from == 2) arq_state = over_async;
  973 
  974 restate:
  975   switch (arq_state) {
  976      case 0:    /* Process segment */
  977         sp = ft->segh;
  978         ft->io_sts = (sp == NULL) ? FTIO_READY : sp->reqtype;
  979 
  980         if (ft->io_sts == FTIO_WRITING)
  981                 async_write(ftu, from);
  982         else
  983                 async_read(ftu, from);
  984         if (ft->io_sts != FTIO_READY) return;
  985 
  986         /* Pull buffer from current I/O queue */
  987         if (sp != NULL) {
  988                 lastreq = sp->reqtype;
  989                 segio_done(ft, sp);
  990 
  991                 /* If I/O cancelled, clear finished queue. */
  992                 if (sp->reqcan) {
  993                         while (ft->doneh != NULL)
  994                                 segio_free(ft, ft->doneh);
  995                         lastreq = FTIO_READY;
  996                 }
  997         } else
  998                 lastreq = FTIO_READY;
  999 
 1000         /* Detect end of track */
 1001         if (((ft->xblk / QCV_BLKSEG) % ftg->g_segtrk) == 0) {
 1002                 ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0);
 1003         }
 1004         arq_state = 1;
 1005         goto restate;
 1006 
 1007      case 1:    /* Next request */
 1008         /* If we have another request queued, start it running. */
 1009         if (ft->segh != NULL) {
 1010                 sp = ft->segh;
 1011                 sp->reqcrc = 0;
 1012                 arq_state = ard_state = awr_state = 0;
 1013                 ft->xblk = sp->reqblk;
 1014                 ft->xseg = sp->reqseg;
 1015                 ft->xcnt = 0;
 1016                 ft->xptr = sp->buff;
 1017                 DPRT(("I/O reqblk = %d\n", ft->xblk));
 1018                 goto restate;
 1019         }
 1020 
 1021         /* If the last request was reading, do read ahead. */
 1022         if ((lastreq == FTIO_READING || lastreq == FTIO_RDAHEAD) &&
 1023                                         (sp = segio_alloc(ft)) != NULL) {
 1024                 sp->reqtype = FTIO_RDAHEAD;
 1025                 sp->reqblk = ft->xblk;
 1026                 sp->reqseg = ft->xseg+1;
 1027                 sp->reqcrc = 0;
 1028                 sp->reqcan = 0;
 1029                 segio_queue(ft, sp);
 1030                 bzero(sp->buff, QCV_SEGSIZE);
 1031                 arq_state = ard_state = awr_state = 0;
 1032                 ft->xblk = sp->reqblk;
 1033                 ft->xseg = sp->reqseg;
 1034                 ft->xcnt = 0;
 1035                 ft->xptr = sp->buff;
 1036                 DPRT(("Processing readahead reqblk = %d\n", ft->xblk));
 1037                 goto restate;
 1038         }
 1039 
 1040         if (ft->moving) {
 1041                 DPRT(("No more I/O.. Stopping.\n"));
 1042                 ft->moving = 0;
 1043                 ACMD_FUNC(7, ACMD_SEEKSTS, QC_PAUSE, QS_READY, 0);
 1044                 break;
 1045         }
 1046         arq_state = 7;
 1047         goto restate;
 1048 
 1049      case 2:    /* End of track */
 1050         ft->moving = 0;
 1051         ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
 1052         break;
 1053 
 1054      case 3:
 1055         DPRT(("async_req seek head to track %d\n", ft->xblk / ftg->g_blktrk));
 1056         ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0);
 1057         break;
 1058 
 1059      case 4:
 1060         cmd = (ft->xblk / ftg->g_blktrk) + 2;
 1061         if (ft->segh != NULL) {
 1062                 ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0);
 1063         } else {
 1064                 ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0);
 1065         }
 1066         break;
 1067 
 1068      case 5:
 1069         ft->moving = 1;
 1070         ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0);
 1071         break;
 1072 
 1073      case 6:
 1074         arq_state = 1;
 1075         timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
 1076         break;
 1077 
 1078      case 7:
 1079         /* Time to rest. */
 1080         ft->active = 0;
 1081         ft->lastpos = -2;
 1082 
 1083         /* wakeup those who want an i/o chg */
 1084         wakeup((caddr_t)wc_iosts_change);
 1085         break;
 1086   }
 1087 }
 1088 
 1089 
 1090 /*
 1091  *  Entry for async read.
 1092  */
 1093 static void
 1094 async_read(ftu_t ftu, int from)
 1095 {
 1096   ft_p ft = ft_data[ftu];
 1097   fdcu_t fdcu = ft->fdc->fdcu;          /* fdc active unit */
 1098   int i, rddta[7];
 1099   int where;
 1100   static int over_async;
 1101   static int retries = 0;
 1102 
 1103   if (from == 2) ard_state = over_async;
 1104 
 1105 restate:
 1106 #if FTDBGALL
 1107   DPRT(("async_read: state: %d  from = %d\n", ard_state, from));
 1108 #endif
 1109   switch (ard_state) {
 1110      case 0:    /* Start off */
 1111         /* If tape is not at desired position, stop and locate */
 1112         if (ft->lastpos != (ft->xblk-1)) {
 1113                 DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
 1114                         ftu, ft->lastpos, ft->xblk));
 1115                 ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0);
 1116         }
 1117 
 1118         /* Tape is in position but stopped. */
 1119         if (!ft->moving) {
 1120                 DPRT(("async_read ******STARTING TAPE\n"));
 1121                 ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
 1122         }
 1123         ard_state = 1;
 1124         goto restate;
 1125 
 1126      case 1:    /* Start DMA */
 1127         /* Tape is now moving and in position-- start DMA now! */
 1128         isa_dmastart(B_READ, ft->xptr, QCV_BLKSIZE, 2);
 1129         out_fdc(fdcu, 0x66);                            /* read */
 1130         out_fdc(fdcu, ftu);                             /* unit */
 1131         out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk);       /* cylinder */
 1132         out_fdc(fdcu, ft->xblk / ftg->g_fdside);                /* head */
 1133         out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1);   /* sector */
 1134         out_fdc(fdcu, 0x03);                            /* 1K sectors */
 1135         out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1);   /* count */
 1136         out_fdc(fdcu, 0x74);                            /* gap length */
 1137         out_fdc(fdcu, 0xff);                            /* transfer size */
 1138         ard_state = 2;
 1139         break;
 1140 
 1141      case 2:    /* DMA completed */
 1142         /* Transfer complete, get status */
 1143         for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu);
 1144         isa_dmadone(B_READ, ft->xptr, QCV_BLKSIZE, 2);
 1145 
 1146 #if FTDBGALL
 1147         /* Compute where the controller thinks we are */
 1148         where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside)
 1149                         + rddta[5]-1;
 1150         DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
 1151             rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
 1152             where, ft->xblk));
 1153 #endif
 1154 
 1155         /* Check for errors */
 1156         if ((rddta[0] & 0xc0) != 0x00) {
 1157 #if !FTDBGALL
 1158                 where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside)
 1159                         + rddta[5]-1;
 1160                 DPRT(("xd: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
 1161                     rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
 1162                     where, ft->xblk));
 1163 #endif
 1164                 if ((rddta[1] & 0x04) == 0x04 && retries < 2) {
 1165                         /* Probably wrong position */
 1166                         DPRT(("async_read: doing retry %d\n", retries));
 1167                         ft->lastpos = ft->xblk;
 1168                         ard_state = 0;
 1169                         retries++;
 1170                         goto restate;
 1171                 } else {
 1172                         /* CRC/Address-mark/Data-mark, et. al. */
 1173                         DPRT(("ft%d: CRC error on block %d\n", fdcu, ft->xblk));
 1174                         ft->segh->reqcrc |= (1 << ft->xcnt);
 1175                 }
 1176         }
 1177 
 1178         /* Otherwise, transfer completed okay. */
 1179         retries = 0;
 1180         ft->lastpos = ft->xblk;
 1181         ft->xblk++;
 1182         ft->xcnt++;
 1183         ft->xptr += QCV_BLKSIZE;
 1184         if (ft->xcnt < QCV_BLKSEG && ft->segh->reqcan == 0) {
 1185                 ard_state = 0;
 1186                 goto restate;
 1187         }
 1188         DPRT(("Read done..  Cancel = %d\n", ft->segh->reqcan));
 1189         ft->io_sts = FTIO_READY;
 1190         break;
 1191 
 1192      case 3:
 1193         ft->moving = 1;
 1194         ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0);
 1195         break;
 1196 
 1197      case 4:
 1198         ard_state = 1;
 1199         timeout(ft_timeout, (caddr_t)ftu, hz/10);  /* XXX */
 1200         break;
 1201 
 1202      default:
 1203         DPRT(("ft%d: bad async_read state %d!!\n", ftu, ard_state));
 1204         break;
 1205   }
 1206 }
 1207 
 1208 
 1209 /*
 1210  *  Entry for async write.  If from is 0, this came from the interrupt
 1211  *  routine, if it's 1 then it was a timeout, if it's 2, then an
 1212  *  async_cmd completed.
 1213  */
 1214 static void
 1215 async_write(ftu_t ftu, int from)
 1216 {
 1217   ft_p ft = ft_data[ftu];
 1218   fdcu_t fdcu = ft->fdc->fdcu;          /* fdc active unit */
 1219   int i, rddta[7];
 1220   int where;
 1221   static int over_async;
 1222   static int retries = 0;
 1223 
 1224   if (from == 2) awr_state = over_async;
 1225 
 1226 restate:
 1227 #if FTDBGALL
 1228   DPRT(("async_write: state: %d  from = %d\n", awr_state, from));
 1229 #endif
 1230   switch (awr_state) {
 1231      case 0:    /* Start off */
 1232         /* If tape is not at desired position, stop and locate */
 1233         if (ft->lastpos != (ft->xblk-1)) {
 1234                 DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
 1235                         ftu, ft->lastpos, ft->xblk));
 1236                 ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0);
 1237         }
 1238 
 1239         /* Tape is in position but stopped. */
 1240         if (!ft->moving) {
 1241                 DPRT(("async_write ******STARTING TAPE\n"));
 1242                 ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
 1243         }
 1244         awr_state = 1;
 1245         goto restate;
 1246 
 1247      case 1:    /* Start DMA */
 1248         /* Tape is now moving and in position-- start DMA now! */
 1249         isa_dmastart(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
 1250         out_fdc(fdcu, 0x45);                            /* write */
 1251         out_fdc(fdcu, ftu);                             /* unit */
 1252         out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cyl */
 1253         out_fdc(fdcu, ft->xblk / ftg->g_fdside);                /* head */
 1254         out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1);   /* sector */
 1255         out_fdc(fdcu, 0x03);                            /* 1K sectors */
 1256         out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1);   /* count */
 1257         out_fdc(fdcu, 0x74);                            /* gap length */
 1258         out_fdc(fdcu, 0xff);                            /* transfer size */
 1259         awr_state = 2;
 1260         break;
 1261 
 1262      case 2:    /* DMA completed */
 1263         /* Transfer complete, get status */
 1264         for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu);
 1265         isa_dmadone(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
 1266 
 1267 #if FTDBGALL
 1268         /* Compute where the controller thinks we are */
 1269         where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1;
 1270         DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
 1271             rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
 1272             where, ft->xblk));
 1273 #endif
 1274 
 1275         /* Check for errors */
 1276         if ((rddta[0] & 0xc0) != 0x00) {
 1277 #if !FTDBGALL
 1278                 where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside)
 1279                          + rddta[5]-1;
 1280                 DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
 1281                     rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
 1282                     where, ft->xblk));
 1283 #endif
 1284                 if (retries < 3) {
 1285                         /* Something happened -- try again */
 1286                         DPRT(("async_write: doing retry %d\n", retries));
 1287                         ft->lastpos = ft->xblk;
 1288                         awr_state = 0;
 1289                         retries++;
 1290                         goto restate;
 1291                 } else {
 1292                         /*
 1293                          *  Retries failed.  Note the unrecoverable error.
 1294                          *  Marking the block as bad is useless right now.
 1295                          */
 1296                         printf("ft%d: unrecoverable write error on block %d\n",
 1297                                         ftu, ft->xblk);
 1298                         ft->segh->reqcrc |= (1 << ft->xcnt);
 1299                 }
 1300         }
 1301 
 1302         /* Otherwise, transfer completed okay. */
 1303         retries = 0;
 1304         ft->lastpos = ft->xblk;
 1305         ft->xblk++;
 1306         ft->xcnt++;
 1307         ft->xptr += QCV_BLKSIZE;
 1308         if (ft->xcnt < QCV_BLKSEG) {
 1309                 awr_state = 0;  /* next block */
 1310                 goto restate;
 1311         }
 1312 #if FTDBGALL
 1313         DPRT(("Write done.\n"));
 1314 #endif
 1315         ft->io_sts = FTIO_READY;
 1316         break;
 1317 
 1318      case 3:
 1319         ft->moving = 1;
 1320         ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0);
 1321         break;
 1322 
 1323      case 4:
 1324         awr_state = 1;
 1325         timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
 1326         break;
 1327 
 1328      default:
 1329         DPRT(("ft%d: bad async_write state %d!!\n", ftu, awr_state));
 1330         break;
 1331   }
 1332 }
 1333 
 1334 
 1335 /*
 1336  *  Interrupt handler for active tape.  Bounced off of fdintr().
 1337  */
 1338 int
 1339 ftintr(ftu_t ftu)
 1340 {
 1341   int st0, pcn, i;
 1342   ft_p  ft = ft_data[ftu];
 1343   fdcu_t fdcu = ft->fdc->fdcu;          /* fdc active unit */
 1344   int s = splbio();
 1345 
 1346   st0 = 0;
 1347   pcn = 0;
 1348 
 1349   /* I/O segment transfer completed */
 1350   if (ft->active) {
 1351         if (async_func != ACMD_NONE) {
 1352                 async_cmd(ftu);
 1353                 splx(s);
 1354                 return(1);
 1355         }
 1356 #if FTDBGALL
 1357         DPRT(("Got request interrupt\n"));
 1358 #endif
 1359         async_req(ftu, 0);
 1360         splx(s);
 1361         return(1);
 1362   }
 1363 
 1364   /* Get interrupt status */
 1365   if (ft->cmd_wait != FTCMD_READID) {
 1366         out_fdc(fdcu, NE7CMD_SENSEI);
 1367         st0 = in_fdc(fdcu);
 1368         pcn = in_fdc(fdcu);
 1369   }
 1370 
 1371   if (ft->cmd_wait == FTCMD_NONE || ft->sts_wait != FTSTS_SNOOZE) {
 1372 huh_what:
 1373         printf("ft%d: unexpected interrupt; st0 = $%02x pcn = %d\n",
 1374                                 ftu, st0, pcn);
 1375         splx(s);
 1376         return(1);
 1377   }
 1378 
 1379   switch (ft->cmd_wait) {
 1380      case FTCMD_RESET:
 1381         ft->sts_wait = FTSTS_INTERRUPT;
 1382         wakeup((caddr_t)wc_intr_wait);
 1383         break;
 1384      case FTCMD_RECAL:
 1385      case FTCMD_SEEK:
 1386         if (st0 & 0x20) {       /* seek done */
 1387                 ft->sts_wait = FTSTS_INTERRUPT;
 1388                 ft->pcn = pcn;
 1389                 wakeup((caddr_t)wc_intr_wait);
 1390         }
 1391 #if FTDBGALL
 1392         else
 1393                 DPRT(("ft%d: seek error st0 = $%02x pcn = %d\n",
 1394                                                 ftu, st0, pcn));
 1395 #endif
 1396         break;
 1397      case FTCMD_READID:
 1398         for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
 1399         ft->sts_wait = FTSTS_INTERRUPT;
 1400         wakeup((caddr_t)wc_intr_wait);
 1401         break;
 1402 
 1403      default:
 1404         goto huh_what;
 1405   }
 1406 
 1407   splx(s);
 1408   return(1);
 1409 }
 1410 
 1411 
 1412 /*
 1413  *  Interrupt timeout routine.
 1414  */
 1415 static void
 1416 ft_timeout(void *arg1)
 1417 {
 1418   int s;
 1419   ftu_t ftu = (ftu_t)arg1;
 1420   ft_p  ft = ft_data[ftu];
 1421 
 1422   s = splbio();
 1423   if (ft->active) {
 1424         if (async_func != ACMD_NONE) {
 1425                 async_cmd(ftu);
 1426                 splx(s);
 1427                 return;
 1428         }
 1429         async_req(ftu, 1);
 1430   } else {
 1431           ft->sts_wait = FTSTS_TIMEOUT;
 1432           wakeup((caddr_t)wc_intr_wait);
 1433   }
 1434   splx(s);
 1435 }
 1436 
 1437 
 1438 /*
 1439  *  Wait for a particular interrupt to occur.  ftintr() will wake us up
 1440  *  if it sees what we want.  Otherwise, time out and return error.
 1441  *  Should always disable ints before trigger is sent and calling here.
 1442  */
 1443 static int
 1444 ftintr_wait(ftu_t ftu, int cmd, int ticks)
 1445 {
 1446   int retries, st0, pcn;
 1447   ft_p  ft = ft_data[ftu];
 1448   fdcu_t fdcu = ft->fdc->fdcu;          /* fdc active unit */
 1449 
 1450   ft->cmd_wait = cmd;
 1451   ft->sts_wait = FTSTS_SNOOZE;
 1452 
 1453   /* At attach time, we can't rely on having interrupts serviced */
 1454   if (ft->attaching) {
 1455         switch (cmd) {
 1456             case FTCMD_RESET:
 1457                 DELAY(100);
 1458                 ft->sts_wait = FTSTS_INTERRUPT;
 1459                 goto intrdone;
 1460             case FTCMD_RECAL:
 1461             case FTCMD_SEEK:
 1462                 for (retries = 0; retries < 10000; retries++) {
 1463                         DELAY(150);
 1464                         out_fdc(fdcu, NE7CMD_SENSEI);
 1465                         st0 = in_fdc(fdcu);
 1466                         if ((st0 & 0xc0) == 0x80) continue;
 1467                         pcn = in_fdc(fdcu);
 1468                         if (st0 & 0x20) {
 1469                                 ft->sts_wait = FTSTS_INTERRUPT;
 1470                                 ft->pcn = pcn;
 1471                                 goto intrdone;
 1472                         }
 1473                 }
 1474                 break;
 1475         }
 1476         ft->sts_wait = FTSTS_TIMEOUT;
 1477         goto intrdone;
 1478   }
 1479 
 1480   ftsleep(wc_intr_wait, ticks);
 1481 
 1482 intrdone:
 1483   if (ft->sts_wait == FTSTS_TIMEOUT) {  /* timeout */
 1484 #if FTDBGALL
 1485         if (ft->cmd_wait != FTCMD_RESET)
 1486                 DPRT(("ft%d: timeout on command %d\n", ftu, ft->cmd_wait));
 1487 #endif
 1488         ft->cmd_wait = FTCMD_NONE;
 1489         ft->sts_wait = FTSTS_NONE;
 1490         return(1);
 1491   }
 1492 
 1493   /* got interrupt */
 1494   if (ft->attaching == 0 && ticks) untimeout(ft_timeout, (caddr_t)ftu);
 1495   ft->cmd_wait = FTCMD_NONE;
 1496   ft->sts_wait = FTSTS_NONE;
 1497   return(0);
 1498 }
 1499 
 1500 
 1501 /*
 1502  *  Recalibrate tape drive.  Parameter totape is true, if we should
 1503  *  recalibrate to tape drive settings.
 1504  */
 1505 static int
 1506 tape_recal(ftu_t ftu, int totape)
 1507 {
 1508   int s;
 1509   ft_p  ft = ft_data[ftu];
 1510   fdcu_t fdcu = ft->fdc->fdcu;          /* fdc active unit */
 1511 
 1512   DPRT(("tape_recal start\n"));
 1513 
 1514 #ifdef PC98
 1515   outb(0xbe, FDP_FDDEXC | FDP_PORTEXC);
 1516 #endif
 1517   out_fdc(fdcu, NE7CMD_SPECIFY);
 1518 #ifdef PC98
 1519   out_fdc(fdcu, (totape) ? 0xEF : 0xCF);
 1520   out_fdc(fdcu, 0x02);
 1521 #else
 1522   out_fdc(fdcu, (totape) ? 0xAD : 0xDF);
 1523   out_fdc(fdcu, 0x02);
 1524 #endif
 1525 
 1526   s = splbio();
 1527   out_fdc(fdcu, NE7CMD_RECAL);
 1528   out_fdc(fdcu, ftu);
 1529 
 1530   if (ftintr_wait(ftu, FTCMD_RECAL, hz)) {
 1531         splx(s);
 1532         DPRT(("ft%d: recalibrate timeout\n", ftu));
 1533         return(1);
 1534   }
 1535   splx(s);
 1536 
 1537   out_fdc(fdcu, NE7CMD_SPECIFY);
 1538 #ifdef PC98
 1539   out_fdc(fdcu, (totape) ? 0xEF : 0xCF);
 1540   out_fdc(fdcu, 0x02);
 1541 #else
 1542   out_fdc(fdcu, (totape) ? 0xFD : 0xDF);
 1543   out_fdc(fdcu, 0x02);
 1544 #endif
 1545 
 1546   DPRT(("tape_recal end\n"));
 1547   return(0);
 1548 }
 1549 
 1550 /*
 1551  *  Wait for a particular tape status to be met.  If all is TRUE, then
 1552  *  all states must be met, otherwise any state can be met.
 1553  */
 1554 static int
 1555 tape_state(ftu_t ftu, int all, int mask, int seconds)
 1556 {
 1557   int r, tries, maxtries;
 1558 
 1559   maxtries = (seconds) ? (4 * seconds) : 1;
 1560   for (tries = 0; tries < maxtries; tries++) {
 1561         r = tape_status(ftu);
 1562         if (r >= 0) {
 1563                 if (all && (r & mask) == mask) return(r);
 1564                 if ((r & mask) != 0) return(r);
 1565         }
 1566         if (seconds) ftsleep(wc_long_delay, hz/4);
 1567   }
 1568   DPRT(("ft%d: tape_state failed on mask=$%02x maxtries=%d\n",
 1569                                 ftu, mask, maxtries));
 1570   return(-1);
 1571 }
 1572 
 1573 
 1574 /*
 1575  *  Send a QIC command to tape drive, wait for completion.
 1576  */
 1577 static int
 1578 tape_cmd(ftu_t ftu, int cmd)
 1579 {
 1580   int newcn;
 1581   int retries = 0;
 1582   int s;
 1583   ft_p  ft = ft_data[ftu];
 1584   fdcu_t fdcu = ft->fdc->fdcu;          /* fdc active unit */
 1585 
 1586   DPRT(("===> tape_cmd: %d\n",cmd));
 1587   newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd;
 1588 
 1589 retry:
 1590 
 1591   /* Perform seek */
 1592   s = splbio();
 1593   out_fdc(fdcu, NE7CMD_SEEK);
 1594   out_fdc(fdcu, ftu);
 1595   out_fdc(fdcu, newcn);
 1596 
 1597   if (ftintr_wait(ftu, FTCMD_SEEK, hz)) {
 1598         DPRT(("ft%d: tape_cmd seek timeout\n", ftu));
 1599 redo:
 1600         splx(s);
 1601         if (++retries < 5) goto retry;
 1602         DPRT(("ft%d: tape_cmd seek failed!\n", ftu));
 1603         return(1);
 1604   }
 1605   splx(s);
 1606 
 1607   if (ft->pcn != newcn) {
 1608         DPRT(("ft%d: bad seek in tape_cmd; pcn = %d  newcn = %d\n",
 1609                         ftu, ft->pcn, newcn));
 1610         goto redo;
 1611   }
 1612   DELAY(2500);
 1613   return(0);
 1614 }
 1615 
 1616 
 1617 /*
 1618  *  Return status of tape drive
 1619  */
 1620 static int
 1621 tape_status(ftu_t ftu)
 1622 {
 1623   int r, err, tries;
 1624   ft_p ft = ft_data[ftu];
 1625   int max = (ft->attaching) ? 2 : 3;
 1626 
 1627   for (r = -1, tries = 0; r < 0 && tries < max; tries++)
 1628         r = qic_status(ftu, QC_STATUS, 8);
 1629   if (tries == max) return(-1);
 1630 
 1631 recheck:
 1632   DPRT(("tape_status got $%04x\n",r));
 1633   ft->laststs = r;
 1634 
 1635   if (r & (QS_ERROR|QS_NEWCART)) {
 1636         err = qic_status(ftu, QC_ERRCODE, 16);
 1637         ft->lasterr = err;
 1638         if (r & QS_NEWCART) {
 1639                 ft->newcart = 1;
 1640                 /* If tape not referenced, do a seek load point. */
 1641                 if ((r & QS_FMTOK) == 0 && !ft->attaching) {
 1642                         tape_cmd(ftu, QC_SEEKLP);
 1643                         do {
 1644                                 ftsleep(wc_long_delay, hz);
 1645                         } while ((r = qic_status(ftu, QC_STATUS, 8)) < 0 ||
 1646                                         (r & (QS_READY|QS_CART)) == QS_CART);
 1647                         goto recheck;
 1648                 }
 1649         } else if (err && !ft->attaching) {
 1650                 DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
 1651                                         ftu, err & 0xff, err >> 8));
 1652         }
 1653         r = qic_status(ftu, QC_STATUS, 8);
 1654         ft->laststs = r;
 1655         DPRT(("tape_status got error code $%04x new sts = $%02x\n",err,r));
 1656   }
 1657 
 1658   ft->rdonly = (r & QS_RDONLY);
 1659   return(r);
 1660 }
 1661 
 1662 
 1663 /*
 1664  *  Transfer control to tape drive.
 1665  */
 1666 static void
 1667 tape_start(ftu_t ftu, int motor)
 1668 {
 1669   ft_p  ft = ft_data[ftu];
 1670   fdc_p fdc = ft->fdc;
 1671   int s, mbits;
 1672 #ifndef PC98
 1673   static int mbmotor[] = { FDO_MOEN0, FDO_MOEN1, FDO_MOEN2, FDO_MOEN3 };
 1674 #endif
 1675 
 1676   s = splbio();
 1677   DPRT(("tape_start start\n"));
 1678 
 1679   /* reset, dma disable */
 1680 #ifdef PC98
 1681   outb(fdc->baseport+FDOUT, FDO_RST | FDO_FRY | FDO_AIE | FDO_MTON);
 1682 #else
 1683   outb(fdc->baseport+FDOUT, 0x00);
 1684 #endif
 1685   (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
 1686 
 1687   /* raise reset, enable DMA, motor on if needed */
 1688 #ifdef PC98
 1689   outb(fdc->baseport+FDOUT, FDO_DMAE | FDO_MTON);
 1690 #else
 1691   mbits = ftu & 3;
 1692   if (motor && ftu < 4)
 1693         mbits |= mbmotor[ftu];
 1694 
 1695   outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN | mbits);
 1696 #endif
 1697   (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
 1698 
 1699   splx(s);
 1700 
 1701   tape_recal(ftu, 1);
 1702 
 1703   /* set transfer speed */
 1704 #ifndef PC98
 1705   outb(fdc->baseport+FDCTL, FDC_500KBPS);
 1706   DELAY(10);
 1707 #endif
 1708 
 1709   DPRT(("tape_start end\n"));
 1710 }
 1711 
 1712 
 1713 /*
 1714  *  Transfer control back to floppy disks.
 1715  */
 1716 static void
 1717 tape_end(ftu_t ftu)
 1718 {
 1719   ft_p  ft = ft_data[ftu];
 1720   fdc_p fdc = ft->fdc;
 1721   int s;
 1722 
 1723   DPRT(("tape_end start\n"));
 1724   tape_recal(ftu, 0);
 1725 
 1726   s = splbio();
 1727 
 1728   /* reset, dma disable */
 1729 #ifdef PC98
 1730   outb(fdc->baseport+FDOUT, FDO_RST | FDO_FRY | FDO_AIE | FDO_MTON);
 1731 #else
 1732   outb(fdc->baseport+FDOUT, 0x00);
 1733 #endif
 1734   (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
 1735 
 1736   /* raise reset, enable DMA */
 1737 #ifdef PC98
 1738   outb(fdc->baseport+FDOUT, FDO_DMAE | FDO_MTON);
 1739 #else
 1740   outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN);
 1741 #endif
 1742   (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
 1743 
 1744   splx(s);
 1745 
 1746   /* set transfer speed */
 1747 #ifndef PC98
 1748   outb(fdc->baseport+FDCTL, FDC_500KBPS);
 1749   DELAY(10);
 1750 #endif
 1751   fdc->flags &= ~FDC_TAPE_BUSY;
 1752 
 1753   DPRT(("tape_end end\n"));
 1754 }
 1755 
 1756 
 1757 /*
 1758  *  Wait for the driver to go inactive, cancel readahead if necessary.
 1759  */
 1760 static void
 1761 tape_inactive(ftu_t ftu)
 1762 {
 1763   ft_p  ft = ft_data[ftu];
 1764   int s = splbio();
 1765 
 1766   if (ft->segh != NULL) {
 1767         if (ft->segh->reqtype == FTIO_RDAHEAD) {
 1768                 /* cancel read-ahead */
 1769                 ft->segh->reqcan = 1;
 1770         } else if (ft->segh->reqtype == FTIO_WRITING && !ft->active) {
 1771                 /* flush out any remaining writes */
 1772                 DPRT(("Flushing write I/O chain\n"));
 1773                 arq_state = ard_state = awr_state = 0;
 1774                 ft->xblk = ft->segh->reqblk;
 1775                 ft->xseg = ft->segh->reqseg;
 1776                 ft->xcnt = 0;
 1777                 ft->xptr = ft->segh->buff;
 1778                 ft->active = 1;
 1779                 timeout(ft_timeout, (caddr_t)ftu, 1);
 1780         }
 1781   }
 1782   while (ft->active) ftsleep(wc_iosts_change, 0);
 1783   splx(s);
 1784 }
 1785 
 1786 
 1787 /*
 1788  *  Get the geometry of the tape currently in the drive.
 1789  */
 1790 static int
 1791 ftgetgeom(ftu_t ftu)
 1792 {
 1793   int r, i, tries;
 1794   int cfg, qic80, ext;
 1795   int sts, fmt, len;
 1796   ft_p  ft = ft_data[ftu];
 1797 
 1798   r = tape_status(ftu);
 1799 
 1800   /* XXX fix me when format mode is finished */
 1801   if (r < 0 || (r & QS_CART) == 0 || (r & QS_FMTOK) == 0) {
 1802         DPRT(("ftgetgeom: no cart or not formatted 0x%04x\n",r));
 1803         ftg = NULL;
 1804         ft->newcart = 1;
 1805         return(0);
 1806   }
 1807 
 1808   /* Report drive configuration */
 1809   for (cfg = -1, tries = 0; cfg < 0 && tries < 3; tries++)
 1810         cfg = qic_status(ftu, QC_CONFIG, 8);
 1811   if (tries == 3) {
 1812         DPRT(("ftgetgeom report config failed\n"));
 1813         ftg = NULL;
 1814         return(-1);
 1815   }
 1816   DPRT(("ftgetgeom report config got $%04x\n", cfg));
 1817   ft->lastcfg = cfg;
 1818 
 1819   qic80 = cfg & QCF_QIC80;
 1820   ext = cfg & QCF_EXTRA;
 1821 
 1822 /*
 1823  *  XXX - This doesn't seem to work on my Colorado Jumbo 250...
 1824  *  if it works on your drive, I'd sure like to hear about it.
 1825  */
 1826 #if 0
 1827   /* Report drive status */
 1828   for (sts = -1, tries = 0; sts < 0 && tries < 3; tries++)
 1829         sts = qic_status(ftu, QC_TSTATUS, 8);
 1830   if (tries == 3) {
 1831         DPRT(("ftgetgeom report tape status failed\n"));
 1832         ftg = NULL;
 1833         return(-1);
 1834   }
 1835   DPRT(("ftgetgeom report tape status got $%04x\n", sts));
 1836 #else
 1837   /*
 1838    *  XXX - Forge a fake tape status based upon the returned
 1839    *  configuration, since the above command or code is broken
 1840    *  for my drive and probably other older drives.
 1841    */
 1842   sts = 0;
 1843   sts = (qic80) ? QTS_QIC80 : QTS_QIC40;
 1844   sts |= (ext) ? QTS_LEN2 : QTS_LEN1;
 1845 #endif
 1846 
 1847   fmt = sts & QTS_FMMASK;
 1848   len = (sts & QTS_LNMASK) >> 4;
 1849 
 1850   if (fmt > QCV_NFMT) {
 1851         ftg = NULL;
 1852         printf("ft%d: unsupported tape format\n", ftu);
 1853         return(-1);
 1854   }
 1855   if (len > QCV_NLEN) {
 1856         ftg = NULL;
 1857         printf("ft%d: unsupported tape length\n", ftu);
 1858         return(-1);
 1859   }
 1860 
 1861   /* Look up geometry in the table */
 1862   for (i = 1; i < NGEOM; i++)
 1863         if (ftgtbl[i].g_fmtno == fmt && ftgtbl[i].g_lenno == len) break;
 1864   if (i == NGEOM) {
 1865         printf("ft%d: unknown tape geometry\n", ftu);
 1866         ftg = NULL;
 1867         return(-1);
 1868   }
 1869   ftg = &ftgtbl[i];
 1870   if (!ftg->g_trktape) {
 1871         printf("ft%d: unsupported format %s w/len %s\n",
 1872                                 ftu, ftg->g_fmtdesc, ftg->g_lendesc);
 1873         ftg = NULL;
 1874         return(-1);
 1875   }
 1876   DPRT(("Tape format is %s, length is %s\n", ftg->g_fmtdesc, ftg->g_lendesc));
 1877   ft->newcart = 0;
 1878   return(0);
 1879 }
 1880 
 1881 
 1882 /*
 1883  *  Switch between tape/floppy.  This will send the tape enable/disable
 1884  *  codes for this drive's manufacturer.
 1885  */
 1886 static int
 1887 set_fdcmode(dev_t dev, int newmode)
 1888 {
 1889   ftu_t ftu = FDUNIT(minor(dev));
 1890   ft_p  ft = ft_data[ftu];
 1891   fdc_p fdc = ft->fdc;
 1892   static int havebufs = 0;
 1893   int i;
 1894   SegReq *sp, *rsp;
 1895 
 1896   if (newmode == FDC_TAPE_MODE) {
 1897         /* Wake up the tape drive */
 1898         switch (ft->type) {
 1899             case NO_TYPE:
 1900                 fdc->flags &= ~FDC_TAPE_BUSY;
 1901                 return(ENXIO);
 1902             case FT_NONE:
 1903                 tape_start(ftu, 0);
 1904                 break;
 1905             case FT_COLORADO:
 1906                 tape_start(ftu, 0);
 1907                 if (tape_cmd(ftu, QC_COL_ENABLE1)) {
 1908                         tape_end(ftu);
 1909                         return(EIO);
 1910                 }
 1911                 if (tape_cmd(ftu, QC_COL_ENABLE2 + ftu)) {
 1912                         tape_end(ftu);
 1913                         return(EIO);
 1914                 }
 1915                 break;
 1916             case FT_MOUNTAIN:
 1917                 tape_start(ftu, 0);
 1918                 if (tape_cmd(ftu, QC_MTN_ENABLE1)) {
 1919                         tape_end(ftu);
 1920                         return(EIO);
 1921                 }
 1922                 if (tape_cmd(ftu, QC_MTN_ENABLE2)) {
 1923                         tape_end(ftu);
 1924                         return(EIO);
 1925                 }
 1926                 break;
 1927             case FT_INSIGHT:
 1928                 tape_start(ftu, 1);
 1929                 break;
 1930             default:
 1931                 DPRT(("ft%d: bad tape type\n", ftu));
 1932                 return(ENXIO);
 1933         }
 1934         if (tape_status(ftu) < 0) {
 1935                 if (ft->type == FT_COLORADO)
 1936                         tape_cmd(ftu, QC_COL_DISABLE);
 1937                 else if (ft->type == FT_MOUNTAIN)
 1938                         tape_cmd(ftu, QC_MTN_DISABLE);
 1939                 tape_end(ftu);
 1940                 return(EIO);
 1941         }
 1942 
 1943         /* Grab buffers from memory. */
 1944         if (!havebufs) {
 1945                 ft->segh = ft->segt = NULL;
 1946                 ft->doneh = ft->donet = NULL;
 1947                 ft->segfree = NULL;
 1948                 ft->hdr = NULL;
 1949                 ft->nsegq = ft->ndoneq = ft->nfreelist = 0;
 1950                 for (i = 0; i < FTNBUFF; i++) {
 1951                         sp = malloc(sizeof(SegReq), M_DEVBUF, M_WAITOK);
 1952                         if (sp == NULL) {
 1953                                 printf("ft%d: not enough memory for buffers\n", ftu);
 1954                                 for (sp=ft->segfree; sp != NULL; sp=sp->next)
 1955                                         free(sp, M_DEVBUF);
 1956                                 if (ft->type == FT_COLORADO)
 1957                                         tape_cmd(ftu, QC_COL_DISABLE);
 1958                                 else if (ft->type == FT_MOUNTAIN)
 1959                                         tape_cmd(ftu, QC_MTN_DISABLE);
 1960                                 tape_end(ftu);
 1961                                 return(ENOMEM);
 1962                         }
 1963                         sp->reqtype = FTIO_READY;
 1964                         sp->next = ft->segfree;
 1965                         ft->segfree = sp;
 1966                         ft->nfreelist++;
 1967                 }
 1968                 /* take one buffer for header */
 1969                 ft->hdr = ft->segfree;
 1970                 ft->segfree = ft->segfree->next;
 1971                 ft->nfreelist--;
 1972                 havebufs = 1;
 1973         }
 1974         ft->io_sts = FTIO_READY;        /* tape drive is ready */
 1975         ft->active = 0;                 /* interrupt driver not active */
 1976         ft->moving = 0;                 /* tape not moving */
 1977         ft->rdonly = 0;                 /* tape read only */
 1978         ft->newcart = 0;                /* new cartridge flag */
 1979         ft->lastpos = -1;               /* tape is rewound */
 1980         async_func = ACMD_NONE;         /* No async function */
 1981         tape_state(ftu, 0, QS_READY, 60);
 1982         tape_cmd(ftu, QC_RATE);
 1983         tape_cmd(ftu, QCF_RT500+2);             /* 500K bps */
 1984         tape_state(ftu, 0, QS_READY, 60);
 1985         ft->mode = FTM_PRIMARY;
 1986         tape_cmd(ftu, QC_PRIMARY);      /* Make sure we're in primary mode */
 1987         tape_state(ftu, 0, QS_READY, 60);
 1988         ftg = NULL;                     /* No geometry yet */
 1989         ftgetgeom(ftu);                 /* Get tape geometry */
 1990         ftreq_rewind(ftu);              /* Make sure tape is rewound */
 1991   } else {
 1992         if (ft->type == FT_COLORADO)
 1993                 tape_cmd(ftu, QC_COL_DISABLE);
 1994         else if (ft->type == FT_MOUNTAIN)
 1995                 tape_cmd(ftu, QC_MTN_DISABLE);
 1996         tape_end(ftu);
 1997         ft->newcart = 0;                /* clear new cartridge */
 1998         if (ft->hdr != NULL) free(ft->hdr, M_DEVBUF);
 1999         if (havebufs) {
 2000                 for (sp = ft->segfree; sp != NULL;) {
 2001                         rsp = sp; sp = sp->next;
 2002                         free(rsp, M_DEVBUF);
 2003                 }
 2004                 for (sp = ft->segh; sp != NULL;) {
 2005                         rsp = sp; sp = sp->next;
 2006                         free(rsp, M_DEVBUF);
 2007                 }
 2008                 for (sp = ft->doneh; sp != NULL;) {
 2009                         rsp = sp; sp = sp->next;
 2010                         free(rsp, M_DEVBUF);
 2011                 }
 2012         }
 2013         havebufs = 0;
 2014   }
 2015   return(0);
 2016 }
 2017 
 2018 
 2019 /*
 2020  *  Perform a QIC status function.
 2021  */
 2022 static int
 2023 qic_status(ftu_t ftu, int cmd, int nbits)
 2024 {
 2025   int st3, r, i;
 2026   ft_p  ft = ft_data[ftu];
 2027   fdcu_t fdcu = ft->fdc->fdcu;          /* fdc active unit */
 2028 
 2029   if (tape_cmd(ftu, cmd)) {
 2030         DPRT(("ft%d: QIC status timeout\n", ftu));
 2031         return(-1);
 2032   }
 2033 
 2034   /* Sense drive status */
 2035   out_fdc(fdcu, NE7CMD_SENSED);
 2036   out_fdc(fdcu, ftu);
 2037   st3 = in_fdc(fdcu);
 2038 
 2039   if ((st3 & 0x10) == 0) {      /* track 0 */
 2040         DPRT(("qic_status has dead drive...  st3 = $%02x\n", st3));
 2041         return(-1);
 2042   }
 2043 
 2044   for (i = r = 0; i <= nbits; i++) {
 2045         if (tape_cmd(ftu, QC_NEXTBIT)) {
 2046                 DPRT(("ft%d: QIC status bit timed out on %d\n", ftu, i));
 2047                 return(-1);
 2048         }
 2049 
 2050         out_fdc(fdcu, NE7CMD_SENSED);
 2051         out_fdc(fdcu, ftu);
 2052         st3 = in_fdc(fdcu);
 2053         if (st3 < 0) {
 2054                 DPRT(("ft%d: controller timed out on bit %d r=$%02x\n",
 2055                                         ftu, i, r));
 2056                 return(-1);
 2057         }
 2058 
 2059         r >>= 1;
 2060         if (i < nbits)
 2061                 r |= ((st3 & 0x10) ? 1 : 0) << nbits;
 2062         else if ((st3 & 0x10) == 0) {
 2063                 DPRT(("ft%d: qic status stop bit missing at %d, st3=$%02x r=$%04x\n",
 2064                         ftu,i,st3,r));
 2065                 return(-1);
 2066         }
 2067   }
 2068 
 2069   DPRT(("qic_status returned $%02x\n", r));
 2070   return(r);
 2071 }
 2072 
 2073 
 2074 /*
 2075  *  Open tape drive for use.  Bounced off of Fdopen if tape minor is
 2076  *  detected.
 2077  */
 2078 int
 2079 ftopen(dev_t dev, int arg2) {
 2080   ftu_t ftu = FDUNIT(minor(dev));
 2081   fdc_p fdc;
 2082 
 2083   /* check bounds */
 2084   if (ftu >= NFT)
 2085         return(ENXIO);
 2086   if (!ft_data[ftu])
 2087         return(ENXIO);
 2088   fdc = ft_data[ftu]->fdc;
 2089   if ((fdc == NULL) || (ft_data[ftu]->type == NO_TYPE))
 2090           return(ENXIO);
 2091   /* check for controller already busy with tape */
 2092   if (fdc->flags & FDC_TAPE_BUSY)
 2093         return(EBUSY);
 2094   /* make sure we found a tape when probed */
 2095   if (!(fdc->flags & FDC_HASFTAPE))
 2096         return(ENODEV);
 2097   fdc->fdu = ftu;
 2098   fdc->flags |= FDC_TAPE_BUSY;
 2099   return(set_fdcmode(dev, FDC_TAPE_MODE)); /* try to switch to tape */
 2100 }
 2101 
 2102 
 2103 /*
 2104  *  Close tape and return floppy controller to disk mode.
 2105  */
 2106 int
 2107 ftclose(dev_t dev, int flags)
 2108 {
 2109   ftu_t ftu = FDUNIT(minor(dev));
 2110   ft_p  ft = ft_data[ftu];
 2111 
 2112 
 2113   /* Wait for any remaining I/O activity to complete. */
 2114   tape_inactive(ftu);
 2115 
 2116   ft->mode = FTM_PRIMARY;
 2117   tape_cmd(ftu, QC_PRIMARY);
 2118   tape_state(ftu, 0, QS_READY, 60);
 2119   ftreq_rewind(ftu);
 2120   return(set_fdcmode(dev, FDC_DISK_MODE));      /* Otherwise, close tape */
 2121 }
 2122 
 2123 /*
 2124  *  Read or write a segment.
 2125  */
 2126 static int
 2127 ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p)
 2128 {
 2129   int r, i;
 2130   SegReq *sp;
 2131   int s;
 2132   long blk, bad, seg;
 2133   unsigned char *cp, *cp2;
 2134   ft_p  ft = ft_data[ftu];
 2135 
 2136   if (!ft->active && ft->segh == NULL) {
 2137         r = tape_status(ftu);
 2138         if ((r & QS_CART) == 0)
 2139                 return(ENXIO);  /* No cartridge */
 2140         if ((r & QS_FMTOK) == 0)
 2141                 return(ENXIO);  /* Not formatted */
 2142         tape_state(ftu, 0, QS_READY, 90);
 2143   }
 2144 
 2145   if (ftg == NULL || ft->newcart) {
 2146         tape_inactive(ftu);
 2147         tape_state(ftu, 0, QS_READY, 90);
 2148         if (ftgetgeom(ftu) < 0)
 2149                 return(ENXIO);
 2150   }
 2151 
 2152   /* Write not allowed on a read-only tape. */
 2153   if (cmd == QIOWRITE && ft->rdonly)
 2154         return(EROFS);
 2155 
 2156   /* Quick check of request and buffer. */
 2157   if (sr == NULL || sr->sg_data == NULL)
 2158         return(EINVAL);
 2159 
 2160   /* Make sure requested track and segment is in range. */
 2161   if (sr->sg_trk >= ftg->g_trktape || sr->sg_seg >= ftg->g_segtrk)
 2162         return(EINVAL);
 2163 
 2164   blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG;
 2165   seg = sr->sg_trk * ftg->g_segtrk + sr->sg_seg;
 2166 
 2167   s = splbio();
 2168   if (cmd == QIOREAD) {
 2169         /*
 2170          *  See if the driver is reading ahead.
 2171          */
 2172         if (ft->doneh != NULL ||
 2173                 (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD)) {
 2174                 /*
 2175                  *  Eat the completion queue and see if the request
 2176                  *  is already there.
 2177                  */
 2178                 while (ft->doneh != NULL) {
 2179                         if (blk == ft->doneh->reqblk) {
 2180                                 sp = ft->doneh;
 2181                                 sp->reqtype = FTIO_READING;
 2182                                 sp->reqbad = sr->sg_badmap;
 2183                                 goto rddone;
 2184                         }
 2185                         segio_free(ft, ft->doneh);
 2186                 }
 2187 
 2188                 /*
 2189                  *  Not on the completed queue, in progress maybe?
 2190                  */
 2191                 if (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD &&
 2192                                 blk == ft->segh->reqblk) {
 2193                         sp = ft->segh;
 2194                         sp->reqtype = FTIO_READING;
 2195                         sp->reqbad = sr->sg_badmap;
 2196                         goto rdwait;
 2197                 }
 2198         }
 2199 
 2200         /* Wait until we're ready. */
 2201         tape_inactive(ftu);
 2202 
 2203         /* Set up a new read request. */
 2204         sp = segio_alloc(ft);
 2205         sp->reqcrc = 0;
 2206         sp->reqbad = sr->sg_badmap;
 2207         sp->reqblk = blk;
 2208         sp->reqseg = seg;
 2209         sp->reqcan = 0;
 2210         sp->reqtype = FTIO_READING;
 2211         segio_queue(ft, sp);
 2212 
 2213         /* Start the read request off. */
 2214         DPRT(("Starting read I/O chain\n"));
 2215         arq_state = ard_state = awr_state = 0;
 2216         ft->xblk = sp->reqblk;
 2217         ft->xseg = sp->reqseg;
 2218         ft->xcnt = 0;
 2219         ft->xptr = sp->buff;
 2220         ft->active = 1;
 2221         timeout(ft_timeout, (caddr_t)ftu, 1);
 2222 
 2223 rdwait:
 2224         ftsleep(wc_buff_done, 0);
 2225 
 2226 rddone:
 2227         bad = sp->reqbad;
 2228         sr->sg_crcmap = sp->reqcrc & ~bad;
 2229 
 2230         /* Copy out segment and discard bad mapped blocks. */
 2231         cp = sp->buff; cp2 = sr->sg_data;
 2232         for (i = 0; i < QCV_BLKSEG; cp += QCV_BLKSIZE, i++) {
 2233                 if (bad & (1 << i)) continue;
 2234                 copyout(cp, cp2, QCV_BLKSIZE);
 2235                 cp2 += QCV_BLKSIZE;
 2236         }
 2237         segio_free(ft, sp);
 2238   } else {
 2239         if (ft->segh != NULL && ft->segh->reqtype != FTIO_WRITING)
 2240                 tape_inactive(ftu);
 2241 
 2242         /* Allocate a buffer and start tape if we're running low. */
 2243         sp = segio_alloc(ft);
 2244         if (!ft->active && (sp == NULL || ft->nfreelist <= 1)) {
 2245                 DPRT(("Starting write I/O chain\n"));
 2246                 arq_state = ard_state = awr_state = 0;
 2247                 ft->xblk = ft->segh->reqblk;
 2248                 ft->xseg = ft->segh->reqseg;
 2249                 ft->xcnt = 0;
 2250                 ft->xptr = ft->segh->buff;
 2251                 ft->active = 1;
 2252                 timeout(ft_timeout, (caddr_t)ftu, 1);
 2253         }
 2254 
 2255         /* Sleep until a buffer becomes available. */
 2256         while (sp == NULL) {
 2257                 ftsleep(wc_buff_avail, 0);
 2258                 sp = segio_alloc(ft);
 2259         }
 2260 
 2261         /* Copy in segment and expand bad blocks. */
 2262         bad = sr->sg_badmap;
 2263         cp = sr->sg_data; cp2 = sp->buff;
 2264         for (i = 0; i < QCV_BLKSEG; cp2 += QCV_BLKSIZE, i++) {
 2265                 if (bad & (1 << i)) continue;
 2266                 copyin(cp, cp2, QCV_BLKSIZE);
 2267                 cp += QCV_BLKSIZE;
 2268         }
 2269         sp->reqblk = blk;
 2270         sp->reqseg = seg;
 2271         sp->reqcan = 0;
 2272         sp->reqtype = FTIO_WRITING;
 2273         segio_queue(ft, sp);
 2274   }
 2275   splx(s);
 2276   return(0);
 2277 }
 2278 
 2279 
 2280 /*
 2281  *  Rewind to beginning of tape
 2282  */
 2283 static int
 2284 ftreq_rewind(ftu_t ftu)
 2285 {
 2286   ft_p  ft = ft_data[ftu];
 2287 
 2288   tape_inactive(ftu);
 2289   tape_cmd(ftu, QC_STOP);
 2290   tape_state(ftu, 0, QS_READY, 90);
 2291   tape_cmd(ftu, QC_SEEKSTART);
 2292   tape_state(ftu, 0, QS_READY, 90);
 2293   tape_cmd(ftu, QC_SEEKTRACK);
 2294   tape_cmd(ftu, 2);
 2295   tape_state(ftu, 0, QS_READY, 90);
 2296   ft->lastpos = -1;
 2297   ft->moving = 0;
 2298   return(0);
 2299 }
 2300 
 2301 
 2302 /*
 2303  *  Move to logical beginning or end of track
 2304  */
 2305 static int
 2306 ftreq_trkpos(ftu_t ftu, int req)
 2307 {
 2308   int curtrk, r, cmd;
 2309   ft_p  ft = ft_data[ftu];
 2310 
 2311   tape_inactive(ftu);
 2312   tape_cmd(ftu, QC_STOP);
 2313   tape_state(ftu, 0, QS_READY, 90);
 2314 
 2315   r = tape_status(ftu);
 2316   if ((r & QS_CART) == 0) return(ENXIO);        /* No cartridge */
 2317   if ((r & QS_FMTOK) == 0) return(ENXIO);       /* Not formatted */
 2318 
 2319   if (ftg == NULL || ft->newcart) {
 2320         if (ftgetgeom(ftu) < 0) return(ENXIO);
 2321   }
 2322 
 2323   curtrk = (ft->lastpos < 0) ? 0 : ft->lastpos / ftg->g_blktrk;
 2324   if (req == QIOBOT)
 2325         cmd = (curtrk & 1) ? QC_SEEKEND : QC_SEEKSTART;
 2326   else
 2327         cmd = (curtrk & 1) ? QC_SEEKSTART : QC_SEEKEND;
 2328   tape_cmd(ftu, cmd);
 2329   tape_state(ftu, 0, QS_READY, 90);
 2330   return(0);
 2331 }
 2332 
 2333 
 2334 /*
 2335  *  Seek tape head to a particular track.
 2336  */
 2337 static int
 2338 ftreq_trkset(ftu_t ftu, int *trk)
 2339 {
 2340   int r;
 2341   ft_p  ft = ft_data[ftu];
 2342 
 2343   tape_inactive(ftu);
 2344   tape_cmd(ftu, QC_STOP);
 2345   tape_state(ftu, 0, QS_READY, 90);
 2346 
 2347   r = tape_status(ftu);
 2348   if ((r & QS_CART) == 0) return(ENXIO);        /* No cartridge */
 2349   if ((r & QS_FMTOK) == 0) return(ENXIO);       /* Not formatted */
 2350   if (ftg == NULL || ft->newcart) {
 2351         if (ftgetgeom(ftu) < 0) return(ENXIO);
 2352   }
 2353 
 2354   tape_cmd(ftu, QC_SEEKTRACK);
 2355   tape_cmd(ftu, *trk + 2);
 2356   tape_state(ftu, 0, QS_READY, 90);
 2357   return(0);
 2358 }
 2359 
 2360 
 2361 /*
 2362  *  Start tape moving forward.
 2363  */
 2364 static int
 2365 ftreq_lfwd(ftu_t ftu)
 2366 {
 2367   ft_p  ft = ft_data[ftu];
 2368 
 2369   tape_inactive(ftu);
 2370   tape_cmd(ftu, QC_STOP);
 2371   tape_state(ftu, 0, QS_READY, 90);
 2372   tape_cmd(ftu, QC_FORWARD);
 2373   ft->moving = 1;
 2374   return(0);
 2375 }
 2376 
 2377 
 2378 /*
 2379  *  Stop the tape
 2380  */
 2381 static int
 2382 ftreq_stop(ftu_t ftu)
 2383 {
 2384   ft_p  ft = ft_data[ftu];
 2385 
 2386   tape_inactive(ftu);
 2387   tape_cmd(ftu, QC_STOP);
 2388   tape_state(ftu, 0, QS_READY, 90);
 2389   ft->moving = 0;
 2390   return(0);
 2391 }
 2392 
 2393 
 2394 /*
 2395  *  Set the particular mode the drive should be in.
 2396  */
 2397 static int
 2398 ftreq_setmode(ftu_t ftu, int cmd)
 2399 {
 2400   int r;
 2401   ft_p  ft = ft_data[ftu];
 2402 
 2403   tape_inactive(ftu);
 2404   r = tape_status(ftu);
 2405 
 2406   switch(cmd) {
 2407      case QIOPRIMARY:
 2408         ft->mode = FTM_PRIMARY;
 2409         tape_cmd(ftu, QC_PRIMARY);
 2410         break;
 2411      case QIOFORMAT:
 2412         if (r & QS_RDONLY) return(ENXIO);
 2413         if ((r & QS_BOT) == 0) return(ENXIO);
 2414         tape_cmd(ftu, QC_FORMAT);
 2415         break;
 2416      case QIOVERIFY:
 2417         if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
 2418         tape_cmd(ftu, QC_VERIFY);
 2419         break;
 2420   }
 2421   tape_state(ftu, 0, QS_READY, 60);
 2422   return(0);
 2423 }
 2424 
 2425 
 2426 /*
 2427  *  Return drive status bits
 2428  */
 2429 static int
 2430 ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p)
 2431 {
 2432   ft_p  ft = ft_data[ftu];
 2433 
 2434   if (ft->active)
 2435         *sts = ft->laststs & ~QS_READY;
 2436   else
 2437         *sts = tape_status(ftu);
 2438   return(0);
 2439 }
 2440 
 2441 
 2442 /*
 2443  *  Return drive configuration bits
 2444  */
 2445 static int
 2446 ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p)
 2447 {
 2448   int r, tries;
 2449   ft_p  ft = ft_data[ftu];
 2450 
 2451   if (ft->active)
 2452         r = ft->lastcfg;
 2453   else {
 2454         for (r = -1, tries = 0; r < 0 && tries < 3; tries++)
 2455                 r = qic_status(ftu, QC_CONFIG, 8);
 2456         if (tries == 3) return(ENXIO);
 2457   }
 2458   *cfg = r;
 2459   return(0);
 2460 }
 2461 
 2462 
 2463 /*
 2464  *  Return current tape's geometry.
 2465  */
 2466 static int
 2467 ftreq_geom(ftu_t ftu, QIC_Geom *g)
 2468 {
 2469   tape_inactive(ftu);
 2470   if (ftg == NULL && ftgetgeom(ftu) < 0) return(ENXIO);
 2471   bcopy(ftg, g, sizeof(QIC_Geom));
 2472   return(0);
 2473 }
 2474 
 2475 
 2476 /*
 2477  *  Return drive hardware information
 2478  */
 2479 static int
 2480 ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp)
 2481 {
 2482   int tries;
 2483   int rom, vend;
 2484 
 2485   tape_inactive(ftu);
 2486   bzero(hwp, sizeof(QIC_HWInfo));
 2487 
 2488   for (rom = -1, tries = 0; rom < 0 && tries < 3; tries++)
 2489         rom = qic_status(ftu, QC_VERSION, 8);
 2490   if (rom > 0) {
 2491         hwp->hw_rombeta = (rom >> 7) & 0x01;
 2492         hwp->hw_romid = rom & 0x7f;
 2493   }
 2494 
 2495   for (vend = -1, tries = 0; vend < 0 && tries < 3; tries++)
 2496         vend = qic_status(ftu, QC_VENDORID, 16);
 2497   if (vend > 0) {
 2498         hwp->hw_make = (vend >> 6) & 0x3ff;
 2499         hwp->hw_model = vend & 0x3f;
 2500   }
 2501 
 2502   return(0);
 2503 }
 2504 
 2505 
 2506 /*
 2507  *  Receive or Send the in-core header segment.
 2508  */
 2509 static int
 2510 ftreq_hdr(ftu_t ftu, int cmd, QIC_Segment *sp)
 2511 {
 2512   ft_p  ft = ft_data[ftu];
 2513   QIC_Header *h = (QIC_Header *)ft->hdr->buff;
 2514 
 2515   if (sp == NULL || sp->sg_data == NULL) return(EINVAL);
 2516   if (cmd == QIOSENDHDR) {
 2517         copyin(sp->sg_data, ft->hdr->buff, QCV_SEGSIZE);
 2518   } else {
 2519         if (h->qh_sig != QCV_HDRMAGIC) return(EIO);
 2520         copyout(ft->hdr->buff, sp->sg_data, QCV_SEGSIZE);
 2521   }
 2522   return(0);
 2523 }
 2524 
 2525 /*
 2526  *  I/O functions.
 2527  */
 2528 int
 2529 ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
 2530 {
 2531   ftu_t ftu = FDUNIT(minor(dev));
 2532 
 2533   switch(cmd) {
 2534      case QIOREAD:      /* Request reading a segment from tape.         */
 2535      case QIOWRITE:     /* Request writing a segment to tape.           */
 2536         return(ftreq_rw(ftu, cmd, (QIC_Segment *)data, p));
 2537 
 2538      case QIOREWIND:    /* Rewind tape.                                 */
 2539         return(ftreq_rewind(ftu));
 2540 
 2541      case QIOBOT:       /* Seek to logical beginning of track.          */
 2542      case QIOEOT:       /* Seek to logical end of track.                */
 2543         return(ftreq_trkpos(ftu, cmd));
 2544 
 2545      case QIOTRACK:     /* Seek tape head to specified track.           */
 2546         return(ftreq_trkset(ftu, (int *)data));
 2547 
 2548      case QIOSEEKLP:    /* Seek load point.                             */
 2549         goto badreq;
 2550 
 2551      case QIOFORWARD:   /* Move tape in logical forward direction.      */
 2552         return(ftreq_lfwd(ftu));
 2553 
 2554      case QIOSTOP:      /* Causes tape to stop.                         */
 2555         return(ftreq_stop(ftu));
 2556 
 2557      case QIOPRIMARY:   /* Enter primary mode.                          */
 2558      case QIOFORMAT:    /* Enter format mode.                           */
 2559      case QIOVERIFY:    /* Enter verify mode.                           */
 2560         return(ftreq_setmode(ftu, cmd));
 2561 
 2562      case QIOWRREF:     /* Write reference burst.                       */
 2563         goto badreq;
 2564 
 2565      case QIOSTATUS:    /* Get drive status.                            */
 2566         return(ftreq_status(ftu, cmd, (int *)data, p));
 2567 
 2568      case QIOCONFIG:    /* Get tape configuration.                      */
 2569         return(ftreq_config(ftu, cmd, (int *)data, p));
 2570 
 2571      case QIOGEOM:
 2572         return(ftreq_geom(ftu, (QIC_Geom *)data));
 2573 
 2574      case QIOHWINFO:
 2575         return(ftreq_hwinfo(ftu, (QIC_HWInfo *)data));
 2576 
 2577      case QIOSENDHDR:
 2578      case QIORECVHDR:
 2579         return(ftreq_hdr(ftu, cmd, (QIC_Segment *)data));
 2580   }
 2581 badreq:
 2582   DPRT(("ft%d: unknown ioctl(%d) request\n", ftu, cmd));
 2583   return(ENXIO);
 2584 }
 2585 
 2586 #endif

Cache object: 47a151ad70c2ac81d74f81ce62ea35a9


[ 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.