/*
 * Version with Stopwatches
 *
 * 0 - Not used
 * 1 - rx_fsm run time
 * 2 - drtx_active run time (per character tx time)
 *
 * Interface driver for the DRSI board for KA9Q's TCP/IP on an IBM-PC ONLY!
 *
 * Derived from a driver written by Art Goldman, WA3CVG
 * (c) Copyright 1987 All Rights Reserved
 * Permission for non-commercial use is hereby granted provided this notice
 * is retained.  For info call: (301) 997-3838.
 *
 * Heavily re-written from the original,  a driver for the EAGLE board into
 * a driver for the DRSI PC* Packet adpator. Copyright as original, all
 * amendments likewise providing credit given and notice retained.
 * Stu Phillips - N6TTO, W6/G8HQA (yes Virginia,  really !).
 * For info call: (408) 285-4142
 *
 * This driver supports 1 (one) DRSI board.
 *
 * Reformatted and added ANSI-style declarations, integrated into NOS
 * by KA9Q, 10/14/89
 *
 * Latest set of defect fixes added 1/2/90 by N6TTO
 * 1. Made P-PERSIST work properly
 * 2. Fixed UNDERRUN bug when in DEFER state
 * 3. Tx now defers correctly when DCD is high (!)
 *
 * Changed 3/4/90 by N6TTO
 * Changed method of enabling the IRQ to the 8259 to call maskon()
 * instead of clrbit(); change made to allow interrupts > 8 to work
 * on an AT.
 *
 * Changed 11/14/90 by N6TTO
 * Fixed incompatiblity between current NOS memory allocation scheme
 * and changes made to speed up drsi transmit state machine.
 *
 */
  
#include <dos.h>
#include <time.h>
#include "global.h"
#ifdef DRSI
#include "mbuf.h"
#include "iface.h"
#include "pktdrvr.h"
#include "netuser.h"
#include "drsi.h"
#include "ax25.h"
#include "trace.h"
#include "pc.h"
#include "z8530.h"
#include "devparam.h"
  
static int32 dr_ctl __ARGS((struct iface *iface,int cmd,int set,int32 val));
static int dr_raw __ARGS((struct iface *iface,struct mbuf *bp));
static int dr_stop __ARGS((struct iface *iface));
static void dr_wake __ARGS((struct drchan *hp,int rx_or_tx,
void (*routine) __ARGS((struct drchan *)),int ticks));
static int drchanparam __ARGS((struct drchan *hp));
static void drexint __ARGS((struct drchan *hp));
static void drinitctc __ARGS((unsigned port));
static void drrx_active __ARGS((struct drchan *hp));
static void drrx_enable __ARGS((struct drchan *hp));
static void drtx_active __ARGS((struct drchan *hp));
static void drtx_defer __ARGS((struct drchan *hp));
static void drtx_downtx __ARGS((struct drchan *hp));
static void drtx_flagout __ARGS((struct drchan *hp));
static void drtx_idle __ARGS((struct drchan *hp));
static void drtx_rrts __ARGS((struct drchan *hp));
static void drtx_tfirst __ARGS((struct drchan *hp));
static char read_ctc __ARGS((unsigned port,unsigned reg));
static void rx_fsm __ARGS((struct drchan *hp));
static void tx_fsm __ARGS((struct drchan *hp));
static void write_ctc __ARGS((unsigned port,unsigned reg,unsigned val));
  
struct DRTAB Drsi[DRMAX];   /* Device table - one entry per card */
INTERRUPT (*Drhandle[]) __ARGS((void)) = { dr0vec };  /* handler interrupt vector table */
struct drchan Drchan[2*DRMAX];   /* channel table - 2 entries per card */
int16 Drnbr;
  
/* Set specified routine to be 'woken' up after specified number
 * of ticks (allows CPU to be freed up and reminders posted);
 */
static void
dr_wake(hp, rx_or_tx, routine, ticks)
struct drchan *hp;
int rx_or_tx;
void (*routine) __ARGS((struct drchan *));
int ticks;
{
    hp->w[rx_or_tx].wcall = routine;
    hp->w[rx_or_tx].wakecnt = ticks;
}
  
/* Master interrupt handler.  One interrupt at a time is handled.
 * here. Service routines are called from here.
 */
