File:  [Power 6/32 Unix Tahoe 4.2BSD] / cci / sys / vba / dr.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Sun Jul 28 12:30:09 2019 UTC (6 years, 11 months ago) by root
Branches: bsd, MAIN
CVS tags: v121, HEAD
Power 6/32 Unix version 1.21

#include "dr.h"
#if NDR > 0

/*      DRV11-W DMA interface driver.
 */

#include "../machine/mtpr.h"
#include "../machine/pte.h"
#include "../h/param.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/map.h"
#include "../h/ioctl.h"
#include "../h/buf.h"
#include "../h/vm.h"
#include "../h/uio.h"

#include "../vba/vbavar.h"
#include "../h/drreg.h"

#define YES 1
#define NO  0

struct  vba_device  *drinfo[NDR];
struct  dr_aux dr_aux[NDR];

caddr_t vtoph();
unsigned drminphys();
int     drprobe(), drintr(), drattach(), drtime(), drrwtimo();
int     drstrategy();
extern struct  vba_device  *drinfo[];
static long drstd[] = { 0 };
struct  vba_driver drdriver =
	{ drprobe, 0, drattach, 0, drstd, "rs", drinfo };
extern long hz;

#define RSUNIT(dev) (minor(dev) & 7)
#define SPL_UP spl5

/* -------- Per-unit data -------- */

extern struct dr_aux dr_aux[];

struct rs_data {
    struct buf  rs_buf;
    int         rs_ubainfo;
    short       rs_debug;
    short       rs_busy;
    short       rs_tout; 
    short       rs_uid;
    short       rs_isopen;
    short       rs_func;
} rs_data[NDR];


#ifdef DR_DEBUG
long DR11 = 0;
#endif

drprobe(reg)
caddr_t reg;
{
    register int br, cvec;
    register struct rsdevice *rsaddr = (struct rsdevice *) reg;

#ifdef lint
    br = 0; cvec = br; br = cvec;
#endif lint

    if (badaddr(reg,1)) {
	printf("\ndrprobe: DR11 at %lx not found",reg);
	return(0);
    }
    return (sizeof (struct rsdevice));		/* DR11 exist */
}

/* ARGSUSED */
drattach(ui)
struct vba_device *ui;
{
    register struct dr_aux *rsd;
    register struct rsdevice *dr;
    register ushort status;

    rsd = &dr_aux[ui->ui_unit];
    rsd->dr_flags = DR_PRES;		/* This dr11 is present */
    rsd->dr_addr = (struct rsdevice *)ui->ui_addr; /* Save addr of this dr11 */
#ifdef DR_DEBUG
    printf("\ndrattach: Set interrupt vector %lx",DRINTV + ui->ui_unit);
#endif
    dr = rsd->dr_addr;
    dr->dr_intvect = DRINTV + ui->ui_unit;   /* Set interrupt vector */
#ifdef DR_DEBUG
    printf("\ndrattach: Init board and device");
#endif
    dr->dr_cstat = MCLR;		/* init board and device */
    status = dr->dr_cstat;		/* read initial status */
#ifdef DR_DEBUG
    printf("\ndrattach: Initial status %lx\n",status & 0xffff);
#endif
    rsd->dr_istat = 0;
    rsd->dr_bycnt = 0;
    rsd->dr_cmd = 0;
    rsd->currenttimo = 0;
    return;
}

