/****************************************************************************
*
*            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_at.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_attach - Performs attach functions for TCP/IP, i.e.
*                   attach the tsync_driver to TCP/IP.
*
*    do_tsync_stat  - Applications' routine for "displaying" the
*                     tsync_driver statistics.
*
*    tsync_ioctl  - Set the IO Control parameters.
*
*
*/
  
  
#include <dos.h>
#include "global.h"
#ifdef PACKETWIN
#include "hardware.h"
#include "mbuf.h"
#include "iface.h"
#include "ax25.h"
#include "trace.h"
#include "netuser.h"
#include "proc.h"
#include "pktdrvr.h"
#include "devparam.h"
#include "z8530.h"
#include "gracilis.h"
#include "twin.h"
  
/* If using the Borland stdio library... */
#ifdef  OLD_KA9Q
#else
/* If using the new KA9Q stdio library... */
#define tprintf printf
#endif
  
  
/*******************************************************************
*                        TSYNC_ATTACH                              *
*                                                                  *
* File: tsync_at.c                                                 *
* Rev : 3.0                                                        *
*                                                                  *
********************************************************************
*
*
* DESCRIPTION:
* -----------
* Attaches the PackeTwin's synchronous SCC driver to TCP/IP.
*
* ARGUMENTS:
* ---------
*    All argv's are strings
* argv[0]:  hardware type,  "tsync"
* argv[1]:  "0" = 8530 SCC A
*           "1" = 8530 SCC B
* argv[2]:  vector, Interrupt request line #
*
* argv[3]:  IOBASE : base address for PackeTwin default 0x230
*
* argv[4]:  "HDX"  half-duplex
*           "FDX"  full-duplex
*
* argv[5]:  mode, DMA/INTS for channel 1, MUST be INTS
*
* argv[6]:  Dma channel to use for receive...
*           0 = none...
*           1 = Rx on dma channel 1 and if FULL DUPLEX tx on channel 3
*           3 = Rx on dma channel 3 and if FULL DUPLEX tx on channel 1
*
* argv[7]:  interface label
*
*           This label is provided by the user and the attach uses
*           it in its tprintf's to tell the user which interface
*           is being talked about.
*
* argv[8]:  max receive packet buffer  size in bytes
* argv[9]:  max xmit packet buffer size in bytes
* argv[10]: interface baud rate, e.g, "9600"
* argv[11]: OPTIONAL - clocking options
* argv[12]: OPTIONAL - clocking options
* argv[13]: OPTIONAL - Data Encoding: NRZ/NRZI
* argv[14]: OPTIONAL - IP address - default is Ip_addr
* argv[15]: OPTIONAL - number of RX buffers to pre-allocate
*
* RETURN VALUES:
* -------------
*  0 - success
* -1 - failure, driver not attached and no memory left allocated
*
* GLOBAL INPUTS:
* -------------
*
* GLOBAL OUTPUTS:
* --------------
*
* FUNCTIONS CALLED:
* ----------------
* tprintf
* calloc
* malloc
* tsync_hwinit
* free
*
* IO:
* --
*
*
******************************************************************/
  
  
  
                                    /* Extern declaration for the */
extern UNI_DCB  Twin_udcb[];            /* array of universal control */
                                    /* blocks for this driver */
  
/* interrupt handler */
extern INTERRUPT twinvec();
  
extern INTERRUPT (*Twin_oldvec) __ARGS((void)); /* Old vector contents */
extern INTERRUPT (*Twin_handler) __ARGS((void));
  
extern  int Twin_io_base;
  
  
extern  int16   Twin_vector;
extern  int             Twin_cputype;           /* Global to identify the machine type */
                    /* 0 = 8086             */
                    /* 2 = 80286            */
                    /* 3 = 80386            */
  
void enable_escc __ARGS((DCB *));
void tsync_loopback __ARGS((DCB *,char));
  
  
int
tsync_attach(argc,argv,p)
int argc;
char *argv[];
void *p;                                /* new for NOS */
{
    extern void tsyncvec();
  
    extern struct iface *ifaces;
    int i_state;
    int32   ipaddr;                 /* new for NOS - IP address */
    int     i;
    char    tmpbuf[80];
    register struct iface *intfc_p;
    DCB *dcbp;
    UNI_DCB *udcbp;
    int udcb_dev;
    int16 rbufsize, xbufsize;
    int     rxdmachan,txdmachan;
    struct drv_timer *timer1;
    unsigned char far *cpusig_ptr;  /* ptr to CPU signature byte in rom */
  
    bool    hdxflg,dmaflg;
#ifdef OLD_KA9Q
    struct phdr *hdr;               /* new for NOS - packet header*/
#endif
  
    void tsync_recv(), tsync_txisr(), tsync_rxisr(), tsync_xstatisr();
  
    int32 tsync_ioctl();
    int tsync_send();
    int tsync_output();
    int tsync_stop();
    int tsync_raw();
    int ax_send();
    int ax_output();
  
  
  
    /* Try reading the Machine ID in ROM... */
    cpusig_ptr = (unsigned char *)MK_FP(0xf000,0xfffe);
  
    if((*cpusig_ptr == 0xfc) || (*cpusig_ptr == 0xf8))
        Twin_cputype = 2;               /* 80286 or better */
  
    else            Twin_cputype = 0;               /* Default to type 8086 */
  
  
#ifdef TSYNC_DEBUG_PRT
    printf("tsync_attach: argument count is %d\n",argc);
    for(i=0;i<argc;i++)
        printf("arg #%d: %s\n",i,argv[i]);
    printf("\n\n");
#endif
  
  
    /*
     * validate input params
     */
    if ( argc < 11 )
    {
        tprintf("tsync_attach: too few arguments (%d) \n", argc);
  
        tprintf("tsync <chan> <vec> <iobase> <hdx|fdx> <dma|ints> <rx dmachan>\n");
        tprintf("      <name> <rxsize> <txsize> <speed> [txclk] [rxclk] [nrz|nrzi] \n");
        tprintf("      [ipaddr] [#rxbufs]\n");
        return(1); /* should this be a -1, milt milt */
    }
  
    /* get the PackeTwin device number/address
     * and validate it for a synchronous driver
     */
    udcb_dev = htoi(argv[1]);
  
    /* range check device number */
    if ( (udcb_dev < 0) || (udcb_dev > 1) )
    {
        tprintf("tsync_attach error: Channel number %x out of range!\n", udcb_dev);
        return(-1);
    }
  
    udcbp = &Twin_udcb[udcb_dev];   /* dev num is index number!!! */
  
    /* See if already attached */
    if ( udcbp->attached  == TRUE )
    {
        tprintf("tsync_attach error: Channel %s already attached; interface is %s", argv[1], argv[7]);
        return(-1);
    }
  
  
    if ( if_lookup(argv[7]) != NULLIF )     /* new for NOS */
    {
        tprintf("Interface %s already exists \n", argv[7]);
        return(-1);
    }
  
    Twin_vector = htoi(argv[2]);
  
    if( Twin_vector <= 0 || Twin_vector > 15)
    {
        tprintf("Illegal vector specified: %s\n", argv[2]);
        return(-1);
  
  
    }
  
    if((htoi(argv[3])) >= 0x100)
        Twin_io_base = htoi(argv[3]);
    else
    {
        tprintf("Illegal iobase specified: %s\n", argv[3]);
        return(-1);
    }
  
    if(strcmpi(argv[4],"hdx") == 0) hdxflg = TRUE;
    else if(strcmpi(argv[4],"fdx") == 0) hdxflg = FALSE;
    else
    {
        tprintf("tsync_attach error: Specified %s instead of \"hdx\" or \"fdx\" for interface %s\n",
        argv[4],argv[7]);
        return(-1);
    }
  
    /* Are we in DMA mode? */
    if (strcmpi(argv[5],"dma") == 0)
    {
        dmaflg = TRUE;
        rxdmachan = htoi(argv[6]);
  
            /* Did they enter a valid dma channel #? */
        if((rxdmachan != 3) && (rxdmachan != 1))
        {
                /* NO... bad channel # */
            tprintf("tsync_attach error: Specified dma channel: %d instead of 1 or 3 for interface %s\n",
            rxdmachan,argv[7]);
            return(-1);
        }
  
            /* YES valid channel # */
        else
        {
            if(hdxflg == FALSE)
            {
                if(rxdmachan == 1)txdmachan = 3;
                else    txdmachan = 1;
            }
            else txdmachan = rxdmachan;
        }
    }
  
    /* in interrupt mode? */
    else if (strcmpi(argv[5],"ints") == 0)dmaflg = FALSE;
    else
    {
        tprintf("tsync_attach error: Specified %s instead of \"dma\" or \"ints\" for interface %s\n",
        argv[5],argv[7]);
        return(-1);
    }
  
    /* Can't specify DMA for port B */
    if((dmaflg == TRUE) && (udcb_dev != 0))
    {
        tprintf("tsync_attach error: DMA is not supported on channel 1!\n");
        return(-1);
    }
  
    if ( (rbufsize = atoi(argv[8])) == 0 )
    {
        tprintf("tsync_attach error: Receive buffer size error %s, interface %s\n",
        argv[8],argv[7]);
        return(-1);
    }
  
    if ( (xbufsize = atoi(argv[9])) == 0 )
    {
        tprintf("tsync_attach error: Transmit buffer size error %s, interface %s\n",
        argv[9],argv[7]);
        return(-1);
    }
  
  
    /* new for NOS, Set the IP address */
    ipaddr = Ip_addr;                       /* default value */
    if ( argc >= 15 )
        ipaddr = resolve(argv[14]);
    if ( ipaddr == 0 )
    {
        tprintf(Noipaddr);
        return(-1);
    }
  
  
    /*
     * try to allocate necessary memory
     */
    intfc_p = (struct iface *)calloc(1,sizeof(struct iface));
    if ( intfc_p !=  (struct iface *)NULLBUF )
    {
        intfc_p->name = malloc((unsigned int)strlen(argv[7])+2);
        if (intfc_p->name != (char *)NULLBUF )
        {
            /*  device hw addr  */
            intfc_p->hwaddr = malloc(AXALEN);
            if ( intfc_p->hwaddr == NULLCHAR )
            {
                free(intfc_p->name);
                free(intfc_p);
                tprintf("tsync_attach error: no memory for interface hwaddr, size of sizeof(struct mycall) bytes, interface %s", argv[7]);
                return(-1);
            }
  
        }
        else
        {
            free(intfc_p);
            tprintf("tsync_attach error: no memory for interface name, size 7 bytes, interface %s", argv[7]);
            return(-1);
        }
    }
    else
    {
        tprintf("tsync_attach error: no memory for interface struct, size of sizeof(struct iface), interface %s", argv[7]);
        return(-1);
    }
  
    /* get the timer control blocks */
    /* must have zeros in structure, so use calloc */
    if ( (timer1 = (struct drv_timer *)calloc(1,sizeof(struct drv_timer))) == (struct drv_timer *)NULLBUF )
    {
        free(intfc_p->hwaddr);
        free(intfc_p->name);
        free(intfc_p);
        tprintf("tsync_attach error: no memory for timer control block structure, size of sizeof(struct drv_timer), interface %s", argv[7]);
        return(-1);
    }
  
  
    dcbp = (DCB *)calloc(1,sizeof(DCB));    /* zero-out the dcb */
    if ( dcbp != (DCB *)NULLBUF )
    {
        dcbp->rxbufsize = rbufsize;
#ifdef OLD_KA9Q
        dcbp->rxbufp = alloc_mbuf(rbufsize+sizeof(struct phdr));
#else
        dcbp->rxbufp = alloc_mbuf(rbufsize);
#endif
        if (dcbp->rxbufp != NULLBUF )
        {
            dcbp->ioctlparams = (struct tsync_ioctl *)malloc(sizeof(struct tsync_ioctl));
            if ( dcbp->ioctlparams == (struct tsync_ioctl *)NULLBUF )
            {
                free(timer1);
                free(intfc_p->hwaddr);
                free(intfc_p->name);
                free(intfc_p);
                free(dcbp->rxbufp);
                free(dcbp);
                tprintf("tsync_attach error: no memory for IOCTL params size %x bytes, interface %s", sizeof(struct tsync_ioctl), argv[7]);
                return(-1);
            }
        }
        else
        {
            free(timer1);
            free(intfc_p->hwaddr);
            free(intfc_p->name);
            free(intfc_p);
            free(dcbp);
            tprintf("tsync_attach error: no memory for receive buffer, size %x bytes, interface %s", rbufsize, argv[7]);
            return(-1);
        }
    }
    else
    {
        free(timer1);
        free(intfc_p->hwaddr);
        free(intfc_p->name);
        free(intfc_p);
        tprintf("tsync_attach error: no memory for device control block structure, size of sizeof(DCB), interface %s", argv[7]);
        return(-1);
    }
  
  
    if ( Mycall[0] == '\0' )
    {
        free(timer1);
        free(intfc_p->hwaddr);
        free(intfc_p->name);
        free(intfc_p);
        free(dcbp->rxbufp);
        free(dcbp);
        tprintf("tsync_attach error: mycall not set\n");
        return(-1);
    }
  
  
    /*
     *  Universal Driver Control Block Init -
     *  Except for the saving of old vectors and attached field.
     *  Attached field is saved for the very end.
     */
    udcbp->type = SYNC_8530;                        /* 8530 scc */
  
    /* set address of the unit's driver control block */
    udcbp->dcbp = (char *)dcbp;
  
    /* setup this device's interrupt handlers */
    udcbp->prev_vec2 = tsync_txisr;
    udcbp->prev_vec3 = tsync_rxisr;
    udcbp->prev_vec4 = tsync_xstatisr;
  
  
    /*
     * Setup the interface structure and link it to the list
     * of interface structures.
     */
    /* the Ascii name of the driver unit */
    strcpy(intfc_p->name,argv[7]);
  
    /* set the device number, must be able to tell whether */
    /* channel A or channel B is being used */
    dcbp->dev = udcb_dev;
    switch ( udcb_dev )
    {
        case TWINCOMM1:
            dcbp->zhwmap.ctl = (int16)(Twin_io_base+SCCA_CMD);
            dcbp->zhwmap.data = (int16)(Twin_io_base+SCCA_DATA);
            break;
  
        case TWINCOMM2:
            dcbp->zhwmap.ctl = (int16)(Twin_io_base+SCCB_CMD);
            dcbp->zhwmap.data = (int16)(Twin_io_base+SCCB_DATA);
            break;
  
    }
  
  
/* not for NOS  intfc_p->recv = tsync_recv;
*/
    intfc_p->mtu = xbufsize - MAX_AX25_HDR_LEN;
    intfc_p->ioctl =  tsync_ioctl;
    intfc_p->raw = tsync_raw;
    intfc_p->addr = ipaddr;                 /* new for NOS */
    intfc_p->stop = tsync_stop;
    intfc_p->dev = udcb_dev;
  
    setencap(intfc_p,"AX25");
  
    /* For packet radio put in the sender's "call letters" */
    memcpy(intfc_p->hwaddr,Mycall,AXALEN);
  
    /* lastly, but very importantly, put in the LL of interfaces */
    intfc_p->next = Ifaces;
    Ifaces = intfc_p;
  
  
    /*
     * Initialize the unit's driver control block
     */
    dcbp->iface = intfc_p;          /* new for NOS - save the iface */
                    /* address for use in queueing */
                    /* mbufs onto Hopper */
    dcbp->txstate = IDLE;
    dcbp->xmtq = (struct drvbuf *)NULLBUF;
    dcbp->xmtqtail = (struct drvbuf *)NULLBUF;     /* milt */
    dcbp->freem = (struct drvbuf *)NULLBUF;       /* milt */
  
    dcbp->rxstate = IDLE;
    dcbp->rxbufsize = atoi(argv[8]);
  
/*milt 3/15/91
    dcbp->rxqhat.headp = NULLBUF;
    dcbp->rxqhat.tailp = NULLBUF;
*/
  
    /* Configure DMA stuff */
    dcbp->dma_flg = dmaflg;
    if(dmaflg == TRUE)
    {
  
        dcbp->dma_rx_chan = rxdmachan;
        dcbp->dma_tx_chan = txdmachan;
  
        switch(dcbp->dma_tx_chan)
        {
            case 1: dcbp->dma_tx_pagereg = 0x83;
                dcbp->dma_tx_cnt_reg = DMA1CNT;
                dcbp->dma_tx_addr_reg = DMA1ADR;
                break;
            case 3: dcbp->dma_tx_pagereg = 0x82;
                dcbp->dma_tx_cnt_reg = DMA3CNT;
                dcbp->dma_tx_addr_reg = DMA3ADR;
                break;
        }
  
        switch(dcbp->dma_rx_chan)
        {
            case 1: dcbp->dma_rx_pagereg = 0x83;
                dcbp->dma_rx_cnt_reg = DMA1CNT;
                dcbp->dma_rx_addr_reg = DMA1ADR;
                break;
            case 3: dcbp->dma_rx_pagereg = 0x82;
                dcbp->dma_rx_cnt_reg = DMA3CNT;
                dcbp->dma_rx_addr_reg = DMA3ADR;
                break;
        }
  
        /* SET up of the mode reg values for the DMA channels to be used */
        if(hdxflg == FALSE)
        {
            /* FULL DUPLEX the values never change between rx/tx */
            if(rxdmachan == 3)
                dcbp->dma_rx_mode = dcbp->dma_tx_mode = PKTWIN_DMA_FDX_T1R3;
            else
                dcbp->dma_rx_mode = dcbp->dma_tx_mode = PKTWIN_DMA_FDX_T3R1;
        }
        else
        {
            /* Half DUPLEX... */
            if(rxdmachan == 3)
            {
                dcbp->dma_rx_mode = PKTWIN_DMA_HDX_R3;
                dcbp->dma_tx_mode = PKTWIN_DMA_HDX_T3;
            }
            else
            {
                dcbp->dma_rx_mode = PKTWIN_DMA_HDX_R1;
                dcbp->dma_tx_mode = PKTWIN_DMA_HDX_T1;
            }
        }
  
    }               /* End of dma info setup */
  
  
    /* no need to init stats because calloc zero-ed the dcbp */
  
    /* default IOCTL Params */
    dcbp->ioctlparams->xmitdelay = 250;     /* 250 Ms */
    dcbp->ioctlparams->persist= 64;         /* 25% of 255 = 64 persistance*/
    dcbp->ioctlparams->slotime = 10;        /* 10 Ms */
    dcbp->ioctlparams->squelch   = 200;     /* 200 Ms */
  
    /* set the rxavailbufs parameter */
    dcbp->ioctlparams->rxavailbufs = 0;     /* zero 1st */
    if ( argc >= 16 )
        dcbp->ioctlparams->rxavailbufs = atoi(argv[15]);
  
    if ( dcbp->ioctlparams->rxavailbufs < 4 )
        dcbp->ioctlparams->rxavailbufs = 4;     /* 4 is min!! */
  
    /*
     * setup the packet header for the DRIVER
     * calling this routine
     */
    /* init to packet header's byte count */
#ifdef OLD_KA9Q
    dcbp->rxbufp->cnt = sizeof(struct phdr);
    hdr = (struct phdr *)dcbp->rxbufp->data;
    hdr->type = intfc_p->type;
    hdr->iface = intfc_p;
    /* Rx data goes in after packet header  */
    /* works because data is a char pointer */
    dcbp->rxbufp->data += sizeof(struct phdr);
#else
    dcbp->rxbufp->cnt = 0;
#endif
  
    dcbp->rxcurp = dcbp->rxbufp->data;
  
    /* get as many of the requested rx pre-allocated mbuf's */
    dcbp->rxavailq = NULLBUF;
  
    f_getavail((struct mbuf **)&(dcbp->rxavailq),
    (int16)dcbp->ioctlparams->rxavailbufs,
    (int16)dcbp->rxbufsize,
    (int16 *)&(dcbp->rxavailcount),
    (struct iface *)intfc_p,
    (bool)dcbp->dma_flg);
  
#ifdef TSYNC_DEBUG_PRT
    printf("TSYNC_ATTACH: rxavailbufs is %d, rxavailcount is %d\n", dcbp->ioctlparams->rxavailbufs, dcbp->rxavailcount);
#endif
  
    /* put in the timer control blocks */
    dcbp->timer1 = timer1;
  
    dcbp->baud = (int16)atoi(argv[10]);
    /* set up duplex mode... */
    dcbp->hduplex = hdxflg;
  
  
    /* SET UP CLOCKING OPTIONS... */
    /* Default to Externally supplied clocks */
  
    /* select Tx clock mode for SCC */
    /* Default is to expect clock to be supplied externally */
    if((argc >= 12) && (strcmpi(argv[11],"int") == 0))dcbp->txclk_extrn = FALSE;
    else dcbp->txclk_extrn = TRUE;
  
    /* select Rx clock mode for SCC */
    /* Default is to expect clock to be supplied externally */
    if((argc >= 13) && (strcmpi(argv[12],"int") == 0))dcbp->rxclk_extrn = FALSE;
    else dcbp->rxclk_extrn = TRUE;
  
    /* select data coding scheme for SCC */
    /* Default is NRZ */
    if(argc >= 14)
    {
        if((strlen(argv[13]) >= strlen("nrzi")) &&
            (strcmpi(argv[13],"nrzi") == 0))
        {
            dcbp->nrzi_flg = TRUE;
        }
        else dcbp->nrzi_flg = FALSE;
    }
  
    /*
     *  Now for the hardware dependent initializations.
     *  If device control block is an 8530 driver, then SCC
     *  stuff needs to be done.
     *
     *  udcbp->type tells if its an 8530
     *  intfc_p->dev tells which unit/dev
     *
     */
  
#ifdef TSYNC_DEBUG_PRT
    printf("tsync_attach: intfc_p->dev = %x config 8530 HW \n", intfc_p->dev);
#endif
  
  
    i_state = dirps();      /* {dgl} protect the writescc's */
                /* and the vector reads/writes */
    /* disable our ints */
    maskoff(Twin_vector);
    outportb(Twin_io_base+INT_CFG,(Twin_sercfg &= (~PKTWIN_EI)));
  
  
    /*
     * Can only reset the chip when it is not being used!!!
     * This handles first time use of 8530.
     * Set the base vector - since it is common set it here.
     */
  
  
    if ( (Twin_udcb[TWINCOMM1].attached == FALSE) &&
        (Twin_udcb[TWINCOMM2].attached == FALSE) )
    {
  
        /* This should NOT be done... if either ports are in use */
        /* for ANYTHING */
        Twin_write_scc(dcbp->zhwmap.ctl,R9,(dcbp->wr9 = FHWRES));
  
  
        /* Save original interrupt vector */
        Twin_oldvec = (INTERRUPT (*)())getirq(Twin_vector);
  
        udcbp->prev_vec1 = (void (*)())Twin_oldvec;
  
        /* New vector... */
        setirq(Twin_vector, Twin_handler);
  
        /* set up register images, and regs to defaults */
  
        Twin_sercfg = Twin_dmacfg = 0;
        outportb(Twin_io_base+INT_CFG,Twin_sercfg);
        outportb(Twin_io_base+DMA_CFG,Twin_dmacfg);
    }
    /*
     * Reset the individual channel.  Handles cases of re-attaching
     * channels and first attachment.
     *
     * For safetys sake set the Disable Lower Chain bit too.
     */
    if ( dcbp->dev == TWINCOMM1 )
        Twin_write_scc(dcbp->zhwmap.ctl,R9,(dcbp->wr9 = 0x84));
    else
        Twin_write_scc(dcbp->zhwmap.ctl,R9,(dcbp->wr9 = 0x44));
  
    /* wait a bit... for chip to settle after reset */
    /* Just in case... */
    for(i=0;i<20;i++);     /* milt */
  
    /* configure the 8530 SCC */
    if(tsync_hwinit(dcbp) == -1)
    {
        /* An error occurred in the hw init */
        /* so clean up and leave... */
  
        /* Mark it attached so the following detach routine */
        /* won't complain */
        udcbp->attached = TRUE;
        if_detach(intfc_p);
  
        /* re-enable this interrupt input */
        maskon(Twin_vector);
  
        restore(i_state);
        tprintf("PackeTwin hardware unavailable or malfunctioning...\n");
        return(-1);
    }
  
  
    /* enable PackeTwin ints */
    outportb(Twin_io_base+INT_CFG,(Twin_sercfg |= PKTWIN_EI));
  
    /* enable this level ints on 8259 */
    maskon(Twin_vector);
  
    /* {dgl} enable system-wide ints */
    restore(i_state);
  
    /*
     *  This channel is ATTACHED.
     */
    udcbp->attached = TRUE;
  
  
    /* new for NOS - start the two processes needed by the */
    /* sync driver; one is for getting pre-allocated buffers */
    /* the other is for kicking the transmitter after its */
    /* timer has expired or when it needs a transmitted */
    /* message free-ed.  P.S. must do after attached set to TRUE */
  
    sprintf(tmpbuf,"%s:getbuf",intfc_p->name);
    dcbp->rxavproc = newproc(tmpbuf,150,tsync_rxavget,udcb_dev,NULL,NULL,0);
  
#ifdef TSYNC_DEBUG_PRT
    printf("tsync_attach: called newproc for tsync_getbfs, procid = %lx \n",
    dcbp->rxavproc);
#endif
  
  
    sprintf(tmpbuf,"%s:kick",intfc_p->name);
    dcbp->txkickproc = newproc(tmpbuf,150,tsync_txkick,udcb_dev,NULL,NULL,0);
  
#ifdef TSYNC_DEBUG_PRT
    printf("tsync_attach: called newproc for tsync_txkick, procid = %lx \n",
    dcbp->txkickproc);
  
    tprintf("tsync_attach: All done!!! \n");
#endif
    sprintf(tmpbuf,"%s:free",intfc_p->name);
    dcbp->freetxproc = newproc(tmpbuf,150,tsync_freetx,udcb_dev,NULL,NULL,0);
  
    /* Let our new processes get running... */
    for(i=0;i<10;i++)pwait(NULL);
  
    return(0);
}
/*
 * End of tsync_attach()
 ******************************************************************/
  
  
  