void
drint(dev)
int dev;
{
    register char st;
    register int16 pcbase, i;
    struct drchan *hpa,*hpb;
  
    Drsi[dev].ints++;
    pcbase = Drsi[dev].addr;
    hpa = &Drchan[2 * dev];
    hpb = &Drchan[(2 * dev)+1];
  
    yuk:
    /* Check CTC for timer interrupt */
    st = read_ctc(pcbase, Z8536_CSR3);
    if(st & Z_IP){
        /* Reset interrupt pending */
        write_ctc(pcbase, Z8536_CSR3, Z_CIP|Z_GCB);
        for(i=0;i<=1;i++){
            if(hpa->w[i].wakecnt){
                if(--hpa->w[i].wakecnt == 0){
                    (hpa->w[i].wcall)(hpa);
                }
            }
            if(hpb->w[i].wakecnt){
                if(--hpb->w[i].wakecnt == 0){
                    (hpb->w[i].wcall)(hpb);
                }
            }
        }
    }
    /* Check the SIO for interrupts */
  
    /* Read interrupt status register from channel A */
    while((st = read_scc(pcbase+CHANA+CTL,R3)) != 0){
        /* Use IFs to process ALL interrupts pending
         * because we need to check all interrupt conditions
         */
        if(st & CHARxIP){
            /* Channel A Rcv Interrupt Pending */
            rx_fsm(hpa);
        }
        if(st & CHBRxIP){
            /* Channel B Rcv Interrupt Pending */
            rx_fsm(hpb);
        }
        if(st & CHATxIP){
            /* Channel A Transmit Int Pending */
            tx_fsm(hpa);
        }
        if(st & CHBTxIP){
            /* Channel B Transmit Int Pending */
            tx_fsm(hpb);
        }
        if(st & CHAEXT){
            /* Channel A External Status Int */
            drexint(hpa);
        }
        if(st & CHBEXT){
            /* Channel B External Status Int */
            drexint(hpb);
        }
        /* Reset highest interrupt under service */
        write_scc(hpa->base+CTL,R0,RES_H_IUS);
  
    } /* End of while loop on int processing */
    if(read_ctc(pcbase, Z8536_CSR3) & Z_IP)
        goto yuk;
}
  
  
/* DRSI SIO External/Status interrupts
 * This can be caused by a receiver abort, or a Tx UNDERRUN/EOM.
 * Receiver automatically goes to Hunt on an abort.
 *
 * If the Tx Underrun interrupt hits, change state and
 * issue a reset command for it, and return.
 */
static void
drexint(hp)
register struct drchan *hp;
{
    register int base = hp->base;
    char st, i_state;
  
    i_state = dirps();   /* disable interrupts */
    hp->exints++;
  
    st = read_scc(base+CTL,R0);     /* Fetch status */
  
    /* Check for Tx UNDERRUN/EOM - only in Transmit Mode */
        /* Note that the TxEOM bit remains set once we go    */
    /* back to receive.  The following qualifications    */
    /* are necessary to prevent an aborted frame causing */
    /* a queued transmit frame to be tossed when in      */
    /* DEFER state on transmit.              */
    if((hp->tstate != DEFER) && (hp->rstate==0) && (st & TxEOM)){
        if(hp->tstate != UNDERRUN){
            /* This is an unexpected underrun.  Discard the current
             * frame (there's no way to rewind),  kill the transmitter
             * and return to receive with a wakeup posted to get the
             * next (if any) frame.  Any recovery will have to be done
             * by higher level protocols (yuk).
             */
            write_scc(base, R5, Tx8|DTR);   /* Tx off now */
            write_scc(base, R1, 0);     /* Prevent ext.status int */
            write_scc(base, R0, RES_Tx_P);  /* Reset Tx int pending */
            write_scc(base, R0, ERR_RES);
            write_scc(base, R0, RES_EOM_L); /* Reset underrun latch */
            free_p(hp->sndbuf);
            hp->tstate = IDLE;
            hp->tx_state = drtx_idle;
            dr_wake(hp, TX, tx_fsm, hp->slotime);
            hp->rstate = ENABLE;
            hp->rx_state = drrx_enable;
            drrx_enable(hp);
        }
    }
    /* 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((hp->rstate != IDLE) && (st & BRK_ABRT)){
        if(hp->rcvbuf != NULLBUF){
            hp->rcp = hp->rcvbuf->data;
            hp->rcvbuf->cnt = 0;
        }
        while(read_scc(base,R0) & Rx_CH_AV)
            (void) inportb(base+DATA);
        hp->aborts++;
        hp->rstate = ACTIVE;
        write_scc(base, R0, ERR_RES);
    }
    /* reset external status latch */
    write_scc(base,R0,RES_EXT_INT);
    restore(i_state);
}
  
/* Receive Finite State Machine - dispatcher */
static void
rx_fsm(hp)
struct drchan *hp;
{
    char i_state = dirps();
  
    hp->rxints++;
    (*hp->rx_state)(hp);
    restore(i_state);
}
  
/* drrx_enable
 * Receive ENABLE state processor
 */
static void
drrx_enable(hp)
struct drchan *hp;
{
    register int16 base = hp->base;
  
    write_scc(base, R1, INT_ALL_Rx|EXT_INT_ENAB);
    write_scc(base, R15, BRKIE);    /* Allow ABORT Int */
    write_scc(base, R14, BRSRC|BRENABL|SEARCH);
    /* Turn on rx and enter hunt mode */
    write_scc(base, R3, ENT_HM|RxENABLE|RxCRC_ENAB|Rx8);
  
    if(hp->rcvbuf != NULLBUF){
        hp->rcvbuf->cnt = 0;
        hp->rcp = hp->rcvbuf->data;
    }
    hp->rstate = ACTIVE;
    hp->rx_state = drrx_active;
}
  
/* drrx_active
 * Receive ACTIVE state processor
 */
static void
drrx_active(hp)
struct drchan *hp;
{
    register int16 base = hp->base;
    unsigned char rse,st;
    struct phdr *phdr;
    struct mbuf *bp;
  
