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 = ⊤
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