/*
 *  Stop I/O
 */
int
tsync_stop(intfcp)
struct iface *intfcp;
{
    DCB *sp;
    UNI_DCB *udcbp;
    int16 dev;
    int i_state;
    struct drvbuf *xbufp, *tmpptr;
    struct mbuf *tmpbufp;
  
  
    dev = intfcp->dev;
    udcbp = &Twin_udcb[dev];                /* dev num is index number!!! */
  
    if ( udcbp->attached != TRUE )
    {
        tprintf("tsync_stop err: Device already stopped/de-attached!!\n");
        return(0);
    }
    else if ( (dev != TWINCOMM1) && (dev != TWINCOMM2) )
        return(0);             /* not for this driver */
  
    sp = (DCB *)Twin_udcb[dev].dcbp;
  
    /* kill off the support processes */
    /* so they don't allocate memory */
    /* whils't we're freeing it */
  
    killproc(sp->rxavproc);
    killproc(sp->txkickproc);
    killproc(sp->freetxproc);
  
    /* Turn off interrupts */
  
    i_state = dirps();
  
    /* mark port as available - do it now for if below */
    udcbp->attached = FALSE;
  
  
  
    /* Disable the DMA channels if any... */
    if(sp->dma_flg)
    {
        outportb(DMAWSMR,sp->dma_rx_chan | 0x04);
        outportb(DMAWSMR,sp->dma_tx_chan | 0x04);
    }
  
