Annotation of sys/dev/pcmcia/if_cnw.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: if_cnw.c,v 1.16 2006/03/25 22:41:46 djm Exp $ */
2: /*-
3: * Copyright (c) 1998 The NetBSD Foundation, Inc.
4: * All rights reserved.
5: *
6: * This code is derived from software contributed to The NetBSD Foundation
7: * by Michael Eriksson.
8: *
9: * Redistribution and use in source and binary forms, with or without
10: * modification, are permitted provided that the following conditions
11: * are met:
12: * 1. Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: * 3. All advertising materials mentioning features or use of this software
18: * must display the following acknowledgement:
19: * This product includes software developed by the NetBSD
20: * Foundation, Inc. and its contributors.
21: * 4. Neither the name of The NetBSD Foundation nor the names of its
22: * contributors may be used to endorse or promote products derived
23: * from this software without specific prior written permission.
24: *
25: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35: * POSSIBILITY OF SUCH DAMAGE.
36: */
37:
38: /*
39: * This is a driver for the Xircom CreditCard Netwave (also known as
40: * the Netwave Airsurfer) wireless LAN PCMCIA adapter.
41: *
42: * When this driver was developed, the Linux Netwave driver was used
43: * as a hardware manual. That driver is Copyright (c) 1997 University
44: * of Tromsų, Norway. It is part of the Linux pcmcia-cs package that
45: * can be found at http://pcmcia-cs.sourceforge.net/. The most
46: * recent version of the pcmcia-cs package when this driver was
47: * written was 3.0.6.
48: *
49: * Unfortunately, a lot of explicit numeric constants were used in the
50: * Linux driver. I have tried to use symbolic names whenever possible,
51: * but since I don't have any real hardware documentation, there's
52: * still one or two "magic numbers" :-(.
53: *
54: * Driver limitations: This driver doesn't do multicasting or receiver
55: * promiscuity, because of missing hardware documentation. I couldn't
56: * get receiver promiscuity to work, and I haven't even tried
57: * multicast. Volunteers are welcome, of course :-).
58: */
59:
60: #include "bpfilter.h"
61:
62: #include <sys/param.h>
63: #include <sys/systm.h>
64: #include <sys/device.h>
65: #include <sys/socket.h>
66: #include <sys/mbuf.h>
67: #include <sys/ioctl.h>
68:
69: #include <dev/pcmcia/if_cnwreg.h>
70:
71: #include <dev/pcmcia/pcmciareg.h>
72: #include <dev/pcmcia/pcmciavar.h>
73: #include <dev/pcmcia/pcmciadevs.h>
74:
75: #include <net/if.h>
76: #include <net/if_dl.h>
77:
78: #ifdef INET
79: #include <netinet/in.h>
80: #include <netinet/in_systm.h>
81: #include <netinet/in_var.h>
82: #include <netinet/ip.h>
83: #include <netinet/if_ether.h>
84: #endif
85:
86: #if NBPFILTER > 0
87: #include <net/bpf.h>
88: #endif
89:
90:
91: /*
92: * Let these be patchable variables, initialized from macros that can
93: * be set in the kernel config file. Someone with lots of spare time
94: * could probably write a nice Netwave configuration program to do
95: * this a little bit more elegantly :-).
96: */
97: #ifndef CNW_DOMAIN
98: #define CNW_DOMAIN 0x100
99: #endif
100: int cnw_domain = CNW_DOMAIN; /* Domain */
101: #ifndef CNW_SCRAMBLEKEY
102: #define CNW_SCRAMBLEKEY 0
103: #endif
104: int cnw_skey = CNW_SCRAMBLEKEY; /* Scramble key */
105:
106:
107: int cnw_match(struct device *, void *, void *);
108: void cnw_attach(struct device *, struct device *, void *);
109: int cnw_detach(struct device *, int);
110: int cnw_activate(struct device *, enum devact);
111:
112: struct cnw_softc {
113: struct device sc_dev; /* Device glue (must be first) */
114: struct arpcom sc_arpcom; /* Ethernet common part */
115: int sc_domain; /* Netwave domain */
116: int sc_skey; /* Netwave scramble key */
117:
118: /* PCMCIA-specific stuff */
119: struct pcmcia_function *sc_pf; /* PCMCIA function */
120: struct pcmcia_io_handle sc_pcioh; /* PCMCIA I/O space handle */
121: int sc_iowin; /* ...window */
122: bus_space_tag_t sc_iot; /* ...bus_space tag */
123: bus_space_handle_t sc_ioh; /* ...bus_space handle */
124: struct pcmcia_mem_handle sc_pcmemh; /* PCMCIA memory handle */
125: bus_addr_t sc_memoff; /* ...offset */
126: int sc_memwin; /* ...window */
127: bus_space_tag_t sc_memt; /* ...bus_space tag */
128: bus_space_handle_t sc_memh; /* ...bus_space handle */
129: void *sc_ih; /* Interrupt cookie */
130: };
131:
132: struct cfattach cnw_ca = {
133: sizeof(struct cnw_softc), cnw_match, cnw_attach,
134: cnw_detach, cnw_activate
135: };
136:
137: struct cfdriver cnw_cd = {
138: NULL, "cnw", DV_IFNET
139: };
140:
141: void cnw_reset(struct cnw_softc *);
142: void cnw_init(struct cnw_softc *);
143: int cnw_enable(struct cnw_softc *sc);
144: void cnw_disable(struct cnw_softc *sc);
145: void cnw_config(struct cnw_softc *sc, u_int8_t *);
146: void cnw_start(struct ifnet *);
147: void cnw_transmit(struct cnw_softc *, struct mbuf *);
148: struct mbuf *cnw_read(struct cnw_softc *);
149: void cnw_recv(struct cnw_softc *);
150: int cnw_intr(void *arg);
151: int cnw_ioctl(struct ifnet *, u_long, caddr_t);
152: void cnw_watchdog(struct ifnet *);
153:
154: /* ---------------------------------------------------------------- */
155:
156: /* Help routines */
157: static int wait_WOC(struct cnw_softc *, int);
158: static int read16(struct cnw_softc *, int);
159: static int cnw_cmd(struct cnw_softc *, int, int, int, int);
160:
161: /*
162: * Wait until the WOC (Write Operation Complete) bit in the
163: * ASR (Adapter Status Register) is asserted.
164: */
165: static int
166: wait_WOC(sc, line)
167: struct cnw_softc *sc;
168: int line;
169: {
170: int i, asr;
171:
172: for (i = 0; i < 5000; i++) {
173: asr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR);
174: if (asr & CNW_ASR_WOC)
175: return (0);
176: DELAY(100);
177: }
178: if (line > 0)
179: printf("%s: wedged at line %d\n", sc->sc_dev.dv_xname, line);
180: return (1);
181: }
182: #define WAIT_WOC(sc) wait_WOC(sc, __LINE__)
183:
184:
185: /*
186: * Read a 16 bit value from the card.
187: */
188: static int
189: read16(sc, offset)
190: struct cnw_softc *sc;
191: int offset;
192: {
193: int hi, lo;
194:
195: /* This could presumably be done more efficient with
196: * bus_space_read_2(), but I don't know anything about the
197: * byte sex guarantees... Besides, this is pretty cheap as
198: * well :-)
199: */
200: lo = bus_space_read_1(sc->sc_memt, sc->sc_memh,
201: sc->sc_memoff + offset);
202: hi = bus_space_read_1(sc->sc_memt, sc->sc_memh,
203: sc->sc_memoff + offset + 1);
204: return ((hi << 8) | lo);
205: }
206:
207:
208: /*
209: * Send a command to the card by writing it to the command buffer.
210: */
211: int
212: cnw_cmd(sc, cmd, count, arg1, arg2)
213: struct cnw_softc *sc;
214: int cmd, count, arg1, arg2;
215: {
216: int ptr = sc->sc_memoff + CNW_EREG_CB;
217:
218: if (wait_WOC(sc, 0)) {
219: printf("%s: wedged when issuing cmd 0x%x\n",
220: sc->sc_dev.dv_xname, cmd);
221: /*
222: * We'll continue anyway, as that's probably the best
223: * thing we can do; at least the user knows there's a
224: * problem, and can reset the interface with ifconfig
225: * down/up.
226: */
227: }
228:
229: bus_space_write_1(sc->sc_memt, sc->sc_memh, ptr, cmd);
230: if (count > 0) {
231: bus_space_write_1(sc->sc_memt, sc->sc_memh, ptr + 1, arg1);
232: if (count > 1)
233: bus_space_write_1(sc->sc_memt, sc->sc_memh,
234: ptr + 2, arg2);
235: }
236: bus_space_write_1(sc->sc_memt, sc->sc_memh,
237: ptr + count + 1, CNW_CMD_EOC);
238: return (0);
239: }
240: #define CNW_CMD0(sc, cmd) \
241: do { cnw_cmd(sc, cmd, 0, 0, 0); } while (0)
242: #define CNW_CMD1(sc, cmd, arg1) \
243: do { cnw_cmd(sc, cmd, 1, arg1 , 0); } while (0)
244: #define CNW_CMD2(sc, cmd, arg1, arg2) \
245: do { cnw_cmd(sc, cmd, 2, arg1, arg2); } while (0)
246:
247: /* ---------------------------------------------------------------- */
248:
249: /*
250: * Reset the hardware.
251: */
252: void
253: cnw_reset(sc)
254: struct cnw_softc *sc;
255: {
256: #ifdef CNW_DEBUG
257: if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG)
258: printf("%s: resetting\n", sc->sc_dev.dv_xname);
259: #endif
260: wait_WOC(sc, 0);
261: bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_PMR, CNW_PMR_RESET);
262: bus_space_write_1(sc->sc_memt, sc->sc_memh,
263: sc->sc_memoff + CNW_EREG_ASCC, CNW_ASR_WOC);
264: bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_PMR, 0);
265: }
266:
267:
268: /*
269: * Initialize the card.
270: */
271: void
272: cnw_init(sc)
273: struct cnw_softc *sc;
274: {
275: /* Reset the card */
276: cnw_reset(sc);
277:
278: /* Issue a NOP to check the card */
279: CNW_CMD0(sc, CNW_CMD_NOP);
280:
281: /* Set up receive configuration */
282: CNW_CMD1(sc, CNW_CMD_SRC, CNW_RXCONF_RXENA | CNW_RXCONF_BCAST);
283:
284: /* Set up transmit configuration */
285: CNW_CMD1(sc, CNW_CMD_STC, CNW_TXCONF_TXENA);
286:
287: /* Set domain */
288: CNW_CMD2(sc, CNW_CMD_SMD, sc->sc_domain, sc->sc_domain >> 8);
289:
290: /* Set scramble key */
291: CNW_CMD2(sc, CNW_CMD_SSK, sc->sc_skey, sc->sc_skey >> 8);
292:
293: /* Enable interrupts */
294: WAIT_WOC(sc);
295: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
296: CNW_REG_IMR, CNW_IMR_IENA | CNW_IMR_RFU1);
297:
298: /* Enable receiver */
299: CNW_CMD0(sc, CNW_CMD_ER);
300:
301: /* "Set the IENA bit in COR" */
302: WAIT_WOC(sc);
303: bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_COR,
304: CNW_COR_IENA | CNW_COR_LVLREQ);
305: }
306:
307:
308: /*
309: * Enable and initialize the card.
310: */
311: int
312: cnw_enable(sc)
313: struct cnw_softc *sc;
314: {
315: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
316:
317: sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_NET,
318: cnw_intr, sc, sc->sc_dev.dv_xname);
319: if (sc->sc_ih == NULL) {
320: printf("%s: couldn't establish interrupt handler\n",
321: sc->sc_dev.dv_xname);
322: return (EIO);
323: }
324: if (pcmcia_function_enable(sc->sc_pf) != 0) {
325: printf("%s: couldn't enable card\n", sc->sc_dev.dv_xname);
326: return (EIO);
327: }
328: cnw_init(sc);
329: ifp->if_flags |= IFF_RUNNING;
330: return (0);
331: }
332:
333:
334: /*
335: * Stop and disable the card.
336: */
337: void
338: cnw_disable(sc)
339: struct cnw_softc *sc;
340: {
341: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
342:
343: pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
344: pcmcia_function_disable(sc->sc_pf);
345: ifp->if_flags &= ~IFF_RUNNING;
346: ifp->if_timer = 0;
347: }
348:
349:
350: /*
351: * Match the hardware we handle.
352: */
353: int
354: cnw_match(parent, match, aux)
355: struct device *parent;
356: void *match, *aux;
357: {
358: struct pcmcia_attach_args *pa = aux;
359:
360: if (pa->manufacturer == PCMCIA_VENDOR_XIRCOM &&
361: pa->product == PCMCIA_PRODUCT_XIRCOM_XIR_CNW_801)
362: return (1);
363: if (pa->manufacturer == PCMCIA_VENDOR_XIRCOM &&
364: pa->product == PCMCIA_PRODUCT_XIRCOM_XIR_CNW_802)
365: return (1);
366: return (0);
367: }
368:
369:
370: /*
371: * Attach the card.
372: */
373: void
374: cnw_attach(parent, self, aux)
375: struct device *parent, *self;
376: void *aux;
377: {
378: struct cnw_softc *sc = (void *) self;
379: struct pcmcia_attach_args *pa = aux;
380: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
381: int i;
382:
383: /* Enable the card */
384: sc->sc_pf = pa->pf;
385: pcmcia_function_init(sc->sc_pf, SIMPLEQ_FIRST(&sc->sc_pf->cfe_head));
386: if (pcmcia_function_enable(sc->sc_pf)) {
387: printf(": function enable failed\n");
388: return;
389: }
390:
391: /* Map I/O register and "memory" */
392: if (pcmcia_io_alloc(sc->sc_pf, 0, CNW_IO_SIZE, CNW_IO_SIZE,
393: &sc->sc_pcioh) != 0) {
394: printf(": can't allocate i/o space\n");
395: return;
396: }
397: if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_IO16, 0,
398: CNW_IO_SIZE, &sc->sc_pcioh, &sc->sc_iowin) != 0) {
399: printf(": can't map i/o space\n");
400: return;
401: }
402: sc->sc_iot = sc->sc_pcioh.iot;
403: sc->sc_ioh = sc->sc_pcioh.ioh;
404: if (pcmcia_mem_alloc(sc->sc_pf, CNW_MEM_SIZE, &sc->sc_pcmemh) != 0) {
405: printf(": can't allocate memory\n");
406: return;
407: }
408: if (pcmcia_mem_map(sc->sc_pf, PCMCIA_MEM_COMMON, CNW_MEM_ADDR,
409: CNW_MEM_SIZE, &sc->sc_pcmemh, &sc->sc_memoff,
410: &sc->sc_memwin) != 0) {
411: printf(": can't map memory\n");
412: return;
413: }
414: sc->sc_memt = sc->sc_pcmemh.memt;
415: sc->sc_memh = sc->sc_pcmemh.memh;
416:
417: /* Finish setup of softc */
418: sc->sc_domain = cnw_domain;
419: sc->sc_skey = cnw_skey;
420:
421: /* Get MAC address */
422: cnw_reset(sc);
423: for (i = 0; i < ETHER_ADDR_LEN; i++)
424: sc->sc_arpcom.ac_enaddr[i] = bus_space_read_1(sc->sc_memt,
425: sc->sc_memh, sc->sc_memoff + CNW_EREG_PA + i);
426: printf("%s: address %s\n", sc->sc_dev.dv_xname,
427: ether_sprintf(sc->sc_arpcom.ac_enaddr));
428:
429: /* Set up ifnet structure */
430: bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
431: ifp->if_softc = sc;
432: ifp->if_start = cnw_start;
433: ifp->if_ioctl = cnw_ioctl;
434: ifp->if_watchdog = cnw_watchdog;
435: ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
436: IFQ_SET_READY(&ifp->if_snd);
437:
438: /* Attach the interface */
439: if_attach(ifp);
440: ether_ifattach(ifp);
441:
442: /* Disable the card now, and turn it on when the interface goes up */
443: pcmcia_function_disable(sc->sc_pf);
444: }
445:
446: /*
447: * Start outputting on the interface.
448: */
449: void
450: cnw_start(ifp)
451: struct ifnet *ifp;
452: {
453: struct cnw_softc *sc = ifp->if_softc;
454: struct mbuf *m0;
455: int asr;
456:
457: #ifdef CNW_DEBUG
458: if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG)
459: printf("%s: cnw_start\n", ifp->if_xname);
460: #endif
461:
462: for (;;) {
463: /* Is there any buffer space available on the card? */
464: WAIT_WOC(sc);
465: asr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR);
466: if (!(asr & CNW_ASR_TXBA)) {
467: #ifdef CNW_DEBUG
468: if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG)
469: printf("%s: no buffer space\n", ifp->if_xname);
470: #endif
471: return;
472: }
473:
474: IFQ_DEQUEUE(&ifp->if_snd, m0);
475: if (m0 == 0)
476: return;
477:
478: #if NBPFILTER > 0
479: if (ifp->if_bpf)
480: bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT);
481: #endif
482:
483: cnw_transmit(sc, m0);
484: ++ifp->if_opackets;
485: ifp->if_timer = 3; /* start watchdog timer */
486: }
487: }
488:
489:
490: /*
491: * Transmit a packet.
492: */
493: void
494: cnw_transmit(sc, m0)
495: struct cnw_softc *sc;
496: struct mbuf *m0;
497: {
498: int buffer, bufsize, bufoffset, bufptr, bufspace, len, mbytes, n;
499: struct mbuf *m;
500: u_int8_t *mptr;
501:
502: /* Get buffer info from card */
503: buffer = read16(sc, CNW_EREG_TDP);
504: bufsize = read16(sc, CNW_EREG_TDP + 2);
505: bufoffset = read16(sc, CNW_EREG_TDP + 4);
506: #ifdef CNW_DEBUG
507: if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG)
508: printf("%s: cnw_transmit b=0x%x s=%d o=0x%x\n",
509: sc->sc_dev.dv_xname, buffer, bufsize, bufoffset);
510: #endif
511:
512: /* Copy data from mbuf chain to card buffers */
513: bufptr = sc->sc_memoff + buffer + bufoffset;
514: bufspace = bufsize;
515: len = 0;
516: for (m = m0; m; ) {
517: mptr = mtod(m, u_int8_t *);
518: mbytes = m->m_len;
519: len += mbytes;
520: while (mbytes > 0) {
521: if (bufspace == 0) {
522: buffer = read16(sc, buffer);
523: bufptr = sc->sc_memoff + buffer + bufoffset;
524: bufspace = bufsize;
525: #ifdef CNW_DEBUG
526: if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG)
527: printf("%s: next buffer @0x%x\n",
528: sc->sc_dev.dv_xname, buffer);
529: #endif
530: }
531: n = mbytes <= bufspace ? mbytes : bufspace;
532: bus_space_write_region_1(sc->sc_memt, sc->sc_memh,
533: bufptr, mptr, n);
534: bufptr += n;
535: bufspace -= n;
536: mptr += n;
537: mbytes -= n;
538: }
539: MFREE(m, m0);
540: m = m0;
541: }
542:
543: /* Issue transmit command */
544: CNW_CMD2(sc, CNW_CMD_TL, len, len >> 8);
545: }
546:
547:
548: /*
549: * Pull a packet from the card into an mbuf chain.
550: */
551: struct mbuf *
552: cnw_read(sc)
553: struct cnw_softc *sc;
554: {
555: struct mbuf *m, *top, **mp;
556: int totbytes, buffer, bufbytes, bufptr, mbytes, n;
557: u_int8_t *mptr;
558:
559: WAIT_WOC(sc);
560: totbytes = read16(sc, CNW_EREG_RDP);
561: #ifdef CNW_DEBUG
562: if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG)
563: printf("%s: recv %d bytes\n", sc->sc_dev.dv_xname, totbytes);
564: #endif
565: buffer = CNW_EREG_RDP + 2;
566: bufbytes = 0;
567: bufptr = 0; /* XXX make gcc happy */
568:
569: MGETHDR(m, M_DONTWAIT, MT_DATA);
570: if (m == 0)
571: return (0);
572: m->m_pkthdr.rcvif = &sc->sc_arpcom.ac_if;
573: m->m_pkthdr.len = totbytes;
574: mbytes = MHLEN;
575: top = 0;
576: mp = ⊤
577:
578: while (totbytes > 0) {
579: if (top) {
580: MGET(m, M_DONTWAIT, MT_DATA);
581: if (m == 0) {
582: m_freem(top);
583: return (0);
584: }
585: mbytes = MLEN;
586: }
587: if (totbytes >= MINCLSIZE) {
588: MCLGET(m, M_DONTWAIT);
589: if ((m->m_flags & M_EXT) == 0) {
590: m_free(m);
591: m_freem(top);
592: return (0);
593: }
594: mbytes = MCLBYTES;
595: }
596: if (!top) {
597: int pad =
598: ALIGN(sizeof(struct ether_header)) -
599: sizeof(struct ether_header);
600: m->m_data += pad;
601: mbytes -= pad;
602: }
603: mptr = mtod(m, u_int8_t *);
604: mbytes = m->m_len = min(totbytes, mbytes);
605: totbytes -= mbytes;
606: while (mbytes > 0) {
607: if (bufbytes == 0) {
608: buffer = read16(sc, buffer);
609: bufbytes = read16(sc, buffer + 2);
610: bufptr = sc->sc_memoff + buffer +
611: read16(sc, buffer + 4);
612: #ifdef CNW_DEBUG
613: if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG)
614: printf("%s: %d bytes @0x%x+0x%x\n",
615: sc->sc_dev.dv_xname, bufbytes,
616: buffer, bufptr - buffer -
617: sc->sc_memoff);
618: #endif
619: }
620: n = mbytes <= bufbytes ? mbytes : bufbytes;
621: bus_space_read_region_1(sc->sc_memt, sc->sc_memh,
622: bufptr, mptr, n);
623: bufbytes -= n;
624: bufptr += n;
625: mbytes -= n;
626: mptr += n;
627: }
628: *mp = m;
629: mp = &m->m_next;
630: }
631:
632: return (top);
633: }
634:
635:
636: /*
637: * Handle received packets.
638: */
639: void
640: cnw_recv(sc)
641: struct cnw_softc *sc;
642: {
643: int rser;
644: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
645: struct mbuf *m;
646: struct ether_header *eh;
647:
648: for (;;) {
649: WAIT_WOC(sc);
650: rser = bus_space_read_1(sc->sc_memt, sc->sc_memh,
651: sc->sc_memoff + CNW_EREG_RSER);
652: if (!(rser & CNW_RSER_RXAVAIL))
653: return;
654:
655: /* Pull packet off card */
656: m = cnw_read(sc);
657:
658: /* Acknowledge packet */
659: CNW_CMD0(sc, CNW_CMD_SRP);
660:
661: /* Did we manage to get the packet from the interface? */
662: if (m == 0) {
663: ++ifp->if_ierrors;
664: return;
665: }
666: ++ifp->if_ipackets;
667:
668: #if NBPFILTER > 0
669: if (ifp->if_bpf)
670: bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
671: #endif
672:
673: /*
674: * Check that the packet is for us or {multi,broad}cast. Maybe
675: * there's a fool-poof hardware check for this, but I don't
676: * really know...
677: */
678: eh = mtod(m, struct ether_header *);
679: if ((eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
680: bcmp(sc->sc_arpcom.ac_enaddr, eh->ether_dhost,
681: sizeof(eh->ether_dhost)) != 0) {
682: m_freem(m);
683: continue;
684: }
685:
686: ether_input_mbuf(ifp, m);
687: }
688: }
689:
690:
691: /*
692: * Interrupt handler.
693: */
694: int
695: cnw_intr(arg)
696: void *arg;
697: {
698: struct cnw_softc *sc = arg;
699: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
700: int ret, status, rser, tser;
701:
702: if (!(sc->sc_arpcom.ac_if.if_flags & IFF_RUNNING))
703: return (0);
704: ifp->if_timer = 0; /* stop watchdog timer */
705:
706: ret = 0;
707: for (;;) {
708: WAIT_WOC(sc);
709: if (!(bus_space_read_1(sc->sc_iot, sc->sc_ioh,
710: CNW_REG_CCSR) & 0x02)) {
711: if (ret == 0)
712: printf("%s: spurious interrupt\n",
713: sc->sc_dev.dv_xname);
714: return (ret);
715: }
716: ret = 1;
717: status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR);
718:
719: /* Anything to receive? */
720: if (status & CNW_ASR_RXRDY)
721: cnw_recv(sc);
722:
723: /* Receive error */
724: if (status & CNW_ASR_RXERR) {
725: /*
726: * I get a *lot* of spurious receive errors
727: * (many per second), even when the interface
728: * is quiescent, so we don't increment
729: * if_ierrors here.
730: */
731: rser = bus_space_read_1(sc->sc_memt, sc->sc_memh,
732: sc->sc_memoff + CNW_EREG_RSER);
733: /* Clear error bits in RSER */
734: WAIT_WOC(sc);
735: bus_space_write_1(sc->sc_memt, sc->sc_memh,
736: sc->sc_memoff + CNW_EREG_RSERW,
737: CNW_RSER_RXERR |
738: (rser & (CNW_RSER_RXCRC | CNW_RSER_RXBIG)));
739: /* Clear RXERR in ASR */
740: WAIT_WOC(sc);
741: bus_space_write_1(sc->sc_memt, sc->sc_memh,
742: sc->sc_memoff + CNW_EREG_ASCC, CNW_ASR_RXERR);
743: }
744:
745: /* Transmit done */
746: if (status & CNW_ASR_TXDN) {
747: tser = bus_space_read_1(sc->sc_memt, sc->sc_memh,
748: CNW_EREG_TSER);
749: if (tser & CNW_TSER_TXOK) {
750: WAIT_WOC(sc);
751: bus_space_write_1(sc->sc_memt, sc->sc_memh,
752: sc->sc_memoff + CNW_EREG_TSERW,
753: CNW_TSER_TXOK | CNW_TSER_RTRY);
754: }
755: if (tser & CNW_TSER_ERROR) {
756: ++ifp->if_oerrors;
757: WAIT_WOC(sc);
758: bus_space_write_1(sc->sc_memt, sc->sc_memh,
759: sc->sc_memoff + CNW_EREG_TSERW,
760: (tser & CNW_TSER_ERROR) |
761: CNW_TSER_RTRY);
762: }
763: /* Continue to send packets from the queue */
764: cnw_start(&sc->sc_arpcom.ac_if);
765: }
766:
767: }
768: }
769:
770:
771: /*
772: * Handle device ioctls.
773: */
774: int
775: cnw_ioctl(ifp, cmd, data)
776: register struct ifnet *ifp;
777: u_long cmd;
778: caddr_t data;
779: {
780: struct cnw_softc *sc = ifp->if_softc;
781: struct ifaddr *ifa = (struct ifaddr *)data;
782: int s, error = 0;
783:
784: s = splnet();
785:
786: switch (cmd) {
787:
788: case SIOCSIFADDR:
789: if (!(ifp->if_flags & IFF_RUNNING) &&
790: (error = cnw_enable(sc)) != 0)
791: break;
792: ifp->if_flags |= IFF_UP;
793: switch (ifa->ifa_addr->sa_family) {
794: #ifdef INET
795: case AF_INET:
796: arp_ifinit(&sc->sc_arpcom, ifa);
797: break;
798: #endif
799: }
800: break;
801:
802: case SIOCSIFFLAGS:
803: if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_RUNNING) {
804: /*
805: * The interface is marked down and it is running, so
806: * stop it.
807: */
808: cnw_disable(sc);
809: } else if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP){
810: /*
811: * The interface is marked up and it is stopped, so
812: * start it.
813: */
814: error = cnw_enable(sc);
815: }
816: break;
817:
818: default:
819: error = EINVAL;
820: break;
821: }
822:
823: splx(s);
824: return (error);
825: }
826:
827:
828: /*
829: * Device timeout/watchdog routine. Entered if the device neglects to
830: * generate an interrupt after a transmit has been started on it.
831: */
832: void
833: cnw_watchdog(ifp)
834: struct ifnet *ifp;
835: {
836: struct cnw_softc *sc = ifp->if_softc;
837:
838: printf("%s: device timeout; card reset\n", sc->sc_dev.dv_xname);
839: ++ifp->if_oerrors;
840: cnw_init(sc);
841: }
842:
843:
844: int
845: cnw_detach(dev, flags)
846: struct device *dev;
847: int flags;
848: {
849: struct cnw_softc *sc = (struct cnw_softc *)dev;
850: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
851: int rv = 0;
852:
853: pcmcia_io_unmap(sc->sc_pf, sc->sc_iowin);
854: pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh);
855: pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwin);
856: pcmcia_mem_free(sc->sc_pf, &sc->sc_pcmemh);
857:
858: ether_ifdetach(ifp);
859: if_detach(ifp);
860:
861: return (rv);
862: }
863:
864: int
865: cnw_activate(dev, act)
866: struct device *dev;
867: enum devact act;
868: {
869: struct cnw_softc *sc = (struct cnw_softc *)dev;
870: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
871: int s;
872:
873: s = splnet();
874: switch (act) {
875: case DVACT_ACTIVATE:
876: pcmcia_function_enable(sc->sc_pf);
877: sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_NET,
878: cnw_intr, sc, sc->sc_dev.dv_xname);
879: cnw_init(sc);
880: break;
881:
882: case DVACT_DEACTIVATE:
883: ifp->if_timer = 0;
884: ifp->if_flags &= ~IFF_RUNNING; /* XXX no cnw_stop() ? */
885: pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
886: pcmcia_function_disable(sc->sc_pf);
887: break;
888: }
889: splx(s);
890: return (0);
891: }
CVSweb