/****************************************************************************
*
*            COPYRIGHT 1990,91,92 BY GRACILIS INC.
*
*                          623 Palace St.
*                        Aurora, Il. 60506
*
* GRACILIS, INC., MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS
* SOFTWARE FOR ANY PURPOSE.
*
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
*
* Permission is granted for non-commercial distribution only.
*
******************************************************************************/
  
/****************************************************************************
*
*
* File: twin_dr.c
*
* Rev : 3.0
*
* Description:
*
*    This File contains routines that implement PackeTwin
*    synchronous SCC driver with an interface to KA9Q's TCP/IP suite.
*
*
* Routines:
*
*    tsync_raw    - Applications' routine for transmitting
*                   a message via tsync_driver.
*
*    tsync_stop   - Stop I/O
*
*    tsync_recv   - Applications' routine for receiving a message
*                   from the tsync_driver.   Left over from NET.
*
*
*    tsync_hwinit - Initialize the SCC chip for synchronous operation.
*
*    tsync_txisr  - Handles transmit interrupts.
*
*    tsync_rxisr  - Handles receive interrupts.
*
*    tsync_xstatisr - Handles the SCC's external status interrupts.
*
*    transon      - Turn-on transmitter and turn-off receiver or
*                   turn-off transmitter and turn-on receiver.
*                   Will change txstate.  Also
*                   handles time delays needed for data set
*                   control signals (RTS).
*
*/
  
#include <dos.h>
#include "global.h"
#ifdef PACKETWIN
#include "hardware.h"
#include "mbuf.h"
#include "iface.h"
#include "ax25.h"
#include "trace.h"
#include "proc.h"
#include "pktdrvr.h"
#include "z8530.h"
#include "gracilis.h"
#include "twin.h"
  
#ifdef TSYNC_DEBUG
unsigned char Lastint;
#endif
  
#ifdef SCOPE_LOOP
unsigned char scope_state = 0;
#endif
  
  
/* interrupt handler */
extern INTERRUPT twinvec();
  
INTERRUPT (*Twin_oldvec) __ARGS((void)); /* Old vector contents */
INTERRUPT (*Twin_handler) __ARGS((void)) = { twinvec };
  
unsigned int    Twin_io_base = 0x230;
int16                   Twin_vector;
int                     Twin_cputype;
  
/* The pwait/psignal inter-process communication wake-up array */
/* for getting pre-allocated receive buffers */
int  Twin_bufpp[5];
  
/* The pwait/psignal inter-process communication wake-up array */
/* for kicking the transmitter */
int  Twin_kickpp[5];
  
/* The pwait/psignal inter-process communication wake-up array */
/* for free-ing transmitted messages */
int  Twin_freetxpp[5];
  
uchar                   Twin_sercfg = 0;                /* image of the SERIAL_CFG port */
uchar                   Twin_dmacfg = 0;                /* image of the DMA_CFG port */
  
/* Master device control blocks for the Tsync drivers... */
UNI_DCB Twin_udcb[2] = {
    FALSE, 0, NULL, NULL, NULL, NULL, NULL,
    FALSE, 0, NULL, NULL, NULL, NULL, NULL,
};
  
/* a simple delay instruction... */
int     nop1;
#define nop nop1++;
  
void tsync_txisr(DCB *);
void tsync_entpt(void);
void empty_scc(int16,int16);
void tsync_srcisr(DCB *);
void time_wait(DCB *,struct drv_timer *,int16,void (*timerfunc)(),unsigned long);
void transon(DCB *,int16);
void tsync_persist(DCB *);
void tsync_sqdelay(DCB *);
void tsync_txondelay(DCB *);
  
/* Send raw packet */
int
tsync_raw(intfp, mbufp)
register struct iface *intfp;
register struct mbuf *mbufp;
{
    register DCB *dcbp;
    register struct drvbuf *xbufp ;
    int16  msgsize;
    int i_state;
  
#ifdef TSYNC_DEBUG_PRT
    int16  pullsize;
#endif
  
#ifdef OLD_KA9Q
    dump(intfp, IF_TRACE_OUT, CL_AX25, mbufp);
#else
    dump(intfp,IF_TRACE_OUT,mbufp);
#endif
  
    dcbp = (DCB *)Twin_udcb[intfp->dev].dcbp;
  
    /* get packet's transmission size */
    msgsize = len_p(mbufp);
  
    xbufp = (struct drvbuf *)malloc(sizeof(struct drvbuf) + msgsize);
  
    /* If no memory for buffering... then dump the packet on floor */
    /* and return error to caller */
    if(xbufp == (struct drvbuf *)NULL)
    {
        free_p(mbufp);
        return(-1);
    }
  
  
    xbufp->msgsize = msgsize;
    xbufp->next = (struct drvbuf *)NULLBUF;
  
#ifdef TSYNC_DEBUG_PRT
    if ( (pullsize = dqdata(mbufp, &(xbufp->buf), msgsize+1)) != msgsize )
    {   /* somethings wrong.  should have gotten all of message */
        printf("tsync_raw error wanted %d bytes pullup-up got only %d\n",
        msgsize, pullsize);
    }
#else
    /* there will be no check for mismatching msgsize and pullsize */
    dqdata(mbufp, &(xbufp->buf), msgsize);
#endif
  
    /* now enqueue the message !!! */
    i_state = dirps();
    if ( dcbp->xmtq == (struct drvbuf *)NULLBUF )
    {
        dcbp->xmtq = dcbp->xmtqtail = xbufp;
    }
    else
    {
        dcbp->xmtqtail->next = xbufp;
        dcbp->xmtqtail = xbufp;
    }
  
    /* Note tsync_txisr must be able to run from ISR level as well */
    /* as from "task" level, since tsync_raw is called from task */
    /* level */
  
    /* Only if the transmitter is not doing anything then call it */
  
    if ( dcbp->txstate == IDLE )tsync_txisr(dcbp);
  
    restore(i_state);
  
    return(0);
}
  
/*
 * Master interrupt handler.  One interrupt at a time is handled.
 * here. Service routines are called from here.
 *
 * This 8530 interrupt handler will take care of both async and
 * sync drivers' interrupts.
 */
  