    /*
     * Can only reset the chip when it is not being used!!!
     * This handles first time use of 8530.
     * Set the base vector - since it is common set it here.
     */
  
  
    if ( (Twin_udcb[TWINCOMM2].attached == FALSE) &&
        (Twin_udcb[TWINCOMM1].attached == FALSE) )
    {
  
        /* This should NOT be done... if either ports are in use */
        /* for ANYTHING */
        Twin_write_scc(sp->zhwmap.ctl,R9,(sp->wr9 = FHWRES));
  
        /* Restore original interrupt vector */
        setirq(Twin_vector,(INTERRUPT (*)())udcbp->prev_vec1);
  
        /* Disable our board from generating ints... */
        outportb(Twin_io_base+INT_CFG,(Twin_sercfg &= (~PKTWIN_EI)));
    }
  
    /*
     * Reset the individual channel.  Handles cases of re-attaching
     * channels and first attachment.
     *
     */
  
    if ( sp->dev == TWINCOMM1 )
    {
        Twin_write_scc(sp->zhwmap.ctl,R9,(sp->wr9 = 0x88));
  
        /* Drop DTR to indicate we are gone...*/
        outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg &= (~PKTWIN_DTRA_ON)));
    }
    else
    {
        Twin_write_scc(sp->zhwmap.ctl,R9,(sp->wr9 = 0x48));
        /* Drop DTR to indicate we are gone...*/
        outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg &= (~PKTWIN_DTRB_ON)));
    }
  
    restore(i_state);
  
    /*
     * Flush all buffers and queue's
     */
  
    /* Now free all preallocated rx buffers on the rx available queue */
    while((tmpbufp = f_dequeavail((struct mbuf **)&sp->rxavailq,(int16 *)&sp->rxavailcount)) != NULLBUF)
        free(tmpbufp);          /* these mbuf's are one contigous */
                    /* chunk of memory */
  
  
    if ( sp->rxbufp != NULLBUF )
        free(sp->rxbufp);
  
    /* free up any pending transmit buffers... */
    while(sp->xmtq != (struct drvbuf *)NULLBUF)
    {
        tmpptr = sp->xmtq->next;
        free(sp->xmtq);
        sp->xmtq = tmpptr;
    }
  
    /* Free any previously sent frames, which are in the waiting to be */
    /* free'd queue */
  
    /* disable ints here to get queue head... */
    i_state = dirps();                      /* seems unecessary milt */
    xbufp = sp->freem;
    sp->freem = (struct drvbuf *)NULLBUF;
    restore(i_state);
  
    /* Here xbufp is a "to be free'd"  */
    /* queue pointer" */
    /* now free memory of all the xmitted messages */
    while ( xbufp != (struct drvbuf *)NULLBUF )
    {
        tmpptr = xbufp->next;
        free((char *)xbufp);
        xbufp = tmpptr;
    }
  
  
  
    /*
     * Deallocate other driver related memory structures
     */
    free(sp->timer1);
    free(sp->ioctlparams);
    free(sp);
  
    return(0);
}
  
  
/*
 * Display a PackeTwin SYNCHRONOUS channel's statistics.
 */
