import floppy tape controller

This commit is contained in:
Andrew Moore 1994-02-07 04:36:09 +00:00
parent 97acce82ca
commit 6dac698341
8 changed files with 1676 additions and 0 deletions

4
sbin/ft/Makefile Normal file
View File

@ -0,0 +1,4 @@
PROG= ft
SRCS= ft.c ftecc.c
.include <bsd.prog.mk>

86
sbin/ft/ft.8 Normal file
View File

@ -0,0 +1,86 @@
.\" Copyright (c) 1980, 1989, 1991 The Regents of the University of California.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" @(#)ft.8
.\"
.Dd February 7, 1994
.Dt FT 8
.Os BSD 4
.Sh NAME
.Nm ft
.Nd QIC 40/80 floppy tape drive controller
.Sh SYNOPSIS
.Nm ft
.Op Fl f Ar tape
.Op Fl description
.Sh DESCRIPTION
The
.Nm ft
command allows multi-volume dump, extract, and view of tape labels, for
any pre-formatted QIC-40/80 tapes. It is totally system dependent,
and has nothing to do with the QIC standards.
.Pp
.Nm ft
is used primarily as a filter for tape i/o.
For example, to save and compress the /usr directory to tape:
.Bd -literal -offset indent
% tar cvzf - /usr | ft "/usr save"
.Ed
.Pp
To extract /usr from tape:
.Bd -literal -offset indent
% ft | tar xvzf -
.Ed
.Sh SEE ALSO
.Xr qtar 1
.Sh BUGS
Formatting/Verifying is in the works. You will need to use your
existing backup program to do this for the time being.
.Sh NOTES
The floppy tape driver supports tape drives such as the Colorado
Jumbo, Mountain Summit Express, some Archive/Conner models, and
probably many others. These tape drives attach between your floppy
disk controller card and your existing floppy disks' ribbon cable.
This driver does not currently support attachments via a proprietary
tape controller card or by the parallel port.
.Pp
QIC-40/80 drives are more CPU intensive than a SCSI drive. This is
really only a factor if your machine is networked or has multiple concurrent
users. For personal use (i.e. your typical home Unix user), response time
is perfectly acceptable. The tape drives cannot detect write errors.
Instead, they make up for it by using CRC's, error correction, and bad
spot mapping. Formatting time is extremely long because of this. The
drive makes a first pass over the entire tape writing out sectors. It
then makes a second pass at a slower rate than usual (for sensitivity)
to detect bad spots on the tape. Typically it takes an hour to format
a single QIC-80 (120Mb uncompressed) tape.
.Sh AUTHOR
Steve Gerakines <steve2@genesis.nred.ma.us>

432
sbin/ft/ft.c Normal file
View File

@ -0,0 +1,432 @@
/*
* Copyright (c) 1993 Steve Gerakines
*
* This is freely redistributable software. You may do anything you
* wish with it, so long as the above notice stays intact.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* ft.c - simple floppy tape filter
*
* 01/28/94 v0.3b (Jim Babb)
* Fixed bug when all sectors in a segment are marked bad.
*
* 10/30/93 v0.3
* Minor revisions. Seems pretty stable.
*
* 09/02/93 v0.2 pl01
* Initial revision.
*
* usage: ftfilt [ -f tape ] [ description ]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <sys/ftape.h>
#define DEFQIC "/dev/rft0"
char buff[QCV_SEGSIZE]; /* scratch buffer */
char hbuff[QCV_SEGSIZE]; /* header buffer */
QIC_Header *hptr = (QIC_Header *)hbuff; /* header structure */
int hsn = -1; /* segment number of header */
int dhsn = -1; /* segment number of duplicate header */
int tfd; /* tape file descriptor */
QIC_Geom geo; /* tape geometry */
int tvno = 1; /* tape volume number */
int tvlast; /* TRUE if last volume in set */
long tvsize = 0; /* tape volume size in bytes */
long tvtime = NULL; /* tape change time */
char *tvnote = ""; /* tape note */
/* Lookup the badmap for a given track and segment. */
#define BADMAP(t,s) hptr->qh_badmap[(t)*geo.g_segtrk+(s)]
/* Retrieve values from a character array. */
#define UL_VAL(s,p) (*((ULONG *)&(s)[p]))
#define US_VAL(s,p) (*((USHORT *)&(s)[p]))
#define equal(s1,s2) (strcmp(s1, s2) == 0)
/* Entry */
main(int argc, char *argv[])
{
int r, s;
char *tape, *getenv();
if (argc > 2 && (equal(argv[1], "-t") || equal(argv[1], "-f"))) {
argc -= 2;
tape = argv[2];
argv += 2;
} else
if ((tape = getenv("TAPE")) == NULL)
tape = DEFQIC;
if (argc > 1) {
tvnote = argv[1];
if (strlen(tvnote) > 18) argv[1][18] = '\0';
}
/* Open the tape device */
if ((tfd = open(tape, 2)) < 0) {
perror(tape);
exit(1);
}
if (!isatty(0))
do_write();
else if (!isatty(1))
do_read();
else
do_getname();
close(tfd);
exit(0);
}
/* Check status of tape drive */
int check_stat(int fd, int wr)
{
int r, s;
int sawit = 0;
/* get tape status */
if (ioctl(fd, QIOSTATUS, &s) < 0) {
fprintf(stderr, "could not get drive status\n");
return(1);
}
/* wait for the tape drive to become ready */
while ((s & QS_READY) == 0) {
if (!sawit) {
fprintf(stderr, "waiting for drive to become ready...\n");
sawit = 1;
}
sleep(2);
if (ioctl(fd, QIOSTATUS, &s) < 0) {
fprintf(stderr, "could not get drive status\n");
return(1);
}
}
if ((s & QS_FMTOK) == 0) {
fprintf(stderr, "tape is not formatted\n");
return(2);
}
if (wr && (s & QS_RDONLY) != 0) {
fprintf(stderr, "tape is write protected\n");
return(3);
}
return(0);
}
ULONG qtimeval(time_t t)
{
struct tm *tp;
ULONG r;
tp = localtime(&t);
r = 2678400 * tp->tm_mon +
86400 *(tp->tm_mday-1) +
3600 * tp->tm_hour +
60 * tp->tm_min +
tp->tm_sec;
r |= (tp->tm_year - 70) << 25;
return(r);
}
/* Return tm struct from QIC date format. */
struct tm *qtime(UCHAR *qt)
{
ULONG *vp = (ULONG *)qt;
struct tm t;
ULONG v;
time_t tv;
v = *vp;
t.tm_year = ((v >> 25) & 0x7f)+70; v &= 0x1ffffff;
t.tm_mon = v / 2678400; v %= 2678400;
t.tm_mday = v / 86400 + 1; v %= 86400;
t.tm_hour = v / 3600; v %= 3600;
t.tm_min = v / 60; v %= 60;
t.tm_sec = v;
t.tm_wday = 0; /* XXX - let mktime do the real work */
t.tm_yday = 0;
t.tm_isdst = 0;
t.tm_gmtoff = 0;
t.tm_zone = NULL;
tv = mktime(&t);
return(localtime(&tv));
}
/* Return a string, zero terminated */
char *qstr(char *str, int nchar)
{
static char tstr[256];
strncpy(tstr, str, nchar);
tstr[nchar] = '\0';
return(tstr);
}
/* Read header from tape */
get_header(int fd)
{
int r, sn, bytes;
QIC_Segment s;
int gothdr = 0;
if (ioctl(fd, QIOGEOM, &geo) < 0) {
fprintf(stderr, "couldn't determine tape geometry\n");
return(1);
}
/* Get the header and duplicate */
for (sn = 0; sn < 16; sn++) {
s.sg_trk = 0;
s.sg_seg = sn;
s.sg_badmap = 0;
s.sg_data = (UCHAR *)&buff[0];
ioctl(fd, QIOREAD, &s);
r = check_parity(s.sg_data, 0, s.sg_crcmap);
if (s.sg_data[0] == 0x55 && s.sg_data[1] == 0xaa &&
s.sg_data[2] == 0x55 && s.sg_data[3] == 0xaa) {
if (hsn >= 0) {
dhsn = sn;
if (!r && !gothdr) {
fprintf(stderr, "using secondary header\n");
bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
gothdr = 1;
}
break;
}
hsn = sn;
if (!r) {
bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
gothdr = 1;
} else {
fprintf(stderr, "too many errors in primary header\n");
}
}
}
if (!gothdr) {
fprintf(stderr, "couldn't read header segment\n");
ioctl(fd, QIOREWIND);
return(1);
}
return(0);
}
ask_vol(int vn)
{
FILE *inp;
int fd;
char c;
if ((fd = open("/dev/tty", 2)) < 0) {
fprintf(stderr, "argh!! can't open /dev/tty\n");
exit(1);
}
fprintf(stderr, "Insert ftfilt volume %02d and press enter:", vn);
read(fd, &c, 1);
close(fd);
}
/* Return the name of the tape only. */
do_getname()
{
if (check_stat(tfd, 0)) exit(1);
if (get_header(tfd)) exit(1);
fprintf(stderr, "\"%s\" - %s",
qstr(hptr->qh_tname,44), asctime(qtime(hptr->qh_chgdate)));
ioctl(tfd, QIOREWIND);
}
/* Extract data from tape to stdout */
do_read()
{
int sno, vno, sbytes, r;
long curpos;
char *hname;
QIC_Segment s;
/* Process multiple volumes if necessary */
vno = 1;
for (;;) {
if (check_stat(tfd, 0)) {
ask_vol(vno);
continue;
}
if (get_header(tfd)) {
ask_vol(vno);
continue;
}
/* extract volume and header info from label */
hname = hptr->qh_tname;
hname[43] = '\0';
tvno = atoi(&hname[11]);
tvlast = (hname[10] == '*') ? 1 : 0;
tvsize = atoi(&hname[14]);
tvnote = &hname[25];
if (vno != tvno || strncmp(hname, "ftfilt", 6) != 0) {
fprintf(stderr, "Incorrect volume inserted. This tape is:\n");
fprintf(stderr,"\"%s\" - %s\n", hname,
asctime(qtime(hptr->qh_chgdate)));
ask_vol(vno);
continue;
}
/* Process this volume */
curpos = 0;
for (sno = hptr->qh_first; tvsize > 0; sno++) {
s.sg_trk = sno / geo.g_segtrk;
s.sg_seg = sno % geo.g_segtrk;
s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg);
sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE;
s.sg_data = (UCHAR *)&buff[0];
/* skip segments with *all* sectors flagged as bad */
if (sbytes > 0) {
if (ioctl(tfd, QIOREAD, &s) < 0) perror("QIOREAD");
r = check_parity(s.sg_data, s.sg_badmap, s.sg_crcmap);
if (r) fprintf(stderr, "** warning: ecc failed at byte %ld\n",
curpos);
if (tvsize < sbytes) sbytes = tvsize;
write(1, s.sg_data, sbytes);
tvsize -= sbytes;
curpos += sbytes;
}
}
if (tvlast) break;
ioctl(tfd, QIOREWIND);
ask_vol(++vno);
}
ioctl(tfd, QIOREWIND);
return(0);
}
/* Dump data from stdin to tape */
do_write()
{
int sno, vno, amt, sbytes;
int c, maxseg, r;
ULONG qnow;
QIC_Segment s;
char tmpstr[80];
qnow = qtimeval(time(NULL));
vno = 1;
for (;;) {
if (check_stat(tfd, 1)) {
ask_vol(vno);
continue;
}
if (get_header(tfd)) {
ask_vol(vno);
continue;
}
maxseg = geo.g_segtrk * geo.g_trktape - 1;
sno = hptr->qh_first;
tvno = vno;
tvsize = 0;
tvlast = 0;
/* Process until end of volume or end of data */
for (sno = hptr->qh_first; sno < maxseg && tvlast == 0; ++sno) {
/* Prepare to load the next segment */
s.sg_trk = sno / geo.g_segtrk;
s.sg_seg = sno % geo.g_segtrk;
s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg);
sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE;
s.sg_data = (UCHAR *)&buff[0];
/* Ugh. Loop to get the full amt. */
for (amt = 0; amt < sbytes; amt += r) {
r = read(0, &s.sg_data[amt], sbytes - amt);
if (r <= 0) {
tvlast = 1;
break;
}
}
/* skip the segment if *all* sectors are flagged as bad */
if (amt) {
if (amt < sbytes)
bzero(&s.sg_data[amt], sbytes - amt);
r = set_parity(s.sg_data, s.sg_badmap);
if (r) fprintf(stderr, "** warning: ecc problem !!\n");
if (ioctl(tfd, QIOWRITE, &s) < 0) {
perror("QIOWRITE");
exit(1);
}
tvsize += amt;
}
}
/* Build new header info */
/* ftfilt vol*xx yyyyyyyyyy note56789012345678 */
/* 01234567890123456789012345678901234567890123 */
sprintf(tmpstr, "ftfilt vol%s%02d %010d %s",
(tvlast) ? "*" : " ", tvno, tvsize, tvnote);
strncpy(hptr->qh_tname, tmpstr, 44);
UL_VAL(hptr->qh_chgdate,0) = qnow;
/* Update the header for this volume */
if (hsn >= 0) {
s.sg_trk = hsn / geo.g_segtrk;
s.sg_seg = hsn % geo.g_segtrk;
s.sg_badmap = 0;
s.sg_data = (UCHAR *)hbuff;
r = set_parity(s.sg_data, s.sg_badmap);
if (r) fprintf(stderr, "** warning: header ecc problem !!\n");
if (ioctl(tfd, QIOWRITE, &s) < 0) {
perror("QIOWRITE");
exit(1);
}
}
if (dhsn >= 0) {
s.sg_trk = dhsn / geo.g_segtrk;
s.sg_seg = dhsn % geo.g_segtrk;
s.sg_badmap = 0;
s.sg_data = (UCHAR *)hbuff;
r = set_parity(s.sg_data, s.sg_badmap);
if (r) fprintf(stderr, "** warning: duphdr ecc problem !!\n");
if (ioctl(tfd, QIOWRITE, &s) < 0) {
perror("QIOWRITE");
exit(1);
}
}
ioctl(tfd, QIOREWIND);
if (tvlast) break;
ask_vol(++vno);
}
return(0);
}