    /* Allocate a receive buffer if not already present */
    if(hp->rcvbuf == NULLBUF){
        bp = hp->rcvbuf = alloc_mbuf(hp->bufsiz);
        if(bp == NULLBUF){
            /* No buffer - abort the receiver */
            write_scc(base, R3, ENT_HM|RxENABLE|RxCRC_ENAB|Rx8);
            /* Clear character from rx buffer in SIO */
            (void) inportb(base+DATA);
            return;
        }
        hp->rcvbuf->cnt = 0;
        hp->rcp = hp->rcvbuf->data;
    }
  
    st = read_scc(base, R0); /* get interrupt status from R0 */
    rse = read_scc(base,R1); /* get special status from R1 */
  
    if(st & Rx_CH_AV){
        /* there is a char to be stored
         * read special condition bits before reading the data char
         * (already read above)
         */
        if(rse & Rx_OVR){
            /* Rx overrun - toss buffer */
            hp->rcp = hp->rcvbuf->data; /* reset buffer pointers */
            hp->rcvbuf->cnt = 0;
            hp->rstate = RXERROR;   /* set error flag */
            hp->rovers++;       /* count overruns */
        } else if(hp->rcvbuf->cnt >= hp->bufsiz){
            /* Too large -- toss buffer */
            hp->toobig++;
            hp->rcp = hp->rcvbuf->data; /* reset buffer pointers */
            hp->rcvbuf->cnt = 0;
            hp->rstate = TOOBIG;    /* when set, chars are not stored */
        }
        /* ok, we can store the received character now */
        if((hp->rstate == ACTIVE) && ((st & BRK_ABRT) == 0)){
            *hp->rcp++ = inportb(base+DATA); /* char to rcv buff */
            hp->rcvbuf->cnt++;       /* bump count */
        } else {
            /* got to empty FIFO */
            (void) inportb(base+DATA);
            hp->rcp = hp->rcvbuf->data; /* reset buffer pointers */
            hp->rcvbuf->cnt = 0;
            hp->rstate = RXABORT;
            write_scc(base,R0,ERR_RES); /* reset err latch */
        }
    }
    /* 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
     */
    if(rse & END_FR){
        hp->rxframes++;
        /* END OF FRAME -- Make sure Rx was active */
        if(hp->rcvbuf->cnt > 0){    /* any data to store */
            /* looks like a frame was received
             * now is the only time we can check for CRC error
             */
            if((rse & CRC_ERR) || (hp->rstate > ACTIVE) ||
            (hp->rcvbuf->cnt < 10) || (st & BRK_ABRT)){
                /* error occurred; toss frame */
                if(rse & CRC_ERR)
                    hp->crcerr++;   /* count CRC errs */
                hp->rcp = hp->rcvbuf->data;
                hp->rcvbuf->cnt = 0;
                hp->rstate = ACTIVE;   /* Clear error state */
            } else {
                /* Here we have a valid frame */
                bp = alloc_mbuf(sizeof(struct phdr));
                bp->cnt = sizeof(struct phdr);
                phdr = (struct phdr *)bp->data;
                phdr->type = CL_AX25;
                phdr->iface = hp->iface;
                bp->next = hp->rcvbuf;
                hp->rcvbuf->cnt -= 2;    /* chuck FCS bytes */
                enqueue(&Hopper, bp);    /* queue it in */
                hp->enqueued++;
                /* packet queued - reset buffer pointer */
                hp->rcvbuf = NULLBUF;
            } /* end good frame queued */
        }  /* end check for active receive upon EOF */
    }
}
  
/*
 * TX finite state machine - dispatcher
 */
static void
tx_fsm(hp)
struct drchan *hp;
{
    char i_state = dirps();
    if(hp->tstate != DEFER && hp->tstate)
        hp->txints++;
    (*hp->tx_state)(hp);
    restore(i_state);
}
  
/* drtx_idle
 * Transmit IDLE transmit state processor
 */
static void
drtx_idle(hp)
struct drchan *hp;
{
    register int16 base;
  
    /* Tx idle - is there a frame to transmit ? */
    if((hp->sndbuf = dequeue(&hp->sndq)) == NULLBUF){
        /* Nothing to send - return to receive mode
         * Turn Tx off - any trailing flag should have been sent
         * by now
         */
#ifdef DRSIDEBUG
        printf("Nothing to TX\n");
#endif
        base = hp->base;
        write_scc(base, R5, Tx8|DTR);   /* Tx off now */
        write_scc(base, R0, ERR_RES);   /* Reset error bits */
  
        /* Delay for squelch tail before enabling receiver */
        hp->rstate = ENABLE;
        hp->rx_state = drrx_enable;
        dr_wake(hp, RX, rx_fsm, hp->squeldelay);
    } else {
        /* Frame to transmit */
        hp->tstate = DEFER;
        hp->tx_state = drtx_defer;
        drtx_defer(hp);
    }
}
  
/* drtx_defer
 * Transmit DEFER state processor
 */
static void
drtx_defer(hp)
struct drchan *hp;
{
    register int16 base = hp->base;
  