int
do_tsync_stat()
{
    DCB *dcbp;
    UNI_DCB *udcbp;
    int16 dev;
    unsigned long rxerrcnt;
    bool hd8530;
  
    /* Header for all the Synchronous channel's on the card */
    tprintf("                PackeTwin Synchronous Channel Statistics\n\n");
  
    hd8530 = FALSE;
    for (dev=0, udcbp = &Twin_udcb[0]; dev < 2 ; udcbp++, dev++)
    {
        if ( udcbp->type == SYNC_8530 )
        {
            if ( udcbp->attached == TRUE )
            {
                if ( !hd8530 )
                {
                    tprintf("\n");
                    tprintf("8530   RxChars   RxPacks  Rxerrs  RxBf   Txchars   TxPacks TxUns  RxNoBf\n");
                    tprintf("----- ---------  -------  ------  ----  ---------  ------- -----  ------\n");
                    hd8530 = TRUE;
                }
  
                dcbp = (DCB *)udcbp->dcbp;
                /* total receiver error count */
                rxerrcnt = dcbp->rxcrcerrcnt + dcbp->rxovercnt;
  
                tprintf(" %4.1d %#9lu  %#7lu  %#6lu  %#4lu  %#9lu  %#7lu %#5lu  %#6lu\n",
                dev, dcbp->rxbytecnt,
                dcbp->rxpackcnt,
                rxerrcnt,
                dcbp->rxavailcount,
                dcbp->txbytecnt,
                dcbp->txpackcnt,
                dcbp->txunderun,
                dcbp->nobufs);
  
#ifdef TSYNC_DEBUG_PRT
                printf("Crc's: %lx Overruns: %lx\n",dcbp->rxcrcerrcnt,dcbp->rxovercnt);
                printf("DCB ptr: %p\n",dcbp);
                printf("rxcurp: %p  rxbufp: %p  rxbufp->cnt: %d\n",dcbp->rxcurp,
                dcbp->rxbufp,
                dcbp->rxbufp->cnt);
  
#endif
            }
        }
  
    }  /* end of for loop */
  
    for (dev=0, udcbp = &Twin_udcb[0]; dev < 2; udcbp++, dev++)
    {
        if ( udcbp->attached == FALSE )
            tprintf("\nChannel %d is UN-attached", dev);
    }
  
    tprintf("\n");
    return(0);
}
  
/* Subroutine to set kiss params in channel tables */
int32
tsync_ioctl(iface,cmd,set,val)
struct iface *iface;
int cmd;
int set;
int32 val;
{
    DCB *dcbp;
  
    dcbp = (DCB *)Twin_udcb[iface->dev].dcbp;
  
    switch(cmd){
        case PARAM_TXDELAY:
            if(set)
                dcbp->ioctlparams->xmitdelay = (int16)val;
            return dcbp->ioctlparams->xmitdelay;
  
        case PARAM_PERSIST:
            if(set)
                dcbp->ioctlparams->persist = (uchar)val;
            return uchar(dcbp->ioctlparams->persist);
        case PARAM_SLOTTIME:
            if(set)
                dcbp->ioctlparams->slotime = (int16)val;
            return dcbp->ioctlparams->slotime;
        case PARAM_TXTAIL:
            if(set)
                dcbp->ioctlparams->squelch = (int16)val;
            return dcbp->ioctlparams->squelch;
  
        case PARAM_RXBUFS:
            if(set)
                dcbp->ioctlparams->rxavailbufs = (int16)val;
            return dcbp->ioctlparams->rxavailbufs;
  
    }
    return -1;
}
  
/*
 * Initialize the SZ driver's hardware for SDLC, i.e. the 85c30 SCC.
 */