dropen (dev, flag)
dev_t dev;
int flag;
{
    register int unit = RSUNIT(dev);
    register struct rsdevice *dr;
    register struct dr_aux *rsd;

    if ((drinfo[unit] == 0) || (!drinfo[unit]->ui_alive))
	return ENXIO;

    dr = RSADDR(unit);
    rsd = &dr_aux[unit];
    if (rsd->dr_flags & DR_OPEN) {
#ifdef DR_DEBUG
	printf("\ndropen: dr11 unit %ld already open",unit);
#endif
	return ENXIO;      		/* DR11 already open */
    }
    rsd->dr_flags |= DR_OPEN;		/* Mark it OPEN */
    rsd->dr_istat = 0;			/* Clear status of previous interrupt */
    rsd->rtimoticks = hz;		/* Set read no stall timout to 1 sec */
    rsd->wtimoticks = hz*60;		/* Set write no stall timout to 1 min */
    dr->dr_cstat = DR_ZERO;		/* Clear function & latches */
    dr->dr_pulse = (RDMA | RATN);	/* clear leftover attn & e-o-r flags */
    drtimo(dev);			/* start the self kicker */
    return 0;
}

drclose (dev)
dev_t dev;
{
    register int unit = RSUNIT(dev);
    register struct dr_aux *dra;
    register struct rsdevice *rs;
    register short s;

    dra = &dr_aux[unit];
    if (!(dra->dr_flags & DR_OPEN)) {
#ifdef DR_DEBUG
	printf("\ndrclose: DR11 device %ld not open",unit);
#endif
	return;
    }
    dra->dr_flags &= ~(DR_OPEN|DR_ACTV);
    rs = dra->dr_addr;
    s=SPL_UP();
    rs->dr_cstat = DR_ZERO;
    if (dra->dr_buf.b_flags & B_BUSY) {
    	dra->dr_buf.b_flags &= ~B_BUSY;
	wakeup(&dra->dr_buf.b_flags);
    }
    splx(s);
    return;
}


/*	drread() works exactly like drwrite() except that the
	B_READ flag is used when physio() is called
*/
drread (dev, uio)
dev_t dev;
struct uio *uio;
{	register struct dr_aux *dra;
	register struct buf *bp;
	register long spl, err;
    	register int unit = RSUNIT(dev);

    if (   uio->uio_iov->iov_len <= 0		/* Negative count */
	|| uio->uio_iov->iov_len & 1		/* odd count */
	|| (int)uio->uio_iov->iov_base & 1	/* odd destination address */
       )
	return EINVAL;

#ifdef DR_DEBUG
    if (DR11 & 8) {
	printf("\ndrread: (len:%ld)(base:%lx)",
    		uio->uio_iov->iov_len,(int)uio->uio_iov->iov_base); 
    }
#endif

    dra = &dr_aux[RSUNIT(dev)];
    dra->dr_op = DR_READ;
    bp =  &dra->dr_buf;
    bp->b_resid = 0;
    if (dra->dr_flags & DR_NORSTALL) {
	/* We are in no stall mode, start the timer, raise IPL so nothing
	   can stop us once the timer's running */
	spl = SPL_UP();
	timeout(drrwtimo,(caddr_t)((dra->currenttimo<<8) | unit),
				dra->rtimoticks);
    	err = physio (drstrategy, bp, dev,B_READ, drminphys, uio);
	splx(spl);
	if (err)
		return(err);
	dra->currenttimo++;		/* Update current timeout number */
	/* Did we timeout */
	if (dra->dr_flags & DR_TMDM) {
		dra->dr_flags &= ~DR_TMDM;	/* Clear timeout flag */
		u.u_error = 0;		/* Made the error ourself, ignore it */
	}
    }
    else {
    	return physio (drstrategy, bp, dev,B_READ, drminphys, uio);
    }
}

drwrite (dev, uio)
dev_t dev;
struct uio *uio;
{	register struct dr_aux *dra;
	register struct buf *bp;
    	register int unit = RSUNIT(dev);
	register long spl, err;

    if (   uio->uio_iov->iov_len <= 0
	|| uio->uio_iov->iov_len & 1
	|| (int)uio->uio_iov->iov_base & 1
       )
	return EINVAL;

#ifdef DR_DEBUG
    if (DR11 & 4) {
	printf("\ndrwrite: (len:%ld)(base:%lx)",
    		uio->uio_iov->iov_len,(int)uio->uio_iov->iov_base); 
    }
#endif