316
sbin/ft/ftecc.c Normal file
View File

@ -0,0 +1,316 @@
/*
* ftecc.c 10/30/93 v0.3
* Handle error correction for floppy tape drives.
*
* File contents are copyrighted by David L. Brown and falls under the
* terms of the GPL version 2 or greater. See his original release for
* the specific terms.
*
* Steve Gerakines
* steve2@genesis.nred.ma.us
* Modified slightly to fit with my tape driver. I'm not at all happy
* with this module and will have it replaced with a more functional one
* in the next release(/RSN). I am close, but progress will continue to
* be slow until I can find a book on the subject where the translator
* understands both the to and from languages. :-( For now it will
* suffice.
*/
#include <sys/ftape.h>
/*
* In order to speed up the correction and adjustment, we can compute
* a matrix of coefficients for the multiplication.
*/
struct inv_mat {
UCHAR log_denom; /* The log z of the denominator. */
UCHAR zs[3][3]; /* The coefficients for the adjustment matrix. */
};
/* This array is a table of powers of x, from 0 to 254. */
static UCHAR alpha_power[] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4,
0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb,
0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd,
0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31,
0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67,
0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc,
0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b,
0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4,
0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26,
0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21,
0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba,
0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30,
0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0,
0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3,
0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a,
0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9,
0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44,
0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef,
0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85,
0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6,
0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf,
0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff,
0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58,
0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a,
0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24,
0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8,
0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64,
0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2,
0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda,
0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77,
0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3
};
/*
* This is the reverse lookup table. There is no log of 0, so the
* first element is not valid.
*/
static UCHAR alpha_log[] = {
0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a,
0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a,
0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1,
0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3,
0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83,
0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4,
0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35,
0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38,
0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70,
0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48,
0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24,
0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15,
0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f,
0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10,
0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7,
0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b,
0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08,
0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a,
0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91,
0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb,
0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2,
0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf,
0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52,
0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86,
0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc,
0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc,
0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8,
0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44,
0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1,
0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97,
0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5,
0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7
};
/* Return number of sectors available in a segment. */
int sect_count(ULONG badmap)
{
int i, amt;
for (amt = QCV_BLKSEG, i = 0; i < QCV_BLKSEG; i++)
if (badmap & (1 << i)) amt--;
return(amt);
}
/* Return number of bytes available in a segment. */
int sect_bytes(ULONG badmap)
{
int i, amt;
for (amt = QCV_SEGSIZE, i = 0; i < QCV_BLKSEG; i++)
if (badmap & (1 << i)) amt -= QCV_BLKSIZE;
return(amt);
}
/* Multiply two numbers in the field. */
static UCHAR multiply(UCHAR a, UCHAR b)
{
int tmp;
if (a == 0 || b == 0) return(0);
tmp = (alpha_log[a] + alpha_log[b]);
if (tmp > 254) tmp -= 255;
return (alpha_power[tmp]);
}
static UCHAR divide(UCHAR a, UCHAR b)
{
int tmp;
if (a == 0 || b == 0) return(0);
tmp = (alpha_log[a] - alpha_log[b]);
if (tmp < 0) tmp += 255;
return (alpha_power[tmp]);
}
/*
* This is just like divide, except we have already looked up the log
* of the second number.
*/
static UCHAR divide_out(UCHAR a, UCHAR b)
{
int tmp;
if (a == 0) return 0;
tmp = alpha_log[a] - b;
if (tmp < 0) tmp += 255;
return (alpha_power[tmp]);
}
/* This returns the value z^{a-b}. */
static UCHAR z_of_ab(UCHAR a, UCHAR b)
{
int tmp = (int)a - (int)b;
if (tmp < 0)
tmp += 255;
else if (tmp >= 255)
tmp -= 255;
return(alpha_power[tmp]);
}
/* Calculate the inverse matrix. Returns 1 if the matrix is valid, or
* zero if there is no inverse. The i's are the indices of the bytes
* to be corrected.
*/
static int calculate_inverse (int *pblk, struct inv_mat *inv)
{
/* First some variables to remember some of the results. */
UCHAR z20, z10, z21, z12, z01, z02;
UCHAR i0, i1, i2;
i0 = pblk[0]; i1 = pblk[1]; i2 = pblk[2];
z20 = z_of_ab (i2, i0); z10 = z_of_ab (i1, i0);
z21 = z_of_ab (i2, i1); z12 = z_of_ab (i1, i2);
z01 = z_of_ab (i0, i1); z02 = z_of_ab (i0, i2);
inv->log_denom = (z20 ^ z10 ^ z21 ^ z12 ^ z01 ^ z02);
if (inv->log_denom == 0) return 0;
inv->log_denom = alpha_log[inv->log_denom];
/* Calculate all of the coefficients on the top. */
inv->zs[0][0] = alpha_power[i1] ^ alpha_power[i2];
inv->zs[0][1] = z21 ^ z12;
inv->zs[0][2] = alpha_power[255-i1] ^ alpha_power[255-i2];
inv->zs[1][0] = alpha_power[i0] ^ alpha_power[i2];
inv->zs[1][1] = z20 ^ z02;
inv->zs[1][2] = alpha_power[255-i0] ^ alpha_power[255-i2];
inv->zs[2][0] = alpha_power[i0] ^ alpha_power[i1];
inv->zs[2][1] = z10 ^ z01;
inv->zs[2][2] = alpha_power[255-i0] ^ alpha_power[255-i1];
return(1);
}
/*
* Determine the error values for a given inverse matrix and syndromes.
*/
static void determine3(struct inv_mat *inv, UCHAR *es, UCHAR *ss)
{
UCHAR tmp;
int i, j;
for (i = 0; i < 3; i++) {
tmp = 0;
for (j = 0; j < 3; j++) tmp ^= multiply (ss[j], inv->zs[i][j]);
es[i] = divide_out(tmp, inv->log_denom);
}
}
/*
* Compute the 3 syndrome values. The data pointer should point to
* the offset within the first block of the column to calculate. The
* count of blocks is in blocks. The three bytes will be placed in
* ss[0], ss[1], and ss[2].
*/
static void compute_syndromes(UCHAR *data, int nblks, int col, UCHAR *ss)
{
int i;
UCHAR v;
ss[0] = 0; ss[1] = 0; ss[2] = 0;
for (i = (nblks-1)*QCV_BLKSIZE; i >= 0; i -= QCV_BLKSIZE) {
v = data[i+col];
if (ss[0] & 0x01) { ss[0] >>= 1; ss[0] ^= 0xc3; } else ss[0] >>= 1;
ss[0] ^= v;
ss[1] ^= v;
if (ss[2] & 0x80) { ss[2] <<= 1; ss[2] ^= 0x87; } else ss[2] <<= 1;
ss[2] ^= v;
}
}
/*
* Calculate the parity bytes for a segment. Returns 0 on success.
*/
int set_parity (UCHAR *data, ULONG badmap)
{
int col;
struct inv_mat inv;
UCHAR ss[3], es[3];
int nblks, pblk[3];
nblks = sect_count(badmap);
pblk[0] = nblks-3; pblk[1] = nblks-2; pblk[2] = nblks-1;
if (!calculate_inverse(pblk, &inv)) return(1);
pblk[0] *= QCV_BLKSIZE; pblk[1] *= QCV_BLKSIZE; pblk[2] *= QCV_BLKSIZE;
for (col = 0; col < QCV_BLKSIZE; col++) {
compute_syndromes (data, nblks-3, col, ss);
determine3(&inv, es, ss);
data[pblk[0]+col] = es[0];
data[pblk[1]+col] = es[1];
data[pblk[2]+col] = es[2];
}
return(0);
}
/*
* Check and correct errors in a block. Returns 0 on success,
* 1 if failed.
*/
int check_parity(UCHAR *data, ULONG badmap, ULONG crcmap)
{
int i, j, col, crcerrs, r, tries, nblks;
struct inv_mat inv;
UCHAR ss[3], es[3];
int i1, i2, eblk[3];
nblks = sect_count(badmap);
crcerrs = 0;
for (i = 0; crcerrs < 3 && i < nblks; i++)
if (crcmap & (1 << i)) eblk[crcerrs++] = i;
for (i = 1, j = crcerrs; j < 3 && i < nblks; i++)
if ((crcmap & (1 << i)) == 0) eblk[j++] = i;
if (!calculate_inverse (eblk, &inv)) return(1);
eblk[0] *= QCV_BLKSIZE; eblk[1] *= QCV_BLKSIZE; eblk[2] *= QCV_BLKSIZE;
r = 0;
for (col = 0; col < QCV_BLKSIZE; col++) {
compute_syndromes (data, nblks, col, ss);
if (!ss[0] && !ss[1] && !ss[2]) continue;
if (crcerrs) {
determine3 (&inv, es, ss);
for (j = 0; j < crcerrs; j++)
data[eblk[j] + col] ^= es[j];
compute_syndromes (data, nblks, col, ss);
if (!ss[0] && !ss[1] && !ss[2]) {
r = 1;
continue;
}
}
determine3 (&inv, es, ss);
i1 = alpha_log[divide(ss[2], ss[1])];
i2 = alpha_log[divide(ss[1], ss[0])];
if (i1 != i2 || ((QCV_BLKSIZE * i1) + col) > QCV_SEGSIZE)
r = 1;
else
data[QCV_BLKSIZE * i1 + col] ^= ss[1];
}
return(r);
}