int
tsync_hwinit(dcbp)
register DCB *dcbp;
{
    int i_state;       /* for PackeTwin this is 2 bytes */
    int16 tc;               /* time constant for 8530 */
    int     i;
    bool    baudclk_flg;
    bool    both_clocks_internal;
    void tsync_loopback();
    int     nop1 = 0;
  
#define nop nop1++;
  
  
#ifdef TSYNC_DEBUG_PRT
    printf("Initializing TSYNC_DRIVER device num = %d\n", dcbp->dev);
#endif
    i_state = dirps();
  
  
    /* if both clocks are specified as internal, then override the */
    /* specified data coding scheme, and MAKE it NRZI */
    if((dcbp->rxclk_extrn == FALSE) && (dcbp->txclk_extrn == FALSE))
    {
        dcbp->nrzi_flg = TRUE;
        both_clocks_internal = TRUE;
    }
    else
        both_clocks_internal = FALSE;
  
    /* If we are not using DMA, then try to enable the extended TX FIFO */
    if(!(dcbp->dma_flg)) enable_escc(dcbp);
  
    /* times 1 x clock, SDLC mode */
    /* always set R4 before R1, R3, R6, and R7 */
    Twin_write_scc(dcbp->zhwmap.ctl,R4,(dcbp->wr4 = 0x20));
  
    /* mask-off all interrupt types */
    /* No DMA, No Ints... */
    Twin_write_scc(dcbp->zhwmap.ctl,R1,(dcbp->wr1 = 0));
  
    /* Rx 8 bit/chars, enter hunt mode, CRC enable */
    Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xd8));
  
    /* DTR, Tx 8 bit/chars and Tx disabled */
    Twin_write_scc(dcbp->zhwmap.ctl,R5,(dcbp->wr5 = 0x60));
  
    /* no SDLC transmit secondary address */
    Twin_write_scc(dcbp->zhwmap.ctl,R6,(dcbp->wr6 = 0));
  
    /* Set SDLC flag */
    Twin_write_scc(dcbp->zhwmap.ctl,R7,(dcbp->wr7 = FLAG));
  
    if(dcbp->nrzi_flg != TRUE)
    {
        /* Preset Tx CRC, NRZ Mode, flags on idle and */
        /* underrun 8 bit sync mode */
        Twin_write_scc(dcbp->zhwmap.ctl,R10,(dcbp->wr10 = 0x84));
    }
  
    else
    {
        /* Preset Tx CRC, NRZI Mode, flags on idle and */
        /* aborts on underrun 8 bit sync mode */
        Twin_write_scc(dcbp->zhwmap.ctl,R10,(dcbp->wr10 = 0xa4));
    }
  
    /* Set up BRG and DPLL multiplexers */
  
    /* Case where only the Rx clock is supplied externally */
    if((dcbp->rxclk_extrn == TRUE) && (dcbp->txclk_extrn != TRUE))
    {
        /* Tx Clk from BRG. Rcv Clk external on RTxC, */
        /* TRxC pin outputs BRgen */
        Twin_write_scc(dcbp->zhwmap.ctl,R11,(dcbp->wr11 = 0x16));
        tc = ( (XTAL) / (dcbp->baud *2))-2;
        baudclk_flg = TRUE;
  
        /* disable the external TX clock receiver input */
        if(dcbp->dev == TWINCOMM1) outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg &= (~PKTWIN_EXTCLKA)));
        else outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg &= (~PKTWIN_EXTCLKB)));
    }
  
    /* Case where only the Tx clock is supplied externally */
    if((dcbp->txclk_extrn == TRUE) && (dcbp->rxclk_extrn != TRUE))
    {
        /* Tx Clk external on TRxC,  Rcv Clk internal BRgen, */
        /* TRxC pin is input TX clock */
        Twin_write_scc(dcbp->zhwmap.ctl,R11,(dcbp->wr11 = 0x4a));
        tc = ( (XTAL) /(dcbp->baud *2))-2;
        baudclk_flg = TRUE;
  
        /* enable the external TX clock receiver input */
        if(dcbp->dev == TWINCOMM1) outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg |= PKTWIN_EXTCLKA));
        else outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg |= PKTWIN_EXTCLKB));
    }
  
    /* Case where both clocks are supplied externally */
    if((dcbp->txclk_extrn == TRUE) && (dcbp->rxclk_extrn == TRUE))
    {
        /* Tx Clk external on TRxC,  Rcv Clk external on RTxC, */
        /* TRxC pin is input TX clock */
        Twin_write_scc(dcbp->zhwmap.ctl,R11,(dcbp->wr11 = 0x0a));
        baudclk_flg = FALSE;
  
        /* enable the external TX clock receiver input */
        if(dcbp->dev == TWINCOMM1) outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg |= PKTWIN_EXTCLKA));
        else outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg |= PKTWIN_EXTCLKB));
  
        /*DTR is in REQ mode for possible DMA */
        if(dcbp->dma_flg)Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0x04));
        else Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0x00));
    }
  
    if(both_clocks_internal == TRUE)
    {
        /* Tx Clk from BRG. Rcv Clk from DPLL, TRxC pin outputs DPLL */
        Twin_write_scc(dcbp->zhwmap.ctl,R11,(dcbp->wr11 = 0x77));
  
        /* time constant 32X BRG divisor */
        tc = ((XTAL/32)/(dcbp->baud * 2))-2;
        /* DGL... fix a cut and paste error... */
        /* baudclk_flg = FALSE; */
        baudclk_flg = TRUE;
  
        /* disable the external TX clock receiver input */
        if(dcbp->dev == TWINCOMM1) outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg &= (~PKTWIN_EXTCLKA)));
        else outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg &= (~PKTWIN_EXTCLKB)));
  
    }
  
    if(baudclk_flg == TRUE)
    {
  
        /* lower byte */
        Twin_write_scc(dcbp->zhwmap.ctl,R12,(dcbp->wr12 = (tc&0xFF)));
  
        /* upper byte */
        Twin_write_scc(dcbp->zhwmap.ctl,R13,(dcbp->wr13 = ((tc>>8)&0xFF)));
  
        /* enable the BR generator... */
        /*DTR is in REQ mode for possible DMA */
        if(dcbp->dma_flg)Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0x07));
        else Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0x03));
    }
  
  
    if(both_clocks_internal == TRUE)
    {
  
        /* BRG off, Enter Search mode */
        /*DTR is in REQ mode if possible DMA */
        if(dcbp->dma_flg)Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0x26));
        else Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0x22));
  
        /* Set DPLL source to BRGEN */
        /*DTR is in REQ mode for possible DMA */
        if(dcbp->dma_flg)Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0x86));
        else Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0x82));
  
        /* Set DPLL mode to NRZI */
        /*DTR is in REQ mode for possible DMA */
        if(dcbp->dma_flg)Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0xe6));
        else Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0xe2));
  
  
  
