Index: sys/dev/usb/if_smsc.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/if_smsc.c,v retrieving revision 1.30 diff -u -p -r1.30 if_smsc.c --- sys/dev/usb/if_smsc.c 15 Dec 2016 09:28:06 -0000 1.30 +++ sys/dev/usb/if_smsc.c 29 Aug 2017 07:48:43 -0000 @@ -67,17 +67,15 @@ #include #include -#include -#include +#include +#include #include #include -#include #include -#include - -#include - #include +#include +#include +#include #include #include @@ -161,9 +159,12 @@ int smsc_detach(device_t, int); int smsc_activate(device_t, enum devact); int smsc_init(struct ifnet *); +int smsc_init_locked(struct ifnet *); void smsc_start(struct ifnet *); +void smsc_start_locked(struct ifnet *); int smsc_ioctl(struct ifnet *, u_long, void *); void smsc_stop(struct ifnet *, int); +void smsc_stop_locked(struct ifnet *, int); void smsc_reset(struct smsc_softc *); struct mbuf *smsc_newbuf(void); @@ -171,6 +172,7 @@ struct mbuf *smsc_newbuf(void); void smsc_tick(void *); void smsc_tick_task(void *); void smsc_miibus_statchg(struct ifnet *); +void smsc_miibus_statchg_locked(struct ifnet *); int smsc_miibus_readreg(device_t, int, int); void smsc_miibus_writereg(device_t, int, int, int); int smsc_ifmedia_upd(struct ifnet *); @@ -179,7 +181,9 @@ void smsc_lock_mii(struct smsc_softc * void smsc_unlock_mii(struct smsc_softc *); int smsc_tx_list_init(struct smsc_softc *); +void smsc_tx_list_free(struct smsc_softc *); int smsc_rx_list_init(struct smsc_softc *); +void smsc_rx_list_free(struct smsc_softc *); int smsc_encap(struct smsc_softc *, struct mbuf *, int); void smsc_rxeof(struct usbd_xfer *, void *, usbd_status); void smsc_txeof(struct usbd_xfer *, void *, usbd_status); @@ -256,7 +260,7 @@ smsc_wait_for_bits(struct smsc_softc *sc int smsc_miibus_readreg(device_t dev, int phy, int reg) { - struct smsc_softc *sc = device_private(dev); + struct smsc_softc * const sc = device_private(dev); uint32_t addr; uint32_t val = 0; @@ -277,13 +281,13 @@ smsc_miibus_readreg(device_t dev, int ph done: smsc_unlock_mii(sc); - return val & 0xFFFF; + return val & 0xffff; } void smsc_miibus_writereg(device_t dev, int phy, int reg, int val) { - struct smsc_softc *sc = device_private(dev); + struct smsc_softc * const sc = device_private(dev); uint32_t addr; if (sc->sc_phyno != phy) @@ -309,15 +313,37 @@ smsc_miibus_writereg(device_t dev, int p void smsc_miibus_statchg(struct ifnet *ifp) { - struct smsc_softc *sc = ifp->if_softc; - struct mii_data *mii = &sc->sc_mii; + if (ifp == NULL) + return; + + struct smsc_softc * const sc = ifp->if_softc; + + mutex_enter(&sc->sc_lock); + if (sc->sc_dying) { + mutex_exit(&sc->sc_lock); + return; + } + smsc_miibus_statchg_locked(ifp); + + mutex_exit(&sc->sc_lock); +} + + +void +smsc_miibus_statchg_locked(struct ifnet *ifp) +{ + struct smsc_softc * const sc = ifp->if_softc; + struct mii_data * const mii = &sc->sc_mii; int err; uint32_t flow; uint32_t afc_cfg; - if (mii == NULL || ifp == NULL || - (ifp->if_flags & IFF_RUNNING) == 0) + KASSERT(mutex_owned(&sc->sc_lock)); + + if ((ifp->if_flags & IFF_RUNNING) == 0) { + smsc_dbg_printf(sc, "%s: not running\n", __func__); return; + } /* Use the MII status to determine link status */ sc->sc_flags &= ~SMSC_FLAG_LINK; @@ -364,7 +390,6 @@ smsc_miibus_statchg(struct ifnet *ifp) afc_cfg |= 0xf; else afc_cfg &= ~0xf; - } else { smsc_dbg_printf(sc, "half duplex operation\n"); sc->sc_mac_csr &= ~SMSC_MAC_CSR_FDPX; @@ -384,8 +409,8 @@ smsc_miibus_statchg(struct ifnet *ifp) int smsc_ifmedia_upd(struct ifnet *ifp) { - struct smsc_softc *sc = ifp->if_softc; - struct mii_data *mii = &sc->sc_mii; + struct smsc_softc * const sc = ifp->if_softc; + struct mii_data * const mii = &sc->sc_mii; int err; if (mii->mii_instance) { @@ -401,29 +426,36 @@ smsc_ifmedia_upd(struct ifnet *ifp) void smsc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { - struct smsc_softc *sc = ifp->if_softc; - struct mii_data *mii = &sc->sc_mii; + struct smsc_softc * const sc = ifp->if_softc; + struct mii_data * const mii = &sc->sc_mii; + + /* SMSC_LOCK */ mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; + + /* SMSC_UNLOCK */ } static inline uint32_t smsc_hash(uint8_t addr[ETHER_ADDR_LEN]) { + return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26) & 0x3f; } void smsc_setmulti(struct smsc_softc *sc) { - struct ifnet *ifp = &sc->sc_ec.ec_if; - struct ether_multi *enm; - struct ether_multistep step; - uint32_t hashtbl[2] = { 0, 0 }; - uint32_t hash; + struct ifnet * const ifp = &sc->sc_ec.ec_if; + struct ether_multi *enm; + struct ether_multistep step; + uint32_t hashtbl[2] = { 0, 0 }; + uint32_t hash; + + KASSERT(mutex_owned(&sc->sc_lock)); if (sc->sc_dying) return; @@ -440,16 +472,19 @@ allmulti: sc->sc_mac_csr &= ~(SMSC_MAC_CSR_PRMS | SMSC_MAC_CSR_MCPAS); } + ETHER_LOCK(&sc->sc_ec); ETHER_FIRST_MULTI(step, &sc->sc_ec, enm); while (enm != NULL) { - if (memcmp(enm->enm_addrlo, enm->enm_addrhi, - ETHER_ADDR_LEN) != 0) + if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { + ETHER_UNLOCK(&sc->sc_ec); goto allmulti; + } hash = smsc_hash(enm->enm_addrlo); hashtbl[hash >> 5] |= 1 << (hash & 0x1F); ETHER_NEXT_MULTI(step, enm); } + ETHER_UNLOCK(&sc->sc_ec); /* Debug */ if (sc->sc_mac_csr & SMSC_MAC_CSR_HPFILT) { @@ -468,13 +503,10 @@ allmulti: int smsc_sethwcsum(struct smsc_softc *sc) { - struct ifnet *ifp = &sc->sc_ec.ec_if; + struct ifnet * const ifp = &sc->sc_ec.ec_if; uint32_t val; int err; - if (!ifp) - return EIO; - err = smsc_read_reg(sc, SMSC_COE_CTRL, &val); if (err != 0) { smsc_warn_printf(sc, "failed to read SMSC_COE_CTRL (err=%d)\n", @@ -530,6 +562,7 @@ done: void smsc_reset(struct smsc_softc *sc) { + KASSERT(mutex_owned(&sc->sc_lock)); if (sc->sc_dying) return; @@ -543,19 +576,26 @@ smsc_reset(struct smsc_softc *sc) int smsc_init(struct ifnet *ifp) { - struct smsc_softc *sc = ifp->if_softc; - struct smsc_chain *c; - usbd_status err; - int s, i; + struct smsc_softc * const sc = ifp->if_softc; + + mutex_enter(&sc->sc_lock); + int ret = smsc_init_locked(ifp); + mutex_exit(&sc->sc_lock); + + return ret; +} + +int +smsc_init_locked(struct ifnet *ifp) +{ + struct smsc_softc * const sc = ifp->if_softc; + usbd_status err; if (sc->sc_dying) return EIO; - s = splnet(); - /* Cancel pending I/O */ - if (ifp->if_flags & IFF_RUNNING) - smsc_stop(ifp, 1); + smsc_stop_locked(ifp, 1); /* Reset the ethernet interface. */ smsc_reset(sc); @@ -568,83 +608,120 @@ smsc_init(struct ifnet *ifp) /* Open RX and TX pipes. */ err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[SMSC_ENDPT_RX], - USBD_EXCLUSIVE_USE, &sc->sc_ep[SMSC_ENDPT_RX]); + USBD_EXCLUSIVE_USE | USBD_MPSAFE, &sc->sc_ep[SMSC_ENDPT_RX]); if (err) { printf("%s: open rx pipe failed: %s\n", device_xname(sc->sc_dev), usbd_errstr(err)); - splx(s); - return EIO; + goto fail; } err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[SMSC_ENDPT_TX], - USBD_EXCLUSIVE_USE, &sc->sc_ep[SMSC_ENDPT_TX]); + USBD_EXCLUSIVE_USE | USBD_MPSAFE, &sc->sc_ep[SMSC_ENDPT_TX]); if (err) { printf("%s: open tx pipe failed: %s\n", device_xname(sc->sc_dev), usbd_errstr(err)); - splx(s); - return EIO; + goto fail1; } /* Init RX ring. */ if (smsc_rx_list_init(sc)) { aprint_error_dev(sc->sc_dev, "rx list init failed\n"); - splx(s); - return EIO; + goto fail2; } /* Init TX ring. */ if (smsc_tx_list_init(sc)) { aprint_error_dev(sc->sc_dev, "tx list init failed\n"); - splx(s); - return EIO; + goto fail3; } + mutex_enter(&sc->sc_rxlock); + mutex_enter(&sc->sc_txlock); + sc->sc_stopping = false; + /* Start up the receive pipe. */ - for (i = 0; i < SMSC_RX_LIST_CNT; i++) { - c = &sc->sc_cdata.rx_chain[i]; + for (size_t i = 0; i < SMSC_RX_LIST_CNT; i++) { + struct smsc_chain *c = &sc->sc_cdata.rx_chain[i]; usbd_setup_xfer(c->sc_xfer, c, c->sc_buf, sc->sc_bufsz, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, smsc_rxeof); usbd_transfer(c->sc_xfer); } + mutex_exit(&sc->sc_txlock); + mutex_exit(&sc->sc_rxlock); + /* Indicate we are up and running. */ ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; - splx(s); - callout_reset(&sc->sc_stat_ch, hz, smsc_tick, sc); return 0; + +fail3: + smsc_rx_list_free(sc); +fail2: + usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_TX]); +fail1: + usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_RX]); +fail: + return EIO; } void smsc_start(struct ifnet *ifp) { - struct smsc_softc *sc = ifp->if_softc; - struct mbuf *m_head = NULL; + struct smsc_softc * const sc = ifp->if_softc; + KASSERT(ifp->if_extflags & IFEF_START_MPSAFE); + + mutex_enter(&sc->sc_txlock); + if (!sc->sc_stopping) + smsc_start_locked(ifp); + mutex_exit(&sc->sc_txlock); +} + +void +smsc_start_locked(struct ifnet *ifp) +{ + struct smsc_softc * const sc = ifp->if_softc; + struct mbuf *m_head = NULL; + + KASSERT(mutex_owned(&sc->sc_txlock)); /* Don't send anything if there is no link or controller is busy. */ if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) { + smsc_dbg_printf(sc, "%s: no link\n", __func__); return; } - if ((ifp->if_flags & (IFF_OACTIVE|IFF_RUNNING)) != IFF_RUNNING) + /* transmitter busy ? */ + if (sc->sc_cdata.tx_free == 0) + return; + + if ((ifp->if_flags & (IFF_OACTIVE|IFF_RUNNING)) != IFF_RUNNING) { + smsc_dbg_printf(sc, "%s: not running\n", __func__); return; + } IFQ_POLL(&ifp->if_snd, m_head); if (m_head == NULL) return; - if (smsc_encap(sc, m_head, 0)) { - ifp->if_flags |= IFF_OACTIVE; + sc->sc_cdata.tx_free--; + + IFQ_DEQUEUE(&ifp->if_snd, m_head); + if (smsc_encap(sc, m_head, sc->sc_cdata.tx_next)) { + m_free(m_head); + sc->sc_cdata.tx_free++; return; } - IFQ_DEQUEUE(&ifp->if_snd, m_head); + + sc->sc_cdata.tx_next = (sc->sc_cdata.tx_next + 1) % SMSC_TX_LIST_CNT; bpf_mtap(ifp, m_head); - ifp->if_flags |= IFF_OACTIVE; + if (sc->sc_cdata.tx_free == 0) + ifp->if_flags |= IFF_OACTIVE; /* * Set a timeout in case the chip goes out to lunch. @@ -655,29 +732,48 @@ smsc_start(struct ifnet *ifp) void smsc_tick(void *xsc) { - struct smsc_softc *sc = xsc; + struct smsc_softc * const sc = xsc; if (sc == NULL) return; - if (sc->sc_dying) + mutex_enter(&sc->sc_lock); + + if (sc->sc_dying) { + mutex_exit(&sc->sc_lock); return; + } + + if (!sc->sc_ttpending) { + sc->sc_ttpending = true; + usb_add_task(sc->sc_udev, &sc->sc_tick_task, USB_TASKQ_DRIVER); + } - usb_add_task(sc->sc_udev, &sc->sc_tick_task, USB_TASKQ_DRIVER); + mutex_exit(&sc->sc_lock); } void smsc_stop(struct ifnet *ifp, int disable) { - usbd_status err; - struct smsc_softc *sc = ifp->if_softc; - int i; + struct smsc_softc * const sc = ifp->if_softc; - smsc_reset(sc); + mutex_enter(&sc->sc_lock); + smsc_stop_locked(ifp, disable); + mutex_exit(&sc->sc_lock); +} - ifp = &sc->sc_ec.ec_if; - ifp->if_timer = 0; - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); +void +smsc_stop_locked(struct ifnet *ifp, int disable) +{ + struct smsc_softc * const sc = ifp->if_softc; + usbd_status err; + + KASSERT(mutex_owned(&sc->sc_lock)); + mutex_enter(&sc->sc_rxlock); + mutex_enter(&sc->sc_txlock); + sc->sc_stopping = true; + mutex_exit(&sc->sc_txlock); + mutex_exit(&sc->sc_rxlock); callout_stop(&sc->sc_stat_ch); @@ -706,29 +802,10 @@ smsc_stop(struct ifnet *ifp, int disable } } - /* Free RX resources. */ - for (i = 0; i < SMSC_RX_LIST_CNT; i++) { - if (sc->sc_cdata.rx_chain[i].sc_mbuf != NULL) { - m_freem(sc->sc_cdata.rx_chain[i].sc_mbuf); - sc->sc_cdata.rx_chain[i].sc_mbuf = NULL; - } - if (sc->sc_cdata.rx_chain[i].sc_xfer != NULL) { - usbd_destroy_xfer(sc->sc_cdata.rx_chain[i].sc_xfer); - sc->sc_cdata.rx_chain[i].sc_xfer = NULL; - } - } + smsc_rx_list_free(sc); + + smsc_tx_list_free(sc); - /* Free TX resources. */ - for (i = 0; i < SMSC_TX_LIST_CNT; i++) { - if (sc->sc_cdata.tx_chain[i].sc_mbuf != NULL) { - m_freem(sc->sc_cdata.tx_chain[i].sc_mbuf); - sc->sc_cdata.tx_chain[i].sc_mbuf = NULL; - } - if (sc->sc_cdata.tx_chain[i].sc_xfer != NULL) { - usbd_destroy_xfer(sc->sc_cdata.tx_chain[i].sc_xfer); - sc->sc_cdata.tx_chain[i].sc_xfer = NULL; - } - } /* Close pipes */ if (sc->sc_ep[SMSC_ENDPT_RX] != NULL) { err = usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_RX]); @@ -756,6 +833,13 @@ smsc_stop(struct ifnet *ifp, int disable } sc->sc_ep[SMSC_ENDPT_INTR] = NULL; } + + ifp->if_timer = 0; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + if (disable) { + /* drain */ + } } int @@ -787,7 +871,7 @@ smsc_chip_init(struct smsc_softc *sc) usbd_delay_ms(sc->sc_udev, 40); /* Set the mac address */ - struct ifnet *ifp = &sc->sc_ec.ec_if; + struct ifnet * const ifp = &sc->sc_ec.ec_if; const char *eaddr = CLLADDR(ifp->if_sadl); if ((err = smsc_setmacaddress(sc, eaddr)) != 0) { smsc_warn_printf(sc, "failed to set the MAC address\n"); @@ -906,67 +990,64 @@ init_failed: return err; } -int -smsc_ioctl(struct ifnet *ifp, u_long cmd, void *data) +static int +smsc_ifflags_cb(struct ethercom *ec) { - struct smsc_softc *sc = ifp->if_softc; - struct ifreq /*const*/ *ifr = data; - int s, error = 0; + struct ifnet *ifp = &ec->ec_if; + struct smsc_softc *sc = ifp->if_softc; - if (sc->sc_dying) - return EIO; + mutex_enter(&sc->sc_lock); - s = splnet(); + const int change = ifp->if_flags ^ sc->sc_if_flags; + if ((change & ~(IFF_CANTCHANGE | IFF_DEBUG)) != 0) { + mutex_exit(&sc->sc_lock); + return ENETRESET; + } - switch(cmd) { - case SIOCSIFFLAGS: - if ((error = ifioctl_common(ifp, cmd, data)) != 0) - break; - - switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { - case IFF_RUNNING: - smsc_stop(ifp, 1); - break; - case IFF_UP: - smsc_init(ifp); - break; - case IFF_UP | IFF_RUNNING: - if (ifp->if_flags & IFF_PROMISC && - !(sc->sc_if_flags & IFF_PROMISC)) { - sc->sc_mac_csr |= SMSC_MAC_CSR_PRMS; - smsc_write_reg(sc, SMSC_MAC_CSR, - sc->sc_mac_csr); - smsc_setmulti(sc); - } else if (!(ifp->if_flags & IFF_PROMISC) && - sc->sc_if_flags & IFF_PROMISC) { - sc->sc_mac_csr &= ~SMSC_MAC_CSR_PRMS; - smsc_write_reg(sc, SMSC_MAC_CSR, - sc->sc_mac_csr); - smsc_setmulti(sc); - } else { - smsc_init(ifp); - } - break; + smsc_dbg_printf(sc, "%s: change %x\n", __func__, change); + + if ((change & IFF_PROMISC) != 0) { + if (ifp->if_flags & IFF_PROMISC) { + sc->sc_mac_csr |= SMSC_MAC_CSR_PRMS; + smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); + } else if (!(ifp->if_flags & IFF_PROMISC)) { + sc->sc_mac_csr &= ~SMSC_MAC_CSR_PRMS; + smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); } - sc->sc_if_flags = ifp->if_flags; - break; + smsc_setmulti(sc); + } - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd); - break; + mutex_exit(&sc->sc_lock); - default: - if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET) - break; + return 0; +} - error = 0; - if (cmd == SIOCADDMULTI || cmd == SIOCDELMULTI) - smsc_setmulti(sc); +int +smsc_ioctl(struct ifnet *ifp, u_long cmd, void *data) +{ + struct smsc_softc * const sc = ifp->if_softc; + + smsc_dbg_printf(sc, "%s: cmd %0lx data %p\n", __func__, cmd, data); + int error = ether_ioctl(ifp, cmd, data); + + if (error == ENETRESET) { + error = 0; + if (cmd == SIOCADDMULTI || cmd == SIOCDELMULTI) { + if (ifp->if_flags & IFF_RUNNING) { + mutex_enter(&sc->sc_lock); + smsc_setmulti(sc); + mutex_exit(&sc->sc_lock); + } + } } - splx(s); + + mutex_enter(&sc->sc_rxlock); + mutex_enter(&sc->sc_txlock); + sc->sc_if_flags = ifp->if_flags; + mutex_exit(&sc->sc_txlock); + mutex_exit(&sc->sc_rxlock); return error; } @@ -991,11 +1072,12 @@ smsc_attach(device_t parent, device_t se char *devinfop; struct mii_data *mii; struct ifnet *ifp; - int err, s, i; + int err, i; uint32_t mac_h, mac_l; sc->sc_dev = self; sc->sc_udev = dev; + sc->sc_stopping = false; aprint_naive("\n"); aprint_normal("\n"); @@ -1010,11 +1092,8 @@ smsc_attach(device_t parent, device_t se ", err=%s\n", usbd_errstr(err)); return; } - /* Setup the endpoints for the SMSC LAN95xx device(s) */ - usb_init_task(&sc->sc_tick_task, smsc_tick_task, sc, 0); - usb_init_task(&sc->sc_stop_task, (void (*)(void *))smsc_stop, sc, 0); - mutex_init(&sc->sc_mii_lock, MUTEX_DEFAULT, IPL_NONE); + /* Setup the endpoints for the SMSC LAN95xx device(s) */ err = usbd_device2interface_handle(dev, SMSC_IFACE_IDX, &sc->sc_iface); if (err) { aprint_error_dev(self, "getting interface handle failed\n"); @@ -1047,12 +1126,18 @@ smsc_attach(device_t parent, device_t se } } - s = splnet(); + usb_init_task(&sc->sc_tick_task, smsc_tick_task, sc, USB_TASKQ_MPSAFE); + + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&sc->sc_txlock, MUTEX_DEFAULT, IPL_SOFTUSB); + mutex_init(&sc->sc_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB); + mutex_init(&sc->sc_mii_lock, MUTEX_DEFAULT, IPL_NONE); ifp = &sc->sc_ec.ec_if; ifp->if_softc = sc; strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_extflags = IFEF_START_MPSAFE; ifp->if_init = smsc_init; ifp->if_ioctl = smsc_ioctl; ifp->if_start = smsc_start; @@ -1087,19 +1172,21 @@ smsc_attach(device_t parent, device_t se KASSERT(prop_data_size(eaprop) == ETHER_ADDR_LEN); memcpy(sc->sc_enaddr, prop_data_data_nocopy(eaprop), ETHER_ADDR_LEN); - } else - /* Check if there is already a MAC address in the register */ - if ((smsc_read_reg(sc, SMSC_MAC_ADDRL, &mac_l) == 0) && - (smsc_read_reg(sc, SMSC_MAC_ADDRH, &mac_h) == 0)) { - sc->sc_enaddr[5] = (uint8_t)((mac_h >> 8) & 0xff); - sc->sc_enaddr[4] = (uint8_t)((mac_h) & 0xff); - sc->sc_enaddr[3] = (uint8_t)((mac_l >> 24) & 0xff); - sc->sc_enaddr[2] = (uint8_t)((mac_l >> 16) & 0xff); - sc->sc_enaddr[1] = (uint8_t)((mac_l >> 8) & 0xff); - sc->sc_enaddr[0] = (uint8_t)((mac_l) & 0xff); + } else { + /* Check if there is already a MAC address in the register */ + if ((smsc_read_reg(sc, SMSC_MAC_ADDRL, &mac_l) == 0) && + (smsc_read_reg(sc, SMSC_MAC_ADDRH, &mac_h) == 0)) { + sc->sc_enaddr[5] = (uint8_t)((mac_h >> 8) & 0xff); + sc->sc_enaddr[4] = (uint8_t)((mac_h) & 0xff); + sc->sc_enaddr[3] = (uint8_t)((mac_l >> 24) & 0xff); + sc->sc_enaddr[2] = (uint8_t)((mac_l >> 16) & 0xff); + sc->sc_enaddr[1] = (uint8_t)((mac_l >> 8) & 0xff); + sc->sc_enaddr[0] = (uint8_t)((mac_l) & 0xff); + } } - aprint_normal_dev(self, "Ethernet address %s\n", ether_sprintf(sc->sc_enaddr)); + aprint_normal_dev(self, "Ethernet address %s\n", + ether_sprintf(sc->sc_enaddr)); IFQ_SET_READY(&ifp->if_snd); @@ -1120,16 +1207,17 @@ smsc_attach(device_t parent, device_t se } else ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); - if_attach(ifp); + callout_init(&sc->sc_stat_ch, CALLOUT_MPSAFE); + + if_initialize(ifp); + sc->sc_ipq = if_percpuq_create(&sc->sc_ec.ec_if); ether_ifattach(ifp, sc->sc_enaddr); + ether_set_ifflags_cb(&sc->sc_ec, smsc_ifflags_cb); + if_register(ifp); rnd_attach_source(&sc->sc_rnd_source, device_xname(sc->sc_dev), RND_TYPE_NET, RND_FLAG_DEFAULT); - callout_init(&sc->sc_stat_ch, 0); - - splx(s); - usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); } @@ -1138,40 +1226,27 @@ smsc_detach(device_t self, int flags) { struct smsc_softc *sc = device_private(self); struct ifnet *ifp = &sc->sc_ec.ec_if; - int s; - callout_stop(&sc->sc_stat_ch); + mutex_enter(&sc->sc_lock); + sc->sc_dying = true; + mutex_exit(&sc->sc_lock); + + callout_halt(&sc->sc_stat_ch, NULL); - if (sc->sc_ep[SMSC_ENDPT_TX] != NULL) - usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_TX]); - if (sc->sc_ep[SMSC_ENDPT_RX] != NULL) - usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_RX]); - if (sc->sc_ep[SMSC_ENDPT_INTR] != NULL) - usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_INTR]); + if (ifp->if_flags & IFF_RUNNING) + smsc_stop_locked(ifp, 1); /* * Remove any pending tasks. They cannot be executing because they run * in the same thread as detach. */ usb_rem_task(sc->sc_udev, &sc->sc_tick_task); - usb_rem_task(sc->sc_udev, &sc->sc_stop_task); - - s = splusb(); - if (--sc->sc_refcnt >= 0) { + mutex_enter(&sc->sc_lock); + sc->sc_refcnt--; + while (sc->sc_refcnt > 0) { /* Wait for processes to go away */ - usb_detach_waitold(sc->sc_dev); - } - - if (ifp->if_flags & IFF_RUNNING) - smsc_stop(ifp ,1); - - rnd_detach_source(&sc->sc_rnd_source); - mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY); - ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY); - if (ifp->if_softc != NULL) { - ether_ifdetach(ifp); - if_detach(ifp); + cv_wait(&sc->sc_detachcv, &sc->sc_lock); } #ifdef DIAGNOSTIC @@ -1182,15 +1257,23 @@ smsc_detach(device_t self, int flags) device_xname(sc->sc_dev)); #endif - if (--sc->sc_refcnt >= 0) { - /* Wait for processes to go away. */ - usb_detach_waitold(sc->sc_dev); + mutex_exit(&sc->sc_lock); + + rnd_detach_source(&sc->sc_rnd_source); + mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY); + ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY); + if (ifp->if_softc != NULL) { + ether_ifdetach(ifp); + if_detach(ifp); } - splx(s); usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); + cv_destroy(&sc->sc_detachcv); mutex_destroy(&sc->sc_mii_lock); + mutex_destroy(&sc->sc_rxlock); + mutex_destroy(&sc->sc_txlock); + mutex_destroy(&sc->sc_lock); return 0; } @@ -1198,29 +1281,41 @@ smsc_detach(device_t self, int flags) void smsc_tick_task(void *xsc) { - int s; - struct smsc_softc *sc = xsc; - struct ifnet *ifp; - struct mii_data *mii; + struct smsc_softc * const sc = xsc; if (sc == NULL) return; - if (sc->sc_dying) - return; - ifp = &sc->sc_ec.ec_if; - mii = &sc->sc_mii; - if (mii == NULL) + mutex_enter(&sc->sc_lock); + + if (sc->sc_dying) { + mutex_exit(&sc->sc_lock); return; + } - s = splnet(); + struct ifnet * const ifp = &sc->sc_ec.ec_if; + struct mii_data * const mii = &sc->sc_mii; + + sc->sc_refcnt++; + mutex_exit(&sc->sc_lock); mii_tick(mii); if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) smsc_miibus_statchg(ifp); + + mutex_enter(&sc->sc_lock); + sc->sc_ttpending = false; + + if (--sc->sc_refcnt < 0) + cv_broadcast(&sc->sc_detachcv); + + if (sc->sc_dying) { + mutex_exit(&sc->sc_lock); + return; + } callout_reset(&sc->sc_stat_ch, hz, smsc_tick, sc); - splx(s); + mutex_exit(&sc->sc_lock); } int @@ -1231,7 +1326,17 @@ smsc_activate(device_t self, enum devact switch (act) { case DVACT_DEACTIVATE: if_deactivate(&sc->sc_ec.ec_if); - sc->sc_dying = 1; + + mutex_enter(&sc->sc_lock); + sc->sc_dying = true; + + mutex_enter(&sc->sc_rxlock); + mutex_enter(&sc->sc_txlock); + sc->sc_stopping = true; + mutex_exit(&sc->sc_txlock); + mutex_exit(&sc->sc_rxlock); + + mutex_exit(&sc->sc_lock); return 0; default: return EOPNOTSUPP; @@ -1242,40 +1347,52 @@ smsc_activate(device_t self, enum devact void smsc_lock_mii(struct smsc_softc *sc) { + + mutex_enter(&sc->sc_lock); sc->sc_refcnt++; + mutex_exit(&sc->sc_lock); + mutex_enter(&sc->sc_mii_lock); } void smsc_unlock_mii(struct smsc_softc *sc) { + mutex_exit(&sc->sc_mii_lock); + mutex_enter(&sc->sc_lock); if (--sc->sc_refcnt < 0) - usb_detach_wakeupold(sc->sc_dev); + cv_broadcast(&sc->sc_detachcv); + mutex_exit(&sc->sc_lock); } void smsc_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) { - struct smsc_chain *c = (struct smsc_chain *)priv; - struct smsc_softc *sc = c->sc_sc; - struct ifnet *ifp = &sc->sc_ec.ec_if; - u_char *buf = c->sc_buf; - uint32_t total_len; - uint32_t rxhdr; - uint16_t pktlen; - struct mbuf *m; - int s; - - if (sc->sc_dying) + struct smsc_chain * const c = (struct smsc_chain *)priv; + struct smsc_softc * const sc = c->sc_sc; + struct ifnet * const ifp = &sc->sc_ec.ec_if; + u_char *buf = c->sc_buf; + uint32_t total_len; + + mutex_enter(&sc->sc_rxlock); + if (sc->sc_stopping) { + smsc_dbg_printf(sc, "%s: stopping\n", __func__); + mutex_exit(&sc->sc_rxlock); return; + } - if (!(ifp->if_flags & IFF_RUNNING)) + if (!(sc->sc_if_flags & IFF_RUNNING)) { + smsc_dbg_printf(sc, "%s: not running\n", __func__); + mutex_exit(&sc->sc_rxlock); return; + } if (status != USBD_NORMAL_COMPLETION) { - if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { + mutex_exit(&sc->sc_rxlock); return; + } if (usbd_ratecheck(&sc->sc_rx_notice)) { printf("%s: usb errors on rx: %s\n", device_xname(sc->sc_dev), usbd_errstr(status)); @@ -1289,6 +1406,7 @@ smsc_rxeof(struct usbd_xfer *xfer, void smsc_dbg_printf(sc, "xfer status total_len %d\n", total_len); while (total_len != 0) { + uint32_t rxhdr; if (total_len < sizeof(rxhdr)) { smsc_dbg_printf(sc, "total_len %d < sizeof(rxhdr) %zu\n", total_len, sizeof(rxhdr)); @@ -1312,7 +1430,7 @@ smsc_rxeof(struct usbd_xfer *xfer, void goto done; } - pktlen = (uint16_t)SMSC_RX_STAT_FRM_LENGTH(rxhdr); + uint16_t pktlen = (uint16_t)SMSC_RX_STAT_FRM_LENGTH(rxhdr); smsc_dbg_printf(sc, "rxeof total_len %d pktlen %d rxhdr " "0x%08x\n", total_len, pktlen, rxhdr); @@ -1339,7 +1457,7 @@ smsc_rxeof(struct usbd_xfer *xfer, void goto done; } - m = smsc_newbuf(); + struct mbuf *m = smsc_newbuf(); if (m == NULL) { smsc_dbg_printf(sc, "smc_newbuf returned NULL\n"); ifp->if_ierrors++; @@ -1413,13 +1531,22 @@ smsc_rxeof(struct usbd_xfer *xfer, void buf += pktlen; total_len -= pktlen; + mutex_exit(&sc->sc_rxlock); + /* push the packet up */ - s = splnet(); - if_percpuq_enqueue(ifp->if_percpuq, m); - splx(s); + if_percpuq_enqueue(sc->sc_ipq, m); + + mutex_enter(&sc->sc_rxlock); + if (sc->sc_stopping) { + smsc_dbg_printf(sc, "%s: stopping\n", __func__); + mutex_exit(&sc->sc_rxlock); + return; + } } done: + mutex_exit(&sc->sc_rxlock); + /* Setup new transfer. */ usbd_setup_xfer(xfer, c, c->sc_buf, sc->sc_bufsz, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, smsc_rxeof); @@ -1431,26 +1558,24 @@ done: void smsc_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status) { - struct smsc_softc *sc; - struct smsc_chain *c; - struct ifnet *ifp; - int s; - - c = priv; - sc = c->sc_sc; - ifp = &sc->sc_ec.ec_if; + struct smsc_chain *c = priv; + struct smsc_softc *sc = c->sc_sc; + struct ifnet *ifp = &sc->sc_ec.ec_if; - if (sc->sc_dying) + mutex_enter(&sc->sc_txlock); + if (sc->sc_stopping) { + smsc_dbg_printf(sc, "%s: stopping\n", __func__); + mutex_exit(&sc->sc_txlock); return; + } - s = splnet(); - + sc->sc_cdata.tx_free++; ifp->if_timer = 0; ifp->if_flags &= ~IFF_OACTIVE; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { - splx(s); + mutex_exit(&sc->sc_txlock); return; } ifp->if_oerrors++; @@ -1458,7 +1583,7 @@ smsc_txeof(struct usbd_xfer *xfer, void usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_ep[SMSC_ENDPT_TX]); - splx(s); + mutex_exit(&sc->sc_txlock); return; } ifp->if_opackets++; @@ -1467,19 +1592,18 @@ smsc_txeof(struct usbd_xfer *xfer, void c->sc_mbuf = NULL; if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) - smsc_start(ifp); + smsc_start_locked(ifp); - splx(s); + mutex_exit(&sc->sc_txlock); } int smsc_tx_list_init(struct smsc_softc *sc) { - struct smsc_cdata *cd; + struct smsc_cdata *cd = &sc->sc_cdata; struct smsc_chain *c; int i; - cd = &sc->sc_cdata; for (i = 0; i < SMSC_TX_LIST_CNT; i++) { c = &cd->tx_chain[i]; c->sc_sc = sc; @@ -1495,17 +1619,35 @@ smsc_tx_list_init(struct smsc_softc *sc) } } + cd->tx_free = SMSC_TX_LIST_CNT; + cd->tx_next = 0; + return 0; } +void +smsc_tx_list_free(struct smsc_softc *sc) +{ + /* Free TX resources. */ + for (size_t i = 0; i < SMSC_TX_LIST_CNT; i++) { + if (sc->sc_cdata.tx_chain[i].sc_mbuf != NULL) { + m_freem(sc->sc_cdata.tx_chain[i].sc_mbuf); + sc->sc_cdata.tx_chain[i].sc_mbuf = NULL; + } + if (sc->sc_cdata.tx_chain[i].sc_xfer != NULL) { + usbd_destroy_xfer(sc->sc_cdata.tx_chain[i].sc_xfer); + sc->sc_cdata.tx_chain[i].sc_xfer = NULL; + } + } +} + int smsc_rx_list_init(struct smsc_softc *sc) { - struct smsc_cdata *cd; + struct smsc_cdata *cd = &sc->sc_cdata; struct smsc_chain *c; int i; - cd = &sc->sc_cdata; for (i = 0; i < SMSC_RX_LIST_CNT; i++) { c = &cd->rx_chain[i]; c->sc_sc = sc; @@ -1523,10 +1665,26 @@ smsc_rx_list_init(struct smsc_softc *sc) return 0; } +void +smsc_rx_list_free(struct smsc_softc *sc) +{ + /* Free RX resources. */ + for (size_t i = 0; i < SMSC_RX_LIST_CNT; i++) { + if (sc->sc_cdata.rx_chain[i].sc_mbuf != NULL) { + m_freem(sc->sc_cdata.rx_chain[i].sc_mbuf); + sc->sc_cdata.rx_chain[i].sc_mbuf = NULL; + } + if (sc->sc_cdata.rx_chain[i].sc_xfer != NULL) { + usbd_destroy_xfer(sc->sc_cdata.rx_chain[i].sc_xfer); + sc->sc_cdata.rx_chain[i].sc_xfer = NULL; + } + } +} + struct mbuf * smsc_newbuf(void) { - struct mbuf *m; + struct mbuf *m; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) @@ -1544,20 +1702,16 @@ smsc_newbuf(void) int smsc_encap(struct smsc_softc *sc, struct mbuf *m, int idx) { - struct ifnet *ifp = &sc->sc_ec.ec_if; - struct smsc_chain *c; - usbd_status err; - uint32_t txhdr; - uint32_t frm_len = 0; - - c = &sc->sc_cdata.tx_chain[idx]; + struct smsc_chain * const c = &sc->sc_cdata.tx_chain[idx]; + uint32_t txhdr; + uint32_t frm_len = 0; /* * Each frame is prefixed with two 32-bit values describing the * length of the packet and buffer. */ txhdr = SMSC_TX_CTRL_0_BUF_SIZE(m->m_pkthdr.len) | - SMSC_TX_CTRL_0_FIRST_SEG | SMSC_TX_CTRL_0_LAST_SEG; + SMSC_TX_CTRL_0_FIRST_SEG | SMSC_TX_CTRL_0_LAST_SEG; txhdr = htole32(txhdr); memcpy(c->sc_buf, &txhdr, sizeof(txhdr)); @@ -1576,14 +1730,10 @@ smsc_encap(struct smsc_softc *sc, struct usbd_setup_xfer(c->sc_xfer, c, c->sc_buf, frm_len, USBD_FORCE_SHORT_XFER, 10000, smsc_txeof); - err = usbd_transfer(c->sc_xfer); - /* XXXNH get task to stop interface */ + usbd_status err = usbd_transfer(c->sc_xfer); if (err != USBD_IN_PROGRESS) { - smsc_stop(ifp, 0); return EIO; } - sc->sc_cdata.tx_cnt++; - return 0; } Index: sys/dev/usb/if_smscvar.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/if_smscvar.h,v retrieving revision 1.5 diff -u -p -r1.5 if_smscvar.h --- sys/dev/usb/if_smscvar.h 23 Apr 2016 10:15:31 -0000 1.5 +++ sys/dev/usb/if_smscvar.h 29 Aug 2017 07:48:43 -0000 @@ -46,16 +46,17 @@ struct smsc_chain { struct smsc_cdata { struct smsc_chain tx_chain[SMSC_TX_LIST_CNT]; struct smsc_chain rx_chain[SMSC_RX_LIST_CNT]; - int tx_prod; - int tx_cons; - int tx_cnt; - int rx_prod; + int tx_free; + int tx_next; + int rx_next; }; struct smsc_softc { device_t sc_dev; struct usbd_device * sc_udev; bool sc_dying; + bool sc_stopping; + bool sc_ttpending; uint8_t sc_enaddr[ETHER_ADDR_LEN]; struct ethercom sc_ec; @@ -77,12 +78,15 @@ struct smsc_softc { int sc_refcnt; struct usb_task sc_tick_task; - struct usb_task sc_stop_task; int sc_ed[SMSC_ENDPT_MAX]; struct usbd_pipe * sc_ep[SMSC_ENDPT_MAX]; + kmutex_t sc_lock; + kmutex_t sc_txlock; + kmutex_t sc_rxlock; kmutex_t sc_mii_lock; + kcondvar_t sc_detachcv; struct smsc_cdata sc_cdata; callout_t sc_stat_ch; @@ -92,6 +96,9 @@ struct smsc_softc { uint32_t sc_flags; #define SMSC_FLAG_LINK 0x0001 + + struct if_percpuq *sc_ipq; /* softint-based input queues */ + }; #define SMSC_MIN_BUFSZ 2048