/******************************************************/
/* 85230 Mods:                                        */
/******************************************************/
/* NOTE: This interrupt handler is constructed to get */
/*              around a bug in the Zilog 85c230 while            */
/*              still allowing use of it's advanced FIFOs     */
/*                                                    */
/*              The basic bug is as follows...                            */
/*                      If an interrupt driver is written for the */
/*                      85230, which does NOT make use of the     */
/*                      automatic vector generation facilities    */
/*                      provided by RR2,      AND.....            */
/*                      If RR1 is used as the ONLY means of       */
/*                      identifying the End of a received frame,  */
/*                      then the 85230 will mis-report the EOF    */
/*          when the 1st byte of a CRC is read from   */
/*                      the receive FIFO, resulting in checking   */
/*                      the CRC status flag at the wrong time.    */
/*          This of course then indicates a CRC       */
/*                      error, and results in the driver          */
/*          discarding an otherwise good frame...     */
/*                                                                                                        */
/*              The FIX:                                                                          */
/*                      The Basic idea here is to simulate the    */
/*          use of true VECTORED interrupts.          */
/*                                                    */
/*                      First look for a pending interrupt in     */
/*                      RR3, then read the VECTOR which would     */
/*                      have been generated IF we were using      */
/*                      vectored interrupts.  Dispatch to the     */
/*                      appropriate handler routine.                      */
/*                                                                                                        */
/*              The reason this approach works is that the    */
/*              Special Receive condition interrupt VECTOR    */
/*              is not asserted until AFTER the first CRC     */
/*              byte is read from the FIFO.  This is the      */
/*              expected behaviour, and allows the programmer */
/*              to read the second CRC byte, and know that    */
/*              he/she may appropriately check the CRC status */
/*      indicator, for the completed frame.           */
/*              Additionally, it is known at this point that  */
/*              the last two bytes received are the CRC bytes */
/*      which must be discarded, since they are not   */
/*              a part of the user data frame.                */
/*                                                    */
/* MORAL: Dont believe RR1's end of received frame    */
/*        indication, until you have first waited     */
/*        for a SPECIAL RECEIVE CONDITION VECTOR      */
/*        to be asserted... or YOU'll be SORRY!!!     */
/******************************************************/
void
tsync_entpt()
{
    register char st;
  
#ifdef notdef
    int i_state;
#endif
  
    register DCB  *dcbp;
    register int16  ctl;
    register unsigned intreg;
    struct drv_timer *tblk;
    void    (*timer_func)(unsigned long);
  
#ifdef notdef
    i_state = dirps();
#endif
  
    intreg = inportb( Twin_io_base + INT_REG);
  
    while((intreg & (0x07)) != 0x07 )
    {
  
  
      /* Read interrupt pending register from channel A */
        while(Twin_read_scc( Twin_io_base+SCCA_CMD,R3) != 0 )
        {
        /* Get interrupt vector info from Channel B RR2 */
            st = (Twin_read_scc( Twin_io_base+SCCB_CMD,R2) & 0x0E);
  
#ifdef TSYNC_DEBUG
            Lastint = st;
#endif
            switch(st)
            {
                case    VEC_CHB_TBE:
                /* Channel B Transmit Int Pending */
                    dcbp = (DCB *)Twin_udcb[TWINCOMM2].dcbp;
                /* milt tsync_txisr(dcbp); */
                /* Call the Transmit Buffer Empty Handler */
                    (*(Twin_udcb[TWINCOMM2].prev_vec2))(dcbp);
                    ctl = (int16)(Twin_io_base+SCCB_CMD);
                    break;
  
                case    VEC_CHB_XTS:
                /* Channel B External Status Int */
                    dcbp = (DCB *)Twin_udcb[TWINCOMM2].dcbp;
                /* milt tsync_xstatisr(dcbp); */
                /* Call the External Status Change Handler */
                    (*(Twin_udcb[TWINCOMM2].prev_vec4))(dcbp);
                    ctl = (int16)(Twin_io_base+SCCB_CMD);
                    break;
  
                case    VEC_CHB_RCAV:
                /* Channel B Rcv Interrupt Pending */
                    dcbp = (DCB *)Twin_udcb[TWINCOMM2].dcbp;
                /* milt tsync_rxisr(dcbp); */
                /* Call the Receive Character Available Handler */
                    (*(Twin_udcb[TWINCOMM2].prev_vec3))(dcbp);
                    ctl = (int16)(Twin_io_base+SCCB_CMD);
                    break;
  
                case    VEC_CHB_SRC:
                /* Channel B Special Rcv Int Pending */
                    dcbp = (DCB *)Twin_udcb[TWINCOMM2].dcbp;
                /* milt tsync_rxisr(dcbp); */
                /* Call the Special Receive Condition Handler */
                    (*(Twin_udcb[TWINCOMM2].prev_vec3))(dcbp);
                    ctl = (int16)(Twin_io_base+SCCB_CMD);
                    break;
  
                case    VEC_CHA_TBE:
                /* Channel A Transmit Int Pending */
                    dcbp = (DCB *)Twin_udcb[TWINCOMM1].dcbp;
                /* milt tsync_txisr(dcbp); */
                /* Call the Transmit Buffer Empty Handler */
                    (*(Twin_udcb[TWINCOMM1].prev_vec2))(dcbp);
                    ctl = (int16)(Twin_io_base+SCCA_CMD);
                    break;
  
                case    VEC_CHA_XTS:
                /* Channel A External Status Int */
                    dcbp = (DCB *)Twin_udcb[TWINCOMM1].dcbp;
                /* milt tsync_xstatisr(dcbp); */
                /* Call the External Status Change Handler */
                    (*(Twin_udcb[TWINCOMM1].prev_vec4))(dcbp);
                    ctl = (int16)(Twin_io_base+SCCA_CMD);
                    break;
  
                case    VEC_CHA_RCAV:
                /* Channel A Rcv Interrupt Pending */
                    dcbp = (DCB *)Twin_udcb[TWINCOMM1].dcbp;
                /* milt tsync_rxisr(dcbp); */
                /* Call the Receive Character Available Handler */
                    (*(Twin_udcb[TWINCOMM1].prev_vec3))(dcbp);
                    ctl = (int16)(Twin_io_base+SCCA_CMD);
                    break;
  
                case    VEC_CHA_SRC:
                /* Channel A Special Rcv Int Pending */
                    dcbp = (DCB *)Twin_udcb[TWINCOMM1].dcbp;
                /* milt tsync_rxisr(dcbp); */
                /* Call the Special Receive Condition Handler */
                    (*(Twin_udcb[TWINCOMM1].prev_vec3))(dcbp);
                    ctl = (int16)(Twin_io_base+SCCA_CMD);
                    break;
            }
  
        /* Reset highest interrupt under service */
#ifdef TSYNC_DEBUG
            Twin_write_scc(ctl,R0,(dcbp->wr0 = RES_H_IUS));
#else
            Twin_write_scc(ctl,R0,RES_H_IUS);
#endif
        }
  
        if(!(intreg & PKTWIN_TMR1_MSK))
        {
            dcbp = (DCB *)Twin_udcb[TWINCOMM1].dcbp;
            tblk = dcbp->timer1;
        /* Now clear the timer interrupt */
            inportb(Twin_io_base + CLR_TMR1);
  
        /* Now perform the timer's requested function */
            if(tblk->thandler != NULL)
            {
                timer_func = tblk->thandler;
                tblk->thandler = (void (*)())NULL;
                (timer_func)(tblk->targ);
            }
        }
  
  
        if(!(intreg & PKTWIN_TMR2_MSK))
        {
            dcbp = (DCB *)Twin_udcb[TWINCOMM2].dcbp;
            tblk = dcbp->timer1;
        /* Now clear the timer interrupt */
            inportb(Twin_io_base + CLR_TMR2);
  
        /* Now perform the timer's requested function */
            if(tblk->thandler != NULL)
            {
                timer_func = tblk->thandler;
                tblk->thandler = (void (*)())NULL;
                (timer_func)(tblk->targ);
            }
  
        }
  
        intreg = inportb( Twin_io_base + INT_REG);
  
    }
  
#ifdef notdef
/* Dont need this... causewe only got here though an int... */
    restore(i_state);
#endif
}
  
  
/*
 * External/Status interrupts caused by a receiver abort,
 * a Transmit UNDERRUN/EOM.
 * SCC enters hunt mode on  an abort.
 *
 * On a Transmit Underrun, change state and
 * issue a reset command for it, and return.
 */
void
tsync_xstatisr(dcbp)
register DCB *dcbp;
{
    register uchar  scc_stat,newstat,new_xbits;
  
    /* find out why the external/status interrupt occurred */
    scc_stat = Twin_read_scc(dcbp->zhwmap.ctl,R0);
  
/* 9/23/91 DGL Try to fix possible race condition */
    /* reset external status latch  to enable future changes to catch us */
  
#ifdef TSYNC_DEBUG
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = RES_EXT_INT));
#else
    Twin_write_scc(dcbp->zhwmap.ctl,R0,RES_EXT_INT);
