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