home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 1
/
HamRadio.cdr
/
misc
/
src0131
/
scc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-12-09
|
47KB
|
1,481 lines
/* Generic driver for Z8530 boards, modified from the PE1CHL
* driver for use with NOS. This version also supports the NRS
* mode when used as an asynch port. Device setup is similar to
* that of the PE1CHL version, with the addition of user specification
* of buffer size (bufsize). See the file "scc.txt" for general
* information on the use of this driver and setup procedures.
*
* General differences between this driver and the original version:
*
* 1) Slip encoding and decoding is not done in the driver, but
* using the routines in slip.c, and these routines are supported
* in a manner similar to the asynch routines for the 8250. The
* input is handled via fifo buffer, while output is direct. The
* routines scc_send and get_scc are called via pointers in the
* Slip and Nrs structs for the parcticular channel.
*
* 2) The timer routine, scctim, is not installed directly in the
* timer interrupt chain, but is called through the systick routine
* in pc.c.
*
* 3) Facilities of nos are used whenever possible in place of direct
* structure or variable manipulation. Mbuf management is handled
* this way, along with interface initialization.
*
* 4) Nrs mode support is added in a manner similar to that of the
* Slip support. I have not had an opportunity to test this, but
* it is essentially identical to the way the 8250 version works.
*
* 5) Callsign specification on radio modes (kiss,nrs,ax25) is an
* option. If not supplied, the value of Mycall will be used.
*
* 6) Bufsize specification is now a parameter on setup of each channel.
* This is the size of the fifo on asynch input, and the size of
* mbuf buffers for sdlc mode. Since the fifo buffer can fill up,
* this value should be reasonably large for asynch mode. Mbufs
* are chained when they fill up, so having a small bufsize with
* sdlc modes (ax25) does not result in loss of characters.
*
* 7) Because slip and nrs decoding is handled outside the driver,
* sccstat cannot be used to report sent and receive packet counts
* in asynch mode, and these fields are blanked on display in asynch
* modes.
*
*
* I am interested in setting up some default initializations for
* the popular Z8530 boards, to minimize user problems in constructing
* the proper attach init entries. These would allow for shortened
* entries to use the defaults, such as "attach scc 1 init drsi" to
* attach a DRSI board in standard configuration at its default address.
* Since I do not have complete technical information on all such boards,
* I would very much appreciate any information that users can provide
* me regarding particular boards.
*
* 1/25/90
*
* Modifications:
*
* 2/17/90:
*
* 1) Added mods from PE1CHL which reflect relevent changes to the
* scc driver in his version of net between 10/89 and 1/90. Changes
* incorporated include additional delays in sccvec.asm, addition
* of external clock mode, and initialization for the 8536 as a
* clock divider on the DRSI board. "INLINE" is a slight delay
* for register access incorporated for use with the inline i/o
* code in MSC. This may not be useful or necessary with TURBO.
* Changes making "TPS" a variable were not added, since the
* scc timer does not install itself on the hardware interrupt
* in this version.
*
*
* Ken Mitchum, KY3B km@cs.pitt.edu km@dsl.pitt.edu
* or mail to the tcpip group
*
*/
/* Added ANSI-style prototypes, reformatted source, minor delinting.
* Integrated into standard 900201 NOS by KA9Q.
*/
/*
* Generic driver for Z8530 SCC chip in SLIP, KISS or AX.25 mode.
*
* Written by R.E. Janssen (PE1CHL) using material from earlier
* EAGLE and PC100 drivers in this package.
*
* The driver has initially been written for my own Atari SCC interface
* board, but it could eventually replace the other SCC drivers.
*
* Unfortunately, there is little consistency between the different interface
* boards, as to the use of a clock source, the solution for the fullduplex
* clocking problem, and most important of all: the generation of the INTACK
* signal. Most designs do not even support the generation of an INTACK and
* the read of the interrupt vector provided by the chip.
* This results in lots of configuration parameters, and a fuzzy
* polltable to be able to support multiple chips connected at one interrupt
* line...
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <dos.h>
#include "global.h"
#include "mbuf.h"
#include "config.h"
#include "netuser.h"
#include "proc.h"
#include "iface.h"
#include "pktdrvr.h"
#include "slip.h"
#include "nrs.h"
#include "8250.h"
#include "scc.h"
#include "8530.h"
#include "8536.h"
#include "ax25.h"
#include "trace.h"
#include "pc.h"
#include "kiss.h"
/* interrupt handlers */
extern INTERRUPT sccvec();
extern INTERRUPT sccnovec();
/* variables used by the SCC interrupt handler in sccvec.asm */
static INTERRUPT (*Orgivec)(); /* original interrupt vector */
struct sccinfo Sccinfo = {0}; /* global info about SCCs */
struct sccchan *Sccchan[2 * MAXSCC] = {0}; /* information per channel */
ioaddr Sccvecloc = {0}; /* location to access for SCC vector */
unsigned char Sccmaxvec = {0}; /* maximum legal vector from SCC */
ioaddr Sccpolltab[MAXSCC+1][2] = {0}; /* polling table when no vectoring */
#if defined(INLINE)
static unsigned scc_delay __ARGS((unsigned v));
static unsigned scc_delay (v) /* delay for about 5 PCLK cycles */
unsigned v; /* pass-through used for input */
{
register int i,j; /* it takes time to save them */
return v; /* return the passed parameter */
}
#endif
unsigned char Random = 0; /* random number for p-persist */
static int scc_call __ARGS((struct iface *ifp,char *call));
static int scc_init __ARGS((int nchips,ioaddr iobase,int space,int aoff,
int boff,int doff,ioaddr intack,int ivec,long clk,int pclk,int hwtype,
int hwparam));
static int scc_raw __ARGS((struct iface *ifp,struct mbuf *bp));
static int scc_stop __ARGS((struct iface *ifp));
static int get_scc __ARGS((int dev));
static int scc_send __ARGS((int dev,struct mbuf *bp));
static int scc_async __ARGS((struct sccchan *scc));
static void scc_sdlc __ARGS((struct sccchan *scc));
static void scc_tossb __ARGS((struct sccchan *scc));
static void scc_txon __ARGS((struct sccchan *scc));
static void scc_txoff __ARGS((struct sccchan *scc));
static int scc_aioctl __ARGS((struct iface *ifp,int argc,char *argv[]));
static int scc_sioctl __ARGS((struct iface *ifp,int argc,char *argv[]));
static void scc_sstart __ARGS((struct sccchan *scc));
static unsigned int scc_speed __ARGS((struct sccchan *scc,
unsigned int clkmode,long speed));
static void scc_asytx __ARGS((struct sccchan *scc));
static void scc_asyex __ARGS((struct sccchan *scc));
static void scc_asyrx __ARGS((struct sccchan *scc));
static void scc_asysp __ARGS((struct sccchan *scc));
static void scc_sdlctx __ARGS((struct sccchan *scc));
static void scc_sdlcex __ARGS((struct sccchan *scc));
static void scc_sdlcrx __ARGS((struct sccchan *scc));
static void scc_sdlcsp __ARGS((struct sccchan *scc));
/* Attach an SCC channel to the system, or initialize SCC driver.
* operation depends on argv[2]:
* when "init", the SCC driver is initialized, and global information about
* the hardware is set up.
* argv[0]: hardware type, must be "scc"
* argv[1]: number of SCC chips we will support
* argv[2]: mode, must be: "init" in this case
* argv[3]: base address of SCC chip #0 (hex)
* argv[4]: spacing between SCC chip base addresses
* argv[5]: offset from chip base address to channel A control register
* argv[6]: offset from chip base address to channel B control register
* argv[7]: offset from each channel's control register to data register
* argv[8]: address of INTACK/Read Vector port. 0 to read from RR3A/RR2B
* argv[9]: CPU interrupt vector number for all connected SCCs
* argv[10]: clock frequency (PCLK/RTxC) of all SCCs in cycles per second
* prefix with "p" for PCLK, "r" for RTxC clock (for baudrate gen)
* argv[11]: optional hardware type (for special features)
* argv[12]: optional extra parameter for special hardware
*
* otherwise, a single channel is attached using the specified parameters:
* argv[0]: hardware type, must be "scc"
* argv[1]: SCC channel number to attach, 0/1 for first chip A/B, 2/3 for 2nd...
* argv[2]: mode, can be:
* "slip", "kiss", "ax25"
* argv[3]: interface label, e.g., "sl0"
* argv[4]: maximum transmission unit, bytes
* argv[5]: interface speed, e.g, "1200". prefix with "d" when an external
* divider is available to generate the TX clock. When the clock
* source is PCLK, this can be a /32 divider between TRxC and RTxC.
* When the clock is at RTxC, the TX rate must be supplied at TRxC.
* This is needed only for AX.25 fullduplex.
* When this arg is given as "ext", the transmit and receive clock
* are external, and the BRG and DPLL are not used.
* argv[6]: buffer size
* argv[7]: callsign used on the radio channels (optional)
*/
int
scc_attach(argc,argv)
int argc;
char *argv[];
{
register struct iface *ifp;
struct sccchan *scc;
unsigned int chan,brgrate;
int pclk = 0,hwtype = 0,hwparam = 0;
int xdev;
/* first handle the special "init" mode, to initialize global stuff */
if(!strcmp(argv[2],"init")){
if(argc < 11) /* need at least argv[1]..argv[10] */
return -1;
if(isupper(argv[10][0]))
argv[10][0] = tolower(argv[10][0]);
if(argv[10][0] == 'p'){ /* wants to use PCLK as clock? */
pclk = 1;
argv[10]++;
} else {
if(argv[10][0] == 'r') /* wants to use RTxC? */
argv[10]++; /* that's the default */
}
if(argc > 11) /* optional hardware type */
hwtype = htoi(argv[11]); /* it is given in hex */
if(argc > 12) /* optional hardware param */
hwparam = htoi(argv[12]); /* also in hex */
return scc_init(atoi(argv[1]),(ioaddr) htol(argv[3]),atoi(argv[4]),
atoi(argv[5]),atoi(argv[6]),atoi(argv[7]),
(ioaddr) htol(argv[8]),atoi(argv[9]),
atol(argv[10]),pclk,hwtype,hwparam);
}
/* not "init", so it must be a valid mode to attach a channel */
if(strcmp(argv[2],"ax25") && strcmp(argv[2],"kiss") &&
strcmp(argv[2],"slip")){
printf("Mode %s unknown for SCC\n",argv[2]);
return -1;
}
if(strcmp(argv[2],"slip") == 0 || strcmp(argv[2],"kiss") == 0){
for(xdev = 0;xdev < SLIP_MAX;xdev++)
if(Slip[xdev].iface == NULLIF)
break;
if(xdev >= SLIP_MAX){
tprintf("Too many slip devices\n");
return -1;
}
}
if(strcmp(argv[2],"nrs") == 0){
for(xdev = 0;xdev < NRS_MAX;xdev++)
if(Nrs[xdev].iface == NULLIF)
break;
if(xdev >= NRS_MAX){
tprintf("Too many nrs devices\n");
return -1;
}
}
if(!Sccinfo.init){
printf("First init SCC driver\n");
return -1;
}
if((chan = atoi(argv[1])) > Sccinfo.maxchan){
printf("SCC channel %d out of range\n",chan);
return -1;
}
if(Sccchan[chan] != NULLCHAN){
printf("SCC channel %d already attached\n",chan);
return -1;
}
/* create interface structure and fill in details */
ifp = (struct iface *) callocw(1,sizeof(struct iface));
ifp->name = mallocw(strlen(argv[3]) + 1);
strcpy(ifp->name,argv[3]);
ifp->mtu = atoi(argv[4]);
ifp->dev = chan;
ifp->stop = scc_stop;
scc = (struct sccchan *) callocw(1,sizeof(struct sccchan));
scc->ctrl = Sccinfo.iobase + (chan / 2) * Sccinfo.space + Sccinfo.off[chan % 2];
scc->data = scc->ctrl + Sccinfo.doff;
scc->iface = ifp;
if(isupper(argv[5][0]))
argv[5][0] = tolower(argv[5][0]);
switch (argv[5][0]) {
case 'd': /* fulldup divider installed? */
scc->fulldup = 1; /* set appropriate flag */
argv[5]++; /* skip the 'd' */
break;
case 'e': /* external clocking? */
scc->extclock = 1; /* set the flag */
break;
}
scc->bufsiz = atoi(argv[6]);
ifp->addr = Ip_addr;
Sccchan[chan] = scc; /* put addr in table for interrupts */
switch(argv[2][0]){ /* mode already checked above */
#ifdef AX25
case 'a': /* AX.25 */
scc_sdlc(scc); /* init SCC in SDLC mode */
if (!scc->extclock) {
brgrate = scc_speed(scc,32,atol(argv[5]));/* init SCC speed */
scc->speed = Sccinfo.clk / (64L * (brgrate + 2));/* calc real speed */
}
brgrate = scc_speed(scc,32,atol(argv[5]));/* init SCC speed */
scc->speed = Sccinfo.clk / (64L * (brgrate + 2));/* calc real speed */
setencap(ifp,"AX25");
scc_call(ifp,argc > 7 ? argv[7] : (char *) 0); /* set the callsign */
ifp->ioctl = scc_sioctl;
ifp->raw = scc_raw;
/* default KISS Params */
scc->a.params[TXDELAY] = 36*TPS/100; /* 360 ms */
scc->a.params[PERSIST] = 25; /* 10% persistence */
scc->a.params[SLOTTIME] = 16*TPS/100; /* 160 ms */
#if TPS > 67
scc->a.params[TAILTIME] = 3*TPS/100; /* 30 ms */
#else
scc->a.params[TAILTIME] = 2; /* minimal reasonable value */
#endif
scc->a.params[FULLDUP] = 0; /* CSMA */
scc->a.params[TNCSPECIF] = 1; /* DTR set */
scc->a.params[WAITTIME] = 50*TPS/100; /* 500 ms */
scc->a.params[MAXKEYUP] = 7; /* 7 s */
scc->a.params[MINTIME] = 3; /* 3 s */
scc->a.params[IDLETIME] = 120; /* 120 s */
break;
case 'k': /* kiss */
scc_async(scc); /* init SCC in async mode */
brgrate = scc_speed(scc,16,atol(argv[5]));
scc->speed = Sccinfo.clk / (32L * (brgrate + 2));
setencap(ifp,"AX25");
scc_call(ifp,argc > 7 ? argv[7] : (char *) 0); /* set the callsign */
ifp->ioctl = kiss_ioctl;
ifp->raw = kiss_raw;
for(xdev = 0;xdev < SLIP_MAX;xdev++){
if(Slip[xdev].iface == NULLIF)
break;
}
ifp->xdev = xdev;
Slip[xdev].iface = ifp;
Slip[xdev].type = CL_KISS;
Slip[xdev].send = scc_send;
Slip[xdev].get = get_scc;
ifp->rxproc = newproc("ascc rx",256,asy_rx,xdev,NULL,NULL,0);
break;
#endif
#ifdef SLIP
case 's': /* slip */
scc_async(scc); /* init SCC in async mode */
brgrate = scc_speed(scc,16,atol(argv[5]));
scc->speed = Sccinfo.clk / (32L * (brgrate + 2));
setencap(ifp,"SLIP");
ifp->ioctl = scc_aioctl;
ifp->raw = slip_raw;
for(xdev = 0;xdev < SLIP_MAX;xdev++){
if(Slip[xdev].iface == NULLIF)
break;
}
ifp->xdev = xdev;
Slip[xdev].iface = ifp;
Slip[xdev].type = CL_SERIAL_LINE;
Slip[xdev].send = scc_send;
Slip[xdev].get = get_scc;
ifp->rxproc = newproc("ascc rx",256,asy_rx,xdev,NULL,NULL,0);
break;
#endif
#ifdef NRS
case 'n': /* nrs */
scc_async(scc); /* init SCC in async mode */
brgrate = scc_speed(scc,16,atol(argv[5]));
scc->speed = Sccinfo.clk / (32L * (brgrate + 2));
setencap(ifp,"AX25");
scc_call(ifp,argc > 7 ? argv[7] : (char *) 0); /* set the callsign */
ifp->ioctl = scc_aioctl;
ifp->raw = nrs_raw;
for(xdev = 0;xdev < NRS_MAX;xdev++)
if(Nrs[xdev].iface == NULLIF)
break;
ifp->xdev = xdev;
Nrs[xdev].iface = ifp;
Nrs[xdev].send = scc_send;
Nrs[xdev].get = get_scc;
ifp->rxproc = newproc("nscc rx",256,nrs_recv,xdev,NULL,NULL,0);
break;
#endif
}
ifp->next = Ifaces; /* link interface in list */
Ifaces = ifp;
return 0;
}
/* SCC driver initialisation. called on "attach scc <num> init ..." */
static int
scc_init(nchips,iobase,space,aoff,boff,doff,intack,ivec,clk,pclk,hwtype,hwparam)
int nchips; /* number of chips */
ioaddr iobase; /* base of first chip */
int space,aoff,boff,doff;
ioaddr intack; /* INTACK ioport or 0 for no INTACK */
int ivec; /* interrupt vector number */
long clk; /* clock frequency */
int pclk; /* PCLK or RTxC for clock */
int hwtype; /* selection of special hardware types */
int hwparam; /* extra parameter for special hardware */
{
int chip,chan;
ioaddr chipbase;
register ioaddr ctrl;
int i_state,d;
int dum = 1;
#define z 0
if(Sccinfo.init){
printf("SCC driver already initialized - nothing done\n");
return 1;
}
Sccinfo.init = 1;
Sccinfo.nchips = nchips;
Sccinfo.maxchan = (2 * nchips) - 1;
Sccinfo.iobase = iobase;
Sccinfo.space = space;
Sccinfo.off[0] = aoff;
Sccinfo.off[1] = boff;
Sccinfo.doff = doff;
Sccinfo.ivec = ivec;
Sccinfo.clk = clk;
Sccinfo.pclk = pclk;
Sccinfo.hwtype = hwtype;
Sccinfo.hwparam = hwparam;
/* reset and pre-init all chips in the system */
for(chip = 0; chip < nchips; chip++){
chipbase = iobase + chip * space;
ctrl = chipbase + Sccinfo.off[0];
i_state = dirps(); /* because of 2-step accesses */
VOID(RDREG(ctrl)); /* make sure pointer is written */
WRSCC(ctrl,R9,FHWRES); /* force hardware reset */
for (d = 0; d < 1000; d++) /* wait a while to be sure */
dum *= 10;
for(chan = 0; chan < 2; chan++){
ctrl = chipbase + Sccinfo.off[chan];
/* initialize a single channel to no-op */
VOID(RDREG(ctrl)); /* make sure pointer is written */
WRSCC(ctrl,R4,z); /* no mode selected yet */
WRSCC(ctrl,R1,z); /* no W/REQ operation */
WRSCC(ctrl,R2,16 * chip); /* chip# in upper 4 bits of vector */
WRSCC(ctrl,R3,z); /* disable rx */
WRSCC(ctrl,R5,z); /* disable tx */
WRSCC(ctrl,R9,VIS); /* vector includes status, MIE off */
Sccpolltab[chip][chan] = ctrl; /* store ctrl addr for polling */
}
if(hwtype & HWEAGLE) /* this is an EAGLE card */
WRREG(chipbase + 4,0x08); /* enable interrupt on the board */
if(hwtype & HWPC100) /* this is a PC100 card */
WRREG(chipbase,hwparam); /* set the MODEM mode (22H normally) */
if(hwtype & HWPRIMUS) /* this is a PRIMUS-PC */
WRREG(chipbase + 4,hwparam); /* set the MODEM mode (02H normally) */
if (hwtype & HWDRSI) { /* this is a DRSI PC*Packet card */
ioaddr z8536 = chipbase + 7; /* point to 8536 master ctrl reg */
/* Initialize 8536 to perform its divide-by-32 function */
/* This part copied from N6TTO DRSI-driver */
/* Start by forcing chip into known state */
VOID(RDREG(z8536)); /* make sure pointer is written */
WRSCC(z8536,CIO_MICR,0x01); /* force hardware reset */
for (d = 0; d < 1000; d++) /* wait a while to be sure */
dum *= 10;
WRSCC(z8536,CIO_MICR,0x00); /* Clear reset and start */
/* Wait for chip to come ready */
while (RDSCC(z8536,CIO_MICR) != 0x02)
dum *= 10;
WRSCC(z8536,CIO_MICR,0x26); /* NV|CT_VIS|RJA */
WRSCC(z8536,CIO_MCCR,0xf4); /* PBE|CT1E|CT2E|CT3E|PAE */
WRSCC(z8536,CIO_CTMS1,0xe2);/* Continuous, EOE, ECE, Pulse output */
WRSCC(z8536,CIO_CTMS2,0xe2);/* Continuous, EOE, ECE, Pulse output */
WRSCC(z8536,CIO_CT1MSB,0x00); /* Load time constant CTC #1 */
WRSCC(z8536,CIO_CT1LSB,0x10);
WRSCC(z8536,CIO_CT2MSB,0x00); /* Load time constant CTC #2 */
WRSCC(z8536,CIO_CT2LSB,0x10);
WRSCC(z8536,CIO_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 */
WRSCC(z8536,CIO_PDCA,0x22);
WRSCC(z8536,CIO_PDCB,0x22);
WRSCC(z8536,CIO_CSR1,CIO_GCB|CIO_TCB); /* Start CTC #1 running */
WRSCC(z8536,CIO_CSR2,CIO_GCB|CIO_TCB); /* Start CTC #2 running */
}
restore(i_state);
}
Sccpolltab[chip][0] = 0; /* terminate the polling table */
Sccvecloc = intack; /* location of INTACK/vector read */
Sccmaxvec = 16 * nchips; /* upper limit on valid vector */
/* save original interrupt vector */
Orgivec = getirq(ivec);
if(intack){ /* INTACK method selected? */
/* set interrupt vector to INTACK-generating routine */
setirq(ivec,sccvec);
} else {
/* set interrupt vector to polling routine */
setirq(ivec,sccnovec);
}
/* enable the interrupt */
maskon(ivec);
return 0;
}
/* initialize an SCC channel in asynchronous mode */
static int
scc_async(scc)
register struct sccchan *scc;
{
int i_state;
register struct fifo *fp = &(scc->fifo);
if((fp->buf = malloc(scc->bufsiz)) == NULLCHAR){
tprintf("scc%d: No space for rx buffer\n",scc->iface->dev);
return -1;
}
fp->bufsize = scc->bufsiz;
fp->wp = fp->rp = fp->buf;
fp->cnt = 0;
scc->int_transmit = scc_asytx; /* set interrupt handlers */
scc->int_extstat = scc_asyex;
scc->int_receive = scc_asyrx;
scc->int_special = scc_asysp;
i_state = dirps();
wr(scc,R4,X16CLK|SB1); /* *16 clock, 1 stopbit, no parity */
wr(scc,R1,z); /* no W/REQ operation */
wr(scc,R3,Rx8); /* RX 8 bits/char, disabled */
wr(scc,R5,Tx8|DTR|RTS); /* TX 8 bits/char, disabled, DTR RTS */
wr(scc,R9,VIS); /* vector includes status */
wr(scc,R10,NRZ|z); /* select NRZ */
wr(scc,R11,RCBR|TCBR); /* clocks are BR generator */
wr(scc,R14,Sccinfo.pclk? BRSRC:z); /* brg source = PCLK/RTxC */
wr(scc,R15,BRKIE); /* enable BREAK ext/status int */
or(scc,R3,RxENABLE); /* enable receiver */
or(scc,R5,TxENAB); /* enable transmitter */
WRREG(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
WRREG(scc->ctrl,RES_EXT_INT); /* must be done twice */
scc->status = RDREG(scc->ctrl); /* read initial status */
or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
or(scc,R9,MIE); /* master interrupt enable */
restore(i_state);
return 0;
}
/* initialize an SCC channel in SDLC mode */
static void
scc_sdlc(scc)
register struct sccchan *scc;
{
int i_state;
scc->int_transmit = scc_sdlctx; /* set interrupt handlers */
scc->int_extstat = scc_sdlcex;
scc->int_receive = scc_sdlcrx;
scc->int_special = scc_sdlcsp;
i_state = dirps();
wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */
wr(scc,R1,z); /* no W/REQ operation */
wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */
wr(scc,R5,Tx8|DTR|TxCRC_ENAB); /* TX 8 bits/char, disabled, DTR */
wr(scc,R6,z); /* SDLC address zero (not used) */
wr(scc,R7,FLAG); /* SDLC flag value */
wr(scc,R9,VIS); /* vector includes status */
wr(scc,R10,CRCPS|NRZI|ABUNDER); /* CRC preset 1, select NRZI, ABORT on underrun */
if (scc->extclock){ /* when using external clocks */
/* RXclk RTxC, TXclk TRxC. */
wr(scc,R11,RCRTxCP|TCTRxCP);
wr(scc,R14,z); /* No BRG options */
WRSCC(scc->ctrl,R14,DISDPLL|scc->wreg[R14]); /* No DPLL operation */
} else {
if(scc->fulldup){ /* when external clock divider */
if(Sccinfo.pclk){ /* when using PCLK as clock source */
/* RXclk DPLL, TXclk RTxC, out=BRG. external /32 TRxC->RTxC */
wr(scc,R11,RCDPLL|TCRTxCP|TRxCOI|TRxCBR);
} else {
/* RXclk DPLL, TXclk TRxC. external TX clock to TRxC */
wr(scc,R11,RCDPLL|TCTRxCP);
}
} else { /* only half-duplex operation */
/* RXclk DPLL, TXclk BRG. BRG reprogrammed at every TX/RX switch */
#ifdef notdef /* KA9Q - for PSK modem */
wr(scc,R11,RCDPLL|TCBR);
#else
/* DPLL -> Rx clk, DPLL -> Tx CLK, TxCLK -> TRxC pin */
wr(scc,R11,RCDPLL|TCDPLL|TRxCOI|TRxCDP);
#endif
}
wr(scc,R14,Sccinfo.pclk? BRSRC:z); /* BRG source = PCLK/RTxC */
WRSCC(scc->ctrl,R14,SSBR|scc->wreg[R14]); /* DPLL source = BRG */
WRSCC(scc->ctrl,R14,SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */
}
wr(scc,R15,BRKIE|CTSIE|DCDIE); /* enable ABORT, CTS & DCD interrupts */
if(RDREG(scc->ctrl) & DCD){ /* DCD is now ON */
if (!scc->extclock)
WRSCC(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
}
WRREG(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
WRREG(scc->ctrl,RES_EXT_INT); /* must be done twice */
scc->status = RDREG(scc->ctrl); /* read initial status */
or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
or(scc,R9,MIE); /* master interrupt enable */
restore(i_state);
}
/* set SCC channel speed
* clkmode specifies the division rate (1,16,32) inside the SCC
* returns the selected brgrate for "real speed" calculation
*/
static unsigned int
scc_speed(scc,clkmode,speed)
register struct sccchan *scc;
unsigned int clkmode;
long speed; /* the desired baudrate */
{
unsigned int brgrate;
long spdclkm;
int i_state;
/* calculate baudrate generator value */
if ((spdclkm = speed * clkmode) == 0)
return 65000U; /* avoid divide-by-zero */
brgrate = (unsigned) ((Sccinfo.clk + spdclkm) / (spdclkm * 2)) - 2;
i_state = dirps(); /* 2-step register accesses... */
cl(scc,R14,BRENABL); /* disable baudrate generator */
wr(scc,R12,brgrate); /* brg rate LOW */
wr(scc,R13,brgrate >> 8); /* brg rate HIGH */
or(scc,R14,BRENABL); /* enable baudrate generator */
restore(i_state);
return brgrate;
}
/* de-activate SCC channel */
static int
scc_stop(ifp)
struct iface *ifp;
{
struct sccchan *scc = Sccchan[ifp->dev];
int i_state;
i_state = dirps();
VOID(RDREG(scc->ctrl)); /* make sure pointer is written */
wr(scc,R9,(ifp->dev % 2)? CHRB : CHRA); /* reset the channel */
switch(ifp->type){
case CL_SERIAL_LINE:
case CL_KISS:
free(scc->fifo.buf);
default:
break;
}
free(scc);
Sccchan[ifp->dev] = NULLCHAN;
restore(i_state);
return 0;
}
/* de-activate SCC driver on program exit */
void
sccstop()
{
if(Sccinfo.init){ /* was it initialized? */
maskoff(Sccinfo.ivec); /* disable the interrupt */
setirq(Sccinfo.ivec,Orgivec); /* restore original interrupt vector */
}
}
/* perform ioctl on SCC (async) channel
* this is used for SLIP mode only, and will read/set the line speed
*/
static int
scc_aioctl(ifp,argc,argv)
struct iface *ifp;
int argc;
char *argv[];
{
struct sccchan *scc;
unsigned int brgrate;
scc = Sccchan[ifp->dev];
if(argc < 1){
printf("%ld\n",scc->speed);
return 0;
}
brgrate = scc_speed(scc,16,atol(argv[0]));
scc->speed = Sccinfo.clk / (32L * (brgrate + 2));
return 0;
}
/* perform ioctl on SCC (sdlc) channel
* this is used for AX.25 mode only, and will set the "kiss" parameters
*/
static int
scc_sioctl(ifp,argc,argv)
struct iface *ifp;
int argc;
char *argv[];
{
struct sccchan *scc;
int param,i_state;
unsigned int brgrate;
scc = Sccchan[ifp->dev];
if(argc == 0){
if (scc->extclock)
printf("ext.clk");
else
printf("speed=%s%ld",scc->fulldup? "d":"",scc->speed);
printf("group=%03x tx=%c",scc->group,scc->tx_inhibit? 'n':'y');
for(param = 1; param < sizeof(scc->a.params); param++)
printf(" %d=%d",param,scc->a.params[param]);
printf("\n");
return 0;
}
if(argc < 2){
printf("Data field missing\n");
return 1;
}
switch(argv[0][0]){ /* check special param setting */
case 'g': /* setting of group */
scc->group = htoi(argv[1]);
return 0;
case 's': /* setting of speed */
if (!scc->extclock) {
brgrate = scc_speed(scc,32,atol(argv[1]));/* init SCC speed */
scc->speed = Sccinfo.clk / (64L * (brgrate + 2));/* calc real speed */
}
return 0;
case 't': /* set tx inhibit */
if (tolower(argv[1][0]) == 'n')
scc->tx_inhibit = 1; /* no tx = inhibit it */
else
scc->tx_inhibit = 0;
return 0;
}
if((param = atoi(argv[0])) <= 0 || param >= sizeof(scc->a.params)){
printf("Param number out of range\n");
return 1;
}
scc->a.params[param] = atoi(argv[1]);
/* and now, a special hack to allow setting of DTR with param #6 */
if(param == TNCSPECIF){
i_state = dirps();
if(scc->a.params[TNCSPECIF]) /* set or clear DTR image bit */
scc->wreg[R5] |= DTR;
else
scc->wreg[R5] &= ~DTR;
if(scc->a.tstate == IDLE && scc->timercount == 0)
scc->timercount = 1; /* force an update */
restore(i_state);
}
return 0;
}
/* start SCC transmitter when it is idle (SLIP/KISS mode only) */
static void
scc_sstart(scc)
register struct sccchan *scc;
{
if(scc->tbp != NULLBUF || /* busy */
scc->sndq == NULLBUF) /* no work */
return;
scc->tbp = dequeue(&scc->sndq);
WRREG(scc->data,FR_END);
}
/* show SCC status */
int
dosccstat()
{
register struct sccchan *scc;
int i;
if(!Sccinfo.init){
printf("SCC driver not initialized\n");
return 0;
}
printf("Ch Iface Sent Rcvd Error Space Overr Rxints Txints Exints Spints\n");
for(i = 0; i <= Sccinfo.maxchan; i++){
if((scc = Sccchan[i]) == NULLCHAN)
continue;
if(scc->int_receive == scc_asyrx)
printf("%2d %-6s ** asynch ** %7lu %5u %5u %8lu %8lu %8lu %8lu\n",i,scc->iface->name,
scc->rxerrs,scc->nospace,scc->rovers,
scc->rxints,scc->txints,scc->exints,scc->spints);
else
printf("%2d %-6s %6lu %6lu %7lu %5u %5u %8lu %8lu %8lu %8lu\n",i,scc->iface->name,
scc->enqueued,scc->rxframes,scc->rxerrs,scc->nospace,scc->rovers,
scc->rxints,scc->txints,scc->exints,scc->spints);
}
return 0;
}
/* send raw frame to SCC. used for AX.25 */
static int
scc_raw(ifp,bp)
struct iface *ifp;
struct mbuf *bp;
{
struct sccchan *scc;
int i_state;
dump(ifp,IF_TRACE_OUT,CL_AX25,bp);
ifp->rawsndcnt++;
ifp->lastsent = secclock();
scc = Sccchan[ifp->dev];
if (scc->tx_inhibit){ /* transmitter inhibit */
free_p(bp);
return -1;
}
enqueue(&scc->sndq,bp); /* enqueue packet */
scc->enqueued++;
i_state = dirps();
if(scc->a.tstate == IDLE){ /* when transmitter is idle */
scc->a.tstate = DEFER; /* start the key-up sequence */
scc->a.maxdefer = TPS * scc->a.params[IDLETIME] /
scc->a.params[SLOTTIME];
scc->timercount = scc->a.params[WAITTIME];
}
restore(i_state);
return 0;
}
static int
scc_send(dev,bp)
int dev;
struct mbuf *bp;
{
struct sccchan *scc;
scc = Sccchan[dev];
enqueue(&scc->sndq,bp);
if(scc->tbp == NULLBUF)
scc_sstart(scc);
return(0);
}
/* initialize interface for AX.25 use */
static int
scc_call(ifp,call)
register struct iface *ifp;
char *call;
{
char out[AXALEN];
ifp->hwaddr = mallocw(AXALEN);
if(setcall(out,call) == 0)
memcpy(ifp->hwaddr,out,AXALEN);
else
memcpy(ifp->hwaddr,Mycall,AXALEN);
return 0;
}
/* Interrupt handlers for asynchronous modes (kiss, slip) */
/* Transmitter interrupt handler */
/* This routine sends data from mbufs in SLIP format */
static void
scc_asytx(scc)
register struct sccchan *scc;
{
register struct mbuf *bp;
scc->txints++;
if(scc->txchar != 0){ /* a character pending for transmit? */
WRREG(scc->data,scc->txchar); /* send it now */
scc->txchar = 0; /* next time, ignore it */
return;
}
if(scc->tbp == NULLBUF){ /* nothing to send? */
if((scc->tbp = scc->sndq) != NULLBUF){ /* dequeue next frame */
scc->sndq = scc->sndq->anext;
WRREG(scc->data,FR_END); /* send FR_END to flush line garbage */
} else {
WRREG(scc->ctrl,RES_Tx_P); /* else only reset pending int */
}
return;
}
while ((bp = scc->tbp)->cnt == 0){ /* nothing left in this mbuf? */
bp = bp->next; /* save link to next */
free_mbuf(scc->tbp);
if((scc->tbp = bp) == NULLBUF){ /* see if more mbufs follow */
WRREG(scc->data,FR_END); /* frame complete, send FR_END */
return;
}
}
/* now bp = scc->tbp (either from while or from if stmt above) */
WRREG(scc->data,*(bp->data)); /* just send the character */
bp->cnt--; /* decrease mbuf byte count */
bp->data++; /* and increment the data pointer */
}
/* External/Status interrupt handler */
static void
scc_asyex(scc)
register struct sccchan *scc;
{
register unsigned char status,changes;
scc->exints++;
status = RDREG(scc->ctrl);
changes = status ^ scc->status;
if(changes & BRK_ABRT){ /* BREAK? */
if((status & BRK_ABRT) == 0) /* BREAK now over? */
VOID(RDREG(scc->data)); /* read the NUL character */
}
scc->status = status;
WRREG(scc->ctrl,RES_EXT_INT);
}
/* Receiver interrupt handler under NOS.
* Since the higher serial protocol routines are all written to work
* well with the routines in 8250.c, it makes sense to handle
* asynch i/o with the 8530 in a similar manner. Therefore, these
* routines are as close to their counterparts in 8250.c as possible.
*/
static void
scc_asyrx(scc)
register struct sccchan *scc;
{
register struct fifo *fp;
char c;
scc->rxints++;
fp = &(scc->fifo);
do {
c = RDREG(scc->data);
if(fp->cnt != fp->bufsize){
*fp->wp++ = c;
if(fp->wp >= &fp->buf[fp->bufsize])
fp->wp = fp->buf;
fp->cnt++;
} else
scc->nospace++;
} while(RDREG(scc->ctrl) & Rx_CH_AV);
psignal(fp,1); /* eventually move this to timer routine */
}
/* Blocking read from asynch input.
* Essentially the same as get_asy() in 8250.c
* See comments in asy_rxint().
*/
static int
get_scc(dev)
int dev;
{
char i_state;
register struct fifo *fp;
char c;
fp = &(Sccchan[dev]->fifo);
i_state = dirps();
while(fp->cnt == 0)
pwait(fp);
fp->cnt--;
restore(i_state);
c = *fp->rp++;
if(fp->rp >= &fp->buf[fp->bufsize])
fp->rp = fp->buf;
return uchar(c);
}
int
scc_frameup(dev)
int dev;
{
Sccchan[dev]->rxframes++;
return 0;
}
/* Receive Special Condition interrupt handler */
static void
scc_asysp(scc)
register struct sccchan *scc;
{
register unsigned char status;
scc->spints++;
status = rd(scc,R1); /* read receiver status */
VOID(RDREG(scc->data)); /* flush offending character */
if(status & (CRC_ERR | Rx_OVR)) /* did a framing error or overrun occur ? */
scc->rovers++; /* report as overrun */
WRREG(scc->ctrl,ERR_RES);
}
/* Interrupt handlers for sdlc mode (AX.25) */
/* Transmitter interrupt handler */
static void
scc_sdlctx(scc)
register struct sccchan *scc;
{
register struct mbuf *bp;
scc->txints++;
switch(scc->a.tstate){ /* look at transmitter state */
case ACTIVE: /* busy sending data bytes */
while ((bp = scc->tbp)->cnt == 0){ /* nothing left in this mbuf? */
bp = bp->next; /* save link to next */
free_mbuf(scc->tbp); /*KM*/
if((scc->tbp = bp) == NULLBUF){/* see if more mbufs follow */
if(RDREG(scc->ctrl) & TxEOM){ /* check tx underrun status */
scc->rovers++; /* oops, an underrun! count them */
WRREG(scc->ctrl,SEND_ABORT);/* send an abort to be sure */
scc->a.tstate = TAIL; /* key down tx after TAILTIME */
scc->timercount = scc->a.params[TAILTIME];
return;
}
cl(scc,R10,ABUNDER); /* frame complete, allow CRC transmit */
scc->a.tstate = FLUSH;
WRREG(scc->ctrl,RES_Tx_P); /* reset pending int */
return;
}
}
/* now bp = scc->tbp (either from while or from if stmt above) */
WRREG(scc->data,*(bp->data++)); /* send the character */
bp->cnt--; /* decrease mbuf byte count */
return;
case FLUSH: /* CRC just went out, more to send? */
or(scc,R10,ABUNDER); /* re-install underrun protection */
/* verify that we are not exeeding max tx time (if defined) */
if((scc->timercount != 0 || scc->a.params[MAXKEYUP] == 0) &&
(scc->tbp = scc->sndq) != NULLBUF){ /* dequeue a frame */
scc->sndq = scc->sndq->anext;
WRREG(scc->ctrl,RES_Tx_CRC); /* reset the TX CRC generator */
scc->a.tstate = ACTIVE;
scc_sdlctx(scc); /* write 1st byte */
WRREG(scc->ctrl,RES_EOM_L); /* reset the EOM latch */
return;
}
scc->a.tstate = TAIL; /* no more, key down tx after TAILTIME */
scc->timercount = scc->a.params[TAILTIME];
WRREG(scc->ctrl,RES_Tx_P);
return;
default: /* another state */
WRREG(scc->ctrl,RES_Tx_P); /* then don't send anything */
return;
}
}
/* External/Status interrupt handler */
static void
scc_sdlcex(scc)
register struct sccchan *scc;
{
register unsigned char status,changes;
scc->exints++;
status = RDREG(scc->ctrl);
changes = status ^ scc->status;
if(changes & BRK_ABRT){ /* Received an ABORT */
if(status & BRK_ABRT){ /* is this the beginning? */
if(scc->rbp != NULLBUF){/* did we receive something? */
/* check if a significant amount of data came in */
/* this is because the drop of DCD tends to generate an ABORT */
if(scc->rbp->next != NULLBUF || scc->rbp->cnt > sizeof(struct phdr))
scc->rxerrs++; /* then count it as an error */
scc_tossb(scc); /* throw away buffer */
}
VOID(RDREG(scc->data)); /* flush the FIFO */
VOID(RDREG(scc->data));
VOID(RDREG(scc->data));
}
}
if(changes & CTS){ /* CTS input changed state */
if(status & CTS){ /* CTS is now ON */
if(scc->a.tstate == KEYWT &&
scc->a.params[TXDELAY] == 0) /* zero TXDELAY = wait for CTS */
scc->timercount = 1; /* it will start within 10 ms */
}
}
if(changes & DCD){ /* DCD input changed state */
if(status & DCD){ /* DCD is now ON */
if (!scc->extclock)
WRSCC(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
} else { /* DCD is now OFF */
cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
VOID(RDREG(scc->data)); /* flush the FIFO */
VOID(RDREG(scc->data));
VOID(RDREG(scc->data));
if(scc->rbp != NULLBUF){/* did we receive something? */
/* check if a significant amount of data came in */
/* this is because some characters precede the drop of DCD */
if(scc->rbp->next != NULLBUF || scc->rbp->cnt > sizeof(struct phdr))
scc->rxerrs++; /* then count it as an error */
scc_tossb(scc); /* throw away buffer */
}
}
}
scc->status = status;
WRREG(scc->ctrl,RES_EXT_INT);
}
/* Receiver interrupt handler */
static void
scc_sdlcrx(scc)
register struct sccchan *scc;
{
register struct mbuf *bp;
scc->rxints++;
if((bp = scc->rbp1) == NULLBUF){ /* no buffer available now */
if(scc->rbp == NULLBUF){
if((bp = alloc_mbuf(scc->bufsiz+sizeof(struct phdr))) != NULLBUF){
scc->rbp = scc->rbp1 = bp;
bp->cnt = sizeof(struct phdr); /* get past the header */
}
} else if((bp = alloc_mbuf(scc->bufsiz)) != NULLBUF){
scc->rbp1 = bp;
for(bp = scc->rbp; bp->next != NULLBUF; bp = bp->next);
bp->next = scc->rbp1;
bp = scc->rbp1;
}
if(bp == NULLBUF){
VOID(RDREG(scc->data)); /* so we have to discard the char */
or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
scc_tossb(scc); /* put buffers back on pool */
scc->nospace++; /* count these events */
return;
}
}
/* now, we have a buffer (at bp). read character and store it */
bp->data[bp->cnt++] = RDREG(scc->data);
if(bp->cnt == bp->size) /* buffer full? */
scc->rbp1 = NULLBUF; /* acquire a new one next time */
}
/* Receive Special Condition interrupt handler */
static void
scc_sdlcsp(scc)
register struct sccchan *scc;
{
register unsigned char status;
register struct mbuf *bp;
struct phdr phdr;
scc->spints++;
status = rd(scc,R1); /* read receiver status */
VOID(RDREG(scc->data)); /* flush offending character */
if(status & Rx_OVR){ /* receiver overrun */
scc->rovers++; /* count them */
or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
scc_tossb(scc); /* rewind the buffer and toss */
}
if(status & END_FR && /* end of frame */
scc->rbp != NULLBUF){ /* at least received something */
if((status & CRC_ERR) == 0 && /* no CRC error is indicated */
(status & 0xe) == RES8 && /* 8 bits in last byte */
scc->rbp->cnt > sizeof(phdr)){
/* we seem to have a good frame. but the last byte received */
/* from rx interrupt is in fact a CRC byte, so discard it */
if(scc->rbp1 != NULLBUF){
scc->rbp1->cnt--; /* current mbuf was not full */
} else {
for(bp = scc->rbp; bp->next != NULLBUF; bp = bp->next);
/* find last mbuf */
bp->cnt--; /* last byte is first CRC byte */
}
phdr.iface = scc->iface;
phdr.type = CL_AX25;
memcpy(&scc->rbp->data[0],(char *)&phdr,sizeof(phdr));
enqueue(&Hopper,scc->rbp);
scc->rbp = scc->rbp1 = NULLBUF;
scc->rxframes++;
} else { /* a bad frame */
scc_tossb(scc); /* throw away frame */
scc->rxerrs++;
}
}
WRREG(scc->ctrl,ERR_RES);
}
/* Throw away receive mbuf(s) when an error occurred */
static void
scc_tossb (scc)
register struct sccchan *scc;
{
register struct mbuf *bp;
if((bp = scc->rbp) != NULLBUF){
free_p(bp->next);
free_p(bp->dup); /* Should be NULLBUF */
bp->next = NULLBUF;
scc->rbp1 = bp; /* Don't throw this one away */
bp->cnt = sizeof(struct phdr); /* Simply rewind it */
}
}
/* Switch the SCC to "transmit" mode */
/* Only to be called from an interrupt handler, while in AX.25 mode */
static void
scc_txon(scc)
register struct sccchan *scc;
{
if (!scc->fulldup && !scc->extclock){ /* no fulldup divider? */
cl(scc,R3,RxENABLE); /* then switch off receiver */
cl(scc,R5,TxENAB); /* transmitter off during switch */
scc_speed(scc,1,scc->speed); /* reprogram baudrate generator */
}
or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */
if(Sccinfo.hwtype & HWPRIMUS) /* PRIMUS has another PTT bit... */
WRREG(scc->ctrl + 4,Sccinfo.hwparam | 0x80); /* set that bit! */
}
/* Switch the SCC to "receive" mode (or: switch off transmitter)
* Only to be called from an interrupt handler, while in AX.25 mode
*/
static void
scc_txoff(scc)
register struct sccchan *scc;
{
cl(scc,R5,RTS); /* turn off RTS line */
if(Sccinfo.hwtype & HWPRIMUS) /* PRIMUS has another PTT bit... */
WRREG(scc->ctrl + 4,Sccinfo.hwparam); /* clear that bit! */
if (!scc->fulldup && !scc->extclock){ /* no fulldup divider? */
cl(scc,R5,TxENAB); /* then disable the transmitter */
scc_speed(scc,32,scc->speed); /* back to receiver baudrate */
}
}
/* SCC timer interrupt handler. Will be called every 1/TPS s by the
* routine systick in pc.c
*/
void scctimer()
{
register struct sccchan *scc;
register struct sccchan **sccp;
char i_state;
i_state = dirps();
for(sccp = Sccchan + Sccinfo.maxchan; sccp >= Sccchan; sccp--){
if((scc = *sccp) != NULLCHAN &&
scc->timercount != 0 &&
--(scc->timercount) == 0){
/* handle an SCC timer event for this SCC channel
* this can only happen when the channel is AX.25 type
* (the SLIP/KISS driver does not use timers)
*/
switch(scc->a.tstate){
case IDLE: /* it was idle, this is FULLDUP2 timeout */
scc_txoff(scc); /* switch-off the transmitter */
break;
case DEFER: /* trying to get the channel */
/* operation is as follows:
* CSMA: when channel clear AND persistence randomgenerator
* wins, AND group restrictions allow it:
* keyup the transmitter
* if not, delay one SLOTTIME and try again
* FULL: always keyup the transmitter
*/
if(scc->a.params[FULLDUP] == 0){
Random = 21 * Random + 53;
if(scc->status & DCD || scc->a.params[PERSIST] < Random){
/* defer transmission again. check for limit */
defer_it: if(--(scc->a.maxdefer) == 0){
/* deferred too long. choice is to:
* - throw away pending frames, or
* - smash-on the transmitter and send them.
* the first would be the choice in a clean
* environment, but in the amateur radio world
* a distant faulty station could tie us up
* forever, so the second may be better...
*/
#ifdef THROW_AWAY_AFTER_DEFER_TIMEOUT
struct mbuf *bp,*bp1;
while ((bp = scc->sndq) != NULLBUF){
scc->sndq = scc->sndq->anext;
free_p(bp);
}
#else
goto keyup; /* just keyup the transmitter... */
#endif
}
scc->timercount = scc->a.params[SLOTTIME];
break;
}
if(uchar(scc->group) != NOGROUP){
int i;
struct sccchan *scc2;
for(i = 0; i <= Sccinfo.maxchan; i++)
if((scc2 = Sccchan[i]) != NULLCHAN &&
scc2 != scc &&
uchar(scc2->group) & uchar(scc->group) &&
((scc->group & TXGROUP && scc2->wreg[R5] & RTS) ||
(scc->group & RXGROUP && scc2->status & DCD))){
goto defer_it;
}
}
}
case KEYUP: /* keyup transmitter (note fallthrough) */
keyup: if((scc->wreg[R5] & RTS) == 0){ /* when not yet keyed */
scc->a.tstate = KEYWT;
scc->timercount = scc->a.params[TXDELAY]; /* 0 if CTSwait */
scc_txon(scc);
break;
}
/* when already keyed, directly fall through */
case KEYWT: /* waited for CTS or TXDELAY */
/* when a frame is available (it should be...):
* - dequeue it from the send queue
* - reset the transmitter CRC generator
* - set a timeout on transmission length, if defined
* - send the first byte of the frame
* - reset the EOM latch
* when no frame available, proceed to TAIL handling
*/
if((scc->tbp = scc->sndq) != NULLBUF){
scc->sndq = scc->sndq->anext;
WRREG(scc->ctrl,RES_Tx_CRC);
scc->a.tstate = ACTIVE;
scc->timercount = TPS * scc->a.params[MAXKEYUP];
scc_sdlctx(scc);
WRREG(scc->ctrl,RES_EOM_L);
break;
}
/* when no frame queued, fall through to TAIL case */
case TAIL: /* at end of frame */
/* when fulldup is 0 or 1, switch off the transmitter.
* when frames are still queued (because of transmit time limit),
* restart the procedure to get the channel after MINTIME.
* when fulldup is 2, the transmitter remains keyed and we
* continue sending. IDLETIME is an idle timeout in this case.
*/
if(scc->a.params[FULLDUP] < 2){
scc->a.tstate = IDLE;
scc_txoff(scc);
if(scc->sndq != NULLBUF){
scc->a.tstate = DEFER;
scc->a.maxdefer = TPS * scc->a.params[IDLETIME] /
scc->a.params[SLOTTIME];
scc->timercount = TPS * scc->a.params[MINTIME];
}
break;
}
if(scc->sndq != NULLBUF){ /* still frames on the queue? */
scc->a.tstate = KEYWT; /* continue sending */
scc->timercount = TPS * scc->a.params[MINTIME]; /* after mintime */
} else {
scc->a.tstate = IDLE;
scc->timercount = TPS * scc->a.params[IDLETIME];
}
break;
case ACTIVE: /* max keyup time expired */
case FLUSH: /* same while in flush mode */
break; /* no action required yet */
default: /* unexpected state */
scc->a.tstate = IDLE; /* that should not happen, but... */
scc_txoff(scc); /* at least stop the transmitter */
break;
}
}
}
restore(i_state);
}