    /* We may have defered a previous tx attempt - in any event...
     * Check DCD in case someone is already transmitting
     * then check to see if we should defer due to P-PERSIST.
     */
  
#ifdef DRSIDEBUG
    printf("drtx_defer - checking for DCD\n");
#endif
    if((read_scc(base+CTL, R0) & DCD) > 0){
        /* Carrier detected - defer */
        hp->txdefers++;
        dr_wake(hp, TX, tx_fsm, 10);    /* Defer for 100 mS */
#ifdef DRSIDEBUG
        printf("drtx_defer - TX deferred\n");
#endif
        return;
    }
  
#ifdef DRSIDEBUG
    printf("drtx_defer - checking for P-PERSIST backoff\n");
#endif
    /* P-PERSIST is checked against channel 3 of the 8536 which is
     * the free running counter for the 10 mS tick; The counter
     * goes through 0x6000 ticks per 10 mS or one tick every
     * 407 nS - this is pretty random compared to the DOS time of
     * day clock (0x40:0x6C) used by the other (EAGLE) drivers.
     */
    if (hp->persist <= read_ctc(base,Z8536_CC3LSB)) {
#ifdef DRSIDEBUG
        printf("drtx_defer - BACKOFF\n");
#endif
        hp->txppersist++;
        dr_wake (hp, TX, tx_fsm, hp->slotime);
        return;
    }
    /* No backoff - set RTS and start to transmit frame */
    write_scc(base, R1, 0);     /* Prevent external status int */
    write_scc(base, R3, Rx8);   /* Turn Rx off */
    hp->rstate = IDLE;      /* Mark Rx as idle */
    hp->tstate = RRTS;
    hp->tx_state = drtx_rrts;
#ifdef DRSIDEBUG
    printf("drtx_defer - wake posted for drtx_rrts\n");
#endif
    write_scc(base, R9, 0);     /* Interrupts off */
    write_scc(base,R5,RTS|Tx8|DTR); /* Turn tx on */
    dr_wake(hp, TX, tx_fsm, 10);
}
  
/* drtx_rrts
 * Transmit RRTS state processor
 */
static void
drtx_rrts(hp)
struct drchan *hp;
{
    register int16 base = hp->base;
  
    write_scc(base, R9, 0); /* Interrupts off */
    write_scc(base,R5,TxCRC_ENAB|RTS|TxENAB|Tx8|DTR);   /* Tx now on */
    hp->tstate = TFIRST;
    hp->tx_state = drtx_tfirst;
#ifdef DRSIDEBUG
    printf("8530 Int status %x\n", read_scc(base+CHANA,R3) & 0xff);
    printf("drtx_rrts - Wake posted for TXDELAY\n");
#endif
    dr_wake(hp, TX, tx_fsm, hp->txdelay);
}
  
/* drtx_tfirst
 * Transmit TFIRST state processor
 */
static void
drtx_tfirst(hp)
struct drchan *hp;
{
    register int16 base = hp->base;
    char c;
  
    /* Copy data to a local buffer to save on mbuf overheads
     * during transmit interrupt time.
     */
    hp->drtx_cnt = len_p(hp->sndbuf);
    hp->drtx_tcp = hp->drtx_buffer;
  
    pullup(&hp->sndbuf, hp->drtx_tcp, hp->drtx_cnt);
  
    /* Transmit the first character in the buffer */
    c = *hp->drtx_tcp++;
    hp->drtx_cnt--;
  
    write_scc(base, R0, RES_Tx_CRC);    /* Reset CRC */
    write_scc(base, R0, RES_EOM_L);     /* Reset underrun latch */
    outportb(base+DATA, c);         /* Output first character */
    write_scc(base, R15, TxUIE);        /* Allow underrun ints only */
    write_scc(base, R1, TxINT_ENAB|EXT_INT_ENAB); /* Tx/Ext status ints on */
    write_scc(base, R9, MIE|NV);        /* master enable */
    hp->tstate = ACTIVE;
    hp->tx_state = drtx_active;
}
  
/* drtx_active
 * Transmit ACTIVE state processor
 */
static void
drtx_active(hp)
struct drchan *hp;
{
    if(hp->drtx_cnt-- > 0){
        /* Send next character */
        outportb(hp->base+DATA, *hp->drtx_tcp++);
    } else {
        /* No more to send - wait for underrun to hit */
        hp->tstate = UNDERRUN;
        hp->tx_state = drtx_flagout;
        free_p(hp->sndbuf);
        write_scc(hp->base, R0, RES_EOM_L);  /* Send CRC on underrun */
        write_scc(hp->base, R0, RES_Tx_P);   /* Reset Tx Int pending */
    }
}
  
/* drtx_flagout
 * Transmit FLAGOUT state processor
 */
static void
drtx_flagout(hp)
struct drchan *hp;
{
    /* Arrive here after CRC sent and Tx interrupt fires.
     * Post a wake for ENDDELAY
     */
  
    hp->tstate = UNDERRUN;
    hp->tx_state = drtx_downtx;
    write_scc(hp->base, R9, 0);
    write_scc(hp->base, R0,  RES_Tx_P);
    dr_wake(hp, TX, tx_fsm, hp->enddelay);
}
  
/* drtx_downtx
 * Transmit DOWNTX state processor
 */
