📄 drsi.c
字号:
/*
* 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 <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 */
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -