[net.sources] Unix driver for a hitachi optical disc.

jd@stmvax.UUCP (Joe Davida) (03/07/86)

	I am posting this for a friend.

	Here is a driver for a hitachi optical disc "juke box" with
	a GPIB interface. It was written for a WICAT uniplus unix
	(a system 3?). Please refer all questions to mr. Jungbo Yang
	at ttihwe, email
	<ametek|group3|cadovax|ghsvax|scgvaxd|oberon>!stmvax!ttihwe!jy

------------------------------CUT HERE-----------------------------------------
: Run this file as a shell script to extract contents.
: This file contains files hita.c hita.h hitachi.h opt_fs.h
echo x - hita.c
cat << '//E*O*F hita.c' > hita.c
/* Hitachi optical disk and jukebox device driver for wicat */
/* IEEE-488 (GPIB) / multibus interface board from */
/* National Instruments is used to interface to the disk */

/* Author: Jungbo Yang */

#include <sys/param.h>
#include <sys/text.h>
#include <sys/tty.h>
#include <sys/mx.h>
#include <sys/ino.h>
#include <sys/inode.h>
#include <sys/filsys.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/dir.h>
#include <sys/conf.h>
#include <sys/user.h>
#include <signal.h>

#include "hitachi.h"
#include "hita.h"
#include "opt_fs.h"

/* #define HITADEBUG              Uncomment to turn driver debugging on */

#undef DEBUG
#ifdef HITADEBUG
#define DEBUG(args)		printf args
#else
#   define DEBUG(args)
#endif

#define TRUE                    1
#define FALSE                   0
#define odd(n)			((int)(n) & 01)
#define UNIT(devnum)            (minor(devnum))
#ifndef void
#define void                    int
#endif
#define wlowbyte(x)	(0xff & x)
#define whibyte(x)	(0xff & (x >> 8))


/* GPIB 796 registers */
typedef struct {   	        /*  bytes are swaped. you know why. */
	char    imr1;           /*  +1 interrupt mask register 1        */
	char    cdor;           /*  +0 byte out register                */
	char    spmr;           /*  +3 serial poll mode register        */
	char    imr2;           /*  +2 interrupt mask register 2        */
	char    auxmr;          /*  +5 auxiliary mode register          */
	char    admr;           /*  +4 address mode register            */
	char    eosr;           /*  +7 end of string register           */
	char    adr;            /*  +6 address register 0/1             */
	char    bcr1;           /*  +9 byte count register 1            */
	char    bcr0;           /*  +8 byte count register 0            */
	char    ccfr;           /* +11 carry cycle function register    */
	char    cr0;            /* +10 control register 0               */
	char    acr1;           /* +13 address count register 1         */
	char    acr0;           /* +12 address count register 0         */
	char    cr1;            /* +15 control register 1               */
	char    acr2;           /* +14 address count register 2         */
} ibregs ;

/*
 *  one 488 interface board with 2 optical disk drives
 */
ibregs *ib = ((ibregs *) 0xf00600);  /* only one board */

#define NHITA		1		/* one OFC */
#define NODD		2
static HITA_t hita[NODD]; 	/* two ODDs */
static HGPIB_t gpib;
#define HFMSSIZE	512	/* fms */
#define HNMINOR		8	/* max number of minor device */
static char fms[HNMINOR][HFMSSIZE];
#define HSTATSS		0xe0	/* status tables */
#define HSTATDEVID	0xe9
#define HSTATCELL	0x68
#define HSTATODDS	0xec
#define HSTATSECT	0x61
static char ss_stat[HSTAT_L_SS+2];
static char devid_stat[HSTAT_L_DEVID+2];
static char cell_stat[HSTAT_L_CELL+2];
static char odd_stat[HSTAT_L_ODDS+2];
static char sect_stat[HSTAT_L_SECT+2];
static char codd = -1;		/* current ODD */
/* proc sync */
static int sleep_cnt = 0;
static int sleep_out = 0;
static int sleep_in = 0;
/* PSleeP should be done before the critical section */
#define PSleeP	{ \
	if (hita[UNIT(dev)].h_state & HBUSY) { \
	    if (++sleep_cnt >= HMAXOPEN) { \
		--sleep_cnt; \
		u.u_error = EBUSY; \
		return; \
	    } \
	    if (++sleep_in >= HMAXOPEN) \
		sleep_in = 0; \
	    sleep( (caddr_t) ss_stat + sleep_in, HITAPRI); \
	} \
	hita[(UNIT(dev))].h_state |= HBUSY; \
}
/* done at the end but inside of the critical section */
#define PWakeuP { \
	if (sleep_cnt) { \
	    if (++sleep_out >= HMAXOPEN) \
		sleep_out = 0; \
	    wakeup( (caddr_t) ss_stat + sleep_out); \
	    sleep_cnt--; \
	} else { \
		hita[UNIT(dev)].h_state &= ~(HBUSY); \
	} \
}

/*
 * hita structure bit definitions
 */
#define HOPEN		0x1	/* state */
#define HREAD		0x1	/* state */
#define HWRITE		0x2	/* state */
#define HITASLEEP	0x4	/* state, process called sleep() */
#define HITAPHYS	0x8	/* state, user buffer is the hidden memory */
#define HEXCLUSIVE	0x10	/* state */
#define HBUSY		0x20	/* state */
#define DUAL		0x1 	/* opop dual write on */
#define DCI		0x2	/* opop delete block check inhibit */

/*
 * CRC checksum (this should really be done in hardware)
 */
#define POLY      0x8408
crc (start, end, crc1, crc2)
register  char  *start, *end, *crc1, *crc2;
{

  register c;
  register counter, data = 0xffff;          /* data will start from all one's */
  register polynom = POLY;

    while (start < end) {
	c = *start++ & 0xFF;
	if (start < end)
	    c |= (0xff00 & (*start++ << 8));
	data ^= c;
	counter = 17;
	while (--counter) {              /* loop for 16 shifts */
	    if (data & 1) {
		data >>= 1;
		data ^= polynom;
	    } else
		data >>= 1;
	}
    }
    *crc1 = (char) ~(data & 0xff);
    *crc2 = (char) ~(data >> 8) & 0xff;
    /* DEBUG(("crc1= %x, crc2= %x\n",0xff&(*crc1),0xff&(*crc2))); */
}

/*
 * Programmed I/O  (busy wait)
 * readone(), cmdone() and dataone()
 */
char readone()
{
    register char t;

    do {
         t = ib->isr1;
	 gpib.h_isr1 |= t;
    }  while ((t & HR_DI) == 0);
    return(ib->dir);
}

cmdone(cmd)
    char cmd;
{
    register char t;

    ib->cdor = cmd;
    do {
	t = ib->isr2;
        gpib.h_isr2 |= t;
    } while ((t & HR_CO) == 0);	/* busy wait until set */
}

dataone(dat)
    char dat;
{
    register char t;

    ib->cdor = dat;
    do {
	t = ib->isr1;
        gpib.h_isr2 |= t;
    }  while ((t & HR_DO) == 0);	/* busy wait until set */
}

/*
 *  wait for SRQ routine  (busy wait)
 */
srq() {
    register char t;

    DEBUG(("waiting for SRQ..."));
    ib->imr2 = HR_SRQIE;
    do  {
	t = ib->isr2;
	gpib.h_isr2 |= t;
    } while ((t & HR_SRQI) == 0);  /* busy wait until set */
    ib->imr2 &= ~HR_SRQIE;
}

/* short delay (busy wait) */
ibdelay(n) {
    int j,i,t;

    for (j=0; j<n; j++)
        for (t=i=0; i<1000; i++)
	    t++;
    return(t);
} 

/*
 * initialize the Interface board
 */
init(tl)
  char tl;
{
    char t;

    /* DEBUG(("init(%c).\n",tl)); */
    ib->cr0 = HR_LMR;		/* set local master reset */
    ib->cr0 = 0;		/* clear it */
    ib->cr1 = 0;		/* clear it */
    ib->auxmr = 2;		/* chip reset */
    t = ib->cptr;		/* clear stat by reading */
    t = ib->isr1;
    t = ib->isr2;
    ib->imr1 = ib->imr2 = ib->spmr = 0;	/* disable all interrupt */
    ib->adr = 0;		/* set MTA=0100 and MLA=040 */
    ib->adr = HR_ARS | HR_DT | HR_DL;
    ib->admr = HR_TRM1 | HR_TRM0 | HR_ADM0;
    if (tl == 't')
        ib->admr = HR_TON;
    else
        ib->admr = HR_LON;
    ib->auxmr = ICR | 5;
    ib->auxmr = PPR | HR_PPU;
    ib->auxmr = AUXRA | 0;
    ib->auxmr = AUXRB | HR_INV;
    ib->auxmr = AUXRE | 0;
    ib->cr1 = HR_MIE | HR_SC;	/* main interrupt switch on */
    ib->auxmr = AUX_PON;	/* power on */
}

