home *** CD-ROM | disk | FTP | other *** search
- /* 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 <stdio.h>
- #include <dos.h>
- #include "global.h"
- #ifdef ANSIPROTO
- #include <stdarg.h>
- #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.
- */
- void
- 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 */
- }
- }
- /* 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];
- struct phdr phdr;
-
- 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 */
- bp = alloc_mbuf(sizeof(phdr));
- bp->cnt = sizeof(phdr);
- phdr.iface = hp->iface;
- phdr.type = CL_AX25;
- memcpy(&bp->data[0],(char *)&phdr,sizeof(phdr));
- bp->next = hp->rcvbuf;
- hp->rcvbuf = NULLBUF;
- enqueue(&Hopper, bp);
- 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 = htoi(argv[2]);
-
- /* 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);
- 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;
- }
-