diff options
Diffstat (limited to 'sys/i386/isa/ft.c')
-rw-r--r-- | sys/i386/isa/ft.c | 1188 |
1 files changed, 821 insertions, 367 deletions
diff --git a/sys/i386/isa/ft.c b/sys/i386/isa/ft.c index 09bc127090aa..1204613bcd8e 100644 --- a/sys/i386/isa/ft.c +++ b/sys/i386/isa/ft.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1993 Steve Gerakines + * Copyright (c) 1993, 1994 Steve Gerakines * * This is freely redistributable software. You may do anything you * wish with it, so long as the above notice stays intact. @@ -17,8 +17,15 @@ * POSSIBILITY OF SUCH DAMAGE. * * ft.c - QIC-40/80 floppy tape driver - * $Id: ft.c,v 1.4 1994/02/14 22:24:28 nate Exp $ + * $Id: ft.c,v 1.7 1994/06/22 05:52:36 jkh Exp $ * + * 06/07/94 v0.9 ++sg + * Tape stuck on segment problem should be gone. Re-wrote buffering + * scheme. Added support for drives that do not automatically perform + * seek load point. Can handle more wakeup types now and should correctly + * report most manufacturer names. Fixed places where unit 0 was being + * sent to the fdc instead of the actual unit number. Added ioctl support + * for an in-core badmap. * * 01/26/94 v0.3b - Jim Babb * Got rid of the hard coded device selection. Moved (some of) the @@ -76,12 +83,13 @@ #include "ftreg.h" /* Enable or disable debugging messages. */ -#define FTDBGALL 0 /* everything */ -/* #define DPRT(a) printf a */ -#define DPRT(a) +#define FTDBGALL 0 /* 1 if you want everything */ +/*#define DPRT(a) printf a */ +#define DPRT(a) /* Constants private to the driver */ #define FTPRI (PRIBIO) /* sleep priority */ +#define FTNBUFF 9 /* 8 for buffering, 1 for header */ /* The following items are needed from the fd driver. */ extern int in_fdc(int); /* read fdc registers */ @@ -92,11 +100,13 @@ extern int hz; /* system clock rate */ /* Type of tape attached */ /* use numbers that don't interfere with the possible floppy types */ #define NO_TYPE 0 /* (same as NO_TYPE in fd.c) */ - /* F_TAPE_TYPE must match value in fd.c */ -#define F_TAPE_TYPE 0x020 /* bit for ft->types to indicate tape */ -#define FT_MOUNTAIN (F_TAPE_TYPE | 1) -#define FT_COLORADO (F_TAPE_TYPE | 2) +/* F_TAPE_TYPE must match value in fd.c */ +#define F_TAPE_TYPE 0x020 /* bit for ft->types to indicate tape */ +#define FT_NONE (F_TAPE_TYPE | 0) /* no method required */ +#define FT_MOUNTAIN (F_TAPE_TYPE | 1) /* mountain */ +#define FT_COLORADO (F_TAPE_TYPE | 2) /* colorado */ +#define FT_INSIGHT (F_TAPE_TYPE | 3) /* insight */ /* Mode FDC is currently in: tape or disk */ enum { FDC_TAPE_MODE, FDC_DISK_MODE }; @@ -150,15 +160,25 @@ QIC_Geom *ftg = NULL; /* Current tape's geometry */ /* * things relating to asynchronous commands */ -static int astk_depth; /* async_cmd stack depth */ static int awr_state; /* state of async write */ static int ard_state; /* state of async read */ static int arq_state; /* state of async request */ static int async_retries; /* retries, one per invocation */ static int async_func; /* function to perform */ static int async_state; /* state current function is at */ -static int async_arg[5]; /* up to 5 arguments for async cmds */ +static int async_arg0; /* up to 3 arguments for async cmds */ +static int async_arg1; /**/ +static int async_arg2; /**/ static int async_ret; /* return value */ +static struct _astk { + int over_func; + int over_state; + int over_retries; + int over_arg0; + int over_arg1; + int over_arg2; +} astk[10]; +static struct _astk *astk_ptr = &astk[0]; /* Pointer to stack position */ /* List of valid async (interrupt driven) tape support functions. */ enum { @@ -172,29 +192,36 @@ enum { }; /* Call another asyncronous command from within async_cmd(). */ -#define CALL_ACMD(r,f,a,b,c,d,e) \ - astk[astk_depth].over_retries = async_retries; \ - astk[astk_depth].over_func = async_func; \ - astk[astk_depth].over_state = (r); \ - for (i = 0; i < 5; i++) \ - astk[astk_depth].over_arg[i] = async_arg[i]; \ +#define CALL_ACMD(r,f,a,b,c) \ + astk_ptr->over_retries = async_retries; \ + astk_ptr->over_func = async_func; \ + astk_ptr->over_state = (r); \ + astk_ptr->over_arg0 = async_arg0; \ + astk_ptr->over_arg1 = async_arg1; \ + astk_ptr->over_arg2 = async_arg2; \ async_func = (f); async_state = 0; async_retries = 0; \ - async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \ - async_arg[3]=(d); async_arg[4]=(e); \ - astk_depth++; \ + async_arg0=(a); async_arg1=(b); async_arg2=(c); \ + astk_ptr++; \ goto restate /* Perform an asyncronous command from outside async_cmd(). */ -#define ACMD_FUNC(r,f,a,b,c,d,e) over_async = (r); astk_depth = 0; \ +#define ACMD_FUNC(r,f,a,b,c) over_async = (r); astk_ptr = &astk[0]; \ async_func = (f); async_state = 0; async_retries = 0; \ - async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \ - async_arg[3]=(d); async_arg[4]=(e); \ + async_arg0=(a); async_arg1=(b); async_arg2=(c); \ async_cmd(ftu); \ return /* Various wait channels */ +static char *wc_buff_avail = "bavail"; +static char *wc_buff_done = "bdone"; +static char *wc_iosts_change = "iochg"; +static char *wc_long_delay = "ldelay"; +static char *wc_intr_wait = "intrw"; +#define ftsleep(wc,to) tsleep((caddr_t)(wc),FTPRI,(wc),(to)) + static struct { int buff_avail; + int buff_done; int iosts_change; int long_delay; int intr_wait; @@ -223,8 +250,17 @@ struct ft_data { unsigned char *xptr; /* pointer to buffer blk to xfer */ int xcnt; /* transfer count */ int xblk; /* block number to transfer */ - SegReq *curseg; /* Current segment to do I/O on */ - SegReq *bufseg; /* Buffered segment to r/w ahead */ + int xseg; /* segment being transferred */ + SegReq *segh; /* Current I/O request */ + SegReq *segt; /* Tail of queued I/O requests */ + SegReq *doneh; /* Completed I/O request queue */ + SegReq *donet; /* Completed I/O request tail */ + SegReq *segfree; /* Free segments */ + SegReq *hdr; /* Current tape header */ + int nsegq; /* Segments on request queue */ + int ndoneq; /* Segments on completed queue */ + int nfreelist; /* Segments on free list */ + /* the next 3 should be defines in 'flags' */ int active; /* TRUE if transfer is active */ int rdonly; /* TRUE if tape is read-only */ @@ -261,85 +297,237 @@ void ftstrategy(struct buf *); int ftioctl(dev_t, int, caddr_t, int, struct proc *); int ftdump(dev_t); int ftsize(dev_t); -static void ft_timeout(caddr_t arg1, int arg2); -void async_cmd(ftu_t); -void async_req(ftu_t, int); -void async_read(ftu_t, int); -void async_write(ftu_t, int); -void tape_start(ftu_t); -void tape_end(ftu_t); -void tape_inactive(ftu_t); +static void ft_timeout(caddr_t, int); +static void async_cmd(ftu_t); +static void async_req(ftu_t, int); +static void async_read(ftu_t, int); +static void async_write(ftu_t, int); +static void tape_start(ftu_t, int); +static void tape_end(ftu_t); +static void tape_inactive(ftu_t); +static int tape_cmd(ftu_t, int); +static int tape_status(ftu_t); +static int qic_status(ftu_t, int, int); +static int ftreq_rewind(ftu_t); +static int ftreq_hwinfo(ftu_t, QIC_HWInfo *); + +/*****************************************************************************/ + + +/* + * Allocate a segment I/O buffer from the free list. + */ +static SegReq * +segio_alloc(ft_p ft) +{ + SegReq *r; + + /* Grab first item from free list */ + if ((r = ft->segfree) != NULL) { + ft->segfree = ft->segfree->next; + ft->nfreelist--; + } + DPRT(("segio_alloc: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); + return(r); +} + + +/* + * Queue a segment I/O request. + */ +static void +segio_queue(ft_p ft, SegReq *sp) +{ + /* Put request on in process queue. */ + if (ft->segt == NULL) + ft->segh = sp; + else + ft->segt->next = sp; + sp->next = NULL; + ft->segt = sp; + ft->nsegq++; + DPRT(("segio_queue: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); +} +/* + * Segment I/O completed, place on correct queue. + */ +static void +segio_done(ft_p ft, SegReq *sp) +{ + /* First remove from current I/O queue */ + ft->segh = sp->next; + if (ft->segh == NULL) ft->segt = NULL; + ft->nsegq--; + + if (sp->reqtype == FTIO_WRITING) { + /* Place on free list */ + sp->next = ft->segfree; + ft->segfree = sp; + ft->nfreelist++; + wakeup((caddr_t)wc_buff_avail); + DPRT(("segio_done: (w) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); + } else { + /* Put on completed I/O queue */ + if (ft->donet == NULL) + ft->doneh = sp; + else + ft->donet->next = sp; + sp->next = NULL; + ft->donet = sp; + ft->ndoneq++; + wakeup((caddr_t)wc_buff_done); + DPRT(("segio_done: (r) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); + } +} +/* + * Take I/O request from finished queue to free queue. + */ +static void +segio_free(ft_p ft, SegReq *sp) +{ + /* First remove from done queue */ + ft->doneh = sp->next; + if (ft->doneh == NULL) ft->donet = NULL; + ft->ndoneq--; + + /* Place on free list */ + sp->next = ft->segfree; + ft->segfree = sp; + ft->nfreelist++; + wakeup((caddr_t)wc_buff_avail); + DPRT(("segio_free: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); +} + /* * Probe/attach floppy tapes. */ -int ftattach(isadev, fdup) +int +ftattach(isadev, fdup) struct isa_device *isadev, *fdup; { - fdcu_t fdcu = isadev->id_unit; /* fdc active unit */ - fdc_p fdc = fdc_data + fdcu; /* pointer to controller structure */ - ftu_t ftu = fdup->id_unit; - ft_p ft; - ftsu_t ftsu = fdup->id_physid; - - if (ftu >= NFT) - return 0; - ft = &ft_data[ftu]; - /* Probe for tape */ - ft->attaching = 1; - ft->type = NO_TYPE; - ft->fdc = fdc; - ft->ftsu = ftsu; - - tape_start(ftu); /* ready controller for tape */ - tape_cmd(ftu, QC_COL_ENABLE1); - tape_cmd(ftu, QC_COL_ENABLE2); - if (tape_status(ftu) >= 0) { - ft->type = FT_COLORADO; - fdc->flags |= FDC_HASFTAPE; - printf(" [%d: ft%d: Colorado tape]", - fdup->id_physid, fdup->id_unit ); - tape_cmd(ftu, QC_COL_DISABLE); - goto out; - } + fdcu_t fdcu = isadev->id_unit; /* fdc active unit */ + fdc_p fdc = fdc_data + fdcu; /* pointer to controller structure */ + ftu_t ftu = fdup->id_unit; + ft_p ft; + ftsu_t ftsu = fdup->id_physid; + QIC_HWInfo hw; + char *manu; + + if (ftu >= NFT) return 0; + ft = &ft_data[ftu]; + + /* Probe for tape */ + ft->attaching = 1; + ft->type = NO_TYPE; + ft->fdc = fdc; + ft->ftsu = ftsu; - tape_start(ftu); /* ready controller for tape */ - tape_cmd(ftu, QC_MTN_ENABLE1); - tape_cmd(ftu, QC_MTN_ENABLE2); - if (tape_status(ftu) >= 0) { - ft->type = FT_MOUNTAIN; - fdc->flags |= FDC_HASFTAPE; - printf(" [%d: ft%d: Mountain tape]", - fdup->id_physid, fdup->id_unit ); - tape_cmd(ftu, QC_MTN_DISABLE); - goto out; - } + /* + * FT_NONE - no method, just do it + */ + tape_start(ftu, 0); + if (tape_status(ftu) >= 0) { + ft->type = FT_NONE; + ftreq_hwinfo(ftu, &hw); + goto out; + } + + /* + * FT_COLORADO - colorado style + */ + tape_start(ftu, 0); + tape_cmd(ftu, QC_COL_ENABLE1); + tape_cmd(ftu, QC_COL_ENABLE2 + ftu); + if (tape_status(ftu) >= 0) { + ft->type = FT_COLORADO; + ftreq_hwinfo(ftu, &hw); + tape_cmd(ftu, QC_COL_DISABLE); + goto out; + } + + /* + * FT_MOUNTAIN - mountain style + */ + tape_start(ftu, 0); + tape_cmd(ftu, QC_MTN_ENABLE1); + tape_cmd(ftu, QC_MTN_ENABLE2); + if (tape_status(ftu) >= 0) { + ft->type = FT_MOUNTAIN; + ftreq_hwinfo(ftu, &hw); + tape_cmd(ftu, QC_MTN_DISABLE); + goto out; + } + + /* + * FT_INSIGHT - insight style + */ + tape_start(ftu, 1); + if (tape_status(ftu) >= 0) { + ft->type = FT_INSIGHT; + ftreq_hwinfo(ftu, &hw); + goto out; + } out: - tape_end(ftu); - ft->attaching = 0; - return(ft->type); + tape_end(ftu); + if (ft->type != NO_TYPE) { + fdc->flags |= FDC_HASFTAPE; + switch(hw.hw_make) { + case 0x0000: + if (ft->type == FT_COLORADO) + manu = "Colorado"; + else if (ft->type == FT_INSIGHT) + manu = "Insight"; + else if (ft->type == FT_MOUNTAIN && hw.hw_model == 0x05) + manu = "Archive"; + else if (ft->type == FT_MOUNTAIN) + manu = "Mountain"; + else + manu = "Unknown"; + break; + case 0x0001: + manu = "Colorado"; + break; + case 0x0005: + if (hw.hw_model >= 0x09) + manu = "Conner"; + else + manu = "Archive"; + break; + case 0x0006: + manu = "Mountain"; + break; + case 0x0007: + manu = "Wangtek"; + break; + case 0x0222: + manu = "IOMega"; + break; + default: + manu = "Unknown"; + break; + } + printf(" [%d: ft%d: %s tape]", fdup->id_physid, fdup->id_unit, manu); + } + ft->attaching = 0; + return(ft->type); } /* * Perform common commands asynchronously. */ -void async_cmd(ftu_t ftu) { +static void +async_cmd(ftu_t ftu) { ft_p ft = &ft_data[ftu]; fdcu_t fdcu = ft->fdc->fdcu; int cmd, i, st0, st3, pcn; static int bitn, retval, retpos, nbits, newcn; - static struct { - int over_func; - int over_state; - int over_retries; - int over_arg[5]; - } astk[15]; static int wanttrk, wantblk, wantdir; static int curpos, curtrk, curblk, curdir, curdiff; static int errcnt = 0; @@ -356,7 +544,7 @@ restate: */ switch (async_state) { case 0: - cmd = async_arg[0]; + cmd = async_arg0; #if FTDBGALL DPRT(("===>async_seek cmd = %d\n", cmd)); #endif @@ -364,7 +552,7 @@ restate: async_state = 1; i = 0; if (out_fdc(fdcu, NE7CMD_SEEK) < 0) i = 1; - if (!i && out_fdc(fdcu, 0x00) < 0) i = 1; + if (!i && out_fdc(fdcu, ftu) < 0) i = 1; if (!i && out_fdc(fdcu, newcn) < 0) i = 1; if (i) { if (++async_retries >= 10) { @@ -399,7 +587,7 @@ restate: DPRT(("ft%d: async_seek error st0 = $%02x pcn = %d\n", ftu, st0, pcn)); #endif - if (async_arg[1]) goto complete; + if (async_arg1) goto complete; async_state = 2; timeout(ft_timeout, (caddr_t)ftu, hz/50); break; @@ -420,14 +608,14 @@ restate: case 0: bitn = 0; retval = 0; - cmd = async_arg[0]; - nbits = async_arg[1]; + cmd = async_arg0; + nbits = async_arg1; DPRT(("async_status got cmd = %d nbits = %d\n", cmd,nbits)); - CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0); /* NOTREACHED */ case 1: out_fdc(fdcu, NE7CMD_SENSED); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); st3 = in_fdc(fdcu); if (st3 < 0) { DPRT(("ft%d: async_status timed out on bit %d r=$%02x\n", @@ -440,7 +628,7 @@ restate: if (bitn >= (nbits+2)) { if ((retval & 1) && (retval & (1 << (nbits+1)))) { async_ret = (retval & ~(1<<(nbits+1))) >> 1; - if (async_arg[0] == QC_STATUS && async_arg[2] == 0 && + if (async_arg0 == QC_STATUS && async_arg2 == 0 && (async_ret & (QS_ERROR|QS_NEWCART))) { async_state = 2; goto restate; @@ -453,31 +641,31 @@ restate: } goto complete; } - CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0); /* NOTREACHED */ case 2: if (async_ret & QS_NEWCART) ft->newcart = 1; - CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1, 0, 0); + CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1); case 3: ft->lasterr = async_ret; if ((ft->lasterr & QS_NEWCART) == 0 && ft->lasterr) { DPRT(("ft%d: QIC error %d occurred on cmd %d\n", ftu, ft->lasterr & 0xff, ft->lasterr >> 8)); } - cmd = async_arg[0]; - nbits = async_arg[1]; - CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1, 0, 0); + cmd = async_arg0; + nbits = async_arg1; + CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1); case 4: goto complete; case 5: - CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0); case 6: - CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0); case 7: - CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0); case 8: - cmd = async_arg[0]; - CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0); + cmd = async_arg0; + CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0); } break; @@ -488,9 +676,9 @@ restate: */ switch(async_state) { case 0: - CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); + CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0); case 1: - if ((async_ret & async_arg[0]) != 0) goto complete; + if ((async_ret & async_arg0) != 0) goto complete; async_state = 0; if (++async_retries == 360) { /* 90 secs. */ DPRT(("ft%d: acmd_state exceeded retry count\n", ftu)); @@ -510,13 +698,13 @@ restate: */ switch(async_state) { case 0: - cmd = async_arg[0]; - async_retries = (async_arg[2]) ? (async_arg[2]*4) : 10; - CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0); + cmd = async_arg0; + async_retries = (async_arg2) ? (async_arg2 * 4) : 10; + CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0); case 1: - CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); + CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0); case 2: - if ((async_ret & async_arg[1]) != 0) goto complete; + if ((async_ret & async_arg1) != 0) goto complete; if (--async_retries == 0) { DPRT(("ft%d: acmd_seeksts retries exceeded\n", ftu)); goto complete; @@ -534,12 +722,12 @@ restate: switch(async_state) { case 0: if (!ft->moving) { - CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); /* NOTREACHED */ } async_state = 1; out_fdc(fdcu, 0x4a); /* READ_ID */ - out_fdc(fdcu, 0); + out_fdc(fdcu, ftu); break; case 1: for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu); @@ -548,25 +736,36 @@ restate: DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n", ft->rid[0], ft->rid[1], ft->rid[2], ft->rid[3], ft->rid[4], ft->rid[5], async_ret)); - if ((ft->rid[0] & 0xc0) == 0x40) { - if (++errcnt >= 10) { + if ((ft->rid[0] & 0xc0) != 0 || async_ret < 0) { + /* + * Method for retry: + * errcnt == 1 regular retry + * 2 microstep head 1 + * 3 microstep head 2 + * 4 microstep head back to 0 + * 5 fail + */ + if (++errcnt >= 5) { DPRT(("ft%d: acmd_readid errcnt exceeded\n", fdcu)); - async_ret = ft->lastpos; + async_ret = -2; errcnt = 0; goto complete; } - if (errcnt > 2) { + if (errcnt == 1) { + ft->moving = 0; + CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); + } else { ft->moving = 0; - CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(4, ACMD_SEEKSTS, QC_STPAUSE, QS_READY, 0); } - DPRT(("readid retry...\n")); + DPRT(("readid retry %d...\n", errcnt)); async_state = 0; goto restate; } if ((async_ret % ftg->g_blktrk) == (ftg->g_blktrk-1)) { DPRT(("acmd_readid detected last block on track\n")); retpos = async_ret; - CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0); + CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0); /* NOTREACHED */ } ft->lastpos = async_ret; @@ -574,13 +773,13 @@ restate: goto complete; /* NOTREACHED */ case 2: - CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0); case 3: ft->moving = 0; async_ret = retpos+1; goto complete; case 4: - CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0); case 5: ft->moving = 1; async_state = 0; @@ -598,27 +797,27 @@ restate: */ switch (async_state) { case 0: - wanttrk = async_arg[0] / ftg->g_blktrk; - wantblk = async_arg[0] % ftg->g_blktrk; + wanttrk = async_arg0 / ftg->g_blktrk; + wantblk = async_arg0 % ftg->g_blktrk; wantdir = wanttrk & 1; ft->moving = 0; - CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); case 1: curtrk = wanttrk; curdir = curtrk & 1; DPRT(("Changing to track %d\n", wanttrk)); - CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0); + CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0); case 2: cmd = wanttrk+2; - CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); + CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0); case 3: - CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); + CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0); case 4: ft->laststs = async_ret; if (wantblk == 0) { curblk = 0; cmd = (wantdir) ? QC_SEEKEND : QC_SEEKSTART; - CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90, 0, 0); + CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90); } if (ft->laststs & QS_BOT) { DPRT(("Tape is at BOT\n")); @@ -632,15 +831,23 @@ restate: async_state = 6; goto restate; } - CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_READID, 0, 0, 0); case 5: + if (async_ret < 0) { + ft->moving = 0; + ft->lastpos = -2; + if (async_ret == -2) { + CALL_ACMD(9, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); + } + CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); + } curtrk = (async_ret+1) / ftg->g_blktrk; curblk = (async_ret+1) % ftg->g_blktrk; DPRT(("gotid: curtrk=%d wanttrk=%d curblk=%d wantblk=%d\n", curtrk, wanttrk, curblk, wantblk)); if (curtrk != wanttrk) { /* oops! */ DPRT(("oops!! wrong track!\n")); - CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); } async_state = 6; goto restate; @@ -650,22 +857,22 @@ restate: ft->lastpos = curblk - 1; async_ret = ft->lastpos; if (ft->moving) goto complete; - CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0, 0, 0); + CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0); } if (curblk > wantblk) { /* passed it */ ft->moving = 0; - CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); } - if ((wantblk - curblk) <= 96) { /* approaching it */ - CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); + if ((wantblk - curblk) <= 256) { /* approaching it */ + CALL_ACMD(5, ACMD_READID, 0, 0, 0); } /* way up ahead */ ft->moving = 0; - CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); break; case 7: ft->moving = 1; - CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 8: async_state = 9; @@ -677,26 +884,26 @@ restate: curdiff = ((curblk - wantblk) / QCV_BLKSEG) + 2; if (curdiff >= ftg->g_segtrk) curdiff = ftg->g_segtrk - 1; DPRT(("pos %d past %d, reverse %d\n", curblk, wantblk, curdiff)); - CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0, 0, 0); + CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0); case 11: DPRT(("reverse 1 done\n")); - CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0); + CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0); case 12: DPRT(("reverse 2 done\n")); - CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0); + CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90); case 13: - CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_READID, 0, 0, 0); case 14: curdiff = ((wantblk - curblk) / QCV_BLKSEG) - 2; if (curdiff < 0) curdiff = 0; DPRT(("pos %d before %d, forward %d\n", curblk, wantblk, curdiff)); - CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0, 0, 0); + CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0); case 15: DPRT(("forward 1 done\n")); - CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0); + CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0); case 16: DPRT(("forward 2 done\n")); - CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0); + CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90); } break; } @@ -704,13 +911,14 @@ restate: return; complete: - if (astk_depth) { - astk_depth--; - async_retries = astk[astk_depth].over_retries; - async_func = astk[astk_depth].over_func; - async_state = astk[astk_depth].over_state; - for(i = 0; i < 5; i++) - async_arg[i] = astk[astk_depth].over_arg[i]; + if (astk_ptr != &astk[0]) { + astk_ptr--; + async_retries = astk_ptr->over_retries; + async_func = astk_ptr->over_func; + async_state = astk_ptr->over_state; + async_arg0 = astk_ptr->over_arg0; + async_arg1 = astk_ptr->over_arg1; + async_arg2 = astk_ptr->over_arg2; goto restate; } async_func = ACMD_NONE; @@ -720,6 +928,7 @@ complete: async_req(ftu, 2); break; case FTIO_READING: + case FTIO_RDAHEAD: async_read(ftu, 2); break; case FTIO_WRITING: @@ -735,10 +944,11 @@ complete: /* * Entry point for the async request processor. */ -void async_req(ftu_t ftu, int from) +static void +async_req(ftu_t ftu, int from) { ft_p ft = &ft_data[ftu]; - SegReq *sp; + SegReq *nsp, *sp; static int over_async, lastreq, domore; int cmd; @@ -747,56 +957,73 @@ void async_req(ftu_t ftu, int from) restate: switch (arq_state) { case 0: /* Process segment */ - ft->io_sts = ft->curseg->reqtype; + sp = ft->segh; + ft->io_sts = (sp == NULL) ? FTIO_READY : sp->reqtype; + if (ft->io_sts == FTIO_WRITING) async_write(ftu, from); else async_read(ftu, from); if (ft->io_sts != FTIO_READY) return; - /* Swap buffered and current segment */ - lastreq = ft->curseg->reqtype; - ft->curseg->reqtype = FTIO_READY; - sp = ft->curseg; - ft->curseg = ft->bufseg; - ft->bufseg = sp; + /* Pull buffer from current I/O queue */ + if (sp != NULL) { + lastreq = sp->reqtype; + segio_done(ft, sp); - wakeup((caddr_t)&ftsem.buff_avail); + /* If I/O cancelled, clear finished queue. */ + if (sp->reqcan) { + while (ft->doneh != NULL) + segio_free(ft, ft->doneh); + lastreq = FTIO_READY; + } + } else + lastreq = FTIO_READY; /* Detect end of track */ if (((ft->xblk / QCV_BLKSEG) % ftg->g_segtrk) == 0) { - domore = (ft->curseg->reqtype != FTIO_READY); - ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0); + ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0); } arq_state = 1; goto restate; case 1: /* Next request */ - if (ft->curseg->reqtype != FTIO_READY) { - ft->curseg->reqcrc = 0; + /* If we have another request queued, start it running. */ + if (ft->segh != NULL) { + sp = ft->segh; + sp->reqcrc = 0; arq_state = ard_state = awr_state = 0; - ft->xblk = ft->curseg->reqblk; + ft->xblk = sp->reqblk; + ft->xseg = sp->reqseg; ft->xcnt = 0; - ft->xptr = ft->curseg->buff; - DPRT(("I/O reqblk = %d\n", ft->curseg->reqblk)); + ft->xptr = sp->buff; + DPRT(("I/O reqblk = %d\n", ft->xblk)); goto restate; } - if (lastreq == FTIO_READING) { - ft->curseg->reqtype = FTIO_RDAHEAD; - ft->curseg->reqblk = ft->xblk; - ft->curseg->reqcrc = 0; - ft->curseg->reqcan = 0; - bzero(ft->curseg->buff, QCV_SEGSIZE); + + /* If the last request was reading, do read ahead. */ + if ((lastreq == FTIO_READING || lastreq == FTIO_RDAHEAD) && + (sp = segio_alloc(ft)) != NULL) { + sp->reqtype = FTIO_RDAHEAD; + sp->reqblk = ft->xblk; + sp->reqseg = ft->xseg+1; + sp->reqcrc = 0; + sp->reqcan = 0; + segio_queue(ft, sp); + bzero(sp->buff, QCV_SEGSIZE); arq_state = ard_state = awr_state = 0; - ft->xblk = ft->curseg->reqblk; + ft->xblk = sp->reqblk; + ft->xseg = sp->reqseg; ft->xcnt = 0; - ft->xptr = ft->curseg->buff; - DPRT(("Processing readahead reqblk = %d\n", ft->curseg->reqblk)); + ft->xptr = sp->buff; + DPRT(("Processing readahead reqblk = %d\n", ft->xblk)); goto restate; } + if (ft->moving) { DPRT(("No more I/O.. Stopping.\n")); - ACMD_FUNC(7, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + ft->moving = 0; + ACMD_FUNC(7, ACMD_SEEKSTS, QC_PAUSE, QS_READY, 0); break; } arq_state = 7; @@ -804,26 +1031,26 @@ restate: case 2: /* End of track */ ft->moving = 0; - ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0); break; case 3: DPRT(("async_req seek head to track %d\n", ft->xblk / ftg->g_blktrk)); - ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0); + ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0); break; case 4: cmd = (ft->xblk / ftg->g_blktrk) + 2; - if (domore) { - ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); + if (ft->segh != NULL) { + ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0); } else { - ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); + ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0); } break; case 5: ft->moving = 1; - ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 6: @@ -832,26 +1059,22 @@ restate: break; case 7: - ft->moving = 0; - - /* Check one last time to see if a request came in. */ - if (ft->curseg->reqtype != FTIO_READY) { - DPRT(("async_req: Never say no!\n")); - arq_state = 1; - goto restate; - } - /* Time to rest. */ ft->active = 0; - wakeup((caddr_t)&ftsem.iosts_change); /* wakeup those who want an i/o chg */ + ft->lastpos = -2; + + /* wakeup those who want an i/o chg */ + wakeup((caddr_t)wc_iosts_change); break; } } + /* * Entry for async read. */ -void async_read(ftu_t ftu, int from) +static void +async_read(ftu_t ftu, int from) { ft_p ft = &ft_data[ftu]; fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ @@ -859,6 +1082,7 @@ void async_read(ftu_t ftu, int from) int i, cmd, newcn, rddta[7]; int st0, pcn, where; static int over_async; + static int retries = 0; if (from == 2) ard_state = over_async; @@ -872,13 +1096,13 @@ restate: if (ft->lastpos != (ft->xblk-1)) { DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n", ftu, ft->lastpos, ft->xblk)); - ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0); + ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0); } /* Tape is in position but stopped. */ if (!ft->moving) { DPRT(("async_read ******STARTING TAPE\n")); - ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0); } ard_state = 1; goto restate; @@ -887,8 +1111,8 @@ restate: /* Tape is now moving and in position-- start DMA now! */ isa_dmastart(B_READ, ft->xptr, QCV_BLKSIZE, 2); out_fdc(fdcu, 0x66); /* read */ - out_fdc(fdcu, 0x00); /* unit */ - out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ + out_fdc(fdcu, ftu); /* unit */ + out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */ out_fdc(fdcu, 0x03); /* 1K sectors */ @@ -905,7 +1129,8 @@ restate: #if FTDBGALL /* Compute where the controller thinks we are */ - where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1; + where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + + rddta[5]-1; DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], where, ft->xblk)); @@ -913,34 +1138,44 @@ restate: /* Check for errors */ if ((rddta[0] & 0xc0) != 0x00) { - if (rddta[1] & 0x04) { +#if !FTDBGALL + where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + + rddta[5]-1; + DPRT(("xd: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", + rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], + where, ft->xblk)); +#endif + if ((rddta[1] & 0x04) == 0x04 && retries < 2) { /* Probably wrong position */ + DPRT(("async_read: doing retry %d\n", retries)); ft->lastpos = ft->xblk; ard_state = 0; + retries++; goto restate; } else { /* CRC/Address-mark/Data-mark, et. al. */ DPRT(("ft%d: CRC error on block %d\n", fdcu, ft->xblk)); - ft->curseg->reqcrc |= (1 << ft->xcnt); + ft->segh->reqcrc |= (1 << ft->xcnt); } } /* Otherwise, transfer completed okay. */ + retries = 0; ft->lastpos = ft->xblk; ft->xblk++; ft->xcnt++; ft->xptr += QCV_BLKSIZE; - if (ft->xcnt < QCV_BLKSEG && ft->curseg->reqcan == 0) { + if (ft->xcnt < QCV_BLKSEG && ft->segh->reqcan == 0) { ard_state = 0; goto restate; } - DPRT(("Read done.. Cancel = %d\n", ft->curseg->reqcan)); + DPRT(("Read done.. Cancel = %d\n", ft->segh->reqcan)); ft->io_sts = FTIO_READY; break; case 3: ft->moving = 1; - ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 4: @@ -960,7 +1195,8 @@ restate: * routine, if it's 1 then it was a timeout, if it's 2, then an * async_cmd completed. */ -void async_write(ftu_t ftu, int from) +static void +async_write(ftu_t ftu, int from) { ft_p ft = &ft_data[ftu]; fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ @@ -982,13 +1218,13 @@ restate: if (ft->lastpos != (ft->xblk-1)) { DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n", ftu, ft->lastpos, ft->xblk)); - ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0); + ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0); } /* Tape is in position but stopped. */ if (!ft->moving) { DPRT(("async_write ******STARTING TAPE\n")); - ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0); } awr_state = 1; goto restate; @@ -997,8 +1233,8 @@ restate: /* Tape is now moving and in position-- start DMA now! */ isa_dmastart(B_WRITE, ft->xptr, QCV_BLKSIZE, 2); out_fdc(fdcu, 0x45); /* write */ - out_fdc(fdcu, 0x00); /* unit */ - out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ + out_fdc(fdcu, ftu); /* unit */ + out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cyl */ out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */ out_fdc(fdcu, 0x03); /* 1K sectors */ @@ -1023,13 +1259,16 @@ restate: /* Check for errors */ if ((rddta[0] & 0xc0) != 0x00) { - if (rddta[1] & 0x04) { - /* Probably wrong position */ - ft->lastpos = ft->xblk; - awr_state = 0; - goto restate; - } else if (retries < 5) { +#if !FTDBGALL + where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + + rddta[5]-1; + DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", + rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], + where, ft->xblk)); +#endif + if (retries < 3) { /* Something happened -- try again */ + DPRT(("async_write: doing retry %d\n", retries)); ft->lastpos = ft->xblk; awr_state = 0; retries++; @@ -1037,11 +1276,11 @@ restate: } else { /* * Retries failed. Note the unrecoverable error. - * Marking the block as bad is fairly useless. + * Marking the block as bad is useless right now. */ printf("ft%d: unrecoverable write error on block %d\n", ftu, ft->xblk); - ft->curseg->reqcrc |= (1 << ft->xcnt); + ft->segh->reqcrc |= (1 << ft->xcnt); } } @@ -1063,7 +1302,7 @@ restate: case 3: ft->moving = 1; - ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 4: @@ -1081,11 +1320,14 @@ restate: /* * Interrupt handler for active tape. Bounced off of fdintr(). */ -int ftintr(ftu_t ftu) +int +ftintr(ftu_t ftu) { int st0, pcn, i; ft_p ft = &ft_data[ftu]; - fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ + fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ + int s = splbio(); + st0 = 0; pcn = 0; @@ -1093,12 +1335,14 @@ int ftintr(ftu_t ftu) if (ft->active) { if (async_func != ACMD_NONE) { async_cmd(ftu); + splx(s); return(1); } #if FTDBGALL DPRT(("Got request interrupt\n")); #endif async_req(ftu, 0); + splx(s); return(1); } @@ -1113,20 +1357,21 @@ int ftintr(ftu_t ftu) huh_what: printf("ft%d: unexpected interrupt; st0 = $%02x pcn = %d\n", ftu, st0, pcn); + splx(s); return(1); } switch (ft->cmd_wait) { case FTCMD_RESET: ft->sts_wait = FTSTS_INTERRUPT; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); break; case FTCMD_RECAL: case FTCMD_SEEK: if (st0 & 0x20) { /* seek done */ ft->sts_wait = FTSTS_INTERRUPT; ft->pcn = pcn; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); } #if FTDBGALL else @@ -1137,20 +1382,23 @@ huh_what: case FTCMD_READID: for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu); ft->sts_wait = FTSTS_INTERRUPT; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); break; default: goto huh_what; } + splx(s); return(1); } + /* * Interrupt timeout routine. */ -static void ft_timeout(caddr_t arg1, int arg2) +static void +ft_timeout(caddr_t arg1, int arg2) { int s; ftu_t ftu = (ftu_t)arg1; @@ -1166,17 +1414,19 @@ static void ft_timeout(caddr_t arg1, int arg2) async_req(ftu, 1); } else { ft->sts_wait = FTSTS_TIMEOUT; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); } splx(s); } + /* * Wait for a particular interrupt to occur. ftintr() will wake us up * if it sees what we want. Otherwise, time out and return error. * Should always disable ints before trigger is sent and calling here. */ -int ftintr_wait(ftu_t ftu, int cmd, int ticks) +static int +ftintr_wait(ftu_t ftu, int cmd, int ticks) { int retries, st0, pcn; ft_p ft = &ft_data[ftu]; @@ -1211,8 +1461,7 @@ int ftintr_wait(ftu_t ftu, int cmd, int ticks) goto intrdone; } - if (ticks) timeout(ft_timeout, (caddr_t)ftu, ticks); - sleep((caddr_t)&ftsem.intr_wait, FTPRI); + ftsleep(wc_intr_wait, ticks); intrdone: if (ft->sts_wait == FTSTS_TIMEOUT) { /* timeout */ @@ -1230,11 +1479,13 @@ intrdone: return(0); } + /* * Recalibrate tape drive. Parameter totape is true, if we should * recalibrate to tape drive settings. */ -int tape_recal(ftu_t ftu, int totape) +static int +tape_recal(ftu_t ftu, int totape) { int s; ft_p ft = &ft_data[ftu]; @@ -1248,7 +1499,7 @@ int tape_recal(ftu_t ftu, int totape) s = splbio(); out_fdc(fdcu, NE7CMD_RECAL); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); if (ftintr_wait(ftu, FTCMD_RECAL, hz)) { splx(s); @@ -1265,18 +1516,25 @@ int tape_recal(ftu_t ftu, int totape) return(0); } -static void state_timeout(caddr_t arg1, int arg2) + +/* + * Timeout for long delays. + */ +static void +state_timeout(caddr_t arg1, int arg2) { ftu_t ftu = (ftu_t)arg1; - wakeup((caddr_t)&ftsem.long_delay); + wakeup((caddr_t)wc_long_delay); } + /* * Wait for a particular tape status to be met. If all is TRUE, then * all states must be met, otherwise any state can be met. */ -int tape_state(ftu_t ftu, int all, int mask, int seconds) +static int +tape_state(ftu_t ftu, int all, int mask, int seconds) { int r, tries, maxtries; @@ -1287,20 +1545,19 @@ int tape_state(ftu_t ftu, int all, int mask, int seconds) if (all && (r & mask) == mask) return(r); if ((r & mask) != 0) return(r); } - if (seconds) { - timeout(state_timeout, (caddr_t)ftu, hz/4); - sleep((caddr_t)&ftsem.long_delay, FTPRI); - } + if (seconds) ftsleep(wc_long_delay, hz/4); } DPRT(("ft%d: tape_state failed on mask=$%02x maxtries=%d\n", ftu, mask, maxtries)); return(-1); } + /* * Send a QIC command to tape drive, wait for completion. */ -int tape_cmd(ftu_t ftu, int cmd) +static int +tape_cmd(ftu_t ftu, int cmd) { int newcn; int retries = 0; @@ -1316,7 +1573,7 @@ retry: /* Perform seek */ s = splbio(); out_fdc(fdcu, NE7CMD_SEEK); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); out_fdc(fdcu, newcn); if (ftintr_wait(ftu, FTCMD_SEEK, hz)) { @@ -1338,25 +1595,40 @@ redo: return(0); } + /* * Return status of tape drive */ -int tape_status(ftu_t ftu) +static int +tape_status(ftu_t ftu) { int r, err, tries; - ft_p ft = &ft_data[ftu]; + ft_p ft = &ft_data[ftu]; + int max = (ft->attaching) ? 2 : 3; - for (r = -1, tries = 0; r < 0 && tries < 3; tries++) + for (r = -1, tries = 0; r < 0 && tries < max; tries++) r = qic_status(ftu, QC_STATUS, 8); - if (tries == 3) return(-1); + if (tries == max) return(-1); + +recheck: DPRT(("tape_status got $%04x\n",r)); ft->laststs = r; if (r & (QS_ERROR|QS_NEWCART)) { - if (r & QS_NEWCART) ft->newcart = 1; err = qic_status(ftu, QC_ERRCODE, 16); ft->lasterr = err; - if ((r & QS_NEWCART) == 0 && err && ft->attaching == 0) { + if (r & QS_NEWCART) { + ft->newcart = 1; + /* If tape not referenced, do a seek load point. */ + if ((r & QS_FMTOK) == 0 && !ft->attaching) { + tape_cmd(ftu, QC_SEEKLP); + do { + ftsleep(wc_long_delay, hz); + } while ((r = qic_status(ftu, QC_STATUS, 8)) < 0 || + (r & (QS_READY|QS_CART)) == QS_CART); + goto recheck; + } + } else if (err && !ft->attaching) { DPRT(("ft%d: QIC error %d occurred on cmd %d\n", ftu, err & 0xff, err >> 8)); } @@ -1364,29 +1636,36 @@ int tape_status(ftu_t ftu) ft->laststs = r; DPRT(("tape_status got error code $%04x new sts = $%02x\n",err,r)); } + ft->rdonly = (r & QS_RDONLY); return(r); } + /* * Transfer control to tape drive. */ -void tape_start(ftu_t ftu) +static void +tape_start(ftu_t ftu, int motor) { ft_p ft = &ft_data[ftu]; fdc_p fdc = ft->fdc; - int s; - - DPRT(("tape_start start\n")); + int s, mbits; s = splbio(); + DPRT(("tape_start start\n")); /* reset, dma disable */ - outb(fdc->baseport+fdout, 0x00); + outb(fdc->baseport+FDOUT, 0x00); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); - /* raise reset, enable DMA */ - outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN); + /* raise reset, enable DMA, motor on if needed */ + if (motor) + mbits = (!ftu) ? FDO_MOEN0 : FDO_MOEN1; + else + mbits = 0; + + outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN | mbits); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); splx(s); @@ -1394,16 +1673,18 @@ void tape_start(ftu_t ftu) tape_recal(ftu, 1); /* set transfer speed */ - outb(fdc->baseport+fdctl, FDC_500KBPS); + outb(fdc->baseport+FDCTL, FDC_500KBPS); DELAY(10); DPRT(("tape_start end\n")); } + /* * Transfer control back to floppy disks. */ -void tape_end(ftu_t ftu) +static void +tape_end(ftu_t ftu) { ft_p ft = &ft_data[ftu]; fdc_p fdc = ft->fdc; @@ -1415,41 +1696,59 @@ void tape_end(ftu_t ftu) s = splbio(); /* reset, dma disable */ - outb(fdc->baseport+fdout, 0x00); + outb(fdc->baseport+FDOUT, 0x00); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); /* raise reset, enable DMA */ - outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN); + outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); splx(s); /* set transfer speed */ - outb(fdc->baseport+fdctl, FDC_500KBPS); + outb(fdc->baseport+FDCTL, FDC_500KBPS); DELAY(10); fdc->flags &= ~FDC_TAPE_BUSY; DPRT(("tape_end end\n")); } + /* * Wait for the driver to go inactive, cancel readahead if necessary. */ -void tape_inactive(ftu_t ftu) +static void +tape_inactive(ftu_t ftu) { ft_p ft = &ft_data[ftu]; - - if (ft->curseg->reqtype == FTIO_RDAHEAD) { - ft->curseg->reqcan = 1; /* XXX cancel rdahead */ - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + int s = splbio(); + + if (ft->segh != NULL) { + if (ft->segh->reqtype == FTIO_RDAHEAD) { + /* cancel read-ahead */ + ft->segh->reqcan = 1; + } else if (ft->segh->reqtype == FTIO_WRITING && !ft->active) { + /* flush out any remaining writes */ + DPRT(("Flushing write I/O chain\n")); + arq_state = ard_state = awr_state = 0; + ft->xblk = ft->segh->reqblk; + ft->xseg = ft->segh->reqseg; + ft->xcnt = 0; + ft->xptr = ft->segh->buff; + ft->active = 1; + timeout(ft_timeout, (caddr_t)ftu, 1); + } } - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + while (ft->active) ftsleep(wc_iosts_change, 0); + splx(s); } + /* * Get the geometry of the tape currently in the drive. */ -int ftgetgeom(ftu_t ftu) +static int +ftgetgeom(ftu_t ftu) { int r, i, tries; int cfg, qic80, ext; @@ -1459,7 +1758,7 @@ int ftgetgeom(ftu_t ftu) r = tape_status(ftu); /* XXX fix me when format mode is finished */ - if ((r & QS_CART) == 0 || (r & QS_FMTOK) == 0) { + if (r < 0 || (r & QS_CART) == 0 || (r & QS_FMTOK) == 0) { DPRT(("ftgetgeom: no cart or not formatted 0x%04x\n",r)); ftg = NULL; ft->newcart = 1; @@ -1539,20 +1838,21 @@ int ftgetgeom(ftu_t ftu) return(0); } + /* * Switch between tape/floppy. This will send the tape enable/disable * codes for this drive's manufacturer. */ -int set_fdcmode(dev_t dev, int newmode) +static int +set_fdcmode(dev_t dev, int newmode) { ftu_t ftu = FDUNIT(minor(dev)); ft_p ft = &ft_data[ftu]; fdc_p fdc = ft->fdc; - static int havebufs = 0; void *buf; int r, s, i; - SegReq *sp; + SegReq *sp, *rsp; if (newmode == FDC_TAPE_MODE) { /* Wake up the tape drive */ @@ -1560,19 +1860,22 @@ int set_fdcmode(dev_t dev, int newmode) case NO_TYPE: fdc->flags &= ~FDC_TAPE_BUSY; return(ENXIO); + case FT_NONE: + tape_start(ftu, 0); + break; case FT_COLORADO: - tape_start(ftu); + tape_start(ftu, 0); if (tape_cmd(ftu, QC_COL_ENABLE1)) { tape_end(ftu); return(EIO); } - if (tape_cmd(ftu, QC_COL_ENABLE2)) { + if (tape_cmd(ftu, QC_COL_ENABLE2 + ftu)) { tape_end(ftu); return(EIO); } break; case FT_MOUNTAIN: - tape_start(ftu); + tape_start(ftu, 0); if (tape_cmd(ftu, QC_MTN_ENABLE1)) { tape_end(ftu); return(EIO); @@ -1582,56 +1885,93 @@ int set_fdcmode(dev_t dev, int newmode) return(EIO); } break; + case FT_INSIGHT: + tape_start(ftu, 1); + break; default: DPRT(("ft%d: bad tape type\n", ftu)); return(ENXIO); } if (tape_status(ftu) < 0) { - tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE); + if (ft->type == FT_COLORADO) + tape_cmd(ftu, QC_COL_DISABLE); + else if (ft->type == FT_MOUNTAIN) + tape_cmd(ftu, QC_MTN_DISABLE); tape_end(ftu); return(EIO); } /* Grab buffers from memory. */ if (!havebufs) { - ft->curseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT); - if (ft->curseg == NULL) { - printf("ft%d: not enough memory for buffers\n", ftu); - return(ENOMEM); - } - ft->bufseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT); - if (ft->bufseg == NULL) { - free(ft->curseg, M_DEVBUF); - printf("ft%d: not enough memory for buffers\n", ftu); - return(ENOMEM); + ft->segh = ft->segt = NULL; + ft->doneh = ft->donet = NULL; + ft->segfree = NULL; + ft->hdr = NULL; + ft->nsegq = ft->ndoneq = ft->nfreelist = 0; + for (i = 0; i < FTNBUFF; i++) { + sp = malloc(sizeof(SegReq), M_DEVBUF, M_WAITOK); + if (sp == NULL) { + printf("ft%d: not enough memory for buffers\n", ftu); + for (sp=ft->segfree; sp != NULL; sp=sp->next) + free(sp, M_DEVBUF); + if (ft->type == FT_COLORADO) + tape_cmd(ftu, QC_COL_DISABLE); + else if (ft->type == FT_MOUNTAIN) + tape_cmd(ftu, QC_MTN_DISABLE); + tape_end(ftu); + return(ENOMEM); + } + sp->reqtype = FTIO_READY; + sp->next = ft->segfree; + ft->segfree = sp; + ft->nfreelist++; } + /* take one buffer for header */ + ft->hdr = ft->segfree; + ft->segfree = ft->segfree->next; + ft->nfreelist--; havebufs = 1; } - ft->curseg->reqtype = FTIO_READY; - ft->bufseg->reqtype = FTIO_READY; ft->io_sts = FTIO_READY; /* tape drive is ready */ ft->active = 0; /* interrupt driver not active */ ft->moving = 0; /* tape not moving */ ft->rdonly = 0; /* tape read only */ - ft->newcart = 0; /* a new cart was inserted */ + ft->newcart = 0; /* new cartridge flag */ ft->lastpos = -1; /* tape is rewound */ + async_func = ACMD_NONE; /* No async function */ tape_state(ftu, 0, QS_READY, 60); tape_cmd(ftu, QC_RATE); tape_cmd(ftu, QCF_RT500+2); /* 500K bps */ tape_state(ftu, 0, QS_READY, 60); ft->mode = FTM_PRIMARY; - tape_cmd(ftu, QC_PRIMARY); /* Make sure we're in primary mode */ + tape_cmd(ftu, QC_PRIMARY); /* Make sure we're in primary mode */ tape_state(ftu, 0, QS_READY, 60); ftg = NULL; /* No geometry yet */ ftgetgeom(ftu); /* Get tape geometry */ ftreq_rewind(ftu); /* Make sure tape is rewound */ } else { - tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE); + if (ft->type == FT_COLORADO) + tape_cmd(ftu, QC_COL_DISABLE); + else if (ft->type == FT_MOUNTAIN) + tape_cmd(ftu, QC_MTN_DISABLE); tape_end(ftu); ft->newcart = 0; /* clear new cartridge */ + if (ft->hdr != NULL) free(ft->hdr, M_DEVBUF); + if (havebufs) { + for (sp = ft->segfree; sp != NULL;) { + rsp = sp; sp = sp->next; + free(rsp, M_DEVBUF); + } + for (sp = ft->segh; sp != NULL;) { + rsp = sp; sp = sp->next; + free(rsp, M_DEVBUF); + } + for (sp = ft->doneh; sp != NULL;) { + rsp = sp; sp = sp->next; + free(rsp, M_DEVBUF); + } + } havebufs = 0; - free(ft->curseg, M_DEVBUF); - free(ft->bufseg, M_DEVBUF); } return(0); } @@ -1640,7 +1980,8 @@ int set_fdcmode(dev_t dev, int newmode) /* * Perform a QIC status function. */ -int qic_status(ftu_t ftu, int cmd, int nbits) +static int +qic_status(ftu_t ftu, int cmd, int nbits) { int st3, val, r, i; ft_p ft = &ft_data[ftu]; @@ -1653,7 +1994,7 @@ int qic_status(ftu_t ftu, int cmd, int nbits) /* Sense drive status */ out_fdc(fdcu, NE7CMD_SENSED); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); st3 = in_fdc(fdcu); if ((st3 & 0x10) == 0) { /* track 0 */ @@ -1668,7 +2009,7 @@ int qic_status(ftu_t ftu, int cmd, int nbits) } out_fdc(fdcu, NE7CMD_SENSED); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); st3 = in_fdc(fdcu); if (st3 < 0) { DPRT(("ft%d: controller timed out on bit %d r=$%02x\n", @@ -1690,11 +2031,13 @@ int qic_status(ftu_t ftu, int cmd, int nbits) return(r); } + /* * Open tape drive for use. Bounced off of Fdopen if tape minor is * detected. */ -int ftopen(dev_t dev, int arg2) { +int +ftopen(dev_t dev, int arg2) { ftu_t ftu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fdc_p fdc; @@ -1714,19 +2057,21 @@ int ftopen(dev_t dev, int arg2) { return(set_fdcmode(dev, FDC_TAPE_MODE)); /* try to switch to tape */ } + /* * Close tape and return floppy controller to disk mode. */ -int ftclose(dev_t dev, int flags) +int +ftclose(dev_t dev, int flags) { int s; SegReq *sp; ftu_t ftu = FDUNIT(minor(dev)); ft_p ft = &ft_data[ftu]; + /* Wait for any remaining I/O activity to complete. */ - if (ft->curseg->reqtype == FTIO_RDAHEAD) ft->curseg->reqcan = 1; - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + tape_inactive(ftu); ft->mode = FTM_PRIMARY; tape_cmd(ftu, QC_PRIMARY); @@ -1735,94 +2080,124 @@ int ftclose(dev_t dev, int flags) return(set_fdcmode(dev, FDC_DISK_MODE)); /* Otherwise, close tape */ } + /* - * Perform strategy on a given buffer (not!). The driver was not - * performing very efficiently using the buffering routines. After - * support for error correction was added, this routine became - * obsolete in favor of doing ioctl's. Ugly, yes. + * Perform strategy on a given buffer (not!). Changed so that the + * driver will at least return 'Operation not supported'. */ -void ftstrategy(struct buf *bp) +void +ftstrategy(struct buf *bp) { - return; + bp->b_error = ENODEV; + bp->b_flags |= B_ERROR; + biodone(bp); } -/* Read or write a segment. */ -int ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p) + +/* + * Read or write a segment. + */ +static int +ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p) { int r, i, j; SegReq *sp; int s; - long blk, bad; + long blk, bad, seg; unsigned char *cp, *cp2; ft_p ft = &ft_data[ftu]; - if (!ft->active) { + if (!ft->active && ft->segh == NULL) { r = tape_status(ftu); - if ((r & QS_CART) == 0) { + if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */ - } - if ((r & QS_FMTOK) == 0) { + if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */ - } tape_state(ftu, 0, QS_READY, 90); } if (ftg == NULL || ft->newcart) { - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + tape_inactive(ftu); tape_state(ftu, 0, QS_READY, 90); - if (ftgetgeom(ftu) < 0) { + if (ftgetgeom(ftu) < 0) return(ENXIO); - } } /* Write not allowed on a read-only tape. */ - if (cmd == QIOWRITE && ft->rdonly) { + if (cmd == QIOWRITE && ft->rdonly) return(EROFS); - } + /* Quick check of request and buffer. */ - if (sr == NULL || sr->sg_data == NULL) { + if (sr == NULL || sr->sg_data == NULL) return(EINVAL); - } - if (sr->sg_trk >= ftg->g_trktape || - sr->sg_seg >= ftg->g_segtrk) { + + /* Make sure requested track and segment is in range. */ + if (sr->sg_trk >= ftg->g_trktape || sr->sg_seg >= ftg->g_segtrk) return(EINVAL); - } + blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG; + seg = sr->sg_trk * ftg->g_segtrk + sr->sg_seg; s = splbio(); if (cmd == QIOREAD) { - if (ft->curseg->reqtype == FTIO_RDAHEAD) { - if (blk == ft->curseg->reqblk) { - sp = ft->curseg; + /* + * See if the driver is reading ahead. + */ + if (ft->doneh != NULL || + (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD)) { + /* + * Eat the completion queue and see if the request + * is already there. + */ + while (ft->doneh != NULL) { + if (blk == ft->doneh->reqblk) { + sp = ft->doneh; + sp->reqtype = FTIO_READING; + sp->reqbad = sr->sg_badmap; + goto rddone; + } + segio_free(ft, ft->doneh); + } + + /* + * Not on the completed queue, in progress maybe? + */ + if (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD && + blk == ft->segh->reqblk) { + sp = ft->segh; sp->reqtype = FTIO_READING; sp->reqbad = sr->sg_badmap; goto rdwait; - } else - ft->curseg->reqcan = 1; /* XXX cancel rdahead */ + } } /* Wait until we're ready. */ - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + tape_inactive(ftu); /* Set up a new read request. */ - sp = ft->curseg; + sp = segio_alloc(ft); sp->reqcrc = 0; sp->reqbad = sr->sg_badmap; sp->reqblk = blk; + sp->reqseg = seg; sp->reqcan = 0; sp->reqtype = FTIO_READING; + segio_queue(ft, sp); /* Start the read request off. */ DPRT(("Starting read I/O chain\n")); arq_state = ard_state = awr_state = 0; ft->xblk = sp->reqblk; + ft->xseg = sp->reqseg; ft->xcnt = 0; ft->xptr = sp->buff; ft->active = 1; timeout(ft_timeout, (caddr_t)ftu, 1); rdwait: - sleep((caddr_t)&ftsem.buff_avail, FTPRI); + ftsleep(wc_buff_done, 0); + +rddone: bad = sp->reqbad; sr->sg_crcmap = sp->reqcrc & ~bad; @@ -1833,17 +2208,29 @@ rdwait: copyout(cp, cp2, QCV_BLKSIZE); cp2 += QCV_BLKSIZE; } + segio_free(ft, sp); } else { - if (ft->curseg->reqtype == FTIO_RDAHEAD) { - ft->curseg->reqcan = 1; /* XXX cancel rdahead */ - while (ft->active) - sleep((caddr_t)&ftsem.iosts_change, FTPRI); + if (ft->segh != NULL && ft->segh->reqtype != FTIO_WRITING) + tape_inactive(ftu); + + /* Allocate a buffer and start tape if we're running low. */ + sp = segio_alloc(ft); + if (!ft->active && (sp == NULL || ft->nfreelist <= 1)) { + DPRT(("Starting write I/O chain\n")); + arq_state = ard_state = awr_state = 0; + ft->xblk = ft->segh->reqblk; + ft->xseg = ft->segh->reqseg; + ft->xcnt = 0; + ft->xptr = ft->segh->buff; + ft->active = 1; + timeout(ft_timeout, (caddr_t)ftu, 1); } /* Sleep until a buffer becomes available. */ - while (ft->bufseg->reqtype != FTIO_READY) - sleep((caddr_t)&ftsem.buff_avail, FTPRI); - sp = (ft->curseg->reqtype == FTIO_READY) ? ft->curseg : ft->bufseg; + while (sp == NULL) { + ftsleep(wc_buff_avail, 0); + sp = segio_alloc(ft); + } /* Copy in segment and expand bad blocks. */ bad = sr->sg_badmap; @@ -1853,28 +2240,22 @@ rdwait: copyin(cp, cp2, QCV_BLKSIZE); cp += QCV_BLKSIZE; } - sp->reqblk = blk; + sp->reqseg = seg; sp->reqcan = 0; sp->reqtype = FTIO_WRITING; - - if (!ft->active) { - DPRT(("Starting write I/O chain\n")); - arq_state = ard_state = awr_state = 0; - ft->xblk = sp->reqblk; - ft->xcnt = 0; - ft->xptr = sp->buff; - ft->active = 1; - timeout(ft_timeout, (caddr_t)ftu, 1); - } + segio_queue(ft, sp); } splx(s); return(0); } -/* Rewind to beginning of tape */ -int ftreq_rewind(ftu_t ftu) +/* + * Rewind to beginning of tape + */ +static int +ftreq_rewind(ftu_t ftu) { ft_p ft = &ft_data[ftu]; @@ -1891,8 +2272,12 @@ int ftreq_rewind(ftu_t ftu) return(0); } -/* Move to logical beginning or end of track */ -int ftreq_trkpos(ftu_t ftu, int req) + +/* + * Move to logical beginning or end of track + */ +static int +ftreq_trkpos(ftu_t ftu, int req) { int curtrk, r, cmd; ft_p ft = &ft_data[ftu]; @@ -1919,8 +2304,12 @@ int ftreq_trkpos(ftu_t ftu, int req) return(0); } -/* Seek tape head to a particular track. */ -int ftreq_trkset(ftu_t ftu, int *trk) + +/* + * Seek tape head to a particular track. + */ +static int +ftreq_trkset(ftu_t ftu, int *trk) { int curtrk, r, cmd; ft_p ft = &ft_data[ftu]; @@ -1942,27 +2331,45 @@ int ftreq_trkset(ftu_t ftu, int *trk) return(0); } -/* Start tape moving forward. */ -int ftreq_lfwd(ftu_t ftu) + +/* + * Start tape moving forward. + */ +static int +ftreq_lfwd(ftu_t ftu) { + ft_p ft = &ft_data[ftu]; + tape_inactive(ftu); tape_cmd(ftu, QC_STOP); tape_state(ftu, 0, QS_READY, 90); tape_cmd(ftu, QC_FORWARD); + ft->moving = 1; return(0); } -/* Stop the tape */ -int ftreq_stop(ftu_t ftu) + +/* + * Stop the tape + */ +static int +ftreq_stop(ftu_t ftu) { + ft_p ft = &ft_data[ftu]; + tape_inactive(ftu); tape_cmd(ftu, QC_STOP); tape_state(ftu, 0, QS_READY, 90); + ft->moving = 0; return(0); } -/* Set the particular mode the drive should be in. */ -int ftreq_setmode(ftu_t ftu, int cmd) + +/* + * Set the particular mode the drive should be in. + */ +static int +ftreq_setmode(ftu_t ftu, int cmd) { int r; ft_p ft = &ft_data[ftu]; @@ -1989,8 +2396,12 @@ int ftreq_setmode(ftu_t ftu, int cmd) return(0); } -/* Return drive status bits */ -int ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p) + +/* + * Return drive status bits + */ +static int +ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p) { ft_p ft = &ft_data[ftu]; @@ -2001,8 +2412,12 @@ int ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p) return(0); } -/* Return drive configuration bits */ -int ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p) + +/* + * Return drive configuration bits + */ +static int +ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p) { int r, tries; ft_p ft = &ft_data[ftu]; @@ -2018,8 +2433,12 @@ int ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p) return(0); } -/* Return current tape's geometry. */ -int ftreq_geom(ftu_t ftu, QIC_Geom *g) + +/* + * Return current tape's geometry. + */ +static int +ftreq_geom(ftu_t ftu, QIC_Geom *g) { tape_inactive(ftu); if (ftg == NULL && ftgetgeom(ftu) < 0) return(ENXIO); @@ -2027,8 +2446,12 @@ int ftreq_geom(ftu_t ftu, QIC_Geom *g) return(0); } -/* Return drive hardware information */ -int ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp) + +/* + * Return drive hardware information + */ +static int +ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp) { int r, tries; int rom, vend; @@ -2053,10 +2476,31 @@ int ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp) return(0); } + +/* + * Receive or Send the in-core header segment. + */ +static int +ftreq_hdr(ftu_t ftu, int cmd, QIC_Segment *sp) +{ + ft_p ft = &ft_data[ftu]; + QIC_Header *h = (QIC_Header *)ft->hdr->buff; + + if (sp == NULL || sp->sg_data == NULL) return(EINVAL); + if (cmd == QIOSENDHDR) { + copyin(sp->sg_data, ft->hdr->buff, QCV_SEGSIZE); + } else { + if (h->qh_sig != QCV_HDRMAGIC) return(EIO); + copyout(ft->hdr->buff, sp->sg_data, QCV_SEGSIZE); + } + return(0); +} + /* * I/O functions. */ -int ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) +int +ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { ftu_t ftu = FDUNIT(minor(dev)); ft_p ft = &ft_data[ftu]; @@ -2104,20 +2548,30 @@ int ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) case QIOHWINFO: return(ftreq_hwinfo(ftu, (QIC_HWInfo *)data)); + + case QIOSENDHDR: + case QIORECVHDR: + return(ftreq_hdr(ftu, cmd, (QIC_Segment *)data)); } badreq: DPRT(("ft%d: unknown ioctl(%d) request\n", ftu, cmd)); return(ENXIO); } -/* Not implemented */ -int ftdump(dev_t dev) +/* + * Not implemented + */ +int +ftdump(dev_t dev) { return(EINVAL); } -/* Not implemented */ -int ftsize(dev_t dev) +/* + * Not implemented + */ +int +ftsize(dev_t dev) { return(EINVAL); } |