xref: /netbsd-src/sys/dev/pcmcia/if_cnw.c (revision 2d501ecfdc9978b51b8915a8c9ad1ceb359a68ac)
1*2d501ecfSthorpej /*	$NetBSD: if_cnw.c,v 1.68 2020/01/29 13:54:41 thorpej Exp $	*/
2512db612Schristos 
3512db612Schristos /*-
4adbea351Smycroft  * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
5512db612Schristos  * All rights reserved.
6512db612Schristos  *
7512db612Schristos  * This code is derived from software contributed to The NetBSD Foundation
8512db612Schristos  * by Michael Eriksson.
9512db612Schristos  *
10512db612Schristos  * Redistribution and use in source and binary forms, with or without
11512db612Schristos  * modification, are permitted provided that the following conditions
12512db612Schristos  * are met:
13512db612Schristos  * 1. Redistributions of source code must retain the above copyright
14512db612Schristos  *    notice, this list of conditions and the following disclaimer.
15512db612Schristos  * 2. Redistributions in binary form must reproduce the above copyright
16512db612Schristos  *    notice, this list of conditions and the following disclaimer in the
17512db612Schristos  *    documentation and/or other materials provided with the distribution.
18512db612Schristos  *
19512db612Schristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20512db612Schristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21512db612Schristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22512db612Schristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23512db612Schristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24512db612Schristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25512db612Schristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26512db612Schristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27512db612Schristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28512db612Schristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29512db612Schristos  * POSSIBILITY OF SUCH DAMAGE.
30512db612Schristos  */
31512db612Schristos 
32512db612Schristos /*
331b6504a1Sitojun  * Copyright (c) 1996, 1997 Berkeley Software Design, Inc.
341b6504a1Sitojun  * All rights reserved.
351b6504a1Sitojun  *
361b6504a1Sitojun  * Redistribution and use in source and binary forms, with or without
371b6504a1Sitojun  * modification, are permitted provided that this notice is retained,
381b6504a1Sitojun  * the conditions in the following notices are met, and terms applying
391b6504a1Sitojun  * to contributors in the following notices also apply to Berkeley
401b6504a1Sitojun  * Software Design, Inc.
411b6504a1Sitojun  *
421b6504a1Sitojun  * 1. Redistributions of source code must retain the above copyright
431b6504a1Sitojun  *    notice, this list of conditions and the following disclaimer.
441b6504a1Sitojun  * 2. Redistributions in binary form must reproduce the above copyright
451b6504a1Sitojun  *    notice, this list of conditions and the following disclaimer in the
461b6504a1Sitojun  *    documentation and/or other materials provided with the distribution.
471b6504a1Sitojun  * 3. All advertising materials mentioning features or use of this software
481b6504a1Sitojun  *    must display the following acknowledgement:
491b6504a1Sitojun  *      This product includes software developed by
501b6504a1Sitojun  *	Berkeley Software Design, Inc.
511b6504a1Sitojun  * 4. Neither the name of the Berkeley Software Design, Inc. nor the names
521b6504a1Sitojun  *    of its contributors may be used to endorse or promote products derived
531b6504a1Sitojun  *    from this software without specific prior written permission.
541b6504a1Sitojun  *
551b6504a1Sitojun  * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
561b6504a1Sitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
571b6504a1Sitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
581b6504a1Sitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
591b6504a1Sitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
601b6504a1Sitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
611b6504a1Sitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
621b6504a1Sitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
631b6504a1Sitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
641b6504a1Sitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
651b6504a1Sitojun  * SUCH DAMAGE.
661b6504a1Sitojun  *
671b6504a1Sitojun  * Paul Borman, December 1996
681b6504a1Sitojun  *
691b6504a1Sitojun  * This driver is derived from a generic frame work which is
701b6504a1Sitojun  * Copyright(c) 1994,1995,1996
711b6504a1Sitojun  * Yoichi Shinoda, Yoshitaka Tokugawa, WIDE Project, Wildboar Project
721b6504a1Sitojun  * and Foretune.  All rights reserved.
731b6504a1Sitojun  *
741b6504a1Sitojun  * A linux driver was used as the "hardware reference manual" (i.e.,
751b6504a1Sitojun  * to determine registers and a general outline of how the card works)
761b6504a1Sitojun  * That driver is publically available and copyright
771b6504a1Sitojun  *
78a534cd42Saugustss  * John Markus Bj�rndalen
791b6504a1Sitojun  * Department of Computer Science
80a534cd42Saugustss  * University of Troms�
811b6504a1Sitojun  * Norway
821b6504a1Sitojun  * johnm@staff.cs.uit.no, http://www.cs.uit.no/~johnm/
831b6504a1Sitojun  */
841b6504a1Sitojun 
851b6504a1Sitojun /*
86512db612Schristos  * This is a driver for the Xircom CreditCard Netwave (also known as
87512db612Schristos  * the Netwave Airsurfer) wireless LAN PCMCIA adapter.
88512db612Schristos  *
89512db612Schristos  * When this driver was developed, the Linux Netwave driver was used
90512db612Schristos  * as a hardware manual. That driver is Copyright (c) 1997 University
9134eb26eeSwiz  * of Troms�, Norway. It is part of the Linux pcmcia-cs package that
9234eb26eeSwiz  * can be found at http://pcmcia-cs.sourceforge.net/. The most recent
9334eb26eeSwiz  * version of the pcmcia-cs package when this driver was written was
9434eb26eeSwiz  * 3.0.6.
95512db612Schristos  *
96512db612Schristos  * Unfortunately, a lot of explicit numeric constants were used in the
97512db612Schristos  * Linux driver. I have tried to use symbolic names whenever possible,
98512db612Schristos  * but since I don't have any real hardware documentation, there's
99512db612Schristos  * still one or two "magic numbers" :-(.
100512db612Schristos  *
101512db612Schristos  * Driver limitations: This driver doesn't do multicasting or receiver
102512db612Schristos  * promiscuity, because of missing hardware documentation. I couldn't
103512db612Schristos  * get receiver promiscuity to work, and I haven't even tried
104512db612Schristos  * multicast. Volunteers are welcome, of course :-).
105512db612Schristos  */
106512db612Schristos 
107ab5d9d2bSlukem #include <sys/cdefs.h>
108*2d501ecfSthorpej __KERNEL_RCSID(0, "$NetBSD: if_cnw.c,v 1.68 2020/01/29 13:54:41 thorpej Exp $");
109ab5d9d2bSlukem 
110512db612Schristos #include "opt_inet.h"
111512db612Schristos 
112512db612Schristos #include <sys/param.h>
113512db612Schristos #include <sys/systm.h>
114512db612Schristos #include <sys/device.h>
115512db612Schristos #include <sys/socket.h>
116512db612Schristos #include <sys/mbuf.h>
117512db612Schristos #include <sys/ioctl.h>
1181b6504a1Sitojun #include <sys/proc.h>
1192867b68bSelad #include <sys/kauth.h>
1201b6504a1Sitojun 
1211b6504a1Sitojun #include <net/if.h>
122512db612Schristos 
123512db612Schristos #include <dev/pcmcia/if_cnwreg.h>
1241b6504a1Sitojun #include <dev/pcmcia/if_cnwioctl.h>
125512db612Schristos 
126512db612Schristos #include <dev/pcmcia/pcmciareg.h>
127512db612Schristos #include <dev/pcmcia/pcmciavar.h>
128512db612Schristos #include <dev/pcmcia/pcmciadevs.h>
129512db612Schristos 
130512db612Schristos #include <net/if_dl.h>
131512db612Schristos #include <net/if_ether.h>
1324b508fb1Smsaitoh #include <net/bpf.h>
133512db612Schristos 
134512db612Schristos #ifdef INET
135512db612Schristos #include <netinet/in.h>
136512db612Schristos #include <netinet/in_systm.h>
137512db612Schristos #include <netinet/in_var.h>
138512db612Schristos #include <netinet/ip.h>
139512db612Schristos #include <netinet/if_inarp.h>
140512db612Schristos #endif
141512db612Schristos 
142512db612Schristos /*
143512db612Schristos  * Let these be patchable variables, initialized from macros that can
144512db612Schristos  * be set in the kernel config file. Someone with lots of spare time
145512db612Schristos  * could probably write a nice Netwave configuration program to do
146512db612Schristos  * this a little bit more elegantly :-).
147512db612Schristos  */
148512db612Schristos #ifndef CNW_DOMAIN
149512db612Schristos #define CNW_DOMAIN	0x100
150512db612Schristos #endif
151512db612Schristos int cnw_domain = CNW_DOMAIN;		/* Domain */
152512db612Schristos #ifndef CNW_SCRAMBLEKEY
153512db612Schristos #define CNW_SCRAMBLEKEY 0
154512db612Schristos #endif
155512db612Schristos int cnw_skey = CNW_SCRAMBLEKEY;		/* Scramble key */
156512db612Schristos 
1579d17b2caSitojun /*
1589d17b2caSitojun  * The card appears to work much better when we only allow one packet
1599d17b2caSitojun  * "in the air" at a time.  This is done by not allowing another packet
1609d17b2caSitojun  * on the card, even if there is room.  Turning this off will allow the
1619d17b2caSitojun  * driver to stuff packets on the card as soon as a transmit buffer is
1629d17b2caSitojun  * available.  This does increase the number of collisions, though.
1639d17b2caSitojun  * We can que a second packet if there are transmit buffers available,
1649d17b2caSitojun  * but we do not actually send the packet until the last packet has
1659d17b2caSitojun  * been written.
1669d17b2caSitojun  */
1675f1c4bb5Sitojun #define	ONE_AT_A_TIME
1685f1c4bb5Sitojun 
1695f1c4bb5Sitojun /*
1705f1c4bb5Sitojun  * Netwave cards choke if we try to use io memory address >= 0x400.
1715f1c4bb5Sitojun  * Even though, CIS tuple does not talk about this.
1725f1c4bb5Sitojun  * Use memory mapped access.
1735f1c4bb5Sitojun  */
1745f1c4bb5Sitojun #define MEMORY_MAPPED
175512db612Schristos 
1768d129e6eSmaxv static int	cnw_match(device_t, cfdata_t, void *);
1778d129e6eSmaxv static void	cnw_attach(device_t, device_t, void *);
1788d129e6eSmaxv static int	cnw_detach(device_t, int);
1794e1b4b72Sitojun 
1808d129e6eSmaxv static int	cnw_activate(device_t, enum devact);
181512db612Schristos 
182512db612Schristos struct cnw_softc {
183cbab9cadSchs 	device_t sc_dev;		    /* Device glue (must be first) */
184512db612Schristos 	struct ethercom sc_ethercom;	    /* Ethernet common part */
185512db612Schristos 	int sc_domain;			    /* Netwave domain */
186512db612Schristos 	int sc_skey;			    /* Netwave scramble key */
1871b6504a1Sitojun 	struct cnwstats sc_stats;
188512db612Schristos 
189512db612Schristos 	/* PCMCIA-specific stuff */
190512db612Schristos 	struct pcmcia_function *sc_pf;	    /* PCMCIA function */
1915f1c4bb5Sitojun #ifndef MEMORY_MAPPED
192512db612Schristos 	struct pcmcia_io_handle sc_pcioh;   /* PCMCIA I/O space handle */
193512db612Schristos 	int sc_iowin;			    /*   ...window */
194512db612Schristos 	bus_space_tag_t sc_iot;		    /*   ...bus_space tag */
195512db612Schristos 	bus_space_handle_t sc_ioh;	    /*   ...bus_space handle */
1965f1c4bb5Sitojun #endif
197512db612Schristos 	struct pcmcia_mem_handle sc_pcmemh; /* PCMCIA memory handle */
198875bff09Ssoren 	bus_size_t sc_memoff;		    /*   ...offset */
199512db612Schristos 	int sc_memwin;			    /*   ...window */
200512db612Schristos 	bus_space_tag_t sc_memt;	    /*   ...bus_space tag */
201512db612Schristos 	bus_space_handle_t sc_memh;	    /*   ...bus_space handle */
202512db612Schristos 	void *sc_ih;			    /* Interrupt cookie */
2039d17b2caSitojun 	struct timeval sc_txlast;	    /* When the last xmit was made */
2049d17b2caSitojun 	int sc_active;			    /* Currently xmitting a packet */
2054e1b4b72Sitojun 
2064e1b4b72Sitojun 	int sc_resource;		    /* Resources alloc'ed on attach */
2077aa608e5Sitojun #define CNW_RES_PCIC	1
2087aa608e5Sitojun #define CNW_RES_IO	2
2097aa608e5Sitojun #define CNW_RES_MEM	4
2107aa608e5Sitojun #define CNW_RES_NET	8
211512db612Schristos };
212512db612Schristos 
213cbab9cadSchs CFATTACH_DECL_NEW(cnw, sizeof(struct cnw_softc),
214b75a007dSthorpej     cnw_match, cnw_attach, cnw_detach, cnw_activate);
215512db612Schristos 
2168d129e6eSmaxv static void cnw_reset(struct cnw_softc *);
2178d129e6eSmaxv static void cnw_init(struct cnw_softc *);
2188d129e6eSmaxv static int cnw_enable(struct cnw_softc *sc);
2198d129e6eSmaxv static void cnw_disable(struct cnw_softc *sc);
2208d129e6eSmaxv static void cnw_start(struct ifnet *);
2218d129e6eSmaxv static void cnw_transmit(struct cnw_softc *, struct mbuf *);
2228d129e6eSmaxv static struct mbuf *cnw_read(struct cnw_softc *);
2238d129e6eSmaxv static void cnw_recv(struct cnw_softc *);
2248d129e6eSmaxv static int cnw_intr(void *arg);
2258d129e6eSmaxv static int cnw_ioctl(struct ifnet *, u_long, void *);
2268d129e6eSmaxv static void cnw_watchdog(struct ifnet *);
22718db93c7Sperry static int cnw_setdomain(struct cnw_softc *, int);
22818db93c7Sperry static int cnw_setkey(struct cnw_softc *, int);
229512db612Schristos 
230512db612Schristos /* ---------------------------------------------------------------- */
231512db612Schristos 
232512db612Schristos /* Help routines */
23318db93c7Sperry static int wait_WOC(struct cnw_softc *, int);
23418db93c7Sperry static int read16(struct cnw_softc *, int);
23518db93c7Sperry static int cnw_cmd(struct cnw_softc *, int, int, int, int);
236512db612Schristos 
237512db612Schristos /*
238512db612Schristos  * Wait until the WOC (Write Operation Complete) bit in the
239512db612Schristos  * ASR (Adapter Status Register) is asserted.
240512db612Schristos  */
241512db612Schristos static int
wait_WOC(struct cnw_softc * sc,int line)242454af1c0Sdsl wait_WOC(struct cnw_softc *sc, int line)
243512db612Schristos {
244512db612Schristos 	int i, asr;
245512db612Schristos 
246512db612Schristos 	for (i = 0; i < 5000; i++) {
2475f1c4bb5Sitojun #ifndef MEMORY_MAPPED
248512db612Schristos 		asr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR);
2495f1c4bb5Sitojun #else
2505f1c4bb5Sitojun 		asr = bus_space_read_1(sc->sc_memt, sc->sc_memh,
2515f1c4bb5Sitojun 		    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR);
2525f1c4bb5Sitojun #endif
253512db612Schristos 		if (asr & CNW_ASR_WOC)
254512db612Schristos 			return (0);
255512db612Schristos 		DELAY(100);
256512db612Schristos 	}
257512db612Schristos 	if (line > 0)
258cbab9cadSchs 		printf("%s: wedged at line %d\n", device_xname(sc->sc_dev), line);
259512db612Schristos 	return (1);
260512db612Schristos }
261512db612Schristos #define WAIT_WOC(sc) wait_WOC(sc, __LINE__)
262512db612Schristos 
263512db612Schristos 
264512db612Schristos /*
265512db612Schristos  * Read a 16 bit value from the card.
266512db612Schristos  */
267512db612Schristos static int
read16(struct cnw_softc * sc,int offset)268454af1c0Sdsl read16(struct cnw_softc *sc, int offset)
269512db612Schristos {
270512db612Schristos 	int hi, lo;
271512db612Schristos 	int offs = sc->sc_memoff + offset;
272512db612Schristos 
273512db612Schristos 	/* This could presumably be done more efficient with
274512db612Schristos 	 * bus_space_read_2(), but I don't know anything about the
275512db612Schristos 	 * byte sex guarantees... Besides, this is pretty cheap as
276512db612Schristos 	 * well :-)
277512db612Schristos 	 */
278512db612Schristos 	lo = bus_space_read_1(sc->sc_memt, sc->sc_memh, offs);
279512db612Schristos 	hi = bus_space_read_1(sc->sc_memt, sc->sc_memh, offs + 1);
280512db612Schristos 	return ((hi << 8) | lo);
281512db612Schristos }
282512db612Schristos 
283512db612Schristos 
284512db612Schristos /*
285512db612Schristos  * Send a command to the card by writing it to the command buffer.
286512db612Schristos  */
287512db612Schristos int
cnw_cmd(struct cnw_softc * sc,int cmd,int count,int arg1,int arg2)28882357f6dSdsl cnw_cmd(struct cnw_softc *sc, int cmd, int count, int arg1, int arg2)
289512db612Schristos {
290512db612Schristos 	int ptr = sc->sc_memoff + CNW_EREG_CB;
291512db612Schristos 
292512db612Schristos 	if (wait_WOC(sc, 0)) {
293512db612Schristos 		printf("%s: wedged when issuing cmd 0x%x\n",
294cbab9cadSchs 		    device_xname(sc->sc_dev), cmd);
295512db612Schristos 		/*
296512db612Schristos 		 * We'll continue anyway, as that's probably the best
297512db612Schristos 		 * thing we can do; at least the user knows there's a
298512db612Schristos 		 * problem, and can reset the interface with ifconfig
299512db612Schristos 		 * down/up.
300512db612Schristos 		 */
301512db612Schristos 	}
302512db612Schristos 
303512db612Schristos 	bus_space_write_1(sc->sc_memt, sc->sc_memh, ptr, cmd);
304512db612Schristos 	if (count > 0) {
305512db612Schristos 		bus_space_write_1(sc->sc_memt, sc->sc_memh, ptr + 1, arg1);
306512db612Schristos 		if (count > 1)
307512db612Schristos 			bus_space_write_1(sc->sc_memt, sc->sc_memh,
308512db612Schristos 			    ptr + 2, arg2);
309512db612Schristos 	}
310512db612Schristos 	bus_space_write_1(sc->sc_memt, sc->sc_memh,
311512db612Schristos 	    ptr + count + 1, CNW_CMD_EOC);
312512db612Schristos 	return (0);
313512db612Schristos }
314512db612Schristos #define CNW_CMD0(sc, cmd) \
315512db612Schristos     do { cnw_cmd(sc, cmd, 0, 0, 0); } while (0)
316512db612Schristos #define CNW_CMD1(sc, cmd, arg1)	\
317512db612Schristos     do { cnw_cmd(sc, cmd, 1, arg1 , 0); } while (0)
318512db612Schristos #define CNW_CMD2(sc, cmd, arg1, arg2) \
319512db612Schristos     do { cnw_cmd(sc, cmd, 2, arg1, arg2); } while (0)
320512db612Schristos 
321512db612Schristos /* ---------------------------------------------------------------- */
322512db612Schristos 
323512db612Schristos /*
324512db612Schristos  * Reset the hardware.
325512db612Schristos  */
3268d129e6eSmaxv static void
cnw_reset(struct cnw_softc * sc)327454af1c0Sdsl cnw_reset(struct cnw_softc *sc)
328512db612Schristos {
329512db612Schristos #ifdef CNW_DEBUG
330512db612Schristos 	if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
331cbab9cadSchs 		printf("%s: resetting\n", device_xname(sc->sc_dev));
332512db612Schristos #endif
333512db612Schristos 	wait_WOC(sc, 0);
3345f1c4bb5Sitojun #ifndef MEMORY_MAPPED
335512db612Schristos 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_PMR, CNW_PMR_RESET);
336e93c7e8dSitojun #else
337e93c7e8dSitojun 	bus_space_write_1(sc->sc_memt, sc->sc_memh,
3385f1c4bb5Sitojun 	    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_PMR, CNW_PMR_RESET);
3395f1c4bb5Sitojun #endif
340512db612Schristos 	bus_space_write_1(sc->sc_memt, sc->sc_memh,
341512db612Schristos 	    sc->sc_memoff + CNW_EREG_ASCC, CNW_ASR_WOC);
3425f1c4bb5Sitojun #ifndef MEMORY_MAPPED
343512db612Schristos 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_PMR, 0);
3445f1c4bb5Sitojun #else
3455f1c4bb5Sitojun 	bus_space_write_1(sc->sc_memt, sc->sc_memh,
3465f1c4bb5Sitojun 	    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_PMR, 0);
3475f1c4bb5Sitojun #endif
348512db612Schristos }
349512db612Schristos 
350512db612Schristos 
351512db612Schristos /*
352512db612Schristos  * Initialize the card.
353512db612Schristos  */
3548d129e6eSmaxv static void
cnw_init(struct cnw_softc * sc)355454af1c0Sdsl cnw_init(struct cnw_softc *sc)
356512db612Schristos {
3571b6504a1Sitojun 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
3581b6504a1Sitojun 	const u_int8_t rxmode =
3591b6504a1Sitojun 	    CNW_RXCONF_RXENA | CNW_RXCONF_BCAST | CNW_RXCONF_AMP;
3601b6504a1Sitojun 
361512db612Schristos 	/* Reset the card */
362512db612Schristos 	cnw_reset(sc);
363512db612Schristos 
364512db612Schristos 	/* Issue a NOP to check the card */
365512db612Schristos 	CNW_CMD0(sc, CNW_CMD_NOP);
366512db612Schristos 
367512db612Schristos 	/* Set up receive configuration */
3681b6504a1Sitojun 	CNW_CMD1(sc, CNW_CMD_SRC,
3691b6504a1Sitojun 	    rxmode | ((ifp->if_flags & IFF_PROMISC) ? CNW_RXCONF_PRO : 0));
370512db612Schristos 
371512db612Schristos 	/* Set up transmit configuration */
372512db612Schristos 	CNW_CMD1(sc, CNW_CMD_STC, CNW_TXCONF_TXENA);
373512db612Schristos 
374512db612Schristos 	/* Set domain */
375512db612Schristos 	CNW_CMD2(sc, CNW_CMD_SMD, sc->sc_domain, sc->sc_domain >> 8);
376512db612Schristos 
377512db612Schristos 	/* Set scramble key */
378512db612Schristos 	CNW_CMD2(sc, CNW_CMD_SSK, sc->sc_skey, sc->sc_skey >> 8);
379512db612Schristos 
380512db612Schristos 	/* Enable interrupts */
381512db612Schristos 	WAIT_WOC(sc);
3825f1c4bb5Sitojun #ifndef MEMORY_MAPPED
383512db612Schristos 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
384512db612Schristos 	    CNW_REG_IMR, CNW_IMR_IENA | CNW_IMR_RFU1);
3855f1c4bb5Sitojun #else
3865f1c4bb5Sitojun 	bus_space_write_1(sc->sc_memt, sc->sc_memh,
3875f1c4bb5Sitojun 	    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_IMR,
3885f1c4bb5Sitojun 	    CNW_IMR_IENA | CNW_IMR_RFU1);
3895f1c4bb5Sitojun #endif
390512db612Schristos 
391512db612Schristos 	/* Enable receiver */
392512db612Schristos 	CNW_CMD0(sc, CNW_CMD_ER);
393512db612Schristos 
394512db612Schristos 	/* "Set the IENA bit in COR" */
395512db612Schristos 	WAIT_WOC(sc);
3965f1c4bb5Sitojun #ifndef MEMORY_MAPPED
397512db612Schristos 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_COR,
398512db612Schristos 	    CNW_COR_IENA | CNW_COR_LVLREQ);
3995f1c4bb5Sitojun #else
4005f1c4bb5Sitojun 	bus_space_write_1(sc->sc_memt, sc->sc_memh,
4015f1c4bb5Sitojun 	    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_COR,
4025f1c4bb5Sitojun 	    CNW_COR_IENA | CNW_COR_LVLREQ);
4035f1c4bb5Sitojun #endif
404512db612Schristos }
405512db612Schristos 
406512db612Schristos 
407512db612Schristos /*
408512db612Schristos  * Enable and initialize the card.
409512db612Schristos  */
4108d129e6eSmaxv static int
cnw_enable(struct cnw_softc * sc)411454af1c0Sdsl cnw_enable(struct cnw_softc *sc)
412512db612Schristos {
413512db612Schristos 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
414512db612Schristos 
4154e1b4b72Sitojun 	if ((ifp->if_flags & IFF_RUNNING) != 0)
4164e1b4b72Sitojun 		return (0);
4174e1b4b72Sitojun 
418512db612Schristos 	sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_NET, cnw_intr, sc);
419512db612Schristos 	if (sc->sc_ih == NULL) {
420cbab9cadSchs 		aprint_error_dev(sc->sc_dev, "couldn't establish interrupt handler\n");
421512db612Schristos 		return (EIO);
422512db612Schristos 	}
423512db612Schristos 	if (pcmcia_function_enable(sc->sc_pf) != 0) {
424cbab9cadSchs 		aprint_error_dev(sc->sc_dev, "couldn't enable card\n");
425512db612Schristos 		return (EIO);
426512db612Schristos 	}
4277aa608e5Sitojun 	sc->sc_resource |= CNW_RES_PCIC;
428512db612Schristos 	cnw_init(sc);
4299d17b2caSitojun 	ifp->if_flags &= ~IFF_OACTIVE;
430512db612Schristos 	ifp->if_flags |= IFF_RUNNING;
431512db612Schristos 	return (0);
432512db612Schristos }
433512db612Schristos 
434512db612Schristos 
435512db612Schristos /*
436512db612Schristos  * Stop and disable the card.
437512db612Schristos  */
4388d129e6eSmaxv static void
cnw_disable(struct cnw_softc * sc)439454af1c0Sdsl cnw_disable(struct cnw_softc *sc)
440512db612Schristos {
441512db612Schristos 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
442512db612Schristos 
4434e1b4b72Sitojun 	if ((ifp->if_flags & IFF_RUNNING) == 0)
4444e1b4b72Sitojun 		return;
4454e1b4b72Sitojun 
446512db612Schristos 	pcmcia_function_disable(sc->sc_pf);
4477aa608e5Sitojun 	sc->sc_resource &= ~CNW_RES_PCIC;
448512db612Schristos 	pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
449512db612Schristos 	ifp->if_flags &= ~IFF_RUNNING;
450512db612Schristos 	ifp->if_timer = 0;
451512db612Schristos }
452512db612Schristos 
453512db612Schristos 
454512db612Schristos /*
455512db612Schristos  * Match the hardware we handle.
456512db612Schristos  */
4578d129e6eSmaxv static int
cnw_match(device_t parent,cfdata_t match,void * aux)458cbab9cadSchs cnw_match(device_t parent, cfdata_t match, void *aux)
459512db612Schristos {
460512db612Schristos 	struct pcmcia_attach_args *pa = aux;
461512db612Schristos 
46263ce2b19Sgmcgarry 	if (pa->manufacturer == PCMCIA_VENDOR_XIRCOM &&
46363ce2b19Sgmcgarry 	    pa->product == PCMCIA_PRODUCT_XIRCOM_CNW_801)
4641b6504a1Sitojun 		return 1;
46563ce2b19Sgmcgarry 	if (pa->manufacturer == PCMCIA_VENDOR_XIRCOM &&
46663ce2b19Sgmcgarry 	    pa->product == PCMCIA_PRODUCT_XIRCOM_CNW_802)
4671b6504a1Sitojun 		return 1;
4681b6504a1Sitojun 	return 0;
469512db612Schristos }
470512db612Schristos 
471512db612Schristos 
472512db612Schristos /*
473512db612Schristos  * Attach the card.
474512db612Schristos  */
4758d129e6eSmaxv static void
cnw_attach(device_t parent,device_t self,void * aux)476cbab9cadSchs cnw_attach(device_t parent, device_t self, void *aux)
477512db612Schristos {
478cbab9cadSchs 	struct cnw_softc *sc = device_private(self);
479512db612Schristos 	struct pcmcia_attach_args *pa = aux;
480512db612Schristos 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
481512db612Schristos 	u_int8_t macaddr[ETHER_ADDR_LEN];
482512db612Schristos 	int i;
4835f1c4bb5Sitojun 	bus_size_t memsize;
484512db612Schristos 
485cbab9cadSchs 	sc->sc_dev = self;
4864e1b4b72Sitojun 	sc->sc_resource = 0;
4874e1b4b72Sitojun 
488512db612Schristos 	/* Enable the card */
489512db612Schristos 	sc->sc_pf = pa->pf;
49006de4264Slukem 	pcmcia_function_init(sc->sc_pf, SIMPLEQ_FIRST(&sc->sc_pf->cfe_head));
491512db612Schristos 	if (pcmcia_function_enable(sc->sc_pf)) {
492bebbf57bScegger 		aprint_error_dev(self, "function enable failed\n");
493512db612Schristos 		return;
494512db612Schristos 	}
4957aa608e5Sitojun 	sc->sc_resource |= CNW_RES_PCIC;
496512db612Schristos 
497512db612Schristos 	/* Map I/O register and "memory" */
4985f1c4bb5Sitojun #ifndef MEMORY_MAPPED
499512db612Schristos 	if (pcmcia_io_alloc(sc->sc_pf, 0, CNW_IO_SIZE, CNW_IO_SIZE,
500512db612Schristos 	    &sc->sc_pcioh) != 0) {
501bebbf57bScegger 		aprint_error_dev(self, "can't allocate i/o space\n");
5027aa608e5Sitojun 		goto fail;
503512db612Schristos 	}
504ebed70c6Smycroft 	if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_IO16, &sc->sc_pcioh,
505ebed70c6Smycroft 	    &sc->sc_iowin) != 0) {
506bebbf57bScegger 		aprint_error_dev(self, "can't map i/o space\n");
5077aa608e5Sitojun 		pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh);
5087aa608e5Sitojun 		goto fail;
509512db612Schristos 	}
510512db612Schristos 	sc->sc_iot = sc->sc_pcioh.iot;
511512db612Schristos 	sc->sc_ioh = sc->sc_pcioh.ioh;
5124e1b4b72Sitojun 	sc->sc_resource |= CNW_RES_IO;
5135f1c4bb5Sitojun #endif
5145f1c4bb5Sitojun #ifndef MEMORY_MAPPED
5155f1c4bb5Sitojun 	memsize = CNW_MEM_SIZE;
5165f1c4bb5Sitojun #else
5175f1c4bb5Sitojun 	memsize = CNW_MEM_SIZE + CNW_IOM_SIZE;
5185f1c4bb5Sitojun #endif
519d01f7f6eSitojun 	if (pcmcia_mem_alloc(sc->sc_pf, memsize, &sc->sc_pcmemh) != 0) {
520bebbf57bScegger 		aprint_error_dev(self, "can't allocate memory\n");
521d01f7f6eSitojun 		goto fail;
522d01f7f6eSitojun 	}
523beecfd48Sitojun 	if (pcmcia_mem_map(sc->sc_pf, PCMCIA_WIDTH_MEM8|PCMCIA_MEM_COMMON,
5245f1c4bb5Sitojun 	    CNW_MEM_ADDR, memsize, &sc->sc_pcmemh, &sc->sc_memoff,
525512db612Schristos 	    &sc->sc_memwin) != 0) {
526bebbf57bScegger 		aprint_error_dev(self, "can't map memory\n");
5274e1b4b72Sitojun 		pcmcia_mem_free(sc->sc_pf, &sc->sc_pcmemh);
5287aa608e5Sitojun 		goto fail;
529512db612Schristos 	}
530512db612Schristos 	sc->sc_memt = sc->sc_pcmemh.memt;
531512db612Schristos 	sc->sc_memh = sc->sc_pcmemh.memh;
5324e1b4b72Sitojun 	sc->sc_resource |= CNW_RES_MEM;
533512db612Schristos 
534512db612Schristos 	/* Finish setup of softc */
535512db612Schristos 	sc->sc_domain = cnw_domain;
536512db612Schristos 	sc->sc_skey = cnw_skey;
537512db612Schristos 
538512db612Schristos 	/* Get MAC address */
539512db612Schristos 	cnw_reset(sc);
540512db612Schristos 	for (i = 0; i < ETHER_ADDR_LEN; i++)
541512db612Schristos 		macaddr[i] = bus_space_read_1(sc->sc_memt, sc->sc_memh,
542512db612Schristos 		    sc->sc_memoff + CNW_EREG_PA + i);
543cbab9cadSchs 	printf("%s: address %s\n", device_xname(sc->sc_dev),
544512db612Schristos 	    ether_sprintf(macaddr));
545512db612Schristos 
546512db612Schristos 	/* Set up ifnet structure */
547cbab9cadSchs 	strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
548512db612Schristos 	ifp->if_softc = sc;
549512db612Schristos 	ifp->if_start = cnw_start;
550512db612Schristos 	ifp->if_ioctl = cnw_ioctl;
551512db612Schristos 	ifp->if_watchdog = cnw_watchdog;
552091e1526Smsaitoh 	ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
5536b16911aSthorpej 	IFQ_SET_READY(&ifp->if_snd);
554512db612Schristos 
555512db612Schristos 	/* Attach the interface */
556512db612Schristos 	if_attach(ifp);
557c0e7885fSozaki-r 	if_deferred_start_init(ifp, NULL);
558512db612Schristos 	ether_ifattach(ifp, macaddr);
559b84f740bSthorpej 
5604e1b4b72Sitojun 	sc->sc_resource |= CNW_RES_NET;
561512db612Schristos 
56291c44a72Sthorpej 	ifp->if_baudrate = IF_Mbps(1);
5631b6504a1Sitojun 
564512db612Schristos 	/* Disable the card now, and turn it on when the interface goes up */
565512db612Schristos 	pcmcia_function_disable(sc->sc_pf);
5667aa608e5Sitojun 	sc->sc_resource &= ~CNW_RES_PCIC;
5677aa608e5Sitojun 	return;
5687aa608e5Sitojun 
5697aa608e5Sitojun fail:
5705f1c4bb5Sitojun #ifndef MEMORY_MAPPED
5717aa608e5Sitojun 	if ((sc->sc_resource & CNW_RES_IO) != 0) {
5727aa608e5Sitojun 		pcmcia_io_unmap(sc->sc_pf, sc->sc_iowin);
5737aa608e5Sitojun 		pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh);
5747aa608e5Sitojun 		sc->sc_resource &= ~CNW_RES_IO;
5757aa608e5Sitojun 	}
5765f1c4bb5Sitojun #endif
5777aa608e5Sitojun 	if ((sc->sc_resource & CNW_RES_PCIC) != 0) {
5787aa608e5Sitojun 		pcmcia_function_disable(sc->sc_pf);
5797aa608e5Sitojun 		sc->sc_resource &= ~CNW_RES_PCIC;
5807aa608e5Sitojun 	}
581512db612Schristos }
582512db612Schristos 
583512db612Schristos /*
584512db612Schristos  * Start outputting on the interface.
585512db612Schristos  */
5868d129e6eSmaxv static void
cnw_start(struct ifnet * ifp)587454af1c0Sdsl cnw_start(struct ifnet *ifp)
588512db612Schristos {
589512db612Schristos 	struct cnw_softc *sc = ifp->if_softc;
590512db612Schristos 	struct mbuf *m0;
5919d17b2caSitojun 	int lif;
592512db612Schristos 	int asr;
5939d17b2caSitojun #ifdef ONE_AT_A_TIME
5949d17b2caSitojun 	struct timeval now;
5959d17b2caSitojun #endif
596512db612Schristos 
597512db612Schristos #ifdef CNW_DEBUG
598512db612Schristos 	if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
599512db612Schristos 		printf("%s: cnw_start\n", ifp->if_xname);
6009d17b2caSitojun 	if (ifp->if_flags & IFF_OACTIVE)
6019d17b2caSitojun 		printf("%s: cnw_start reentered\n", ifp->if_xname);
602512db612Schristos #endif
603512db612Schristos 
6049d17b2caSitojun 	ifp->if_flags |= IFF_OACTIVE;
6059d17b2caSitojun 
606512db612Schristos 	for (;;) {
6079d17b2caSitojun #ifdef ONE_AT_A_TIME
6089d17b2caSitojun 		microtime(&now);
6099d17b2caSitojun 		now.tv_sec -= sc->sc_txlast.tv_sec;
6109d17b2caSitojun 		now.tv_usec -= sc->sc_txlast.tv_usec;
6119d17b2caSitojun 		if (now.tv_usec < 0) {
6129d17b2caSitojun 			now.tv_usec += 1000000;
6139d17b2caSitojun 			now.tv_sec--;
6149d17b2caSitojun 		}
6159d17b2caSitojun 
6169d17b2caSitojun 		/*
6179d17b2caSitojun 		 * Don't ship this packet out until the last
6189d17b2caSitojun 		 * packet has left the building.
6199d17b2caSitojun 		 * If we have not tried to send a packet for 1/5
6209d17b2caSitojun 		 * a second then we assume we lost an interrupt,
6219d17b2caSitojun 		 * lets go on and send the next packet anyhow.
6229d17b2caSitojun 		 *
6239d17b2caSitojun 		 * I suppose we could check to see if it is okay
6249d17b2caSitojun 		 * to put additional packets on the card (beyond
6259d17b2caSitojun 		 * the one already waiting to be sent) but I don't
6269d17b2caSitojun 		 * think we would get any improvement in speed as
6279d17b2caSitojun 		 * we should have ample time to put the next packet
6289d17b2caSitojun 		 * on while this one is going out.
6299d17b2caSitojun 		 */
6309d17b2caSitojun 		if (sc->sc_active && now.tv_sec == 0 && now.tv_usec < 200000)
6319d17b2caSitojun 			break;
6329d17b2caSitojun #endif
6339d17b2caSitojun 
6349d17b2caSitojun 		/* Make sure the link integrity field is on */
6359d17b2caSitojun 		WAIT_WOC(sc);
6369d17b2caSitojun 		lif = bus_space_read_1(sc->sc_memt, sc->sc_memh,
6379d17b2caSitojun 		    sc->sc_memoff + CNW_EREG_LIF);
6389d17b2caSitojun 		if (lif == 0) {
6399d17b2caSitojun #ifdef CNW_DEBUG
6409d17b2caSitojun 			if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
64135ca6c8bSchristos 				printf("%s: link integrity %d\n", ifp->if_xname, lif);
6429d17b2caSitojun #endif
6439d17b2caSitojun 			break;
6449d17b2caSitojun 		}
6459d17b2caSitojun 
646512db612Schristos 		/* Is there any buffer space available on the card? */
647512db612Schristos 		WAIT_WOC(sc);
6485f1c4bb5Sitojun #ifndef MEMORY_MAPPED
649512db612Schristos 		asr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR);
6505f1c4bb5Sitojun #else
6515f1c4bb5Sitojun 		asr = bus_space_read_1(sc->sc_memt, sc->sc_memh,
6525f1c4bb5Sitojun 		    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR);
6535f1c4bb5Sitojun #endif
654512db612Schristos 		if (!(asr & CNW_ASR_TXBA)) {
655512db612Schristos #ifdef CNW_DEBUG
656512db612Schristos 			if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
657512db612Schristos 				printf("%s: no buffer space\n", ifp->if_xname);
658512db612Schristos #endif
6599d17b2caSitojun 			break;
660512db612Schristos 		}
661512db612Schristos 
6621b6504a1Sitojun 		sc->sc_stats.nws_tx++;
6631b6504a1Sitojun 
6646b16911aSthorpej 		IFQ_DEQUEUE(&ifp->if_snd, m0);
665512db612Schristos 		if (m0 == 0)
6669d17b2caSitojun 			break;
667512db612Schristos 
6683cd62456Smsaitoh 		bpf_mtap(ifp, m0, BPF_D_OUT);
669512db612Schristos 
670512db612Schristos 		cnw_transmit(sc, m0);
671*2d501ecfSthorpej 		if_statinc(ifp, if_opackets);
672512db612Schristos 		ifp->if_timer = 3; /* start watchdog timer */
6739d17b2caSitojun 
6749d17b2caSitojun 		microtime(&sc->sc_txlast);
6759d17b2caSitojun 		sc->sc_active = 1;
676512db612Schristos 	}
677512db612Schristos 
6789d17b2caSitojun 	ifp->if_flags &= ~IFF_OACTIVE;
6799d17b2caSitojun }
680512db612Schristos 
681512db612Schristos /*
682512db612Schristos  * Transmit a packet.
683512db612Schristos  */
6848d129e6eSmaxv static void
cnw_transmit(struct cnw_softc * sc,struct mbuf * m0)685454af1c0Sdsl cnw_transmit(struct cnw_softc *sc, struct mbuf *m0)
686512db612Schristos {
687512db612Schristos 	int buffer, bufsize, bufoffset, bufptr, bufspace, len, mbytes, n;
688512db612Schristos 	struct mbuf *m;
689512db612Schristos 	u_int8_t *mptr;
690512db612Schristos 
691512db612Schristos 	/* Get buffer info from card */
692512db612Schristos 	buffer = read16(sc, CNW_EREG_TDP);
693512db612Schristos 	bufsize = read16(sc, CNW_EREG_TDP + 2);
694512db612Schristos 	bufoffset = read16(sc, CNW_EREG_TDP + 4);
695512db612Schristos #ifdef CNW_DEBUG
696512db612Schristos 	if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
697512db612Schristos 		printf("%s: cnw_transmit b=0x%x s=%d o=0x%x\n",
698cbab9cadSchs 		    device_xname(sc->sc_dev), buffer, bufsize, bufoffset);
699512db612Schristos #endif
700512db612Schristos 
701512db612Schristos 	/* Copy data from mbuf chain to card buffers */
702512db612Schristos 	bufptr = sc->sc_memoff + buffer + bufoffset;
703512db612Schristos 	bufspace = bufsize;
704512db612Schristos 	len = 0;
705512db612Schristos 	for (m = m0; m; ) {
706512db612Schristos 		mptr = mtod(m, u_int8_t *);
707512db612Schristos 		mbytes = m->m_len;
708512db612Schristos 		len += mbytes;
709512db612Schristos 		while (mbytes > 0) {
710512db612Schristos 			if (bufspace == 0) {
711512db612Schristos 				buffer = read16(sc, buffer);
712512db612Schristos 				bufptr = sc->sc_memoff + buffer + bufoffset;
713512db612Schristos 				bufspace = bufsize;
714512db612Schristos #ifdef CNW_DEBUG
715512db612Schristos 				if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
716512db612Schristos 					printf("%s:   next buffer @0x%x\n",
717cbab9cadSchs 					    device_xname(sc->sc_dev), buffer);
718512db612Schristos #endif
719512db612Schristos 			}
720512db612Schristos 			n = mbytes <= bufspace ? mbytes : bufspace;
721512db612Schristos 			bus_space_write_region_1(sc->sc_memt, sc->sc_memh,
722512db612Schristos 			    bufptr, mptr, n);
723512db612Schristos 			bufptr += n;
724512db612Schristos 			bufspace -= n;
725512db612Schristos 			mptr += n;
726512db612Schristos 			mbytes -= n;
727512db612Schristos 		}
7289c7db92fSchristos 		m = m0 = m_free(m);
729512db612Schristos 	}
730512db612Schristos 
731512db612Schristos 	/* Issue transmit command */
732512db612Schristos 	CNW_CMD2(sc, CNW_CMD_TL, len, len >> 8);
733512db612Schristos }
734512db612Schristos 
735512db612Schristos 
736512db612Schristos /*
737512db612Schristos  * Pull a packet from the card into an mbuf chain.
738512db612Schristos  */
7398d129e6eSmaxv static struct mbuf *
cnw_read(struct cnw_softc * sc)740454af1c0Sdsl cnw_read(struct cnw_softc *sc)
741512db612Schristos {
742512db612Schristos 	struct mbuf *m, *top, **mp;
743512db612Schristos 	int totbytes, buffer, bufbytes, bufptr, mbytes, n;
744512db612Schristos 	u_int8_t *mptr;
745512db612Schristos 
746512db612Schristos 	WAIT_WOC(sc);
747512db612Schristos 	totbytes = read16(sc, CNW_EREG_RDP);
748512db612Schristos #ifdef CNW_DEBUG
749512db612Schristos 	if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
750cbab9cadSchs 		printf("%s: recv %d bytes\n", device_xname(sc->sc_dev), totbytes);
751512db612Schristos #endif
752512db612Schristos 	buffer = CNW_EREG_RDP + 2;
753512db612Schristos 	bufbytes = 0;
754512db612Schristos 	bufptr = 0; /* XXX make gcc happy */
755512db612Schristos 
756512db612Schristos 	MGETHDR(m, M_DONTWAIT, MT_DATA);
757512db612Schristos 	if (m == 0)
758512db612Schristos 		return (0);
759d938d837Sozaki-r 	m_set_rcvif(m, &sc->sc_ethercom.ec_if);
760512db612Schristos 	m->m_pkthdr.len = totbytes;
761512db612Schristos 	mbytes = MHLEN;
762512db612Schristos 	top = 0;
763512db612Schristos 	mp = &top;
764512db612Schristos 
765512db612Schristos 	while (totbytes > 0) {
766512db612Schristos 		if (top) {
767512db612Schristos 			MGET(m, M_DONTWAIT, MT_DATA);
768512db612Schristos 			if (m == 0) {
769512db612Schristos 				m_freem(top);
770512db612Schristos 				return (0);
771512db612Schristos 			}
772512db612Schristos 			mbytes = MLEN;
773512db612Schristos 		}
774512db612Schristos 		if (totbytes >= MINCLSIZE) {
775512db612Schristos 			MCLGET(m, M_DONTWAIT);
776512db612Schristos 			if ((m->m_flags & M_EXT) == 0) {
777512db612Schristos 				m_free(m);
778512db612Schristos 				m_freem(top);
779512db612Schristos 				return (0);
780512db612Schristos 			}
781512db612Schristos 			mbytes = MCLBYTES;
782512db612Schristos 		}
783512db612Schristos 		if (!top) {
784512db612Schristos 			int pad = ALIGN(sizeof(struct ether_header)) -
785512db612Schristos 			    sizeof(struct ether_header);
786512db612Schristos 			m->m_data += pad;
787512db612Schristos 			mbytes -= pad;
788512db612Schristos 		}
789512db612Schristos 		mptr = mtod(m, u_int8_t *);
790d1579b2dSriastradh 		mbytes = m->m_len = uimin(totbytes, mbytes);
791512db612Schristos 		totbytes -= mbytes;
792512db612Schristos 		while (mbytes > 0) {
793512db612Schristos 			if (bufbytes == 0) {
794512db612Schristos 				buffer = read16(sc, buffer);
795512db612Schristos 				bufbytes = read16(sc, buffer + 2);
796512db612Schristos 				bufptr = sc->sc_memoff + buffer +
797512db612Schristos 				    read16(sc, buffer + 4);
798512db612Schristos #ifdef CNW_DEBUG
799512db612Schristos 				if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
80035ca6c8bSchristos 					printf("%s:   %d bytes @0x%x+0x%lx\n",
801cbab9cadSchs 					    device_xname(sc->sc_dev), bufbytes,
80273920b41Sbouyer 					    buffer, (u_long)(bufptr - buffer -
80373920b41Sbouyer 					    sc->sc_memoff));
804512db612Schristos #endif
805512db612Schristos 			}
806512db612Schristos 			n = mbytes <= bufbytes ? mbytes : bufbytes;
807512db612Schristos 			bus_space_read_region_1(sc->sc_memt, sc->sc_memh,
808512db612Schristos 			    bufptr, mptr, n);
809512db612Schristos 			bufbytes -= n;
810512db612Schristos 			bufptr += n;
811512db612Schristos 			mbytes -= n;
812512db612Schristos 			mptr += n;
813512db612Schristos 		}
814512db612Schristos 		*mp = m;
815512db612Schristos 		mp = &m->m_next;
816512db612Schristos 	}
817512db612Schristos 
818512db612Schristos 	return (top);
819512db612Schristos }
820512db612Schristos 
821512db612Schristos 
822512db612Schristos /*
823512db612Schristos  * Handle received packets.
824512db612Schristos  */
8258d129e6eSmaxv static void
cnw_recv(struct cnw_softc * sc)826454af1c0Sdsl cnw_recv(struct cnw_softc *sc)
827512db612Schristos {
828512db612Schristos 	int rser;
829512db612Schristos 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
830512db612Schristos 	struct mbuf *m;
831512db612Schristos 
832512db612Schristos 	for (;;) {
833512db612Schristos 		WAIT_WOC(sc);
834512db612Schristos 		rser = bus_space_read_1(sc->sc_memt, sc->sc_memh,
835512db612Schristos 		    sc->sc_memoff + CNW_EREG_RSER);
836512db612Schristos 		if (!(rser & CNW_RSER_RXAVAIL))
837512db612Schristos 			return;
838512db612Schristos 
839512db612Schristos 		/* Pull packet off card */
840512db612Schristos 		m = cnw_read(sc);
841512db612Schristos 
842512db612Schristos 		/* Acknowledge packet */
843512db612Schristos 		CNW_CMD0(sc, CNW_CMD_SRP);
844512db612Schristos 
845512db612Schristos 		/* Did we manage to get the packet from the interface? */
846512db612Schristos 		if (m == 0) {
847*2d501ecfSthorpej 			if_statinc(ifp, if_ierrors);
848512db612Schristos 			return;
849512db612Schristos 		}
850512db612Schristos 
851f98d358aSthorpej 		/* Pass the packet up. */
8529c4cd063Sozaki-r 		if_percpuq_enqueue(ifp->if_percpuq, m);
853512db612Schristos 	}
854512db612Schristos }
855512db612Schristos 
856512db612Schristos 
857512db612Schristos /*
858512db612Schristos  * Interrupt handler.
859512db612Schristos  */
8608d129e6eSmaxv static int
cnw_intr(void * arg)861454af1c0Sdsl cnw_intr(void *arg)
862512db612Schristos {
863512db612Schristos 	struct cnw_softc *sc = arg;
864512db612Schristos 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
865512db612Schristos 	int ret, status, rser, tser;
866512db612Schristos 
8674e1b4b72Sitojun 	if ((sc->sc_ethercom.ec_if.if_flags & IFF_RUNNING) == 0 ||
868cbab9cadSchs 	    !device_is_active(sc->sc_dev))
869512db612Schristos 		return (0);
870512db612Schristos 	ifp->if_timer = 0;	/* stop watchdog timer */
871512db612Schristos 
872512db612Schristos 	ret = 0;
873512db612Schristos 	for (;;) {
874512db612Schristos 		WAIT_WOC(sc);
8755f1c4bb5Sitojun #ifndef MEMORY_MAPPED
8765f1c4bb5Sitojun 		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
8775f1c4bb5Sitojun 		    CNW_REG_CCSR);
8785f1c4bb5Sitojun #else
8795f1c4bb5Sitojun 		status = bus_space_read_1(sc->sc_memt, sc->sc_memh,
8805f1c4bb5Sitojun 		    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_CCSR);
8815f1c4bb5Sitojun #endif
8829a2a9c3fSdsl 		if (!(status & 0x02))
8839a2a9c3fSdsl 			/* No more commands, or shared IRQ */
884512db612Schristos 			return (ret);
885512db612Schristos 		ret = 1;
8865f1c4bb5Sitojun #ifndef MEMORY_MAPPED
887512db612Schristos 		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR);
8885f1c4bb5Sitojun #else
8895f1c4bb5Sitojun 		status = bus_space_read_1(sc->sc_memt, sc->sc_memh,
8905f1c4bb5Sitojun 		    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR);
8915f1c4bb5Sitojun #endif
892512db612Schristos 
893512db612Schristos 		/* Anything to receive? */
8941b6504a1Sitojun 		if (status & CNW_ASR_RXRDY) {
8951b6504a1Sitojun 			sc->sc_stats.nws_rx++;
896512db612Schristos 			cnw_recv(sc);
8971b6504a1Sitojun 		}
898512db612Schristos 
899512db612Schristos 		/* Receive error */
900512db612Schristos 		if (status & CNW_ASR_RXERR) {
901512db612Schristos 			/*
902512db612Schristos 			 * I get a *lot* of spurious receive errors
903512db612Schristos 			 * (many per second), even when the interface
904512db612Schristos 			 * is quiescent, so we don't increment
905512db612Schristos 			 * if_ierrors here.
906512db612Schristos 			 */
907512db612Schristos 			rser = bus_space_read_1(sc->sc_memt, sc->sc_memh,
908512db612Schristos 			    sc->sc_memoff + CNW_EREG_RSER);
9091b6504a1Sitojun 
9101b6504a1Sitojun 			/* RX statistics */
9111b6504a1Sitojun 			sc->sc_stats.nws_rxerr++;
9121b6504a1Sitojun 			if (rser & CNW_RSER_RXBIG)
9131b6504a1Sitojun 				sc->sc_stats.nws_rxframe++;
9141b6504a1Sitojun 			if (rser & CNW_RSER_RXCRC)
9151b6504a1Sitojun 				sc->sc_stats.nws_rxcrcerror++;
9161b6504a1Sitojun 			if (rser & CNW_RSER_RXOVERRUN)
9171b6504a1Sitojun 				sc->sc_stats.nws_rxoverrun++;
9181b6504a1Sitojun 			if (rser & CNW_RSER_RXOVERFLOW)
9191b6504a1Sitojun 				sc->sc_stats.nws_rxoverflow++;
9201b6504a1Sitojun 			if (rser & CNW_RSER_RXERR)
9211b6504a1Sitojun 				sc->sc_stats.nws_rxerrors++;
9221b6504a1Sitojun 			if (rser & CNW_RSER_RXAVAIL)
9231b6504a1Sitojun 				sc->sc_stats.nws_rxavail++;
9241b6504a1Sitojun 
925512db612Schristos 			/* Clear error bits in RSER */
926512db612Schristos 			WAIT_WOC(sc);
927512db612Schristos 			bus_space_write_1(sc->sc_memt, sc->sc_memh,
928512db612Schristos 			    sc->sc_memoff + CNW_EREG_RSERW,
929512db612Schristos 			    CNW_RSER_RXERR |
930512db612Schristos 			    (rser & (CNW_RSER_RXCRC | CNW_RSER_RXBIG)));
931512db612Schristos 			/* Clear RXERR in ASR */
932512db612Schristos 			WAIT_WOC(sc);
933512db612Schristos 			bus_space_write_1(sc->sc_memt, sc->sc_memh,
934512db612Schristos 			    sc->sc_memoff + CNW_EREG_ASCC, CNW_ASR_RXERR);
935512db612Schristos 		}
936512db612Schristos 
937512db612Schristos 		/* Transmit done */
938512db612Schristos 		if (status & CNW_ASR_TXDN) {
939512db612Schristos 			tser = bus_space_read_1(sc->sc_memt, sc->sc_memh,
940512db612Schristos 						CNW_EREG_TSER);
9411b6504a1Sitojun 
9421b6504a1Sitojun 			/* TX statistics */
9431b6504a1Sitojun 			if (tser & CNW_TSER_TXERR)
9441b6504a1Sitojun 				sc->sc_stats.nws_txerrors++;
9451b6504a1Sitojun 			if (tser & CNW_TSER_TXNOAP)
9461b6504a1Sitojun 				sc->sc_stats.nws_txlostcd++;
9471b6504a1Sitojun 			if (tser & CNW_TSER_TXGU)
9481b6504a1Sitojun 				sc->sc_stats.nws_txabort++;
9491b6504a1Sitojun 
950512db612Schristos 			if (tser & CNW_TSER_TXOK) {
9511b6504a1Sitojun 				sc->sc_stats.nws_txokay++;
9521b6504a1Sitojun 				sc->sc_stats.nws_txretries[status & 0xf]++;
953512db612Schristos 				WAIT_WOC(sc);
954512db612Schristos 				bus_space_write_1(sc->sc_memt, sc->sc_memh,
955512db612Schristos 				    sc->sc_memoff + CNW_EREG_TSERW,
956512db612Schristos 				    CNW_TSER_TXOK | CNW_TSER_RTRY);
957512db612Schristos 			}
9581b6504a1Sitojun 
959512db612Schristos 			if (tser & CNW_TSER_ERROR) {
960*2d501ecfSthorpej 				if_statinc(ifp, if_oerrors);
961512db612Schristos 				WAIT_WOC(sc);
962512db612Schristos 				bus_space_write_1(sc->sc_memt, sc->sc_memh,
963512db612Schristos 				    sc->sc_memoff + CNW_EREG_TSERW,
964512db612Schristos 				    (tser & CNW_TSER_ERROR) |
965512db612Schristos 				    CNW_TSER_RTRY);
966512db612Schristos 			}
9679d17b2caSitojun 
9689d17b2caSitojun 			sc->sc_active = 0;
9699d17b2caSitojun 			ifp->if_flags &= ~IFF_OACTIVE;
9709d17b2caSitojun 
971512db612Schristos 			/* Continue to send packets from the queue */
972c0e7885fSozaki-r 			if_schedule_deferred_start(&sc->sc_ethercom.ec_if);
973512db612Schristos 		}
974512db612Schristos 
975512db612Schristos 	}
976512db612Schristos }
977512db612Schristos 
978512db612Schristos 
979512db612Schristos /*
980512db612Schristos  * Handle device ioctls.
981512db612Schristos  */
9828d129e6eSmaxv static int
cnw_ioctl(struct ifnet * ifp,u_long cmd,void * data)983de87fe67Sdyoung cnw_ioctl(struct ifnet *ifp, u_long cmd, void *data)
984512db612Schristos {
985512db612Schristos 	struct cnw_softc *sc = ifp->if_softc;
986512db612Schristos 	struct ifaddr *ifa = (struct ifaddr *)data;
9871b6504a1Sitojun 	struct ifreq *ifr = (struct ifreq *)data;
988512db612Schristos 	int s, error = 0;
9893029ac48Sad 	struct lwp *l = curlwp;	/*XXX*/
990512db612Schristos 
991211d7b1cSelad 	switch (cmd) {
992de87fe67Sdyoung 	case SIOCINITIFADDR:
993211d7b1cSelad 	case SIOCSIFFLAGS:
994211d7b1cSelad 	case SIOCADDMULTI:
995211d7b1cSelad 	case SIOCDELMULTI:
996211d7b1cSelad 	case SIOCGCNWDOMAIN:
997211d7b1cSelad 	case SIOCGCNWSTATS:
998211d7b1cSelad 		break;
999211d7b1cSelad 	case SIOCSCNWDOMAIN:
1000211d7b1cSelad 	case SIOCSCNWKEY:
10012d1c9683Selad 		error = kauth_authorize_network(l->l_cred,
10022d1c9683Selad 		    KAUTH_NETWORK_INTERFACE,
10032d1c9683Selad 		    KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, KAUTH_ARG(cmd),
10042d1c9683Selad 		    NULL);
10052d1c9683Selad 		if (error)
10062d1c9683Selad 			return (error);
10072d1c9683Selad 		break;
1008211d7b1cSelad 	case SIOCGCNWSTATUS:
10092d1c9683Selad 		error = kauth_authorize_network(l->l_cred,
10102d1c9683Selad 		    KAUTH_NETWORK_INTERFACE,
10112d1c9683Selad 		    KAUTH_REQ_NETWORK_INTERFACE_GETPRIV, ifp, KAUTH_ARG(cmd),
10122d1c9683Selad 		    NULL);
1013211d7b1cSelad 		if (error)
1014211d7b1cSelad 			return (error);
1015211d7b1cSelad 		break;
1016211d7b1cSelad 	default:
1017211d7b1cSelad 		return (EINVAL);
1018211d7b1cSelad 	}
1019211d7b1cSelad 
1020512db612Schristos 	s = splnet();
1021512db612Schristos 
1022512db612Schristos 	switch (cmd) {
1023512db612Schristos 
1024de87fe67Sdyoung 	case SIOCINITIFADDR:
1025512db612Schristos 		if (!(ifp->if_flags & IFF_RUNNING) &&
1026512db612Schristos 		    (error = cnw_enable(sc)) != 0)
1027512db612Schristos 			break;
1028512db612Schristos 		ifp->if_flags |= IFF_UP;
1029de87fe67Sdyoung 		cnw_init(sc);
1030512db612Schristos 		switch (ifa->ifa_addr->sa_family) {
1031512db612Schristos #ifdef INET
1032512db612Schristos 		case AF_INET:
1033512db612Schristos 			arp_ifinit(&sc->sc_ethercom.ec_if, ifa);
1034512db612Schristos 			break;
1035512db612Schristos #endif
10361b6504a1Sitojun 		default:
10371b6504a1Sitojun 			break;
1038512db612Schristos 		}
1039512db612Schristos 		break;
1040512db612Schristos 
1041512db612Schristos 	case SIOCSIFFLAGS:
1042de87fe67Sdyoung 		if ((error = ifioctl_common(ifp, cmd, data)) != 0)
1043de87fe67Sdyoung 			break;
1044de87fe67Sdyoung 		/* XXX re-use ether_ioctl() */
1045de87fe67Sdyoung 		switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) {
1046de87fe67Sdyoung 		case IFF_RUNNING:
1047512db612Schristos 			/*
1048512db612Schristos 			 * The interface is marked down and it is running, so
1049512db612Schristos 			 * stop it.
1050512db612Schristos 			 */
1051512db612Schristos 			cnw_disable(sc);
1052de87fe67Sdyoung 			break;
1053de87fe67Sdyoung 		case IFF_UP:
1054512db612Schristos 			/*
1055512db612Schristos 			 * The interface is marked up and it is stopped, so
1056512db612Schristos 			 * start it.
1057512db612Schristos 			 */
1058512db612Schristos 			error = cnw_enable(sc);
1059de87fe67Sdyoung 			break;
1060de87fe67Sdyoung 		default:
10611b6504a1Sitojun 			/* IFF_PROMISC may be changed */
10621b6504a1Sitojun 			cnw_init(sc);
1063de87fe67Sdyoung 			break;
1064512db612Schristos 		}
1065512db612Schristos 		break;
1066512db612Schristos 
10671b6504a1Sitojun 	case SIOCADDMULTI:
10681b6504a1Sitojun 	case SIOCDELMULTI:
10691b6504a1Sitojun 		/* Update our multicast list. */
1070dcd89234Sdyoung 		if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) {
1071e9818f5bSthorpej 			if (ifp->if_flags & IFF_RUNNING)
10721b6504a1Sitojun 				cnw_init(sc);
10731b6504a1Sitojun 			error = 0;
10741b6504a1Sitojun 		}
10751b6504a1Sitojun 		break;
10761b6504a1Sitojun 
10771b6504a1Sitojun 	case SIOCGCNWDOMAIN:
1078dcd89234Sdyoung 		ifr->ifr_domain = sc->sc_domain;
10791b6504a1Sitojun 		break;
10801b6504a1Sitojun 
10811b6504a1Sitojun 	case SIOCSCNWDOMAIN:
10821b6504a1Sitojun 		error = cnw_setdomain(sc, ifr->ifr_domain);
10831b6504a1Sitojun 		break;
10841b6504a1Sitojun 
10851b6504a1Sitojun 	case SIOCSCNWKEY:
10861b6504a1Sitojun 		error = cnw_setkey(sc, ifr->ifr_key);
10871b6504a1Sitojun 		break;
10881b6504a1Sitojun 
10891b6504a1Sitojun 	case SIOCGCNWSTATUS:
10901b6504a1Sitojun 		if ((ifp->if_flags & IFF_RUNNING) == 0)
10911b6504a1Sitojun 			break;
10921b6504a1Sitojun 		bus_space_read_region_1(sc->sc_memt, sc->sc_memh,
10931b6504a1Sitojun 		    sc->sc_memoff + CNW_EREG_CB,
10941b6504a1Sitojun 		    ((struct cnwstatus *)data)->data,
10951b6504a1Sitojun 		    sizeof(((struct cnwstatus *)data)->data));
10961b6504a1Sitojun 		break;
10971b6504a1Sitojun 
10981b6504a1Sitojun 	case SIOCGCNWSTATS:
10993ae6eaeaSthorpej 		memcpy((void *)&(((struct cnwistats *)data)->stats),
11003ae6eaeaSthorpej 		    (void *)&sc->sc_stats, sizeof(struct cnwstats));
11011b6504a1Sitojun 			break;
11021b6504a1Sitojun 
1103512db612Schristos 	default:
1104de87fe67Sdyoung 		error = ether_ioctl(ifp, cmd, data);
1105512db612Schristos 		break;
1106512db612Schristos 	}
1107512db612Schristos 
1108512db612Schristos 	splx(s);
1109512db612Schristos 	return (error);
1110512db612Schristos }
1111512db612Schristos 
1112512db612Schristos 
1113512db612Schristos /*
1114512db612Schristos  * Device timeout/watchdog routine. Entered if the device neglects to
1115512db612Schristos  * generate an interrupt after a transmit has been started on it.
1116512db612Schristos  */
11178d129e6eSmaxv static void
cnw_watchdog(struct ifnet * ifp)1118454af1c0Sdsl cnw_watchdog(struct ifnet *ifp)
1119512db612Schristos {
1120512db612Schristos 	struct cnw_softc *sc = ifp->if_softc;
1121512db612Schristos 
1122cbab9cadSchs 	printf("%s: device timeout; card reset\n", device_xname(sc->sc_dev));
1123*2d501ecfSthorpej 	if_statinc(ifp, if_oerrors);
1124512db612Schristos 	cnw_init(sc);
1125512db612Schristos }
11261b6504a1Sitojun 
11278d129e6eSmaxv static int
cnw_setdomain(struct cnw_softc * sc,int domain)1128454af1c0Sdsl cnw_setdomain(struct cnw_softc *sc, int domain)
11291b6504a1Sitojun {
11301b6504a1Sitojun 	int s;
11311b6504a1Sitojun 
11321b6504a1Sitojun 	if (domain & ~0x1ff)
11331b6504a1Sitojun 		return EINVAL;
11341b6504a1Sitojun 
11351b6504a1Sitojun 	s = splnet();
11361b6504a1Sitojun 	CNW_CMD2(sc, CNW_CMD_SMD, domain, domain >> 8);
11371b6504a1Sitojun 	splx(s);
11381b6504a1Sitojun 
11391b6504a1Sitojun 	sc->sc_domain = domain;
11401b6504a1Sitojun 	return 0;
11411b6504a1Sitojun }
11421b6504a1Sitojun 
11438d129e6eSmaxv static int
cnw_setkey(struct cnw_softc * sc,int key)1144454af1c0Sdsl cnw_setkey(struct cnw_softc *sc, int key)
11451b6504a1Sitojun {
11461b6504a1Sitojun 	int s;
11471b6504a1Sitojun 
11481b6504a1Sitojun 	if (key & ~0xffff)
11491b6504a1Sitojun 		return EINVAL;
11501b6504a1Sitojun 
11511b6504a1Sitojun 	s = splnet();
11521b6504a1Sitojun 	CNW_CMD2(sc, CNW_CMD_SSK, key, key >> 8);
11531b6504a1Sitojun 	splx(s);
11541b6504a1Sitojun 
11551b6504a1Sitojun 	sc->sc_skey = key;
11561b6504a1Sitojun 	return 0;
11571b6504a1Sitojun }
11584e1b4b72Sitojun 
11598d129e6eSmaxv static int
cnw_activate(device_t self,enum devact act)11607cf29912Scegger cnw_activate(device_t self, enum devact act)
11614e1b4b72Sitojun {
1162cbab9cadSchs 	struct cnw_softc *sc = device_private(self);
11634e1b4b72Sitojun 
11644e1b4b72Sitojun 	switch (act) {
11654e1b4b72Sitojun 	case DVACT_DEACTIVATE:
11664e1b4b72Sitojun 		if_deactivate(&sc->sc_ethercom.ec_if);
1167903d1910Sdyoung 		return 0;
1168903d1910Sdyoung 	default:
1169903d1910Sdyoung 		return EOPNOTSUPP;
11704e1b4b72Sitojun 	}
11714e1b4b72Sitojun }
11724e1b4b72Sitojun 
11738d129e6eSmaxv static int
cnw_detach(device_t self,int flags)11747cf29912Scegger cnw_detach(device_t self, int flags)
11754e1b4b72Sitojun {
1176cbab9cadSchs 	struct cnw_softc *sc = device_private(self);
11774e1b4b72Sitojun 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
11784e1b4b72Sitojun 
11794e1b4b72Sitojun 	/* cnw_disable() checks IFF_RUNNING */
11804e1b4b72Sitojun 	cnw_disable(sc);
11814e1b4b72Sitojun 
11824e1b4b72Sitojun 	if ((sc->sc_resource & CNW_RES_NET) != 0) {
11834e1b4b72Sitojun 		ether_ifdetach(ifp);
11844e1b4b72Sitojun 		if_detach(ifp);
11854e1b4b72Sitojun 	}
11864e1b4b72Sitojun 
11875f1c4bb5Sitojun #ifndef MEMORY_MAPPED
11884e1b4b72Sitojun 	/* unmap and free our i/o windows */
11894e1b4b72Sitojun 	if ((sc->sc_resource & CNW_RES_IO) != 0) {
11904e1b4b72Sitojun 		pcmcia_io_unmap(sc->sc_pf, sc->sc_iowin);
11914e1b4b72Sitojun 		pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh);
11924e1b4b72Sitojun 	}
11935f1c4bb5Sitojun #endif
11944e1b4b72Sitojun 
11954e1b4b72Sitojun 	/* unmap and free our memory windows */
11964e1b4b72Sitojun 	if ((sc->sc_resource & CNW_RES_MEM) != 0) {
11974e1b4b72Sitojun 		pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwin);
11984e1b4b72Sitojun 		pcmcia_mem_free(sc->sc_pf, &sc->sc_pcmemh);
11994e1b4b72Sitojun 	}
12004e1b4b72Sitojun 
12014e1b4b72Sitojun 	return (0);
12024e1b4b72Sitojun }
1203