static void
drtx_downtx(hp)
struct drchan *hp;
{
    register int base = hp->base;
  
    /* See if theres anything left to send - if there is,  send it ! */
    if((hp->sndbuf = dequeue(&hp->sndq)) == NULLBUF){
        /* Nothing left to send - return to receive */
        write_scc(base, R5, Tx8|DTR);   /* Tx off now */
        write_scc(base, R0, ERR_RES);   /* Reset error bits */
        hp->tstate = IDLE;
        hp->tx_state = drtx_idle;
        hp->rstate = ENABLE;
        hp->rx_state = drrx_enable;
        drrx_enable(hp);
    } else
        drtx_tfirst(hp);
  
}
  
/* Write CTC register */
static void
write_ctc(port, reg, val)
unsigned port,reg,val;
{
    char i_state;
    int16 base = port;
  
    i_state = dirps();
    /* Select register */
    outportb(base+Z8536_MASTER,(char)reg);
    outportb(base+Z8536_MASTER,(char)val);
    restore(i_state);
}
  
/* Read CTC register */
static char
read_ctc(port, reg)
unsigned port, reg;
{
    char c,i_state;
    int16 i,base = port;
  
    i_state = dirps();
    /* Select register */
    outportb(base+Z8536_MASTER,(char)(reg&0xFF));
    /* Delay for a short time to allow 8536 to settle */
    for(i=0;i<100;i++);
    c = inportb(base+Z8536_MASTER);
    restore(i_state);
    return(c&0xFF);
}
  
/* Initialize dr controller parameters */
static int
drchanparam(hp)
register struct drchan *hp;
{
    int16 tc;
    long br;
    char i_state;
    register int16 base;
  
    /* Initialize 8530 channel for SDLC operation */
    base = hp->base;
    i_state = dirps();
  
    switch(base & 2){
        case 2:
            write_scc(base,R9,CHRA);    /* Reset channel A */
            break;
        case 0:
            write_scc(base,R9,CHRB);    /* Reset channel B */
            break;
    }
    /* Deselect all Rx and Tx interrupts */
    write_scc(base,R1,0);
  
    /* Turn off external interrupts (like CTS/CD) */
    write_scc(base,R15,0);
  
    /* X1 clock, SDLC mode */
    write_scc(base,R4,SDLC|X1CLK);   /* SDLC mode and X1 clock */
  
    /* Now some misc Tx/Rx parameters */
    /* CRC PRESET 1, NRZI Mode */
    write_scc(base,R10,CRCPS|NRZI);
  
    /* Set up BRG and DPLL multiplexers */
    /* Tx Clk from RTxC. Rcv Clk from DPLL, TRxC pin outputs BRG */
    write_scc(base,R11,RCDPLL|TCRTxCP|TRxCOI|TRxCBR);
  
    /* Null out SDLC start address */
    write_scc(base,R6,0);
  
    /* SDLC flag */
    write_scc(base,R7,FLAG);
  
    /* Set up the Transmitter but don't enable it */
    /*  DTR, 8 bit TX chars only - TX NOT ENABLED */
    write_scc(base,R5,Tx8|DTR);
  
    /* Receiver - initial setup only - more later */
    write_scc(base,R3,Rx8);      /* 8 bits/char */
  
    /* Setting up BRG now - turn it off first */
    write_scc(base,R14,BRSRC);     /* BRG off, but keep Pclk source */
  
    /* set the 32x time constant for the BRG */
  
    br = hp->speed;         /* get desired speed */
    tc = ((XTAL/32)/br)-2;      /* calc 32X BRG divisor */
  
    write_scc(base,R12,tc&0xFF);      /* lower byte */
    write_scc(base,R13,(tc>>8)&0xFF); /* upper bite */
  
    /* Time to set up clock control register for RECEIVE mode
     * DRSI has xtal osc going to pclk at 4.9152 Mhz
     * The BRG is sourced from that, and set to 32x clock
     * The DPLL is sourced from the BRG.  BRG is fed to the TRxC pin
     * Transmit clock is provided by the BRG externally divided by
     * 32 in the CTC counter 1 and 2.
     * Receive clock is from the DPLL
     */
  
    /* Following subroutine sets up and ENABLES the receiver */
    drrx_enable(hp);
  
    /* DPLL from BRG, BRG source is PCLK */
    write_scc(hp->base,R14,BRSRC|SSBR);
    /* SEARCH mode, keep BRG source */
    write_scc(hp->base,R14,BRSRC|SEARCH);
    /* Enable the BRG */
    write_scc(hp->base,R14,BRSRC|BRENABL);
  
    /* enable the receive interrupts */
  
    write_scc(hp->base,R1,(INT_ALL_Rx|EXT_INT_ENAB));
    write_scc(hp->base,R15,BRKIE);  /* ABORT int */
    write_scc(hp->base,R9,MIE|NV);  /* master enable */
  
  
    /* Now, turn on the receiver and hunt for a flag */
    write_scc(hp->base,R3,RxENABLE|RxCRC_ENAB|Rx8);
  
    restore(i_state);
    return 0;
}
  