#endif
  
    new_xbits = scc_stat ^ dcbp->extreg;
  
    /* Check for Tx UNDERRUN/EOM  */
    if(new_xbits & TxEOM )
    {
        if(dcbp->dma_flg == TRUE)
        {
            /* Disable this DMA channel */
            outportb(DMAWSMR,dcbp->dma_tx_chan | 0x04);
  
            dcbp->txstate = TX_DONE;
  
            /* update the xmit data tally*/
            dcbp->txbytecnt += dcbp->cur_xbytes;
  
            /* don't allow Underruns to interrupt us */
            Twin_write_scc(dcbp->zhwmap.ctl,R15,(dcbp->wr15 &= ~0x40));
            /* set the TxEOM bit in the image */
            dcbp->extreg |= TxEOM;
  
  
            /* enable the tx empty interrupt... */
            /* so it can catch us if necessary */
            /* This handles condition where the buffer */
            /* goes empty while we're checking... */
            /* Avoid's a race. 9/23/91 */
#ifdef TSYNC_DEBUG
            Twin_write_scc(dcbp->zhwmap.ctl,R1,(dcbp->wr1 = 0xfb));
#else
            Twin_write_scc(dcbp->zhwmap.ctl,R1,0xfb);
#endif
  
            /* Now check to see if the CRC has already cleared */
            /* the SCC's xmit buffer... */
            newstat = Twin_read_scc(dcbp->zhwmap.ctl,R0);
  
            /* If so, turn off the tx empty interrupt we just enabled */
            /* and process the end of frame condition */
            if(newstat & Tx_BUF_EMP)
            {
                /* Rx and Extern ints on : No Tx ints */
#ifdef TSYNC_DEBUG
                Twin_write_scc(dcbp->zhwmap.ctl,R1,(dcbp->wr1 = 0xf9));
#else
                Twin_write_scc(dcbp->zhwmap.ctl,R1,0xf9);
#endif
  
                tsync_txisr(dcbp);
            }
  
            /* else... it will happen under interrupt ctl */
            /* when the buffer goes empty */
  
        }
  
        else
        {
            /* set the TxEOM bit in the image */
            dcbp->extreg |= TxEOM;
  
  
            if((dcbp->txstate == TX_ACTIVE) || (dcbp->txstate == CRCING))
            {
                dcbp->txstate = TX_DONE;
  
                /* don't allow Underruns to interrupt us */
                Twin_write_scc(dcbp->zhwmap.ctl,R15,(dcbp->wr15 &= ~0x40));
  
  
                /* if unexpected... bump transmit underrun counts */
                if( dcbp->cur_xbytes > 0 )
                {
                    dcbp->txunderun++;
                }
  
                /* enable the tx empty interrupt... */
                /* so it can catch us if necessary */
                /* This handles condition where the buffer */
                /* goes empty while we're checking... */
                /* Avoid's a race. 9/23/91 */
#ifdef TSYNC_DEBUG
                Twin_write_scc(dcbp->zhwmap.ctl,R1,(dcbp->wr1 = 0x13));
#else
                Twin_write_scc(dcbp->zhwmap.ctl,R1,0x13);
#endif
  
                /* check to see if the CRC has already cleared */
                /* the SCC's xmit buffer... */
                newstat = Twin_read_scc(dcbp->zhwmap.ctl,R0);
  
                /* If so, turn off the tx empty interrupt we just enabled */
                /* and process the end of frame condition */
                if(newstat & Tx_BUF_EMP)
                {
#ifdef TSYNC_DEBUG
                    Twin_write_scc(dcbp->zhwmap.ctl,R1,(dcbp->wr1 = 0x11));
#else
                    Twin_write_scc(dcbp->zhwmap.ctl,R1,0x11);
#endif
                    tsync_txisr(dcbp);
                }
  
                /* else... it will happen under interrupt ctl */
  
            }
        }
  
    }
  
    /* Receive Mode only
     * This triggers when hunt mode is entered, & since an ABORT
     * automatically enters hunt mode, we use that to clean up
     * any waiting garbage
     */
    if(new_xbits & BRK_ABRT )
    {
        if(scc_stat & BRK_ABRT ) dcbp->extreg |= BRK_ABRT;
        else dcbp->extreg &= ~BRK_ABRT;
        dcbp->rxstate = RXABORT;
        dcbp->rxabortcnt++;       /* bump aborts */
  
        /* No  more Abort interrupts  */
#ifdef notdef
        Twin_write_scc(dcbp->zhwmap.ctl,R15,(dcbp->wr15 &= ~(0x80)));
#endif
        if(dcbp->dma_flg == TRUE)
        {
            rxprime_dma(dcbp);        /* restart for a new frame */
        }
        else
        {
            /* reset err latch */
#ifdef TSYNC_DEBUG
            Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = ERR_RES));
#else
            Twin_write_scc(dcbp->zhwmap.ctl,R0,ERR_RES);
#endif
            /* read and toss any  data */
            inportb(dcbp->zhwmap.data);
            inportb(dcbp->zhwmap.data);
            inportb(dcbp->zhwmap.data);
            inportb(dcbp->zhwmap.data);
  
            /* make sure dcbp->rxbufp has a data buffer to work with!!! */
            dcbp->rxcurp = dcbp->rxbufp->data;
#ifdef OLD_KA9Q
            dcbp->rxbufp->cnt = sizeof(struct phdr);
#else
            dcbp->rxbufp->cnt = 0;
#endif
            /* resync the receiver... */
#ifdef TSYNC_DEBUG
            Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xf9));
#else
            Twin_write_scc(dcbp->zhwmap.ctl,R3,0xf9);
#endif
  
        }
    }
    if(new_xbits & DCD )
    {
        if((scc_stat & DCD) == 0)
        {
            dcbp->extreg &= ~DCD;
            if(dcbp->dma_flg == TRUE)
            {
                rxprime_dma(dcbp);        /* restart for a new frame */
            }
            else
            {
                /* reset err latch */
#ifdef TSYNC_DEBUG
                Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0=ERR_RES));
#else
                Twin_write_scc(dcbp->zhwmap.ctl,R0,ERR_RES);
#endif
                /* read and toss any  data */
                inportb(dcbp->zhwmap.data);
                inportb(dcbp->zhwmap.data);
                inportb(dcbp->zhwmap.data);
                inportb(dcbp->zhwmap.data);
  
                /* make sure dcbp->rxbufp has a data buffer to work with!!! */
                dcbp->rxcurp = dcbp->rxbufp->data;
#ifdef OLD_KA9Q
                dcbp->rxbufp->cnt = sizeof(struct phdr);
#else
                dcbp->rxbufp->cnt = 0;
#endif
                /* resync the receiver... */
                /* Rx 8 bit/chars, enter hunt mode, CRC enable */
#ifdef TSYNC_DEBUG
                Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xf9));
#else
                Twin_write_scc(dcbp->zhwmap.ctl,R3,0xf9);
#endif
  
            }
        }
        else    dcbp->extreg |= DCD;
    }
  
    if(new_xbits & CTS )
    {
        if((scc_stat & CTS) == 0)
        {
            dcbp->extreg &= ~CTS;
  
            dcbp->txctslost++;
  
            if((dcbp->txstate == TX_ACTIVE) || (dcbp->txstate == CRCING))
            {
                dcbp->txstate = TX_DONE;
  
                /* don't allow Underruns to interrupt us */
                Twin_write_scc(dcbp->zhwmap.ctl,R15,(dcbp->wr15 &= ~0x40));
  
  
                /* if unexpected... bump transmit underrun counts */
                if( dcbp->cur_xbytes > 0 )
                {
                    dcbp->txunderun++;
                }
  
                /* Toss the frame being sent, and continue... */
                tsync_txisr(dcbp);
  
            }
        }
        else    dcbp->extreg |= CTS;
    }
  
#ifdef notdef           /* fix race 9/23/91 */
  
    /* reset external status latch */
#ifdef TSYNC_DEBUG
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = RES_EXT_INT));
#else
    Twin_write_scc(dcbp->zhwmap.ctl,R0,RES_EXT_INT);
#endif
  
#endif
  
    return;
}
/*
 * end of tsync_xstatisr
 ******************************************************************/
  
  
/*
 * Receive ISR.
 * The first receive buffer is pre-allocated
 * in the init routine.  Thereafter, it is filled here, queued out, and a
 * new one acquired.  CRC, OVERRUN and TOOBIG errors merely 'rewind' the
 * pointers and reuse the same buffer.
 */
