/*- * NCR WaveLAN PCMCIA driver. * * Copyright 1994, Anders Klemets * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The names of the authors may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * HISTORY * $Log: if_wlp.c,v $ * Revision 1.1.2.2 1998/02/19 05:01:53 hosokawa * PR: * update my e-mail addresses. * * Revision 1.1.2.1 1997/12/11 14:00:17 itojun * PAO-971210 import. hope this works well... * * Obtained from: hosokawa * * Revision 1.1 1994/06/02 20:21:27 klemets * Initial revision * * Ported to BSDI by Robert Morris. * * Ported to freebsd by Jim Binkley. jrb@cs.pdx.edu, 1/96. * 1. Emasculated rtm's code for hot-swapping since I don't have * time to get that going and I expect the freebsd pcmcia * general code will be arriving soon. * The assumption here is that wlpprobe simply turns on the * pcmcia bus in a manner analogous to if_ze.c. * 2. Added code for BPF and promiscous mode seems to work fine * on unicast packets for other nodes. Not sure yet about * different NWIDs. * 3. #ifdef TIMER looks funky to me. Have tested it a bit * and modified it to use if_timer, but it isn't ON. * 4. multicast is ON and seems to work at this point. * * config note: * The irq passed in from the config line is the irq that * will indeed be used. It should be correct for pcmcia/isa. * Only: 3,4,5,7,9,10,11,12,14,15 can be used. * The driver takes what it gets during attach and programs * the pcmcia card accordingly to map to that ISA irq. * * The if_wl driver being a different piece of hardware ignores * what it gets from config (should check it though) and the irq is * effectively set by the dos instconf.exe utility in the PSA * nvram area. * * HW note: HW consists logically of the Intel 82593 lan controller * (on the pc card itself) + the radio modem. There is also a PSA * or nvram storage area. I run the dos instconf.exe utility to * set the card up before the driver itself runs under unix. * * Working config line on ibm 755 thinkpad and ibm 701c thinkpads: * * device wlp0 at isa? port 0x300 net irq 11 iomem 0xd8000 vector wlpintr * */ /* * PC-card hotplug extention (WL_PCCARD): * Tatsumi Hosokawa , 1996 */ #define WL_PCCARD 1 #include "wlp.h" #include "bpfilter.h" #include "card.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #ifdef NS #include #include #endif #if NBPFILTER > 0 #include #include #endif #include #include #include #include #include #if 0 #include #include #endif #include #include #define WLP_NPORT 8 /* 8 8-bit registers */ #if 0 typedef int boolean_t; #endif #define loutb outsb #define linb insb #define TRUE 1 #define FALSE 0 int xtradebug = 0; char t_packet[WAVELAN_MTU + WAVELAN_ADDR_SIZE + sizeof(long)]; static int xmt_watch = 0; static int gathersnr = 0; int wlp_overrunning; struct wlp_softc{ struct arpcom arpcom; /* ethernet common */ #ifdef BSDI struct device wlp_dev; /* base device */ struct isadev wlp_id; /* ISA device */ struct intrhand wlp_ih; /* interrupt vectoring */ #endif u_char nwid[2]; /* shadows radio modem registers */ int base; /* Address of I/O mapped registers */ int unit; caddr_t psa; /* Address of PSA */ int stop; /* Current 82593 Stop Hit Register */ int flags; short mode; int rfp; /* last DMA machine receive pointer */ u_char status; boolean_t seated; boolean_t tbusy; boolean_t mmi_inited; boolean_t cmd_request; boolean_t cmd_wait; boolean_t deferred; boolean_t power_down; struct i82593_conf_block cfblk; int band_2400MHz; #if NBPFILTER > 0 #if 0 caddr_t wlp_bpf; #endif #endif #if NCARD > 0 int gone; struct wavelan_conf wl_conf; #endif } wlp_softc[NWLP]; /* globals for wavelan signal strength cache */ int w_sigitems; /* number of cached entries */ struct w_sigcache w_sigcache[ MAXCACHEITEMS ]; /* array of cache entries */ int NextCacheItem; /* index for next cache entry * and also indicates number * of cached entries */ int wlpprobe(struct isa_device *); int wlpattach(struct isa_device *); struct isa_driver wlpdriver = { wlpprobe, wlpattach, "wlp", 0 }; void wlpintr(int); static void wlpstart(struct ifnet *); static void wlpinit(int unit); void wlprustrt(int unit); int wlphwrst(int unit); static void wlpinitmmi(int unit); int wlpreset(int unit); void wlpwatch(struct ifnet *b_ptr); static void wlprcv(int unit); void wlpxmt(int unit, struct mbuf *m); int wlpdiag(int unit); int wlpconfig(int unit); int wlpcmd(int unit, char *str, int cmd, int result); u_short wlpmmiread(int base, u_short reg); static int wlp_start_of_frame(int unit, int rfp); static void wlp_graceful_shutdown(int unit); static void wlpgetsnr(int unit, struct ether_header *ehp, u_char siglvl, u_char sillvl, u_char sigqual); int wlpioctl(struct ifnet *ifp, int cmd, caddr_t data); static void wlpsetnwid(int unit, int base, int data); void wlpdump(int unit); static int read_ringbuf(int unit, int addr, char *buf, int len); #ifdef bsdi void wlpnotify(struct device *dev); #endif static void wlpread(int unit, int fd_p, int len); static void wl_cache_store(int unit, int base, struct ether_header *eh, struct mbuf *m); static int OldestEntryNdx( void ); /* diagnostic info */ struct wl_cntrs wlp_cntrs[NWLP]; /* * Keep interface structures separately so that the rest of the * kernel network code won't be messed up if the card is taken * out and the wlp_softc deallocated. Also remember if we've * ever called if_attach() on each interface, so as to avoid * doing it again after ejection and re-insertion. */ #if 0 struct arpcom wlp_ac[NWLP]; #endif int wlp_ifattached[NWLP]; #define WLPSOFTC(unit) ((struct wlp_softc *) &wlp_softc[unit]) #if 0 #define WLPIF(unit) (wlp_ac[unit].ac_if) #define WLPADDR(unit) (wlp_ac[unit].ac_enaddr) #else #define WLPIF(unit) (wlp_softc[unit].arpcom.ac_if) #define WLPADDR(unit) (wlp_softc[unit].arpcom.ac_enaddr) #endif #if NCARD > 0 #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int card_intr(struct pccard_devinfo *); /* Interrupt handler */ static void wlpunload(struct pccard_devinfo *); /* Disable driver */ static void wlpsuspend(struct pccard_devinfo *); /* Suspend driver */ static int wlpcrdinit(struct pccard_devinfo *, int); /* init device */ static struct pccard_device wlp_info = { "wlp", wlpcrdinit, wlpunload, card_intr, /* wlpsuspend, */ 0, /* Attributes - presently unused */ &net_imask /* Interrupt mask for device */ }; DATA_SET(pccarddrv_set, wlp_info); /* * Called when a power down is requested. Shuts down the * device and configures the device as unavailable (but * still loaded...). A resume is done by calling * wlpcrdinit with first=0. This is called when the user suspends * the system, or the APM code suspends the system. */ static void wlpsuspend(struct pccard_devinfo *devi) { struct wlp_softc *sc = &wlp_softc[devi->isahd.id_unit]; printf("wlp%d: suspending\n", devi->isahd.id_unit); sc->gone = 1; } /* * Initialize the device - called from Slot manager. * If first is set, then check for the device's existence * before initializing it. Once initialized, the device table may * be set up. */ static int wlpcrdinit(struct pccard_devinfo *devi, int first) { struct wlp_softc *sc = &wlp_softc[devi->isahd.id_unit]; /* validate unit number. */ if (first) { if (devi->isahd.id_unit >= NWLP) return(ENODEV); /* * Probe the device. If a value is returned, the * device was found at the location. */ sc->gone = 0; sc->wl_conf = *(struct wavelan_conf *)devi->misc; #if 0 if (wlpprobe(&devi->isahd,devi->misc)==0) return(ENXIO); #endif if (wlpattach(&devi->isahd)==0) return(ENXIO); } /* * XXX TODO: * If it was initialized before, the device structure * should also be initialized. We should * reset (and possibly restart) the hardware, but * I am not sure of the best way to do this... */ return(0); } /* * wlpunload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void wlpunload(struct pccard_devinfo *devi) { struct wlp_softc *sc = &wlp_softc[devi->isahd.id_unit]; struct ifnet *ifp = &sc->arpcom.ac_if; ifp->if_flags &= ~IFF_RUNNING; if_down(ifp); sc->gone = 1; printf("wlp%d: unload\n", devi->isahd.id_unit); } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(struct pccard_devinfo *devi) { wlpintr(devi->isahd.id_unit); return(1); } #endif /* NCARD > 0 */ /* * wlpprobe: * * This function "probes" or checks for the WaveLAN board on the bus to * see if it is there. It reads the PCMCIA Card Information Structure, * looking for the level 1 version/product info tuple. It will return * successfully only if the product information string begins with * "wavelan". Case is not important. The config code expects to see * a successful return from the probe routine before attach will be * called. * * input : address device is mapped to, and unit # being checked * output : a '1' is returned if the board exists, and a 0 otherwise * * CALLS: * pcmcia_card_name(); * bcmp(); */ #ifndef WL_PCCARD int pcmciaprobe (unsigned int, int); int pcmciaattach (unsigned int, int); #endif int wlpprobe(struct isa_device *id) { #ifndef WL_PCCARD unsigned long oldpri; unsigned char tuplebuf[80], *p; int len; int rc; extern int pcmcia_slot; int slot = pcmcia_slot; char card_name[256]; #endif /* WL_PCCARD */ #ifdef DEBUG printf("wlpprobe:%d base %x, irq %x, maddr %x\n", unit, base, id->id_irq+ id->id_maddr); /* ###ori### */ #endif #ifndef WL_PCCARD /* TBD make sure irq/maddr set */ rc = pcmciaprobe((unsigned int)id->id_maddr, 0); if (rc == 0) return(0); pcmciaattach((unsigned int)id->id_maddr, 0); if(pcmcia_card_name(slot, card_name, sizeof(card_name)) < 0){ printf("wlpprobe: cannot get card name\n"); return(0); } /* note: I have cards with both versions. jrb */ #define NAME1 "NCR~WaveLAN/PCMCIA~Version 1.01" #define NAME0 "NCR~WaveLAN/PCMCIA~Version 1.00" if(bcmp(card_name, NAME1, sizeof(NAME1)-1) != 0){ if(bcmp(card_name, NAME0, sizeof(NAME0)-1) != 0){ printf("wlpprobe: card name does not match\n"); printf("got %s\n", card_name); printf("expected %s\n", NAME0); return(0); } } #endif /* !WL_PCCARD */ #ifdef bsdi /* sanity check on irq would be useful. set in psa and used * by device presumably BUT fetching it would be useful * and comparing against device setup * TBD. jrb. * note. irq is id->id_irq, and iobase id->id_iobase */ base = ia->ia_iobase; if (isa_portcheck(base, WLP_NPORT) == 0) { return (0); } if (ia->ia_irq == IRQUNK) { if ((ia->ia_irq = isa_irqalloc(PCMCIA_IRQS)) == 0) { printf("wlp%d: no irq available\n", cf->cf_unit); return (0); } } ia->ia_iosize = WLP_NPORT; #endif #ifdef WL_PCCARD return 0; #else /* WL_PCCARD */ return (WLP_NPORT); #endif /* WL_PCCARD */ } /* * wlpattach: * * This function attaches a WaveLAN board to the "system". The rest of * runtime structures are initialized here (this routine is called after * a successful probe of the board). Once the ethernet address is read * and stored, the board's ifnet structure is attached and readied. * * input : isa_dev structure setup in autoconfig * output : board structs and ifnet is setup * * CALLS: * pcmcia_set_cor(); * pcic_map_memory(); * pcic_map_io(); */ int wlpattach(struct isa_device *id) { struct wlp_softc *sp = WLPSOFTC(id->id_unit); int unit = id->id_unit; int base = id->id_iobase; int please_init = 0; register struct ifnet *ifp = &WLPIF(unit); int i; int configured; #ifndef WL_PCCARD int comp_number, netw_addr; extern int pcmcia_slot; /* set in if_wlp_pcmcia.c */ int slot = pcmcia_slot; char tuplebuf[80], *p; int len; #endif sp->band_2400MHz = 0; #ifdef WAVELAN_PCMCIA_24 sp->band_2400MHz = 1; #endif /* WAVELAN_PCMCIA_24 */ if (id->id_flags & WLP_2400MHZ) { sp->band_2400MHz = 1; } sp->unit = unit; sp->base = base; sp->seated = 1; #ifdef DEBUG printf("wlpattach%d, base=%x\n", unit, base); #endif #ifdef WL_PCCARD sp->flags = 0; sp->mode = 0; configured = sp->wl_conf.wc_confstat & 1; sp->nwid[0] = sp->wl_conf.wc_nwid[0]; sp->nwid[1] = sp->wl_conf.wc_nwid[1]; for (i = 0; i < 6; i++) { WLPADDR(unit)[i] = sp->wl_conf.wc_macaddr[i]; } printf("wlp%d: nwid [%x:%x] mac:[%x:%x:%x:%x:%x:%x]\n", id->id_unit, sp->nwid[0], sp->nwid[1], WLPADDR(unit)[0], WLPADDR(unit)[1], WLPADDR(unit)[2], WLPADDR(unit)[3], WLPADDR(unit)[4], WLPADDR(unit)[5]); #else /* WL_PCCARD */ /* Tell the card it's configured by setting low bit of COR. */ /* Also does a software reset. */ if(pcmcia_set_cor(slot, 1) < 0) { return(0); } /* permanently map attribute memory */ pcic_map_memory(slot, 0, kvtop((char *)pcmcia_maddr(slot)), 0x0, 0x4000, ATTRIBUTE, 1); sp->psa = (char *)pcmcia_maddr(slot); sp->psa += 0xe00; /* Permanently map the I/O registers. */ pcic_map_io(slot, 0, base, WLP_NPORT, 1); sp->flags = 0; sp->mode = 0; configured = READ_PSA(PSA_CONF_STATUS) & 1; sp->nwid[0] = READ_PSA(NETW_ID); sp->nwid[1] = READ_PSA(NETW_ID+1); if(configured) { if(READ_PSA(PSA_MAC_SELECT) & 1) { netw_addr = NETW_ADDR_LOCAL; } else { netw_addr = NETW_ADDR; /* use factory assigned address */ } } else { netw_addr = NETW_ADDR; } for(i=0; i < WAVELAN_ADDR_SIZE; ++i) { WLPADDR(unit)[i] = READ_PSA(netw_addr+i); } printf("wlp%d: nwid [%x:%x] mac:[%x:%x:%x:%x:%x:%x]\n", id->id_unit, sp->nwid[0], sp->nwid[1], WLPADDR(unit)[0], WLPADDR(unit)[1], WLPADDR(unit)[2], WLPADDR(unit)[3], WLPADDR(unit)[4], WLPADDR(unit)[5]); comp_number = READ_PSA(PSA_COMP_NUMBER); if(comp_number & 1) printf("PC-MC "); else if(comp_number & 4) printf("PCMCIA "); else printf("PC-AT "); if(comp_number & 2) { switch(READ_PSA(PSA_SUBBAND) & 15) { case 1: printf("2425"); break; case 2: printf("2460"); break; case 3: printf("2484"); break; case 4: printf("2430.5"); break; default: printf("???"); } } else { if (sp->band_2400MHz) { printf("2400"); } else { printf("915"); } } printf(" MHz\n"); #endif /* WL_PCCARD */ /* turn on the power and check that the modem is connected before we attempt to access the Modem Management Interface */ outb(HACR(base), HACR_PWR_STAT); if(wlp_ifattached[unit] == 0){ ifp->if_softc = sp; ifp->if_flags = IFF_BROADCAST|IFF_SIMPLEX; #ifdef MULTICAST ifp->if_flags |= IFF_MULTICAST; #endif #ifdef DEBUG ifp->if_flags |= IFF_DEBUG; #endif ifp->if_unit = sp->unit; ifp->if_name = "wlp"; ifp->if_output = ether_output; ifp->if_start = wlpstart; ifp->if_ioctl = wlpioctl; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; if_attach(ifp); ether_ifattach(ifp); wlp_ifattached[unit] = 1; #if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif } else { /* We've already seen this card; just reset it. */ please_init = 1; } #ifdef bsdi sp->wlp_ih.ih_fun = wlpintr; sp->wlp_ih.ih_arg = (void *) sp; pcmcia_attach_child(parent, self, &sp->wlp_ih, ia, wlpnotify, &wlpcd); #endif #ifndef WL_PCCARD /* tell pcmcia controller to map irq */ pcic_map_irq(slot, ffs(id->id_irq) - 1); #endif /* WL_PCCARD */ if(please_init) { wlpinit(unit); } return(1); } #ifdef bsdi /* * The card has been removed. Soon the wlp_softc structure won't * be available; stop using it. But the ifnet structure isn't * part of wlp_softc in this driver, so we can leave it alone. * Thus when the card is re-inserted, it will start working * where it left off. */ void wlpnotify(struct device *dev) { int unit = dev->dv_unit; struct wlp_softc *sp = WLPSOFTC(unit); struct ifnet *ifp = &WLPIF(unit); #ifdef DEBUG if (xtradebug) printf("wlp%d: wlpnotify()\n", unit); #endif isa_portfree(sp->base, WLP_NPORT); } #endif /* * initialize rf-modem * * CALLERS: wlphwrst */ static void wlpinitmmi(int unit) { struct wlp_softc *sp = WLPSOFTC(unit); int configured; #ifdef WL_PCCARD configured = sp->wl_conf.wc_confstat & 1; #else /* WL_PCCARD */ configured = READ_PSA(PSA_CONF_STATUS) & 1; #endif /* WL_PCCARD */ /* Set default modem control parameters. Taken from NCR document 407-0024326 Rev. A */ MMI_WRITE(MMC_JABBER_ENABLE, 0x01); MMI_WRITE(MMC_ANTEN_SEL, 0x02); MMI_WRITE(MMC_IFS, 0x20); MMI_WRITE(MMC_MOD_DELAY, 0x04); MMI_WRITE(MMC_JAM_TIME, 0x38); MMI_WRITE(MMC_DECAY_PRM, 0x00); /* obsolete ? */ MMI_WRITE(MMC_DECAY_UPDAT_PRM, 0x00); if(!configured) { MMI_WRITE(MMC_LOOPT_SEL, 0x00); MMI_WRITE(MMC_THR_PRE_SET, 0x03); /* 0x03 for PCMCIA */ MMI_WRITE(MMC_QUALITY_THR, 0x03); } else { /* use configuration defaults from parameter storage area */ #ifdef WL_PCCARD if(sp->wl_conf.wc_netw_id_sel & 1) { MMI_WRITE(MMC_LOOPT_SEL, 0x00); } else { MMI_WRITE(MMC_LOOPT_SEL, 0x40); /* disable network id check */ } MMI_WRITE(MMC_THR_PRE_SET, sp->wl_conf.wc_thr_pre_set & 0x3f); MMI_WRITE(MMC_QUALITY_THR, sp->wl_conf.wc_quality_thr & 0x0f); #else /* WL_PCCARD */ if(READ_PSA(PSA_NETW_ID_SELECT) & 1) { MMI_WRITE(MMC_LOOPT_SEL, 0x00); } else { MMI_WRITE(MMC_LOOPT_SEL, 0x40); /* disable network id check */ } MMI_WRITE(MMC_THR_PRE_SET, READ_PSA(PSA_THR_PRE_SET) & 0x3f); MMI_WRITE(MMC_QUALITY_THR, READ_PSA(PSA_QUALITY_THR) & 0x0f); #endif /* WL_PCCARD */ } MMI_WRITE(MMC_FREEZE, 0x00); MMI_WRITE(MMC_ENCR_ENABLE, 0x00); MMI_WRITE(MMC_NETW_ID_L,sp->nwid[1]); /* set NWID */ MMI_WRITE(MMC_NETW_ID_H,sp->nwid[0]); if (sp->band_2400MHz) { /* * Start the modem's receive unit on version 2.00 * frequency select cards. It is based on Joe Finney's * (joe@comp.lancs.ac.uk) code for Linux. */ MMI_WRITE(0x21, 0x0f); MMI_WRITE(0x20, 0x0e); DELAY(100000); MMI_WRITE(0x21, 0x61); MMI_WRITE(0x20, 0x0e); DELAY(100000); } sp->mmi_inited = TRUE; } u_short wlpmmiread(int base, u_short reg) { while(inb(HASR(base)) & HASR_MMI_BUSY) ; outb(MMR(base),reg << 1); outb(MMD(base),0); /* dummy write */ while(inb(HASR(base)) & HASR_MMI_BUSY) ; return (u_short)inb(MMD(base)); } /* * wlpinit: * * Another routine that interfaces the "if" layer to this driver. * Simply resets the structures that are used by "upper layers". * As well as calling wlphwrst that does reset the WaveLAN board. * * input : board number * output : structures (if structs) and board are reset * */ static void wlpinit(int unit) { struct wlp_softc *sp = WLPSOFTC(unit); struct ifnet *ifp; int stat; unsigned long oldpri; #ifdef DEBUG printf("wlpinit\n"); #endif /* The card might have been ejected. */ if(sp == 0) return; /* initialize the signal cache */ NextCacheItem = 0; w_sigitems = 0; ifp = &WLPIF(unit); if (ifp->if_addrlist == (struct ifaddr *)0) { return; } #ifdef DEBUG if (ifp->if_flags & IFF_DEBUG) { printf("wl%d: entered wlpinit() \n",unit); } #endif if ((stat = wlphwrst(unit)) == TRUE) { #ifdef TIMER WLPIF(unit).if_timer = 60; #endif oldpri = splimp(); WLPIF(unit).if_flags |= IFF_RUNNING; sp->flags |= DSF_RUNNING; sp->tbusy = FALSE; WLPIF(unit).if_flags &= ~IFF_OACTIVE; sp->cmd_request = FALSE; sp->cmd_wait = FALSE; sp->deferred = FALSE; wlpstart(ifp); splx(oldpri); } else printf("wl%d init(): trouble resetting board.\n", unit); #ifdef DEBUG printf("END wlpinit\n"); #endif } /* * wlphwrst: * * This routine resets the WaveLAN board that corresponds to the * board number passed in. * * input : board number to do a hardware reset * output : board is reset * */ int wlphwrst(int unit) { int i; struct wlp_softc *sp = WLPSOFTC(unit); int base = sp->base; #ifdef DEBUG printf("wl%d: entered wlphwrst()\n",unit); #endif /* reset host adapter */ outb(HACR(base), HACR_PWR_STAT | HACR_TX_DMA_RESET | HACR_RX_DMA_RESET); sp->power_down = FALSE; if(inb(HASR(base)) & HASR_NO_CLK) { printf("wl%d: modem not connected.\n", unit); return FALSE; } outb(LCCR(base), OP0_RESET); /* reset the LAN controller */ DELAY(100000); /* initialize modem */ #ifdef DEBUG printf("wlpinitmm\n"); #endif wlpinitmmi(unit); #ifdef DEBUG printf("END wlpinitmm, START wlpconfig\n"); #endif if (wlpconfig(unit) == FALSE) return(FALSE); /* * insert code for loopback test here * */ #ifdef DEBUG printf("start wlprustrt\n"); #endif wlprustrt(unit); /* start receive unit */ #ifdef DEBUG printf("END wlprustrt/wlphwrst()\n"); #endif return(TRUE); } /* * wlpstart: * * This is yet another interface routine that simply tries to output a * in a packet after a reset. * * input : board number * output : stuff sent to board if any there * */ static void wlpstart(struct ifnet *ifp) { struct mbuf *m; int unit = ifp->if_unit; struct wlp_softc *is = WLPSOFTC(unit); int base; #ifdef DEBUG if (ifp->if_flags & IFF_DEBUG) { printf("wl%d: entered wlpstart() qlen %d\n", unit, ifp->if_snd.ifq_len); } #endif /* The card might have been ejected. */ if(is == 0) return; base = is->base; /* XXX the following is a debug statement, remove for efficiency */ #ifdef undef if(is->cmd_request) { printf("wlpstart called with cmd_request=%d deferred=%d\n", is->cmd_request,is->deferred); } #endif if (is->tbusy || is->power_down) return; IF_DEQUEUE(&ifp->if_snd, m); if (m != (struct mbuf *) 0) { is->tbusy = TRUE; WLPIF(unit).if_flags |= IFF_OACTIVE; wlp_cntrs[unit].xmt.xmt++; ifp->if_opackets++; #if NBPFILTER > 0 if (ifp->if_bpf) { bpf_mtap(ifp, m); } #endif wlpxmt(unit, m); } return; } /* return the starting address of the frame pointed to by the receive frame pointer */ static int wlp_start_of_frame(int unit,int rfp) { struct wlp_softc *sp = WLPSOFTC(unit); int base = sp->base; int rp, len, len1, len_ptr; unsigned char c; rp = (rfp - 5 + RX_SIZE) % RX_SIZE; outb(PIORL(base), rp & 0xff); outb(PIORH(base), ((rp >> 8) & PIORH_MASK)); len = inb(PIOP(base)); len |= inb(PIOP(base)) << 8; len_ptr = rp; len_ptr = read_ringbuf(unit, len_ptr, &c, 1); len1 = c; len_ptr = read_ringbuf(unit, len_ptr, &c, 1); len1 |= c << 8; if(len != len1 || len > 1600) printf("oops, rfp %d rp %d len 0x%x len1 0x%x\n", rfp, rp, len, len1); if(len > 1600) return(-1); else return (rp - len + RX_SIZE) % RX_SIZE; } /* * wlprcv: * * This routine is called by the interrupt handler to initiate a * packet transfer from the board to the "if" layer above this * driver. This routine checks if a buffer has been successfully * received by the WaveLAN. If so, the routine wlpread is called * to do the actual transfer of the board data (including the * ethernet header) into a packet (consisting of an mbuf chain). * * input : number of the board to check * output : if a packet is available, it is "sent up" * */ static void wlprcv(int unit) { struct wlp_softc *sp = WLPSOFTC(unit); int base = sp->base; int newrfp, rp, len, f_start, status; int i593_rfp, stat_ptr; unsigned char c; int sanity; #ifdef DEBUG if (xtradebug) printf("wl%d: entered wlprcv()\n",unit); #endif /* get the new receive frame pointer from the i82593 chip */ outb(LCCR(base), CR0_STATUS_2 | OP0_NOP); i593_rfp = inb(LCSR(base)); i593_rfp |= inb(LCSR(base)) << 8; i593_rfp %= RX_SIZE; /* Get the new receive frame pointer from the WaveLAN host adapter. * It is 3 bytes more than te increment of the i82593 receive * frame pointer, for each packet. */ newrfp = inb(RPLL(base)); newrfp |= inb(RPLH(base)) << 8; newrfp %= RX_SIZE; #ifdef WISHIKNEWWHY if(wlp_overrunning || newrfp == sp->rfp){ printf("wl%d: odd RFPs:\n", unit); printf(" i593_rfp %d stop %d newrfp %d sp->rfp %d\n", i593_rfp, sp->stop, newrfp, sp->rfp); } #endif while(newrfp != sp->rfp) { rp = newrfp; /* find first frame */ sanity = 0; while ((f_start = wlp_start_of_frame(unit,rp)) != sp->rfp){ if(f_start == -1 || sanity++ > 200){ #if 0 printf("wlp%d: cannot find start of frame\n", unit); printf(" i593_rfp %d stop %d newrfp %d sp->rfp %d\n", i593_rfp, sp->stop, newrfp, sp->rfp); #endif return; } rp = f_start; } stat_ptr = (rp - 7 + RX_SIZE) % RX_SIZE; stat_ptr = read_ringbuf(unit, stat_ptr, &c, 1); status = c; stat_ptr = read_ringbuf(unit, stat_ptr, &c, 1); status |= c << 8; stat_ptr = read_ringbuf(unit, stat_ptr, &c, 1); len = c; stat_ptr = read_ringbuf(unit, stat_ptr, &c, 1); len |= c << 8; if(!(status & RX_RCV_OK)) { if(status & RX_NO_SFD) ++wlp_cntrs[unit].rcv.frame; if(status & RX_CRC_ERR) ++wlp_cntrs[unit].rcv.crc; if(status & RX_OVRRUN) ++wlp_cntrs[unit].rcv.ovrnerrs; #if 0 printf("wl%d: packet not received ok, status = %x\n",unit,status); #endif WLPIF(unit).if_ierrors++; } else { WLPIF(unit).if_ipackets++; wlpread(unit, f_start, len - 2); } sp->rfp = rp; } /* * Update the frame stop register, but set it to less than * the full 8K to allow space for 3 bytes of signal strength * per packet. */ sp->stop = (i593_rfp + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; outb(LCCR(base), OP0_SWIT_TO_PORT_1 | CR0_CHNL); outb(LCCR(base), CR1_STOP_REG_UPDATE | (sp->stop >> RX_SIZE_SHIFT)); outb(LCCR(base), OP1_SWIT_TO_PORT_0); } /* * wlpread: * * This routine does the actual copy of data (including ethernet header * structure) from the WaveLAN to an mbuf chain that will be passed up * to the "if" (network interface) layer. NOTE: we currently * don't handle trailer protocols, so if that is needed, it will * (at least in part) be added here. The contents of the receive * buffer are copied to an message chain that is then enqueued onto * the appropriate "if" queue. * * input : board number, and an frame descriptor address * output : the packet is put into an ipc_kmsg, and passed up * assumes : if any errors occur, packet is "dropped on the floor" * */ static void wlpread(int unit, int fd_p, int len) { struct wlp_softc *is = WLPSOFTC(unit); register struct ifnet *ifp = &WLPIF(unit); struct mbuf *m, *tm; int base = is->base; u_char *mb_p; u_short mlen; u_short bytes_in_msg, bytes_in_mbuf, bytes; struct ether_header eh; /* tbd. jrb. not initialized */ bytes_in_msg = len - sizeof(struct ether_header); #ifdef DEBUG if (xtradebug) printf("wl%d: entered wlpread() fd_p 0x%x len %d\n", unit, fd_p, len); #endif if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { printf("wl%d read(): board is not running.\n", ifp->if_unit); /* XXX turn off interrupts? */ } wlp_cntrs[unit].rcv.rcv++; /* * read out ether header. note: separate read from rest * of data */ fd_p = read_ringbuf(unit, fd_p, (char *) &eh, sizeof(eh)); #ifdef DEBUG if (xtradebug) { eh.ether_type = ntohs(eh.ether_type); printf("wlpread: got packet, ether type %x\n", eh.ether_type); eh.ether_type = htons(eh.ether_type); } #endif #ifdef DUMPSIGNAL printf("mac %s: signal %x silence %x quality %x\n", ether_sprintf(eh.ether_shost), wlpmmiread(base, MMC_SIGNAL_LVL) & 0x3f, wlpmmiread(base, MMC_SILENCE_LVL) & 0x3f, wlpmmiread(base, MMC_SIGN_QUAL) & 0x0f); #endif MGETHDR(m, M_DONTWAIT, MT_DATA); tm = m; if (m == (struct mbuf *)0) { /* * not only do we want to return, we need to drop the packet on * the floor to clear the interrupt. */ /* return 1; */ return; } m->m_next = (struct mbuf *) 0; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = len - sizeof(struct ether_header); m->m_len = MHLEN; if (bytes_in_msg >= MINCLSIZE) { MCLGET(m, M_DONTWAIT); if (m->m_flags & M_EXT) m->m_len = MCLBYTES; #ifdef undef else { m_freem(m); return; } #endif } mlen = 0; bytes_in_mbuf = m->m_len; /* bytes_in_msg = len - sizeof(struct ether_header); */ mb_p = mtod(tm, u_char *); do { bytes = min(bytes_in_mbuf, bytes_in_msg); fd_p = read_ringbuf(unit, fd_p, mb_p, bytes); mlen += bytes; if (!(bytes_in_msg -= bytes)) { tm->m_len = mlen; break; } MGET(tm->m_next, M_DONTWAIT, MT_DATA); tm = tm->m_next; if (tm == (struct mbuf *)0) { m_freem(m); printf("wl%d read(): No mbuf nth\n", unit); /* return 0; */ return; } mlen = 0; tm->m_len = MLEN; bytes_in_mbuf = MLEN; mb_p = mtod(tm, u_char *); } while(1); #ifdef IF_CNTRS wlp_ifcntrs.pkt_ein[log_2(len)]++; if (len < 128) wlp_ifcntrs.pkt_lin[len>>3]++; if (ehp->ether_type == ETHERTYPE_ARP) { wlp_ifcntrs.pkt_arp++; if (pkt_narp) { wlp_ifcntrs.pkt_ein[log_2(len)]--; if (len < 128) wlp_ifcntrs.pkt_lin[len>>3]--; } } #endif IF_CNTRS if(gathersnr) { u_char siglvl, sillvl, sigqual; /* skip status and len fields */ fd_p = read_ringbuf(unit, fd_p, NULL, 4); /* read signal level, silence level and signal quality bytes */ fd_p = read_ringbuf(unit, fd_p, &siglvl, 1); fd_p = read_ringbuf(unit, fd_p, &sillvl, 1); fd_p = read_ringbuf(unit, fd_p, &sigqual, 1); wlpgetsnr(unit,&eh,siglvl,sillvl,sigqual); } #if NBPFILTER > 0 if (WLPIF(unit).if_bpf) { struct mbuf m0; m0.m_len = sizeof eh; m0.m_data = (caddr_t) &eh; m0.m_next = m; bpf_mtap(&WLPIF(unit), &m0); /* * Note that the interface cannot be in promiscuous mode if * there are no BPF listeners. And if we are in promiscuous * mode, we have to check if this packet is really ours. * * logic: if promiscuous mode AND not multicast/bcast AND * not to us, throw away */ if ((WLPIF(unit).if_flags & IFF_PROMISC) && (eh.ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */ bcmp(eh.ether_dhost, WLPADDR(unit), sizeof(eh.ether_dhost)) != 0 ) { m_freem(m); return; } } #endif wl_cache_store(unit, base, &eh, m); /* * Hand the packet to the Network Module */ ether_input(&WLPIF(unit), &eh, m); /* return 1; */ return; } /* Read len bytes from ring buffer, starting at address addr. * Result is stored in buf. Return value is addr to be used for next call. */ static int read_ringbuf(int unit, int addr, char *buf, int len) { struct wlp_softc *is = WLPSOFTC(unit); int base = is->base; int i; static int ring_ptr=0; int chunk_len; char *buf_ptr; /* if buf is NULL, just increment the ring buffer pointer */ if(buf == NULL) return (ring_ptr - RX_BASE + len) % RX_SIZE + RX_BASE; buf_ptr = buf; ring_ptr = addr; while(len > 0) { /* position Program I/O Register at ring buffer pointer */ outb(PIORL(base), ring_ptr & 0xff); outb(PIORH(base), ((ring_ptr >> 8) & PIORH_MASK)); /* determine how much we can read without wrapping around */ if(addr + len < RX_BASE + RX_SIZE) chunk_len = len; else chunk_len = RX_BASE + RX_SIZE - addr; linb(PIOP(base), buf_ptr, chunk_len); buf_ptr += chunk_len; len -= chunk_len; ring_ptr = (ring_ptr - RX_BASE + chunk_len) % RX_SIZE + RX_BASE; } return ring_ptr; } /* * wlpwatch(): * * This routine is the watchdog timer routine for the WaveLAN chip. If * chip wedges, this routine will fire and cause a board reset and * begin again. * * input : which board is timing out * output : potential board reset if wedged * */ void wlpwatch(struct ifnet *ifp) { int opri; int unit = ifp->if_unit; if ((ifp->if_flags & IFF_UP) == 0) { return; } ifp->if_timer = 20; wlp_cntrs[unit].watch++; opri = splimp(); wlphwrst(unit); splx(opri); } /* Gracefully turn off reception, and wait for any commands to complete */ static void wlp_graceful_shutdown(int unit) { struct wlp_softc *sp = WLPSOFTC(unit); int base = sp->base; int opri, status; wlpcmd(unit, "wlp_graceful_shutdown(): stop-rcv", OP0_STOP_RCV, SR0_NO_RESULT); /* spin until the receive unit is idle */ do { opri = splimp(); outb(LCCR(base), OP0_NOP | CR0_STATUS_3); status = inb(LCSR(base)); splx(opri); } while((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE); sp->cmd_request = TRUE; /* signal that we want to issue a command */ /* spin until the chip finished executing any current command */ do { opri = splimp(); outb(LCCR(base), OP0_NOP | CR0_STATUS_3); status = inb(LCSR(base)); splx(opri); } while ((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE); sp->cmd_request = FALSE; } /* * wlprustrt: * * This routine starts the receive unit running. First checks if the * board is actually ready, then the board is instructed to receive * packets again. * */ void wlprustrt(unit) int unit; { struct wlp_softc *sp = WLPSOFTC(unit); int base = sp->base; #ifdef DEBUG if (xtradebug) printf("wl%d: entered wlprustrt()\n",unit); #endif /* XXX check if board is running here? */ /* First disable reception in case it was already enabled */ wlp_graceful_shutdown(unit); /* We now know that no command is being executed. */ /* set the receive frame pointer and stop pointer */ sp->rfp = 0; outb(LCCR(base), OP0_SWIT_TO_PORT_1 | CR0_CHNL); /* Reset ring management. This sets the receive frame pointer to 1 */ outb(LCCR(base), OP1_RESET_RING_MNGMT); #if 0 /* XXX the manual page 6-4 seems to indicate that the stop register should be set as below */ /* outb(LCCR(base), CR1_STOP_REG_UPDATE | ((RX_SIZE - 0x40) >> RX_SIZE_SHIFT)); */ #elif 0 /* but I set it 0 instead */ sp->stop = 0; #else /* but I set it to 3 bytes per packet less than 8K */ sp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; #endif outb(LCCR(base), CR1_STOP_REG_UPDATE | (sp->stop >> RX_SIZE_SHIFT)); outb(LCCR(base), OP1_INT_ENABLE); outb(LCCR(base), OP1_SWIT_TO_PORT_0); /* reset receive DMA pointer */ outb(HACR(base), HACR_PWR_STAT | HACR_RX_DMA_RESET); DELAY(1000); outb(HACR(base), HACR_PWR_STAT); DELAY(1000); /* receive DMA on channel 1 */ wlpcmd(unit, "wlprustrt(): rcv-enable", CR0_CHNL | OP0_RCV_ENABLE, SR0_NO_RESULT); return; } /* * wlpdiag: * * This routine does a 593 op-code number 7, and obtains the * diagnose status for the WaveLAN. * */ int wlpdiag(unit) int unit; { struct wlp_softc *sp = WLPSOFTC(unit); int base = sp->base; #ifdef DEBUG printf("wl%d: entered wlpdiag()\n",unit); #endif if(wlpcmd(unit, "wlpdiag(): diagnose", OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED)) return TRUE; printf("wl%d: i82593 Self Test failed!\n", unit); return FALSE; } /* * wlpconfig: * * This routine does a standard config of the WaveLAN board. * */ #ifdef OLD int wlpconfig(int unit) { struct wlp_softc *is = WLPSOFTC(unit); int base = is->base; register struct ifnet *ifp = &WLPIF(unit); #if MULTICAST int cnt = 0; struct ether_multi *enm; struct ether_multistep step; #endif MULTICAST #ifdef DEBUG printf("wl%d: wlpconfig(), size %d\n", unit, sizeof(struct i82593_conf_block)); #endif bzero(&is->cfblk, sizeof(struct i82593_conf_block)); is->cfblk.d6mod = FALSE; /* Run in 82593 advanced mode */ if (ifp->if_flags & IFF_LINK0) is->cfblk.loopback = 1; /* internal loopback */ is->cfblk.fifo_limit = 6; /* = 48 bytes rx and tx fifo thresholds */ is->cfblk.forgnesi = FALSE; /* 0=82C501, 1=AMD7992B compatibility */ is->cfblk.fifo_32 = 0; is->cfblk.throttle_enb = TRUE; is->cfblk.contin = TRUE; /* enable continous mode */ is->cfblk.addr_len = WAVELAN_ADDR_SIZE; is->cfblk.preamb_len = 2; /* 7 byte preamble */ is->cfblk.lin_prio = 0; /* conform to 802.3 backoff algoritm */ is->cfblk.exp_prio = 0; /* conform to 802.3 backoff algoritm */ is->cfblk.bof_met = 0; /* conform to 802.3 backoff algoritm */ is->cfblk.ifrm_spc = 6; /* 96 bit times interframe spacing */ is->cfblk.slottim_low = 0x10 & 0x7; /* 512 bit times slot time */ is->cfblk.slottim_hi = 0x10 >> 3; is->cfblk.max_retr = 15; if(WLPIF(unit).if_flags & (IFF_PROMISC|IFF_LINK1)) is->cfblk.prmisc = TRUE; else is->cfblk.prmisc = FALSE; is->cfblk.crs_1 = TRUE; /* Transmit without carrier sense */ is->cfblk.nocrc_ins = FALSE; /* 82593 generates CRC */ is->cfblk.crc_1632 = FALSE; /* 32-bit Autodin-II CRC */ is->cfblk.crs_cdt = FALSE; /* CD not to be interpreted as CS */ is->cfblk.cs_filter = 0; /* CS is recognized immediately */ is->cfblk.crs_src = FALSE; /* External carrier sense */ is->cfblk.cd_filter = 0; /* CD is recognized immediately */ is->cfblk.min_fr_len = /* Minimum frame length 64 bytes */ (sizeof(struct ether_header) + ETHERMIN + sizeof(long)) >> 2; is->cfblk.lng_typ = FALSE; /* Length field > 1500 = type field */ is->cfblk.lng_fld = TRUE; /* Disable 802.3 length field check */ is->cfblk.rxcrc_xf = TRUE; /* Don't transfer CRC to memory */ is->cfblk.artx = TRUE; /* Disable automatic retransmission */ is->cfblk.sarec = TRUE; /* Disable source addr trig of CD */ is->cfblk.tx_jabber = TRUE; /* Disable jabber jam sequence */ is->cfblk.hash_1 = FALSE; /* Use bits 0-5 in mc address hash */ is->cfblk.lbpkpol = TRUE; /* Loopback pin active high */ is->cfblk.fdx = FALSE; /* Disable full duplex operation */ is->cfblk.dummy_6 = 0x3f; /* all ones */ is->cfblk.mult_ia = FALSE; /* No multiple individual addresses */ is->cfblk.dis_bof = FALSE; /* Disable the backoff algorithm ?! */ is->cfblk.dummy_1 = TRUE; /* set to 1 */ is->cfblk.tx_ifs_retrig = 3; /* Hmm... Disabled */ #if MULTICAST #if 0 if(WLPIF(unit).if_flags & IFF_ALLMULTI) is->cfblk.mc_all = TRUE; else is->cfblk.mc_all = FALSE; #else /* TBD */ is->cfblk.mc_all = TRUE; #endif #endif MULTICAST is->cfblk.rcv_mon = 0; /* Monitor mode disabled */ is->cfblk.frag_acpt = FALSE; /* Do not accept fragments */ is->cfblk.tstrttrs = FALSE; /* No start transmission threshold */ is->cfblk.fretx = TRUE; /* FIFO automatic retransmission */ is->cfblk.syncrqs = TRUE; /* Synchronous DRQ deassertion... */ is->cfblk.sttlen = TRUE; /* 6 byte status registers */ is->cfblk.rx_eop = TRUE; /* Signal EOP on packet reception */ is->cfblk.tx_eop = TRUE; /* Signal EOP on packet transmission */ is->cfblk.rbuf_size = RX_SIZE>>11; /* Set receive buffer size */ is->cfblk.rcvstop = TRUE; /* Enable Receive Stop Register */ outb(PIORL(base), TX_BASE & 0xff); outb(PIORH(base), ((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX); outb(PIOP(base),sizeof(struct i82593_conf_block) & 0xff); /* lsb */ outb(PIOP(base),sizeof(struct i82593_conf_block) >> 8); /* msb */ loutb(PIOP(base), &is->cfblk, sizeof(struct i82593_conf_block)); /* reset transmit DMA pointer */ outb(HACR(base), HACR_PWR_STAT | HACR_TX_DMA_RESET); DELAY(1000); outb(HACR(base), HACR_PWR_STAT); DELAY(1000); if(!wlpcmd(unit, "wlpconfig(): configure", OP0_CONFIGURE, SR0_CONFIGURE_DONE)) return FALSE; #if 0 #ifdef ORIGINAL_CONFIG /* * below is the default board configuration from p2-28 from 586 book */ configure.fifolim_bytecnt = 0x080c; configure.addrlen_mode = 0x2600; configure.linprio_interframe = 0x7820; /* IFS=120, ACS=2 */ configure.slot_time = 0xf00c; /* slottime=12 */ configure.hardware = 0x0008; /* tx even w/o CD */ configure.min_frame_len = 0x0040; #else /* This is the configuration block suggested by Marc Meertens * in an e-mail message to John * Ioannidis on 10 Nov 92. */ configure.fifolim_bytecnt = 0x040c; configure.addrlen_mode = 0x0600; configure.linprio_interframe = 0x2060; configure.slot_time = 0xf000; configure.hardware = 0x0008; /* tx even w/o CD */ configure.min_frame_len = 0x0040; #endif ORIGINAL_CONFIG #endif 0 #if MULTICAST outb(PIORL(base), (TX_BASE + 2) & 0xff); outb(PIORH(base), (((TX_BASE + 2) >> 8) & PIORH_MASK) | PIORH_SEL_TX); ETHER_FIRST_MULTI(step, &wlp_ac[unit], enm); while (enm != NULL) { unsigned int lo, hi; lo = (enm->enm_addrlo[3] << 16) + (enm->enm_addrlo[4] << 8) + enm->enm_addrlo[5]; hi = (enm->enm_addrhi[3] << 16) + (enm->enm_addrhi[4] << 8) + enm->enm_addrhi[5]; while(lo <= hi) { outb(PIOP(base),enm->enm_addrlo[0]); outb(PIOP(base),enm->enm_addrlo[1]); outb(PIOP(base),enm->enm_addrlo[2]); outb(PIOP(base),lo >> 16); outb(PIOP(base),(lo >> 8) & 0xff); outb(PIOP(base),lo & 0xff); ++cnt; ++lo; } ETHER_NEXT_MULTI(step, enm); } /* write the size of the multicast address area */ outb(PIORL(base), TX_BASE & 0xff); outb(PIORH(base), ((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX); outb(PIOP(base), (cnt * WAVELAN_ADDR_SIZE) & 0xff); outb(PIOP(base), (cnt * WAVELAN_ADDR_SIZE) >> 8); /* reset transmit DMA pointer */ outb(HACR(base), HACR_PWR_STAT | HACR_TX_DMA_RESET); if(!wlpcmd(unit, "wlpconfig(): mc-setup", OP0_MC_SETUP, SR0_MC_SETUP_DONE)) { return FALSE; } #endif MULTICAST outb(PIORL(base), TX_BASE & 0xff); outb(PIORH(base), ((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX); outb(PIOP(base), WAVELAN_ADDR_SIZE); /* byte count lsb */ outb(PIOP(base), 0); /* byte count msb */ loutb(PIOP(base), WLPADDR(unit), WAVELAN_ADDR_SIZE); /* reset transmit DMA pointer */ outb(HACR(base), HACR_PWR_STAT | HACR_TX_DMA_RESET); DELAY(1000); outb(HACR(base), HACR_PWR_STAT); DELAY(1000); if(!wlpcmd(unit, "wlpconfig(): ia-setup", OP0_IA_SETUP, SR0_IA_SETUP_DONE)) { return FALSE; } return TRUE; } #else #define matt int wlpconfig(int unit) { struct wlp_softc *sc = WLPSOFTC(unit); int base = sc->base; register struct ifnet *ifp = &WLPIF(unit); #ifdef MULTICAST int cnt = 0; struct ether_multi *enm; struct ether_multistep step; #endif #ifdef DEBUG printf("wl%d: wlpconfig(), size %d\n", unit, sizeof(struct i82593_conf_block)); #endif bzero(&sc->cfblk, sizeof(struct i82593_conf_block)); /* byte 0 -- 0x25 (0x86??) XXX */ sc->cfblk.forgnesi = FALSE; /* 0=82C501, 1=AMD7992B compatibility */ sc->cfblk.d6mod = FALSE; /* Run in 82593 advanced mode */ #ifdef matt sc->cfblk.fifo_limit = 5; /* = 32 byte rx and tx fifo thresholds */ sc->cfblk.fifo_32 = 1; sc->cfblk.throttle_enb = FALSE; #else sc->cfblk.fifo_limit = 6; /* = 48 byte rx and tx fifo thresholds */ sc->cfblk.fifo_32 = 0; sc->cfblk.throttle_enb = TRUE; #endif /* byte 1 -- 0x80 (OK) */ sc->cfblk.contin = TRUE; /* enable continous mode */ /* byte 2 -- 0x0E (0x26??) XXX */ sc->cfblk.addr_len = WAVELAN_ADDR_SIZE; #ifdef matt sc->cfblk.acloc = FALSE; #if 0 sc->cfblk.acloc = TRUE; #endif sc->cfblk.preamb_len = 0; /* 1 (2^(n+1) - 1) byte preamble */ #else sc->cfblk.acloc = FALSE; sc->cfblk.preamb_len = 2; /* 7 (2^(n+1) - 1) byte preamble */ #endif if (ifp->if_flags & IFF_LOOPBACK) sc->cfblk.loopback = 1; /* internal loopback */ /* byte 3 -- 0xD0 (0x00??) XXX */ sc->cfblk.lin_prio = 0; /* conform to 802.3 backoff algoritm */ #ifdef matt sc->cfblk.exp_prio = 5; /* conform to 802.3 backoff algoritm */ sc->cfblk.bof_met = 1; /* conform to 802.3 backoff algoritm */ #else sc->cfblk.exp_prio = 0; /* conform to 802.3 backoff algoritm */ sc->cfblk.bof_met = 0; /* conform to 802.3 backoff algoritm */ #endif /* byte 4 -- 0x20 (0x60??) XXX */ #ifdef matt sc->cfblk.ifrm_spc = 2; /* 32 bit times interframe spacing */ #else sc->cfblk.ifrm_spc = 6; /* 96 bit times interframe spacing */ #endif /* byte 5 -- 0x20 (0x00??) XXX */ /* byte 6 -- 0xF0 (0xF2??) XXX */ #ifdef matt sc->cfblk.slottim_low = (0x20 >> 5); /* 64 bit times slot time */ sc->cfblk.slottim_hi = (0x20 >> 8); #else sc->cfblk.slottim_low = (0x200 >> 5); /* 512 bit times slot time */ sc->cfblk.slottim_hi = (0x200 >> 8); #endif sc->cfblk.max_retr = 15; /* byte 7 -- 0x08 (OK) */ sc->cfblk.prmisc = (WLPIF(unit).if_flags & IFF_PROMISC) != 0; sc->cfblk.crs_1 = TRUE; /* Transmit without carrier sense */ sc->cfblk.nocrc_ins = FALSE; /* 82593 generates CRC */ sc->cfblk.crc_1632 = FALSE; /* 32-bit Autodin-II CRC */ sc->cfblk.crs_cdt = FALSE; /* CD not to be interpreted as CS */ /* byte 8 -- 0x00 (OK) */ sc->cfblk.cs_filter = 0; /* CS is recognized immediately */ sc->cfblk.crs_src = FALSE; /* External carrier sense */ sc->cfblk.cd_filter = 0; /* CD is recognized immediately */ /* byte 9 -- 0x3C (0x10??) XXX */ sc->cfblk.min_fr_len = /* Minimum frame length 64 bytes */ (sizeof(struct ether_header) + ETHERMIN + sizeof(long)) >> 2; /* byte 10 -- 0xFF (0xBE??) XXX */ sc->cfblk.lng_typ = FALSE; /* Length field > 1500 = type field */ sc->cfblk.lng_fld = TRUE; /* Disable 802.3 length field check */ sc->cfblk.rxcrc_xf = TRUE; /* Don't transfer CRC to memory */ sc->cfblk.artx = TRUE; /* Disable automatic retransmission */ sc->cfblk.sarec = TRUE; /* Disable source addr trig of CD */ sc->cfblk.tx_jabber = TRUE; /* Disable jabber jam sequence */ sc->cfblk.hash_1 = FALSE; /* Use bits 0-5 in mc address hash */ sc->cfblk.lbpkpol = TRUE; /* Loopback pin active high */ /* byte 11 -- 0x00 (OK) */ sc->cfblk.fdx = FALSE; /* Disable full duplex operation */ /* byte 12 -- 0x3f (OK) */ sc->cfblk.dummy_6 = 0x3f; /* all ones */ sc->cfblk.mult_ia = FALSE; /* No multiple individual addresses */ sc->cfblk.dis_bof = FALSE; /* Disable the backoff algorithm ?! */ /* byte 13 -- 0x07 (OK) */ sc->cfblk.dummy_1 = TRUE; /* set to 1 */ sc->cfblk.tx_ifs_retrig = 3; /* Hmm... Disabled */ #ifdef MULTICAST #if 0 sc->cfblk.mc_all = (WLPIF(unit).if_flags & IFF_ALLMULTI) != 0; #endif sc->cfblk.mc_all = TRUE; #endif /* MULTICAST */ sc->cfblk.rcv_mon = 0; /* Monitor mode disabled */ sc->cfblk.frag_acpt = FALSE; /* Do not accept fragments */ sc->cfblk.tstrttrs = FALSE; /* No start transmission threshold */ /* byte 14 -- 0xE1 (OK) */ sc->cfblk.fretx = TRUE; /* FIFO automatic retransmission */ sc->cfblk.syncrqs = TRUE; /* Synchronous DRQ deassertion... */ sc->cfblk.sttlen = TRUE; /* 6 byte status registers */ sc->cfblk.rx_eop = TRUE; /* Signal EOP on packet reception */ sc->cfblk.tx_eop = TRUE; /* Signal EOP on packet transmission */ /* byte 15 -- 0x24 (OK) */ sc->cfblk.rbuf_size = RX_SIZE >> 11; /* Set receive buffer size */ sc->cfblk.rcvstop = TRUE; /* Enable Receive Stop Register */ outb(PIORL(base), TX_BASE & 0xff); outb(PIORH(base), ((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX); outb(PIOP(base),sizeof(struct i82593_conf_block) & 0xff); /* lsb */ outb(PIOP(base),sizeof(struct i82593_conf_block) >> 8); /* msb */ loutb(PIOP(base), &sc->cfblk, sizeof(struct i82593_conf_block)); /* reset transmit DMA pointer */ outb(HACR(base), HACR_PWR_STAT | HACR_TX_DMA_RESET); DELAY(1000); outb(HACR(base), HACR_PWR_STAT); DELAY(1000); if(!wlpcmd(unit, "wlpconfig(): configure", OP0_CONFIGURE, SR0_CONFIGURE_DONE)) return FALSE; #if 0 #ifdef ORIGINAL_CONFIG /* * below is the default board configuration from p2-28 from 586 book */ configure.fifolim_bytecnt = 0x080c; configure.addrlen_mode = 0x2600; configure.linprio_interframe = 0x7820; /* IFS=120, ACS=2 */ configure.slot_time = 0xf00c; /* slottime=12 */ configure.hardware = 0x0008; /* tx even w/o CD */ configure.min_frame_len = 0x0040; #else /* This is the configuration block suggested by Marc Meertens * in an e-mail message to John * Ioannidis on 10 Nov 92. */ configure.fifolim_bytecnt = 0x040c; configure.addrlen_mode = 0x0600; configure.linprio_interframe = 0x2060; configure.slot_time = 0xf000; configure.hardware = 0x0008; /* tx even w/o CD */ configure.min_frame_len = 0x0040; #endif ORIGINAL_CONFIG #endif 0 #ifdef MULTICAST outb(PIORL(base), (TX_BASE + 2) & 0xff); outb(PIORH(base), (((TX_BASE + 2) >> 8) & PIORH_MASK) | PIORH_SEL_TX); ETHER_FIRST_MULTI(step, &wlp_ac[unit], enm); while (enm != NULL) { unsigned int lo, hi; lo = (enm->enm_addrlo[3] << 16) + (enm->enm_addrlo[4] << 8) + enm->enm_addrlo[5]; hi = (enm->enm_addrhi[3] << 16) + (enm->enm_addrhi[4] << 8) + enm->enm_addrhi[5]; while(lo <= hi) { outb(PIOP(base),enm->enm_addrlo[0]); outb(PIOP(base),enm->enm_addrlo[1]); outb(PIOP(base),enm->enm_addrlo[2]); outb(PIOP(base),lo >> 16); outb(PIOP(base),(lo >> 8) & 0xff); outb(PIOP(base),lo & 0xff); ++cnt; ++lo; } ETHER_NEXT_MULTI(step, enm); } /* write the size of the multicast address area */ outb(PIORL(base), TX_BASE & 0xff); outb(PIORH(base), ((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX); outb(PIOP(base), (cnt * WAVELAN_ADDR_SIZE) & 0xff); outb(PIOP(base), (cnt * WAVELAN_ADDR_SIZE) >> 8); /* reset transmit DMA pointer */ outb(HACR(base), HACR_PWR_STAT | HACR_TX_DMA_RESET); if(!wlpcmd(unit, "wlpconfig(): mc-setup", OP0_MC_SETUP, SR0_MC_SETUP_DONE)) { return FALSE; } #endif MULTICAST #if 0 /* * zero out the receive buffer space. */ outb(PIORL(base), RX_BASE & 0xff); outb(PIORH(base), ((RX_BASE >> 8) & PIORH_MASK)); for (i = 0; i < RX_SIZE; i++) outb(PIOP(base), 0); outb(PIORL(base), RX_BASE & 0xff); outb(PIORH(base), ((RX_BASE >> 8) & PIORH_MASK)); #endif outb(PIORL(base), TX_BASE & 0xff); outb(PIORH(base), ((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX); outb(PIOP(base), WAVELAN_ADDR_SIZE); /* byte count lsb */ outb(PIOP(base), 0); /* byte count msb */ loutb(PIOP(base), WLPADDR(unit), WAVELAN_ADDR_SIZE); /* reset transmit DMA pointer */ outb(HACR(base), HACR_PWR_STAT | HACR_TX_DMA_RESET); DELAY(1000); outb(HACR(base), HACR_PWR_STAT); DELAY(1000); if(!wlpcmd(unit, "wlpconfig(): ia-setup", OP0_IA_SETUP, SR0_IA_SETUP_DONE)) return FALSE; return TRUE; } #endif void wlpdump(int unit) { struct wlp_softc *is = WLPSOFTC(unit); int base = is->base; int i, c; /* disable receiver so we can use channel 1 */ outb(LCCR(base), OP0_RCV_DISABLE); outb(HACR(base), HACR_PWR_STAT | HACR_RX_DMA_RESET); DELAY(1000); outb(HACR(base), HACR_PWR_STAT); DELAY(1000); /* dump into receive buffer */ wlpcmd(unit, "wlpdump(): dump", CR0_CHNL|OP0_DUMP, SR0_DUMP_DONE); /* set read pointer to start of receive buffer */ outb(PIORL(base), 0); outb(PIORH(base), 0); printf("wlp%d dump:", unit); for(i = 0; i < 72; i++){ if((i % 16) == 0) printf("\n"); c = inb(PIOP(base)); printf("%02x ", c); } printf("\n"); /* enable the receiver again */ wlprustrt(unit); } /* * wlpxmt: * * This routine fills in the appropriate registers and memory * locations on the WaveLAN board and starts the board off on * the transmit. * * input : board number of interest, and a pointer to the mbuf * output : board memory and registers are set for xfer and attention * */ void wlpxmt(int unit, struct mbuf *m) { struct wlp_softc *is = WLPSOFTC(unit); register u_short xmtdata_p, xmtdata_base = TX_BASE; u_short clen = 0; /* total pkt data len */ int count; /* data in current mbuf */ int base = is->base; register struct ether_header *eh_p = mtod(m, struct ether_header *); struct mbuf *tm_p = m; u_char *mb_p = mtod(m, u_char *) + sizeof(struct ether_header); #ifdef DEBUG if (xtradebug) printf("wlp%d: wlpxmt, type 0x%04x\n", unit, eh_p->ether_type); #endif xmtdata_p = xmtdata_base + 2; /* skip length field */ outb(PIORL(base), xmtdata_p & 0xff); outb(PIORH(base), ((xmtdata_p >> 8) & PIORH_MASK) | PIORH_SEL_TX); loutb(PIOP(base), eh_p->ether_dhost, WAVELAN_ADDR_SIZE); outb(PIOP(base), eh_p->ether_type & 0xff); outb(PIOP(base), eh_p->ether_type >> 8); count = m->m_len - sizeof(struct ether_header); clen = 0; while(1){ if (count) { loutb(PIOP(base), mb_p, count); clen += count; } if ((tm_p = tm_p->m_next) == (struct mbuf *)0) break; count = tm_p->m_len; mb_p = mtod(tm_p, u_char *); } if (clen < ETHERMIN) { for (; clen < ETHERMIN; ++clen) outb(PIOP(base), 0); } outb(PIOP(base), OP0_NOP); /* Indicate end of transmit chain */ /* write length of data buffer */ clen += WAVELAN_ADDR_SIZE + sizeof(eh_p->ether_type); outb(PIORL(base), xmtdata_base & 0xff); outb(PIORH(base), ((xmtdata_base >> 8) & PIORH_MASK) | PIORH_SEL_TX); outb(PIOP(base), clen & 0xff); /* lsb */ outb(PIOP(base), clen >> 8); /* msb */ #ifdef IF_CNTRS clen += 4 /* crc */; wlp_ifcntrs.pkt_eout[log_2(clen)]++; if (clen < 128) wlp_ifcntrs.pkt_lout[clen>>3]++; #endif IF_CNTRS /* reset transmit DMA pointer */ outb(HACR(base), HACR_PWR_STAT | HACR_TX_DMA_RESET); DELAY(1000); outb(HACR(base), HACR_PWR_STAT); DELAY(1000); /* transmit command */ wlpcmd(unit, "wlpxmt(): transmit", OP0_TRANSMIT, SR0_NO_RESULT); m_freem(m); return; } /* * wlpintr: * * This function is the interrupt handler for the WaveLAN * board. This routine will be called whenever either a packet * is received, or a packet has successfully been transfered and * the unit is ready to transmit another packet. * * input : board number that interrupted * output : either a packet is received, or a packet is transfered * */ void wlpintr(int unit) { struct wlp_softc *sp = WLPSOFTC(unit); int base = sp->base; int next, x, opri; int i, expected = 0; u_short int_type; int status0; #ifdef DEBUG if (xtradebug) printf("wl%d: wlpintr() called\n",unit); #endif if(sp->gone) return; if (sp->seated == FALSE) { printf("wl%d intr(): board not seated\n", unit); return; } for(expected = 0; ; expected = 1) { outb(LCCR(base), CR0_STATUS_0 | OP0_NOP); status0 = inb(LCSR(base)); /* return if no interrupt from 82593 */ if(!(status0 & SR0_INTERRUPT)) { return; } sp->status = status0; if (status0 & SR0_RECEPTION) { if((status0 & SR0_EVENT_MASK) == SR0_STOP_REG_HIT) { #if 0 printf("wl%d: receive buffer overflow\n", unit); #endif wlp_cntrs[unit].rcv.ovw++; WLPIF(unit).if_ierrors++; #if 0 wlp_overrunning = 1; #endif outb(LCCR(base), CR0_INT_ACK | OP0_NOP); wlphwrst(unit); return; } wlprcv(unit); wlp_overrunning = 0; if (status0 & SR0_EXECUTION) { printf("wl%d: interrupt is both rx and tx, status0 = %x\n", unit,status0); } /* acknowledge the interrupt */ outb(LCCR(base), CR0_INT_ACK | OP0_NOP); continue; } else if (!(status0 & SR0_EXECUTION)) { printf("wl%d: interrupt is neither rx or tx, status0 = %x\n", unit,status0); /* acknowledge the interrupt */ outb(LCCR(base), CR0_INT_ACK | OP0_NOP); break; } if(sp->cmd_wait) { /* We are waiting for a command to complete */ sp->cmd_wait = FALSE; /* signal command completed */ /* acknowledge the interrupt */ outb(LCCR(base), CR0_INT_ACK | OP0_NOP); /* If we had to defer call to wlpstart previously, make up for it by calling wlpstart now */ if(sp->deferred) { wlpstart(&(WLPIF(unit))); sp->deferred = FALSE; } continue; } if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE || (status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE) { int tx_status; tx_status = inb(LCSR(base)); tx_status |= inb(LCSR(base)) << 8; if (!(tx_status & TX_OK)) { if (tx_status & TX_FRTL) { if (xmt_watch) printf("wl%d: frame too long\n", unit); WLPIF(unit).if_oerrors++; } if (tx_status & TX_UND_RUN) { if (xmt_watch) printf("wl%d: DMA underrun\n", unit); wlp_cntrs[unit].xmt.dma++; WLPIF(unit).if_oerrors++; } if (tx_status & TX_LOST_CTS) { if (xmt_watch) printf("wl%d: no CTS\n", unit); wlp_cntrs[unit].xmt.nocts++; WLPIF(unit).if_oerrors++; } if (tx_status & TX_LOST_CRS) { /* if (xmt_watch) printf("wl%d: no carrier\n", unit); */ wlp_cntrs[unit].xmt.nodcd++; } /* Ignore late collisions. Act only when maximum retransmit attempts exceeded */ if (tx_status & TX_COLL) { if (tx_status & TX_MAX_COL) { if (xmt_watch) printf("wl%d: channel congestion\n", unit); if (!(tx_status & TX_NCOL_MASK)) { WLPIF(unit).if_collisions += 0x10; wlp_cntrs[unit].xmt.ncoll += 0x10; } } wlp_cntrs[unit].xmt.coll++; WLPIF(unit).if_oerrors++; } } if(tx_status & TX_DEFER) { if (xmt_watch) printf("wl%d: channel jammed\n",unit); wlp_cntrs[unit].xmt.defer++; } if (tx_status & TX_HRT_BEAT) { if (xmt_watch) printf("wl%d: heart beat\n", unit); wlp_cntrs[unit].xmt.heart++; } WLPIF(unit).if_collisions += (tx_status & TX_NCOL_MASK); wlp_cntrs[unit].xmt.ncoll += (tx_status & TX_NCOL_MASK); wlp_cntrs[unit].xmt.xmti++; sp->tbusy = FALSE; WLPIF(unit).if_flags &= ~IFF_OACTIVE; /* acknowledge the interrupt */ outb(LCCR(base), CR0_INT_ACK | OP0_NOP); /* Defer call to wlpstart if we are waiting to be able to issue a command */ if(sp->cmd_request) { sp->deferred = TRUE; break; } wlpstart(&(WLPIF(unit))); } else { printf("wlp%d: unknown interrupt, status0 = %02x\n", unit, status0); /* acknowledge the interrupt */ outb(LCCR(base), CR0_INT_ACK | OP0_NOP); } } return; } /* Send a command to the 82593 chip. Must be called with interrupts enabled */ int wlpcmd(int unit, char *str, int cmd, int result) { struct wlp_softc *sp = WLPSOFTC(unit); int base = sp->base; int status, spin; int opri; sp->cmd_request = TRUE; /* signal that we want to issue a command */ /* spin until the chip finished executing any current command */ do { opri = splimp(); outb(LCCR(base), OP0_NOP | CR0_STATUS_3); status = inb(LCSR(base)); splx(opri); } while ((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE); sp->cmd_wait = TRUE; /* signal that we are waiting for command completion */ sp->cmd_request = FALSE; outb(LCCR(base), cmd); if(result == SR0_NO_RESULT) { /* Return immediately */ sp->cmd_wait = FALSE; /* if a call to wlpstart was deferred, make up for it now */ if(sp->deferred) { wlpstart(&(WLPIF(unit))); /* idempotent */ sp->deferred = FALSE; } return TRUE; } /* interrupts better be enabled while we busy wait */ /* but in_ifinit() turns them off! */ /* could enable just the WaveLAN's IRQ... */ for(spin = 0; spin < 10000000 && sp->cmd_wait; spin++) ; if(sp->cmd_wait){ outb(LCCR(base), OP0_NOP); status = inb(LCSR(base)); if(status & 0x80){ wlpintr(unit); } else { sp->cmd_wait = 0; /* XXX */ printf("wlpcmd timeout, status0 %02x\n", status); return(FALSE); } } /* check the return code, provided by the interrupt handler */ if((sp->status & SR0_EVENT_MASK) != result) { printf("wl%d: %s failed, status0 = %x\n", unit, str, sp->status); return FALSE; } return TRUE; } /* Calculates the SNR ratio. Ignores sigqual and the ethernet header for the time being */ static void wlpgetsnr(int unit, struct ether_header *ehp, u_char siglvl, u_char sillvl, u_char sigqual) { int s; int snrindex; if((s = (siglvl & 0x3f)) > 0) { s -= (sillvl & 0x3f) - 0x2d; if(s >= 0x38) snrindex = 4; else if(s >= 0x2a) snrindex = 3; else if(s >= 0x1c) snrindex = 2; else if(s >= 0x0e) snrindex = 1; else if(s >= 0) snrindex = 0; else { printf("wl%d: bogus snr level\n",unit); return; } wlp_cntrs[unit].rcv.snr[snrindex]++; } } /* * wlpioctl: * * This routine processes an ioctl request from the "if" layer * above. * * input : pointer the appropriate "if" struct, command, and data * output : based on command appropriate action is taken on the * WaveLAN board(s) or related structures * return : error is returned containing exit conditions * */ int wlpioctl(ifp, cmd, data) struct ifnet *ifp; int cmd; caddr_t data; { register struct ifaddr *ifa = (struct ifaddr *)data; register struct ifreq *ifr = (struct ifreq *)data; int unit = ifp->if_unit; register struct wlp_softc *sc = WLPSOFTC(unit); short mode = 0; short base; int opri, error = 0; u_short tmp; int reinit = 0; #ifdef DEBUG printf("wlp%d: entered wlpioctl(), cpl %x\n",unit, cpl); #endif /* The card might have been ejected. */ if(sc == 0) return(EINVAL); base = sc->base; switch (cmd) { case SIOCSIFADDR: #ifdef DEBUG printf("SIOCSIFADDR\n"); #endif /* Set own IP address and enable interface */ ifp->if_flags |= IFF_UP; wlpinit(unit); switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: opri = splimp(); arp_ifinit((struct arpcom *)ifp, ifa); splx(opri); break; #endif #ifdef NS case AF_NS: { register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *)(ds->wlp_addr); else wlpsetaddr(ina->x_host.c_host, unit); break; } #endif } break; case SIOCSIFFLAGS: #ifdef DEBUG printf("SIOCSIFFLAGS\n"); #endif wlpinit(unit); reinit = 1; /* TBD. need to reinit if promisc mode set ? */ if ((ifp->if_flags & IFF_UP) == 0 && sc->flags & DSF_RUNNING) { printf("wlp%d ioctl(): board is not running\n", unit); sc->flags &= ~DSF_RUNNING; ifp->if_timer = 0; /* TBD turn off interrupts? */ } else if ((ifp->if_flags & IFF_UP && (sc->flags & DSF_RUNNING) == 0)) { if (reinit == 0) wlpinit(unit); } if(ifp->if_flags & IFF_LINK2) wlpdump(unit); break; #ifdef MULTICAST case SIOCADDMULTI: case SIOCDELMULTI: error = (cmd == SIOCADDMULTI) ? ether_addmulti(ifr, &wlp_ac[unit]) : ether_delmulti(ifr, &wlp_ac[unit]); wlphwrst(unit); if (error == ENETRESET) { wlpinit(unit); error = 0; } break; #endif MULTICAST /* get the NWID out of the sc since we stored it there */ case SIOCGWLNWID: ifr->ifr_data = (caddr_t) (sc->nwid[0] << 8 | sc->nwid[1]); break; /* set the nwid */ case SIOCSWLNWID: /* root only * we need to check suser status but the p * parameter is not passed down to us. * This is not good. XXX. jrb error = suser(p->p_ucred, &p->p_acflag); if (error != 0) break; */ wlpsetnwid(unit, base, (int)ifr->ifr_data); break; default: error = EINVAL; } return (error); } static void wlpsetnwid(int unit, int base, int data) { struct wlp_softc *sp = WLPSOFTC(unit); sp->nwid[0] = data >> 8; sp->nwid[1] = data & 0xff; MMI_WRITE(MMC_NETW_ID_L,sp->nwid[1]); MMI_WRITE(MMC_NETW_ID_H,sp->nwid[0]); } /* following 2 routines are addition to do wlansignal caching * (1) int OldestEntryNdx( void) * utility fn: used by REVERSEage(lowest age is oldest pkt.) LRU alg. * to find oldest entry in cache * (2) void wl_cache_store (int unit, int base, struct ether_header *eh, * struct mbuf *m) * routine to store the wlansignal strength, to be used * in the handoff algorithm in Mobile-IP or other uses. */ static int OldestEntryNdx( void ) { int i; int OldestEntryAge; int OldestEntryIndex; OldestEntryAge = MAX_AGE; OldestEntryIndex = 0; for(i = 0; i < NextCacheItem; i++) { if ( w_sigcache[i].age < OldestEntryAge ) { OldestEntryAge = w_sigcache[i].age; OldestEntryIndex = i; } } return (OldestEntryIndex); } /* * for this input packet, * if packet is not IP packet * return; * lookup the MAC address in array * if it's not there or there is no room * find a free slot * simple LRU, free pointer and wrap it if full * at this point we have a pointer to an entry * read out and store signal/silence/quality from rmodem * extract ip src from ip header and store * when AGE overflows (this may take awhile) * resort array * * Some things to think about: * note that no space is malloced. * We might hash the mac address if the cache were bigger. * It is not clear that the cache is big enough. * It is also not clear how big it should be. * The cache is IP-specific. We don't care about that as * we want it to be IP-specific. * The last N recv. packets are saved. This will tend * to reward agents and mobile hosts that beacon. * That is probably fine for mobile ip. * Cache overflow we occur rarely especially if agents * beacon one time per second. Probably longer than * laptops will be up. */ static void wl_cache_store (int unit, int base, struct ether_header *eh, struct mbuf *m) { struct ip *ip; int i; /* InsertCacheItem holds index for adding the next cache entry * AgeSequence is the counter for finding LRU entry */ static int InsertCacheItem = 0; static int AgeSequence = 0; /* The packet received may be a broadcast * or multicast or a packet addressed to us. * cache only IP packets ... */ /* check if IP packet */ if ( ntohs(eh->ether_type) != 0x800 ) { return; } /* check if broadcast or multicast packet. we toss * unicast packets */ if ((eh->ether_dhost[0] & 1) == 0) { return; } /* use the mtod macro(in mbuf.h) * to typecast m to struct ip * */ ip = mtod(m, struct ip *); /* do a linear search for a matching MAC address * in the cache table * . MAC address is 6 bytes, * . var NextCacheItem holds total number of entries already cached */ for(i = 0; i < NextCacheItem; i++) { if (! bcmp(eh->ether_shost ,w_sigcache[i].macsrc, 6 )) { /* Match!, * so we already have this entry, * update the data, and LRU age */ break; } } /* did we break in the for loop? * if yes, then overwrite a previously existing cache entry */ if (i < NextCacheItem ) { InsertCacheItem = i; } /* else, have a new address entry,so * add this new entry, * if table full, then we need to replace LRU entry */ else { /* check for space in cache table * note: NextCacheItem also holds number of entries * added in the cache table */ if ( NextCacheItem < MAXCACHEITEMS ) { InsertCacheItem = NextCacheItem; NextCacheItem++; w_sigitems = NextCacheItem; } /* no space found, so find LRU entry & overwrite it * update global var w_sigitem to hold the current no of * entries in the cache */ else { InsertCacheItem = OldestEntryNdx(); } } /* at this point var InsertCacheItem holds * the index to be used for updating or adding * for updating or adding the cache entry information */ /* Populate the values here !! * .ipsrc * .macsrc * .signal,silence,quality */ w_sigcache[InsertCacheItem].ipsrc = ip->ip_src.s_addr; bcopy( eh->ether_shost ,w_sigcache[InsertCacheItem].macsrc, 6); w_sigcache[InsertCacheItem].signal = wlpmmiread(base, MMC_SIGNAL_LVL) & 0x3f; w_sigcache[InsertCacheItem].silence = wlpmmiread(base, MMC_SILENCE_LVL) & 0x3f; w_sigcache[InsertCacheItem].quality = wlpmmiread(base, MMC_SIGN_QUAL) & 0x0f; #ifdef KERNELTALK /* note this will cause signal values to go to the kernel log. * and is ONLY intended for debugging */ printf("signal %d silence %d quality %d\n", w_sigcache[InsertCacheItem].signal, w_sigcache[InsertCacheItem].silence, w_sigcache[InsertCacheItem].quality); #endif /* Assign an age counter to this entry. * note: Lower value means Older entry */ w_sigcache[InsertCacheItem].age = AgeSequence++; /* sanity checking on InsertCacheItem */ if (InsertCacheItem < 0 || InsertCacheItem >= MAXCACHEITEMS) { printf("Error: wavelan signal cache: index insane %d!\n", InsertCacheItem); return ; } /* check age modulation! * before age counter overflows max int value!! * check if need to reassign ages to avoid overflow */ if ( AgeSequence == MAX_AGE ) { for (i = 0; i < NextCacheItem; i++) { w_sigcache[ OldestEntryNdx() ].age = MAX_AGE+i; } /* re-assign the ages finding newest entries */ for (i = 0; i < NextCacheItem; i++) { w_sigcache[i].age -= MAX_AGE; } /* after performing ageMODULATION * the ages are in sequence from 1 to index of items in cache * the next entry should get subsequent * index value of NextCacheItem incremented by 1 * as its age value */ AgeSequence = NextCacheItem; } }