    dra = &dr_aux[RSUNIT(dev)];
    dra->dr_op = DR_WRITE;
    bp =  &dra->dr_buf;
    bp->b_resid = 0;
    if (dra->dr_flags & DR_NOWSTALL) {
	/* We are in no stall mode, start the timer, raise IPL so nothing
	   can stop us once the timer's running */
	spl = SPL_UP();
	timeout(drrwtimo,(caddr_t)((dra->currenttimo<<8) | unit),
				dra->wtimoticks);
    	err = physio (drstrategy, bp, dev,B_WRITE, drminphys, uio);
	splx(spl);
	if (err)
		return(err);
	dra->currenttimo++;		/* Update current timeout number */
	/* Did we timeout */
	if (dra->dr_flags & DR_TMDM) {
		dra->dr_flags &= ~DR_TMDM;	/* Clear timeout flag */
		u.u_error = 0;		/* Made the error ourself, ignore it */
	}
    }
    else {
    	return physio (drstrategy, bp, dev,B_WRITE, drminphys, uio);
    }
}

/*  Routine used by calling program to issue commands to dr11 driver and 
    through it to the device.
    It is also used to read status from the device and driver and to wait 
    for attention interrupts.
    Status is returned in an 8 elements unsigned short integer array, the 
    first two elements of the array are also used to pass arguments to 
    drioctl() if required.
    The function bits to be written to the dr11 are included in the cmd
    argument. Even if they are not being written to the dr11 in a particular
    drioctl() call, they will update the copy of cmd that is stored in the
    driver. When drstrategy() is called, this updated copy is used if a 
    deferred function bit write has been specified. The "side effect" of
    calls to the drioctl() requires that the last call prior to a read or
    write has an appropriate copy of the function bits in cmd if they are
    to be used in drstrategy().
    When used as command value, the contents of data[0] is the command
    parameter.
*/