/*
 * GPIB ACTIVE state
 */
active(tl)
  char tl;
{
    /* DEBUG(("active(%c).\n",tl)); */
    /* become active controller */
    ib->cr1 = HR_SC;		/* prepare for IFC and REN */
    if (tl == 't')
        ib->admr = HR_TON;
    else
        ib->admr = HR_LON;
    ib->auxmr = AUX_PON;	/* power on */

    ib->auxmr = AUX_SIFC;	/* set IFC */
    ibdelay(1); 		/* > 100 micro seconds delay */
    ib->auxmr = AUX_CIFC;	/* clear IFC */
    /* now, check to see I am active or not ***************
    if (ib->adsr & HR_CIC) {
	if (tl == 't')
		if (ib->adsr & HR_TA)
		    DEBUG(("Active talker. ok.\n"));
		else
		    DEBUG(("Active talker. NOT ok.\n"));
	else
		if (ib->adsr & HR_LA)
		    DEBUG(("Active listenner. ok.\n"));
		else
		    DEBUG(("Active listenner. NOT ok.\n"));
    }
    ***************** */
}

/* LISTEN and STANDBY for data in */
listen_sb() {
	/* DEBUG(("listen and standby for data in\n")); */
        ib->auxmr = LISTEN;
        ib->auxmr = SB;
}

/* EOI with the final data out */
eoi() {
    /* DEBUG(("eoi().\n")); */
    ib->auxmr = AUX_SEOI;
}

/*
 * POWER ON to reset some parameters, such as talker/listenner
 *     Also, this causes the state to shift from ACTIVE to PASSIVE
 */
pon() {
    /* DEBUG(("pon().\n")); */
    ib->auxmr = AUX_PON;
}

/*
 * DMA i/o with interrrupt
 */
int dma(dev,rw,addr,cnt)
    dev_t dev;
    int cnt;
    char rw;
    char *addr;
{
    register HITA_t *g = &hita[UNIT(dev)];
    int ret;
    char t;
    register int i,fcnt;
    register char *faddr;

    /* state should be the active in standby mode */
    fcnt = cnt;
    faddr = addr;

    /* set the byte count and address*/
    cnt = -cnt;
    DEBUG(("dma: %s cnt = %d\n",rw=='r'?"read":"write",cnt));
    ib->bcr0 = (char) (0xff & cnt);
    ib->bcr1 = (char) (0xff & (cnt >> 8));
    ib->acr0 = lobyte(addr);	/* physical memory addr */
    ib->acr1 = midbyte(addr);
    ib->acr2 = hibyte(addr);

    if (rw == 'r') {
	t = ib->isr1;           /* clear it */
	ib->auxmr = AUX_FH;
	ib->auxmr = AUXRA;      /* normal handshake */
	ib->ccfr = 0;
	ib->imr1 = HR_ENDIE;    /* set for reading */
	ib->imr2 = HR_DMAI;

	/* DEBUG(("dma: read: sleeping...")); */
	g->h_state |= HITASLEEP;
        ib->cr1 = HR_MIE|HR_SC;
	ib->cr0 = HR_DMAE|HR_FINIE;
	ib->cr0 = HR_DMAE|HR_FINIE|HR_GO;
	sleep( (caddr_t) g, HITAPRI);
	/* DEBUG(("dma: read: woken up.\n")); */

    } else { /* WRITE */
	t = ib->sr;             /* clear to start */
	ib->imr1 = 0;
	ib->imr2 = HR_DMAO;

	/* DEBUG(("dma: write: sleeping...")); */
	g->h_state |= HITASLEEP;
	if (rw == 'l') {	         /* the last write. send EOI */
            ib->ccfr = AUX_SEOI;         /* send EOI with the last byte */
            ib->cr1 = HR_MIE|HR_SC;
	    ib->cr0 = HR_MEMRD|HR_ECC|HR_DMAE|HR_FINIE;
	    ib->cr0 = HR_MEMRD|HR_ECC|HR_DMAE|HR_FINIE|HR_GO;
	} else {
            ib->ccfr = 0;
            ib->cr1 = HR_MIE|HR_SC;
	    ib->cr0 = HR_MEMRD|HR_DMAE|HR_FINIE;
	    ib->cr0 = HR_MEMRD|HR_DMAE|HR_FINIE|HR_GO;
	}
	sleep( (caddr_t) g, HITAPRI);
	/* DEBUG(("dma: write: woken up.\n")); */
    }
    ib->cr0 = 0;            /* clear DMA */
    /* g->h_state &= ~(HITASLEEP);   done in hitaintr() */
    gpib.h_sr    &= ~(HR_NGPIBIR|HR_NFIN|HR_DONE);

    ret = (ib->bcr1 << 8) + (unsigned char) ib->bcr0;
    DEBUG(("dma: byte leftover = %d\n",-ret));
    return(ret-cnt);
}

getstat(dev)
dev_t dev;
{
    register int i;
    char t,ret;
    register HITA_t *g = &hita[UNIT(dev)];

   /* catch the SQR */
    gpib.h_isr2 &= ~(HR_SRQI);

    /* set the ib to active Listenner */
    active('l');

    /* wait for SRQ */
    if ((gpib.h_isr2 & HR_SRQI) == 0)
        srq();

    cmdone(UNL);
    cmdone(UNT);
    cmdone(SPE);
    cmdone(g->h_mta);

    /* aux listen and standby command */
    listen_sb();

    /* read one byte status */
    ret = 0xff & readone();
    DEBUG(("getstat: %x\n",ret));

    /* set the ib back to active mode for sending commands */
    active('t');

    cmdone(SPD);
    cmdone(UNT);
    cmdone(UNL);

    /* save the status, when they are available, here */
    if (ret==0x80 || ret==0x40 || ret==0x10) {
	return(0);
    } else {
	active('l');
	cmdone(UNT);
	cmdone(UNL);
	cmdone(g->h_mta);
	listen_sb();
	gpib.h_isr1 &= ~(HR_END);
	for (i=0; i<6; i++)		/* through away the header */
	    readone();
	i=0;
        switch(0xff & ret) {
	    case HSTATSS:
	        while(1) {
		    ss_stat[i++] = readone();
	            if ((gpib.h_isr1 & HR_END) || i>HSTAT_L_SS+2)
		        break;
	        }
		break;
	    case HSTATDEVID:
	        while(1) {
		    devid_stat[i++] = readone();
	            if ((gpib.h_isr1 & HR_END) || i>HSTAT_L_DEVID+2)
		        break;
	        }
		break;
	    case HSTATCELL:
	        while(1) {
		    cell_stat[i++] = readone();
	            if ((gpib.h_isr1 & HR_END) || i>HSTAT_L_CELL+2)
		        break;
	        }
		break;
	    case HSTATODDS:
	        while(1) {
		    odd_stat[i++] = readone();
	            if ((gpib.h_isr1 & HR_END) || i>HSTAT_L_ODDS+2)
		        break;
	        }
		break;
	    case HSTATSECT:
	        while(1) {
		    sect_stat[i++] = readone();
	            if ((gpib.h_isr1 & HR_END) || i>HSTAT_L_SECT+2)
		        break;
	        }
		break;
	    default:
	        while(1) {
		    readone();
	            if (gpib.h_isr1 & HR_END) /* unknown stat. dump it */
		        break;
	        }
		break;
	}
        return(0xff & ret);
    }
}

hitainit(u)
register short u;
{
    register HITA_t *g;
    int i;

    DEBUG(("hitainit called:\n"));
    g = &hita[u];
    g -> h_state  = 0;
    g -> h_opfd  = 0;
    g -> h_opop = 0;
    g -> h_pbn = -1;	/* not initialized */
    g -> h_woff = 0;	/* empty */
    g -> h_roff = 512;
    g -> h_mta    = MTA + u;
    g -> h_mla    = MLA + u;
    sleep_cnt = sleep_in = sleep_out = 0;
}