/*
 * Initialize the CTC (8536)
 * Only the counter/timers are used - the IO ports are un-comitted.
 * Channels 1 and 2 are linked to provide a /32 counter to convert
 * the SIO BRG to a real clock for Transmit clocking.
 * CTC 3 is left free running on a 10 mS period.  It is always polled
 * and therefore all interrupts from the chip are disabled.
 *
 * Updated 02/16/89 by N6TTO
 * Changed to support the use of the second channel on the 8530.
 * Added so that the driver works on the DRSI type 2 PC Adaptor
 * which has 2 1200 bps modems.
 *
 */
static void
drinitctc(port)
unsigned port;
{
    long i;
  
    /* Initialize 8536 CTC */
  
    /* Initialize 8536 */
    /* Start by forcing chip into known state */
    (void) read_ctc(port, Z8536_MICR);
  
    write_ctc(port, Z8536_MICR, 0x01);  /* Reset the CTC */
    for(i=0;i < 1000L; i++)     /* Loop to delay */
        ;
    write_ctc(port, Z8536_MICR, 0x00);  /* Clear reset and start init seq. */
  
    /* Wait for chip to come ready */
    while((read_ctc(port, Z8536_MICR)) != (char) 0x02)
        ;
  
    write_ctc(port, Z8536_MICR, 0xa6);  /* MIE|NV|CT_VIS|RJA */
    write_ctc(port, Z8536_MCCR, 0xf4);  /* PBE|CT1E|CT2E|CT3E|PAE */
  
    write_ctc(port, Z8536_CTMS1, 0xe2); /* Continuous,EOE,ECE, Pulse output */
    write_ctc(port, Z8536_CTMS2, 0xe2); /* Continuous,EOE,ECE, Pulse output */
    write_ctc(port, Z8536_CTMS3, 0x80); /* Continuous,Pulse output */
    write_ctc(port, Z8536_CT1MSB, 0x00);    /* Load time constant CTC #1 */
    write_ctc(port, Z8536_CT1LSB, 0x10);
    write_ctc(port, Z8536_CT2MSB, 0x00);    /* Load time constant CTC #2 */
    write_ctc(port, Z8536_CT2LSB, 0x10);
    write_ctc(port, Z8536_CT3MSB, 0x60);    /* Load time constant CTC #3 */
    write_ctc(port, Z8536_CT3LSB, 0x00);
  
    write_ctc(port, Z8536_IVR, 0x06);
  
    /* Set port direction bits in port A and B
     * Data is input on bits d1 and d5, output on d0 and d4.
     * The direction is set by 1 for input and 0 for output
     */
    write_ctc(port, Z8536_PDCA, 0x22);
    write_ctc(port, Z8536_PDCB, 0x22);
  
    write_ctc(port, Z8536_CSR1, Z_GCB|Z_TCB);  /* Start CTC #1 running */
    write_ctc(port, Z8536_CSR2, Z_GCB|Z_TCB);  /* Start CTC #2 running */
    write_ctc(port, Z8536_CSR3, Z_IE|Z_GCB|Z_TCB); /* Start CTC #3 running */
}
  
/* Attach a DRSI interface to the system
 * argv[0]: hardware type, must be "drsi"
 * argv[1]: I/O address, e.g., "0x300"
 * argv[2]: vector, e.g., "2"
 * argv[3]: mode, must be "ax25"
 * argv[4]: iface label, e.g., "dr0"
 * argv[5]: receiver packet buffer size in bytes
 * argv[6]: maximum transmission unit, bytes
 * argv[7]: iface speed for channel A
 * argv[8]: iface speed for channel B (defaults to same as A if absent)
 * argv[9]: First IP address, optional (defaults to Ip_addr)
 * argv[10]: Second IP address, optional (defaults to Ip_addr)
 */
