if_cnw.c revision 1.63
1230557Sjimharris/*	$NetBSD: if_cnw.c,v 1.63 2018/06/26 06:48:01 msaitoh Exp $	*/
2230557Sjimharris
3230557Sjimharris/*-
4230557Sjimharris * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
5230557Sjimharris * All rights reserved.
6230557Sjimharris *
7230557Sjimharris * This code is derived from software contributed to The NetBSD Foundation
8230557Sjimharris * by Michael Eriksson.
9230557Sjimharris *
10230557Sjimharris * Redistribution and use in source and binary forms, with or without
11230557Sjimharris * modification, are permitted provided that the following conditions
12230557Sjimharris * are met:
13230557Sjimharris * 1. Redistributions of source code must retain the above copyright
14230557Sjimharris *    notice, this list of conditions and the following disclaimer.
15230557Sjimharris * 2. Redistributions in binary form must reproduce the above copyright
16230557Sjimharris *    notice, this list of conditions and the following disclaimer in the
17230557Sjimharris *    documentation and/or other materials provided with the distribution.
18230557Sjimharris *
19230557Sjimharris * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20230557Sjimharris * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21230557Sjimharris * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22230557Sjimharris * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23230557Sjimharris * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24230557Sjimharris * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25230557Sjimharris * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26230557Sjimharris * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27230557Sjimharris * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28230557Sjimharris * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29230557Sjimharris * POSSIBILITY OF SUCH DAMAGE.
30230557Sjimharris */
31230557Sjimharris
32230557Sjimharris/*
33230557Sjimharris * Copyright (c) 1996, 1997 Berkeley Software Design, Inc.
34230557Sjimharris * All rights reserved.
35230557Sjimharris *
36230557Sjimharris * Redistribution and use in source and binary forms, with or without
37230557Sjimharris * modification, are permitted provided that this notice is retained,
38230557Sjimharris * the conditions in the following notices are met, and terms applying
39230557Sjimharris * to contributors in the following notices also apply to Berkeley
40230557Sjimharris * Software Design, Inc.
41230557Sjimharris *
42230557Sjimharris * 1. Redistributions of source code must retain the above copyright
43230557Sjimharris *    notice, this list of conditions and the following disclaimer.
44230557Sjimharris * 2. Redistributions in binary form must reproduce the above copyright
45230557Sjimharris *    notice, this list of conditions and the following disclaimer in the
46230557Sjimharris *    documentation and/or other materials provided with the distribution.
47230557Sjimharris * 3. All advertising materials mentioning features or use of this software
48230557Sjimharris *    must display the following acknowledgement:
49230557Sjimharris *      This product includes software developed by
50230557Sjimharris *	Berkeley Software Design, Inc.
51230557Sjimharris * 4. Neither the name of the Berkeley Software Design, Inc. nor the names
52230557Sjimharris *    of its contributors may be used to endorse or promote products derived
53230557Sjimharris *    from this software without specific prior written permission.
54230557Sjimharris *
55230557Sjimharris * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
56230557Sjimharris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57230557Sjimharris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58230557Sjimharris * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
59230557Sjimharris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60230557Sjimharris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61230557Sjimharris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62230557Sjimharris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63230557Sjimharris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64230557Sjimharris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65230557Sjimharris * SUCH DAMAGE.
66230557Sjimharris *
67230557Sjimharris * Paul Borman, December 1996
68230557Sjimharris *
69230557Sjimharris * This driver is derived from a generic frame work which is
70230557Sjimharris * Copyright(c) 1994,1995,1996
71230557Sjimharris * Yoichi Shinoda, Yoshitaka Tokugawa, WIDE Project, Wildboar Project
72230557Sjimharris * and Foretune.  All rights reserved.
73230557Sjimharris *
74230557Sjimharris * A linux driver was used as the "hardware reference manual" (i.e.,
75230557Sjimharris * to determine registers and a general outline of how the card works)
76230557Sjimharris * That driver is publically available and copyright
77230557Sjimharris *
78230557Sjimharris * John Markus Bj�rndalen
79230557Sjimharris * Department of Computer Science
80230557Sjimharris * University of Troms�
81230557Sjimharris * Norway
82230557Sjimharris * johnm@staff.cs.uit.no, http://www.cs.uit.no/~johnm/
83230557Sjimharris */
84230557Sjimharris
85230557Sjimharris/*
86230557Sjimharris * This is a driver for the Xircom CreditCard Netwave (also known as
87230557Sjimharris * the Netwave Airsurfer) wireless LAN PCMCIA adapter.
88230557Sjimharris *
89230557Sjimharris * When this driver was developed, the Linux Netwave driver was used
90230557Sjimharris * as a hardware manual. That driver is Copyright (c) 1997 University
91230557Sjimharris * of Troms�, Norway. It is part of the Linux pcmcia-cs package that
92230557Sjimharris * can be found at http://pcmcia-cs.sourceforge.net/. The most recent
93230557Sjimharris * version of the pcmcia-cs package when this driver was written was
94230557Sjimharris * 3.0.6.
95230557Sjimharris *
96230557Sjimharris * Unfortunately, a lot of explicit numeric constants were used in the
97230557Sjimharris * Linux driver. I have tried to use symbolic names whenever possible,
98230557Sjimharris * but since I don't have any real hardware documentation, there's
99230557Sjimharris * still one or two "magic numbers" :-(.
100230557Sjimharris *
101230557Sjimharris * Driver limitations: This driver doesn't do multicasting or receiver
102230557Sjimharris * promiscuity, because of missing hardware documentation. I couldn't
103230557Sjimharris * get receiver promiscuity to work, and I haven't even tried
104230557Sjimharris * multicast. Volunteers are welcome, of course :-).
105230557Sjimharris */
106230557Sjimharris
107230557Sjimharris#include <sys/cdefs.h>
108230557Sjimharris__KERNEL_RCSID(0, "$NetBSD: if_cnw.c,v 1.63 2018/06/26 06:48:01 msaitoh Exp $");
109230557Sjimharris
110230557Sjimharris#include "opt_inet.h"
111230557Sjimharris
112230557Sjimharris#include <sys/param.h>
113230557Sjimharris#include <sys/systm.h>
114230557Sjimharris#include <sys/device.h>
115230557Sjimharris#include <sys/socket.h>
116230557Sjimharris#include <sys/mbuf.h>
117230557Sjimharris#include <sys/ioctl.h>
118230557Sjimharris#include <sys/proc.h>
119230557Sjimharris#include <sys/kauth.h>
120230557Sjimharris
121230557Sjimharris#include <net/if.h>
122230557Sjimharris
123230557Sjimharris#include <dev/pcmcia/if_cnwreg.h>
124230557Sjimharris#include <dev/pcmcia/if_cnwioctl.h>
125230557Sjimharris
126230557Sjimharris#include <dev/pcmcia/pcmciareg.h>
127230557Sjimharris#include <dev/pcmcia/pcmciavar.h>
128230557Sjimharris#include <dev/pcmcia/pcmciadevs.h>
129230557Sjimharris
130230557Sjimharris#include <net/if_dl.h>
131230557Sjimharris#include <net/if_ether.h>
132230557Sjimharris#include <net/bpf.h>
133230557Sjimharris
134230557Sjimharris#ifdef INET
135230557Sjimharris#include <netinet/in.h>
136230557Sjimharris#include <netinet/in_systm.h>
137230557Sjimharris#include <netinet/in_var.h>
138230557Sjimharris#include <netinet/ip.h>
139230557Sjimharris#include <netinet/if_inarp.h>
140230557Sjimharris#endif
141230557Sjimharris
142230557Sjimharris/*
143230557Sjimharris * Let these be patchable variables, initialized from macros that can
144230557Sjimharris * be set in the kernel config file. Someone with lots of spare time
145230557Sjimharris * could probably write a nice Netwave configuration program to do
146230557Sjimharris * this a little bit more elegantly :-).
147 */
148#ifndef CNW_DOMAIN
149#define CNW_DOMAIN	0x100
150#endif
151int cnw_domain = CNW_DOMAIN;		/* Domain */
152#ifndef CNW_SCRAMBLEKEY
153#define CNW_SCRAMBLEKEY 0
154#endif
155int cnw_skey = CNW_SCRAMBLEKEY;		/* Scramble key */
156
157/*
158 * The card appears to work much better when we only allow one packet
159 * "in the air" at a time.  This is done by not allowing another packet
160 * on the card, even if there is room.  Turning this off will allow the
161 * driver to stuff packets on the card as soon as a transmit buffer is
162 * available.  This does increase the number of collisions, though.
163 * We can que a second packet if there are transmit buffers available,
164 * but we do not actually send the packet until the last packet has
165 * been written.
166 */
167#define	ONE_AT_A_TIME
168
169/*
170 * Netwave cards choke if we try to use io memory address >= 0x400.
171 * Even though, CIS tuple does not talk about this.
172 * Use memory mapped access.
173 */
174#define MEMORY_MAPPED
175
176int	cnw_match(device_t, cfdata_t, void *);
177void	cnw_attach(device_t, device_t, void *);
178int	cnw_detach(device_t, int);
179
180int	cnw_activate(device_t, enum devact);
181
182struct cnw_softc {
183	device_t sc_dev;		    /* Device glue (must be first) */
184	struct ethercom sc_ethercom;	    /* Ethernet common part */
185	int sc_domain;			    /* Netwave domain */
186	int sc_skey;			    /* Netwave scramble key */
187	struct cnwstats sc_stats;
188
189	/* PCMCIA-specific stuff */
190	struct pcmcia_function *sc_pf;	    /* PCMCIA function */
191#ifndef MEMORY_MAPPED
192	struct pcmcia_io_handle sc_pcioh;   /* PCMCIA I/O space handle */
193	int sc_iowin;			    /*   ...window */
194	bus_space_tag_t sc_iot;		    /*   ...bus_space tag */
195	bus_space_handle_t sc_ioh;	    /*   ...bus_space handle */
196#endif
197	struct pcmcia_mem_handle sc_pcmemh; /* PCMCIA memory handle */
198	bus_size_t sc_memoff;		    /*   ...offset */
199	int sc_memwin;			    /*   ...window */
200	bus_space_tag_t sc_memt;	    /*   ...bus_space tag */
201	bus_space_handle_t sc_memh;	    /*   ...bus_space handle */
202	void *sc_ih;			    /* Interrupt cookie */
203	struct timeval sc_txlast;	    /* When the last xmit was made */
204	int sc_active;			    /* Currently xmitting a packet */
205
206	int sc_resource;		    /* Resources alloc'ed on attach */
207#define CNW_RES_PCIC	1
208#define CNW_RES_IO	2
209#define CNW_RES_MEM	4
210#define CNW_RES_NET	8
211};
212
213CFATTACH_DECL_NEW(cnw, sizeof(struct cnw_softc),
214    cnw_match, cnw_attach, cnw_detach, cnw_activate);
215
216void cnw_reset(struct cnw_softc *);
217void cnw_init(struct cnw_softc *);
218int cnw_enable(struct cnw_softc *sc);
219void cnw_disable(struct cnw_softc *sc);
220void cnw_config(struct cnw_softc *sc, u_int8_t *);
221void cnw_start(struct ifnet *);
222void cnw_transmit(struct cnw_softc *, struct mbuf *);
223struct mbuf *cnw_read(struct cnw_softc *);
224void cnw_recv(struct cnw_softc *);
225int cnw_intr(void *arg);
226int cnw_ioctl(struct ifnet *, u_long, void *);
227void cnw_watchdog(struct ifnet *);
228static int cnw_setdomain(struct cnw_softc *, int);
229static int cnw_setkey(struct cnw_softc *, int);
230
231/* ---------------------------------------------------------------- */
232
233/* Help routines */
234static int wait_WOC(struct cnw_softc *, int);
235static int read16(struct cnw_softc *, int);
236static int cnw_cmd(struct cnw_softc *, int, int, int, int);
237
238/*
239 * Wait until the WOC (Write Operation Complete) bit in the
240 * ASR (Adapter Status Register) is asserted.
241 */
242static int
243wait_WOC(struct cnw_softc *sc, int line)
244{
245	int i, asr;
246
247	for (i = 0; i < 5000; i++) {
248#ifndef MEMORY_MAPPED
249		asr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR);
250#else
251		asr = bus_space_read_1(sc->sc_memt, sc->sc_memh,
252		    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR);
253#endif
254		if (asr & CNW_ASR_WOC)
255			return (0);
256		DELAY(100);
257	}
258	if (line > 0)
259		printf("%s: wedged at line %d\n", device_xname(sc->sc_dev), line);
260	return (1);
261}
262#define WAIT_WOC(sc) wait_WOC(sc, __LINE__)
263
264
265/*
266 * Read a 16 bit value from the card.
267 */
268static int
269read16(struct cnw_softc *sc, int offset)
270{
271	int hi, lo;
272	int offs = sc->sc_memoff + offset;
273
274	/* This could presumably be done more efficient with
275	 * bus_space_read_2(), but I don't know anything about the
276	 * byte sex guarantees... Besides, this is pretty cheap as
277	 * well :-)
278	 */
279	lo = bus_space_read_1(sc->sc_memt, sc->sc_memh, offs);
280	hi = bus_space_read_1(sc->sc_memt, sc->sc_memh, offs + 1);
281	return ((hi << 8) | lo);
282}
283
284
285/*
286 * Send a command to the card by writing it to the command buffer.
287 */
288int
289cnw_cmd(struct cnw_softc *sc, int cmd, int count, int arg1, int arg2)
290{
291	int ptr = sc->sc_memoff + CNW_EREG_CB;
292
293	if (wait_WOC(sc, 0)) {
294		printf("%s: wedged when issuing cmd 0x%x\n",
295		    device_xname(sc->sc_dev), cmd);
296		/*
297		 * We'll continue anyway, as that's probably the best
298		 * thing we can do; at least the user knows there's a
299		 * problem, and can reset the interface with ifconfig
300		 * down/up.
301		 */
302	}
303
304	bus_space_write_1(sc->sc_memt, sc->sc_memh, ptr, cmd);
305	if (count > 0) {
306		bus_space_write_1(sc->sc_memt, sc->sc_memh, ptr + 1, arg1);
307		if (count > 1)
308			bus_space_write_1(sc->sc_memt, sc->sc_memh,
309			    ptr + 2, arg2);
310	}
311	bus_space_write_1(sc->sc_memt, sc->sc_memh,
312	    ptr + count + 1, CNW_CMD_EOC);
313	return (0);
314}
315#define CNW_CMD0(sc, cmd) \
316    do { cnw_cmd(sc, cmd, 0, 0, 0); } while (0)
317#define CNW_CMD1(sc, cmd, arg1)	\
318    do { cnw_cmd(sc, cmd, 1, arg1 , 0); } while (0)
319#define CNW_CMD2(sc, cmd, arg1, arg2) \
320    do { cnw_cmd(sc, cmd, 2, arg1, arg2); } while (0)
321
322/* ---------------------------------------------------------------- */
323
324/*
325 * Reset the hardware.
326 */
327void
328cnw_reset(struct cnw_softc *sc)
329{
330#ifdef CNW_DEBUG
331	if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
332		printf("%s: resetting\n", device_xname(sc->sc_dev));
333#endif
334	wait_WOC(sc, 0);
335#ifndef MEMORY_MAPPED
336	bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_PMR, CNW_PMR_RESET);
337#else
338	bus_space_write_1(sc->sc_memt, sc->sc_memh,
339	    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_PMR, CNW_PMR_RESET);
340#endif
341	bus_space_write_1(sc->sc_memt, sc->sc_memh,
342	    sc->sc_memoff + CNW_EREG_ASCC, CNW_ASR_WOC);
343#ifndef MEMORY_MAPPED
344	bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_PMR, 0);
345#else
346	bus_space_write_1(sc->sc_memt, sc->sc_memh,
347	    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_PMR, 0);
348#endif
349}
350
351
352/*
353 * Initialize the card.
354 */
355void
356cnw_init(struct cnw_softc *sc)
357{
358	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
359	const u_int8_t rxmode =
360	    CNW_RXCONF_RXENA | CNW_RXCONF_BCAST | CNW_RXCONF_AMP;
361
362	/* Reset the card */
363	cnw_reset(sc);
364
365	/* Issue a NOP to check the card */
366	CNW_CMD0(sc, CNW_CMD_NOP);
367
368	/* Set up receive configuration */
369	CNW_CMD1(sc, CNW_CMD_SRC,
370	    rxmode | ((ifp->if_flags & IFF_PROMISC) ? CNW_RXCONF_PRO : 0));
371
372	/* Set up transmit configuration */
373	CNW_CMD1(sc, CNW_CMD_STC, CNW_TXCONF_TXENA);
374
375	/* Set domain */
376	CNW_CMD2(sc, CNW_CMD_SMD, sc->sc_domain, sc->sc_domain >> 8);
377
378	/* Set scramble key */
379	CNW_CMD2(sc, CNW_CMD_SSK, sc->sc_skey, sc->sc_skey >> 8);
380
381	/* Enable interrupts */
382	WAIT_WOC(sc);
383#ifndef MEMORY_MAPPED
384	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
385	    CNW_REG_IMR, CNW_IMR_IENA | CNW_IMR_RFU1);
386#else
387	bus_space_write_1(sc->sc_memt, sc->sc_memh,
388	    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_IMR,
389	    CNW_IMR_IENA | CNW_IMR_RFU1);
390#endif
391
392	/* Enable receiver */
393	CNW_CMD0(sc, CNW_CMD_ER);
394
395	/* "Set the IENA bit in COR" */
396	WAIT_WOC(sc);
397#ifndef MEMORY_MAPPED
398	bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_COR,
399	    CNW_COR_IENA | CNW_COR_LVLREQ);
400#else
401	bus_space_write_1(sc->sc_memt, sc->sc_memh,
402	    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_COR,
403	    CNW_COR_IENA | CNW_COR_LVLREQ);
404#endif
405}
406
407
408/*
409 * Enable and initialize the card.
410 */
411int
412cnw_enable(struct cnw_softc *sc)
413{
414	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
415
416	if ((ifp->if_flags & IFF_RUNNING) != 0)
417		return (0);
418
419	sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_NET, cnw_intr, sc);
420	if (sc->sc_ih == NULL) {
421		aprint_error_dev(sc->sc_dev, "couldn't establish interrupt handler\n");
422		return (EIO);
423	}
424	if (pcmcia_function_enable(sc->sc_pf) != 0) {
425		aprint_error_dev(sc->sc_dev, "couldn't enable card\n");
426		return (EIO);
427	}
428	sc->sc_resource |= CNW_RES_PCIC;
429	cnw_init(sc);
430	ifp->if_flags &= ~IFF_OACTIVE;
431	ifp->if_flags |= IFF_RUNNING;
432	return (0);
433}
434
435
436/*
437 * Stop and disable the card.
438 */
439void
440cnw_disable(struct cnw_softc *sc)
441{
442	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
443
444	if ((ifp->if_flags & IFF_RUNNING) == 0)
445		return;
446
447	pcmcia_function_disable(sc->sc_pf);
448	sc->sc_resource &= ~CNW_RES_PCIC;
449	pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
450	ifp->if_flags &= ~IFF_RUNNING;
451	ifp->if_timer = 0;
452}
453
454
455/*
456 * Match the hardware we handle.
457 */
458int
459cnw_match(device_t parent, cfdata_t match, void *aux)
460{
461	struct pcmcia_attach_args *pa = aux;
462
463	if (pa->manufacturer == PCMCIA_VENDOR_XIRCOM &&
464	    pa->product == PCMCIA_PRODUCT_XIRCOM_CNW_801)
465		return 1;
466	if (pa->manufacturer == PCMCIA_VENDOR_XIRCOM &&
467	    pa->product == PCMCIA_PRODUCT_XIRCOM_CNW_802)
468		return 1;
469	return 0;
470}
471
472
473/*
474 * Attach the card.
475 */
476void
477cnw_attach(device_t parent, device_t self, void *aux)
478{
479	struct cnw_softc *sc = device_private(self);
480	struct pcmcia_attach_args *pa = aux;
481	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
482	u_int8_t macaddr[ETHER_ADDR_LEN];
483	int i;
484	bus_size_t memsize;
485
486	sc->sc_dev = self;
487	sc->sc_resource = 0;
488
489	/* Enable the card */
490	sc->sc_pf = pa->pf;
491	pcmcia_function_init(sc->sc_pf, SIMPLEQ_FIRST(&sc->sc_pf->cfe_head));
492	if (pcmcia_function_enable(sc->sc_pf)) {
493		aprint_error_dev(self, "function enable failed\n");
494		return;
495	}
496	sc->sc_resource |= CNW_RES_PCIC;
497
498	/* Map I/O register and "memory" */
499#ifndef MEMORY_MAPPED
500	if (pcmcia_io_alloc(sc->sc_pf, 0, CNW_IO_SIZE, CNW_IO_SIZE,
501	    &sc->sc_pcioh) != 0) {
502		aprint_error_dev(self, "can't allocate i/o space\n");
503		goto fail;
504	}
505	if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_IO16, &sc->sc_pcioh,
506	    &sc->sc_iowin) != 0) {
507		aprint_error_dev(self, "can't map i/o space\n");
508		pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh);
509		goto fail;
510	}
511	sc->sc_iot = sc->sc_pcioh.iot;
512	sc->sc_ioh = sc->sc_pcioh.ioh;
513	sc->sc_resource |= CNW_RES_IO;
514#endif
515#ifndef MEMORY_MAPPED
516	memsize = CNW_MEM_SIZE;
517#else
518	memsize = CNW_MEM_SIZE + CNW_IOM_SIZE;
519#endif
520	if (pcmcia_mem_alloc(sc->sc_pf, memsize, &sc->sc_pcmemh) != 0) {
521		aprint_error_dev(self, "can't allocate memory\n");
522		goto fail;
523	}
524	if (pcmcia_mem_map(sc->sc_pf, PCMCIA_WIDTH_MEM8|PCMCIA_MEM_COMMON,
525	    CNW_MEM_ADDR, memsize, &sc->sc_pcmemh, &sc->sc_memoff,
526	    &sc->sc_memwin) != 0) {
527		aprint_error_dev(self, "can't map memory\n");
528		pcmcia_mem_free(sc->sc_pf, &sc->sc_pcmemh);
529		goto fail;
530	}
531	sc->sc_memt = sc->sc_pcmemh.memt;
532	sc->sc_memh = sc->sc_pcmemh.memh;
533	sc->sc_resource |= CNW_RES_MEM;
534
535	/* Finish setup of softc */
536	sc->sc_domain = cnw_domain;
537	sc->sc_skey = cnw_skey;
538
539	/* Get MAC address */
540	cnw_reset(sc);
541	for (i = 0; i < ETHER_ADDR_LEN; i++)
542		macaddr[i] = bus_space_read_1(sc->sc_memt, sc->sc_memh,
543		    sc->sc_memoff + CNW_EREG_PA + i);
544	printf("%s: address %s\n", device_xname(sc->sc_dev),
545	    ether_sprintf(macaddr));
546
547	/* Set up ifnet structure */
548	strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
549	ifp->if_softc = sc;
550	ifp->if_start = cnw_start;
551	ifp->if_ioctl = cnw_ioctl;
552	ifp->if_watchdog = cnw_watchdog;
553	ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX |
554	    IFF_NOTRAILERS;
555	IFQ_SET_READY(&ifp->if_snd);
556
557	/* Attach the interface */
558	if_attach(ifp);
559	if_deferred_start_init(ifp, NULL);
560	ether_ifattach(ifp, macaddr);
561
562	sc->sc_resource |= CNW_RES_NET;
563
564	ifp->if_baudrate = IF_Mbps(1);
565
566	/* Disable the card now, and turn it on when the interface goes up */
567	pcmcia_function_disable(sc->sc_pf);
568	sc->sc_resource &= ~CNW_RES_PCIC;
569	return;
570
571fail:
572#ifndef MEMORY_MAPPED
573	if ((sc->sc_resource & CNW_RES_IO) != 0) {
574		pcmcia_io_unmap(sc->sc_pf, sc->sc_iowin);
575		pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh);
576		sc->sc_resource &= ~CNW_RES_IO;
577	}
578#endif
579	if ((sc->sc_resource & CNW_RES_PCIC) != 0) {
580		pcmcia_function_disable(sc->sc_pf);
581		sc->sc_resource &= ~CNW_RES_PCIC;
582	}
583}
584
585/*
586 * Start outputting on the interface.
587 */
588void
589cnw_start(struct ifnet *ifp)
590{
591	struct cnw_softc *sc = ifp->if_softc;
592	struct mbuf *m0;
593	int lif;
594	int asr;
595#ifdef ONE_AT_A_TIME
596	struct timeval now;
597#endif
598
599#ifdef CNW_DEBUG
600	if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
601		printf("%s: cnw_start\n", ifp->if_xname);
602	if (ifp->if_flags & IFF_OACTIVE)
603		printf("%s: cnw_start reentered\n", ifp->if_xname);
604#endif
605
606	ifp->if_flags |= IFF_OACTIVE;
607
608	for (;;) {
609#ifdef ONE_AT_A_TIME
610		microtime(&now);
611		now.tv_sec -= sc->sc_txlast.tv_sec;
612		now.tv_usec -= sc->sc_txlast.tv_usec;
613		if (now.tv_usec < 0) {
614			now.tv_usec += 1000000;
615			now.tv_sec--;
616		}
617
618		/*
619		 * Don't ship this packet out until the last
620		 * packet has left the building.
621		 * If we have not tried to send a packet for 1/5
622		 * a second then we assume we lost an interrupt,
623		 * lets go on and send the next packet anyhow.
624		 *
625		 * I suppose we could check to see if it is okay
626		 * to put additional packets on the card (beyond
627		 * the one already waiting to be sent) but I don't
628		 * think we would get any improvement in speed as
629		 * we should have ample time to put the next packet
630		 * on while this one is going out.
631		 */
632		if (sc->sc_active && now.tv_sec == 0 && now.tv_usec < 200000)
633			break;
634#endif
635
636		/* Make sure the link integrity field is on */
637		WAIT_WOC(sc);
638		lif = bus_space_read_1(sc->sc_memt, sc->sc_memh,
639		    sc->sc_memoff + CNW_EREG_LIF);
640		if (lif == 0) {
641#ifdef CNW_DEBUG
642			if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
643				printf("%s: link integrity %d\n", ifp->if_xname, lif);
644#endif
645			break;
646		}
647
648		/* Is there any buffer space available on the card? */
649		WAIT_WOC(sc);
650#ifndef MEMORY_MAPPED
651		asr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR);
652#else
653		asr = bus_space_read_1(sc->sc_memt, sc->sc_memh,
654		    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR);
655#endif
656		if (!(asr & CNW_ASR_TXBA)) {
657#ifdef CNW_DEBUG
658			if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
659				printf("%s: no buffer space\n", ifp->if_xname);
660#endif
661			break;
662		}
663
664		sc->sc_stats.nws_tx++;
665
666		IFQ_DEQUEUE(&ifp->if_snd, m0);
667		if (m0 == 0)
668			break;
669
670		bpf_mtap(ifp, m0, BPF_D_OUT);
671
672		cnw_transmit(sc, m0);
673		++ifp->if_opackets;
674		ifp->if_timer = 3; /* start watchdog timer */
675
676		microtime(&sc->sc_txlast);
677		sc->sc_active = 1;
678	}
679
680	ifp->if_flags &= ~IFF_OACTIVE;
681}
682
683/*
684 * Transmit a packet.
685 */
686void
687cnw_transmit(struct cnw_softc *sc, struct mbuf *m0)
688{
689	int buffer, bufsize, bufoffset, bufptr, bufspace, len, mbytes, n;
690	struct mbuf *m;
691	u_int8_t *mptr;
692
693	/* Get buffer info from card */
694	buffer = read16(sc, CNW_EREG_TDP);
695	bufsize = read16(sc, CNW_EREG_TDP + 2);
696	bufoffset = read16(sc, CNW_EREG_TDP + 4);
697#ifdef CNW_DEBUG
698	if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
699		printf("%s: cnw_transmit b=0x%x s=%d o=0x%x\n",
700		    device_xname(sc->sc_dev), buffer, bufsize, bufoffset);
701#endif
702
703	/* Copy data from mbuf chain to card buffers */
704	bufptr = sc->sc_memoff + buffer + bufoffset;
705	bufspace = bufsize;
706	len = 0;
707	for (m = m0; m; ) {
708		mptr = mtod(m, u_int8_t *);
709		mbytes = m->m_len;
710		len += mbytes;
711		while (mbytes > 0) {
712			if (bufspace == 0) {
713				buffer = read16(sc, buffer);
714				bufptr = sc->sc_memoff + buffer + bufoffset;
715				bufspace = bufsize;
716#ifdef CNW_DEBUG
717				if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
718					printf("%s:   next buffer @0x%x\n",
719					    device_xname(sc->sc_dev), buffer);
720#endif
721			}
722			n = mbytes <= bufspace ? mbytes : bufspace;
723			bus_space_write_region_1(sc->sc_memt, sc->sc_memh,
724			    bufptr, mptr, n);
725			bufptr += n;
726			bufspace -= n;
727			mptr += n;
728			mbytes -= n;
729		}
730		m = m0 = m_free(m);
731	}
732
733	/* Issue transmit command */
734	CNW_CMD2(sc, CNW_CMD_TL, len, len >> 8);
735}
736
737
738/*
739 * Pull a packet from the card into an mbuf chain.
740 */
741struct mbuf *
742cnw_read(struct cnw_softc *sc)
743{
744	struct mbuf *m, *top, **mp;
745	int totbytes, buffer, bufbytes, bufptr, mbytes, n;
746	u_int8_t *mptr;
747
748	WAIT_WOC(sc);
749	totbytes = read16(sc, CNW_EREG_RDP);
750#ifdef CNW_DEBUG
751	if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
752		printf("%s: recv %d bytes\n", device_xname(sc->sc_dev), totbytes);
753#endif
754	buffer = CNW_EREG_RDP + 2;
755	bufbytes = 0;
756	bufptr = 0; /* XXX make gcc happy */
757
758	MGETHDR(m, M_DONTWAIT, MT_DATA);
759	if (m == 0)
760		return (0);
761	m_set_rcvif(m, &sc->sc_ethercom.ec_if);
762	m->m_pkthdr.len = totbytes;
763	mbytes = MHLEN;
764	top = 0;
765	mp = &top;
766
767	while (totbytes > 0) {
768		if (top) {
769			MGET(m, M_DONTWAIT, MT_DATA);
770			if (m == 0) {
771				m_freem(top);
772				return (0);
773			}
774			mbytes = MLEN;
775		}
776		if (totbytes >= MINCLSIZE) {
777			MCLGET(m, M_DONTWAIT);
778			if ((m->m_flags & M_EXT) == 0) {
779				m_free(m);
780				m_freem(top);
781				return (0);
782			}
783			mbytes = MCLBYTES;
784		}
785		if (!top) {
786			int pad = ALIGN(sizeof(struct ether_header)) -
787			    sizeof(struct ether_header);
788			m->m_data += pad;
789			mbytes -= pad;
790		}
791		mptr = mtod(m, u_int8_t *);
792		mbytes = m->m_len = min(totbytes, mbytes);
793		totbytes -= mbytes;
794		while (mbytes > 0) {
795			if (bufbytes == 0) {
796				buffer = read16(sc, buffer);
797				bufbytes = read16(sc, buffer + 2);
798				bufptr = sc->sc_memoff + buffer +
799				    read16(sc, buffer + 4);
800#ifdef CNW_DEBUG
801				if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG)
802					printf("%s:   %d bytes @0x%x+0x%lx\n",
803					    device_xname(sc->sc_dev), bufbytes,
804					    buffer, (u_long)(bufptr - buffer -
805					    sc->sc_memoff));
806#endif
807			}
808			n = mbytes <= bufbytes ? mbytes : bufbytes;
809			bus_space_read_region_1(sc->sc_memt, sc->sc_memh,
810			    bufptr, mptr, n);
811			bufbytes -= n;
812			bufptr += n;
813			mbytes -= n;
814			mptr += n;
815		}
816		*mp = m;
817		mp = &m->m_next;
818	}
819
820	return (top);
821}
822
823
824/*
825 * Handle received packets.
826 */
827void
828cnw_recv(struct cnw_softc *sc)
829{
830	int rser;
831	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
832	struct mbuf *m;
833
834	for (;;) {
835		WAIT_WOC(sc);
836		rser = bus_space_read_1(sc->sc_memt, sc->sc_memh,
837		    sc->sc_memoff + CNW_EREG_RSER);
838		if (!(rser & CNW_RSER_RXAVAIL))
839			return;
840
841		/* Pull packet off card */
842		m = cnw_read(sc);
843
844		/* Acknowledge packet */
845		CNW_CMD0(sc, CNW_CMD_SRP);
846
847		/* Did we manage to get the packet from the interface? */
848		if (m == 0) {
849			++ifp->if_ierrors;
850			return;
851		}
852		++ifp->if_ipackets;
853
854		/* Pass the packet up. */
855		if_percpuq_enqueue(ifp->if_percpuq, m);
856	}
857}
858
859
860/*
861 * Interrupt handler.
862 */
863int
864cnw_intr(void *arg)
865{
866	struct cnw_softc *sc = arg;
867	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
868	int ret, status, rser, tser;
869
870	if ((sc->sc_ethercom.ec_if.if_flags & IFF_RUNNING) == 0 ||
871	    !device_is_active(sc->sc_dev))
872		return (0);
873	ifp->if_timer = 0;	/* stop watchdog timer */
874
875	ret = 0;
876	for (;;) {
877		WAIT_WOC(sc);
878#ifndef MEMORY_MAPPED
879		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
880		    CNW_REG_CCSR);
881#else
882		status = bus_space_read_1(sc->sc_memt, sc->sc_memh,
883		    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_CCSR);
884#endif
885		if (!(status & 0x02))
886			/* No more commands, or shared IRQ */
887			return (ret);
888		ret = 1;
889#ifndef MEMORY_MAPPED
890		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR);
891#else
892		status = bus_space_read_1(sc->sc_memt, sc->sc_memh,
893		    sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR);
894#endif
895
896		/* Anything to receive? */
897		if (status & CNW_ASR_RXRDY) {
898			sc->sc_stats.nws_rx++;
899			cnw_recv(sc);
900		}
901
902		/* Receive error */
903		if (status & CNW_ASR_RXERR) {
904			/*
905			 * I get a *lot* of spurious receive errors
906			 * (many per second), even when the interface
907			 * is quiescent, so we don't increment
908			 * if_ierrors here.
909			 */
910			rser = bus_space_read_1(sc->sc_memt, sc->sc_memh,
911			    sc->sc_memoff + CNW_EREG_RSER);
912
913			/* RX statistics */
914			sc->sc_stats.nws_rxerr++;
915			if (rser & CNW_RSER_RXBIG)
916				sc->sc_stats.nws_rxframe++;
917			if (rser & CNW_RSER_RXCRC)
918				sc->sc_stats.nws_rxcrcerror++;
919			if (rser & CNW_RSER_RXOVERRUN)
920				sc->sc_stats.nws_rxoverrun++;
921			if (rser & CNW_RSER_RXOVERFLOW)
922				sc->sc_stats.nws_rxoverflow++;
923			if (rser & CNW_RSER_RXERR)
924				sc->sc_stats.nws_rxerrors++;
925			if (rser & CNW_RSER_RXAVAIL)
926				sc->sc_stats.nws_rxavail++;
927
928			/* Clear error bits in RSER */
929			WAIT_WOC(sc);
930			bus_space_write_1(sc->sc_memt, sc->sc_memh,
931			    sc->sc_memoff + CNW_EREG_RSERW,
932			    CNW_RSER_RXERR |
933			    (rser & (CNW_RSER_RXCRC | CNW_RSER_RXBIG)));
934			/* Clear RXERR in ASR */
935			WAIT_WOC(sc);
936			bus_space_write_1(sc->sc_memt, sc->sc_memh,
937			    sc->sc_memoff + CNW_EREG_ASCC, CNW_ASR_RXERR);
938		}
939
940		/* Transmit done */
941		if (status & CNW_ASR_TXDN) {
942			tser = bus_space_read_1(sc->sc_memt, sc->sc_memh,
943						CNW_EREG_TSER);
944
945			/* TX statistics */
946			if (tser & CNW_TSER_TXERR)
947				sc->sc_stats.nws_txerrors++;
948			if (tser & CNW_TSER_TXNOAP)
949				sc->sc_stats.nws_txlostcd++;
950			if (tser & CNW_TSER_TXGU)
951				sc->sc_stats.nws_txabort++;
952
953			if (tser & CNW_TSER_TXOK) {
954				sc->sc_stats.nws_txokay++;
955				sc->sc_stats.nws_txretries[status & 0xf]++;
956				WAIT_WOC(sc);
957				bus_space_write_1(sc->sc_memt, sc->sc_memh,
958				    sc->sc_memoff + CNW_EREG_TSERW,
959				    CNW_TSER_TXOK | CNW_TSER_RTRY);
960			}
961
962			if (tser & CNW_TSER_ERROR) {
963				++ifp->if_oerrors;
964				WAIT_WOC(sc);
965				bus_space_write_1(sc->sc_memt, sc->sc_memh,
966				    sc->sc_memoff + CNW_EREG_TSERW,
967				    (tser & CNW_TSER_ERROR) |
968				    CNW_TSER_RTRY);
969			}
970
971			sc->sc_active = 0;
972			ifp->if_flags &= ~IFF_OACTIVE;
973
974			/* Continue to send packets from the queue */
975			if_schedule_deferred_start(&sc->sc_ethercom.ec_if);
976		}
977
978	}
979}
980
981
982/*
983 * Handle device ioctls.
984 */
985int
986cnw_ioctl(struct ifnet *ifp, u_long cmd, void *data)
987{
988	struct cnw_softc *sc = ifp->if_softc;
989	struct ifaddr *ifa = (struct ifaddr *)data;
990	struct ifreq *ifr = (struct ifreq *)data;
991	int s, error = 0;
992	struct lwp *l = curlwp;	/*XXX*/
993
994	switch (cmd) {
995	case SIOCINITIFADDR:
996	case SIOCSIFFLAGS:
997	case SIOCADDMULTI:
998	case SIOCDELMULTI:
999	case SIOCGCNWDOMAIN:
1000	case SIOCGCNWSTATS:
1001		break;
1002	case SIOCSCNWDOMAIN:
1003	case SIOCSCNWKEY:
1004		error = kauth_authorize_network(l->l_cred,
1005		    KAUTH_NETWORK_INTERFACE,
1006		    KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, KAUTH_ARG(cmd),
1007		    NULL);
1008		if (error)
1009			return (error);
1010		break;
1011	case SIOCGCNWSTATUS:
1012		error = kauth_authorize_network(l->l_cred,
1013		    KAUTH_NETWORK_INTERFACE,
1014		    KAUTH_REQ_NETWORK_INTERFACE_GETPRIV, ifp, KAUTH_ARG(cmd),
1015		    NULL);
1016		if (error)
1017			return (error);
1018		break;
1019	default:
1020		return (EINVAL);
1021	}
1022
1023	s = splnet();
1024
1025	switch (cmd) {
1026
1027	case SIOCINITIFADDR:
1028		if (!(ifp->if_flags & IFF_RUNNING) &&
1029		    (error = cnw_enable(sc)) != 0)
1030			break;
1031		ifp->if_flags |= IFF_UP;
1032		cnw_init(sc);
1033		switch (ifa->ifa_addr->sa_family) {
1034#ifdef INET
1035		case AF_INET:
1036			arp_ifinit(&sc->sc_ethercom.ec_if, ifa);
1037			break;
1038#endif
1039		default:
1040			break;
1041		}
1042		break;
1043
1044	case SIOCSIFFLAGS:
1045		if ((error = ifioctl_common(ifp, cmd, data)) != 0)
1046			break;
1047		/* XXX re-use ether_ioctl() */
1048		switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) {
1049		case IFF_RUNNING:
1050			/*
1051			 * The interface is marked down and it is running, so
1052			 * stop it.
1053			 */
1054			cnw_disable(sc);
1055			break;
1056		case IFF_UP:
1057			/*
1058			 * The interface is marked up and it is stopped, so
1059			 * start it.
1060			 */
1061			error = cnw_enable(sc);
1062			break;
1063		default:
1064			/* IFF_PROMISC may be changed */
1065			cnw_init(sc);
1066			break;
1067		}
1068		break;
1069
1070	case SIOCADDMULTI:
1071	case SIOCDELMULTI:
1072		/* Update our multicast list. */
1073		if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) {
1074			if (ifp->if_flags & IFF_RUNNING)
1075				cnw_init(sc);
1076			error = 0;
1077		}
1078		break;
1079
1080	case SIOCGCNWDOMAIN:
1081		ifr->ifr_domain = sc->sc_domain;
1082		break;
1083
1084	case SIOCSCNWDOMAIN:
1085		error = cnw_setdomain(sc, ifr->ifr_domain);
1086		break;
1087
1088	case SIOCSCNWKEY:
1089		error = cnw_setkey(sc, ifr->ifr_key);
1090		break;
1091
1092	case SIOCGCNWSTATUS:
1093		if ((ifp->if_flags & IFF_RUNNING) == 0)
1094			break;
1095		bus_space_read_region_1(sc->sc_memt, sc->sc_memh,
1096		    sc->sc_memoff + CNW_EREG_CB,
1097		    ((struct cnwstatus *)data)->data,
1098		    sizeof(((struct cnwstatus *)data)->data));
1099		break;
1100
1101	case SIOCGCNWSTATS:
1102		memcpy((void *)&(((struct cnwistats *)data)->stats),
1103		    (void *)&sc->sc_stats, sizeof(struct cnwstats));
1104			break;
1105
1106	default:
1107		error = ether_ioctl(ifp, cmd, data);
1108		break;
1109	}
1110
1111	splx(s);
1112	return (error);
1113}
1114
1115
1116/*
1117 * Device timeout/watchdog routine. Entered if the device neglects to
1118 * generate an interrupt after a transmit has been started on it.
1119 */
1120void
1121cnw_watchdog(struct ifnet *ifp)
1122{
1123	struct cnw_softc *sc = ifp->if_softc;
1124
1125	printf("%s: device timeout; card reset\n", device_xname(sc->sc_dev));
1126	++ifp->if_oerrors;
1127	cnw_init(sc);
1128}
1129
1130int
1131cnw_setdomain(struct cnw_softc *sc, int domain)
1132{
1133	int s;
1134
1135	if (domain & ~0x1ff)
1136		return EINVAL;
1137
1138	s = splnet();
1139	CNW_CMD2(sc, CNW_CMD_SMD, domain, domain >> 8);
1140	splx(s);
1141
1142	sc->sc_domain = domain;
1143	return 0;
1144}
1145
1146int
1147cnw_setkey(struct cnw_softc *sc, int key)
1148{
1149	int s;
1150
1151	if (key & ~0xffff)
1152		return EINVAL;
1153
1154	s = splnet();
1155	CNW_CMD2(sc, CNW_CMD_SSK, key, key >> 8);
1156	splx(s);
1157
1158	sc->sc_skey = key;
1159	return 0;
1160}
1161
1162int
1163cnw_activate(device_t self, enum devact act)
1164{
1165	struct cnw_softc *sc = device_private(self);
1166
1167	switch (act) {
1168	case DVACT_DEACTIVATE:
1169		if_deactivate(&sc->sc_ethercom.ec_if);
1170		return 0;
1171	default:
1172		return EOPNOTSUPP;
1173	}
1174}
1175
1176int
1177cnw_detach(device_t self, int flags)
1178{
1179	struct cnw_softc *sc = device_private(self);
1180	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
1181
1182	/* cnw_disable() checks IFF_RUNNING */
1183	cnw_disable(sc);
1184
1185	if ((sc->sc_resource & CNW_RES_NET) != 0) {
1186		ether_ifdetach(ifp);
1187		if_detach(ifp);
1188	}
1189
1190#ifndef MEMORY_MAPPED
1191	/* unmap and free our i/o windows */
1192	if ((sc->sc_resource & CNW_RES_IO) != 0) {
1193		pcmcia_io_unmap(sc->sc_pf, sc->sc_iowin);
1194		pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh);
1195	}
1196#endif
1197
1198	/* unmap and free our memory windows */
1199	if ((sc->sc_resource & CNW_RES_MEM) != 0) {
1200		pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwin);
1201		pcmcia_mem_free(sc->sc_pf, &sc->sc_pcmemh);
1202	}
1203
1204	return (0);
1205}
1206