Annotation of sys/dev/usb/if_axe.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: if_axe.c,v 1.78 2007/06/14 10:11:15 mbalmer Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2005, 2006, 2007 Jonathan Gray <jsg@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: /*
! 20: * Copyright (c) 1997, 1998, 1999, 2000-2003
! 21: * Bill Paul <wpaul@windriver.com>. All rights reserved.
! 22: *
! 23: * Redistribution and use in source and binary forms, with or without
! 24: * modification, are permitted provided that the following conditions
! 25: * are met:
! 26: * 1. Redistributions of source code must retain the above copyright
! 27: * notice, this list of conditions and the following disclaimer.
! 28: * 2. Redistributions in binary form must reproduce the above copyright
! 29: * notice, this list of conditions and the following disclaimer in the
! 30: * documentation and/or other materials provided with the distribution.
! 31: * 3. All advertising materials mentioning features or use of this software
! 32: * must display the following acknowledgement:
! 33: * This product includes software developed by Bill Paul.
! 34: * 4. Neither the name of the author nor the names of any co-contributors
! 35: * may be used to endorse or promote products derived from this software
! 36: * without specific prior written permission.
! 37: *
! 38: * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
! 39: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 40: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 41: * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
! 42: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 43: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 44: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 45: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 46: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 47: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
! 48: * THE POSSIBILITY OF SUCH DAMAGE.
! 49: */
! 50:
! 51: #include <sys/cdefs.h>
! 52:
! 53: /*
! 54: * ASIX Electronics AX88172 USB 2.0 ethernet driver. Used in the
! 55: * LinkSys USB200M and various other adapters.
! 56: *
! 57: * Manuals available from:
! 58: * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF
! 59: * Note: you need the manual for the AX88170 chip (USB 1.x ethernet
! 60: * controller) to find the definitions for the RX control register.
! 61: * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF
! 62: *
! 63: * Written by Bill Paul <wpaul@windriver.com>
! 64: * Senior Engineer
! 65: * Wind River Systems
! 66: */
! 67:
! 68: /*
! 69: * The AX88172 provides USB ethernet supports at 10 and 100Mbps.
! 70: * It uses an external PHY (reference designs use a RealTek chip),
! 71: * and has a 64-bit multicast hash filter. There is some information
! 72: * missing from the manual which one needs to know in order to make
! 73: * the chip function:
! 74: *
! 75: * - You must set bit 7 in the RX control register, otherwise the
! 76: * chip won't receive any packets.
! 77: * - You must initialize all 3 IPG registers, or you won't be able
! 78: * to send any packets.
! 79: *
! 80: * Note that this device appears to only support loading the station
! 81: * address via autload from the EEPROM (i.e. there's no way to manaully
! 82: * set it).
! 83: *
! 84: * (Adam Weinberger wanted me to name this driver if_gir.c.)
! 85: */
! 86:
! 87: /*
! 88: * Ported to OpenBSD 3/28/2004 by Greg Taleck <taleck@oz.net>
! 89: * with bits and pieces from the aue and url drivers.
! 90: */
! 91:
! 92: #include "bpfilter.h"
! 93:
! 94: #include <sys/param.h>
! 95: #include <sys/systm.h>
! 96: #include <sys/sockio.h>
! 97: #include <sys/rwlock.h>
! 98: #include <sys/mbuf.h>
! 99: #include <sys/kernel.h>
! 100: #include <sys/proc.h>
! 101: #include <sys/socket.h>
! 102:
! 103: #include <sys/device.h>
! 104:
! 105: #include <machine/bus.h>
! 106:
! 107: #include <net/if.h>
! 108: #include <net/if_dl.h>
! 109: #include <net/if_media.h>
! 110:
! 111: #if NBPFILTER > 0
! 112: #include <net/bpf.h>
! 113: #endif
! 114:
! 115: #ifdef INET
! 116: #include <netinet/in.h>
! 117: #include <netinet/in_systm.h>
! 118: #include <netinet/in_var.h>
! 119: #include <netinet/ip.h>
! 120: #include <netinet/if_ether.h>
! 121: #endif
! 122:
! 123: #include <dev/mii/mii.h>
! 124: #include <dev/mii/miivar.h>
! 125:
! 126: #include <dev/usb/usb.h>
! 127: #include <dev/usb/usbdi.h>
! 128: #include <dev/usb/usbdi_util.h>
! 129: #include <dev/usb/usbdivar.h>
! 130: #include <dev/usb/usbdevs.h>
! 131:
! 132: #include <dev/usb/if_axereg.h>
! 133:
! 134: #ifdef AXE_DEBUG
! 135: #define DPRINTF(x) do { if (axedebug) printf x; } while (0)
! 136: #define DPRINTFN(n,x) do { if (axedebug >= (n)) printf x; } while (0)
! 137: int axedebug = 0;
! 138: #else
! 139: #define DPRINTF(x)
! 140: #define DPRINTFN(n,x)
! 141: #endif
! 142:
! 143: /*
! 144: * Various supported device vendors/products.
! 145: */
! 146: const struct axe_type axe_devs[] = {
! 147: { { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UF200}, 0 },
! 148: { { USB_VENDOR_ACERCM, USB_PRODUCT_ACERCM_EP1427X2}, 0 },
! 149: { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172}, 0 },
! 150: { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772}, AX772 },
! 151: { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178}, AX178 },
! 152: { { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T}, 0 },
! 153: { { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055 }, AX178 },
! 154: { { USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_SNAPPORT}, 0 },
! 155: { { USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR}, 0},
! 156: { { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2}, AX772 },
! 157: { { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX }, 0},
! 158: { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100}, 0 },
! 159: { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1 }, AX772 },
! 160: { { USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E}, 0 },
! 161: { { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2 }, AX178 },
! 162: { { USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1}, 0 },
! 163: { { USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M}, 0 },
! 164: { { USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000 }, AX178 },
! 165: { { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX}, 0 },
! 166: { { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120}, 0 },
! 167: { { USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS }, AX772 },
! 168: { { USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T }, AX178 },
! 169: { { USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL}, 0 },
! 170: { { USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029}, 0 },
! 171: { { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028 }, AX178 }
! 172: };
! 173:
! 174: #define axe_lookup(v, p) ((struct axe_type *)usb_lookup(axe_devs, v, p))
! 175:
! 176: int axe_match(struct device *, void *, void *);
! 177: void axe_attach(struct device *, struct device *, void *);
! 178: int axe_detach(struct device *, int);
! 179: int axe_activate(struct device *, enum devact);
! 180:
! 181: struct cfdriver axe_cd = {
! 182: NULL, "axe", DV_IFNET
! 183: };
! 184:
! 185: const struct cfattach axe_ca = {
! 186: sizeof(struct axe_softc),
! 187: axe_match,
! 188: axe_attach,
! 189: axe_detach,
! 190: axe_activate,
! 191: };
! 192:
! 193: int axe_tx_list_init(struct axe_softc *);
! 194: int axe_rx_list_init(struct axe_softc *);
! 195: struct mbuf *axe_newbuf(void);
! 196: int axe_encap(struct axe_softc *, struct mbuf *, int);
! 197: void axe_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
! 198: void axe_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
! 199: void axe_tick(void *);
! 200: void axe_tick_task(void *);
! 201: void axe_start(struct ifnet *);
! 202: int axe_ioctl(struct ifnet *, u_long, caddr_t);
! 203: void axe_init(void *);
! 204: void axe_stop(struct axe_softc *);
! 205: void axe_watchdog(struct ifnet *);
! 206: int axe_miibus_readreg(struct device *, int, int);
! 207: void axe_miibus_writereg(struct device *, int, int, int);
! 208: void axe_miibus_statchg(struct device *);
! 209: int axe_cmd(struct axe_softc *, int, int, int, void *);
! 210: int axe_ifmedia_upd(struct ifnet *);
! 211: void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
! 212: void axe_reset(struct axe_softc *sc);
! 213:
! 214: void axe_setmulti(struct axe_softc *);
! 215: void axe_lock_mii(struct axe_softc *sc);
! 216: void axe_unlock_mii(struct axe_softc *sc);
! 217:
! 218: void axe_ax88178_init(struct axe_softc *);
! 219: void axe_ax88772_init(struct axe_softc *);
! 220:
! 221: /* Get exclusive access to the MII registers */
! 222: void
! 223: axe_lock_mii(struct axe_softc *sc)
! 224: {
! 225: sc->axe_refcnt++;
! 226: rw_enter_write(&sc->axe_mii_lock);
! 227: }
! 228:
! 229: void
! 230: axe_unlock_mii(struct axe_softc *sc)
! 231: {
! 232: rw_exit_write(&sc->axe_mii_lock);
! 233: if (--sc->axe_refcnt < 0)
! 234: usb_detach_wakeup(&sc->axe_dev);
! 235: }
! 236:
! 237: int
! 238: axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf)
! 239: {
! 240: usb_device_request_t req;
! 241: usbd_status err;
! 242:
! 243: if (sc->axe_dying)
! 244: return(0);
! 245:
! 246: if (AXE_CMD_DIR(cmd))
! 247: req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
! 248: else
! 249: req.bmRequestType = UT_READ_VENDOR_DEVICE;
! 250: req.bRequest = AXE_CMD_CMD(cmd);
! 251: USETW(req.wValue, val);
! 252: USETW(req.wIndex, index);
! 253: USETW(req.wLength, AXE_CMD_LEN(cmd));
! 254:
! 255: err = usbd_do_request(sc->axe_udev, &req, buf);
! 256:
! 257: if (err)
! 258: return(-1);
! 259:
! 260: return(0);
! 261: }
! 262:
! 263: int
! 264: axe_miibus_readreg(struct device *dev, int phy, int reg)
! 265: {
! 266: struct axe_softc *sc = (void *)dev;
! 267: usbd_status err;
! 268: uWord val;
! 269:
! 270: if (sc->axe_dying) {
! 271: DPRINTF(("axe: dying\n"));
! 272: return(0);
! 273: }
! 274:
! 275: #ifdef notdef
! 276: /*
! 277: * The chip tells us the MII address of any supported
! 278: * PHYs attached to the chip, so only read from those.
! 279: */
! 280:
! 281: DPRINTF(("axe_miibus_readreg: phy 0x%x reg 0x%x\n", phy, reg));
! 282:
! 283: if (sc->axe_phyaddrs[0] != AXE_NOPHY && phy != sc->axe_phyaddrs[0])
! 284: return (0);
! 285:
! 286: if (sc->axe_phyaddrs[1] != AXE_NOPHY && phy != sc->axe_phyaddrs[1])
! 287: return (0);
! 288: #endif
! 289: if (sc->axe_phyaddrs[0] != 0xFF && sc->axe_phyaddrs[0] != phy)
! 290: return (0);
! 291:
! 292: USETW(val, 0);
! 293:
! 294: axe_lock_mii(sc);
! 295: axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
! 296: err = axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, val);
! 297: axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
! 298: axe_unlock_mii(sc);
! 299:
! 300: if (err) {
! 301: printf("axe%d: read PHY failed\n", sc->axe_unit);
! 302: return(-1);
! 303: }
! 304:
! 305: if (UGETW(val))
! 306: sc->axe_phyaddrs[0] = phy;
! 307:
! 308: return (UGETW(val));
! 309: }
! 310:
! 311: void
! 312: axe_miibus_writereg(struct device *dev, int phy, int reg, int val)
! 313: {
! 314: struct axe_softc *sc = (void *)dev;
! 315: usbd_status err;
! 316: uWord uval;
! 317:
! 318: if (sc->axe_dying)
! 319: return;
! 320:
! 321: USETW(uval, val);
! 322:
! 323: axe_lock_mii(sc);
! 324: axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
! 325: err = axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, uval);
! 326: axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
! 327: axe_unlock_mii(sc);
! 328:
! 329: if (err) {
! 330: printf("axe%d: write PHY failed\n", sc->axe_unit);
! 331: return;
! 332: }
! 333: }
! 334:
! 335: void
! 336: axe_miibus_statchg(struct device *dev)
! 337: {
! 338: struct axe_softc *sc = (void *)dev;
! 339: struct mii_data *mii = GET_MII(sc);
! 340: int val, err;
! 341:
! 342: if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
! 343: val = AXE_MEDIA_FULL_DUPLEX;
! 344: else
! 345: val = 0;
! 346:
! 347: if (sc->axe_flags & AX178 || sc->axe_flags & AX772) {
! 348: val |= (AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC);
! 349:
! 350: switch (IFM_SUBTYPE(mii->mii_media_active)) {
! 351: case IFM_1000_T:
! 352: val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK;
! 353: break;
! 354: case IFM_100_TX:
! 355: val |= AXE_178_MEDIA_100TX;
! 356: break;
! 357: case IFM_10_T:
! 358: /* doesn't need to be handled */
! 359: break;
! 360: }
! 361: }
! 362:
! 363: DPRINTF(("axe_miibus_statchg: val=0x%x\n", val));
! 364: err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL);
! 365: if (err) {
! 366: printf("%s: media change failed\n", sc->axe_dev.dv_xname);
! 367: return;
! 368: }
! 369: }
! 370:
! 371: /*
! 372: * Set media options.
! 373: */
! 374: int
! 375: axe_ifmedia_upd(struct ifnet *ifp)
! 376: {
! 377: struct axe_softc *sc = ifp->if_softc;
! 378: struct mii_data *mii = GET_MII(sc);
! 379:
! 380: sc->axe_link = 0;
! 381: if (mii->mii_instance) {
! 382: struct mii_softc *miisc;
! 383: LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
! 384: mii_phy_reset(miisc);
! 385: }
! 386: mii_mediachg(mii);
! 387:
! 388: return (0);
! 389: }
! 390:
! 391: /*
! 392: * Report current media status.
! 393: */
! 394: void
! 395: axe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
! 396: {
! 397: struct axe_softc *sc = ifp->if_softc;
! 398: struct mii_data *mii = GET_MII(sc);
! 399:
! 400: mii_pollstat(mii);
! 401: ifmr->ifm_active = mii->mii_media_active;
! 402: ifmr->ifm_status = mii->mii_media_status;
! 403: }
! 404:
! 405: void
! 406: axe_setmulti(struct axe_softc *sc)
! 407: {
! 408: struct ifnet *ifp;
! 409: struct ether_multi *enm;
! 410: struct ether_multistep step;
! 411: u_int32_t h = 0;
! 412: uWord urxmode;
! 413: u_int16_t rxmode;
! 414: u_int8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
! 415:
! 416: if (sc->axe_dying)
! 417: return;
! 418:
! 419: ifp = GET_IFP(sc);
! 420:
! 421: axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, urxmode);
! 422: rxmode = UGETW(urxmode);
! 423:
! 424: if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
! 425: allmulti:
! 426: rxmode |= AXE_RXCMD_ALLMULTI;
! 427: axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
! 428: return;
! 429: } else
! 430: rxmode &= ~AXE_RXCMD_ALLMULTI;
! 431:
! 432: /* now program new ones */
! 433: ETHER_FIRST_MULTI(step, &sc->arpcom, enm);
! 434: while (enm != NULL) {
! 435: if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
! 436: ETHER_ADDR_LEN) != 0)
! 437: goto allmulti;
! 438:
! 439: h = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN) >> 26;
! 440: hashtbl[h / 8] |= 1 << (h % 8);
! 441: ETHER_NEXT_MULTI(step, enm);
! 442: }
! 443:
! 444: ifp->if_flags &= ~IFF_ALLMULTI;
! 445: axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl);
! 446: axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
! 447: return;
! 448: }
! 449:
! 450: void
! 451: axe_reset(struct axe_softc *sc)
! 452: {
! 453: if (sc->axe_dying)
! 454: return;
! 455: /* XXX What to reset? */
! 456:
! 457: /* Wait a little while for the chip to get its brains in order. */
! 458: DELAY(1000);
! 459: return;
! 460: }
! 461:
! 462: void
! 463: axe_ax88178_init(struct axe_softc *sc)
! 464: {
! 465: int gpio0 = 0, phymode = 0;
! 466: u_int16_t eeprom;
! 467:
! 468: axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL);
! 469: /* XXX magic */
! 470: axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom);
! 471: axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL);
! 472:
! 473: eeprom = letoh16(eeprom);
! 474:
! 475: DPRINTF((" EEPROM is 0x%x\n", eeprom));
! 476:
! 477: /* if EEPROM is invalid we have to use to GPIO0 */
! 478: if (eeprom == 0xffff) {
! 479: phymode = 0;
! 480: gpio0 = 1;
! 481: } else {
! 482: phymode = eeprom & 7;
! 483: gpio0 = (eeprom & 0x80) ? 0 : 1;
! 484: }
! 485:
! 486: DPRINTF(("use gpio0: %d, phymode %d\n", gpio0, phymode));
! 487:
! 488: axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x008c, NULL);
! 489: usbd_delay_ms(sc->axe_udev, 40);
! 490: if ((eeprom >> 8) != 1) {
! 491: axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL);
! 492: usbd_delay_ms(sc->axe_udev, 30);
! 493:
! 494: axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x001c, NULL);
! 495: usbd_delay_ms(sc->axe_udev, 300);
! 496:
! 497: axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL);
! 498: usbd_delay_ms(sc->axe_udev, 30);
! 499: } else {
! 500: DPRINTF(("axe gpio phymode == 1 path\n"));
! 501: axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x0004, NULL);
! 502: usbd_delay_ms(sc->axe_udev, 30);
! 503: axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x000c, NULL);
! 504: usbd_delay_ms(sc->axe_udev, 30);
! 505: }
! 506:
! 507: /* soft reset */
! 508: axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL);
! 509: usbd_delay_ms(sc->axe_udev, 150);
! 510: axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
! 511: AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL);
! 512: usbd_delay_ms(sc->axe_udev, 150);
! 513: axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
! 514: }
! 515:
! 516: void
! 517: axe_ax88772_init(struct axe_softc *sc)
! 518: {
! 519: axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL);
! 520: usbd_delay_ms(sc->axe_udev, 40);
! 521:
! 522: if (sc->axe_phyaddrs[1] == AXE_INTPHY) {
! 523: /* ask for the embedded PHY */
! 524: axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL);
! 525: usbd_delay_ms(sc->axe_udev, 10);
! 526:
! 527: /* power down and reset state, pin reset state */
! 528: axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL);
! 529: usbd_delay_ms(sc->axe_udev, 60);
! 530:
! 531: /* power down/reset state, pin operating state */
! 532: axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
! 533: AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL);
! 534: usbd_delay_ms(sc->axe_udev, 150);
! 535:
! 536: /* power up, reset */
! 537: axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL);
! 538:
! 539: /* power up, operating */
! 540: axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
! 541: AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL);
! 542: } else {
! 543: /* ask for external PHY */
! 544: axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL);
! 545: usbd_delay_ms(sc->axe_udev, 10);
! 546:
! 547: /* power down internal PHY */
! 548: axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
! 549: AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL);
! 550: }
! 551:
! 552: usbd_delay_ms(sc->axe_udev, 150);
! 553: axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
! 554: }
! 555:
! 556: /*
! 557: * Probe for a AX88172 chip.
! 558: */
! 559: int
! 560: axe_match(struct device *parent, void *match, void *aux)
! 561: {
! 562: struct usb_attach_arg *uaa = aux;
! 563:
! 564: if (!uaa->iface)
! 565: return(UMATCH_NONE);
! 566:
! 567: return (axe_lookup(uaa->vendor, uaa->product) != NULL ?
! 568: UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
! 569: }
! 570:
! 571: /*
! 572: * Attach the interface. Allocate softc structures, do ifmedia
! 573: * setup and ethernet/BPF attach.
! 574: */
! 575: void
! 576: axe_attach(struct device *parent, struct device *self, void *aux)
! 577: {
! 578: struct axe_softc *sc = (struct axe_softc *)self;
! 579: struct usb_attach_arg *uaa = aux;
! 580: usbd_device_handle dev = uaa->device;
! 581: usbd_status err;
! 582: usb_interface_descriptor_t *id;
! 583: usb_endpoint_descriptor_t *ed;
! 584: struct mii_data *mii;
! 585: u_char eaddr[ETHER_ADDR_LEN];
! 586: char *devinfop;
! 587: char *devname = sc->axe_dev.dv_xname;
! 588: struct ifnet *ifp;
! 589: int i, s;
! 590:
! 591: devinfop = usbd_devinfo_alloc(dev, 0);
! 592: printf("\n");
! 593:
! 594: sc->axe_unit = self->dv_unit; /*device_get_unit(self);*/
! 595:
! 596: err = usbd_set_config_no(dev, AXE_CONFIG_NO, 1);
! 597: if (err) {
! 598: printf("axe%d: getting interface handle failed\n",
! 599: sc->axe_unit);
! 600: usbd_devinfo_free(devinfop);
! 601: return;
! 602: }
! 603:
! 604: sc->axe_flags = axe_lookup(uaa->vendor, uaa->product)->axe_flags;
! 605:
! 606: usb_init_task(&sc->axe_tick_task, axe_tick_task, sc);
! 607: rw_init(&sc->axe_mii_lock, "axemii");
! 608: usb_init_task(&sc->axe_stop_task, (void (*)(void *))axe_stop, sc);
! 609:
! 610: err = usbd_device2interface_handle(dev, AXE_IFACE_IDX, &sc->axe_iface);
! 611: if (err) {
! 612: printf("axe%d: getting interface handle failed\n",
! 613: sc->axe_unit);
! 614: usbd_devinfo_free(devinfop);
! 615: return;
! 616: }
! 617:
! 618: sc->axe_udev = dev;
! 619: sc->axe_product = uaa->product;
! 620: sc->axe_vendor = uaa->vendor;
! 621:
! 622: id = usbd_get_interface_descriptor(sc->axe_iface);
! 623:
! 624: printf("%s: %s", sc->axe_dev.dv_xname, devinfop);
! 625: usbd_devinfo_free(devinfop);
! 626:
! 627: /* decide on what our bufsize will be */
! 628: if (sc->axe_flags & AX178 || sc->axe_flags & AX772)
! 629: sc->axe_bufsz = (sc->axe_udev->speed == USB_SPEED_HIGH) ?
! 630: AXE_178_MAX_BUFSZ : AXE_178_MIN_BUFSZ;
! 631: else
! 632: sc->axe_bufsz = AXE_172_BUFSZ;
! 633:
! 634: /* Find endpoints. */
! 635: for (i = 0; i < id->bNumEndpoints; i++) {
! 636: ed = usbd_interface2endpoint_descriptor(sc->axe_iface, i);
! 637: if (!ed) {
! 638: printf(" couldn't get ep %d\n", i);
! 639: return;
! 640: }
! 641: if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
! 642: UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
! 643: sc->axe_ed[AXE_ENDPT_RX] = ed->bEndpointAddress;
! 644: } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
! 645: UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
! 646: sc->axe_ed[AXE_ENDPT_TX] = ed->bEndpointAddress;
! 647: } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
! 648: UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
! 649: sc->axe_ed[AXE_ENDPT_INTR] = ed->bEndpointAddress;
! 650: }
! 651: }
! 652:
! 653: s = splnet();
! 654:
! 655: /* We need the PHYID for init dance in some cases */
! 656: axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, (void *)&sc->axe_phyaddrs);
! 657:
! 658: DPRINTF((" phyaddrs[0]: %x phyaddrs[1]: %x\n",
! 659: sc->axe_phyaddrs[0], sc->axe_phyaddrs[1]));
! 660:
! 661: if (sc->axe_flags & AX178) {
! 662: axe_ax88178_init(sc);
! 663: printf(", AX88178");
! 664: } else if (sc->axe_flags & AX772) {
! 665: axe_ax88772_init(sc);
! 666: printf(", AX88772");
! 667: } else
! 668: printf(", AX88172");
! 669:
! 670: /*
! 671: * Get station address.
! 672: */
! 673: if (sc->axe_flags & AX178 || sc->axe_flags & AX772)
! 674: axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, &eaddr);
! 675: else
! 676: axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, &eaddr);
! 677:
! 678: /*
! 679: * Load IPG values
! 680: */
! 681: axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, (void *)&sc->axe_ipgs);
! 682:
! 683: /*
! 684: * Work around broken adapters that appear to lie about
! 685: * their PHY addresses.
! 686: */
! 687: sc->axe_phyaddrs[0] = sc->axe_phyaddrs[1] = 0xFF;
! 688:
! 689: /*
! 690: * An ASIX chip was detected. Inform the world.
! 691: */
! 692: printf(", address %s\n", ether_sprintf(eaddr));
! 693:
! 694: bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
! 695:
! 696: /* Initialize interface info.*/
! 697: ifp = &sc->arpcom.ac_if;
! 698: ifp->if_softc = sc;
! 699: strlcpy(ifp->if_xname, devname, IFNAMSIZ);
! 700: ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
! 701: ifp->if_ioctl = axe_ioctl;
! 702: ifp->if_start = axe_start;
! 703:
! 704: ifp->if_watchdog = axe_watchdog;
! 705:
! 706: /* ifp->if_baudrate = 10000000; */
! 707: /* ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;*/
! 708:
! 709: IFQ_SET_READY(&ifp->if_snd);
! 710:
! 711: /* Initialize MII/media info. */
! 712: mii = &sc->axe_mii;
! 713: mii->mii_ifp = ifp;
! 714: mii->mii_readreg = axe_miibus_readreg;
! 715: mii->mii_writereg = axe_miibus_writereg;
! 716: mii->mii_statchg = axe_miibus_statchg;
! 717: mii->mii_flags = MIIF_AUTOTSLEEP;
! 718:
! 719: ifmedia_init(&mii->mii_media, 0, axe_ifmedia_upd, axe_ifmedia_sts);
! 720: mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
! 721:
! 722: if (LIST_FIRST(&mii->mii_phys) == NULL) {
! 723: ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
! 724: ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
! 725: } else
! 726: ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
! 727:
! 728: /* Attach the interface. */
! 729: if_attach(ifp);
! 730: ether_ifattach(ifp);
! 731:
! 732: timeout_set(&sc->axe_stat_ch, NULL, NULL);
! 733:
! 734: sc->axe_attached = 1;
! 735: splx(s);
! 736:
! 737: usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->axe_udev,
! 738: &sc->axe_dev);
! 739: }
! 740:
! 741: int
! 742: axe_detach(struct device *self, int flags)
! 743: {
! 744: struct axe_softc *sc = (struct axe_softc *)self;
! 745: int s;
! 746: struct ifnet *ifp = GET_IFP(sc);
! 747:
! 748: DPRINTFN(2,("%s: %s: enter\n", sc->axe_dev.dv_xname, __func__));
! 749:
! 750: /* Detached before attached finished, so just bail out. */
! 751: if (!sc->axe_attached)
! 752: return (0);
! 753:
! 754: timeout_del(&sc->axe_stat_ch);
! 755:
! 756: sc->axe_dying = 1;
! 757:
! 758: ether_ifdetach(ifp);
! 759:
! 760: if (sc->axe_ep[AXE_ENDPT_TX] != NULL)
! 761: usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_TX]);
! 762: if (sc->axe_ep[AXE_ENDPT_RX] != NULL)
! 763: usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_RX]);
! 764: if (sc->axe_ep[AXE_ENDPT_INTR] != NULL)
! 765: usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_INTR]);
! 766:
! 767: /*
! 768: * Remove any pending tasks. They cannot be executing because they run
! 769: * in the same thread as detach.
! 770: */
! 771: usb_rem_task(sc->axe_udev, &sc->axe_tick_task);
! 772: usb_rem_task(sc->axe_udev, &sc->axe_stop_task);
! 773:
! 774: s = splusb();
! 775:
! 776: if (--sc->axe_refcnt >= 0) {
! 777: /* Wait for processes to go away */
! 778: usb_detach_wait(&sc->axe_dev);
! 779: }
! 780:
! 781: if (ifp->if_flags & IFF_RUNNING)
! 782: axe_stop(sc);
! 783:
! 784: mii_detach(&sc->axe_mii, MII_PHY_ANY, MII_OFFSET_ANY);
! 785: ifmedia_delete_instance(&sc->axe_mii.mii_media, IFM_INST_ANY);
! 786: ether_ifdetach(ifp);
! 787: if_detach(ifp);
! 788:
! 789: #ifdef DIAGNOSTIC
! 790: if (sc->axe_ep[AXE_ENDPT_TX] != NULL ||
! 791: sc->axe_ep[AXE_ENDPT_RX] != NULL ||
! 792: sc->axe_ep[AXE_ENDPT_INTR] != NULL)
! 793: printf("%s: detach has active endpoints\n",
! 794: sc->axe_dev.dv_xname);
! 795: #endif
! 796:
! 797: sc->axe_attached = 0;
! 798:
! 799: if (--sc->axe_refcnt >= 0) {
! 800: /* Wait for processes to go away. */
! 801: usb_detach_wait(&sc->axe_dev);
! 802: }
! 803: splx(s);
! 804:
! 805: usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->axe_udev,
! 806: &sc->axe_dev);
! 807:
! 808: return (0);
! 809: }
! 810:
! 811: int
! 812: axe_activate(struct device *self, enum devact act)
! 813: {
! 814: struct axe_softc *sc = (struct axe_softc *)self;
! 815:
! 816: DPRINTFN(2,("%s: %s: enter\n", sc->axe_dev.dv_xname, __func__));
! 817:
! 818: switch (act) {
! 819: case DVACT_ACTIVATE:
! 820: break;
! 821:
! 822: case DVACT_DEACTIVATE:
! 823: sc->axe_dying = 1;
! 824: break;
! 825: }
! 826: return (0);
! 827: }
! 828:
! 829: struct mbuf *
! 830: axe_newbuf(void)
! 831: {
! 832: struct mbuf *m;
! 833:
! 834: MGETHDR(m, M_DONTWAIT, MT_DATA);
! 835: if (m == NULL)
! 836: return (NULL);
! 837:
! 838: MCLGET(m, M_DONTWAIT);
! 839: if (!(m->m_flags & M_EXT)) {
! 840: m_freem(m);
! 841: return (NULL);
! 842: }
! 843:
! 844: m->m_len = m->m_pkthdr.len = MCLBYTES;
! 845: m_adj(m, ETHER_ALIGN);
! 846:
! 847: return (m);
! 848: }
! 849:
! 850: int
! 851: axe_rx_list_init(struct axe_softc *sc)
! 852: {
! 853: struct axe_cdata *cd;
! 854: struct axe_chain *c;
! 855: int i;
! 856:
! 857: DPRINTF(("%s: %s: enter\n", sc->axe_dev.dv_xname, __func__));
! 858:
! 859: cd = &sc->axe_cdata;
! 860: for (i = 0; i < AXE_RX_LIST_CNT; i++) {
! 861: c = &cd->axe_rx_chain[i];
! 862: c->axe_sc = sc;
! 863: c->axe_idx = i;
! 864: c->axe_mbuf = NULL;
! 865: if (c->axe_xfer == NULL) {
! 866: c->axe_xfer = usbd_alloc_xfer(sc->axe_udev);
! 867: if (c->axe_xfer == NULL)
! 868: return (ENOBUFS);
! 869: c->axe_buf = usbd_alloc_buffer(c->axe_xfer,
! 870: sc->axe_bufsz);
! 871: if (c->axe_buf == NULL) {
! 872: usbd_free_xfer(c->axe_xfer);
! 873: return (ENOBUFS);
! 874: }
! 875: }
! 876: }
! 877:
! 878: return (0);
! 879: }
! 880:
! 881: int
! 882: axe_tx_list_init(struct axe_softc *sc)
! 883: {
! 884: struct axe_cdata *cd;
! 885: struct axe_chain *c;
! 886: int i;
! 887:
! 888: DPRINTF(("%s: %s: enter\n", sc->axe_dev.dv_xname, __func__));
! 889:
! 890: cd = &sc->axe_cdata;
! 891: for (i = 0; i < AXE_TX_LIST_CNT; i++) {
! 892: c = &cd->axe_tx_chain[i];
! 893: c->axe_sc = sc;
! 894: c->axe_idx = i;
! 895: c->axe_mbuf = NULL;
! 896: if (c->axe_xfer == NULL) {
! 897: c->axe_xfer = usbd_alloc_xfer(sc->axe_udev);
! 898: if (c->axe_xfer == NULL)
! 899: return (ENOBUFS);
! 900: c->axe_buf = usbd_alloc_buffer(c->axe_xfer,
! 901: sc->axe_bufsz);
! 902: if (c->axe_buf == NULL) {
! 903: usbd_free_xfer(c->axe_xfer);
! 904: return (ENOBUFS);
! 905: }
! 906: }
! 907: }
! 908:
! 909: return (0);
! 910: }
! 911:
! 912: /*
! 913: * A frame has been uploaded: pass the resulting mbuf chain up to
! 914: * the higher level protocols.
! 915: */
! 916: void
! 917: axe_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
! 918: {
! 919: struct axe_chain *c = (struct axe_chain *)priv;
! 920: struct axe_softc *sc = c->axe_sc;
! 921: struct ifnet *ifp = GET_IFP(sc);
! 922: u_char *buf = c->axe_buf;
! 923: u_int32_t total_len;
! 924: u_int16_t pktlen = 0;
! 925: struct mbuf *m;
! 926: struct axe_sframe_hdr hdr;
! 927: int s;
! 928:
! 929: DPRINTFN(10,("%s: %s: enter\n", sc->axe_dev.dv_xname,__func__));
! 930:
! 931: if (sc->axe_dying)
! 932: return;
! 933:
! 934: if (!(ifp->if_flags & IFF_RUNNING))
! 935: return;
! 936:
! 937: if (status != USBD_NORMAL_COMPLETION) {
! 938: if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
! 939: return;
! 940: if (usbd_ratecheck(&sc->axe_rx_notice)) {
! 941: printf("%s: usb errors on rx: %s\n",
! 942: sc->axe_dev.dv_xname, usbd_errstr(status));
! 943: }
! 944: if (status == USBD_STALLED)
! 945: usbd_clear_endpoint_stall_async(sc->axe_ep[AXE_ENDPT_RX]);
! 946: goto done;
! 947: }
! 948:
! 949: usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
! 950:
! 951: do {
! 952: if (sc->axe_flags & AX178 || sc->axe_flags & AX772) {
! 953: if (total_len < sizeof(hdr)) {
! 954: ifp->if_ierrors++;
! 955: goto done;
! 956: }
! 957:
! 958: buf += pktlen;
! 959:
! 960: memcpy(&hdr, buf, sizeof(hdr));
! 961: total_len -= sizeof(hdr);
! 962:
! 963: if ((hdr.len ^ hdr.ilen) != 0xffff) {
! 964: ifp->if_ierrors++;
! 965: goto done;
! 966: }
! 967: pktlen = letoh16(hdr.len);
! 968: if (pktlen > total_len) {
! 969: ifp->if_ierrors++;
! 970: goto done;
! 971: }
! 972:
! 973: buf += sizeof(hdr);
! 974:
! 975: if ((pktlen % 2) != 0)
! 976: pktlen++;
! 977:
! 978: if ((total_len - pktlen) < 0)
! 979: total_len = 0;
! 980: else
! 981: total_len -= pktlen;
! 982: } else {
! 983: pktlen = total_len; /* crc on the end? */
! 984: total_len = 0;
! 985: }
! 986:
! 987: m = axe_newbuf();
! 988: if (m == NULL) {
! 989: ifp->if_ierrors++;
! 990: goto done;
! 991: }
! 992:
! 993: ifp->if_ipackets++;
! 994: m->m_pkthdr.rcvif = ifp;
! 995: m->m_pkthdr.len = m->m_len = pktlen;
! 996:
! 997: memcpy(mtod(m, char *), buf, pktlen);
! 998:
! 999: /* push the packet up */
! 1000: s = splnet();
! 1001: #if NBPFILTER > 0
! 1002: if (ifp->if_bpf)
! 1003: bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
! 1004: #endif
! 1005:
! 1006: ether_input_mbuf(ifp, m);
! 1007:
! 1008: splx(s);
! 1009:
! 1010: } while (total_len > 0);
! 1011:
! 1012: done:
! 1013: memset(c->axe_buf, 0, sc->axe_bufsz);
! 1014:
! 1015: /* Setup new transfer. */
! 1016: usbd_setup_xfer(xfer, sc->axe_ep[AXE_ENDPT_RX],
! 1017: c, c->axe_buf, sc->axe_bufsz,
! 1018: USBD_SHORT_XFER_OK | USBD_NO_COPY,
! 1019: USBD_NO_TIMEOUT, axe_rxeof);
! 1020: usbd_transfer(xfer);
! 1021:
! 1022: DPRINTFN(10,("%s: %s: start rx\n", sc->axe_dev.dv_xname, __func__));
! 1023:
! 1024: return;
! 1025: }
! 1026:
! 1027: /*
! 1028: * A frame was downloaded to the chip. It's safe for us to clean up
! 1029: * the list buffers.
! 1030: */
! 1031:
! 1032: void
! 1033: axe_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
! 1034: {
! 1035: struct axe_softc *sc;
! 1036: struct axe_chain *c;
! 1037: struct ifnet *ifp;
! 1038: int s;
! 1039:
! 1040: c = priv;
! 1041: sc = c->axe_sc;
! 1042: ifp = &sc->arpcom.ac_if;
! 1043:
! 1044: if (sc->axe_dying)
! 1045: return;
! 1046:
! 1047: s = splnet();
! 1048:
! 1049: if (status != USBD_NORMAL_COMPLETION) {
! 1050: if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
! 1051: splx(s);
! 1052: return;
! 1053: }
! 1054: ifp->if_oerrors++;
! 1055: printf("axe%d: usb error on tx: %s\n", sc->axe_unit,
! 1056: usbd_errstr(status));
! 1057: if (status == USBD_STALLED)
! 1058: usbd_clear_endpoint_stall_async(sc->axe_ep[AXE_ENDPT_TX]);
! 1059: splx(s);
! 1060: return;
! 1061: }
! 1062:
! 1063: ifp->if_timer = 0;
! 1064: ifp->if_flags &= ~IFF_OACTIVE;
! 1065:
! 1066: m_freem(c->axe_mbuf);
! 1067: c->axe_mbuf = NULL;
! 1068:
! 1069: if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
! 1070: axe_start(ifp);
! 1071:
! 1072: ifp->if_opackets++;
! 1073: splx(s);
! 1074: return;
! 1075: }
! 1076:
! 1077: void
! 1078: axe_tick(void *xsc)
! 1079: {
! 1080: struct axe_softc *sc = xsc;
! 1081:
! 1082: if (sc == NULL)
! 1083: return;
! 1084:
! 1085: DPRINTFN(0xff, ("%s: %s: enter\n", sc->axe_dev.dv_xname,
! 1086: __func__));
! 1087:
! 1088: if (sc->axe_dying)
! 1089: return;
! 1090:
! 1091: /* Perform periodic stuff in process context */
! 1092: usb_add_task(sc->axe_udev, &sc->axe_tick_task);
! 1093:
! 1094: }
! 1095:
! 1096: void
! 1097: axe_tick_task(void *xsc)
! 1098: {
! 1099: int s;
! 1100: struct axe_softc *sc;
! 1101: struct ifnet *ifp;
! 1102: struct mii_data *mii;
! 1103:
! 1104: sc = xsc;
! 1105:
! 1106: if (sc == NULL)
! 1107: return;
! 1108:
! 1109: if (sc->axe_dying)
! 1110: return;
! 1111:
! 1112: ifp = GET_IFP(sc);
! 1113: mii = GET_MII(sc);
! 1114: if (mii == NULL)
! 1115: return;
! 1116:
! 1117: s = splnet();
! 1118:
! 1119: mii_tick(mii);
! 1120: if (!sc->axe_link && mii->mii_media_status & IFM_ACTIVE &&
! 1121: IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
! 1122: DPRINTF(("%s: %s: got link\n",
! 1123: sc->axe_dev.dv_xname, __func__));
! 1124: sc->axe_link++;
! 1125: if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
! 1126: axe_start(ifp);
! 1127: }
! 1128:
! 1129: timeout_del(&sc->axe_stat_ch);
! 1130: timeout_set(&sc->axe_stat_ch, axe_tick, sc);
! 1131: timeout_add(&sc->axe_stat_ch, hz);
! 1132:
! 1133: splx(s);
! 1134: }
! 1135:
! 1136: int
! 1137: axe_encap(struct axe_softc *sc, struct mbuf *m, int idx)
! 1138: {
! 1139: struct axe_chain *c;
! 1140: usbd_status err;
! 1141: struct axe_sframe_hdr hdr;
! 1142: int length, boundary;
! 1143:
! 1144: c = &sc->axe_cdata.axe_tx_chain[idx];
! 1145:
! 1146: if (sc->axe_flags & AX178 || sc->axe_flags & AX772) {
! 1147: boundary = (sc->axe_udev->speed == USB_SPEED_HIGH) ? 512 : 64;
! 1148:
! 1149: hdr.len = htole16(m->m_pkthdr.len);
! 1150: hdr.ilen = ~hdr.len;
! 1151:
! 1152: memcpy(c->axe_buf, &hdr, sizeof(hdr));
! 1153: length = sizeof(hdr);
! 1154:
! 1155: m_copydata(m, 0, m->m_pkthdr.len, c->axe_buf + length);
! 1156: length += m->m_pkthdr.len;
! 1157:
! 1158: if ((length % boundary) == 0) {
! 1159: hdr.len = 0x0000;
! 1160: hdr.ilen = 0xffff;
! 1161: memcpy(c->axe_buf + length, &hdr, sizeof(hdr));
! 1162: length += sizeof(hdr);
! 1163: }
! 1164:
! 1165: } else {
! 1166: m_copydata(m, 0, m->m_pkthdr.len, c->axe_buf);
! 1167: length = m->m_pkthdr.len;
! 1168: }
! 1169:
! 1170: c->axe_mbuf = m;
! 1171:
! 1172: usbd_setup_xfer(c->axe_xfer, sc->axe_ep[AXE_ENDPT_TX],
! 1173: c, c->axe_buf, length, USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
! 1174: 10000, axe_txeof);
! 1175:
! 1176: /* Transmit */
! 1177: err = usbd_transfer(c->axe_xfer);
! 1178: if (err != USBD_IN_PROGRESS) {
! 1179: axe_stop(sc);
! 1180: return(EIO);
! 1181: }
! 1182:
! 1183: sc->axe_cdata.axe_tx_cnt++;
! 1184:
! 1185: return(0);
! 1186: }
! 1187:
! 1188: void
! 1189: axe_start(struct ifnet *ifp)
! 1190: {
! 1191: struct axe_softc *sc;
! 1192: struct mbuf *m_head = NULL;
! 1193:
! 1194: sc = ifp->if_softc;
! 1195:
! 1196: if (!sc->axe_link)
! 1197: return;
! 1198:
! 1199: if (ifp->if_flags & IFF_OACTIVE)
! 1200: return;
! 1201:
! 1202: IFQ_POLL(&ifp->if_snd, m_head);
! 1203: if (m_head == NULL)
! 1204: return;
! 1205:
! 1206: if (axe_encap(sc, m_head, 0)) {
! 1207: ifp->if_flags |= IFF_OACTIVE;
! 1208: return;
! 1209: }
! 1210: IFQ_DEQUEUE(&ifp->if_snd, m_head);
! 1211:
! 1212: /*
! 1213: * If there's a BPF listener, bounce a copy of this frame
! 1214: * to him.
! 1215: */
! 1216: #if NBPFILTER > 0
! 1217: if (ifp->if_bpf)
! 1218: bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
! 1219: #endif
! 1220:
! 1221: ifp->if_flags |= IFF_OACTIVE;
! 1222:
! 1223: /*
! 1224: * Set a timeout in case the chip goes out to lunch.
! 1225: */
! 1226: ifp->if_timer = 5;
! 1227:
! 1228: return;
! 1229: }
! 1230:
! 1231: void
! 1232: axe_init(void *xsc)
! 1233: {
! 1234: struct axe_softc *sc = xsc;
! 1235: struct ifnet *ifp = &sc->arpcom.ac_if;
! 1236: struct axe_chain *c;
! 1237: usbd_status err;
! 1238: int rxmode;
! 1239: int i, s;
! 1240:
! 1241: s = splnet();
! 1242:
! 1243: /*
! 1244: * Cancel pending I/O and free all RX/TX buffers.
! 1245: */
! 1246: axe_reset(sc);
! 1247:
! 1248: /* Enable RX logic. */
! 1249:
! 1250: /* Init RX ring. */
! 1251: if (axe_rx_list_init(sc) == ENOBUFS) {
! 1252: printf("axe%d: rx list init failed\n", sc->axe_unit);
! 1253: splx(s);
! 1254: return;
! 1255: }
! 1256:
! 1257: /* Init TX ring. */
! 1258: if (axe_tx_list_init(sc) == ENOBUFS) {
! 1259: printf("axe%d: tx list init failed\n", sc->axe_unit);
! 1260: splx(s);
! 1261: return;
! 1262: }
! 1263:
! 1264: /* Set transmitter IPG values */
! 1265: if (sc->axe_flags & AX178 || sc->axe_flags & AX772)
! 1266: axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->axe_ipgs[2],
! 1267: (sc->axe_ipgs[1] << 8) | (sc->axe_ipgs[0]), NULL);
! 1268: else {
! 1269: axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->axe_ipgs[0], NULL);
! 1270: axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->axe_ipgs[1], NULL);
! 1271: axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->axe_ipgs[2], NULL);
! 1272: }
! 1273:
! 1274: /* Enable receiver, set RX mode */
! 1275: rxmode = AXE_RXCMD_MULTICAST|AXE_RXCMD_ENABLE;
! 1276: if (sc->axe_flags & AX178 || sc->axe_flags & AX772) {
! 1277: if (sc->axe_udev->speed == USB_SPEED_HIGH) {
! 1278: /* largest possible USB buffer size for AX88178 */
! 1279: rxmode |= AXE_178_RXCMD_MFB;
! 1280: }
! 1281: } else
! 1282: rxmode |= AXE_172_RXCMD_UNICAST;
! 1283:
! 1284: /* If we want promiscuous mode, set the allframes bit. */
! 1285: if (ifp->if_flags & IFF_PROMISC)
! 1286: rxmode |= AXE_RXCMD_PROMISC;
! 1287:
! 1288: if (ifp->if_flags & IFF_BROADCAST)
! 1289: rxmode |= AXE_RXCMD_BROADCAST;
! 1290:
! 1291: axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
! 1292:
! 1293: /* Load the multicast filter. */
! 1294: axe_setmulti(sc);
! 1295:
! 1296: /* Open RX and TX pipes. */
! 1297: err = usbd_open_pipe(sc->axe_iface, sc->axe_ed[AXE_ENDPT_RX],
! 1298: USBD_EXCLUSIVE_USE, &sc->axe_ep[AXE_ENDPT_RX]);
! 1299: if (err) {
! 1300: printf("axe%d: open rx pipe failed: %s\n",
! 1301: sc->axe_unit, usbd_errstr(err));
! 1302: splx(s);
! 1303: return;
! 1304: }
! 1305:
! 1306: err = usbd_open_pipe(sc->axe_iface, sc->axe_ed[AXE_ENDPT_TX],
! 1307: USBD_EXCLUSIVE_USE, &sc->axe_ep[AXE_ENDPT_TX]);
! 1308: if (err) {
! 1309: printf("axe%d: open tx pipe failed: %s\n",
! 1310: sc->axe_unit, usbd_errstr(err));
! 1311: splx(s);
! 1312: return;
! 1313: }
! 1314:
! 1315: /* Start up the receive pipe. */
! 1316: for (i = 0; i < AXE_RX_LIST_CNT; i++) {
! 1317: c = &sc->axe_cdata.axe_rx_chain[i];
! 1318: usbd_setup_xfer(c->axe_xfer, sc->axe_ep[AXE_ENDPT_RX],
! 1319: c, c->axe_buf, sc->axe_bufsz,
! 1320: USBD_SHORT_XFER_OK | USBD_NO_COPY,
! 1321: USBD_NO_TIMEOUT, axe_rxeof);
! 1322: usbd_transfer(c->axe_xfer);
! 1323: }
! 1324:
! 1325: ifp->if_flags |= IFF_RUNNING;
! 1326: ifp->if_flags &= ~IFF_OACTIVE;
! 1327:
! 1328: splx(s);
! 1329:
! 1330: timeout_del(&sc->axe_stat_ch);
! 1331: timeout_set(&sc->axe_stat_ch, axe_tick, sc);
! 1332: timeout_add(&sc->axe_stat_ch, hz);
! 1333: return;
! 1334: }
! 1335:
! 1336: int
! 1337: axe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
! 1338: {
! 1339: struct axe_softc *sc = ifp->if_softc;
! 1340: struct ifreq *ifr = (struct ifreq *)data;
! 1341: struct ifaddr *ifa = (struct ifaddr *)data;
! 1342: struct mii_data *mii;
! 1343: uWord rxmode;
! 1344: int error = 0;
! 1345:
! 1346: switch(cmd) {
! 1347: case SIOCSIFADDR:
! 1348: ifp->if_flags |= IFF_UP;
! 1349: if (!(ifp->if_flags & IFF_RUNNING))
! 1350: axe_init(sc);
! 1351: #ifdef INET
! 1352: if (ifa->ifa_addr->sa_family == AF_INET)
! 1353: arp_ifinit(&sc->arpcom, ifa);
! 1354: #endif
! 1355: break;
! 1356:
! 1357: case SIOCSIFMTU:
! 1358: if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ifp->if_hardmtu)
! 1359: error = EINVAL;
! 1360: else if (ifp->if_mtu != ifr->ifr_mtu)
! 1361: ifp->if_mtu = ifr->ifr_mtu;
! 1362: break;
! 1363:
! 1364: case SIOCSIFFLAGS:
! 1365: if (ifp->if_flags & IFF_UP) {
! 1366: if (ifp->if_flags & IFF_RUNNING &&
! 1367: ifp->if_flags & IFF_PROMISC &&
! 1368: !(sc->axe_if_flags & IFF_PROMISC)) {
! 1369:
! 1370: axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, rxmode);
! 1371: axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0,
! 1372: UGETW(rxmode) | AXE_RXCMD_PROMISC, NULL);
! 1373:
! 1374: axe_setmulti(sc);
! 1375: } else if (ifp->if_flags & IFF_RUNNING &&
! 1376: !(ifp->if_flags & IFF_PROMISC) &&
! 1377: sc->axe_if_flags & IFF_PROMISC) {
! 1378: axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, rxmode);
! 1379: axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0,
! 1380: UGETW(rxmode) & ~AXE_RXCMD_PROMISC, NULL);
! 1381: axe_setmulti(sc);
! 1382: } else if (!(ifp->if_flags & IFF_RUNNING))
! 1383: axe_init(sc);
! 1384: } else {
! 1385: if (ifp->if_flags & IFF_RUNNING)
! 1386: axe_stop(sc);
! 1387: }
! 1388: sc->axe_if_flags = ifp->if_flags;
! 1389: break;
! 1390: case SIOCADDMULTI:
! 1391: case SIOCDELMULTI:
! 1392: error = (cmd == SIOCADDMULTI) ?
! 1393: ether_addmulti(ifr, &sc->arpcom) :
! 1394: ether_delmulti(ifr, &sc->arpcom);
! 1395:
! 1396: if (error == ENETRESET) {
! 1397: /*
! 1398: * Multicast list has changed; set the hardware
! 1399: * filter accordingly.
! 1400: */
! 1401: if (ifp->if_flags & IFF_RUNNING)
! 1402: axe_setmulti(sc);
! 1403: error = 0;
! 1404: }
! 1405: break;
! 1406: case SIOCGIFMEDIA:
! 1407: case SIOCSIFMEDIA:
! 1408: mii = GET_MII(sc);
! 1409: error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
! 1410: break;
! 1411:
! 1412: default:
! 1413: error = EINVAL;
! 1414: break;
! 1415: }
! 1416:
! 1417: return(error);
! 1418: }
! 1419:
! 1420: void
! 1421: axe_watchdog(struct ifnet *ifp)
! 1422: {
! 1423: struct axe_softc *sc;
! 1424: struct axe_chain *c;
! 1425: usbd_status stat;
! 1426: int s;
! 1427:
! 1428: sc = ifp->if_softc;
! 1429:
! 1430: ifp->if_oerrors++;
! 1431: printf("axe%d: watchdog timeout\n", sc->axe_unit);
! 1432:
! 1433: s = splusb();
! 1434: c = &sc->axe_cdata.axe_tx_chain[0];
! 1435: usbd_get_xfer_status(c->axe_xfer, NULL, NULL, NULL, &stat);
! 1436: axe_txeof(c->axe_xfer, c, stat);
! 1437:
! 1438: if (!IFQ_IS_EMPTY(&ifp->if_snd))
! 1439: axe_start(ifp);
! 1440: splx(s);
! 1441: }
! 1442:
! 1443: /*
! 1444: * Stop the adapter and free any mbufs allocated to the
! 1445: * RX and TX lists.
! 1446: */
! 1447: void
! 1448: axe_stop(struct axe_softc *sc)
! 1449: {
! 1450: usbd_status err;
! 1451: struct ifnet *ifp;
! 1452: int i;
! 1453:
! 1454: axe_reset(sc);
! 1455:
! 1456: ifp = &sc->arpcom.ac_if;
! 1457: ifp->if_timer = 0;
! 1458: ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
! 1459:
! 1460: timeout_del(&sc->axe_stat_ch);
! 1461:
! 1462: /* Stop transfers. */
! 1463: if (sc->axe_ep[AXE_ENDPT_RX] != NULL) {
! 1464: err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_RX]);
! 1465: if (err) {
! 1466: printf("axe%d: abort rx pipe failed: %s\n",
! 1467: sc->axe_unit, usbd_errstr(err));
! 1468: }
! 1469: err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_RX]);
! 1470: if (err) {
! 1471: printf("axe%d: close rx pipe failed: %s\n",
! 1472: sc->axe_unit, usbd_errstr(err));
! 1473: }
! 1474: sc->axe_ep[AXE_ENDPT_RX] = NULL;
! 1475: }
! 1476:
! 1477: if (sc->axe_ep[AXE_ENDPT_TX] != NULL) {
! 1478: err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_TX]);
! 1479: if (err) {
! 1480: printf("axe%d: abort tx pipe failed: %s\n",
! 1481: sc->axe_unit, usbd_errstr(err));
! 1482: }
! 1483: err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_TX]);
! 1484: if (err) {
! 1485: printf("axe%d: close tx pipe failed: %s\n",
! 1486: sc->axe_unit, usbd_errstr(err));
! 1487: }
! 1488: sc->axe_ep[AXE_ENDPT_TX] = NULL;
! 1489: }
! 1490:
! 1491: if (sc->axe_ep[AXE_ENDPT_INTR] != NULL) {
! 1492: err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_INTR]);
! 1493: if (err) {
! 1494: printf("axe%d: abort intr pipe failed: %s\n",
! 1495: sc->axe_unit, usbd_errstr(err));
! 1496: }
! 1497: err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_INTR]);
! 1498: if (err) {
! 1499: printf("axe%d: close intr pipe failed: %s\n",
! 1500: sc->axe_unit, usbd_errstr(err));
! 1501: }
! 1502: sc->axe_ep[AXE_ENDPT_INTR] = NULL;
! 1503: }
! 1504:
! 1505: /* Free RX resources. */
! 1506: for (i = 0; i < AXE_RX_LIST_CNT; i++) {
! 1507: if (sc->axe_cdata.axe_rx_chain[i].axe_mbuf != NULL) {
! 1508: m_freem(sc->axe_cdata.axe_rx_chain[i].axe_mbuf);
! 1509: sc->axe_cdata.axe_rx_chain[i].axe_mbuf = NULL;
! 1510: }
! 1511: if (sc->axe_cdata.axe_rx_chain[i].axe_xfer != NULL) {
! 1512: usbd_free_xfer(sc->axe_cdata.axe_rx_chain[i].axe_xfer);
! 1513: sc->axe_cdata.axe_rx_chain[i].axe_xfer = NULL;
! 1514: }
! 1515: }
! 1516:
! 1517: /* Free TX resources. */
! 1518: for (i = 0; i < AXE_TX_LIST_CNT; i++) {
! 1519: if (sc->axe_cdata.axe_tx_chain[i].axe_mbuf != NULL) {
! 1520: m_freem(sc->axe_cdata.axe_tx_chain[i].axe_mbuf);
! 1521: sc->axe_cdata.axe_tx_chain[i].axe_mbuf = NULL;
! 1522: }
! 1523: if (sc->axe_cdata.axe_tx_chain[i].axe_xfer != NULL) {
! 1524: usbd_free_xfer(sc->axe_cdata.axe_tx_chain[i].axe_xfer);
! 1525: sc->axe_cdata.axe_tx_chain[i].axe_xfer = NULL;
! 1526: }
! 1527: }
! 1528:
! 1529: sc->axe_link = 0;
! 1530: }
! 1531:
CVSweb