/* * Interface driver for the VE3IFB 8530 card (PI card) * Copyright 1990 by Dave Perry, VE3IFB * Minor delinting - KA9Q 2/2/91 * * Portions of this driver were derived from the Eagle card * driver by Art Goldman, WA3CVG. It has been very extensively * modified from his work, due to the addition of DMA support * and due to differences in the hardware. The PI card is NOT * an Eagle card clone. It is an original design by Dave Perry, * VE3IFB. Art's copyright notice follows: * * 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. * */ #include #include #include #include #include "global.h" #include "mbuf.h" #include "iface.h" #include "pktdrvr.h" #include "netuser.h" #include "pi.h" #include "8530.h" #include "ax25.h" #include "trace.h" #include "pc.h" #include "session.h" #include "lapb.h" #include "proc.h" #include "ip.h" #include "devparam.h" #ifndef FP_OFF #define FP_OFF(fp) ((unsigned)(fp)) #endif #ifndef FP_SEG #define FP_SEG(fp) ((unsigned)((unsigned long)(fp) >> 16)) #endif static void xwrite_scc __ARGS((struct pichan *hp,int16 ctl,int16 reg, int16 val )); static char xread_scc __ARGS((struct pichan *hp, int16 ctl, char reg)); static int32 pi_ctl __ARGS((struct iface *iface,int cmd,int set,int32 val)); static int pi_raw __ARGS((struct iface *iface,struct mbuf *bp)); static int pi_stop __ARGS((struct iface *iface)); static void rts __ARGS((struct pichan *hp, int16 x)); void setup_rx_dma __ARGS((struct pichan *hp)); void setup_tx_dma __ARGS((struct pichan *hp, char *buffer, int length)); static void set_acc_delay __ARGS((void)); static void tdelay __ARGS((register struct pichan *hp,unsigned int time)); static struct PITAB Pi[PIMAX]; /* Device table - one entry per card */ static INTERRUPT (*pihandle[])() = { /* handler interrupt vector table */ pi0vec, pi1vec, pi2vec }; static int16 Page_regs[] = { 0x87,0x83,0x81,0x82,0,0x8b,0x89,0x8a }; static struct pichan Pichan[2*PIMAX]; /* channel table - 2 entries per card */ static int16 pinbr; extern int16 acc_delay; /* Delay for the 8530 chip access recovery time */ /* Allocate a buffer which does not cross a dma page boundary */ /* This really belongs in mbuf.c */ struct mbuf * alloc_dmabuf(size) register int16 size; { struct mbuf *bp[20],*retbuf; unsigned buf_offset, buf_segment; long longseg, dma_abs, dma_page; int n; for(n = 0; n < 20 ;n++){ if((bp[n] = alloc_mbuf(size)) == NULLBUF){ /* Free the buffers which failed the test */ while(--n >= 0) free_p(bp[n]); return(NULLBUF); } /* Calculate the DMA page */ buf_offset = FP_OFF(bp[n]); buf_segment= FP_SEG(bp[n]); longseg = (long) buf_segment; dma_abs = (longseg << 4) + (long) buf_offset; dma_page = dma_abs >> 16; if(((dma_abs+size) >> 16) == dma_page){ /* Save the one that passed */ retbuf = bp[n]; /* Free the buffers which failed the test */ while(--n >= 0) free_p(bp[n]); return(retbuf); } } /* Free the buffers which failed the test */ while(--n >= 0) free_p(bp[n]); return(NULLBUF); } /* This calculates the constant to be used in the delay loops * which satify the SCC's access recovery time. It needs to be timed and * calculated because a fixed value would not work in a 4.77mhz XT * to a 40mhz 486 (and beyond). */ static void set_acc_delay() { long starttime, endtime; int n; int ticks; starttime = bioscnt(); for(n = 0; n < 10; n++) mloop(); endtime = bioscnt(); ticks = (int) (endtime - starttime); if(ticks == 0) ticks = 1; acc_delay = 61/ticks; if(acc_delay == 0) acc_delay = 1; fflush(stdout); } /* Write 8530 register */ static void xwrite_scc(hp,ctl,reg,val) register struct pichan *hp; register int16 ctl; int16 reg,val; { wrtscc(hp->cardbase,ctl,reg,val); } /* Read 8530 register */ static char xread_scc(hp,ctl,reg) register struct pichan *hp; register int16 ctl; char reg; { return(rdscc(hp->cardbase,ctl,reg)); } /* Setup 8253 chip for time delay */ static void tdelay(hp,time) register struct pichan *hp; unsigned int time; /* Time to delay in milliseconds */ { int n,port; unsigned int t1; unsigned char sc; if(hp->base & 2){ /* If A channel */ sc = SC1; t1 = time; port = hp->cardbase+TMR1; } else { sc = SC2; t1 = 10 * time; /* 10s of milliseconds for the B channel */ port = hp->cardbase+TMR2; } /* Setup timer sc */ outportb(hp->cardbase+TMRCMD, sc|LSB_MSB|MODE0); /* satisfy access time restriction */ for(n=0; n<5;n++) ; /* times 2 to make millisecs */ outportb(port, (t1 << 1) & 0xFF); /* satisfy access time restriction */ for(n=0; n<5;n++) ; outportb(port, (t1 >> 7) & 0xFF); /* Enable correct int for timeout */ xwrite_scc(hp,hp->base+CTL,R15,CTSIE); xwrite_scc(hp,hp->base+CTL,R1,EXT_INT_ENAB); xwrite_scc(hp,hp->base+CTL,R0,RES_EXT_INT); } /* Master interrupt handler. One interrupt at a time is handled. * here. Service routines are called from here. */ INTERRUPT (far *(piint)(dev))() int dev; { register char st; register int16 pcbase; struct pichan *hp; struct PITAB *pip; void b_rxint(),b_txint(),b_exint(); void a_rxint(),a_txint(),a_exint(); pip = &Pi[dev]; pip->ints++; pcbase = pip->addr; /* Read interrupt status register (only valid from channel A) * Process all pending interrupts in while loop */ hp = &Pichan[2 * dev]; /* Assume channel A */ while((st = xread_scc(hp,pcbase+CHANA+CTL,R3)) != 0){ if(st & CHARxIP){ /* Channel A Rcv Interrupt Pending */ hp = &Pichan[2 * dev]; a_rxint(hp); } else if(st & CHATxIP){ /* Channel A Transmit Int Pending */ hp = &Pichan[2 * dev]; a_txint(hp); } else if(st & CHAEXT){ /* Channel A External Status Int */ hp = &Pichan[2 * dev]; a_exint(hp); } else if(st & CHBRxIP){ /* Channel B Rcv Interrupt Pending */ hp = &Pichan[(2 * dev)+1]; b_rxint(hp); } else if(st & CHBTxIP){ /* Channel B Transmit Int Pending */ hp = &Pichan[(2 * dev)+1]; b_txint(hp); } else if(st & CHBEXT){ /* Channel B External Status Int */ hp = &Pichan[(2 * dev)+1]; b_exint(hp); } /* Reset highest interrupt under service */ xwrite_scc(hp,hp->base+CTL,R0,RES_H_IUS); } /* End of while loop on int processing */ return pip->chain ? pip->oldvec : NULL; } static void a_exint(hp) register struct pichan *hp; { register int16 cmd; char st, i_state; int length; int32 t,ca; i_state = dirps(); /* disable interrupts */ st = xread_scc(hp,hp->base+CTL,R0); /* Fetch status */ /* reset external status latch */ xwrite_scc(hp,CTL+hp->base,R0,RES_EXT_INT); cmd = hp->base+CTL; hp->exints++; if((hp->rstate >= ACTIVE) && (st & BRK_ABRT)){ setup_rx_dma(hp); hp->rstate = ACTIVE; } switch(hp->tstate){ case ACTIVE: free_p(hp->sndbuf); hp->sndbuf = NULLBUF; hp->tstate = FLAGOUT; tdelay(hp,hp->squeldelay); break; case FLAGOUT: if((hp->sndbuf = dequeue(&hp->sndq)) == NULLBUF){ /* Nothing to send - return to receive mode */ hp->tstate = IDLE; rts(hp,OFF); restore(i_state); return; } /* NOTE - fall through if more to send */ case ST_TXDELAY: /* Disable DMA chan */ outportb(DMA_MASK, DMA_DISABLE|hp->dmachan); /* Set up for TX dma */ xwrite_scc(hp,cmd,R1,WT_FN_RDYFN|EXT_INT_ENAB); /* Get all chars */ length = pullup(&hp->sndbuf,hp->txdmabuf,hp->bufsiz); /* Setup DMA controller for tx */ setup_tx_dma(hp,hp->txdmabuf,length); /* select transmit interrupts to enable */ /* Allow DMA on chan */ outportb(DMA_MASK,DMA_ENABLE|hp->dmachan); /* reset CRC, Txint pend*/ xwrite_scc(hp,cmd,R0,RES_Tx_CRC|RES_Tx_P); /* allow Underrun int only */ xwrite_scc(hp,cmd,R15,TxUIE); /* Enable TX DMA */ xwrite_scc(hp,cmd,R1,WT_RDY_ENAB|WT_FN_RDYFN|EXT_INT_ENAB); /* Send CRC on underrun */ xwrite_scc(hp,cmd,R0,RES_EOM_L); /* packet going out now */ hp->tstate = ACTIVE; break; case DEFER: /* we have deferred prev xmit attempt * See Intel Microcommunications Handbook, p2-308 */ xwrite_scc(hp,cmd,R0,RES_EXT_INT); xwrite_scc(hp,cmd,R0,RES_EXT_INT); if((xread_scc(hp,cmd,R0) & DCD) != 0){ hp->tstate = DEFER; tdelay(hp,100); /* Defer until dcd transition or 100mS timeout */ xwrite_scc(hp,CTL+hp->base,R15,CTSIE|DCDIE); restore(i_state); return; } /* Defer logic. Wait until deftime is in the past (so we * defer to any overheard CTS messages) AND the p-persistence * dice roll succeeds. The computation of ca allows for clock * rollover (which happens every 49+ days). */ t = msclock(); ca = hp->deftime - t; if(ca > 0){ hp->tstate = DEFER; tdelay(hp,ca); restore(i_state); return; } hp->deftime = t; /* Keep from getting too old */ if((rand() & 0xff) > uchar(hp->persist)){ hp->tstate = DEFER; tdelay(hp,hp->slotime); restore(i_state); return; } /* Assert RTS early minimize collision window */ xwrite_scc(hp,cmd,R5,TxCRC_ENAB|RTS|Tx8); rts(hp,ON); /* Transmitter on */ hp->tstate = ST_TXDELAY; tdelay(hp,hp->txdelay); restore(i_state); return; } /* switch(hp->tstate) */ restore(i_state); } /* a_exint() */ /* Receive interrupt handler for the A channel */ static void a_rxint(hp) register struct pichan *hp; { register int16 cmd; register int16 bytecount; char rse, i_state; i_state = dirps(); /* disable interrupts */ hp->rxints++; cmd = hp->base+CTL; rse = xread_scc(hp,cmd,R1); /* Get special condition bits from R1 */ if(rse & Rx_OVR){ /* If receiver overrun */ hp->rovers++; hp->rstate = RXERROR; } if(rse & END_FR){ /* If end of frame */ /* figure length of frame from 8237 */ outportb(DMA_RESETFF,0); /* reset firstlast ff */ bytecount = inportb(hp->dma_wcr); bytecount += inportb(hp->dma_wcr) << 8; /* Allow for the extra space for phdr */ bytecount = (hp->bufsiz - 1 - sizeof(struct phdr)) - bytecount; if((rse & CRC_ERR)||(hp->rstate > ACTIVE)||(bytecount < 10)){ if((bytecount >= 10) && (rse & CRC_ERR)) hp->crcerr++; /* Ignore noise */ /* Reset buffer pointers */ hp->rstate = ACTIVE; setup_rx_dma(hp); } else { /* Here we have a valid frame */ /* Toss 2 crc bytes */ hp->rcvbuf->cnt = bytecount - 2; /* "Can't fail" */ net_route(hp->iface,CL_AX25,hp->rcvbuf); hp->rcvbuf = NULLBUF; hp->rxframes++; /* packet queued - get buffer for next frame */ hp->rcvbuf = alloc_mbuf(hp->bufsiz + sizeof(struct phdr)); if(hp->rcvbuf != NULLBUF) /* Allow room for phdr */ hp->rcvbuf->data += sizeof(struct phdr); setup_rx_dma(hp); } /* end good frame queued */ } /* end EOF check */ xwrite_scc(hp,hp->base+CTL,R0,ERR_RES); /* error reset */ restore(i_state); } void a_txint(hp) register struct pichan *hp; { register int16 cmd; char i_state; int32 t,ca; i_state = dirps(); cmd = CTL+hp->base; switch(hp->tstate){ case IDLE: /* Transmitter idle. Find a frame for transmission */ if((hp->sndbuf = dequeue(&hp->sndq)) == NULLBUF){ rts(hp,OFF); restore(i_state); return; } /* If a buffer to send, we drop thru here */ case DEFER: /* we may have deferred prev xmit attempt */ /* Check DCD - debounce it * See Intel Microcommunications Handbook, p2-308 */ xwrite_scc(hp,cmd,R0,RES_EXT_INT); xwrite_scc(hp,cmd,R0,RES_EXT_INT); if((xread_scc(hp,cmd,R0) & DCD) != 0){ hp->tstate = DEFER; tdelay(hp,100); /* defer until DCD transition or timeout */ xwrite_scc(hp,cmd,R15,CTSIE|DCDIE); restore(i_state); return; } /* Defer logic. Wait until deftime is in the past (so we * defer to any overheard CTS messages) AND the p-persistence * dice roll succeeds. The computation of ca allows for clock * rollover (which happens every 49+ days). */ t = msclock(); ca = hp->deftime - t; if(ca > 0){ hp->tstate = DEFER; tdelay(hp,ca); restore(i_state); return; } hp->deftime = t; /* Keep from getting too old */ if((rand() & 0xff) > uchar(hp->persist)){ hp->tstate = DEFER; tdelay(hp,hp->slotime); restore(i_state); return; } /* Assert RTS early minimize collision window */ xwrite_scc(hp,cmd,R5,TxCRC_ENAB|RTS|Tx8); rts(hp,ON); /* Transmitter on */ hp->tstate = ST_TXDELAY; tdelay(hp,hp->txdelay); restore(i_state); return; default: break; } /* end switch(hp->state) */ restore(i_state); } /*a_txint */ static void b_rxint(hp) register struct pichan *hp; { register int16 cmd; char rse, i_state; i_state = dirps(); /* disable interrupts */ hp->rxints++; cmd = CTL+hp->base; if((xread_scc(hp,cmd,R0)) & Rx_CH_AV){ /* there is a char to be stored * read special condition bits before reading the data char */ rse = xread_scc(hp,cmd,R1); /* get status byte from R1 */ if(rse & Rx_OVR){ /* Rx overrun - toss buffer */ /* reset buffer pointers */ hp->rcp = hp->rcvbuf->data; hp->rcvbuf->cnt = 0; hp->rstate = RXERROR; /* set error flag */ hp->rovers++; } else if(hp->rcvbuf->cnt >= hp->bufsiz - sizeof(struct phdr)){ /* Too large -- toss buffer */ /* reset buffer pointers */ hp->rcp = hp->rcvbuf->data; 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){ /* If no errors... */ *hp->rcp++ = xread_scc(hp,cmd,R8); /* char to rcv buff */ hp->rcvbuf->cnt++; /* bump count */ } else { /* got to empty FIFO */ (void) xread_scc(hp,cmd,R8); xwrite_scc(hp,cmd,R0,ERR_RES); /* reset err latch */ hp->rstate = ACTIVE; } } if(rse & END_FR){ /* END OF FRAME -- Make sure Rx was active */ if(hp->rcvbuf->cnt > 0){ if((rse & CRC_ERR)||(hp->rstate > ACTIVE)||(hp->rcvbuf->cnt < 10)){ if((hp->rcvbuf->cnt >= 10) && (rse & CRC_ERR)) hp->crcerr++; /* Ignore noise */ hp->rcp = hp->rcvbuf->data; hp->rcvbuf->cnt = 0; } else { /* Here we have a valid frame */ hp->rcvbuf->cnt -= 2; /* Toss 2 crc bytes */ /* "Can't fail" */ net_route(hp->iface,CL_AX25,hp->rcvbuf); hp->rxframes++; /* packet queued - get buffer for next frame */ hp->rcvbuf = alloc_mbuf(hp->bufsiz+sizeof(struct phdr)); if(hp->rcvbuf == NULLBUF){ /* No memory - abort rx */ xwrite_scc(hp,cmd,R3,Rx8); restore(i_state); return; } hp->rcvbuf->data += sizeof(struct phdr); hp->rcp = hp->rcvbuf->data; hp->rcvbuf->cnt = 0; } /* end good frame queued */ } /* end check for active receive upon EOF */ hp->rstate = ACTIVE; /* and clear error status */ } /* end EOF check */ restore(i_state); } static void b_txint(hp) register struct pichan *hp; { register int16 cmd; char i_state,c; i_state = dirps(); cmd = CTL+hp->base; if(hp->tstate != DEFER && hp->tstate) hp->txints++; switch(hp->tstate){ case CRCOUT: hp->tstate = FLAGOUT; tdelay(hp,hp->squeldelay); restore(i_state); return; case IDLE: /* Transmitter idle. Find a frame for transmission */ if((hp->sndbuf = dequeue(&hp->sndq)) == NULLBUF){ /* Nothing to send - return to receive mode * Tx OFF now - flag should have gone */ rts(hp,OFF); restore(i_state); return; } /* If a buffer to send, we drop thru here */ case DEFER: /* we may have deferred prev xmit attempt */ /* Check DCD - debounce it */ /* See Intel Microcommunications Handbook, p2-308 */ xwrite_scc(hp,cmd,R0,RES_EXT_INT); xwrite_scc(hp,cmd,R0,RES_EXT_INT); if((xread_scc(hp,cmd,R0) & DCD) != 0){ hp->tstate = DEFER; tdelay(hp,100); /* defer until DCD transition or timeout */ xwrite_scc(hp,cmd,R15,CTSIE|DCDIE); restore(i_state); return; } /* p - persist calculation */ if(inportb(hp->cardbase+TMR0) > hp->persist){ inportb(hp->cardbase+TMR0); /* Discard MSB */ hp->tstate = DEFER; tdelay(hp,hp->slotime); restore(i_state); return; } inportb(hp->cardbase+TMR0); /* Discard MSB */ rts(hp,ON); /* Transmitter on */ hp->tstate = ST_TXDELAY; tdelay(hp,hp->txdelay); restore(i_state); return; case ACTIVE: /* Here we are actively sending a frame */ if((c = PULLCHAR(&hp->sndbuf)) != -1){ /* next char is gone */ xwrite_scc(hp,cmd,R8,c); /* stuffing a char satisfies Interrupt condition */ } else { /* No more to send */ free_p(hp->sndbuf); if((xread_scc(hp,cmd,R0) & 0x40)){ /* Did we underrun? */ /* unexpected underrun */ hp->tunders++; xwrite_scc(hp,cmd,R0,SEND_ABORT); hp->tstate = FLAGOUT; tdelay(hp,hp->squeldelay); restore(i_state); return; } hp->tstate = UNDERRUN; /* Now we expect to underrun */ /* Send flags on underrun */ if(hp->speed){ /* If externally clocked */ xwrite_scc(hp,cmd,R10,CRCPS|NRZI); } else { xwrite_scc(hp,cmd,R10,CRCPS); } xwrite_scc(hp,cmd,R0,RES_Tx_P); /* reset Tx Int Pend */ } restore(i_state); return; /* back to wait for interrupt */ } /* end switch */ restore(i_state); } /* Pi SIO External/Status interrupts (for the B channel) * 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 b_exint(hp) register struct pichan *hp; { char st, i_state; register int16 cmd; char c; cmd = CTL+hp->base; i_state = dirps(); /* disable interrupts */ hp->exints++; st = xread_scc(hp,cmd,R0); /* Fetch status */ /* reset external status latch */ xwrite_scc(hp,cmd,R0,RES_EXT_INT); switch(hp->tstate){ case ACTIVE: /* Unexpected underrun */ free_p(hp->sndbuf); xwrite_scc(hp,cmd,R0,SEND_ABORT); hp->tstate = FLAGOUT; hp->tunders++; tdelay(hp,hp->squeldelay); restore(i_state); return; case UNDERRUN: hp->tstate = CRCOUT; restore(i_state); return; case FLAGOUT: /* Find a frame for transmission */ if((hp->sndbuf = dequeue(&hp->sndq)) == NULLBUF){ /* Nothing to send - return to receive mode * Tx OFF now - flag should have gone */ rts(hp,OFF); hp->tstate = IDLE; restore(i_state); return; } /* Get next char to send */ pullup(&hp->sndbuf,&c,1); /* one char at a time */ xwrite_scc(hp,cmd,R0,RES_Tx_CRC);/* reset for next frame */ /* Send abort on underrun */ if(hp->speed){ /* If externally clocked */ xwrite_scc(hp,cmd,R10,CRCPS|NRZI|ABUNDER); } else { xwrite_scc(hp,cmd,R10,CRCPS|ABUNDER); } xwrite_scc(hp,cmd,R8,c); /* First char out now */ xwrite_scc(hp,cmd,R0,RES_EOM_L);/* Reset end of message latch */ /* select transmit interrupts to enable */ xwrite_scc(hp,cmd,R15,TxUIE); /* allow Underrun int only */ xwrite_scc(hp,cmd,R0,RES_EXT_INT); xwrite_scc(hp,cmd,R1,TxINT_ENAB|EXT_INT_ENAB); /* Tx/Extern ints on */ hp->tstate = ACTIVE; /* char going out now */ restore(i_state); return; case DEFER: /* Check DCD - debounce it * See Intel Microcommunications Handbook, p2-308 */ xwrite_scc(hp,cmd,R0,RES_EXT_INT); xwrite_scc(hp,cmd,R0,RES_EXT_INT); if((xread_scc(hp,cmd,R0) & DCD) != 0){ hp->tstate = DEFER; tdelay(hp,100); /* defer until DCD transition or timeout */ xwrite_scc(hp,cmd,R15,CTSIE|DCDIE); restore(i_state); return; } /* p - persist calculation */ if(inportb(hp->cardbase+TMR0) > hp->persist){ inportb(hp->cardbase+TMR0); /* Discard MSB */ hp->tstate = DEFER; tdelay(hp,hp->slotime); restore(i_state); return; } inportb(hp->cardbase+TMR0); /* Discard MSB */ rts(hp,ON); /* Transmitter on */ hp->tstate = ST_TXDELAY; tdelay(hp,hp->txdelay); restore(i_state); return; case ST_TXDELAY: /* Get next char to send */ pullup(&hp->sndbuf,&c,1); /* one char at a time */ xwrite_scc(hp,cmd,R0,RES_Tx_CRC);/* reset for next frame */ /* Send abort on underrun */ if(hp->speed){ /* If externally clocked */ xwrite_scc(hp,cmd,R10,CRCPS|NRZI|ABUNDER); } else { xwrite_scc(hp,cmd,R10,CRCPS|ABUNDER); } xwrite_scc(hp,cmd,R8,c); /* First char out now */ xwrite_scc(hp,cmd,R0,RES_EOM_L);/* Reset end of message latch */ /* select transmit interrupts to enable */ xwrite_scc(hp,cmd,R15,TxUIE); /* allow Underrun int only */ xwrite_scc(hp,cmd,R0,RES_EXT_INT); /* Tx/Extern ints on */ xwrite_scc(hp,cmd,R1,TxINT_ENAB|EXT_INT_ENAB); hp->tstate = ACTIVE; /* char going out now */ restore(i_state); return; } /* 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 == ACTIVE) && (st & BRK_ABRT)){ (void) xread_scc(hp,cmd,R8); (void) xread_scc(hp,cmd,R8); (void) xread_scc(hp,cmd,R8); hp->rcp = hp->rcvbuf->data; hp->rcvbuf->cnt = 0; /* rewind on DCD transition */ } restore(i_state); } /* SET Transmit or Receive Mode * Set RTS (request-to-send) to modem on Transmit */ static void rts(hp,x) register struct pichan *hp; int16 x; { int16 tc; long br; int16 cmd; cmd = CTL+hp->base; /* Reprogram BRG and turn on transmitter to send flags */ if(x == ON){ /* Turn Tx ON and Receive OFF */ /* Exints off first to avoid abort int */ xwrite_scc(hp,cmd,R15,0); xwrite_scc(hp,cmd,R3,Rx8); /* Rx off */ hp->rstate = IDLE; if(cmd & 2){ /* if channel a */ /* Set up for TX dma */ xwrite_scc(hp,cmd,R1,WT_FN_RDYFN|EXT_INT_ENAB); } else { xwrite_scc(hp,cmd,R1,0); /* No interrupts */ } if(hp->speed){ /* if internally clocked */ br = hp->speed; /* get desired speed */ tc = (XTAL/br)-2; /* calc 1X BRG divisor */ xwrite_scc(hp,cmd,R12,tc&0xFF); /* lower byte */ xwrite_scc(hp,cmd,R13,(tc>>8)&0xFF);/* upper byte */ } xwrite_scc(hp,cmd,R5,TxCRC_ENAB|RTS|TxENAB|Tx8|DTR); /* Transmitter now on */ } else { /* Tx OFF and Rx ON */ hp->tstate = IDLE; xwrite_scc(hp,cmd,R5,Tx8|DTR); /* TX off */ if(hp->speed){ /* if internally clocked */ /* Reprogram BRG for 32x clock for receive DPLL */ /* BRG off, keep Pclk source */ xwrite_scc(hp,cmd,R14,BRSRC); br = hp->speed; /* get desired speed */ /* calc 32X BRG divisor */ tc = ((XTAL/32)/br)-2; xwrite_scc(hp,cmd,R12,tc&0xFF); /* lower byte */ xwrite_scc(hp,cmd,R13,(tc>>8)&0xFF);/* upper byte */ /* SEARCH mode, BRG source */ xwrite_scc(hp,cmd,R14,BRSRC|SEARCH); /* Enable the BRG */ xwrite_scc(hp,cmd,R14,BRSRC|BRENABL); } /* Now, turn on the receiver and hunt for a flag */ xwrite_scc(hp,cmd,R3,RxENABLE|RxCRC_ENAB|Rx8); hp->rstate = ACTIVE; /* Normal state */ if(cmd & 2){/* if channel a */ setup_rx_dma(hp); } else { /* reset error bits */ /* xwrite_scc(hp,cmd,R0,ERR_RES); */ /* reset buffer pointers */ hp->rcp = hp->rcvbuf->data; hp->rcvbuf->cnt = 0; xwrite_scc(hp,cmd,R1,(INT_ALL_Rx|EXT_INT_ENAB)); } xwrite_scc(hp,cmd,R15,BRKIE); /* allow ABORT int */ /* Hold tx off long enough for other station to reply */ hp->deftime = msclock() + hp->txdelay + 500; } } void setup_rx_dma(hp) register struct pichan *hp; { unsigned buf_offset, buf_segment; int cmd; long longseg, dma_abs, dma_page; char i_state; i_state = dirps(); /* disable interrupts */ cmd = hp->base+CTL; if(!hp->rcvbuf){ /* No rx buffer available */ restore(i_state); return; } /* Calculate high order 4 bits of the buffer area and store * them in the DMA page register */ buf_offset = FP_OFF(hp->rcvbuf->data); buf_segment= FP_SEG(hp->rcvbuf->data); longseg = (long) buf_segment; dma_abs = (longseg << 4) + (long) buf_offset; dma_page = dma_abs >> 16; if(((dma_abs + hp->bufsiz -1) >> 16) != dma_page) tprintf("PI: ERROR - RX DMA page boundary violation\n"); /* Get ready for RX DMA */ xwrite_scc(hp,cmd,R1,WT_FN_RDYFN|WT_RDY_RT|INT_ERR_Rx|EXT_INT_ENAB); outportb(DMA_MASK, DMA_DISABLE|hp->dmachan); /* Disable DMA chan */ /* Set DMA mode register to single transfers, incrementing address, * auto init, writes */ outportb(DMA_MODE,DMA_RX_MODE|hp->dmachan); outportb(hp->page_addr,dma_page);/* Store in 64K DMA page */ outportb(DMA_RESETFF,0); /* reset byte pointer flipflop */ /* Output buffer start (dest) address */ outportb(hp->dma_dest,dma_abs); outportb(hp->dma_dest,dma_abs >> 8); /* output DMA maximum byte count */ outportb(hp->dma_wcr,hp->bufsiz - 1 - sizeof(struct phdr)); outportb(hp->dma_wcr, (hp->bufsiz - 1 - sizeof(struct phdr)) >> 8); /* Unmask channel 1 (start DMA) */ outportb(DMA_MASK, DMA_ENABLE|hp->dmachan); /* Enable DMA chan */ /* If a packet is already coming in, this line is supposed * to mess up the crc to avoid receiving a partial packet */ xwrite_scc(hp,cmd,R0,RES_Rx_CRC); /* Enable RX dma */ xwrite_scc(hp,cmd,R1,WT_RDY_ENAB|WT_FN_RDYFN|WT_RDY_RT|INT_ERR_Rx|EXT_INT_ENAB); restore(i_state); } void setup_tx_dma(hp,buffer,length) struct pichan *hp; char *buffer; int length; { unsigned buf_offset, buf_segment; long longseg, dma_abs, dma_page; char i_state; i_state = dirps(); /* disable interrupts */ /* Calculate high order 4 bits of the buffer area and store * them in the DMA page register */ buf_offset = FP_OFF(buffer); buf_segment= FP_SEG(buffer); longseg = (long) buf_segment; dma_abs = (longseg << 4) + (long) buf_offset; dma_page = dma_abs >> 16; outportb(DMA_MASK, DMA_DISABLE|hp->dmachan); /* Disable DMA chan */ if(((dma_abs + length) >> 16) != dma_page) tprintf("PI: ERROR - TX DMA page boundary violation\n"); --length; /* Adjust length for DMA chip */ /* Set DMA mode register to single transfers, incrementing address, * no auto init, reads */ outportb(DMA_MODE,DMA_TX_MODE|hp->dmachan); outportb(hp->page_addr,dma_page); /* Store in 64K DMA page */ outportb(DMA_RESETFF,0); /* reset byte pointer flipflop */ outportb(hp->dma_dest,dma_abs); /* Output buffer start (source) address */ outportb(hp->dma_dest,dma_abs >> 8); /* output byte count */ outportb(hp->dma_wcr,length); outportb(hp->dma_wcr, (length) >> 8); restore(i_state); } /* Initialize pi controller parameters */ static int scc_init(hp) register struct pichan *hp; { int16 tc; long br; char i_state; register int16 cmd; /* Initialize 8530 channel for SDLC operation */ cmd = CTL+hp->base; #ifdef notdef tprintf("Pi: Initializing Channel %c - Base = %x\n",cmd&2?'A':'B',cmd&~CTL); #endif i_state = dirps(); switch(cmd & 2){ case 2: xwrite_scc(hp,cmd,R9,CHRA); /* Reset channel A */ xwrite_scc(hp,cmd,R2,0xff); /* Initialize interrupt vector */ break; case 0: xwrite_scc(hp,cmd,R9,CHRB); /* Reset channel B */ break; } /* Deselect all Rx and Tx interrupts */ xwrite_scc(hp,cmd,R1,0); /* Turn off external interrupts (like CTS/CD) */ xwrite_scc(hp,cmd,R15,0); /* X1 clock, SDLC mode */ xwrite_scc(hp,cmd,R4,SDLC|X1CLK); /* Now some misc Tx/Rx parameters */ /* CRC PRESET 1, NRZI Mode */ if(hp->speed){ xwrite_scc(hp,cmd,R10,CRCPS|NRZI); /* Tx Clk from BRG. Rcv Clk from DPLL, TRxC pin outputs DPLL */ xwrite_scc(hp,cmd,R11,TCBR|RCDPLL|TRxCDP|TRxCOI); } else { xwrite_scc(hp,cmd,R10,CRCPS); /* Tx Clk from Trxcl. Rcv Clk from Rtxcl, TRxC pin is input */ xwrite_scc(hp,cmd,R11,TCTRxCP); } /* Null out SDLC start address */ xwrite_scc(hp,cmd,R6,0); /* SDLC flag */ xwrite_scc(hp,cmd,R7,FLAG); /* Set up the Transmitter but don't enable it * DTR, 8 bit TX chars only - TX NOT ENABLED */ xwrite_scc(hp,cmd,R5,Tx8|DTR); /* Receiver - intial setup only - more later */ xwrite_scc(hp,cmd,R3,Rx8); /* 8 bits/char */ /* Setting up BRG now - turn it off first */ xwrite_scc(hp,cmd,R14,BRSRC); /* BRG off, but keep Pclk source */ /* set the 32x time constant for the BRG in Receive mode */ if(hp->speed){ br = hp->speed; /* get desired speed */ tc = ((XTAL/32)/br)-2; /* calc 32X BRG divisor */ } else { tc = 14; } xwrite_scc(hp,cmd,R12,tc&0xFF); /* lower byte */ xwrite_scc(hp,cmd,R13,(tc>>8)&0xFF); /* upper byte */ /* Following subroutine sets up and ENABLES the receiver */ rts(hp,OFF); /* TX OFF and RX ON */ if(hp->speed){ /* DPLL frm BRG, BRG src PCLK */ xwrite_scc(hp,cmd,R14,BRSRC|SSBR); } else { /* DPLL frm rtxc,BRG src PCLK */ xwrite_scc(hp,cmd,R14,BRSRC|SSRTxC); } xwrite_scc(hp,cmd,R14,BRSRC|SEARCH); /* SEARCH mode, keep BRG source */ xwrite_scc(hp,cmd,R14,BRSRC|BRENABL);/* Enable the BRG */ if(!(cmd & 2)) /* if channel b */ xwrite_scc(hp,cmd,R1,(INT_ALL_Rx|EXT_INT_ENAB)); xwrite_scc(hp,cmd,R15,BRKIE); /* ABORT int */ /* Now, turn on the receiver and hunt for a flag */ xwrite_scc(hp,cmd,R3,RxENABLE|RxCRC_ENAB|Rx8); restore(i_state); return 0; } /* Process to recover from ibuffails. * This could be done in the function network() in config.c, * to save a context switch. I put it here so the driver would * be more self contained. */ void buf_recover(unused,b,a) int unused; void *b; /* Unused */ void *a; /* Unused */ { struct pichan *hp0, *hp1; char i_state; int i; for(;;){ pwait(NULL); for(i=0; ircvbuf){ /* No rx buffer allocated */ i_state = dirps(); hp0->rcvbuf = alloc_mbuf(hp0->bufsiz + sizeof(struct phdr)); if(hp0->rcvbuf != NULLBUF) hp0->rcvbuf->data += sizeof(struct phdr); restore(i_state); setup_rx_dma(hp0); } i_state = dirps(); if(!hp1->rcvbuf && (hp1->rstate == ACTIVE)){ /* No rx buf allocated */ if((hp1->rcvbuf = alloc_mbuf(hp1->bufsiz+sizeof(struct phdr))) != NULL){ hp1->rcvbuf->data += sizeof(struct phdr); hp1->rcp = hp1->rcvbuf->data; hp1->rcvbuf->cnt = 0; xwrite_scc(hp1,CTL+hp1->base,R3,RxENABLE|RxCRC_ENAB|Rx8); } } restore(i_state); } } } /* Attach a PI interface to the system * argv[0]: hardware type, must be "pi" * argv[1]: I/O address, e.g., "0x300" * argv[2]: vector, e.g., "2" * argv[3]: dma channel (1..3) * argv[4]: mode, must be: * "ax25" (AX.25 UI frame format) * argv[5]: interface label, e.g., "pi0" * argv[6]: receiver packet buffer size in bytes * argv[7]: maximum transmission unit, bytes * argv[8]: channel A interface speed, e.g, "1200", 0 = ext. clock * argv[9]: channel B interface speed * argv[10]: First IP address, optional (defaults to Ip_addr); * argv[11]: Second IP address, optional (defaults to Ip_addr); */ int pi_attach(argc,argv) int argc; char *argv[]; { struct mbuf *bp; extern void refiq(); register struct iface *if_pca,*if_pcb; struct pichan *hp; int dev; char i_state; int n; refiq(); /* replenish interrupt buffer pool (in mbuf.c) */ if(acc_delay == 0){ /* Only do this once */ /* Adapt recovery time delay to processor speed */ set_acc_delay(); } /* Quick check to make sure args are good and mycall is set */ if(strcmp(argv[4],"ax25") != 0){ tprintf("PI: Mode %s unknown for interface %s\n", argv[4],argv[5]); return -1; } if(if_lookup(argv[5]) != NULLIF){ tprintf("PI: Interface %s already exists\n",argv[5]); return -1; } if(Mycall[0] == '\0'){ tprintf("PI: Set mycall first\n"); return -1; } /* Note: each card must have a unique address, IRQ and DMA */ if(pinbr >= PIMAX){ tprintf("PI: Maximum of %d PI cards supported\n",PIMAX); return -1; } dev = pinbr++; /* Initialize hardware-level control structure */ Pi[dev].addr = htoi(argv[1]); Pi[dev].vec = atoi(argv[2]); if(strchr(argv[2],'c') != NULLCHAR) Pi[dev].chain = 1; else Pi[dev].chain = 0; /* Set up counter chip */ /* 500 uS square wave */ outportb(Pi[dev].addr+TMRCMD, SC0|LSB_MSB|MODE3); for(n=0; n<5;n++) /* satisfy access time restriction */ ; outportb(Pi[dev].addr+TMR0, 922 & 0xFF); for(n=0; n<5;n++) /* satisfy access time restriction */ ; outportb(Pi[dev].addr+TMR0, 922 >> 8); for(n=0; n<5;n++) /* satisfy access time restriction */ ; /* Save original interrupt vector */ Pi[dev].oldvec = getirq(Pi[dev].vec); /* Set new interrupt vector */ if(setirq(Pi[dev].vec,pihandle[dev]) == -1){ tprintf("PI: IRQ %u out of range\n",Pi[dev].vec); pinbr--; return -1; } if((atoi(argv[3]) < 1) || (atoi(argv[3]) > 3)){ tprintf("PI: DMA %d out of range\n",atoi(argv[3])); pinbr--; return -1; } /* Create interface 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 > 10) if_pca->addr = resolve(argv[10]); if(argc > 11) if_pcb->addr = resolve(argv[11]); if(if_pca->addr == 0 || if_pcb->addr == 0){ tprintf("PI: No IP address"); free((char *)if_pca); free((char *)if_pcb); return -1; } /* Append "a" to interface associated with A channel */ if_pca->name = malloc((unsigned)strlen(argv[5])+2); strcpy(if_pca->name,argv[5]); strcat(if_pca->name,"a"); /* Append "b" to interface associated with B channel */ if_pcb->name = malloc((unsigned)strlen(argv[5])+2); strcpy(if_pcb->name,argv[5]); strcat(if_pcb->name,"b"); if_pcb->mtu = if_pca->mtu = atoi(argv[7]); if_pcb->type = if_pca->type = CL_AX25; if_pcb->ioctl = if_pca->ioctl = pi_ctl; if_pca->dev = 2*dev; /* pi0a */ if_pcb->dev = 2*dev + 1; /* pi0b */ if_pcb->stop = if_pca->stop = pi_stop; if_pcb->output = if_pca->output = ax_output; if_pcb->raw = if_pca->raw = pi_raw; if(strcmp(argv[4],"ax25") == 0){ /* Must be true, was checked at top */ if(if_pcb->hwaddr == NULLCHAR) if_pcb->hwaddr = mallocw(AXALEN); memcpy(if_pcb->hwaddr,Mycall,AXALEN); if(if_pca->hwaddr == NULLCHAR) if_pca->hwaddr = mallocw(AXALEN); memcpy(if_pca->hwaddr,Mycall,AXALEN); } /* Link em in to the interface chain */ if_pca->next = if_pcb; if_pcb->next = Ifaces; Ifaces = if_pca; /* set params in pichan table for CHANNEL B */ hp = &Pichan[2*dev+1]; /* pi1 is offset 1 */ hp->dmachan = 0; /* Channel B does not have dma */ hp->cardbase = Pi[dev].addr; hp->iface = if_pcb; hp->stata = Pi[dev].addr + CHANA + CTL; /* permanent status */ hp->statb = Pi[dev].addr + CHANB + CTL; /* addrs for CHANA/B*/ hp->speed = (int16)atoi(argv[9]); hp->base = Pi[dev].addr + CHANB; hp->bufsiz = atoi(argv[6]); hp->tstate = IDLE; /* default channel access Params */ hp->txdelay = 30; /* 300 Ms */ hp->persist = 128; /* 50% persistence */ hp->slotime = 30; /* 300 Ms */ hp->squeldelay = 3; /* 30 Ms */ xwrite_scc(hp,CTL+hp->stata,R9,FHWRES); /* Hardware reset */ /* one time only */ /* Disable interrupts with Master interrupt ctrl reg */ xwrite_scc(hp,CTL+hp->stata,R9,0); scc_init(hp); /* Pre-allocate a receive buffer */ i_state = dirps(); hp->rcvbuf = alloc_mbuf(hp->bufsiz+sizeof(struct phdr)); restore(i_state); if(hp->rcvbuf == NULLBUF){ /* No memory, abort receiver */ tprintf("PI: No memory available for receive buffers\n"); /* Restore original interrupt vector */ setirq(Pi[dev].vec,Pi[dev].oldvec); pinbr--; return -1; } hp->rcvbuf->data += sizeof(struct phdr); hp->rcp = hp->rcvbuf->data; hp->rcvbuf->cnt = 0; hp->sndq = NULLBUF; /* set params in pichan table for CHANNEL A */ hp = &Pichan[2*dev]; /* pi0a is offset 0 */ hp->dmachan = (unsigned char)atoi(argv[3]); /* Figure out where the dma page register is. */ if(hp->dmachan < 8 && hp->dmachan >= 0){ hp->page_addr = Page_regs[hp->dmachan]; } else { printf("PI: DMA channel %d out of range\n",hp->dmachan); free_p(hp->rcvbuf); /* Restore original interrupt vector */ setirq(Pi[dev].vec,Pi[dev].oldvec); pinbr--; return -1; } hp->dma_dest = hp->dmachan * 2; hp->dma_wcr = hp->dma_dest + 1; hp->cardbase = Pi[dev].addr; hp->iface = if_pca; hp->speed = (int16)atoi(argv[8]); hp->base = Pi[dev].addr + CHANA; hp->bufsiz = atoi(argv[6]); hp->tstate = IDLE; /* default channel access Params */ hp->txdelay = 15; /* 15 mS */ hp->persist = 128; /* 50% persistence */ hp->slotime = 15; /* 15 mS */ hp->squeldelay = 1; /* 1 mS */ newproc("buf_recover",256,buf_recover,0,hp,NULL,0); /* Pre-allocate a receive buffer */ /* buffer is allocated with ints off */ i_state = dirps(); hp->rcvbuf = alloc_mbuf(hp->bufsiz+sizeof(struct phdr)); restore(i_state); if(hp->rcvbuf == NULLBUF){ /* No memory, abort receiver */ tprintf("PI: No memory available for receive buffers\n"); /* Restore original interrupt vector */ setirq(Pi[dev].vec,Pi[dev].oldvec); pinbr--; return -1; } hp->rcvbuf->data += sizeof(struct phdr); hp->rcvbuf->cnt = 0; hp->sndq = NULLBUF; /* Get a buffer for tx which does not cross a dma boundary */ /* buffer is allocated with ints off */ i_state = dirps(); bp = alloc_mbuf(hp->bufsiz); if(bp != NULLBUF) hp->txdmabuf = bp->data; else hp->txdmabuf = NULLCHAR; restore(i_state); if(!hp->txdmabuf) tprintf("PI: No memory available for transmit buffer"); scc_init(hp); /* master interrupt enable */ xwrite_scc(hp,CTL+hp->base,R9,MIE|NV); /* Enable interrupt */ maskon(Pi[dev].vec); if_pca->txproc = newproc("pi tx",512,if_tx,0,if_pca,NULL,0); if_pcb->txproc = newproc("pi tx",512,if_tx,0,if_pcb,NULL,0); return 0; } /* Shut down interface */ int pi_stop(iface) struct iface *iface; { int16 dev; struct pichan *hp; dev = iface->dev; if(dev & 1) /* Because there are 2 devices per card */ return 0; dev >>= 1; /* Convert back into pi number */ hp = &Pichan[2*dev]; /* pi0a is offset 0 */ outportb(DMA_MASK, DMA_DISABLE|hp->dmachan); /* Disable DMA channel */ /* Turn off interrupts */ maskoff(Pi[dev].vec); /* Restore original interrupt vector */ setirq(Pi[dev].vec,Pi[dev].oldvec); /* Force hardware reset */ xwrite_scc(&Pichan[2*dev],CTL+Pi[dev].addr + CHANA,R9,FHWRES); return 0; } /* Send raw packet on pi card */ int pi_raw(iface,bp) struct iface *iface; struct mbuf *bp; { char i_state; char kickflag; struct pichan *hp; dump(iface,IF_TRACE_OUT,CL_AX25,bp); iface->rawsndcnt++; iface->lastsent = secclock(); hp = &Pichan[iface->dev]; kickflag = (hp->sndq == NULLBUF) & (hp->sndbuf == NULLBUF); enqueue(&hp->sndq,bp); hp->enqueued++; if(kickflag){ /* simulate interrupt to xmit */ switch(hp->base & 2){ case 2: a_txint(hp); /* process interrupt */ break; case 0: i_state = dirps(); if(hp->tstate == IDLE) b_txint(hp); restore(i_state); break; } } return 0; } /* display PI Channel stats */ int dopistat() { struct pichan *hp; int i; tprintf("PI Board Statistics:\n\n"); tprintf("Base Addr Rxints Txints Exints TxFrms RxFrms Crcerr RxOvrs TxUndr \n"); tprintf("--------- ------ ------ ------ ------ ------ ------ ------ ------ \n"); for(i=0; ibase, hp->rxints, hp->txints, hp->exints, hp->enqueued, hp->rxframes, hp->crcerr, hp->rovers, hp->tunders, hp->rstate==0 ? "IDLE" : hp->rstate==1 ? "ACTIVE" : hp->rstate==2 ? "RXERROR" : hp->rstate==3 ? "RXABORT":"TOOBIG" ); tprintf("Tstate = %s\n", hp->tstate == 0 ? "IDLE" : hp->tstate == 1 ? "ACTIVE" : hp->tstate == 2 ? "UNDERRUN" : hp->tstate == 3 ? "FLAGOUT" : hp->tstate == 4 ? "DEFER" : hp->tstate == 5 ? "TXDELAY" : "CRCOUT" ); } return 0; } /* Subroutine to set kiss params in channel tables */ int32 pi_ctl(iface,cmd,set,val) struct iface *iface; int cmd; int set; int32 val; { struct pichan *hp; int32 t,ca; hp = &Pichan[iface->dev]; /* point to channel table */ switch(cmd){ case PARAM_TXDELAY: if(set) hp->txdelay = val; return hp->txdelay; case PARAM_PERSIST: if(set) hp->persist = val; return uchar(hp->persist); case PARAM_SLOTTIME: if(set) hp->slotime = val; return hp->slotime; case PARAM_TXTAIL: if(set) hp->squeldelay = val; return hp->squeldelay; case PARAM_MUTE: if(set){ if(val == -1){ /* Special case for duration of a CTS */ val = hp->txdelay + 500; } hp->deftime = msclock() + val; } t = msclock(); ca = hp->deftime - t; if(ca < 0){ hp->deftime = t; ca = 0; } return ca; } return -1; }