/*
Fd is available in u.
	fd.f_flag 1==read(FREAD) and 2==write(FWRITE)
	rwflag =1 for FREAD and 2 for FWRITE and 0 for exclusive?
Allow only one WRITE open (mode 1,2 or 3)
    is hita.state HWRITE?
Check the max number of open
    is opfd < HMAXOPEN?
Increment opfd
Set hita.stat=HWRITE
*/
hitaopen(dev,rwflag)
dev_t dev;
short rwflag;
{
    register HITA_t *g;
    register struct a {
	char *fname;
	int fmode;
    } *uap;
    int i,x;
    HFMS_t *pfms;

    uap = (struct a *) u.u_ap;
    DEBUG (("HITA OPEN called, dev = %d/%d, rwflag=%x, h.state=%x, mode=%x\n",
	 major(dev), UNIT(dev),rwflag,hita[UNIT(dev)].h_state,uap->fmode));

    if (codd < 0) {		/* first open */
	for (i=0; i<NODD; i++) {
	    hitainit(i);
	    /* init fms states */
    	    for (i=0; i<HNMINOR; i++) {
		pfms = (HFMS_t *) &fms[i][0];
		pfms->fms_state = FMS_NOT_MOUNTED;
		pfms->fms_vol[0] = 0;
    	    }
	}
        gpib.h_isr1 = gpib.h_isr2 = gpib.h_sr = codd = 0;
        init('t');
        active('t');
        cmdone(DCL);
        cmdone(UNT);
        cmdone(UNL);
    }
    PSleeP
    x = spl7();

    g = &hita[UNIT(dev)];
    if (g->h_opfd <= 0)		/* IMPORTANT you do it first */
	hitainit(UNIT(dev));

    if ((UNIT(dev)) >= NODD || g->h_opfd > HMAXOPEN) {
        u.u_error = EMFILE;              /* too many open */
        goto op_ret;
    }

/* NOTE
 *    WICAT unix passes 0 for read and 2 for write.
	most of unix pass 1 for read, 2 for write and 3 for both
 *    therefore there is no way to know it is opening for mode 1 or 2
 *
 *    So, i have to get the mode from u structure!
 */
    rwflag = uap->fmode;
    /*
     * EXCLUSIVE open is done. no open is allowd
     */
    if ( (g->h_state & HEXCLUSIVE) ||
    /*
     * EXCLUSIVE open failes, when any open is already done
     */
    (((rwflag&(HREAD|HWRITE))==(HREAD|HWRITE)) && g->h_opfd) ||
    /*
     * only one open for WRITE
     */
    ((rwflag&HWRITE) && (g->h_state & HWRITE)) )
    {
        u.u_error = ENXIO;              /* Open fails */
        goto op_ret;
    }
    g->h_opfd += 1;

    g -> h_state  |= (HOPEN | (rwflag & (HREAD|HWRITE)));
    if ( (rwflag & (HREAD|HWRITE))==(HREAD|HWRITE))
        g -> h_state |= HEXCLUSIVE;

op_ret:
    PWakeuP
    splx(x);
    DEBUG(("OPEN end: h.state=%x\n",g->h_state));
}

hitaclose(dev,flag)
dev_t dev;
int flag;
{
    register HITA_t *g;
    int x;

    DEBUG (("HITA CLOSE called, dev = %d/%d opfd=%d, flag=%x, h.state=%x\n",
	 major(dev), UNIT(dev),g->h_opfd,flag,hita[UNIT(dev)].h_state));

    PSleeP
    x = spl7();

    g = &hita[UNIT(dev)];
/*  ! fd is already closed before it gets here */
/*  close is only called at the last close on a unit */
    g->h_state = 0;
    g->h_opfd = 0;
    PWakeuP
    splx(x);
    DEBUG(("CLOSE end: h.state=%x\n",g->h_state));
}

/*
 * ** NOTE **
 * The WICAT UNIX system does not pass the UNIT device number to the
 * interrupt handler as most UNIX systems do.  So we must treat one
 * interrupt as an interrupt from each.
 */
hitaintr()
{
    register int i;

    DEBUG (("HITA INTERRUPT called\n"));

    gpib.h_sr   |= ib -> sr;    /* clears FIN in sr */
    gpib.h_isr1 |= ib -> isr1;	/* clear the interrupt by reading */
    gpib.h_isr2 |= ib -> isr2;

    /* is this my interrupt? */
    if ( gpib.h_isr2 & HR_INTR ) {
	for (i=0; i<NODD; i++) {
	    if (hita[i].h_state & HITASLEEP) {
	        hita[i].h_state &= ~(HITASLEEP);
	        DEBUG (("intr: waking up\n"));
	        wakeup( (caddr_t) (&hita[i]));
	    }
	}
    }
}

/*
 * The Recent versions of UNIX (recent being later than V7)
 * have an additional parameter to physio: physio(..., minphy).
 * Although not used in the Xenix kernel, I've included it
 * here in case this driver is ported to a newer release.
 */
hitaminphys()
{
    /* No minimum i/o */
}

/*
 * Setup wbuf,rbuf and woff,roff according to the given offset
 *   Should be called to verify the current buffer is in sync
 *   at the beginning of read() and write()
 */
int set_offset(dev,offset, rwflag)
dev_t dev;
off_t offset;
char rwflag;
{
    register HITA_t *g = &hita[UNIT(dev)];
    off_t tpbn;
    int toff;

    tpbn = offset/HBLOCKSIZE;
    toff = offset%HBLOCKSIZE;

    /* DEBUG(("set_offset: pbn=%d, off=%d, g->pbn=%d\n",tpbn,toff,g->h_pbn)); */

    if (rwflag == B_READ) {
	if (g->h_pbn != tpbn)
	    if ((g->h_pbn = rfill(dev,0,tpbn,0,0)) < 0) {
		g->h_pbn = -1;
		return(-1);
	    }
	g->h_roff = toff;
    } else { /* WRITE */
	if (g->h_pbn != tpbn) {
	    if (g->h_woff)
		if (wflush(dev,0,g->h_pbn,0,0) < 0) {
		    g->h_pbn = -1;
		    return(-1);
		}
	    g->h_pbn = tpbn;
	}
	g->h_woff = toff;
    }
    return(0);
}

hitarw(dev, offset, count, flags)
dev_t dev;
off_t offset;
unsigned int count;
char flags;
{
    register HITA_t *g = &hita[UNIT(dev)];
    register struct file *fp;
    register struct a {
		int	fdes;
		char	*cbuf;
		unsigned count;
    } *uap;

    DEBUG (("HITArw %s offset=%d, count=%d\n",
        flags & B_READ ? "READ":"WRITE", offset, count));

    if (count == 0)
	return;
    if ((UNIT(dev)) >= NODD)
    {
        u.u_error = ENXIO;
        return;
    }
    uap = (struct a *)u.u_ap;
    GETF(fp, uap->fdes);

    DEBUG (("HITA XFER: %d bytes\n", count));

/*
 *  IMPORTATANT! only one exit from this routine after this point
 */

/*
 * adjust the device buffer and offset
 */
    if (set_offset(dev,offset,flags) < 0)
	goto error_ret;

/*
 * count = how many more to read.
 * offset = offset from the beginning of the file
 */
    if (flags == B_READ) {
	register int t;
	if (count >= (t = HBUFSIZE-(g->h_roff))) {
	    if (t) {
                iomove(&g->h_rbuf[g->h_roff],t,B_READ);   /* empty the buffer */
	        count -= t;
	    }
	    for(; count>=HBUFSIZE; count -= HBUFSIZE) {
		if (rfill(dev,0,++g->h_pbn,0,0) < 0) {
		    g->h_pbn = -1;
		    goto ok_ret;
		}
                iomove(g->h_rbuf,HBUFSIZE,B_READ);
	    }
	    if (rfill(dev,0,++g->h_pbn,0,0) < 0) {
		    g->h_pbn = -1;
		    goto ok_ret;
		}
	}
	if (count) {
            iomove(&g->h_rbuf[g->h_roff],count,B_READ); /* read from rbuf */
	    g->h_roff +=count;
	}
    } else { /* WRITE */
	register int t;
	if (count >= (t = HBUFSIZE-(g->h_woff))) {
	    if (t) {
                iomove(&g->h_wbuf[g->h_woff],t,B_WRITE);   /* fill it up */
	        count -= t;
	    }
	    for(; count>=HBUFSIZE; count -= HBUFSIZE) {
		if (wflush(dev,0,g->h_pbn,0,0) < 0) {
		    g->h_woff = 0;
		    goto error_ret;
		}
		g->h_pbn += 1;
                iomove(g->h_wbuf,HBUFSIZE,B_WRITE);
	    }
	    if (wflush(dev,0,g->h_pbn,0,0) < 0) {
		    g->h_woff = 0;
		    goto error_ret;
	    }
	    g->h_pbn += 1;
	}
	if (count) {
            iomove(&g->h_wbuf[g->h_woff],count,B_WRITE);
	    g->h_woff +=count;
	}
    }
    goto ok_ret;

error_ret:
    u.u_error = EIO;
    g->h_pbn = -1;

ok_ret:
    return;

}

hitaread(dev)
dev_t dev;
{
    int x;
    /* DEBUG (("HITA READ called, dev = %d/%d\n", major(dev), UNIT(dev))); */
    PSleeP
    x = spl7();
    hitarw(dev,(int) u.u_offset, u.u_count, B_READ);
    PWakeuP
    splx (x);
}

hitawrite(dev)
dev_t dev;
{
    int x;
    /* DEBUG(("HITA WRITE: dev=%d/%d, count=%d\n",
		major(dev),UNIT(dev),u.u_count)); */
    PSleeP
    x = spl7();
    hitarw(dev,(int) u.u_offset, u.u_count, B_WRITE);
    PWakeuP
    splx (x);
}

ss(dev,cmd,paddr,lmta,lmla)
    dev_t dev;
    int cmd;
    char lmta,lmla;
    caddr_t paddr;
{
    register int i;
    char ret,t,crc1,crc2;
    char dd[24];
    char s[300];
    register HITA_t *g = &hita[UNIT(dev)];

    DEBUG(("SS(%d,mta=%d,mla=%d) called\n",cmd,lmta,lmla));

    /* init the ib and set it to active taker */
    active('t'); /* active */
    cmdone(UNT);
    cmdone(UNL);
    cmdone(lmla);
    pon();

    /* send the LOCK (hitachi) command as data */
    dataone(CONTROL_OP);
    dataone(SS_OR);
    switch(cmd) {
	case 's':
	    /* DEBUG(("Read secondary status\n")); */
            dataone(READSS);
	    break;
	case 't':
	    /* DEBUG(("Sector status\n")); */
            dataone(SECTOR);
	    break;
	case 'c':
	    /* DEBUG(("Cell status\n")); */
            dataone(CELLSTAT);
	    break;
	case 'o':
	    /* DEBUG(("OD status\n")); */
	    dataone(ODSTAT);
	    break;
	case 'd':
	    /* DEBUG(("Device ID status\n")); */
	    dataone(DEVID);
	    break;
    }
    dataone(~(CONTROL_OP));
    dataone(~(SS_OR));
    switch(cmd) {
	case 's':
            dataone(~(READSS));
	    break;
	case 't':
            dataone(~(SECTOR));
	    break;
	case 'c':
            dataone(~(CELLSTAT));
	    break;
	case 'o':
	    dataone(~(ODSTAT));
	    break;
	case 'd':
	    dataone(~(DEVID));
	    break;
    }
    dd[0] = SS_POP;
    for (i=1; i<18; i++)
	dd[i] = 0;
    crc(dd,&dd[17],&crc1,&crc2);
    for (i=0; i<18; i++)
	dataone(dd[i]);
    dataone(crc1);
    /* send EOI */
    eoi();
    dataone(crc2);

    if (getstat(dev)) {
	switch(cmd) {
	case 's':
	    /* copyout(ss_stat,paddr,HSTAT_L_SS);  just save it */
	    break;
	case 't':
	    copyout(sect_stat,paddr,HSTAT_L_SECT);
	    break;
	case 'c':
	    copyout(cell_stat,paddr,HSTAT_L_CELL);
	    break;
	case 'o':
	    copyout(odd_stat,paddr,HSTAT_L_ODDS);
	    break;
	case 'd':
	    copyout(devid_stat,paddr,HSTAT_L_DEVID);
	    break;
	}
	return(0);
    }
    return(EFAULT);
}

control(dev,cmd,paddr,lmta,lmla)
    dev_t dev;
    int cmd;
    char lmta,lmla;
    caddr_t paddr;
{
    register int i;
    char side = 0;
    int cell;
    char crc1,crc2,dd[24];
    char s[32];

DEBUG (("CONTROL(cmd=%c,paddr=%x,mta=%d,mla=%d) called\n",cmd,paddr,lmta,lmla));

    /* init the ib and set it to active taker */
    active('t'); /* active */

    /* init the hitachi */
    cmdone(UNT);
    cmdone(UNL);
    cmdone(lmla);

    pon();

    /* send the LOCK (hitachi) command as data */
    dataone(CONTROL_OP);
    dataone(CONTROL_OR);
    switch(cmd) {
	case 'm':
	    DEBUG(("Mount/Lock\n"));
	    cell = whibyte( ((int) paddr) );
	    if (cell < 0 || cell > 31) {
		DEBUG(("Invalid cell number (%d)\n",cell));
		return(HERR_BADCELL);
	    }
	    side = wlowbyte( ((int) paddr) );
	    if (side != 'a' && side != 'b') {
		DEBUG(("Invalid side (%x)\n",side));
		return(HERR_BADSIDE);
	    }
            dataone(MOUNT);
	    break;
	case 'd':
	    DEBUG(("Demount/Unlock\n"));
	    cell = whibyte( ((int) paddr) );
	    if (cell < 0 || cell > 31) {
		DEBUG(("Invalid cell number (%d)\n",cell));
		return(HERR_BADCELL);
	    }
	    side = 'a';
            dataone(DEMOUNT);
	    break;
	case 'l':
	    /* DEBUG(("Lock\n")); */
	    dataone(MOUNT);
	    break;
	case 'u':
	    /* DEBUG(("Unlock\n")); */
	    dataone(DEMOUNT);
	    break;
	case 'c':
	    /* DEBUG(("Change\n")); */
	    dataone(CHANGE);
	    break;
	case 'e':
	    /* DEBUG(("Eject\n")); */
	    cell = whibyte( ((int) paddr) );
	    if (cell < 0 || cell > 31) {
		DEBUG(("Invalid cell number (%d)\n",cell));
		return(HERR_BADCELL);
	    }
	    side = wlowbyte( ((int) paddr) );
	    if (side != 'i' && side != 'o') {
		DEBUG(("Invalid side (%x)\n",side));
		return(HERR_BADSIDE);
	    }
	    dataone(EJECT);
	    break;
	case 'r':
	    /* DEBUG(("Rezero\n")); */
	    dataone(REZERO);
	    break;
	case 'n':
	    /* DEBUG(("Noop\n")); */
	    dataone(NOP);
	    break;
    }
    dataone(~(CONTROL_OP));
    dataone(~(CONTROL_OR));
    switch(cmd) {
	case 'm':
            dataone(~(MOUNT));
	    break;
	case 'd':
            dataone(~(DEMOUNT));
	    break;
	case 'l':
            dataone(~(MOUNT));
	    break;
	case 'u':
            dataone(~(DEMOUNT));
	    break;
	case 'c':
	    dataone(~(CHANGE));
	    break;
	case 'e':
	    dataone(~(EJECT));
	    break;
	case 'r':
	    dataone(~(REZERO));
	    break;
	case 'n':
	    dataone(~(NOP));
	    break;
    }
    if (side) {   /* mount or demount, send cell format (2 bytes) */
	dd[0] = 0;
	dd[1] = 0;
	dd[2] = 0;
	switch(side) {
	    case 'a':
	        dd[3] = 0x40;	/* de/mount side a */
		break;
	    case 'b':
	        dd[3] = 0xc0;	/* de/mount side b */
		break;
	    case 'i':
		dd[3] = 0;	/* eject. store only */
		break;
	    case 'o':
		dd[3] = 0x40;	/* eject. eject out and store in */
	}
	dd[4] = cell;
        for (i=5; i<18; i++)
	    dd[i] = 0;
	crc(dd,dd+17,&crc1,&crc2);
        for (i=0; i<18; i++)
	    dataone(dd[i]);
	dataone(crc1);
	eoi();
        dataone(crc2);
    } else {
        for (i=0; i<18; i++)  /* need to put Cell number for mount/demount */
	    dd[i] = 0;
	crc(dd,dd+17,&crc1,&crc2);
        for (i=0; i<18; i++)  /* need to put Cell number for mount/demount */
	    dataone(dd[i]);
        dataone(crc1);
	eoi();
        dataone(crc2);
    }

    return(getstat(dev)); /* FIX it to return a correct error code */
}

/*
 * Seek (in a character device!?)
 *    Hitachi character dev is special.
 *    An extra entry in the file struct. I think its needed it, anyway.
 */
hseek(dev,paddr)
caddr_t paddr;
{
    register HITA_t *g = &hita[UNIT(dev)];
    register struct file *fp;
    register struct a {
		int	fdes;
		int	cmd;
		caddr_t	cmarg;
    } *uap;

/* flush the read buffer whenever SEEK is done */
    g->h_roff = HBLOCKSIZE;

	uap = (struct a *)u.u_ap;
	GETF(fp, uap->fdes);
/*
 * If it is writing and the wbuffer is not empty, it's flushed.
 */
	if (fp->f_flag&FWRITE && g->h_woff)
		if (wflush(dev,0,g->h_pbn,0,0) < 0) {
		    u.u_error = EIO;
		    return;
		}
	g->h_woff = 0;
	fp->f_un.f_offset = (long)paddr * HBLOCKSIZE;
	/* DEBUG(("hseek: offset=%d, paddr=%d\n",
		fp->f_un.f_offset, (long)paddr)); */
}

/*
 * flush the wbuf to the disk (actual i/o happens here)
 *  woff = 0
 *  init wbuf with 0
 *  returns pbn to where the block is written or -1 on error
 */
off_t wflush(dev,fg,pbn,pbcnt,pphys)
dev_t dev;
char fg;
off_t pbn;
int pbcnt;
caddr_t pphys;
{
    register HITA_t *g = &hita[UNIT(dev)];
    register int j,i;
    int cell;
    char ret,crc1,crc2,dd[24];
    long offs;
    int fd,bcnt;

    DEBUG(("wflush: pbn=%d\n",pbn));

    if (!fg) {
	pbcnt = 1;
	pphys = 0;
    }
    /* init the ib and set it to active taker */
    active('t'); /* active */

    /* ready to talk to hitachi */
    /* init the hitachi */
    cmdone(UNT);
    cmdone(UNL);
    cmdone(g->h_mla);
    pon();

    /* send the LOCK (hitachi) command as data
     * Write/alt track
     */
    dataone(CONTROL_OP);
    dataone(0x1);
    dataone(0xe0);
    dataone(~(CONTROL_OP));
    dataone(~(0x1));
    dataone(~(0xe0));
    dd[0] = 0xc0;	/* POP for data transfer with no CRC */
    dd[1] = dd[2] = 0;
    dd[3] = 0xff & (pbn >> 24);		/* pbn */
    dd[4] = 0xff & (pbn >> 16);
    dd[5] = 0xff & (pbn >> 8);
    dd[6] = 0xff & pbn;
    dd[7] = 0xff & (pbcnt >> 8);  	/* block count, hibyte */
    dd[8] = 0xff & pbcnt;		/* lowbyte */
    dd[9]=dd[10]=dd[11]=dd[12] = 0;	/* offset for dual write */
    for (i=13; i<18; i++)
	dd[i] = 0;
    crc(dd,&dd[17],&crc1,&crc2);
    for (i=0; i<18; i++) {		/* send the command */
	dataone(dd[i]);
    }
    dataone(crc1);
    eoi();
    dataone(crc2);

    if (getstat(dev) == 0) {
/* this is done in getstat()
	active('t');
	cmdone(UNT);
	cmdone(UNL);
*/
	cmdone(g->h_mla);
	pon();
	dataone(0xc0);  /* data trx again, must match with POP */
	dataone(0);
	dataone(0);
	dataone(~(0xc0));
	dataone(0xff);
	dataone(0xff);
	if (fg) {
	    for(; pbcnt>=63; pbcnt -= 63, pphys += 32256) {
		if (pbcnt == 63)
			break;
		if (dma(dev,'w',pphys,32256) != 32256) {  /* 63*512 */
		        pbn = -1;
			pbcnt = 0;
			break;
		}
	    }
	    if (pbcnt) {
	        if (dma(dev,'l',pphys,pbcnt*512) != (pbcnt*512))
		    pbn = -1;
	    }
	    if (getstat(dev))
		pbn = -1;
	} else {
	    if ( ((dma(dev,'l',g->h_wbuf,HBUFSIZE)) != HBUFSIZE) ||
               getstat(dev) )
                pbn = -1;
	}
    } else {
	pbn = -1;
    }
/* done in getstat()
    active('t');
    cmdone(UNT);
    cmdone(UNL);
*/
    g->h_woff = 0;
    for (i=0; i<HBUFSIZE; i++)
	g->h_wbuf[i] = 0;
    return(pbn);
}

/*
 * fill the rbuf
 *  set hita.roff = 0
 *  returns pbn from where the data is read or -1 on error
 */
off_t rfill(dev,fg,pbn,pbcnt,pphys)
dev_t dev;
char fg;
off_t pbn;
int pbcnt;
caddr_t pphys;
{
    register int i,j;
    register HITA_t *g = &hita[UNIT(dev)];
    char ret,crc1,crc2,dd[24],con[12];

    DEBUG(("rfill: pbn=%d\n",pbn));

    if (!fg) {
	pbcnt = 1;
	pphys = 0;
    }

    /* init the ib and set it to active taker */
    active('t'); /* active */

    /* init the hitachi */
    cmdone(UNT);
    cmdone(UNL);
    cmdone(g->h_mla);
    pon();

    /* send the LOCK (hitachi) command as data */
    dataone(CONTROL_OP);
    /* Logical Read */
    dataone(RD_OR);
    dataone(RDL_NO);
    dataone( ~(CONTROL_OP));
    dataone( ~(RD_OR));
    dataone( ~(RDL_NO));

    dd[0] = 0xc0;	/* POP for data transfer with no CRC */
    dd[1] = dd[2] = 0;
    dd[3] = 0xff & (pbn >> 24);
    dd[4] = 0xff & (pbn >> 16);
    dd[5] = 0xff & (pbn >> 8);
    dd[6] = 0xff & pbn;
    dd[7] = 0xff & (pbcnt >> 8);
    dd[8] = 0xff & pbcnt;
    dd[9]=dd[10]=dd[11]=dd[12] = 0; 	/* offset */
    for (i=13; i<18; i++)
	dd[i] = 0;
    for (i=0; i<18; i++)
	dataone(dd[i]);
    crc(dd,dd+17,&crc1,&crc2);
    dataone(crc1);
    eoi();
    dataone(crc2);

    if (getstat(dev) == 0) {
	    active('l');
	    cmdone(UNT);
	    cmdone(UNL);
	    cmdone(g->h_mta);
	    listen_sb();
	    /* read and print */
	    gpib.h_isr1 &= ~(HR_END);
	    for (i=0; i<6; i++)  /* through away the header for now */
	        readone(dev);
	    if (fg) { /* fast read */
	        for(; pbcnt>=63; pbcnt -= 63, pphys += 32256) {
		    if (dma(dev,'r',pphys,32256) != 32256) {  /* 63*512 */
		        pbn = -1;
			pbcnt = 0;
			break;
		    }
	        }
	        if (pbcnt) {
	            if (dma(dev,'r',pphys,pbcnt*512) != (pbcnt*512))
		        pbn = -1;
	        }
		if (getstat(dev))
		    pbn = -1;
	    } else {
	        if( ((i=dma(dev,'r',g->h_rbuf,HBUFSIZE)) != HBUFSIZE) ||
    	           getstat(dev) )
		    pbn = -1;
	    }
    } else {
	pbn = -1;
    }
/* done in getstat()
    active('t');
    cmdone(UNT);
    cmdone(UNL);
*/
    if (!fg)
        g->h_roff = 0;
    return(pbn);
}

hflush(dev)
{
    register HITA_t *g = &hita[UNIT(dev)];
    register struct file *fp;
    register struct a {
		int	fdes;
		int	cmd;
		caddr_t	cmarg;
    } *uap;

	uap = (struct a *)u.u_ap;
	GETF(fp, uap->fdes);
	if (fp->f_flag&FWRITE && g->h_woff)
		if (wflush(dev,0,g->h_pbn,0,0) < 0) {
		    u.u_error = EIO;
		    return;
		}
	++g->h_pbn;
	g->h_woff = 0;
}