void
tsync_rxisr(dcbp)
register DCB *dcbp;
{
    register int16 ctl;
    int16 data;
    struct mbuf *f_dequeavail();
    unsigned char   vecmsk;
  
    /* If this channel is a DMA channel handle it elsewhere */
    if(dcbp->dma_flg == TRUE)
    {
        tsync_rxdmaisr(dcbp);
        return;
    }
  
    /* speed up access to 8530 regs */
    ctl = dcbp->zhwmap.ctl;
    data = dcbp->zhwmap.data;
  
  
    /* Get interrupt vector info from Channel B RR2 */
    vecmsk = (Twin_read_scc( Twin_io_base+SCCB_CMD,R2) & 0x0E);
  
    /* If this is an SRC interrupt... handle it that way... */
    if((vecmsk == VEC_CHA_SRC) || (vecmsk == VEC_CHB_SRC))
    {
        tsync_srcisr(dcbp);
        return;
    }
  
  
    /* Check status of receive interrupt */
    if(Twin_read_scc(ctl,R0) & Rx_CH_AV)
    {
  
        /* get char and store it */
        *dcbp->rxcurp++ = inportb(data);
  
        /* tally data byte received */
        dcbp->rxbytecnt++;
  
        /* Allow aborts to get us after we receive a first char */
#ifdef OLD_KA9Q
        if(dcbp->rxbufp->cnt == sizeof(struct phdr))
#else
            if(dcbp->rxbufp->cnt == 0)
#endif
            {
            /* allow Aborts to interrupt us. */
                Twin_write_scc(ctl,R15,(dcbp->wr15 |= 0x80));
            }
  
        /* bump count & check for TOOBIG frames... */
#ifdef OLD_KA9Q
        if ((++dcbp->rxbufp->cnt) >= (dcbp->rxbufsize+sizeof(struct phdr)))
#else
            if ((++dcbp->rxbufp->cnt) >= dcbp->rxbufsize)
#endif
            {
            /* dump buffer - incoming packet is too big */
                dcbp->rxtruncnt++;
  
            /* read all the data out of the rx fifo... */
            /* and TOSS it.. */
                empty_scc(ctl,data);
  
            /* reset buf "pointers" - dump data */
                dcbp->rxcurp = dcbp->rxbufp->data;
#ifdef OLD_KA9Q
                dcbp->rxbufp->cnt = sizeof(struct phdr);
#else
                dcbp->rxbufp->cnt = 0;
#endif
            /* restart the receiver in HUNT  MODE... */
            /* to resync it... */
#ifdef TSYNC_DEBUG
                Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xf9));
#else
                Twin_write_scc(dcbp->zhwmap.ctl,R3,0xf9);
#endif
            }
  
    }
  
    return;
}
/*
 * end of tsync_rxisr
 ****************************************************************/
  
  
/****************************************************************/
/* Special Receive Condition Interrupt Handler... */
/****************************************************************/
void
tsync_srcisr(dcbp)
register DCB *dcbp;
{
    unsigned char rx1stat;
    struct mbuf *tmpbufp;
    struct mbuf *f_dequeavail();
    register int16 ctl;
    int16 data;
  
        /* speed up access to 8530 regs */
    ctl = dcbp->zhwmap.ctl;
    data = dcbp->zhwmap.data;
  
        /* Per ZILOG... only read R1 when R0 says there is */
        /* a char waiting... */
        /* get status byte from R1 */
  
    if(Twin_read_scc(ctl,R0) & Rx_CH_AV)
    {
        rx1stat = Twin_read_scc(ctl,R1);
  
        /* empty the 2nd CRC char */
        inportb(data);
  
        /* The End of Frame bit is ALWAYS associated with a character,
        * usually, it is the last CRC char.  Only when EOF is true can
        * we look at the CRC byte to see if we have a valid frame
        */
        dcbp->rxpackcnt++;
  
        /* No  Abort interrupts  */
        Twin_write_scc(ctl,R15,(dcbp->wr15 &= ~(0x80)));
  
        /* END OF FRAME -- Make sure Rx was active */
        if(rx1stat & END_FR)
        {
            /* got a packet - check for CRC error */
            if ( (rx1stat & CRC_ERR) ||
#ifdef OLD_KA9Q
                (dcbp->rxbufp->cnt < (sizeof(struct phdr) + 10)) )
#else
                (dcbp->rxbufp->cnt <  10 ) )
#endif
            {
                /* error occurred; toss frame */
                if (( rx1stat & CRC_ERR ) &&
#ifdef OLD_KA9Q
                    (dcbp->rxbufp->cnt  >= (sizeof(struct phdr) + 10)))
#else
                    (dcbp->rxbufp->cnt  >= 10) )
#endif
                    dcbp->rxcrcerrcnt++;    /* bump CRC errs */
  
                /* read all data out of the rx fifo...*/
                /* and TOSS it.. */
                empty_scc(ctl,data);
  
                /* reusing the old data buffer "dumps" the data */
  
                /* restart the receiver in HUNT  MODE... */
                /* to resync it... */
#ifdef TSYNC_DEBUG
                Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xf9));
#else
                Twin_write_scc(dcbp->zhwmap.ctl,R3,0xf9);
#endif
            }
            else
            {
                /* Got a valid packet !! */
  
                /* get next buffer */
                tmpbufp = f_dequeavail((struct mbuf **)&dcbp->rxavailq, (int16 *)&dcbp->rxavailcount);
  
                if ( tmpbufp != NULLBUF )
                {
  
                    /* dump crc bytes, i.e. '-1' */
                    dcbp->rxbufp->cnt -= 1;
  
                    /* new for NOS */
                    /* adjust for packet header */
#ifdef OLD_KA9Q
                    dcbp->rxbufp->data -= sizeof(struct phdr);
                    enqueue(&Hopper, dcbp->rxbufp);
#else
                    net_route(dcbp->iface,dcbp->rxbufp);
#endif
                    /* bump packets rreceived */
                    /* successfully */
                    dcbp->rxnqued++;
  
                    dcbp->rxbufp = tmpbufp;
                }
  
                /* No buffers available... */
                /* re-use old buffer - dump data */
                else dcbp->nobufs++;
  
                /* signal the process which */
                /* allocates the rx buffers */
                psignal(&Twin_bufpp[dcbp->dev],1);
  
            } /* end good packet > 10 long received */
  
        }       /* End of FRAME */
  
        /* Not EOF... check for overrun */
        else if (rx1stat & Rx_OVR)
        {
            /* Rx overrun - toss buffer */
            /* reset buffer pointers */
            dcbp->rxovercnt++;      /* bump overruns */
  
            /* read all data out of the rx fifo...*/
            /* and TOSS it.. */
            empty_scc(ctl,data);
  
            /* reuse old rx buffer.. */
            /* restart the receiver in HUNT  MODE... */
            /* to resync it... */
#ifdef TSYNC_DEBUG
            Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xf9));
#else
            Twin_write_scc(dcbp->zhwmap.ctl,R3,0xf9);
#endif
        }
  
        /* regardless of why we terminating/restarting... */
        /* make sure dcbp->rxbufp has a data buffer!!! */
        dcbp->rxcurp = dcbp->rxbufp->data;
#ifdef OLD_KA9Q
        dcbp->rxbufp->cnt = sizeof(struct phdr);
#else
        dcbp->rxbufp->cnt = 0;
#endif
  
  
        /* reset err latch */
#ifdef TSYNC_DEBUG
        Twin_write_scc(ctl,R0,(dcbp->wr0 = ERR_RES));
#else
        Twin_write_scc(ctl,R0,ERR_RES);
#endif
    }
}
  
  
  
  
  
/* read the SCC channel till no more data in receiver... */
void
empty_scc(ctl,data)
int16   ctl,data;
{
  
    while(Twin_read_scc(ctl,R0) & Rx_CH_AV)
    {
            /* get char and toss it */
        inportb(data);
    }
}
  
  
/*
 * DMA Receive ISR.
 * The first receive buffer is pre-allocated
 * in the init routine.  Thereafter, it is filled here, queued out, and a
 * new one acquired.  CRC, OVERRUN and TOOBIG errors merely 'rewind' the
 * pointers and reuse the same buffer.
 */
