/* Copyright 1987 by the Massachusetts Institute of Technology */
/* See permission and disclaimer notice in file notice.h */
#include <notice.h>

/* Device driver for a DHV-11 interface.  Written for the uVAX.
 */

/*
 *------------------------------------------------------------------
 *
 * $Source: /mit/cgw/src/gw/dev.vax/RCS/dhv.c,v $
 * $Revision: 1.3 $
 * $Date: 88/06/06 21:53:37 $
 * $State: Exp $
 * $Author: jon $
 * $Locker: jon $
 *
 * $Log:	dhv.c,v $
 * Revision 1.3  88/06/06  21:53:37  jon
 * Put back the #include's I missed.
 * 
 * Revision 1.2  88/06/06  21:51:20  jon
 * This is newer and is supposed to work.
 * 
 * Revision 1.1  88/06/05  22:49:16  jon
 * Initial revision
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_dhv_c = "$Header: dhv.c,v 1.3 88/06/06 21:53:37 jon Locked $";
#endif	lint

#include	<types.h>
#include	<sys.h>
#include	<gw/src/status.h>
#include	"async.h"
#include	"dhv.h"

/* DHV device initialization routine.  This is called once for each
 * input line and not at all for the output lines.  The device is
 * configured in systbl.c as two arrays.  An array of input devices and
 * an array of output devices.  The device links point to the
 * corresponding device in the other array.  Only the first device of
 * each the input and the output arrays should have the interrupt set.
 */
dhup(devp)
reg dct *devp;
{
    dct *ldevp;
    reg struct dhv_reg *dreg;
    struct async_conf *ac;
    unss i;

    /* If the corresponding device has already been initialized, then
     * don't do it again. */
    if (devp->d_lnk->d_flg & D_INI)
      return;

    /* Get the device CSR address */
    dreg = (struct dhv_reg *)devp->d_csr;

    /* If this is the first line then do the device initialization */
    if (devp->d_crf == 0) {
	/* Number lines */
	for (ldevp = devp, i = 0; i < LINES_PER_DH; ldevp = ldevp++, i++)
	  ldevp->d_lnk->d_crf = ldevp->d_crf = i;

	/* If the DHV is not in the process of reseting then reset it. */
	if (!(dreg->csr & RESET))
	  dreg->csr |= RESET;
	/* Wait for the reset to finish */
	while (dreg->csr & RESET)
	  ;
	
	/* If the onboard diagnostics report failure, then mark all lines
	 * as down. */
	if (dreg->csr & DIAGFAIL) {
	    for (ldevp = devp, i = 0; i < LINES_PER_DH; ldevp++, i++) {
		ldevp->d_flg |= D_DWN;
		ldevp->d_lnk->d_flg |= D_DWN;
	    }
	    return;
	}
	/* Enable device interrupts */
	dreg->csr = RXIE | TXIE;
    }

    /* Now use the async_conf structure in d_dev3 field to configure the
     * line. */
    if ((ac = (struct async_conf *)devp->d_dev3) != 0) {
	dreg->csr = devp->d_crf;
	i = RXSPEED(ac->rx_speed) | TXSPEED(ac->tx_speed);
	switch (ac->parity) {
	case PARITY_OFF:  break;
	case PARITY_ODD:  i |= PAREN; break;
	case PARITY_EVEN: i |= PAREN|PAREVN; break;
	default:
	    bughalt("DHV: unable to do parity type %d\n", ac->parity);
	    break;
	}
	switch (ac->clen) {
	case CHAR_LEN_5: i |= DHCHAR5; break;
	case CHAR_LEN_6: i |= DHCHAR6; break;
	case CHAR_LEN_7: i |= DHCHAR7; break;
	case CHAR_LEN_8: i |= DHCHAR8; break;
	default:
	    bughalt("DHV: unknown character len %d\n", ac->clen);
	    break;
	}
	if (ac->stop == STOP2)
	  i |= DHSTOP2;
	dreg->lpr = i;
	dreg->lnctrl = RXENA|DTR|RTS|(ac->modem ? DHMODEM : 0);
    }
    else {
	devp->d_flg |= D_OFL;
	devp->d_lnk->d_flg |= D_OFL;
    }
    devp->d_flg |= D_INI;
}

