1*9060cf22Skevlo /* $OpenBSD: if_axen.c,v 1.34 2024/10/07 07:35:40 kevlo Exp $ */ 2b5ab0bc5Syuo 3b5ab0bc5Syuo /* 4b5ab0bc5Syuo * Copyright (c) 2013 Yojiro UO <yuo@openbsd.org> 5b5ab0bc5Syuo * 6b5ab0bc5Syuo * Permission to use, copy, modify, and distribute this software for any 7b5ab0bc5Syuo * purpose with or without fee is hereby granted, provided that the above 8b5ab0bc5Syuo * copyright notice and this permission notice appear in all copies. 9b5ab0bc5Syuo * 10b5ab0bc5Syuo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11b5ab0bc5Syuo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12b5ab0bc5Syuo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13b5ab0bc5Syuo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14b5ab0bc5Syuo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15b5ab0bc5Syuo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16b5ab0bc5Syuo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17b5ab0bc5Syuo */ 18b5ab0bc5Syuo 19b5ab0bc5Syuo /* 20*9060cf22Skevlo * ASIX Electronics AX88178a/AX88772d USB 2.0 ethernet and 219c887effSkevlo * AX88179/AX88179a USB 3.0 Ethernet driver. 22b5ab0bc5Syuo */ 23b5ab0bc5Syuo 24b5ab0bc5Syuo #include "bpfilter.h" 25b5ab0bc5Syuo 26b5ab0bc5Syuo #include <sys/param.h> 27b5ab0bc5Syuo #include <sys/systm.h> 28b5ab0bc5Syuo #include <sys/sockio.h> 29b5ab0bc5Syuo #include <sys/rwlock.h> 30b5ab0bc5Syuo #include <sys/mbuf.h> 31b5ab0bc5Syuo 32b5ab0bc5Syuo #include <sys/device.h> 33b5ab0bc5Syuo 34b5ab0bc5Syuo #include <machine/bus.h> 35b5ab0bc5Syuo 36b5ab0bc5Syuo #include <net/if.h> 37b5ab0bc5Syuo #include <net/if_media.h> 38b5ab0bc5Syuo 39b5ab0bc5Syuo #if NBPFILTER > 0 40b5ab0bc5Syuo #include <net/bpf.h> 41b5ab0bc5Syuo #endif 42b5ab0bc5Syuo 43b5ab0bc5Syuo #include <netinet/in.h> 44b5ab0bc5Syuo #include <netinet/if_ether.h> 45b5ab0bc5Syuo 46b5ab0bc5Syuo #include <dev/mii/mii.h> 47b5ab0bc5Syuo #include <dev/mii/miivar.h> 48b5ab0bc5Syuo 49b5ab0bc5Syuo #include <dev/usb/usb.h> 50b5ab0bc5Syuo #include <dev/usb/usbdi.h> 51b5ab0bc5Syuo #include <dev/usb/usbdi_util.h> 52b5ab0bc5Syuo #include <dev/usb/usbdivar.h> 53b5ab0bc5Syuo #include <dev/usb/usbdevs.h> 54b5ab0bc5Syuo 55b5ab0bc5Syuo #include <dev/usb/if_axenreg.h> 56b5ab0bc5Syuo 57b5ab0bc5Syuo #ifdef AXEN_DEBUG 58b5ab0bc5Syuo #define DPRINTF(x) do { if (axendebug) printf x; } while (0) 59b5ab0bc5Syuo #define DPRINTFN(n,x) do { if (axendebug >= (n)) printf x; } while (0) 60b5ab0bc5Syuo int axendebug = 0; 61b5ab0bc5Syuo #else 62b5ab0bc5Syuo #define DPRINTF(x) 63b5ab0bc5Syuo #define DPRINTFN(n,x) 64b5ab0bc5Syuo #endif 65b5ab0bc5Syuo 66b5ab0bc5Syuo #define AXEN_TOE /* enable checksum offload function */ 67b5ab0bc5Syuo 68b5ab0bc5Syuo /* 69b5ab0bc5Syuo * Various supported device vendors/products. 70b5ab0bc5Syuo */ 71b5ab0bc5Syuo const struct axen_type axen_devs[] = { 72834d143cSyuo #if 0 /* not tested */ 73834d143cSyuo { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178A}, AX178A }, 74834d143cSyuo #endif 758e553514Sjsg { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88179}, AX179 }, 768e553514Sjsg { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUB1312}, AX179 }, 778e553514Sjsg { { USB_VENDOR_LENOVO, USB_PRODUCT_LENOVO_AX88179}, AX179 }, 788e553514Sjsg { { USB_VENDOR_SAMSUNG2, USB_PRODUCT_SAMSUNG2_AX88179}, AX179 }, 798e553514Sjsg { { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN032}, AX179 } 80b5ab0bc5Syuo }; 81b5ab0bc5Syuo 82b5ab0bc5Syuo #define axen_lookup(v, p) ((struct axen_type *)usb_lookup(axen_devs, v, p)) 83b5ab0bc5Syuo 84b5ab0bc5Syuo int axen_match(struct device *, void *, void *); 85b5ab0bc5Syuo void axen_attach(struct device *, struct device *, void *); 86b5ab0bc5Syuo int axen_detach(struct device *, int); 87b5ab0bc5Syuo 88b5ab0bc5Syuo struct cfdriver axen_cd = { 89b5ab0bc5Syuo NULL, "axen", DV_IFNET 90b5ab0bc5Syuo }; 91b5ab0bc5Syuo 92b5ab0bc5Syuo const struct cfattach axen_ca = { 9353c6612dSmpi sizeof(struct axen_softc), axen_match, axen_attach, axen_detach 94b5ab0bc5Syuo }; 95b5ab0bc5Syuo 96b5ab0bc5Syuo int axen_tx_list_init(struct axen_softc *); 97b5ab0bc5Syuo int axen_rx_list_init(struct axen_softc *); 98b5ab0bc5Syuo struct mbuf *axen_newbuf(void); 99b5ab0bc5Syuo int axen_encap(struct axen_softc *, struct mbuf *, int); 100b5ab0bc5Syuo void axen_rxeof(struct usbd_xfer *, void *, usbd_status); 101b5ab0bc5Syuo void axen_txeof(struct usbd_xfer *, void *, usbd_status); 102b5ab0bc5Syuo void axen_tick(void *); 103b5ab0bc5Syuo void axen_tick_task(void *); 104b5ab0bc5Syuo void axen_start(struct ifnet *); 105b5ab0bc5Syuo int axen_ioctl(struct ifnet *, u_long, caddr_t); 106b5ab0bc5Syuo void axen_init(void *); 107b5ab0bc5Syuo void axen_stop(struct axen_softc *); 108b5ab0bc5Syuo void axen_watchdog(struct ifnet *); 109b5ab0bc5Syuo int axen_miibus_readreg(struct device *, int, int); 110b5ab0bc5Syuo void axen_miibus_writereg(struct device *, int, int, int); 111b5ab0bc5Syuo void axen_miibus_statchg(struct device *); 112b5ab0bc5Syuo int axen_cmd(struct axen_softc *, int, int, int, void *); 113b5ab0bc5Syuo int axen_ifmedia_upd(struct ifnet *); 114b5ab0bc5Syuo void axen_ifmedia_sts(struct ifnet *, struct ifmediareq *); 115b5ab0bc5Syuo void axen_reset(struct axen_softc *sc); 116b5ab0bc5Syuo void axen_iff(struct axen_softc *); 117b5ab0bc5Syuo void axen_lock_mii(struct axen_softc *sc); 118b5ab0bc5Syuo void axen_unlock_mii(struct axen_softc *sc); 119b5ab0bc5Syuo 120b5ab0bc5Syuo void axen_ax88179_init(struct axen_softc *); 121b5ab0bc5Syuo 122b5ab0bc5Syuo /* Get exclusive access to the MII registers */ 123b5ab0bc5Syuo void 124b5ab0bc5Syuo axen_lock_mii(struct axen_softc *sc) 125b5ab0bc5Syuo { 126b5ab0bc5Syuo sc->axen_refcnt++; 127b5ab0bc5Syuo rw_enter_write(&sc->axen_mii_lock); 128b5ab0bc5Syuo } 129b5ab0bc5Syuo 130b5ab0bc5Syuo void 131b5ab0bc5Syuo axen_unlock_mii(struct axen_softc *sc) 132b5ab0bc5Syuo { 133b5ab0bc5Syuo rw_exit_write(&sc->axen_mii_lock); 134b5ab0bc5Syuo if (--sc->axen_refcnt < 0) 135b5ab0bc5Syuo usb_detach_wakeup(&sc->axen_dev); 136b5ab0bc5Syuo } 137b5ab0bc5Syuo 138b5ab0bc5Syuo int 139b5ab0bc5Syuo axen_cmd(struct axen_softc *sc, int cmd, int index, int val, void *buf) 140b5ab0bc5Syuo { 141b5ab0bc5Syuo usb_device_request_t req; 142b5ab0bc5Syuo usbd_status err; 143b5ab0bc5Syuo 144b5ab0bc5Syuo if (usbd_is_dying(sc->axen_udev)) 145834d143cSyuo return 0; 146b5ab0bc5Syuo 147b5ab0bc5Syuo if (AXEN_CMD_DIR(cmd)) 148b5ab0bc5Syuo req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 149b5ab0bc5Syuo else 150b5ab0bc5Syuo req.bmRequestType = UT_READ_VENDOR_DEVICE; 151b5ab0bc5Syuo req.bRequest = AXEN_CMD_CMD(cmd); 152b5ab0bc5Syuo USETW(req.wValue, val); 153b5ab0bc5Syuo USETW(req.wIndex, index); 154b5ab0bc5Syuo USETW(req.wLength, AXEN_CMD_LEN(cmd)); 155b5ab0bc5Syuo 156b5ab0bc5Syuo err = usbd_do_request(sc->axen_udev, &req, buf); 157b5ab0bc5Syuo DPRINTFN(5, ("axen_cmd: cmd 0x%04x val 0x%04x len %d\n", 158b5ab0bc5Syuo cmd, val, AXEN_CMD_LEN(cmd))); 159b5ab0bc5Syuo 160b5ab0bc5Syuo if (err) { 161834d143cSyuo DPRINTF(("axen_cmd err: cmd: %d, error: %d\n", cmd, err)); 162834d143cSyuo return -1; 163b5ab0bc5Syuo } 164b5ab0bc5Syuo 165834d143cSyuo return 0; 166b5ab0bc5Syuo } 167b5ab0bc5Syuo 168b5ab0bc5Syuo int 169b5ab0bc5Syuo axen_miibus_readreg(struct device *dev, int phy, int reg) 170b5ab0bc5Syuo { 171b5ab0bc5Syuo struct axen_softc *sc = (void *)dev; 172bf6de56eSmpi int err; 173b5ab0bc5Syuo uWord val; 174b5ab0bc5Syuo int ival; 175b5ab0bc5Syuo 176b5ab0bc5Syuo if (usbd_is_dying(sc->axen_udev)) { 177b5ab0bc5Syuo DPRINTF(("axen: dying\n")); 178834d143cSyuo return 0; 179b5ab0bc5Syuo } 180b5ab0bc5Syuo 181b5ab0bc5Syuo if (sc->axen_phyno != phy) 182834d143cSyuo return 0; 183b5ab0bc5Syuo 184b5ab0bc5Syuo axen_lock_mii(sc); 185b5ab0bc5Syuo err = axen_cmd(sc, AXEN_CMD_MII_READ_REG, reg, phy, &val); 186b5ab0bc5Syuo axen_unlock_mii(sc); 187b5ab0bc5Syuo 188b5ab0bc5Syuo if (err) { 189b5ab0bc5Syuo printf("axen%d: read PHY failed\n", sc->axen_unit); 190834d143cSyuo return -1; 191b5ab0bc5Syuo } 192b5ab0bc5Syuo 193b5ab0bc5Syuo ival = UGETW(val); 194834d143cSyuo DPRINTFN(2,("axen_miibus_readreg: phy 0x%x reg 0x%x val 0x%x\n", 195834d143cSyuo phy, reg, ival)); 196834d143cSyuo 197b5ab0bc5Syuo if (reg == MII_BMSR) { 198b5ab0bc5Syuo ival &= ~BMSR_EXTCAP; 199b5ab0bc5Syuo } 200b5ab0bc5Syuo 201834d143cSyuo return ival; 202b5ab0bc5Syuo } 203b5ab0bc5Syuo 204b5ab0bc5Syuo void 205b5ab0bc5Syuo axen_miibus_writereg(struct device *dev, int phy, int reg, int val) 206b5ab0bc5Syuo { 207b5ab0bc5Syuo struct axen_softc *sc = (void *)dev; 208bf6de56eSmpi int err; 209b5ab0bc5Syuo uWord uval; 210b5ab0bc5Syuo 211b5ab0bc5Syuo if (usbd_is_dying(sc->axen_udev)) 212b5ab0bc5Syuo return; 213834d143cSyuo 214b5ab0bc5Syuo if (sc->axen_phyno != phy) 215b5ab0bc5Syuo return; 216b5ab0bc5Syuo 217b5ab0bc5Syuo USETW(uval, val); 218b5ab0bc5Syuo axen_lock_mii(sc); 219834d143cSyuo err = axen_cmd(sc, AXEN_CMD_MII_WRITE_REG, reg, phy, &uval); 220b5ab0bc5Syuo axen_unlock_mii(sc); 221b5ab0bc5Syuo DPRINTFN(2, ("axen_miibus_writereg: phy 0x%x reg 0x%x val 0x%0x\n", 222b5ab0bc5Syuo phy, reg, val)); 223b5ab0bc5Syuo 224b5ab0bc5Syuo if (err) { 225b5ab0bc5Syuo printf("axen%d: write PHY failed\n", sc->axen_unit); 226b5ab0bc5Syuo return; 227b5ab0bc5Syuo } 228b5ab0bc5Syuo } 229b5ab0bc5Syuo 230b5ab0bc5Syuo void 231b5ab0bc5Syuo axen_miibus_statchg(struct device *dev) 232b5ab0bc5Syuo { 233b5ab0bc5Syuo struct axen_softc *sc = (void *)dev; 234b5ab0bc5Syuo struct mii_data *mii = GET_MII(sc); 235b5ab0bc5Syuo struct ifnet *ifp; 236b5ab0bc5Syuo int err; 237b5ab0bc5Syuo uint16_t val; 238b5ab0bc5Syuo uWord wval; 239b5ab0bc5Syuo 240b5ab0bc5Syuo ifp = GET_IFP(sc); 241b5ab0bc5Syuo if (mii == NULL || ifp == NULL || 242b5ab0bc5Syuo (ifp->if_flags & IFF_RUNNING) == 0) 243b5ab0bc5Syuo return; 244b5ab0bc5Syuo 245b5ab0bc5Syuo sc->axen_link = 0; 246b5ab0bc5Syuo if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 247b5ab0bc5Syuo (IFM_ACTIVE | IFM_AVALID)) { 248b5ab0bc5Syuo switch (IFM_SUBTYPE(mii->mii_media_active)) { 249b5ab0bc5Syuo case IFM_10_T: 250b5ab0bc5Syuo case IFM_100_TX: 251b5ab0bc5Syuo sc->axen_link++; 252b5ab0bc5Syuo break; 253b5ab0bc5Syuo case IFM_1000_T: 254*9060cf22Skevlo if ((sc->axen_flags & AX772D) != 0) 255*9060cf22Skevlo break; 256b5ab0bc5Syuo sc->axen_link++; 257b5ab0bc5Syuo break; 258b5ab0bc5Syuo default: 259b5ab0bc5Syuo break; 260b5ab0bc5Syuo } 261b5ab0bc5Syuo } 262b5ab0bc5Syuo 263b5ab0bc5Syuo /* Lost link, do nothing. */ 264b5ab0bc5Syuo if (sc->axen_link == 0) 265b5ab0bc5Syuo return; 266b5ab0bc5Syuo 267b5ab0bc5Syuo val = 0; 268b5ab0bc5Syuo if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 269b5ab0bc5Syuo val |= AXEN_MEDIUM_FDX; 270b5ab0bc5Syuo 271b5ab0bc5Syuo val |= (AXEN_MEDIUM_RECV_EN | AXEN_MEDIUM_ALWAYS_ONE); 272b5ab0bc5Syuo val |= (AXEN_MEDIUM_RXFLOW_CTRL_EN | AXEN_MEDIUM_TXFLOW_CTRL_EN); 273b5ab0bc5Syuo 274b5ab0bc5Syuo switch (IFM_SUBTYPE(mii->mii_media_active)) { 275b5ab0bc5Syuo case IFM_1000_T: 276b5ab0bc5Syuo val |= AXEN_MEDIUM_GIGA | AXEN_MEDIUM_EN_125MHZ; 277b5ab0bc5Syuo break; 278b5ab0bc5Syuo case IFM_100_TX: 279b5ab0bc5Syuo val |= AXEN_MEDIUM_PS; 280b5ab0bc5Syuo break; 281b5ab0bc5Syuo case IFM_10_T: 282b5ab0bc5Syuo /* doesn't need to be handled */ 283b5ab0bc5Syuo break; 284b5ab0bc5Syuo } 285b5ab0bc5Syuo 286b5ab0bc5Syuo DPRINTF(("axen_miibus_statchg: val=0x%x\n", val)); 287b5ab0bc5Syuo USETW(wval, val); 288834d143cSyuo axen_lock_mii(sc); 289b5ab0bc5Syuo err = axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval); 290834d143cSyuo axen_unlock_mii(sc); 291b5ab0bc5Syuo if (err) { 292b5ab0bc5Syuo printf("%s: media change failed\n", sc->axen_dev.dv_xname); 293b5ab0bc5Syuo return; 294b5ab0bc5Syuo } 295b5ab0bc5Syuo } 296b5ab0bc5Syuo 297b5ab0bc5Syuo /* 298b5ab0bc5Syuo * Set media options. 299b5ab0bc5Syuo */ 300b5ab0bc5Syuo int 301b5ab0bc5Syuo axen_ifmedia_upd(struct ifnet *ifp) 302b5ab0bc5Syuo { 303b5ab0bc5Syuo struct axen_softc *sc = ifp->if_softc; 304b5ab0bc5Syuo struct mii_data *mii = GET_MII(sc); 305834d143cSyuo int err; 306834d143cSyuo 307834d143cSyuo sc->axen_link = 0; 308b5ab0bc5Syuo 309b5ab0bc5Syuo if (mii->mii_instance) { 310b5ab0bc5Syuo struct mii_softc *miisc; 311b5ab0bc5Syuo LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 312b5ab0bc5Syuo mii_phy_reset(miisc); 313b5ab0bc5Syuo } 314b5ab0bc5Syuo 315834d143cSyuo err = mii_mediachg(mii); 316834d143cSyuo if (err == ENXIO) 317834d143cSyuo return 0; 318834d143cSyuo else 319834d143cSyuo return err; 320b5ab0bc5Syuo } 321b5ab0bc5Syuo 322b5ab0bc5Syuo /* 323b5ab0bc5Syuo * Report current media status. 324b5ab0bc5Syuo */ 325b5ab0bc5Syuo void 326b5ab0bc5Syuo axen_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 327b5ab0bc5Syuo { 328b5ab0bc5Syuo struct axen_softc *sc = ifp->if_softc; 329b5ab0bc5Syuo struct mii_data *mii = GET_MII(sc); 330b5ab0bc5Syuo 331b5ab0bc5Syuo mii_pollstat(mii); 332b5ab0bc5Syuo ifmr->ifm_active = mii->mii_media_active; 333b5ab0bc5Syuo ifmr->ifm_status = mii->mii_media_status; 334b5ab0bc5Syuo } 335b5ab0bc5Syuo 336b5ab0bc5Syuo void 337b5ab0bc5Syuo axen_iff(struct axen_softc *sc) 338b5ab0bc5Syuo { 339b5ab0bc5Syuo struct ifnet *ifp = GET_IFP(sc); 340b5ab0bc5Syuo struct arpcom *ac = &sc->arpcom; 341b5ab0bc5Syuo struct ether_multi *enm; 342b5ab0bc5Syuo struct ether_multistep step; 343b5ab0bc5Syuo u_int32_t h = 0; 344b5ab0bc5Syuo u_int16_t rxmode; 345b5ab0bc5Syuo u_int8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 346b5ab0bc5Syuo uWord wval; 347b5ab0bc5Syuo 348b5ab0bc5Syuo if (usbd_is_dying(sc->axen_udev)) 349b5ab0bc5Syuo return; 350b5ab0bc5Syuo 351b5ab0bc5Syuo rxmode = 0; 352b5ab0bc5Syuo 353b5ab0bc5Syuo /* Enable receiver, set RX mode */ 354834d143cSyuo axen_lock_mii(sc); 355b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); 356b5ab0bc5Syuo rxmode = UGETW(wval); 357b5ab0bc5Syuo rxmode &= ~(AXEN_RXCTL_ACPT_ALL_MCAST | AXEN_RXCTL_ACPT_PHY_MCAST | 358b5ab0bc5Syuo AXEN_RXCTL_PROMISC); 359b5ab0bc5Syuo ifp->if_flags &= ~IFF_ALLMULTI; 360b5ab0bc5Syuo 361b5ab0bc5Syuo /* 362b5ab0bc5Syuo * Always accept broadcast frames. 363b5ab0bc5Syuo * Always accept frames destined to our station address. 364b5ab0bc5Syuo */ 365b5ab0bc5Syuo rxmode |= AXEN_RXCTL_ACPT_BCAST; 366b5ab0bc5Syuo 367b5ab0bc5Syuo if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0) { 368b5ab0bc5Syuo ifp->if_flags |= IFF_ALLMULTI; 369b5ab0bc5Syuo rxmode |= AXEN_RXCTL_ACPT_ALL_MCAST | AXEN_RXCTL_ACPT_PHY_MCAST; 370b5ab0bc5Syuo if (ifp->if_flags & IFF_PROMISC) 371b5ab0bc5Syuo rxmode |= AXEN_RXCTL_PROMISC; 372b5ab0bc5Syuo } else { 373b5ab0bc5Syuo rxmode |= AXEN_RXCTL_ACPT_ALL_MCAST | AXEN_RXCTL_ACPT_PHY_MCAST; 374b5ab0bc5Syuo 375b5ab0bc5Syuo /* now program new ones */ 376b5ab0bc5Syuo ETHER_FIRST_MULTI(step, ac, enm); 377b5ab0bc5Syuo while (enm != NULL) { 378b5ab0bc5Syuo h = ether_crc32_be(enm->enm_addrlo, 379b5ab0bc5Syuo ETHER_ADDR_LEN) >> 26; 380b5ab0bc5Syuo hashtbl[h / 8] |= 1 << (h % 8); 381b5ab0bc5Syuo ETHER_NEXT_MULTI(step, enm); 382b5ab0bc5Syuo } 383b5ab0bc5Syuo } 384b5ab0bc5Syuo 385b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE_FILTER, 8, AXEN_FILTER_MULTI, 386b5ab0bc5Syuo (void *)&hashtbl); 387b5ab0bc5Syuo USETW(wval, rxmode); 388b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); 389834d143cSyuo axen_unlock_mii(sc); 390b5ab0bc5Syuo } 391b5ab0bc5Syuo 392b5ab0bc5Syuo void 393b5ab0bc5Syuo axen_reset(struct axen_softc *sc) 394b5ab0bc5Syuo { 395b5ab0bc5Syuo if (usbd_is_dying(sc->axen_udev)) 396b5ab0bc5Syuo return; 39744e6486eSbmercer 39844e6486eSbmercer axen_ax88179_init(sc); 399b5ab0bc5Syuo 400b5ab0bc5Syuo /* Wait a little while for the chip to get its brains in order. */ 401b5ab0bc5Syuo DELAY(1000); 402b5ab0bc5Syuo return; 403b5ab0bc5Syuo } 404b5ab0bc5Syuo 405b5ab0bc5Syuo void 406b5ab0bc5Syuo axen_ax88179_init(struct axen_softc *sc) 407b5ab0bc5Syuo { 408b5ab0bc5Syuo uWord wval; 409b5ab0bc5Syuo uByte val; 410b5ab0bc5Syuo u_int16_t ctl, temp; 411b5ab0bc5Syuo struct axen_qctrl qctrl; 412b5ab0bc5Syuo 413834d143cSyuo axen_lock_mii(sc); 414834d143cSyuo 415b5ab0bc5Syuo /* XXX: ? */ 416b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_UNK_05, &val); 417b5ab0bc5Syuo DPRINTFN(5, ("AXEN_CMD_MAC_READ(0x05): 0x%02x\n", val)); 418b5ab0bc5Syuo 419b5ab0bc5Syuo /* check AX88179 version, UA1 / UA2 */ 420b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_GENERAL_STATUS, &val); 421b5ab0bc5Syuo /* UA1 */ 422b5ab0bc5Syuo if (!(val & AXEN_GENERAL_STATUS_MASK)) { 423b5ab0bc5Syuo sc->axen_rev = AXEN_REV_UA1; 4247ac27c38Syuo DPRINTF(("AX88179 ver. UA1\n")); 425b5ab0bc5Syuo } else { 426b5ab0bc5Syuo sc->axen_rev = AXEN_REV_UA2; 4277ac27c38Syuo DPRINTF(("AX88179 ver. UA2\n")); 428b5ab0bc5Syuo } 429b5ab0bc5Syuo 430b5ab0bc5Syuo /* power up ethernet PHY */ 431b5ab0bc5Syuo USETW(wval, 0); 432b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_PHYPWR_RSTCTL, &wval); 433b5ab0bc5Syuo 434b5ab0bc5Syuo USETW(wval, AXEN_PHYPWR_RSTCTL_IPRL); 435b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_PHYPWR_RSTCTL, &wval); 436b5ab0bc5Syuo usbd_delay_ms(sc->axen_udev, 200); 437b5ab0bc5Syuo 438b5ab0bc5Syuo /* set clock mode */ 439b5ab0bc5Syuo val = AXEN_PHYCLK_ACS | AXEN_PHYCLK_BCS; 440b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PHYCLK, &val); 441b5ab0bc5Syuo usbd_delay_ms(sc->axen_udev, 100); 442b5ab0bc5Syuo 443b5ab0bc5Syuo /* set monitor mode (disable) */ 444b5ab0bc5Syuo val = AXEN_MONITOR_NONE; 445b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_MONITOR_MODE, &val); 446b5ab0bc5Syuo 447834d143cSyuo /* enable auto detach */ 448b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_EEPROM_READ, 2, AXEN_EEPROM_STAT, &wval); 449b5ab0bc5Syuo temp = UGETW(wval); 450b5ab0bc5Syuo DPRINTFN(2,("EEPROM0x43 = 0x%04x\n", temp)); 451b5ab0bc5Syuo if (!(temp == 0xffff) && !(temp & 0x0100)) { 452834d143cSyuo /* Enable auto detach bit */ 453b5ab0bc5Syuo val = 0; 454b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PHYCLK, &val); 455b5ab0bc5Syuo val = AXEN_PHYCLK_ULR; 456b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PHYCLK, &val); 457b5ab0bc5Syuo usbd_delay_ms(sc->axen_udev, 100); 458b5ab0bc5Syuo 459b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_PHYPWR_RSTCTL, &wval); 460b5ab0bc5Syuo ctl = UGETW(wval); 461b5ab0bc5Syuo ctl |= AXEN_PHYPWR_RSTCTL_AUTODETACH; 462b5ab0bc5Syuo USETW(wval, ctl); 463b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_PHYPWR_RSTCTL, &wval); 464b5ab0bc5Syuo usbd_delay_ms(sc->axen_udev, 200); 465bc563cddSmlarkin printf("%s: enable auto detach (0x%04x)\n", 466bc563cddSmlarkin sc->axen_dev.dv_xname, ctl); 467b5ab0bc5Syuo } 468b5ab0bc5Syuo 469b5ab0bc5Syuo /* bulkin queue setting */ 470b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_USB_UPLINK, &val); 471b5ab0bc5Syuo switch (val) { 472b5ab0bc5Syuo case AXEN_USB_FS: 4737ac27c38Syuo DPRINTF(("uplink: USB1.1\n")); 474b5ab0bc5Syuo qctrl.ctrl = 0x07; 475b5ab0bc5Syuo qctrl.timer_low = 0xcc; 476b5ab0bc5Syuo qctrl.timer_high= 0x4c; 477b5ab0bc5Syuo qctrl.bufsize = AXEN_BUFSZ_LS - 1; 478b5ab0bc5Syuo qctrl.ifg = 0x08; 479b5ab0bc5Syuo break; 480b5ab0bc5Syuo case AXEN_USB_HS: 4817ac27c38Syuo DPRINTF(("uplink: USB2.0\n")); 482b5ab0bc5Syuo qctrl.ctrl = 0x07; 483b5ab0bc5Syuo qctrl.timer_low = 0x02; 484b5ab0bc5Syuo qctrl.timer_high= 0xa0; 485b5ab0bc5Syuo qctrl.bufsize = AXEN_BUFSZ_HS - 1; 486b5ab0bc5Syuo qctrl.ifg = 0xff; 487b5ab0bc5Syuo break; 488b5ab0bc5Syuo case AXEN_USB_SS: 4897ac27c38Syuo DPRINTF(("uplink: USB3.0\n")); 490b5ab0bc5Syuo qctrl.ctrl = 0x07; 491b5ab0bc5Syuo qctrl.timer_low = 0x4f; 492b5ab0bc5Syuo qctrl.timer_high= 0x00; 493b5ab0bc5Syuo qctrl.bufsize = AXEN_BUFSZ_SS - 1; 494b5ab0bc5Syuo qctrl.ifg = 0xff; 495b5ab0bc5Syuo break; 496b5ab0bc5Syuo default: 497bc563cddSmlarkin printf("%s: unknown uplink bus:0x%02x\n", 498bc563cddSmlarkin sc->axen_dev.dv_xname, val); 499834d143cSyuo axen_unlock_mii(sc); 500b5ab0bc5Syuo return; 501b5ab0bc5Syuo } 502b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_SET_RXSR, 5, AXEN_RX_BULKIN_QCTRL, &qctrl); 503b5ab0bc5Syuo 50437962954Smpi /* Set MAC address. */ 50537962954Smpi axen_cmd(sc, AXEN_CMD_MAC_WRITE_ETHER, ETHER_ADDR_LEN, 50637962954Smpi AXEN_CMD_MAC_NODE_ID, &sc->arpcom.ac_enaddr); 50737962954Smpi 508834d143cSyuo /* 509834d143cSyuo * set buffer high/low watermark to pause/resume. 510834d143cSyuo * write 2byte will set high/log simultaneous with AXEN_PAUSE_HIGH. 511834d143cSyuo * XXX: what is the best value? OSX driver uses 0x3c-0x4c as LOW-HIGH 512834d143cSyuo * watermark parameters. 513834d143cSyuo */ 514834d143cSyuo val = 0x34; 515b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PAUSE_LOW_WATERMARK, &val); 516834d143cSyuo val = 0x52; 517b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PAUSE_HIGH_WATERMARK, &val); 518b5ab0bc5Syuo 519b5ab0bc5Syuo /* Set RX/TX configuration. */ 5204b1a56afSjsg /* Offloading enable */ 521b5ab0bc5Syuo #ifdef AXEN_TOE 522b5ab0bc5Syuo val = AXEN_RXCOE_IPv4 | AXEN_RXCOE_TCPv4 | AXEN_RXCOE_UDPv4 | 523b5ab0bc5Syuo AXEN_RXCOE_TCPv6 | AXEN_RXCOE_UDPv6; 524b5ab0bc5Syuo #else 525b5ab0bc5Syuo val = AXEN_RXCOE_OFF; 526b5ab0bc5Syuo #endif 527b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_RX_COE, &val); 528834d143cSyuo 529b5ab0bc5Syuo #ifdef AXEN_TOE 530b5ab0bc5Syuo val = AXEN_TXCOE_IPv4 | AXEN_TXCOE_TCPv4 | AXEN_TXCOE_UDPv4 | 531b5ab0bc5Syuo AXEN_TXCOE_TCPv6 | AXEN_TXCOE_UDPv6; 532b5ab0bc5Syuo #else 533b5ab0bc5Syuo val = AXEN_TXCOE_OFF; 534b5ab0bc5Syuo #endif 535b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_TX_COE, &val); 536b5ab0bc5Syuo 537b5ab0bc5Syuo /* Set RX control register */ 538b5ab0bc5Syuo ctl = AXEN_RXCTL_IPE | AXEN_RXCTL_DROPCRCERR | AXEN_RXCTL_AUTOB; 539b5ab0bc5Syuo ctl |= AXEN_RXCTL_ACPT_PHY_MCAST | AXEN_RXCTL_ACPT_ALL_MCAST; 540b5ab0bc5Syuo ctl |= AXEN_RXCTL_START; 541b5ab0bc5Syuo USETW(wval, ctl); 542b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); 543b5ab0bc5Syuo 544b5ab0bc5Syuo /* set monitor mode (enable) */ 545b5ab0bc5Syuo val = AXEN_MONITOR_PMETYPE | AXEN_MONITOR_PMEPOL | AXEN_MONITOR_RWMP; 546b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_MONITOR_MODE, &val); 547b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_MONITOR_MODE, &val); 5487ac27c38Syuo DPRINTF(("axen: Monitor mode = 0x%02x\n", val)); 549b5ab0bc5Syuo 550b5ab0bc5Syuo /* set medium type */ 551b5ab0bc5Syuo ctl = AXEN_MEDIUM_GIGA | AXEN_MEDIUM_FDX | AXEN_MEDIUM_ALWAYS_ONE | 552b5ab0bc5Syuo AXEN_MEDIUM_RXFLOW_CTRL_EN | AXEN_MEDIUM_TXFLOW_CTRL_EN; 553b5ab0bc5Syuo ctl |= AXEN_MEDIUM_RECV_EN; 554b5ab0bc5Syuo USETW(wval, ctl); 5557ac27c38Syuo DPRINTF(("axen: set to medium mode: 0x%04x\n", UGETW(wval))); 556b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval); 557b5ab0bc5Syuo usbd_delay_ms(sc->axen_udev, 100); 558b5ab0bc5Syuo 559b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MEDIUM_STATUS, &wval); 5607ac27c38Syuo DPRINTF(("axen: current medium mode: 0x%04x\n", UGETW(wval))); 561834d143cSyuo axen_unlock_mii(sc); 562b5ab0bc5Syuo 563b5ab0bc5Syuo #if 0 /* XXX: TBD.... */ 564b5ab0bc5Syuo #define GMII_LED_ACTIVE 0x1a 565b5ab0bc5Syuo #define GMII_PHY_PAGE_SEL 0x1e 566b5ab0bc5Syuo #define GMII_PHY_PAGE_SEL 0x1f 567b5ab0bc5Syuo #define GMII_PAGE_EXT 0x0007 568b5ab0bc5Syuo axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, GMII_PHY_PAGE_SEL, 569b5ab0bc5Syuo GMII_PAGE_EXT); 570b5ab0bc5Syuo axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, GMII_PHY_PAGE, 571b5ab0bc5Syuo 0x002c); 572b5ab0bc5Syuo #endif 573b5ab0bc5Syuo 574b5ab0bc5Syuo #if 1 /* XXX: phy hack ? */ 575b5ab0bc5Syuo axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, 0x1F, 0x0005); 576b5ab0bc5Syuo axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, 0x0C, 0x0000); 577b5ab0bc5Syuo val = axen_miibus_readreg(&sc->axen_dev, sc->axen_phyno, 0x0001); 578b5ab0bc5Syuo axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, 0x01, 579b5ab0bc5Syuo val | 0x0080); 580b5ab0bc5Syuo axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, 0x1F, 0x0000); 581b5ab0bc5Syuo #endif 582b5ab0bc5Syuo } 583b5ab0bc5Syuo 584b5ab0bc5Syuo int 585b5ab0bc5Syuo axen_match(struct device *parent, void *match, void *aux) 586b5ab0bc5Syuo { 587b5ab0bc5Syuo struct usb_attach_arg *uaa = aux; 588b5ab0bc5Syuo 5897ebc5b51Smpi if (uaa->iface == NULL || uaa->configno != 1) 5907ebc5b51Smpi return (UMATCH_NONE); 591b5ab0bc5Syuo 592b5ab0bc5Syuo return (axen_lookup(uaa->vendor, uaa->product) != NULL ? 5937ebc5b51Smpi UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE); 594b5ab0bc5Syuo } 595b5ab0bc5Syuo 596b5ab0bc5Syuo void 597b5ab0bc5Syuo axen_attach(struct device *parent, struct device *self, void *aux) 598b5ab0bc5Syuo { 599b5ab0bc5Syuo struct axen_softc *sc = (struct axen_softc *)self; 600b5ab0bc5Syuo struct usb_attach_arg *uaa = aux; 601b5ab0bc5Syuo usb_interface_descriptor_t *id; 602b5ab0bc5Syuo usb_endpoint_descriptor_t *ed; 6039c887effSkevlo usb_device_descriptor_t *dd; 604b5ab0bc5Syuo struct mii_data *mii; 605b5ab0bc5Syuo u_char eaddr[ETHER_ADDR_LEN]; 606b5ab0bc5Syuo char *devname = sc->axen_dev.dv_xname; 607b5ab0bc5Syuo struct ifnet *ifp; 608b5ab0bc5Syuo int i, s; 609b5ab0bc5Syuo 610b5ab0bc5Syuo sc->axen_unit = self->dv_unit; /*device_get_unit(self);*/ 6117ebc5b51Smpi sc->axen_udev = uaa->device; 6127ebc5b51Smpi sc->axen_iface = uaa->iface; 613b5ab0bc5Syuo sc->axen_flags = axen_lookup(uaa->vendor, uaa->product)->axen_flags; 614b5ab0bc5Syuo 615b5ab0bc5Syuo usb_init_task(&sc->axen_tick_task, axen_tick_task, sc, 616b5ab0bc5Syuo USB_TASK_TYPE_GENERIC); 617b5ab0bc5Syuo rw_init(&sc->axen_mii_lock, "axenmii"); 618b5ab0bc5Syuo usb_init_task(&sc->axen_stop_task, (void (*)(void *))axen_stop, sc, 619b5ab0bc5Syuo USB_TASK_TYPE_GENERIC); 620b5ab0bc5Syuo 621b5ab0bc5Syuo sc->axen_product = uaa->product; 622b5ab0bc5Syuo sc->axen_vendor = uaa->vendor; 623b5ab0bc5Syuo 624b5ab0bc5Syuo id = usbd_get_interface_descriptor(sc->axen_iface); 625b5ab0bc5Syuo 626b5ab0bc5Syuo /* decide on what our bufsize will be */ 627b3e310cbSyuo switch (sc->axen_udev->speed) { 628b3e310cbSyuo case USB_SPEED_FULL: 629b3e310cbSyuo sc->axen_bufsz = AXEN_BUFSZ_LS * 1024; 630b3e310cbSyuo break; 631b3e310cbSyuo case USB_SPEED_HIGH: 632b3e310cbSyuo sc->axen_bufsz = AXEN_BUFSZ_HS * 1024; 633b3e310cbSyuo break; 634b3e310cbSyuo case USB_SPEED_SUPER: 635b3e310cbSyuo sc->axen_bufsz = AXEN_BUFSZ_SS * 1024; 636b3e310cbSyuo break; 637b3e310cbSyuo default: 638b3e310cbSyuo printf("%s: not supported usb bus type", sc->axen_dev.dv_xname); 639b3e310cbSyuo return; 640b3e310cbSyuo } 641b5ab0bc5Syuo 642b5ab0bc5Syuo /* Find endpoints. */ 643b5ab0bc5Syuo for (i = 0; i < id->bNumEndpoints; i++) { 644b5ab0bc5Syuo ed = usbd_interface2endpoint_descriptor(sc->axen_iface, i); 645b5ab0bc5Syuo if (!ed) { 646b5ab0bc5Syuo printf("%s: couldn't get ep %d\n", 647b5ab0bc5Syuo sc->axen_dev.dv_xname, i); 648b5ab0bc5Syuo return; 649b5ab0bc5Syuo } 650b5ab0bc5Syuo if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 651b5ab0bc5Syuo UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 652b5ab0bc5Syuo sc->axen_ed[AXEN_ENDPT_RX] = ed->bEndpointAddress; 653b5ab0bc5Syuo } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && 654b5ab0bc5Syuo UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 655b5ab0bc5Syuo sc->axen_ed[AXEN_ENDPT_TX] = ed->bEndpointAddress; 656b5ab0bc5Syuo } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 657b5ab0bc5Syuo UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { 658b5ab0bc5Syuo sc->axen_ed[AXEN_ENDPT_INTR] = ed->bEndpointAddress; 659b5ab0bc5Syuo } 660b5ab0bc5Syuo } 661b5ab0bc5Syuo 6629c887effSkevlo dd = usbd_get_device_descriptor(sc->axen_udev); 663*9060cf22Skevlo switch (UGETW(dd->bcdDevice)) { 664*9060cf22Skevlo case 0x200: 6659c887effSkevlo sc->axen_flags = AX179A; 666*9060cf22Skevlo break; 667*9060cf22Skevlo case 0x300: 668*9060cf22Skevlo sc->axen_flags = AX772D; 669*9060cf22Skevlo break; 670*9060cf22Skevlo } 6719c887effSkevlo 672b5ab0bc5Syuo s = splnet(); 673834d143cSyuo 674b5ab0bc5Syuo sc->axen_phyno = AXEN_PHY_ID; 675b5ab0bc5Syuo DPRINTF((" get_phyno %d\n", sc->axen_phyno)); 676b5ab0bc5Syuo 677b5ab0bc5Syuo /* 678b5ab0bc5Syuo * Get station address. 679b5ab0bc5Syuo */ 68044e6486eSbmercer /* use MAC command */ 681834d143cSyuo axen_lock_mii(sc); 68237962954Smpi axen_cmd(sc, AXEN_CMD_MAC_READ_ETHER, ETHER_ADDR_LEN, 68337962954Smpi AXEN_CMD_MAC_NODE_ID, &eaddr); 684834d143cSyuo axen_unlock_mii(sc); 685b5ab0bc5Syuo 686b5ab0bc5Syuo /* 687b5ab0bc5Syuo * An ASIX chip was detected. Inform the world. 688b5ab0bc5Syuo */ 689b5ab0bc5Syuo printf("%s:", sc->axen_dev.dv_xname); 690b5ab0bc5Syuo if (sc->axen_flags & AX178A) 691b5ab0bc5Syuo printf(" AX88178a"); 692b5ab0bc5Syuo else if (sc->axen_flags & AX179) 693b5ab0bc5Syuo printf(" AX88179"); 694*9060cf22Skevlo else if (sc->axen_flags & AX772D) 695*9060cf22Skevlo printf(" AX88772D"); 6969c887effSkevlo else 6979c887effSkevlo printf(" AX88179A"); 698b5ab0bc5Syuo printf(", address %s\n", ether_sprintf(eaddr)); 699b5ab0bc5Syuo 700b5ab0bc5Syuo bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 701b5ab0bc5Syuo 70237962954Smpi axen_ax88179_init(sc); 70337962954Smpi 704b5ab0bc5Syuo /* Initialize interface info. */ 705b5ab0bc5Syuo ifp = &sc->arpcom.ac_if; 706b5ab0bc5Syuo ifp->if_softc = sc; 707b5ab0bc5Syuo strlcpy(ifp->if_xname, devname, IFNAMSIZ); 708b5ab0bc5Syuo ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 709b5ab0bc5Syuo ifp->if_ioctl = axen_ioctl; 710b5ab0bc5Syuo ifp->if_start = axen_start; 711b5ab0bc5Syuo ifp->if_watchdog = axen_watchdog; 712b5ab0bc5Syuo 713b5ab0bc5Syuo ifp->if_capabilities = IFCAP_VLAN_MTU; 714b5ab0bc5Syuo #ifdef AXEN_TOE 715b5ab0bc5Syuo ifp->if_capabilities |= IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | 716b5ab0bc5Syuo IFCAP_CSUM_UDPv4 | IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6; 717b5ab0bc5Syuo #endif 718b5ab0bc5Syuo 719b5ab0bc5Syuo /* Initialize MII/media info. */ 720b5ab0bc5Syuo mii = &sc->axen_mii; 721b5ab0bc5Syuo mii->mii_ifp = ifp; 722b5ab0bc5Syuo mii->mii_readreg = axen_miibus_readreg; 723b5ab0bc5Syuo mii->mii_writereg = axen_miibus_writereg; 724b5ab0bc5Syuo mii->mii_statchg = axen_miibus_statchg; 725b5ab0bc5Syuo mii->mii_flags = MIIF_AUTOTSLEEP; 726b5ab0bc5Syuo 727b5ab0bc5Syuo ifmedia_init(&mii->mii_media, 0, axen_ifmedia_upd, axen_ifmedia_sts); 728b5ab0bc5Syuo mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); 729b5ab0bc5Syuo 730b5ab0bc5Syuo if (LIST_FIRST(&mii->mii_phys) == NULL) { 731b5ab0bc5Syuo ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); 732b5ab0bc5Syuo ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE); 733b5ab0bc5Syuo } else 734b5ab0bc5Syuo ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); 735b5ab0bc5Syuo 736b5ab0bc5Syuo /* Attach the interface. */ 737b5ab0bc5Syuo if_attach(ifp); 738b5ab0bc5Syuo ether_ifattach(ifp); 739b5ab0bc5Syuo 740b5ab0bc5Syuo timeout_set(&sc->axen_stat_ch, axen_tick, sc); 741b5ab0bc5Syuo 742b5ab0bc5Syuo splx(s); 743b5ab0bc5Syuo } 744b5ab0bc5Syuo 745b5ab0bc5Syuo int 746b5ab0bc5Syuo axen_detach(struct device *self, int flags) 747b5ab0bc5Syuo { 748b5ab0bc5Syuo struct axen_softc *sc = (struct axen_softc *)self; 749b5ab0bc5Syuo int s; 750b5ab0bc5Syuo struct ifnet *ifp = GET_IFP(sc); 751b5ab0bc5Syuo 752b5ab0bc5Syuo DPRINTFN(2,("%s: %s: enter\n", sc->axen_dev.dv_xname, __func__)); 753b5ab0bc5Syuo 754b5ab0bc5Syuo if (timeout_initialized(&sc->axen_stat_ch)) 755b5ab0bc5Syuo timeout_del(&sc->axen_stat_ch); 756b5ab0bc5Syuo 757b5ab0bc5Syuo if (sc->axen_ep[AXEN_ENDPT_TX] != NULL) 758b5ab0bc5Syuo usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_TX]); 759b5ab0bc5Syuo if (sc->axen_ep[AXEN_ENDPT_RX] != NULL) 760b5ab0bc5Syuo usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_RX]); 761b5ab0bc5Syuo if (sc->axen_ep[AXEN_ENDPT_INTR] != NULL) 762b5ab0bc5Syuo usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_INTR]); 763b5ab0bc5Syuo 764b5ab0bc5Syuo /* 765b5ab0bc5Syuo * Remove any pending tasks. They cannot be executing because they run 766b5ab0bc5Syuo * in the same thread as detach. 767b5ab0bc5Syuo */ 768b5ab0bc5Syuo usb_rem_task(sc->axen_udev, &sc->axen_tick_task); 769b5ab0bc5Syuo usb_rem_task(sc->axen_udev, &sc->axen_stop_task); 770b5ab0bc5Syuo 771b5ab0bc5Syuo s = splusb(); 772b5ab0bc5Syuo 773b5ab0bc5Syuo if (--sc->axen_refcnt >= 0) { 774b5ab0bc5Syuo /* Wait for processes to go away */ 775b5ab0bc5Syuo usb_detach_wait(&sc->axen_dev); 776b5ab0bc5Syuo } 777b5ab0bc5Syuo 778b5ab0bc5Syuo if (ifp->if_flags & IFF_RUNNING) 779b5ab0bc5Syuo axen_stop(sc); 780b5ab0bc5Syuo 781b5ab0bc5Syuo mii_detach(&sc->axen_mii, MII_PHY_ANY, MII_OFFSET_ANY); 782b5ab0bc5Syuo ifmedia_delete_instance(&sc->axen_mii.mii_media, IFM_INST_ANY); 783b5ab0bc5Syuo if (ifp->if_softc != NULL) { 784b5ab0bc5Syuo ether_ifdetach(ifp); 785b5ab0bc5Syuo if_detach(ifp); 786b5ab0bc5Syuo } 787b5ab0bc5Syuo 788b5ab0bc5Syuo #ifdef DIAGNOSTIC 789b5ab0bc5Syuo if (sc->axen_ep[AXEN_ENDPT_TX] != NULL || 790b5ab0bc5Syuo sc->axen_ep[AXEN_ENDPT_RX] != NULL || 791b5ab0bc5Syuo sc->axen_ep[AXEN_ENDPT_INTR] != NULL) 792b5ab0bc5Syuo printf("%s: detach has active endpoints\n", 793b5ab0bc5Syuo sc->axen_dev.dv_xname); 794b5ab0bc5Syuo #endif 795b5ab0bc5Syuo 796b5ab0bc5Syuo splx(s); 797b5ab0bc5Syuo 798834d143cSyuo return 0; 799b5ab0bc5Syuo } 800b5ab0bc5Syuo 801b5ab0bc5Syuo struct mbuf * 802b5ab0bc5Syuo axen_newbuf(void) 803b5ab0bc5Syuo { 804b5ab0bc5Syuo struct mbuf *m; 805b5ab0bc5Syuo 806b5ab0bc5Syuo MGETHDR(m, M_DONTWAIT, MT_DATA); 807b5ab0bc5Syuo if (m == NULL) 808834d143cSyuo return NULL; 809b5ab0bc5Syuo 810b5ab0bc5Syuo MCLGET(m, M_DONTWAIT); 811b5ab0bc5Syuo if (!(m->m_flags & M_EXT)) { 812b5ab0bc5Syuo m_freem(m); 813834d143cSyuo return NULL; 814b5ab0bc5Syuo } 815b5ab0bc5Syuo 816b5ab0bc5Syuo m->m_len = m->m_pkthdr.len = MCLBYTES; 817b5ab0bc5Syuo m_adj(m, ETHER_ALIGN); 818b5ab0bc5Syuo 819834d143cSyuo return m; 820b5ab0bc5Syuo } 821b5ab0bc5Syuo 822b5ab0bc5Syuo int 823b5ab0bc5Syuo axen_rx_list_init(struct axen_softc *sc) 824b5ab0bc5Syuo { 825b5ab0bc5Syuo struct axen_cdata *cd; 826b5ab0bc5Syuo struct axen_chain *c; 827b5ab0bc5Syuo int i; 828b5ab0bc5Syuo 829b5ab0bc5Syuo DPRINTF(("%s: %s: enter\n", sc->axen_dev.dv_xname, __func__)); 830b5ab0bc5Syuo 831b5ab0bc5Syuo cd = &sc->axen_cdata; 832b5ab0bc5Syuo for (i = 0; i < AXEN_RX_LIST_CNT; i++) { 833b5ab0bc5Syuo c = &cd->axen_rx_chain[i]; 834b5ab0bc5Syuo c->axen_sc = sc; 835b5ab0bc5Syuo c->axen_idx = i; 836b5ab0bc5Syuo c->axen_mbuf = NULL; 837b5ab0bc5Syuo if (c->axen_xfer == NULL) { 838b5ab0bc5Syuo c->axen_xfer = usbd_alloc_xfer(sc->axen_udev); 839b5ab0bc5Syuo if (c->axen_xfer == NULL) 840834d143cSyuo return ENOBUFS; 841b5ab0bc5Syuo c->axen_buf = usbd_alloc_buffer(c->axen_xfer, 842b5ab0bc5Syuo sc->axen_bufsz); 843b5ab0bc5Syuo if (c->axen_buf == NULL) { 844b5ab0bc5Syuo usbd_free_xfer(c->axen_xfer); 845834d143cSyuo return ENOBUFS; 846b5ab0bc5Syuo } 847b5ab0bc5Syuo } 848b5ab0bc5Syuo } 849b5ab0bc5Syuo 850834d143cSyuo return 0; 851b5ab0bc5Syuo } 852b5ab0bc5Syuo 853b5ab0bc5Syuo int 854b5ab0bc5Syuo axen_tx_list_init(struct axen_softc *sc) 855b5ab0bc5Syuo { 856b5ab0bc5Syuo struct axen_cdata *cd; 857b5ab0bc5Syuo struct axen_chain *c; 858b5ab0bc5Syuo int i; 859b5ab0bc5Syuo 860b5ab0bc5Syuo DPRINTF(("%s: %s: enter\n", sc->axen_dev.dv_xname, __func__)); 861b5ab0bc5Syuo 862b5ab0bc5Syuo cd = &sc->axen_cdata; 863b5ab0bc5Syuo for (i = 0; i < AXEN_TX_LIST_CNT; i++) { 864b5ab0bc5Syuo c = &cd->axen_tx_chain[i]; 865b5ab0bc5Syuo c->axen_sc = sc; 866b5ab0bc5Syuo c->axen_idx = i; 867b5ab0bc5Syuo c->axen_mbuf = NULL; 868b5ab0bc5Syuo if (c->axen_xfer == NULL) { 869b5ab0bc5Syuo c->axen_xfer = usbd_alloc_xfer(sc->axen_udev); 870b5ab0bc5Syuo if (c->axen_xfer == NULL) 871834d143cSyuo return ENOBUFS; 872b5ab0bc5Syuo c->axen_buf = usbd_alloc_buffer(c->axen_xfer, 873b5ab0bc5Syuo sc->axen_bufsz); 874b5ab0bc5Syuo if (c->axen_buf == NULL) { 875b5ab0bc5Syuo usbd_free_xfer(c->axen_xfer); 876834d143cSyuo return ENOBUFS; 877b5ab0bc5Syuo } 878b5ab0bc5Syuo } 879b5ab0bc5Syuo } 880b5ab0bc5Syuo 881834d143cSyuo return 0; 882b5ab0bc5Syuo } 883b5ab0bc5Syuo 884b5ab0bc5Syuo /* 885b5ab0bc5Syuo * A frame has been uploaded: pass the resulting mbuf chain up to 886b5ab0bc5Syuo * the higher level protocols. 887b5ab0bc5Syuo */ 888b5ab0bc5Syuo void 889b5ab0bc5Syuo axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) 890b5ab0bc5Syuo { 891b5ab0bc5Syuo struct axen_chain *c = (struct axen_chain *)priv; 892b5ab0bc5Syuo struct axen_softc *sc = c->axen_sc; 893b5ab0bc5Syuo struct ifnet *ifp = GET_IFP(sc); 894b5ab0bc5Syuo u_char *buf = c->axen_buf; 895f00dacdfSmpi struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 896b5ab0bc5Syuo struct mbuf *m; 897834d143cSyuo u_int32_t total_len; 898b5ab0bc5Syuo u_int32_t rx_hdr, pkt_hdr; 899b5ab0bc5Syuo u_int32_t *hdr_p; 900b5ab0bc5Syuo u_int16_t hdr_offset, pkt_count; 901b5ab0bc5Syuo size_t pkt_len; 902b5ab0bc5Syuo size_t temp; 9039c887effSkevlo int padlen, s; 904b5ab0bc5Syuo 905b5ab0bc5Syuo DPRINTFN(10,("%s: %s: enter\n", sc->axen_dev.dv_xname,__func__)); 906b5ab0bc5Syuo 907b5ab0bc5Syuo if (usbd_is_dying(sc->axen_udev)) 908b5ab0bc5Syuo return; 909b5ab0bc5Syuo 910b5ab0bc5Syuo if (!(ifp->if_flags & IFF_RUNNING)) 911b5ab0bc5Syuo return; 912b5ab0bc5Syuo 913b5ab0bc5Syuo if (status != USBD_NORMAL_COMPLETION) { 914b5ab0bc5Syuo if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) 915b5ab0bc5Syuo return; 916b5ab0bc5Syuo if (usbd_ratecheck(&sc->axen_rx_notice)) { 917b5ab0bc5Syuo printf("%s: usb errors on rx: %s\n", 918b5ab0bc5Syuo sc->axen_dev.dv_xname, usbd_errstr(status)); 919b5ab0bc5Syuo } 920b5ab0bc5Syuo if (status == USBD_STALLED) 921b5ab0bc5Syuo usbd_clear_endpoint_stall_async(sc->axen_ep[AXEN_ENDPT_RX]); 922b5ab0bc5Syuo goto done; 923b5ab0bc5Syuo } 924b5ab0bc5Syuo 925b5ab0bc5Syuo usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); 926b5ab0bc5Syuo 927b5ab0bc5Syuo if (total_len < sizeof(pkt_hdr)) { 928b5ab0bc5Syuo ifp->if_ierrors++; 929b5ab0bc5Syuo goto done; 930b5ab0bc5Syuo } 931b5ab0bc5Syuo 932b5ab0bc5Syuo /* 933b5ab0bc5Syuo * buffer map 9349c887effSkevlo * 9359c887effSkevlo * for ax88179 936b5ab0bc5Syuo * [packet #0]...[packet #n][pkt hdr#0]..[pkt hdr#n][recv_hdr] 9379c887effSkevlo * 9389c887effSkevlo * for ax88179a 9399c887effSkevlo * [packet #0]...[packet #n][pkt hdr#0][dummy_hdr].. 9409c887effSkevlo * [pkt hdr#n][dummy_hdr][recv_hdr] 9419c887effSkevlo * 9424b1a56afSjsg * each packet has 0xeeee as pseudo header.. 943b5ab0bc5Syuo */ 944b5ab0bc5Syuo hdr_p = (u_int32_t *)(buf + total_len - sizeof(u_int32_t)); 945b5ab0bc5Syuo rx_hdr = letoh32(*hdr_p); 946b5ab0bc5Syuo hdr_offset = (u_int16_t)(rx_hdr >> 16); 947b5ab0bc5Syuo pkt_count = (u_int16_t)(rx_hdr & 0xffff); 948b5ab0bc5Syuo 949b5ab0bc5Syuo if (total_len > sc->axen_bufsz) { 950bc563cddSmlarkin printf("%s: rxeof: too large transfer\n", 951bc563cddSmlarkin sc->axen_dev.dv_xname); 952b5ab0bc5Syuo goto done; 953b5ab0bc5Syuo } 954b5ab0bc5Syuo 955b5ab0bc5Syuo /* sanity check */ 956b3e310cbSyuo if (hdr_offset > total_len) { 957b5ab0bc5Syuo ifp->if_ierrors++; 958b5ab0bc5Syuo goto done; 959b5ab0bc5Syuo } 960b5ab0bc5Syuo 961b5ab0bc5Syuo /* point first packet header */ 962b5ab0bc5Syuo hdr_p = (u_int32_t*)(buf + hdr_offset); 963b5ab0bc5Syuo 964b5ab0bc5Syuo /* 965b5ab0bc5Syuo * ax88179 will pack multiple ip packet to a USB transaction. 966b5ab0bc5Syuo * process all of packets in the buffer 967b5ab0bc5Syuo */ 968834d143cSyuo 969834d143cSyuo #if 1 /* XXX: paranoiac check. need to remove later */ 970834d143cSyuo #define AXEN_MAX_PACKED_PACKET 200 971b5ab0bc5Syuo if (pkt_count > AXEN_MAX_PACKED_PACKET) { 972b5ab0bc5Syuo DPRINTF(("Too many packets (%d) in a transaction, discard.\n", 973b5ab0bc5Syuo pkt_count)); 974b5ab0bc5Syuo goto done; 975b5ab0bc5Syuo } 976834d143cSyuo #endif 977b5ab0bc5Syuo 9789c887effSkevlo /* skip pseudo header (2byte) */ 9799c887effSkevlo padlen = 2; 9809c887effSkevlo /* skip trailer padding (4Byte) for ax88179 */ 981*9060cf22Skevlo if (!(sc->axen_flags & (AX179A | AX772D))) 9829c887effSkevlo padlen += 4; 9839c887effSkevlo 984b5ab0bc5Syuo do { 9859c887effSkevlo pkt_hdr = letoh32(*hdr_p); 9869c887effSkevlo pkt_len = (pkt_hdr >> 16) & 0x1fff; 9879c887effSkevlo 9889c887effSkevlo DPRINTFN(10,("rxeof: packet#%d, pkt_hdr 0x%08x, pkt_len %zu\n", 9899c887effSkevlo pkt_count, pkt_hdr, pkt_len)); 9909c887effSkevlo 9919c887effSkevlo /* skip dummy packet header */ 9929c887effSkevlo if (pkt_len == 0) 9939c887effSkevlo goto nextpkt; 9949c887effSkevlo 995b5ab0bc5Syuo if ((buf[0] != 0xee) || (buf[1] != 0xee)){ 996bc563cddSmlarkin printf("%s: invalid buffer(pkt#%d), continue\n", 997bc563cddSmlarkin sc->axen_dev.dv_xname, pkt_count); 998b5ab0bc5Syuo ifp->if_ierrors += pkt_count; 999b5ab0bc5Syuo goto done; 1000b5ab0bc5Syuo } 1001b5ab0bc5Syuo 1002b5ab0bc5Syuo if ((pkt_hdr & AXEN_RXHDR_CRC_ERR) || 1003b5ab0bc5Syuo (pkt_hdr & AXEN_RXHDR_DROP_ERR)) { 1004b5ab0bc5Syuo ifp->if_ierrors++; 1005b5ab0bc5Syuo /* move to next pkt header */ 1006834d143cSyuo DPRINTF(("crc err(pkt#%d)\n", pkt_count)); 1007b5ab0bc5Syuo goto nextpkt; 1008b5ab0bc5Syuo } 1009b5ab0bc5Syuo 1010b5ab0bc5Syuo /* process each packet */ 1011b5ab0bc5Syuo /* allocate mbuf */ 1012b5ab0bc5Syuo m = axen_newbuf(); 1013b5ab0bc5Syuo if (m == NULL) { 1014b5ab0bc5Syuo ifp->if_ierrors++; 1015b5ab0bc5Syuo goto nextpkt; 1016b5ab0bc5Syuo } 1017b5ab0bc5Syuo 10189c887effSkevlo m->m_pkthdr.len = m->m_len = pkt_len - padlen; 1019b5ab0bc5Syuo 1020b5ab0bc5Syuo #ifdef AXEN_TOE 10214b1a56afSjsg /* checksum err */ 1022b5ab0bc5Syuo if ((pkt_hdr & AXEN_RXHDR_L3CSUM_ERR) || 1023b5ab0bc5Syuo (pkt_hdr & AXEN_RXHDR_L4CSUM_ERR)) { 1024bc563cddSmlarkin printf("%s: checksum err (pkt#%d)\n", 1025bc563cddSmlarkin sc->axen_dev.dv_xname, pkt_count); 1026b5ab0bc5Syuo goto nextpkt; 1027b5ab0bc5Syuo } else { 1028b5ab0bc5Syuo m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK; 1029b5ab0bc5Syuo } 1030b5ab0bc5Syuo 1031b5ab0bc5Syuo int l4_type; 1032b5ab0bc5Syuo l4_type = (pkt_hdr & AXEN_RXHDR_L4_TYPE_MASK) >> 1033b5ab0bc5Syuo AXEN_RXHDR_L4_TYPE_OFFSET; 1034b5ab0bc5Syuo 1035b5ab0bc5Syuo if ((l4_type == AXEN_RXHDR_L4_TYPE_TCP) || 1036b5ab0bc5Syuo (l4_type == AXEN_RXHDR_L4_TYPE_UDP)) 1037b5ab0bc5Syuo m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK | 1038b5ab0bc5Syuo M_UDP_CSUM_IN_OK; 1039b5ab0bc5Syuo #endif 1040b5ab0bc5Syuo 10419c887effSkevlo memcpy(mtod(m, char *), buf + 2, pkt_len - padlen); 1042b5ab0bc5Syuo 1043f00dacdfSmpi ml_enqueue(&ml, m); 1044b5ab0bc5Syuo 1045b5ab0bc5Syuo nextpkt: 1046b5ab0bc5Syuo /* 1047b5ab0bc5Syuo * prepare next packet 1048b5ab0bc5Syuo * as each packet will be aligned 8byte boundary, 1049b5ab0bc5Syuo * need to fix up the start point of the buffer. 1050b5ab0bc5Syuo */ 1051b5ab0bc5Syuo temp = ((pkt_len + 7) & 0xfff8); 1052b5ab0bc5Syuo buf = buf + temp; 1053b5ab0bc5Syuo hdr_p++; 1054b5ab0bc5Syuo pkt_count--; 1055b5ab0bc5Syuo } while( pkt_count > 0); 1056b5ab0bc5Syuo 1057b5ab0bc5Syuo done: 1058f00dacdfSmpi /* push the packet up */ 1059f00dacdfSmpi s = splnet(); 1060f00dacdfSmpi if_input(ifp, &ml); 1061f00dacdfSmpi splx(s); 1062f00dacdfSmpi 1063b5ab0bc5Syuo /* clear buffer for next transaction */ 1064b5ab0bc5Syuo memset(c->axen_buf, 0, sc->axen_bufsz); 1065b5ab0bc5Syuo 1066b5ab0bc5Syuo /* Setup new transfer. */ 1067b5ab0bc5Syuo usbd_setup_xfer(xfer, sc->axen_ep[AXEN_ENDPT_RX], 1068b5ab0bc5Syuo c, c->axen_buf, sc->axen_bufsz, 1069b5ab0bc5Syuo USBD_SHORT_XFER_OK | USBD_NO_COPY, 1070b5ab0bc5Syuo USBD_NO_TIMEOUT, axen_rxeof); 1071b5ab0bc5Syuo usbd_transfer(xfer); 1072b5ab0bc5Syuo 1073b5ab0bc5Syuo DPRINTFN(10,("%s: %s: start rx\n", sc->axen_dev.dv_xname, __func__)); 1074b5ab0bc5Syuo } 1075b5ab0bc5Syuo 1076b5ab0bc5Syuo /* 1077b5ab0bc5Syuo * A frame was downloaded to the chip. It's safe for us to clean up 1078b5ab0bc5Syuo * the list buffers. 1079b5ab0bc5Syuo */ 1080b5ab0bc5Syuo void 1081b5ab0bc5Syuo axen_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status) 1082b5ab0bc5Syuo { 1083b5ab0bc5Syuo struct axen_softc *sc; 1084b5ab0bc5Syuo struct axen_chain *c; 1085b5ab0bc5Syuo struct ifnet *ifp; 1086b5ab0bc5Syuo int s; 1087b5ab0bc5Syuo 1088b5ab0bc5Syuo c = priv; 1089b5ab0bc5Syuo sc = c->axen_sc; 1090b5ab0bc5Syuo ifp = &sc->arpcom.ac_if; 1091b5ab0bc5Syuo 1092b5ab0bc5Syuo if (usbd_is_dying(sc->axen_udev)) 1093b5ab0bc5Syuo return; 1094b5ab0bc5Syuo 1095b5ab0bc5Syuo s = splnet(); 1096b5ab0bc5Syuo 1097b5ab0bc5Syuo if (status != USBD_NORMAL_COMPLETION) { 1098b5ab0bc5Syuo if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { 1099b5ab0bc5Syuo splx(s); 1100b5ab0bc5Syuo return; 1101b5ab0bc5Syuo } 1102b5ab0bc5Syuo ifp->if_oerrors++; 1103b5ab0bc5Syuo printf("axen%d: usb error on tx: %s\n", sc->axen_unit, 1104b5ab0bc5Syuo usbd_errstr(status)); 1105b5ab0bc5Syuo if (status == USBD_STALLED) 1106b5ab0bc5Syuo usbd_clear_endpoint_stall_async(sc->axen_ep[AXEN_ENDPT_TX]); 1107b5ab0bc5Syuo splx(s); 1108b5ab0bc5Syuo return; 1109b5ab0bc5Syuo } 1110b5ab0bc5Syuo 1111b5ab0bc5Syuo ifp->if_timer = 0; 1112de6cd8fbSdlg ifq_clr_oactive(&ifp->if_snd); 1113b5ab0bc5Syuo 1114b5ab0bc5Syuo m_freem(c->axen_mbuf); 1115b5ab0bc5Syuo c->axen_mbuf = NULL; 1116b5ab0bc5Syuo 11170cae21bdSpatrick if (ifq_empty(&ifp->if_snd) == 0) 1118b5ab0bc5Syuo axen_start(ifp); 1119b5ab0bc5Syuo 1120b5ab0bc5Syuo splx(s); 1121b5ab0bc5Syuo } 1122b5ab0bc5Syuo 1123b5ab0bc5Syuo void 1124b5ab0bc5Syuo axen_tick(void *xsc) 1125b5ab0bc5Syuo { 1126b5ab0bc5Syuo struct axen_softc *sc = xsc; 1127b5ab0bc5Syuo 1128b5ab0bc5Syuo if (sc == NULL) 1129b5ab0bc5Syuo return; 1130b5ab0bc5Syuo 1131b5ab0bc5Syuo DPRINTFN(0xff, ("%s: %s: enter\n", sc->axen_dev.dv_xname, 1132b5ab0bc5Syuo __func__)); 1133b5ab0bc5Syuo 1134b5ab0bc5Syuo if (usbd_is_dying(sc->axen_udev)) 1135b5ab0bc5Syuo return; 1136b5ab0bc5Syuo 1137b5ab0bc5Syuo /* Perform periodic stuff in process context */ 1138b5ab0bc5Syuo usb_add_task(sc->axen_udev, &sc->axen_tick_task); 1139b5ab0bc5Syuo } 1140b5ab0bc5Syuo 1141b5ab0bc5Syuo void 1142b5ab0bc5Syuo axen_tick_task(void *xsc) 1143b5ab0bc5Syuo { 1144b5ab0bc5Syuo int s; 1145b5ab0bc5Syuo struct axen_softc *sc; 1146b5ab0bc5Syuo struct mii_data *mii; 1147b5ab0bc5Syuo 1148b5ab0bc5Syuo sc = xsc; 1149b5ab0bc5Syuo 1150b5ab0bc5Syuo if (sc == NULL) 1151b5ab0bc5Syuo return; 1152b5ab0bc5Syuo 1153b5ab0bc5Syuo if (usbd_is_dying(sc->axen_udev)) 1154b5ab0bc5Syuo return; 1155b5ab0bc5Syuo 1156b5ab0bc5Syuo mii = GET_MII(sc); 1157b5ab0bc5Syuo if (mii == NULL) 1158b5ab0bc5Syuo return; 1159b5ab0bc5Syuo 1160b5ab0bc5Syuo s = splnet(); 1161b5ab0bc5Syuo 1162b5ab0bc5Syuo mii_tick(mii); 1163b5ab0bc5Syuo if (sc->axen_link == 0) 1164b5ab0bc5Syuo axen_miibus_statchg(&sc->axen_dev); 1165b5ab0bc5Syuo timeout_add_sec(&sc->axen_stat_ch, 1); 1166b5ab0bc5Syuo 1167b5ab0bc5Syuo splx(s); 1168b5ab0bc5Syuo } 1169b5ab0bc5Syuo 1170b5ab0bc5Syuo int 1171b5ab0bc5Syuo axen_encap(struct axen_softc *sc, struct mbuf *m, int idx) 1172b5ab0bc5Syuo { 1173b5ab0bc5Syuo struct axen_chain *c; 1174b5ab0bc5Syuo usbd_status err; 1175b5ab0bc5Syuo struct axen_sframe_hdr hdr; 1176b5ab0bc5Syuo int length, boundary; 1177b5ab0bc5Syuo 1178b5ab0bc5Syuo c = &sc->axen_cdata.axen_tx_chain[idx]; 1179b5ab0bc5Syuo 1180b3e310cbSyuo switch (sc->axen_udev->speed) { 1181b3e310cbSyuo case USB_SPEED_FULL: 1182b3e310cbSyuo boundary = 64; 1183b3e310cbSyuo break; 1184b3e310cbSyuo case USB_SPEED_HIGH: 1185b3e310cbSyuo boundary = 512; 1186b3e310cbSyuo break; 1187b3e310cbSyuo case USB_SPEED_SUPER: 1188b3e310cbSyuo boundary = 4096; /* XXX */ 1189b3e310cbSyuo break; 1190b3e310cbSyuo default: 1191b3e310cbSyuo printf("%s: not supported usb bus type", sc->axen_dev.dv_xname); 1192b3e310cbSyuo return EIO; 1193b3e310cbSyuo } 1194b5ab0bc5Syuo 1195b5ab0bc5Syuo hdr.plen = htole32(m->m_pkthdr.len); 1196b5ab0bc5Syuo hdr.gso = 0; /* disable segmentation offloading */ 1197b5ab0bc5Syuo 1198b5ab0bc5Syuo memcpy(c->axen_buf, &hdr, sizeof(hdr)); 1199b5ab0bc5Syuo length = sizeof(hdr); 1200b5ab0bc5Syuo 1201b5ab0bc5Syuo m_copydata(m, 0, m->m_pkthdr.len, c->axen_buf + length); 1202b5ab0bc5Syuo length += m->m_pkthdr.len; 1203b5ab0bc5Syuo 1204b5ab0bc5Syuo if ((length % boundary) == 0) { 1205b5ab0bc5Syuo hdr.plen = 0x0; 1206b5ab0bc5Syuo hdr.gso |= 0x80008000; /* enable padding */ 1207b5ab0bc5Syuo memcpy(c->axen_buf + length, &hdr, sizeof(hdr)); 1208b5ab0bc5Syuo length += sizeof(hdr); 1209b5ab0bc5Syuo } 1210b5ab0bc5Syuo 1211b5ab0bc5Syuo c->axen_mbuf = m; 1212b5ab0bc5Syuo 1213b5ab0bc5Syuo usbd_setup_xfer(c->axen_xfer, sc->axen_ep[AXEN_ENDPT_TX], 1214b5ab0bc5Syuo c, c->axen_buf, length, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, 1215b5ab0bc5Syuo 10000, axen_txeof); 1216b5ab0bc5Syuo 1217b5ab0bc5Syuo /* Transmit */ 1218b5ab0bc5Syuo err = usbd_transfer(c->axen_xfer); 1219b5ab0bc5Syuo if (err != USBD_IN_PROGRESS) { 12204ced949bSgerhard c->axen_mbuf = NULL; 1221b5ab0bc5Syuo axen_stop(sc); 1222834d143cSyuo return EIO; 1223b5ab0bc5Syuo } 1224b5ab0bc5Syuo 1225b5ab0bc5Syuo sc->axen_cdata.axen_tx_cnt++; 1226b5ab0bc5Syuo 1227834d143cSyuo return 0; 1228b5ab0bc5Syuo } 1229b5ab0bc5Syuo 1230b5ab0bc5Syuo void 1231b5ab0bc5Syuo axen_start(struct ifnet *ifp) 1232b5ab0bc5Syuo { 1233b5ab0bc5Syuo struct axen_softc *sc; 1234b5ab0bc5Syuo struct mbuf *m_head = NULL; 1235b5ab0bc5Syuo 1236b5ab0bc5Syuo sc = ifp->if_softc; 1237b5ab0bc5Syuo 1238b5ab0bc5Syuo if (!sc->axen_link) 1239b5ab0bc5Syuo return; 1240b5ab0bc5Syuo 1241de6cd8fbSdlg if (ifq_is_oactive(&ifp->if_snd)) 1242b5ab0bc5Syuo return; 1243b5ab0bc5Syuo 12444ced949bSgerhard m_head = ifq_dequeue(&ifp->if_snd); 1245b5ab0bc5Syuo if (m_head == NULL) 1246b5ab0bc5Syuo return; 1247b5ab0bc5Syuo 1248b5ab0bc5Syuo if (axen_encap(sc, m_head, 0)) { 12494ced949bSgerhard m_freem(m_head); 1250de6cd8fbSdlg ifq_set_oactive(&ifp->if_snd); 1251b5ab0bc5Syuo return; 1252b5ab0bc5Syuo } 1253b5ab0bc5Syuo 1254b5ab0bc5Syuo /* 1255b5ab0bc5Syuo * If there's a BPF listener, bounce a copy of this frame 1256b5ab0bc5Syuo * to him. 1257b5ab0bc5Syuo */ 1258b5ab0bc5Syuo #if NBPFILTER > 0 1259b5ab0bc5Syuo if (ifp->if_bpf) 1260b5ab0bc5Syuo bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT); 1261b5ab0bc5Syuo #endif 1262b5ab0bc5Syuo 1263de6cd8fbSdlg ifq_set_oactive(&ifp->if_snd); 1264b5ab0bc5Syuo 1265b5ab0bc5Syuo /* 1266b5ab0bc5Syuo * Set a timeout in case the chip goes out to lunch. 1267b5ab0bc5Syuo */ 1268b5ab0bc5Syuo ifp->if_timer = 5; 1269b5ab0bc5Syuo } 1270b5ab0bc5Syuo 1271b5ab0bc5Syuo void 1272b5ab0bc5Syuo axen_init(void *xsc) 1273b5ab0bc5Syuo { 1274b5ab0bc5Syuo struct axen_softc *sc = xsc; 1275b5ab0bc5Syuo struct ifnet *ifp = &sc->arpcom.ac_if; 1276b5ab0bc5Syuo struct axen_chain *c; 1277b5ab0bc5Syuo usbd_status err; 1278b5ab0bc5Syuo int i, s; 1279b5ab0bc5Syuo uByte bval; 1280b5ab0bc5Syuo uWord wval; 1281b5ab0bc5Syuo uint16_t rxmode; 1282b5ab0bc5Syuo 1283b5ab0bc5Syuo s = splnet(); 1284b5ab0bc5Syuo 1285b5ab0bc5Syuo /* 1286b5ab0bc5Syuo * Cancel pending I/O and free all RX/TX buffers. 1287b5ab0bc5Syuo */ 1288b5ab0bc5Syuo axen_reset(sc); 1289b5ab0bc5Syuo 1290b5ab0bc5Syuo /* XXX: ? */ 1291b5ab0bc5Syuo bval = 0x01; 129244e6486eSbmercer axen_lock_mii(sc); 1293b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_UNK_28, &bval); 129444e6486eSbmercer axen_unlock_mii(sc); 1295b5ab0bc5Syuo 1296b5ab0bc5Syuo /* Init RX ring. */ 1297b5ab0bc5Syuo if (axen_rx_list_init(sc) == ENOBUFS) { 1298b5ab0bc5Syuo printf("axen%d: rx list init failed\n", sc->axen_unit); 1299b5ab0bc5Syuo splx(s); 1300b5ab0bc5Syuo return; 1301b5ab0bc5Syuo } 1302b5ab0bc5Syuo 1303b5ab0bc5Syuo /* Init TX ring. */ 1304b5ab0bc5Syuo if (axen_tx_list_init(sc) == ENOBUFS) { 1305b5ab0bc5Syuo printf("axen%d: tx list init failed\n", sc->axen_unit); 1306b5ab0bc5Syuo splx(s); 1307b5ab0bc5Syuo return; 1308b5ab0bc5Syuo } 1309b5ab0bc5Syuo 1310b5ab0bc5Syuo /* Program promiscuous mode and multicast filters. */ 1311b5ab0bc5Syuo axen_iff(sc); 1312b5ab0bc5Syuo 1313b5ab0bc5Syuo /* Enable receiver, set RX mode */ 1314834d143cSyuo axen_lock_mii(sc); 1315b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); 1316b5ab0bc5Syuo rxmode = UGETW(wval); 1317b5ab0bc5Syuo rxmode |= AXEN_RXCTL_START; 1318834d143cSyuo USETW(wval, rxmode); 1319b5ab0bc5Syuo axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); 1320834d143cSyuo axen_unlock_mii(sc); 1321b5ab0bc5Syuo 1322b5ab0bc5Syuo /* Open RX and TX pipes. */ 1323b5ab0bc5Syuo err = usbd_open_pipe(sc->axen_iface, sc->axen_ed[AXEN_ENDPT_RX], 1324b5ab0bc5Syuo USBD_EXCLUSIVE_USE, &sc->axen_ep[AXEN_ENDPT_RX]); 1325b5ab0bc5Syuo if (err) { 1326b5ab0bc5Syuo printf("axen%d: open rx pipe failed: %s\n", 1327b5ab0bc5Syuo sc->axen_unit, usbd_errstr(err)); 1328b5ab0bc5Syuo splx(s); 1329b5ab0bc5Syuo return; 1330b5ab0bc5Syuo } 1331b5ab0bc5Syuo 1332b5ab0bc5Syuo err = usbd_open_pipe(sc->axen_iface, sc->axen_ed[AXEN_ENDPT_TX], 1333b5ab0bc5Syuo USBD_EXCLUSIVE_USE, &sc->axen_ep[AXEN_ENDPT_TX]); 1334b5ab0bc5Syuo if (err) { 1335b5ab0bc5Syuo printf("axen%d: open tx pipe failed: %s\n", 1336b5ab0bc5Syuo sc->axen_unit, usbd_errstr(err)); 1337b5ab0bc5Syuo splx(s); 1338b5ab0bc5Syuo return; 1339b5ab0bc5Syuo } 1340b5ab0bc5Syuo 1341b5ab0bc5Syuo /* Start up the receive pipe. */ 1342b5ab0bc5Syuo for (i = 0; i < AXEN_RX_LIST_CNT; i++) { 1343b5ab0bc5Syuo c = &sc->axen_cdata.axen_rx_chain[i]; 1344b5ab0bc5Syuo usbd_setup_xfer(c->axen_xfer, sc->axen_ep[AXEN_ENDPT_RX], 1345b5ab0bc5Syuo c, c->axen_buf, sc->axen_bufsz, 1346b5ab0bc5Syuo USBD_SHORT_XFER_OK | USBD_NO_COPY, 1347b5ab0bc5Syuo USBD_NO_TIMEOUT, axen_rxeof); 1348b5ab0bc5Syuo usbd_transfer(c->axen_xfer); 1349b5ab0bc5Syuo } 1350b5ab0bc5Syuo 1351b5ab0bc5Syuo sc->axen_link = 0; 1352b5ab0bc5Syuo ifp->if_flags |= IFF_RUNNING; 1353de6cd8fbSdlg ifq_clr_oactive(&ifp->if_snd); 1354b5ab0bc5Syuo 1355b5ab0bc5Syuo splx(s); 1356b5ab0bc5Syuo 1357b5ab0bc5Syuo timeout_add_sec(&sc->axen_stat_ch, 1); 1358b5ab0bc5Syuo return; 1359b5ab0bc5Syuo } 1360b5ab0bc5Syuo 1361b5ab0bc5Syuo int 1362b5ab0bc5Syuo axen_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 1363b5ab0bc5Syuo { 1364b5ab0bc5Syuo struct axen_softc *sc = ifp->if_softc; 1365b5ab0bc5Syuo struct ifreq *ifr = (struct ifreq *)data; 1366834d143cSyuo int s; 1367834d143cSyuo int error = 0; 1368b5ab0bc5Syuo 1369b5ab0bc5Syuo s = splnet(); 1370b5ab0bc5Syuo 1371b5ab0bc5Syuo switch(cmd) { 1372b5ab0bc5Syuo case SIOCSIFADDR: 1373b5ab0bc5Syuo ifp->if_flags |= IFF_UP; 1374b5ab0bc5Syuo if (!(ifp->if_flags & IFF_RUNNING)) 1375b5ab0bc5Syuo axen_init(sc); 1376b5ab0bc5Syuo break; 1377b5ab0bc5Syuo 1378b5ab0bc5Syuo case SIOCSIFFLAGS: 1379b5ab0bc5Syuo if (ifp->if_flags & IFF_UP) { 1380b5ab0bc5Syuo if (ifp->if_flags & IFF_RUNNING) 1381b5ab0bc5Syuo error = ENETRESET; 1382b5ab0bc5Syuo else 1383b5ab0bc5Syuo axen_init(sc); 1384b5ab0bc5Syuo } else { 1385b5ab0bc5Syuo if (ifp->if_flags & IFF_RUNNING) 1386b5ab0bc5Syuo axen_stop(sc); 1387b5ab0bc5Syuo } 1388b5ab0bc5Syuo break; 1389b5ab0bc5Syuo 1390b5ab0bc5Syuo case SIOCGIFMEDIA: 1391b5ab0bc5Syuo case SIOCSIFMEDIA: 1392b5ab0bc5Syuo error = ifmedia_ioctl(ifp, ifr, &sc->axen_mii.mii_media, cmd); 1393b5ab0bc5Syuo break; 1394b5ab0bc5Syuo 1395b5ab0bc5Syuo #if 0 1396b5ab0bc5Syuo case SCIOCSIFMTU: 1397834d143cSyuo /* XXX need to set AX_MEDIUM_JUMBO_EN here? */ 1398b5ab0bc5Syuo /* fall through */ 1399b5ab0bc5Syuo #endif 1400b5ab0bc5Syuo default: 1401b5ab0bc5Syuo error = ether_ioctl(ifp, &sc->arpcom, cmd, data); 1402b5ab0bc5Syuo } 1403b5ab0bc5Syuo 1404b5ab0bc5Syuo if (error == ENETRESET) { 1405b5ab0bc5Syuo if (ifp->if_flags & IFF_RUNNING) 1406b5ab0bc5Syuo axen_iff(sc); 1407b5ab0bc5Syuo error = 0; 1408b5ab0bc5Syuo } 1409b5ab0bc5Syuo 1410b5ab0bc5Syuo splx(s); 1411834d143cSyuo 1412834d143cSyuo return error; 1413b5ab0bc5Syuo } 1414b5ab0bc5Syuo 1415b5ab0bc5Syuo void 1416b5ab0bc5Syuo axen_watchdog(struct ifnet *ifp) 1417b5ab0bc5Syuo { 1418b5ab0bc5Syuo struct axen_softc *sc; 1419b5ab0bc5Syuo struct axen_chain *c; 1420b5ab0bc5Syuo usbd_status stat; 1421b5ab0bc5Syuo int s; 1422b5ab0bc5Syuo 1423b5ab0bc5Syuo sc = ifp->if_softc; 1424b5ab0bc5Syuo 1425b5ab0bc5Syuo ifp->if_oerrors++; 1426b5ab0bc5Syuo printf("axen%d: watchdog timeout\n", sc->axen_unit); 1427b5ab0bc5Syuo 1428b5ab0bc5Syuo s = splusb(); 1429b5ab0bc5Syuo c = &sc->axen_cdata.axen_tx_chain[0]; 1430b5ab0bc5Syuo usbd_get_xfer_status(c->axen_xfer, NULL, NULL, NULL, &stat); 1431b5ab0bc5Syuo axen_txeof(c->axen_xfer, c, stat); 1432b5ab0bc5Syuo 14330cae21bdSpatrick if (!ifq_empty(&ifp->if_snd)) 1434b5ab0bc5Syuo axen_start(ifp); 1435b5ab0bc5Syuo splx(s); 1436b5ab0bc5Syuo } 1437b5ab0bc5Syuo 1438b5ab0bc5Syuo /* 1439b5ab0bc5Syuo * Stop the adapter and free any mbufs allocated to the 1440b5ab0bc5Syuo * RX and TX lists. 1441b5ab0bc5Syuo */ 1442b5ab0bc5Syuo void 1443b5ab0bc5Syuo axen_stop(struct axen_softc *sc) 1444b5ab0bc5Syuo { 1445b5ab0bc5Syuo usbd_status err; 1446b5ab0bc5Syuo struct ifnet *ifp; 1447b5ab0bc5Syuo int i; 1448b5ab0bc5Syuo 1449b5ab0bc5Syuo axen_reset(sc); 1450b5ab0bc5Syuo 1451b5ab0bc5Syuo ifp = &sc->arpcom.ac_if; 1452b5ab0bc5Syuo ifp->if_timer = 0; 1453de6cd8fbSdlg ifp->if_flags &= ~IFF_RUNNING; 1454de6cd8fbSdlg ifq_clr_oactive(&ifp->if_snd); 1455b5ab0bc5Syuo 1456b5ab0bc5Syuo timeout_del(&sc->axen_stat_ch); 1457b5ab0bc5Syuo 1458b5ab0bc5Syuo /* Stop transfers. */ 1459b5ab0bc5Syuo if (sc->axen_ep[AXEN_ENDPT_RX] != NULL) { 1460b5ab0bc5Syuo err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_RX]); 1461b5ab0bc5Syuo if (err) { 1462b5ab0bc5Syuo printf("axen%d: close rx pipe failed: %s\n", 1463b5ab0bc5Syuo sc->axen_unit, usbd_errstr(err)); 1464b5ab0bc5Syuo } 1465b5ab0bc5Syuo sc->axen_ep[AXEN_ENDPT_RX] = NULL; 1466b5ab0bc5Syuo } 1467b5ab0bc5Syuo 1468b5ab0bc5Syuo if (sc->axen_ep[AXEN_ENDPT_TX] != NULL) { 1469b5ab0bc5Syuo err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_TX]); 1470b5ab0bc5Syuo if (err) { 1471b5ab0bc5Syuo printf("axen%d: close tx pipe failed: %s\n", 1472b5ab0bc5Syuo sc->axen_unit, usbd_errstr(err)); 1473b5ab0bc5Syuo } 1474b5ab0bc5Syuo sc->axen_ep[AXEN_ENDPT_TX] = NULL; 1475b5ab0bc5Syuo } 1476b5ab0bc5Syuo 1477b5ab0bc5Syuo if (sc->axen_ep[AXEN_ENDPT_INTR] != NULL) { 1478b5ab0bc5Syuo err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_INTR]); 1479b5ab0bc5Syuo if (err) { 1480b5ab0bc5Syuo printf("axen%d: close intr pipe failed: %s\n", 1481b5ab0bc5Syuo sc->axen_unit, usbd_errstr(err)); 1482b5ab0bc5Syuo } 1483b5ab0bc5Syuo sc->axen_ep[AXEN_ENDPT_INTR] = NULL; 1484b5ab0bc5Syuo } 1485b5ab0bc5Syuo 1486b5ab0bc5Syuo /* Free RX resources. */ 1487b5ab0bc5Syuo for (i = 0; i < AXEN_RX_LIST_CNT; i++) { 1488b5ab0bc5Syuo if (sc->axen_cdata.axen_rx_chain[i].axen_mbuf != NULL) { 1489b5ab0bc5Syuo m_freem(sc->axen_cdata.axen_rx_chain[i].axen_mbuf); 1490b5ab0bc5Syuo sc->axen_cdata.axen_rx_chain[i].axen_mbuf = NULL; 1491b5ab0bc5Syuo } 1492b5ab0bc5Syuo if (sc->axen_cdata.axen_rx_chain[i].axen_xfer != NULL) { 1493b5ab0bc5Syuo usbd_free_xfer(sc->axen_cdata.axen_rx_chain[i].axen_xfer); 1494b5ab0bc5Syuo sc->axen_cdata.axen_rx_chain[i].axen_xfer = NULL; 1495b5ab0bc5Syuo } 1496b5ab0bc5Syuo } 1497b5ab0bc5Syuo 1498b5ab0bc5Syuo /* Free TX resources. */ 1499b5ab0bc5Syuo for (i = 0; i < AXEN_TX_LIST_CNT; i++) { 1500834d143cSyuo 1501b5ab0bc5Syuo if (sc->axen_cdata.axen_tx_chain[i].axen_mbuf != NULL) { 1502b5ab0bc5Syuo m_freem(sc->axen_cdata.axen_tx_chain[i].axen_mbuf); 1503b5ab0bc5Syuo sc->axen_cdata.axen_tx_chain[i].axen_mbuf = NULL; 1504b5ab0bc5Syuo } 1505b5ab0bc5Syuo if (sc->axen_cdata.axen_tx_chain[i].axen_xfer != NULL) { 1506b5ab0bc5Syuo usbd_free_xfer(sc->axen_cdata.axen_tx_chain[i].axen_xfer); 1507b5ab0bc5Syuo sc->axen_cdata.axen_tx_chain[i].axen_xfer = NULL; 1508b5ab0bc5Syuo } 1509b5ab0bc5Syuo } 1510b5ab0bc5Syuo 1511b5ab0bc5Syuo sc->axen_link = 0; 1512b5ab0bc5Syuo } 1513