tsync_rxdmaisr(dcbp)
register DCB *dcbp;
{
    register int16 ctl;
    register int16 dmacnt;
    char rxstat;
    int16   rxbytecnt;
    struct mbuf *f_dequeavail();
    struct mbuf *tmpbufp;
  
    /* mask off this DMA channel */
    outportb(DMAWSMR,(dcbp->dma_rx_chan | 0x04));
  
    /* speed up access to 8530 regs */
    ctl = dcbp->zhwmap.ctl;
  
    /* Check status of receive interrupt */
    rxstat = Twin_read_scc(ctl,R1); /* get status byte from R1 */
  
    /* clear the DMA byte ptr FF */
    outportb(DMAFFCL,00);
  
    /* Rx overrun - toss data by reusing the buffer */
    if (rxstat & Rx_OVR ) dcbp->rxovercnt++;        /* bump overruns */
  
    /* The End of Frame bit is ALWAYS associated with a character,
     * usually, it is the last CRC char.  Only when EOF is true can
     * we look at the CRC byte to see if we have a valid frame
     */
  
    /* CRC error occurred; toss frame */
    else if ((rxstat & (CRC_ERR|END_FR)) == (CRC_ERR|END_FR) )
    {
        dcbp->rxpackcnt++;
        dcbp->rxcrcerrcnt++;
    }
    else if( rxstat & END_FR )
    {
        /* Disable further Abort interrupts  */
        Twin_write_scc(ctl,R15,(dcbp->wr15 &= ~(0x80)));
  
        dcbp->rxpackcnt++;
  
        /* calculate # of bytes received less CRC */
        dmacnt = inportb(dcbp->dma_rx_cnt_reg);
        dmacnt += (inportb(dcbp->dma_rx_cnt_reg) << 8);
  
  
        rxbytecnt = (dcbp->rxbufsize-1) - dmacnt;
  
        dcbp->rxbufp->cnt += rxbytecnt;
  
        /* # of data bytes received... Not including CRC's */
        dcbp->rxbytecnt += rxbytecnt-2;
  
        /* Make sure Rx was active */
#ifdef OLD_KA9Q
        if ( dcbp->rxbufp->cnt > sizeof(struct phdr) )
#else
            if ( dcbp->rxbufp->cnt > 0)
#endif
            {       /* then bytes have been received */
  
#ifdef OLD_KA9Q
                if ( dcbp->rxbufp->cnt >= (sizeof(struct phdr)+10) )
#else
                    if ( dcbp->rxbufp->cnt >= 10 )
#endif
                    {
                /* Got a valid packet !! */
  
                /* get next buffer */
                        tmpbufp = f_dequeavail((struct mbuf **)&dcbp->rxavailq, (int16 *)&dcbp->rxavailcount);
  
                        if ( tmpbufp != NULLBUF )
                        {
  
                    /* dump crc bytes, i.e. '-2' */
                            dcbp->rxbufp->cnt -= 2;
                    /* new for NOS */
                    /* adjust for packet header */
#ifdef OLD_KA9Q
                            dcbp->rxbufp->data -= sizeof(struct phdr);
                            enqueue(&Hopper, dcbp->rxbufp);
#else
                            net_route(dcbp->iface,dcbp->rxbufp);
#endif
                    /* bump packets rreceived */
                    /* successfully */
                            dcbp->rxnqued++;
  
                            dcbp->rxbufp = tmpbufp;
                        }
  
                /* No buffers available... */
                /* re-use old buffer - dump data */
                        else dcbp->nobufs++;
  
                /* signal the process which */
                /* allocates the rx buffers */
                        psignal(&Twin_bufpp[dcbp->dev],1);
  
                    } /* end good packet > 10 long received */
  
            }  /* received frame with > 0 chars */
  
    } /* end END_FR check */
  
    rxprime_dma(dcbp);
  
    return;
}
/*
 * end of tsync_rxdmaisr
 ****************************************************************/
  
void
rxprime_dma(dcbp)
register DCB *dcbp;
{
    register int16 ctl;
    ulong realaddr;
    unsigned page;
  
    /* speed up access to 8530 regs */
    ctl = dcbp->zhwmap.ctl;
  
    /* make sure dcbp->rxbufp has data!!! */
    dcbp->rxcurp = dcbp->rxbufp->data;
#ifdef OLD_KA9Q
    dcbp->rxbufp->cnt = sizeof(struct phdr);
#else
    dcbp->rxbufp->cnt = 0;
#endif
    /* reset any errors... */
#ifdef TSYNC_DEBUG
    Twin_write_scc(ctl,R0,(dcbp->wr0 = ERR_RES));
#else
    Twin_write_scc(ctl,R0,ERR_RES);
#endif
    /* Reprime the dma pump... */
  
    /* A rendition of an absolute address for the ptr */
    realaddr = ((ulong)FP_SEG(dcbp->rxcurp) << 4) +
    (ulong)FP_OFF(dcbp->rxcurp);
  
    /* DMA page reg for 8237 */
    page = (unsigned)(realaddr >> 16);
  
    outportb(dcbp->dma_rx_pagereg,page);    /* setup 64k page */
  
  
    /* mode setup required if we are in HDX operation */
    if(dcbp->hduplex == TRUE)
    {
        outportb(DMAMODE,dcbp->dma_rx_chan |  RX_DMA);
        outportb(Twin_io_base+DMA_CFG,dcbp->dma_rx_mode);
    }
  
    /* clear the DMA byte ptr FF */
    outportb(DMAFFCL,00);
  
    /* Max xfer count setup */
    outportb(dcbp->dma_rx_cnt_reg,dcbp->rxbufsize-1);
    outportb(dcbp->dma_rx_cnt_reg,((dcbp->rxbufsize-1)>>8));
  
    /* destination buffer address */
    outportb(dcbp->dma_rx_addr_reg,(realaddr & 0xff));
    outportb(dcbp->dma_rx_addr_reg,((realaddr >> 8) & 0xff));
  
    /* If we are in DMA mode/Half duplex: Turn on the recvr */
    /* Rx 8 bit/chars, enter hunt mode, CRC enable */
    /* Autoenables... */
    if(dcbp->hduplex == TRUE)
#ifdef TSYNC_DEBUG
        Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xf9));
#else
    Twin_write_scc(dcbp->zhwmap.ctl,R3,0xf9);
#endif
  
    /* Enable this DMA channel */
    outportb(DMAWSMR,dcbp->dma_rx_chan);
    return;
}
  
  
/*
 * PackTen's SCC transmit interrupt service routine
 */
