introduce a library of common code / backends to share code between USB ethernet drivers. usb_net.h introduces a new set of APIs to provide common solutions for these drivers: - USB endpoint pipe handling - rx and tx chain handling - generic handlers or support for several struct ifnet callbacks - MII bus locking - interrupt handling - partial autoconf handling currently, only axen(4) is converted. Index: distrib/sets/lists/modules/mi =================================================================== RCS file: /cvsroot/src/distrib/sets/lists/modules/mi,v retrieving revision 1.122 diff -p -u -r1.122 mi --- distrib/sets/lists/modules/mi 20 Jun 2019 03:31:55 -0000 1.122 +++ distrib/sets/lists/modules/mi 29 Jul 2019 07:43:54 -0000 @@ -440,6 +440,8 @@ ./@MODULEDIR@/umap/umap.kmod base-kernel-modules kmod ./@MODULEDIR@/union base-kernel-modules kmod ./@MODULEDIR@/union/union.kmod base-kernel-modules kmod +./@MODULEDIR@/usb_net base-kernel-modules kmod +./@MODULEDIR@/usb_net/usb_net.kmod base-kernel-modules kmod ./@MODULEDIR@/usbverbose base-kernel-modules kmod ./@MODULEDIR@/usbverbose/usbverbose.kmod base-kernel-modules kmod ./@MODULEDIR@/v7fs base-kernel-modules kmod Index: sys/dev/usb/files.usb =================================================================== RCS file: /cvsroot/src/sys/dev/usb/files.usb,v retrieving revision 1.156 diff -p -u -r1.156 files.usb --- sys/dev/usb/files.usb 8 May 2019 13:40:19 -0000 1.156 +++ sys/dev/usb/files.usb 29 Jul 2019 07:43:54 -0000 @@ -103,6 +103,9 @@ file dev/usb/usb_quirks.c usb define usb_dma: usb file dev/usb/usb_mem.c usb_dma needs-flag +define usb_net: usb +file dev/usb/usb_net.c usb_net + file dev/usb/usb_verbose.c usbverbose & usb # Hub driver @@ -365,7 +368,7 @@ attach axe at usbdevif file dev/usb/if_axe.c axe # ASIX AX88178a and AX88179 -device axen: arp, ether, ifnet, mii +device axen: arp, ether, ifnet, mii, usb_net attach axen at usbdevif file dev/usb/if_axen.c axen Index: sys/dev/usb/if_axen.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/if_axen.c,v retrieving revision 1.50 diff -p -u -r1.50 if_axen.c --- sys/dev/usb/if_axen.c 15 Jul 2019 03:14:22 -0000 1.50 +++ sys/dev/usb/if_axen.c 29 Jul 2019 07:43:54 -0000 @@ -40,8 +40,6 @@ __KERNEL_RCSID(0, "$NetBSD: if_axen.c,v #include #include -#include - #include #include #include @@ -60,6 +58,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_axen.c,v #include #include #include +#include #include @@ -72,70 +71,17 @@ int axendebug = 0; #define DPRINTFN(n, x) #endif -struct axen_softc; - -struct axen_chain { - struct axen_softc *axen_sc; - struct usbd_xfer *axen_xfer; - uint8_t *axen_buf; -}; - -struct axen_cdata { - struct axen_chain axen_tx_chain[AXEN_TX_LIST_CNT]; - struct axen_chain axen_rx_chain[AXEN_RX_LIST_CNT]; - int axen_tx_prod; - int axen_tx_cnt; -}; - struct axen_softc { - device_t axen_dev; - struct ethercom axen_ec; - struct mii_data axen_mii; - krndsource_t rnd_source; - struct usbd_device * axen_udev; - struct usbd_interface * axen_iface; - - uint16_t axen_vendor; - uint16_t axen_product; - uint16_t axen_flags; - uint16_t axen_timer; - - int axen_ed[AXEN_ENDPT_MAX]; - struct usbd_pipe *axen_ep[AXEN_ENDPT_MAX]; - int axen_if_flags; - struct axen_cdata axen_cdata; - struct callout axen_stat_ch; - - int axen_refcnt; - bool axen_dying; - bool axen_stopping; + struct usb_net axen_un; bool axen_attached; - - struct usb_task axen_tick_task; - - kmutex_t axen_lock; - kmutex_t axen_mii_lock; - kmutex_t axen_rxlock; - kmutex_t axen_txlock; - kcondvar_t axen_detachcv; - - int axen_link; - - int axen_phyno; - struct timeval axen_rx_notice; - struct timeval axen_tx_notice; - u_int axen_rx_bufsz; - u_int axen_tx_bufsz; int axen_rev; - -#define sc_if axen_ec.ec_if }; -#define GET_MII(sc) (&(sc)->axen_mii) -#define GET_IFP(sc) (&(sc)->sc_if) +#define GET_MII(sc) (&(sc)->axen_un.un_mii) +#define GET_IFP(sc) (&(sc)->axen_un.un_ec.ec_if) struct axen_type { - struct usb_devno axen_dev; + struct usb_devno axen_devno; uint16_t axen_flags; #define AX178A 0x0001 /* AX88178a */ #define AX179 0x0002 /* AX88179 */ @@ -157,93 +103,38 @@ static const struct axen_type axen_devs[ static int axen_match(device_t, cfdata_t, void *); static void axen_attach(device_t, device_t, void *); static int axen_detach(device_t, int); -static int axen_activate(device_t, devact_t); CFATTACH_DECL_NEW(axen, sizeof(struct axen_softc), - axen_match, axen_attach, axen_detach, axen_activate); + axen_match, axen_attach, axen_detach, usb_net_activate); -static int axen_tx_list_init(struct axen_softc *); -static int axen_rx_list_init(struct axen_softc *); -static struct mbuf *axen_newbuf(void); -static int axen_encap(struct axen_softc *, struct mbuf *, int); -static void axen_rxeof(struct usbd_xfer *, void *, usbd_status); -static int axen_csum_flags_rx(struct ifnet *, uint32_t); -static void axen_txeof(struct usbd_xfer *, void *, usbd_status); -static void axen_tick(void *); -static void axen_tick_task(void *); -static void axen_start(struct ifnet *); -static void axen_start_locked(struct ifnet *); -static int axen_ioctl(struct ifnet *, u_long, void *); +static unsigned axen_tx_prepare(struct usb_net *, struct mbuf *, + struct usb_net_chain *); static int axen_init(struct ifnet *); static void axen_stop(struct ifnet *, int); static void axen_stop_locked(struct ifnet *, int); -static void axen_watchdog(struct ifnet *); -static int axen_miibus_readreg(device_t, int, int, uint16_t *); -static int axen_miibus_writereg(device_t, int, int, uint16_t); static void axen_miibus_statchg(struct ifnet *); static int axen_cmd(struct axen_softc *, int, int, int, void *); static int axen_ifmedia_upd(struct ifnet *); -static void axen_ifmedia_sts(struct ifnet *, struct ifmediareq *); static void axen_reset(struct axen_softc *); static int axen_get_eaddr(struct axen_softc *, void *); -static void axen_iff(struct axen_softc *); +static void axen_stop_cb(struct ifnet *, int); static void axen_ax88179_init(struct axen_softc *); -static void axen_setcoe(struct axen_softc *); - -/* - * Access functions for MII. Take the MII lock to call axen_cmd(). - * Two forms: softc lock currently held or not. - */ -static void -axen_lock_mii(struct axen_softc *sc) -{ - - mutex_enter(&sc->axen_lock); - sc->axen_refcnt++; - mutex_exit(&sc->axen_lock); - - mutex_enter(&sc->axen_mii_lock); -} - -static void -axen_lock_mii_sc_locked(struct axen_softc *sc) -{ - KASSERT(mutex_owned(&sc->axen_lock)); - - sc->axen_refcnt++; - mutex_enter(&sc->axen_mii_lock); -} -static void -axen_unlock_mii(struct axen_softc *sc) -{ - - mutex_exit(&sc->axen_mii_lock); - mutex_enter(&sc->axen_lock); - if (--sc->axen_refcnt < 0) - cv_broadcast(&sc->axen_detachcv); - mutex_exit(&sc->axen_lock); -} - -static void -axen_unlock_mii_sc_locked(struct axen_softc *sc) -{ - KASSERT(mutex_owned(&sc->axen_lock)); - - mutex_exit(&sc->axen_mii_lock); - if (--sc->axen_refcnt < 0) - cv_broadcast(&sc->axen_detachcv); -} +static usbd_status axen_mii_read_reg(struct usb_net *, int, int, uint16_t *); +static usbd_status axen_mii_write_reg(struct usb_net *, int, int, uint16_t); +static void axen_rxeof_loop(struct usb_net *, struct usbd_xfer *, + struct usb_net_chain *, uint32_t); static int axen_cmd(struct axen_softc *sc, int cmd, int index, int val, void *buf) { + struct usb_net * const un = &sc->axen_un; usb_device_request_t req; usbd_status err; - KASSERT(mutex_owned(&sc->axen_mii_lock)); + KASSERT(mutex_owned(&un->un_miilock)); - if (sc->axen_dying) + if (un->un_dying) return 0; if (AXEN_CMD_DIR(cmd)) @@ -255,7 +146,7 @@ axen_cmd(struct axen_softc *sc, int cmd, USETW(req.wIndex, index); USETW(req.wLength, AXEN_CMD_LEN(cmd)); - err = usbd_do_request(sc->axen_udev, &req, buf); + err = usbd_do_request(un->un_udev, &req, buf); DPRINTFN(5, ("axen_cmd: cmd 0x%04x val 0x%04x len %d\n", cmd, val, AXEN_CMD_LEN(cmd))); @@ -267,93 +158,55 @@ axen_cmd(struct axen_softc *sc, int cmd, return 0; } -static int -axen_miibus_readreg(device_t dev, int phy, int reg, uint16_t *val) +static usbd_status +axen_mii_read_reg(struct usb_net *un, int reg, int phy, uint16_t *val) { - struct axen_softc * const sc = device_private(dev); - usbd_status err; + struct axen_softc * const sc = un->un_sc; uint16_t data; + usbd_status err = axen_cmd(sc, AXEN_CMD_MII_READ_REG, reg, phy, &data); - mutex_enter(&sc->axen_lock); - if (sc->axen_dying || sc->axen_phyno != phy) { - mutex_exit(&sc->axen_lock); - return -1; - } - mutex_exit(&sc->axen_lock); + if (!err) { + *val = le16toh(data); - axen_lock_mii(sc); - err = axen_cmd(sc, AXEN_CMD_MII_READ_REG, reg, phy, &data); - axen_unlock_mii(sc); - - if (err) { - aprint_error_dev(sc->axen_dev, "read PHY failed: %d\n", err); - return err; - } - - *val = le16toh(data); - DPRINTFN(2,("axen_miibus_readreg: phy 0x%x reg 0x%x val 0x%hx\n", - phy, reg, *val)); - - if (reg == MII_BMSR) { - *val &= ~BMSR_EXTCAP; + if (reg == MII_BMSR) + *val &= ~BMSR_EXTCAP; } - return 0; + return err; } -static int -axen_miibus_writereg(device_t dev, int phy, int reg, uint16_t val) +static usbd_status +axen_mii_write_reg(struct usb_net *un, int reg, int phy, uint16_t val) { - struct axen_softc * const sc = device_private(dev); - usbd_status err; - uint16_t uval; - - mutex_enter(&sc->axen_lock); - if (sc->axen_dying || sc->axen_phyno != phy) { - mutex_exit(&sc->axen_lock); - return -1; - } - mutex_exit(&sc->axen_lock); - - uval = htole16(val); + struct axen_softc * const sc = un->un_sc; + uint16_t uval = htole16(val); - axen_lock_mii(sc); - err = axen_cmd(sc, AXEN_CMD_MII_WRITE_REG, reg, phy, &uval); - axen_unlock_mii(sc); - - DPRINTFN(2, ("axen_miibus_writereg: phy 0x%x reg 0x%x val 0x%04hx\n", - phy, reg, val)); - - if (err) { - aprint_error_dev(sc->axen_dev, "write PHY failed: %d\n", err); - return err; - } - - return 0; + return axen_cmd(sc, AXEN_CMD_MII_WRITE_REG, reg, phy, &uval); } static void axen_miibus_statchg(struct ifnet *ifp) { - struct axen_softc * const sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); + struct usb_net * const un = ifp->if_softc; + struct axen_softc * const sc = un->un_sc; + struct mii_data * const mii = usb_net_mii(un); int err; uint16_t val; uint16_t wval; - if (sc->axen_dying) + if (un->un_dying) return; - sc->axen_link = 0; + un->un_link = false; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: - sc->axen_link++; + un->un_link = true; break; case IFM_1000_T: - sc->axen_link++; + un->un_link = true; break; default: break; @@ -361,7 +214,7 @@ axen_miibus_statchg(struct ifnet *ifp) } /* Lost link, do nothing. */ - if (sc->axen_link == 0) + if (!un->un_link) return; val = 0; @@ -384,11 +237,11 @@ axen_miibus_statchg(struct ifnet *ifp) DPRINTF(("%s: val=0x%x\n", __func__, val)); wval = htole16(val); - axen_lock_mii(sc); + usb_net_lock_mii(un); err = axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval); - axen_unlock_mii(sc); + usb_net_unlock_mii(un); if (err) { - aprint_error_dev(sc->axen_dev, "media change failed\n"); + aprint_error_dev(un->un_dev, "media change failed\n"); return; } } @@ -399,11 +252,11 @@ axen_miibus_statchg(struct ifnet *ifp) static int axen_ifmedia_upd(struct ifnet *ifp) { - struct axen_softc * const sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); - int rc; + struct usb_net * const un = ifp->if_softc; + struct axen_softc * const sc = un->un_sc; + struct mii_data * const mii = GET_MII(sc); - sc->axen_link = 0; + un->un_link = false; if (mii->mii_instance) { struct mii_softc *miisc; @@ -412,30 +265,15 @@ axen_ifmedia_upd(struct ifnet *ifp) mii_phy_reset(miisc); } - if ((rc = mii_mediachg(mii)) == ENXIO) - return 0; - return rc; + return ether_mediachange(ifp); } -/* - * Report current media status. - */ static void -axen_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +axen_setmulti_locked(struct usb_net *un) { - struct axen_softc * const sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); - - mii_pollstat(mii); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; -} - -static void -axen_iff_locked(struct axen_softc *sc) -{ - struct ifnet *ifp = GET_IFP(sc); - struct ethercom *ec = &sc->axen_ec; + struct axen_softc * const sc = un->un_sc; + struct ifnet * const ifp = usb_net_ifp(un); + struct ethercom *ec = &un->un_ec; struct ether_multi *enm; struct ether_multistep step; uint32_t h = 0; @@ -443,10 +281,10 @@ axen_iff_locked(struct axen_softc *sc) uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; uint16_t wval; - if (sc->axen_dying) + if (un->un_dying) return; - KASSERT(mutex_owned(&sc->axen_mii_lock)); + KASSERT(mutex_owned(&un->un_miilock)); rxmode = 0; @@ -457,7 +295,7 @@ axen_iff_locked(struct axen_softc *sc) AXEN_RXCTL_ACPT_MCAST); if (ifp->if_flags & IFF_PROMISC) { - DPRINTF(("%s: promisc\n", device_xname(sc->axen_dev))); + DPRINTF(("%s: promisc\n", device_xname(un->un_dev))); rxmode |= AXEN_RXCTL_PROMISC; allmulti: ETHER_LOCK(ec); @@ -468,7 +306,7 @@ allmulti: } else { /* now program new ones */ DPRINTF(("%s: initializing hash table\n", - device_xname(sc->axen_dev))); + device_xname(un->un_dev))); ETHER_LOCK(ec); ec->ec_flags &= ~ETHER_F_ALLMULTI; @@ -477,7 +315,7 @@ allmulti: if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { DPRINTF(("%s: allmulti\n", - device_xname(sc->axen_dev))); + device_xname(un->un_dev))); memset(hashtbl, 0, sizeof(hashtbl)); ETHER_UNLOCK(ec); goto allmulti; @@ -486,7 +324,7 @@ allmulti: ETHER_ADDR_LEN) >> 26; hashtbl[h / 8] |= 1 << (h % 8); DPRINTF(("%s: %s added\n", - device_xname(sc->axen_dev), + device_xname(un->un_dev), ether_sprintf(enm->enm_addrlo))); ETHER_NEXT_MULTI(step, enm); } @@ -500,20 +338,20 @@ allmulti: } static void -axen_iff(struct axen_softc *sc) +axen_setmulti_cb(struct usb_net *un) { - - axen_lock_mii(sc); - axen_iff_locked(sc); - axen_unlock_mii(sc); + usb_net_lock_mii(un); + axen_setmulti_locked(un); + usb_net_unlock_mii(un); } static void axen_reset(struct axen_softc *sc) { + struct usb_net * const un = &sc->axen_un; - KASSERT(mutex_owned(&sc->axen_lock)); - if (sc->axen_dying) + KASSERT(mutex_owned(&un->un_lock)); + if (un->un_dying) return; /* XXX What to reset? */ @@ -521,11 +359,6 @@ axen_reset(struct axen_softc *sc) DELAY(1000); } -#define AXEN_GPIO_WRITE(x, y) do { \ - axen_cmd(sc, AXEN_CMD_WRITE_GPIO, 0, (x), NULL); \ - usbd_delay_ms(sc->axen_udev, (y)); \ -} while (/*CONSTCOND*/0) - static int axen_get_eaddr(struct axen_softc *sc, void *addr) { @@ -533,6 +366,7 @@ axen_get_eaddr(struct axen_softc *sc, vo return axen_cmd(sc, AXEN_CMD_MAC_READ_ETHER, 6, AXEN_CMD_MAC_NODE_ID, addr); #else + struct usb_net * const un = &sc->axen_un; int i, retry; uint8_t eeprom[20]; uint16_t csum; @@ -551,7 +385,7 @@ axen_get_eaddr(struct axen_softc *sc, vo retry = 3; do { buf = htole16(AXEN_EEPROM_READ); - usbd_delay_ms(sc->axen_udev, 10); + usbd_delay_ms(un->un_udev, 10); axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_MAC_EEPROM_CMD, &buf); retry--; @@ -584,12 +418,13 @@ axen_get_eaddr(struct axen_softc *sc, vo static void axen_ax88179_init(struct axen_softc *sc) { + struct usb_net * const un = &sc->axen_un; struct axen_qctrl qctrl; uint16_t ctl, temp; uint16_t wval; uint8_t val; - axen_lock_mii(sc); + usb_net_lock_mii(un); /* XXX: ? */ axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_UNK_05, &val); @@ -612,12 +447,12 @@ axen_ax88179_init(struct axen_softc *sc) wval = htole16(AXEN_PHYPWR_RSTCTL_IPRL); axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_PHYPWR_RSTCTL, &wval); - usbd_delay_ms(sc->axen_udev, 200); + usbd_delay_ms(un->un_udev, 200); /* set clock mode */ val = AXEN_PHYCLK_ACS | AXEN_PHYCLK_BCS; axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PHYCLK, &val); - usbd_delay_ms(sc->axen_udev, 100); + usbd_delay_ms(un->un_udev, 100); /* set monitor mode (disable) */ val = AXEN_MONITOR_NONE; @@ -633,15 +468,15 @@ axen_ax88179_init(struct axen_softc *sc) axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PHYCLK, &val); val = AXEN_PHYCLK_ULR; axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PHYCLK, &val); - usbd_delay_ms(sc->axen_udev, 100); + usbd_delay_ms(un->un_udev, 100); axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_PHYPWR_RSTCTL, &wval); ctl = le16toh(wval); ctl |= AXEN_PHYPWR_RSTCTL_AUTODETACH; wval = htole16(ctl); axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_PHYPWR_RSTCTL, &wval); - usbd_delay_ms(sc->axen_udev, 200); - aprint_error_dev(sc->axen_dev, "enable auto detach (0x%04x)\n", + usbd_delay_ms(un->un_udev, 200); + aprint_error_dev(un->un_dev, "enable auto detach (0x%04x)\n", ctl); } @@ -673,9 +508,9 @@ axen_ax88179_init(struct axen_softc *sc) qctrl.ifg = 0xff; break; default: - aprint_error_dev(sc->axen_dev, "unknown uplink bus:0x%02x\n", + aprint_error_dev(un->un_dev, "unknown uplink bus:0x%02x\n", val); - axen_unlock_mii(sc); + usb_net_unlock_mii(un); return; } axen_cmd(sc, AXEN_CMD_MAC_SET_RXSR, 5, AXEN_RX_BULKIN_QCTRL, &qctrl); @@ -710,42 +545,42 @@ axen_ax88179_init(struct axen_softc *sc) wval = htole16(ctl); DPRINTF(("axen: set to medium mode: 0x%04x\n", ctl)); axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval); - usbd_delay_ms(sc->axen_udev, 100); + usbd_delay_ms(un->un_udev, 100); axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MEDIUM_STATUS, &wval); DPRINTF(("axen: current medium mode: 0x%04x\n", le16toh(wval))); - axen_unlock_mii(sc); + usb_net_unlock_mii(un); #if 0 /* XXX: TBD.... */ #define GMII_LED_ACTIVE 0x1a #define GMII_PHY_PAGE_SEL 0x1e #define GMII_PHY_PAGE_SEL 0x1f #define GMII_PAGE_EXT 0x0007 - axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, GMII_PHY_PAGE_SEL, + usb_net_miibus_writereg(un->un_dev, un->un_phyno, GMII_PHY_PAGE_SEL, GMII_PAGE_EXT); - axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, GMII_PHY_PAGE, + usb_net_miibus_writereg(un->un_dev, un->un_phyno, GMII_PHY_PAGE, 0x002c); #endif #if 1 /* XXX: phy hack ? */ - axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x1F, 0x0005); - axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x0C, 0x0000); - axen_miibus_readreg(sc->axen_dev, sc->axen_phyno, 0x0001, &wval); - axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x01, - wval | 0x0080); - axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x1F, 0x0000); + usb_net_miibus_writereg(un->un_dev, un->un_phyno, 0x1F, 0x0005); + usb_net_miibus_writereg(un->un_dev, un->un_phyno, 0x0C, 0x0000); + usb_net_miibus_readreg(un->un_dev, un->un_phyno, 0x0001, &wval); + usb_net_miibus_writereg(un->un_dev, un->un_phyno, 0x01, wval | 0x0080); + usb_net_miibus_writereg(un->un_dev, un->un_phyno, 0x1F, 0x0000); #endif } static void -axen_setcoe(struct axen_softc *sc) +axen_setoe_locked(struct usb_net *un) { - struct ifnet *ifp = GET_IFP(sc); + struct axen_softc * const sc = un->un_sc; + struct ifnet * const ifp = usb_net_ifp(un); uint64_t enabled = ifp->if_capenable; uint8_t val; - KASSERT(mutex_owned(&sc->axen_mii_lock)); + KASSERT(mutex_owned(&un->un_miilock)); val = AXEN_RXCOE_OFF; if (enabled & IFCAP_CSUM_IPv4_Rx) @@ -774,6 +609,15 @@ axen_setcoe(struct axen_softc *sc) axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_TX_COE, &val); } +static void +axen_setoe_cb(struct usb_net *un) +{ + + mutex_enter(&un->un_lock); + axen_setoe_locked(un); + mutex_exit(&un->un_lock); +} + static int axen_match(device_t parent, cfdata_t match, void *aux) { @@ -787,6 +631,7 @@ static void axen_attach(device_t parent, device_t self, void *aux) { struct axen_softc * const sc = device_private(self); + struct usb_net * const un = &sc->axen_un; struct usb_attach_arg *uaa = aux; struct usbd_device *dev = uaa->uaa_device; usbd_status err; @@ -797,14 +642,28 @@ axen_attach(device_t parent, device_t se char *devinfop; const char *devname = device_xname(self); struct ifnet *ifp; + uint16_t axen_flags; int i; + /* Switch to usb_net for device_private() */ + self->dv_private = un; + aprint_naive("\n"); aprint_normal("\n"); - sc->axen_dev = self; - sc->axen_udev = dev; - + un->un_dev = self; + un->un_udev = dev; + un->un_sc = sc; + un->un_setmulti_cb = axen_setmulti_cb; + un->un_setoe_cb = axen_setoe_cb; + un->un_stop_cb = axen_stop_cb; + un->un_read_reg_cb = axen_mii_read_reg; + un->un_write_reg_cb = axen_mii_write_reg; + un->un_tx_prepare_cb = axen_tx_prepare; + un->un_rx_loop_cb = axen_rxeof_loop; + un->un_rx_xfer_flags = USBD_SHORT_XFER_OK; + un->un_tx_xfer_flags = USBD_FORCE_SHORT_XFER; + devinfop = usbd_devinfo_alloc(dev, 0); aprint_normal_dev(self, "%s\n", devinfop); usbd_devinfo_free(devinfop); @@ -816,107 +675,94 @@ axen_attach(device_t parent, device_t se return; } - sc->axen_flags = axen_lookup(uaa->uaa_vendor, uaa->uaa_product)->axen_flags; - - usb_init_task(&sc->axen_tick_task, axen_tick_task, sc, USB_TASKQ_MPSAFE); + axen_flags = axen_lookup(uaa->uaa_vendor, uaa->uaa_product)->axen_flags; - err = usbd_device2interface_handle(dev, AXEN_IFACE_IDX,&sc->axen_iface); + err = usbd_device2interface_handle(dev, AXEN_IFACE_IDX, &un->un_iface); if (err) { aprint_error_dev(self, "getting interface handle failed\n"); return; } - sc->axen_product = uaa->uaa_product; - sc->axen_vendor = uaa->uaa_vendor; - - id = usbd_get_interface_descriptor(sc->axen_iface); - /* decide on what our bufsize will be */ - switch (sc->axen_udev->ud_speed) { + switch (dev->ud_speed) { case USB_SPEED_SUPER: - sc->axen_rx_bufsz = AXEN_BUFSZ_SS * 1024; + un->un_cdata.uncd_rx_bufsz = AXEN_BUFSZ_SS * 1024; break; case USB_SPEED_HIGH: - sc->axen_rx_bufsz = AXEN_BUFSZ_HS * 1024; + un->un_cdata.uncd_rx_bufsz = AXEN_BUFSZ_HS * 1024; break; default: - sc->axen_rx_bufsz = AXEN_BUFSZ_LS * 1024; + un->un_cdata.uncd_rx_bufsz = AXEN_BUFSZ_LS * 1024; break; } - sc->axen_tx_bufsz = IP_MAXPACKET + + un->un_cdata.uncd_tx_bufsz = IP_MAXPACKET + ETHER_HDR_LEN + ETHER_CRC_LEN + ETHER_VLAN_ENCAP_LEN + sizeof(struct axen_sframe_hdr); /* Find endpoints. */ + id = usbd_get_interface_descriptor(un->un_iface); for (i = 0; i < id->bNumEndpoints; i++) { - ed = usbd_interface2endpoint_descriptor(sc->axen_iface, i); + ed = usbd_interface2endpoint_descriptor(un->un_iface, i); if (!ed) { aprint_error_dev(self, "couldn't get ep %d\n", i); return; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { - sc->axen_ed[AXEN_ENDPT_RX] = ed->bEndpointAddress; + un->un_ed[USB_NET_ENDPT_RX] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { - sc->axen_ed[AXEN_ENDPT_TX] = ed->bEndpointAddress; + un->un_ed[USB_NET_ENDPT_TX] = ed->bEndpointAddress; +#if 0 /* not used yet */ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { - sc->axen_ed[AXEN_ENDPT_INTR] = ed->bEndpointAddress; + un->un_ed[USB_NET_ENDPT_INTR] = ed->bEndpointAddress; +#endif } } /* Set these up now for axen_cmd(). */ - mutex_init(&sc->axen_mii_lock, MUTEX_DEFAULT, IPL_NONE); - mutex_init(&sc->axen_txlock, MUTEX_DEFAULT, IPL_SOFTUSB); - mutex_init(&sc->axen_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB); - mutex_init(&sc->axen_lock, MUTEX_DEFAULT, IPL_NONE); - cv_init(&sc->axen_detachcv, "axendet"); + usb_net_attach(un, "axendet", AXEN_RX_LIST_CNT, AXEN_TX_LIST_CNT); - sc->axen_phyno = AXEN_PHY_ID; - DPRINTF(("%s: phyno %d\n", device_xname(self), sc->axen_phyno)); + un->un_phyno = AXEN_PHY_ID; + DPRINTF(("%s: phyno %d\n", device_xname(self), un->un_phyno)); /* Get station address. */ - axen_lock_mii(sc); + usb_net_lock_mii(un); if (axen_get_eaddr(sc, &eaddr)) { - axen_unlock_mii(sc); + usb_net_unlock_mii(un); printf("EEPROM checksum error\n"); - cv_destroy(&sc->axen_detachcv); - mutex_destroy(&sc->axen_lock); - mutex_destroy(&sc->axen_rxlock); - mutex_destroy(&sc->axen_txlock); - mutex_destroy(&sc->axen_mii_lock); + usb_net_detach(un); return; } - axen_unlock_mii(sc); + usb_net_unlock_mii(un); axen_ax88179_init(sc); - /* - * An ASIX chip was detected. Inform the world. - */ - if (sc->axen_flags & AX178A) + /* An ASIX chip was detected. Inform the world. */ + if (axen_flags & AX178A) aprint_normal_dev(self, "AX88178a\n"); - else if (sc->axen_flags & AX179) + else if (axen_flags & AX179) aprint_normal_dev(self, "AX88179\n"); + else + aprint_normal_dev(self, "(unknown)\n"); aprint_normal_dev(self, "Ethernet address %s\n", ether_sprintf(eaddr)); /* Initialize interface info. */ - - ifp = &sc->sc_if; - ifp->if_softc = sc; + ifp = GET_IFP(sc); + ifp->if_softc = &sc->axen_un; strlcpy(ifp->if_xname, devname, IFNAMSIZ); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_extflags = IFEF_MPSAFE; - ifp->if_ioctl = axen_ioctl; - ifp->if_start = axen_start; + ifp->if_ioctl = usb_net_ioctl; + ifp->if_start = usb_net_start; ifp->if_init = axen_init; ifp->if_stop = axen_stop; IFQ_SET_READY(&ifp->if_snd); - sc->axen_ec.ec_capabilities = ETHERCAP_VLAN_MTU; + un->un_ec.ec_capabilities = ETHERCAP_VLAN_MTU; /* Adapter does not support TSOv6 (They call it LSOv2). */ ifp->if_capabilities |= IFCAP_TSOv4 | @@ -927,15 +773,15 @@ axen_attach(device_t parent, device_t se IFCAP_CSUM_UDPv6_Rx | IFCAP_CSUM_UDPv6_Tx; /* Initialize MII/media info. */ - mii = &sc->axen_mii; + mii = &un->un_mii; mii->mii_ifp = ifp; - mii->mii_readreg = axen_miibus_readreg; - mii->mii_writereg = axen_miibus_writereg; + mii->mii_readreg = usb_net_miibus_readreg; + mii->mii_writereg = usb_net_miibus_writereg; mii->mii_statchg = axen_miibus_statchg; mii->mii_flags = MIIF_AUTOTSLEEP; - sc->axen_ec.ec_mii = mii; - ifmedia_init(&mii->mii_media, 0, axen_ifmedia_upd, axen_ifmedia_sts); + un->un_ec.ec_mii = mii; + ifmedia_init(&mii->mii_media, 0, axen_ifmedia_upd, ether_mediastatus); mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); if (LIST_FIRST(&mii->mii_phys) == NULL) { @@ -947,15 +793,10 @@ axen_attach(device_t parent, device_t se /* Attach the interface. */ if_attach(ifp); ether_ifattach(ifp, eaddr); - rnd_attach_source(&sc->rnd_source, device_xname(sc->axen_dev), - RND_TYPE_NET, RND_FLAG_DEFAULT); - - callout_init(&sc->axen_stat_ch, CALLOUT_MPSAFE); - callout_setfunc(&sc->axen_stat_ch, axen_tick, sc); sc->axen_attached = true; - usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->axen_udev,sc->axen_dev); + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, un->un_udev, un->un_dev); if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); @@ -964,14 +805,15 @@ axen_attach(device_t parent, device_t se static int axen_detach(device_t self, int flags) { - struct axen_softc * const sc = device_private(self); - struct ifnet *ifp = GET_IFP(sc); + struct usb_net * const un = device_private(self); + struct axen_softc * const sc = un->un_sc; + struct ifnet * const ifp = usb_net_ifp(un); + + mutex_enter(&un->un_lock); + un->un_dying = true; + mutex_exit(&un->un_lock); - mutex_enter(&sc->axen_lock); - sc->axen_dying = true; - mutex_exit(&sc->axen_lock); - - DPRINTFN(2,("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); + DPRINTFN(2,("%s: %s: enter\n", device_xname(un->un_dev), __func__)); /* Detached before attached finished, so just bail out. */ if (!sc->axen_attached) @@ -979,199 +821,104 @@ axen_detach(device_t self, int flags) pmf_device_deregister(self); - callout_halt(&sc->axen_stat_ch, NULL); - usb_rem_task_wait(sc->axen_udev, &sc->axen_tick_task, - USB_TASKQ_DRIVER, NULL); - if (ifp->if_flags & IFF_RUNNING) { IFNET_LOCK(ifp); axen_stop(ifp, 1); IFNET_UNLOCK(ifp); } - mutex_enter(&sc->axen_lock); - sc->axen_refcnt--; - while (sc->axen_refcnt > 0) { + mutex_enter(&un->un_lock); + un->un_refcnt--; + while (un->un_refcnt > 0) { /* Wait for processes to go away */ - cv_wait(&sc->axen_detachcv, &sc->axen_lock); + cv_wait(&un->un_detachcv, &un->un_lock); } + mutex_exit(&un->un_lock); #ifdef DIAGNOSTIC - if (sc->axen_ep[AXEN_ENDPT_TX] != NULL || - sc->axen_ep[AXEN_ENDPT_RX] != NULL || - sc->axen_ep[AXEN_ENDPT_INTR] != NULL) + if (un->un_ep[USB_NET_ENDPT_TX] != NULL || + un->un_ep[USB_NET_ENDPT_RX] != NULL || + un->un_ep[USB_NET_ENDPT_INTR] != NULL) aprint_debug_dev(self, "detach has active endpoints\n"); #endif - mutex_exit(&sc->axen_lock); + usb_net_detach(un); - callout_destroy(&sc->axen_stat_ch); - rnd_detach_source(&sc->rnd_source); - mii_detach(&sc->axen_mii, MII_PHY_ANY, MII_OFFSET_ANY); - ifmedia_delete_instance(&sc->axen_mii.mii_media, IFM_INST_ANY); + mii_detach(&un->un_mii, MII_PHY_ANY, MII_OFFSET_ANY); + ifmedia_delete_instance(&un->un_mii.mii_media, IFM_INST_ANY); ether_ifdetach(ifp); if_detach(ifp); sc->axen_attached = false; - usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->axen_udev,sc->axen_dev); - - cv_destroy(&sc->axen_detachcv); - mutex_destroy(&sc->axen_lock); - mutex_destroy(&sc->axen_rxlock); - mutex_destroy(&sc->axen_txlock); - mutex_destroy(&sc->axen_mii_lock); + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, un->un_udev, un->un_dev); return 0; } static int -axen_activate(device_t self, devact_t act) +axen_csum_flags_rx(struct ifnet *ifp, uint32_t pkt_hdr) { - struct axen_softc * const sc = device_private(self); - struct ifnet *ifp = GET_IFP(sc); - - DPRINTFN(2,("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); - - switch (act) { - case DVACT_DEACTIVATE: - if_deactivate(ifp); - - mutex_enter(&sc->axen_lock); - sc->axen_dying = true; - mutex_exit(&sc->axen_lock); - - mutex_enter(&sc->axen_rxlock); - mutex_enter(&sc->axen_txlock); - sc->axen_stopping = true; - mutex_exit(&sc->axen_txlock); - mutex_exit(&sc->axen_rxlock); + int enabled_flags = ifp->if_csum_flags_rx; + int csum_flags = 0; + int l3_type, l4_type; + if (enabled_flags == 0) return 0; - default: - return EOPNOTSUPP; - } -} - -static struct mbuf * -axen_newbuf(void) -{ - struct mbuf *m; - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return NULL; - - MCLGET(m, M_DONTWAIT); - if (!(m->m_flags & M_EXT)) { - m_freem(m); - return NULL; - } - - m->m_len = m->m_pkthdr.len = MCLBYTES; - m_adj(m, ETHER_ALIGN); - - return m; -} -static int -axen_rx_list_init(struct axen_softc *sc) -{ - struct axen_cdata *cd; - struct axen_chain *c; - int i; - - DPRINTF(("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); - - cd = &sc->axen_cdata; - for (i = 0; i < AXEN_RX_LIST_CNT; i++) { - c = &cd->axen_rx_chain[i]; - c->axen_sc = sc; - if (c->axen_xfer == NULL) { - int err = usbd_create_xfer(sc->axen_ep[AXEN_ENDPT_RX], - sc->axen_rx_bufsz, 0, 0, &c->axen_xfer); - if (err) - return err; - c->axen_buf = usbd_get_buffer(c->axen_xfer); - } - } - - return 0; -} + l3_type = (pkt_hdr & AXEN_RXHDR_L3_TYPE_MASK) >> + AXEN_RXHDR_L3_TYPE_OFFSET; -static int -axen_tx_list_init(struct axen_softc *sc) -{ - struct axen_cdata *cd; - struct axen_chain *c; - int i; + if (l3_type == AXEN_RXHDR_L3_TYPE_IPV4) + csum_flags |= M_CSUM_IPv4; - DPRINTF(("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); + l4_type = (pkt_hdr & AXEN_RXHDR_L4_TYPE_MASK) >> + AXEN_RXHDR_L4_TYPE_OFFSET; - cd = &sc->axen_cdata; - for (i = 0; i < AXEN_TX_LIST_CNT; i++) { - c = &cd->axen_tx_chain[i]; - c->axen_sc = sc; - if (c->axen_xfer == NULL) { - int err = usbd_create_xfer(sc->axen_ep[AXEN_ENDPT_TX], - sc->axen_tx_bufsz, USBD_FORCE_SHORT_XFER, 0, - &c->axen_xfer); - if (err) - return err; - c->axen_buf = usbd_get_buffer(c->axen_xfer); - } + switch (l4_type) { + case AXEN_RXHDR_L4_TYPE_TCP: + if (l3_type == AXEN_RXHDR_L3_TYPE_IPV4) + csum_flags |= M_CSUM_TCPv4; + else + csum_flags |= M_CSUM_TCPv6; + break; + case AXEN_RXHDR_L4_TYPE_UDP: + if (l3_type == AXEN_RXHDR_L3_TYPE_IPV4) + csum_flags |= M_CSUM_UDPv4; + else + csum_flags |= M_CSUM_UDPv6; + break; + default: + break; } - cd->axen_tx_prod = cd->axen_tx_cnt = 0; + csum_flags &= enabled_flags; + if ((csum_flags & M_CSUM_IPv4) && (pkt_hdr & AXEN_RXHDR_L3CSUM_ERR)) + csum_flags |= M_CSUM_IPv4_BAD; + if ((csum_flags & ~M_CSUM_IPv4) && (pkt_hdr & AXEN_RXHDR_L4CSUM_ERR)) + csum_flags |= M_CSUM_TCP_UDP_BAD; - return 0; + return csum_flags; } -/* - * A frame has been uploaded: pass the resulting mbuf chain up to - * the higher level protocols. - */ static void -axen_rxeof(struct usbd_xfer *xfer, void * priv, usbd_status status) +axen_rxeof_loop(struct usb_net * un, struct usbd_xfer *xfer, + struct usb_net_chain *c, uint32_t total_len) { - struct axen_chain *c = (struct axen_chain *)priv; - struct axen_softc * const sc = c->axen_sc; - struct ifnet *ifp = GET_IFP(sc); - uint8_t *buf = c->axen_buf; - struct mbuf *m; - uint32_t total_len; + struct ifnet *ifp = usb_net_ifp(un); + uint8_t *buf = c->unc_buf; uint32_t rx_hdr, pkt_hdr; uint32_t *hdr_p; uint16_t hdr_offset, pkt_count; size_t pkt_len; size_t temp; - DPRINTFN(10,("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); - - mutex_enter(&sc->axen_rxlock); - - if (sc->axen_dying || sc->axen_stopping || - status == USBD_INVAL || status == USBD_NOT_STARTED || - status == USBD_CANCELLED || !(ifp->if_flags & IFF_RUNNING)) { - mutex_exit(&sc->axen_rxlock); - return; - } - - if (status != USBD_NORMAL_COMPLETION) { - if (usbd_ratecheck(&sc->axen_rx_notice)) - aprint_error_dev(sc->axen_dev, "usb errors on rx: %s\n", - usbd_errstr(status)); - if (status == USBD_STALLED) - usbd_clear_endpoint_stall_async(sc->axen_ep[AXEN_ENDPT_RX]); - goto done; - } - - usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); + KASSERT(mutex_owned(&un->un_rxlock)); if (total_len < sizeof(pkt_hdr)) { - aprint_error_dev(sc->axen_dev, "rxeof: too short transfer\n"); + aprint_error_dev(un->un_dev, "rxeof: too short transfer\n"); ifp->if_ierrors++; - goto done; + return; } /* @@ -1184,21 +931,14 @@ axen_rxeof(struct usbd_xfer *xfer, void hdr_offset = (uint16_t)(rx_hdr >> 16); pkt_count = (uint16_t)(rx_hdr & 0xffff); - if (total_len > sc->axen_rx_bufsz) { - aprint_error_dev(sc->axen_dev, - "rxeof: too large transfer (%u > %u)\n", - total_len, sc->axen_rx_bufsz); - goto done; - } - /* sanity check */ if (hdr_offset > total_len) { - aprint_error_dev(sc->axen_dev, + aprint_error_dev(un->un_dev, "rxeof: invalid hdr offset (%u > %u)\n", hdr_offset, total_len); ifp->if_ierrors++; - usbd_delay_ms(sc->axen_udev, 100); - goto done; + usbd_delay_ms(un->un_udev, 100); + return; } /* point first packet header */ @@ -1213,60 +953,40 @@ axen_rxeof(struct usbd_xfer *xfer, void #define AXEN_MAX_PACKED_PACKET 200 if (pkt_count > AXEN_MAX_PACKED_PACKET) { DPRINTF(("%s: Too many packets (%d) in a transaction, discard.\n", - device_xname(sc->axen_dev), pkt_count)); - goto done; + device_xname(un->un_dev), pkt_count)); + return; } #endif + if (pkt_count) + rnd_add_uint32(&un->un_rndsrc, pkt_count); + do { if ((buf[0] != 0xee) || (buf[1] != 0xee)) { - aprint_error_dev(sc->axen_dev, + aprint_error_dev(un->un_dev, "invalid buffer(pkt#%d), continue\n", pkt_count); ifp->if_ierrors += pkt_count; - goto done; + return; } pkt_hdr = le32toh(*hdr_p); pkt_len = (pkt_hdr >> 16) & 0x1fff; DPRINTFN(10, ("%s: rxeof: packet#%d, pkt_hdr 0x%08x, pkt_len %zu\n", - device_xname(sc->axen_dev), pkt_count, pkt_hdr, pkt_len)); + device_xname(un->un_dev), pkt_count, pkt_hdr, pkt_len)); if (pkt_hdr & (AXEN_RXHDR_CRC_ERR | AXEN_RXHDR_DROP_ERR)) { ifp->if_ierrors++; /* move to next pkt header */ DPRINTF(("%s: %s err (pkt#%d)\n", - device_xname(sc->axen_dev), + device_xname(un->un_dev), (pkt_hdr & AXEN_RXHDR_CRC_ERR) ? "crc" : "drop", pkt_count)); goto nextpkt; } - /* process each packet */ - /* allocate mbuf */ - m = axen_newbuf(); - if (m == NULL) { - ifp->if_ierrors++; - goto nextpkt; - } - - /* skip pseudo header (2byte) */ - m_set_rcvif(m, ifp); - m->m_pkthdr.len = m->m_len = pkt_len - 6; - - m->m_pkthdr.csum_flags = axen_csum_flags_rx(ifp, pkt_hdr); - memcpy(mtod(m, char *), buf + 2, pkt_len - 6); - - mutex_exit(&sc->axen_rxlock); - - /* push the packet up */ - if_percpuq_enqueue((ifp)->if_percpuq, (m)); - - mutex_enter(&sc->axen_rxlock); - if (sc->axen_dying || sc->axen_stopping) { - mutex_exit(&sc->axen_rxlock); - return; - } + usb_net_enqueue(un, buf + 2, pkt_len - 6, + axen_csum_flags_rx(ifp, pkt_hdr)); nextpkt: /* @@ -1279,191 +999,18 @@ nextpkt: hdr_p++; pkt_count--; } while (pkt_count > 0); - -done: - if (sc->axen_dying || sc->axen_stopping) { - mutex_exit(&sc->axen_rxlock); - return; - } - - mutex_exit(&sc->axen_rxlock); - - /* Setup new transfer. */ - usbd_setup_xfer(xfer, c, c->axen_buf, sc->axen_rx_bufsz, - USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, axen_rxeof); - usbd_transfer(xfer); - - DPRINTFN(10,("%s: %s: start rx\n",device_xname(sc->axen_dev),__func__)); } -static int -axen_csum_flags_rx(struct ifnet *ifp, uint32_t pkt_hdr) +static unsigned +axen_tx_prepare(struct usb_net *un, struct mbuf *m, struct usb_net_chain *c) { - int enabled_flags = ifp->if_csum_flags_rx; - int csum_flags = 0; - int l3_type, l4_type; - - if (enabled_flags == 0) - return 0; - - l3_type = (pkt_hdr & AXEN_RXHDR_L3_TYPE_MASK) >> - AXEN_RXHDR_L3_TYPE_OFFSET; - - if (l3_type == AXEN_RXHDR_L3_TYPE_IPV4) - csum_flags |= M_CSUM_IPv4; - - l4_type = (pkt_hdr & AXEN_RXHDR_L4_TYPE_MASK) >> - AXEN_RXHDR_L4_TYPE_OFFSET; - - switch (l4_type) { - case AXEN_RXHDR_L4_TYPE_TCP: - if (l3_type == AXEN_RXHDR_L3_TYPE_IPV4) - csum_flags |= M_CSUM_TCPv4; - else - csum_flags |= M_CSUM_TCPv6; - break; - case AXEN_RXHDR_L4_TYPE_UDP: - if (l3_type == AXEN_RXHDR_L3_TYPE_IPV4) - csum_flags |= M_CSUM_UDPv4; - else - csum_flags |= M_CSUM_UDPv6; - break; - default: - break; - } - - csum_flags &= enabled_flags; - if ((csum_flags & M_CSUM_IPv4) && (pkt_hdr & AXEN_RXHDR_L3CSUM_ERR)) - csum_flags |= M_CSUM_IPv4_BAD; - if ((csum_flags & ~M_CSUM_IPv4) && (pkt_hdr & AXEN_RXHDR_L4CSUM_ERR)) - csum_flags |= M_CSUM_TCP_UDP_BAD; - - return csum_flags; -} - -/* - * A frame was downloaded to the chip. It's safe for us to clean up - * the list buffers. - */ -static void -axen_txeof(struct usbd_xfer *xfer, void * priv, usbd_status status) -{ - struct axen_chain *c = (struct axen_chain *)priv; - struct axen_softc * const sc = c->axen_sc; - struct axen_cdata *cd = &sc->axen_cdata; - struct ifnet *ifp = GET_IFP(sc); - - mutex_enter(&sc->axen_txlock); - if (sc->axen_stopping || sc->axen_dying) { - mutex_exit(&sc->axen_txlock); - return; - } - - KASSERT(cd->axen_tx_cnt > 0); - cd->axen_tx_cnt--; - - sc->axen_timer = 0; - - switch (status) { - case USBD_NOT_STARTED: - case USBD_CANCELLED: - break; - - case USBD_NORMAL_COMPLETION: - ifp->if_opackets++; - if (!IFQ_IS_EMPTY(&ifp->if_snd)) - axen_start_locked(ifp); - break; - - default: - - ifp->if_oerrors++; - if (usbd_ratecheck(&sc->axen_tx_notice)) - aprint_error_dev(sc->axen_dev, "usb error on tx: %s\n", - usbd_errstr(status)); - if (status == USBD_STALLED) - usbd_clear_endpoint_stall_async(sc->axen_ep[AXEN_ENDPT_TX]); - break; - } - - mutex_exit(&sc->axen_txlock); -} - -static void -axen_tick(void *xsc) -{ - struct axen_softc * const sc = xsc; - - if (sc == NULL) - return; - - DPRINTFN(0xff,("%s: %s: enter\n", device_xname(sc->axen_dev),__func__)); - - mutex_enter(&sc->axen_lock); - if (!sc->axen_stopping && !sc->axen_dying) { - /* Perform periodic stuff in process context */ - usb_add_task(sc->axen_udev, &sc->axen_tick_task, USB_TASKQ_DRIVER); - } - mutex_exit(&sc->axen_lock); -} - -static void -axen_tick_task(void *xsc) -{ - struct axen_softc * const sc = xsc; - struct ifnet *ifp; - struct mii_data *mii; - - if (sc == NULL) - return; - - mutex_enter(&sc->axen_lock); - if (sc->axen_stopping || sc->axen_dying) { - mutex_exit(&sc->axen_lock); - return; - } - - ifp = GET_IFP(sc); - mii = GET_MII(sc); - if (mii == NULL) { - mutex_exit(&sc->axen_lock); - return; - } - - sc->axen_refcnt++; - mutex_exit(&sc->axen_lock); - - if (sc->axen_timer != 0 && --sc->axen_timer == 0) - axen_watchdog(ifp); - - mii_tick(mii); - - if (sc->axen_link == 0) - axen_miibus_statchg(ifp); - - mutex_enter(&sc->axen_lock); - if (--sc->axen_refcnt < 0) - cv_broadcast(&sc->axen_detachcv); - if (!sc->axen_stopping && !sc->axen_dying) - callout_schedule(&sc->axen_stat_ch, hz); - mutex_exit(&sc->axen_lock); -} - -static int -axen_encap(struct axen_softc *sc, struct mbuf *m, int idx) -{ - struct ifnet *ifp = GET_IFP(sc); - struct axen_chain *c; - usbd_status err; struct axen_sframe_hdr hdr; u_int length, boundary; - KASSERT(mutex_owned(&sc->axen_txlock)); - - c = &sc->axen_cdata.axen_tx_chain[idx]; + KASSERT(mutex_owned(&un->un_txlock)); /* XXX Is this needed? wMaxPacketSize? */ - switch (sc->axen_udev->ud_speed) { + switch (un->un_udev->ud_speed) { case USB_SPEED_SUPER: boundary = 4096; break; @@ -1476,107 +1023,36 @@ axen_encap(struct axen_softc *sc, struct } length = m->m_pkthdr.len + sizeof(hdr); - KASSERT(length <= sc->axen_tx_bufsz); + KASSERT(length <= un->un_cdata.uncd_tx_bufsz); hdr.plen = htole32(m->m_pkthdr.len); hdr.gso = (m->m_pkthdr.csum_flags & M_CSUM_TSOv4) ? m->m_pkthdr.segsz : 0; if ((length % boundary) == 0) { - DPRINTF(("%s: boundary hit\n", device_xname(sc->axen_dev))); + DPRINTF(("%s: boundary hit\n", device_xname(un->un_dev))); hdr.gso |= 0x80008000; /* XXX enable padding */ } hdr.gso = htole32(hdr.gso); - memcpy(c->axen_buf, &hdr, sizeof(hdr)); - m_copydata(m, 0, m->m_pkthdr.len, c->axen_buf + sizeof(hdr)); - - if (__predict_false(c->axen_xfer == NULL)) - return EIO; /* XXX plugged out or down */ - - usbd_setup_xfer(c->axen_xfer, c, c->axen_buf, length, - USBD_FORCE_SHORT_XFER, 10000, axen_txeof); - - /* Transmit */ - err = usbd_transfer(c->axen_xfer); - if (err != USBD_IN_PROGRESS) { - /* XXXSMP IFNET_LOCK */ - axen_stop(ifp, 0); - return EIO; - } + memcpy(c->unc_buf, &hdr, sizeof(hdr)); + m_copydata(m, 0, m->m_pkthdr.len, c->unc_buf + sizeof(hdr)); - return 0; -} - -static void -axen_start_locked(struct ifnet *ifp) -{ - struct axen_softc * const sc = ifp->if_softc; - struct mbuf *m; - struct axen_cdata *cd = &sc->axen_cdata; - int idx; - - KASSERT(mutex_owned(&sc->axen_txlock)); - KASSERT(cd->axen_tx_cnt <= AXEN_TX_LIST_CNT); - - if (sc->axen_link == 0 || (ifp->if_flags & IFF_RUNNING) == 0) - return; - - idx = cd->axen_tx_prod; - while (cd->axen_tx_cnt < AXEN_TX_LIST_CNT) { - IFQ_POLL(&ifp->if_snd, m); - if (m == NULL) - break; - - if (axen_encap(sc, m, idx)) { - ifp->if_oerrors++; - break; - } - IFQ_DEQUEUE(&ifp->if_snd, m); - - /* - * If there's a BPF listener, bounce a copy of this frame - * to him. - */ - bpf_mtap(ifp, m, BPF_D_OUT); - m_freem(m); - - idx = (idx + 1) % AXEN_TX_LIST_CNT; - cd->axen_tx_cnt++; - } - cd->axen_tx_prod = idx; - - /* - * Set a timeout in case the chip goes out to lunch. - */ - sc->axen_timer = 5; -} - -static void -axen_start(struct ifnet *ifp) -{ - struct axen_softc * const sc = ifp->if_softc; - - mutex_enter(&sc->axen_txlock); - if (!sc->axen_stopping) - axen_start_locked(ifp); - mutex_exit(&sc->axen_txlock); + return length; } static int axen_init_locked(struct ifnet *ifp) { - struct axen_softc * const sc = ifp->if_softc; - struct axen_chain *c; - usbd_status err; - int i; + struct usb_net * const un = ifp->if_softc; + struct axen_softc * const sc = un->un_sc; uint16_t rxmode; uint16_t wval; uint8_t bval; - KASSERT(mutex_owned(&sc->axen_lock)); + KASSERT(mutex_owned(&un->un_lock)); - if (sc->axen_dying) + if (un->un_dying) return EIO; /* Cancel pending I/O */ @@ -1585,17 +1061,17 @@ axen_init_locked(struct ifnet *ifp) /* Reset the ethernet interface. */ axen_reset(sc); - axen_lock_mii_sc_locked(sc); + usb_net_lock_mii_un_locked(un); /* XXX: ? */ bval = 0x01; axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_UNK_28, &bval); /* Configure offloading engine. */ - axen_setcoe(sc); + axen_setoe_locked(un); /* Program promiscuous mode and multicast filters. */ - axen_iff_locked(sc); + axen_setmulti_locked(un); /* Enable receiver, set RX mode */ axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); @@ -1604,275 +1080,65 @@ axen_init_locked(struct ifnet *ifp) wval = htole16(rxmode); axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); - axen_unlock_mii_sc_locked(sc); - - /* Open RX and TX pipes. */ - err = usbd_open_pipe(sc->axen_iface, sc->axen_ed[AXEN_ENDPT_RX], - USBD_EXCLUSIVE_USE | USBD_MPSAFE, &sc->axen_ep[AXEN_ENDPT_RX]); - if (err) { - aprint_error_dev(sc->axen_dev, "open rx pipe failed: %s\n", - usbd_errstr(err)); - return EIO; - } - - err = usbd_open_pipe(sc->axen_iface, sc->axen_ed[AXEN_ENDPT_TX], - USBD_EXCLUSIVE_USE | USBD_MPSAFE, &sc->axen_ep[AXEN_ENDPT_TX]); - if (err) { - aprint_error_dev(sc->axen_dev, "open tx pipe failed: %s\n", - usbd_errstr(err)); - return EIO; - } - - /* Init RX ring. */ - if (axen_rx_list_init(sc)) { - aprint_error_dev(sc->axen_dev, "rx list init failed\n"); - return ENOBUFS; - } - - /* Init TX ring. */ - if (axen_tx_list_init(sc)) { - aprint_error_dev(sc->axen_dev, "tx list init failed\n"); - return ENOBUFS; - } - - mutex_enter(&sc->axen_rxlock); - mutex_enter(&sc->axen_txlock); - sc->axen_stopping = false; - - /* Start up the receive pipe. */ - for (i = 0; i < AXEN_RX_LIST_CNT; i++) { - c = &sc->axen_cdata.axen_rx_chain[i]; + usb_net_unlock_mii_un_locked(un); - usbd_setup_xfer(c->axen_xfer, c, c->axen_buf, sc->axen_rx_bufsz, - USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, axen_rxeof); - usbd_transfer(c->axen_xfer); - } - - mutex_exit(&sc->axen_txlock); - mutex_exit(&sc->axen_rxlock); - - /* Indicate we are up and running. */ - KASSERT(IFNET_LOCKED(ifp)); - ifp->if_flags |= IFF_RUNNING; - - callout_schedule(&sc->axen_stat_ch, hz); - return 0; + return usb_net_init_rx_tx(un, 0, 0); } static int axen_init(struct ifnet *ifp) { - struct axen_softc * const sc = ifp->if_softc; + struct usb_net * const un = ifp->if_softc; - mutex_enter(&sc->axen_lock); + mutex_enter(&un->un_lock); int ret = axen_init_locked(ifp); - mutex_exit(&sc->axen_lock); + mutex_exit(&un->un_lock); return ret; } -static int -axen_ioctl(struct ifnet *ifp, u_long cmd, void *data) -{ - struct axen_softc * const sc = ifp->if_softc; - int error = 0; - - switch (cmd) { - case SIOCSIFFLAGS: - if ((error = ifioctl_common(ifp, cmd, data)) != 0) - break; - - switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { - case IFF_RUNNING: - axen_stop(ifp, 1); - break; - case IFF_UP: - axen_init(ifp); - break; - case IFF_UP | IFF_RUNNING: - if ((ifp->if_flags ^ sc->axen_if_flags) == IFF_PROMISC) { - axen_iff(sc); - } else - axen_init(ifp); - break; - } - - mutex_enter(&sc->axen_rxlock); - mutex_enter(&sc->axen_txlock); - sc->axen_if_flags = ifp->if_flags; - mutex_exit(&sc->axen_txlock); - mutex_exit(&sc->axen_rxlock); - break; - - default: - if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET) - break; - - error = 0; - switch (cmd) { - case SIOCADDMULTI: - case SIOCDELMULTI: - axen_iff(sc); - break; - case SIOCSIFCAP: - mutex_enter(&sc->axen_lock); - axen_setcoe(sc); - mutex_exit(&sc->axen_lock); - break; - default: - break; - } - break; - } - - return error; -} - -static void -axen_watchdog(struct ifnet *ifp) -{ - struct axen_softc * const sc = ifp->if_softc; - struct axen_chain *c; - usbd_status stat; - - ifp->if_oerrors++; - aprint_error_dev(sc->axen_dev, "watchdog timeout\n"); - - c = &sc->axen_cdata.axen_tx_chain[0]; - usbd_get_xfer_status(c->axen_xfer, NULL, NULL, NULL, &stat); - axen_txeof(c->axen_xfer, c, stat); - - if (!IFQ_IS_EMPTY(&ifp->if_snd)) - axen_start(ifp); -} - /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void -axen_stop_locked(struct ifnet *ifp, int disable) +axen_stop_cb(struct ifnet *ifp, int disable) { - struct axen_softc * const sc = ifp->if_softc; - struct axen_chain *c; - usbd_status err; - int i; + struct usb_net * const un = ifp->if_softc; + struct axen_softc * const sc = un->un_sc; uint16_t rxmode, wval; - KASSERT(mutex_owned(&sc->axen_lock)); - mutex_enter(&sc->axen_rxlock); - mutex_enter(&sc->axen_txlock); - sc->axen_stopping = true; - mutex_exit(&sc->axen_txlock); - mutex_exit(&sc->axen_rxlock); - axen_reset(sc); /* Disable receiver, set RX mode */ - axen_lock_mii_sc_locked(sc); + usb_net_lock_mii_un_locked(un); axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); rxmode = le16toh(wval); rxmode &= ~AXEN_RXCTL_START; wval = htole16(rxmode); axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); - axen_unlock_mii_sc_locked(sc); - - /* - * XXXSMP Would like to - * KASSERT(IFNET_LOCKED(ifp)) - * here but the locking order is: - * ifnet -> sc lock -> rxlock -> txlock - * and sc lock is already held. - */ - ifp->if_flags &= ~IFF_RUNNING; - sc->axen_timer = 0; - - callout_stop(&sc->axen_stat_ch); - sc->axen_link = 0; - - /* Stop transfers. */ - if (sc->axen_ep[AXEN_ENDPT_RX] != NULL) { - err = usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_RX]); - if (err) { - aprint_error_dev(sc->axen_dev, - "abort rx pipe failed: %s\n", usbd_errstr(err)); - } - } - - if (sc->axen_ep[AXEN_ENDPT_TX] != NULL) { - err = usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_TX]); - if (err) { - aprint_error_dev(sc->axen_dev, - "abort tx pipe failed: %s\n", usbd_errstr(err)); - } - } - - if (sc->axen_ep[AXEN_ENDPT_INTR] != NULL) { - err = usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_INTR]); - if (err) { - aprint_error_dev(sc->axen_dev, - "abort intr pipe failed: %s\n", usbd_errstr(err)); - } - } - - /* Free RX resources. */ - for (i = 0; i < AXEN_RX_LIST_CNT; i++) { - c = &sc->axen_cdata.axen_rx_chain[i]; - if (c->axen_xfer != NULL) { - usbd_destroy_xfer(c->axen_xfer); - c->axen_xfer = NULL; - } - } - - /* Free TX resources. */ - for (i = 0; i < AXEN_TX_LIST_CNT; i++) { - c = &sc->axen_cdata.axen_tx_chain[i]; - if (c->axen_xfer != NULL) { - usbd_destroy_xfer(c->axen_xfer); - c->axen_xfer = NULL; - } - } - - /* Close pipes. */ - if (sc->axen_ep[AXEN_ENDPT_RX] != NULL) { - err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_RX]); - if (err) { - aprint_error_dev(sc->axen_dev, - "close rx pipe failed: %s\n", usbd_errstr(err)); - } - sc->axen_ep[AXEN_ENDPT_RX] = NULL; - } + usb_net_unlock_mii_un_locked(un); +} - if (sc->axen_ep[AXEN_ENDPT_TX] != NULL) { - err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_TX]); - if (err) { - aprint_error_dev(sc->axen_dev, - "close tx pipe failed: %s\n", usbd_errstr(err)); - } - sc->axen_ep[AXEN_ENDPT_TX] = NULL; - } +static void +axen_stop_locked(struct ifnet *ifp, int disable) +{ + struct usb_net * const un = ifp->if_softc; - if (sc->axen_ep[AXEN_ENDPT_INTR] != NULL) { - err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_INTR]); - if (err) { - aprint_error_dev(sc->axen_dev, - "close intr pipe failed: %s\n", usbd_errstr(err)); - } - sc->axen_ep[AXEN_ENDPT_INTR] = NULL; - } + usb_net_stop(un, ifp, disable); } static void axen_stop(struct ifnet *ifp, int disable) { - struct axen_softc * const sc = ifp->if_softc; + struct usb_net * const un = ifp->if_softc; - mutex_enter(&sc->axen_lock); + mutex_enter(&un->un_lock); axen_stop_locked(ifp, disable); - mutex_exit(&sc->axen_lock); + mutex_exit(&un->un_lock); } -MODULE(MODULE_CLASS_DRIVER, if_axen, NULL); +MODULE(MODULE_CLASS_DRIVER, if_axen, "usb_net"); #ifdef _MODULE #include "ioconf.c" Index: sys/dev/usb/if_axenreg.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/if_axenreg.h,v retrieving revision 1.14 diff -p -u -r1.14 if_axenreg.h --- sys/dev/usb/if_axenreg.h 18 Jun 2019 09:34:57 -0000 1.14 +++ sys/dev/usb/if_axenreg.h 29 Jul 2019 07:43:54 -0000 @@ -221,15 +221,6 @@ #define AXEN_CONFIG_NO 1 #define AXEN_IFACE_IDX 0 -/* - * The interrupt endpoint is currently unused - * by the ASIX part. - */ -#define AXEN_ENDPT_RX 0x0 -#define AXEN_ENDPT_TX 0x1 -#define AXEN_ENDPT_INTR 0x2 -#define AXEN_ENDPT_MAX 0x3 - struct axen_qctrl { uint8_t ctrl; uint8_t timer_low; Index: sys/dev/usb/usb_net.c =================================================================== RCS file: sys/dev/usb/usb_net.c diff -N sys/dev/usb/usb_net.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/usb/usb_net.c 29 Jul 2019 07:43:54 -0000 @@ -0,0 +1,907 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 2019 Matthew R. Green + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Common code shared between USB ethernet drivers. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +static int usb_net_modcmd(modcmd_t, void *); + +/* Interrupt handling. */ + +static struct mbuf * +usb_net_newbuf(void) +{ + struct mbuf *m; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return NULL; + + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(m); + return NULL; + } + + m->m_len = m->m_pkthdr.len = MCLBYTES; + m_adj(m, ETHER_ALIGN); + + return m; +} + +/* + * usb_net_rxeof() is designed to be the done callback for rx completion. + * it provides generic setup and finalisation, calls a different usb_net + * rx_loop callback in the middle, which can use usb_net_enqueue() to + * enqueue a packet for higher levels. + */ +void +usb_net_enqueue(struct usb_net * const un, uint8_t *buf, size_t buflen, + int flags) +{ + struct ifnet *ifp = &un->un_ec.ec_if; + struct mbuf *m; + + KASSERT(mutex_owned(&un->un_rxlock)); + + m = usb_net_newbuf(); + if (m == NULL) { + ifp->if_ierrors++; + return; + } + + m_set_rcvif(m, ifp); + m->m_pkthdr.len = m->m_len = buflen; + m->m_pkthdr.csum_flags = flags; + memcpy(mtod(m, char *), buf, buflen); + + /* push the packet up */ + if_percpuq_enqueue(ifp->if_percpuq, m); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void +usb_net_rxeof(struct usbd_xfer *xfer, void * priv, usbd_status status) +{ + struct usb_net_chain *c = (struct usb_net_chain *)priv; + struct usb_net * const un = c->unc_un; + struct ifnet *ifp = &un->un_ec.ec_if; + uint32_t total_len; + + mutex_enter(&un->un_rxlock); + + if (un->un_dying || un->un_stopping || + status == USBD_INVAL || status == USBD_NOT_STARTED || + status == USBD_CANCELLED || !(ifp->if_flags & IFF_RUNNING)) + goto out; + + if (status != USBD_NORMAL_COMPLETION) { + if (usbd_ratecheck(&un->un_rx_notice)) + aprint_error_dev(un->un_dev, "usb errors on rx: %s\n", + usbd_errstr(status)); + if (status == USBD_STALLED) + usbd_clear_endpoint_stall_async(un->un_ep[USB_NET_ENDPT_RX]); + goto done; + } + + usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); + + if (total_len > un->un_cdata.uncd_rx_bufsz) { + aprint_error_dev(un->un_dev, + "rxeof: too large transfer (%u > %u)\n", + total_len, un->un_cdata.uncd_rx_bufsz); + goto done; + } + + (*un->un_rx_loop_cb)(un, xfer, c, total_len); + KASSERT(mutex_owned(&un->un_rxlock)); + +done: + if (un->un_dying || un->un_stopping) + goto out; + + mutex_exit(&un->un_rxlock); + + /* Setup new transfer. */ + usbd_setup_xfer(xfer, c, c->unc_buf, un->un_cdata.uncd_rx_bufsz, + un->un_rx_xfer_flags, USBD_NO_TIMEOUT, usb_net_rxeof); + usbd_transfer(xfer); + return; + +out: + mutex_exit(&un->un_rxlock); +} + +void +usb_net_txeof(struct usbd_xfer *xfer, void * priv, usbd_status status) +{ + struct usb_net_chain *c = (struct usb_net_chain *)priv; + struct usb_net * const un = c->unc_un; + struct usb_net_cdata *cd = &un->un_cdata; + struct ifnet * const ifp = usb_net_ifp(un); + + mutex_enter(&un->un_txlock); + if (un->un_stopping || un->un_dying) { + mutex_exit(&un->un_txlock); + return; + } + + KASSERT(cd->uncd_tx_cnt > 0); + cd->uncd_tx_cnt--; + + un->un_timer = 0; + + switch (status) { + case USBD_NOT_STARTED: + case USBD_CANCELLED: + break; + + case USBD_NORMAL_COMPLETION: + ifp->if_opackets++; + break; + + default: + + ifp->if_oerrors++; + if (usbd_ratecheck(&un->un_tx_notice)) + aprint_error_dev(un->un_dev, "usb error on tx: %s\n", + usbd_errstr(status)); + if (status == USBD_STALLED) + usbd_clear_endpoint_stall_async(un->un_ep[USB_NET_ENDPT_TX]); + break; + } + + mutex_exit(&un->un_txlock); + + if (status == USBD_NORMAL_COMPLETION && !IFQ_IS_EMPTY(&ifp->if_snd)) + (*ifp->if_start)(ifp); +} + +static void +usb_net_start_locked(struct ifnet *ifp) +{ + struct usb_net * const un = ifp->if_softc; + struct usb_net_cdata *cd = &un->un_cdata; + struct mbuf *m; + unsigned length; + int idx; + + KASSERT(mutex_owned(&un->un_txlock)); + KASSERT(cd->uncd_tx_cnt <= cd->uncd_tx_list_cnt); + + if (!un->un_link || (ifp->if_flags & IFF_RUNNING) == 0) + return; + + idx = cd->uncd_tx_prod; + while (cd->uncd_tx_cnt < cd->uncd_tx_list_cnt) { + IFQ_POLL(&ifp->if_snd, m); + if (m == NULL) + break; + + struct usb_net_chain *c = &un->un_cdata.uncd_tx_chain[idx]; + + length = (*un->un_tx_prepare_cb)(un, m, c); + if (length == 0) { + ifp->if_oerrors++; + break; + } + + if (__predict_false(c->unc_xfer == NULL)) { + ifp->if_oerrors++; + break; + } + + usbd_setup_xfer(c->unc_xfer, c, c->unc_buf, length, + un->un_tx_xfer_flags, 10000, usb_net_txeof); + + /* Transmit */ + usbd_status err = usbd_transfer(c->unc_xfer); + if (err != USBD_IN_PROGRESS) { + ifp->if_oerrors++; + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m); + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + bpf_mtap(ifp, m, BPF_D_OUT); + m_freem(m); + + idx = (idx + 1) % cd->uncd_tx_list_cnt; + cd->uncd_tx_cnt++; + } + cd->uncd_tx_prod = idx; + + /* + * Set a timeout in case the chip goes out to lunch. + */ + un->un_timer = 5; +} + +void +usb_net_start(struct ifnet *ifp) +{ + struct usb_net * const un = ifp->if_softc; + + mutex_enter(&un->un_txlock); + if (!un->un_stopping) + usb_net_start_locked(ifp); + mutex_exit(&un->un_txlock); +} + +/* + * Chain management. + * + * RX and TX are identical. Keep them that way. + */ + +/* Start of common RX functions */ + +static size_t +usb_net_rx_list_size(struct usb_net_cdata *cd) +{ + return sizeof(*cd->uncd_rx_chain) * cd->uncd_rx_list_cnt; +} + +static void +usb_net_rx_list_alloc(struct usb_net *un, unsigned cnt) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + cd->uncd_rx_list_cnt = cnt; + cd->uncd_rx_chain = kmem_zalloc(usb_net_rx_list_size(cd), KM_SLEEP); +} + +static void +usb_net_rx_list_free(struct usb_net *un) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + if (cd->uncd_rx_chain) { + kmem_free(cd->uncd_rx_chain, usb_net_rx_list_size(cd)); + cd->uncd_rx_chain = NULL; + } +} + +static int +usb_net_rx_list_init(struct usb_net *un, unsigned xfer_flags) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_rx_list_cnt; i++) { + struct usb_net_chain *c = &cd->uncd_rx_chain[i]; + + c->unc_un = un; + if (c->unc_xfer == NULL) { + int err = usbd_create_xfer(un->un_ep[USB_NET_ENDPT_RX], + cd->uncd_rx_bufsz, xfer_flags, 0, &c->unc_xfer); + if (err) + return err; + c->unc_buf = usbd_get_buffer(c->unc_xfer); + } + } + + return 0; +} + +static void +usb_net_rx_list_fini(struct usb_net *un) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_rx_list_cnt; i++) { + struct usb_net_chain *c = &cd->uncd_rx_chain[i]; + + if (c->unc_xfer != NULL) { + usbd_destroy_xfer(c->unc_xfer); + c->unc_xfer = NULL; + c->unc_buf = NULL; + } + } +} + +/* End of common RX functions */ + +static void +usb_net_rx_start_pipes(struct usb_net *un, usbd_callback cb) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + mutex_enter(&un->un_rxlock); + mutex_enter(&un->un_txlock); + un->un_stopping = false; + + for (size_t i = 0; i < cd->uncd_rx_list_cnt; i++) { + struct usb_net_chain *c = &cd->uncd_rx_chain[i]; + + usbd_setup_xfer(c->unc_xfer, c, c->unc_buf, cd->uncd_rx_bufsz, + un->un_rx_xfer_flags, USBD_NO_TIMEOUT, cb); + usbd_transfer(c->unc_xfer); + } + + mutex_exit(&un->un_txlock); + mutex_exit(&un->un_rxlock); +} + +/* Start of common TX functions */ + +static size_t +usb_net_tx_list_size(struct usb_net_cdata *cd) +{ + return sizeof(*cd->uncd_tx_chain) * cd->uncd_tx_list_cnt; +} + +static void +usb_net_tx_list_alloc(struct usb_net *un, unsigned cnt) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + cd->uncd_tx_list_cnt = cnt; + cd->uncd_tx_chain = kmem_zalloc(usb_net_tx_list_size(cd), KM_SLEEP); +} + +static void +usb_net_tx_list_free(struct usb_net *un) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + if (cd->uncd_tx_chain) { + kmem_free(cd->uncd_tx_chain, usb_net_tx_list_size(cd)); + cd->uncd_tx_chain = NULL; + } +} + +static int +usb_net_tx_list_init(struct usb_net *un, unsigned xfer_flags) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_tx_list_cnt; i++) { + struct usb_net_chain *c = &cd->uncd_tx_chain[i]; + + c->unc_un = un; + if (c->unc_xfer == NULL) { + int err = usbd_create_xfer(un->un_ep[USB_NET_ENDPT_TX], + cd->uncd_tx_bufsz, xfer_flags, 0, &c->unc_xfer); + if (err) + return err; + c->unc_buf = usbd_get_buffer(c->unc_xfer); + } + } + + return 0; +} + +static void +usb_net_tx_list_fini(struct usb_net *un) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_tx_list_cnt; i++) { + struct usb_net_chain *c = &cd->uncd_tx_chain[i]; + + if (c->unc_xfer != NULL) { + usbd_destroy_xfer(c->unc_xfer); + c->unc_xfer = NULL; + c->unc_buf = NULL; + } + } +} + +/* End of common TX functions */ + +/* Endpoint pipe management. */ + +static void +usb_net_ep_close_pipes(struct usb_net *un) +{ + for (size_t i = 0; i < __arraycount(un->un_ep); i++) { + if (un->un_ep[i] == NULL) + continue; + usbd_status err = usbd_close_pipe(un->un_ep[i]); + if (err) + aprint_error_dev(un->un_dev, "close pipe %zu: %s\n", i, + usbd_errstr(err)); + un->un_ep[i] = NULL; + } +} + +static usbd_status +usb_net_ep_open_pipes(struct usb_net *un) +{ + for (size_t i = 0; i < __arraycount(un->un_ep); i++) { + if (un->un_ed[i] == 0) + continue; + usbd_status err = usbd_open_pipe(un->un_iface, un->un_ed[i], + USBD_EXCLUSIVE_USE | USBD_MPSAFE, &un->un_ep[i]); + if (err) { + usb_net_ep_close_pipes(un); + return err; + } + } + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +usb_net_ep_stop_pipes(struct usb_net *un) +{ + for (size_t i = 0; i < __arraycount(un->un_ep); i++) { + if (un->un_ep[i] == NULL) + continue; + usbd_status err = usbd_abort_pipe(un->un_ep[i]); + if (err) + return err; + } + + return USBD_NORMAL_COMPLETION; +} + +int +usb_net_init_rx_tx(struct usb_net * const un, unsigned rxflags, unsigned txflags) +{ + struct ifnet * const ifp = usb_net_ifp(un); + usbd_status err; + + /* Open RX and TX pipes. */ + err = usb_net_ep_open_pipes(un); + if (err) { + aprint_error_dev(un->un_dev, "open rx/tx pipes failed: %s\n", + usbd_errstr(err)); + return EIO; + } + + /* Init RX ring. */ + if (usb_net_rx_list_init(un, rxflags)) { + aprint_error_dev(un->un_dev, "rx list init failed\n"); + return ENOBUFS; + } + + /* Init TX ring. */ + if (usb_net_tx_list_init(un, txflags)) { + aprint_error_dev(un->un_dev, "tx list init failed\n"); + return ENOBUFS; + } + + /* Start up the receive pipe(s). */ + usb_net_rx_start_pipes(un, usb_net_rxeof); + + /* Indicate we are up and running. */ + KASSERT(IFNET_LOCKED(ifp)); + ifp->if_flags |= IFF_RUNNING; + + callout_schedule(&un->un_stat_ch, hz); + return 0; +} + +/* MII management. */ + +/* + * Access functions for MII. Take the MII lock to call access MII regs. + * Two forms: usb_net (softc) lock currently held or not. + */ +void +usb_net_lock_mii(struct usb_net *un) +{ + + mutex_enter(&un->un_lock); + un->un_refcnt++; + mutex_exit(&un->un_lock); + + mutex_enter(&un->un_miilock); +} + +void +usb_net_lock_mii_un_locked(struct usb_net *un) +{ + KASSERT(mutex_owned(&un->un_lock)); + + un->un_refcnt++; + mutex_enter(&un->un_miilock); +} + +void +usb_net_unlock_mii(struct usb_net *un) +{ + + mutex_exit(&un->un_miilock); + mutex_enter(&un->un_lock); + if (--un->un_refcnt < 0) + cv_broadcast(&un->un_detachcv); + mutex_exit(&un->un_lock); +} + +void +usb_net_unlock_mii_un_locked(struct usb_net *un) +{ + KASSERT(mutex_owned(&un->un_lock)); + + mutex_exit(&un->un_miilock); + if (--un->un_refcnt < 0) + cv_broadcast(&un->un_detachcv); +} + +int +usb_net_miibus_readreg(device_t dev, int phy, int reg, uint16_t *val) +{ + struct usb_net * const un = device_private(dev); + usbd_status err; + + mutex_enter(&un->un_lock); + if (un->un_dying || un->un_phyno != phy) { + mutex_exit(&un->un_lock); + return EIO; + } + mutex_exit(&un->un_lock); + + usb_net_lock_mii(un); + err = (*un->un_read_reg_cb)(un, reg, phy, val); + usb_net_unlock_mii(un); + + if (err) { + aprint_error_dev(un->un_dev, "read PHY failed: %d\n", err); + return EIO; + } + + return 0; +} + +int +usb_net_miibus_writereg(device_t dev, int phy, int reg, uint16_t val) +{ + struct usb_net * const un = device_private(dev); + usbd_status err; + + mutex_enter(&un->un_lock); + if (un->un_dying || un->un_phyno != phy) { + mutex_exit(&un->un_lock); + return EIO; + } + mutex_exit(&un->un_lock); + + usb_net_lock_mii(un); + err = (*un->un_write_reg_cb)(un, reg, phy, val); + usb_net_unlock_mii(un); + + if (err) { + aprint_error_dev(un->un_dev, "write PHY failed: %d\n", err); + return EIO; + } + + return 0; +} + +/* ioctl */ +int +usb_net_ioctl(struct ifnet *ifp, u_long cmd, void *data) +{ + struct usb_net * const un = ifp->if_softc; + int error = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + if ((error = ifioctl_common(ifp, cmd, data)) != 0) + break; + + switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { + case IFF_RUNNING: + (*ifp->if_stop)(ifp, 1); + break; + case IFF_UP: + (*ifp->if_init)(ifp); + break; + case IFF_UP | IFF_RUNNING: + if ((ifp->if_flags ^ un->un_if_flags) == IFF_PROMISC) + (*un->un_setmulti_cb)(un); + else + (*ifp->if_init)(ifp); + break; + } + + mutex_enter(&un->un_rxlock); + mutex_enter(&un->un_txlock); + un->un_if_flags = ifp->if_flags; + mutex_exit(&un->un_txlock); + mutex_exit(&un->un_rxlock); + break; + + default: + if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET) + break; + + error = 0; + switch (cmd) { + case SIOCADDMULTI: + case SIOCDELMULTI: + (*un->un_setmulti_cb)(un); + break; + case SIOCSIFCAP: + (*un->un_setoe_cb)(un); + break; + default: + break; + } + break; + } + + return error; +} + + +/* + * Generic stop network function: + * - mark as stopping + * - call DD routine to stop the device + * - turn off running, timer, statchg callout, link + * - stop transfers + * - free RX and TX resources + * - close pipes + */ +void +usb_net_stop(struct usb_net *un, struct ifnet *ifp, int disable) +{ + KASSERT(mutex_owned(&un->un_lock)); + + mutex_enter(&un->un_rxlock); + mutex_enter(&un->un_txlock); + un->un_stopping = true; + mutex_exit(&un->un_txlock); + mutex_exit(&un->un_rxlock); + + (*un->un_stop_cb)(ifp, disable); + + /* + * XXXSMP Would like to + * KASSERT(IFNET_LOCKED(ifp)) + * here but the locking order is: + * ifnet -> lock -> rxlock -> txlock + * and sc lock is already held. + */ + ifp->if_flags &= ~IFF_RUNNING; + un->un_timer = 0; + + callout_stop(&un->un_stat_ch); + un->un_link = false; + + /* Stop transfers. */ + usb_net_ep_stop_pipes(un); + + /* Free RX/TX resources. */ + usb_net_rx_list_fini(un); + usb_net_tx_list_fini(un); + + /* Close pipes. */ + usb_net_ep_close_pipes(un); +} + +/* + * Generic tick task function. + * + * usb_net_tick() is triggered from a callout, and triggers a call to + * usb_net_tick_task() from the usb_task subsystem. + */ +static void +usb_net_tick(void *arg) +{ + struct usb_net * const un = arg; + + mutex_enter(&un->un_lock); + if (!un->un_stopping && !un->un_dying) { + /* Perform periodic stuff in process context */ + usb_add_task(un->un_udev, &un->un_ticktask, USB_TASKQ_DRIVER); + } + mutex_exit(&un->un_lock); +} + +static void +usb_net_watchdog(struct ifnet *ifp) +{ + struct usb_net * const un = ifp->if_softc; + struct usb_net_cdata *cd = &un->un_cdata; + usbd_status stat; + + ifp->if_oerrors++; + aprint_error_dev(un->un_dev, "watchdog timeout\n"); + + if (cd->uncd_tx_cnt > 0) { + /* + * XXX index 0 + */ + struct usb_net_chain *c = &un->un_cdata.uncd_tx_chain[0]; + usbd_get_xfer_status(c->unc_xfer, NULL, NULL, NULL, &stat); + usb_net_txeof(c->unc_xfer, c, stat); + } + + if (!IFQ_IS_EMPTY(&ifp->if_snd)) + (*ifp->if_start)(ifp); +} + +static void +usb_net_tick_task(void *arg) +{ + struct usb_net * const un = arg; + + mutex_enter(&un->un_lock); + if (un->un_stopping || un->un_dying) { + mutex_exit(&un->un_lock); + return; + } + + struct ifnet * const ifp = usb_net_ifp(un); + struct mii_data * const mii = usb_net_mii(un); + + if (mii == NULL) { + mutex_exit(&un->un_lock); + return; + } + + un->un_refcnt++; + mutex_exit(&un->un_lock); + + if (un->un_timer != 0 && --un->un_timer == 0) + usb_net_watchdog(ifp); + + mii_tick(mii); + + if (!un->un_link) + (*mii->mii_statchg)(ifp); + + mutex_enter(&un->un_lock); + if (--un->un_refcnt < 0) + cv_broadcast(&un->un_detachcv); + if (!un->un_stopping && !un->un_dying) + callout_schedule(&un->un_stat_ch, hz); + mutex_exit(&un->un_lock); +} + +/* Autoconf management. */ + +/* + * Setup 'struct usb_net *un'. + * + * Perform any setup that is global to all consumers of this interface. + */ +void +usb_net_attach(struct usb_net *un, const char *detname, + unsigned rx_list_cnt, unsigned tx_list_cnt) +{ + + usb_init_task(&un->un_ticktask, usb_net_tick_task, un, USB_TASKQ_MPSAFE); + callout_init(&un->un_stat_ch, CALLOUT_MPSAFE); + callout_setfunc(&un->un_stat_ch, usb_net_tick, un); + + mutex_init(&un->un_miilock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&un->un_txlock, MUTEX_DEFAULT, IPL_SOFTUSB); + mutex_init(&un->un_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB); + mutex_init(&un->un_lock, MUTEX_DEFAULT, IPL_NONE); + cv_init(&un->un_detachcv, detname); + + rnd_attach_source(&un->un_rndsrc, device_xname(un->un_dev), + RND_TYPE_NET, RND_FLAG_DEFAULT); + + usb_net_rx_list_alloc(un, rx_list_cnt); + usb_net_tx_list_alloc(un, tx_list_cnt); +} + +void +usb_net_detach(struct usb_net *un) +{ + + usb_net_rx_list_free(un); + usb_net_tx_list_free(un); + + rnd_detach_source(&un->un_rndsrc); + + callout_halt(&un->un_stat_ch, NULL); + callout_destroy(&un->un_stat_ch); + usb_rem_task_wait(un->un_udev, &un->un_ticktask, USB_TASKQ_DRIVER, NULL); + + cv_destroy(&un->un_detachcv); + mutex_destroy(&un->un_lock); + mutex_destroy(&un->un_rxlock); + mutex_destroy(&un->un_txlock); + mutex_destroy(&un->un_miilock); +} + +int +usb_net_activate(device_t self, devact_t act) +{ + struct usb_net * const un = device_private(self); + struct ifnet * const ifp = usb_net_ifp(un); + + switch (act) { + case DVACT_DEACTIVATE: + if_deactivate(ifp); + + mutex_enter(&un->un_lock); + un->un_dying = true; + mutex_exit(&un->un_lock); + + mutex_enter(&un->un_rxlock); + mutex_enter(&un->un_txlock); + un->un_stopping = true; + mutex_exit(&un->un_txlock); + mutex_exit(&un->un_rxlock); + + return 0; + default: + return EOPNOTSUPP; + } +} + +MODULE(MODULE_CLASS_MISC, usb_net, NULL); + +static int +usb_net_modcmd(modcmd_t cmd, void *arg) +{ + switch (cmd) { + case MODULE_CMD_INIT: + case MODULE_CMD_FINI: + return 0; + case MODULE_CMD_STAT: + case MODULE_CMD_AUTOUNLOAD: + default: + return ENOTTY; + } +} Index: sys/dev/usb/usb_net.h =================================================================== RCS file: sys/dev/usb/usb_net.h diff -N sys/dev/usb/usb_net.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/usb/usb_net.h 29 Jul 2019 07:43:54 -0000 @@ -0,0 +1,248 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 2019 Matthew R. Green + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DEV_USB_USB_NET_H +#define _DEV_USB_USB_NET_H + +/* + * Common code/data shared by all USB ethernet drivers (using these routines.) + * + * This framework provides the following features for USB ethernet drivers: + * + * - USB endpoint pipe handling + * - rx and tx chain handling + * - generic handlers or support for several struct ifnet callbacks + * - MII bus locking + * - interrupt handling + * - partial autoconf handling + * + * Consumers of this interface need to: + * + * - replace most softc members with "struct usb_net" usage, in particular + * use usb_net pointer for ifp->if_softc, and device_private (real softc + * can be stored in un_sc member) + * - use MII bus lock / access methods + * - usb_net_attach() to initialise and allocate rx/tx chains + * - usb_net_detach() to clean them up + * - usb_net_activate() for autoconf + * - interface ioctl and start have direct frontends with callbacks for + * device specific handling: + * - ioctl uses several interface functions (start, init), as well as + * usb_net specific callbacks (multicast, offload engine) + * - start uses usb_net send callback + * - interface init and stop have helper functions + * - device specific init should use usb_net_init_rx_tx() to open pipes + * to the device and setup the rx/tx chains for use after any device + * specific setup + * - usb_net_stop() must be called with the un_lock held, and will + * call the device-specific usb_net stop callback, which enables the + * standard init calls stop idiom. + * - interrupt handling: + * - for rx, usb_net_init_rx_tx() will enable the receive pipes and + * call the rx_loop callback to handle device specific processing of + * packets, which can use usb_net_enqueue() to provide data to the + * higher layers + * - for tx, usb_net_start (if_start) will pull entries out of the + * transmit queue and use the send callback for the given mbuf. the + * send callback should use usb_net_txeof() for the transmit completion + * function + */ + +/* + * Converted drivers: if_axen. + * + * Note: these drivers have slightly different mbuf handling that need to be + * adjusted to the common method: + * + * if_atu if_aue if_cdce if_cue if_smsc if_udav if_upl if_url if_urndis. + */ + +#include + +/* + * Per-transfer data, initialised in usb_net_[rt]x_list_init(). + * + * Front-end must set uncd_tx_list_cnt and uncd_rx_list_cnt before calling + * list init, which the first time, will allocate the chain arrays, which + * must also be NULL to indicate the first call. + */ +struct usb_net; +struct usb_net_chain { + struct usb_net *unc_un; + struct usbd_xfer *unc_xfer; + uint8_t *unc_buf; +}; + +struct usb_net_cdata { + struct usb_net_chain *uncd_tx_chain; + struct usb_net_chain *uncd_rx_chain; + + unsigned uncd_rx_bufsz; + unsigned uncd_tx_bufsz; + unsigned uncd_rx_list_cnt; + unsigned uncd_tx_list_cnt; + + int uncd_tx_prod; + int uncd_tx_cnt; + int uncd_rx_cnt; +}; + +/* + * Extend this as necessary. axe(4) claims to want 6 total, but + * does not implement them. + */ +enum usb_net_ep { + USB_NET_ENDPT_RX, + USB_NET_ENDPT_TX, + USB_NET_ENDPT_INTR, + USB_NET_ENDPT_MAX, +}; + +/* Interface stop callback. */ +typedef void (*usb_net_stop_cb)(struct ifnet *, int); +/* Interface set multi-cast callback. */ +typedef void (*usb_net_setmulti_cb)(struct usb_net *); +/* Interface set offload-engine callback. */ +typedef void (*usb_net_setoe_cb)(struct usb_net *); +/* MII read register callback. */ +typedef usbd_status (*usb_net_mii_read_reg_cb)(struct usb_net *, int reg, + int phy, uint16_t *val); +/* MII write register callback. */ +typedef usbd_status (*usb_net_mii_write_reg_cb)(struct usb_net *, int reg, + int phy, uint16_t val); +/* Prepare packet to send callback, returns length. */ +typedef unsigned (*usb_net_tx_prepare_cb)(struct usb_net *, struct mbuf *, + struct usb_net_chain *); +/* Receive some packets callback. */ +typedef void (*usb_net_rx_loop_cb)(struct usb_net *, struct usbd_xfer *, + struct usb_net_chain *, uint32_t); + +/* + * Generic USB ethernet structure. Use this as ifp->if_softc. + */ +struct usb_net { + void *un_sc; /* real softc */ + device_t un_dev; + struct usbd_interface *un_iface; + struct usbd_device * un_udev; + krndsource_t un_rndsrc; + + struct ethercom un_ec; + struct mii_data un_mii; + int un_phyno; + + enum usb_net_ep un_ed[USB_NET_ENDPT_MAX]; + struct usbd_pipe *un_ep[USB_NET_ENDPT_MAX]; + + struct usb_net_cdata un_cdata; + struct callout un_stat_ch; + int un_if_flags; + + /* + * - un_lock protects most of the structure + * - un_miilock must be held to access this device's MII bus + * - un_rxlock protects the rx path and its data + * - un_txlock protects the tx path and its data + * - un_detachcv handles detach vs open references + */ + kmutex_t un_lock; + kmutex_t un_miilock; + kmutex_t un_rxlock; + kmutex_t un_txlock; + kcondvar_t un_detachcv; + + struct usb_task un_ticktask; + + int un_refcnt; + int un_timer; + + bool un_dying; + bool un_stopping; + bool un_link; + + struct timeval un_rx_notice; + struct timeval un_tx_notice; + + usb_net_stop_cb un_stop_cb; + usb_net_setmulti_cb un_setmulti_cb; + usb_net_setoe_cb un_setoe_cb; + usb_net_mii_read_reg_cb un_read_reg_cb; + usb_net_mii_write_reg_cb un_write_reg_cb; + usb_net_tx_prepare_cb un_tx_prepare_cb; + usb_net_rx_loop_cb un_rx_loop_cb; + + /* Passed to usbd_setup_xfer(). */ + int un_rx_xfer_flags; + int un_tx_xfer_flags; +}; + +#define usb_net_ifp(un) (&(un)->un_ec.ec_if) +#define usb_net_mii(un) (&(un)->un_mii) + +/* + * Endpoint / rx/tx chain management: + * + * usb_net_attach() allocates rx and tx chains + * usb_net_init_rx_tx() open pipes, initialises the rx/tx chains for use + * usb_net_stop() stops pipes, cleans (not frees) rx/tx chains + * usb_net_detach() frees the rx/tx chains + * + * Setup un_ed[] with valid end points before calling usb_net_init_rx_tx(). + * Will return with un_ep[] initialised upon success. + */ +int usb_net_init_rx_tx(struct usb_net * const, unsigned, unsigned); + +/* mii */ +void usb_net_lock_mii(struct usb_net *); +void usb_net_lock_mii_un_locked(struct usb_net *); +void usb_net_unlock_mii(struct usb_net *); +void usb_net_unlock_mii_un_locked(struct usb_net *); + +int usb_net_miibus_readreg(device_t, int, int, uint16_t *); +int usb_net_miibus_writereg(device_t, int, int, uint16_t); +void usb_net_miibus_statchg(struct ifnet *); + +/* interrupt handling */ +void usb_net_enqueue(struct usb_net * const, uint8_t *, size_t, int); +void usb_net_txeof(struct usbd_xfer *, void *, usbd_status); +void usb_net_start(struct ifnet *); + +/* autoconf */ +void usb_net_attach(struct usb_net *un, const char *, unsigned, unsigned); +void usb_net_detach(struct usb_net *un); +int usb_net_activate(device_t, devact_t); + +/* stop backend */ +void usb_net_stop(struct usb_net *, struct ifnet *, int); + +/* generic ioctl handler */ +int usb_net_ioctl(struct ifnet *, u_long, void *); + +#endif /* _DEV_USB_USB_NET_H */ Index: sys/modules/Makefile =================================================================== RCS file: /cvsroot/src/sys/modules/Makefile,v retrieving revision 1.222 diff -p -u -r1.222 Makefile --- sys/modules/Makefile 20 Jun 2019 03:31:29 -0000 1.222 +++ sys/modules/Makefile 29 Jul 2019 07:43:54 -0000 @@ -163,6 +163,7 @@ SUBDIR+= udf SUBDIR+= ufs SUBDIR+= umap SUBDIR+= union +SUBDIR+= usb_net SUBDIR+= usbverbose SUBDIR+= vcoda SUBDIR+= v7fs Index: sys/modules/usb_net/Makefile =================================================================== RCS file: sys/modules/usb_net/Makefile diff -N sys/modules/usb_net/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/modules/usb_net/Makefile 29 Jul 2019 07:43:54 -0000 @@ -0,0 +1,12 @@ +# $NetBSD$ + +.include "../Makefile.inc" + +.PATH: ${S}/dev/usb + +KMOD= usb_net +SRCS= usb_net.c + +WARNS= 3 + +.include