/* Driver for HAPN-1 8273 card on PC * Jon Bloom, KE3Z; adapted from KA9Q's PC-100 driver * Modified Rx interrupt routine to prevent lockup * John Tanner VK2ZXQ 6th Feb 1988 * Adapted back into 871225.9 by KA9Q 15 Feb 1988 */ #include #include #include "global.h" #ifdef ANSIPROTO #include #endif #include "timer.h" #include "mbuf.h" #include "iface.h" #include "pktdrvr.h" #include "netuser.h" #include "hapn.h" #include "ax25.h" #include "trace.h" #include "pc.h" #include "proc.h" static void cmd_8273 __ARGS((int16 base,int cmd,int np,...)); static int hapn_init __ARGS((struct hapn *hp)); static int hapn_raw __ARGS((struct iface *iface,struct mbuf *bp)); static int hapn_stop __ARGS((struct iface *iface)); static int hcdchk __ARGS((int16 base)); static void hrxint __ARGS((struct hapn *hp)); static void hrxgo __ARGS((struct hapn *hp)); static void htxint __ARGS((void *p)); static struct hapn Hapn[NHAPN]; static INTERRUPT (*H_handle[])() = { ha0vec }; static int16 Nhapn; /* send command to the 8273 * "base" = base port of 8273 * "cmd" = command byte * "np" = number of parameter bytes * "p1" = first parameter (parameters are int) */ #ifdef ANSIPROTO static void cmd_8273(int16 base, int cmd, int np, ...) { int p; va_list ap; while(inportb(base+STA) & CBSY) ; outportb(base+CMD, cmd); va_start(ap,np); while(np--){ while(inportb(base+STA) & CPBF) ; p = va_arg(ap,int); outportb(base+PAR, p); } va_end(ap); } #else /*VARARGS3*/ static void cmd_8273(base, cmd, np, p1) int16 base; int cmd, np, p1; { int *p; while(inportb(base+STA) & CBSY) ; outportb(base+CMD, cmd); p = &p1; while(np--){ while(inportb(base+STA) & CPBF) ; outportb(base+PAR, *p++); } } #endif /* Start receiver of 8273 */ static void hrxgo(hp) register struct hapn *hp; { cmd_8273(hp->base, GENERAL_RX, 2, hp->bufsiz & 0xff, hp->bufsiz >> 8); } /* Interrupt service function. Entered with hapn index * The "flag" variable is used in this routine to indicate a * valid TX or RX interrupt. If an invalid interrupt is detected * the 8273 is reset. */ INTERRUPT (far *(haint)(dev))() int dev; { register struct hapn *hp; register int16 base; char flag = 0; hp = &Hapn[dev]; base = hp->base; /* Check for TX interrupt */ if(inportb(base+STA) & TXINT){ flag = 1; /* Valid interrupt, set flag */ htxint(hp); } /* Check for RX interrupt */ if(inportb(base+STA) & RXINT){ flag = 1; /* Valid interrupt, set flag */ hrxint(hp); } /* Check for unknown interrupt */ if(!flag){ hp->badint++; /* Increment error counter */ hapn_init(hp); /* Reinitialise the 8273 */ } return hp->chain ? hp->oldvec : NULL; } /* RX interrupt service * if status register bit "RXIRA" is set, interrupt is final, * otherwise, interrupt is data request */ static void hrxint(hp) register struct hapn *hp; { register struct mbuf *bp; register int16 base; unsigned char results[10]; hp->rxints++; base = hp->base; if(inportb(base+STA) & RXIRA){ /* RX result interrupt * If the result is a good frame 3 bytes need to be read * If an error has occurred only one byte need to be read */ /* Read first result byte and test for good data */ if((results[0]=(inportb(base + RXI))) == 0xe0){ /* Good result; read two more result bytes */ while((inportb(base + STA) & RXIRA) == 0) ; /* Read second result byte */ results[1] = inportb(base + RXI); /* Wait for third result byte */ while((inportb(base + STA) & RXIRA) == 0) ; results[2] = inportb(base + RXI);/* Read it */ /* Since this frame is ok put it on the queue */ net_route(hp->iface,CL_AX25,hp->rcvbuf); hp->rcvbuf = NULLBUF; hp->rframes++; } else { /* Error termination * Parse RIC and act accordingly * Only one result byte returned on error */ switch(results[0]){ case CRCERR: hp->crcerr++; break; case ABORT_DET: hp->aborts++; break; case DMA_OVRN: hp->dmaorun++; break; case MEM_OVFL: hp->toobig++; break; case CD_LOSS: hp->cdloss++; hapn_init(hp); /* 8273 reset on cd error */ break; case RX_ORUN: hp->rxorun++; break; } /* Throw rx buffer contents away to start over */ hp->rcp = hp->rcvbuf->data; hp->rcvbuf->cnt = 0; } /* Restart the receiver */ cmd_8273(base,RX_DISABLE,0); hrxgo(hp); } else { /* RX data interrupt; allocate new rx buffer if none present */ if((bp = hp->rcvbuf) == NULLBUF){ bp = hp->rcvbuf = alloc_mbuf(hp->bufsiz); if(bp == NULLBUF){ /* No memory available */ hp->nomem++; cmd_8273(base, RX_DISABLE, 0); hrxgo(hp); return; } /* Init buffer pointer */ hp->rcp = hp->rcvbuf->data; } /* Barf if rx data is more than buffer can hold (should never * happen since 8273 is also counting bytes). */ if(bp->cnt++ >= hp->bufsiz){ hp->toobig++; cmd_8273(base, RX_DISABLE, 0); hrxgo(hp); free_p(bp); hp->rcvbuf = NULLBUF; return; } /* Store the received byte */ *hp->rcp++ = inportb(base+RXD); } } /* test for busy channel (CD active) * returns TRUE if channel busy */ static int hcdchk(base) int16 base; { char isav; isav = dirps(); cmd_8273(base, READ_A, 0); while(!(inportb(base+STA) & CRBF)) ; restore(isav); return((inportb(base+RES) & CD) != 0); } /* TX interrupt service * if status register bit "TXIRA" is set, interrupt is final, * otherwise, interrupt is data request */ static void htxint(p) void *p; { register struct hapn *hp; char isav; register int16 base; int16 len; int c; hp = (struct hapn *)p; isav = dirps(); hp->txints++; base = hp->base; c = 0; if(inportb(base+STA) & TXIRA){ /* TX result interupt */ hp->tstate = IDLE; free_p(hp->sndbuf); hp->sndbuf = NULLBUF; /* Read result */ while((inportb(base+STA) & (TXINT | TXIRA)) != (TXINT | TXIRA)) ; c = inportb(base+TXI); /* Test for tx abort */ switch(c & 0x1f){ case DMA_URUN: hp->t_urun++; break; case CTS_LOSS: hp->ctsloss++; break; case ABORT_CMPLT: hp->taborts++; break; } } switch(hp->tstate){ case IDLE: /* See if a buffer is ready to be sent */ if((hp->sndbuf = dequeue(&hp->sndq)) == NULLBUF) break; case DEFER: /* Busy-channel check */ if(hp->mode == CSMA && (c & 0x1f) != EARLY_TXI){ if(hcdchk(base)){ hp->tstate = DEFER; start_timer(&hp->defer); break; } } /* Start transmitter */ stop_timer(&hp->defer); len = len_p(hp->sndbuf); cmd_8273(base, TX_FRAME, 2, len & 0xff, len >> 8); hp->tstate = ACTIVE; hp->tframes++; break; case ACTIVE: /* Get next byte to send */ if((c = PULLCHAR(&hp->sndbuf)) == -1){ cmd_8273(base, ABORT_TXF, 0); hp->tstate = IDLE; } else outportb(base+TXD, c); break; } restore(isav); } /* Attach a HAPN adaptor to the system * argv[0]: hardware type, must be "hapn" * argv[1]: I/O address, e.g. "0x310" * argv[2]: vector, e.g. "2" * argv[3]: mode, must be "ax25" * argv[4]: interface name, e.g. "ha0" * argv[5]: rx packet buffer size in bytes * argv[6]: maximum transmission unit in bytes * argv[7]: channel-access mechanism, "csma" or "full" * argv[8]: IP address, optional (defaults to Ip_addr) */ int hapn_attach(argc, argv,p) int argc; char *argv[]; void *p; { register struct iface *if_h; struct hapn *hp; int dev, i; char isav; static struct { char *str; char type; } ch_access [] = { "csma", 0, "full", 1 }; if(Nhapn >= NHAPN){ tprintf("Too many HAPN adaptors\n"); return -1; } if(if_lookup(argv[4]) != NULLIF){ tprintf("Interface %s already exists\n",argv[4]); return -1; } /* Create new interface structure */ if_h = (struct iface *) callocw(1,sizeof(struct iface)); /* Set interface address */ if_h->addr = Ip_addr; if(argc > 8) if_h->addr = resolve(argv[8]); if(if_h->addr == 0){ tprintf(Noipaddr); free((char *)if_h); return -1; } dev = Nhapn++; hp = &Hapn[dev]; /* Initialize hardware constants */ hp->base = htoi(argv[1]); hp->vec = atoi(argv[2]); if(strchr(argv[2],'c') != NULLCHAR) hp->chain = 1; else hp->chain = 0; /* Save original interrupt vector */ hp->oldvec = getirq(Hapn[dev].vec); /* Set new interrupt vector */ setirq(hp->vec, H_handle[dev]); /* Continue filling interface structure */ if_h->name = strdup(argv[4]); if_h->type = CL_AX25; if_h->mtu = atoi(argv[6]); if_h->dev = dev; if_h->stop = hapn_stop; if_h->output = ax_output; if_h->raw = hapn_raw; hp->iface = if_h; if(strcmp(argv[3], "ax25")){ tprintf("Mode %s unknown for interface %s\n", argv[3], argv[4]); free(if_h->name); free((char *) if_h); return -1; } if(Mycall[0] == '\0'){ tprintf("set mycall first\n"); free(if_h->name); free((char *) if_h); return -1; } if_h->send = ax_send; if(if_h->hwaddr == NULLCHAR) if_h->hwaddr = mallocw(AXALEN); memcpy(if_h->hwaddr,Mycall,AXALEN); /* Link the interface into the interface list */ if_h->next = Ifaces; Ifaces = if_h; /* Fill the local data structure */ hp->bufsiz = atoi(argv[5]); for(i = 0; i < (sizeof ch_access / sizeof ch_access[0]); i++) if(!strcmp(argv[7], ch_access[i].str)) hp->mode = ch_access[i].type; /* Initialize the hardware */ isav = dirps(); hapn_init(hp); /* Initialize the defer timer */ set_timer(&hp->defer,MSPTICK); hp->defer.func = htxint; hp->defer.arg = hp; /* Enable the interrupt */ maskon(hp->vec); restore(isav); if_h->txproc = newproc("hapn tx",512,if_tx,0,if_h,NULL,0); return 0; } /* initialize the HAPN adaptor */ static int hapn_init(hp) register struct hapn *hp; { register int16 base; char isav; isav = dirps(); base = hp->base; /* Reset the 8273 */ outportb(base+RST, 1); outportb(base+RST, 0); inportb(base+TXI); /* Clear any old IR contents */ inportb(base+RXI); /* Select the operating modes */ cmd_8273(base, SET_XFER, 1, 1); cmd_8273(base, SET_MODE, 1, HDLC | EARLY | PREFRM | FLG_STM); cmd_8273(base, SET_SERIAL, 1, NRZI); cmd_8273(base, SET_B, 1, IRQ_ENB | RTS); cmd_8273(base, RST_B, 1, 0xff ^ RTS); hrxgo(hp); restore(isav); return 0; } /* shut down the HAPN adaptor */ static int hapn_stop(iface) struct iface *iface; { int dev; int16 base; struct hapn *hp; dev = iface->dev; hp = &Hapn[dev]; base = hp->base; /* Mask off interrupt input */ maskoff(hp->vec); /* Restore original interrupt vector */ setirq(hp->vec,hp->oldvec); /* Reset the 8273 */ outportb(base+RST, 1); outportb(base+RST, 0); return 0; } /* Display adaptor statistics */ int dohapnstat(argc,argv,p) int argc; char *argv[]; void *p; { struct hapn *hp; int i; if(Nhapn == 0){ tprintf("No HAPN adaptor attached\n"); return 1; } for(i = 0; i < Nhapn; i++){ hp = &Hapn[i]; tprintf("HAPN %d: rxints: %ld txints: %ld badint: %-5d\n", i, hp->rxints,hp->txints,hp->badint); tprintf(" receive - frames: %-5d crcerrs: %-5d aborts: %-5d dmaorun: %-5d\n", hp->rframes,hp->crcerr, hp->aborts, hp->dmaorun); tprintf(" - toobig: %-5d dcdloss: %-5d rxorun: %-5d\n", hp->toobig,hp->cdloss,hp->rxorun); if(tprintf(" transmit - frames: %-5d aborts : %-5d uruns : %-5d ctsloss: %-5d\n", hp->tframes,hp->taborts, hp->t_urun, hp->ctsloss) == EOF) break; } return 0; } /* Send raw packet on HAPN interface */ static int hapn_raw(iface,bp) struct iface *iface; struct mbuf *bp; { struct hapn *hp; hp = &Hapn[iface->dev]; enqueue(&hp->sndq, bp); /* See if anything being transmitted */ if(hp->tstate == IDLE) htxint(hp); return 0; }