/* Driver for HAPN-1 8273 card * 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 "global.h" #include "mbuf.h" #include "iface.h" #include "hapn.h" #include "ax25.h" #include "trace.h" struct hapn hapn[NHAPN]; void ha0vec(); void (*h_handle[])() = { ha0vec }; 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) */ /*VARARGS3*/ static 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++); } } /* Start receiver of 8273 */ static 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. */ void haint(dev) int dev; { register struct hapn *hp; register int16 base; char flag = 0; void htxint(),hrxint(); 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 */ } } /* 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 */ enqueue(&hp->rcvq, hp->rcvbuf); hp->rcvbuf = NULLBUF; hp->rcvcnt++; 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 = disable(); 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(hp) register struct hapn *hp; { char isav; register int16 base; int16 len; char c; isav = disable(); 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; break; } } /* Start transmitter */ len = len_mbuf(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(pullup(&hp->sndbuf, &c, 1) != 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" */ int hapn_attach(argc, argv) int argc; char *argv[]; { register struct interface *if_h; extern struct interface *ifaces; struct hapn *hp; int dev, i; char isav; int hapn_init(), hapn_stop(), ax_send(), ax_output(), hapn_raw(); void dohapn(); void (*getirq())(); /* Getirq is a function returning a pointer to * a function returning void */ static struct { char *str; char type; } ch_access [] = { "csma", 0, "full", 1 }; if(nhapn >= NHAPN){ printf("Too many HAPN adaptors\n"); return -1; } dev = nhapn++; /* Initialize hardware constants */ hapn[dev].base = htoi(argv[1]); hapn[dev].vec = htoi(argv[2]); /* Save original interrupt vector */ hapn[dev].oldvec = getirq(hapn[dev].vec); /* Set new interrupt vector */ setirq(hapn[dev].vec, h_handle[dev]); /* Create new interface structure */ if_h = (struct interface *) calloc(1,sizeof(struct interface)); /* Fill interface structure */ if_h->name = malloc((unsigned) strlen(argv[4]) +1); strcpy(if_h->name, argv[4]); if_h->mtu = atoi(argv[6]); if_h->dev = dev; if_h->recv = dohapn; if_h->stop = hapn_stop; if_h->output = ax_output; if_h->raw = hapn_raw; if(strcmp(argv[3], "ax25")){ printf("Mode %s unknown for interface %s\n", argv[3], argv[4]); free(if_h->name); free((char *) if_h); return -1; } axarp(); if(mycall.call[0] == '\0'){ printf("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 = malloc(sizeof(mycall)); memcpy(if_h->hwaddr,(char *)&mycall,sizeof(mycall)); /* Link the interface into the interface list */ if_h->next = ifaces; ifaces = if_h; /* Fill the local data structure */ hp = &hapn[dev]; 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 = disable(); hapn_init(hp); /* Enable the interrupt */ maskon(hapn[dev].vec); restore(isav); return 0; } /* initialize the HAPN adaptor */ int hapn_init(hp) register struct hapn *hp; { register int16 base; char isav; isav = disable(); 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 */ int hapn_stop(iface) struct interface *iface; { int16 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() { struct hapn *hp; int i; if(nhapn == 0){ printf("No HAPN adaptor attached\n"); return 1; } for(i = 0; i < nhapn; i++){ hp = &hapn[i]; printf("HAPN %d: rxints: %ld txints: %ld badint: %-5d\r\n", i, hp->rxints,hp->txints,hp->badint); printf(" receive - frames: %-5d crcerrs: %-5d aborts: %-5d dmaorun: %-5d\r\n", hp->rframes,hp->crcerr, hp->aborts, hp->dmaorun); printf(" - toobig: %-5d dcdloss: %-5d rxorun: %-5d\r\n", hp->toobig,hp->cdloss,hp->rxorun); printf(" transmit - frames: %-5d aborts : %-5d uruns : %-5d ctsloss: %-5d\r\n", hp->tframes,hp->taborts, hp->t_urun, hp->ctsloss); } return 0; } /* periodically kicked by mainline routine * process any queued received frames * kick tx if waiting on busy channel */ void dohapn(iface) struct interface *iface; { struct hapn *hp; struct mbuf *bp; hp = &hapn[iface->dev]; /* Process any received frames */ while((bp = dequeue(&hp->rcvq)) != NULLBUF){ hp->rcvcnt--; dump(iface,IF_TRACE_IN,TRACE_AX25,bp); ax_recv(iface, bp); } /* Test for deferred transmit (CSMA) */ if(hp->tstate == DEFER) htxint(hp); } /* Send raw packet on HAPN interface */ int hapn_raw(interface,bp) struct interface *interface; struct mbuf *bp; { struct hapn *hp; dump(interface,IF_TRACE_OUT,TRACE_AX25,bp); hp = &hapn[interface->dev]; enqueue(&hp->sndq, bp); /* See if anything being transmitted */ if(hp->tstate == IDLE) htxint(hp); return 0; }