hsrchunwtn(dev,paddr)
    dev_t dev;
    caddr_t paddr;
{
    register HITA_t *g = &hita[UNIT(dev)];
    register int i,j;
    char ret,crc1,crc2,dd[300];
    HIOCTL_t io;
    off_t pbn;

    active('t'); /* active */
    cmdone(UNT);
    cmdone(UNL);
    cmdone(g->h_mla);
    pon();
    /* Search Unwritten block */
    dataone(CONTROL_OP);
    dataone(RD_OR);
    dataone(RD_SU);
    dataone(~(CONTROL_OP));
    dataone(~(RD_OR));
    dataone(~(RD_SU));

    dd[0] = 0;		/* search unwritten command */
    dd[1] = dd[2] = 0;
    copyin( paddr, &io, sizeof(HIOCTL_t));
    /* DEBUG(("HSRCHUNWTN: pbn=%d, bcnt=%d\n",io.h_start_pbn,io.h_bcnt)); */
    dd[3] = 0xff & (io.h_start_pbn >> 24);
    dd[4] = 0xff & (io.h_start_pbn >> 16);
    dd[5] = 0xff & (io.h_start_pbn >> 8);
    dd[6] = 0xff & io.h_start_pbn;
    dd[7] = 0xff & (io.h_bcnt >> 8);
    dd[8] = 0xff & io.h_bcnt;
    for (i=9; i<18; i++)
        dd[i] = 0;
    for (i=0; i<18; i++)
	    dataone(dd[i]);
    crc(dd,dd+17,&crc1,&crc2);
    dataone(crc1);
    eoi();
    dataone(crc2);

/* get the sector status to find the result */
    if (getstat(dev) == 0x61) {
	    if (sect_stat[4] == 0 && sect_stat[5] == 0) {
		/* DEBUG(("HSRCHUNWTN: failed.\n")); */
		u.u_error = EFAULT;
	    } else {
	        pbn = 0xff & sect_stat[0];
	        pbn = (pbn << 8) + (0xff & sect_stat[1]);
	        pbn = (pbn << 8) + (0xff & sect_stat[2]);
	        pbn = (pbn << 8) + (0xff & sect_stat[3]);
	        /* DEBUG(("HSRCHUNWTN: unwritten pbn = %d\n",pbn)); */
	        io.h_start_pbn = pbn;
	        copyout(&io, paddr, sizeof(HIOCTL_t));
	    }
    } else {
        u.u_error = EFAULT;
    }
    active('t');
    cmdone(UNT);
    cmdone(UNL);
}

hdelete(dev,paddr)
    dev_t dev;
    caddr_t paddr;
{
    register HITA_t *g = &hita[UNIT(dev)];
    register int i,j;
    HIOCTL_t io;
    char ret,crc1,crc2,dd[24];

    active('t'); /* active */
    cmdone(UNT);
    cmdone(UNL);
    cmdone(g->h_mla);
    pon();
    /* Delete */
    dataone(CONTROL_OP);
    dataone(0x11);
    dataone(0x80);
    dataone(~(CONTROL_OP));
    dataone(~(0x11));
    dataone(~(0x80));

    dd[0] = 0;		/* search unwritten command */
    dd[1] = dd[2] = 0;
    copyin( paddr, &io, sizeof(HIOCTL_t)); /* hope bytes are in right order */
    /* DEBUG(("HDELETE: start_pbn=%d, bcnt=%d\n", io.h_start_pbn,io.h_bcnt)); */
    dd[3] = 0xff & (io.h_start_pbn >> 24);
    dd[4] = 0xff & (io.h_start_pbn >> 16);
    dd[5] = 0xff & (io.h_start_pbn >> 8);
    dd[6] = 0xff & io.h_start_pbn;
    dd[7] = 0xff & (io.h_bcnt >> 8);	/* block count */
    dd[8] = 0xff & io.h_bcnt;
    for (i=9; i<18; i++)
        dd[i] = 0;
    for (i=0; i<18; i++)
	    dataone(dd[i]);
    crc(dd,dd+17,&crc1,&crc2);
    dataone(crc1);
    eoi();
    dataone(crc2);

/* get the sector status to find the result */
    if(getstat(dev))
	u.u_error = EIO;
}

/*
 * fast i/o
 *  does not change the standard I/O buffer
 */
hfast(dev,paddr)
    dev_t dev;
    caddr_t paddr;
{
    register off_t fpbn;
    register int fbcnt;
    register caddr_t fpaddr;
    HIOCTL_t io;

    copyin( paddr, &io, sizeof(HIOCTL_t)); /* hope bytes are in right order */
    /* DEBUG(("HFAST: rw=%c,start_pbn=%d, bcnt=%d, phys_addr=%d\n",
	io.h_rw,io.h_start_pbn,io.h_bcnt,io.h_phys_addr)); */

    if ((fbcnt = io.h_bcnt) < 0 || (fpbn = io.h_start_pbn) < 0 ||
        (fpaddr = io.h_phys_addr) < 0) {
	u.u_error = EIO;
	return;
    }

    DEBUG(("HFAST: pbn=%d, bcnt=%d, phys_addr=%d\n",fpbn,fbcnt,fpaddr));

    if (io.h_rw == 'r') {	/* read */
	    if (rfill(dev,1,fpbn,fbcnt,fpaddr) < 0)
		u.u_error = EIO;
    } else {
	    if (wflush(dev,1,fpbn,fbcnt,fpaddr) < 0)
		u.u_error = EIO;
    }
}

hitaioctl(dev, cmd, paddr)
dev_t dev;
int cmd;
caddr_t paddr;
{
    register HITA_t *g;
    int x;
    register struct file *fp;
    register struct a {
		int	fdes;
		int	cmd;
		caddr_t	cmarg;
    } *uap;
    HFMS_t *lfms;

DEBUG(("IOCTL: dev=%d/%d, cmd=%d, addr=%o\n",major(dev),UNIT(dev),cmd));

    PSleeP
    x = spl7();

    uap = (struct a *)u.u_ap;
    GETF(fp, uap->fdes);

    if ((UNIT(dev)) >= NODD)
    {
        u.u_error = ENXIO;
        goto ctl_ret;
    }

    g = &hita[UNIT(dev)];
    switch (cmd)
    {
	case HCLOSEWRITE:
		if (fp->f_flag & HWRITE)
		    g->h_state &= ~(HWRITE|HEXCLUSIVE);
		break;
	case HFAST:
	    	hfast(dev,paddr);
		break;
	case HLGETFMS:
		lfms = (HFMS_t *) &fms[UNIT(dev)][0];
		lfms->fms_state |= FMS_LOCK;
	    	copyout((caddr_t) &fms[UNIT(dev)][0],paddr,sizeof(HFMS_t));
		break;
	case HULFMS:
		lfms = (HFMS_t *) &fms[UNIT(dev)][0];
		lfms->fms_state &= ~FMS_LOCK;
		break;
	case HGETFMS:
	    	copyout((caddr_t) &fms[UNIT(dev)][0],paddr,sizeof(HFMS_t));
		break;
	case HPUTFMS:
		copyin(paddr,(caddr_t) &fms[UNIT(dev)][0],sizeof(HFMS_t));
		break;
	case HDELETE:
	 	hdelete(dev,paddr);
		break;
	case HSRCHUNWTN:
	 	hsrchunwtn(dev,paddr);
		break;
	case HFLUSH:
		hflush(dev);
		break;
	case HSEEK:
		hseek(dev,paddr);
		break;
	case HREZERO:
		u.u_error = control(dev,'r', paddr, g->h_mta, g->h_mla);
		break;
	case HMOUNT:
		u.u_error = control(dev,'m', paddr, g->h_mta, g->h_mla );
		break;
	case HDMOUNT:
		lfms = (HFMS_t *) &fms[UNIT(dev)][0];
		lfms->fms_state = FMS_NOT_MOUNTED;
		lfms->fms_ocnt = 0;
		u.u_error = control(dev,'d', paddr, g->h_mta, g->h_mla );
		break;
	case HLIBOPEN:
		u.u_error = control(dev,'e', paddr, g->h_mta, g->h_mla );
		break;
	case HFLIP:
		u.u_error = control(dev,'c', paddr, g->h_mta, g->h_mla );
		break;
	case HNOOP:
		u.u_error = control(dev,'n', paddr, g->h_mta, g->h_mla );
		break;
	case HSS:
	        copyout(ss_stat,paddr,HSTAT_L_SS);
		u.u_error = ss(dev,'s', paddr, g->h_mta, g->h_mla );
		break;
	case HSECTS:
	        copyout(sect_stat,paddr,HSTAT_L_SECT);
		break;
	case HODDS:
		u.u_error = ss(dev,'o', paddr, g->h_mta, g->h_mla );
		break;
	case HCELLS:
		u.u_error = ss(dev,'c', paddr, g->h_mta, g->h_mla );
		break;
	case HDEVID:
		u.u_error = ss(dev,'d', paddr, g->h_mta, g->h_mla );
		break;
        default:
            u.u_error = EINVAL;
	    break;
    }
ctl_ret:
    PWakeuP
    splx(x);
}
//E*O*F hita.c
echo x - hita.h
cat << '//E*O*F hita.h' > hita.h
/*	hita.h	1.0	85/04/23	*/
	
/* Hitachi optical disk driver ioctrl commands */
/* Author:  Jungbo Yang  4/23/85  */