void
tsync_txisr(dcbp)
register DCB  *dcbp;
{
    register int16 ctl;
    register struct drvbuf *btemp;
    int i_state;
  
#ifdef TSYNC_DEBUG
    int     tstbyt_cnt= 0;
#endif
  
    /* This routine MAY be able to run with ints ENABLED later... */
    /* We need to investigate... milt / {dgl} */
    /* NEVER NEVER NEVER!!!!!!!!! */
    i_state = dirps();
  
    /* speedup access to 8530 regs */
    ctl = dcbp->zhwmap.ctl;
  
    switch (dcbp->txstate) {
  
        case TX_ACTIVE:
  
        /* Here we are actively sending a frame */
        /* We should only hit this case in NON-DMA modes */
  
        /* If there are more bytes to send... */
            if( dcbp->cur_xbytes > 0 )
            {
            /* Try to feed the ESCC/SCC as many bytes as we can */
                while((Twin_read_scc(ctl,R0) & Tx_BUF_EMP) && (dcbp->cur_xbytes > 0))
                {
                /* send next char and bump the pointer */
                    outportb(dcbp->zhwmap.data,*dcbp->cur_xbufp++);
  
                /* tally data byte sent */
                    dcbp->txbytecnt++;
                    dcbp->cur_xbytes--;
#ifdef TSYNC_DEBUG
                    tstbyt_cnt++;
#endif
                }
  
#ifdef TSYNC_DEBUG
                if(tstbyt_cnt > dcbp->maxtxbytes)dcbp->maxtxbytes = tstbyt_cnt;
#endif
                if(dcbp->cur_xbytes <= 0)
                {
  
                /* turn-off the xmission of Abort on underrun */
                /* To allow the CRC to go out */
#ifdef TSYNC_DEBUG
                    if(dcbp->nrzi_flg == TRUE)
                        Twin_write_scc(ctl,R10,(dcbp->wr10 = 0xa0));
                    else Twin_write_scc(ctl,R10,(dcbp->wr10 = 0x80));
#else
                    if(dcbp->nrzi_flg == TRUE)
                        Twin_write_scc(ctl,R10,0xa0);
                    else Twin_write_scc(ctl,R10,0x80);
#endif
                /* Sent all the data bytes, now send CRCs */
                /* CRCING is a tsync_xstatisr state */
/*                              dcbp->txstate = CRCING;
*/
  
                }
            }
  
#ifdef notdef
            if( dcbp->cur_xbytes > 0 )
            {
  
                /* send next char and bump the pointer */
                outportb(dcbp->zhwmap.data,*dcbp->cur_xbufp++);
  
                /* tally data byte sent */
                dcbp->txbytecnt++;
  
                /* sent a byte */
                if((--dcbp->cur_xbytes) <= 0)
                {
  
                    /* turn-off the xmission of Abort on underrun */
                    /* To allow the CRC to go out */
#ifdef TSYNC_DEBUG
                    if(dcbp->nrzi_flg == TRUE)
                        Twin_write_scc(ctl,R10,(dcbp->wr10 = 0xa0));
                    else Twin_write_scc(ctl,R10,(dcbp->wr10 = 0x80));
#else
                    if(dcbp->nrzi_flg == TRUE)
                        Twin_write_scc(ctl,R10,0xa0);
                    else Twin_write_scc(ctl,R10,0x80);
#endif
                    /* Sent all the data bytes, now send CRCs */
                    /* CRCING is a tsync_xstatisr state */
/*                                      dcbp->txstate = CRCING;
*/
  
/* for slow PCs... */
                }
  
            }
#endif
  
            else
            {
  
#ifdef TSYNC_DEBUG
            /* reset Tx int pending  to allow the */
            /* Underrun/EOM to happen */
                Twin_write_scc(ctl,R0,(dcbp->wr0 = 0x28));
  
            /* disable tx ints... */
                Twin_write_scc(ctl,R1,(dcbp->wr1 = 0x11));
#else
            /* reset Tx int pending  to allow the */
            /* Underrun/EOM to happen */
                Twin_write_scc(ctl,R0,0x28);
  
            /* disable tx ints... */
                Twin_write_scc(ctl,R1,0x11);
#endif
  
            /* Sent all the data bytes, now send CRCs */
            /* CRCING is a tsync_xstatisr state */
                dcbp->txstate = CRCING;
  
            }
            break;
  
        case CRCING:
  
            /* reset Tx int pending  to allow the */
            /* Underrun/EOM to happen */
#ifdef TSYNC_DEBUG
            Twin_write_scc(ctl,R0,(dcbp->wr0 = 0x28));
#else
            Twin_write_scc(ctl,R0,0x28);
#endif
            break;
  
  
  
        case TX_DONE:   /* All done sending a packet... */
  
            tsync_txdone:
  
        /* for slow PCs... reenable the timer ints... */
            if ((Twin_cputype < 2) && (dcbp->baud >= 9600L)) maskon(0);
  
        /* sent a packet */
            dcbp->txpackcnt++;
  
        /* reset Tx int pending */
#ifdef TSYNC_DEBUG
            Twin_write_scc(ctl,R0,(dcbp->wr0 = 0x28));
#else
            Twin_write_scc(ctl,R0,0x28);
#endif
        /* If we were not called by mistake... */
        /* I.E. by an underrun with no current buffer... */
            if(dcbp->xmtq != (struct drvbuf *)NULLBUF)
            {
  
            /* done sending message so move on to next one */
                if ( dcbp->freem == (struct drvbuf *)NULLBUF )
                {
                    dcbp->freem = dcbp->xmtq;
                    dcbp->xmtq = dcbp->xmtq->next;
                    dcbp->freem->next = (struct drvbuf *)NULLBUF;
                }
                else
                {
                    btemp = dcbp->xmtq; /* addr of one to free */
                    dcbp->xmtq = btemp->next; /* next one TO BE xmitted */
                    btemp->next = dcbp->freem; /* one to be freed is queued */
                    dcbp->freem = btemp; /* now set free head ptr */
                }
  
            /* signal the process which is supposed to free the */
            /* transmitted message */
                psignal(&Twin_freetxpp[dcbp->dev],1);
  
            }
  
        /* Check NEW xmtq */
            if(dcbp->xmtq == (struct drvbuf *)NULLBUF)
            {
  
            /* no more msgs to send */
                dcbp->xmtqtail = (struct drvbuf *)NULLBUF;
  
            /* take care of SQUELCH DELAY wait for */
            /* flag byte to go out */
                dcbp->txstate = TX_KEYDOWN_DELAY;
  
#ifdef notdef
/* shouldn't need this... if we reset the tx ip bit */
            /* Turn off Tx ints */
                if(dcbp->dma_flg == FALSE)Twin_write_scc(ctl,R1,(dcbp->wr1 = 0x11));
                else Twin_write_scc(ctl,R1,(dcbp->wr1 = 0xf9));
  
#endif
  
                transon(dcbp,OFF);
            }
            else
            {
            /* immediately start sending the next msg */
                dcbp->cur_xbufp = &(dcbp->xmtq->buf);
                dcbp->cur_xbytes = dcbp->xmtq->msgsize;
                txprime_dma(dcbp);      /* start sending a frame */
  
            }
            break;
  
        case IDLE: /* Transmitter idle. Find a frame for transmission */
            if ( dcbp->xmtq == (struct drvbuf *)NULLBUF )
            {
            /* totally idle dudes */
  
            /* reset Tx int pending */
#ifdef TSYNC_DEBUG
                Twin_write_scc(ctl,R0,(dcbp->wr0 = 0x28));
#else
                Twin_write_scc(ctl,R0,0x28);
#endif
                break;
            }
            else
            {
            /* point to the next message to send */
                dcbp->cur_xbufp = &(dcbp->xmtq->buf);
                dcbp->cur_xbytes = dcbp->xmtq->msgsize;
  
                dcbp->txstate = DEFER; /* AND FALL THROUGH */
            }
        case DEFER:
  
        /* If we are in HALF-DUPLEX mode, then we must wait */
        /* for a free channel, and be polite about it (ppersist) */
        /* Otherwise just fire up the transmitter... */
  
            if(dcbp->hduplex == TRUE)
            {
  
           /* Check DCD so we don't step on a frame being received */
           /* DCD is ACTIVE LOW on the SCC DCD pin, but the bit in R0 */
           /* is SET when DCD is ACTIVE!! */
  
#ifdef notdef
/* This was the way it was released originally...  */
/* I changed it cause I think this is more robust, if we somehow dropped */
/* an external status event.... We now poll the CD line to determine if  */
/* we can send, and if needed, then we also will re_sync the extrn stats reg */
  
                if(dcbp->extreg & DCD)
#endif
                    if ( (Twin_read_scc(dcbp->zhwmap.ctl,R0) & DCD) != 0 )
                    {
  
                /* DGL 9/22/91 */
                /* sync_up the extreg, if it somehow gets out of sync */
                        if(!(dcbp->extreg & DCD)) dcbp->extreg |= DCD;
  
  
#ifdef TSYNC_DEBUG_PRT
                        printf("TSYNC_TXISR: Device num %d DEFER state DCD is not right to xmit \n", dcbp->dev);
#endif
                        psignal(&Twin_kickpp[dcbp->dev],1);
                        break;
                    }
  
           /* DCD is down */
                if ( (dcbp->ioctlparams->persist & 0x00ff) <= (rand() & 0x00ff) )
                { /* then have to wait, be a less persistent */
                    dcbp->txstate = TX_PERSIST;
                    time_wait(dcbp,dcbp->timer1, dcbp->ioctlparams->slotime, tsync_persist,(long)dcbp);
                    break;
                }
  
            }       /* end of Half duplex carrier handling... */
  
        /* Raise RTS and start transmiting */
        /* txstate is changed by the routine, but when */
        /* tsync_txisr is called next the state will be KEYED_UP */
            dcbp->txstate = TX_KEYUP_DELAY;
            transon(dcbp,ON);
            break;
  
        case KEYED_UP:
        /* RTS is ACTIVE or in radio terms the radio is keyed */
        /* and transmiting */
  
            txprime_dma(dcbp);      /* start sending a frame */
        /* chars will be going out */
            break;
  
  
        default:
#ifdef TSYNC_DEBUG_PRT
            printf("DEBUG tsync_txisr: invalid txstate, %d \n\r", dcbp->txstate);
#endif
  
                /* reset Tx int pending */
#ifdef TSYNC_DEBUG
            Twin_write_scc(ctl,R0,(dcbp->wr0 = 0x28));
#else
            Twin_write_scc(ctl,R0,0x28);
#endif
            break;
  
        /* end switch */
    }
  
    restore(i_state);
    return;
}
  