4
sbin/i386/ft/Makefile Normal file
View File

@ -0,0 +1,4 @@
PROG= ft
SRCS= ft.c ftecc.c
.include <bsd.prog.mk>

86
sbin/i386/ft/ft.8 Normal file
View File

@ -0,0 +1,86 @@
.\" Copyright (c) 1980, 1989, 1991 The Regents of the University of California.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" @(#)ft.8
.\"
.Dd February 7, 1994
.Dt FT 8
.Os BSD 4
.Sh NAME
.Nm ft
.Nd QIC 40/80 floppy tape drive controller
.Sh SYNOPSIS
.Nm ft
.Op Fl f Ar tape
.Op Fl description
.Sh DESCRIPTION
The
.Nm ft
command allows multi-volume dump, extract, and view of tape labels, for
any pre-formatted QIC-40/80 tapes. It is totally system dependent,
and has nothing to do with the QIC standards.
.Pp
.Nm ft
is used primarily as a filter for tape i/o.
For example, to save and compress the /usr directory to tape:
.Bd -literal -offset indent
% tar cvzf - /usr | ft "/usr save"
.Ed
.Pp
To extract /usr from tape:
.Bd -literal -offset indent
% ft | tar xvzf -
.Ed
.Sh SEE ALSO
.Xr qtar 1
.Sh BUGS
Formatting/Verifying is in the works. You will need to use your
existing backup program to do this for the time being.
.Sh NOTES
The floppy tape driver supports tape drives such as the Colorado
Jumbo, Mountain Summit Express, some Archive/Conner models, and
probably many others. These tape drives attach between your floppy
disk controller card and your existing floppy disks' ribbon cable.
This driver does not currently support attachments via a proprietary
tape controller card or by the parallel port.
.Pp
QIC-40/80 drives are more CPU intensive than a SCSI drive. This is
really only a factor if your machine is networked or has multiple concurrent
users. For personal use (i.e. your typical home Unix user), response time
is perfectly acceptable. The tape drives cannot detect write errors.
Instead, they make up for it by using CRC's, error correction, and bad
spot mapping. Formatting time is extremely long because of this. The
drive makes a first pass over the entire tape writing out sectors. It
then makes a second pass at a slower rate than usual (for sensitivity)
to detect bad spots on the tape. Typically it takes an hour to format
a single QIC-80 (120Mb uncompressed) tape.
.Sh AUTHOR
Steve Gerakines <steve2@genesis.nred.ma.us>

432
sbin/i386/ft/ft.c Normal file
View File

@ -0,0 +1,432 @@
/*
* Copyright (c) 1993 Steve Gerakines
*
* This is freely redistributable software. You may do anything you
* wish with it, so long as the above notice stays intact.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* ft.c - simple floppy tape filter
*
* 01/28/94 v0.3b (Jim Babb)
* Fixed bug when all sectors in a segment are marked bad.
*
* 10/30/93 v0.3
* Minor revisions. Seems pretty stable.
*
* 09/02/93 v0.2 pl01
* Initial revision.
*
* usage: ftfilt [ -f tape ] [ description ]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <sys/ftape.h>
#define DEFQIC "/dev/rft0"
char buff[QCV_SEGSIZE]; /* scratch buffer */
char hbuff[QCV_SEGSIZE]; /* header buffer */
QIC_Header *hptr = (QIC_Header *)hbuff; /* header structure */
int hsn = -1; /* segment number of header */
int dhsn = -1; /* segment number of duplicate header */
int tfd; /* tape file descriptor */
QIC_Geom geo; /* tape geometry */
int tvno = 1; /* tape volume number */
int tvlast; /* TRUE if last volume in set */
long tvsize = 0; /* tape volume size in bytes */
long tvtime = NULL; /* tape change time */
char *tvnote = ""; /* tape note */
/* Lookup the badmap for a given track and segment. */
#define BADMAP(t,s) hptr->qh_badmap[(t)*geo.g_segtrk+(s)]
/* Retrieve values from a character array. */
#define UL_VAL(s,p) (*((ULONG *)&(s)[p]))
#define US_VAL(s,p) (*((USHORT *)&(s)[p]))
#define equal(s1,s2) (strcmp(s1, s2) == 0)
/* Entry */
main(int argc, char *argv[])
{
int r, s;
char *tape, *getenv();
if (argc > 2 && (equal(argv[1], "-t") || equal(argv[1], "-f"))) {
argc -= 2;
tape = argv[2];
argv += 2;
} else
if ((tape = getenv("TAPE")) == NULL)
tape = DEFQIC;
if (argc > 1) {
tvnote = argv[1];
if (strlen(tvnote) > 18) argv[1][18] = '\0';
}
/* Open the tape device */
if ((tfd = open(tape, 2)) < 0) {
perror(tape);
exit(1);
}
if (!isatty(0))
do_write();
else if (!isatty(1))
do_read();
else
do_getname();
close(tfd);
exit(0);
}
/* Check status of tape drive */
int check_stat(int fd, int wr)
{
int r, s;
int sawit = 0;
/* get tape status */
if (ioctl(fd, QIOSTATUS, &s) < 0) {
fprintf(stderr, "could not get drive status\n");
return(1);
}
/* wait for the tape drive to become ready */
while ((s & QS_READY) == 0) {
if (!sawit) {
fprintf(stderr, "waiting for drive to become ready...\n");
sawit = 1;
}
sleep(2);
if (ioctl(fd, QIOSTATUS, &s) < 0) {
fprintf(stderr, "could not get drive status\n");
return(1);
}
}
if ((s & QS_FMTOK) == 0) {
fprintf(stderr, "tape is not formatted\n");
return(2);
}
if (wr && (s & QS_RDONLY) != 0) {
fprintf(stderr, "tape is write protected\n");
return(3);
}
return(0);
}
ULONG qtimeval(time_t t)
{
struct tm *tp;
ULONG r;
tp = localtime(&t);
r = 2678400 * tp->tm_mon +
86400 *(tp->tm_mday-1) +
3600 * tp->tm_hour +
60 * tp->tm_min +
tp->tm_sec;
r |= (tp->tm_year - 70) << 25;
return(r);
}
/* Return tm struct from QIC date format. */
struct tm *qtime(UCHAR *qt)
{
ULONG *vp = (ULONG *)qt;
struct tm t;
ULONG v;
time_t tv;
v = *vp;
t.tm_year = ((v >> 25) & 0x7f)+70; v &= 0x1ffffff;
t.tm_mon = v / 2678400; v %= 2678400;
t.tm_mday = v / 86400 + 1; v %= 86400;
t.tm_hour = v / 3600; v %= 3600;
t.tm_min = v / 60; v %= 60;
t.tm_sec = v;
t.tm_wday = 0; /* XXX - let mktime do the real work */
t.tm_yday = 0;
t.tm_isdst = 0;
t.tm_gmtoff = 0;
t.tm_zone = NULL;
tv = mktime(&t);
return(localtime(&tv));
}
/* Return a string, zero terminated */
char *qstr(char *str, int nchar)
{
static char tstr[256];
strncpy(tstr, str, nchar);
tstr[nchar] = '\0';
return(tstr);
}
/* Read header from tape */
get_header(int fd)
{
int r, sn, bytes;
QIC_Segment s;
int gothdr = 0;
if (ioctl(fd, QIOGEOM, &geo) < 0) {
fprintf(stderr, "couldn't determine tape geometry\n");
return(1);
}
/* Get the header and duplicate */
for (sn = 0; sn < 16; sn++) {
s.sg_trk = 0;
s.sg_seg = sn;
s.sg_badmap = 0;
s.sg_data = (UCHAR *)&buff[0];
ioctl(fd, QIOREAD, &s);
r = check_parity(s.sg_data, 0, s.sg_crcmap);
if (s.sg_data[0] == 0x55 && s.sg_data[1] == 0xaa &&
s.sg_data[2] == 0x55 && s.sg_data[3] == 0xaa) {
if (hsn >= 0) {
dhsn = sn;
if (!r && !gothdr) {
fprintf(stderr, "using secondary header\n");
bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
gothdr = 1;
}
break;
}
hsn = sn;
if (!r) {
bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
gothdr = 1;
} else {
fprintf(stderr, "too many errors in primary header\n");
}
}
}
if (!gothdr) {
fprintf(stderr, "couldn't read header segment\n");
ioctl(fd, QIOREWIND);
return(1);
}
return(0);
}
ask_vol(int vn)
{
FILE *inp;
int fd;
char c;
if ((fd = open("/dev/tty", 2)) < 0) {
fprintf(stderr, "argh!! can't open /dev/tty\n");
exit(1);
}
fprintf(stderr, "Insert ftfilt volume %02d and press enter:", vn);
read(fd, &c, 1);
close(fd);
}
/* Return the name of the tape only. */
do_getname()
{
if (check_stat(tfd, 0)) exit(1);
if (get_header(tfd)) exit(1);
fprintf(stderr, "\"%s\" - %s",
qstr(hptr->qh_tname,44), asctime(qtime(hptr->qh_chgdate)));
ioctl(tfd, QIOREWIND);
}
/* Extract data from tape to stdout */
do_read()
{
int sno, vno, sbytes, r;
long curpos;
char *hname;
QIC_Segment s;
/* Process multiple volumes if necessary */
vno = 1;
for (;;) {
if (check_stat(tfd, 0)) {
ask_vol(vno);
continue;
}
if (get_header(tfd)) {
ask_vol(vno);
continue;
}
/* extract volume and header info from label */
hname = hptr->qh_tname;
hname[43] = '\0';
tvno = atoi(&hname[11]);
tvlast = (hname[10] == '*') ? 1 : 0;
tvsize = atoi(&hname[14]);
tvnote = &hname[25];
if (vno != tvno || strncmp(hname, "ftfilt", 6) != 0) {
fprintf(stderr, "Incorrect volume inserted. This tape is:\n");
fprintf(stderr,"\"%s\" - %s\n", hname,
asctime(qtime(hptr->qh_chgdate)));
ask_vol(vno);
continue;
}
/* Process this volume */
curpos = 0;
for (sno = hptr->qh_first; tvsize > 0; sno++) {
s.sg_trk = sno / geo.g_segtrk;
s.sg_seg = sno % geo.g_segtrk;
s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg);
sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE;
s.sg_data = (UCHAR *)&buff[0];
/* skip segments with *all* sectors flagged as bad */
if (sbytes > 0) {
if (ioctl(tfd, QIOREAD, &s) < 0) perror("QIOREAD");
r = check_parity(s.sg_data, s.sg_badmap, s.sg_crcmap);
if (r) fprintf(stderr, "** warning: ecc failed at byte %ld\n",
curpos);
if (tvsize < sbytes) sbytes = tvsize;
write(1, s.sg_data, sbytes);
tvsize -= sbytes;
curpos += sbytes;
}
}
if (tvlast) break;
ioctl(tfd, QIOREWIND);
ask_vol(++vno);
}
ioctl(tfd, QIOREWIND);
return(0);
}
/* Dump data from stdin to tape */
do_write()
{
int sno, vno, amt, sbytes;
int c, maxseg, r;
ULONG qnow;
QIC_Segment s;
char tmpstr[80];
qnow = qtimeval(time(NULL));
vno = 1;
for (;;) {
if (check_stat(tfd, 1)) {
ask_vol(vno);
continue;
}
if (get_header(tfd)) {
ask_vol(vno);
continue;
}
maxseg = geo.g_segtrk * geo.g_trktape - 1;
sno = hptr->qh_first;
tvno = vno;
tvsize = 0;
tvlast = 0;
/* Process until end of volume or end of data */
for (sno = hptr->qh_first; sno < maxseg && tvlast == 0; ++sno) {
/* Prepare to load the next segment */
s.sg_trk = sno / geo.g_segtrk;
s.sg_seg = sno % geo.g_segtrk;
s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg);
sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE;
s.sg_data = (UCHAR *)&buff[0];
/* Ugh. Loop to get the full amt. */
for (amt = 0; amt < sbytes; amt += r) {
r = read(0, &s.sg_data[amt], sbytes - amt);
if (r <= 0) {
tvlast = 1;
break;
}
}
/* skip the segment if *all* sectors are flagged as bad */
if (amt) {
if (amt < sbytes)
bzero(&s.sg_data[amt], sbytes - amt);
r = set_parity(s.sg_data, s.sg_badmap);
if (r) fprintf(stderr, "** warning: ecc problem !!\n");
if (ioctl(tfd, QIOWRITE, &s) < 0) {
perror("QIOWRITE");
exit(1);
}
tvsize += amt;
}
}
/* Build new header info */
/* ftfilt vol*xx yyyyyyyyyy note56789012345678 */
/* 01234567890123456789012345678901234567890123 */
sprintf(tmpstr, "ftfilt vol%s%02d %010d %s",
(tvlast) ? "*" : " ", tvno, tvsize, tvnote);
strncpy(hptr->qh_tname, tmpstr, 44);
UL_VAL(hptr->qh_chgdate,0) = qnow;
/* Update the header for this volume */
if (hsn >= 0) {
s.sg_trk = hsn / geo.g_segtrk;
s.sg_seg = hsn % geo.g_segtrk;
s.sg_badmap = 0;
s.sg_data = (UCHAR *)hbuff;
r = set_parity(s.sg_data, s.sg_badmap);
if (r) fprintf(stderr, "** warning: header ecc problem !!\n");
if (ioctl(tfd, QIOWRITE, &s) < 0) {
perror("QIOWRITE");
exit(1);
}
}
if (dhsn >= 0) {
s.sg_trk = dhsn / geo.g_segtrk;
s.sg_seg = dhsn % geo.g_segtrk;
s.sg_badmap = 0;
s.sg_data = (UCHAR *)hbuff;
r = set_parity(s.sg_data, s.sg_badmap);
if (r) fprintf(stderr, "** warning: duphdr ecc problem !!\n");
if (ioctl(tfd, QIOWRITE, &s) < 0) {
perror("QIOWRITE");
exit(1);
}
}
ioctl(tfd, QIOREWIND);
if (tvlast) break;
ask_vol(++vno);
}
return(0);
}

