*BSD News Article 41339


Return to BSD News archive

Newsgroups: comp.os.386bsd.development
Path: sserve!newshost.anu.edu.au!harbinger.cc.monash.edu.au!news.cs.su.oz.au!metro!extro!christie
From: christie@extro.ucc.su.OZ.AU (Chris Tham)
Subject: NetBSD-1.0 ft driver for QIC-40/80 tape drives shar file
Message-ID: <D2sJ7n.4rs@ucc.su.OZ.AU>
Sender: news@ucc.su.OZ.AU
Nntp-Posting-Host: extro.ucc.su.oz.au
Organization: Information Services, Sydney University, Sydney, NSW, Australia
Date: Sun, 22 Jan 1995 04:59:46 GMT
Lines: 6169

Note: this is a _long_ posting (about 160K) so if your site did not receive
this correctly, you may want to grab the uuencoded posting instead.

cheers, christie

--- cut here ---
# 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:
#
#	ft-netbsd
#	ft-netbsd/ft
#	ft-netbsd/ft/Makefile
#	ft-netbsd/ft/ft.8
#	ft-netbsd/ft/ft.c
#	ft-netbsd/ft/ftape.h
#	ft-netbsd/ft/ftecc.c
#	ft-netbsd/README
#	ft-netbsd/install.sh
#	ft-netbsd/sys
#	ft-netbsd/sys/fdc.h
#	ft-netbsd/sys/ft.c
#	ft-netbsd/sys/ftreg.h
#	ft-netbsd/sys/ftape.h
#	ft-netbsd/sys/fd.c.diffs
#	ft-netbsd/sys/fd.c.new
#	ft-netbsd/sys/ft.patch
#	ft-netbsd/dev
#	ft-netbsd/dev/MAKEDEV.local.diffs
#
echo c - ft-netbsd
mkdir ft-netbsd > /dev/null 2>&1
echo c - ft-netbsd/ft
mkdir ft-netbsd/ft > /dev/null 2>&1
echo x - ft-netbsd/ft/Makefile
sed 's/^X//' >ft-netbsd/ft/Makefile << 'END-of-ft-netbsd/ft/Makefile'
X#	$Id: Makefile,v 1.3 1994/06/22 04:49:02 jkh Exp $
X
XPROG=	ft
XMAN8=	ft.8
XSRCS=	ft.c ftecc.c
XCOPTS=	-O2 -finline-functions -funroll-loops -fexpensive-optimizations
X
X.include <bsd.prog.mk>
END-of-ft-netbsd/ft/Makefile
echo x - ft-netbsd/ft/ft.8
sed 's/^X//' >ft-netbsd/ft/ft.8 << 'END-of-ft-netbsd/ft/ft.8'
X.\" Copyright (c) 1980, 1989, 1991 The Regents of the University of California.
X.\" All rights reserved.
X.\"
X.\" Redistribution and use in source and binary forms, with or without
X.\" modification, are permitted provided that the following conditions
X.\" are met:
X.\" 1. Redistributions of source code must retain the above copyright
X.\"    notice, this list of conditions and the following disclaimer.
X.\" 2. Redistributions in binary form must reproduce the above copyright
X.\"    notice, this list of conditions and the following disclaimer in the
X.\"    documentation and/or other materials provided with the distribution.
X.\" 3. All advertising materials mentioning features or use of this software
X.\"    must display the following acknowledgement:
X.\"	This product includes software developed by the University of
X.\"	California, Berkeley and its contributors.
X.\" 4. Neither the name of the University nor the names of its contributors
X.\"    may be used to endorse or promote products derived from this software
X.\"    without specific prior written permission.
X.\"
X.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X.\" SUCH DAMAGE.
X.\"
X.\"     @(#)ft.8
X.\"
X.Dd February 7, 1994
X.Dt FT 8
X.Os BSD 4
X.Sh NAME
X.Nm ft
X.Nd QIC 40/80 floppy tape drive controller
X.Sh SYNOPSIS
X.Nm ft
X.Op Fl f Ar tape
X.Op Ar description
X.Sh DESCRIPTION
XThe 
X.Nm ft
Xcommand allows multi-volume dump, extract, and view of tape labels, for
Xany pre-formatted QIC-40/80 tapes.  It is totally system dependent,
Xand has nothing to do with the QIC standards.
X.Pp
X.Nm ft
Xis used primarily as a filter for tape i/o.
XFor example, to save and compress the /usr directory to tape:
X.Bd -literal -offset indent
X% tar cvzf - /usr | ft "/usr save"
X.Ed
X.Pp
XTo extract /usr from tape:
X.Bd -literal -offset indent
X% ft | tar xvzf -
X.Ed
X.\" .Sh SEE ALSO
X.\" .Xr qtar 1
X.Sh BUGS
XFormatting/Verifying is in the works.  You will need to use your
Xexisting backup program to do this for the time being.
X.Sh NOTES
XThe floppy tape driver supports tape drives such as the Colorado
XJumbo, Mountain Summit Express, some Archive/Conner models, and
Xprobably many others.  These tape drives attach between your floppy
Xdisk controller card and your existing floppy disks' ribbon cable.
XThis driver does not currently support attachments via a proprietary
Xtape controller card or by the parallel port.
X.Pp
XQIC-40/80 drives are more CPU intensive than a SCSI drive.  This is
Xreally only a factor if your machine is networked or has multiple concurrent
Xusers.  For personal use (i.e. your typical home Unix user), response time
Xis perfectly acceptable.  The tape drives cannot detect write errors.
XInstead, they make up for it by using CRC's, error correction, and bad
Xspot mapping.  Formatting time is extremely long because of this.  The
Xdrive makes a first pass over the entire tape writing out sectors.  It
Xthen makes a second pass at a slower rate than usual (for sensitivity)
Xto detect bad spots on the tape.  Typically it takes an hour to format
Xa single QIC-80 (120Mb uncompressed) tape.
X.Sh AUTHOR
XSteve Gerakines <steve2@genesis.nred.ma.us>
END-of-ft-netbsd/ft/ft.8
echo x - ft-netbsd/ft/ft.c
sed 's/^X//' >ft-netbsd/ft/ft.c << 'END-of-ft-netbsd/ft/ft.c'
X/*
X *  Copyright (c) 1993, 1994 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 - simple floppy tape filter
X *
X *  06/07/94 v1.0 ++sg
X *  Added support for tape retension.  Added retries for ecc failures.
X *  Moved to release.
X *
X *  01/28/94 v0.3b (Jim Babb)
X *  Fixed bug when all sectors in a segment are marked bad.
X *
X *  10/30/93 v0.3
X *  Minor revisions.  Seems pretty stable.
X *
X *  09/02/93 v0.2 pl01
X *  Initial revision.
X *
X *  usage: ft [ -f tape ] [ description ]
X */
X
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <signal.h>
X#include <time.h>
X#include <sys/ftape.h>
X
X#define	DEFQIC	"/dev/rft0"
X
Xchar buff[QCV_SEGSIZE];			/* scratch buffer */
Xchar hbuff[QCV_SEGSIZE];		/* header buffer */
XQIC_Header *hptr = (QIC_Header *)hbuff;	/* header structure */
Xint hsn = -1;				/* segment number of header */
Xint dhsn = -1;				/* segment number of duplicate header */
Xint tfd;				/* tape file descriptor */
XQIC_Geom geo;				/* tape geometry */
Xint tvno = 1;				/* tape volume number */
Xint tvlast;				/* TRUE if last volume in set */
Xlong tvsize = 0;			/* tape volume size in bytes */
Xlong tvtime = NULL;			/* tape change time */
Xchar *tvnote = "";			/* tape note */
Xint doretension = 0;			/* TRUE if we should retension tape */
X
X/* Lookup the badmap for a given track and segment. */
X#define BADMAP(t,s)	hptr->qh_badmap[(t)*geo.g_segtrk+(s)]
X
X/* Retrieve values from a character array. */
X#define UL_VAL(s,p)	(*((ULONG *)&(s)[p]))
X#define US_VAL(s,p)	(*((USHORT *)&(s)[p]))
X
X#define	equal(s1,s2)	(strcmp(s1, s2) == 0)
X
X
X/*
X *  Print tape usage and then leave.
X */
Xvoid
Xusage(void)
X{
X  fprintf(stderr, "usage: ft [ -r ] [ -f device ] [ \"description\" ]\n");
X  exit(1);
X}
X
X
X/*
X *  Check status of tape drive
X */
Xint
Xcheck_stat(int fd, int wr)
X{
X  int r, s;
X  int sawit = 0;
X
X  /* get tape status */
X  if (ioctl(fd, QIOSTATUS, &s) < 0) {
X	fprintf(stderr, "could not get drive status\n");
X	return(1);
X  }
X
X  /* wait for the tape drive to become ready */
X  while ((s & QS_READY) == 0) {
X	if (!sawit) {
X		fprintf(stderr, "waiting for drive to become ready...\n");
X		sawit = 1;
X	}
X	sleep(2);
X	if (ioctl(fd, QIOSTATUS, &s) < 0) {
X		fprintf(stderr, "could not get drive status\n");
X		return(1);
X	}
X  }
X 
X  if ((s & QS_FMTOK) == 0) {
X	fprintf(stderr, "tape is not formatted\n");
X	return(2);
X  }
X
X  if (wr && (s & QS_RDONLY) != 0) {
X	fprintf(stderr, "tape is write protected\n");
X	return(3);
X  }
X
X  return(0);
X}
X
X
X/*
X *  Convert time_t value to QIC time value.
X */
XULONG
Xqtimeval(time_t t)
X{
X  struct tm *tp;
X  ULONG r;
X
X  tp = localtime(&t);
X  r = 2678400 * tp->tm_mon +
X	86400 *(tp->tm_mday-1) +
X	 3600 * tp->tm_hour +
X	   60 * tp->tm_min +
X	        tp->tm_sec;
X  r |= (tp->tm_year - 70) << 25;
X  return(r);
X}
X
X
X/*
X *  Return tm struct from QIC date format.
X */
Xstruct tm *
Xqtime(UCHAR *qt)
X{
X  ULONG *vp = (ULONG *)qt;
X  struct tm t;
X  ULONG v;
X  time_t tv;
X
X  v = *vp;
X  t.tm_year = ((v >> 25) & 0x7f)+70; v &= 0x1ffffff;
X  t.tm_mon = v / 2678400; v %= 2678400;
X  t.tm_mday = v / 86400 + 1; v %= 86400;
X  t.tm_hour = v / 3600; v %= 3600;
X  t.tm_min = v / 60; v %= 60;
X  t.tm_sec = v;
X  t.tm_wday = 0;	/* XXX - let mktime do the real work */
X  t.tm_yday = 0;
X  t.tm_isdst = 0;
X  t.tm_gmtoff = 0;
X  t.tm_zone = NULL;
X  tv = mktime(&t);
X  return(localtime(&tv));
X}
X
X
X/*
X *  Return a string, zero terminated.
X */
Xchar *qstr(char *str, int nchar)
X{
X  static char tstr[256];
X  strncpy(tstr, str, nchar);
X  tstr[nchar] = '\0';
X  return(tstr);
X}
X
X
X/*
X *  Read header from tape
X */
Xint
Xget_header(int fd)
X{
X  int r, sn, bytes;
X  QIC_Segment s;
X  int gothdr = 0;
X
X  if (ioctl(fd, QIOGEOM, &geo) < 0) {
X	fprintf(stderr, "couldn't determine tape geometry\n");
X	return(1);
X  }
X
X  /* Get the header and duplicate */
X  for (sn = 0; sn < 16; sn++) {
X	s.sg_trk = 0;
X	s.sg_seg = sn;
X	s.sg_badmap = 0;
X	s.sg_data = (UCHAR *)&buff[0];
X	ioctl(fd, QIOREAD, &s);
X	r = check_parity(s.sg_data, 0, s.sg_crcmap);
X	if (s.sg_data[0] == 0x55 && s.sg_data[1] == 0xaa &&
X	    s.sg_data[2] == 0x55 && s.sg_data[3] == 0xaa) {
X		if (hsn >= 0) {
X			dhsn = sn;
X			if (!r && !gothdr) {
X				fprintf(stderr, "using secondary header\n");
X				bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
X				gothdr = 1;
X			}
X			break;
X		}
X		hsn = sn;
X		if (!r) {
X			bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
X			gothdr = 1;
X		} else {
X			fprintf(stderr, "too many errors in primary header\n");
X		}
X	}
X  }
X
X  if (!gothdr) {
X	fprintf(stderr, "couldn't read header segment\n");
X	ioctl(fd, QIOREWIND);
X	return(1);
X  }
X
X  return(0);
X}
X
X
X/*
X *  Open /dev/tty and ask for next volume.
X */
Xask_vol(int vn)
X{
X  FILE *inp;
X  int fd;
X  char c;
X
X  if ((fd = open("/dev/tty", 2)) < 0) {
X	fprintf(stderr, "argh!! can't open /dev/tty\n");
X	exit(1);
X  }
X
X  fprintf(stderr, "Insert ftfilt volume %02d and press enter:", vn);
X  read(fd, &c, 1);
X  close(fd);
X}
X
X
X/*
X *  Return the name of the tape only.
X */
Xvoid
Xdo_getname(void)
X{
X  if (check_stat(tfd, 0)) exit(1);
X  if (get_header(tfd)) exit(1);
X  fprintf(stderr, "\"%s\" - %s",
X		qstr(hptr->qh_tname,44), asctime(qtime(hptr->qh_chgdate)));
X}
X
X
X/*
X *  Extract data from tape to stdout.
X */
Xvoid
Xdo_read(void)
X{
X  int sno, vno, sbytes, r, eccfails;
X  long curpos;
X  char *hname;
X  QIC_Segment s;
X
X  /* Process multiple volumes if necessary */
X  vno = 1;
X  for (;;) {
X	if (check_stat(tfd, 0)) {
X		ask_vol(vno);
X		continue;
X	}
X
X	if (doretension) {
X		ioctl(tfd, QIOBOT);
X		ioctl(tfd, QIOEOT);
X		ioctl(tfd, QIOBOT);
X	}
X
X	if (get_header(tfd)) {
X		ask_vol(vno);
X		continue;
X	}
X
X	/* extract volume and header info from label */
X	hname = hptr->qh_tname;
X	hname[43] = '\0';
X	tvno = atoi(&hname[11]);
X	tvlast = (hname[10] == '*') ? 1 : 0;
X	tvsize = atoi(&hname[14]);
X	tvnote = &hname[25];
X	if (vno != tvno || strncmp(hname, "ftfilt", 6) != 0) {
X		fprintf(stderr, "Incorrect volume inserted.  This tape is:\n");
X		fprintf(stderr,"\"%s\" - %s\n", hname,
X				asctime(qtime(hptr->qh_chgdate)));
X		ask_vol(vno);
X		continue;
X	}
X
X	/* Process this volume */
X	curpos = 0;
X	eccfails = 0;
X	sno = hptr->qh_first;
X	while (tvsize > 0) {
X		s.sg_trk = sno / geo.g_segtrk;
X		s.sg_seg = sno % geo.g_segtrk;
X		s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg);
X		sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE;
X		s.sg_data = (UCHAR *)&buff[0];
X		if (sbytes <= 0) {
X			sno++;
X			continue;
X		}
X		if (ioctl(tfd, QIOREAD, &s) < 0) perror("QIOREAD");
X
X		if (check_parity(s.sg_data, s.sg_badmap, s.sg_crcmap)) {
X			if (++eccfails <= 5) {
X				fprintf(stderr,
X					"ft: retry %d at segment %d byte %ld\n",
X					eccfails, sno, curpos);
X				continue;
X			} else
X				fprintf(stderr,
X				"ft: *** ecc failure in segment %d at byte %ld\n",
X						sno, curpos);
X		}
X		if (tvsize < sbytes) sbytes = tvsize;
X		write(1, s.sg_data, sbytes);
X		tvsize -= sbytes;
X		curpos += sbytes;
X		sno++;
X		eccfails = 0;
X	}
X	if (tvlast) break;
X	ioctl(tfd, QIOREWIND);
X	ask_vol(++vno);
X  }
X}
X
X
X/*
X *  Dump data from stdin to tape.
X */
Xvoid
Xdo_write(void)
X{
X  int sno, vno, amt, sbytes;
X  int c, maxseg, r;
X  ULONG qnow;
X  QIC_Segment s;
X  char tmpstr[80];
X
X  qnow = qtimeval(time(NULL));
X  vno = 1;
X
X  for (;;) {
X	if (check_stat(tfd, 1)) {
X		ask_vol(vno);
X		continue;
X	}
X
X	if (doretension) {
X		ioctl(tfd, QIOBOT);
X		ioctl(tfd, QIOEOT);
X		ioctl(tfd, QIOBOT);
X	}
X
X	if (get_header(tfd)) {
X		ask_vol(vno);
X		continue;
X	}
X
X	maxseg = geo.g_segtrk * geo.g_trktape - 1;
X	sno = hptr->qh_first;
X	tvno = vno;
X	tvsize = 0;
X	tvlast = 0;
X
X	/* Process until end of volume or end of data */
X	for (sno = hptr->qh_first; sno < maxseg && tvlast == 0; ++sno) {
X		/* Prepare to load the next segment */
X		s.sg_trk = sno / geo.g_segtrk;
X		s.sg_seg = sno % geo.g_segtrk;
X		s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg);
X		sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE;
X		s.sg_data = (UCHAR *)&buff[0];
X
X		/* Ugh.  Loop to get the full amt. */
X		for (amt = 0; amt < sbytes; amt += r) {
X			r = read(0, &s.sg_data[amt], sbytes - amt);
X			if (r <= 0) {
X				tvlast = 1;
X				break;
X			}
X		}
X
X		/* skip the segment if *all* sectors are flagged as bad */
X		if (amt) {
X			if (amt < sbytes)
X				bzero(&s.sg_data[amt], sbytes - amt);
X			r = set_parity(s.sg_data, s.sg_badmap);
X			if (r) fprintf(stderr, "** warning: ecc problem !!\n");
X			if (ioctl(tfd, QIOWRITE, &s) < 0) {
X				perror("QIOWRITE");
X				exit(1);
X			}
X			tvsize += amt;
X		}
X	}
X
X	/* Build new header info */
X	/* ftfilt vol*xx yyyyyyyyyy note56789012345678  */
X	/* 01234567890123456789012345678901234567890123 */
X
X	sprintf(tmpstr, "ftfilt vol%s%02d %010d %s",
X		(tvlast) ? "*" : " ", tvno, tvsize, tvnote);
X	strncpy(hptr->qh_tname, tmpstr, 44);
X	UL_VAL(hptr->qh_chgdate,0) = qnow;
X
X	/* Update the header for this volume */
X	if (hsn >= 0) {
X		s.sg_trk = hsn / geo.g_segtrk;
X		s.sg_seg = hsn % geo.g_segtrk;
X		s.sg_badmap = 0;
X		s.sg_data = (UCHAR *)hbuff;
X		r = set_parity(s.sg_data, s.sg_badmap);
X		if (r) fprintf(stderr, "** warning: header ecc problem !!\n");
X		if (ioctl(tfd, QIOWRITE, &s) < 0) {
X			perror("QIOWRITE");
X			exit(1);
X		}
X	}
X	if (dhsn >= 0) {
X		s.sg_trk = dhsn / geo.g_segtrk;
X		s.sg_seg = dhsn % geo.g_segtrk;
X		s.sg_badmap = 0;
X		s.sg_data = (UCHAR *)hbuff;
X		r = set_parity(s.sg_data, s.sg_badmap);
X		if (r) fprintf(stderr, "** warning: duphdr ecc problem !!\n");
X		if (ioctl(tfd, QIOWRITE, &s) < 0) {
X			perror("QIOWRITE");
X			exit(1);
X			}
X	}
X	ioctl(tfd, QIOREWIND);
X	if (tvlast) break;
X	ask_vol(++vno);
X  }
X}
X
X
X/*
X *  Entry.
X */
Xvoid
Xmain(int argc, char *argv[])
X{
X  int r, s, i;
X  char *tape, *getenv();
X
X
X  /* Get device from environment, command line will override. */
X  if ((tape = getenv("TAPE")) == NULL) tape = DEFQIC;
X
X  /* Process args. */
X  for (i = 1; i < argc; i++) {
X	if (argv[i][0] != '-') break;
X	switch (argv[i][1]) {
X	    case 'f':
X	    case 't':
X		if (i == (argc - 1)) usage();
X		tape = argv[++i];
X		break;
X	    case 'r':
X		doretension = 1;
X		break;
X	    default:
X		usage();
X	}
X  }
X  if (i < (argc - 1)) usage();
X  if (i < argc) {
X	tvnote = argv[i];
X	if (strlen(tvnote) > 18) argv[i][18] = '\0';
X  }
X
X  /* Open the tape device */
X  if ((tfd = open(tape, 2)) < 0) {
X	perror(tape);
X	exit(1);
X  }
X
X  if (!isatty(0))
X	do_write();
X  else if (!isatty(1))
X	do_read();
X  else
X	do_getname();
X
X  close(tfd);
X  exit(0);
X}
END-of-ft-netbsd/ft/ft.c
echo x - ft-netbsd/ft/ftape.h
sed 's/^X//' >ft-netbsd/ft/ftape.h << 'END-of-ft-netbsd/ft/ftape.h'
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 *  ftape.h - QIC-40/80 floppy tape driver functions
X *  10/30/93 v0.3
X *  Set up constant values.  Added support to get hardware info.
X *
X *  08/07/93 v0.2
X *  Header file that sits in /sys/sys, first revision.  Support for
X *  ioctl functions added.
X */
X
X#ifndef	_FTAPE_H_
X#define	_FTAPE_H_
X
X#ifndef _IOCTL_H_
X#include <sys/ioctl.h>
X#endif
X
X/* Miscellaneous constant values */
X#define QCV_BLKSIZE	1024		/* Size of a block */
X#define QCV_SEGSIZE	32768		/* Size of a segment */
X#define QCV_BLKSEG	32		/* Blocks per segment */
X#define QCV_ECCSIZE	3072		/* Bytes ecc eats */
X#define QCV_ECCBLKS	3		/* Blocks ecc eats */
X#define QCV_NFMT	3		/* Number of tape formats */
X#define QCV_NLEN	5		/* Number of tape lengths */
X#define QCV_HDRMAGIC	0xaa55aa55	/* Magic for header segment */
X#define QCV_FSMAGIC	0x33cc33cc	/* Magic for fileset */
X
X#define UCHAR		unsigned char
X#define USHORT		unsigned short
X#define ULONG		unsigned long
X
X/* Segment request structure. */
Xtypedef struct qic_segment {
X	ULONG sg_trk;		/* Track number */
X	ULONG sg_seg;		/* Segment number */
X	ULONG sg_crcmap;	/* Returned bitmap of CRC errors */
X	ULONG sg_badmap;	/* Map of known bad sectors */
X	UCHAR *sg_data;		/* Segment w/bad blocks discarded */
X} QIC_Segment;
X
X/* Tape geometry structure. */
Xtypedef struct qic_geom {
X	int g_fmtno;			/* Format number */
X	int g_lenno;			/* Length number */
X	char g_fmtdesc[16];		/* Format text description */
X	char g_lendesc[16];		/* Length text description */
X	int g_trktape;			/* Number of tracks per tape */
X	int g_segtrk;			/* Number of segments per track */
X	int g_blktrk;			/* Number of blocks per track */
X	int g_fdtrk;			/* Floppy disk tracks */
X	int g_fdside;			/* Floppy disk sectors/side */
X} QIC_Geom;
X
X/* Tape hardware info */
Xtypedef struct qic_hwinfo {
X	int hw_make;			/* 10-bit drive make */
X	int hw_model;			/* 6-bit model */
X	int hw_rombeta;			/* TRUE if beta rom */
X	int hw_romid;			/* 8-bit rom ID */
X} QIC_HWInfo;
X
X/* Various ioctl() routines. */
X#define QIOREAD		_IOWR('q', 1, QIC_Segment)	/* Read segment     */
X#define QIOWRITE	_IOW('q', 2, QIC_Segment)	/* Write segment    */
X#define QIOREWIND	_IO('q', 3)			/* Rewind tape      */
X#define QIOBOT		_IO('q', 4)			/* Seek beg of trk  */
X#define QIOEOT		_IO('q', 5)			/* Seek end of trk  */
X#define QIOTRACK	_IOW('q', 6, int)		/* Seek to track    */
X#define QIOSEEKLP	_IO('q', 7)			/* Seek load point  */
X#define QIOFORWARD	_IO('q', 8)			/* Move tape fwd    */
X#define QIOSTOP		_IO('q', 9)			/* Stop tape	    */
X#define QIOPRIMARY	_IO('q', 10)			/* Primary mode     */
X#define QIOFORMAT	_IO('q', 11)			/* Format mode      */
X#define QIOVERIFY	_IO('q', 12)			/* Verify mode      */
X#define QIOWRREF	_IO('q', 13)			/* Write ref burst  */
X#define QIOSTATUS	_IOR('q', 14, int)		/* Get drive status */
X#define QIOCONFIG	_IOR('q', 15, int)		/* Get tape config  */
X#define QIOGEOM		_IOR('q', 16, QIC_Geom)		/* Get geometry	    */
X#define QIOHWINFO	_IOR('q', 17, QIC_HWInfo)	/* Get hardware inf */
X#define QIOSENDHDR	_IOW('q', 18, QIC_Segment)	/* Send header      */
X#define QIORECVHDR	_IOWR('q', 19, QIC_Segment)	/* Receive header   */
X
X/* QIC drive status bits. */
X#define QS_READY			0x01	/* Drive ready */
X#define QS_ERROR			0x02	/* Error detected */
X#define QS_CART				0x04	/* Tape in drive */
X#define QS_RDONLY			0x08	/* Write protect */
X#define QS_NEWCART			0x10	/* New tape inserted */
X#define QS_FMTOK			0x20	/* Tape is formatted */
X#define QS_BOT				0x40	/* Tape at beginning */
X#define QS_EOT				0x80	/* Tape at end */
X
X/* QIC configuration bits. */
X#define QCF_RTMASK			0x18	/* Rate mask */
X#define QCF_RT250			0x00	/* 250K bps */
X#define QCF_RT2				0x01	/* 2M bps */
X#define QCF_RT500			0x02	/* 500K bps */
X#define QCF_RT1				0x03	/* 1M bps */
X#define QCF_EXTRA			0x40	/* Extra length tape */
X#define QCF_QIC80			0x80	/* QIC-80 detected */
X
X/* QIC tape status bits. */
X#define QTS_FMMASK			0x0f	/* Tape format mask */
X#define QTS_LNMASK			0xf0	/* Tape length mask */
X#define QTS_QIC40			0x01	/* QIC-40 tape */
X#define QTS_QIC80			0x02	/* QIC-80 tape */
X#define QTS_QIC500			0x03	/* QIC-500 tape */
X#define QTS_LEN1			0x10	/* 205 ft/550 Oe */
X#define QTS_LEN2			0x20	/* 307.5 ft/550 Oe */
X#define QTS_LEN3			0x30	/* 295 ft/900 Oe */
X#define QTS_LEN4			0x40	/* 1100 ft/550 Oe */
X#define QTS_LEN5			0x50	/* 1100 ft/900 Oe */
X
X/* Tape header segment structure */
Xtypedef struct qic_header {
X	ULONG qh_sig;		/* Header signature 0x55aa55aa */
X	UCHAR qh_fmtc;		/* Format code */
X	UCHAR qh_unused1;
X	USHORT qh_hseg;		/* Header segment number */
X	USHORT qh_dhseg;	/* Duplicate header segment number */
X	USHORT qh_first;	/* First logical area data segment */
X	USHORT qh_last;		/* Last logical area data segment */
X	UCHAR qh_fmtdate[4];	/* Most recent format date */
X	UCHAR qh_chgdate[4];	/* Most recent tape change date */
X	UCHAR qh_unused2[2];
X	USHORT qh_tstrk;	/* Tape segments per track */
X	UCHAR qh_ttcart;	/* Tape tracks per cartridge */
X	UCHAR qh_mfside;	/* Max floppy sides */
X	UCHAR qh_mftrk;		/* Max floppy tracks */
X	UCHAR qh_mfsect;	/* Max floppy sector */
X	char qh_tname[44];	/* Tape name (ASCII, space filled) */
X	UCHAR qh_namdate[4];	/* Date tape was given a name */
X	USHORT qh_cprseg;	/* Compression map start segment */
X	UCHAR qh_unused3[48];
X	UCHAR qh_refmt;		/* Re-format flag */
X	UCHAR qh_unused4;
X	UCHAR qh_iocount[4];	/* I/O count for life of tape */
X	UCHAR qh_unused5[4];
X	UCHAR qh_ffmtdate[4];	/* Date first formatted */
X	USHORT qh_fmtcount;	/* Number of times formatted */
X	USHORT qh_badsect;	/* Failed sector count */
X	char qh_mfname[44];	/* Manufacturer name if pre-formatted */
X	char qh_mflot[44];	/* Manufacturer lot code */
X	UCHAR qh_unused6[22];
X	ULONG qh_fail[448];	/* Failed sector log */
X	ULONG qh_badmap[6912];	/* Bad sector map */
X} QIC_Header;
X
X/* Volume table of contents entry structure. */
Xtypedef struct qic_vtbl {
X	UCHAR vt_sig[4];	/* Signature "VTBL" if entry used */
X	USHORT vt_first;	/* Starting segment */
X	USHORT vt_last;		/* Ending segment */
X	char vt_vname[44];	/* Set name */
X	UCHAR vt_savdate[4];	/* Date saved */
X	UCHAR vt_flags;		/* Volume flags */
X	UCHAR vt_multi;		/* Multi cartidge sequence no. */
X	UCHAR vt_vext[26];	/* Extension data */
X	char vt_passwd[8];	/* Password for volume */
X	UCHAR vt_dirsize[4];	/* Directory section size */
X	UCHAR vt_dtasize[4];	/* Data section size */
X	USHORT vt_osver;	/* Operating System version */
X	char vt_label[16];	/* Source drive label */
X	UCHAR vt_ldev;		/* Logical device origin */
X	UCHAR vt_pdev;		/* Physical device origin */
X	UCHAR vt_cprtype;	/* Compression type */
X	UCHAR vt_ostype;	/* Operating System type */
X	UCHAR vt_ostype2;	/* Always zero ?? */
X	UCHAR vt_isocpr;	/* ISO compression type */
X	UCHAR vt_unused1[4];
X} QIC_VTbl;
X
X/* Data compression map structure. */
Xtypedef struct qic_dcmap {
X	UCHAR dc_sig[4];	/* Siguature "DCMS" */
X	USHORT dc_mlen;		/* Total map length */
X	UCHAR dc_unused1[6];
X	ULONG dc_offset[7421];	/* Byte offsets to segments */
X} QIC_DCMap;
X
X/* System specific file set structures - Unix */
Xtypedef struct qic_unix_set {
X	UCHAR fsu_perm;		/* Permissions */
X	UCHAR fsu_attr2;	/* More attributes */
X	UCHAR fsu_ctime[4];	/* Creation time */
X	UCHAR fsu_atime[4];	/* Last access time */
X	UCHAR fsu_inode[4];	/* i-node number */
X	UCHAR fsu_user[4];	/* User number */
X	UCHAR fsu_group[4];	/* Group number */
X	UCHAR fsu_major;	/* Major device number */
X	UCHAR fsu_minor;	/* Minor device number */
X	UCHAR fsu_nsize;	/* Name size */
X	UCHAR fsu_name;		/* Entry name starts here */
X} QIC_Unix_Set;
X
X/* File set structure */
Xtypedef struct qic_fileset {
X	UCHAR fs_size;		/* Size of fixed + system size - 1 */
X	UCHAR fs_attr;		/* Attributes */
X	UCHAR fs_mtime;		/* Modification time */
X	UCHAR fs_dsize[4];	/* Data size */
X} QIC_FileSet;
X
X#endif	/* _FTAPE_H_ */
END-of-ft-netbsd/ft/ftape.h
echo x - ft-netbsd/ft/ftecc.c
sed 's/^X//' >ft-netbsd/ft/ftecc.c << 'END-of-ft-netbsd/ft/ftecc.c'
X/*
X *  Copyright (c) 1994 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 *  ftecc.c - QIC-40/80 Reed-Solomon error correction
X *  05/30/94 v1.0 ++sg
X *  Did some minor optimization.  The multiply by 0xc0 was a dog so it
X *  was replaced with a table lookup.  Fixed a couple of places where
X *  bad sectors could go unnoticed.  Moved to release.
X *
X *  03/22/94 v0.4
X *  Major re-write.  It can handle everything required by QIC now.
X *
X *  09/14/93 v0.2 pl01
X *  Modified slightly to fit with my driver.  Based entirely upon David
X *  L. Brown's package.
X */
X#include <sys/ftape.h>
X
X/* Inverse matrix */
Xstruct inv_mat {
X  UCHAR log_denom;      /* Log of the denominator */
X  UCHAR zs[3][3];	/* The matrix */
X};
X
X
X/*
X *  Powers of x, modulo 255.
X */
Xstatic UCHAR alpha_power[] = {
X  0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
X  0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4,
X  0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb,
X  0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd,
X  0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31,
X  0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67,
X  0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc,
X  0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b,
X  0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4,
X  0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26,
X  0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21,
X  0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba,
X  0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30,
X  0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0,
X  0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3,
X  0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a,
X  0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9,
X  0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44,
X  0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef,
X  0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85,
X  0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6,
X  0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf,
X  0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff,
X  0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58,
X  0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a,
X  0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24,
X  0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8,
X  0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64,
X  0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2,
X  0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda,
X  0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77,
X  0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3, 0x01
X};
X
X
X/*
X *  Log table, modulo 255 + 1.
X */
Xstatic UCHAR alpha_log[] = {
X  0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a,
X  0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a,
X  0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1,
X  0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3,
X  0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83,
X  0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4,
X  0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35,
X  0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38,
X  0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70,
X  0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48,
X  0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24,
X  0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15,
X  0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f,
X  0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10,
X  0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7,
X  0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b,
X  0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08,
X  0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a,
X  0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91,
X  0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb,
X  0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2,
X  0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf,
X  0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52,
X  0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86,
X  0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc,
X  0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc,
X  0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8,
X  0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44,
X  0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1,
X  0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97,
X  0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5,
X  0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7
X};
X
X
X/*
X *  Multiplication table for 0xc0.
X */
Xstatic UCHAR mult_c0[] = {
X  0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9,
X  0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5,
X  0x38, 0xf8, 0x3f, 0xff, 0x36, 0xf6, 0x31, 0xf1,
X  0x24, 0xe4, 0x23, 0xe3, 0x2a, 0xea, 0x2d, 0xed,
X  0x70, 0xb0, 0x77, 0xb7, 0x7e, 0xbe, 0x79, 0xb9,
X  0x6c, 0xac, 0x6b, 0xab, 0x62, 0xa2, 0x65, 0xa5,
X  0x48, 0x88, 0x4f, 0x8f, 0x46, 0x86, 0x41, 0x81,
X  0x54, 0x94, 0x53, 0x93, 0x5a, 0x9a, 0x5d, 0x9d,
X  0xe0, 0x20, 0xe7, 0x27, 0xee, 0x2e, 0xe9, 0x29,
X  0xfc, 0x3c, 0xfb, 0x3b, 0xf2, 0x32, 0xf5, 0x35,
X  0xd8, 0x18, 0xdf, 0x1f, 0xd6, 0x16, 0xd1, 0x11,
X  0xc4, 0x04, 0xc3, 0x03, 0xca, 0x0a, 0xcd, 0x0d,
X  0x90, 0x50, 0x97, 0x57, 0x9e, 0x5e, 0x99, 0x59,
X  0x8c, 0x4c, 0x8b, 0x4b, 0x82, 0x42, 0x85, 0x45,
X  0xa8, 0x68, 0xaf, 0x6f, 0xa6, 0x66, 0xa1, 0x61,
X  0xb4, 0x74, 0xb3, 0x73, 0xba, 0x7a, 0xbd, 0x7d,
X  0x47, 0x87, 0x40, 0x80, 0x49, 0x89, 0x4e, 0x8e,
X  0x5b, 0x9b, 0x5c, 0x9c, 0x55, 0x95, 0x52, 0x92,
X  0x7f, 0xbf, 0x78, 0xb8, 0x71, 0xb1, 0x76, 0xb6,
X  0x63, 0xa3, 0x64, 0xa4, 0x6d, 0xad, 0x6a, 0xaa,
X  0x37, 0xf7, 0x30, 0xf0, 0x39, 0xf9, 0x3e, 0xfe,
X  0x2b, 0xeb, 0x2c, 0xec, 0x25, 0xe5, 0x22, 0xe2,
X  0x0f, 0xcf, 0x08, 0xc8, 0x01, 0xc1, 0x06, 0xc6,
X  0x13, 0xd3, 0x14, 0xd4, 0x1d, 0xdd, 0x1a, 0xda,
X  0xa7, 0x67, 0xa0, 0x60, 0xa9, 0x69, 0xae, 0x6e,
X  0xbb, 0x7b, 0xbc, 0x7c, 0xb5, 0x75, 0xb2, 0x72,
X  0x9f, 0x5f, 0x98, 0x58, 0x91, 0x51, 0x96, 0x56,
X  0x83, 0x43, 0x84, 0x44, 0x8d, 0x4d, 0x8a, 0x4a,
X  0xd7, 0x17, 0xd0, 0x10, 0xd9, 0x19, 0xde, 0x1e,
X  0xcb, 0x0b, 0xcc, 0x0c, 0xc5, 0x05, 0xc2, 0x02,
X  0xef, 0x2f, 0xe8, 0x28, 0xe1, 0x21, 0xe6, 0x26,
X  0xf3, 0x33, 0xf4, 0x34, 0xfd, 0x3d, 0xfa, 0x3a
X};
X
X
X/*
X *  Return number of sectors available in a segment.
X */
Xint
Xsect_count(ULONG badmap)
X{
X  int i, amt;
X
X  for (amt = QCV_BLKSEG, i = 0; i < QCV_BLKSEG; i++)
X	if (badmap & (1 << i)) amt--;
X  return(amt);
X}
X
X
X/*
X *  Return number of bytes available in a segment.
X */
Xint
Xsect_bytes(ULONG badmap)
X{
X  int i, amt;
X
X  for (amt = QCV_SEGSIZE, i = 0; i < QCV_BLKSEG; i++)
X	if (badmap & (1 << i)) amt -= QCV_BLKSIZE;
X  return(amt);
X}
X
X
X/*
X *  Multiply two numbers in the field.
X */
Xstatic inline UCHAR
Xmultiply(UCHAR a, UCHAR b)
X{
X  int tmp;
X
X  if (!a || !b) return(0);
X  tmp = alpha_log[a] + alpha_log[b];
X  if (tmp > 254) tmp -= 255;
X  return(alpha_power[tmp]);
X}
X
X
X/*
X *  Multiply by an exponent.
X */
Xstatic inline UCHAR
Xmultiply_out(UCHAR a, int b)
X{
X  int tmp;
X
X  if (!a) return(0);
X  tmp = alpha_log[a] + b;
X  if (tmp > 254) tmp -= 255;
X  return(alpha_power[tmp]);
X}
X
X
X/*
X *  Divide two numbers.
X */
Xstatic inline UCHAR
Xdivide(UCHAR a, UCHAR b)
X{
X  int tmp;
X
X  if (!a || !b) return(0);
X  tmp = alpha_log[a] - alpha_log[b];
X  if (tmp < 0) tmp += 255;
X  return (alpha_power[tmp]);
X}
X
X
X/*
X *  Divide using exponent.
X */
Xstatic inline UCHAR
Xdivide_out(UCHAR a, UCHAR b)
X{
X  int tmp;
X
X  if (!a) return 0;
X  tmp = alpha_log[a] - b;
X  if (tmp < 0) tmp += 255;
X  return (alpha_power[tmp]);
X}
X
X
X/*
X *  This returns the value z^{a-b}.
X */
Xstatic inline UCHAR
Xz_of_ab(UCHAR a, UCHAR b)
X{
X  int tmp = a - b;
X
X  if (tmp < 0) tmp += 255;
X  return(alpha_power[tmp]);
X}
X
X
X/*
X *  Calculate the inverse matrix for two or three errors.  Returns 0
X *  if there is no inverse or 1 if successful.
X */
Xstatic inline int
Xcalculate_inverse(int nerrs, int *pblk, struct inv_mat *inv)
X{
X  /* First some variables to remember some of the results. */
X  UCHAR z20, z10, z21, z12, z01, z02;
X  UCHAR i0, i1, i2;
X  UCHAR iv0, iv1, iv2;
X
X  if (nerrs < 2) return(1);
X  if (nerrs > 3) return(0);
X
X  i0 = pblk[0]; i1 = pblk[1]; i2 = pblk[2];
X  if (nerrs == 2) {
X	/* 2 errs */
X	z01 = alpha_power[255 - i0];
X	z02 = alpha_power[255 - i1];
X	inv->log_denom = (z01 ^ z02);
X	if (!inv->log_denom) return(0);
X	inv->log_denom = 255 - alpha_log[inv->log_denom];
X
X	inv->zs[0][0] = multiply_out(  1, inv->log_denom);
X	inv->zs[0][1] = multiply_out(z02, inv->log_denom);
X	inv->zs[1][0] = multiply_out(  1, inv->log_denom);
X	inv->zs[1][1] = multiply_out(z01, inv->log_denom);
X  } else {
X	/* 3 errs */
X	z20 = z_of_ab (i2, i0);
X	z10 = z_of_ab (i1, i0);
X	z21 = z_of_ab (i2, i1);
X	z12 = z_of_ab (i1, i2);
X	z01 = z_of_ab (i0, i1);
X	z02 = z_of_ab (i0, i2);
X	inv->log_denom = (z20 ^ z10 ^ z21 ^ z12 ^ z01 ^ z02);
X	if (!inv->log_denom) return(0);
X	inv->log_denom = 255 - alpha_log[inv->log_denom];
X
X	iv0 = alpha_power[255 - i0];
X	iv1 = alpha_power[255 - i1];
X	iv2 = alpha_power[255 - i2];
X	i0 = alpha_power[i0];
X	i1 = alpha_power[i1];
X	i2 = alpha_power[i2];
X	inv->zs[0][0] = multiply_out(i1 ^ i2, inv->log_denom);
X	inv->zs[0][1] = multiply_out(z21 ^ z12, inv->log_denom);
X	inv->zs[0][2] = multiply_out(iv1 ^ iv2, inv->log_denom);
X	inv->zs[1][0] = multiply_out(i0 ^ i2, inv->log_denom);
X	inv->zs[1][1] = multiply_out(z20 ^ z02, inv->log_denom);
X	inv->zs[1][2] = multiply_out(iv0 ^ iv2, inv->log_denom);
X	inv->zs[2][0] = multiply_out(i0 ^ i1, inv->log_denom);
X	inv->zs[2][1] = multiply_out(z10 ^ z01, inv->log_denom);
X	inv->zs[2][2] = multiply_out(iv0 ^ iv1, inv->log_denom);
X  }
X  return(1);
X}
X
X
X/*
X *  Determine the error magnitudes for a given matrix and syndromes.
X */
Xstatic inline void
Xdetermine(int nerrs, struct inv_mat *inv, UCHAR *ss, UCHAR *es)
X{
X  UCHAR tmp;
X  int i, j;
X
X  for (i = 0; i < nerrs; i++) {
X	es[i] = 0;
X	for (j = 0; j < nerrs; j++)
X		es[i] ^= multiply(ss[j], inv->zs[i][j]);
X  }
X}
X
X
X/*
X *  Compute the 3 syndrome values.
X */
Xstatic inline int
Xcompute_syndromes(UCHAR *data, int nblks, int col, UCHAR *ss)
X{
X  UCHAR r0, r1, r2, t1, t2;
X  UCHAR *rptr;
X
X  rptr = data + col;
X  data += nblks << 10;
X  r0 = r1 = r2 = 0;
X  while (rptr < data) {
X	t1 = *rptr ^ r0;
X	t2 = mult_c0[t1];
X	r0 = t2 ^ r1;
X	r1 = t2 ^ r2;
X	r2 = t1;
X	rptr += QCV_BLKSIZE;
X  }
X  if (r0 || r1 || r2) {
X	ss[0] = divide_out(r0 ^ divide_out(r1 ^ divide_out(r2, 1), 1), nblks);
X	ss[1] = r0 ^ r1 ^ r2;
X	ss[2] = multiply_out(r0 ^ multiply_out(r1 ^ multiply_out(r2, 1), 1), nblks);
X	return(0);
X  }
X  return(1);
X}
X
X
X/*
X *  Calculate the parity bytes for a segment, returns 0 on success (always).
X */
Xint
Xset_parity (UCHAR *data, ULONG badmap)
X{
X  UCHAR r0, r1, r2, t1, t2;
X  UCHAR *rptr;
X  int max, row, col;
X
X  max = sect_count(badmap) - 3;
X  col = QCV_BLKSIZE;
X  while (col--) {
X	rptr = data;
X	r0 = r1 = r2 = 0;
X	row = max;
X	while (row--) {
X		t1 = *rptr ^ r0;
X		t2 = mult_c0[t1];
X		r0 = t2 ^ r1;
X		r1 = t2 ^ r2;
X		r2 = t1;
X		rptr += QCV_BLKSIZE;
X	}
X	*rptr = r0; rptr += QCV_BLKSIZE;
X	*rptr = r1; rptr += QCV_BLKSIZE;
X	*rptr = r2;
X	data++;
X  }
X  return(0);
X}
X
X
X/*
X *  Check and correct errors in a block.  Returns 0 on success,
X *  1 if failed.
X */
Xint
Xcheck_parity(UCHAR *data, ULONG badmap, ULONG crcmap)
X{
X  int crcerrs, eblk[3];
X  int col, row;
X  int i, j, nblks;
X  UCHAR ss[3], es[3];
X  int i1, i2, saverrs;
X  struct inv_mat inv;
X
X  nblks = sect_count(badmap);
X
X  /* Count the number of CRC errors and note their locations. */
X  crcerrs = 0;
X  if (crcmap) {
X	for (i = 0; i < nblks; i++) {
X		if (crcmap & (1 << i)) {
X			if (crcerrs == 3) return(1);
X			eblk[crcerrs++] = i;
X		}
X	}
X  }
X
X  /* Calculate the inverse matrix */
X  if (!calculate_inverse(crcerrs, eblk, &inv)) return(1);
X
X  /* Scan each column for problems and attempt to correct. */
X  for (col = 0; col < QCV_BLKSIZE; col++) {
X	if (compute_syndromes(data, nblks, col, ss)) continue;
X	es[0] = es[1] = es[2] = 0;
X
X	/* Analyze the error situation. */
X	switch (crcerrs) {
X	    case 0:	/* 0 errors >0 failures */
X		if (!ss[0]) return(1);
X		eblk[crcerrs] = alpha_log[divide(ss[1], ss[0])];
X		if (eblk[crcerrs] >= nblks) return(1);
X		es[0] = ss[1];
X		if (++crcerrs > 3) return(1);
X		break;
X
X	    case 1:	/* 1 error (+ possible failures) */
X		i1 = ss[2] ^ multiply_out(ss[1], eblk[0]);
X		i2 = ss[1] ^ multiply_out(ss[0], eblk[0]);
X		if (!i1 && !i2) {			/* only 1 error */
X			inv.zs[0][0] = alpha_power[eblk[0]];
X			inv.log_denom = 0;
X		} else if (!i1 || !i2) {		/* too many errors */
X			return(1);
X		} else {				/* add failure */
X			eblk[crcerrs] = alpha_log[divide(i1, i2)];
X			if (eblk[crcerrs] >= nblks) return(1);
X			if (++crcerrs > 3) return(1);
X			if (!calculate_inverse(crcerrs, eblk, &inv)) return(1);
X		}
X		determine(crcerrs, &inv, ss, es);
X		break;
X
X	    case 2:	/* 2 errors */
X	    case 3:	/* 3 errors */
X		determine(crcerrs, &inv, ss, es);
X		break;
X
X	    default:
X		return(1);
X	}
X
X	/* Make corrections. */
X	for (i = 0; i < crcerrs; i++) {
X		data[(eblk[i] << 10) | col] ^= es[i];
X		ss[0] ^= divide_out(es[i], eblk[i]);
X		ss[1] ^= es[i];
X		ss[2] ^= multiply_out(es[i], eblk[i]);
X	}
X	if (ss[0] || ss[1] || ss[2]) return(1);
X  }
X  return(0);
X}
END-of-ft-netbsd/ft/ftecc.c
echo x - ft-netbsd/README
sed 's/^X//' >ft-netbsd/README << 'END-of-ft-netbsd/README'
XQIC-40/80 floppy tape driver for NetBSD-1.0
XChris Tham <christie@extro.ucc.su.oz.au>
XVersion 1.0 dated Sun Jan 22 14:33:53 EST 1995
X
XBased on the FreeBSD 2.0 ft driver written by
XSteve Gerakines <steve2@genesis.nred.ma.us>
X
X
X                          Introduction
X                          ============
X
XThis driver supports QIC-40/80 tape drives connected to the floppy disk
Xcontroller such as such as the Colorado Jumbo, Mountain Summit Express,
Xsome Archive/Conner models, and probably many others.
X
XThese tape drives attach between your floppy disk controller card and
Xyour existing floppy disks' ribbon cable.  This driver does not currently
Xsupport attachments via a proprietary tape controller card or by the
Xparallel port.
X
XQIC-40/80 drives have become extremely popular, mostly because of their
Xcapacity and cheap price.  To clarify some of the mystery behind them,
Xhere are a few of their pro's and cons.
X
XFirst the cons.  They are more CPU intensive than a SCSI drive.  This is
Xreally only a factor if your machine is networked or has multiple concurrent
Xusers.  For personal use (i.e. your typical home Unix user), response time
Xis perfectly acceptable.  The tape drives cannot detect write errors.
XInstead, they make up for it by using CRC's, error correction, and bad
Xspot mapping.  Formatting time is extremely long because of this.  The
Xdrive makes a first pass over the entire tape writing out sectors.  It
Xthen makes a second pass at a slower rate than usual (for sensitivity)
Xto detect bad spots on the tape.  Typically it takes an hour to format
Xa single QIC-80 (120Mb uncompressed) tape.
X
XNow for the good news.  A QIC-80 drive can hold 250Mb of compressed data
X(or more depending on what type of tape is used).  A QIC-40 drive can
Xhold 120Mb compressed.  Since the drive itself is fairly dumb and you use
Xyour existing floppy disk controller for the interface, the prices are much
Xlower compared to other drives.  (Good for your wallet, painful for the
Xprogrammer. :-))  Backup and restore speeds are good.  As long as the error
Xcorrection being used, backups are also very reliable.
X
X
X                           What You Get
X                           ============
X
XHere's what's included in this package:
X
X o The NetBSD floppy tape driver for your kernel.  QIC-40 and 80 drives
X   will be probed/attached upon boot.  New ioctl()'s to control the drives.
X   Tested with NetBSD-1.0.  The code currently only supports one drive per
X   floppy controller, although the controller can probably support two
X   drives.
X
X o ft - a backup/restore filter to save your system
X
XWhat the driver cannot do yet:
X
X o Formatting/Verifying is in the works.  You will need to use your
X   existing backup program to do this for the time being.
X
X o Error correction is not as robust as I'd like.
X
X
X                       Driver Installation
X                       ===================
X
XYou may want to look at the file install.sh which will automate the
Xinstallation for you.  WARNING:  I HAVE NOT TESTED THIS SCRIPT.  IT IS
XPROVIDED TO GIVE YOU AN IDEA OF WHAT NEEDS TO BE DONE TO INSTALL THE DRIVER.
XUSE AT YOUR OWN RISK!  If you don't want to run install.sh, just execute
Xthe following steps:
X
X1. Place the fdc.h, ft.c and ftreg.h files (in the sys directory) in your
X   /sys/arch/i386/isa directory.
X
X2. Place the sys/ftape.h file in your /usr/include/sys directory.
X
X	% install -c -o bin -g bin -m 444 ftape.h /usr/include/sys
X
X3. Apply the patch file fd.c.diffs to your existing /sys/arch/i386/isa/fd.c
X   file.  If you are running vanilla NetBSD-1.0, you may want to replace
X   the existing fd.c file with fd.c.new in the sys directory.
X
X	% cd /sys
X	% patch -p < <ft-netbsd directory>/sys/fd.c.diffs
X
X   or
X
X	% mv /sys/arch/i386/isa/fd.c /sys/arch/i386/isa/fd.c.old
X	% cp sys/fd.c.new /sys/arch/i386/isa/fd.c
X
X4. Modify your /sys/arch/i386/conf/files.i386 file to contain:
X
X	arch/i386/isa/ft.c	optional ft device-driver requires fd isa
X
X   Or, you may want to patch the file using sys/ft.patch (see step #6).
X
X5. Modify your config (/sys/arch/i386/conf/MYCONFIG) file to have a line
X   like this:
X
X	controller	fdc0	at isa? port "IO_FD1" irq 6 drq 2
X	disk		fd0	at fdc0 drive ?
X	disk		fd1	at fdc0 drive ?
X   ===>	tape		ft0	at fdc0 drive ?
X
X   Note that you do not necessarily have to have two floppy drives.
X   I have not tested installing the driver with an ft unit number other than
X   zero.
X
X6. Now you need to pick a major block device number and major character
X   device number for the floppy tape drive.  In vanilla NetBSD-1.0, I have
X   chosen a major block device number of 8 and a major character device number
X   of 11.
X
X   You can choose any number you like (provided the block/character
X   major numbers are currently unused), but make sure you make the
X   necessary changes consistently in /sys/arch/i386/conf/devices.i386,
X   /sys/arch/i386/i386/conf.c, and /dev/MAKEDEV.local!
X
X   An easy way to patch all three files (if you use my device numbering)
X   is to apply the sys/ft.patch and dev/MAKEDEV.local.diffs patches:
X
X	% cd /sys
X	% patch -p < <ft-netbsd directory>/sys/ft.patch
X	% cd /dev
X	% patch -p < <ft-netbsd directory>/dev/MAKEDEV.local.diffs
X
X   Otherwise, apply the changes yourself by looking at the patch files.
X
X6. Create the necessary devices:
X
X	% cd /dev
X	% sh MAKEDEV.local ft0
X
X   or
X
X	% cd /dev
X	% mknod ft0 b 8 0
X	% mknod rft0 c 11 0
X
X7. Recompile your kernel and reboot.
X
X	% cd /sys/arch/i386/conf
X	% config MYCONFIG
X	% cd /sys/arch/i386/compile/MYCONFIG
X	% make depend; make
X	% cp /netbsd /netbsd.old
X	% cp netbsd /
X	% fastboot
X
X   When you reboot, your kernel will display a message indicating it has
X   successfully attached a tape drive to the floppy controller, like this:
X
X	ft0 at fdc0 drive 2: [unit 0: Colorado tape]
X
X   The actual drive number will depend on how many floppy devices you have
X   connected to your system.  It should also display the correct tape
X   manufacturer.
X
X
X                        Tools Installation
X                        ==================
X
XGo to the directory where you unpacked everything and type:
X
X	% cd ft
X	% make LDSTATIC=-static
X	% install -c -s -o bin -g bin -m 555 ft /sbin
X	% nroff -man ft.8 > ft.0
X	% install -c -o bin -g bin -m 444 ft.0 /usr/share/man/cat8)
X
XThe "ft" program will allow
Xyou to do a multi-volume dump, extract, and view of tape labels, for
Xany pre-formatted tapes.  This program is totally system dependent,
Xand has nothing to do with the QIC standards.  It is being provided
Xso that for the time being, you will be able to back up your system,
Xsleep easier at night, and maybe even trade tapes with your <glob>BSD
Xfriends.
X
XThe command syntax looks like this:
X
X	usage: ft [-f device] [ "description" ]
X
XThe description is optional while writing tapes and may be up to
X18 characters in length.
X
XFor example, if I was using tar to do my archives:
X
X% tar cvf - /usr/bin | ft "/usr/bin save"
X
XThis would save and compress my /usr/bin directory to tape.
X
X% ft
X
XThis would display the tape label.
X
X% ft | tar tvf -
X
XWould list all the files I just saved.
X
X% ft | tar xvf -
X
XThis would extract my /usr/bin directory from tape.
X
X% ft | tar dvf -
X
XThis will display all the files in the save that are different than
Xwhat's really on disk.  This is always an excellent idea after doing
Xa backup, just to make sure everything was written okay.
X
X
X                        That's all Folks!
X                        =================
X
XPlease report any problems to me at christie@extro.ucc.su.oz.au
END-of-ft-netbsd/README
echo x - ft-netbsd/install.sh
sed 's/^X//' >ft-netbsd/install.sh << 'END-of-ft-netbsd/install.sh'
X#!/bin/sh
Xcp sys/ftape.h /usr/include/sys
X(cd ft; echo Making ft filter
Xmake LDSTATIC=-static
Xinstall -c -s -o bin -g bin -m 555 ft /sbin
Xnroff -man ft.8 > ft.0
Xinstall -c -o bin -g bin -m 444 ft.0 /usr/share/man/cat8)
X(cd sys; echo Patching kernel source directories
Xcp fdc.h ft.c ftreg.h /sys/arch/i386/isa
Xsysdir=`pwd`
X# Uncomment if you have not modified fd.c in NetBSD-1.0
X# mv /sys/arch/i386/isa/fd.c /sys/arch/i386/isa/fd.c.old
X# cp fd.c.new /sys/arch/i386/isa/fd.c
X# Otherwise do the diffs - its safer!
Xcd /sys; patch -p < $sysdir/fd.c.diffs
Xcd /sys; patch -p < $sysdir/ft.patch)
X(cd dev; echo Making floppy tape /dev/ft0 and /dev/rft0 devices
Xdevdir=`pwd`
Xcd /dev; patch < $devdir/MAKEDEV.local.diffs
Xsh MAKEDEV.local ft0)
END-of-ft-netbsd/install.sh
echo c - ft-netbsd/sys
mkdir ft-netbsd/sys > /dev/null 2>&1
echo x - ft-netbsd/sys/fdc.h
sed 's/^X//' >ft-netbsd/sys/fdc.h << 'END-of-ft-netbsd/sys/fdc.h'
X/*-
X * Copyright (c) 1991 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by the University of
X *	California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X *	fdc.h
X */
X
X#define FDUNIT(dev)	(minor(dev) / 8)
X#define FDTYPE(dev)	(minor(dev) % 8)
X
X#define FDC_ATTACHED	0x01
X#define FDC_HASFTAPE	0x02
X#define FDC_TAPE_BUSY	0x04
X#define FDC_STAT_VALID	0x08
X
Xenum fdc_state {
X	DEVIDLE = 0,
X	MOTORWAIT,
X	DOSEEK,
X	SEEKWAIT,
X	SEEKTIMEDOUT,
X	SEEKCOMPLETE,
X	DOIO,
X	IOCOMPLETE,
X	IOTIMEDOUT,
X	DORESET,
X	RESETCOMPLETE,
X	RESETTIMEDOUT,
X	DORECAL,
X	RECALWAIT,
X	RECALTIMEDOUT,
X	RECALCOMPLETE,
X};
X
X/* software state, per controller */
Xstruct fdc_softc {
X	struct device sc_dev;		/* boilerplate */
X	struct isadev sc_id;
X	struct intrhand sc_ih;
X
X	u_short	sc_iobase;
X	u_short	sc_drq;
X	int	sc_flags;
X
X	struct fd_softc *sc_fd[4];	/* pointers to children */
X	TAILQ_HEAD(drivehead, fd_softc) sc_drives;
X	enum fdc_state sc_state;
X	int sc_errors;			/* number of retries so far */
X	u_char sc_status[7];		/* copy of registers */
X};
X
X/*
X * Arguments passed between fdcattach and fdprobe.
X */
Xstruct fdc_attach_args {
X	int fa_drive;
X	struct fd_type *fa_deftype;
X};
END-of-ft-netbsd/sys/fdc.h
echo x - ft-netbsd/sys/ft.c
sed 's/^X//' >ft-netbsd/sys/ft.c << 'END-of-ft-netbsd/sys/ft.c'
X/*
X *  Copyright (c) 1993, 1994 Steve Gerakines
X *
X *  Modified for NetBSD-1.0 by Chris Tham (christie@extro.ucc.su.oz.au)
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 - QIC-40/80 floppy tape driver
X *  $Id: ft.c,v 1.15 1994/12/04 03:10:09 jkh Exp $
X *
X *  06/07/94 v0.9 ++sg
X *  Tape stuck on segment problem should be gone.  Re-wrote buffering
X *  scheme.  Added support for drives that do not automatically perform
X *  seek load point.  Can handle more wakeup types now and should correctly
X *  report most manufacturer names.  Fixed places where unit 0 was being
X *  sent to the fdc instead of the actual unit number.  Added ioctl support
X *  for an in-core badmap.
X *
X *  01/26/94 v0.3b - Jim Babb
X *  Got rid of the hard coded device selection.  Moved (some of) the
X *  static variables into a structure for support of multiple devices.
X *  ( still has a way to go for 2 controllers - but closer )
X *  Changed the interface with fd.c so we no longer 'steal' it's 
X *  driver routine vectors.
X * 
X *  10/30/93 v0.3
X *  Fixed a couple more bugs.  Reading was sometimes looping when an
X *  an error such as address-mark-missing was encountered.  Both
X *  reading and writing was having more backup-and-retries than was
X *  necessary.  Added support to get hardware info.  Updated for use
X *  with FreeBSD.
X *
X *  09/15/93 v0.2 pl01
X *  Fixed a bunch of bugs:  extra isa_dmadone() in async_write() (shouldn't
X *  matter), fixed double buffering in async_req(), changed tape_end() in
X *  set_fdcmode() to reduce unexpected interrupts, changed end of track
X *  processing in async_req(), protected more of ftreq_rw() with an
X *  splbio().  Changed some of the ftreq_*() functions so that they wait
X *  for inactivity and then go, instead of aborting immediately.
X *
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 <sys/param.h>
X#include <sys/systm.h>
X#include <sys/kernel.h>
X#include <sys/conf.h>
X#include <sys/ioctl.h>
X#include <sys/device.h>
X#include <sys/disklabel.h>	/* temp. for dkunit() in fdc.h */
X#include <sys/file.h>
X#include <sys/proc.h>
X#include <sys/malloc.h>
X#include <sys/buf.h>
X#include <sys/uio.h>
X#include <sys/ftape.h>
X
X#include <machine/cpu.h>
X#include <machine/pio.h>
X
X#ifndef NEWCONFIG
X#include <i386/isa/isa_device.h>
X#endif
X
X#include <i386/isa/isavar.h>
X#include <i386/isa/dmavar.h>
X#include <i386/isa/icu.h>
X
X#include <i386/isa/fdreg.h>
X#include <i386/isa/ftreg.h>
X#include <i386/isa/fdc.h>
X
X#ifdef NEWCONFIG
X#include <i386/isa/nvram.h>
X#else
X#include <i386/isa/rtc.h>
X#endif
X
X#ifdef DDB
Xint	Debugger();
X#else
X#define Debugger()
X#endif
X
X/* Enable or disable debugging messages. */
X#define FTDBGALL 0			/* 1 if you want everything */
X#if FTDBGALL
X#define DPRT(a) printf a
X#else
X#define DPRT(a) 
X#endif
X
X/* Constants private to the driver */
X#define FTPRI		(PRIBIO)	/* sleep priority */
X#define	FTNBUFF		9		/* 8 for buffering, 1 for header */
X
Xextern int hz;				/* system clock rate */
X
X/* Type of tape attached */
X/* use numbers that don't interfere with the possible floppy types */
X#define F_TAPE_TYPE	0x10
X#define FT_NOTYPE 0
X
X#define FT_NONE		(F_TAPE_TYPE | 0)	/* no method required */
X#define	FT_MOUNTAIN	(F_TAPE_TYPE | 1)	/* mountain */
X#define	FT_COLORADO	(F_TAPE_TYPE | 2)	/* colorado */
X#define FT_INSIGHT	(F_TAPE_TYPE | 3)	/* insight */
X
X/* Mode FDC is currently in: tape or disk */
Xenum { FDC_TAPE_MODE, FDC_DISK_MODE };
X
X/* Command we are awaiting completion of */
Xenum { FTCMD_NONE, FTCMD_RESET, FTCMD_RECAL, FTCMD_SEEK, FTCMD_READID };
X
X/* Tape interrupt status of current request */
Xenum { FTSTS_NONE, FTSTS_SNOOZE, FTSTS_INTERRUPT, FTSTS_TIMEOUT };
X
X/* Tape I/O status */
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};
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 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_arg0;		/* up to 3 arguments for async cmds */
Xstatic int async_arg1;		/**/
Xstatic int async_arg2;		/**/
Xstatic int async_ret;		/* return value */
Xstatic struct _astk {
X	int over_func;
X	int over_state;
X	int over_retries;
X	int over_arg0;
X	int over_arg1;
X	int over_arg2;
X} astk[10];
Xstatic struct _astk *astk_ptr = &astk[0]; /* Pointer to stack position */
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) \
X			astk_ptr->over_retries = async_retries; \
X			astk_ptr->over_func = async_func; \
X			astk_ptr->over_state = (r); \
X			astk_ptr->over_arg0 = async_arg0; \
X			astk_ptr->over_arg1 = async_arg1; \
X			astk_ptr->over_arg2 = async_arg2; \
X			async_func = (f); async_state = 0; async_retries = 0; \
X			async_arg0=(a); async_arg1=(b); async_arg2=(c); \
X			astk_ptr++; \
X			goto restate
X
X/* Perform an asyncronous command from outside async_cmd(). */
X#define ACMD_FUNC(r,f,a,b,c) over_async = (r); astk_ptr = &astk[0]; \
X			async_func = (f); async_state = 0; async_retries = 0; \
X			async_arg0=(a); async_arg1=(b); async_arg2=(c); \
X			async_cmd(ft); \
X			return
X
X/* Various wait channels */
Xstatic char *wc_buff_avail	= "bavail";
Xstatic char *wc_buff_done	= "bdone";
Xstatic char *wc_iosts_change	= "iochg";
Xstatic char *wc_long_delay	= "ldelay";
Xstatic char *wc_intr_wait	= "intrw";
X#define ftsleep(wc,to)	tsleep((caddr_t)(wc),FTPRI,(wc),(to))
X
X/***********************************************************************\
X* Per tape drive structure.						*
X\***********************************************************************/
Xstruct ft_softc {
X	struct device sc_dev;
X#ifdef NEWCONFIG
X	struct dkdevice sc_dk;
X#endif
X
X	struct	fdc_softc *fdc;	/* pointer to controller structure */
X	int	sc_drive;	/* drive number on this tape */
X	int	sc_unit;	/* unit number on this controller */
X	int	sc_type;	/* Drive type (Mountain, Colorado) */
X	char	*sc_name;	/* Drive type name */
X	int	sc_flags;
X
X/*	QIC_Geom *ftg;	*/	/* pointer to Current tape's geometry */
X	int	cmd_wait;	/* Command we are awaiting completion of */
X	int	sts_wait;	/* Tape interrupt status of current request */
X	int	io_sts;		/* Tape I/O status */
X	int	mode;
X	int	pcn;		/* present cylinder number */
X	int	attaching;	/* true when ft is attaching */
X	unsigned char *xptr;	/* pointer to buffer blk to xfer  */
X	int	xcnt;		/* transfer count                 */
X	int	xblk;		/* block number to transfer       */
X	int	xseg;		/* segment being transferred	  */
X	SegReq *segh;		/* Current I/O request		  */
X	SegReq *segt;		/* Tail of queued I/O requests	  */
X	SegReq *doneh;		/* Completed I/O request queue    */
X	SegReq *donet;		/* Completed I/O request tail	  */
X	SegReq *segfree;	/* Free segments		  */
X	SegReq *hdr;		/* Current tape header		  */
X	int nsegq;		/* Segments on request queue	  */
X	int ndoneq;		/* Segments on completed queue	  */
X	int nfreelist;		/* Segments on free list	  */
X
X				/* the next 3 should be defines in 'flags' */
X	int active;		/* TRUE if transfer is active	  */
X	int rdonly;		/* TRUE if tape is read-only	  */
X	int newcart;		/* TRUE if new cartridge detected */
X	int laststs;		/* last reported status code      */
X	int lastcfg;		/* last reported QIC config	  */
X	int lasterr;		/* last QIC error code 		  */
X	int lastpos;		/* last known segment number	  */
X	int moving;		/* TRUE if tape is moving	  */
X
X};
X
Xint ftopen __P((dev_t, int));
Xint ftclose __P((dev_t, int));
Xvoid ftstrategy __P((struct buf *));
Xint ftioctl __P((dev_t, int, caddr_t, int, struct proc *));
Xint ftdump __P((dev_t));
Xint ftsize __P((dev_t));
Xstatic void ft_timeout __P((void *arg));
Xstatic void async_cmd __P((struct ft_softc *));
Xstatic void async_req __P((struct ft_softc *, int));
Xstatic void async_read __P((struct ft_softc *, int));
Xstatic void async_write __P((struct ft_softc *, int));
Xstatic void tape_start __P((struct ft_softc *, int));
Xstatic void tape_end __P((struct ft_softc *));
Xstatic void tape_inactive __P((struct ft_softc *));
Xstatic int tape_cmd __P((struct ft_softc *, int));
Xstatic int tape_status __P((struct ft_softc *));
Xstatic int qic_status __P((struct ft_softc *, int, int));
Xstatic int ftreq_rewind __P((struct ft_softc *));
Xstatic int ftreq_hwinfo __P((struct ft_softc *, QIC_HWInfo *));
X
Xextern int fdcresult __P((struct fdc_softc *fdc));
Xextern int out_fdc __P((u_short iobase, u_char x));
X
X/*****************************************************************************/
X
Xstatic int
Xin_fdc(fdc, expectn, errstr)
Xstruct fdc_softc *fdc;
Xint expectn;
Xchar *errstr;
X{
X	int	n = fdcresult(fdc);
X
X	if (n != expectn)
X	{
X		DPRT(("ft: %s bad fdcresult() = %d != %d\n", errstr, n, expectn));
X		printf("ft: %s bad fdcresult() = %d != %d\n", errstr, n, expectn);
X		return -1;
X	}
X
X	return 0;
X}
X
X/*
X *  Allocate a segment I/O buffer from the free list.
X */
Xstatic SegReq *
Xsegio_alloc(struct ft_softc *ft)
X{
X  SegReq *r;
X
X  /* Grab first item from free list */
X  if ((r = ft->segfree) != NULL) {
X	ft->segfree = ft->segfree->next;
X	ft->nfreelist--;
X  }
X  DPRT(("segio_alloc: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
X  return(r);
X}
X
X
X/*
X *  Queue a segment I/O request.
X */
Xstatic void
Xsegio_queue(struct ft_softc  *ft, SegReq *sp)
X{
X  /* Put request on in process queue. */
X  if (ft->segt == NULL)
X	ft->segh = sp;
X  else
X	ft->segt->next = sp;
X  sp->next = NULL;
X  ft->segt = sp;
X  ft->nsegq++;
X  DPRT(("segio_queue: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
X}
X
X
X/*
X *  Segment I/O completed, place on correct queue.
X */
Xstatic void
Xsegio_done(struct ft_softc *ft, SegReq *sp)
X{
X  /* First remove from current I/O queue */
X  ft->segh = sp->next;
X  if (ft->segh == NULL) ft->segt = NULL;
X  ft->nsegq--;
X
X  if (sp->reqtype == FTIO_WRITING) {
X	/* Place on free list */
X	sp->next = ft->segfree;
X	ft->segfree = sp;
X	ft->nfreelist++;
X	wakeup((caddr_t)wc_buff_avail);
X	DPRT(("segio_done: (w) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
X  } else {
X	/* Put on completed I/O queue */
X	if (ft->donet == NULL)
X		ft->doneh = sp;
X	else
X		ft->donet->next = sp;
X	sp->next = NULL;
X	ft->donet = sp;
X	ft->ndoneq++;
X	wakeup((caddr_t)wc_buff_done);
X	DPRT(("segio_done: (r) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
X  }
X}
X
X
X/*
X *  Take I/O request from finished queue to free queue.
X */
Xstatic void
Xsegio_free(struct ft_softc *ft, SegReq *sp)
X{
X  /* First remove from done queue */
X  ft->doneh = sp->next;
X  if (ft->doneh == NULL) ft->donet = NULL;
X  ft->ndoneq--;
X
X  /* Place on free list */
X  sp->next = ft->segfree;
X  ft->segfree = sp;
X  ft->nfreelist++;
X  wakeup((caddr_t)wc_buff_avail);
X  DPRT(("segio_free: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
X}
X
X/* floppy tape driver configuration */
Xint ftprobe();
Xvoid ftattach();
X
Xstruct cfdriver ftcd = {
X	NULL, "ft", ftprobe, ftattach, DV_TAPE, sizeof(struct ft_softc)
X};
X
X#ifdef NEWCONFIG
Xvoid ftstrategy __P((struct buf *));
X
Xstruct dkdriver ftdkdriver = { ftstrategy };
X#endif
X
X/*
X *  Probe floppy tapes.
X */
Xint
Xftprobe(parent, self, aux)
X	struct device *parent, *self;
X	void *aux;
X{
X	struct fdc_softc *fdc = (void *)parent;
X	struct ft_softc *ft = (void *)self;
X	struct cfdata *cf = self->dv_cfdata;
X	struct fdc_attach_args *fa = aux;
X	static int ftu_count = 0;
X	static struct fdc_softc *oldfdc = NULL;
X
X	QIC_HWInfo hw;
X
X
X#ifdef NEWCONFIG
X#define cf_drive cf_loc[0]
X
X	if (cf->cf_drive != -1 && cf->cf_drive != fa->fa_drive)
X		return 0;
X#undef cf_drive
X#else
X	struct isa_device *id = (void *)cf->cf_loc;
X
X	if (id->id_physid != -1 && id->id_physid != fa->fa_drive)
X		return 0;
X#endif
X
X	/* XXX Super kludge -- christie */
X	if (!oldfdc)
X		oldfdc = fdc;
X	if (fdc != oldfdc)
X		ftu_count = 0;
X	ft->sc_unit = ftu_count++;
X
X	ft->fdc = fdc;
X	ft->sc_drive = fa->fa_drive;
X	ft->sc_type = FT_NOTYPE;
X	ft->sc_name = "Unknown";
X	ft->attaching = 1;
X
X	/* Probe for tape */
X	tape_start(ft, 0);
X	if (tape_status(ft) >= 0)
X	{
X		ftreq_hwinfo(ft, &hw);
X		goto out;
X	}
X
X	/*
X	 *  FT_COLORADO - colorado style
X	 */
X	tape_start(ft, 0);
X	tape_cmd(ft, QC_COL_ENABLE1);
X	tape_cmd(ft, QC_COL_ENABLE2 + ft->sc_unit);
X	if (tape_status(ft) >= 0) {
X		ft->sc_type = FT_COLORADO;
X		ftreq_hwinfo(ft, &hw);
X		tape_cmd(ft, QC_COL_DISABLE);
X		goto out;
X	}
X
X	/*
X	*  FT_MOUNTAIN - mountain style
X	*/
X	tape_start(ft, 0);
X	tape_cmd(ft, QC_MTN_ENABLE1);
X	tape_cmd(ft, QC_MTN_ENABLE2);
X	if (tape_status(ft) >= 0) {
X		ft->sc_type = FT_MOUNTAIN;
X		ftreq_hwinfo(ft, &hw);
X		tape_cmd(ft, QC_MTN_DISABLE);
X		goto out;
X	}
X
X	/*
X	*  FT_INSIGHT - insight style
X	*/
X	tape_start(ft, 1);
X	if (tape_status(ft) >= 0) {
X		ft->sc_type = FT_INSIGHT;
X		ftreq_hwinfo(ft, &hw);
X		goto out;
X	}
X
Xout:
X	tape_end(ft);
X
X	if (ft->sc_type != FT_NOTYPE) {
X		fdc->sc_flags |= FDC_HASFTAPE;
X		switch(hw.hw_make) {
X		case 0x0000:
X			if (ft->sc_type == FT_COLORADO)
X				ft->sc_name = "Colorado";
X			else if (ft->sc_type == FT_INSIGHT)
X				ft->sc_name = "Insight";
X			else if (ft->sc_type == FT_MOUNTAIN && hw.hw_model == 0x05)
X				ft->sc_name = "Archive";
X			else if (ft->sc_type == FT_MOUNTAIN)
X				ft->sc_name = "Mountain";
X			else
X				ft->sc_name = "Unknown";
X			break;
X		case 0x0001:
X			ft->sc_name = "Colorado";
X			break;
X		case 0x0005:
X			if (hw.hw_model >= 0x09)
X				ft->sc_name = "Conner";
X			else
X				ft->sc_name = "Archive";
X			break;
X		case 0x0006:
X			ft->sc_name = "Mountain";
X			break;
X		case 0x0007:
X			ft->sc_name = "Wangtek";
X			break;
X		case 0x0222:
X			ft->sc_name = "IOMega";
X			break;
X		default:
X			ft->sc_name = "Unknown";
X		}
X	}
X
X	ft->attaching = 0;
X
X	return (ft->sc_type != FT_NOTYPE);
X}
X
X/*
X *  Attach floppy tapes.
X */
Xvoid
Xftattach(parent, self, aux)
X	struct device *parent, *self;
X	void *aux;
X{
X	struct fdc_softc *fdc = (void *)parent;
X	struct ft_softc *ft = (void *)self;
X	struct fdc_attach_args *fa = aux;
X
X	ft->fdc = fdc;
X	ft->sc_drive = fa->fa_drive;
X
X	fdc->sc_fd[fa->fa_drive] = (struct fd_softc *)ft;
X
X	printf(": [unit %d: %s tape]\n", ft->sc_unit, ft->sc_name);
X
X#ifdef NEWCONFIG
X	ft->sc_dk.dk_driver = &ftdkdriver;
X	/* XXX need to do some more fiddling with sc_dk */
X	dk_establish(&ft->sc_dk, &ft->sc_dev);
X#endif
X}
X
X/*
X *  Perform common commands asynchronously.
X */
Xstatic void
Xasync_cmd(struct ft_softc *ft)
X{
X	struct	fdc_softc *fdc = ft->fdc;
X	u_int	iobase = fdc->sc_iobase;
X	int cmd, i, st0, st3, pcn;
X	static int bitn, retval, retpos, nbits, newcn;
X	static int wanttrk, wantblk, wantdir;
X	static int curtrk, curblk, curdir, curdiff;
X	static int errcnt = 0;
X
Xrestate:
X#if FTDBGALL
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_arg0;
X#if FTDBGALL
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(iobase, NE7CMD_SEEK) < 0) i = 1;
X		if (!i && out_fdc(iobase, ft->sc_unit) < 0) i = 1;
X		if (!i && out_fdc(iobase, newcn) < 0) i = 1;
X		if (i) {
X			if (++async_retries >= 10) {
X				DPRT(("ft%d: async_cmd command seek failed!!\n", ft->sc_unit));
X				goto complete;
X			}
X			DPRT(("ft%d: async_cmd command seek retry...\n",ft->sc_unit));
X			async_state = 0;
X			goto restate;
X		}
X		break;
X	    case 1:
X		out_fdc(iobase, NE7CMD_SENSEI);
X		if (in_fdc(fdc, 2, "async_cmd seek"))
X			break;
X		st0 = fdc->sc_status[0];
X		pcn = fdc->sc_status[1];
X		if (st0 < 0 || pcn < 0 || newcn != pcn) {
X			if (++async_retries >= 10) {
X				DPRT(("ft%d: async_cmd seek retries exceeded\n",ft->sc_unit));
X				goto complete;
X			}
X			DPRT(("ft%d: async_cmd command bad st0=$%02x pcn=$%02x\n",
X							ft->sc_unit, st0, pcn));
X			async_state = 0;
X			timeout(ft_timeout, (caddr_t)ft, hz/10);
X			break;
X		}
X		if (st0 & 0x20)	{ 	/* seek done */
X			ft->pcn = pcn;
X		}
X#if FTDBGALL
X		 else
X			DPRT(("ft%d: async_seek error st0 = $%02x pcn = %d\n",
X							ft->sc_unit, st0, pcn));
X#endif
X		if (async_arg1) goto complete;
X		async_state = 2;
X		timeout(ft_timeout, (caddr_t)ft, 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_arg0;
X		nbits = async_arg1;
X		DPRT(("async_status got cmd = %d nbits = %d\n", cmd,nbits));
X		CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0);
X		/* NOTREACHED */
X	    case 1:
X		out_fdc(iobase, NE7CMD_SENSED);
X		out_fdc(iobase, ft->sc_unit);
X		if (in_fdc(fdc, 1, "async_cmd status"))
X			break;
X		st3 = fdc->sc_status[0];
X		if (st3 < 0) {
X		DPRT(("ft%d: async_status timed out on bit %d r=$%02x\n",
X					ft->sc_unit,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_arg0 == QC_STATUS && async_arg2 == 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				DPRT(("ft%d: async_status failed: retval=$%04x nbits=%d\n",
X						ft->sc_unit, retval,nbits));
X				async_ret = -2; 
X			}
X			goto complete;
X		}
X		CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0);
X		/* NOTREACHED */
X	    case 2:
X		if (async_ret & QS_NEWCART) ft->newcart = 1;
X		CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1);
X	    case 3:
X		ft->lasterr = async_ret;
X		if ((ft->lasterr & QS_NEWCART) == 0 && ft->lasterr) {
X			DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
X				ft->sc_unit, ft->lasterr & 0xff, ft->lasterr >> 8));
X		}
X	        cmd = async_arg0;
X		nbits = async_arg1;
X		CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1);
X	    case 4:
X		goto complete;
X	    case 5:
X		CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0);
X	    case 6:
X		CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0);
X	    case 7:
X		CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0);
X	    case 8:
X		cmd = async_arg0;
X		CALL_ACMD(1, ACMD_SEEK, cmd, 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);
X	    case 1:
X		if ((async_ret & async_arg0) != 0) goto complete;
X		async_state = 0;
X		if (++async_retries == 360) {	/* 90 secs. */
X			DPRT(("ft%d: acmd_state exceeded retry count\n", ft->sc_unit));
X			goto complete;
X		}
X		timeout(ft_timeout, (caddr_t)ft, 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_arg0;
X		async_retries = (async_arg2) ? (async_arg2 * 4) : 10;
X		CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0);
X	    case 1:
X		CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0);
X	    case 2:
X		if ((async_ret & async_arg1) != 0) goto complete;
X		if (--async_retries == 0) {
X			DPRT(("ft%d: acmd_seeksts retries exceeded\n", ft->sc_unit));
X			goto complete;
X		}
X		async_state = 1;
X		timeout(ft_timeout, (caddr_t)ft, 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 (!ft->moving) {
X			CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
X			/* NOTREACHED */
X		}
X		async_state = 1;
X		out_fdc(iobase, 0x4a);		/* READ_ID */
X		out_fdc(iobase, ft->sc_unit);
X		break;
X	    case 1:
X		if (in_fdc(fdc, 7, "async_cmd readid"))
X			break;
X		async_ret = (fdc->sc_status[3]*ftg->g_fdtrk) +
X			    (fdc->sc_status[4]*ftg->g_fdside) + fdc->sc_status[5] - 1;
X		DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n",
X			fdc->sc_status[0], fdc->sc_status[1], fdc->sc_status[2], fdc->sc_status[3],
X			fdc->sc_status[4], fdc->sc_status[5], async_ret));
X		if ((fdc->sc_status[0] & 0xc0) != 0 || async_ret < 0) {
X			/*
X			 *  Method for retry:
X			 *    errcnt == 1 regular retry
X			 *		2 microstep head 1
X			 * 		3 microstep head 2
X			 *		4 microstep head back to 0
X			 *		5 fail
X			 */
X			if (++errcnt >= 5) {
X				DPRT(("ft%d: acmd_readid errcnt exceeded\n", ft->sc_unit));
X				async_ret = -2;
X				errcnt = 0;
X				goto complete;
X			}
X			if (errcnt == 1) {
X				ft->moving = 0;
X				CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
X			} else {
X				ft->moving = 0;
X				CALL_ACMD(4, ACMD_SEEKSTS, QC_STPAUSE, QS_READY, 0);
X			}
X			DPRT(("readid retry %d...\n", errcnt));
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);
X			/* NOTREACHED */
X		}
X		ft->lastpos = async_ret;
X		errcnt = 0;
X		goto complete;
X		/* NOTREACHED */
X	    case 2:
X		CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0);
X	    case 3:
X		ft->moving = 0;
X		async_ret = retpos+1;
X		goto complete;
X	    case 4:
X		CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0);
X	    case 5:
X		ft->moving = 1;
X		async_state = 0;
X		timeout(ft_timeout, (caddr_t)ft, hz/10); /* XXX */
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_arg0 / ftg->g_blktrk;
X		wantblk = async_arg0 % ftg->g_blktrk;
X		wantdir = wanttrk & 1;
X		ft->moving = 0;
X		CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 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);
X	    case 2:
X		cmd = wanttrk+2;
X		CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0);
X	    case 3:
X		CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0);
X	    case 4:
X		ft->laststs = 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);
X		}
X		if (ft->laststs & QS_BOT) {
X			DPRT(("Tape is at BOT\n"));
X			curblk = (wantdir) ? 4800 : 0;
X			async_state = 6;
X			goto restate;
X		}
X		if (ft->laststs & 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);
X	    case 5:
X		if (async_ret < 0) {
X			ft->moving = 0;
X			ft->lastpos = -2;
X			if (async_ret == -2) {
X				CALL_ACMD(9, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
X			}
X			CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
X		}
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);
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			ft->lastpos = curblk - 1;
X			async_ret = ft->lastpos;
X			if (ft->moving) goto complete;
X			CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0);
X		}
X		if (curblk > wantblk) {		/* passed it */
X			ft->moving = 0;
X			CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
X		}
X		if ((wantblk - curblk) <= 256) {	/* approaching it */
X			CALL_ACMD(5, ACMD_READID, 0, 0, 0);
X		}
X		/* way up ahead */
X		ft->moving = 0;
X		CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
X		break;
X	    case 7:
X		ft->moving = 1;
X		CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0);
X		break;
X	    case 8:
X		async_state = 9;
X		timeout(ft_timeout, (caddr_t)ft, hz/10);  /* XXX */
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);
X	    case 11:
X		DPRT(("reverse 1 done\n"));
X		CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0);
X	    case 12:
X		DPRT(("reverse 2 done\n"));
X		CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90);
X	    case 13:
X		CALL_ACMD(5, ACMD_READID, 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);
X	    case 15:
X		DPRT(("forward 1 done\n"));
X		CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0);
X	    case 16:
X		DPRT(("forward 2 done\n"));
X		CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90);
X	}
X	break;
X  }
X
X  return;
X
Xcomplete:
X  if (astk_ptr != &astk[0]) {
X	astk_ptr--;
X	async_retries = astk_ptr->over_retries;
X	async_func = astk_ptr->over_func;
X	async_state = astk_ptr->over_state;
X	async_arg0 = astk_ptr->over_arg0;
X	async_arg1 = astk_ptr->over_arg1;
X	async_arg2 = astk_ptr->over_arg2;
X	goto restate;
X  }
X  async_func = ACMD_NONE;
X  async_state = 0;
X  switch (ft->io_sts) {
X     case FTIO_READY:
X	async_req(ft, 2);
X	break;
X     case FTIO_READING:
X     case FTIO_RDAHEAD:
X	async_read(ft, 2);
X	break;
X     case FTIO_WRITING:
X	async_write(ft, 2);
X	break;
X     default:
X	DPRT(("ft%d: bad async_cmd ending I/O state!\n", ft->sc_unit));
X	break;
X  }
X}
X
X
X/*
X *  Entry point for the async request processor.
X */
Xstatic void
Xasync_req(struct ft_softc *ft, int from)
X{
X  struct fdc_softc *fdc = ft->fdc;
X  u_int	iobase = fdc->sc_iobase;
X  SegReq *sp;
X  static int over_async, lastreq;
X  int cmd;
X
X  if (from == 2) arq_state = over_async;
X
Xrestate:
X  switch (arq_state) {
X     case 0:	/* Process segment */
X	sp = ft->segh;
X	ft->io_sts = (sp == NULL) ? FTIO_READY : sp->reqtype;
X
X	if (ft->io_sts == FTIO_WRITING)
X		async_write(ft, from);
X	else
X		async_read(ft, from);
X	if (ft->io_sts != FTIO_READY) return;
X
X	/* Pull buffer from current I/O queue */
X	if (sp != NULL) {
X		lastreq = sp->reqtype;
X		segio_done(ft, sp);
X
X		/* If I/O cancelled, clear finished queue. */
X		if (sp->reqcan) {
X			while (ft->doneh != NULL)
X				segio_free(ft, ft->doneh);
X			lastreq = FTIO_READY;
X		}
X	} else
X		lastreq = FTIO_READY;
X
X	/* Detect end of track */
X	if (((ft->xblk / QCV_BLKSEG) % ftg->g_segtrk) == 0) {
X		ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0);
X	}
X	arq_state = 1;
X	goto restate;
X
X     case 1:	/* Next request */
X	/* If we have another request queued, start it running. */
X	if (ft->segh != NULL) {
X		sp = ft->segh;
X		sp->reqcrc = 0;
X		arq_state = ard_state = awr_state = 0;
X		ft->xblk = sp->reqblk;
X		ft->xseg = sp->reqseg;
X		ft->xcnt = 0;
X		ft->xptr = sp->buff;
X		DPRT(("I/O reqblk = %d\n", ft->xblk));
X		goto restate;
X	}
X
X	/* If the last request was reading, do read ahead. */
X	if ((lastreq == FTIO_READING || lastreq == FTIO_RDAHEAD) &&
X					(sp = segio_alloc(ft)) != NULL) {
X		sp->reqtype = FTIO_RDAHEAD;
X		sp->reqblk = ft->xblk;
X		sp->reqseg = ft->xseg+1;
X		sp->reqcrc = 0;
X		sp->reqcan = 0;
X		segio_queue(ft, sp);
X		bzero(sp->buff, QCV_SEGSIZE);
X		arq_state = ard_state = awr_state = 0;
X		ft->xblk = sp->reqblk;
X		ft->xseg = sp->reqseg;
X		ft->xcnt = 0;
X		ft->xptr = sp->buff;
X		DPRT(("Processing readahead reqblk = %d\n", ft->xblk));
X		goto restate;
X	}
X
X	if (ft->moving) {
X		DPRT(("No more I/O.. Stopping.\n"));
X		ft->moving = 0;
X		ACMD_FUNC(7, ACMD_SEEKSTS, QC_PAUSE, QS_READY, 0);
X		break;
X	}
X	arq_state = 7;
X	goto restate;
X
X     case 2:	/* End of track */
X	ft->moving = 0;
X	ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
X	break;
X
X     case 3:
X	DPRT(("async_req seek head to track %d\n", ft->xblk / ftg->g_blktrk));
X	ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0);
X	break;
X
X     case 4:
X	cmd = (ft->xblk / ftg->g_blktrk) + 2;
X	if (ft->segh != NULL) {
X		ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0);
X	} else {
X		ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0);
X	}
X	break;
X
X     case 5:
X	ft->moving = 1;
X	ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0);
X	break;
X
X     case 6:
X	arq_state = 1;
X	timeout(ft_timeout, (caddr_t)ft, hz/10); /* XXX */
X	break;
X
X     case 7:
X	/* Time to rest. */
X	ft->active = 0;
X	ft->lastpos = -2;
X
X	/* wakeup those who want an i/o chg */
X	wakeup((caddr_t)wc_iosts_change);
X	break;
X  }
X}
X
X
X/*
X *  Entry for async read.
X */
Xstatic void
Xasync_read(struct ft_softc *ft, int from)
X{
X  struct fdc_softc *fdc = ft->fdc;
X  u_int	iobase = fdc->sc_iobase;
X  int i;
X  int where;
X  static int over_async;
X  static int retries = 0;
X
X  if (from == 2) ard_state = over_async;
X
Xrestate:
X#if FTDBGALL
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 (ft->lastpos != (ft->xblk-1)) {
X		DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
X			ft->sc_unit, ft->lastpos, ft->xblk));
X		ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0);
X	}
X
X	/* Tape is in position but stopped. */
X	if (!ft->moving) {
X		DPRT(("async_read ******STARTING TAPE\n"));
X		ACMD_FUNC(3, ACMD_STATE, QS_READY, 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, ft->xptr, QCV_BLKSIZE, 2);
X	out_fdc(iobase, 0x66);				/* read */
X	out_fdc(iobase, ft->sc_unit);				/* unit */
X	out_fdc(iobase, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); 	/* cylinder */
X	out_fdc(iobase, ft->xblk / ftg->g_fdside);		/* head */
X	out_fdc(iobase, (ft->xblk % ftg->g_fdtrk) + 1);	/* sector */
X	out_fdc(iobase, 0x03);				/* 1K sectors */
X	out_fdc(iobase, (ft->xblk % ftg->g_fdtrk) + 1);	/* count */
X	out_fdc(iobase, 0x74);				/* gap length */
X	out_fdc(iobase, 0xff);				/* transfer size */
X	ard_state = 2;
X	break;
X
X     case 2:	/* DMA completed */
X	/* Transfer complete, get status */
X	if (in_fdc(fdc, 7, "async_read"))
X		break;
X	isa_dmadone(B_READ, ft->xptr, QCV_BLKSIZE, 2);
X
X#if FTDBGALL
X	/* Compute where the controller thinks we are */
X	where = (fdc->sc_status[3]*ftg->g_fdtrk) + (fdc->sc_status[4]*ftg->g_fdside)
X			+ fdc->sc_status[5]-1;
X	DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
X	    fdc->sc_status[0], fdc->sc_status[1], fdc->sc_status[2], fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5],
X	    where, ft->xblk));
X#endif
X
X	/* Check for errors */
X	if ((fdc->sc_status[0] & 0xc0) != 0x00) {
X#if !FTDBGALL
X		where = (fdc->sc_status[3]*ftg->g_fdtrk) + (fdc->sc_status[4]*ftg->g_fdside)
X			+ fdc->sc_status[5]-1;
X		DPRT(("xd: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
X		    fdc->sc_status[0], fdc->sc_status[1], fdc->sc_status[2], fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5],
X		    where, ft->xblk));
X#endif
X		if ((fdc->sc_status[1] & 0x04) == 0x04 && retries < 2) {
X			/* Probably wrong position */
X			DPRT(("async_read: doing retry %d\n", retries));
X			ft->lastpos = ft->xblk;
X			ard_state = 0;
X			retries++;
X			goto restate;
X		} else {
X			/* CRC/Address-mark/Data-mark, et. al. */
X			DPRT(("ft%d: CRC error on block %d\n", ft->sc_unit, ft->xblk));
X			ft->segh->reqcrc |= (1 << ft->xcnt);
X		}
X	}
X
X	/* Otherwise, transfer completed okay. */
X	retries = 0;
X	ft->lastpos = ft->xblk;
X	ft->xblk++;
X	ft->xcnt++;
X	ft->xptr += QCV_BLKSIZE;
X	if (ft->xcnt < QCV_BLKSEG && ft->segh->reqcan == 0) {
X		ard_state = 0;
X		goto restate;
X	}
X	DPRT(("Read done..  Cancel = %d\n", ft->segh->reqcan));
X	ft->io_sts = FTIO_READY;
X	break;
X
X     case 3:
X	ft->moving = 1;
X	ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0);
X	break;
X
X     case 4:
X	ard_state = 1;
X	timeout(ft_timeout, (caddr_t)ft, hz/10);  /* XXX */
X	break;
X
X     default:
X	DPRT(("ft%d: bad async_read state %d!!\n", ft->sc_unit, 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 */
Xstatic void
Xasync_write(struct ft_softc *ft, int from)
X{
X  struct fdc_softc *fdc = ft->fdc;
X  u_int	iobase = fdc->sc_iobase;
X  int i;
X  int where;
X  static int over_async;
X  static int retries = 0;
X
X  if (from == 2) awr_state = over_async;
X
Xrestate:
X#if FTDBGALL
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 (ft->lastpos != (ft->xblk-1)) {
X		DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
X			ft->sc_unit, ft->lastpos, ft->xblk));
X		ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0);
X	}
X
X	/* Tape is in position but stopped. */
X	if (!ft->moving) {
X		DPRT(("async_write ******STARTING TAPE\n"));
X		ACMD_FUNC(3, ACMD_STATE, QS_READY, 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, ft->xptr, QCV_BLKSIZE, 2);
X	out_fdc(iobase, 0x45);				/* write */
X	out_fdc(iobase, ft->sc_unit);				/* unit */
X	out_fdc(iobase, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cyl */
X	out_fdc(iobase, ft->xblk / ftg->g_fdside);		/* head */
X	out_fdc(iobase, (ft->xblk % ftg->g_fdtrk) + 1);	/* sector */
X	out_fdc(iobase, 0x03);				/* 1K sectors */
X	out_fdc(iobase, (ft->xblk % ftg->g_fdtrk) + 1);	/* count */
X	out_fdc(iobase, 0x74);				/* gap length */
X	out_fdc(iobase, 0xff);				/* transfer size */
X	awr_state = 2;
X	break;
X
X     case 2:	/* DMA completed */
X	/* Transfer complete, get status */
X	if (in_fdc(fdc, 7, "async_write"))
X		break;
X	isa_dmadone(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
X
X#if FTDBGALL
X	/* Compute where the controller thinks we are */
X	where = (fdc->sc_status[3]*ftg->g_fdtrk) + (fdc->sc_status[4]*ftg->g_fdside) + fdc->sc_status[5]-1;
X	DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
X	    fdc->sc_status[0], fdc->sc_status[1], fdc->sc_status[2], fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5],
X	    where, ft->xblk));
X#endif
X
X	/* Check for errors */
X	if ((fdc->sc_status[0] & 0xc0) != 0x00) {
X#if !FTDBGALL
X		where = (fdc->sc_status[3]*ftg->g_fdtrk) + (fdc->sc_status[4]*ftg->g_fdside)
X			 + fdc->sc_status[5]-1;
X		DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
X		    fdc->sc_status[0], fdc->sc_status[1], fdc->sc_status[2], fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5],
X		    where, ft->xblk));
X#endif
X		if (retries < 3) {
X			/* Something happened -- try again */
X			DPRT(("async_write: doing retry %d\n", retries));
X			ft->lastpos = ft->xblk;
X			awr_state = 0;
X			retries++;
X			goto restate;
X		} else {
X			/*
X			 *  Retries failed.  Note the unrecoverable error.
X			 *  Marking the block as bad is useless right now.
X			 */ 
X			printf("ft%d: unrecoverable write error on block %d\n",
X					ft->sc_unit, ft->xblk);
X			ft->segh->reqcrc |= (1 << ft->xcnt);
X		}
X	}
X
X	/* Otherwise, transfer completed okay. */
X	retries = 0;
X	ft->lastpos = ft->xblk;
X	ft->xblk++;
X	ft->xcnt++;
X	ft->xptr += QCV_BLKSIZE;
X	if (ft->xcnt < QCV_BLKSEG) {
X		awr_state = 0;	/* next block */
X		goto restate;
X	}
X#if FTDBGALL
X	DPRT(("Write done.\n"));
X#endif
X	ft->io_sts = FTIO_READY;
X	break;
X
X     case 3:
X	ft->moving = 1;
X	ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0);
X	break;
X
X     case 4:
X	awr_state = 1;
X	timeout(ft_timeout, (caddr_t)ft, hz/10); /* XXX */
X	break;
X
X     default:
X	DPRT(("ft%d: bad async_write state %d!!\n", ft->sc_unit, awr_state));
X	break;
X  }
X}
X
X
X/*
X *  Interrupt handler for active tape.  Bounced off of fdcintr().
X */
Xint
Xftintr(struct fdc_softc *fdc)
X{
X  int st0, pcn, i;
X  struct ft_softc *ft = ftcd.cd_devs[0]; /* XXX: assume only one tape drive! */
X  int s = splbio();
X
X  st0 = 0;
X  pcn = 0;
X
X  /* I/O segment transfer completed */
X  if (ft->active) {
X	if (async_func != ACMD_NONE) {
X		async_cmd(ft);
X		splx(s);
X		return(1);
X	}
X#if FTDBGALL
X	DPRT(("Got request interrupt\n"));
X#endif
X	async_req(ft, 0);
X	splx(s);
X	return(1);
X  }
X
X  /* Get interrupt status */
X  if (ft->cmd_wait != FTCMD_READID) {
X	out_fdc(fdc->sc_iobase, NE7CMD_SENSEI);
X	i = fdcresult(fdc);
X	if (i >= 1)
X		st0 = fdc->sc_status[0];
X	if (i >= 2)
X		pcn = fdc->sc_status[1];
X  }
X
X  if (ft->cmd_wait == FTCMD_NONE || ft->sts_wait != FTSTS_SNOOZE) {
Xhuh_what:
X	printf("ft%d: unexpected interrupt; st0 = $%02x pcn = %d\n",
X				ft->sc_unit, st0, pcn);
X	splx(s);
X	return(1);
X  }
X
X  switch (ft->cmd_wait) {
X     case FTCMD_RESET:
X	ft->sts_wait = FTSTS_INTERRUPT;
X	wakeup((caddr_t)wc_intr_wait);
X	break;
X     case FTCMD_RECAL:
X     case FTCMD_SEEK:
X	if (st0 & 0x20)	{ 	/* seek done */
X		ft->sts_wait = FTSTS_INTERRUPT;
X		ft->pcn = pcn;
X		wakeup((caddr_t)wc_intr_wait);
X	}
X#if FTDBGALL
X	else
X		DPRT(("ft%d: seek error st0 = $%02x pcn = %d\n",
X						ft->sc_unit, st0, pcn));
X#endif
X	break;
X     case FTCMD_READID:
X	if (in_fdc(fdc, 7, "ftintr readid"))
X		break;
X	ft->sts_wait = FTSTS_INTERRUPT;
X	wakeup((caddr_t)wc_intr_wait);
X	break;
X
X     default:
X	goto huh_what;
X  }
X
X  splx(s);
X  return(1);
X}
X
X
X/*
X *  Interrupt timeout routine.
X */
Xstatic void
Xft_timeout(void *arg1)
X{
X  int s;
X  struct ft_softc *ft = (struct ft_softc *)arg1;
X
X  s = splbio();
X  if (ft->active) {
X	if (async_func != ACMD_NONE) {
X		async_cmd(ft);
X		splx(s);
X		return;
X	}
X	async_req(ft, 1);
X  } else {
X	  ft->sts_wait = FTSTS_TIMEOUT;
X	  wakeup((caddr_t)wc_intr_wait);
X  }
X  splx(s);
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 */
Xstatic int
Xftintr_wait(struct ft_softc *ft, int cmd, int ticks)
X{
X  int retries, st0, pcn;
X  struct fdc_softc *fdc = ft->fdc;
X  u_int	iobase = fdc->sc_iobase;
X  int i;
X
X  ft->cmd_wait = cmd;
X  ft->sts_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(100);
X		ft->sts_wait = FTSTS_INTERRUPT;
X		goto intrdone;
X	    case FTCMD_RECAL:
X	    case FTCMD_SEEK:
X		for (retries = 0; retries < 10000; retries++) {
X			out_fdc(iobase, NE7CMD_SENSEI);
X			i = fdcresult(fdc);
X			if (i >= 1)
X				st0 = fdc->sc_status[0];
X			if (i >= 2)
X				pcn = fdc->sc_status[1];
X			if (i == 2 && (st0 & 0x20)) {
X				ft->sts_wait = FTSTS_INTERRUPT;
X				ft->pcn = pcn;
X				goto intrdone;
X			}
X			delay(100);
X		}
X		break;
X	}
X	ft->sts_wait = FTSTS_TIMEOUT;
X	goto intrdone;
X  }
X
X  ftsleep(wc_intr_wait, ticks);
X
Xintrdone:
X  if (ft->sts_wait == FTSTS_TIMEOUT) {	/* timeout */
X#if FTDBGALL
X	if (ft->cmd_wait != FTCMD_RESET)
X		DPRT(("ft%d: timeout on command %d\n", ft->sc_unit, ft->cmd_wait));
X#endif
X	ft->cmd_wait = FTCMD_NONE;
X	ft->sts_wait = FTSTS_NONE;
X	return(1);
X  }
X
X  /* got interrupt */
X  if (ft->attaching == 0 && ticks) untimeout(ft_timeout, (caddr_t)ft);
X  ft->cmd_wait = FTCMD_NONE;
X  ft->sts_wait = FTSTS_NONE;
X  return(0);
X}
X
X
X/*
X *  Recalibrate tape drive.  Parameter totape is true, if we should
X *  recalibrate to tape drive settings.
X */
Xstatic int
Xtape_recal(struct ft_softc *ft, int totape)
X{
X  int s;
X  u_int	iobase = ft->fdc->sc_iobase;
X
X  DPRT(("tape_recal start\n"));
X
X  out_fdc(iobase, NE7CMD_SPECIFY);
X  out_fdc(iobase, (totape) ? 0xAD : 0xDF);
X  out_fdc(iobase, 0x02);
X
X  s = splbio();
X  out_fdc(iobase, NE7CMD_RECAL);
X  out_fdc(iobase, ft->sc_unit);
X
X  if (ftintr_wait(ft, FTCMD_RECAL, hz)) {
X	splx(s);
X	DPRT(("ft%d: recalibrate timeout\n", ft->sc_unit));
X	return(1);
X  }
X  splx(s);
X
X  out_fdc(iobase, NE7CMD_SPECIFY);
X  out_fdc(iobase, (totape) ? 0xFD : 0xDF);
X  out_fdc(iobase, 0x02);
X
X  DPRT(("tape_recal end\n"));
X  return(0);
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 */
Xstatic int
Xtape_state(struct ft_softc *ft, 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(ft);
X	if (r >= 0) {
X		if (all && (r & mask) == mask) return(r);
X		if ((r & mask) != 0) return(r);
X	}
X	if (seconds) ftsleep(wc_long_delay, hz/4);
X  }
X  DPRT(("ft%d: tape_state failed on mask=$%02x maxtries=%d\n",
X				ft->sc_unit, mask, maxtries));
X  return(-1);
X}
X
X
X/*
X *  Send a QIC command to tape drive, wait for completion.
X */
Xstatic int
Xtape_cmd(struct ft_softc *ft, int cmd)
X{
X  int newcn;
X  int retries = 0;
X  int s;
X  u_int	iobase = ft->fdc->sc_iobase;
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(iobase, NE7CMD_SEEK);
X  out_fdc(iobase, ft->sc_unit);
X  out_fdc(iobase, newcn);
X
X  if (ftintr_wait(ft, FTCMD_SEEK, hz)) {
X	DPRT(("ft%d: tape_cmd seek timeout\n", ft->sc_unit));
Xredo:
X	splx(s);
X	if (++retries < 5) goto retry;
X	DPRT(("ft%d: tape_cmd seek failed!\n", ft->sc_unit));
X	return(1);
X  }
X  splx(s);
X
X  if (ft->pcn != newcn) {
X	DPRT(("ft%d: bad seek in tape_cmd; pcn = %d  newcn = %d\n",
X			ft->sc_unit, ft->pcn, newcn));
X	goto redo;
X  }
X  delay(2500);
X  return(0);
X}
X
X
X/*
X *  Return status of tape drive
X */
Xstatic int
Xtape_status(struct ft_softc *ft)
X{
X  int r, err, tries;
X  int max = (ft->attaching) ? 2 : 3;
X
X  for (r = -1, tries = 0; r < 0 && tries < max; tries++)
X	r = qic_status(ft, QC_STATUS, 8);
X  if (tries == max) return(-1);
X
Xrecheck:
X  DPRT(("tape_status got $%04x\n",r));
X  ft->laststs = r;
X
X  if (r & (QS_ERROR|QS_NEWCART)) {
X	err = qic_status(ft, QC_ERRCODE, 16);
X	ft->lasterr = err;
X	if (r & QS_NEWCART) {
X		ft->newcart = 1;
X		/* If tape not referenced, do a seek load point. */
X		if ((r & QS_FMTOK) == 0 && !ft->attaching) {
X			tape_cmd(ft, QC_SEEKLP);
X			do {
X				ftsleep(wc_long_delay, hz);
X			} while ((r = qic_status(ft, QC_STATUS, 8)) < 0 ||
X					(r & (QS_READY|QS_CART)) == QS_CART);
X			goto recheck;
X		}
X	} else if (err && !ft->attaching) {
X		DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
X					ft->sc_unit, err & 0xff, err >> 8));
X	}
X	r = qic_status(ft, QC_STATUS, 8);
X	ft->laststs = r;
X	DPRT(("tape_status got error code $%04x new sts = $%02x\n",err,r));
X  }
X
X  ft->rdonly = (r & QS_RDONLY);
X  return(r);
X}
X
X
X/*
X *  Transfer control to tape drive.
X */
Xstatic void
Xtape_start(struct ft_softc *ft, int motor)
X{
X  u_int	iobase = ft->fdc->sc_iobase;
X  int s, mbits;
X
X  s = splbio();
X  DPRT(("tape_start start\n"));
X
X  /* reset, dma disable */
X  outb(iobase+fdout, 0x00);
X  (void)ftintr_wait(ft, FTCMD_RESET, hz/10);
X
X  /* raise reset, enable DMA, motor on if needed */
X  if (motor)
X	mbits = (!ft->sc_unit) ? FDO_MOEN(0) : FDO_MOEN(1);
X  else
X	mbits = 0;
X
X  outb(iobase+fdout, FDO_FRST | FDO_FDMAEN | mbits);
X  (void)ftintr_wait(ft, FTCMD_RESET, hz/10);
X
X  splx(s);
X
X  tape_recal(ft, 1);
X
X  /* set transfer speed */
X  outb(iobase+fdctl, FDC_500KBPS);
X  delay(10);
X
X  DPRT(("tape_start end\n"));
X}
X
X
X/*
X *  Transfer control back to floppy disks.
X */
Xstatic void
Xtape_end(struct ft_softc *ft)
X{
X  struct fdc_softc *fdc = ft->fdc;
X  u_int	iobase = fdc->sc_iobase;
X  int s;
X
X  DPRT(("tape_end start\n"));
X  tape_recal(ft, 0);
X
X  s = splbio();
X
X  /* reset, dma disable */
X  outb(iobase+fdout, 0x00);
X  /* (void)ftintr_wait(ft, FTCMD_RESET, hz/10); */
X  delay(100);
X  outb(iobase+fdout, FDO_FRST);
X  delay(100);
X
X  /* raise reset, enable DMA */
X  outb(iobase+fdout, FDO_FRST | FDO_FDMAEN);
X  (void)ftintr_wait(ft, FTCMD_RESET, hz/10);
X
X  splx(s);
X
X  /* set transfer speed */
X  outb(iobase+fdctl, FDC_500KBPS);
X  delay(10);
X  fdc->sc_flags &= ~FDC_TAPE_BUSY; 
X
X  DPRT(("tape_end end\n"));
X}
X
X
X/*
X *  Wait for the driver to go inactive, cancel readahead if necessary.
X */
Xstatic void
Xtape_inactive(struct ft_softc *ft)
X{
X  int s = splbio();
X
X  if (ft->segh != NULL) {
X	if (ft->segh->reqtype == FTIO_RDAHEAD) {
X		/* cancel read-ahead */
X		ft->segh->reqcan = 1;
X	} else if (ft->segh->reqtype == FTIO_WRITING && !ft->active) {
X		/* flush out any remaining writes */
X		DPRT(("Flushing write I/O chain\n"));
X		arq_state = ard_state = awr_state = 0;
X		ft->xblk = ft->segh->reqblk;
X		ft->xseg = ft->segh->reqseg;
X		ft->xcnt = 0;
X		ft->xptr = ft->segh->buff;
X		ft->active = 1;
X		timeout(ft_timeout, (caddr_t)ft, 1);
X	}
X  }
X  while (ft->active) ftsleep(wc_iosts_change, 0);
X  splx(s);
X}
X
X
X/*
X *  Get the geometry of the tape currently in the drive.
X */
Xstatic int
Xftgetgeom(struct ft_softc *ft)
X{
X  int r, i, tries;
X  int cfg, qic80, ext;
X  int sts, fmt, len;
X
X  r = tape_status(ft);
X
X  /* XXX fix me when format mode is finished */
X  if (r < 0 || (r & QS_CART) == 0 || (r & QS_FMTOK) == 0) {
X	DPRT(("ftgetgeom: no cart or not formatted 0x%04x\n",r));
X	ftg = NULL;
X	ft->newcart = 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(ft, 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  ft->lastcfg = 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(ft, 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("ft%d: unsupported tape format\n", ft->sc_unit);
X	return(-1);
X  }
X  if (len > QCV_NLEN) {
X	ftg = NULL;
X	printf("ft%d: unsupported tape length\n", ft->sc_unit);
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("ft%d: unknown tape geometry\n", ft->sc_unit);
X	ftg = NULL;
X	return(-1);
X  }
X  ftg = &ftgtbl[i];
X  if (!ftg->g_trktape) {
X	printf("ft%d: unsupported format %s w/len %s\n",
X				ft->sc_unit, 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  ft->newcart = 0;
X  return(0);
X}
X
X
X/*
X *  Switch between tape/floppy.  This will send the tape enable/disable
X *  codes for this drive's manufacturer.
X */
Xstatic int
Xset_fdcmode(dev_t dev, int newmode)
X{
X  struct ft_softc *ft = ftcd.cd_devs[FDUNIT(dev)];
X  struct fdc_softc *fdc = ft->fdc;
X  static int havebufs = 0;
X  int i;
X  SegReq *sp, *rsp;
X
X  if (newmode == FDC_TAPE_MODE) {
X	/* Wake up the tape drive */
X	switch (ft->sc_type) {
X	    case FT_NOTYPE:
X		fdc->sc_flags &= ~FDC_TAPE_BUSY; 
X		return(ENXIO);
X	    case FT_NONE:
X		tape_start(ft, 0);
X		break;
X	    case FT_COLORADO:
X		tape_start(ft, 0);
X		if (tape_cmd(ft, QC_COL_ENABLE1)) {
X			tape_end(ft);
X			return(EIO);
X		}
X		if (tape_cmd(ft, QC_COL_ENABLE2 + ft->sc_unit)) {
X			tape_end(ft);
X			return(EIO);
X		}
X		break;
X	    case FT_MOUNTAIN:
X		tape_start(ft, 0);
X		if (tape_cmd(ft, QC_MTN_ENABLE1)) {
X			tape_end(ft);
X			return(EIO);
X		}
X		if (tape_cmd(ft, QC_MTN_ENABLE2)) {
X			tape_end(ft);
X			return(EIO);
X		}
X		break;
X	    case FT_INSIGHT:
X		tape_start(ft, 1);
X		break;
X	    default:
X		DPRT(("ft%d: bad tape type\n", ft->sc_unit));
X		return(ENXIO);
X	}
X	if (tape_status(ft) < 0) {
X		if (ft->sc_type == FT_COLORADO)
X			tape_cmd(ft, QC_COL_DISABLE);
X		else if (ft->sc_type == FT_MOUNTAIN)
X			tape_cmd(ft, QC_MTN_DISABLE);
X		tape_end(ft);
X		return(EIO);
X	}
X
X	/* Grab buffers from memory. */
X	if (!havebufs) {
X		ft->segh = ft->segt = NULL;
X		ft->doneh = ft->donet = NULL;
X		ft->segfree = NULL;
X		ft->hdr = NULL;
X		ft->nsegq = ft->ndoneq = ft->nfreelist = 0;
X		for (i = 0; i < FTNBUFF; i++) {
X			sp = malloc(sizeof(SegReq), M_DEVBUF, M_WAITOK);
X			if (sp == NULL) {
X				printf("ft%d: not enough memory for buffers\n", ft->sc_unit);
X				for (sp=ft->segfree; sp != NULL; sp=sp->next)
X					free(sp, M_DEVBUF);
X				if (ft->sc_type == FT_COLORADO)
X					tape_cmd(ft, QC_COL_DISABLE);
X				else if (ft->sc_type == FT_MOUNTAIN)
X					tape_cmd(ft, QC_MTN_DISABLE);
X				tape_end(ft);
X				return(ENOMEM);
X			}
X			sp->reqtype = FTIO_READY;
X			sp->next = ft->segfree;
X			ft->segfree = sp;
X			ft->nfreelist++;
X		}
X		/* take one buffer for header */
X		ft->hdr = ft->segfree;
X		ft->segfree = ft->segfree->next;
X		ft->nfreelist--;
X		havebufs = 1;
X	}
X	ft->io_sts = FTIO_READY;	/* tape drive is ready */
X	ft->active = 0;			/* interrupt driver not active */
X	ft->moving = 0;			/* tape not moving */
X	ft->rdonly = 0;			/* tape read only */
X	ft->newcart = 0;		/* new cartridge flag */
X	ft->lastpos = -1;		/* tape is rewound */
X	async_func = ACMD_NONE;		/* No async function */
X	tape_state(ft, 0, QS_READY, 60);
X	tape_cmd(ft, QC_RATE);
X	tape_cmd(ft, QCF_RT500+2);		/* 500K bps */
X	tape_state(ft, 0, QS_READY, 60);
X	ft->mode = FTM_PRIMARY;
X	tape_cmd(ft, QC_PRIMARY);	/* Make sure we're in primary mode */
X	tape_state(ft, 0, QS_READY, 60);
X	ftg = NULL;			/* No geometry yet */
X	ftgetgeom(ft);			/* Get tape geometry */
X	ftreq_rewind(ft);		/* Make sure tape is rewound */
X  } else {
X	if (ft->sc_type == FT_COLORADO)
X		tape_cmd(ft, QC_COL_DISABLE);
X	else if (ft->sc_type == FT_MOUNTAIN)
X		tape_cmd(ft, QC_MTN_DISABLE);
X	tape_end(ft);
X	ft->newcart = 0;		/* clear new cartridge */
X	if (ft->hdr != NULL) free(ft->hdr, M_DEVBUF);
X	if (havebufs) {
X		for (sp = ft->segfree; sp != NULL;) {
X			rsp = sp; sp = sp->next;
X			free(rsp, M_DEVBUF);
X		}
X		for (sp = ft->segh; sp != NULL;) {
X			rsp = sp; sp = sp->next;
X			free(rsp, M_DEVBUF);
X		}
X		for (sp = ft->doneh; sp != NULL;) {
X			rsp = sp; sp = sp->next;
X			free(rsp, M_DEVBUF);
X		}
X	}
X	havebufs = 0;
X  }
X  return(0);
X}
X
X
X/*
X *  Perform a QIC status function.
X */
Xstatic int
Xqic_status(struct ft_softc *ft, int cmd, int nbits)
X{
X  int st3, r, i;
X  struct fdc_softc *fdc = ft->fdc;
X  u_int	iobase = fdc->sc_iobase;
X
X  if (tape_cmd(ft, cmd)) {
X	DPRT(("ft%d: QIC status timeout\n", ft->sc_unit));
X	return(-1);
X  }
X
X  /* Sense drive status */
X  out_fdc(iobase, NE7CMD_SENSED);
X  out_fdc(iobase, ft->sc_unit);
X  if (in_fdc(fdc, 1, "qic_status"))
X	  return -1;
X  st3 = fdc->sc_status[0];
X
X  if ((st3 & 0x10) == 0) {	/* track 0 */
X	DPRT(("qic_status has dead drive...  st3 = $%02x\n", st3));
X	return(-1);
X  }
X
X  for (i = r = 0; i <= nbits; i++) {
X	if (tape_cmd(ft, QC_NEXTBIT)) {
X		DPRT(("ft%d: QIC status bit timed out on %d\n", ft->sc_unit, i));
X		return(-1);
X	}
X
X	out_fdc(iobase, NE7CMD_SENSED);
X	out_fdc(iobase, ft->sc_unit);
X	if (in_fdc(fdc, 1, "qic_status 2"))
X		return -1;
X        st3 = fdc->sc_status[0];
X	if (st3 < 0) {
X		DPRT(("ft%d: controller timed out on bit %d r=$%02x\n",
X					ft->sc_unit, 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		DPRT(("ft%d: qic status stop bit missing at %d, st3=$%02x r=$%04x\n",
X			ft->sc_unit,i,st3,r));
X		return(-1);
X	}
X  }
X
X  DPRT(("qic_status returned $%02x\n", r));
X  return(r);
X}
X
X
X/*
X *  Open tape drive for use.  Bounced off of Fdopen if tape minor is
X *  detected.
X */
Xint
Xftopen(dev_t dev, int arg2) {
X  struct ft_softc *ft;
X  struct fdc_softc *fdc;
X  int ftu = FDUNIT(dev);
X
X  /* check bounds */
X  if (ftu >= ftcd.cd_ndevs || ftu >= NFT)
X	  return ENXIO;
X
X  ft = ftcd.cd_devs[FDUNIT(dev)];
X  if (!ft)
X	  return ENXIO;
X
X  fdc = ft->fdc;
X  if ((fdc == NULL) || (ft->sc_type == FT_NOTYPE))
X	  return(ENXIO);
X
X  /* check for controller already busy with tape */
X  if (fdc->sc_flags & FDC_TAPE_BUSY)
X	return(EBUSY); 
X  /* make sure we found a tape when probed */
X  if (!(fdc->sc_flags & FDC_HASFTAPE))
X	return(ENODEV);
X  fdc->sc_flags |= FDC_TAPE_BUSY; 
X  return(set_fdcmode(dev, FDC_TAPE_MODE)); /* try to switch to tape */
X}
X
X
X/*
X *  Close tape and return floppy controller to disk mode.
X */
Xint
Xftclose(dev_t dev, int flags)
X{
X  struct ft_softc *ft = ftcd.cd_devs[FDUNIT(dev)];
X
X  /* Wait for any remaining I/O activity to complete. */
X  tape_inactive(ft);
X
X  ft->mode = FTM_PRIMARY;
X  tape_cmd(ft, QC_PRIMARY);
X  tape_state(ft, 0, QS_READY, 60);
X  ftreq_rewind(ft);
X  return(set_fdcmode(dev, FDC_DISK_MODE));	/* Otherwise, close tape */
X}
X
X
X/*
X *  Perform strategy on a given buffer (not!).  Changed so that the
X *  driver will at least return 'Operation not supported'.
X */
Xvoid
Xftstrategy(struct buf *bp)
X{
X  bp->b_error = ENXIO;
X  bp->b_flags |= B_ERROR;
X  biodone(bp);
X}
X
X
X/*
X *  Read or write a segment.
X */
Xstatic int
Xftreq_rw(struct ft_softc *ft, int cmd, QIC_Segment *sr, struct proc *p)
X{
X  int r, i;
X  SegReq *sp;
X  int s;
X  long blk, bad, seg;
X  unsigned char *cp, *cp2;
X
X  if (!ft->active && ft->segh == NULL) {
X	r = tape_status(ft);
X	if ((r & QS_CART) == 0)
X		return(ENXIO);	/* No cartridge */
X	if ((r & QS_FMTOK) == 0)
X		return(ENXIO);	/* Not formatted */
X	tape_state(ft, 0, QS_READY, 90);
X  }
X
X  if (ftg == NULL || ft->newcart) {
X	tape_inactive(ft);
X	tape_state(ft, 0, QS_READY, 90);
X	if (ftgetgeom(ft) < 0)
X		return(ENXIO);
X  }
X
X  /* Write not allowed on a read-only tape. */
X  if (cmd == QIOWRITE && ft->rdonly)
X	return(EROFS);
X
X  /* Quick check of request and buffer. */
X  if (sr == NULL || sr->sg_data == NULL)
X	return(EINVAL);
X
X  /* Make sure requested track and segment is in range. */
X  if (sr->sg_trk >= ftg->g_trktape || sr->sg_seg >= ftg->g_segtrk)
X	return(EINVAL);
X
X  blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG;
X  seg = sr->sg_trk * ftg->g_segtrk + sr->sg_seg;
X
X  s = splbio();
X  if (cmd == QIOREAD) {
X	/*
X	 *  See if the driver is reading ahead.
X	 */
X	if (ft->doneh != NULL ||
X		(ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD)) {
X		/*
X		 *  Eat the completion queue and see if the request
X		 *  is already there.
X		 */
X		while (ft->doneh != NULL) {
X			if (blk == ft->doneh->reqblk) {
X				sp = ft->doneh;
X				sp->reqtype = FTIO_READING;
X				sp->reqbad = sr->sg_badmap;
X				goto rddone;
X			}
X			segio_free(ft, ft->doneh);
X		}
X
X		/*
X		 *  Not on the completed queue, in progress maybe?
X		 */
X		if (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD &&
X				blk == ft->segh->reqblk) {
X			sp = ft->segh;
X			sp->reqtype = FTIO_READING;
X			sp->reqbad = sr->sg_badmap;
X			goto rdwait;
X 		}
X	}
X
X	/* Wait until we're ready. */
X	tape_inactive(ft);
X
X	/* Set up a new read request. */
X	sp = segio_alloc(ft);
X	sp->reqcrc = 0;
X	sp->reqbad = sr->sg_badmap;
X	sp->reqblk = blk;
X	sp->reqseg = seg;
X	sp->reqcan = 0;
X	sp->reqtype = FTIO_READING;
X	segio_queue(ft, sp);
X
X	/* Start the read request off. */
X	DPRT(("Starting read I/O chain\n"));
X	arq_state = ard_state = awr_state = 0;
X	ft->xblk = sp->reqblk;
X	ft->xseg = sp->reqseg;
X	ft->xcnt = 0;
X	ft->xptr = sp->buff;
X	ft->active = 1;
X	timeout(ft_timeout, (caddr_t)ft, 1);
X
Xrdwait:
X	ftsleep(wc_buff_done, 0);
X
Xrddone:
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	segio_free(ft, sp);
X  } else {
X	if (ft->segh != NULL && ft->segh->reqtype != FTIO_WRITING)
X		tape_inactive(ft);
X
X	/* Allocate a buffer and start tape if we're running low. */
X	sp = segio_alloc(ft);
X	if (!ft->active && (sp == NULL || ft->nfreelist <= 1)) {
X		DPRT(("Starting write I/O chain\n"));
X		arq_state = ard_state = awr_state = 0;
X		ft->xblk = ft->segh->reqblk;
X		ft->xseg = ft->segh->reqseg;
X		ft->xcnt = 0;
X		ft->xptr = ft->segh->buff;
X		ft->active = 1;
X		timeout(ft_timeout, (caddr_t)ft, 1);
X	}
X
X	/* Sleep until a buffer becomes available. */
X	while (sp == NULL) {
X		ftsleep(wc_buff_avail, 0);
X		sp = segio_alloc(ft);
X	}
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	sp->reqblk = blk;
X	sp->reqseg = seg;
X	sp->reqcan = 0;
X	sp->reqtype = FTIO_WRITING;
X	segio_queue(ft, sp);
X  }
X  splx(s);
X  return(0);
X}
X
X
X/*
X *  Rewind to beginning of tape
X */
Xstatic int
Xftreq_rewind(struct ft_softc *ft)
X{
X  tape_inactive(ft);
X  tape_cmd(ft, QC_STOP);
X  tape_state(ft, 0, QS_READY, 90);
X  tape_cmd(ft, QC_SEEKSTART);
X  tape_state(ft, 0, QS_READY, 90);
X  tape_cmd(ft, QC_SEEKTRACK);
X  tape_cmd(ft, 2);
X  tape_state(ft, 0, QS_READY, 90);
X  ft->lastpos = -1;
X  ft->moving = 0;
X  return(0);
X}
X
X
X/*
X *  Move to logical beginning or end of track
X */
Xstatic int
Xftreq_trkpos(struct ft_softc *ft, int req)
X{
X  int curtrk, r, cmd;
X
X  tape_inactive(ft);
X  tape_cmd(ft, QC_STOP);
X  tape_state(ft, 0, QS_READY, 90);
X
X  r = tape_status(ft);
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 || ft->newcart) {
X	if (ftgetgeom(ft) < 0) return(ENXIO);
X  }
X
X  curtrk = (ft->lastpos < 0) ? 0 : ft->lastpos / 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(ft, cmd);
X  tape_state(ft, 0, QS_READY, 90);
X  return(0);
X}
X
X
X/*
X *  Seek tape head to a particular track.
X */
Xstatic int
Xftreq_trkset(struct ft_softc *ft, int *trk)
X{
X  int r;
X
X  tape_inactive(ft);
X  tape_cmd(ft, QC_STOP);
X  tape_state(ft, 0, QS_READY, 90);
X
X  r = tape_status(ft);
X  if ((r & QS_CART) == 0) return(ENXIO);	/* No cartridge */
X  if ((r & QS_FMTOK) == 0) return(ENXIO);	/* Not formatted */
X  if (ftg == NULL || ft->newcart) {
X	if (ftgetgeom(ft) < 0) return(ENXIO);
X  }
X
X  tape_cmd(ft, QC_SEEKTRACK);
X  tape_cmd(ft, *trk + 2);
X  tape_state(ft, 0, QS_READY, 90);
X  return(0);
X}
X
X
X/*
X *  Start tape moving forward.
X */
Xstatic int
Xftreq_lfwd(struct ft_softc *ft)
X{
X  tape_inactive(ft);
X  tape_cmd(ft, QC_STOP);
X  tape_state(ft, 0, QS_READY, 90);
X  tape_cmd(ft, QC_FORWARD);
X  ft->moving = 1;
X  return(0);
X}
X
X
X/*
X *  Stop the tape
X */
Xstatic int
Xftreq_stop(struct ft_softc *ft)
X{
X  tape_inactive(ft);
X  tape_cmd(ft, QC_STOP);
X  tape_state(ft, 0, QS_READY, 90);
X  ft->moving = 0;
X  return(0);
X}
X
X
X/*
X *  Set the particular mode the drive should be in.
X */
Xstatic int
Xftreq_setmode(struct ft_softc *ft, int cmd)
X{
X  int r;
X
X  tape_inactive(ft);
X  r = tape_status(ft);
X
X  switch(cmd) {
X     case QIOPRIMARY:
X	ft->mode = FTM_PRIMARY;
X	tape_cmd(ft, 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(ft, QC_FORMAT);
X	break;
X     case QIOVERIFY:
X	if ((r & QS_FMTOK) == 0) return(ENXIO);	/* Not formatted */
X	tape_cmd(ft, QC_VERIFY);
X	break;
X  }
X  tape_state(ft, 0, QS_READY, 60);
X  return(0);
X}
X
X
X/*
X *  Return drive status bits
X */
Xstatic int
Xftreq_status(struct ft_softc *ft, int cmd, int *sts, struct proc *p)
X{
X  if (ft->active)
X	*sts = ft->laststs & ~QS_READY;
X  else
X	*sts = tape_status(ft);
X  return(0);
X}
X
X
X/*
X *  Return drive configuration bits
X */
Xstatic int
Xftreq_config(struct ft_softc *ft, int cmd, int *cfg, struct proc *p)
X{
X  int r, tries;
X
X  if (ft->active)
X	r = ft->lastcfg;
X  else {
X	for (r = -1, tries = 0; r < 0 && tries < 3; tries++)
X		r = qic_status(ft, QC_CONFIG, 8);
X	if (tries == 3) return(ENXIO);
X  }
X  *cfg = r;
X  return(0);
X}
X
X
X/*
X *  Return current tape's geometry.
X */
Xstatic int
Xftreq_geom(struct ft_softc *ft, QIC_Geom *g)
X{
X  tape_inactive(ft);
X  if (ftg == NULL && ftgetgeom(ft) < 0) return(ENXIO);
X  bcopy(ftg, g, sizeof(QIC_Geom));
X  return(0);
X}
X
X
X/*
X *  Return drive hardware information
X */
Xstatic int
Xftreq_hwinfo(struct ft_softc *ft, QIC_HWInfo *hwp)
X{
X  int tries;
X  int rom, vend;
X
X  tape_inactive(ft);
X  bzero(hwp, sizeof(QIC_HWInfo));
X
X  for (rom = -1, tries = 0; rom < 0 && tries < 3; tries++)
X	rom = qic_status(ft, QC_VERSION, 8);
X  if (rom > 0) {
X	hwp->hw_rombeta = (rom >> 7) & 0x01;
X	hwp->hw_romid = rom & 0x7f;
X  }
X
X  for (vend = -1, tries = 0; vend < 0 && tries < 3; tries++)
X	vend = qic_status(ft, QC_VENDORID, 16);
X  if (vend > 0) {
X	hwp->hw_make = (vend >> 6) & 0x3ff;
X	hwp->hw_model = vend & 0x3f;
X  }
X
X  return(0);
X}
X
X
X/*
X *  Receive or Send the in-core header segment.
X */
Xstatic int
Xftreq_hdr(struct ft_softc *ft, int cmd, QIC_Segment *sp)
X{
X  QIC_Header *h = (QIC_Header *)ft->hdr->buff;
X
X  if (sp == NULL || sp->sg_data == NULL) return(EINVAL);
X  if (cmd == QIOSENDHDR) {
X	copyin(sp->sg_data, ft->hdr->buff, QCV_SEGSIZE);
X  } else {
X	if (h->qh_sig != QCV_HDRMAGIC) return(EIO);
X	copyout(ft->hdr->buff, sp->sg_data, QCV_SEGSIZE);
X  }
X  return(0);	
X}
X
X/*
X *  I/O functions.
X */
Xint
Xftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
X{
X  struct ft_softc *ft = ftcd.cd_devs[FDUNIT(dev)];
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(ft, cmd, (QIC_Segment *)data, p));
X
X     case QIOREWIND:	/* Rewind tape.					*/
X	return(ftreq_rewind(ft));
X
X     case QIOBOT:	/* Seek to logical beginning of track.		*/
X     case QIOEOT:	/* Seek to logical end of track.		*/
X	return(ftreq_trkpos(ft, cmd));
X
X     case QIOTRACK:	/* Seek tape head to specified track.		*/
X	return(ftreq_trkset(ft, (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(ft));
X
X     case QIOSTOP:	/* Causes tape to stop.				*/
X	return(ftreq_stop(ft));
X
X     case QIOPRIMARY:	/* Enter primary mode.				*/
X     case QIOFORMAT:	/* Enter format mode.				*/
X     case QIOVERIFY:	/* Enter verify mode.				*/
X	return(ftreq_setmode(ft, cmd));
X
X     case QIOWRREF:	/* Write reference burst.			*/
X	goto badreq;
X
X     case QIOSTATUS:	/* Get drive status.				*/
X	return(ftreq_status(ft, cmd, (int *)data, p));
X
X     case QIOCONFIG:	/* Get tape configuration.			*/
X	return(ftreq_config(ft, cmd, (int *)data, p));
X
X     case QIOGEOM:
X	return(ftreq_geom(ft, (QIC_Geom *)data));
X
X     case QIOHWINFO:
X	return(ftreq_hwinfo(ft, (QIC_HWInfo *)data));
X
X     case QIOSENDHDR:
X     case QIORECVHDR:
X	return(ftreq_hdr(ft, cmd, (QIC_Segment *)data));
X  }
Xbadreq:
X  DPRT(("ft%d: unknown ioctl(%d) request\n", ft->sc_unit, cmd));
X  return(ENXIO);
X}
X
X/*
X *  Not implemented
X */
Xint
Xftdump(dev_t dev)
X{
X  return(EINVAL);
X}
X
X/*
X *  Not implemented
X */
Xint
Xftsize(dev_t dev)
X{
X  return(EINVAL);
X}
X#endif
END-of-ft-netbsd/sys/ft.c
echo x - ft-netbsd/sys/ftreg.h
sed 's/^X//' >ft-netbsd/sys/ftreg.h << 'END-of-ft-netbsd/sys/ftreg.h'
X/*
X *  Copyright (c) 1993, 1994 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 *  ftreg.h - QIC-40/80 floppy tape driver header
X *  06/03/94 v0.9
X *  Changed seek load point to QC_SEEKLP, added reqseg to SegReq structure.
X *
X *  10/30/93 v0.3
X *  More things will end up here.  QC_VENDORID and QC_VERSION now used.
X *
X *  08/07/93 v0.2 release
X *  Things that should've been here in the first place were moved.
X *  Tape geometry and segment request types were added.
X *
X *  06/03/93 v0.1 Alpha release
X *  Initial revision.  Many more things should be moved here.
X */
X
X/* QIC-117 command set. */
X#define QC_RESET			1	/* reset */
X#define QC_NEXTBIT			2	/* report next bit */
X#define QC_PAUSE			3	/* pause */
X#define QC_STPAUSE			4	/* step pause */
X#define QC_TIMEOUT			5	/* alt timeout */
X#define QC_STATUS			6	/* report status */
X#define QC_ERRCODE			7	/* report error code */
X#define QC_CONFIG			8	/* report config */
X#define QC_VERSION			9	/* report version */
X#define QC_FORWARD			10	/* logical forward */
X#define QC_SEEKSTART			11	/* seek to track start */
X#define QC_SEEKEND			12	/* seek to track end */
X#define QC_SEEKTRACK			13	/* seek head to track */
X#define QC_SEEKLP			14	/* seek load point */
X#define QC_FORMAT			15	/* format mode */
X#define QC_WRITEREF			16	/* write reference */
X#define QC_VERIFY			17	/* verify mode */
X#define QC_STOP				18	/* stop tape */
X#define QC_STEPUP			21	/* step head up */
X#define QC_STEPDOWN			22	/* step head down */
X#define QC_SEEKREV			25	/* seek reverse */
X#define QC_SEEKFWD			26	/* seek forward */
X#define QC_RATE				27	/* select data rate */
X#define QC_DIAG1			28	/* diagnostic mode 1 */
X#define QC_DIAG2			29	/* diagnostic mode 2 */
X#define QC_PRIMARY			30	/* primary mode */
X#define QC_VENDORID			32	/* vendor id */
X#define QC_TSTATUS			33	/* report tape status */
X#define QC_EXTREV			34	/* extended skip reverse */
X#define QC_EXTFWD			35	/* extended skip forward */
X
X/* Colorado enable/disable. */
X#define QC_COL_ENABLE1			46	/* enable */
X#define QC_COL_ENABLE2			2	/* unit+2 */
X#define QC_COL_DISABLE			47	/* disable */
X
X/* Mountain enable/disable. */
X#define QC_MTN_ENABLE1			23	/* enable 1 */
X#define QC_MTN_ENABLE2			20	/* enable 2 */
X#define QC_MTN_DISABLE			24	/* disable */
X
X/* Segment I/O request. */
Xtypedef struct segq {
X	unsigned char buff[QCV_SEGSIZE];/* Segment data; first for alignment */
X	int reqtype;			/* Request type */
X	long reqcrc;			/* CRC Errors found */
X	long reqbad;			/* Bad sector map */
X	long reqblk;			/* Block request starts at */
X	long reqseg;			/* Segment request is at */
X	int reqcan;			/* Cancel read-ahead */
X	struct segq *next;		/* Next request */
X} SegReq;
END-of-ft-netbsd/sys/ftreg.h
echo x - ft-netbsd/sys/ftape.h
sed 's/^X//' >ft-netbsd/sys/ftape.h << 'END-of-ft-netbsd/sys/ftape.h'
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 *  ftape.h - QIC-40/80 floppy tape driver functions
X *  10/30/93 v0.3
X *  Set up constant values.  Added support to get hardware info.
X *
X *  08/07/93 v0.2
X *  Header file that sits in /sys/sys, first revision.  Support for
X *  ioctl functions added.
X */
X
X#ifndef	_FTAPE_H_
X#define	_FTAPE_H_
X
X#ifndef _IOCTL_H_
X#include <sys/ioctl.h>
X#endif
X
X/* Miscellaneous constant values */
X#define QCV_BLKSIZE	1024		/* Size of a block */
X#define QCV_SEGSIZE	32768		/* Size of a segment */
X#define QCV_BLKSEG	32		/* Blocks per segment */
X#define QCV_ECCSIZE	3072		/* Bytes ecc eats */
X#define QCV_ECCBLKS	3		/* Blocks ecc eats */
X#define QCV_NFMT	3		/* Number of tape formats */
X#define QCV_NLEN	5		/* Number of tape lengths */
X#define QCV_HDRMAGIC	0xaa55aa55	/* Magic for header segment */
X#define QCV_FSMAGIC	0x33cc33cc	/* Magic for fileset */
X
X#define UCHAR		unsigned char
X#define USHORT		unsigned short
X#define ULONG		unsigned long
X
X/* Segment request structure. */
Xtypedef struct qic_segment {
X	ULONG sg_trk;		/* Track number */
X	ULONG sg_seg;		/* Segment number */
X	ULONG sg_crcmap;	/* Returned bitmap of CRC errors */
X	ULONG sg_badmap;	/* Map of known bad sectors */
X	UCHAR *sg_data;		/* Segment w/bad blocks discarded */
X} QIC_Segment;
X
X/* Tape geometry structure. */
Xtypedef struct qic_geom {
X	int g_fmtno;			/* Format number */
X	int g_lenno;			/* Length number */
X	char g_fmtdesc[16];		/* Format text description */
X	char g_lendesc[16];		/* Length text description */
X	int g_trktape;			/* Number of tracks per tape */
X	int g_segtrk;			/* Number of segments per track */
X	int g_blktrk;			/* Number of blocks per track */
X	int g_fdtrk;			/* Floppy disk tracks */
X	int g_fdside;			/* Floppy disk sectors/side */
X} QIC_Geom;
X
X/* Tape hardware info */
Xtypedef struct qic_hwinfo {
X	int hw_make;			/* 10-bit drive make */
X	int hw_model;			/* 6-bit model */
X	int hw_rombeta;			/* TRUE if beta rom */
X	int hw_romid;			/* 8-bit rom ID */
X} QIC_HWInfo;
X
X/* Various ioctl() routines. */
X#define QIOREAD		_IOWR('q', 1, QIC_Segment)	/* Read segment     */
X#define QIOWRITE	_IOW('q', 2, QIC_Segment)	/* Write segment    */
X#define QIOREWIND	_IO('q', 3)			/* Rewind tape      */
X#define QIOBOT		_IO('q', 4)			/* Seek beg of trk  */
X#define QIOEOT		_IO('q', 5)			/* Seek end of trk  */
X#define QIOTRACK	_IOW('q', 6, int)		/* Seek to track    */
X#define QIOSEEKLP	_IO('q', 7)			/* Seek load point  */
X#define QIOFORWARD	_IO('q', 8)			/* Move tape fwd    */
X#define QIOSTOP		_IO('q', 9)			/* Stop tape	    */
X#define QIOPRIMARY	_IO('q', 10)			/* Primary mode     */
X#define QIOFORMAT	_IO('q', 11)			/* Format mode      */
X#define QIOVERIFY	_IO('q', 12)			/* Verify mode      */
X#define QIOWRREF	_IO('q', 13)			/* Write ref burst  */
X#define QIOSTATUS	_IOR('q', 14, int)		/* Get drive status */
X#define QIOCONFIG	_IOR('q', 15, int)		/* Get tape config  */
X#define QIOGEOM		_IOR('q', 16, QIC_Geom)		/* Get geometry	    */
X#define QIOHWINFO	_IOR('q', 17, QIC_HWInfo)	/* Get hardware inf */
X#define QIOSENDHDR	_IOW('q', 18, QIC_Segment)	/* Send header      */
X#define QIORECVHDR	_IOWR('q', 19, QIC_Segment)	/* Receive header   */
X
X/* QIC drive status bits. */
X#define QS_READY			0x01	/* Drive ready */
X#define QS_ERROR			0x02	/* Error detected */
X#define QS_CART				0x04	/* Tape in drive */
X#define QS_RDONLY			0x08	/* Write protect */
X#define QS_NEWCART			0x10	/* New tape inserted */
X#define QS_FMTOK			0x20	/* Tape is formatted */
X#define QS_BOT				0x40	/* Tape at beginning */
X#define QS_EOT				0x80	/* Tape at end */
X
X/* QIC configuration bits. */
X#define QCF_RTMASK			0x18	/* Rate mask */
X#define QCF_RT250			0x00	/* 250K bps */
X#define QCF_RT2				0x01	/* 2M bps */
X#define QCF_RT500			0x02	/* 500K bps */
X#define QCF_RT1				0x03	/* 1M bps */
X#define QCF_EXTRA			0x40	/* Extra length tape */
X#define QCF_QIC80			0x80	/* QIC-80 detected */
X
X/* QIC tape status bits. */
X#define QTS_FMMASK			0x0f	/* Tape format mask */
X#define QTS_LNMASK			0xf0	/* Tape length mask */
X#define QTS_QIC40			0x01	/* QIC-40 tape */
X#define QTS_QIC80			0x02	/* QIC-80 tape */
X#define QTS_QIC500			0x03	/* QIC-500 tape */
X#define QTS_LEN1			0x10	/* 205 ft/550 Oe */
X#define QTS_LEN2			0x20	/* 307.5 ft/550 Oe */
X#define QTS_LEN3			0x30	/* 295 ft/900 Oe */
X#define QTS_LEN4			0x40	/* 1100 ft/550 Oe */
X#define QTS_LEN5			0x50	/* 1100 ft/900 Oe */
X
X/* Tape header segment structure */
Xtypedef struct qic_header {
X	ULONG qh_sig;		/* Header signature 0x55aa55aa */
X	UCHAR qh_fmtc;		/* Format code */
X	UCHAR qh_unused1;
X	USHORT qh_hseg;		/* Header segment number */
X	USHORT qh_dhseg;	/* Duplicate header segment number */
X	USHORT qh_first;	/* First logical area data segment */
X	USHORT qh_last;		/* Last logical area data segment */
X	UCHAR qh_fmtdate[4];	/* Most recent format date */
X	UCHAR qh_chgdate[4];	/* Most recent tape change date */
X	UCHAR qh_unused2[2];
X	USHORT qh_tstrk;	/* Tape segments per track */
X	UCHAR qh_ttcart;	/* Tape tracks per cartridge */
X	UCHAR qh_mfside;	/* Max floppy sides */
X	UCHAR qh_mftrk;		/* Max floppy tracks */
X	UCHAR qh_mfsect;	/* Max floppy sector */
X	char qh_tname[44];	/* Tape name (ASCII, space filled) */
X	UCHAR qh_namdate[4];	/* Date tape was given a name */
X	USHORT qh_cprseg;	/* Compression map start segment */
X	UCHAR qh_unused3[48];
X	UCHAR qh_refmt;		/* Re-format flag */
X	UCHAR qh_unused4;
X	UCHAR qh_iocount[4];	/* I/O count for life of tape */
X	UCHAR qh_unused5[4];
X	UCHAR qh_ffmtdate[4];	/* Date first formatted */
X	USHORT qh_fmtcount;	/* Number of times formatted */
X	USHORT qh_badsect;	/* Failed sector count */
X	char qh_mfname[44];	/* Manufacturer name if pre-formatted */
X	char qh_mflot[44];	/* Manufacturer lot code */
X	UCHAR qh_unused6[22];
X	ULONG qh_fail[448];	/* Failed sector log */
X	ULONG qh_badmap[6912];	/* Bad sector map */
X} QIC_Header;
X
X/* Volume table of contents entry structure. */
Xtypedef struct qic_vtbl {
X	UCHAR vt_sig[4];	/* Signature "VTBL" if entry used */
X	USHORT vt_first;	/* Starting segment */
X	USHORT vt_last;		/* Ending segment */
X	char vt_vname[44];	/* Set name */
X	UCHAR vt_savdate[4];	/* Date saved */
X	UCHAR vt_flags;		/* Volume flags */
X	UCHAR vt_multi;		/* Multi cartidge sequence no. */
X	UCHAR vt_vext[26];	/* Extension data */
X	char vt_passwd[8];	/* Password for volume */
X	UCHAR vt_dirsize[4];	/* Directory section size */
X	UCHAR vt_dtasize[4];	/* Data section size */
X	USHORT vt_osver;	/* Operating System version */
X	char vt_label[16];	/* Source drive label */
X	UCHAR vt_ldev;		/* Logical device origin */
X	UCHAR vt_pdev;		/* Physical device origin */
X	UCHAR vt_cprtype;	/* Compression type */
X	UCHAR vt_ostype;	/* Operating System type */
X	UCHAR vt_ostype2;	/* Always zero ?? */
X	UCHAR vt_isocpr;	/* ISO compression type */
X	UCHAR vt_unused1[4];
X} QIC_VTbl;
X
X/* Data compression map structure. */
Xtypedef struct qic_dcmap {
X	UCHAR dc_sig[4];	/* Siguature "DCMS" */
X	USHORT dc_mlen;		/* Total map length */
X	UCHAR dc_unused1[6];
X	ULONG dc_offset[7421];	/* Byte offsets to segments */
X} QIC_DCMap;
X
X/* System specific file set structures - Unix */
Xtypedef struct qic_unix_set {
X	UCHAR fsu_perm;		/* Permissions */
X	UCHAR fsu_attr2;	/* More attributes */
X	UCHAR fsu_ctime[4];	/* Creation time */
X	UCHAR fsu_atime[4];	/* Last access time */
X	UCHAR fsu_inode[4];	/* i-node number */
X	UCHAR fsu_user[4];	/* User number */
X	UCHAR fsu_group[4];	/* Group number */
X	UCHAR fsu_major;	/* Major device number */
X	UCHAR fsu_minor;	/* Minor device number */
X	UCHAR fsu_nsize;	/* Name size */
X	UCHAR fsu_name;		/* Entry name starts here */
X} QIC_Unix_Set;
X
X/* File set structure */
Xtypedef struct qic_fileset {
X	UCHAR fs_size;		/* Size of fixed + system size - 1 */
X	UCHAR fs_attr;		/* Attributes */
X	UCHAR fs_mtime;		/* Modification time */
X	UCHAR fs_dsize[4];	/* Data size */
X} QIC_FileSet;
X
X#endif	/* _FTAPE_H_ */
END-of-ft-netbsd/sys/ftape.h
echo x - ft-netbsd/sys/fd.c.diffs
sed 's/^X//' >ft-netbsd/sys/fd.c.diffs << 'END-of-ft-netbsd/sys/fd.c.diffs'
X*** ./arch/i386/isa/fd.c.orig	Mon Oct 24 09:24:28 1994
X--- ./arch/i386/isa/fd.c	Sun Jan 15 16:38:44 1995
X***************
X*** 6,11 ****
X--- 6,15 ----
X   * This code is derived from software contributed to Berkeley by
X   * Don Ahn.
X   *
X+  * Additions made to support the ft QIC-40/QIC-80 floppy tape driver by
X+  * Chris Tham (christie@extro.ucc.su.oz.au) based on the FreeBSD driver
X+  * by Steve Gerakines.
X+  *
X   * Redistribution and use in source and binary forms, with or without
X   * modification, are permitted provided that the following conditions
X   * are met:
X***************
X*** 38,43 ****
X--- 42,49 ----
X   *	$Id: fd.c,v 1.48.2.3 1994/10/23 23:24:28 cgd Exp $
X   */
X  
X+ #include "ft.h"
X+ 
X  #include <sys/param.h>
X  #include <sys/systm.h>
X  #include <sys/kernel.h>
X***************
X*** 53,58 ****
X--- 59,67 ----
X  #include <sys/uio.h>
X  #include <sys/syslog.h>
X  #include <sys/queue.h>
X+ #if NFT > 0
X+ #include <sys/ftape.h>
X+ #endif
X  
X  #include <machine/cpu.h>
X  #include <machine/pio.h>
X***************
X*** 70,115 ****
X  #include <i386/isa/rtc.h>
X  #endif
X  
X! #define FDUNIT(dev)	(minor(dev) / 8)
X! #define FDTYPE(dev)	(minor(dev) % 8)
X  
X  #define b_cylin b_resid
X  
X- enum fdc_state {
X- 	DEVIDLE = 0,
X- 	MOTORWAIT,
X- 	DOSEEK,
X- 	SEEKWAIT,
X- 	SEEKTIMEDOUT,
X- 	SEEKCOMPLETE,
X- 	DOIO,
X- 	IOCOMPLETE,
X- 	IOTIMEDOUT,
X- 	DORESET,
X- 	RESETCOMPLETE,
X- 	RESETTIMEDOUT,
X- 	DORECAL,
X- 	RECALWAIT,
X- 	RECALTIMEDOUT,
X- 	RECALCOMPLETE,
X- };
X- 
X- /* software state, per controller */
X- struct fdc_softc {
X- 	struct device sc_dev;		/* boilerplate */
X- 	struct isadev sc_id;
X- 	struct intrhand sc_ih;
X- 
X- 	u_short	sc_iobase;
X- 	u_short	sc_drq;
X- 
X- 	struct fd_softc *sc_fd[4];	/* pointers to children */
X- 	TAILQ_HEAD(drivehead, fd_softc) sc_drives;
X- 	enum fdc_state sc_state;
X- 	int sc_errors;			/* number of retries so far */
X- 	u_char sc_status[7];		/* copy of registers */
X- };
X- 
X  /* controller driver configuration */
X  int fdcprobe();
X  #ifdef NEWCONFIG
X--- 79,88 ----
X  #include <i386/isa/rtc.h>
X  #endif
X  
X! #include <i386/isa/fdc.h>
X  
X  #define b_cylin b_resid
X  
X  /* controller driver configuration */
X  int fdcprobe();
X  #ifdef NEWCONFIG
X***************
X*** 141,146 ****
X--- 114,123 ----
X  	char	*name;
X  };
X  
X+ #if NFT > 0
X+ int ftintr __P((struct fdc_softc *));
X+ #endif
X+ 
X  /* The order of entries in the following table is important -- BEWARE! */
X  struct fd_type fd_types[] = {
X          { 18,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,2,"1.44MB"    }, /* 1.44MB diskette */
X***************
X*** 260,273 ****
X  #endif
X  
X  /*
X-  * Arguments passed between fdcattach and fdprobe.
X-  */
X- struct fdc_attach_args {
X- 	int fa_drive;
X- 	struct fd_type *fa_deftype;
X- };
X- 
X- /*
X   * Print the location of a disk drive (called just before attaching the
X   * the drive).  If `fdc' is not NULL, the drive was found but was not
X   * in the system config file; print the drive name as well.
X--- 237,242 ----
X***************
X*** 299,304 ****
X--- 268,274 ----
X  	fdc->sc_iobase = ia->ia_iobase;
X  	fdc->sc_drq = ia->ia_drq;
X  	fdc->sc_state = DEVIDLE;
X+ 	fdc->sc_flags = 0;
X  	TAILQ_INIT(&fdc->sc_drives);
X  
X  	printf("\n");
X***************
X*** 488,493 ****
X--- 458,473 ----
X  	daddr_t blkno;
X   	int s;
X  
X+ #if NFT > 0
X+ 	if (fdc->sc_flags & FDC_TAPE_BUSY)
X+ 	{
X+ 		printf("fd%d: accessed when tape is in use\n", fdu);
X+ 		bp->b_error = EBUSY;
X+ 		bp->b_flags |= B_ERROR;
X+ 		goto bad;
X+ 	}
X+ #endif
X+ 
X  #ifdef DIAGNOSTIC
X  	if (bp->b_blkno < 0 || fdu < 0 || fdu >= fdcd.cd_ndevs) {
X  		printf("fdstrategy: fdu=%d, blkno=%d, bcount=%d\n", fdu,
X***************
X*** 790,795 ****
X--- 770,780 ----
X  	u_short iobase = fdc->sc_iobase;
X  	int read, head, trac, sec, i, s, sectrac, blkno, nblks;
X  	struct fd_type *type;
X+ 
X+ #if NFT > 0
X+ 	if (fdc->sc_flags & FDC_TAPE_BUSY)
X+ 		ftintr(fdc);
X+ #endif
X  
X  again:
X  	fd = fdc->sc_drives.tqh_first;
END-of-ft-netbsd/sys/fd.c.diffs
echo x - ft-netbsd/sys/fd.c.new
sed 's/^X//' >ft-netbsd/sys/fd.c.new << 'END-of-ft-netbsd/sys/fd.c.new'
X/*-
X * Copyright (c) 1993, 1994 Charles Hannum.
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Don Ahn.
X *
X * Additions made to support the ft QIC-40/QIC-80 floppy tape driver by
X * Chris Tham (christie@extro.ucc.su.oz.au) based on the FreeBSD driver
X * by Steve Gerakines.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by the University of
X *	California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X *	from: @(#)fd.c	7.4 (Berkeley) 5/25/91
X *	$Id: fd.c,v 1.48.2.3 1994/10/23 23:24:28 cgd Exp $
X */
X
X#include "ft.h"
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/kernel.h>
X#include <sys/conf.h>
X#include <sys/file.h>
X#include <sys/ioctl.h>
X#include <sys/device.h>
X#include <sys/disklabel.h>
X#include <sys/dkstat.h>
X#include <sys/dkbad.h>
X#include <sys/disk.h>
X#include <sys/buf.h>
X#include <sys/uio.h>
X#include <sys/syslog.h>
X#include <sys/queue.h>
X#if NFT > 0
X#include <sys/ftape.h>
X#endif
X
X#include <machine/cpu.h>
X#include <machine/pio.h>
X
X#ifndef NEWCONFIG
X#include <i386/isa/isa_device.h>
X#endif
X#include <i386/isa/isavar.h>
X#include <i386/isa/dmavar.h>
X#include <i386/isa/icu.h>
X#include <i386/isa/fdreg.h>
X#ifdef NEWCONFIG
X#include <i386/isa/nvram.h>
X#else
X#include <i386/isa/rtc.h>
X#endif
X
X#include <i386/isa/fdc.h>
X
X#define b_cylin b_resid
X
X/* controller driver configuration */
Xint fdcprobe();
X#ifdef NEWCONFIG
Xvoid fdcforceintr __P((void *));
X#endif
Xvoid fdcattach();
Xint fdcintr __P((struct fdc_softc *));
X
Xstruct cfdriver fdccd = {
X	NULL, "fdc", fdcprobe, fdcattach, DV_DULL, sizeof(struct fdc_softc)
X};
X
X/*
X * Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how
X * we tell them apart.
X */
Xstruct fd_type {
X	int	sectrac;	/* sectors per track */
X	int	secsize;	/* size code for sectors */
X	int	datalen;	/* data len when secsize = 0 */
X	int	steprate;	/* step rate and head unload time */
X	int	gap1;		/* gap len between sectors */
X	int	gap2;		/* formatting gap */
X	int	tracks;		/* total num of tracks */
X	int	size;		/* size of disk in sectors */
X	int	step;		/* steps per cylinder */
X	int	rate;		/* transfer speed code */
X	int	heads;		/* number of heads */
X	char	*name;
X};
X
X#if NFT > 0
Xint ftintr __P((struct fdc_softc *));
X#endif
X
X/* The order of entries in the following table is important -- BEWARE! */
Xstruct fd_type fd_types[] = {
X        { 18,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,2,"1.44MB"    }, /* 1.44MB diskette */
X        { 15,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS,2, "1.2MB"    }, /* 1.2 MB AT-diskettes */
X        {  9,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS,2, "360KB/AT" }, /* 360kB in 1.2MB drive */
X        {  9,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS,2, "360KB/PC" }, /* 360kB PC diskettes */
X        {  9,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS,2, "720KB"    }, /* 3.5" 720kB diskette */
X        {  9,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS,2, "720KB/x"  }, /* 720kB in 1.2MB drive */
X        {  9,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS,2, "360KB/x"  }, /* 360kB in 720kB drive */
X};
X
X/* software state, per disk (with up to 4 disks per ctlr) */
Xstruct fd_softc {
X	struct device sc_dev;
X#ifdef NEWCONFIG
X	struct dkdevice sc_dk;
X#endif
X
X	struct fd_type *sc_deftype;	/* default type descriptor */
X	TAILQ_ENTRY(fd_softc) sc_drivechain;
X	struct buf sc_q;		/* head of buf chain */
X	int sc_drive;			/* unit number on this controller */
X	int sc_flags;
X#define	FD_OPEN		0x01		/* it's open */
X#define	FD_MOTOR	0x02		/* motor should be on */
X#define	FD_MOTOR_WAIT	0x04		/* motor coming up */
X	int sc_skip;			/* bytes transferred so far */
X	int sc_track;			/* where we think the head is */
X	int sc_nblks;			/* number of blocks tranferring */
X	int sc_ops;			/* I/O operations completed */
X	daddr_t	sc_blkno;		/* starting block number */
X};
X
X/* floppy driver configuration */
Xint fdprobe();
Xvoid fdattach();
X
Xstruct cfdriver fdcd = {
X	NULL, "fd", fdprobe, fdattach, DV_DISK, sizeof(struct fd_softc)
X};
X
X#ifdef NEWCONFIG
Xvoid fdstrategy __P((struct buf *));
X
Xstruct dkdriver fddkdriver = { fdstrategy };
X#endif
X
Xstruct fd_type *fd_nvtotype __P((char *, int, int));
Xvoid fdstart __P((struct fd_softc *fd));
Xvoid fd_set_motor __P((struct fdc_softc *fdc, int reset));
Xvoid fd_motor_off __P((void *arg));
Xvoid fd_motor_on __P((void *arg));
Xint fdcresult __P((struct fdc_softc *fdc));
Xint out_fdc __P((u_short iobase, u_char x));
Xvoid fdcstart __P((struct fdc_softc *fdc));
Xvoid fdcstatus __P((struct device *dv, int n, char *s));
Xvoid fdctimeout __P((void *arg));
Xvoid fdcpseudointr __P((void *arg));
Xint fdcintr __P((struct fdc_softc *fdc));
Xvoid fdcretry __P((struct fdc_softc *fdc));
Xvoid fdfinish __P((struct fd_softc *fd, struct buf *bp));
X
Xint
Xfdcprobe(parent, self, aux)
X	struct device *parent, *self;
X	void *aux;
X{
X	register struct isa_attach_args *ia = aux;
X	u_short iobase = ia->ia_iobase;
X
X	/* reset */
X	outb(iobase + fdout, 0);
X	delay(100);
X	outb(iobase + fdout, FDO_FRST);
X
X	/* see if it can handle a command */
X	if (out_fdc(iobase, NE7CMD_SPECIFY) < 0)
X		return 0;
X	out_fdc(iobase, 0xdf);
X	out_fdc(iobase, 2);
X
X#ifdef NEWCONFIG
X	if (iobase == IOBASEUNK || ia->ia_drq == DRQUNK)
X		return 0;
X
X	if (ia->ia_irq == IRQUNK) {
X		ia->ia_irq = isa_discoverintr(fdcforceintr, aux);
X		if (ia->ia_irq == IRQNONE)
X			return 0;
X
X		/* reset it again */
X		outb(iobase + fdout, 0);
X		delay(100);
X		outb(iobase + fdout, FDO_FRST);
X	}
X#endif
X
X	ia->ia_iosize = FDC_NPORT;
X	ia->ia_msize = 0;
X	return 1;
X}
X
X#ifdef NEWCONFIG
Xvoid
Xfdcforceintr(aux)
X	void *aux;
X{
X	struct isa_attach_args *ia = aux;
X	u_short iobase = ia->ia_iobase;
X
X	/* the motor is off; this should generate an error with or
X	   without a disk drive present */
X	out_fdc(iobase, NE7CMD_SEEK);
X	out_fdc(iobase, 0);
X	out_fdc(iobase, 0);
X}
X#endif
X
X/*
X * Print the location of a disk drive (called just before attaching the
X * the drive).  If `fdc' is not NULL, the drive was found but was not
X * in the system config file; print the drive name as well.
X * Return QUIET (config_find ignores this if the device was configured) to
X * avoid printing `fdN not configured' messages.
X */
Xint
Xfdprint(aux, fdc)
X	void *aux;
X	char *fdc;
X{
X	register struct fdc_attach_args *fa = aux;
X
X	if (!fdc)
X		printf(" drive %d", fa->fa_drive);
X	return QUIET;
X}
X
Xvoid
Xfdcattach(parent, self, aux)
X	struct device *parent, *self;
X	void *aux;
X{
X	register struct fdc_softc *fdc = (struct fdc_softc *)self;
X	struct isa_attach_args *ia = aux;
X	int type;
X	struct fdc_attach_args fa;
X
X	fdc->sc_iobase = ia->ia_iobase;
X	fdc->sc_drq = ia->ia_drq;
X	fdc->sc_state = DEVIDLE;
X	fdc->sc_flags = 0;
X	TAILQ_INIT(&fdc->sc_drives);
X
X	printf("\n");
X
X#ifdef NEWCONFIG
X	at_setup_dmachan(fdc->sc_drq, FDC_MAXIOSIZE);
X	isa_establish(&fdc->sc_id, &fdc->sc_dev);
X#endif
X	fdc->sc_ih.ih_fun = fdcintr;
X	fdc->sc_ih.ih_arg = fdc;
X	fdc->sc_ih.ih_level = IPL_BIO;
X	intr_establish(ia->ia_irq, &fdc->sc_ih);
X
X	/*
X	 * The NVRAM info only tells us about the first two disks on the
X	 * `primary' floppy controller.
X	 */
X	if (fdc->sc_dev.dv_unit == 0)
X#ifdef NEWCONFIG
X		type = nvram(NVRAM_DISKETTE);
X#else
X		type = rtcin(RTC_FDISKETTE);
X#endif
X	else
X		type = -1;
X
X	/* physical limit: four drives per controller. */
X	for (fa.fa_drive = 0; fa.fa_drive < 4; fa.fa_drive++) {
X		if (type >= 0 && fa.fa_drive < 2)
X			fa.fa_deftype = fd_nvtotype(fdc->sc_dev.dv_xname,
X			    type, fa.fa_drive);
X		else
X			fa.fa_deftype = NULL;		/* unknown */
X		(void)config_found(self, (void *)&fa, fdprint);
X	}
X}
X
Xint
Xfdprobe(parent, self, aux)
X	struct device *parent, *self;
X	void *aux;
X{
X	struct fdc_softc *fdc = (void *)parent;
X	struct cfdata *cf = self->dv_cfdata;
X	struct fdc_attach_args *fa = aux;
X	u_short iobase = fdc->sc_iobase;
X	int n;
X	int drive = fa->fa_drive;
X#ifdef NEWCONFIG
X
X#define cf_drive cf_loc[0]
X	if (cf->cf_drive != -1 && cf->cf_drive != drive)
X		return 0;
X#undef cf_drive
X#else
X	struct isa_device *id = (void *)cf->cf_loc;
X
X	if (id->id_physid != -1 && id->id_physid != drive)
X		return 0;
X#endif
X
X	/* select drive and turn on motor */
X	outb(iobase + fdout, drive | FDO_FRST | FDO_MOEN(drive));
X	/* wait for motor to spin up */
X	delay(250000);
X	out_fdc(iobase, NE7CMD_RECAL);
X	out_fdc(iobase, drive);
X	/* wait for recalibrate */
X	delay(2000000);
X	out_fdc(iobase, NE7CMD_SENSEI);
X	n = fdcresult(fdc);
X#ifdef DEBUG
X	{
X		int i;
X		printf("fdprobe: status");
X		for (i = 0; i < n; i++)
X			printf(" %x", fdc->sc_status[i]);
X		printf("\n");
X	}
X#endif
X	if (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20)
X		return 0;
X	/* turn off motor */
X	outb(iobase + fdout, FDO_FRST);
X
X	return 1;
X}
X
X/*
X * Controller is working, and drive responded.  Attach it.
X */
Xvoid
Xfdattach(parent, self, aux)
X	struct device *parent, *self;
X	void *aux;
X{
X	struct fdc_softc *fdc = (void *)parent;
X	struct fd_softc *fd = (void *)self;
X	struct fdc_attach_args *fa = aux;
X	struct fd_type *type = fa->fa_deftype;
X	int drive = fa->fa_drive;
X
X	/* XXXX should allow `flags' to override device type */
X
X	if (type)
X		printf(": %s %d cyl, %d head, %d sec\n", type->name,
X		    type->tracks, type->heads, type->sectrac);
X	else
X		printf(": density unknown\n");
X	fd->sc_track = -1;
X	fd->sc_drive = drive;
X	fd->sc_deftype = type;
X	fdc->sc_fd[drive] = fd;
X#ifdef NEWCONFIG
X	fd->sc_dk.dk_driver = &fddkdriver;
X	/* XXX need to do some more fiddling with sc_dk */
X	dk_establish(&fd->sc_dk, &fd->sc_dev);
X#endif
X}
X
X/*
X * Translate nvram type into internal data structure.  Return NULL for
X * none/unknown/unusable.
X */
Xstruct fd_type *
Xfd_nvtotype(fdc, nvraminfo, drive)
X	char *fdc;
X	int nvraminfo, drive;
X{
X	int type;
X
X	type = (drive == 0 ? nvraminfo : nvraminfo << 4) & 0xf0;
X	switch (type) {
X#ifdef NEWCONFIG
X	case NVRAM_DISKETTE_NONE:
X		return NULL;
X	case NVRAM_DISKETTE_12M:
X		return &fd_types[1];
X	case NVRAM_DISKETTE_144M:
X		return &fd_types[0];
X	case NVRAM_DISKETTE_360K:
X		return &fd_types[3];
X	case NVRAM_DISKETTE_720K:
X		return &fd_types[4];
X#else
X	case RTCFDT_NONE:
X		return NULL;
X	case RTCFDT_12M:
X		return &fd_types[1];
X	case RTCFDT_TYPE5:
X	case RTCFDT_TYPE6:
X		/* XXX We really ought to handle 2.88MB format. */
X	case RTCFDT_144M:
X		return &fd_types[0];
X	case RTCFDT_360K:
X		return &fd_types[3];
X	case RTCFDT_720K:
X		return &fd_types[4];
X#endif
X	default:
X		printf("%s: drive %d: unknown device type 0x%x\n",
X		    fdc, drive, type);
X		return NULL;
X	}
X}
X
Xinline struct fd_type *
Xfd_dev_to_type(fd, dev)
X	struct fd_softc *fd;
X	dev_t dev;
X{
X	int type = FDTYPE(dev);
X
X	return type ? &fd_types[type - 1] : fd->sc_deftype;
X}
X
Xvoid
Xfdstrategy(bp)
X	register struct buf *bp;	/* IO operation to perform */
X{
X	int fdu = FDUNIT(bp->b_dev);
X	struct fd_softc *fd = fdcd.cd_devs[fdu];
X	struct fdc_softc *fdc = (struct fdc_softc *)fd->sc_dev.dv_parent;
X	struct fd_type *type = fd_dev_to_type(fd, bp->b_dev);
X	struct buf *dp;
X	int nblks;
X	daddr_t blkno;
X 	int s;
X
X#if NFT > 0
X	if (fdc->sc_flags & FDC_TAPE_BUSY)
X	{
X		printf("fd%d: accessed when tape is in use\n", fdu);
X		bp->b_error = EBUSY;
X		bp->b_flags |= B_ERROR;
X		goto bad;
X	}
X#endif
X
X#ifdef DIAGNOSTIC
X	if (bp->b_blkno < 0 || fdu < 0 || fdu >= fdcd.cd_ndevs) {
X		printf("fdstrategy: fdu=%d, blkno=%d, bcount=%d\n", fdu,
X		    bp->b_blkno, bp->b_bcount);
X		bp->b_flags |= B_ERROR;
X		goto bad;
X	}
X#endif
X
X	blkno = bp->b_blkno * DEV_BSIZE / FDC_BSIZE;
X 	nblks = type->size;
X	if (blkno + (bp->b_bcount / FDC_BSIZE) > nblks) {
X		if (blkno == nblks) {
X			/* if we try to read past end of disk, return count of 0 */
X			bp->b_resid = bp->b_bcount;
X		} else {
X			bp->b_error = ENOSPC;
X			bp->b_flags |= B_ERROR;
X		}
X		goto bad;
X	}
X 	bp->b_cylin = (blkno / (type->sectrac * type->heads)) * type->step;
X#ifdef notyet
X	bp->b_type = type;
X#endif
X#ifdef DEBUG
X	printf("fdstrategy: b_blkno %d b_bcount %d blkno %d cylin %d nblks %d\n",
X	    bp->b_blkno, bp->b_bcount, fd->sc_blkno, bp->b_cylin, nblks);
X#endif
X	s = splbio();
X	disksort(&fd->sc_q, bp);
X	untimeout(fd_motor_off, fd); /* a good idea */
X	if (!fd->sc_q.b_active)
X		fdstart(fd);
X#ifdef DIAGNOSTIC
X	else if (fdc->sc_state == DEVIDLE) {
X		printf("fdstrategy: controller inactive\n");
X		fdcstart(fdc);
X	}
X#endif
X	splx(s);
X	return;
X
Xbad:
X	biodone(bp);
X}
X
Xvoid
Xfdstart(fd)
X	struct fd_softc *fd;
X{
X	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
X	int active = fdc->sc_drives.tqh_first != 0;
X
X	/* Link into controller queue. */
X	fd->sc_q.b_active = 1;
X	TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
X
X	/* If controller not already active, start it. */
X	if (!active)
X		fdcstart(fdc);
X}
X
Xvoid
Xfd_set_motor(fdc, reset)
X	struct fdc_softc *fdc;
X	int reset;
X{
X	struct fd_softc *fd;
X	u_char status;
X	int n;
X
X	if (fd = fdc->sc_drives.tqh_first)
X		status = fd->sc_drive;
X	else
X		status = 0;
X	if (!reset)
X		status |= FDO_FRST | FDO_FDMAEN;
X	for (n = 0; n < 4; n++)
X		if ((fd = fdc->sc_fd[n]) && (fd->sc_flags & FD_MOTOR))
X			status |= FDO_MOEN(n);
X	outb(fdc->sc_iobase + fdout, status);
X}
X
Xvoid
Xfd_motor_off(arg)
X	void *arg;
X{
X	int s;
X	struct fd_softc *fd;
X
X	fd = (struct fd_softc *)arg;
X
X	s = splbio();
X	fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
X	fd_set_motor((struct fdc_softc *)fd->sc_dev.dv_parent, 0);
X	splx(s);
X}
X
Xvoid
Xfd_motor_on(arg)
X	void *arg;
X{
X	struct fd_softc *fd;
X	struct fdc_softc *fdc;
X	int s;
X
X	fd = (struct fd_softc *)arg;
X	fdc = (struct fdc_softc *)fd->sc_dev.dv_parent;
X
X	s = splbio();
X	fd->sc_flags &= ~FD_MOTOR_WAIT;
X	if ((fdc->sc_drives.tqh_first == fd) && (fdc->sc_state == MOTORWAIT))
X		(void) fdcintr(fdc);
X	splx(s);
X}
X
Xint
Xfdcresult(fdc)
X	struct fdc_softc *fdc;
X{
X	u_short iobase = fdc->sc_iobase;
X	u_char i;
X	int j = 100000,
X	    n = 0;
X
X	for (; j; j--) {
X		i = inb(iobase + fdsts) & (NE7_DIO | NE7_RQM | NE7_CB);
X		if (i == NE7_RQM)
X			return n;
X		if (i == (NE7_DIO | NE7_RQM | NE7_CB)) {
X			if (n >= sizeof(fdc->sc_status)) {
X				log(LOG_ERR, "fdcresult: overrun\n");
X				return -1;
X			}
X			fdc->sc_status[n++] = inb(iobase + fddata);
X		}
X	}
X	log(LOG_ERR, "fdcresult: timeout\n");
X	return -1;
X}
X
Xint
Xout_fdc(iobase, x)
X	u_short iobase;
X	u_char x;
X{
X	int i = 100000;
X
X	while ((inb(iobase + fdsts) & NE7_DIO) && i-- > 0);
X	if (i <= 0)
X		return -1;
X	while ((inb(iobase + fdsts) & NE7_RQM) == 0 && i-- > 0);
X	if (i <= 0)
X		return -1;
X	outb(iobase + fddata, x);
X	return 0;
X}
X
Xint
XFdopen(dev, flags)
X	dev_t dev;
X	int flags;
X{
X 	int fdu = FDUNIT(dev);
X 	int type = FDTYPE(dev);
X	struct fd_softc *fd;
X
X	if (fdu >= fdcd.cd_ndevs)
X		return ENXIO;
X	fd = fdcd.cd_devs[fdu];
X	if (!fd)
X		return ENXIO;
X
X	if (type > (sizeof(fd_types) / sizeof(fd_types[0])))
X		return EINVAL;
X
X	fd->sc_track = -1;
X	/* XXX disallow multiple opens? */
X	fd->sc_flags |= FD_OPEN;
X
X	return 0;
X}
X
Xint
Xfdclose(dev, flags)
X	dev_t dev;
X	int flags;
X{
X 	int fdu = FDUNIT(dev);
X	struct fd_softc *fd = fdcd.cd_devs[fdu];
X
X	fd->sc_flags &= ~FD_OPEN;
X	return 0;
X}
X
Xvoid
Xfdcstart(fdc)
X	struct fdc_softc *fdc;
X{
X
X#ifdef DIAGNOSTIC
X	/* only got here if controller's drive queue was inactive; should
X	   be in idle state */
X	if (fdc->sc_state != DEVIDLE) {
X		printf("fdcstart: not idle\n");
X		return;
X	}
X#endif
X	(void) fdcintr(fdc);
X}
X
Xvoid
Xfdcstatus(dv, n, s)
X	struct device *dv;
X	int n;
X	char *s;
X{
X	struct fdc_softc *fdc = (void *)dv->dv_parent;
X	u_short iobase = fdc->sc_iobase;
X
X	if (n == 0) {
X		out_fdc(fdc->sc_iobase, NE7CMD_SENSEI);
X		(void) fdcresult(fdc);
X		n = 2;
X	}
X
X	printf("%s: %s", dv->dv_xname, s);
X
X	switch (n) {
X	case 0:
X		printf("\n");
X		break;
X	case 2:
X		printf(" (st0 %b cyl %d)\n",
X		    fdc->sc_status[0], NE7_ST0BITS,
X		    fdc->sc_status[1]);
X		break;
X	case 7:
X		printf(" (st0 %b st1 %b st2 %b cyl %d head %d sec %d)\n",
X		    fdc->sc_status[0], NE7_ST0BITS,
X		    fdc->sc_status[1], NE7_ST1BITS,
X		    fdc->sc_status[2], NE7_ST2BITS,
X		    fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
X		break;
X#ifdef DIAGNOSTIC
X	default:
X		printf("\nfdcstatus: weird size");
X		break;
X#endif
X	}
X}
X
Xvoid
Xfdctimeout(arg)
X	void *arg;
X{
X	struct fdc_softc *fdc;
X	struct fd_softc *fd;
X	int s;
X
X	fdc = (struct fdc_softc *)arg;
X	fd = fdc->sc_drives.tqh_first;
X
X	s = splbio();
X	fdcstatus(&fd->sc_dev, 0, "timeout");
X
X	if (fd->sc_q.b_actf)
X		fdc->sc_state++;
X	else
X		fdc->sc_state = DEVIDLE;
X
X	(void) fdcintr(fdc);
X	splx(s);
X}
X
Xvoid
Xfdcpseudointr(arg)
X	void *arg;
X{
X	struct fdc_softc *fdc;
X	int s;
X
X	fdc = (struct fdc_softc *)arg;
X
X	/* just ensure it has the right spl */
X	s = splbio();
X	(void) fdcintr(fdc);
X	splx(s);
X}
X
Xint
Xfdcintr(fdc)
X	struct fdc_softc *fdc;
X{
X#define	st0	fdc->sc_status[0]
X#define	cyl	fdc->sc_status[1]
X	struct fd_softc *fd;
X	struct buf *bp;
X	u_short iobase = fdc->sc_iobase;
X	int read, head, trac, sec, i, s, sectrac, blkno, nblks;
X	struct fd_type *type;
X
X#if NFT > 0
X	if (fdc->sc_flags & FDC_TAPE_BUSY)
X		ftintr(fdc);
X#endif
X
Xagain:
X	fd = fdc->sc_drives.tqh_first;
X	if (!fd) {
X		/* no drives waiting; end */
X		fdc->sc_state = DEVIDLE;
X 		return 1;
X	}
X	bp = fd->sc_q.b_actf;
X	if (!bp) {
X		/* nothing queued on this drive; try next */
X		fd->sc_ops = 0;
X		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
X		fd->sc_q.b_active = 0;
X		goto again;
X	}
X
X	switch (fdc->sc_state) {
X	case DEVIDLE:
X		fdc->sc_errors = 0;
X		fd->sc_skip = 0;
X		fd->sc_blkno = bp->b_blkno * DEV_BSIZE / FDC_BSIZE;
X		untimeout(fd_motor_off, fd);
X		if (fd->sc_flags & FD_MOTOR_WAIT) {
X			fdc->sc_state = MOTORWAIT;
X			return 1;
X		}
X		if (!(fd->sc_flags & FD_MOTOR)) {
X			/* lame controller */
X			struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1];
X			if (ofd && ofd->sc_flags & FD_MOTOR) {
X				untimeout(fd_motor_off, ofd);
X				ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
X			}
X			fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
X			fd_set_motor(fdc, 0);
X			fdc->sc_state = MOTORWAIT;
X			/* allow .25s for motor to stabilize */
X			timeout(fd_motor_on, fd, hz / 4);
X			return 1;
X		}
X		/* at least make sure we are selected */
X		fd_set_motor(fdc, 0);
X
X		/* fall through */
X	case DOSEEK:
X	doseek:
X		if (fd->sc_track == bp->b_cylin)
X			goto doio;
X
X#ifdef notyet
X		type = bp->b_type;
X#else
X		type = fd_dev_to_type(fd, bp->b_dev);
X#endif
X		out_fdc(iobase, NE7CMD_SPECIFY);/* specify command */
X		out_fdc(iobase, type->steprate);
X		out_fdc(iobase, 6);		/* XXX head load time == 6ms */
X
X		out_fdc(iobase, NE7CMD_SEEK);	/* seek function */
X		out_fdc(iobase, fd->sc_drive);	/* drive number */
X		out_fdc(iobase, bp->b_cylin);
X		fd->sc_track = -1;
X		fdc->sc_state = SEEKWAIT;
X		timeout(fdctimeout, fdc, 4 * hz);
X		return 1;
X
X	case DOIO:
X	doio:
X#ifdef notyet
X		type = bp->b_type;
X#else
X		type = fd_dev_to_type(fd, bp->b_dev);
X#endif
X		sectrac = type->sectrac;
X		sec = fd->sc_blkno % (sectrac * type->heads);
X		nblks = (sectrac * type->heads) - sec;
X		nblks = min(nblks,
X		    (bp->b_bcount - fd->sc_skip) / FDC_BSIZE);
X		nblks = min(nblks, FDC_MAXIOSIZE / FDC_BSIZE);
X		fd->sc_nblks = nblks;
X		head = sec / sectrac;
X		sec %= sectrac;
X#ifdef DIAGNOSTIC
X		{int block;
X		 block = (fd->sc_track * type->heads / type->step + head) * sectrac + sec;
X		 if (block != fd->sc_blkno) {
X			 printf("fdcintr: block %d != blkno %d\n", block, fd->sc_blkno);
X#ifdef DDB
X			 Debugger();
X#endif
X		 }}
X#endif
X		read = bp->b_flags & B_READ;
X#ifdef NEWCONFIG
X		at_dma(read, bp->b_data + fd->sc_skip, nblks * FDC_BSIZE,
X		    fdc->sc_drq);
X#else
X		isa_dmastart(read, bp->b_data + fd->sc_skip, nblks * FDC_BSIZE,
X		    fdc->sc_drq);
X#endif
X		outb(iobase + fdctl, type->rate);
X#ifdef DEBUG
X		printf("fdcintr: %s drive %d track %d head %d sec %d nblks %d\n",
X		    read ? "read" : "write", fd->sc_drive, fd->sc_track, head,
X		    sec, nblks);
X#endif
X		if (read)
X			out_fdc(iobase, NE7CMD_READ);	/* READ */
X		else
X			out_fdc(iobase, NE7CMD_WRITE);	/* WRITE */
X		out_fdc(iobase, (head << 2) | fd->sc_drive);
X		out_fdc(iobase, fd->sc_track / type->step);	/* track */
X		out_fdc(iobase, head);
X		out_fdc(iobase, sec + 1);		/* sector +1 */
X		out_fdc(iobase, type->secsize);		/* sector size */
X		out_fdc(iobase, sectrac);		/* sectors/track */
X		out_fdc(iobase, type->gap1);		/* gap1 size */
X		out_fdc(iobase, type->datalen);		/* data length */
X		fdc->sc_state = IOCOMPLETE;
X		/* allow 2 seconds for operation */
X		timeout(fdctimeout, fdc, 2 * hz);
X		return 1;				/* will return later */
X
X	case SEEKWAIT:
X		untimeout(fdctimeout, fdc);
X		fdc->sc_state = SEEKCOMPLETE;
X		/* allow 1/50 second for heads to settle */
X		timeout(fdcpseudointr, fdc, hz / 50);
X		return 1;
X		
X	case SEEKCOMPLETE:
X		/* make sure seek really happened */
X		out_fdc(iobase, NE7CMD_SENSEI);
X		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != bp->b_cylin) {
X#ifdef FD_DEBUG
X			fdcstatus(&fd->sc_dev, 2, "seek failed");
X#endif
X			fdcretry(fdc);
X			goto again;
X		}
X		fd->sc_track = bp->b_cylin;
X		goto doio;
X
X	case IOTIMEDOUT:
X#ifdef NEWCONFIG
X		at_dma_abort(fdc->sc_drq);
X#else
X		isa_dmaabort(fdc->sc_drq);
X#endif
X	case SEEKTIMEDOUT:
X	case RECALTIMEDOUT:
X	case RESETTIMEDOUT:
X		fdcretry(fdc);
X		goto again;
X
X	case IOCOMPLETE: /* IO DONE, post-analyze */
X		untimeout(fdctimeout, fdc);
X		if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) {
X#ifdef NEWCONFIG
X			at_dma_abort(fdc->sc_drq);
X#else
X			isa_dmaabort(fdc->sc_drq);
X#endif
X#ifdef FD_DEBUG
X			fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ?
X			    "read failed" : "write failed");
X			printf("blkno %d nblks %d\n",
X			    fd->sc_blkno, fd->sc_nblks);
X#endif
X			fdcretry(fdc);
X			goto again;
X		}
X		nblks = fd->sc_nblks;
X#ifdef NEWCONFIG
X		at_dma_terminate(fdc->sc_drq);
X#else
X		isa_dmadone(bp->b_flags & B_READ, bp->b_data + fd->sc_skip,
X		    nblks * FDC_BSIZE, fdc->sc_drq);
X#endif
X		if (fdc->sc_errors) {
X			diskerr(bp, "fd", "soft error", LOG_PRINTF,
X			    fd->sc_skip / FDC_BSIZE, (struct disklabel *)NULL);
X			printf("\n");
X			fdc->sc_errors = 0;
X		}
X		fd->sc_skip += nblks * FDC_BSIZE;
X		if (fd->sc_skip < bp->b_bcount) {
X			/* set up next transfer */
X			blkno = fd->sc_blkno += nblks;
X#ifdef notyet
X			type = bp->b_type;
X#else
X			type = fd_dev_to_type(fd, bp->b_dev);
X#endif
X			bp->b_cylin = (blkno / (type->sectrac * type->heads)) * type->step;
X			goto doseek;
X		} else {
X			fdfinish(fd, bp);
X			goto again;
X		}
X
X	case DORESET:
X		/* try a reset, keep motor on */
X		fd_set_motor(fdc, 1);
X		delay(100);
X		fd_set_motor(fdc, 0);
X		fdc->sc_state = RESETCOMPLETE;
X		timeout(fdctimeout, fdc, hz / 2);
X		return 1;			/* will return later */
X
X	case RESETCOMPLETE:
X		untimeout(fdctimeout, fdc);
X		/* clear the controller output buffer */
X		for (i = 0; i < 4; i++) {
X			out_fdc(iobase, NE7CMD_SENSEI);
X			(void) fdcresult(fdc);
X		}
X
X		/* fall through */
X	case DORECAL:
X		out_fdc(iobase, NE7CMD_RECAL);	/* recalibrate function */
X		out_fdc(iobase, fd->sc_drive);
X		fdc->sc_state = RECALWAIT;
X		timeout(fdctimeout, fdc, 5 * hz);
X		return 1;			/* will return later */
X
X	case RECALWAIT:
X		untimeout(fdctimeout, fdc);
X		fdc->sc_state = RECALCOMPLETE;
X		/* allow 1/30 second for heads to settle */
X		timeout(fdcpseudointr, fdc, hz / 30);
X		return 1;			/* will return later */
X
X	case RECALCOMPLETE:
X		out_fdc(iobase, NE7CMD_SENSEI);
X		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) {
X#ifdef FD_DEBUG
X			fdcstatus(&fd->sc_dev, 2, "recalibrate failed");
X#endif
X			fdcretry(fdc);
X			goto again;
X		}
X		fd->sc_track = 0;
X		goto doseek;
X
X	case MOTORWAIT:
X		if (fd->sc_flags & FD_MOTOR_WAIT)
X			return 1;		/* time's not up yet */
X		goto doseek;
X
X	default:
X		fdcstatus(&fd->sc_dev, 0, "stray interrupt");
X		return 1;
X	}
X#ifdef DIAGNOSTIC
X	panic("fdcintr: impossible");
X#endif
X#undef	st0
X#undef	cyl
X}
X
Xvoid
Xfdcretry(fdc)
X	struct fdc_softc *fdc;
X{
X	struct fd_softc *fd;
X	struct buf *bp;
X
X	fd = fdc->sc_drives.tqh_first;
X	bp = fd->sc_q.b_actf;
X
X	switch (fdc->sc_errors) {
X	case 0:
X		/* try again */
X		fdc->sc_state = SEEKCOMPLETE;
X		break;
X
X	case 1: case 2: case 3:
X		/* didn't work; try recalibrating */
X		fdc->sc_state = DORECAL;
X		break;
X
X	case 4:
X		/* still no go; reset the bastard */
X		fdc->sc_state = DORESET;
X		break;
X
X	default:
X		diskerr(bp, "fd", "hard error", LOG_PRINTF,
X		    fd->sc_skip / FDC_BSIZE, (struct disklabel *)NULL);
X		printf(" (st0 %b st1 %b st2 %b cyl %d head %d sec %d)\n",
X		    fdc->sc_status[0], NE7_ST0BITS,
X		    fdc->sc_status[1], NE7_ST1BITS,
X		    fdc->sc_status[2], NE7_ST2BITS,
X		    fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
X
X		bp->b_flags |= B_ERROR;
X		bp->b_error = EIO;
X		fdfinish(fd, bp);
X	}
X	fdc->sc_errors++;
X}
X
Xvoid
Xfdfinish(fd, bp)
X	struct fd_softc *fd;
X	struct buf *bp;
X{
X	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
X
X	/*
X	 * Move this drive to the end of the queue to give others a `fair'
X	 * chance.  We only force a switch if N operations are completed while
X	 * another drive is waiting to be serviced, since there is a long motor
X	 * startup delay whenever we switch.
X	 */
X	if (fd->sc_drivechain.tqe_next && ++fd->sc_ops >= 8) {
X		fd->sc_ops = 0;
X		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
X		if (bp->b_actf) {
X			TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
X		} else
X			fd->sc_q.b_active = 0;
X	}
X	bp->b_resid = bp->b_bcount - fd->sc_skip;
X	fd->sc_skip = 0;
X	fd->sc_q.b_actf = bp->b_actf;
X	biodone(bp);
X	/* turn off motor 5s from now */
X	timeout(fd_motor_off, fd, 5 * hz);
X	fdc->sc_state = DEVIDLE;
X}
X
Xint
Xfdsize(dev)
X	dev_t dev;
X{
X
X	/* Swapping to floppies would not make sense. */
X	return -1;
X}
X
Xint
Xfddump()
X{
X
X	/* Not implemented. */
X	return EINVAL;
X}
X
Xint
Xfdioctl(dev, cmd, addr, flag)
X	dev_t dev;
X	int cmd;
X	caddr_t addr;
X	int flag;
X{
X	struct fd_softc *fd = fdcd.cd_devs[FDUNIT(dev)];
X	struct fd_type *type;
X	struct disklabel buffer;
X	int error;
X
X	switch (cmd) {
X	case DIOCGDINFO:
X		bzero(&buffer, sizeof(buffer));
X		
X		type = fd_dev_to_type(fd, dev);
X		buffer.d_secpercyl = type->size / type->tracks;
X		buffer.d_type = DTYPE_FLOPPY;
X		buffer.d_secsize = FDC_BSIZE;
X
X		if (readdisklabel(dev, fdstrategy, &buffer, NULL) != NULL)
X			return EINVAL;
X
X		*(struct disklabel *)addr = buffer;
X		return 0;
X
X	case DIOCWLABEL:
X		if ((flag & FWRITE) == 0)
X			return EBADF;
X		/* XXX do something */
X		return 0;
X
X	case DIOCWDINFO:
X		if ((flag & FWRITE) == 0)
X			return EBADF;
X
X		error = setdisklabel(&buffer, (struct disklabel *)addr, 0, NULL);
X		if (error)
X			return error;
X
X		error = writedisklabel(dev, fdstrategy, &buffer, NULL);
X		return error;
X
X	default:
X		return ENOTTY;
X	}
X
X#ifdef DIAGNOSTIC
X	panic("fdioctl: impossible");
X#endif
X}
END-of-ft-netbsd/sys/fd.c.new
echo x - ft-netbsd/sys/ft.patch
sed 's/^X//' >ft-netbsd/sys/ft.patch << 'END-of-ft-netbsd/sys/ft.patch'
X*** ./arch/i386/conf/files.i386.orig	Wed Aug 24 03:59:33 1994
X--- ./arch/i386/conf/files.i386	Sun Jan 15 14:23:10 1995
X***************
X*** 30,35 ****
X--- 30,36 ----
X  arch/i386/isa/dma.c		standard
X  arch/i386/isa/elink.c		optional ep or ie
X  arch/i386/isa/fd.c		optional fd device-driver requires isa
X+ arch/i386/isa/ft.c		optional ft device-driver requires fd isa
X  arch/i386/isa/if_ed.c		optional ed requires ether isa
X  arch/i386/isa/if_eg.c		optional eg requires ether isa
X  arch/i386/isa/if_el.c		optional el requires ether isa
X*** ./arch/i386/conf/devices.i386.orig	Wed Aug 17 05:20:43 1994
X--- ./arch/i386/conf/devices.i386	Sun Jan 15 12:43:06 1995
X***************
X*** 10,13 ****
X--- 10,14 ----
X  st	5
X  cd	6
X  mcd	7
X+ ft	8
X  vnd	14
X*** ./arch/i386/i386/conf.c.orig	Wed Jun 15 05:47:51 1994
X--- ./arch/i386/i386/conf.c	Sat Jan 21 12:26:12 1995
X***************
X*** 101,106 ****
X--- 101,107 ----
X  #include "st.h"
X  #include "cd.h"
X  #include "mcd.h"
X+ #include "ft.h"
X  #include "vn.h"
X  
X  bdev_decl(wd);
X***************
X*** 112,117 ****
X--- 113,119 ----
X  bdev_decl(st);
X  bdev_decl(cd);
X  bdev_decl(mcd);
X+ bdev_decl(ft);
X  bdev_decl(vn);
X  
X  struct bdevsw	bdevsw[] =
X***************
X*** 126,132 ****
X--- 128,138 ----
X  	bdev_tape_init(NST,st),		/* 5: SCSI tape */
X  	bdev_disk_init(NCD,cd),		/* 6: SCSI CD-ROM */
X  	bdev_disk_init(NMCD,mcd),	/* 7: Mitsumi CD-ROM */
X+ #if NFT > 0
X+ 	bdev_tape_init(NFT,ft),		/* 8: QIC-40/QIC-80 floppy tape */
X+ #else
X  	bdev_lkm_dummy(), /* 8 */
X+ #endif
X  	bdev_lkm_dummy(), /* 9 */
X  	bdev_lkm_dummy(), /* 10 */
X  	bdev_lkm_dummy(), /* 11 */
X***************
X*** 306,311 ****
X--- 312,318 ----
X  cdev_decl(st);
X  cdev_decl(cd);
X  cdev_decl(mcd);
X+ cdev_decl(ft);
X  cdev_decl(vn);
X  
X  #include "pc.h"
X***************
X*** 372,378 ****
X--- 379,389 ----
X  	cdev_disk_init(NFD,fd),		/* 9: floppy diskette */
X  #undef	fdopen
X  	cdev_tape_init(NWT,wt),		/* 10: QIC-02/QIC-36 tape */
X+ #if NFT > 0
X+ 	cdev_tape_init(NFT,ft),		/* 11: QIC-40/QIC-80 tape */
X+ #else
X  	cdev_notdef(),			/* 11: unused */
X+ #endif
X  	cdev_pc_init(NPC + NVT,pc),	/* 12: PC console */
X  	cdev_disk_init(NSD,sd),		/* 13: SCSI disk */
X  	cdev_tape_init(NST,st),		/* 14: SCSI tape */
X***************
X*** 455,460 ****
X--- 466,472 ----
X  	case 4:
X  	case 6:
X  	case 7:
X+ 	case 8:
X  	case 14:
X  		return (type == VBLK);
X  	case 3:
X***************
X*** 484,490 ****
X  	/*  8 */	NODEV,
X  	/*  9 */	2,
X  	/* 10 */	3,
X! 	/* 11 */	NODEV,
X  	/* 12 */	NODEV,
X  	/* 13 */	4,
X  	/* 14 */	5,
X--- 496,502 ----
X  	/*  8 */	NODEV,
X  	/*  9 */	2,
X  	/* 10 */	3,
X! 	/* 11 */	8,
X  	/* 12 */	NODEV,
X  	/* 13 */	4,
X  	/* 14 */	5,
END-of-ft-netbsd/sys/ft.patch
echo c - ft-netbsd/dev
mkdir ft-netbsd/dev > /dev/null 2>&1
echo x - ft-netbsd/dev/MAKEDEV.local.diffs
sed 's/^X//' >ft-netbsd/dev/MAKEDEV.local.diffs << 'END-of-ft-netbsd/dev/MAKEDEV.local.diffs'
X*** MAKEDEV.local.orig	Thu Oct 20 05:27:40 1994
X--- MAKEDEV.local	Sun Jan 15 13:26:15 1995
X***************
X*** 36,46 ****
X  
X  # Local device MAKEDEV script.
X  
X! PATH=/sbin:/bin:/usr/bin
X  umask 77
X  for i
X  do
X  case $i in
X  
X  *)
X  	echo 'MAKEDEV.local: no such device.'
X--- 36,54 ----
X  
X  # Local device MAKEDEV script.
X  
X! PATH=/sbin:/bin:/usr/bin:/usr/sbin
X  umask 77
X  for i
X  do
X  case $i in
X+ 
X+ ft*)
X+ 	umask 2
X+ 	rm -f ft0 rft0
X+ 	mknod ft0	b 8 0;	chown root.operator ft0
X+ 	mknod rft0	c 11 0;	chown root.operator rft0
X+ 	umask 77
X+ 	;;
X  
X  *)
X  	echo 'MAKEDEV.local: no such device.'
END-of-ft-netbsd/dev/MAKEDEV.local.diffs
exit