USG_PG3/usr/source/io1/cm.c
#
/* cm.c driver for minitape controller, character device */
#include "../head/param.h"
#include "../head/conf.h"
#include "../head/user.h"
#include "../head/userx.h"
/* commands for tape control system */
#define DINIT 0100 /* drive initialization */
#define UNLD 0105 /* unload cartridge -- wait for completion */
#define SNB 0111 /* set byte cnt n - all drives */
/* sent as lob and hob each with even parity */
#define SMF 0114 /* set record cnt m for spacing - all drives */
/* sent as lob and hob each with even parity */
#define LDTS 0121 /* load drive and track select */
#define WRT 0124 /* write n bytes */
#define WTM 0130 /* write a tape mark */
#define WGP 0135 /* write an extended gap */
#define RD 0141 /* read n bytes or less */
#define RSW 0144 /* read status word */
#define SPFM 0150 /* space forward m records */
#define SPRM 0155 /* space reverse m records */
#define STMF 0160 /* space to tape mark forward */
#define STMR 0165 /* space to tape mark reverse */
#define RWD 0171 /* rewind to LPT */
#define RCRC 0174 /* read crc character */
#define CLAF 0200 /* clear all flags (status word) */
/* status word, first byte bits 0-6, 7 prog uses */
#define RDER 1 /* read error exists after 10 retries */
/* or total failure in writing */
#define EW 2 /* early warning */
#define TMD 4 /* tape mark detected */
#define DPTM 010 /* n is smaller than record read, */
/* or bytes transferred in write not equal to n */
#define WNWEN 020 /* write with protect on - attempted */
#define SPTD 040 /* space operation terminated at EOT or BOT */
#define NONL 0100 /* selected drive off line */
#define TERR 0200 /* timeout on request to controller */
/* status word, second byte bits 0-6, 7 prog uses */
#define TOR 0400 /* tape off reel */
#define EOT 01000 /* end of tape */
#define BOT 02000 /* begining of tape */
#define LPT 04000 /* load point */
#define NRDY 010000 /* execute while not ready */
#define ILCMD 020000 /* illegal cmd or wrong parity on cmd or its arg */
#define RWDG 040000 /* selected drive rewinding when set */
#define SERR 0100000 /*can't read or can't clear status */
#define INTAE 0100 /* data interrupt enable (A interrupt) */
#define INTBE 040 /* command interrupt enable (B interrupt) */
#define CSR0 1 /* control bit 0 to device, set = cmd */
#define CSRN 0 /* control bit 0 to device, clr = data */
#define REQA 0200 /* request A */
#define REQB 0100000 /* request B */
#define RAW 05
#define ILLEGAL 03 /* track adr overflow */
#define NUNIT 4
#define CMADDR 0167750 /* address of dr11-c */
#define FWRITE 02
struct {
int cstat; /* dr11-c offsets */
int cout;
int cin;
};
int c_active; /* controller busy flag*/
char c_test[NUNIT]; /* < 04 = single track only */
char c_open[NUNIT];
char c_ldts[NUNIT] {020,040,0100,0200}; /* bits 4-7 = unit. 0,1 = track */
char c_flgb[NUNIT];
int c_stau[NUNIT]; /* drive status words */
int c_mblkno[NUNIT];
int c_drv;
char c_flgc;
char c_sleep;
/* minors for drives 1,2,3,4 respectively */
/* track 1 single file 0,8,16,24 */
/* track 2 single file 1,9,17,25 */
/* track 3 single file 2,10,18,26 */
/* track 4 single file 3,11,19,27 */
/* multi-track single file 4,12,20,28 */
/* multi-track multi-file 5,13,21,29 */
/* NOTE: The maximum permissible tranfer rate of data or */
/* arguments to or from the cartridge controller is 100kc */
cmopen(dev, flag)
{
register drv,drv1;
drv1 = drv = dev.d_minor;
drv = (drv >> 3) & 03; /* physical drive */
if (c_open[drv])
u.u_error = ENXIO; /* already in use */
else {
c_open[drv]++; /* mark unit busy */
c_mblkno[drv] = 0; /* clr seek # */
c_stau[drv] = 0; /* clr status */
c_flgb[drv] = 1; /* initialize drive */
c_test[drv] = drv1 =& 07;
if (drv1 != RAW) /* if not RAW new track */
{
c_ldts[drv] =& 0360;
c_ldts[drv] =| (drv1 & 03);
}
}
}
cmclose(dev, flag)
{
register int drv;
drv = (dev.d_minor >> 3) &03;
c_flgb[drv] = 3; /* read close */
if (flag & FWRITE) c_flgb[drv]--; /* write close */
cmstart(drv);
c_open[drv] = 0; /* free drive */
}
cmread(dev)
{
register int drv;
drv = (dev.d_minor >> 3) &03; /* physical drive # */
c_open[drv] = RD;
cmstart(drv);
}
cmwrite(dev)
{
register int drv;
drv = (dev.d_minor >> 3) &03; /* physical drive # */
c_open[drv] = WRT;
cmstart(drv);
}
cmstart(driv)
{
extern char partab[];
register int drv,b,c;
drv = driv; /* physical drive # */
if(c_active) /* sleep if in use */
{
spl5();
c_sleep++;
while(c_active) sleep(&c_sleep,-1);
c_sleep--;
spl0();
}
c_drv = drv;
c_active++; /* mark busy */
cwtrd: if(cminit() < 0) goto cerror;
switch(c_flgb[drv])
{ /* initial & closing routines */
case 2: if((b = cmslp(WTM)) < 0) goto cerror; /* write 1st tape mark */
if(b & EOT)
{
if((c = cmrwd()) < 0) goto cerror;
}
if(cmslp(WTM) < 0) goto cerror; /* write 2nd tape mark */
if(b & EOT) goto cunlod; /* only 2nd tm on track */
if(cmslp(SPRM) < 0) goto cerror; /* backspace 1 record */
case 3: if(c_test[drv] == RAW) goto cdone;
cunlod: if(cmslp(UNLD) < 0) goto cerror; /* unload */
goto cdone;
case 1: if (c_test[drv] == RAW)
{
if((c = cmstatus()) < 0) goto cerror;
if (c & LPT)
{
c_ldts[drv] =& 0360; /* track 0 */
if(cminit() < 0) goto cerror; /* chg track */
goto cinit;
}
if((c & BOT) == 0) break;
}
cinit: if(cmslp(DINIT) < 0) goto cerror; /* initialize drive */
if(cmslp(SMF) < 0) goto cerror; /* spacing arg next */
CMADDR->cstat = CSRN;
CMADDR->cout = 0201; /* set space for 1 record */
cmdelay();
CMADDR->cout = 0; /* 2nd byte */
if(cmwait(INTBE) < 0) goto cerror;
}
/* read & write operations */
c_flgb[drv] = 0;
if(((c = c_mblkno[drv]) != (b = lshift(u.u_offset,-9)))
&& ((u.u_offset[1] & 0777) == 0))
{
if(cmseek(c,b) < 0) goto cerror;
c_mblkno[drv] = b;
}
if(cmslp(SNB) < 0) goto cerror; /* byte cnt args next */
c = b = u.u_count; /* positive byte cnt */
/* cnt <10 or >2048 = error */
b =& 0177;
b =| (partab[b] & 0200); /* add even parity */
c = (c >> 7) & 0177;
c =| (partab[c] & 0200); /* add even parity */
CMADDR->cstat = CSRN;
CMADDR->cout = b; /* send cnt low order byte */
cmdelay();
CMADDR->cout = c; /* send cnt high order byte */
if(cmwait(INTBE) < 0) goto cerror;
if (c_open[drv] == WRT) /* write */
{
if(cmslp(WRT) < 0) goto cerror; /* write command */
CMADDR->cstat = CSRN;
while ((c = cpass()) != -1)
CMADDR->cout = c; /* char to controller */
if((c = cmslp(0)) < 0) goto cerror; /* sleep */
if(c & EOT)
if((c = cmrwd()) < 0) goto cerror;
goto ccount;
}
if((c = cmslp(RD)) < 0) goto cerror; /* sleep till readable */
if(c & TMD) goto ccount;
if(c & EOT)
{
if(c = cmrwd() < 0) goto cerror;
goto cwtrd; /* read again */
}
CMADDR->cstat = CSRN;
while((CMADDR->cstat & REQB) == 0) /* record < than req */
if ((passc(CMADDR->cin)) != 0) break;
if(cmwait(INTBE) >= 0) /* wait for B request */
{
ccount: c_mblkno[drv]++;
goto cdone;
}
cerror: u.u_error = EIO; /* report error */
cdone: c_active = 0; /* free controller */
if(c_sleep > 0) wakeup(&c_sleep);
}
cmintr()
{
CMADDR->cstat = 0; /* kill interrupts */
wakeup(&c_active);
}
cminit()
{
if(cmslp(LDTS) < 0) return(-1); /* select drive */
CMADDR->cstat = CSRN;
CMADDR->cout = c_ldts[c_drv]; /* drive & track argument */
if(cmwait(INTBE) < 0) return(-1);
}
cmseek(cur,sek)
{
register int x,y,z;
x = cur; /* current record block # */
y = sek; /* record block # sought */
if(x < y)
{
while(x != y)
{
if((z = cmslp(SPFM)) < 0) break;
if(z & SPTD)
{
if(cmrwd() < 0) break;
if(cminit() != 0) break;
}
x++;
}
goto cog;
}
while(x != y)
{
if((z = cmslp(SPRM)) < 0) break;
if(z & SPTD)
{
if(c_test[c_drv] > ILLEGAL && (c_ldts[c_drv] & 3) != 0)
{
c_ldts[c_drv]--;
if(cminit() != 0) break;
while((z & EOT) == 0)
if((z = cmslp(STMF)) < 0)goto cog;
}
else break;
}
x--;
}
cog: if(x != y) return(-1); /* failed */
}
cmdelay()
{
}
cmrwd()
{
if(c_test[c_drv] > ILLEGAL && (c_ldts[c_drv] & 3) < ILLEGAL)
{
c_ldts[c_drv]++; /* inc track */
return(cmslp(RWD)); /* rewind: 0=ok,negative=trouble */
}
return(-1); /* Illegal, no rewind performed */
}
cmslp(cmmd)
{
register int b,c;
if((c = cmmd))
{
CMADDR->cstat = CSR0;
CMADDR->cout = c; /* send command */
}
b = INTAE;
if((c > WRT && c != RSW) || c < SNB)
{
if(c != RD) b = INTBE;
spl5(); /* insure sleep */
CMADDR->cstat =| b; /* enable interrupt */
while(CMADDR->cstat & b) sleep(&c_active,-1);
spl0();
}
return(cmwait(b)); /* 0=ok,negative=trouble */
}
cmwait(boa)
{
register int b,c,d;
b = REQB;
if(boa == INTBE) d = REQA;
else
{
d = b;
b = REQA;
}
for (c = 0;c < 200;c++)
{
if (CMADDR->cstat & b) /* look for expected request */
{
if (CMADDR->cstat & d) /* both requests, get status */
{
if(c_flgc) return(-1);
return(cmstatus());
}
return(0);
}
}
c_stau[c_drv] =| TERR; /* request overdue = error */
return(-1); /* trouble */
}
cmstatus()
{
register int c,b;
c_flgc++; /* cmstatus is caller */
if(cmslp(RSW) < 0) goto werror; /* read status */
CMADDR->cstat = CSRN;
c = CMADDR->cin & 0177; /* 1st status byte */
cmdelay();
b = CMADDR->cin; /* 2nd status byte */
b = (b << 8) & 077400;
c_stau[c_drv] = c =| b;
if(cmwait(INTBE) < 0) goto werror;
CMADDR->cstat = CSR0;
CMADDR->cout = CLAF; /* clear flags */
if(cmwait(INTBE) < 0)
{
werror: c = -1;
c_stau[c_drv] =| SERR; /* status read or clr ng */
}
if (c & (RDER|DPTM|WNWEN|NONL|TOR|NRDY|ILCMD)) c = -1;
c_flgc = 0;
return(c);
}