int
dr_attach(argc,argv)
int argc;
char *argv[];
{
    register struct iface *if_pca,*if_pcb;
    struct drchan *hp;
    int dev;
  
    /* Quick check to make sure args are good and mycall is set */
    if(strcmp(argv[3],"ax25") != 0){
        printf("Mode %s unknown for interface %s\n",
        argv[3],argv[4]);
        return -1;
    }
    if(if_lookup(argv[4]) != NULLIF){
        printf(Existingiface, argv[4]);
        return -1;
    }
    if(Mycall[0] == '\0'){
        printf("set mycall first\n");
        return -1;
    }
    /* Note: More than one card can be supported if you give up a COM:
     * port, thus freeing up an IRQ line and port address
     */
    if(Drnbr >= DRMAX){
        printf("Only %d DRSI controller(s) supported right now!\n",DRMAX);
        return -1;
    }
    dev = Drnbr++;
  
    /* Initialize hardware-level control structure */
    Drsi[dev].addr = htoi(argv[1]);
    Drsi[dev].vec = htoi(argv[2]);
  
    /* Save original interrupt vector */
    Drsi[dev].oldvec = getirq(Drsi[dev].vec);
  
    /* Set new interrupt vector */
    if(setirq(Drsi[dev].vec,Drhandle[dev]) == -1){
        printf("IRQ %u out of range\n",Drsi[dev].vec);
        Drnbr--;
    }
    /* Initialize the CTC */
    drinitctc(Drsi[dev].addr);
  
    /* Create iface structures and fill in details */
    if_pca = (struct iface *)callocw(1,sizeof(struct iface));
    if_pcb = (struct iface *)callocw(1,sizeof(struct iface));
  
    if_pca->addr = if_pcb->addr = Ip_addr;
    if(argc > 9)
        if_pca->addr = resolve(argv[9]);
    if(argc > 10)
        if_pcb->addr = resolve(argv[10]);
    if(if_pca->addr == 0 || if_pcb->addr == 0){
        tputs(Noipaddr);
        free((char *)if_pca);
        free((char *)if_pcb);
        return -1;
    }
    /* Append "a" to iface associated with A channel */
    if_pca->name = mallocw((unsigned)strlen(argv[4])+2);
    strcpy(if_pca->name,argv[4]);
    strcat(if_pca->name,"a");
  
    /* Append "b" to iface associated with B channel */
    if_pcb->name = mallocw((unsigned)strlen(argv[4])+2);
    strcpy(if_pcb->name,argv[4]);
    strcat(if_pcb->name,"b");
  
    if_pcb->mtu = if_pca->mtu = atoi(argv[6]);
    if_pcb->type = if_pca->type = CL_AX25;
    if_pcb->ioctl = if_pca->ioctl = dr_ctl;
    if_pca->dev = 2*dev;            /* dr0a */
    if_pcb->dev = 2*dev + 1;        /* dr0b */
    if_pcb->stop = if_pca->stop = dr_stop;
    if_pcb->output = if_pca->output = ax_output;
    if_pcb->raw = if_pca->raw = dr_raw;
  
    if(strcmp(argv[3],"ax25") == 0){
        /* Must be true, was checked at top */
        if_pcb->send = if_pca->send = ax_send;
        if(if_pcb->hwaddr == NULLCHAR)
            if_pcb->hwaddr = mallocw(sizeof(Mycall));
        memcpy(if_pcb->hwaddr,(char *)&Mycall,sizeof(Mycall));
        if(if_pca->hwaddr == NULLCHAR)
            if_pca->hwaddr = mallocw(sizeof(Mycall));
        memcpy(if_pca->hwaddr,(char *)&Mycall,sizeof(Mycall));
    }
    /* Link em in to the iface chain */
    if_pca->next = if_pcb;
    if_pcb->next = Ifaces;
    Ifaces = if_pca;
  
    /* Configure the tcp and ax25 parameters - WG7J */
    setencap(if_pca,NULL);
    setencap(if_pcb,NULL);
  
    /* set params in drchan table for CHANNEL B */
  
    hp = &Drchan[2*dev+1];              /* dr1 is offset 1 */
    hp->iface = if_pcb;
    hp->stata = Drsi[dev].addr + CHANA + CTL;   /* permanent status */
    hp->statb = Drsi[dev].addr + CHANB + CTL;   /* addrs for CHANA/B*/
    if(argc > 8){
        /* Separate speed for channel B */
        hp->speed = (int16)atoi(argv[8]);
    } else {
        /* Set speed to same as for channel A */
        hp->speed = (int16)atoi(argv[7]);
    }
    hp->base = Drsi[dev].addr + CHANB;
    hp->bufsiz = atoi(argv[5]);
    hp->drtx_buffer = mallocw(if_pcb->mtu+100);
    hp->tstate = IDLE;
    hp->tx_state = drtx_idle;
    hp->w[RX].wcall = NULL;
    hp->w[RX].wakecnt = 0;
    hp->w[TX].wcall = NULL;
    hp->w[TX].wakecnt = 0;
    /* default KISS Params */
    hp->txdelay = 25;       /* 250 Ms */
    hp->persist = 64;       /* 25% persistence */
    hp->slotime = 10;       /* 100 Ms */
    hp->squeldelay = 20;        /* 200 Ms */
    hp->enddelay = 10;      /* 100 Ms */
  
    write_scc(hp->stata,R9,FHWRES);     /* Hardware reset */
  
    /* Disable interrupts with Master interrupt ctrl reg */
    write_scc(hp->stata,R9,0);
  
    drchanparam(hp);
  
    /* Initialize buffer pointers */
    hp->rcvbuf = NULLBUF;
    hp->rcvbuf->cnt = 0;
    hp->sndq = NULLBUF;
  
    /* set params in drchan table for CHANNEL A */
    hp = &Drchan[2*dev];            /* dr0a is offset 0 */
    hp->iface = if_pca;
    hp->speed = (int16)atoi(argv[7]);
    hp->base = Drsi[dev].addr + CHANA;
    hp->bufsiz = atoi(argv[5]);
    hp->drtx_buffer = mallocw(if_pca->mtu+100);
    hp->tstate = IDLE;
    hp->tx_state = drtx_idle;
    hp->w[RX].wcall = NULL;
    hp->w[RX].wakecnt = 0;
    hp->w[TX].wcall = NULL;
    hp->w[TX].wakecnt = 0;
    /* default KISS Params */
    hp->txdelay = 30;       /* 300 Ms */
    hp->persist = 64;       /* 25% persistence */
    hp->slotime = 10;       /* 100 Ms */
    hp->squeldelay = 20;        /* 200 Ms */
    hp->enddelay = 10;      /* 100 Ms */
  
    drchanparam(hp);
  
    /* Initialize buffer pointers */
    hp->rcvbuf = NULLBUF;
    hp->rcvbuf->cnt = 0;
    hp->sndq = NULLBUF;
  
    write_scc(hp->base,R9,MIE|NV);      /* master interrupt enable */
  
    /* Enable interrupt in 8259 interrupt controller */
    maskon(Drsi[dev].vec);
  
    return 0;
}
  