/* At this point; the DPLL is searching for a data edge to begin clocking */
/*                The baud rate gen is set to 32 times the desired rate.. */
/*                The transmitter is based on the BRGEN. (i.e. 32x )      */
/*                                                                        */
/* Procedure here; Set up an external loopback, then send aborts, to      */
/*                 trigger and sync up the DPLL.                          */
/*                 After the DPLL is running, then reprogram the Xmitter  */
/*                 to use the DPLL output as it's transmitter clock.      */
/*                 This saves us having to reprogram the BRG whenever we  */
/*                 switch from RECV to TX and back.                       */
/*
  
        /* Turn on the recvr */
        /* Rx 8 bit/chars, enter hunt mode, CRC enable */
        Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xd9));
  
        /* Turn on the transmitter */
        /* DTR, Tx 8 bit/chars and Tx enabled */
        Twin_write_scc(dcbp->zhwmap.ctl,R5,(dcbp->wr5 = 0x68));
  
        /* reset TX CRC */
        Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = 0x80));
  
        /* enable the BR generator to start things up...*/
        /*DTR is in REQ mode for possible DMA */
        if(dcbp->dma_flg)Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0x07));
        else Twin_write_scc(dcbp->zhwmap.ctl,R14,(dcbp->wr14 = 0x03));
  
  
        /* OK now turn on the loopback */
        tsync_loopback(dcbp,ON);
  
        /* Now send sum aborts... */
  
        for(i=0;i<25;i++)
        {
  
            /* read and toss any  data */
            inportb(dcbp->zhwmap.data);
            inportb(dcbp->zhwmap.data);
            inportb(dcbp->zhwmap.data);
            inportb(dcbp->zhwmap.data);
  
            Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = 0x18));      /* send abort */
        }
  
        /* OK, now the DPLL should be running at ~32 * baudrate requested */
        /* Turn off the loopback... */
  
        tsync_loopback(dcbp,OFF);
  
  
        /* Now reprogram the transmitter clock to use the DPLL output */
  
        /* TX clk is DPLL, RX clk is DPLL, TRxc is DPLL OUT */
        Twin_write_scc(dcbp->zhwmap.ctl,R11,(dcbp->wr11 = 0x7f));
  
        /* dgl for G3RUH modems... I need to do AUTO-ENABLES... */
        Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xf9));
  
  
    }
  
    else
    {
  
        /* Turn on the recvr */
        /* Rx 8 bit/chars, enter hunt mode, CRC enable */
  
        /*  no autoenables */
        /*
        Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xd9));
        */
  
        /* dgl for G3RUH modems... I need to do AUTO-ENABLES... */
        Twin_write_scc(dcbp->zhwmap.ctl,R3,(dcbp->wr3 = 0xf9));
  
    }
  
    /* Turn off external interrupts (like CTS/CD) */
    Twin_write_scc(dcbp->zhwmap.ctl,R15,(dcbp->wr15 = 0x00));
  
  
    /* TRANSMITTER OFF */
    Twin_write_scc(dcbp->zhwmap.ctl,R5,(dcbp->wr5 = 0x60)); /* TX off now */
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = 0x80)); /* reset TX CRC generator */
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = 0x40)); /* reset RX CRC checker */
  
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = 0x28)); /* reset TX int pending */
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = 0x28)); /* reset TX int pending */
  
  
    /* reset extrn/sts ints... twice */
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = 0x10));
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = 0x10));
  
    /* if we are interrupt driven only: then allow ints for ALL RCV cndxs,
    */
    if(dcbp->dma_flg == FALSE)
        Twin_write_scc(dcbp->zhwmap.ctl,R1,(dcbp->wr1 = 0x11));
  
    /* otherwise interrupt on special rcv cdx only */
    /* DMA req mode Req on rcv, Req enabled */
    else  Twin_write_scc(dcbp->zhwmap.ctl,R1,(dcbp->wr1 = 0xf9));
  
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = 0x30)); /* reset error bits */
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = 0x30)); /* twice superstition */
  
    /* If neither of the PKTwin's ports are currently attached*/
    /* Then set up the prescaler timer */
    if ( (Twin_udcb[TWINCOMM2].attached == FALSE) &&
         (Twin_udcb[TWINCOMM1].attached == FALSE) )
    {
        inportb(Twin_io_base+CLR_TMR1);
        nop;
        inportb(Twin_io_base+CLR_TMR2);
        nop;
  
        /* Timer counter channel 0 */
        /* 1 ms period */
        outportb(Twin_io_base+TMR_CTRL,(unsigned char)0x36);
        nop;
        outportb(Twin_io_base+TMR_CNT0,(unsigned char)0x00);
        nop;
        outportb(Twin_io_base+TMR_CNT0,(unsigned char)0x18);
        nop;
  
        outportb(Twin_io_base+TMR_CTRL,(unsigned char)0x70);
        nop;
        inportb(Twin_io_base+CLR_TMR1);
        nop;
  
        outportb(Twin_io_base+TMR_CTRL,(unsigned char)0xB0);
        nop;
        inportb(Twin_io_base+CLR_TMR2);
        nop;
    }
  
    if(dcbp->dev == TWINCOMM1)
    {
        /* mode setup for this channel's driver timer */
        /* Set it's timer for 1 tick and let it expire */
        outportb(Twin_io_base+TMR_CTRL,(unsigned char)0x70);
        nop;
        inportb(Twin_io_base+CLR_TMR1);
        nop;
  
  
        /* If the timer hasn't gone off, the hardware is either */
        /* not present, or broken... */
        if(!inportb(Twin_io_base+INT_REG) & 0x02)
        {
            restore(i_state);
            return(-1);
        }
  
        /* Assert DTR to indicate we are alive...*/
        outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg |= PKTWIN_DTRA_ON));
  
    }
    if(dcbp->dev == TWINCOMM2)
    {
        /* mode setup for this channel's driver timer */
        outportb(Twin_io_base+TMR_CTRL,(unsigned char)0xb0);
        nop;
        inportb(Twin_io_base+CLR_TMR2);
        nop;
        /* If the timer hasn't gone off, the hardware is either */
        /* not present, or broken... */
        if(!inportb(Twin_io_base+INT_REG) & 0x04)
        {
            restore(i_state);
            return(-1);
        }
        nop;
        /* Assert DTR to indicate we are alive...*/
        outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg |= PKTWIN_DTRB_ON));
    }
  
    if(dcbp->dma_flg == TRUE)
    {
        /* mask off the DMA channel(s) */
        outportb(DMAWSMR,(dcbp->dma_rx_chan | 0x04));
        outportb(DMAWSMR,(dcbp->dma_tx_chan | 0x04));
  
        outportb(Twin_io_base+DMA_CLR_FF,0x00); /* Initially clear the REQ's */
                        /* This is the PackeTwin reg..*/
                        /* NOT the 8237 reg... */
        outportb(DMAMODE,dcbp->dma_rx_chan |  RX_DMA);
  
        if(dcbp->hduplex == FALSE)
        {
            outportb(DMAMODE,dcbp->dma_rx_chan |  RX_DMA);
            outportb(DMAMODE,dcbp->dma_tx_chan |  TX_DMA);
            /* tx dma, enabled, fdx, chan3*/
            /* rx dma, enabled, fdx, chan1*/
            outportb(Twin_io_base+DMA_CFG,dcbp->dma_tx_mode);
  
        }
        else
        {
            outportb(DMAMODE,dcbp->dma_rx_chan |  RX_DMA);
            /* rx dma,hdx, enabled, chan 1*/
            outportb(Twin_io_base+DMA_CFG,dcbp->dma_rx_mode);
        }
        outportb(DMAFFCL,0x00);         /* This is the 8237 reg */
        rxprime_dma(dcbp);
    }
  
    /* reset Tx int pending */
    /* This should keep the chip from underrunning, as */
    /* well as clearing any pending txempty interrupt */
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = 0x28));
  
    /* reset external status latch */
    /* clear any pending ext sts bits */
    /* TWICE for good measure */
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = RES_EXT_INT));
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = RES_EXT_INT));
  
    /* Reset Underrun/EOM latch */
    /* clear the latch... it should stay that way...*/
    /* at least till we send feed some data to the tx fifo */
    Twin_write_scc(dcbp->zhwmap.ctl,R0,(dcbp->wr0 = (RES_EOM_L | RES_EXT_INT)));
  
    /* Turn on DCD interrupt */
    Twin_write_scc(dcbp->zhwmap.ctl,R15,(dcbp->wr15 = CTSIE|DCDIE));
  
    /* save this initial state of RR0 */
    dcbp->extreg = Twin_read_scc(dcbp->zhwmap.ctl,R0);
    dcbp->extreg = dcbp->extreg |= TxEOM;
  
    /* if we are interrupt driven only: then allow ints for ALL RCV cndxs,
    */
    if(dcbp->dma_flg == FALSE)
        Twin_write_scc(dcbp->zhwmap.ctl,R1,(dcbp->wr1 = 0x13));
  
    /* otherwise interrupt on special rcv cdx only */
    /* DMA req mode Req on rcv, Req enabled */
    else  Twin_write_scc(dcbp->zhwmap.ctl,R1,(dcbp->wr1 = 0xf9));
  
    /* master interrupt enable  the channel */
    Twin_write_scc(dcbp->zhwmap.ctl,R9,(dcbp->wr9 = (MIE|DLC|NV)));
  
    restore(i_state);
    return(0);
}
  
void
tsync_loopback(dcbp,flag)
DCB     *dcbp;
char    flag;
{
    if(flag == ON)
    {
        switch(dcbp->dev)
        {
            case TWINCOMM1: outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg |= PKTWIN_LOOPA_ON));
                            break;
  
            case TWINCOMM2: outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg |= PKTWIN_LOOPB_ON));
                            break;
        }
    }
    else
    {
        switch(dcbp->dev)
        {
            case TWINCOMM1: outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg &= (~PKTWIN_LOOPA_ON)));
                            break;
  
            case TWINCOMM2: outportb(Twin_io_base+SERIAL_CFG,(Twin_sercfg &= (~PKTWIN_LOOPB_ON)));
                            break;
        }
    }
  
}
  
  
void
tsync_reset_8530()
{
  
int16   ctl;
  
    ctl = (int16)(Twin_io_base+SCCA_CMD);
    Twin_write_scc(ctl,R9,FHWRES);
  
}
  