drioctl(dev, cmd, data, flag)
dev_t dev;
int cmd;
long *data;
int flag;
{
    register int unit = RSUNIT(dev);
    register struct dr_aux *dra;
    register struct rsdevice *rsaddr = RSADDR(unit);
    struct dr11io dio;
    ushort s, errcode, status;
    long temp;

#ifdef DR_DEBUG
    if (DR11 & 0x10)
    printf("\ndrioctl: (dev:%lx)(cmd:%lx)(data:%lx)(data[0]:%lx)",
	dev,cmd,data,data[0]);
#endif

    dra = &dr_aux[unit];
    dra->dr_cmd = 0;		/* Fresh copy; clear all previous flags */

    switch (cmd) {

    case DRWAIT:
	/* Wait for attention interrupt */
#ifdef DR_DEBUG
	printf("\ndrioctl: wait for attention interrupt");
#endif
	s = SPL_UP();
	/* If the attention flag in dr_flags is set, it probably means that
	   an attention has arrived by the time a previous DMA end-of-range
	   interrupt was serviced. If ATRX is set, we will return with out
	   sleeping, since we have received an attention since the last call
	   to wait on attention.
	   This may not be appropriate for some applications.
	*/
	if (!(dra->dr_flags & DR_ATRX)) {
		dra->dr_flags |= DR_ATWT;	/* Set waiting flag */
		rsaddr->dr_pulse = IENB;	/* Enable interrupt; use pulse
						   reg. so function bits are
						   not changed */
		sleep((caddr_t)&dra->dr_cmd,DRPRI);
	}
	splx(s);
	break;

    case DRPIOW:
	/* Write to p-i/o register */
	rsaddr->dr_data = data[0];
	break;

    case DRPACL:
	/* Send pulse to device */
	rsaddr->dr_pulse = FCN2;
	break;

    case DRDACL:
	/* Defer alco pulse until go */
	dra->dr_cmd |= DR_DACL;
	break;

    case DRPCYL:
	/* Set cycle with next go */
	dra->dr_cmd |= DR_PCYL;
	break;

    case DRDFCN:
	/* Do not update function bits until next go issued */
	dra->dr_cmd |= DR_DFCN;
	break;

    case DRRATN:
	/* Reset attention flag -- use with extreme caution */
	rsaddr->dr_pulse = RATN;
	break;

    case DRRDMA:
	/* Reset DMA e-o-r flag -- should never used */
	rsaddr->dr_pulse = RDMA;
	break;

    case DRSFCN:
	/* Set function bits */
	temp = data[0] & DR_FMSK;
	rsaddr->dr_cstat = temp;	/* Write to control register */
	/* This has a very important side effect -- It clears the interrupt
	   enable flag. That is fine for this driver, but if it is desired
	   to leave interrupt enable at all times, it will be necessary to
	   to read the status register first to get IENB, or carry a software
	   flag that indicates whether interrupts are set, and or this into 
	   the controll register value being written.
	*/
	break;

    case DRRPER:
	/* Clear parity flag */
	rsaddr->dr_pulse = RPER;
	break;

    case DRSETRSTALL:
	/* Set read stall mode. */
	dra->dr_flags &= (~DR_NORSTALL);
	break;

    case DRSETNORSTALL:
	/* Set no stall read  mode. */
	dra->dr_flags |= DR_NORSTALL;
	break;

    case DRGETRSTALL:
	/* Returns true if in read stall mode. */
	data[0]  = (dra->dr_flags & DR_NORSTALL)? 0 : 1;
	break;

    case DRSETRTIMEOUT:
	/* Set the number of ticks before a no stall read times out.
	   The argument is given in tenths of a second. */
	if (data[0] < 1) {
		u.u_error = EINVAL;
		temp = 1;
	}
	dra->rtimoticks = (data[0] * hz )/10;
	break;

    case DRGETRTIMEOUT:
	/* Returns the number of tenths of seconds before
	   a no stall read times out. */
	/* The argument is given in tenths of a second. */
	data[0] = ((dra->rtimoticks)*10)/hz;
	break;

    case DRSETWSTALL:
	/* Set write stall mode. */
	dra->dr_flags &= (~DR_NOWSTALL);
	break;

    case DRSETNOWSTALL:
	/* Set write stall mode. */
	dra->dr_flags |= DR_NOWSTALL;
	break;

    case DRGETWSTALL:
	/* Returns true if in write stall mode. */
	data[0] = (dra->dr_flags & DR_NOWSTALL)? 0 : 1;
	break;

    case DRSETWTIMEOUT:
	/* Set the number of ticks before a no stall write times out.
	   The argument is given in tenths of a second. */
	if (data[0] < 1) {
		u.u_error = EINVAL;
		temp = 1;
	}
	dra->wtimoticks = (data[0] * hz )/10;
	break;

    case DRGETWTIMEOUT:
	/* Returns the number of tenths of seconds before
	   a no stall write times out. */
	/* The argument is given in tenths of a second. */
	data[0] = ((dra->wtimoticks)*10)/hz;
	break;

    case DRWRITEREADY:
	/* Returns a value of 1 if the device can accept
	   data, 0 otherwise. Internally this is the
	   DR11-W STAT A bit. */

	data[0] = (rsaddr->dr_cstat & STTA)? 1 : 0;
	break;

    case DRREADREADY:
	/* Returns a value of 1 if the device has data
	   for host to be read, 0 otherwise. Internally
	   this is the DR11-W STAT B bit. */
	data[0] = (rsaddr->dr_cstat & STTB)? 1 : 0;
	break;

    case DRBUSY:
	/* Returns a value of 1 if the device is busy,
	   0 otherwise. Internally this is the DR11-W
	   STAT C bit, but there is a bug in the Omega 500/FIFO interface
	   board that it cannot drive this signal low for certain DR11-W
	   ctlr such as the Ikon. We use the REDY signal of the CSR on
	   the Ikon DR11-W instead. 

	data[0] = (rsaddr->dr_cstat & STTC)? 1 : 0;
	*/

	data[0] = ((rsaddr->dr_cstat & REDY)? 0 : 1);
	break;

    case DRRESET:
	rsaddr->dr_pulse = (MCLR|RDMA|RATN|RPER);/* Reset DMA ATN RPER flag */
	DELAY(0x1f000);
	while (!(rsaddr->dr_cstat & REDY)) {
		sleep((caddr_t)dra, DRPRI);	/* Wakeup by drtimo() */
	}
    	dra->dr_istat = 0;
    	dra->dr_cmd = 0;
    	dra->currenttimo = 0;
	break;

    default:
	printf("\ndrioctl: Invalid ioctl cmd : %lx",cmd);
	return EINVAL;
    }

#ifdef DR_DEBUG
    if (DR11 & 0x10)
    	printf("**** (data[0]:%lx)",data[0]);
#endif
    return 0;
}