/* Shut down iface */
static int
dr_stop(iface)
struct iface *iface;
{
    int16 dev;
  
    dev = iface->dev;
    if(dev & 1)
        return 0;
    dev >>= 1;  /* Convert back into DRSI number */
  
    /* Set 8259 interrupt mask (turn off interrupts) */
    maskoff(Drsi[dev].vec);
  
    /* Restore original interrupt vector */
    setirq(Drsi[dev].vec, Drsi[dev].oldvec);
    Drnbr--;
  
    /* Force hardware reset */
    write_scc(Drsi[dev].addr + CHANA + CTL,R9,FHWRES);
    /* Reset the CTC */
    (void) read_ctc(Drsi[dev].addr, Z8536_MICR);
    write_ctc(Drsi[dev].addr, Z8536_MICR, 0x01);
    return 0;
}
  
/* Send raw packet on DRSI card */
static int
dr_raw(iface,bp)
struct iface *iface;
struct mbuf *bp;
{
    char kickflag;
    struct drchan *hp;
    char i_st = dirps();
  
    dump(iface,IF_TRACE_OUT,CL_AX25,bp);
    iface->rawsndcnt++;
    iface->lastsent = secclock();
    hp = &Drchan[iface->dev];
    kickflag = (hp->sndq == NULL) & (hp->sndbuf == NULLBUF);
    /* clever! flag=1 if something in queue */
    enqueue(&hp->sndq,bp);
  
    if(kickflag)            /* simulate interrupt to xmit */
        tx_fsm(hp);     /* process interrupt */
  
    restore(i_st);
    return 0;
}
  
/* display DRSI Channel stats */
int
dodrstat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct drchan *hp0, *hp1;
    int i;
  
    for(i=0; i<DRMAX; i++){
        hp0 = &Drchan[i];
        hp1 = &Drchan[i+1];
        i = Drchan[i].base;
        tputs("DRSI Board Statistics - N6TTO 112790.0\n");
        tputs("--------------------------------------\n");
        tprintf("Channel - %s\n", hp0->iface->name);
        tprintf("Rxints  - %8ld  Txints  - %8ld  Exints  - %8ld\n",
        hp0->rxints, hp0->txints, hp0->exints);
        tprintf("Enqued  - %8ld  Crcerr  - %8ld  Aborts  - %8ld\n",
        hp0->enqueued, hp0->crcerr, hp0->aborts);
        tprintf("RFrames - %8ld  Rxovers - %8ld  TooBig  - %8ld\n",
        hp0->rxframes, hp0->rovers, hp0->toobig);
        tprintf("Txdefer - %8ld  Txppers - %8ld  Nomem   - %8ld\n",
        hp0->txdefers, hp0->txppersist, hp0->nomem);
        tprintf("Tx state  %8d  Rx state  %8d\n\n",hp0->tstate,hp0->rstate);
        tprintf("Channel - %s\n", hp1->iface->name);
        tprintf("Rxints  - %8ld  Txints  - %8ld  Exints  - %8ld\n",
        hp1->rxints, hp1->txints, hp1->exints);
        tprintf("Enqued  - %8ld  Crcerr  - %8ld  Aborts  - %8ld\n",
        hp1->enqueued, hp1->crcerr, hp1->aborts);
        tprintf("RFrames - %8ld  Rxovers - %8ld  TooBig  - %8ld\n",
        hp1->rxframes, hp1->rovers, hp1->toobig);
        tprintf("Txdefer - %8ld  Txppers - %8ld  Nomem   - %8ld\n",
        hp1->txdefers, hp1->txppersist, hp1->nomem);
        tprintf("Tx state  %8d  Rx state  %8d\n",hp1->tstate,hp1->rstate);
    }
    return 0;
}
  
/* Subroutine to set kiss params in channel tables */
static int32
dr_ctl(iface,cmd,set,val)
struct iface *iface;
int cmd;
int set;
int32 val;
{
    struct drchan *hp;
    hp = &Drchan[iface->dev];
  
    switch(cmd){
        case PARAM_TXDELAY:
            if(set)
                hp->txdelay = (int)val;
            return hp->txdelay;
        case PARAM_PERSIST:
            if(set)
                hp->persist = (int)val;
            return hp->persist;
        case PARAM_SLOTTIME:
            if(set)
                hp->slotime = (int)val;
            return hp->slotime;
        case PARAM_TXTAIL:
            if(set)
                hp->squeldelay = (int)val;
            return hp->squeldelay;
        case PARAM_ENDDELAY:
            if(set)
                hp->enddelay = (int)val;
            return hp->enddelay;
        case PARAM_SPEED:
            return hp->speed;
    }
    return -1;
}
#endif /* DRSI */
  
