Module Name: src Committed By: mrg Date: Wed Jul 31 09:13:17 UTC 2019
Modified Files: src/distrib/sets/lists/modules: mi src/sys/dev/usb: files.usb if_axen.c if_axenreg.h if_cdce.c src/sys/modules: Makefile Added Files: src/sys/dev/usb: usbnet.c usbnet.h src/sys/modules/usbnet: Makefile Log Message: introduce a library of common code / backends to share code between USB ethernet drivers. usbnet.h introduces a new set of APIs to provide common solutions for these driver features: - 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: much of attach, and detach/activate can use common versions directly. currently, only axen(4) and cdce(4) are converted. the reductions in these drivers are quite significant: if_cdce.c is reduced from 1000 lines to 320 lines, and if_axen is reduced from 1902 lines to 1021 lines. add a "usbnet" module and make the if_axen module depend upon it. To generate a diff of this commit: cvs rdiff -u -r1.122 -r1.123 src/distrib/sets/lists/modules/mi cvs rdiff -u -r1.156 -r1.157 src/sys/dev/usb/files.usb cvs rdiff -u -r1.50 -r1.51 src/sys/dev/usb/if_axen.c cvs rdiff -u -r1.14 -r1.15 src/sys/dev/usb/if_axenreg.h cvs rdiff -u -r1.53 -r1.54 src/sys/dev/usb/if_cdce.c cvs rdiff -u -r0 -r1.1 src/sys/dev/usb/usbnet.c src/sys/dev/usb/usbnet.h cvs rdiff -u -r1.222 -r1.223 src/sys/modules/Makefile cvs rdiff -u -r0 -r1.1 src/sys/modules/usbnet/Makefile Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/modules/mi diff -u src/distrib/sets/lists/modules/mi:1.122 src/distrib/sets/lists/modules/mi:1.123 --- src/distrib/sets/lists/modules/mi:1.122 Thu Jun 20 03:31:55 2019 +++ src/distrib/sets/lists/modules/mi Wed Jul 31 09:13:16 2019 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.122 2019/06/20 03:31:55 pgoyette Exp $ +# $NetBSD: mi,v 1.123 2019/07/31 09:13:16 mrg Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -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@/usbnet base-kernel-modules kmod +./@MODULEDIR@/usbnet/usbnet.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: src/sys/dev/usb/files.usb diff -u src/sys/dev/usb/files.usb:1.156 src/sys/dev/usb/files.usb:1.157 --- src/sys/dev/usb/files.usb:1.156 Wed May 8 13:40:19 2019 +++ src/sys/dev/usb/files.usb Wed Jul 31 09:13:16 2019 @@ -1,4 +1,4 @@ -# $NetBSD: files.usb,v 1.156 2019/05/08 13:40:19 isaki Exp $ +# $NetBSD: files.usb,v 1.157 2019/07/31 09:13:16 mrg Exp $ # # Config file and device description for machine-independent USB code. # Included by ports that need it. Ports that use it must provide @@ -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 usbnet: usb +file dev/usb/usbnet.c usbnet + 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, usbnet attach axen at usbdevif file dev/usb/if_axen.c axen Index: src/sys/dev/usb/if_axen.c diff -u src/sys/dev/usb/if_axen.c:1.50 src/sys/dev/usb/if_axen.c:1.51 --- src/sys/dev/usb/if_axen.c:1.50 Mon Jul 15 03:14:22 2019 +++ src/sys/dev/usb/if_axen.c Wed Jul 31 09:13:16 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: if_axen.c,v 1.50 2019/07/15 03:14:22 mrg Exp $ */ +/* $NetBSD: if_axen.c,v 1.51 2019/07/31 09:13:16 mrg Exp $ */ /* $OpenBSD: if_axen.c,v 1.3 2013/10/21 10:10:22 yuo Exp $ */ /* @@ -23,43 +23,19 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_axen.c,v 1.50 2019/07/15 03:14:22 mrg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_axen.c,v 1.51 2019/07/31 09:13:16 mrg Exp $"); #ifdef _KERNEL_OPT -#include "opt_inet.h" #include "opt_usb.h" #endif #include <sys/param.h> -#include <sys/bus.h> -#include <sys/device.h> -#include <sys/kernel.h> -#include <sys/mbuf.h> #include <sys/module.h> -#include <sys/socket.h> -#include <sys/sockio.h> -#include <sys/systm.h> - -#include <sys/rndsource.h> - -#include <net/if.h> -#include <net/if_dl.h> -#include <net/if_ether.h> -#include <net/if_media.h> - -#include <net/bpf.h> #include <netinet/in.h> /* XXX for netinet/ip.h */ #include <netinet/ip.h> /* XXX for IP_MAXPACKET */ -#include <dev/mii/mii.h> -#include <dev/mii/miivar.h> - -#include <dev/usb/usb.h> -#include <dev/usb/usbdi.h> -#include <dev/usb/usbdi_util.h> -#include <dev/usb/usbdivar.h> -#include <dev/usb/usbdevs.h> +#include <dev/usb/usbnet.h> #include <dev/usb/if_axenreg.h> @@ -72,70 +48,13 @@ 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; - 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; + struct usbnet axen_un; int axen_rev; - -#define sc_if axen_ec.ec_if }; -#define GET_MII(sc) (&(sc)->axen_mii) -#define GET_IFP(sc) (&(sc)->sc_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 */ @@ -156,94 +75,34 @@ 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, usbnet_detach, usbnet_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 usbnet *, struct mbuf *, + struct usbnet_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 usbnet *, int, int, uint16_t *); +static usbd_status axen_mii_write_reg(struct usbnet *, int, int, uint16_t); +static void axen_rxeof_loop(struct usbnet *, struct usbd_xfer *, + struct usbnet_chain *, uint32_t); static int axen_cmd(struct axen_softc *sc, int cmd, int index, int val, void *buf) { + struct usbnet * 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 +114,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 +126,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 usbnet *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); - - axen_lock_mii(sc); - err = axen_cmd(sc, AXEN_CMD_MII_READ_REG, reg, phy, &data); - axen_unlock_mii(sc); + if (!err) { + *val = le16toh(data); - if (err) { - aprint_error_dev(sc->axen_dev, "read PHY failed: %d\n", err); - return err; + if (reg == MII_BMSR) + *val &= ~BMSR_EXTCAP; } - *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; - } - - 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 usbnet *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); - - 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; - } + struct axen_softc * const sc = un->un_sc; + uint16_t uval = htole16(val); - 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 usbnet * const un = ifp->if_softc; + struct axen_softc * const sc = un->un_sc; + struct mii_data * const mii = usbnet_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 +182,7 @@ axen_miibus_statchg(struct ifnet *ifp) } /* Lost link, do nothing. */ - if (sc->axen_link == 0) + if (!un->un_link) return; val = 0; @@ -384,58 +205,19 @@ axen_miibus_statchg(struct ifnet *ifp) DPRINTF(("%s: val=0x%x\n", __func__, val)); wval = htole16(val); - axen_lock_mii(sc); + usbnet_lock_mii(un); err = axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval); - axen_unlock_mii(sc); - if (err) { - aprint_error_dev(sc->axen_dev, "media change failed\n"); - return; - } -} - -/* - * Set media options. - */ -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; - - sc->axen_link = 0; - - if (mii->mii_instance) { - struct mii_softc *miisc; - - LIST_FOREACH(miisc, &mii->mii_phys, mii_list) - mii_phy_reset(miisc); - } - - if ((rc = mii_mediachg(mii)) == ENXIO) - return 0; - return rc; -} - -/* - * Report current media status. - */ -static void -axen_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) -{ - 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; + usbnet_unlock_mii(un); + if (err) + aprint_error_dev(un->un_dev, "media change failed\n"); } static void -axen_iff_locked(struct axen_softc *sc) +axen_setiff_locked(struct usbnet *un) { - struct ifnet *ifp = GET_IFP(sc); - struct ethercom *ec = &sc->axen_ec; + struct axen_softc * const sc = un->un_sc; + struct ifnet * const ifp = usbnet_ifp(un); + struct ethercom *ec = &un->un_ec; struct ether_multi *enm; struct ether_multistep step; uint32_t h = 0; @@ -443,10 +225,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 +239,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 +250,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 +259,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 +268,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 +282,20 @@ allmulti: } static void -axen_iff(struct axen_softc *sc) +axen_setiff(struct usbnet *un) { - - axen_lock_mii(sc); - axen_iff_locked(sc); - axen_unlock_mii(sc); + usbnet_lock_mii(un); + axen_setiff_locked(un); + usbnet_unlock_mii(un); } static void axen_reset(struct axen_softc *sc) { + struct usbnet * 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 +303,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 +310,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 usbnet * const un = &sc->axen_un; int i, retry; uint8_t eeprom[20]; uint16_t csum; @@ -551,7 +329,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 +362,13 @@ axen_get_eaddr(struct axen_softc *sc, vo static void axen_ax88179_init(struct axen_softc *sc) { + struct usbnet * const un = &sc->axen_un; struct axen_qctrl qctrl; uint16_t ctl, temp; uint16_t wval; uint8_t val; - axen_lock_mii(sc); + usbnet_lock_mii(un); /* XXX: ? */ axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_UNK_05, &val); @@ -612,12 +391,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 +412,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 +452,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); + usbnet_unlock_mii(un); return; } axen_cmd(sc, AXEN_CMD_MAC_SET_RXSR, 5, AXEN_RX_BULKIN_QCTRL, &qctrl); @@ -710,42 +489,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); + usbnet_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, + usbnet_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, + usbnet_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); + usbnet_miibus_writereg(un->un_dev, un->un_phyno, 0x1F, 0x0005); + usbnet_miibus_writereg(un->un_dev, un->un_phyno, 0x0C, 0x0000); + usbnet_miibus_readreg(un->un_dev, un->un_phyno, 0x0001, &wval); + usbnet_miibus_writereg(un->un_dev, un->un_phyno, 0x01, wval | 0x0080); + usbnet_miibus_writereg(un->un_dev, un->un_phyno, 0x1F, 0x0000); #endif } static void -axen_setcoe(struct axen_softc *sc) +axen_setoe_locked(struct usbnet *un) { - struct ifnet *ifp = GET_IFP(sc); + struct axen_softc * const sc = un->un_sc; + struct ifnet * const ifp = usbnet_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 +553,37 @@ axen_setcoe(struct axen_softc *sc) axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_TX_COE, &val); } +static void +axen_setoe(struct usbnet *un) +{ + + usbnet_lock_mii(un); + axen_setoe_locked(un); + usbnet_unlock_mii(un); +} + +static int +axen_ioctl_cb(struct ifnet *ifp, u_long cmd, void *data) +{ + struct usbnet * const un = ifp->if_softc; + + switch (cmd) { + case SIOCSIFFLAGS: + case SIOCSETHERCAP: + case SIOCADDMULTI: + case SIOCDELMULTI: + axen_setiff(un); + break; + case SIOCSIFCAP: + axen_setoe(un); + break; + default: + break; + } + + return 0; +} + static int axen_match(device_t parent, cfdata_t match, void *aux) { @@ -787,24 +597,36 @@ static void axen_attach(device_t parent, device_t self, void *aux) { struct axen_softc * const sc = device_private(self); + struct usbnet * const un = &sc->axen_un; struct usb_attach_arg *uaa = aux; struct usbd_device *dev = uaa->uaa_device; usbd_status err; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; - struct mii_data *mii; - uint8_t eaddr[ETHER_ADDR_LEN]; char *devinfop; - const char *devname = device_xname(self); - struct ifnet *ifp; + uint16_t axen_flags; int i; + /* Switch to usbnet 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_stop_cb = axen_stop_cb; + un->un_ioctl_cb = axen_ioctl_cb; + un->un_read_reg_cb = axen_mii_read_reg; + un->un_write_reg_cb = axen_mii_write_reg; + un->un_statchg_cb = axen_miibus_statchg; + un->un_tx_prepare_cb = axen_tx_prepare; + un->un_rx_loop_cb = axen_rxeof_loop; + un->un_init_cb = axen_init; + 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 +638,82 @@ axen_attach(device_t parent, device_t se return; } - sc->axen_flags = axen_lookup(uaa->uaa_vendor, uaa->uaa_product)->axen_flags; + 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); - - 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[USBNET_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[USBNET_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[USBNET_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"); + usbnet_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); - if (axen_get_eaddr(sc, &eaddr)) { - axen_unlock_mii(sc); + usbnet_lock_mii(un); + if (axen_get_eaddr(sc, &un->un_eaddr)) { + usbnet_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); return; } - axen_unlock_mii(sc); + usbnet_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"); - aprint_normal_dev(self, "Ethernet address %s\n", ether_sprintf(eaddr)); - - /* Initialize interface info. */ - - ifp = &sc->sc_if; - ifp->if_softc = sc; - 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_init = axen_init; - ifp->if_stop = axen_stop; - - IFQ_SET_READY(&ifp->if_snd); + else + aprint_normal_dev(self, "(unknown)\n"); + aprint_normal_dev(self, "Ethernet address %s\n", + ether_sprintf(un->un_eaddr)); - sc->axen_ec.ec_capabilities = ETHERCAP_VLAN_MTU; + struct ifnet *ifp = usbnet_ifp(un); + un->un_ec.ec_capabilities = ETHERCAP_VLAN_MTU; /* Adapter does not support TSOv6 (They call it LSOv2). */ ifp->if_capabilities |= IFCAP_TSOv4 | @@ -926,252 +723,73 @@ axen_attach(device_t parent, device_t se IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_TCPv6_Tx | IFCAP_CSUM_UDPv6_Rx | IFCAP_CSUM_UDPv6_Tx; - /* Initialize MII/media info. */ - mii = &sc->axen_mii; - mii->mii_ifp = ifp; - mii->mii_readreg = axen_miibus_readreg; - mii->mii_writereg = axen_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); - mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); - - if (LIST_FIRST(&mii->mii_phys) == NULL) { - ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); - ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE); - } else - ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); - - /* 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); - - if (!pmf_device_register(self, NULL, NULL)) - aprint_error_dev(self, "couldn't establish power handler\n"); + usbnet_attach_ifp(un, true, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST, + 0); } static int -axen_detach(device_t self, int flags) +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); - - 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__)); + int enabled_flags = ifp->if_csum_flags_rx; + int csum_flags = 0; + int l3_type, l4_type; - /* Detached before attached finished, so just bail out. */ - if (!sc->axen_attached) + if (enabled_flags == 0) return 0; - 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) { - /* Wait for processes to go away */ - cv_wait(&sc->axen_detachcv, &sc->axen_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) - aprint_debug_dev(self, "detach has active endpoints\n"); -#endif - - mutex_exit(&sc->axen_lock); - - 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); - 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); - - return 0; -} - -static int -axen_activate(device_t self, devact_t act) -{ - struct axen_softc * const sc = device_private(self); - struct ifnet *ifp = GET_IFP(sc); + l3_type = (pkt_hdr & AXEN_RXHDR_L3_TYPE_MASK) >> + AXEN_RXHDR_L3_TYPE_OFFSET; - DPRINTFN(2,("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); + if (l3_type == AXEN_RXHDR_L3_TYPE_IPV4) + csum_flags |= M_CSUM_IPv4; - 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); + l4_type = (pkt_hdr & AXEN_RXHDR_L4_TYPE_MASK) >> + AXEN_RXHDR_L4_TYPE_OFFSET; - return 0; + 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: - 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; -} - -static int -axen_tx_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_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); - } + 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 usbnet * un, struct usbd_xfer *xfer, + struct usbnet_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 = usbnet_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 +802,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 +824,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; - } + usbnet_enqueue(un, buf + 2, pkt_len - 6, + axen_csum_flags_rx(ifp, pkt_hdr)); nextpkt: /* @@ -1279,191 +870,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 usbnet *un, struct mbuf *m, struct usbnet_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,126 +894,55 @@ 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; - } - - 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; + memcpy(c->unc_buf, &hdr, sizeof(hdr)); + m_copydata(m, 0, m->m_pkthdr.len, c->unc_buf + sizeof(hdr)); - 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 usbnet * 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 */ - axen_stop_locked(ifp, 1); + usbnet_stop(un, ifp, 1); /* Reset the ethernet interface. */ axen_reset(sc); - axen_lock_mii_sc_locked(sc); + usbnet_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_setiff_locked(un); /* Enable receiver, set RX mode */ axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); @@ -1604,275 +951,47 @@ 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; - } + usbnet_unlock_mii_un_locked(un); - /* 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]; - - 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 usbnet_init_rx_tx(un, 0, 0); } static int axen_init(struct ifnet *ifp) { - struct axen_softc * const sc = ifp->if_softc; + struct usbnet * 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 usbnet * 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); + usbnet_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; - } - - 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; - } - - 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; - } -} - -static void -axen_stop(struct ifnet *ifp, int disable) -{ - struct axen_softc * const sc = ifp->if_softc; - - mutex_enter(&sc->axen_lock); - axen_stop_locked(ifp, disable); - mutex_exit(&sc->axen_lock); + usbnet_unlock_mii_un_locked(un); } -MODULE(MODULE_CLASS_DRIVER, if_axen, NULL); +MODULE(MODULE_CLASS_DRIVER, if_axen, "usbnet"); #ifdef _MODULE #include "ioconf.c" Index: src/sys/dev/usb/if_axenreg.h diff -u src/sys/dev/usb/if_axenreg.h:1.14 src/sys/dev/usb/if_axenreg.h:1.15 --- src/sys/dev/usb/if_axenreg.h:1.14 Tue Jun 18 09:34:57 2019 +++ src/sys/dev/usb/if_axenreg.h Wed Jul 31 09:13:16 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: if_axenreg.h,v 1.14 2019/06/18 09:34:57 mrg Exp $ */ +/* $NetBSD: if_axenreg.h,v 1.15 2019/07/31 09:13:16 mrg Exp $ */ /* $OpenBSD: if_axenreg.h,v 1.1 2013/10/07 05:37:41 yuo Exp $ */ /* @@ -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: src/sys/dev/usb/if_cdce.c diff -u src/sys/dev/usb/if_cdce.c:1.53 src/sys/dev/usb/if_cdce.c:1.54 --- src/sys/dev/usb/if_cdce.c:1.53 Sun Jul 21 10:27:56 2019 +++ src/sys/dev/usb/if_cdce.c Wed Jul 31 09:13:16 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: if_cdce.c,v 1.53 2019/07/21 10:27:56 mrg Exp $ */ +/* $NetBSD: if_cdce.c,v 1.54 2019/07/31 09:13:16 mrg Exp $ */ /* * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul <wp...@windriver.com> @@ -37,43 +37,15 @@ /* * USB Communication Device Class (Ethernet Networking Control Model) * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf - * */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_cdce.c,v 1.53 2019/07/21 10:27:56 mrg Exp $"); - -#ifdef _KERNEL_OPT -#include "opt_inet.h" -#endif +__KERNEL_RCSID(0, "$NetBSD: if_cdce.c,v 1.54 2019/07/31 09:13:16 mrg Exp $"); #include <sys/param.h> -#include <sys/systm.h> -#include <sys/sockio.h> -#include <sys/mbuf.h> #include <sys/kernel.h> -#include <sys/socket.h> -#include <sys/device.h> -#include <sys/rndsource.h> - -#include <net/if.h> -#include <net/if_arp.h> -#include <net/if_dl.h> -#include <net/if_media.h> - -#include <net/bpf.h> - -#include <net/if_ether.h> -#ifdef INET -#include <netinet/in.h> -#include <netinet/if_inarp.h> -#endif - -#include <dev/usb/usb.h> -#include <dev/usb/usbdi.h> -#include <dev/usb/usbdivar.h> -#include <dev/usb/usbdi_util.h> -#include <dev/usb/usbdevs.h> + +#include <dev/usb/usbnet.h> #include <dev/usb/usbcdc.h> #include <dev/usb/if_cdcereg.h> @@ -85,69 +57,11 @@ struct cdce_type { #define CDCE_NO_UNION 2 }; -struct cdce_softc; - -struct cdce_chain { - struct cdce_softc *cdce_sc; - struct usbd_xfer *cdce_xfer; - char *cdce_buf; - struct mbuf *cdce_mbuf; - int cdce_accum; - int cdce_idx; -}; - -struct cdce_cdata { - struct cdce_chain cdce_rx_chain[CDCE_RX_LIST_CNT]; - struct cdce_chain cdce_tx_chain[CDCE_TX_LIST_CNT]; - int cdce_tx_prod; - int cdce_tx_cnt; -}; - struct cdce_softc { - device_t cdce_dev; - struct ethercom cdce_ec; - krndsource_t rnd_source; -#define GET_IFP(sc) (&(sc)->cdce_ec.ec_if) - struct usbd_device * cdce_udev; - struct usbd_interface * cdce_ctl_iface; - struct usbd_interface * cdce_data_iface; - int cdce_bulkin_no; - struct usbd_pipe * cdce_bulkin_pipe; - int cdce_bulkout_no; - struct usbd_pipe * cdce_bulkout_pipe; - int cdce_unit; - struct cdce_cdata cdce_cdata; - int cdce_rxeof_errors; + struct usbnet cdce_un; uint16_t cdce_flags; - uint16_t cdce_timer; - - bool cdce_attached; - bool cdce_dying; - bool cdce_stopping; - - struct usb_task cdce_tick_task; - struct callout cdce_stat_ch; - - kmutex_t cdce_lock; - kmutex_t cdce_mii_lock; - kmutex_t cdce_rxlock; - kmutex_t cdce_txlock; }; -static int cdce_tx_list_init(struct cdce_softc *); -static int cdce_rx_list_init(struct cdce_softc *); -static int cdce_newbuf(struct cdce_softc *, struct cdce_chain *, - struct mbuf *); -static int cdce_encap(struct cdce_softc *, struct mbuf *, int); -static void cdce_rxeof(struct usbd_xfer *, void *, usbd_status); -static void cdce_txeof(struct usbd_xfer *, void *, usbd_status); -static void cdce_start(struct ifnet *); -static int cdce_ioctl(struct ifnet *, u_long, void *); -static void cdce_init(void *); -static void cdce_stop(struct cdce_softc *); -static void cdce_tick(void *); -static void cdce_tick_task(void *); - static const struct cdce_type cdce_devs[] = { {{ USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632 }, CDCE_NO_UNION }, {{ USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX }, CDCE_NO_UNION }, @@ -164,15 +78,18 @@ static const struct cdce_type cdce_devs[ #define cdce_lookup(v, p) \ ((const struct cdce_type *)usb_lookup(cdce_devs, v, p)) -int cdce_match(device_t, cfdata_t, void *); -void cdce_attach(device_t, device_t, void *); -int cdce_detach(device_t, int); -int cdce_activate(device_t, enum devact); +static int cdce_match(device_t, cfdata_t, void *); +static void cdce_attach(device_t, device_t, void *); +static int cdce_init(struct ifnet *); +static void cdce_rxeof_loop(struct usbnet *, struct usbd_xfer *, + struct usbnet_chain *, uint32_t); +static unsigned cdce_tx_prepare(struct usbnet *, struct mbuf *, + struct usbnet_chain *); CFATTACH_DECL_NEW(cdce, sizeof(struct cdce_softc), cdce_match, cdce_attach, - cdce_detach, cdce_activate); + usbnet_detach, usbnet_activate); -int +static int cdce_match(device_t parent, cfdata_t match, void *aux) { struct usbif_attach_arg *uiaa = aux; @@ -187,13 +104,13 @@ cdce_match(device_t parent, cfdata_t mat return UMATCH_NONE; } -void +static void cdce_attach(device_t parent, device_t self, void *aux) { - struct cdce_softc *sc = device_private(self); - struct usbif_attach_arg *uiaa = aux; + struct cdce_softc *sc = device_private(self); + struct usbnet * const un = &sc->cdce_un; + struct usbif_attach_arg *uiaa = aux; char *devinfop; - struct ifnet *ifp; struct usbd_device *dev = uiaa->uiaa_device; const struct cdce_type *t; usb_interface_descriptor_t *id; @@ -202,30 +119,37 @@ cdce_attach(device_t parent, device_t se usb_config_descriptor_t *cd; int data_ifcno; int i, j, numalts; - u_char eaddr[ETHER_ADDR_LEN]; const usb_cdc_ethernet_descriptor_t *ue; char eaddr_str[USB_MAX_ENCODED_STRING_LEN]; - sc->cdce_dev = self; + /* Switch to usbnet for device_private() */ + self->dv_private = un; aprint_naive("\n"); aprint_normal("\n"); - devinfop = usbd_devinfo_alloc(dev, 0); aprint_normal_dev(self, "%s\n", devinfop); usbd_devinfo_free(devinfop); - sc->cdce_udev = uiaa->uiaa_device; - sc->cdce_ctl_iface = uiaa->uiaa_iface; + un->un_dev = self; + un->un_udev = dev; + un->un_sc = sc; + un->un_init_cb = cdce_init; + un->un_tx_prepare_cb = cdce_tx_prepare; + un->un_rx_loop_cb = cdce_rxeof_loop; + un->un_rx_xfer_flags = USBD_SHORT_XFER_OK; + un->un_tx_xfer_flags = USBD_FORCE_SHORT_XFER; + un->un_cdata.uncd_rx_bufsz = CDCE_BUFSZ; + un->un_cdata.uncd_tx_bufsz = CDCE_BUFSZ; t = cdce_lookup(uiaa->uiaa_vendor, uiaa->uiaa_product); if (t) sc->cdce_flags = t->cdce_flags; if (sc->cdce_flags & CDCE_NO_UNION) - sc->cdce_data_iface = sc->cdce_ctl_iface; + un->un_iface = uiaa->uiaa_iface; else { - ud = (const usb_cdc_union_descriptor_t *)usb_find_desc(sc->cdce_udev, + ud = (const usb_cdc_union_descriptor_t *)usb_find_desc(un->un_udev, UDESC_CS_INTERFACE, UDESCSUB_CDC_UNION); if (ud == NULL) { aprint_error_dev(self, "no union descriptor\n"); @@ -239,14 +163,13 @@ cdce_attach(device_t parent, device_t se uiaa->uiaa_ifaces[i]); if (id != NULL && id->bInterfaceNumber == data_ifcno) { - sc->cdce_data_iface = uiaa->uiaa_ifaces[i]; + un->un_iface = uiaa->uiaa_ifaces[i]; uiaa->uiaa_ifaces[i] = NULL; } } } } - - if (sc->cdce_data_iface == NULL) { + if (un->un_iface == NULL) { aprint_error_dev(self, "no data interface\n"); return; } @@ -269,21 +192,21 @@ cdce_attach(device_t parent, device_t se * available interface settings looking for one with both IN and OUT * endpoints. */ - id = usbd_get_interface_descriptor(sc->cdce_data_iface); - cd = usbd_get_config_descriptor(sc->cdce_udev); + id = usbd_get_interface_descriptor(un->un_iface); + cd = usbd_get_config_descriptor(un->un_udev); numalts = usbd_get_no_alts(cd, id->bInterfaceNumber); for (j = 0; j < numalts; j++) { - if (usbd_set_interface(sc->cdce_data_iface, j)) { - aprint_error_dev(sc->cdce_dev, + if (usbd_set_interface(un->un_iface, j)) { + aprint_error_dev(un->un_dev, "setting alternate interface failed\n"); return; } /* Find endpoints. */ - id = usbd_get_interface_descriptor(sc->cdce_data_iface); - sc->cdce_bulkin_no = sc->cdce_bulkout_no = -1; + id = usbd_get_interface_descriptor(un->un_iface); + un->un_ed[USBNET_ENDPT_RX] = un->un_ed[USBNET_ENDPT_TX] = -1; for (i = 0; i < id->bNumEndpoints; i++) { - ed = usbd_interface2endpoint_descriptor(sc->cdce_data_iface, i); + ed = usbd_interface2endpoint_descriptor(un->un_iface, i); if (!ed) { aprint_error_dev(self, "could not read endpoint descriptor\n"); @@ -291,10 +214,10 @@ cdce_attach(device_t parent, device_t se } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { - sc->cdce_bulkin_no = ed->bEndpointAddress; + un->un_ed[USBNET_ENDPT_RX] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { - sc->cdce_bulkout_no = ed->bEndpointAddress; + un->un_ed[USBNET_ENDPT_TX] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { /* XXX: CDC spec defines an interrupt pipe, but it is not @@ -304,15 +227,15 @@ cdce_attach(device_t parent, device_t se } } /* If we found something, try and use it... */ - if ((sc->cdce_bulkin_no != -1) && (sc->cdce_bulkout_no != -1)) + if (un->un_ed[USBNET_ENDPT_RX] != -1 && un->un_ed[USBNET_ENDPT_TX] != -1) break; } - if (sc->cdce_bulkin_no == -1) { + if (un->un_ed[USBNET_ENDPT_RX] == -1) { aprint_error_dev(self, "could not find data bulk in\n"); return; } - if (sc->cdce_bulkout_no == -1 ) { + if (un->un_ed[USBNET_ENDPT_TX] == -1 ) { aprint_error_dev(self, "could not find data bulk out\n"); return; } @@ -320,681 +243,78 @@ cdce_attach(device_t parent, device_t se ue = (const usb_cdc_ethernet_descriptor_t *)usb_find_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_ENF); if (!ue || usbd_get_string(dev, ue->iMacAddress, eaddr_str) || - ether_aton_r(eaddr, sizeof(eaddr), eaddr_str)) { + ether_aton_r(un->un_eaddr, sizeof(un->un_eaddr), eaddr_str)) { aprint_normal_dev(self, "faking address\n"); - eaddr[0]= 0x2a; - memcpy(&eaddr[1], &hardclock_ticks, sizeof(uint32_t)); - eaddr[5] = (uint8_t)(device_unit(sc->cdce_dev)); + un->un_eaddr[0] = 0x2a; + memcpy(&un->un_eaddr[1], &hardclock_ticks, sizeof(uint32_t)); + un->un_eaddr[5] = (uint8_t)(device_unit(un->un_dev)); } - mutex_init(&sc->cdce_txlock, MUTEX_DEFAULT, IPL_SOFTUSB); - mutex_init(&sc->cdce_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB); - mutex_init(&sc->cdce_lock, MUTEX_DEFAULT, IPL_NONE); - - usb_init_task(&sc->cdce_tick_task, cdce_tick_task, sc, USB_TASKQ_MPSAFE); - - aprint_normal_dev(self, "address %s\n", ether_sprintf(eaddr)); - - ifp = GET_IFP(sc); - ifp->if_softc = sc; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_extflags = IFEF_MPSAFE; - ifp->if_ioctl = cdce_ioctl; - ifp->if_start = cdce_start; - strlcpy(ifp->if_xname, device_xname(sc->cdce_dev), IFNAMSIZ); - - IFQ_SET_READY(&ifp->if_snd); - - if_attach(ifp); - ether_ifattach(ifp, eaddr); - - callout_init(&sc->cdce_stat_ch, CALLOUT_MPSAFE); - callout_setfunc(&sc->cdce_stat_ch, cdce_tick, sc); + aprint_normal_dev(self, "address %s\n", ether_sprintf(un->un_eaddr)); - sc->cdce_attached = true; - - usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->cdce_udev, - sc->cdce_dev); - - if (!pmf_device_register(self, NULL, NULL)) - aprint_error_dev(self, "couldn't establish power handler\n"); - - return; + usbnet_attach(un, "cdcedet", CDCE_RX_LIST_CNT, CDCE_TX_LIST_CNT); + usbnet_attach_ifp(un, false, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST, + 0); } -int -cdce_detach(device_t self, int flags) +static int +cdce_init(struct ifnet *ifp) { - struct cdce_softc *sc = device_private(self); - struct ifnet *ifp = GET_IFP(sc); + struct usbnet *un = ifp->if_softc; + int rv; - mutex_enter(&sc->cdce_lock); - sc->cdce_dying = true; - mutex_exit(&sc->cdce_lock); - - if (!sc->cdce_attached) - return 0; - - pmf_device_deregister(self); - - callout_halt(&sc->cdce_stat_ch, NULL); - usb_rem_task_wait(sc->cdce_udev, &sc->cdce_tick_task, - USB_TASKQ_DRIVER, NULL); - - if (ifp->if_flags & IFF_RUNNING) { - IFNET_LOCK(ifp); - cdce_stop(sc); - IFNET_UNLOCK(ifp); + mutex_enter(&un->un_lock); + if (un->un_dying) + rv = EIO; + else { + usbnet_stop(un, ifp, 1); + rv = usbnet_init_rx_tx(un, 0, 0); + if (rv == 0) + un->un_link = true; } + mutex_exit(&un->un_lock); - callout_destroy(&sc->cdce_stat_ch); - ether_ifdetach(ifp); - if_detach(ifp); - sc->cdce_attached = false; - - usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->cdce_udev,sc->cdce_dev); - - mutex_destroy(&sc->cdce_lock); - mutex_destroy(&sc->cdce_rxlock); - mutex_destroy(&sc->cdce_txlock); - - return 0; + return rv; } static void -cdce_start_locked(struct ifnet *ifp) +cdce_rxeof_loop(struct usbnet * un, struct usbd_xfer *xfer, + struct usbnet_chain *c, uint32_t total_len) { - struct cdce_softc *sc = ifp->if_softc; - struct mbuf *m_head = NULL; + struct ifnet *ifp = usbnet_ifp(un); + struct cdce_softc *sc = un->un_sc; - KASSERT(mutex_owned(&sc->cdce_txlock)); + KASSERT(mutex_owned(&un->un_rxlock)); - if (sc->cdce_dying || sc->cdce_cdata.cdce_tx_cnt == CDCE_TX_LIST_CNT) - return; - - IFQ_POLL(&ifp->if_snd, m_head); - if (m_head == NULL) - return; + /* Strip off CRC added by Zaurus, if present */ + if (sc->cdce_flags & CDCE_ZAURUS && total_len > 4) + total_len -= 4; - if (cdce_encap(sc, m_head, 0)) { - ifp->if_flags |= IFF_OACTIVE; + if (total_len < sizeof(struct ether_header)) { + ifp->if_ierrors++; return; } - IFQ_DEQUEUE(&ifp->if_snd, m_head); - - bpf_mtap(ifp, m_head, BPF_D_OUT); - - /* - * Set a timeout in case the chip goes out to lunch. - */ - sc->cdce_timer = 6; -} - -static void -cdce_start(struct ifnet *ifp) -{ - struct cdce_softc * const sc = ifp->if_softc; - - mutex_enter(&sc->cdce_txlock); - cdce_start_locked(ifp); - mutex_exit(&sc->cdce_txlock); + usbnet_enqueue(un, c->unc_buf, total_len, 0); } -static int -cdce_encap(struct cdce_softc *sc, struct mbuf *m, int idx) +static unsigned +cdce_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c) { - struct cdce_chain *c; - usbd_status err; + struct cdce_softc *sc = un->un_sc; int extra = 0; - KASSERT(mutex_owned(&sc->cdce_txlock)); + KASSERT(mutex_owned(&un->un_txlock)); - c = &sc->cdce_cdata.cdce_tx_chain[idx]; - - m_copydata(m, 0, m->m_pkthdr.len, c->cdce_buf); + m_copydata(m, 0, m->m_pkthdr.len, c->unc_buf); if (sc->cdce_flags & CDCE_ZAURUS) { /* Zaurus wants a 32-bit CRC appended to every frame */ uint32_t crc; - crc = htole32(~ether_crc32_le(c->cdce_buf, m->m_pkthdr.len)); - memcpy(c->cdce_buf + m->m_pkthdr.len, &crc, sizeof(crc)); + crc = htole32(~ether_crc32_le(c->unc_buf, m->m_pkthdr.len)); + memcpy(c->unc_buf + m->m_pkthdr.len, &crc, sizeof(crc)); extra = sizeof(crc); } - c->cdce_mbuf = m; - - usbd_setup_xfer(c->cdce_xfer, c, c->cdce_buf, m->m_pkthdr.len + extra, - USBD_FORCE_SHORT_XFER, 10000, cdce_txeof); - err = usbd_transfer(c->cdce_xfer); - if (err != USBD_IN_PROGRESS) { - /* XXXSMP IFNET_LOCK */ - cdce_stop(sc); - return EIO; - } - - sc->cdce_cdata.cdce_tx_cnt++; - KASSERT(sc->cdce_cdata.cdce_tx_cnt <= CDCE_TX_LIST_CNT); - - return 0; -} - -static void -cdce_stop_locked(struct cdce_softc *sc) -{ - usbd_status err; - struct ifnet *ifp = GET_IFP(sc); - int i; - - /* XXXSMP can't KASSERT(IFNET_LOCKED(ifp)); */ - KASSERT(mutex_owned(&sc->cdce_lock)); - - mutex_enter(&sc->cdce_rxlock); - mutex_enter(&sc->cdce_txlock); - sc->cdce_stopping = true; - mutex_exit(&sc->cdce_txlock); - mutex_exit(&sc->cdce_rxlock); - - /* - * 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->cdce_timer = 0; - - callout_stop(&sc->cdce_stat_ch); - - if (sc->cdce_bulkin_pipe != NULL) { - err = usbd_abort_pipe(sc->cdce_bulkin_pipe); - if (err) - printf("%s: abort rx pipe failed: %s\n", - device_xname(sc->cdce_dev), usbd_errstr(err)); - } - - if (sc->cdce_bulkout_pipe != NULL) { - err = usbd_abort_pipe(sc->cdce_bulkout_pipe); - if (err) - printf("%s: abort tx pipe failed: %s\n", - device_xname(sc->cdce_dev), usbd_errstr(err)); - } - - for (i = 0; i < CDCE_RX_LIST_CNT; i++) { - if (sc->cdce_cdata.cdce_rx_chain[i].cdce_mbuf != NULL) { - m_freem(sc->cdce_cdata.cdce_rx_chain[i].cdce_mbuf); - sc->cdce_cdata.cdce_rx_chain[i].cdce_mbuf = NULL; - } - if (sc->cdce_cdata.cdce_rx_chain[i].cdce_xfer != NULL) { - usbd_destroy_xfer - (sc->cdce_cdata.cdce_rx_chain[i].cdce_xfer); - sc->cdce_cdata.cdce_rx_chain[i].cdce_xfer = NULL; - } - } - - for (i = 0; i < CDCE_TX_LIST_CNT; i++) { - if (sc->cdce_cdata.cdce_tx_chain[i].cdce_mbuf != NULL) { - m_freem(sc->cdce_cdata.cdce_tx_chain[i].cdce_mbuf); - sc->cdce_cdata.cdce_tx_chain[i].cdce_mbuf = NULL; - } - if (sc->cdce_cdata.cdce_tx_chain[i].cdce_xfer != NULL) { - usbd_destroy_xfer( - sc->cdce_cdata.cdce_tx_chain[i].cdce_xfer); - sc->cdce_cdata.cdce_tx_chain[i].cdce_xfer = NULL; - } - } - - if (sc->cdce_bulkin_pipe != NULL) { - err = usbd_close_pipe(sc->cdce_bulkin_pipe); - if (err) - printf("%s: close rx pipe failed: %s\n", - device_xname(sc->cdce_dev), usbd_errstr(err)); - sc->cdce_bulkin_pipe = NULL; - } - - if (sc->cdce_bulkout_pipe != NULL) { - err = usbd_close_pipe(sc->cdce_bulkout_pipe); - if (err) - printf("%s: close tx pipe failed: %s\n", - device_xname(sc->cdce_dev), usbd_errstr(err)); - sc->cdce_bulkout_pipe = NULL; - } -} - -static void -cdce_stop(struct cdce_softc *sc) -{ - - mutex_enter(&sc->cdce_lock); - cdce_stop_locked(sc); - mutex_exit(&sc->cdce_lock); -} - -static int -cdce_ioctl(struct ifnet *ifp, u_long command, void *data) -{ - struct cdce_softc *sc = ifp->if_softc; - struct ifaddr *ifa = (struct ifaddr *)data; - struct ifreq *ifr = (struct ifreq *)data; - int error = 0; - - if (sc->cdce_dying) - return EIO; - - switch(command) { - case SIOCINITIFADDR: - ifp->if_flags |= IFF_UP; - cdce_init(sc); - switch (ifa->ifa_addr->sa_family) { -#ifdef INET - case AF_INET: - arp_ifinit(ifp, ifa); - break; -#endif /* INET */ - } - break; - - case SIOCSIFMTU: - if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ETHERMTU) - error = EINVAL; - else if ((error = ifioctl_common(ifp, command, data)) == ENETRESET) - error = 0; - break; - - case SIOCSIFFLAGS: - if ((error = ifioctl_common(ifp, command, data)) != 0) - break; - /* XXX re-use ether_ioctl() */ - switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { - case IFF_UP: - cdce_init(sc); - break; - case IFF_RUNNING: - cdce_stop(sc); - break; - default: - break; - } - break; - - default: - error = ether_ioctl(ifp, command, data); - break; - } - - if (error == ENETRESET) - error = 0; - - return error; -} - -static void -cdce_watchdog(struct ifnet *ifp) -{ - struct cdce_softc *sc = ifp->if_softc; - struct cdce_chain *c; - usbd_status stat; - - KASSERT(mutex_owned(&sc->cdce_lock)); - - if (sc->cdce_dying) - return; - - ifp->if_oerrors++; - aprint_error_dev(sc->cdce_dev, "watchdog timeout\n"); - - mutex_enter(&sc->cdce_txlock); - - c = &sc->cdce_cdata.cdce_rx_chain[0]; - usbd_get_xfer_status(c->cdce_xfer, NULL, NULL, NULL, &stat); - cdce_txeof(c->cdce_xfer, c, stat); - - if (!IFQ_IS_EMPTY(&ifp->if_snd)) - cdce_start_locked(ifp); - - mutex_exit(&sc->cdce_txlock); -} - -static void -cdce_init(void *xsc) -{ - struct cdce_softc *sc = xsc; - struct ifnet *ifp = GET_IFP(sc); - struct cdce_chain *c; - usbd_status err; - int i; - - mutex_enter(&sc->cdce_lock); - if (ifp->if_flags & IFF_RUNNING) - goto out; - - /* Maybe set multicast / broadcast here??? */ - - err = usbd_open_pipe(sc->cdce_data_iface, sc->cdce_bulkin_no, - USBD_EXCLUSIVE_USE | USBD_MPSAFE, &sc->cdce_bulkin_pipe); - if (err) { - printf("%s: open rx pipe failed: %s\n", device_xname(sc->cdce_dev), - usbd_errstr(err)); - goto out; - } - - err = usbd_open_pipe(sc->cdce_data_iface, sc->cdce_bulkout_no, - USBD_EXCLUSIVE_USE | USBD_MPSAFE, &sc->cdce_bulkout_pipe); - if (err) { - printf("%s: open tx pipe failed: %s\n", - device_xname(sc->cdce_dev), usbd_errstr(err)); - goto out; - } - - if (cdce_tx_list_init(sc)) { - printf("%s: tx list init failed\n", device_xname(sc->cdce_dev)); - goto out; - } - - if (cdce_rx_list_init(sc)) { - printf("%s: rx list init failed\n", device_xname(sc->cdce_dev)); - goto out; - } - - mutex_enter(&sc->cdce_rxlock); - mutex_enter(&sc->cdce_txlock); - sc->cdce_stopping = false; - - for (i = 0; i < CDCE_RX_LIST_CNT; i++) { - c = &sc->cdce_cdata.cdce_rx_chain[i]; - - usbd_setup_xfer(c->cdce_xfer, c, c->cdce_buf, CDCE_BUFSZ, - USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, cdce_rxeof); - usbd_transfer(c->cdce_xfer); - } - - mutex_exit(&sc->cdce_txlock); - mutex_exit(&sc->cdce_rxlock); - ifp->if_flags |= IFF_RUNNING; - - callout_schedule(&sc->cdce_stat_ch, hz); - -out: - mutex_exit(&sc->cdce_lock); -} - -static int -cdce_newbuf(struct cdce_softc *sc, struct cdce_chain *c, struct mbuf *m) -{ - struct mbuf *m_new = NULL; - - if (m == NULL) { - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("%s: no memory for rx list " - "-- packet dropped!\n", device_xname(sc->cdce_dev)); - return ENOBUFS; - } - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - printf("%s: no memory for rx list " - "-- packet dropped!\n", device_xname(sc->cdce_dev)); - m_freem(m_new); - return ENOBUFS; - } - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - } else { - m_new = m; - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - m_new->m_data = m_new->m_ext.ext_buf; - } - c->cdce_mbuf = m_new; - return 0; -} - -static int -cdce_rx_list_init(struct cdce_softc *sc) -{ - struct cdce_cdata *cd; - struct cdce_chain *c; - int i; - - cd = &sc->cdce_cdata; - for (i = 0; i < CDCE_RX_LIST_CNT; i++) { - c = &cd->cdce_rx_chain[i]; - c->cdce_sc = sc; - c->cdce_idx = i; - if (cdce_newbuf(sc, c, NULL) == ENOBUFS) - return ENOBUFS; - if (c->cdce_xfer == NULL) { - int err = usbd_create_xfer(sc->cdce_bulkin_pipe, - CDCE_BUFSZ, 0, 0, &c->cdce_xfer); - if (err) - return err; - c->cdce_buf = usbd_get_buffer(c->cdce_xfer); - } - } - - return 0; -} - -static int -cdce_tx_list_init(struct cdce_softc *sc) -{ - struct cdce_cdata *cd; - struct cdce_chain *c; - int i; - - cd = &sc->cdce_cdata; - for (i = 0; i < CDCE_TX_LIST_CNT; i++) { - c = &cd->cdce_tx_chain[i]; - c->cdce_sc = sc; - c->cdce_idx = i; - c->cdce_mbuf = NULL; - if (c->cdce_xfer == NULL) { - int err = usbd_create_xfer(sc->cdce_bulkout_pipe, - CDCE_BUFSZ, USBD_FORCE_SHORT_XFER, 0, - &c->cdce_xfer); - if (err) - return err; - c->cdce_buf = usbd_get_buffer(c->cdce_xfer); - } - } - - return 0; -} - -static void -cdce_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) -{ - struct cdce_chain *c = priv; - struct cdce_softc *sc = c->cdce_sc; - struct ifnet *ifp = GET_IFP(sc); - struct mbuf *m; - int total_len = 0; - - mutex_enter(&sc->cdce_rxlock); - - if (sc->cdce_dying || sc->cdce_stopping || - status == USBD_INVAL || status == USBD_NOT_STARTED || - status == USBD_CANCELLED || !(ifp->if_flags & IFF_RUNNING)) { - mutex_exit(&sc->cdce_rxlock); - return; - } - - if (status != USBD_NORMAL_COMPLETION) { - if (sc->cdce_rxeof_errors == 0) - printf("%s: usb error on rx: %s\n", - device_xname(sc->cdce_dev), usbd_errstr(status)); - if (status == USBD_STALLED) - usbd_clear_endpoint_stall_async(sc->cdce_bulkin_pipe); - usbd_delay_ms(sc->cdce_udev, 10); - sc->cdce_rxeof_errors++; - goto done; - } - - sc->cdce_rxeof_errors = 0; - - usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); - if (sc->cdce_flags & CDCE_ZAURUS) - total_len -= 4; /* Strip off CRC added by Zaurus */ - if (total_len <= 1) { - ifp->if_ierrors++; - goto done; - } - - m = c->cdce_mbuf; - memcpy(mtod(m, char *), c->cdce_buf, total_len); - - if (total_len < sizeof(struct ether_header)) { - ifp->if_ierrors++; - goto done; - } - - m->m_pkthdr.len = m->m_len = total_len; - m_set_rcvif(m, ifp); - - if (cdce_newbuf(sc, c, NULL) == ENOBUFS) { - ifp->if_ierrors++; - goto done; - } - - mutex_exit(&sc->cdce_rxlock); - if_percpuq_enqueue(ifp->if_percpuq, m); - mutex_enter(&sc->cdce_rxlock); - -done: - if (sc->cdce_stopping || sc->cdce_dying) { - mutex_exit(&sc->cdce_rxlock); - return; - } - - mutex_exit(&sc->cdce_rxlock); - - /* Setup new transfer. */ - usbd_setup_xfer(c->cdce_xfer, c, c->cdce_buf, CDCE_BUFSZ, - USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, cdce_rxeof); - usbd_transfer(c->cdce_xfer); -} - -static void -cdce_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status) -{ - struct cdce_chain *c = priv; - struct cdce_softc *sc = c->cdce_sc; - struct ifnet *ifp = GET_IFP(sc); - usbd_status err; - - mutex_enter(&sc->cdce_txlock); - - if (sc->cdce_dying) - goto out; - - KASSERT(sc->cdce_cdata.cdce_tx_cnt > 0); - sc->cdce_cdata.cdce_tx_cnt--; - - sc->cdce_timer = 0; - - switch (status) { - case USBD_NOT_STARTED: - case USBD_CANCELLED: - goto out; - - case USBD_NORMAL_COMPLETION: - break; - - default: - ifp->if_oerrors++; - printf("%s: usb error on tx: %s\n", device_xname(sc->cdce_dev), - usbd_errstr(status)); - if (status == USBD_STALLED) - usbd_clear_endpoint_stall_async(sc->cdce_bulkout_pipe); - goto out; - } - - usbd_get_xfer_status(c->cdce_xfer, NULL, NULL, NULL, &err); - - if (c->cdce_mbuf != NULL) { - m_freem(c->cdce_mbuf); - c->cdce_mbuf = NULL; - } - - if (err) - ifp->if_oerrors++; - else - ifp->if_opackets++; - - if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) - cdce_start_locked(ifp); - -out: - mutex_exit(&sc->cdce_txlock); -} - -static void -cdce_tick(void *xsc) -{ - struct cdce_softc * const sc = xsc; - - if (sc == NULL) - return; - - mutex_enter(&sc->cdce_lock); - - if (sc->cdce_stopping || sc->cdce_dying) { - mutex_exit(&sc->cdce_lock); - return; - } - - /* Perform periodic stuff in process context */ - usb_add_task(sc->cdce_udev, &sc->cdce_tick_task, USB_TASKQ_DRIVER); - - mutex_exit(&sc->cdce_lock); -} - -static void -cdce_tick_task(void *xsc) -{ - struct cdce_softc * const sc = xsc; - struct ifnet *ifp; - - if (sc == NULL) - return; - - mutex_enter(&sc->cdce_lock); - - if (sc->cdce_stopping || sc->cdce_dying) { - mutex_exit(&sc->cdce_lock); - return; - } - - ifp = GET_IFP(sc); - - if (sc->cdce_timer != 0 && --sc->cdce_timer == 0) - cdce_watchdog(ifp); - if (!sc->cdce_stopping && !sc->cdce_dying) - callout_schedule(&sc->cdce_stat_ch, hz); - - mutex_exit(&sc->cdce_lock); -} - -int -cdce_activate(device_t self, enum devact act) -{ - struct cdce_softc *sc = device_private(self); - - switch (act) { - case DVACT_DEACTIVATE: - if_deactivate(GET_IFP(sc)); - - mutex_enter(&sc->cdce_lock); - sc->cdce_dying = true; - mutex_exit(&sc->cdce_lock); - - mutex_enter(&sc->cdce_rxlock); - mutex_enter(&sc->cdce_txlock); - sc->cdce_stopping = true; - mutex_exit(&sc->cdce_txlock); - mutex_exit(&sc->cdce_rxlock); - - return 0; - default: - return EOPNOTSUPP; - } + return m->m_pkthdr.len + extra; } Index: src/sys/modules/Makefile diff -u src/sys/modules/Makefile:1.222 src/sys/modules/Makefile:1.223 --- src/sys/modules/Makefile:1.222 Thu Jun 20 03:31:29 2019 +++ src/sys/modules/Makefile Wed Jul 31 09:13:16 2019 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.222 2019/06/20 03:31:29 pgoyette Exp $ +# $NetBSD: Makefile,v 1.223 2019/07/31 09:13:16 mrg Exp $ .include <bsd.own.mk> @@ -163,6 +163,7 @@ SUBDIR+= udf SUBDIR+= ufs SUBDIR+= umap SUBDIR+= union +SUBDIR+= usbnet SUBDIR+= usbverbose SUBDIR+= vcoda SUBDIR+= v7fs Added files: Index: src/sys/dev/usb/usbnet.c diff -u /dev/null src/sys/dev/usb/usbnet.c:1.1 --- /dev/null Wed Jul 31 09:13:17 2019 +++ src/sys/dev/usb/usbnet.c Wed Jul 31 09:13:16 2019 @@ -0,0 +1,1055 @@ +/* $NetBSD: usbnet.c,v 1.1 2019/07/31 09:13:16 mrg Exp $ */ + +/* + * 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 <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: usbnet.c,v 1.1 2019/07/31 09:13:16 mrg Exp $"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/kmem.h> +#include <sys/module.h> + +#include <dev/usb/usbnet.h> + +static int usbnet_modcmd(modcmd_t, void *); + +#define DPRINTF(fmt, ...) \ + printf("%s:%d: " fmt "\n", __func__, __LINE__, ## __VA_ARGS__) + +/* Interrupt handling. */ + +static struct mbuf * +usbnet_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; +} + +/* + * usbnet_rxeof() is designed to be the done callback for rx completion. + * it provides generic setup and finalisation, calls a different usbnet + * rx_loop callback in the middle, which can use usbnet_enqueue() to + * enqueue a packet for higher levels. + */ +void +usbnet_enqueue(struct usbnet * const un, uint8_t *buf, size_t buflen, + int flags) +{ + struct ifnet *ifp = &un->un_ec.ec_if; + struct mbuf *m; + +//DPRINTF("enter"); + KASSERT(mutex_owned(&un->un_rxlock)); + + m = usbnet_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 +usbnet_rxeof(struct usbd_xfer *xfer, void * priv, usbd_status status) +{ + struct usbnet_chain *c = (struct usbnet_chain *)priv; + struct usbnet * const un = c->unc_un; + struct ifnet *ifp = &un->un_ec.ec_if; + uint32_t total_len; + +//DPRINTF("enter"); + 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[USBNET_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, usbnet_rxeof); + usbd_transfer(xfer); + return; + +out: + mutex_exit(&un->un_rxlock); +} + +static void +usbnet_txeof(struct usbd_xfer *xfer, void * priv, usbd_status status) +{ + struct usbnet_chain *c = (struct usbnet_chain *)priv; + struct usbnet * const un = c->unc_un; + struct usbnet_cdata *cd = &un->un_cdata; + struct ifnet * const ifp = usbnet_ifp(un); + +//DPRINTF("enter"); + 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[USBNET_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 +usbnet_start_locked(struct ifnet *ifp) +{ + struct usbnet * const un = ifp->if_softc; + struct usbnet_cdata *cd = &un->un_cdata; + struct mbuf *m; + unsigned length; + int idx; + +//DPRINTF("enter"); + 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; +//DPRINTF("idx %d", idx); + while (cd->uncd_tx_cnt < cd->uncd_tx_list_cnt) { + IFQ_POLL(&ifp->if_snd, m); + if (m == NULL) + break; + + struct usbnet_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, usbnet_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++; + } +//DPRINTF("idx %d", idx); + cd->uncd_tx_prod = idx; + + /* + * Set a timeout in case the chip goes out to lunch. + */ + un->un_timer = 5; +} + +static void +usbnet_start(struct ifnet *ifp) +{ + struct usbnet * const un = ifp->if_softc; + +//DPRINTF("enter"); + mutex_enter(&un->un_txlock); + if (!un->un_stopping) + usbnet_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 +usbnet_rx_list_size(struct usbnet_cdata *cd) +{ + return sizeof(*cd->uncd_rx_chain) * cd->uncd_rx_list_cnt; +} + +static void +usbnet_rx_list_alloc(struct usbnet *un, unsigned cnt) +{ + struct usbnet_cdata *cd = &un->un_cdata; + + cd->uncd_rx_list_cnt = cnt; + cd->uncd_rx_chain = kmem_zalloc(usbnet_rx_list_size(cd), KM_SLEEP); +} + +static void +usbnet_rx_list_free(struct usbnet *un) +{ + struct usbnet_cdata *cd = &un->un_cdata; + + if (cd->uncd_rx_chain) { + kmem_free(cd->uncd_rx_chain, usbnet_rx_list_size(cd)); + cd->uncd_rx_chain = NULL; + } +} + +static int +usbnet_rx_list_init(struct usbnet *un, unsigned xfer_flags) +{ + struct usbnet_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_rx_list_cnt; i++) { + struct usbnet_chain *c = &cd->uncd_rx_chain[i]; + + c->unc_un = un; + if (c->unc_xfer == NULL) { + int err = usbd_create_xfer(un->un_ep[USBNET_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 +usbnet_rx_list_fini(struct usbnet *un) +{ + struct usbnet_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_rx_list_cnt; i++) { + struct usbnet_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 +usbnet_rx_start_pipes(struct usbnet *un, usbd_callback cb) +{ + struct usbnet_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 usbnet_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 +usbnet_tx_list_size(struct usbnet_cdata *cd) +{ + return sizeof(*cd->uncd_tx_chain) * cd->uncd_tx_list_cnt; +} + +static void +usbnet_tx_list_alloc(struct usbnet *un, unsigned cnt) +{ + struct usbnet_cdata *cd = &un->un_cdata; + + cd->uncd_tx_list_cnt = cnt; + cd->uncd_tx_chain = kmem_zalloc(usbnet_tx_list_size(cd), KM_SLEEP); +} + +static void +usbnet_tx_list_free(struct usbnet *un) +{ + struct usbnet_cdata *cd = &un->un_cdata; + + if (cd->uncd_tx_chain) { + kmem_free(cd->uncd_tx_chain, usbnet_tx_list_size(cd)); + cd->uncd_tx_chain = NULL; + } +} + +static int +usbnet_tx_list_init(struct usbnet *un, unsigned xfer_flags) +{ + struct usbnet_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_tx_list_cnt; i++) { + struct usbnet_chain *c = &cd->uncd_tx_chain[i]; + + c->unc_un = un; + if (c->unc_xfer == NULL) { + int err = usbd_create_xfer(un->un_ep[USBNET_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 +usbnet_tx_list_fini(struct usbnet *un) +{ + struct usbnet_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_tx_list_cnt; i++) { + struct usbnet_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 +usbnet_ep_close_pipes(struct usbnet *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 +usbnet_ep_open_pipes(struct usbnet *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) { + usbnet_ep_close_pipes(un); + return err; + } + } + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +usbnet_ep_stop_pipes(struct usbnet *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 +usbnet_init_rx_tx(struct usbnet * const un, unsigned rxflags, unsigned txflags) +{ + struct ifnet * const ifp = usbnet_ifp(un); + usbd_status err; + + /* Open RX and TX pipes. */ + err = usbnet_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 (usbnet_rx_list_init(un, rxflags)) { + aprint_error_dev(un->un_dev, "rx list init failed\n"); + goto nobufs; + } + + /* Init TX ring. */ + if (usbnet_tx_list_init(un, txflags)) { + aprint_error_dev(un->un_dev, "tx list init failed\n"); + goto nobufs; + } + + /* Start up the receive pipe(s). */ + usbnet_rx_start_pipes(un, usbnet_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; + +nobufs: + usbnet_rx_list_fini(un); + usbnet_tx_list_fini(un); + usbnet_ep_close_pipes(un); + + return ENOBUFS; +} + +/* MII management. */ + +/* + * Access functions for MII. Take the MII lock to call access MII regs. + * Two forms: usbnet (softc) lock currently held or not. + */ +void +usbnet_lock_mii(struct usbnet *un) +{ + + mutex_enter(&un->un_lock); + un->un_refcnt++; + mutex_exit(&un->un_lock); + + mutex_enter(&un->un_miilock); +} + +void +usbnet_lock_mii_un_locked(struct usbnet *un) +{ + KASSERT(mutex_owned(&un->un_lock)); + + un->un_refcnt++; + mutex_enter(&un->un_miilock); +} + +void +usbnet_unlock_mii(struct usbnet *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 +usbnet_unlock_mii_un_locked(struct usbnet *un) +{ + KASSERT(mutex_owned(&un->un_lock)); + + mutex_exit(&un->un_miilock); + if (--un->un_refcnt < 0) + cv_broadcast(&un->un_detachcv); +} + +int +usbnet_miibus_readreg(device_t dev, int phy, int reg, uint16_t *val) +{ + struct usbnet * 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); + + usbnet_lock_mii(un); + err = (*un->un_read_reg_cb)(un, reg, phy, val); + usbnet_unlock_mii(un); + + if (err) { + aprint_error_dev(un->un_dev, "read PHY failed: %d\n", err); + return EIO; + } + + return 0; +} + +int +usbnet_miibus_writereg(device_t dev, int phy, int reg, uint16_t val) +{ + struct usbnet * 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); + + usbnet_lock_mii(un); + err = (*un->un_write_reg_cb)(un, reg, phy, val); + usbnet_unlock_mii(un); + + if (err) { + aprint_error_dev(un->un_dev, "write PHY failed: %d\n", err); + return EIO; + } + + return 0; +} + +void +usbnet_miibus_statchg(struct ifnet *ifp) +{ + struct usbnet * const un = ifp->if_softc; + + (*un->un_statchg_cb)(ifp); +} + +static int +usbnet_media_upd(struct ifnet *ifp) +{ + struct usbnet * const un = ifp->if_softc; + struct mii_data * const mii = usbnet_mii(un); + + if (un->un_dying) + return EIO; + + un->un_link = false; + + if (mii->mii_instance) { + struct mii_softc *miisc; + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + + return ether_mediachange(ifp); +} + +/* ioctl */ + +static int +usbnet_ifflags_cb(struct ethercom *ec) +{ + struct ifnet *ifp = &ec->ec_if; + struct usbnet *un = ifp->if_softc; + int rv = 0; + + mutex_enter(&un->un_lock); + + const int changed = ifp->if_flags ^ un->un_if_flags; + if ((changed & ~(IFF_CANTCHANGE | IFF_DEBUG)) == 0) { + un->un_if_flags = ifp->if_flags; + if ((changed & IFF_PROMISC) != 0) + rv = ENETRESET; + } else { + rv = ENETRESET; + } + + mutex_exit(&un->un_lock); + + return rv; +} + +static int +usbnet_ioctl(struct ifnet *ifp, u_long cmd, void *data) +{ + struct usbnet * const un = ifp->if_softc; + int error; + + error = ether_ioctl(ifp, cmd, data); + if (error == ENETRESET && un->un_ioctl_cb) + error = (*un->un_ioctl_cb)(ifp, cmd, data); + + 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 + * + * usbnet_stop() is exported for drivers to use, expects lock held. + * + * usbnet_stop_ifp() is for the if_stop handler. + */ +void +usbnet_stop(struct usbnet *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); + + if (un->un_stop_cb) + (*un->un_stop_cb)(ifp, disable); + + /* + * XXXSMP Would like to + * KASSERT(IFNET_LOCKED(ifp)) + * here but the locking order is: + * ifnet -> unlock -> rxlock -> txlock + * and unlock is already held. + */ + ifp->if_flags &= ~IFF_RUNNING; + un->un_timer = 0; + + callout_stop(&un->un_stat_ch); + un->un_link = false; + + /* Stop transfers. */ + usbnet_ep_stop_pipes(un); + + /* Free RX/TX resources. */ + usbnet_rx_list_fini(un); + usbnet_tx_list_fini(un); + + /* Close pipes. */ + usbnet_ep_close_pipes(un); +} + +static void +usbnet_stop_ifp(struct ifnet *ifp, int disable) +{ + struct usbnet * const un = ifp->if_softc; + + mutex_enter(&un->un_lock); + usbnet_stop(un, ifp, disable); + mutex_exit(&un->un_lock); +} + +/* + * Generic tick task function. + * + * usbnet_tick() is triggered from a callout, and triggers a call to + * usbnet_tick_task() from the usb_task subsystem. + */ +static void +usbnet_tick(void *arg) +{ + struct usbnet * 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 +usbnet_watchdog(struct ifnet *ifp) +{ + struct usbnet * const un = ifp->if_softc; + struct usbnet_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 usbnet_chain *c = &un->un_cdata.uncd_tx_chain[0]; + usbd_get_xfer_status(c->unc_xfer, NULL, NULL, NULL, &stat); + usbnet_txeof(c->unc_xfer, c, stat); + } + + if (!IFQ_IS_EMPTY(&ifp->if_snd)) + (*ifp->if_start)(ifp); +} + +static void +usbnet_tick_task(void *arg) +{ + struct usbnet * 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 = usbnet_ifp(un); + struct mii_data * const mii = usbnet_mii(un); + + un->un_refcnt++; + mutex_exit(&un->un_lock); + + if (ifp && un->un_timer != 0 && --un->un_timer == 0) + usbnet_watchdog(ifp); + + if (mii && 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); +} + +static int +usbnet_init(struct ifnet *ifp) +{ + struct usbnet * const un = ifp->if_softc; + + return (*un->un_init_cb)(ifp); +} + +/* Autoconf management. */ + +/* + * usbnet_attach() and usbnet_attach_ifp() perform setup of the relevant + * 'usbnet'. The first is enough to enable device access (eg, endpoints + * are connected and commands can be sent), and the second connects the + * device to the system networking. + * + * Always call usbnet_detach(), even if usbnet_attach_ifp() is skippped. + * Also usable as driver detach directly. + */ +void +usbnet_attach(struct usbnet *un, + const char *detname, /* detach cv name */ + unsigned rx_list_cnt, /* size of rx chain list */ + unsigned tx_list_cnt) /* size of tx chain list */ +{ + + KASSERT(un->un_tx_prepare_cb); + KASSERT(un->un_rx_loop_cb); + KASSERT(un->un_init_cb); + KASSERT(un->un_cdata.uncd_rx_bufsz); + KASSERT(un->un_cdata.uncd_tx_bufsz); + KASSERT(rx_list_cnt); + KASSERT(tx_list_cnt); + + ether_set_ifflags_cb(&un->un_ec, usbnet_ifflags_cb); + + usb_init_task(&un->un_ticktask, usbnet_tick_task, un, USB_TASKQ_MPSAFE); + callout_init(&un->un_stat_ch, CALLOUT_MPSAFE); + callout_setfunc(&un->un_stat_ch, usbnet_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); + + usbnet_rx_list_alloc(un, rx_list_cnt); + usbnet_tx_list_alloc(un, tx_list_cnt); + + un->un_attached = true; +} + +static void +usbnet_attach_mii(struct usbnet *un) +{ + struct mii_data * const mii = &un->un_mii; + struct ifnet *ifp = usbnet_ifp(un); + + mii->mii_ifp = ifp; + mii->mii_readreg = usbnet_miibus_readreg; + mii->mii_writereg = usbnet_miibus_writereg; + mii->mii_statchg = usbnet_miibus_statchg; + mii->mii_flags = MIIF_AUTOTSLEEP; + + un->un_ec.ec_mii = mii; + ifmedia_init(&mii->mii_media, 0, usbnet_media_upd, ether_mediastatus); + mii_attach(un->un_dev, mii, 0xffffffff, MII_PHY_ANY, + MII_OFFSET_ANY, 0); + + if (LIST_FIRST(&mii->mii_phys) == NULL) { + ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); + ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE); + } else + ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, un->un_udev, un->un_dev); + + if (!pmf_device_register(un->un_dev, NULL, NULL)) + aprint_error_dev(un->un_dev, "couldn't establish power handler\n"); +} + +void +usbnet_attach_ifp(struct usbnet *un, + bool have_mii, /* setup MII */ + unsigned if_flags, /* additional if_flags */ + unsigned if_extflags) /* additional if_extflags */ +{ + struct ifnet *ifp = usbnet_ifp(un); + + KASSERT(un->un_attached); + + IFQ_SET_READY(&ifp->if_snd); + + ifp->if_softc = un; + strlcpy(ifp->if_xname, device_xname(un->un_dev), IFNAMSIZ); + ifp->if_flags = if_flags; + ifp->if_extflags = IFEF_MPSAFE | if_extflags; + ifp->if_ioctl = usbnet_ioctl; + ifp->if_start = usbnet_start; + ifp->if_init = usbnet_init; + ifp->if_stop = usbnet_stop_ifp; + + IFQ_SET_READY(&ifp->if_snd); + + if (have_mii) + usbnet_attach_mii(un); + + /* Attach the interface. */ + if_attach(ifp); + ether_ifattach(ifp, un->un_eaddr); +} + +int +usbnet_detach(device_t self, int flags) +{ + struct usbnet * const un = device_private(self); + struct ifnet *ifp = usbnet_ifp(un); + struct mii_data *mii = usbnet_mii(un); + + mutex_enter(&un->un_lock); + un->un_dying = true; + mutex_exit(&un->un_lock); + + /* Detached before attached finished, so just bail out. */ + if (!un->un_attached) + return 0; + + callout_halt(&un->un_stat_ch, NULL); + usb_rem_task_wait(un->un_udev, &un->un_ticktask, USB_TASKQ_DRIVER, NULL); + + if (ifp->if_flags & IFF_RUNNING) { + IFNET_LOCK(ifp); + usbnet_stop_ifp(ifp, 1); + IFNET_UNLOCK(ifp); + } + + mutex_enter(&un->un_lock); + un->un_refcnt--; + while (un->un_refcnt > 0) { + /* Wait for processes to go away */ + cv_wait(&un->un_detachcv, &un->un_lock); + } + mutex_exit(&un->un_lock); + + usbnet_rx_list_free(un); + usbnet_tx_list_free(un); + + callout_destroy(&un->un_stat_ch); + rnd_detach_source(&un->un_rndsrc); + + if (mii) { + mii_detach(mii, MII_PHY_ANY, MII_OFFSET_ANY); + ifmedia_delete_instance(&mii->mii_media, IFM_INST_ANY); + } + if (ifp->if_softc) { + ether_ifdetach(ifp); + if_detach(ifp); + } + + 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); + + pmf_device_deregister(un->un_dev); + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, un->un_udev, un->un_dev); + + return 0; +} + +int +usbnet_activate(device_t self, devact_t act) +{ + struct usbnet * const un = device_private(self); + struct ifnet * const ifp = usbnet_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, usbnet, NULL); + +static int +usbnet_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: src/sys/dev/usb/usbnet.h diff -u /dev/null src/sys/dev/usb/usbnet.h:1.1 --- /dev/null Wed Jul 31 09:13:17 2019 +++ src/sys/dev/usb/usbnet.h Wed Jul 31 09:13:16 2019 @@ -0,0 +1,270 @@ +/* $NetBSD: usbnet.h,v 1.1 2019/07/31 09:13:16 mrg Exp $ */ + +/* + * 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_USBNET_H +#define _DEV_USB_USBNET_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 usbnet" usage, in particular + * use usbnet pointer for ifp->if_softc, and device_private (real softc + * can be stored in un_sc member) + * - use MII bus lock / access methods + * - usbnet_attach() to initialise and allocate rx/tx chains + * - usbnet_detach() to clean them up + * - usbnet_activate() for autoconf + * - interface ioctl and start have direct frontends with callbacks for + * device specific handling: + * - ioctl replies upon ether_ioctl() and a device-specific callback + * to handle setting multicast/offload/etc + * - start uses usbnet send callback + * - interface init and stop have helper functions + * - device specific init should use usbnet_init_rx_tx() to open pipes + * to the device and setup the rx/tx chains for use after any device + * specific setup + * - usbnet_stop() must be called with the un_lock held, and will + * call the device-specific usbnet stop callback, which enables the + * standard init calls stop idiom. + * - interrupt handling: + * - for rx, usbnet_init_rx_tx() will enable the receive pipes and + * call the rx_loop callback to handle device specific processing of + * packets, which can use usbnet_enqueue() to provide data to the + * higher layers + * - for tx, usbnet_start (if_start) will pull entries out of the + * transmit queue and use the send callback for the given mbuf. + * the usb callback will use usbnet_txeof() for the transmit + * completion function (internal to usbnet) + */ + +/* + * Converted drivers: if_axen if_cdce. + * + * Note: these drivers have slightly different mbuf handling that need to be + * adjusted to the common method (see if_cdce conversion): + * + * if_atu if_aue if_cue if_smsc if_udav if_upl if_url if_urndis. + */ + +#include <sys/device.h> +#include <sys/mbuf.h> +#include <sys/rndsource.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_ether.h> +#include <net/if_media.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> + +/* + * Per-transfer data, initialised in usbnet_[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 usbnet; +struct usbnet_chain { + struct usbnet *unc_un; + struct usbd_xfer *unc_xfer; + uint8_t *unc_buf; +}; + +struct usbnet_cdata { + struct usbnet_chain *uncd_tx_chain; + struct usbnet_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 usbnet_ep { + USBNET_ENDPT_RX, + USBNET_ENDPT_TX, + USBNET_ENDPT_INTR, + USBNET_ENDPT_MAX, +}; + +/* Interface stop callback. */ +typedef void (*usbnet_stop_cb)(struct ifnet *, int); +/* Interface ioctl callback. */ +typedef int (*usbnet_ioctl_cb)(struct ifnet *, u_long, void *); +/* Initialise device callback. */ +typedef int (*usbnet_init_cb)(struct ifnet *); +/* MII read register callback. */ +typedef usbd_status (*usbnet_mii_read_reg_cb)(struct usbnet *, int reg, + int phy, uint16_t *val); +/* MII write register callback. */ +typedef usbd_status (*usbnet_mii_write_reg_cb)(struct usbnet *, int reg, + int phy, uint16_t val); +/* MII status change callback. */ +typedef void (*usbnet_mii_statchg_cb)(struct ifnet *); +/* Prepare packet to send callback, returns length. */ +typedef unsigned (*usbnet_tx_prepare_cb)(struct usbnet *, struct mbuf *, + struct usbnet_chain *); +/* Receive some packets callback. */ +typedef void (*usbnet_rx_loop_cb)(struct usbnet *, struct usbd_xfer *, + struct usbnet_chain *, uint32_t); + +/* + * Generic USB ethernet structure. Use this as ifp->if_softc and + * set as device_private() in attach. + */ +struct usbnet { + 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; + uint8_t un_eaddr[ETHER_ADDR_LEN]; + + enum usbnet_ep un_ed[USBNET_ENDPT_MAX]; + struct usbd_pipe *un_ep[USBNET_ENDPT_MAX]; + + struct usbnet_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; + bool un_attached; + + struct timeval un_rx_notice; + struct timeval un_tx_notice; + + usbnet_stop_cb un_stop_cb; + usbnet_ioctl_cb un_ioctl_cb; + usbnet_init_cb un_init_cb; + usbnet_mii_read_reg_cb un_read_reg_cb; + usbnet_mii_write_reg_cb un_write_reg_cb; + usbnet_mii_statchg_cb un_statchg_cb; + usbnet_tx_prepare_cb un_tx_prepare_cb; + usbnet_rx_loop_cb un_rx_loop_cb; + + /* Passed to usbd_setup_xfer(). */ + int un_rx_xfer_flags; + int un_tx_xfer_flags; +}; + +#define usbnet_ifp(un) (&(un)->un_ec.ec_if) +#define usbnet_mii(un) (un->un_ec.ec_mii) + +/* + * Endpoint / rx/tx chain management: + * + * usbnet_attach() allocates rx and tx chains + * usbnet_init_rx_tx() open pipes, initialises the rx/tx chains for use + * usbnet_stop() stops pipes, cleans (not frees) rx/tx chains, locked + * version assumes un_lock is held + * usbnet_detach() frees the rx/tx chains + * + * Setup un_ed[] with valid end points before calling usbnet_init_rx_tx(). + * Will return with un_ep[] initialised upon success. + */ +int usbnet_init_rx_tx(struct usbnet * const, unsigned, unsigned); + +/* mii */ +void usbnet_lock_mii(struct usbnet *); +void usbnet_lock_mii_un_locked(struct usbnet *); +void usbnet_unlock_mii(struct usbnet *); +void usbnet_unlock_mii_un_locked(struct usbnet *); + +int usbnet_miibus_readreg(device_t, int, int, uint16_t *); +int usbnet_miibus_writereg(device_t, int, int, uint16_t); +void usbnet_miibus_statchg(struct ifnet *); + +/* interrupt handling */ +void usbnet_enqueue(struct usbnet * const, uint8_t *, size_t, int); + +/* autoconf */ +void usbnet_attach(struct usbnet *un, const char *, unsigned, unsigned); +void usbnet_attach_ifp(struct usbnet *, bool, unsigned, unsigned); +int usbnet_detach(device_t, int); +int usbnet_activate(device_t, devact_t); + +/* stop backend */ +void usbnet_stop(struct usbnet *, struct ifnet *, int); +void usbnet_stop_locked(struct usbnet *, struct ifnet *, int); + +#endif /* _DEV_USB_USBNET_H */ Index: src/sys/modules/usbnet/Makefile diff -u /dev/null src/sys/modules/usbnet/Makefile:1.1 --- /dev/null Wed Jul 31 09:13:17 2019 +++ src/sys/modules/usbnet/Makefile Wed Jul 31 09:13:17 2019 @@ -0,0 +1,12 @@ +# $NetBSD: Makefile,v 1.1 2019/07/31 09:13:17 mrg Exp $ + +.include "../Makefile.inc" + +.PATH: ${S}/dev/usb + +KMOD= usbnet +SRCS= usbnet.c + +WARNS= 3 + +.include <bsd.kmodule.mk>