#define HREZERO 	1	/* home the optical head */
#define HMOUNT		2	/* mount/lock */
#define HDMOUNT		3	/* demount/unlock */
#define HLIBOPEN	4	/* eject */
#define HFLIP		5	/* change */
#define HNOOP		6	/* noop */
#define HSS		7	/* read second status */
#define HODDS		8	/* read OD status */
#define HCELLS		9	/* read Cell status */
#define HDEVID		10	/* read Device ID */
#define HSEEK		11	/* seek */
#define HFLUSH		12	/* flush */
#define HSRCHUNWTN	13	/* search unwritten blocks(62) */
#define HDELETE		14	/* delete */
#define HGETFMS		15	/* get FM Structure */
#define HPUTFMS		16	/* put FM Structure */
#define HFAST		17	/* fast I/O */
#define HSECTS		18	/* sector status */
#define HEXCL		19	/* exclusive use request */
#define HCLOSEWRITE	20	/* close write ********* */
#define HLGETFMS	21	/* getfms with locking fms */
#define HULFMS		22	/* unlock fms */
/* NOTE
 * this is because close() is called only at the last close call
 * no way to know when the write operation did close()
 *   close() is redefined to do this automatically
 */
int close();
static int (*hclose)() = close;
#define close(x)	{ \
	ioctl(x,HCLOSEWRITE,0); \
	(*hclose)(x); \
}

#define gethk_cell(x,y) hk_cell(1,x,y)
#define puthk_cell(x,y) hk_cell(0,x,y)

#define RD_OR 0x2		/* read commands */
#define RD_DCI 0x1		/* bit location. OR it */
#define RD_DUAL 0x40 		/* bit location */
#define RDL_NO 0x10 		/* logical read */
#define RDP_NO 0x20 		/* physical read */
#define RD_SU 0x40  		/* search unwritten */
#define RD_SD 0x80  		/* skip deleted blocks */
/*
 * status data length
 */
#define HSTAT_L_SS	24
#define HSTAT_L_DEVID	6
#define HSTAT_L_ODDS	24
#define HSTAT_L_CELL	256
#define HSTAT_L_SECT	254

/* error codes */
#define HERR_BADCELL	1
#define HERR_BADSIDE	2

/* from vax750 */
#define	GETF(fp, fd) { \
	if ((unsigned)(fd) >= NOFILE || ((fp) = u.u_ofile[fd]) == NULL) { \
		u.u_error = EBADF; \
		return; \
	} \
}

#define HBLOCKSIZE 512   /* hitachi block size */
#define HBUFSIZE 512   /* kernel buffer size */
#define HMAXOPEN 20    /* max number of open per device */
#define HITAPRI PRIBIO+1 /* priority for sleep() just slower than fs */

typedef struct
{
    short   h_state;              /* drive status */
				/*  OPEN, WRITE */
    short   h_opfd;		/* number of open */
    char    h_mta;                /* IEEE488 MTA */
    char    h_mla;                /* IEEE488 MLA */
    short  h_opop;      /* operation options      */
                      /*    Dual write          */
                      /*    Delete check inhibit */
    off_t   h_pbn;      /* current pbn            */
                      /*    location of the next read or write */
    short  h_woff;      /* write buffer pointer   */
    short  h_roff;      /* read buffer pointer    */
    char   h_rbuf[HBUFSIZE]; /* read buffer            */
    char   h_wbuf[HBUFSIZE]; /* write buffer           */
} HITA_t;

typedef struct {
    char    h_isr1;               /* interrupt status */
    char    h_isr2;               /* interrupt status */
    char    h_sr;                 /* interface board status */
} HGPIB_t;

typedef struct {
	char    h_rw;
	off_t   h_start_pbn;
	short   h_bcnt;
	caddr_t h_phys_addr;
} HIOCTL_t;
//E*O*F hita.h
echo x - hitachi.h
cat << '//E*O*F hitachi.h' > hitachi.h
/*  hitachi.h  4/23/85	*/

/*
	Test program for GPIB 796 and Hitachi (OFC)
	Sends LOCK and UNLOCK commands to OFC
 */
/* Author:  Jungbo Yang  4/23/85  */

/* read only register and write only registers */

#define dir     cdor
#define isr1    imr1
#define isr2    imr2
#define spsr    spmr
#define adsr    admr
#define cptr    auxmr
#define adr0    adr
#define adr1    eosr
#define sr      cr0

/* Control masks for hidden registers (auxmr) */

#define LISTEN  0x13
#define SB      0x10
#define ICR     0040
#define PPR     0140
#define AUXRA   0200
#define AUXRB   0240
#define AUXRE   0300

/* Hardware register bit definitions */
/*      Name            Bit(s)          register       */

#define HR_DI           (1<<0)          /* isr1         */
#define HR_DO           (1<<1)          /* isr1         */
#define HR_END          (1<<4)          /* isr1         */
#define HR_DOIE         (1<<1)          /* imr1         */
#define HR_ENDIE        (1<<4)          /* imr1         */
#define HR_CO           (1<<3)          /* isr2         */
#define HR_SRQI         (1<<6)          /* isr2         */
#define HR_INTR         (1<<7)          /* isr2         */
#define HR_COIE         (1<<3)          /* imr2         */
#define HR_DMAI         (1<<4)          /* imr2         */
#define HR_DMAO         (1<<5)          /* imr2         */
#define HR_SRQIE        (1<<6)          /* imr2         */
#define HR_ADM0         (1<<0)          /* admr         */
#define HR_TRM0         (1<<4)          /* admr         */
#define HR_TRM1         (1<<5)          /* admr         */
#define HR_TON          (1<<7)          /* admr         */
#define HR_LON          (1<<6)          /* admr         */
#define HR_DL           (1<<5)          /* adr          */
#define HR_DT           (1<<6)          /* adr          */
#define HR_ARS          (1<<7)          /* adr          */
#define HR_GO           (1<<0)          /* cr0          */
#define HR_DMAE         (1<<1)          /* cr0          */
#define HR_LMR          (1<<2)          /* cr0          */
#define HR_FINIE        (1<<3)          /* cr0          */
#define HR_MEMRD        (1<<4)          /* cr0          */
#define HR_ECC          (1<<6)          /* cr0          */
#define HR_NSRQ         (1<<0)          /* sr , this bit not used! */
#define HR_NFIN         (1<<5)          /* sr           */
#define HR_NGPIBIR      (1<<6)          /* sr           */
#define HR_DONE         (1<<7)          /* sr           */
#define HR_CBRQ         (1<<5)          /* cr1          */
#define HR_SC           (1<<3)          /* cr1          */
#define HR_MIE          (1<<7)          /* cr1          */
#define HR_BURST	(1<<6)		/* cr1 */
#define HR_BTIME	(7)		/* cr1 */
#define HR_HLDA         (1<<0)          /* auxra        */
#define HR_HLDE         (1<<1)          /* auxra        */
#define HR_INV          (1<<3)          /* auxrb        */
#define HR_PPU          (1<<4)          /* ppr          */
#define HR_CIC		(1<<7)		/* adsr */
#define HR_TA		(1<<1)		/* adsr */
#define HR_LA		(1<<2)		/* adsr */

/* 7210 Auxiliary Commands */

#define AUX_PON         000     /* Immediate Execute pon        */
#define AUX_FH          003     /* Finish handshake             */
#define AUX_SEOI        006     /* Send EOI                     */
#define AUX_TCA         021     /* Take Control Asynchronously  */
#define AUX_GTS         020     /* Go To Standby                */
#define AUX_EPP         035     /* Execute Parallel Poll        */
#define AUX_SIFC        036     /* Set IFC                      */
#define AUX_CIFC        026     /* Clear IFC                    */
#define AUX_SREN        037     /* Set REN                      */
#define AUX_CREN        027     /* Clear REN                    */

/* ieep 488 codes */
#define UNT '_'
#define UNL '?'
#define SPE '\030'
#define SPD '\031'
#define DCL '\024'	/* device clear */

/* define talker and listers addresses */
#define MTA 0100
#define MLA 040

/* define hitachi commands */
/* control operations */
#define CONTROL_OP 0x89
#define CONTROL_OR 0x3
#define REZERO 0x0		/* rezero */
#define MOUNT 0x1		/* mount/lock */
#define DEMOUNT 0x2		/* demount/unlock */
#define EJECT 0x3		/* eject */
#define CHANGE 0x4		/* change */
#define NOP 0xf			/* nop */
#define CRC1 0x5d   		 /* default. all zero data */
#define CRC2 0x2a
/* send status operations */
#define SS_OP 0x49		/* no CRC? . use CONTROL_SS */
#define SS_OR 0x4
#define READSS 0x0
#define SECTOR 0x1
#define ODSTAT 0xc
#define CELLSTAT 0x8
#define DEVID 0x9
#define SS_POP 0x69		/** no CRC */