/* Input transfer initialization routine.  Just sets up the queue
 * pointers for the per protocol input routine. */
dhin(devp)
dct *devp;
{
    /* d_rfnt and d_rend are used to keep track of the bytes as they
     * come in one at a time. */
    devp->d_rfnt = devp->d_addr;
    devp->d_rend = devp->d_addr + devp->d_breq;
}

/* Output transfer initialization routine.  Calls the per protocol
 * routine to do protocol processing on the packet first. */
dhot(devp)
dct *devp;
{
    reg struct dhv_reg *dreg;

    dreg = (struct dhv_reg *)devp->d_csr;
    dreg->csr = (unss)devp->d_crf | RXIE | TXIE;
    dreg->cnt = (*((int (*)())devp->d_rc)) (devp);
    dreg->addl = (unss)devp->d_rfnt;
    dreg->addh = (unss)((((unsl)devp->d_rfnt >> 16) & ADDRHI) | DMASTART | TXENA);
}

/* input interrupt routine - This assumes it is always called with the
 * devp of line 0.  It figures out which line the character is for, switches
 * the devp to that line, and calls the protocol specific routine pointed
 * to by devp->d_rc. */
dhii(devp)
dct *devp;
{
    reg dct *ndevp;
    reg struct dhv_reg *dreg;
    reg int c;

    dreg = (struct dhv_reg *)devp->d_csr;
    while ((c = dreg->buf) & VALID) {
	if (FIFO_DIAG(c))
	  continue;
	ndevp = &devp[RXLINE(c)];
	if (FIFO_MDM(c) &&
	    (((struct async_conf *)ndevp->d_dev3)->flow_control)) {
	    if (((FIFO_STAT(c) & CTS) == 0) && 
		((ndevp->d_flg & D_STP) == 0)) { /* flow off */
		dreg->csr = RXLINE(c) | RXIE | TXIE;
		dreg->lnctrl |= DMA_ABORT;
		ndevp->d_flg |= D_STP;
	    }
	    else if (((FIFO_STAT(c) & CTS) != 0) &&
		     ((ndevp->d_flg & D_STP) != 0)) { /* flow on */
		dreg->csr = RXLINE(c) | RXIE | TXIE;
		dreg->lnctrl &= ~DMA_ABORT;
		dreg->addh |= DMASTART | TXENA;
		ndevp->d_flg &= ~D_STP;
	    }
	}
	if (ndevp->d_rc)
	  (*((int (*)())ndevp->d_rc)) (ndevp, c);
    }
}

/* output interrupt routine */
dhoi(devp)
dct *devp;
{
    reg dct *ndevp;
    struct dhv_reg *dreg;
    reg iorb *iob;
    unss csr;
    unss cnt;

    dreg = (struct dhv_reg *)devp->d_csr;
    while ((csr = dreg->csr) & TXACTION) {
	ndevp = &devp[TXLINE(csr)];
	if ((iob = ndevp->d_qhd) != 0) {
	    if (csr & DMAERR) {
		iob->i_stat |= I_ERR;
		iocmr(ndevp);
		continue;
	    }

	    cnt = (*((int (*)())ndevp->d_rc)) (ndevp);
	    if (cnt == 0) {
		iob->i_stat |= I_DONE;
		iob->i_bxfr = iob->i_breq;
		iocmr(ndevp);
	    }
	    else {
		dreg->csr = (unss)ndevp->d_crf | RXIE | TXIE;
		dreg->cnt = cnt;
		dreg->addl = (unss)ndevp->d_rfnt;
		dreg->addh = (unss)((((unsl)ndevp->d_rfnt >> 16) & ADDRHI)
				    | DMASTART | TXENA);
	    }
	}
    }
}