/*
          1         2         3         4         5         6         7
01234567890123456789012345678901234567890123456789012345678901234567890123456789
/*
 * Display a PackeTwin SYNCHRONOUS channel's internal state
 */
int
do_tsync_dump(argc,argv,p)
int argc;
char *argv[];
void *p;                                /* new for NOS */
{
    DCB *dcbp;
    UNI_DCB *udcbp;
    int16 dev;
    char    tmpstr[40];
  
    if(argc < 2)
    {
        tprintf("Usage: tsydump channel#\n");
        return 1;
    }
  
    dev = htoi(argv[1]);            /* get port number to look at */
  
    tprintf("                PackeTwin Synchronous Channel Internals\n\n");
  
    udcbp = &Twin_udcb[dev];
    if ( udcbp->type == SYNC_8530 )
    {
        if ( udcbp->attached == TRUE )
        {
            dcbp = (DCB *)udcbp->dcbp;
            tprintf("\n");
            tprintf(" RECEIVER STATUS: \n");
            tprintf(" Packcnt       Nqued     Bytecnt  Availcount      Nobufs\n");
            tprintf("%#8ld    %#8ld    %#8ld    %#8ld    %#8ld\n",
                            dcbp->rxpackcnt,dcbp->rxnqued,
                            dcbp->rxbytecnt,dcbp->rxavailcount,dcbp->nobufs);
  
            tprintf("\n");  /* blank line */
            tprintf(" Overcnt    Abortcnt   Crcerrcnt     Truncnt\n");
            tprintf("%#8ld    %#8ld    %#8ld    %#8ld\n",
                            dcbp->rxovercnt,dcbp->rxabortcnt,
                            dcbp->rxcrcerrcnt,dcbp->rxtruncnt);
  
            tprintf("\n");  /* blank line */
  
            switch(dcbp->txstate)
            {
                case    IDLE:
                                strcpy(tmpstr,"IDLE");
                                break;
  
                case    DEFER:
                                strcpy(tmpstr,"DEFER");
                                break;
  
                case    TX_ACTIVE:
                                strcpy(tmpstr,"TX_ACTIVE");
                                break;
  
                case    KEYED_UP:
                                strcpy(tmpstr,"KEYED_UP");
                                break;
  
                case    CRCING:
                                strcpy(tmpstr,"CRCING");
                                break;
  
                case    TX_KEYUP_DELAY:
                                    strcpy(tmpstr,"TX_KEYUP_DELAY");
                                    break;
  
                case    TX_KEYDOWN_DELAY:
                                    strcpy(tmpstr,"TX_KEYDOWN_DELAY");
                                    break;
  
                case    TX_PERSIST:
                                    strcpy(tmpstr,"TX_PERSIST");
                                    break;
  
                case    TX_DONE:
                                strcpy(tmpstr,"TX_DONE");
                                break;
  
                default:
                                strcpy(tmpstr,"UNKNOWN STATE");
                                break;
  
            }
  
            tprintf(" TRANSMITTER STATUS:\n");
            tprintf(" Packcnt     Bytecnt    Underrun    Cur_xbufp   Cur_xbytes  MxFIFO  TX STATE\n");
  
            tprintf("%8ld    %8ld    %8ld    %8lx    %8d      %2.2d    ",
                    dcbp->txpackcnt,dcbp->txbytecnt,
                    dcbp->txunderun,dcbp->cur_xbufp,
                    dcbp->cur_xbytes,dcbp->maxtxbytes);
            tprintf("%s\n",tmpstr);
  
            tprintf("\n");  /* blank line */
            tprintf(" CTSlost: %d\n",dcbp->txctslost);
            tprintf("\n");  /* blank line */
  
            tprintf( "WR0  WR1  WR2  WR3  WR4  WR5  WR6  WR7  WR8  WR9  WR10 WR11 WR12 WR13 WR14 WR15");
  
  
            tprintf(" %2.2x   %2.2x   %2.2x   %2.2x   %2.2x   %2.2x   %2.2x   %2.2x   %2.2x   %2.2x   %2.2x   %2.2x   %2.2x   %2.2x   %2.2x   %2.2x",
                    dcbp->wr0,dcbp->wr1,dcbp->wr2,dcbp->wr3,dcbp->wr4,
                    dcbp->wr5,dcbp->wr6,dcbp->wr7,0,dcbp->wr9,
                    dcbp->wr10,dcbp->wr11,dcbp->wr12,dcbp->wr13,dcbp->wr14,
                    dcbp->wr15);
  
            tprintf("\n");
            tprintf("\n");
  
tprintf("Extsts  Extreg  Hduplex  Dma_flg  Txclk_extrn  Rxclk_extrn  Nrzi_flg\n");
            tprintf("  %2.2x      %2.2x      %2.2x       %2.2x         %2.2x           %2.2x           %2.2x\n",
                    dcbp->extsts,dcbp->extreg,dcbp->hduplex,
                    dcbp->dma_flg,dcbp->txclk_extrn,
                    dcbp->rxclk_extrn,dcbp->nrzi_flg);
  
  
            tprintf("\n");
  
            tprintf("Dma_rx_chan  Dma_rx_mode  Dma_rx_pagereg  Dma_rx_cnt_reg  Dma_rx_addr_reg\n");
  
            tprintf( "    %2.2x           %2.2x           %2.2x              %2.2x              %2.2x\n",
                        dcbp->dma_rx_chan,dcbp->dma_rx_mode,
                        dcbp->dma_rx_pagereg,dcbp->dma_rx_cnt_reg,
                        dcbp->dma_rx_addr_reg);
  
            tprintf("Dma_tx_chan  Dma_tx_mode  Dma_tx_pagereg  Dma_tx_cnt_reg  Dma_tx_addr_reg\n");
            tprintf( "    %2.2x           %2.2x           %2.2x              %2.2x              %2.2x\n",
                        dcbp->dma_tx_chan,dcbp->dma_tx_mode,
                        dcbp->dma_tx_pagereg,dcbp->dma_tx_cnt_reg,
                        dcbp->dma_tx_addr_reg);
  
        }
        else tprintf("Channel %d: is UN-attached!\n",dev);
    }
  
  
    tprintf("\n");
    return(0);
}
  
/* 85230 Mods: Call this routine to enable the Deeper TX fifo... */
/* Do it BEFORE you set up WR7 (sync char), cause it will trash  */
/* WR7 if the installed chip is actually an 85c30 or 8530 instead*/
/* of an 85230 */
void
enable_escc(dcbp)
register DCB *dcbp;
{
  
    /* try to enable the escc extended registers */
    Twin_write_scc(dcbp->zhwmap.ctl,R15,0x01);
  
    /* Set up the TX FIFO to generate an interrupt as soon */
    /* as there is ANY room in the FIFO.  Rather than the default */
    /* of when the FIFO is empty. */
  
    /* If this is just an SCC, then the R7 will be overwritten */
    /* by the real init code later..., but if not, */
    /* then this is R7' for FIFO control */
    Twin_write_scc(dcbp->zhwmap.ctl,R7,0x00);
  
    /* restore the WR15 to an acceptable value for an 8530 */
    Twin_write_scc(dcbp->zhwmap.ctl,R15,0x00);
}
#endif /* PACKETWIN */
  