void
txprime_dma(dcbp)
register DCB *dcbp;
{
    register int16 ctl;
    ulong realaddr;
    unsigned page;
  
    /* speed up access to 8530 regs */
    ctl = dcbp->zhwmap.ctl;
  
    /* if we are NOT DMA tx driven */
    if(dcbp->dma_flg == FALSE)
    {
  
        /* reset CRC for next frame */
#ifdef TSYNC_DEBUG
        Twin_write_scc(ctl,R0,(dcbp->wr0 = RES_Tx_CRC));
  
        /* reset Tx int pending */
        Twin_write_scc(ctl,R0,(dcbp->wr0 = 0x28));
#else
        Twin_write_scc(ctl,R0,RES_Tx_CRC);
  
        /* reset Tx int pending */
        Twin_write_scc(ctl,R0,0x28);
#endif
        /* Get First char to send */
        /* send the transmission char and bump the pointer */
  
        outportb(dcbp->zhwmap.data,*dcbp->cur_xbufp++);
  
        dcbp->txstate = TX_ACTIVE;
  
        /* sent one byte */
        dcbp->cur_xbytes--;
  
#ifdef TSYNC_DEBUG
        /* Rx/Tx and Extern ints on */
        Twin_write_scc(ctl,R1,(dcbp->wr1 = 0x13));
  
        /* Reset Underrun/EOM latch */
        Twin_write_scc(ctl,R0,(dcbp->wr0 = 0xc0));
  
        /* turn-on the xmission of Abort on underrun */
        if(dcbp->nrzi_flg == TRUE)Twin_write_scc(ctl,R10,(dcbp->wr10 = 0xa4));
        else Twin_write_scc(ctl,R10,(dcbp->wr10 = 0x84));
#else
        /* Rx/Tx and Extern ints on */
        Twin_write_scc(ctl,R1,0x13);
  
        /* Reset Underrun/EOM latch */
        Twin_write_scc(ctl,R0,0xc0);
  
        /* turn-on the xmission of Abort on underrun */
        if(dcbp->nrzi_flg == TRUE)Twin_write_scc(ctl,R10,0xa4);
        else Twin_write_scc(ctl,R10,0x84);
  
#endif
  
/* 4/18/91 */
        /* clear the TxEOM bit in the image */
        dcbp->extreg &= ~TxEOM;
  
        /* allow Underruns to interrupt us */
        Twin_write_scc(ctl,R15,(dcbp->wr15 |= 0x40));
  
        /* for slow PCs... mask out the timer ints... */
        /* This will cause the pc to lose time during xmissions... */
        /* but seems only way to make it work at 9600 */
        if ((Twin_cputype < 2) && (dcbp->baud >= 9600L)) maskoff(0);
  
    }
    else
    {
  
        /* Reprime the tx dma pump... */
  
        /* A rendition of an absolute address for ptr */
        realaddr = ((ulong)FP_SEG(dcbp->cur_xbufp) << 4)
        + (ulong)FP_OFF(dcbp->cur_xbufp);
  
        /* Disable this DMA channel */
        outportb(DMAWSMR,dcbp->dma_tx_chan | 0x04);
  
        /* DMA page reg for 8237 */
        page = (unsigned)(realaddr >> 16);
  
        /* setup 64k page */
        outportb(dcbp->dma_tx_pagereg,page);
  
        /* mode setup required if we are in HDX */
        if(dcbp->hduplex == TRUE)
        {
            outportb(DMAMODE,dcbp->dma_tx_chan |  TX_DMA);
            outportb(Twin_io_base+DMA_CFG,dcbp->dma_tx_mode);
        }
  
        /* clear the DMA byte ptr FF */
        outportb(DMAFFCL,00);
  
        /* Max xfer count setup */
        outportb(dcbp->dma_tx_cnt_reg,dcbp->cur_xbytes-1);
        outportb(dcbp->dma_tx_cnt_reg,((dcbp->cur_xbytes-1)>>8));
  
        /* destination buffer address */
        outportb(dcbp->dma_tx_addr_reg,(realaddr & 0xff));
        outportb(dcbp->dma_tx_addr_reg,((realaddr >> 8) & 0xff));
  
        /* reset CRC for next frame */
#ifdef TSYNC_DEBUG
        Twin_write_scc(ctl,R0,(dcbp->wr0 = RES_Tx_CRC));
#else
        Twin_write_scc(ctl,R0,RES_Tx_CRC);
#endif
        /* Enable this DMA channel */
        outportb(DMAWSMR,dcbp->dma_tx_chan);
        nop;
        nop;
  
        dcbp->txstate = TX_ACTIVE;
  
        /* Reset Underrun/EOM latch */
#ifdef TSYNC_DEBUG
        Twin_write_scc(ctl,R0,(dcbp->wr0 = 0xc0));
#else
        Twin_write_scc(ctl,R0,0xc0);
#endif
        /* allow Underruns to interrupt us */
        Twin_write_scc(ctl,R15,(dcbp->wr15 |= 0x40));
  
        /* clear the TxEOM bit in the image */
        dcbp->extreg &= ~TxEOM;
  
#ifdef TSYNC_DEBUG
        /* Rx and Extern ints on : No Tx ints */
        Twin_write_scc(ctl,R1,(dcbp->wr1 = 0xf9));
  
        /* reset Tx int pending */
        Twin_write_scc(ctl,R0,(dcbp->wr0 = 0x28));
#else
        /* Rx and Extern ints on : No Tx ints */
        Twin_write_scc(ctl,R1,0xf9);
  
        /* reset Tx int pending */
        Twin_write_scc(ctl,R0,0x28);
#endif
  
    }
}
  
/*
 * Transmit and Receive Control
 * SET Transmit or Receive Mode
 * Set RTS (request-to-send) to modem on Transmit
 */
void
transon(dcbp, onoff)
register DCB *dcbp;
int16 onoff;
{
    register int16  ctl;
  
    /* speed-up access to the SCC's control register */
    ctl = dcbp->zhwmap.ctl;
  
    /* Turn on transmitter to send flags */
    if ( onoff == ON )
    {
  
  
        /* If we are in DMA mode/Half duplex: Turn off the recvr */
        /* Rx 8 bit/chars, enter hunt mode, CRC enable */
#ifdef notdef
/* NOT needed with autoenables */
        Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xd8));
#endif
  
        /* flags during KEYUP cause the other guy to sync up while */
        /* he sees garbage*/
#ifdef TSYNC_DEBUG
        if(dcbp->nrzi_flg == TRUE) Twin_write_scc(ctl,R10,(dcbp->wr10 = 0xa0));
        else Twin_write_scc(ctl,R10,(dcbp->wr10 = 0x80));
  
        /* Effectively just activate RTS */
        /* Which is used to key the "radio transmitter" */
        Twin_write_scc(dcbp->zhwmap.ctl,R5,(dcbp->wr5 = 0x6b));
  
#else
        if(dcbp->nrzi_flg == TRUE) Twin_write_scc(ctl,R10,0xa0);
        else Twin_write_scc(ctl,R10,0x80);
  
        /* Effectively just activate RTS */
        /* Which is used to key the "radio transmitter" */
        Twin_write_scc(dcbp->zhwmap.ctl,R5,0x6b);
  
#endif
        /*
         * Transmitter now on
         */
  
        /* Delay after Tx on */
  
        /* Handle the special case of NO keyup delay... */
        if(dcbp->ioctlparams->xmitdelay == 0)tsync_txondelay(dcbp);
  
        /* otherwise set up the keyup timer... */
        else
            time_wait(dcbp,dcbp->timer1, dcbp->ioctlparams->xmitdelay, tsync_txondelay,(long)dcbp);
  
  
    }
  
    else
    {       /* Tx OFF and Rx ON */
  
        /* delay for squelch tail before enable of Rx */
  
        /* Handle the special case of NO keydown delay... */
        if(dcbp->ioctlparams->squelch == 0)tsync_sqdelay(dcbp);
  
        /* otherwise set up the keydown timer... */
        else
            time_wait(dcbp,dcbp->timer1, dcbp->ioctlparams->squelch, tsync_sqdelay,(long)dcbp);
  
    }
    return;
}
/*
 * end of transon
 ********************************************************************/
  
/*
 *  Called by the timer isr at an interrupt level.
 *  Persist time is up.
 */
void tsync_persist(dcbp)
DCB *dcbp;
{
    /* new for NOS */
  
    /* persist time is up, have NOS process check for transmit */
    /* by changing the driver's transmit state */
    dcbp->txstate = DEFER;
  
  
    /* AND signal the process which is supposed to kick  */
    /* the transmitter */
    psignal(&Twin_kickpp[dcbp->dev],1);
  
    return;
}
  