316
sbin/i386/ft/ftecc.c Normal file
View File

@ -0,0 +1,316 @@
/*
* ftecc.c 10/30/93 v0.3
* Handle error correction for floppy tape drives.
*
* File contents are copyrighted by David L. Brown and falls under the
* terms of the GPL version 2 or greater. See his original release for
* the specific terms.
*
* Steve Gerakines
* steve2@genesis.nred.ma.us
* Modified slightly to fit with my tape driver. I'm not at all happy
* with this module and will have it replaced with a more functional one
* in the next release(/RSN). I am close, but progress will continue to
* be slow until I can find a book on the subject where the translator
* understands both the to and from languages. :-( For now it will
* suffice.
*/
#include <sys/ftape.h>
/*
* In order to speed up the correction and adjustment, we can compute
* a matrix of coefficients for the multiplication.
*/
struct inv_mat {
UCHAR log_denom; /* The log z of the denominator. */
UCHAR zs[3][3]; /* The coefficients for the adjustment matrix. */
};
/* This array is a table of powers of x, from 0 to 254. */
static UCHAR alpha_power[] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4,
0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb,
0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd,
0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31,
0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67,
0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc,
0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b,
0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4,
0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26,
0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21,
0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba,
0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30,
0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0,
0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3,
0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a,
0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9,
0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44,
0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef,
0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85,
0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6,
0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf,
0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff,
0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58,
0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a,
0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24,
0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8,
0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64,
0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2,
0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda,
0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77,
0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3
};
/*
* This is the reverse lookup table. There is no log of 0, so the
* first element is not valid.
*/
static UCHAR alpha_log[] = {
0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a,
0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a,
0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1,
0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3,
0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83,
0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4,
0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35,
0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38,
0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70,
0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48,
0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24,
0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15,
0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f,
0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10,
0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7,
0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b,
0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08,
0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a,
0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91,
0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb,
0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2,
0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf,
0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52,
0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86,
0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc,
0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc,
0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8,
0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44,
0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1,
0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97,
0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5,
0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7
};
/* Return number of sectors available in a segment. */
int sect_count(ULONG badmap)
{
int i, amt;
for (amt = QCV_BLKSEG, i = 0; i < QCV_BLKSEG; i++)
if (badmap & (1 << i)) amt--;
return(amt);
}
/* Return number of bytes available in a segment. */
int sect_bytes(ULONG badmap)
{
int i, amt;
for (amt = QCV_SEGSIZE, i = 0; i < QCV_BLKSEG; i++)
if (badmap & (1 << i)) amt -= QCV_BLKSIZE;
return(amt);
}
/* Multiply two numbers in the field. */
static UCHAR multiply(UCHAR a, UCHAR b)
{
int tmp;
if (a == 0 || b == 0) return(0);
tmp = (alpha_log[a] + alpha_log[b]);
if (tmp > 254) tmp -= 255;
return (alpha_power[tmp]);
}
static UCHAR divide(UCHAR a, UCHAR b)
{
int tmp;
if (a == 0 || b == 0) return(0);
tmp = (alpha_log[a] - alpha_log[b]);
if (tmp < 0) tmp += 255;
return (alpha_power[tmp]);
}
/*
* This is just like divide, except we have already looked up the log
* of the second number.
*/
static UCHAR divide_out(UCHAR a, UCHAR b)
{
int tmp;
if (a == 0) return 0;
tmp = alpha_log[a] - b;
if (tmp < 0) tmp += 255;
return (alpha_power[tmp]);
}
/* This returns the value z^{a-b}. */
static UCHAR z_of_ab(UCHAR a, UCHAR b)
{
int tmp = (int)a - (int)b;
if (tmp < 0)
tmp += 255;
else if (tmp >= 255)
tmp -= 255;
return(alpha_power[tmp]);
}
/* Calculate the inverse matrix. Returns 1 if the matrix is valid, or
* zero if there is no inverse. The i's are the indices of the bytes
* to be corrected.
*/
static int calculate_inverse (int *pblk, struct inv_mat *inv)
{
/* First some variables to remember some of the results. */
UCHAR z20, z10, z21, z12, z01, z02;
UCHAR i0, i1, i2;
i0 = pblk[0]; i1 = pblk[1]; i2 = pblk[2];
z20 = z_of_ab (i2, i0); z10 = z_of_ab (i1, i0);
z21 = z_of_ab (i2, i1); z12 = z_of_ab (i1, i2);
z01 = z_of_ab (i0, i1); z02 = z_of_ab (i0, i2);
inv->log_denom = (z20 ^ z10 ^ z21 ^ z12 ^ z01 ^ z02);
if (inv->log_denom == 0) return 0;
inv->log_denom = alpha_log[inv->log_denom];
/* Calculate all of the coefficients on the top. */
inv->zs[0][0] = alpha_power[i1] ^ alpha_power[i2];
inv->zs[0][1] = z21 ^ z12;
inv->zs[0][2] = alpha_power[255-i1] ^ alpha_power[255-i2];
inv->zs[1][0] = alpha_power[i0] ^ alpha_power[i2];
inv->zs[1][1] = z20 ^ z02;
inv->zs[1][2] = alpha_power[255-i0] ^ alpha_power[255-i2];
inv->zs[2][0] = alpha_power[i0] ^ alpha_power[i1];
inv->zs[2][1] = z10 ^ z01;
inv->zs[2][2] = alpha_power[255-i0] ^ alpha_power[255-i1];
return(1);
}
/*
* Determine the error values for a given inverse matrix and syndromes.
*/
static void determine3(struct inv_mat *inv, UCHAR *es, UCHAR *ss)
{
UCHAR tmp;
int i, j;
for (i = 0; i < 3; i++) {
tmp = 0;
for (j = 0; j < 3; j++) tmp ^= multiply (ss[j], inv->zs[i][j]);
es[i] = divide_out(tmp, inv->log_denom);
}
}
/*
* Compute the 3 syndrome values. The data pointer should point to
* the offset within the first block of the column to calculate. The
* count of blocks is in blocks. The three bytes will be placed in
* ss[0], ss[1], and ss[2].
*/
static void compute_syndromes(UCHAR *data, int nblks, int col, UCHAR *ss)
{
int i;
UCHAR v;
ss[0] = 0; ss[1] = 0; ss[2] = 0;
for (i = (nblks-1)*QCV_BLKSIZE; i >= 0; i -= QCV_BLKSIZE) {
v = data[i+col];
if (ss[0] & 0x01) { ss[0] >>= 1; ss[0] ^= 0xc3; } else ss[0] >>= 1;
ss[0] ^= v;
ss[1] ^= v;
if (ss[2] & 0x80) { ss[2] <<= 1; ss[2] ^= 0x87; } else ss[2] <<= 1;
ss[2] ^= v;
}
}
/*
* Calculate the parity bytes for a segment. Returns 0 on success.
*/
int set_parity (UCHAR *data, ULONG badmap)
{
int col;
struct inv_mat inv;
UCHAR ss[3], es[3];
int nblks, pblk[3];
nblks = sect_count(badmap);
pblk[0] = nblks-3; pblk[1] = nblks-2; pblk[2] = nblks-1;
if (!calculate_inverse(pblk, &inv)) return(1);
pblk[0] *= QCV_BLKSIZE; pblk[1] *= QCV_BLKSIZE; pblk[2] *= QCV_BLKSIZE;
for (col = 0; col < QCV_BLKSIZE; col++) {
compute_syndromes (data, nblks-3, col, ss);
determine3(&inv, es, ss);
data[pblk[0]+col] = es[0];
data[pblk[1]+col] = es[1];
data[pblk[2]+col] = es[2];
}
return(0);
}
/*
* Check and correct errors in a block. Returns 0 on success,
* 1 if failed.
*/
int check_parity(UCHAR *data, ULONG badmap, ULONG crcmap)
{
int i, j, col, crcerrs, r, tries, nblks;
struct inv_mat inv;
UCHAR ss[3], es[3];
int i1, i2, eblk[3];
nblks = sect_count(badmap);
crcerrs = 0;
for (i = 0; crcerrs < 3 && i < nblks; i++)
if (crcmap & (1 << i)) eblk[crcerrs++] = i;
for (i = 1, j = crcerrs; j < 3 && i < nblks; i++)
if ((crcmap & (1 << i)) == 0) eblk[j++] = i;
if (!calculate_inverse (eblk, &inv)) return(1);
eblk[0] *= QCV_BLKSIZE; eblk[1] *= QCV_BLKSIZE; eblk[2] *= QCV_BLKSIZE;
r = 0;
for (col = 0; col < QCV_BLKSIZE; col++) {
compute_syndromes (data, nblks, col, ss);
if (!ss[0] && !ss[1] && !ss[2]) continue;
if (crcerrs) {
determine3 (&inv, es, ss);
for (j = 0; j < crcerrs; j++)
data[eblk[j] + col] ^= es[j];
compute_syndromes (data, nblks, col, ss);
if (!ss[0] && !ss[1] && !ss[2]) {
r = 1;
continue;
}
}
determine3 (&inv, es, ss);
i1 = alpha_log[divide(ss[2], ss[1])];
i2 = alpha_log[divide(ss[1], ss[0])];
if (i1 != i2 || ((QCV_BLKSIZE * i1) + col) > QCV_SEGSIZE)
r = 1;
else
data[QCV_BLKSIZE * i1 + col] ^= ss[1];
}
return(r);
}