/*
 * For converting addresses (long) 
 */
#define lobyte(x)        ((long)(x) & 0377)
#define midbyte(x)      (((long)(x) >> 8) & 0377)
#define hibyte(x)       (((long)(x) >> 16) & 0377)
//E*O*F hitachi.h
echo x - opt_fs.h
cat << '//E*O*F opt_fs.h' > opt_fs.h
/* opt_fs.h  4/23/85	*/

/* 	Hitachi Optical Disk Filesystem     */
/*      Author:  Jungbo Yang  7/8/85        */

/* error codes */
#define E_OK		0	/* no error. successful return */
#define E_VOLUME	-1	/* invalid volume */
#define E_OPEN 		-2	/* open error */
#define E_READ		-3	/* read error */
#define E_BADMODE	-4	/* bad open mode */
#define E_NOT_MOUNTED	-5	/* volume not mounted */
#define E_FIXFMS	-6	/* cannot fix FMS */
#define E_FDESC		-7	/* no more free desc block */
#define E_FREE		-8	/* no more free file block */
#define E_WR_DESC	-9	/* write_desc error */
#define E_FREAD		-10	/* fast read error */
#define E_FWRITE	-11	/* fast write error */
#define E_NOVID		-12	/* no more volume block */
#define E_SEEK		-13	/* hseek error */
#define E_SRCHUNWTN	-14	/* srchunwtn error */
#define E_WRITE		-15	/* write error */
#define E_BADFMS	-16	/* bad FMS */
#define E_IOCTL		-17	/* ioctl error return */
#define E_BADPBN	-18	/* bad pbn */
#define E_FULL		-19	/* disk full */
#define E_BADVD		-20	/* bad volume descriptor */
#define E_BADFD		-21	/* bad file descriptor */
#define E_FDMANY	-22	/* too many open files */
#define E_BUSY		-23	/* resource busy */
#define E_EMPTY		-24	/* fs empty */
#define E_BADDRV 	-25	/* bad drive number */
#define E_BADVID	-26	/* bad volume id */
#define E_BADVTYPE	-27	/* bad volume type */
#define E_BADFS		-28	/* bad fs layout */
#define E_BADSIDE	-29	/* bad fs layout */
#define E_BADVLAB	-30	/* bad volume label */
#define E_OUTSWAP	-31	/* out swap error */
#define E_NOT_IN_JUKEBOX	-32	/* volume not found in jukebox */
#define E_DOOR		-33	/* open/close door operation error */
#define E_LOAD		-34	/* platter loading error */
#define E_UNLOAD	-35	/* platter unloading error */
#define E_HK_FILE	-36	/* housekeeping file error */
#define E_FMHK		-37	/* housekeeping file error */
#define E_HK_CELL	-38	/* bad cell entry in hk file */
#define E_DOOR_OPEN	-39	/* jukebox door is open */
#define E_LAST		-39	/* last error code */

/* FMS */
#define VIDLEN 16		/* size of volume id in bytes */
#define FNAMELEN 16
#define VNAMELEN 32
#define FMS_VLEN	(2*62)	/* number of volume block */
#define FMS_NCELL	32	/* number of cells in jukebox */

#define FMS_OK		0	/* fms_dstat, fms_fstat */
#define FMS_DIRTY	1	/* fms_dstat, fms_fstat */
#define FMS_NOT_MOUNTED	0	/* fms_state */
#define FMS_MOUNTED	1	/* fms_state */
#define FMS_WRITE	2	/* fms_state */
#define FMS_EXCLUSIVE	4	/* fms_state */
#define FMS_OPENED	8	/* fms_state */
#define FMS_EMPTY	0x10	/* fms_state */
#define FMS_BADISK	0x20	/* fms_state */
#define FMS_LOCK	0x8000	/* fms_state */
#define FMS_0DRIVE	"/dev/hitaa"	/* drive 0 */
#define FMS_1DRIVE	"/dev/hitab"	/* drive 1 */

#define TYPE_IMAGE	0	/* fs type */
#define TYPE_BACKUP	1	/* fs type */
#define SIDE_A		0	/* platter side a */
#define SIDE_B		1	/* platter side b */

#define FMHK_NAME "fmhousekeeping"
#define NCELL 64	/* number of platters in jukebox */
extern int hk_file(),hk_cell();
#define lockhk_file() hk_file(1)
#define relhk_file() hk_file(0)
#define gethk_cell(a,b) hk_cell(1,a,b)
#define puthk_cell(a,b) hk_cell(0,a,b)

#include <time.h>

typedef struct {         /* FM Structure definition */
    short  fms_state;     /* file system status */
    char fms_vol[VIDLEN];       /* volume ID */
    long fms_dfree;      /* next free PBN in descriptor area */
    short fms_dstat;      /* descriptor area status        */
                         /* status values, such as        */
                         /*        OK    - normal         */
                         /*        DIRTY - d_free is wrong */
    long fms_ffree;      /* next free PBN in file are */
    short fms_fstat;      /* file area status */
                         /* status values, such as        */
                         /*        OK    - normal         */
                         /*        DIRTY - f_free is wrong */
    long fms_vstart;	/* volume area */
    long fms_dstart;	/* desc area */
    long fms_fstart;	/* file area */
    long fms_sstart;	/* spare area */
    short fms_ocnt;	/* open proc count. should be 0 when fms_state
			   is not FMS_OPENED */
    short fms_cell;	/* keeps jukebox cell number here */
} HFMS_t;

/* File descriptor structure */
typedef struct {
	char 	f_name[16];	/* file verification area */
	short	f_version;	/* format version number */
	struct tm f_time;	/* time */
	long	f_type;		/* file type */
	long	f_pbn;		/* starting physical block number */
	long	f_dpbn;		/* desc physical block number */
	long	f_bcnt;		/* length of file in bytes */
	char	f_acc[16];	/* account number (null terminated) */
	char	f_ff[16];	/* file folder number (null terminated) */
	long	f_doc;		/* document number */
	long	f_page;		/* page number */
	long	f_image;	/* image number */
	long	f_doctype;	/* document type */
	long	f_xpos; 	/* x position */
	long	f_ypos; 	/* y position */
	long	f_dispmode;	/* display mode */
	long	f_itype;	/* image type */
} Opt_file_t;

/* Volume block structure */
typedef struct {
	char	v_name[32];	/* volume label verification region */
	short	v_version;	/* label version number */
	char	v_id[VIDLEN];	/* volume ID */
	short	v_vtype;	/* volume type */
	short	v_side;		/* platter side */
	struct tm v_time;	/* time */
	long	v_dfirst;	/* first desc block */
	long	v_ffirst;	/* first data block */
	long	v_sfirst;	/* first spare block */
	char	v_project[32];	/* project name */
	char	v_institution[128];	/* institution description */
} Opt_volume_t;

#define FMS_CLOSE	0
#define FMS_BWRITE	1
#define FMS_BREAD	2
#define FMS_FWRITE	4
#define FMS_FREAD	8
#define FMS_EWRITE	0x10
#define FMS_EREAD	0x20
#define FMS_MAX_FILE	8	/* max # of file can be opened at once */
#define FMS_NDRIVE	2	/* number of optical drives on line */
#define FMS_VD	0x1234		/* magic number for volume desc */
#define FMS_VOPEN 0xf000	/* _vstat volume open bit */

/* globals */

/* optical file struct */
typedef struct {
    int  start_stat;
    int  opt_fd;
    long start_dpbn;
    long start_fpbn;
    long start_cnt;
    Opt_file_t desc;
} _Opt_t;

/* optical volume struct */
typedef struct {
    int vs_stat;
    int vs_fd;
    long vs_vol[VIDLEN];
    long vs_vstart;	/* volume area */
    long vs_dstart;	/* desc area */
    long vs_fstart;	/* file area */
    long vs_sstart;	/* spare area */
    _Opt_t vs_opt[FMS_MAX_FILE];
} _Vol_t;


//E*O*F opt_fs.h
echo The following shows errors detected by using 'wc':
cat <<! >/tmp/sar$$
    1606    4914   36917 hita.c
     105     514    3312 hita.h
     128     605    4759 hitachi.h
     178    1006    6317 opt_fs.h
    2017    7039   51305 total
!
wc hita.c hita.h hitachi.h opt_fs.h | diff -b /tmp/sar$$ -
rm /tmp/sar$$