/* Reset state on Unibus reset */
drreset(uban)
int uban;
{
    register int i;
    register struct vba_device *ui;
    register struct dr_aux *dra;

    for (i = 0; i < NDR; i++, dra++) {
	if (   (ui = drinfo[i]) == 0
	    || !ui->ui_alive
	    || ui->ui_vbanum != uban
	   )
	    continue;
	printf("\ndrreset: %ld",i);
	/* Do something; reset board */
    }
    return;
}

/*
 * An interrupt is caused either by an error,
 * base address overflow, or transfer complete
 */
drintr (unit)
register long unit;
{
    register struct dr_aux *dra = &dr_aux[unit];
    register struct rsdevice *rsaddr = RSADDR(unit);
    register struct buf *bp;
    register short status, csrtmp;

    status = rsaddr->dr_cstat & 0xffff;		/* get board status register */
    dra->dr_istat = status;

#ifdef DR_DEBUG
    if (DR11 & 2)
    	printf("\ndrintr: dr11 status : %lx",status & 0xffff);
#endif

    if (dra->dr_flags & DR_LOOPTST) {
	/* Controller is doing loopback test */
    	dra->dr_flags &= ~DR_LOOPTST;
	return;
    }

    /* Make sure this is not a stray interrupt; at least one of dmaf or attf
       must be set. Note that if the dr11 interrupt enable latch is reset 
       during a hardware interrupt ack sequence, and by the we get to this 
       point in the interrupt code it will be 0. This is done to give the
       programmer some control over how the two more-or-less independent
       interrupt sources on the board are handled.
       If the attention flag is set when drstrategy() is called to start a
       dma read or write an interrupt will be generated as soon as the
       strategy routine enables interrupts for dma end-of-range. This will
       cause execution of the interrupt routine (not necessarily bad) and
       will cause the interrupt enable mask to be reset (very bad since the
       dma end-of-range condition will not be able to generate an interrupt
       when it occurs) causing the dma operation to time-out (even though
       the dma transfer will be done successfully) or hang the process if a
       software time-out capability is not implemented. One way to avoid 
       this situation is to check for a pending attention interrupt (attf
       set) by calling drioctl() before doing a read or a write. For the
       time being this driver will solve the problem by clearing the attf
       flag in the status register before enabling interrupts in drstrategy().

       **** The IKON 10084 for which this driver is written will set both
       attf and dmaf if dma is terminated by an attention pulse. This will
       cause a wakeup(&dr_aux), which will be ignored since it is not being 
       waited on, and an iodone(bp) which is the desired action. Some other
       dr11 emulators, in particular the IKON 10077 for the Multibus, donot
       dmaf in this case. This may require some addtional code in the inter-
       rupt routine to ensure that en iodone(bp) is issued when dma is term-
       inated by attention.
    */

    bp = dra->dr_actf;
    if (!(status & (ATTF | DMAF))) {
	printf("\ndrintr: Stray interrupt, dr11 status : %lx",status);
	return;
    }
    if (status & DMAF) {
	/* End-of-range interrupt */
	dra->dr_flags |= DR_DMAX;

#ifdef DR_DEBUG
    if (DR11 & 2)
	printf("\ndrintr: e-o-r interrupt,cstat:%lx,dr_flags:%lx",
		status&0xffff,dra->dr_flags & DR_ACTV);
#endif
	if (!(dra->dr_flags & DR_ACTV)) {
		/* We are not doing DMA !! */
		bp->b_flags |= B_ERROR;
	}
	else {
		if (dra->dr_op == DR_READ) mtpr(bp->b_un.b_addr,P1DC);
		dra->dr_bycnt -= bp->b_bcount;
		if (dra->dr_bycnt >0) {
			bp->b_un.b_addr += bp->b_bcount;
			bp->b_bcount = (dra->dr_bycnt > NBPG) ? NBPG:
					dra->dr_bycnt;
			drstart(rsaddr,dra,bp);
			return;
		}
	}
	dra->dr_flags &= ~DR_ACTV;
	wakeup(dra);			/* Wakeup proc waiting in drwait() */
	rsaddr->dr_pulse = (RPER|RDMA|RATN);	/* reset dma e-o-r flag */
    }

    /* Now test for attention interrupt -- It may be set in addition to 
       the dma e-o-r interrupt. If we get one we will issue a wakeup to
       the drioctl() routine which is presumable waiting for one.
       The program may have to monitor the attention interrupt received
       flag in addition to doing waits for the interrupt. Futhermore, 
       interrupts are not enabled unless dma is in progress or drioctl()
       has been called to wait for attention -- this may produce some
       strange results if attf is set on the dr11 when a read or a write
       is initiated, since that will enables interrupts.
       **** The appropriate code for this interrupt routine will probably
       be rather application dependent.
    */

    if (status & ATTF) {
	dra->dr_flags |= DR_ATRX;
	dra->dr_flags &= ~DR_ATWT;
	rsaddr->dr_cstat = RATN;	/* reset attention flag */
	wakeup((caddr_t)&dra->dr_cmd);
	/* Some applications which use attention to terminate dma may also
	   want to issue an iodone() here to wakeup physio().
 	*/
    }
    return;
}

