Return to BSD News archive
Path: sserve!newshost.anu.edu.au!munnari.oz.au!news.Hawaii.Edu!ames!agate!howland.reston.ans.net!noc.near.net!ceylon!genesis!steve2 From: steve2@genesis.nred.ma.us Newsgroups: comp.os.386bsd.development Subject: ft dist0.2 part 02/03 Message-ID: <CCtK4z.3AC@genesis.nred.ma.us> Date: 4 Sep 93 07:43:47 GMT Organization: Genesis Public Access Unix +1 508 664 0149 Lines: 1986 # This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # driver # driver/ft.c # echo c - driver mkdir driver > /dev/null 2>&1 echo x - driver/ft.c sed 's/^X//' >driver/ft.c << 'END-of-driver/ft.c' X/* X * Copyright (c) 1993 Steve Gerakines X * X * This is freely redistributable software. You may do anything you X * wish with it, so long as the above notice stays intact. X * X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS X * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE X * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, X * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES X * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, X * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING X * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE X * POSSIBILITY OF SUCH DAMAGE. X * X * ft.c - floppy tape driver X * 08/07/93 v0.2 release X * Shifted from ftstrat to ioctl support for I/O. Streaming is now much X * more reliable. Added internal support for error correction, QIC-40, X * and variable length tapes. Random access of segments greatly X * improved. Formatting and verification support is close but still X * incomplete. X * X * 06/03/93 v0.1 Alpha release X * Hopefully the last re-write. Many bugs fixed, many remain. X */ X X#include "ft.h" X#if NFT > 0 X X#include "param.h" X#include "dkbad.h" X#include "systm.h" X#include "conf.h" X#include "file.h" X#include "ioctl.h" X#include "malloc.h" X#include "buf.h" X#include "uio.h" X#include "i386/isa/isa_device.h" X#include "i386/isa/fdreg.h" X#include "i386/isa/icu.h" X#include "i386/isa/rtc.h" X#include <sys/ftape.h> X#include "ftreg.h" X X/* Enable or disable debugging messages. */ X/*#define DPRT(a) printf a /**/ X#define DPRT(a) /**/ X X/* Constants private to the driver */ X#define FTPRI (PRIBIO) /* sleep priority */ X X/* The following items are needed from the fd driver. */ Xextern int Fdopen(); /* fd open function */ Xextern int in_fdc(); /* read fdc registers */ Xextern int out_fdc(); /* write fdc registers */ X#undef NFD X#define NFD 2 /* Ugh. */ X Xextern int rawread(dev_t, struct uio *, int); Xextern int rawwrite(dev_t, struct uio *, int); Xextern int nullop(); Xextern int enodev(); X Xextern int hz; /* system clock rate */ Xextern int nblkdev, nchrdev; /* number of block/char devs */ Xextern struct bdevsw bdevsw[]; /* block device table */ Xextern struct cdevsw cdevsw[]; /* character device table */ X X/* Type of tape attached */ Xint ft_type; Xenum { FT_NONE, FT_MOUNTAIN, FT_COLORADO }; X X/* Mode FDC is currently in: tape or disk */ Xenum { FDC_TAPE_MODE, FDC_DISK_MODE }; Xint fdc_mode = FDC_DISK_MODE; X X/* Command we are awaiting completion of */ Xstatic int ftcmd_wait; Xenum { FTCMD_NONE, FTCMD_RESET, FTCMD_RECAL, FTCMD_SEEK, FTCMD_READID }; X X/* Tape interrupt status of current request */ Xstatic int ftsts_wait; Xenum { FTSTS_NONE, FTSTS_SNOOZE, FTSTS_INTERRUPT, FTSTS_TIMEOUT }; X X/* Tape I/O status */ Xstatic int ftio_sts; Xenum { X FTIO_READY, /* No I/O activity */ X FTIO_READING, /* Currently reading blocks */ X FTIO_RDAHEAD, /* Currently reading ahead */ X FTIO_WRITING /* Buffers are being written */ X}; X X/* Current tape mode */ Xenum { X FTM_PRIMARY, /* Primary mode */ X FTM_VERIFY, /* Verify mode */ X FTM_FORMAT, /* Format mode */ X FTM_DIAG1, /* Diagnostic mode 1 */ X FTM_DIAG2 /* Diagnostic mode 2 */ X}; Xstatic int ftmode = FTM_PRIMARY; X X/* Tape geometries table */ XQIC_Geom ftgtbl[] = { X { 0, 0, "Unformatted", "Unknown", 0, 0, 0, 0, 0 }, /* XXX */ X { 1, 1, "QIC-40", "205/550", 20, 68, 2176, 128, 21760 }, X { 1, 2, "QIC-40", "307.5/550", 20, 102, 3264, 128, 32640 }, X { 1, 3, "QIC-40", "295/900", 0, 0, 0, 0, 0 }, /* ??? */ X { 1, 4, "QIC-40", "1100/550", 20, 365, 11680, 128, 32512 }, X { 1, 5, "QIC-40", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */ X { 2, 1, "QIC-80", "205/550", 28, 100, 3200, 128, 19200 }, X { 2, 2, "QIC-80", "307.5/550", 28, 150, 4800, 128, 19200 }, X { 2, 3, "QIC-80", "295/900", 0, 0, 0, 0, 0 }, /* ??? */ X { 2, 4, "QIC-80", "1100/550", 28, 537, 17184, 128, 32512 }, X { 2, 5, "QIC-80", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */ X { 3, 1, "QIC-500", "205/550", 0, 0, 0, 0, 0 }, /* ??? */ X { 3, 2, "QIC-500", "307.5/550", 0, 0, 0, 0, 0 }, /* ??? */ X { 3, 3, "QIC-500", "295/900", 0, 0, 0, 0, 0 }, /* ??? */ X { 3, 4, "QIC-500", "1100/550", 0, 0, 0, 0, 0 }, /* ??? */ X { 3, 5, "QIC-500", "1100/900", 0, 0, 0, 0, 0 } /* ??? */ X}; X#define NGEOM (sizeof(ftgtbl) / sizeof(QIC_Geom)) X XQIC_Geom *ftg = NULL; /* Current tape's geometry */ X X/* X * things relating to asynchronous commands X */ Xstatic int astk_depth; /* async_cmd stack depth */ Xstatic int awr_state; /* state of async write */ Xstatic int ard_state; /* state of async read */ Xstatic int arq_state; /* state of async request */ Xstatic int async_retries; /* retries, one per invocation */ Xstatic int async_func; /* function to perform */ Xstatic int async_state; /* state current function is at */ Xstatic int async_arg[5]; /* up to 5 arguments for async cmds */ Xstatic int async_ret; /* return value */ X X/* List of valid async (interrupt driven) tape support functions. */ Xenum { X ACMD_NONE, /* no command */ X ACMD_SEEK, /* command seek */ X ACMD_STATUS, /* report status */ X ACMD_STATE, /* wait for state bits to be true */ X ACMD_SEEKSTS, /* perform command and wait for status */ X ACMD_READID, /* read id */ X ACMD_RUNBLK /* ready tape for I/O on the given block */ X}; X X/* Call another asyncronous command from within async_cmd(). */ X#define CALL_ACMD(r,f,a,b,c,d,e) \ X astk[astk_depth].over_retries = async_retries; \ X astk[astk_depth].over_func = async_func; \ X astk[astk_depth].over_state = (r); \ X for (i = 0; i < 5; i++) \ X astk[astk_depth].over_arg[i] = async_arg[i]; \ X async_func = (f); async_state = 0; async_retries = 0; \ X async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \ X async_arg[3]=(d); async_arg[4]=(e); \ X astk_depth++; \ X goto restate X X/* Perform an asyncronous command from outside async_cmd(). */ X#define ACMD_FUNC(r,f,a,b,c,d,e) over_async = (r); astk_depth = 0; \ X async_func = (f); async_state = 0; async_retries = 0; \ X async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \ X async_arg[3]=(d); async_arg[4]=(e); \ X async_cmd(); \ X return X X/* Various wait channels */ Xstatic struct { X int buff_avail; X int iosts_change; X int long_delay; X int wait_chan; X} ftsem; X Xstatic int fdc; /* fdc address - passed from fd */ Xstatic int ft_pcn; /* present cylinder number */ Xstatic int ft_attaching; /* true when ft is attaching */ Xstatic int bi, ci; /* saved index into blk/chr tables */ Xstatic struct bdevsw fdbdevsw; /* original block entry */ Xstatic struct cdevsw fdcdevsw; /* original character entry */ X Xint ftopen(dev_t, int, int, struct proc *); Xint ftclose(dev_t, int, int, struct proc *); Xint ftstrategy(struct buf *); Xint ftioctl(dev_t, int, caddr_t, int, struct proc *); Xint ftdump(dev_t); Xint ftsize(dev_t); Xint ft_timeout(int); X X/* Tape block and character device entries. */ Xstatic struct bdevsw ftbdevsw = { X ftopen, ftclose, ftstrategy, ftioctl, ftdump, ftsize, B_TAPE X}; Xstatic struct cdevsw ftcdevsw = { X ftopen, ftclose, rawread, rawwrite, ftioctl, X (int (*)(struct tty *, int))enodev, (int (*)(int))nullop, NULL, seltrue, X (int (*)())enodev, ftstrategy X}; X Xstatic int ftfill; /* buffer currently being filled */ Xstatic int ftxfer; /* buffer currently being xferred */ Xstatic unsigned char *ftxptr; /* pointer to buffer blk to xfer */ Xstatic int ftxcnt; /* transfer count */ Xstatic int ftxblk; /* block number to transfer */ Xstatic int ftbbase[2]; /* blocks loaded in this buffer */ Xstatic int ftbcnt[2]; /* count loaded so far */ X Xstatic SegReq *curseg; /* Current segment to do I/O on */ Xstatic SegReq *bufseg; /* Buffered segment to r/w ahead */ Xstatic int ftactive = 0; /* TRUE if transfer is active */ Xstatic int ftrdonly = 0; /* TRUE if tape is read-only */ Xstatic int ftnewcart = 0; /* TRUE if new cartridge detected */ Xstatic int ftlaststs = 0; /* last reported status code */ Xstatic int ftlastcfg = 0; /* last reported QIC config */ Xstatic int ftlasterr = 0; /* last QIC error code */ Xstatic int ftlastpos = -1; /* last known segment number */ Xstatic int ftmoving = 0; /* TRUE if tape is moving */ Xstatic int rid[7]; /* read_id return values */ X X X/* X * Probe/attach floppy tapes. X */ Xint ftattach(int fd_fdc) X{ X X /* Probe for tape */ X fdc = fd_fdc; X ft_attaching = 1; X ft_type = FT_NONE; X X tape_start(); /* ready controller for tape */ X tape_cmd(QC_COL_ENABLE1); X tape_cmd(QC_COL_ENABLE2); X if (tape_status() >= 0) { X ft_type = FT_COLORADO; X tape_cmd(QC_COL_DISABLE); X goto out; X } X X tape_start(); /* ready controller for tape */ X tape_cmd(QC_MTN_ENABLE1); X tape_cmd(QC_MTN_ENABLE2); X if (tape_status() >= 0) { X ft_type = FT_MOUNTAIN; X tape_cmd(QC_MTN_DISABLE); X goto out; X } X Xout: X tape_end(); X ft_attaching = 0; X} X X X/* X * Perform common commands asynchronously. X */ Xasync_cmd() X{ X int cmd, i, st0, st3, pcn; X static int bitn, retval, retpos, nbits, newcn; X static struct { X int over_func; X int over_state; X int over_retries; X int over_arg[5]; X } astk[15]; X static int wanttrk, wantblk, wantdir; X static int curpos, curtrk, curblk, curdir, curdiff; X static int errcnt = 0; X Xrestate: X#if 0 X DPRT(("async_cmd state: func: %d state: %d\n", async_func, async_state)); X#endif X switch(async_func) { X case ACMD_SEEK: X /* X * Arguments: X * 0 - command to perform X */ X switch (async_state) { X case 0: X cmd = async_arg[0]; X#if 0 X DPRT(("===>async_seek cmd = %d\n", cmd)); X#endif X newcn = (cmd <= ft_pcn) ? ft_pcn - cmd : ft_pcn + cmd; X async_state = 1; X i = 0; X if (out_fdc(NE7CMD_SEEK) < 0) i = 1; X if (!i && out_fdc(0x00) < 0) i = 1; X if (!i && out_fdc(newcn) < 0) i = 1; X if (i) { X if (++async_retries >= 10) { X printf("ft0: async_cmd command seek failed!!\n"); X goto complete; X } X DPRT(("ft0: async_cmd command seek retry...\n")); X async_state = 0; X goto restate; X } X break; X case 1: X out_fdc(NE7CMD_SENSEI); X st0 = in_fdc(); X pcn = in_fdc(); X if (st0 < 0 || pcn < 0 || newcn != pcn) { X if (++async_retries >= 10) { X printf("ft0: async_cmd seek retries exceeded\n"); X goto complete; X } X printf("ft0: async_cmd command bad st0=$%02x pcn=$%02x\n", st0, pcn); X async_state = 0; X timeout(ft_timeout, 0, hz/10); X break; X } X if (st0 & 0x20) { /* seek done */ X ft_pcn = pcn; X } else X printf("ft0: async_seek error st0 = $%02x pcn = %d\n", st0, pcn); X if (async_arg[1]) goto complete; X async_state = 2; X timeout(ft_timeout, 0, hz/50); X break; X case 2: X goto complete; X /* NOTREACHED */ X } X break; X X case ACMD_STATUS: X /* X * Arguments: X * 0 - command to issue report from X * 1 - number of bits X * modifies: bitn, retval, st3 X */ X switch (async_state) { X case 0: X bitn = 0; X retval = 0; X cmd = async_arg[0]; X nbits = async_arg[1]; X DPRT(("async_status got cmd = %d nbits = %d\n", cmd,nbits)); X CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); X /* NOTREACHED */ X case 1: X out_fdc(NE7CMD_SENSED); X out_fdc(0x00); X st3 = in_fdc(); X if (st3 < 0) { X printf("ft0: async_status timed out on bit %d r=$%02x\n",bitn,retval); X async_ret = -1; X goto complete; X } X if ((st3 & 0x10) != 0) retval |= (1 << bitn); X bitn++; X if (bitn >= (nbits+2)) { X if ((retval & 1) && (retval & (1 << (nbits+1)))) { X async_ret = (retval & ~(1<<(nbits+1))) >> 1; X if (async_arg[0] == QC_STATUS && async_arg[2] == 0 && X (async_ret & (QS_ERROR|QS_NEWCART))) { X async_state = 2; X goto restate; X } X DPRT(("async status got $%04x ($%04x)\n", async_ret,retval)); X } else { X printf("ft0: async_status failed: retval=$%04x nbits=%d\n",retval,nbits); X async_ret = -2; X } X goto complete; X } X CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); X /* NOTREACHED */ X case 2: X if (async_ret & QS_NEWCART) ftnewcart = 1; X CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1, 0, 0); X case 3: X ftlasterr = async_ret; X if ((ftlasterr & QS_NEWCART) == 0 && ftlasterr) { X DPRT(("ft0: QIC error %d occurred on cmd %d\n", X ftlasterr & 0xff, ftlasterr >> 8)); X } X cmd = async_arg[0]; X nbits = async_arg[1]; X CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1, 0, 0); X case 4: X goto complete; X case 5: X CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); X case 6: X CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); X case 7: X CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); X case 8: X cmd = async_arg[0]; X CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0); X } X break; X X case ACMD_STATE: X /* X * Arguments: X * 0 - status bits to check X */ X switch(async_state) { X case 0: X CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); X case 1: X if ((async_ret & async_arg[0]) != 0) goto complete; X async_state = 0; X if (++async_retries == 10) { X printf("ft0: acmd_state exceeded retry count\n"); X goto complete; X } X timeout(ft_timeout, 0, hz/4); X break; X } X break; X X case ACMD_SEEKSTS: X /* X * Arguments: X * 0 - command to perform X * 1 - status bits to check X * 2 - (optional) seconds to wait until completion X */ X switch(async_state) { X case 0: X cmd = async_arg[0]; X async_retries = (async_arg[2]) ? (async_arg[2]*4) : 10; X CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0); X case 1: X CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); X case 2: X if ((async_ret & async_arg[1]) != 0) goto complete; X if (--async_retries == 0) { X printf("ft0: acmd_seeksts retries exceeded\n"); X goto complete; X } X async_state = 1; X timeout(ft_timeout, 0, hz/4); X break; X } X break; X X case ACMD_READID: X /* X * Arguments: (none) X */ X switch(async_state) { X case 0: X if (!ftmoving) { X CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); X /* NOTREACHED */ X } X async_state = 1; X out_fdc(0x4a); /* READ_ID */ X out_fdc(0); X break; X case 1: X for (i = 0; i < 7; i++) rid[i] = in_fdc(); X async_ret = (rid[3]*ftg->g_fdtrk) + (rid[4]*ftg->g_fdside) + rid[5] - 1; X DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n", X rid[0], rid[1], rid[2], rid[3], rid[4], rid[5], async_ret)); X if ((rid[0] & 0xc0) == 0x40) { X if (++errcnt >= 10) { X DPRT(("ft0: acmd_readid errcnt exceeded\n")); X async_ret = ftlastpos; X errcnt = 0; X goto complete; X } X if (errcnt > 2) { X ftmoving = 0; X CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); X } X DPRT(("readid retry...\n")); X async_state = 0; X goto restate; X } X if ((async_ret % ftg->g_blktrk) == (ftg->g_blktrk-1)) { X DPRT(("acmd_readid detected last block on track\n")); X retpos = async_ret; X CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0); X /* NOTREACHED */ X } X ftlastpos = async_ret; X errcnt = 0; X goto complete; X /* NOTREACHED */ X case 2: X CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); X case 3: X ftmoving = 0; X async_ret = retpos+1; X goto complete; X case 4: X CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); X case 5: X ftmoving = 1; X async_state = 0; X timeout(ft_timeout, 0, hz/4); X break; X } X break; X X case ACMD_RUNBLK: X /* X * Arguments: X * 0 - block number I/O will be performed on X * X * modifies: curpos X */ X switch (async_state) { X case 0: X wanttrk = async_arg[0] / ftg->g_blktrk; X wantblk = async_arg[0] % ftg->g_blktrk; X wantdir = wanttrk & 1; X ftmoving = 0; X CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); X case 1: X curtrk = wanttrk; X curdir = curtrk & 1; X DPRT(("Changing to track %d\n", wanttrk)); X CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0); X case 2: X cmd = wanttrk+2; X CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); X case 3: X CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); X case 4: X ftlaststs = async_ret; X if (wantblk == 0) { X curblk = 0; X cmd = (wantdir) ? QC_SEEKEND : QC_SEEKSTART; X CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90, 0, 0); X } X if (ftlaststs & QS_BOT) { X DPRT(("Tape is at BOT\n")); X curblk = (wantdir) ? 4800 : 0; X async_state = 6; X goto restate; X } X if (ftlaststs & QS_EOT) { X DPRT(("Tape is at EOT\n")); X curblk = (wantdir) ? 0 : 4800; X async_state = 6; X goto restate; X } X CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); X case 5: X curtrk = (async_ret+1) / ftg->g_blktrk; X curblk = (async_ret+1) % ftg->g_blktrk; X DPRT(("gotid: curtrk=%d wanttrk=%d curblk=%d wantblk=%d\n", X curtrk, wanttrk, curblk, wantblk)); X if (curtrk != wanttrk) { /* oops! */ X DPRT(("oops!! wrong track!\n")); X CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); X } X async_state = 6; X goto restate; X case 6: X DPRT(("curtrk = %d nextblk = %d\n", curtrk, curblk)); X if (curblk == wantblk) { X ftlastpos = curblk - 1; X async_ret = ftlastpos; X if (ftmoving) goto complete; X CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0, 0, 0); X } X if (curblk > wantblk) { /* passed it */ X ftmoving = 0; X CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); X } X if ((wantblk - curblk) <= 96) { /* approaching it */ X CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); X } X /* way up ahead */ X ftmoving = 0; X CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); X break; X case 7: X ftmoving = 1; X CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); X break; X case 8: X async_state = 9; X timeout(ft_timeout, 0, hz/4); X break; X case 9: X goto complete; X case 10: X curdiff = ((curblk - wantblk) / QCV_BLKSEG) + 2; X if (curdiff >= ftg->g_segtrk) curdiff = ftg->g_segtrk - 1; X DPRT(("pos %d past %d, reverse %d\n", curblk, wantblk, curdiff)); X CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0, 0, 0); X case 11: X DPRT(("reverse 1 done\n")); X CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0); X case 12: X DPRT(("reverse 2 done\n")); X CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0); X case 13: X CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); X case 14: X curdiff = ((wantblk - curblk) / QCV_BLKSEG) - 2; X if (curdiff < 0) curdiff = 0; X DPRT(("pos %d before %d, forward %d\n", curblk, wantblk, curdiff)); X CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0, 0, 0); X case 15: X DPRT(("forward 1 done\n")); X CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0); X case 16: X DPRT(("forward 2 done\n")); X CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0); X } X break; X } X X return; X Xcomplete: X if (astk_depth) { X astk_depth--; X async_retries = astk[astk_depth].over_retries; X async_func = astk[astk_depth].over_func; X async_state = astk[astk_depth].over_state; X for(i = 0; i < 5; i++) X async_arg[i] = astk[astk_depth].over_arg[i]; X goto restate; X } X async_func = ACMD_NONE; X async_state = 0; X switch (ftio_sts) { X case FTIO_READY: X async_req(2); X break; X case FTIO_READING: X async_read(2); X break; X case FTIO_WRITING: X async_write(2); X break; X default: X printf("ft0: bad async_cmd ending I/O state!\n"); X break; X } X} X X X/* X * Entry point for the async request processor. X */ Xasync_req(int from) X{ X SegReq *sp; X static int over_async; X static int endoftrk = 0; X int cmd; X X if (from == 2) arq_state = over_async; X Xrestate: X switch (arq_state) { X case 0: /* Process segment */ X ftio_sts = curseg->reqtype; X if (ftio_sts == FTIO_WRITING) X async_write(from); X else X async_read(from); X if (ftio_sts != FTIO_READY) return; X X /* Swap buffered and current segment */ X sp = curseg; X curseg = bufseg; X bufseg = sp; X X wakeup(&ftsem.buff_avail); X X /* Detect end of track */ X endoftrk = ((ftxblk / QCV_BLKSEG) % ftg->g_segtrk) == 0; X X if (endoftrk) { X ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0); X } X arq_state = 1; X goto restate; X X case 1: /* Next request */ X if (curseg->reqtype != FTIO_READY) { X bufseg->reqtype = FTIO_READY; X curseg->reqcrc = 0; X arq_state = ard_state = awr_state = 0; X ftxblk = curseg->reqblk; X ftxcnt = 0; X ftxptr = curseg->buff; X DPRT(("Processing I/O reqblk = %d\n", curseg->reqblk)); X goto restate; X } X if (bufseg->reqtype == FTIO_READING && endoftrk == 0) { X bufseg->reqtype = FTIO_READY; X curseg->reqtype = FTIO_RDAHEAD; X curseg->reqblk = ftxblk; X curseg->reqcrc = 0; X curseg->reqcan = 0; X bzero(curseg->buff, QCV_SEGSIZE); X arq_state = ard_state = awr_state = 0; X ftxblk = curseg->reqblk; X ftxcnt = 0; X ftxptr = curseg->buff; X DPRT(("Processing readahead reqblk = %d\n", curseg->reqblk)); X goto restate; X } X X bufseg->reqtype = FTIO_READY; X X if (ftmoving) { X DPRT(("No more I/O.. Stopping.\n")); X ACMD_FUNC(7, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); X break; X } X arq_state = 7; X goto restate; X X case 2: /* End of track */ X ftmoving = 0; X ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); X break; X X case 3: X DPRT(("async_req seek head to track %d\n", ftxblk / ftg->g_blktrk)); X ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0); X break; X X case 4: X cmd = (ftxblk / ftg->g_blktrk) + 2; X ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); X break; X X case 5: X ftmoving = 1; X ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); X break; X X case 6: X arq_state = 1; X timeout(ft_timeout, 0, hz/4); X break; X X case 7: X ftmoving = 0; X X /* Check one last time to see if a request came in. */ X if (curseg->reqtype != FTIO_READY) { X DPRT(("async_req: Never say no!\n")); X arq_state = 1; X goto restate; X } X X /* Time to rest. */ X ftactive = 0; X wakeup(&ftsem.iosts_change); /* wakeup those who want an i/o chg */ X break; X } X} X X/* X * Entry for async read. X */ Xasync_read(int from) X{ X unsigned long paddr; X int i, cmd, newcn, rddta[7]; X int st0, pcn; X static int over_async; X X if (from == 2) ard_state = over_async; X Xrestate: X#if 0 X DPRT(("async_read: state: %d from = %d\n", ard_state, from)); X#endif X switch (ard_state) { X case 0: /* Start off */ X /* If tape is not at desired position, stop and locate */ X if (ftlastpos != (ftxblk-1)) { X DPRT(("ft0: position unknown: lastpos:%d ftxblk:%d\n", X ftlastpos, ftxblk)); X ACMD_FUNC(1, ACMD_RUNBLK, ftxblk, 0, 0, 0, 0); X } X X /* Tape is in position but stopped. */ X if (!ftmoving) { X DPRT(("async_read ******STARTING TAPE\n")); X ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); X } X ard_state = 1; X goto restate; X X case 1: /* Start DMA */ X /* Tape is now moving and in position-- start DMA now! */ X isa_dmastart(B_READ, ftxptr, QCV_BLKSIZE, 2); X out_fdc(0x66); /* read */ X out_fdc(0x00); /* unit */ X out_fdc((ftxblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ X out_fdc(ftxblk / ftg->g_fdside); /* head */ X out_fdc((ftxblk % ftg->g_fdtrk) + 1); /* sector */ X out_fdc(0x03); /* 1K sectors */ X out_fdc((ftxblk % ftg->g_fdtrk) + 1); /* count */ X out_fdc(0x74); /* gap length */ X out_fdc(0xff); /* transfer size */ X ard_state = 2; X break; X X case 2: /* DMA completed */ X /* Check for positional error. */ X for (i = 0; i < 7; i++) rddta[i] = in_fdc(); X isa_dmadone(B_READ, ftxptr, QCV_BLKSIZE, 2); X X i = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5] - 1; X#if 0 X if (rddta[0] || rddta[1] || rddta[2]) { X DPRT(("st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", X rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], X i, ftxblk)); X } X#endif X if ((rddta[0] & 0xc0) == 0x40) { X if (rddta[1] & 0x20) { X#if 0 X DPRT(("ft0: CRC error on block %d\n", ftxblk)); X#endif X curseg->reqcrc |= (1 << ftxcnt); X } else { X /* Probably wrong position */ X ftlastpos = ftxblk; X ard_state = 0; X goto restate; X } X } X#if 0 X DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", X rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], X i, ftxblk)); X#endif X X /* Otherwise, transfer completed okay. */ X ftlastpos = ftxblk; X ftxblk++; X ftxcnt++; X ftxptr += QCV_BLKSIZE; X if (ftxcnt < QCV_BLKSEG && curseg->reqcan == 0) { X ard_state = 0; X goto restate; X } X DPRT(("Read done.. Cancel = %d\n", curseg->reqcan)); X ftio_sts = FTIO_READY; X break; X X case 3: X ftmoving = 1; X ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); X break; X X case 4: X ard_state = 1; X timeout(ft_timeout, 0, hz/4); X break; X X default: X printf("ft0: bad async_read state %d!!\n", ard_state); X break; X } X} X X X/* X * Entry for async write. If from is 0, this came from the interrupt X * routine, if it's 1 then it was a timeout, if it's 2, then an X * async_cmd completed. X */ Xasync_write(int from) X{ X unsigned long paddr; X int i, cmd, newcn, rddta[7]; X int st0, pcn; X static int over_async; X X if (from == 2) awr_state = over_async; X Xrestate: X#if 0 X DPRT(("async_write: state: %d from = %d\n", awr_state, from)); X#endif X switch (awr_state) { X case 0: /* Start off */ X /* If tape is not at desired position, stop and locate */ X if (ftlastpos != (ftxblk-1)) { X DPRT(("ft0: position unknown: lastpos:%d ftxblk:%d\n", X ftlastpos, ftxblk)); X ACMD_FUNC(1, ACMD_RUNBLK, ftxblk, 0, 0, 0, 0); X } X X /* Tape is in position but stopped. */ X if (!ftmoving) { X DPRT(("async_write ******STARTING TAPE\n")); X ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); X } X awr_state = 1; X goto restate; X X case 1: /* Start DMA */ X /* Tape is now moving and in position-- start DMA now! */ X isa_dmastart(B_WRITE, ftxptr, QCV_BLKSIZE, 2); X out_fdc(0x45); /* write */ X out_fdc(0x00); /* unit */ X out_fdc((ftxblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ X out_fdc(ftxblk / ftg->g_fdside); /* head */ X out_fdc((ftxblk % ftg->g_fdtrk) + 1); /* sector */ X out_fdc(0x03); /* 1K sectors */ X out_fdc((ftxblk % ftg->g_fdtrk) + 1); /* count */ X out_fdc(0x74); /* gap length */ X out_fdc(0xff); /* transfer size */ X awr_state = 2; X break; X X case 2: /* DMA completed */ X /* Check for positional error. */ X for (i = 0; i < 7; i++) rddta[i] = in_fdc(); X isa_dmadone(B_WRITE, ftxptr, QCV_BLKSIZE, 2); X X i = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5] - 1; X if (rddta[0] || rddta[1] || rddta[2]) { X DPRT(("st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", X rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], X i, ftxblk)); X } X if ((rddta[0] & 0xc0) == 0x40) { X if (rddta[1] & 0x20) { X DPRT(("ft0: CRC error on block %d\n", ftxblk)); X curseg->reqcrc |= (1 << ftxcnt); X } else { X /* Probably wrong position */ X ftlastpos = ftxblk; X awr_state = 0; X goto restate; X } X } X#if 0 X DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", X rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], X i, ftxblk)); X#endif X isa_dmadone(B_WRITE, ftxptr, QCV_BLKSIZE, 2); X X /* Otherwise, transfer completed okay. */ X ftlastpos = ftxblk; X ftxblk++; X ftxcnt++; X ftxptr += QCV_BLKSIZE; X if (ftxcnt < QCV_BLKSEG) { X awr_state = 0; /* next block */ X goto restate; X } X DPRT(("Write done.\n")); X ftio_sts = FTIO_READY; X break; X X case 3: X ftmoving = 1; X ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); X break; X X case 4: X awr_state = 1; X timeout(ft_timeout, 0, hz/4); X break; X X default: X printf("ft0: bad async_write state %d!!\n", awr_state); X break; X } X} X X X/* X * Interrupt handler for active tape. Bounced off of fdintr(). X */ Xftintr(int unit) X{ X int st0, pcn, i; X X /* I/O segment transfer completed */ X if (ftactive) { X if (async_func != ACMD_NONE) { X async_cmd(); X return(1); X } X#if 0 X DPRT(("Got request interrupt\n")); X#endif X async_req(0); X return(1); X } X X /* Get interrupt status */ X if (ftcmd_wait != FTCMD_READID) { X out_fdc(NE7CMD_SENSEI); X st0 = in_fdc(); X pcn = in_fdc(); X } X X if (ftcmd_wait == FTCMD_NONE || ftsts_wait != FTSTS_SNOOZE) { Xhuh_what: X printf("ft0: unexpected interrupt; st0 = $%02x pcn = %d\n", st0, pcn); X return(1); X } X X switch (ftcmd_wait) { X case FTCMD_RESET: X ftsts_wait = FTSTS_INTERRUPT; X wakeup(&ftsem.wait_chan); X break; X case FTCMD_RECAL: X case FTCMD_SEEK: X if (st0 & 0x20) { /* seek done */ X ftsts_wait = FTSTS_INTERRUPT; X ft_pcn = pcn; X wakeup(&ftsem.wait_chan); X } else X printf("ft0: seek error st0 = $%02x pcn = %d\n", st0, pcn); X break; X case FTCMD_READID: X for (i = 0; i < 7; i++) rid[i] = in_fdc(); X ftsts_wait = FTSTS_INTERRUPT; X wakeup(&ftsem.wait_chan); X break; X X default: X goto huh_what; X } X X return(1); X} X X/* X * Interrupt timeout routine. X */ Xstatic int ft_timeout(int unit) X{ X if (ftactive) { X if (async_func != ACMD_NONE) { X async_cmd(); X return; X } X async_req(1); X } else { X ftsts_wait = FTSTS_TIMEOUT; X wakeup(&ftsem.wait_chan); X } X} X X/* X * Wait for a particular interrupt to occur. ftintr() will wake us up X * if it sees what we want. Otherwise, time out and return error. X * Should always disable ints before trigger is sent and calling here. X */ Xftintr_wait(int cmd, int ticks) X{ X int retries, st0, pcn; X X ftcmd_wait = cmd; X ftsts_wait = FTSTS_SNOOZE; X X /* At attach time, we can't rely on having interrupts serviced */ X if (ft_attaching) { X switch (cmd) { X case FTCMD_RESET: X DELAY(2000); X ftsts_wait = FTSTS_INTERRUPT; X goto intrdone; X case FTCMD_RECAL: X case FTCMD_SEEK: X for (retries = 0; retries < 10000; retries++) { X out_fdc(NE7CMD_SENSEI); X st0 = in_fdc(); X pcn = in_fdc(); X if (st0 & 0x20) { X ftsts_wait = FTSTS_INTERRUPT; X ft_pcn = pcn; X goto intrdone; X } X DELAY(2000); X } X break; X } X ftsts_wait = FTSTS_TIMEOUT; X goto intrdone; X } X X if (ticks) timeout(ft_timeout, 0, ticks); X sleep(&ftsem.wait_chan, FTPRI); X Xintrdone: X if (ftsts_wait == FTSTS_TIMEOUT) { /* timeout */ X if (ftcmd_wait != FTCMD_RESET) X printf("ft0: timeout on command %d\n", ftcmd_wait); X ftcmd_wait = FTCMD_NONE; X ftsts_wait = FTSTS_NONE; X return(1); X } X X /* got interrupt */ X if (ft_attaching == 0 && ticks) untimeout(ft_timeout, 0); X ftcmd_wait = FTCMD_NONE; X ftsts_wait = FTSTS_NONE; X return(0); X} X X/* X * Recalibrate tape drive. Parameter totape is true, if we should X * recalibrate to tape drive settings. X */ Xint tape_recal(int totape) X{ X int s; X X DPRT(("tape_recal start\n")); X X out_fdc(NE7CMD_SPECIFY); X out_fdc((totape) ? 0xAD : 0xDF); X out_fdc(0x02); X X s = splbio(); X out_fdc(NE7CMD_RECAL); X out_fdc(0x00); X if (ftintr_wait(FTCMD_RECAL, 100)) { X splx(s); X printf("ft0: recalibrate timeout\n"); X return(1); X } X splx(s); X X out_fdc(NE7CMD_SPECIFY); X out_fdc((totape) ? 0xFD : 0xDF); X out_fdc(0x02); X X DPRT(("tape_recal end\n")); X return(0); X} X Xint state_timeout() X{ X wakeup(&ftsem.long_delay); X} X X/* X * Wait for a particular tape status to be met. If all is TRUE, then X * all states must be met, otherwise any state can be met. X */ Xint tape_state(int all, int mask, int seconds) X{ X int r, tries, maxtries; X X maxtries = (seconds) ? (4 * seconds) : 1; X for (tries = 0; tries < maxtries; tries++) { X r = tape_status(); X if (r >= 0) { X if (all && (r & mask) == mask) return(r); X if ((r & mask) != 0) return(r); X } X if (seconds) { X timeout(state_timeout, 0, hz/4); X sleep(&ftsem.long_delay, FTPRI); X } X } X DPRT(("ft0: tape_state failed on mask=$%02x maxtries=%d\n", mask, maxtries)); X return(-1); X} X X/* X * Send a QIC command to tape drive, wait for completion. X */ Xint tape_cmd(int cmd) X{ X int newcn; X int retries = 0; X int s; X X DPRT(("===> tape_cmd: %d\n",cmd)); X newcn = (cmd <= ft_pcn) ? ft_pcn - cmd : ft_pcn + cmd; X Xretry: X X /* Perform seek */ X s = splbio(); X out_fdc(NE7CMD_SEEK); X out_fdc(0x00); X out_fdc(newcn); X X if (ftintr_wait(FTCMD_SEEK, 250)) { X DPRT(("ft0: tape_cmd seek timeout\n")); Xredo: X splx(s); X if (++retries < 5) goto retry; X printf("ft0: tape_cmd seek failed!\n"); X return(1); X } X splx(s); X X if (ft_pcn != newcn) { X printf("ft0: bad seek in tape_cmd; pcn = %d newcn = %d\n", ft_pcn, newcn); X goto redo; X } X DELAY(2500); X return(0); X} X X/* X * Return status of tape drive X */ Xtape_status(void) X{ X int r, err, tries; X X for (r = -1, tries = 0; r < 0 && tries < 3; tries++) X r = qic_status(QC_STATUS, 8); X if (tries == 3) return(-1); X DPRT(("tape_status got $%04x\n",r)); X ftlaststs = r; X X if (r & (QS_ERROR|QS_NEWCART)) { X if (r & QS_NEWCART) ftnewcart = 1; X err = qic_status(QC_ERRCODE, 16); X ftlasterr = err; X if ((r & QS_NEWCART) == 0 && err && ft_attaching == 0) { X DPRT(("ft0: QIC error %d occurred on cmd %d\n", err & 0xff, err >> 8)); X } X r = qic_status(QC_STATUS, 8); X ftlaststs = r; X DPRT(("tape_status got error code $%04x new sts = $%02x\n",err,r)); X } X ftrdonly = (r & QS_RDONLY); X return(r); X} X X/* X * Transfer control to tape drive. X */ Xtape_start(void) X{ X int s; X X DPRT(("tape_start start\n")); X fdc_mode = FDC_TAPE_MODE; X X /* reset, dma disable */ X s = splbio(); X outb(fdc+fdout, 0x00); X DELAY(5000); X X /* raise reset, enable DMA */ X outb(fdc+fdout, FDO_FRST | FDO_FDMAEN); X DELAY(10); X X /* set transfer speed */ X outb (fdc+fdctl, FDC_500KBPS); X DELAY(10); X X (void)ftintr_wait(FTCMD_RESET, 500); X splx(s); X tape_recal(1); X DPRT(("tape_start end\n")); X} X X/* X * Transfer control back to floppy disks. X */ Xtape_end(void) X{ X int s; X X DPRT(("tape_end start\n")); X tape_recal(0); X X s = splbio(); X outb (fdc+fdctl, FDC_500KBPS); X DELAY(10); X X outb(fdc+fdout, 0x00); X DELAY(5000); X X outb(fdc+fdout, FDO_FRST | FDO_FDMAEN); X DELAY(10); X X (void)ftintr_wait(FTCMD_RESET, 500); X splx(s); X fdc_mode = FDC_DISK_MODE; X DPRT(("tape_end end\n")); X} X X/* X * Get the geometry of the tape currently in the drive. X */ Xint ftgetgeom() X{ X int r, i, tries; X int cfg, qic80, ext; X int sts, fmt, len; X X r = tape_status(); X X /* XXX fix me when format mode is finished */ X if ((r & QS_CART) == 0 || (r & QS_FMTOK) == 0) { X DPRT(("ftgetgeom: no cart or not formatted 0x%04x\n",r)); X ftg = NULL; X ftnewcart = 1; X return(0); X } X X /* Report drive configuration */ X for (cfg = -1, tries = 0; cfg < 0 && tries < 3; tries++) X cfg = qic_status(QC_CONFIG, 8); X if (tries == 3) { X DPRT(("ftgetgeom report config failed\n")); X ftg = NULL; X return(-1); X } X DPRT(("ftgetgeom report config got $%04x\n", cfg)); X ftlastcfg = cfg; X X qic80 = cfg & QCF_QIC80; X ext = cfg & QCF_EXTRA; X X/* X * XXX - This doesn't seem to work on my Colorado Jumbo 250... X * if it works on your drive, I'd sure like to hear about it. X */ X#if 0 X /* Report drive status */ X for (sts = -1, tries = 0; sts < 0 && tries < 3; tries++) X sts = qic_status(QC_TSTATUS, 8); X if (tries == 3) { X DPRT(("ftgetgeom report tape status failed\n")); X ftg = NULL; X return(-1); X } X DPRT(("ftgetgeom report tape status got $%04x\n", sts)); X#else X /* X * XXX - Forge a fake tape status based upon the returned X * configuration, since the above command or code is broken X * for my drive and probably other older drives. X */ X sts = 0; X sts = (qic80) ? QTS_QIC80 : QTS_QIC40; X sts |= (ext) ? QTS_LEN2 : QTS_LEN1; X#endif X X fmt = sts & QTS_FMMASK; X len = (sts & QTS_LNMASK) >> 4; X X if (fmt > QCV_NFMT) { X ftg = NULL; X printf("ft0: unsupported tape format\n"); X return(-1); X } X if (len > QCV_NLEN) { X ftg = NULL; X printf("ft0: unsupported tape length\n"); X return(-1); X } X X /* Look up geometry in the table */ X for (i = 1; i < NGEOM; i++) X if (ftgtbl[i].g_fmtno == fmt && ftgtbl[i].g_lenno == len) break; X if (i == NGEOM) { X printf("ft0: unknown tape geometry\n"); X ftg = NULL; X return(-1); X } X ftg = &ftgtbl[i]; X if (!ftg->g_trktape) { X printf("ft0: unsupported format %s w/len %s\n", ftg->g_fmtdesc, ftg->g_lendesc); X ftg = NULL; X return(-1); X } X DPRT(("Tape format is %s, length is %s\n", ftg->g_fmtdesc, ftg->g_lendesc)); X ftnewcart = 0; X return(0); X} X X/* X * Switch between tape/floppy. This will send the tape enable/disable X * codes for this drive's manufacturer. It will also install/de-install X * the tape's device driver functions. X */ Xint set_fdcmode(int newmode) X{ X static int havebufs = 0; X void *buf; X int r, s, i; X SegReq *sp; X X if (newmode == FDC_TAPE_MODE) { X if (fdc_mode == FDC_TAPE_MODE) return(EBUSY); X X /* Wake up the tape drive */ X switch (ft_type) { X case FT_NONE: X return(ENXIO); X case FT_COLORADO: X tape_start(); X if (tape_cmd(QC_COL_ENABLE1)) { X tape_end(); X return(EIO); X } X if (tape_cmd(QC_COL_ENABLE2)) { X tape_end(); X return(EIO); X } X break; X case FT_MOUNTAIN: X tape_start(); X if (tape_cmd(QC_MTN_ENABLE1)) { X tape_end(); X return(EIO); X } X if (tape_cmd(QC_MTN_ENABLE2)) { X tape_end(); X return(EIO); X } X break; X default: X printf("ft0: bad tape type\n"); X return(ENXIO); X } X if (tape_status() < 0) { X tape_cmd((ft_type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE); X tape_end(); X return(EIO); X } X X /* Grab buffers from memory. */ X if (!havebufs) { X curseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT); X if (curseg == NULL) { X printf("ft0: not enough memory for buffers\n"); X return(ENOMEM); X } X bufseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT); X if (bufseg == NULL) { X free(curseg, M_DEVBUF); X printf("ft0: not enough memory for buffers\n"); X return(ENOMEM); X } X havebufs = 1; X } X X /* Tell drivers the fd major is now tape! Can't fail now. */ X s = splhigh(); X X /* Save old blk/chr device entries, install tape entries */ X for (bi = 0; bi < nblkdev; bi++) X if ((void *)bdevsw[bi].d_open == (void *)Fdopen) break; X for (ci = 0; ci < nchrdev; ci++) X if ((void *)cdevsw[ci].d_open == (void *)Fdopen) break; X X /* This shouldn't happen! */ X if (bi == nblkdev || ci == nchrdev) { X splx(s); X tape_cmd((ft_type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE); X tape_end(); X printf("ft0: floppy device entry not found!\n"); X return(ENODEV); X } X bcopy(&bdevsw[bi], &fdbdevsw, sizeof(struct bdevsw)); X bcopy(&cdevsw[ci], &fdcdevsw, sizeof(struct cdevsw)); X bcopy(&ftbdevsw, &bdevsw[bi], sizeof(struct bdevsw)); X bcopy(&ftcdevsw, &cdevsw[ci], sizeof(struct cdevsw)); X splx(s); X X curseg->reqtype = FTIO_READY; X bufseg->reqtype = FTIO_READY; X ftio_sts = FTIO_READY; /* tape drive is ready */ X ftactive = 0; /* interrupt driver not active */ X ftmoving = 0; /* tape not moving */ X ftrdonly = 0; /* tape read only */ X ftnewcart = 0; /* a new cart was inserted */ X ftlastpos = -1; /* tape is rewound */ X tape_state(0, QS_READY, 60); X tape_cmd(QC_RATE); X tape_cmd(QCF_RT500+2); /* 500K bps */ X tape_state(0, QS_READY, 60); X ftmode = FTM_PRIMARY; X tape_cmd(QC_PRIMARY); /* Make sure we're in primary mode */ X tape_state(0, QS_READY, 60); X ftg = NULL; /* No geometry yet */ X ftgetgeom(); /* Get tape geometry */ X ftreq_rewind(); /* Make sure tape is rewound */ X } else { X if (fdc_mode == FDC_DISK_MODE) return(0); X tape_cmd((ft_type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE); X X /* Restore floppy device handlers */ X s = splhigh(); X bcopy(&fdbdevsw, &bdevsw[bi], sizeof(struct bdevsw)); X bcopy(&fdcdevsw, &cdevsw[ci], sizeof(struct cdevsw)); X splx(s); X ftnewcart = 0; /* clear new cartridge */ X tape_end(); X havebufs = 0; X free(curseg, M_DEVBUF); X free(bufseg, M_DEVBUF); X } X return(0); X} X X X/* X * Perform a QIC status function. X */ Xint qic_status(int cmd, int nbits) X{ X int st3, val, r, i; X X if (tape_cmd(cmd)) { X printf("ft0: QIC status timeout\n"); X return(-1); X } X X /* Sense drive status */ X out_fdc(NE7CMD_SENSED); X out_fdc(0x00); X st3 = in_fdc(); X X if ((st3 & 0x10) == 0) { /* track 0 */ X DPRT(("qic_status has dead drive... st3 = $%02x\n", st3)); X if (!ft_attaching) X printf("ft0: tape drive seems dead; st3 = $%02x\n", st3); X return(-1); X } X X for (i = r = 0; i <= nbits; i++) { X if (tape_cmd(QC_NEXTBIT)) { X printf("ft0: QIC status bit timed out on %d\n", i); X return(-1); X } X X out_fdc(NE7CMD_SENSED); X out_fdc(0x00); X st3 = in_fdc(); X if (st3 < 0) { X printf("ft0: controller timed out on bit %d r=$%02x\n",i,r); X return(-1); X } X X r >>= 1; X if (i < nbits) X r |= ((st3 & 0x10) ? 1 : 0) << nbits; X else if ((st3 & 0x10) == 0) { X printf("ft0: qic status stop bit missing at %d, st3=$%02x r=$%04x\n",i,st3,r); X return(-1); X } X } X X DPRT(("qic_status returned $%02x\n", r)); X return(r); X} X X/* X * Open tape drive for use. Bounced off of Fdopen if tape minor is X * detected. X */ Xint ftopen(dev_t dev, int oflags, int devtype, struct proc *p) X{ X int unit = (minor(dev) >> 3) & 3; X X if (unit < NFD) return(EBUSY); /* tape open -- reject others */ X return (set_fdcmode(FDC_TAPE_MODE)); /* try to switch to tape */ X} X X/* X * Close tape. If a floppy disk close request comes in, pretend it's ok, X * otherwise we return floppy controller to disk mode. X */ Xint ftclose(dev_t dev, int fflag, int devtype, struct proc *p) X{ X int unit = (minor(dev) >> 3) & 3; X int s; X SegReq *sp; X X if (unit < NFD) return(0); /* If not tape, close ok. */ X X /* Wait for any remaining I/O activity to complete. */ X if (curseg->reqtype == FTIO_RDAHEAD) curseg->reqcan = 1; X while (ftactive) sleep(&ftsem.iosts_change, FTPRI); X X ftmode = FTM_PRIMARY; X tape_cmd(QC_PRIMARY); X tape_state(0, QS_READY, 60); X ftreq_rewind(); X return(set_fdcmode(FDC_DISK_MODE)); /* Otherwise, close tape */ X} X X/* X * Perform strategy on a given buffer (not!). X */ Xint ftstrategy(struct buf *bp) X{ X return(EINVAL); X} X X/* Read or write a segment. */ Xint ftreq_rw(int cmd, QIC_Segment *sr, struct proc *p) X{ X int r, i, j; X SegReq *sp; X int s; X long blk, bad; X unsigned char *cp, *cp2; X X if (!ftactive) { X r = tape_status(); X if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */ X if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */ X tape_state(0, QS_READY, 90); X } X X if (ftg == NULL || ftnewcart) { X while (ftactive) sleep(&ftsem.iosts_change, FTPRI); X if (ftgetgeom() < 0) return(ENXIO); X } X X /* Write not allowed on a read-only tape. */ X if (cmd == QIOWRITE && ftrdonly) return(EROFS); X X /* Quick check of request and buffer. */ X if (sr == NULL || sr->sg_data == NULL) return(EINVAL); X if (sr->sg_trk >= ftg->g_trktape || X sr->sg_seg >= ftg->g_segtrk) return(EINVAL); X blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG; X X if (cmd == QIOREAD) { X s = splbio(); X if (curseg->reqtype == FTIO_RDAHEAD) { X if (blk == curseg->reqblk) { X sp = curseg; X sp->reqtype = FTIO_READING; X sp->reqbad = sr->sg_badmap; X splx(s); X goto rdwait; X } else X curseg->reqcan = 1; /* XXX cancel rdahead */ X } X splx(s); X X /* Wait until we're ready. */ X while (ftactive) sleep(&ftsem.iosts_change, FTPRI); X X /* Set up a new read request. */ X sp = curseg; X sp->reqcrc = 0; X sp->reqbad = sr->sg_badmap; X sp->reqblk = blk; X sp->reqcan = 0; X sp->reqtype = FTIO_READING; X X /* Start the read request off. */ X DPRT(("Starting read I/O chain\n")); X arq_state = ard_state = awr_state = 0; X ftxblk = sp->reqblk; X ftxcnt = 0; X ftxptr = sp->buff; X ftactive = 1; X timeout(ft_timeout, 0, 1); X Xrdwait: X sleep(&ftsem.buff_avail, FTPRI); X bad = sp->reqbad; X sr->sg_crcmap = sp->reqcrc & ~bad; X X /* Copy out segment and discard bad mapped blocks. */ X cp = sp->buff; cp2 = sr->sg_data; X for (i = 0; i < QCV_BLKSEG; cp += QCV_BLKSIZE, i++) { X if (bad & (1 << i)) continue; X copyout(cp, cp2, QCV_BLKSIZE); X cp2 += QCV_BLKSIZE; X } X } else { X if (curseg->reqtype == FTIO_RDAHEAD) { X curseg->reqcan = 1; /* XXX cancel rdahead */ X while (ftactive) sleep(&ftsem.iosts_change, FTPRI); X } X X /* Sleep until a buffer becomes available. */ X while (bufseg->reqtype != FTIO_READY) sleep(&ftsem.buff_avail, FTPRI); X s = splbio(); X sp = (curseg->reqtype == FTIO_READY) ? curseg : bufseg; X splx(s); X X /* Copy in segment and expand bad blocks. */ X bad = sr->sg_badmap; X cp = sr->sg_data; cp2 = sp->buff; X for (i = 0; i < QCV_BLKSEG; cp2 += QCV_BLKSIZE, i++) { X if (bad & (1 << i)) continue; X copyin(cp, cp2, QCV_BLKSIZE); X cp += QCV_BLKSIZE; X } X X sp->reqblk = blk; X sp->reqcan = 0; X s = splbio(); X sp->reqtype = FTIO_WRITING; X X if (!ftactive) { X DPRT(("Starting write I/O chain\n")); X arq_state = ard_state = awr_state = 0; X ftxblk = sp->reqblk; X ftxcnt = 0; X ftxptr = sp->buff; X ftactive = 1; X timeout(ft_timeout, 0, 1); X } X splx(s); X } X return(0); X} X X X/* Rewind to beginning of tape */ Xint ftreq_rewind() X{ X if (curseg->reqtype == FTIO_RDAHEAD) { X curseg->reqcan = 1; /* XXX cancel rdahead */ X while (ftactive) sleep(&ftsem.iosts_change, FTPRI); X } X if (ftactive) return(ENXIO); X X tape_cmd(QC_STOP); X tape_state(0, QS_READY, 90); X tape_cmd(QC_SEEKSTART); X tape_state(0, QS_READY, 90); X tape_cmd(QC_SEEKTRACK); X tape_cmd(2); X tape_state(0, QS_READY, 90); X ftlastpos = -1; X ftmoving = 0; X return(0); X} X X/* Move to logical beginning or end of track */ Xint ftreq_trkpos(int req) X{ X int curtrk, r, cmd; X X if (curseg->reqtype == FTIO_RDAHEAD) { X curseg->reqcan = 1; /* XXX cancel rdahead */ X while (ftactive) sleep(&ftsem.iosts_change, FTPRI); X } X if (ftactive) return(ENXIO); X X tape_cmd(QC_STOP); X tape_state(0, QS_READY, 90); X X r = tape_status(); X if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */ X if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */ X X if (ftg == NULL || ftnewcart) { X if (ftgetgeom() < 0) return(ENXIO); X } X X curtrk = (ftlastpos < 0) ? 0 : ftlastpos / ftg->g_blktrk; X if (req == QIOBOT) X cmd = (curtrk & 1) ? QC_SEEKEND : QC_SEEKSTART; X else X cmd = (curtrk & 1) ? QC_SEEKSTART : QC_SEEKEND; X tape_cmd(cmd); X tape_state(0, QS_READY, 90); X return(0); X} X X/* Seek tape head to a particular track. */ Xint ftreq_trkset(int *trk) X{ X int curtrk, r, cmd; X X if (curseg->reqtype == FTIO_RDAHEAD) { X curseg->reqcan = 1; /* XXX cancel rdahead */ X while (ftactive) sleep(&ftsem.iosts_change, FTPRI); X } X if (ftactive) return(ENXIO); X X tape_cmd(QC_STOP); X tape_state(0, QS_READY, 90); X X r = tape_status(); X if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */ X if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */ X if (ftg == NULL || ftnewcart) { X if (ftgetgeom() < 0) return(ENXIO); X } X X tape_cmd(QC_SEEKTRACK); X tape_cmd(*trk + 2); X tape_state(0, QS_READY, 90); X return(0); X} X X/* Start tape moving forward. */ Xint ftreq_lfwd() X{ X if (ftactive) return(ENXIO); X tape_cmd(QC_STOP); X tape_state(0, QS_READY, 90); X tape_cmd(QC_FORWARD); X return(0); X} X X/* Stop the tape */ Xint ftreq_stop() X{ X if (ftactive) return(ENXIO); X tape_cmd(QC_STOP); X tape_state(0, QS_READY, 90); X return(0); X} X X/* Set the particular mode the drive should be in. */ Xint ftreq_setmode(int cmd) X{ X int r; X X if (curseg->reqtype == FTIO_RDAHEAD) { X curseg->reqcan = 1; /* XXX cancel rdahead */ X while (ftactive) sleep(&ftsem.iosts_change, FTPRI); X } X if (ftactive) return(ENXIO); X r = tape_status(); X X switch(cmd) { X case QIOPRIMARY: X ftmode = FTM_PRIMARY; X tape_cmd(QC_PRIMARY); X break; X case QIOFORMAT: X if (r & QS_RDONLY) return(ENXIO); X if ((r & QS_BOT) == 0) return(ENXIO); X tape_cmd(QC_FORMAT); X break; X case QIOVERIFY: X if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */ X tape_cmd(QC_VERIFY); X break; X } X tape_state(0, QS_READY, 60); X return(0); X} X X/* Return drive status bits */ Xint ftreq_status(int cmd, int *sts, struct proc *p) X{ X if (ftactive) X *sts = ftlaststs; X else X *sts = tape_status(); X return(0); X} X X/* Return drive configuration bits */ Xint ftreq_config(int cmd, int *cfg, struct proc *p) X{ X int r, tries; X X if (ftactive) X r = ftlastcfg; X else { X for (r = -1, tries = 0; r < 0 && tries < 3; tries++) X r = qic_status(QC_CONFIG, 8); X if (tries == 3) return(ENXIO); X } X *cfg = r; X return(0); X} X X/* Return current tape's geometry. */ Xint ftreq_geom(QIC_Geom *g) X{ X if (curseg->reqtype == FTIO_RDAHEAD) { X curseg->reqcan = 1; /* XXX cancel rdahead */ X while (ftactive) sleep(&ftsem.iosts_change, FTPRI); X } X if (ftactive) return(ENXIO); X if (ftg == NULL && ftgetgeom() < 0) return(ENXIO); X bcopy(ftg, g, sizeof(QIC_Geom)); X return(0); X} X X/* X * I/O functions. X */ Xint ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) X{ X switch(cmd) { X case QIOREAD: /* Request reading a segment from tape. */ X case QIOWRITE: /* Request writing a segment to tape. */ X return(ftreq_rw(cmd, (QIC_Segment *)data, p)); X X case QIOREWIND: /* Rewind tape. */ X return(ftreq_rewind()); X X case QIOBOT: /* Seek to logical beginning of track. */ X case QIOEOT: /* Seek to logical end of track. */ X return(ftreq_trkpos(cmd)); X X case QIOTRACK: /* Seek tape head to specified track. */ X return(ftreq_trkset((int *)data)); X X case QIOSEEKLP: /* Seek load point. */ X goto badreq; X X case QIOFORWARD: /* Move tape in logical forward direction. */ X return(ftreq_lfwd()); X X case QIOSTOP: /* Causes tape to stop. */ X return(ftreq_stop()); X X case QIOPRIMARY: /* Enter primary mode. */ X case QIOFORMAT: /* Enter format mode. */ X case QIOVERIFY: /* Enter verify mode. */ X return(ftreq_setmode(cmd)); X X case QIOWRREF: /* Write reference burst. */ X goto badreq; X X case QIOSTATUS: /* Get drive status. */ X return(ftreq_status(cmd, (int *)data, p)); X X case QIOCONFIG: /* Get tape configuration. */ X return(ftreq_config(cmd, (int *)data, p)); X X case QIOGEOM: X return(ftreq_geom((QIC_Geom *)data)); X X } Xbadreq: X printf("ft0: unknown ioctl() request\n"); X return(ENXIO); X} X Xint ftdump(dev_t dev) X{ X} X Xint ftsize(dev_t dev) X{ X} X#endif END-of-driver/ft.c exit