/*
 *  Called by the timer isr at an interrupt level.
 *  Transmitter on delay is up.
 */
void tsync_txondelay(dcbp)
DCB *dcbp;
{
    /* transmiter is ON and can start sending */
    dcbp->txstate = KEYED_UP;
    tsync_txisr(dcbp);
    return;
}
  
/*
 *  Called by the timer isr at an interrupt level.
 *  Squelch delay is up.
 */
void tsync_sqdelay(dcbp)
DCB *dcbp;
{
    register int16  ctl;
  
  
    /* We are going to shut down the "radio" transmitter.    */
    /* REGARDLESS of whether we now have a new frame to send */
    /* or not...  Because if we DO have more to send, we     */
    /* set txstate to DEFER, and let the tsync_recv routine     */
    /* kick us off again from the start, and if we don't     */
    /* have more to send we go to IDLE mode.                 */
  
    /* speed-up access to the SCC's control register */
    ctl = dcbp->zhwmap.ctl;
  
    /* Send MARK on IDLE to make sure other guy aborts any trailing */
    /* garbage */
    /* EXCEPT in NRZI... */
#ifdef TSYNC_DEBUG
    if(dcbp->nrzi_flg == TRUE) Twin_write_scc(ctl,R10,(dcbp->wr10 = 0xa0));
    else Twin_write_scc(ctl,R10,(dcbp->wr10 = 0x88));
  
    Twin_write_scc(ctl,R5,(dcbp->wr5 = 0x68));      /* TX off now */
                                            /* I.E. drop RTS */
#else
    if(dcbp->nrzi_flg == TRUE) Twin_write_scc(ctl,R10,0xa0);
    else Twin_write_scc(ctl,R10,0x88);
  
    Twin_write_scc(ctl,R5,0x68);    /* TX off now */
                                            /* I.E. drop RTS */
#endif
    /* re-prime the dma driver if necessary */
    /* Necessary because we took the SINGLE dma channel away */
    /* and used it to transmit... now give it back to receiver */
  
    if((dcbp->dma_flg == TRUE) && (dcbp->hduplex == TRUE))rxprime_dma(dcbp);
  
    /* perhaps while waiting for the squelch delay tsync_raw was */
    /* called and there is a buffer to send */
  
    if ( dcbp->xmtq != (struct drvbuf *)NULLBUF )
    { /* then start the transmit process */
      /* but not at an interrupt level!! */
      /* now when tsync_recv is called, tx_isr will run */
        dcbp->cur_xbufp = &(dcbp->xmtq->buf);
        dcbp->cur_xbytes = dcbp->xmtq->msgsize;
  
        /* new for NOS */
        /* signal the process which is supposed to kick  */
        /* the transmitter */
        dcbp->txstate = DEFER;
        psignal(&Twin_kickpp[dcbp->dev],1);
    }
    else
    { /* now only when tsync_raw runs will the transmit */
      /* process go on */
  
        dcbp->txstate = IDLE;
    }
  
    return;
}
  
  
/* This is the driver timer service.  */
/* It's resolution is in 1 msec intervals */
  
void
time_wait(dcbp,timer,msecs,timerfunc,arg)
DCB *dcbp;
struct drv_timer *timer;
int16   msecs;
void (*timerfunc)();
unsigned long arg;
{
    int16   hrd_addr;
  
    /* calculate the timer counter reg address */
    hrd_addr = Twin_io_base + TMR_CNT1 + dcbp->dev;
  
    timer->thandler = timerfunc;
    timer->targ = arg;
  
    /* start the timer */
    outportb(hrd_addr,(uchar)(msecs & 0xff));
    nop;
    nop;
    outportb(hrd_addr,(uchar)(msecs >> 8));
    nop;
    nop;
}
  
  
/***********************************************************
 * GRACILIS - processes to manage buffer maintenance for the
 *            PackeTwin synchronous drivers.  These processes
 *                        are used to insure that buffer maintenance is
 *                        not performed at interrupt time.
 ***********************************************************/
  
/* This process is used to pre-allocate receive    */
/* buffers for the 8530 synchronous driver to use. */
void
tsync_rxavget(dev, p1, p2)
int     dev;                    /* device number */
void    *p1;                    /* not used */
void    *p2;                    /* not used */
{
    register int i_state;
    DCB *dcbp;
    int16 cur_rxavail, min_rxavail;
  
  
    /* killself if device is not attached yet */
    if  (dev > 1)           /* only support 0 through 1 */
        return;
    else if (Twin_udcb[dev].attached == FALSE)
        return;
  
    for (;;)
    {
  
        /* wait for the driver to signal it needs */
        /* a look at its pre-allocated receive buffers */
        pwait(&Twin_bufpp[dev]);
  
        dcbp = (DCB *)Twin_udcb[dev].dcbp;
  
        i_state = dirps();
  
        cur_rxavail = dcbp->rxavailcount;
        min_rxavail = dcbp->ioctlparams->rxavailbufs;
  
        restore(i_state);
  
  
        /* see if more receive buffers need allocating */
        if ( cur_rxavail < min_rxavail )
        {
            f_getavail((struct mbuf **)&(dcbp->rxavailq),
            (int16)(min_rxavail - cur_rxavail),
            (int16)dcbp->rxbufsize,
            (int16 *)&(dcbp->rxavailcount),
            (struct iface *)dcbp->iface,
            (bool)dcbp->dma_flg);
        }
    } /* end of for(;;) loop */
  
} /* end of tsync_rxavget() */
  
  
  
  
/* This process is used to periodically kick */
/* the PackeTwin synchronous driver whenever */
/* it is in the DEFERED state  */
void
tsync_txkick(dev, p1, p2)
int     dev;                    /* device number */
void    *p1;                    /* not used */
void    *p2;                    /* not used */
{
    register DCB *dcbp;
    register bool pwait_null = FALSE;       /* 1st time wait for driver's psignal */
    void tsync_txisr();
  
  
    /* killself if device is not attached yet */
    if  (dev > 1)           /* only support 0 through 1 */
        return;
    else if (Twin_udcb[dev].attached == FALSE)
        return;
  
    for (;;)
    {
  
        /* wait for the driver to signal it needs */
        /* kick */
        if ( pwait_null )
            pwait(NULL);                    /* have NOS provide */
                            /* the delay needed */
        else
            pwait(&Twin_kickpp[dev]);
  
        /* driver control block */
        dcbp = (DCB *)Twin_udcb[dev].dcbp;
  
        /*  Test for DEFERED transmit state */
        if (dcbp->txstate == DEFER)
        {
            tsync_txisr(dcbp);
            if ( dcbp->txstate == DEFER )
                pwait_null = TRUE;      /* then driver still */
                            /* needs a kick */
            else
                pwait_null = FALSE;
        }
  
    } /* end of for(;;) loop */
  
} /* end of tsync_txkick() */
  
  
  
/* This process is used to free buffers */
/* which have been transmitted by the driver. */
void
tsync_freetx(dev, p1, p2)
int     dev;                    /* device number */
void    *p1;                    /* not used */
void    *p2;                    /* not used */
{
    register int i_state;
    register DCB *dcbp;
    register struct drvbuf *xbufp, *btemp;
  
  
    /* killself if device is not attached yet */
    if  (dev > 4)           /* only support 0 through 4 */
        return;
    else if (Twin_udcb[dev].attached == FALSE)
        return;
  
    for (;;)
    {
  
        /* wait for driver to signal that there are transmitted */
        /* messages to be freed */
        pwait(&Twin_freetxpp[dev]);
  
        /* driver control block */
        dcbp = (DCB *)Twin_udcb[dev].dcbp;
  
        /* Do not do memory free-ups while interrupts are */
        /* disabled! Here xbufp is a "to be free'd"  */
        /* queue pointer" */
  
        i_state = dirps();
        xbufp = dcbp->freem;
        dcbp->freem = NULLBUF;
        restore(i_state);
  
        /* now free memory of all the xmitted messages */
        while ( xbufp != (struct drvbuf *)NULL )
        {
            btemp = xbufp->next;
            free((char *)xbufp);
            xbufp = btemp;
        }
    } /* end of for(;;) loop */
  
} /* end of tsync_freetx() */
  
#endif
