📄 drsi.c
字号:
#include <stdio.h>
#include <dos.h>
#include <time.h>
#include "global.h"
#include "mbuf.h"
#include "iface.h"
#include "pktdrvr.h"
#include "netuser.h"
#include "drsi.h"
#include "ax25.h"
#include "trace.h"
#include "nospc.h"
#include "z8530.h"
#include "devparam.h"
static int32 dr_ctl(struct iface *iface,int cmd,int set,int32 val);
static int dr_raw(struct iface *iface,struct mbuf **bpp);
static int dr_stop(struct iface *iface);
static void dr_wake(struct drchan *hp,int rx_or_tx,
void (*routine)(struct drchan *),int ticks);
static int drchanparam(struct drchan *hp);
static void drexint(struct drchan *hp);
static void drinitctc(unsigned port);
static void drrx_active(struct drchan *hp);
static void drrx_enable(struct drchan *hp);
static void drtx_active(struct drchan *hp);
static void drtx_defer(struct drchan *hp);
static void drtx_downtx(struct drchan *hp);
static void drtx_flagout(struct drchan *hp);
static void drtx_idle(struct drchan *hp);
static void drtx_rrts(struct drchan *hp);
static void drtx_tfirst(struct drchan *hp);
static char read_ctc(unsigned port,unsigned reg);
static void rx_fsm(struct drchan *hp);
static void tx_fsm(struct drchan *hp);
static void write_ctc(uint16 port,uint8 reg,uint8 val);
struct DRTAB Drsi[DRMAX]; /* Device table - one entry per card */
INTERRUPT (*Drhandle[])(void) = { dr0vec }; /* handler interrupt vector table */
struct drchan Drchan[2*DRMAX]; /* channel table - 2 entries per card */
uint16 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)(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.
*/
INTERRUPT (far *(drint)(dev))()
int dev;
{
register char st;
register uint16 pcbase, i;
struct drchan *hpa,*hpb;
struct DRTAB *dp;
dp = &Drsi[dev];
dp->ints++;
pcbase = dp->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;
return dp->chain ? dp->oldvec : NULL;
}
/* 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;
int i_state;
i_state = dirps();
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 != NULL){
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;
{
int i_state;
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 uint16 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 != NULL){
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 uint16 base = hp->base;
unsigned char rse,st;
struct mbuf *bp;
/* Allocate a receive buffer if not already present */
if(hp->rcvbuf == NULL){
bp = hp->rcvbuf = alloc_mbuf(hp->bufsiz);
if(bp == NULL){
/* 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 */
hp->rcvbuf->cnt -= 2; /* chuck FCS bytes */
/* queue it in */
net_route(hp->iface,&hp->rcvbuf);
hp->enqueued++;
/* packet queued - reset buffer pointer */
hp->rcvbuf = NULL;
} /* end good frame queued */
} /* end check for active receive upon EOF */
}
}
/*
* TX finite state machine - dispatcher
*/
static void
tx_fsm(hp)
struct drchan *hp;
{
int i_state;
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 uint16 base;
/* Tx idle - is there a frame to transmit ? */
if((hp->sndbuf = dequeue(&hp->sndq)) == NULL){
/* 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 uint16 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 uint16 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));
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 uint16 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -