Xinu7/src/lib/lib58/tu58.c
/* LINTLIBRARY */
/*
* This package was written by Matt Bishop of the CS Department at Purdue
* University, in December 1981, for Doug Comer's 690D seminar.
*
* this sets up useful goodies. stdio.h is NOT used, but some ioctl calls
* need NULL. Hence NULL is defined here for the vax; for anything else
* you can include stdio.h.
*/
#ifndef NULL
#define NULL 0
#endif
#include <sgtty.h>
#include <errno.h>
#include <tu58.h>
/*
* this rounds x up to the nearest y; useful for computing number of blocks
* written out
*/
#define nblock(x,y) ((x) + (y) - 1) / (y)
/*
* globals. tape58 is the structure for the TU58; abytes is the number of
* bytes affected by the last io operation, as returned by the end packet.
* errno is the standard error number for UNIX errors, and terrno is the
* corresponding error number for the tape routines.
*/
static struct tu58 tape58; /* TU58 control structure */
static int abytes; /* number of bytes handled last */
extern int errno; /* UNIX error indicator */
int terrno; /* global TU58 errors */
/*
* topen(drive, mode, name)
* char *drive; first char is number of drive being opened
* int mode; 0 for reading, 1 for writing, 2 for both
* char *name; name of device to be opened
*
* note the device "name" (defaulting to the standard TU58FILE) is opened
* for reading and writing regardless of the mode; the mode is used with
* the drives. you need to go both ways to send, and recieve, information
* from the TU58.
* also note the existence of the lock file; to dump it, you must call
* tclose.
*
* RETURNS: -1 on error; terrno indicates what happened
* drive number on success
*/
int topen(drive, mode, name)
char *drive; /* drive being opened */
int mode; /* mode (reading or writing or both) */
char *name; /* Alternate device name */
{
int x; /* temp for initialization error */
int td; /* temp file descriptor */
int dn; /* drive number */
struct dr58 *ds; /* pointer to drive structure */
struct sgttyb tsbuf; /* temp for sgttyb block */
struct tchars tcbuf; /* temp for tchars block */
/* check drive number */
if (*drive != '0' && *drive != '1'){
terrno = TEBUNN;
return(SYSERR);
}
/* check mode number */
if (mode < 0 || mode > 2){
terrno = EINVAL - TEUNIX;
return(SYSERR);
}
/* see if any drive is open */
ds = &(tape58 . t_dr[dn = *drive - '0']);
if (tape58 . t_open > 0){
/* yes - check drive and if closed, open it */
if (ds -> d_flags & OPEN){
terrno = TEREOP;
return(dn);
}
ds -> d_flags = OPEN | (mode ? WRITE : 0) | ((mode & 01) ? 0 : READ);
ds -> d_posit = 0;
ds -> d_mods = (char) VFY;
ds -> d_swit = '\0';
return(dn);
}
/* no drive is open - open the file and set its modes */
if (creat(TU58LOCK, 0600) < 0){
terrno = TETULU;
return(SYSERR);
}
if (name == NULL)
name = TU58FILE;
if ((tape58 . t_fd = td = open(name, 2)) < 0){
terrno = errno - TEUNIX;
(void) unlink(TU58LOCK);
return(SYSERR);
}
ds -> d_flags = OPEN | (mode ? WRITE : 0) | ((mode & 01) ? 0 : READ);
ds -> d_posit = 0;
ds -> d_mods = (char) VFY;
ds -> d_swit = '\0';
/* reset start, stop chars */
if (ioctl(td, TIOCGETC, &tcbuf) < 0){
terrno = errno - TEUNIX;
(void) close(td);
(void) unlink(TU58LOCK);
return(SYSERR);
}
tape58 . t_start = tcbuf . t_startc;
tape58 . t_stop = tcbuf . t_stopc;
tcbuf . t_startc = BCONT;
tcbuf . t_stopc = BXOFF;
if (ioctl(td, TIOCSETC, &tcbuf) < 0){
terrno = errno - TEUNIX;
(void) close(td);
(void) unlink(TU58LOCK);
return(SYSERR);
}
/* reset mode to raw and tandem */
if (ioctl(td, TIOCGETP, &tsbuf) < 0){
terrno = errno - TEUNIX;
tcbuf . t_startc = tape58 . t_start;
tcbuf . t_stopc = tape58 . t_stop;
(void) ioctl(td, TIOCSETC, &tcbuf);
(void) close(td);
(void) unlink(TU58LOCK);
return(SYSERR);
}
tape58 . t_sflag = tsbuf . sg_flags;
tsbuf . sg_flags |= RAW | TANDEM;
if (ioctl(td, TIOCSETP, &tsbuf) < 0){
terrno = errno - TEUNIX;
tcbuf . t_startc = tape58 . t_start;
tcbuf . t_stopc = tape58 . t_stop;
(void) ioctl(td, TIOCSETC, &tcbuf);
(void) close(td);
(void) unlink(TU58LOCK);
return(SYSERR);
}
/* now call the reset routine; if successful, ready! */
if ((x = treset(td)) != TENONE){
terrno = x;
tcbuf . t_startc = tape58 . t_start;
tcbuf . t_stopc = tape58 . t_stop;
(void) ioctl(td, TIOCSETC, &tcbuf);
tsbuf . sg_flags = tape58 . t_sflag;
(void) ioctl(td, TIOCSETP, &tsbuf);
(void) close(td);
(void) unlink(TU58LOCK);
return(SYSERR);
}
/* return drive number */
tape58 . t_open++;
return(dn);
}
/*
* tclose(dn)
* int dn; drive number to be closed
* this frees drive dn if it is open. then, if all drives are closed,
* it closes the device after restoring the modes to what they were.
*
* RETURNS: -1 on error; terrno indicates what happened
* 0 on success
*/
int tclose(dn)
int dn; /* drive being closed */
{
int x; /* temp for error */
struct dr58 *ds; /* pointer to drive structure */
struct sgttyb tsbuf; /* temp for sgttyb block */
struct tchars tcbuf; /* temp for tchars block */
/* check drive number */
if (dn != 0 && dn != 1){
terrno = TEBUNN;
return(SYSERR);
}
/* see if it is closed */
ds = &(tape58 . t_dr[dn]);
if (tape58 . t_open == 0 || (ds -> d_flags & OPEN) == 0){
terrno = TEICLO;
return(SYSERR);
}
/* restore the tcb info */
(void) ioctl(tape58 . t_fd, TIOCGETC, &tcbuf);
tcbuf . t_startc = tape58 . t_start;
tcbuf . t_stopc = tape58 . t_stop;
(void) ioctl(tape58 . t_fd, TIOCSETC, &tcbuf);
(void) ioctl(tape58 . t_fd, TIOCGETP, &tsbuf);
tsbuf . sg_flags = tape58 . t_sflag;
(void) ioctl(tape58 . t_fd, TIOCSETP, &tsbuf);
/* close the drive and the unit if necessary */
ds -> d_flags &= ~OPEN;
tape58 . t_open -= 1;
if ((x = tape58 . t_open) == 0)
if ((x = close(tape58 . t_fd)) < 0 || (x = unlink(TU58LOCK)) < 0)
terrno = x - TEUNIX;
else
x = 0;
/* return status code */
return(x == 0 ? 0 : SYSERR);
}
/*
* tseek(dn, offset, whence)
* int dn; drive to seek on
* int offset; position to seek relative to whence
* int whence; 0 - seek from beginning, 1 - present,
* 2 - end, of tape
*
* this positions drive dn at the indicated block. the block number is
* found with respect to special addressing mode. on error, an error return
* is made; no recovery of any kind is attempted. on error the position
* value may be corrupt. best to reset it in that case.
*
* RETURNS: -1 on error; terrno indicates what happened
* new position on success
*/
int tseek(dn, offset, whence)
int dn; /* drive number */
int offset; /* offset from whence location */
int whence; /* 0 - beginning, 1- current, 2 - end */
{
int x; /* temp for error */
int block; /* block number */
struct dr58 *ds; /* pointer to drive structure */
/* check drive number */
if (dn != 0 && dn != 1){
terrno = TEBUNN;
return(SYSERR);
}
/* be sure drive is open */
ds = &(tape58 . t_dr[dn]);
if (tape58 . t_open == 0 || (ds -> d_flags & OPEN) == 0){
terrno = TEDRNO;
return(SYSERR);
}
/* get the block number */
switch(whence){
case 0: /* from beginning */
block = offset;
break;
case 1: /* from current position */
block = offset + ds -> d_posit;
break;
case 2: /* from end */
if (ds -> d_flags & SAM)
block = NBLKSAM - 1;
else
block = NBLKNORM - 1;
block -= offset;
break;
default: /* bad arg to system call */
terrno = EINVAL - TEUNIX;
return(SYSERR);
}
/* Now position the tape */
if ((x = tcack(tape58 . t_fd, CPOSIT, ds -> d_mods, dn, ds -> d_swit,
0, block)) != TENONE){
terrno = x;
return(SYSERR);
}
/* return new block number */
return(block);
}
/*
* tread(dn, buf, nbytes)
* int dn; drive to be read from
* char *buf; buffer for incoming chars
* int nbytes; number of bytes to be read
*
* this reads nbytes bytes into buf from drive dn. NO CHECKSUM VERIFYING
* IS DONE (yet), so watch it. on any error, you get an error return; no
* recovery of any kind is attempted. reading begins at the current position
* of the drive; its position is updated only if there is no error, and
* on error the position value may be corrupt. best to reset it in that
* case.
*
* RETURNS: -1 on error; terrno indicates what happened
* number of bytes read on success
*/
int tread(dn, buf, nbytes)
int dn; /* drive number */
char *buf; /* buffer */
int nbytes; /* number of bytes to be read */
{
int nby; /* temporary for number of bytes to read */
int nbl; /* number of blocks to read */
int bytes; /* size of next data packet */
int b; /* counter in for loop */
int x; /* temp for error */
struct dr58 *ds; /* pointer to drive structure */
/* check drive number and drive */
if (dn != 0 && dn != 1){
terrno = TEBUNN;
return(SYSERR);
}
/* be sure the drive is open */
ds = &(tape58 . t_dr[dn]);
if (tape58 . t_open == 0 || (ds -> d_flags & ROPEN) != ROPEN){
terrno = TENORD;
return(SYSERR);
}
/* send command packet */
if ((x = tcnak(tape58 . t_fd, CREAD, ds -> d_mods, dn, ds -> d_swit,
nbytes, ds -> d_posit)) != TENONE){
terrno = x;
return(SYSERR);
}
/* now read the data, IOBLKSZ bytes at a time */
nby = nbytes;
nbl = (nbytes + IOBLKSZ - 1) / IOBLKSZ;
for (b = 0; b < nbl; b++){
bytes = (nby > IOBLKSZ) ? IOBLKSZ : nby;
if ((x = tgdata(tape58 . t_fd, buf + b * IOBLKSZ)) != TENONE){
terrno = x;
break;
}
nby -= bytes;
}
/* get end packet, check for errors */
if ((x = tpchk(tape58 . t_fd)) != TENONE){
terrno = x;
return(SYSERR);
}
/* return number of bytes read */
ds -> d_posit += delblk(ds -> d_mods & SAM);
/* return number of bytes successfully read */
return(abytes);
}
/*
* twrite(dn, buf, nbytes)
* int dn; drive to be written to
* char *buf; buffer for outgoing chars
* int nbytes; number of bytes to be written
*
* this writes the first nbytes in buf to dn. a checksum is computed and in
* verify mode (VFY set in modifier byte) it is checked. any error gives
* an error return; no recovery of any kind is attempted. writing begins
* at the current position of the drive; its position is updated only if
* there is no error, and on error the position value may be corrupt.
* best to reset it in that case.
*
* RETURNS: -1 on error; terrno indicates what happened
* number of bytes written on success
*/
int twrite(dn, buf, nbytes)
int dn; /* drive number */
char *buf; /* buffer */
int nbytes; /* number of bytes to be written */
{
int nby; /* temporary for number of bytes to write */
int nbl; /* number of blocks to write */
int bytes; /* size of next data packet */
int b; /* counter in for loop */
int x; /* temp for error */
struct dr58 *ds; /* pointer to drive structure */
/* check drive number and drive */
if (dn != 0 && dn != 1){
terrno = TEBUNN;
return(SYSERR);
}
/* be sure the drive is open */
ds = &(tape58 . t_dr[dn]);
if (tape58 . t_open == 0 || (ds -> d_flags & WOPEN) != WOPEN){
terrno = TENORD;
return(SYSERR);
}
/* send command packet */
if ((x = tcack(tape58 . t_fd, CWRITE, ds -> d_mods, dn, ds -> d_swit,
nbytes, ds -> d_posit)) != TENONE){
terrno = x;
return(SYSERR);
}
/* now write the data, IOBLKSZ bytes at a time */
nby = nbytes;
nbl = (nbytes + IOBLKSZ - 1) / IOBLKSZ;
for (b = 0; b < nbl; b++){
bytes = (nby > IOBLKSZ) ? IOBLKSZ : nby;
if ((x = tpdata(tape58 . t_fd, buf + b * IOBLKSZ, bytes)) != TENONE){
terrno = x;
break;
}
nby -= bytes;
}
/* return number of bytes written */
ds -> d_posit += delblk(ds -> d_mods & SAM);
/* return number of bytes successfully written */
return(abytes);
}
/*
* tioctl(dn, request, arg)
* int dn; drive to be diddled
* int request; how to diddle it
* union tio *arg; argument for diddling
*
* this changes or reports information stored in dn's control block.
* information is returned through arg, which points to an integer or
* a drive structure.
*
* RETURNS: -1 on error; terrno indicates what happened
* 0 on success
*/
int tioctl(dn, request, arg)
int dn; /* drive number */
int request; /* what you want */
union tio *arg; /* where it goes */
{
struct dr58 *ds; /* pointer to drive structure */
/* check drive number and drive */
if (dn != 0 && dn != 1){
terrno = TEBUNN;
return(SYSERR);
}
/* be sure the drive is open */
ds = &(tape58 . t_dr[dn]);
if (tape58 . t_open == 0 || (ds -> d_flags & OPEN) == 0){
terrno = TENORD;
return(SYSERR);
}
/* process the request */
switch(request){
case TU58SSAM: /* set special addressing mode */
ds -> d_mods |= SAM;
return(0);
case TU58CSAM: /* clear special addressing mode */
ds -> d_mods &= ~SAM;
return(0);
case TU58SVFY: /* set verify mode */
ds -> d_mods |= VFY;
return(0);
case TU58CVFY: /* clear verify mode */
ds -> d_mods &= ~VFY;
return(0);
case TU58SMTM: /* set maintenance mode */
ds -> d_swit |= MTM;
return(0);
case TU58CMTM: /* clear maintenance mode */
ds -> d_swit &= ~MTM;
return(0);
case TU58GDCB: /* gets drive control block */
arg -> drive . d_flags = ds -> d_flags;
arg -> drive . d_posit = ds -> d_posit;
arg -> drive . d_mods = ds -> d_mods;
arg -> drive . d_swit = ds -> d_swit;
return(0);
case TU58SPOS: /* set new block position */
ds -> d_posit = arg -> where;
return(0);
default: /* bad value */
terrno = EINVAL - TEUNIX;
return(SYSERR);
}
}
/*
*=========================================================================
* from here on, the routines are invisible to any other routines not in
* this file
*=========================================================================
*
* tcnak: makes and sends a command packet without acknowledgement
*/
static int tcnak(fd, cmd, mod, drive, swit, n, blkno)
int fd; /* TU58 file descriptor */
char cmd; /* TU58 command opcode byte */
char mod; /* TU58 command modifier byte */
int drive; /* TU58 drive being commanded */
char swit; /* TU58 command switches byte */
int n; /* number of bytes being used in this command */
int blkno; /* block number being used in this command */
{
char cpkt[14]; /* command packet */
int x; /* checksum */
/* make the command packet */
cpkt[0] = (char) BCTRL; /* signal command packet */
cpkt[1] = (char) 0xa; /* number of bytes in this packet */
cpkt[2] = cmd; /* command byte */
cpkt[3] = mod; /* modifier byte */
cpkt[4] = (char) drive; /* drive number */
cpkt[5] = swit; /* switch byte */
cpkt[6] = cpkt[7] = '\0'; /* sequence number */
/* number of data bytes */
cpkt[8] = (char) (n & 0377);
cpkt[9] = (char) ((n >> 8) & 0377);
/* block number bytes */
cpkt[10] = (char) (blkno & 0377);
cpkt[11] = (char) ((blkno >> 8) & 0377);
/* checksum */
cpkt[12] = cpkt[13] = '\0';
x = chksum(cpkt, 12);
cpkt[12] = (char) (x & 0377);
cpkt[13] = (char) ((x >> 8) & 0377);
/* send the sucker */
if (write(fd, cpkt, 14) != 14)
return(errno - TEUNIX);
/* return */
return(TENONE);
}
/*
* tcnak: makes and sends a command packet with acknowledgement
*/
static int tcack(fd, cmd, mod, drive, swit, n, blkno)
int fd; /* TU58 file descriptor */
char cmd; /* TU58 command opcode byte */
char mod; /* TU58 command modifier byte */
int drive; /* TU58 drive being commanded */
char swit; /* TU58 command switches byte */
int n; /* number of bytes being used in this command */
int blkno; /* block number being used in this command */
{
int x; /* error code */
/* send command packet */
if ((x = tcnak(fd, cmd, mod, drive, swit, n, blkno)) != TENONE)
return(x);
/* return response */
return(tpchk(fd));
}
/*
* tpdata: puts a data packet onto the TU58
*/
static int tpdata(fd, loc, num)
int fd; /* TU58 file descriptor */
char *loc; /* beginning of the byte stream */
int num; /* number of bytes in the stream */
{
char dpkt[132]; /* data packet */
int i; /* counter in a for loop */
int x; /* checksum */
/* set up the data packet */
dpkt[0] = BDATA; /* flag packet as data */
dpkt[1] = (char) num; /* number of data bytes */
/* put in the data bytes */
for(i = 2; i < num + 2; i++)
dpkt[i] = loc[i - 2];
/* now the checksum */
dpkt[num + 2] = dpkt[num + 3] = '\0';
x = chksum(dpkt, num + 2);
dpkt[num + 2] = (char) (x & 0377);
dpkt[num + 3] = (char) ((x >> 8) & 0377);
/* send the sucker */
if (write(fd, dpkt, num + 4) != num + 4)
return(errno - TEUNIX);
/* return response */
return(tpchk(fd));
}
/*
* tgdata: gets a data packet from the TU58
*/
static int tgdata(fd, loc)
int fd; /* TU58 file descriptor */
char *loc; /* location of block to be read */
{
char pkt[134]; /* input byte */
char chk[2]; /* input checksum */
int x; /* checksum */
int i;
/* get first byte, be sure it's data */
if (read(fd, pkt, 1) != 1)
return(errno - TEUNIX);
if (*pkt == BEND){ /* end -- probable screwup, look */
if ((i = tend(fd)) == TESWRT){
terrno = i;
i = TENONE;
}
return(i);
}
if (*pkt != BDATA) /* garbage - resync line */
return(treset(fd) ? TEREIN : TECLBD);
/* grab the size of the data and then the data */
if (read(fd, pkt + 1, 1) != 1)
return(errno - TEUNIX);
x = (int) pkt[1];
if (x == -128)
x = 128;
for(i = 0; i < x; i++){
if (read(fd, loc + i, 1) != 1)
return(errno - TEUNIX);
pkt[i + 2] = loc[i];
}
/* verify the checksum */
for(i = 0; i < 2; i++)
if (read(fd, chk + i, 1) != 1)
return(errno - TEUNIX);
i = (int) ((unsigned short int) (((chk[1]&0377)<<8)|(chk[0]&0377)));
if (i != chksum(pkt, x+2))
terrno = TEBCSR;
/* return success */
return(TENONE);
}
/*
* tend: processes an end packet
*/
static int tend(fd)
int fd; /* TU58 file descriptor */
{
char pkt[14]; /* end packet */
int i; /* counter in a for loop */
/* load the end packet */
pkt[0] = (char) BEND;
for(i = 1; i <= 13; i++)
if (read(fd, pkt + i, 1) != 1)
return(errno - TEUNIX);
/* verify the checksum */
i = (int) ((unsigned short int) (((pkt[13]&0377)<<8)|(pkt[12]&0377)));
if (i != chksum(pkt, 12))
terrno = TEBCSR;
/* set number of bytes operated on, return error status */
abytes = (int) ((unsigned short int) (((pkt[9]&0377)<<8)|(pkt[8]&0377)));
return((int) pkt[3]);
}
/*
* delblk: this changes the number of bytes read to something the VAX
* can handle (a necessary VAX hack, it and the TU58 have
* different size "integers") and returns the number of blocks
* processed.
*/
static int delblk(sam)
int sam; /* nonzero if special addressing mode on */
{
/* check for special addressing mode */
if (sam > 0){
/* here, note 128 = -128; sign extention kills us */
if (abytes == -128)
abytes = 128;
else if (abytes < 0)
abytes += SAMSIZE;
/* return number of blocks processed */
return(nblock(abytes, SAMSIZE));
}
/* not special addressing mode; neg means complement */
if (abytes < 0)
abytes += NORMSIZE;
/* return number of blocks processed */
return(nblock(abytes, NORMSIZE));
}
/*
* chksum: this computes a one's complement checksum
*/
static int chksum(begin, num)
char *begin; /* begin computing check sum here */
int num; /* compute it for this many bytes */
{
int i; /* counter in a for loop */
unsigned short int losum; /* new checksum */
unsigned short int prsum; /* previous checksum */
/* initialize */
prsum = losum = 0;
/* compute the sum */
for(i = 0; i < num; i += 2){
losum += (unsigned short int) (((begin[i+1]&0377)<<8)|(begin[i]&0377));
if (prsum > losum)
losum++;
prsum = losum;
}
/* put it away */
return((int) losum);
}
/*
* tpchk: this handles a TU58 response to a command or data packet
*/
static int tpchk(fd)
int fd; /* file descriptor */
{
char rec; /* incoming byte */
int i; /* return code from end packet */
/* get byte */
if (read(fd, &rec, 1) != 1)
return(errno - TEUNIX);
/* process it */
switch(rec & 0177){
case BCONT: /* continue byte */
return(TENONE);
case BEND: /* end packet */
if ((i = tend(fd)) == TESWRT){
terrno = i;
i = TENONE;
}
return(i);
default: /* anything else - resync line */
return(treset(fd) ? TEREIN : TECLBD);
}
}
/*
* treset: this resyncs the line
*/
static int treset(fd)
int fd; /* file descriptor */
{
char xmit[2]; /* chars to be sent to the TU58 */
int i; /* counter in a for loop */
int x; /* temp for error */
long numb; /* chars waiting to be read */
/* set break for 2 chars */
if (ioctl(fd, TIOCSBRK, NULL) < 0){
return(errno - TEUNIX);
}
if (write(fd, xmit, 2) != 2){
x = errno - TEUNIX;
(void) ioctl(fd, TIOCCBRK, NULL);
return(x - TEUNIX);
}
if (ioctl(fd, TIOCCBRK, NULL) < 0){
return(errno - TEUNIX);
}
/* now write the two init chars */
xmit[0] = xmit[1] = BINIT;
if (write(fd, xmit, 2) != 2){
return(errno - TEUNIX);
}
/* now gobble until continue or timeout */
for(i = 0; i < MAXCHAR; i++)
if (ioctl(fd, FIONREAD, &numb) < 0)
return(errno - TEUNIX);
else if (numb == 0L)
continue;
else if (read(fd, xmit, 1) != 1){
return(errno - TEUNIX);
}
else if (((*xmit)&0177) == BCONT)
break;
/* no response or wrong response - error return */
if (i == MAXCHAR)
return(TEINTU);
/* return success */
return(TENONE);
}