unsigned
drminphys(bp)
struct buf *bp;
{
    if (bp->b_bcount > 65536)
	bp->b_bcount = 65536;
}

/*
 *  This routine performs the device unique operations on the DR11W
 *  it is passed as an argument to and invoked by physio
 */
drstrategy (bp)
register struct buf *bp;
{
    register int s;
    int unit = RSUNIT(bp->b_dev);
    register struct rsdevice *rsaddr = RSADDR(unit);
    register struct dr_aux *dra = &dr_aux[unit];
    register short go = 0;
    register long baddr, ok;
#ifdef DR_DEBUG
    register char *caddr;
    long drva();
#endif


    if (!(dra->dr_flags & DR_OPEN)) {
	/* Device not open */
	bp->b_error = ENXIO;
	bp->b_flags |= B_ERROR;
	iodone (bp);
	return;
    }

    while (dra->dr_flags & DR_ACTV) {
	/* Device is active; should never be in here... */
	sleep((caddr_t)&dra->dr_flags,DRPRI);
    }

    dra->dr_actf = bp;

#ifdef DR_DEBUG
    drva(dra,bp->b_proc,bp->b_un.b_addr,bp->b_bcount);
#endif

    dra->dr_oba = bp->b_un.b_addr;	/* Save original addr, count */
    dra->dr_obc = bp->b_bcount;
    dra->dr_bycnt = bp->b_bcount;	/* Save xfer count used by drintr() */

    if ((((long)bp->b_un.b_addr & 0x3fffffff) >> PGSHIFT) !=
	((((long)bp->b_un.b_addr & 0x3fffffff) + bp->b_bcount) >> PGSHIFT)) {
    	bp->b_bcount = NBPG - (((long)bp->b_un.b_addr) & PGOFSET);
    }

    dra->dr_flags |= DR_ACTV;	/* Mark it active (use in intr handler) */
    s = SPL_UP();
    drstart(rsaddr,dra,bp);
    splx(s);

    ok = drwait(rsaddr,dra);
#ifdef DR_DEBUG
    if (DR11 & 0x40) {
	caddr = (char *)dra->dr_oba;
    	if (dra->dr_op == DR_READ)
		printf("\nAfter read: (%lx)(%lx)",caddr[0]&0xff,caddr[1]&0xff);
    }
#endif
    dra->dr_flags &= ~DR_ACTV;		/* Clear active flag */
    bp->b_un.b_addr = dra->dr_oba;	/* Restore original addr, count */
    bp->b_bcount = dra->dr_obc;

    if (!ok) bp->b_flags |= B_ERROR;
    iodone(bp);				/* Mark buffer B_DONE,so physstrat()
					   in ml/machdep.c won't sleep */
    wakeup((caddr_t)&dra->dr_flags);

    /* Return to the calling program (physio()). Physio() will sleep
       until awaken by a call to iodone() in the interupt handler --
       which will be called by the dispatcher when it receives dma
       end-of-range interrupt.
    */
    return;
}

