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 <[email protected]>
@@ -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>