*BSD News Article 21213


Return to BSD News archive

Path: sserve!newshost.anu.edu.au!munnari.oz.au!news.Hawaii.Edu!ames!agate!howland.reston.ans.net!noc.near.net!ceylon!genesis!steve2
From: steve2@genesis.nred.ma.us (Steve Gerakines)
Newsgroups: comp.os.386bsd.development
Subject: QIC-40/80 floppy tape driver pl01.shar part 01/01
Message-ID: <CDJB5r.9J6@genesis.nred.ma.us>
Date: 18 Sep 93 05:27:25 GMT
Organization: Genesis Public Access Unix +1 508 664 0149
Lines: 1440

Enclosed are patches to bring the floppy tape driver for 386BSD up
to patchlevel 01.  I found a number of bugs over the last couple of
days that were of high priority.

Also included in this package is a program called "ftfilt".  This
program will let you use [ insert your favorite archiver here ] to
read and write tapes.  I've been using it pretty reliably to back up
my system.

To extract this file, place this pl01.shar file in your directory
tree and type "sh pl01.shar".  Check out the README.pl01 file below
for more info.

Good luck!
- Steve
steve2@genesis.nred.ma.us

---- snip ---- snip ---- snip ----
# 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:
#
#	README.pl01
#	pl01.patch
#	tools/Makefile
#	tools/ftfilt.c
#	tools/wrtest.c
#
echo x - README.pl01
sed 's/^X//' >README.pl01 << 'END-of-README.pl01'
XQIC-40/80 floppy tape driver for 386BSD
XSteve Gerakines <steve2@genesis.nred.ma.us>
X09/14/93 v0.2 pl01
X
XWow, that was fast!  Welcome to ft patchlevel 01.
X
XEnclosed in this bundle you will find:
X
X	- A number of driver bug fixes/enhancements
X	- A couple of error correcting code bug fixes (aaarrgghh!)
X	- A makefile for the tools directory
X	- A new tool called "wrtest" that writes test patterns
X	- A new tool called "ftfilt" to allow you to do backups
X
X---------------------------------------------------------
X
XTo install the patches:
X
X1. Go to your dist 0.2 source tree (where the driver, tools, and qtar
X   directories are).
X
X2. Extract this shar file if you haven't done so already.
X
X3. Type the following commands in the sequence given:
X
X	% mv qtar/ftecc.c tools
X	% patch -p < pl01.patch
X
X4.  You should now be at patchlevel 01.  Re-install and compile the
X    driver as listed in the 0.2 distribution.
X
X5.  Re-boot your machine.
X
X6.  Go to the tools directory and type "make".  This will rebuild
X    all of the tools.
X
X7.  Done!
X
X---------------------------------------------------------
X
XUsing the new tools:
X
XTo use the "wrtest" tool, just type "wrtest".  All it does is write
Xout test patterns to segments 4 thru 100 and then read them back to
Xmake sure all is well.
X
XNext is "ftfilt".  This program will allow you to do a multi-volume
Xdump, extract, and view ftfilt tape labels of any pre-formatted tape.
XThis program is totally system dependent and has nothing to with any
XQIC standard.  I am providing it so that for the time being you will
Xbe able to use your favorite archiver, reliably back up to tape, and
Xtrade them with your 386BSD/BSD386/NetBSD/FreeBSD friends (or feuding
Xclans as the case may be :-)).
X
XThe command syntax looks like this:
X
X	usage: ftfilt [ "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 cvzf - /usr/bin | ftfilt "/usr/bin save"
X
XThis would save and compress my /usr/bin directory.
X
X% ftfilt
X
XThis would display the tape label:
X	"ftfilt vol*01 0002846720 /usr/bin save" - Wed Sep 15 02:42:29 1993
X	 aaaaaa    bcc dddddddddd eeeee...
X
X	a = ftfilt tape identifier
X	b = "*" if this is the last volume in a set, blank if not
X	c = 2 digit volume number
X	d = size of this volume in bytes
X	e = your provided description, blank if none
X
X% ftfilt | tar xvzf -
X
XThis would extract my /usr/bin directory from tape.
X
X---------------------------------------------------------
X
XA number of people have sent me mail regarding the driver, many
Xof which I replied and their messages bounced.  Here are a few
Xof the FAQ's:
X
X  - You have a different floppy driver than me.  Which one is it?
X
X	I am using the old (old! :-() original 0.1 386BSD floppy driver.
X
X  - Will you provide patches for xxxBSD Vx.xx?
X
X	I am going to be upgrading soon to at least pk0.2.4 soon, so I
X	will support the new floppy driver at that time.  The old
X	driver is still (currently) the least common denominator.  I
X	have avoided upgrading since my kernel is very customized.
X	Unfortunately, I cannot support all of the PC based BSD's
X	myself, so if I could get a few people to volunteer to beta test
X	each version I would appreciate it.  Once that happens, I will
X	distribute official patches for each version.
X
X	So far I have received (and so has the net) patches to get the
X	driver working with NetBSD 0.9, but I cannot confirm if these
X	work for everyone.
X
X  - You evil fool!  You removed read()/write() support!
X
X	Yes, I know. :-)  The 0.1 release and the 0.2 design had it.
X	After getting partway through the 0.2 implementation this
X	had to be changed for the following reasons:
X
X		- The I/O buffering code made the driver complex.
X		- The I/O buffering code made the driver slow and
X		  incapable of good streaming.
X		- Error correction requires a bitmap of marked bad
X		  sectors at I/O time.  This could be submitted all
X		  at once using a single ioctl() as with the mach
X		  driver.
X		- After a segment read occurs, a bitmap of CRC errors
X		  needs to be returned.  You could play all kinds of
X		  games to get this, but it would probably require
X		  an ioctl() anyhow.
X		- You cannot rely on getting a fixed amount of data
X		  in blocks, since tape segments are of variable size.
X
X	These are just some of the reasons.  If you still think it
X	can be done _in_a_clean_manner_, by all means go for it.
X
X  - Can we include/modify your driver for our release?
X
X	Certainly!  All I require is that you leave my copyright
X	notices intact so that I retain ownership.  Except for that
X	one tiny demand, you may redistribute the driver in any
X	manner you wish.
X
X---------------------------------------------------------
X
XPlease report your problems as well as successes to me at
Xsteve2@genesis.nred.ma.us.  Sending any debugging information along
Xwith your problem is extremely helpful.
X
XGood luck,
X- Steve
Xsteve2@genesis.nred.ma.us
END-of-README.pl01
echo x - pl01.patch
sed 's/^X//' >pl01.patch << 'END-of-pl01.patch'
X*** ../dist0.2/driver/ft.c	Wed Aug 25 01:32:28 1993
X--- driver/ft.c	Fri Sep 17 14:19:55 1993
X***************
X*** 16,22 ****
X   *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X   *  POSSIBILITY OF SUCH DAMAGE.
X   *
X!  *  ft.c - floppy tape driver
X   *  08/07/93 v0.2 release
X   *  Shifted from ftstrat to ioctl support for I/O.  Streaming is now much
X   *  more reliable.  Added internal support for error correction, QIC-40,
X--- 16,30 ----
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!  *  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***************
X*** 421,427 ****
X  	    case 1:
X  		if ((async_ret & async_arg[0]) != 0) goto complete;
X  		async_state = 0;
X! 		if (++async_retries == 10) {
X  			printf("ft0: acmd_state exceeded retry count\n");
X  			goto complete;
X  		}
X--- 429,435 ----
X  	    case 1:
X  		if ((async_ret & async_arg[0]) != 0) goto complete;
X  		async_state = 0;
X! 		if (++async_retries == 360) {	/* 90 secs. */
X  			printf("ft0: acmd_state exceeded retry count\n");
X  			goto complete;
X  		}
X***************
X*** 665,672 ****
X  async_req(int from)
X  {
X    SegReq *sp;
X!   static int over_async;
X!   static int endoftrk = 0;
X    int cmd;
X  
X    if (from == 2) arq_state = over_async;
X--- 673,679 ----
X  async_req(int from)
X  {
X    SegReq *sp;
X!   static int over_async, lastreq;
X    int cmd;
X  
X    if (from == 2) arq_state = over_async;
X***************
X*** 682,687 ****
X--- 689,696 ----
X  	if (ftio_sts != FTIO_READY) return;
X  
X  	/* Swap buffered and current segment */
X+ 	lastreq = curseg->reqtype;
X+ 	curseg->reqtype = FTIO_READY;
X  	sp = curseg;
X  	curseg = bufseg;
X  	bufseg = sp;
X***************
X*** 689,697 ****
X  	wakeup(&ftsem.buff_avail);
X  
X  	/* Detect end of track */
X! 	endoftrk = ((ftxblk / QCV_BLKSEG) % ftg->g_segtrk) == 0;
X! 
X! 	if (endoftrk) {
X  		ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0);
X  	}
X  	arq_state = 1;
X--- 698,704 ----
X  	wakeup(&ftsem.buff_avail);
X  
X  	/* Detect end of track */
X! 	if (((ftxblk / QCV_BLKSEG) % ftg->g_segtrk) == 0) {
X  		ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0);
X  	}
X  	arq_state = 1;
X***************
X*** 699,705 ****
X  
X       case 1:	/* Next request */
X  	if (curseg->reqtype != FTIO_READY) {
X- 		bufseg->reqtype = FTIO_READY;
X  		curseg->reqcrc = 0;
X  		arq_state = ard_state = awr_state = 0;
X  		ftxblk = curseg->reqblk;
X--- 706,711 ----
X***************
X*** 708,715 ****
X  		DPRT(("Processing I/O reqblk = %d\n", curseg->reqblk));
X  		goto restate;
X  	}
X! 	if (bufseg->reqtype == FTIO_READING && endoftrk == 0) {
X! 		bufseg->reqtype = FTIO_READY;
X  		curseg->reqtype = FTIO_RDAHEAD;
X  		curseg->reqblk = ftxblk;
X  		curseg->reqcrc = 0;
X--- 714,720 ----
X  		DPRT(("Processing I/O reqblk = %d\n", curseg->reqblk));
X  		goto restate;
X  	}
X! 	if (lastreq == FTIO_READING) {
X  		curseg->reqtype = FTIO_RDAHEAD;
X  		curseg->reqblk = ftxblk;
X  		curseg->reqcrc = 0;
X***************
X*** 723,730 ****
X  		goto restate;
X  	}
X  
X- 	bufseg->reqtype = FTIO_READY;
X- 
X  	if (ftmoving) {
X  		DPRT(("No more I/O.. Stopping.\n"));
X  		ACMD_FUNC(7, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
X--- 728,733 ----
X***************
X*** 962,968 ****
X  	    rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
X  	    i, ftxblk));
X  #endif
X- 	isa_dmadone(B_WRITE, ftxptr, QCV_BLKSIZE, 2);
X  
X  	/* Otherwise, transfer completed okay. */
X  	ftlastpos = ftxblk;
X--- 965,970 ----
X***************
X*** 1514,1525 ****
X  	tape_cmd((ft_type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE);
X  
X  	/* Restore floppy device handlers */
X  	s = splhigh();
X  	bcopy(&fdbdevsw, &bdevsw[bi], sizeof(struct bdevsw));
X  	bcopy(&fdcdevsw, &cdevsw[ci], sizeof(struct cdevsw));
X  	splx(s);
X  	ftnewcart = 0;			/* clear new cartridge */
X- 	tape_end();
X  	havebufs = 0;
X  	free(curseg, M_DEVBUF);
X  	free(bufseg, M_DEVBUF);
X--- 1516,1527 ----
X  	tape_cmd((ft_type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE);
X  
X  	/* Restore floppy device handlers */
X+ 	tape_end();
X  	s = splhigh();
X  	bcopy(&fdbdevsw, &bdevsw[bi], sizeof(struct bdevsw));
X  	bcopy(&fdcdevsw, &cdevsw[ci], sizeof(struct cdevsw));
X  	splx(s);
X  	ftnewcart = 0;			/* clear new cartridge */
X  	havebufs = 0;
X  	free(curseg, M_DEVBUF);
X  	free(bufseg, M_DEVBUF);
X***************
X*** 1652,1670 ****
X  	sr->sg_seg >= ftg->g_segtrk) return(EINVAL);
X    blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG;
X  
X    if (cmd == QIOREAD) {
X- 	s = splbio();
X  	if (curseg->reqtype == FTIO_RDAHEAD) {
X  		if (blk == curseg->reqblk) {
X  			sp = curseg;
X  			sp->reqtype = FTIO_READING;
X  			sp->reqbad = sr->sg_badmap;
X- 			splx(s);
X  			goto rdwait;
X  		} else
X  			curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	}
X- 	splx(s);
X  
X  	/* Wait until we're ready. */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X--- 1654,1670 ----
X  	sr->sg_seg >= ftg->g_segtrk) return(EINVAL);
X    blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG;
X  
X+   s = splbio();
X    if (cmd == QIOREAD) {
X  	if (curseg->reqtype == FTIO_RDAHEAD) {
X  		if (blk == curseg->reqblk) {
X  			sp = curseg;
X  			sp->reqtype = FTIO_READING;
X  			sp->reqbad = sr->sg_badmap;
X  			goto rdwait;
X  		} else
X  			curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	}
X  
X  	/* Wait until we're ready. */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X***************
X*** 1706,1714 ****
X  
X  	/* Sleep until a buffer becomes available. */
X  	while (bufseg->reqtype != FTIO_READY) sleep(&ftsem.buff_avail, FTPRI);
X- 	s = splbio();
X  	sp = (curseg->reqtype == FTIO_READY) ? curseg : bufseg;
X- 	splx(s);
X  
X  	/* Copy in segment and expand bad blocks. */
X  	bad = sr->sg_badmap;
X--- 1706,1712 ----
X***************
X*** 1721,1727 ****
X  
X  	sp->reqblk = blk;
X  	sp->reqcan = 0;
X- 	s = splbio();
X  	sp->reqtype = FTIO_WRITING;
X  
X  	if (!ftactive) {
X--- 1719,1724 ----
X***************
X*** 1733,1740 ****
X  		ftactive = 1;
X  		timeout(ft_timeout, 0, 1);
X  	}
X- 	splx(s);
X    }
X    return(0);
X  }
X  
X--- 1730,1737 ----
X  		ftactive = 1;
X  		timeout(ft_timeout, 0, 1);
X  	}
X    }
X+   splx(s);
X    return(0);
X  }
X  
X***************
X*** 1746,1752 ****
X  	curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X    }
X!   if (ftactive) return(ENXIO);
X  
X    tape_cmd(QC_STOP);
X    tape_state(0, QS_READY, 90);
X--- 1743,1749 ----
X  	curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X    }
X!   while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X  
X    tape_cmd(QC_STOP);
X    tape_state(0, QS_READY, 90);
X***************
X*** 1769,1775 ****
X  	curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X    }
X!   if (ftactive) return(ENXIO);
X  
X    tape_cmd(QC_STOP);
X    tape_state(0, QS_READY, 90);
X--- 1766,1772 ----
X  	curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X    }
X!   while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X  
X    tape_cmd(QC_STOP);
X    tape_state(0, QS_READY, 90);
X***************
X*** 1801,1807 ****
X  	curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X    }
X!   if (ftactive) return(ENXIO);
X  
X    tape_cmd(QC_STOP);
X    tape_state(0, QS_READY, 90);
X--- 1798,1804 ----
X  	curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X    }
X!   while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X  
X    tape_cmd(QC_STOP);
X    tape_state(0, QS_READY, 90);
X***************
X*** 1822,1828 ****
X  /* Start tape moving forward. */
X  int ftreq_lfwd()
X  {
X!   if (ftactive) return(ENXIO);
X    tape_cmd(QC_STOP);
X    tape_state(0, QS_READY, 90);
X    tape_cmd(QC_FORWARD);
X--- 1819,1826 ----
X  /* Start tape moving forward. */
X  int ftreq_lfwd()
X  {
X!   while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X! 
X    tape_cmd(QC_STOP);
X    tape_state(0, QS_READY, 90);
X    tape_cmd(QC_FORWARD);
X***************
X*** 1832,1838 ****
X  /* Stop the tape */
X  int ftreq_stop()
X  {
X!   if (ftactive) return(ENXIO);
X    tape_cmd(QC_STOP);
X    tape_state(0, QS_READY, 90);
X    return(0);
X--- 1830,1837 ----
X  /* Stop the tape */
X  int ftreq_stop()
X  {
X!   while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X! 
X    tape_cmd(QC_STOP);
X    tape_state(0, QS_READY, 90);
X    return(0);
X***************
X*** 1847,1853 ****
X  	curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X    }
X!   if (ftactive) return(ENXIO);
X    r = tape_status();
X  
X    switch(cmd) {
X--- 1846,1853 ----
X  	curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X    }
X!   while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X! 
X    r = tape_status();
X  
X    switch(cmd) {
X***************
X*** 1873,1879 ****
X  int ftreq_status(int cmd, int *sts, struct proc *p)
X  {
X    if (ftactive)
X! 	*sts = ftlaststs;
X    else
X  	*sts = tape_status();
X    return(0);
X--- 1873,1879 ----
X  int ftreq_status(int cmd, int *sts, struct proc *p)
X  {
X    if (ftactive)
X! 	*sts = ftlaststs & ~QS_READY;
X    else
X  	*sts = tape_status();
X    return(0);
X***************
X*** 1902,1908 ****
X  	curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X    }
X!   if (ftactive) return(ENXIO);
X    if (ftg == NULL && ftgetgeom() < 0) return(ENXIO);
X    bcopy(ftg, g, sizeof(QIC_Geom));
X    return(0);
X--- 1902,1909 ----
X  	curseg->reqcan = 1;	/* XXX cancel rdahead */
X  	while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X    }
X!   while (ftactive) sleep(&ftsem.iosts_change, FTPRI);
X! 
X    if (ftg == NULL && ftgetgeom() < 0) return(ENXIO);
X    bcopy(ftg, g, sizeof(QIC_Geom));
X    return(0);
X*** ../dist0.2/tools/ftecc.c	Fri Sep 17 14:36:14 1993
X--- tools/ftecc.c	Fri Sep 17 14:37:37 1993
X***************
X*** 1,5 ****
X  /*
X!  *  ftecc.c  07/22/93
X   *  Handle error correction for floppy tape drives.
X   *
X   *  File contents are copyrighted by David L. Brown and falls under the
X--- 1,5 ----
X  /*
X!  *  ftecc.c  09/14/93 v0.2 pl01
X   *  Handle error correction for floppy tape drives.
X   *
X   *  File contents are copyrighted by David L. Brown and falls under the
X***************
X*** 16,22 ****
X   *  suffice.
X   */
X  #include <sys/ftape.h>
X- #include "qtar.h"
X  
X  /*
X   *  In order to speed up the correction and adjustment, we can compute
X--- 16,21 ----
X***************
X*** 102,107 ****
X--- 101,126 ----
X    0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7
X  };
X  
X+ /* Return number of sectors available in a segment. */
X+ int sect_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+ /* Return number of bytes available in a segment. */
X+ int sect_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  /* Multiply two numbers in the field. */
X  static UCHAR multiply(UCHAR a, UCHAR b)
X  {
X***************
X*** 153,159 ****
X   *  zero if there is no inverse.  The i's are the indices of the bytes
X   *  to be corrected.
X   */
X! static int calculate_inverse (UCHAR *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--- 172,178 ----
X   *  zero if there is no inverse.  The i's are the indices of the bytes
X   *  to be corrected.
X   */
X! static int calculate_inverse (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***************
X*** 228,242 ****
X  {
X    int col;
X    struct inv_mat inv;
X!   UCHAR ss[3], es[3], pblk[3];
X!   int nblks;
X  
X    nblks = sect_count(badmap);
X    pblk[0] = nblks-3; pblk[1] = nblks-2; pblk[2] = nblks-1;
X    if (!calculate_inverse(pblk, &inv)) return(1);
X    pblk[0] *= QCV_BLKSIZE; pblk[1] *= QCV_BLKSIZE; pblk[2] *= QCV_BLKSIZE;
X    for (col = 0; col < QCV_BLKSIZE; col++) {
X! 	compute_syndromes (data, nblks, col, ss);
X  	determine3(&inv, es, ss);
X  	data[pblk[0]+col] = es[0];
X  	data[pblk[1]+col] = es[1];
X--- 247,262 ----
X  {
X    int col;
X    struct inv_mat inv;
X!   UCHAR ss[3], es[3];
X!   int nblks, pblk[3];
X  
X    nblks = sect_count(badmap);
X    pblk[0] = nblks-3; pblk[1] = nblks-2; pblk[2] = nblks-1;
X    if (!calculate_inverse(pblk, &inv)) return(1);
X+ 
X    pblk[0] *= QCV_BLKSIZE; pblk[1] *= QCV_BLKSIZE; pblk[2] *= QCV_BLKSIZE;
X    for (col = 0; col < QCV_BLKSIZE; col++) {
X! 	compute_syndromes (data, nblks-3, col, ss);
X  	determine3(&inv, es, ss);
X  	data[pblk[0]+col] = es[0];
X  	data[pblk[1]+col] = es[1];
X***************
X*** 254,260 ****
X  {
X    int i, j, col, crcerrs, r, tries, nblks;
X    struct inv_mat inv;
X!   UCHAR i1, i2, ss[3], es[3], eblk[3];
X  
X    nblks = sect_count(badmap);
X    crcerrs = 0;
X--- 274,281 ----
X  {
X    int i, j, col, crcerrs, r, tries, nblks;
X    struct inv_mat inv;
X!   UCHAR ss[3], es[3];
X!   int i1, i2, eblk[3];
X  
X    nblks = sect_count(badmap);
X    crcerrs = 0;
X*** ../dist0.2/qtar/Makefile	Wed Aug 25 01:33:02 1993
X--- qtar/Makefile	Sat Sep 18 00:03:20 1993
X***************
X*** 17,22 ****
X--- 17,25 ----
X  #  POSSIBILITY OF SUCH DAMAGE.
X  #
X  #  Makefile - QIC tape archiver makefile
X+ #  09/14/93 v0.2 pl01
X+ #  Moved ftecc stuff to the tools directory.
X+ #
X  #  07/22/93 v0.1
X  #  Initial revision.
X  #
X***************
X*** 29,35 ****
X--- 32,42 ----
X  qtar:	$(OBJ)
X  	$(CC) -o qtar $(OBJ)
X  
X+ ftecc.o: ../tools/ftecc.c
X+ 	$(CC) $(CFLAGS) -c ../tools/ftecc.c
X+ 
X  clean:
X  	rm -f qtar $(OBJ) core core.*
X+ 
X  
X  $(OBJ): qtar.h
X*** ../dist0.2/qtar/func.c	Wed Aug 25 01:33:02 1993
X--- qtar/func.c	Fri Sep 17 14:41:30 1993
X***************
X*** 17,22 ****
X--- 17,25 ----
X   *  POSSIBILITY OF SUCH DAMAGE.
X   *
X   *  func.c - QIC tape archiver misc support functions
X+  *  09/14/93 v0.2 pl01
X+  *  Moved sect_* functions to ecc code.  Corrected time functions.
X+  *
X   *  07/22/93 v0.1
X   *  Initial revision.
X   */
X***************
X*** 51,95 ****
X    return(0);
X  }
X  
X- /* Return number of sectors available in a segment. */
X- int sect_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- /* Return number of bytes available in a segment. */
X- int sect_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  /* Return tm struct from QIC date format. */
X  struct tm *qtime(UCHAR *qt)
X  {
X    ULONG *vp = (ULONG *)qt;
X!   static struct tm t;
X    ULONG v;
X  
X    v = *vp;
X!   t.tm_year = ((v >> 24) & 0xff)+47; v &= 0xffffff;
X!   t.tm_sec = v % 60; v /= 60;
X!   t.tm_min = v % 60; v /= 60;
X!   t.tm_hour = v % 24; v /= 24;
X!   t.tm_mday = (v % 31)+1; v /= 31;
X!   t.tm_mon = v % 12; v /= 12;
X!   t.tm_wday = 0;
X    t.tm_yday = 0;
X    t.tm_isdst = 0;
X    t.tm_gmtoff = 0;
X    t.tm_zone = NULL;
X!   return(&t);
X  }
X  
X--- 54,96 ----
X    return(0);
X  }
X  
X  
X  /* Return tm struct from QIC date format. */
X  struct tm *qtime(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+ /* Given a time_t value, build a QIC time value. */
X+ ULONG qtimeval(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*** ../dist0.2/qtar/qtar.h	Wed Aug 25 01:33:02 1993
X--- qtar/qtar.h	Fri Sep 17 14:41:43 1993
X***************
X*** 17,22 ****
X--- 17,25 ----
X   *  POSSIBILITY OF SUCH DAMAGE.
X   *
X   *  qtar.h - QIC tape archiver header file
X+  *  09/14/93 v0.2 pl01
X+  *  Moved ecc stuff around.  Added qtimeval() function.
X+  *
X   *  07/22/93 v0.1
X   *  Initial revision.
X   */
X***************
X*** 80,91 ****
X  /* func.c */
X  extern int check_stat();
X  extern struct tm *qtime();
X! extern int sect_count();
X! extern int sect_bytes();
X  
X  /* ftecc.c */
X  extern int set_parity();
X  extern int check_parity();
X  
X  /* volume.c */
X  extern int vol_init();
X--- 83,95 ----
X  /* func.c */
X  extern int check_stat();
X  extern struct tm *qtime();
X! extern ULONG qtimeval();
X  
X  /* ftecc.c */
X  extern int set_parity();
X  extern int check_parity();
X+ extern int sect_count();
X+ extern int sect_bytes();
X  
X  /* volume.c */
X  extern int vol_init();
END-of-pl01.patch
echo x - tools/Makefile
sed 's/^X//' >tools/Makefile << 'END-of-tools/Makefile'
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#  Makefile - tools makefile
X#  09/14/93 v0.2 pl01
X#  Initial revision.
X#
X
XCC = cc
XCFLAGS = -O
XTOOLS = ftfilt wrtest getseg postest
X
Xall:	$(TOOLS)
X
Xftfilt: ftfilt.o ftecc.o
X	cc -o ftfilt ftfilt.o ftecc.o
X
Xwrtest: wrtest.o ftecc.o
X	cc -o wrtest wrtest.o ftecc.o
X
Xgetseg: getseg.o
X	cc -o getseg getseg.o
X
Xpostest: postest.o
X	cc -o postest postest.o
X
Xclean:
X	rm -f *.o $(TOOLS)
END-of-tools/Makefile
echo x - tools/ftfilt.c
sed 's/^X//' >tools/ftfilt.c << 'END-of-tools/ftfilt.c'
X/*
X *  Copyright (c) 1993 Steve Gerakines
X *
X *  This is freely redistributable software.  You may do anything you
X *  wish with it, so long as the above notice stays intact.
X *
X *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
X *  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
X *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
X *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X *  POSSIBILITY OF SUCH DAMAGE.
X *
X *  ftfilt.c - simple floppy tape filter
X *  09/02/93
X *  Initial revision.
X *
X *  usage: ftfilt ["note18"]
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
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 */
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
X/* Check status of tape drive */
Xint check_stat(int fd, int wr)
X{
X  int r, s;
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	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
XULONG qtimeval(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/* Return tm struct from QIC date format. */
Xstruct tm *qtime(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/* Return a string, zero terminated */
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/* Read header from tape */
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
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/* Return the name of the tape only. */
Xdo_getname()
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  ioctl(tfd, QIOREWIND);
X}
X
X
X/* Extract data from tape to stdout */
Xdo_read()
X{
X  int sno, vno, sbytes, r;
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	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	sno = hptr->qh_first;
X	curpos = 0;
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 (ioctl(tfd, QIOREAD, &s) < 0) perror("QIOREAD");
X		r = check_parity(s.sg_data, s.sg_badmap, s.sg_crcmap);
X		if (r) fprintf(stderr, "** warning: ecc failed at byte %ld\n",
X								 curpos);
X		if (tvsize < sbytes) sbytes = tvsize;
X		write(1, s.sg_data, sbytes);
X		tvsize -= sbytes;
X		curpos += sbytes;
X		sno++;
X	}
X	if (tvlast) break;
X	ioctl(tfd, QIOREWIND);
X	ask_vol(++vno);
X  }
X  ioctl(tfd, QIOREWIND);
X  return(0);
X}
X
X
X/* Dump data from stdin to tape */
Xdo_write()
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	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	while (sno < maxseg && tvlast == 0) {
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		if (amt < sbytes) 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) perror("QIOWRITE");
X		tvsize += amt;
X		sno++;
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) perror("QIOWRITE header");
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) perror("QIOWRITE duphdr");
X	}
X	ioctl(tfd, QIOREWIND);
X	if (tvlast) break;
X	ask_vol(++vno);
X  }
X  return(0);
X}
X
X
X/* Entry */
Xmain(int argc, char *argv[])
X{
X  int r, s;
X
X  if (argc > 1) {
X	tvnote = argv[1];
X	if (strlen(tvnote) > 18) argv[1][18] = '\0';
X  }
X
X  /* Open the tape device */
X  if ((tfd = open("/dev/ft0a", 2)) < 0) {
X	perror("/dev/ft0a");
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-tools/ftfilt.c
echo x - tools/wrtest.c
sed 's/^X//' >tools/wrtest.c << 'END-of-tools/wrtest.c'
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
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 */
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/* Read header from tape */
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;
X	ioctl(fd, QIOREAD, &s);
Xfprintf(stderr, "%5d: %08lx\n", sn, s.sg_crcmap);
X	r = check_parity(s.sg_data, 0, s.sg_crcmap);
X
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) {
Xfprintf(stderr, "duplicate at %d\n", sn);
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;
Xfprintf(stderr, "primary at %d\n", 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
Xmain(int argc, char *argv[])
X{
X  int i, j, k, r, sbytes;
X  QIC_Segment s;
X
X  if ((tfd = open("/dev/ft0a", 2)) < 0) {
X	perror("/dev/ft0a");
X	exit(1);
X  }
X
X  /* Get the tape bad mapping */
X  get_header(tfd);
X
X  /* write test pattern */
X  for (i = 4; i < 100; i++) {
Xfprintf(stderr, "writing segment %d test pattern\n", i);
X	s.sg_trk = i / geo.g_segtrk;
X	s.sg_seg = i % 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;
X	for (j = 0; j < sbytes; j++)
X		buff[j] = ((i-4) % 26) + 'a';
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) perror("QIOWRITE");
X  }
X
X  /* read back test patterns */
X  for (i = 4; i < 100; i++) {
Xfprintf(stderr, "reading segment %d test pattern\n", i);
X	s.sg_trk = i / geo.g_segtrk;
X	s.sg_seg = i % 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;
X	if (ioctl(tfd, QIOREAD, &s) < 0) perror("QIOREAD");
X	r = check_parity(s.sg_data, s.sg_badmap, s.sg_crcmap);
X	if (r) fprintf(stderr, "!!! ecc failed !!!\n");
X	for (j = 0; j < sbytes; j++)
X		if (buff[j] != (((i-4) % 26) + 'a')) break;
X	if (j != sbytes) fprintf(stderr, "***** PATTERNS DON'T MATCH *****\n");
X  }
X
X  close(tfd);
X  exit(0);
X}
END-of-tools/wrtest.c
exit