drwait(rs,dr)
register struct rsdevice *rs;
register struct dr_aux *dr;
{
	register long status, s;

	s = SPL_UP();
    	while (dr->dr_flags & DR_ACTV)
		sleep((caddr_t)dr,DRPRI);
	splx(s);

	if (dr->dr_flags & DR_TMDM) {
		/* DMA timed out */
		dr->dr_flags &= ~DR_TMDM;
		return(0);
	}
	else {
		if (rs->dr_cstat & (PERR|BERR|TERR)) {
			(dr->dr_actf)->b_flags |= B_ERROR;
			return(0);
		}
	}
	dr->dr_flags &= ~DR_DMAX;
	return(1);
}


drrwtimo(tinfo)
register unsigned long tinfo;
/*
 * 	The lower 8-bit of tinfo is the minor device number, the
 *	remaining higher 8-bit is the current timout number
*/
{	register long unit = tinfo & 0xff;
	register struct dr_aux *dr = &dr_aux[unit];
	register struct rsdevice *rs = dr->dr_addr;

	/* If this is not the timeout that drwrite/drread is waiting
	   for then we should just go away */
	if ((tinfo & (~0xff)) != (dr->currenttimo << 8)) return;

	/* Mark the device timed out */
	dr->dr_flags |= DR_TMDM;
	dr->dr_flags &= ~DR_ACTV;
	rs->dr_pulse = RMSK;			/* Inihibit interrupt */
	rs->dr_pulse = (RPER|RDMA|RATN|IENB);	/* Clear DMA logic */

	/* Some applications will not issue a master after dma timeout,
	   since doing so sends an INIT H pulse to the external device,
	   which may produce undesirable side-effects.  */

	/* Wake up process waiting in drwait() and flag the error */
	(dr->dr_actf)->b_flags |= B_ERROR;
	wakeup((caddr_t)dr->dr_cmd);
}


/*
 *	Kick the driver every second
*/
drtimo(dev)
dev_t dev;
{
    	register int unit = RSUNIT(dev);
	register struct dr_aux *dr;

    	dr = &dr_aux[unit];
	if (dr->dr_flags & DR_OPEN)
		timeout(drtimo,(caddr_t)dev,hz);
	wakeup((caddr_t)dr);	/* Wakeup any process waiting for interrupt */
}


#ifdef DR_DEBUG

