Annotation of sys/dev/pcmcia/if_cnw.c, Revision 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