/* * Copyright (c) 1996, 1997 Berkeley Software Design, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that this notice is retained, * the conditions in the following notices are met, and terms applying * to contributors in the following notices also apply to Berkeley * Software Design, Inc. * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by * Berkeley Software Design, Inc. * 4. Neither the name of the Berkeley Software Design, Inc. nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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. * * BSDI $Id: if_cnw.c,v 1.1.8.1 1998/12/05 22:47:10 itojun Exp $ * * Paul Borman, December 1996 * * This driver is derived from a generic frame work which is * Copyright(c) 1994,1995,1996 * Yoichi Shinoda, Yoshitaka Tokugawa, WIDE Project, Wildboar Project * and Foretune. All rights reserved. * * A linux driver was used as the "hardware reference manual" (i.e., * to determine registers and a general outline of how the card works) * That driver is publically available and copyright * * John Markus Bj,Ax(Brndalen * Department of Computer Science * University of Troms,Ax(B * Norway * johnm@staff.cs.uit.no, http://www.cs.uit.no/~johnm/ */ /* * Netwave AirSurfer Wireless LAN * (Formerly known as the Xircom CreditCard Netwave Adapter) */ #if defined(__FreeBSD__) /* * FreeBSD port to use with PAO by Kenjiro Cho . * Fred L. Templin modified pccardd to support * card_mem flags and multiple card_mem entries. */ #include "cnw.h" #endif #if !defined(__FreeBSD__) || NCNW > 0 /* * Keep track of the last 128 packets sent/received, along with status. * Only used to help determine timing related optimizations. #define CNW_TRAIL */ /* * Display each packet on the console as it is received. * This is only for hard core debugging. #define RECV_PKT_DEBUG */ /* * The card appears to work much better when we only allow one packet * "in the air" at a time. This is done by not allowing another packet * on the card, even if there is room. Turning this off will allow the * driver to stuff packets on the card as soon as a transmit buffer is * available. This does increase the number of collisions, though. * We can que a second packet if there are transmit buffers available, * but we do not actually send the packet until the last packet has * been written. */ #define ONE_AT_A_TIME /* * Use internal (slow) versions of bcmp and bcopy. These are declared * as volatile too keep gcc from complaining when we pass in volatile data. * * We should not need these routines, but the tcic controller will not work * without them. This is probably a bug in the tcic controller, but for * now it is much easier to fix here. You can probably turn this off if * you are not using a tcic controller. */ #define USEVBCMP /* * Pad all packets up to at least ETHER_MIN_LEN bytes. I am not sure * this is actually needed. */ #define ETHER_MIN_LEN 64 #if 1 /* __FreeBSD__ */ /* * set 0x100 as a default domain when using access-points. #define USE_ACCESSPOINTS */ #endif /* * If cnw_debug is not 0, basic debugging statements are turned on. * If the low nibble is > 1, more extensive debugging is turned on. * If bit 0x10 is on in cnw_debug, interrupt debugging it turned on. */ int cnw_debug = 0; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #ifdef INET6 #ifndef INET #include #endif #include #endif #include #ifdef PC98 #include #else /*PC98*/ #include #endif /*PC98*/ #if defined(__bsdi__) #include #elif defined(__FreeBSD__) #include #endif #include "bpfilter.h" #if NBPFILTER > 0 #include #include #endif /*NBPFILTER*/ #include #include #if defined(__FreeBSD__) #include #include #include #include #include #endif #if defined(__bsdi__) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "if_cnwreg.h" #include "if_cnwioctl.h" #elif defined(__FreeBSD__) #include "card.h" #include #include #include #include #include #include #include #endif /* __FreeBSD__ */ #ifdef USEVBCMP #define bcmp vbcmp #define bcopy vbcopy static int vbcmp(char volatile *, char volatile *, int); static void vbcopy(char volatile *, char volatile *, int); #endif #if defined(__FreeBSD__) #define MULTICAST #define IFF_NOTRAILERS 0 #define ETHER_IS_MULTICAST(x) ((*(u_char *)x) & 0x01) #endif struct nw_softc { struct device nw_dev; /* base device (must be first) */ #define nw_unit nw_dev.dv_unit #if defined(__bsdi__) struct isadev nw_id; /* ISA device */ struct intrhand nw_ih; /* interrupt vectoring */ #elif defined(__FreeBSD__) struct pccard_devinfo *nw_devi; /* pccard device */ #endif struct arpcom nw_ac; /* Ethernet common part */ #define nw_if nw_ac.ac_if /* network-visible interface */ #define nw_addr nw_ac.ac_enaddr /* hardware Ethernet address */ int nw_ifoflags; caddr_t nw_mbase; /* shared memory base */ int nw_base; /* io port base address */ int nw_psize; /* number of io ports */ volatile struct nwreg *nw_reg; /* memory buffer */ volatile struct nwcreg *nw_creg;/* control registers */ u_short nw_domain; /* current active NetWave domain */ u_short nw_skey; /* current scramble key */ struct cnwstats nw_stats; #if defined(__bsdi__) CliHandle_t nw_clihdl; #endif int nw_socket; #if defined(__bsdi__) cs_rio_t nw_rio; cs_rirq_t nw_rirq; cs_cfg_t nw_rcfg; WinHandle_t nw_winhdl; #endif int nw_irq; /* interrupt vectoring */ u_short nw_txready; /* Number of bytes ready to xmit */ short nw_configured:1; short nw_detaching:1; short nw_active:1; /* Currently xmitting a packet */ struct timeval nw_txlast; /* time of last packet sent */ }; int cnwprobe (struct device *, struct cfdata *, void *); void cnwattach (struct device *, struct device *, void *); int cnwintr (struct nw_softc *); #if defined(__bsdi__) int cnwstart (struct ifnet *); int cnwioctl (struct ifnet *, u_long, caddr_t); int cnw_cse_handler(); #elif defined(__FreeBSD__) void cnwstart (struct ifnet *); int cnwioctl (struct ifnet *, int, caddr_t); #endif #if defined(__FreeBSD__) #define DV_NET DV_IFNET #endif struct cfdriver cnwcd = { NULL, "cnw", cnwprobe, cnwattach, DV_NET, sizeof(struct nw_softc) }; static int cnwmax; #if defined(__bsdi__) static int cnw_cc_probe(); static int cnw_cc_attach(); static int cnw_cc_detach(); #elif defined(__FreeBSD__) static int cnw_cc_probe(struct nw_softc *, int); static int cnw_cc_attach(struct nw_softc *, int); static int cnw_cc_detach(struct nw_softc *, int); #endif static int cnw_cmd(struct nw_softc *, ...); static int cnw_cwait (volatile struct nwcreg *); static int cnw_init (int); #if defined(__FreeBSD__) static void cnw_ifinit (void *); #endif static int cnw_reset(struct nw_softc *); static int cnw_setdomain(struct nw_softc *, int); static int cnw_setkey(struct nw_softc *, int); static void cnw_recv (struct nw_softc *); static void _cnwprintf(struct nw_softc *, char *, ...); static int mcopy_out (struct nw_softc *, struct mbuf *); static struct mbuf *mcopy_in (struct nw_softc *); /* * Wait for last command to complete */ static int wccline = 0; #define WCC(nwc) ((wccline = __LINE__), !((nwc)->nwc_isr & (NWR_READY)) ? cnw_cwait(nwc) : 0) /* * Offsets into memory areas */ #define nwreg_off(x) ((int)&((struct nwreg *)0)->x) #define nwcreg_off(x) ((int)&((struct nwcreg *)0)->x) /* * Debug printfs */ #define cnwprintf if (cnw_debug) _cnwprintf #define cnwprintf2 if ((cnw_debug & 0x0f) >= 2) _cnwprintf #define icnwprintf if (cnw_debug & 0x10) _cnwprintf #ifdef CNW_TRAIL static struct cnwtrail trail[128]; static int head = 0; static int lastsent = 0; #endif #if defined(__FreeBSD__) /* * PC-Card (PCMCIA) specific code. */ static int card_intr(struct pccard_devinfo *); /* Interrupt handler */ static void cnwunload(struct pccard_devinfo *); /* Disable driver */ static int cnwcrdinit(struct pccard_devinfo *); /* init device */ static struct pccard_device cnw_info = { "cnw", cnwcrdinit, cnwunload, card_intr, 0, /* Attributes - presently unused */ &net_imask /* Interrupt mask for device */ }; struct nw_softc *cnw_scs[NCNW]; /* * Initialize the device - called from Slot manager. */ static int cnwcrdinit(struct pccard_devinfo *devi) { struct nw_softc *nw; int sock, unit; unit = devi->pd_unit; /* validate unit number. */ if (devi->pd_unit >= NCNW) return(ENODEV); if ((nw = cnw_scs[devi->pd_unit]) == NULL) { /* first time */ MALLOC(nw, struct nw_softc *, sizeof(struct nw_softc), M_DEVBUF, M_WAITOK); if (nw == NULL) return(ENOBUFS); bzero(nw, sizeof(struct nw_softc)); /* fake a cfdriver structure */ cnwcd.cd_devs = (void **)cnw_scs; cnwcd.cd_devs[unit] = nw; cnwcd.cd_ndevs = NCNW; /* fake a device structure in nw_softc */ nw->nw_dev.dv_unit = unit; sprintf(nw->nw_dev.dv_xname, "cnw%d", unit); nw->nw_if.if_snd.ifq_maxlen = ifqmaxlen; /* XXX */ nw->nw_devi = devi; /* pccard_dev */ cnwattach(NULL, (struct device *)nw, NULL); } else { /* card is reinserted! we don't free nw_softc when * the card is ejected since freeing if (which is * in the nw_softc) is problematic. */ nw->nw_devi = devi; /* don't forget to update pccard_dev */ } /* * Probe the device. If a value is returned, the * device was found at the location. */ nw->nw_socket = -1; #if 1 sock = 0; #else sock = devi->sp->slot; /* really? */ #endif if (cnw_cc_probe(nw, sock)) { if (cnw_cc_attach(nw, sock) == 0) { printf("cnw%d: attach failed!\n", unit); return (ENXIO); } #ifdef INET6 in6_ifattach(&nw->nw_if, IN6_IFT_802, (caddr_t)nw->nw_addr, 0); #endif /* INET6 */ } return(0); } /* * cnwunload - 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 cnwunload(struct pccard_devinfo *devi) { struct nw_softc *nw = cnwcd.cd_devs[devi->pd_unit]; struct ifnet *ifp = &nw->nw_if; ifp->if_flags &= ~IFF_RUNNING; if_down(ifp); nw->nw_socket = -1; printf("cnw%d: unload\n", devi->pd_unit); } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(struct pccard_devinfo *devi) { struct nw_softc *nw = cnwcd.cd_devs[devi->pd_unit]; cnwintr(nw); return(1); } int cnw_isa_probe(struct isa_device *); static int cnw_isa_attach(struct isa_device *is); struct isa_driver cnwdriver = { cnw_isa_probe, cnw_isa_attach, "cnw", 0 }; int cnw_isa_probe(id) struct isa_device *id; { /* * If PC-Card probe required, then register driver with * slot manager. */ pccard_add_driver(&cnw_info); return 0; } /* never called */ static int cnw_isa_attach(id) struct isa_device *id; { printf("cnw: isa_attach!! shouldn't be called\n"); return 1; } #endif /* __FreeBSD__ */ /* ARGSUSED */ int cnwprobe(struct device *parent, struct cfdata *cf, void *aux) { #if defined(__bsdi__) register struct isa_attach_args *ia = (struct isa_attach_args *) aux; ia->ia_irq = IRQNONE; #ifdef DIAGNOSTIC if (sizeof(struct nwreg) != 0x1A0) { printf("cnw: %d != 0x1A0\n", sizeof(struct nwreg)); return (0); } #endif #elif defined(__FreeBSD__) /* never happnes */ #endif /* __FreeBSD__ */ return (1); } /* * cnwattach: this is a dummy routine that make the system think * there's an interface. Make the interface (virtually) visilble * by filling in the network interface record. Also register with * socket driver to inform us in the event of card insertion. */ /* ARGSUSED */ void cnwattach(struct device *parent, struct device *self, void *aux) { register struct nw_softc *nw = (struct nw_softc *) self; int unit = nw->nw_unit; register struct ifnet *ifp = &nw->nw_if; #if defined(__bsdi__) cs_rclient_t rcli; #endif /* * Initialize local variables */ nw->nw_mbase = NULL; #ifndef USE_ACCESSPOINTS nw->nw_domain = NW_DOMAIN; #else nw->nw_domain = NW_ACCESSPOINT; /* default to use access-points */ #endif nw->nw_skey = 0; cnwmax = max(unit, cnwmax); /* * Initialize interface structure */ ifp->if_unit = unit; ifp->if_name = cnwcd.cd_name; /* * Our MTU could be much larger, but ether_attach will set it * to ETHERMTU as well. * An MTU of 3000 has shown up to a 5% improvement in speed, * that is only between machines on the local netwave net. */ ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_NOTRAILERS; #if defined(__bsdi__) ifp->if_init = cnw_init; #elif defined(__FreeBSD__) ifp->if_init = cnw_ifinit; #endif ifp->if_start = cnwstart; ifp->if_ioctl = cnwioctl; ifp->if_watchdog = 0; #if defined(__bsdi__) ether_attach(ifp); #elif defined(__FreeBSD__) ifp->if_output = ether_output; ifp->if_softc = nw; if_attach(ifp); ether_ifattach(ifp); #endif ifp->if_baudrate = 1*1000*1000; /* Default, drivers may update */ #ifndef USE_ACCESSPOINTS ifp->if_mtu = 3000; /* We can do better than 1500 */ #endif #if NBPFILTER > 0 #if defined(__bsdi__) bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); #elif defined(__FreeBSD__) bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif #endif /*NBPFILTER*/ bzero(nw->nw_addr, sizeof(nw->nw_addr)); nw->nw_irq = 0; nw->nw_reg = 0; nw->nw_configured = 0; nw->nw_socket = -1; nw->nw_detaching = 0; #if defined(__bsdi__) nw->nw_ih.ih_fun = cnwintr; nw->nw_ih.ih_arg = (void *)nw; /* * Initialize card client structure */ rcli.Attributes = CSCLI_IO; rcli.EventMask = ~CSEM_CD_CHANGE; rcli.CallBack = cnw_cse_handler; rcli.CallBackArg = nw; cs_RegisterClient(&nw->nw_clihdl, &rcli); isa_establish(&nw->nw_id, &nw->nw_dev); printf(": PCCARD Netwave AirSurfer\n"); #elif defined(__FreeBSD__) printf("cnw%d: PCCARD Netwave AirSurfer: domain set to 0x%x\n", unit, nw->nw_domain); #endif /* __FreeBSD__ */ } /* * Handle a pending interrupt. Keep looping until all interrupts * are handled. */ int cnwintr(register struct nw_softc *nw) { register volatile struct nwreg *nwr = nw->nw_reg; register volatile struct nwcreg *nwc = nw->nw_creg; register u_char isr, stat; register struct ifnet *ifp = &nw->nw_if; if (nw->nw_detaching) return (1); if (nw->nw_socket < 0) { printf("cnwintr: stale\n"); return (0); } nwc->nwc_ictrl = NWR_IDISABLE; again: if (WCC(nwc)) goto wedged; if (!(nwc->nwc_ccsr & NWR_IREADY)) { icnwprintf(nw, "no interrupt ready\n"); nwc->nwc_ictrl = NWR_IENABLE; return (1); } isr = nwc->nwc_isr; icnwprintf(nw, "isr 0x%x\n", isr); if ((isr & (NWR_RXAVAIL|NWR_RXERR|NWR_TXSTAT)) == 0) { icnwprintf(nw, "empty interrupt\n"); nwc->nwc_ictrl = NWR_IENABLE; return (1); } /* * The linux driver actually picks up multiple packets at this * point, looking at nwc->nwc_isr after each packet to see if * the NWR_RXAVAIL bit has been turned off. */ if (isr & NWR_RXAVAIL) { nw->nw_stats.nws_rx++; cnw_recv(nw); } if (isr & NWR_RXERR) { nw->nw_stats.nws_rxerr++; if (WCC(nwc)) goto wedged; nwr->nwr_runstate = NWR_HALT; if (WCC(nwc)) goto wedged; stat = nwr->nwr_rstat; icnwprintf(nw, "rstat 0x%x\n", stat); if (stat & NWR_RSTAT_OVERFLOW) { /* * RX overflow detected. Disable receiver and * process all pending packets before going on. */ nw->nw_stats.nws_rxoverflow++; icnwprintf(nw, "overflow, rbuffer 0x%x\n", nwr->nwr_rbuffer); /* Disable receiver */ if (cnw_cmd(nw, NW_CMD_SET_RXMODE, 0, -1)) goto wedged; cnw_recv(nw); nwr->nwr_rclr = NWR_RSTAT_RXERROR | (stat & (NWR_RSTAT_CRCERR|NWR_RSTAT_FRAME)); /* Re-enable receiver */ if (cnw_cmd(nw, NW_CMD_SET_RXMODE, NW_RXMODE(nw), -1)) goto wedged; } if (stat & NWR_RSTAT_OVERRUN) { /* * RX overrun. Not much we can do here except for * blaming slow 80188. */ nw->nw_stats.nws_rxoverrun++; } if (stat & NWR_RSTAT_CRCERR) { /* * RX crc error. Look for microwave ovens near by. */ nw->nw_stats.nws_rxcrcerror++; } if (stat & NWR_RSTAT_FRAME) { /* * received a framing error */ nw->nw_stats.nws_rxframe++; } if (stat & NWR_RSTAT_RXERROR) { /* * Generic RX error */ nw->nw_stats.nws_rxerrors++; } if (stat & NWR_RSTAT_RXAVAIL) { /* * There is a buffer available */ nw->nw_stats.nws_rxavail++; } if (stat & 1) { /* * This bit is unused? Keep track of it anyhow. */ nw->nw_stats.nws_rxone++; } nwr->nwr_rclr = NWR_RSTAT_RXERROR | (stat & (NWR_RSTAT_CRCERR|NWR_RSTAT_FRAME)); } if (isr & NWR_TXSTAT) { #ifdef ONE_AT_A_TIME /* * Any response to a transmit means the current * packet is done */ nw->nw_active = 0; #endif /* * TX status change interrupt. */ if (WCC(nwc)) goto wedged; stat = nwr->nwr_tstat; icnwprintf(nw, "tstat 0x%x\n", stat); #ifdef CNW_TRAIL microtime(&trail[lastsent].done); trail[lastsent].status = stat; #endif if (stat & NWR_TSTAT_OKAY) { /* * Transmission complete without error. */ nw->nw_stats.nws_txokay++; nw->nw_stats.nws_txretries[stat & 0xf]++; ifp->if_opackets++; ifp->if_collisions += stat & 0xf; icnwprintf(nw, "Packet was sent!\n"); if (WCC(nwc)) goto wedged; nwr->nwr_tclr = NWR_TSTAT_OKAY | 0xf; } if (stat & NWR_TSTAT_ABORT) { /* * TX giveup: Happens if destination is not * within the reach when unicast is attempted * in peer-to-peer mode. Note that broadcast * packets does not cause this error. */ nw->nw_stats.nws_txabort++; icnwprintf(nw, "transmit error\n"); } if (stat & NWR_TSTAT_CARRLOST) { /* * TX carrier lost: Happens if AP is not * responding when operating in the server- * client mode. Note that both unitcast and * broadcast packets cause this error, since * all packets are initially destined for * AP first. */ nw->nw_stats.nws_txlostcd++; icnwprintf(nw, "transmit carrier lost\n"); } if (stat & NWR_TSTAT_TERR) { nw->nw_stats.nws_txerrors++; icnwprintf(nw, "transmit error\n"); } if (stat & (NWR_TSTAT_ABORT|NWR_TSTAT_CARRLOST|NWR_TSTAT_TERR)){ if (WCC(nwc)) goto wedged; nwr->nwr_tclr = NWR_TSTAT_ABORT | NWR_TSTAT_CARRLOST | NWR_TSTAT_TERR | 0xf; ifp->if_oerrors++; } if (WCC(nwc)) goto wedged; ifp->if_flags &= ~IFF_OACTIVE; if (nw->nw_txready || ifp->if_snd.ifq_len) cnwstart(ifp); } goto again; if (0) { wedged: cnwprintf(nw, "controller wedged @ %d\n", wccline); } nwc->nwc_ictrl = NWR_IENABLE; return (1); } /* * Send out any packets that have already been loaded into the card but * not yet sent. Then, start pulling mbufs of the chain and stuffing * them into the card. Stop when we find that the previous packet we * stuffed in was not sent or when we run out of mbufs. */ #if defined(__bsdi__) int #elif defined(__FreeBSD__) void #endif cnwstart(struct ifnet *ifp) { struct nw_softc *nw = cnwcd.cd_devs[ifp->if_unit]; register volatile struct nwreg *nwr = nw->nw_reg; register volatile struct nwcreg *nwc = nw->nw_creg; struct mbuf *m0; struct timeval now; #ifdef DIAGNOSTIC if (nw->nw_if.if_flags & IFF_OACTIVE) printf("cnw%d: cnwstart reentered", nw->nw_unit); #endif for (;;) { microtime(&now); now.tv_sec -= nw->nw_txlast.tv_sec; now.tv_usec -= nw->nw_txlast.tv_usec; if (now.tv_usec < 0) { now.tv_usec += 1000000; now.tv_sec -= 1; } if (nw->nw_txready) { #ifdef ONE_AT_A_TIME /* * Don't ship this packet out until the last * packet has left the building. * If we have not tried to send a packet for 1/5 * a second then we assume we lost an interrupt, * lets go on and send the next packet anyhow. * * I suppose we could check to see if it is okay * to put additional packets on the card (beyond * the one already waiting to be sent) but I don't * think we would get any improvement in speed as * we should have ample time to put the next packet * on while this one is going out. */ if (nw->nw_active && now.tv_sec == 0 && now.tv_usec < 200000) goto out; #endif #ifdef ETHER_MIN_LEN if (nw->nw_txready < ETHER_MIN_LEN) nw->nw_txready = ETHER_MIN_LEN; #endif cnwprintf(nw, "sending packet of %d bytes\n", nw->nw_txready); nw->nw_stats.nws_tx++; microtime(&nw->nw_txlast); #ifdef CNW_TRAIL trail[head].what = NW_CMD_TX_START; trail[head].status = 0; trail[head].length = nw->nw_txready; trail[head].when = nw->nw_txlast; lastsent = head; if (++head == 128) head = 0; #endif if (cnw_cmd(nw,NW_CMD_TX_START,SA(nw->nw_txready),-1)) { nw->nw_txready = 0; goto out; } nw->nw_txready = 0; #ifdef ONE_AT_A_TIME nw->nw_active = 1; #endif } /* * Make sure the link integrity field is on */ if (WCC(nwc)) goto wedged; if (nwr->nwr_lif == 0) { nw->nw_if.if_flags &= ~IFF_OACTIVE; #if defined(__bsdi__) return (ENETDOWN); #elif defined(__FreeBSD__) return; #endif } /* * Make sure the transmit buffer is available */ if (WCC(nwc)) goto wedged; if ((nwc->nwc_isr & NWR_TXEMP) == 0) { cnwprintf(nw, "no empty buffers\n"); goto out; } if (WCC(nwc)) goto wedged; IF_DEQUEUE(&nw->nw_if.if_snd, m0); if (m0 == 0) { cnwprintf(nw, "no more mbufs\n"); goto out; } nw->nw_if.if_flags |= IFF_OACTIVE; /* * Feed outgoing packet to bpf */ #if NBPFILTER > 0 #if defined(__bsdi__) if (nw->nw_if.if_bpf) bpf_mtap(nw->nw_if.if_bpf, m0); #elif defined(__FreeBSD__) if (nw->nw_if.if_bpf) bpf_mtap(&nw->nw_if, m0); #endif #endif /*NBPFILTER*/ nw->nw_txready = mcopy_out(nw, m0); } out: if (0) { wedged: cnwprintf(nw, "controller wedged @ %d\n", wccline); } nw->nw_if.if_flags &= ~IFF_OACTIVE; #if defined(__bsdi__) return (0); #endif } /* * Process an ioctl request. */ int #if defined(__bsdi__) cnwioctl(struct ifnet *ifp, u_long cmd, caddr_t data) #elif defined(__FreeBSD__) cnwioctl(struct ifnet *ifp, int cmd, caddr_t data) #endif { register struct ifaddr *ifa = (struct ifaddr *)data; struct nw_softc *nw = (struct nw_softc *) cnwcd.cd_devs[ifp->if_unit]; int s; int error = 0; struct proc *p = curproc; s = splimp(); switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: cnw_init(ifp->if_unit); arp_ifinit((struct arpcom *)ifp, ifa); break; #endif #ifdef INET6 case AF_INET6: cnw_init(ifp->if_unit); break; #endif default: cnw_init(ifp->if_unit); break; } break; case SIOCSIFFLAGS: if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); #if defined(__FreeBSD__) /* if is down, stop the card. */ cnw_reset(nw); #endif } else cnw_init(ifp->if_unit); break; #ifdef SIOCSIFMTU case SIOCSIFMTU: /* * set the interface MTU. it is ok to set a larger MTU * than Ethernet. */ ifp->if_mtu = ((struct ifreq *)data)->ifr_mtu; break; #endif #ifdef MULTICAST /* * Update our multicast list. */ case SIOCADDMULTI: error = ether_addmulti((struct ifreq *)data, &nw->nw_ac); goto reset; case SIOCDELMULTI: error = ether_delmulti((struct ifreq *)data, &nw->nw_ac); reset: if (error == ENETRESET) { cnw_init(ifp->if_unit); error = 0; } break; #endif #ifdef notdef case SIOCGHWADDR: bcopy(nw->nw_addr, &ifr->ifr_data, sizeof(nw->nw_addr)); break; #endif case SIOCGIFADDR: bcopy(nw->nw_addr, ((struct ifreq *)data)->ifr_addr.sa_data, sizeof(nw->nw_addr)); break; case SIOCGCNWDOMAIN: ((struct ifreq *)data)->ifr_domain = nw->nw_domain; break; case SIOCSCNWDOMAIN: if ((error = suser(p->p_ucred, &p->p_acflag)) == 0) error = cnw_setdomain(nw, ((struct ifreq *)data)->ifr_domain); #if 1 /* __FreeBSD__ */ if (error == 0) { /* * reset MTU. 1500 bytes when using access-points, * 3000 bytes when peer-to-peer. */ if (nw->nw_domain & 0x100) ifp->if_mtu = ETHERMTU; else ifp->if_mtu = 3000; } #endif break; case SIOCSCNWKEY: if ((error = suser(p->p_ucred, &p->p_acflag)) == 0) error = cnw_setkey(nw, ((struct ifreq *)data)->ifr_key); break; case SIOCGCNWSTATUS: if ((error = suser(p->p_ucred, &p->p_acflag)) == 0 && ifp->if_flags & IFF_RUNNING) bcopy((void *)nw->nw_reg->nwr_cmd, ((struct cnwstatus *)data)->data, 0x100); break; case SIOCGCNWSTATS: bcopy((void *)&nw->nw_stats, (void *)&(((struct cnwistats *)data)->stats), sizeof(struct cnwstats)); break; #ifdef CNW_TRAIL case SIOCGCNWTRAIL: bcopy((void *)&trail, (void *)((struct cnwitrail *)data)->trail, sizeof(struct cnwitrail)); ((struct cnwitrail *)data)->head = head; break; #endif default: error = EINVAL; } nw->nw_ifoflags = ifp->if_flags; splx(s); return (error); } #if defined(__bsdi__) /* * Callback interface: * Handler(CliData, Function, Socket, Info, MTDRequest, Buffer, Misc, Status) * * Function: event/function. * Socket: socket affected by the event. * Info: contains other information specific to the event. * MTDRequest: * Buffer: * Misc: */ struct mtdreq; /* we never use it, but this makes gcc2 shut up */ int cnw_cse_handler(void *clidata, int func, int sock, int info, struct mtdreq *mtdreq, char *buf, int misc) { struct nw_softc *nw = (struct nw_softc *)clidata; switch(func) { case CSE_CARD_INSERTION: if (cu_configured(&nw->nw_clihdl, sock) || nw->nw_socket >= 0) break; if (cnw_cc_probe(nw, sock)) cnw_cc_attach(nw, sock); break; case CSE_CARD_REMOVAL: cnw_cc_detach(nw, sock); break; case CSE_CLIENT_INFO: break; default: break; } return (0); } #endif /* __bsdi__ */ /* * Probe routine called upon the card insertion event. */ static int cnw_cc_probe(struct nw_softc *nw, int socket) { u_char addr[6]; int err, j; struct nw_softc *no; #if defined(__bsdi__) cs_rmem_t rmem; cs_rmemrw_t rmemrw; MemHandle_t mh; if (cs_spec_lookup(socket, "cnw", NULL, 0)) { cnwprintf(nw, "cnw_cc_probe: no match\n"); return (0); } rmem.Socket = socket; rmem.Attributes = 0; rmem.Offset = 0; if ((err = cs_OpenMemory(&nw->nw_clihdl, &mh, &rmem)) != 0) { cnwprintf(nw, "OpenMemory err 0x%x\n", err); return (0); } rmemrw.CardOffset = NW_MEM_ADDR + nwreg_off(nwr_addr); rmemrw.Count = sizeof(addr); err = cs_ReadMemory(&nw->nw_clihdl, &mh, addr, &rmemrw); cs_CloseMemory(&nw->nw_clihdl, &mh); if (err) { cnwprintf(nw, "ReadMemory err 0x%x\n", err); return (0); } cnwprintf(nw, "ether address of %s\n", ether_sprintf(addr)); /* * First, check out if this card has been handled by other units. * But before doing that, we scan other units to see * if the card has been accepted by is one of them. */ for (j = 0; j < cnwmax; j++) { if (j == nw->nw_unit) continue; /* This is me */ no = cnwcd.cd_devs[j]; if (bcmp(no->nw_addr, addr, 6) == 0) { cnwprintf(nw, "card handled by cnw%d\n", j); return (0); } } if (nw->nw_addr[0] == 0 && nw->nw_addr[1] == 0 && nw->nw_addr[2] == 0 && nw->nw_addr[3] == 0 && nw->nw_addr[4] == 0 && nw->nw_addr[5] == 0) { register struct ifnet *ifp = &nw->nw_if; register struct ifaddr *ifa; register struct sockaddr_dl *sdl; /* * This client is fresh. Can accept a new card. */ bcopy(addr, (void *)nw->nw_addr, sizeof(nw->nw_addr)); for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) && sdl->sdl_family == AF_LINK) { bcopy((caddr_t)((struct arpcom *)ifp)->ac_enaddr , LLADDR(sdl), ifp->if_addrlen); break; } } cnwprintf(nw, "first card inserted\n"); return (1); } /* * This client has accepted a card before. * Check out if the card is the one we've seen before. */ if (bcmp(addr, nw->nw_addr, sizeof(nw->nw_addr)) == 0) { cnwprintf(nw, "same card re-inserted\n"); return (1); } cnwprintf(nw, "different card inserted (rejected)\n"); return (0); #elif defined(__FreeBSD__) struct pccard_devinfo *devi = nw->nw_devi; int i; /* ethernet address is passed from pccardd */ for (i = 0; i < ETHER_ADDR_LEN; ++i) addr[i] = devi->misc[i]; cnwprintf(nw, "ether address of %6D\n", addr, ":"); if (*(int *)addr == 0) { printf("ether address invalid!\n"); return 0; } /* * FreeBSD reuses unit numbers */ /* * Check out if the card is the one we've seen before. */ if (bcmp(addr, nw->nw_addr, sizeof(nw->nw_addr)) == 0) { cnwprintf(nw, "same card re-inserted\n"); return (1); } do { register struct ifnet *ifp = &nw->nw_if; register struct ifaddr *ifa; register struct sockaddr_dl *sdl; bcopy(addr, (void *)nw->nw_addr, sizeof(nw->nw_addr)); for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) && sdl->sdl_family == AF_LINK) { bcopy((caddr_t)((struct arpcom *)ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); break; } } if (nw->nw_addr[0] == 0 && nw->nw_addr[1] == 0 && nw->nw_addr[2] == 0 && nw->nw_addr[3] == 0 && nw->nw_addr[4] == 0 && nw->nw_addr[5] == 0) cnwprintf(nw, "first card inserted\n"); else cnwprintf(nw, "different card inserted\n"); return (1); } while (0); return (0); #endif /* __FreeBSD__ */ } /* * Attach it. It would be be nice if we could look at the CIS and determine * how to program the card, but some cards contain A WHOLE LOTS OF GARBAGE * in the CIS including several big lies, such as the CISTPL_CONF that's * trying to pretend it is a plain memory card as well as an incorrect VPP * specification, so we will presume the following configuration: * * Power: 5V to Vcc, 0V to Vpp1 and Vpp2. * I/O Ports: either 16 or none required (currently we use memory mapping) * IRQ: One required. * Window: Common memory, card offset 0x20000-0x28fff for controller * command and buffer access. No wait state (Assume 400nsec). * COR: At 0x200 * COR value: 0x41 (0x01) * Other regs: CCSR */ static int cnw_cc_attach(struct nw_softc *nw, int socket) { int unit = nw->nw_dev.dv_unit; #if defined(__bsdi__) cs_rwin_t rwin; cs_rpage_t rpage; #endif int err; nw->nw_socket = socket; #if defined(__bsdi__) /* * Start out with allocating IRQ */ nw->nw_rirq.Socket = socket; nw->nw_rirq.Attributes = 0; nw->nw_rirq.IRQInfo1 = CSIRQ_LEVEL|CSIRQ_MASKS; nw->nw_rirq.IRQInfo2 = 0xffff; err = cs_RequestIRQ(&nw->nw_clihdl, &nw->nw_rirq); if (err) { printf("cnw%d: RequestIRQ err 0x%x\n", unit, err); goto bad; } nw->nw_irq = irq_indextomask(nw->nw_rirq.AssignedIRQ); dyna_intr_establish(nw->nw_irq, &nw->nw_ih, DV_NET); nw->nw_rcfg.cscfg_Socket = socket; nw->nw_rcfg.cscfg_Attributes = 2; /* Enable IRQ */ nw->nw_rcfg.cscfg_Vcc = 50; /* 5V to Vcc */ nw->nw_rcfg.cscfg_Vpp1 = 0; nw->nw_rcfg.cscfg_Vpp2 = 0; /* Vpp1 = Vpp2 = 0 */ nw->nw_rcfg.cscfg_IntType = 2; /* I/O card */ nw->nw_rcfg.cscfg_Present = CREGMAP_COR; nw->nw_rcfg.cscfg_ConfigBase = 0x200; /* tpcc.tpcc_radr */ nw->nw_rcfg.cscfg_ConfigIndex = 0x41; if ((err = cs_RequestConfiguration(&nw->nw_clihdl, &nw->nw_rcfg)) != 0){ printf("cnw%d: RequestConfiguration err 0x%x\n", unit, err); goto bad; } #endif nw->nw_configured = 1; #if defined(__bsdi__) /* * Things are going okay. Memory stuff next. */ rwin.Socket = socket; rwin.Attributes = CSRWIN_ENABLE; rwin.Base = 0; /* Pick available address */ rwin.Size = 0x9000; /* Map 36KB (32K window + 4K register */ rwin.AccessSpeed = (9<<3) | 2; /* 400 nsec */ rwin.AccessSpeed = 1; /* XXX */ if ((err = cs_RequestWindow(&nw->nw_clihdl, &nw->nw_winhdl, &rwin))!=0){ printf("cnw%d: can't alloc win err 0x%x\n", unit, err); goto bad; } rpage.CardOffset = 0x20000; rpage.Page = 0; if ((err = cs_MapMemPage(&nw->nw_clihdl, &nw->nw_winhdl,&rpage)) != 0) { printf("cnw%d: can't map win(comm) err 0x%x\n", unit, err); goto bad; } nw->nw_mbase = (caddr_t)cs_ptokvm(rwin.Base); nw->nw_reg = (struct nwreg *)nw->nw_mbase; nw->nw_creg = (struct nwcreg *)(nw->nw_mbase + NW_REG_ADDR-NW_MEM_ADDR); cnwprintf(nw, "memory 0x%x(0x%x,0x%x)", rwin.Base, nw->nw_reg, nw->nw_creg); #elif defined(__FreeBSD__) if (nw->nw_devi->pd_msize < 0x9000) { printf("allocated memory too small!! size=0x%x need=0x%x\n", nw->nw_devi->pd_msize, 0x9000); goto bad; } nw->nw_mbase = nw->nw_devi->pd_maddr; nw->nw_reg = (struct nwreg *)nw->nw_mbase; nw->nw_creg = (struct nwcreg *)(nw->nw_mbase + NW_REG_ADDR-NW_MEM_ADDR); cnwprintf(nw, "memory 0x%x(0x%x,0x%x)\n", nw->nw_mbase, nw->nw_reg, nw->nw_creg); #endif /* __FreeBSD__ */ /* * Initialize */ if (cnw_reset(nw)) goto bad; /* * Sanity check CS/SS. If mapping has been done without trouble * we should be able to read the mac address from nwr_addr * (offset 0x160). If not, it's a botched assertion, and who knows. */ if (bcmp(nw->nw_addr, (void *)nw->nw_reg->nwr_addr, sizeof(nw->nw_addr))) { printf("cnw%d: sanity check failed on memory access\n", unit); #if defined(__bsdi__) cnwprintf(nw, "%s != ", ether_sprintf(nw->nw_addr)); cnwprintf(NULL, "%s\n", ether_sprintf((u_char *)nw->nw_reg->nwr_addr)); #elif defined(__FreeBSD__) printf("%6D != ", nw->nw_addr, ":"); printf("%6D\n", nw->nw_reg->nwr_addr, ":"); #endif goto bad; } cnwprintf(nw, "ID \"%c%c\" revision %04x %04x\n", nw->nw_reg->nwr_id[0], nw->nw_reg->nwr_id[1], nw->nw_reg->nwr_rev[0], nw->nw_reg->nwr_rev[1]); #if defined(__FreeBSD__) /* initialization is done at if-up, to save power consumption */ nw->nw_if.if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); return (1); #endif if (cnw_init(nw->nw_if.if_unit) == 0) { cnwprintf(nw, "initialization failure\n"); goto bad; } if (WCC(nw->nw_creg)) goto bad; nw->nw_creg->nwc_enable = NWR_ENORMAL; if (cnw_cmd(nw, NW_CMD_RUN, -1)) goto bad; if (WCC(nw->nw_creg)) goto bad; return (1); bad: cnw_cc_detach(nw, socket); return (0); } /* * Detach the card and free all of its resources */ static int cnw_cc_detach(struct nw_softc *nw, int socket) { if (socket < 0 || nw->nw_socket != socket) return (0); nw->nw_detaching = 1; nw->nw_if.if_flags &= ~IFF_RUNNING; if (nw->nw_reg) { cnwprintf(nw, "releasing window\n"); #if defined(__bsdi__) cs_ReleaseWindow(&nw->nw_clihdl, &nw->nw_winhdl); #endif nw->nw_reg = 0; } if (nw->nw_configured) { cnwprintf(nw, "releasing configuration\n"); #if defined(__bsdi__) cs_ReleaseConfiguration(&nw->nw_clihdl, &nw->nw_rcfg); #endif nw->nw_configured = 0; } if (nw->nw_base) { cnwprintf(nw, "releasing io ports\n"); #if defined(__bsdi__) cs_ReleaseIO(&nw->nw_clihdl, &nw->nw_rio); #endif nw->nw_base = 0; } if (nw->nw_irq) { cnwprintf(nw, "releasing interrupt vector\n"); #if defined(__bsdi__) cs_ReleaseIRQ(&nw->nw_clihdl, &nw->nw_rirq); cnwprintf(nw, "releasing interrupt handler\n"); dyna_intr_release(nw->nw_irq, &nw->nw_ih); #endif nw->nw_irq = 0; } nw->nw_socket = -1; nw->nw_detaching = 0; cnwprintf(nw, "released\n"); return (0); } /* * Initialize card */ #if defined(__FreeBSD__) static void cnw_ifinit (sc) void *sc; { struct nw_softc *nw = (struct nw_softc *)sc; cnw_init(nw->nw_unit); } #endif static int cnw_init(int unit) { register struct nw_softc *nw = cnwcd.cd_devs[unit]; struct ifnet *ifp = &nw->nw_if; int s; register volatile struct nwcreg *nwc = nw->nw_creg; if (ifp->if_addrlist == (struct ifaddr *) 0) { cnwprintf(nw, "no address list\n"); return (0); } if ((ifp->if_flags & IFF_RUNNING) && ifp->if_flags == nw->nw_ifoflags) { cnwprintf(nw, "already running\n"); return (0); } if (nw->nw_socket < 0) { cnwprintf(nw, "unit not currently inserted\n"); return (0); } s = splimp(); if (cnw_cmd(nw, NW_CMD_INIT, -1)) goto wedged; /* * RX mode */ if (cnw_cmd(nw, NW_CMD_SET_RXMODE, NW_RXMODE(nw), -1)) goto wedged; /* * TX mode */ if (cnw_cmd(nw, NW_CMD_SET_TXMODE, NW_TXENA, -1)) goto wedged; /* * Set domain (access point or add hoc) */ if (cnw_cmd(nw, NW_CMD_SET_DOMAIN, SA(nw->nw_domain), -1)) goto wedged; /* * Set Scramble Key */ if (cnw_cmd(nw, NW_CMD_SET_SKEY, SA(nw->nw_skey), -1)) goto wedged; if (WCC(nwc)) goto wedged; nwc->nwc_ictrl = NWR_INORMAL; if (WCC(nwc)) goto wedged; #if defined(__FreeBSD__) /* run command is added here to stop the card when if is down. */ nw->nw_creg->nwc_enable = NWR_ENORMAL; if (cnw_cmd(nw, NW_CMD_RUN, -1)) goto wedged; if (WCC(nw->nw_creg)) goto wedged; #endif /* __FreeBSD__ */ nw->nw_if.if_flags &= ~IFF_OACTIVE; nw->nw_if.if_flags |= IFF_RUNNING; cnwstart(ifp); splx(s); return (1); wedged: splx(s); cnwprintf(nw, "controller wedged @ %d\n", wccline); return (0); } /* * do a hardware reset on the card */ static int cnw_reset(struct nw_softc *nw) { int retry = 0; nw->nw_creg->nwc_reset = NWR_RESET; nw->nw_reg->nwr_runstate = NWR_READY; nw->nw_creg->nwc_reset = NWR_NOTRESET; while (!(nw->nw_creg->nwc_isr & NWR_READY)) { if (++retry > 1000) { nw->nw_creg->nwc_reset = NWR_RESET; cnwprintf(nw, "hardware reset failed\n"); return (1); } DELAY(1000); } return (0); } /* * Set the domain for the card. * Domains 0x000-0x0ff are add-hoc. * Domains 0x100-0x1ff are to an access point. */ static int cnw_setdomain(struct nw_softc *nw, int domain) { int s; if (domain & ~0x1ff) return (EINVAL); nw->nw_domain = domain; if (nw->nw_socket < 0) return (0); s = splnet(); if (cnw_cmd(nw, NW_CMD_SET_DOMAIN, SA(domain), -1)) goto wedged; splx(s); return (0); wedged: splx(s); cnwprintf(nw, "controller wedged @ %d\n", wccline); return (EBUSY); } /* * Set the scramble key for the card */ static int cnw_setkey(struct nw_softc *nw, int key) { int s; if (key & ~0xffff) return (EINVAL); nw->nw_skey = key; if (nw->nw_socket < 0) return (0); s = splnet(); if (cnw_cmd(nw, NW_CMD_SET_SKEY, SA(key), -1)) goto wedged; splx(s); return (0); wedged: splx(s); cnwprintf(nw, "controller wedged @ %d\n", wccline); return (EBUSY); } /* * We recieved an interrupt indicating that a packet is ready to be read. * read the packet. An oddity I have noticed is that some packets arrive * as type 0x0811 instead of type 0x0800. I am not sure why, but convert * these to 0x0800 so everyone else likes the packet. */ static void cnw_recv(struct nw_softc *nw) { struct mbuf *m; struct ether_header *eh; if ((m = mcopy_in(nw)) != NULL) { #ifdef RECV_PKT_DEBUG u_char *b = mtod(m, u_char *); int n; printf("cnw%d: read (%d)", nw->nw_unit, m->m_len); for (n = 0; n < m->m_len; ++n, ++b) if ((n & 0xf) == 0) printf("\n%04x %02x", n, *b); else printf(" %02x", *b); printf("\n"); #endif nw->nw_if.if_ipackets++; eh = mtod(m, struct ether_header *); #if NBPFILTER > 0 /* * Check if there's a bpf filter listening on this interface. * If so, hand off the raw packet to bpf. */ #if defined(__bsdi__) if (nw->nw_if.if_bpf) bpf_mtap(nw->nw_if.if_bpf, m); #elif defined(__FreeBSD__) if (nw->nw_if.if_bpf) bpf_mtap(&nw->nw_if, m); #endif #endif /*NBPFILTER*/ /* * 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. * * XXX This test does not support multicasts. */ if ((nw->nw_if.if_flags & IFF_PROMISC) && bcmp(eh->ether_dhost, nw->nw_addr, sizeof(eh->ether_dhost)) #ifdef MULTICAST && !ETHER_IS_MULTICAST(eh->ether_dhost) /*also non-broadcast*/ #else && bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dst)) #endif ) { m_freem(m); return; } #if defined(__bsdi__) eh->ether_type = ntohs(eh->ether_type); if (eh->ether_type == 0x811) { cnwprintf(nw, "converted packet from type 0x0811\n"); eh->ether_type = 0x800; } m_adj(m, sizeof(struct ether_header)) ; cnwprintf(nw, "recieved packet of type %04x\n", eh->ether_type); #elif defined(__FreeBSD__) /* ick! freebsd's ether_input expects ether_type in network byte order */ if (ntohs(eh->ether_type) == 0x811) { cnwprintf(nw, "converted packet from type 0x0811\n"); eh->ether_type = htons(0x800); } m_adj(m, sizeof(struct ether_header)) ; cnwprintf(nw, "recieved packet of type %04x\n", ntohs(eh->ether_type)); #endif ether_input(&nw->nw_if, eh, m) ; } } /* * Send out a command, terminated by a -1. * Normally each argument is a single unsigned byte, however, when we * need to send out a short the bit SHORT_ARG will be sent and we * need to split the short into two bytes. The macro SA(x) will or in * the SHORT_ARG bit to x. */ static int cnw_cmd(struct nw_softc *nw, ...) { volatile struct nwreg *nwr = nw->nw_reg; va_list ap; int i, c; if (WCC(nw->nw_creg)) { printf("cnw%d: controller wedged\n", nw->nw_unit); return (1); } va_start(ap, nw); i = 0; while ((c = va_arg(ap, int)) != -1) { nwr->nwr_cmd[i++] = c; if (c & SHORT_ARG) nwr->nwr_cmd[i++] = c >> 8; } nwr->nwr_cmd[i] = 0; /* No more commands. */ va_end(ap); return (0); } /* * Wait for command completion */ static int cnw_cwait(volatile struct nwcreg *nwc) { register int rv = 0x10000; while ((nwc->nwc_isr & NWR_READY) == 0 && rv-- > 0) (void)inb(0x61); return (rv == 0); } /* * debug print routine. It takes a normal printf style format string * an prepends the unit name before printing it out. */ static void _cnwprintf(struct nw_softc *nw, char *fmt, ...) { va_list ap; va_start(ap, fmt); #if defined(__bsdi__) if (nw != NULL) printf("cnw%d: %r", nw->nw_unit, fmt, ap); else printf("%r", fmt, ap); #elif defined(__FreeBSD__) if (nw != NULL) { printf("cnw%d: ", nw->nw_unit); vprintf(fmt, ap); } else vprintf(fmt, ap); #endif va_end(ap); } /* * Copy the specified mbuf to the next available transmit buffer * We have already verified there is a transmit buffer available. */ static int mcopy_out(struct nw_softc *nw, struct mbuf *m0) { volatile struct nwreg *nwr = nw->nw_reg; volatile u_char *bp; u_short nb; int len, n, xlen, ml; u_short buffer, buflen, bufoff; struct mbuf *m = m0; char *mp; buffer = nwr->nwr_tbuffer; buflen = nwr->nwr_tlen; bufoff = nwr->nwr_toff; #if 0 /*toku*/ len = m->m_pkthdr.len; #else for (len = 0, m = m0; m; m = m->m_next) len += m->m_len; #endif cnwprintf(nw, "mcopy_out %d bytes to buffer %x, off %x, len %d\n", len, buffer, bufoff, buflen); nb = 0; bp = NULL; /* this keeps gcc2 happy */ xlen = 0; m = m0; ml = m->m_len; mp = mtod(m, char *); cnwprintf2(nw, "got an mbuf of %d bytes\n", ml); while (m) { if (nb == 0) { cnwprintf2(nw, "Copy out buffer %x\n", buffer); bp = (u_char *)nwr + buffer; buffer = *(u_short *)bp; bp += bufoff; nb = buflen; cnwprintf2(nw, "buffer has %d bytes (need %d more)\n", nb, len - xlen); } n = nb < ml ? nb : ml; #ifdef DIAGNOSTIC if (xlen + n > len) { printf("cnw%d: mbuf overrun. Wanted to copy %d bytes of len %d\n", nw->nw_unit, xlen + n, len); break; } #endif bcopy(mp, (void *)bp, n); ml -= n; mp += n; if (ml == 0 && (m = m->m_next) != NULL) { cnwprintf2(nw, "got an mbuf of %d bytes\n", ml); ml = m->m_len; mp = mtod(m, char *); } bp += n; nb -= n; xlen += n; cnwprintf2(nw, "copied %d of %d bytes\n", xlen, len); } m_freem(m0); return (xlen < len ? 0 : len); } /* * Copy the next packet into an mbuf and release the associated buffers */ static struct mbuf * mcopy_in(struct nw_softc *nw) { volatile struct nwreg *nwr = nw->nw_reg; volatile u_char *bp; char *mp; u_short nb, buffer; int len, n; struct mbuf *m, *m0, *m1; m0 = NULL; if (WCC(nw->nw_creg)) goto wedged; len = nwr->nwr_rlen; buffer = nwr->nwr_rbuffer; cnwprintf(nw, "recieve buffer %x length %d\n", buffer, len); #ifdef CNW_TRAIL trail[head].what = NW_CMD_RX_RELEASE; trail[head].length = len; microtime(&trail[head].when); if (++head == 128) head = 0; #endif if (len <= 0 || len > nw->nw_if.if_mtu + 14) goto bad; MGETHDR(m0, M_DONTWAIT, MT_DATA); if (m0 != NULL && len + 2 > MHLEN) { MCLGET(m0, M_DONTWAIT); cnwprintf2(nw, "using an mbuf cluster\n"); if ((m0->m_flags & M_EXT) == 0) { printf("cnw%d: out of mbuf clusters\n", nw->nw_unit); goto bad; } } if (m0 == NULL) goto bad; m = m0; m->m_pkthdr.rcvif = &nw->nw_if; m->m_pkthdr.len = len; /* * The ethernet header is 14 bytes long. Since we want the * IP header on a 4 byte boundry, start 2 bytes in */ m->m_data += 2; m->m_len = 0; nb = 0; bp = NULL; /* keep gcc2 happy */ mp = mtod(m, char *); while (len > 0) { if (nb == 0) { cnwprintf2(nw, "Copy buffer %x\n", buffer); #ifdef DIAGNOSTIC if (buffer < 0x200 || buffer > 0x8000) goto bad; #endif bp = (u_char *)nwr + buffer; buffer = *(u_short *)bp; nb = *(u_short *)(bp+2); cnwprintf2(nw, "buffer has %d bytes (need %d more)\n", nb, len); bp += *(u_short *)(bp+4); } n = len < nb ? len : nb; if (M_TRAILINGSPACE(m) == 0) { MGET(m1, M_DONTWAIT, MT_DATA); if (m1 != NULL && len > MLEN) MCLGET(m1, M_DONTWAIT); if (m1 == NULL) goto bad; m->m_next = m1; m = m1; m->m_len = 0; mp = mtod(m, char *); } if (M_TRAILINGSPACE(m) < n) n = M_TRAILINGSPACE(m); bcopy((void *)bp, mp, n); m->m_len += n; len -= n; bp += n; mp += n; nb -= n; } if (0) { bad: if (m0) m_freem(m0); m0 = NULL; } if (cnw_cmd(nw, NW_CMD_RX_RELEASE, -1)) goto wedged; return (m0); wedged: cnwprintf(nw, "controller wedged @ %d\n", wccline); if (m0) m_freem(m0); return (NULL); } #ifdef USEVBCMP static int vbcmp(char volatile *p1, char volatile *p2, int s) { int r = 0; while (s-- > 0) if ((r = (*p1++ - *p2++)) != 0) break; return (r); } static void vbcopy(char volatile *p1, char volatile *p2, int s) { while (s-- > 0) *p2++ = *p1++; } #endif #endif /* !defined(__FreeBSD__) || NCNW > 0 */