drva(dra,p,va,bcnt)
struct dr_aux *dra;
struct proc *p;
char *va;
long bcnt;
{	register long first, last , np;

	if (DR11 & 0x20)  {
		first = ((long)(vtoph(p,va))) >> 10;
		last = ((long)(vtoph(p,va+bcnt))) >> 10;
		np = bcnt / 0x3ff;
		printf("\ndrva: (op:%ld)(first:%ld)(last:%ld)(np:%ld)(cnt:%ld)",
			dra->dr_op,first,last,np,bcnt);
	}
}
#endif


drstart(rsaddr,dra,bp)
register struct rsdevice *rsaddr;
register struct dr_aux *dra;
register struct buf *bp;
{	register long baddr;
	ushort go;
	register char *caddr;

#ifdef DR_DEBUG
	if ((dra->dr_op == DR_READ) && (DR11 & 8)) {
		printf("\ndrstart: READ, bcnt:%ld",bp->b_bcount);
    		caddr = (char *)bp->b_un.b_addr;
		printf(",(%lx)(%lx)",caddr[0]&0xff,caddr[1]&0xff);
	}
#endif
    /* we are doing raw IO, bp->b_un.b_addr is user's address */
    baddr = (long)vtoph(bp->b_proc,(caddr_t)bp->b_un.b_addr);

    /* Set DMA address into DR11 interace registers: DR11 requires that
       the address be right shifted 1 bit position before it is written
       to the board (The board will left shift it one bit position before
       it places the address on the bus
    */
    rsaddr->dr_walo = (ushort)((baddr >> 1) & 0xffff);
    rsaddr->dr_wahi = (ushort)((baddr >> 17) & 0x7fff);

    /* Set DMA range count: (number of words - 1) */
    rsaddr->dr_range = (ushort)((bp->b_bcount >> 1) - 1);

    /* Set address modifier code to be used for DMA access to memory */
    rsaddr->dr_addmod = (char)DRADDMOD;

    /* Now determine whether this is a read or a write. ***** This is
       probably only usefull for link mode operation, since dr11 doesnot
       controll the direction of data transfer. The C1 control input 
       controls whether the hardware is doing a read or a write. In link
       mode this is controlled by function 1 latch (looped back by the
       cable) and could be set the program. In the general case, the dr11
       doesnot know in advance what the direction of transfer is - although
       the program and protocol logic probably is
    */

#ifdef DR_DEBUG
   if (DR11 & 1)
    printf("\ndrstrat: about to GO..,dr_cmd:%lx,drstat:%lx,drcnt:%ld,cdata:%lx,OP:%ld",
	dra->dr_cmd,rsaddr->dr_cstat,rsaddr->dr_range,rsaddr->dr_data,dra->dr_op);
#endif

    /* Update function latches may have been done already by drioctl() if
       request from drioctl()
    */
    if (dra->dr_cmd & DR_DFCN) {
	/* deferred function write */
    	dra->dr_cmd &= ~DR_DFCN;	/* Clear request */
	go = dra->dr_cmd & DR_FMSK;	/* mask out fcn bits */
	rsaddr->dr_cstat = go;		/* Write it to the board */
    }

    /* Clear dmaf and attf to assure a clean dma start */
    rsaddr->dr_pulse = (ushort)(RATN|RDMA|RPER);
    rsaddr->dr_cstat = (ushort)(IENB|GO|CYCL|dra->dr_op); /* GO...... */

    /* Now check for software cycle request -- usually by transmitter in
       link mode.
    */
    if (dra->dr_cmd & DR_PCYL) {
    	dra->dr_cmd &= ~DR_PCYL;	/* Clear request */
	rsaddr->dr_pulse = CYCL;	/* Use pulse register again */
    }

    /* Now check for deferred ACLO FCNT2 pulse request -- usually to tell
       the transmitter (via its attention) that we have enabled dma.
    */
    if (dra->dr_cmd & DR_DACL) {
    	dra->dr_cmd &= ~DR_DACL;	/* Clear request */
	rsaddr->dr_pulse = FCN2;	/* Use pulse register again */
    }
}

#endif  NDR

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.