a large chunk of usb ethernet drivers are largely the same. this change creates a library to support these and share as much code as possible. done: - if_stop() callback now largely done by usb_net_stop() - device activate() callback is done by usb_net_activate() - rx/tx list management is handled by several functions - tick/ticktask handling is in usb_net_tick()/usb_net_tick_task() - MII bus locking and readreg/writereg are mostly handle, with small callbacks for read/write. statchg() is almost always device-dep code so not really shareable - endpoint handling is mostly in usb_net.c. - if_init() callback partly converted, but mostly device-dep - some basic attach/detach functionality is shared. more can very likely be done here. planned: - txeof() - rxeof() - if_start() - if_ioctl() - more attach/detach? Index: usb_net.c =================================================================== RCS file: usb_net.c diff -N usb_net.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ usb_net.c 22 Jul 2019 09:46:41 -0000 @@ -0,0 +1,541 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 2019 Matthew R. Green + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Common code shared between USB ethernet drivers. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Chain management. + * + * RX and TX are identical. Keep them that way. + */ + +/* Start of common RX functions */ + +static size_t +usb_net_rx_list_size(struct usb_net_cdata *cd) +{ + return sizeof(*cd->uncd_rx_chain) * cd->uncd_rx_list_cnt; +} + +void +usb_net_rx_list_alloc(struct usb_net *un, unsigned cnt) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + cd->uncd_rx_list_cnt = cnt; + cd->uncd_rx_chain = kmem_zalloc(usb_net_rx_list_size(cd), KM_SLEEP); +} + +void +usb_net_rx_list_free(struct usb_net *un) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + if (cd->uncd_rx_chain) { + kmem_free(cd->uncd_rx_chain, usb_net_rx_list_size(cd)); + cd->uncd_rx_chain = NULL; + } +} + +int +usb_net_rx_list_init(struct usb_net *un, unsigned xfer_flags) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_rx_list_cnt; i++) { + struct usb_net_chain *c = &cd->uncd_rx_chain[i]; + + c->unc_un = un; + if (c->unc_xfer == NULL) { + int err = usbd_create_xfer(un->un_ep[USB_NET_ENDPT_RX], + cd->uncd_rx_bufsz, xfer_flags, 0, &c->unc_xfer); + if (err) + return err; + c->unc_buf = usbd_get_buffer(c->unc_xfer); + } + } + + return 0; +} + +void +usb_net_rx_list_fini(struct usb_net *un) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_rx_list_cnt; i++) { + struct usb_net_chain *c = &cd->uncd_rx_chain[i]; + + if (c->unc_xfer != NULL) { + usbd_destroy_xfer(c->unc_xfer); + c->unc_xfer = NULL; + c->unc_buf = NULL; + } + } +} + +/* End of common RX functions */ + +void +usb_net_rx_start_pipes(struct usb_net *un, usbd_callback cb) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + mutex_enter(&un->un_rxlock); + mutex_enter(&un->un_txlock); + un->un_stopping = false; + + for (size_t i = 0; i < cd->uncd_rx_list_cnt; i++) { + struct usb_net_chain *c = &cd->uncd_rx_chain[i]; + + usbd_setup_xfer(c->unc_xfer, c, c->unc_buf, cd->uncd_rx_bufsz, + USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, cb); + usbd_transfer(c->unc_xfer); + } + + mutex_exit(&un->un_txlock); + mutex_exit(&un->un_rxlock); +} + +/* Start of common TX functions */ + +static size_t +usb_net_tx_list_size(struct usb_net_cdata *cd) +{ + return sizeof(*cd->uncd_tx_chain) * cd->uncd_tx_list_cnt; +} + +void +usb_net_tx_list_alloc(struct usb_net *un, unsigned cnt) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + cd->uncd_tx_list_cnt = cnt; + cd->uncd_tx_chain = kmem_zalloc(usb_net_tx_list_size(cd), KM_SLEEP); +} + +void +usb_net_tx_list_free(struct usb_net *un) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + if (cd->uncd_tx_chain) { + kmem_free(cd->uncd_tx_chain, usb_net_tx_list_size(cd)); + cd->uncd_tx_chain = NULL; + } +} + +int +usb_net_tx_list_init(struct usb_net *un, unsigned xfer_flags) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_tx_list_cnt; i++) { + struct usb_net_chain *c = &cd->uncd_tx_chain[i]; + + c->unc_un = un; + if (c->unc_xfer == NULL) { + int err = usbd_create_xfer(un->un_ep[USB_NET_ENDPT_TX], + cd->uncd_tx_bufsz, xfer_flags, 0, &c->unc_xfer); + if (err) + return err; + c->unc_buf = usbd_get_buffer(c->unc_xfer); + } + } + + return 0; +} + +void +usb_net_tx_list_fini(struct usb_net *un) +{ + struct usb_net_cdata *cd = &un->un_cdata; + + for (size_t i = 0; i < cd->uncd_tx_list_cnt; i++) { + struct usb_net_chain *c = &cd->uncd_tx_chain[i]; + + if (c->unc_xfer != NULL) { + usbd_destroy_xfer(c->unc_xfer); + c->unc_xfer = NULL; + c->unc_buf = NULL; + } + } +} + +/* End of common TX functions */ + +/* Endpoint pipe management. */ + +usbd_status +usb_net_ep_open_pipes(struct usb_net *un) +{ + for (size_t i = 0; i < __arraycount(un->un_ep); i++) { + if (un->un_ed[i] == 0) + continue; + usbd_status err = usbd_open_pipe(un->un_iface, un->un_ed[i], + USBD_EXCLUSIVE_USE | USBD_MPSAFE, &un->un_ep[i]); + if (err) { + usb_net_ep_close_pipes(un); + return err; + } + } + + return USBD_NORMAL_COMPLETION; +} + +usbd_status +usb_net_ep_stop_pipes(struct usb_net *un) +{ + for (size_t i = 0; i < __arraycount(un->un_ep); i++) { + if (un->un_ep[i] == NULL) + continue; + usbd_status err = usbd_abort_pipe(un->un_ep[i]); + if (err) + return err; + } + + return USBD_NORMAL_COMPLETION; +} + +void +usb_net_ep_close_pipes(struct usb_net *un) +{ + for (size_t i = 0; i < __arraycount(un->un_ep); i++) { + if (un->un_ep[i] == NULL) + continue; + usbd_status err = usbd_close_pipe(un->un_ep[i]); + if (err) + aprint_error_dev(un->un_dev, "close pipe %zu: %s\n", i, + usbd_errstr(err)); + un->un_ep[i] = NULL; + } +} + +/* MII management. */ + +/* + * Access functions for MII. Take the MII lock to call access MII regs. + * Two forms: usb_net (softc) lock currently held or not. + */ +void +usb_net_lock_mii(struct usb_net *un) +{ + + mutex_enter(&un->un_lock); + un->un_refcnt++; + mutex_exit(&un->un_lock); + + mutex_enter(&un->un_miilock); +} + +void +usb_net_lock_mii_un_locked(struct usb_net *un) +{ + KASSERT(mutex_owned(&un->un_lock)); + + un->un_refcnt++; + mutex_enter(&un->un_miilock); +} + +void +usb_net_unlock_mii(struct usb_net *un) +{ + + mutex_exit(&un->un_miilock); + mutex_enter(&un->un_lock); + if (--un->un_refcnt < 0) + cv_broadcast(&un->un_detachcv); + mutex_exit(&un->un_lock); +} + +void +usb_net_unlock_mii_un_locked(struct usb_net *un) +{ + KASSERT(mutex_owned(&un->un_lock)); + + mutex_exit(&un->un_miilock); + if (--un->un_refcnt < 0) + cv_broadcast(&un->un_detachcv); +} + +int +usb_net_miibus_readreg(device_t dev, int phy, int reg, uint16_t *val) +{ + struct usb_net * const un = device_private(dev); + usbd_status err; + + mutex_enter(&un->un_lock); + if (un->un_dying || un->un_phyno != phy) { + mutex_exit(&un->un_lock); + return EIO; + } + mutex_exit(&un->un_lock); + + usb_net_lock_mii(un); + err = (*un->un_read_reg_cb)(un, reg, phy, val); + usb_net_unlock_mii(un); + + if (err) { + aprint_error_dev(un->un_dev, "read PHY failed: %d\n", err); + return EIO; + } + + return 0; +} + +int +usb_net_miibus_writereg(device_t dev, int phy, int reg, uint16_t val) +{ + struct usb_net * const un = device_private(dev); + usbd_status err; + + mutex_enter(&un->un_lock); + if (un->un_dying || un->un_phyno != phy) { + mutex_exit(&un->un_lock); + return EIO; + } + mutex_exit(&un->un_lock); + + usb_net_lock_mii(un); + err = (*un->un_write_reg_cb)(un, reg, phy, val); + usb_net_unlock_mii(un); + + if (err) { + aprint_error_dev(un->un_dev, "write PHY failed: %d\n", err); + return EIO; + } + + return 0; +} + +/* + * Generic stop network function: + * - mark as stopping + * - call DD routine to stop the device + * - turn off running, timer, statchg callout, link + * - stop transfers + * - free RX and TX resources + * - close pipes + */ +void +usb_net_stop(struct usb_net *un, struct ifnet *ifp, int disable) +{ + KASSERT(mutex_owned(&un->un_lock)); + + mutex_enter(&un->un_rxlock); + mutex_enter(&un->un_txlock); + un->un_stopping = true; + mutex_exit(&un->un_txlock); + mutex_exit(&un->un_rxlock); + + (*un->un_stop_cb)(ifp, disable); + + /* + * XXXSMP Would like to + * KASSERT(IFNET_LOCKED(ifp)) + * here but the locking order is: + * ifnet -> lock -> rxlock -> txlock + * and sc lock is already held. + */ + ifp->if_flags &= ~IFF_RUNNING; + un->un_timer = 0; + + callout_stop(&un->un_stat_ch); + un->un_link = false; + + /* Stop transfers. */ + usb_net_ep_stop_pipes(un); + + /* Free RX/TX resources. */ + usb_net_rx_list_fini(un); + usb_net_tx_list_fini(un); + + /* Close pipes. */ + usb_net_ep_close_pipes(un); +} + +/* + * Generic tick task function. + * + * usb_net_tick() is triggered from a callout, and triggers a call to + * usb_net_tick_task() from the usb_task subsystem. + */ +static void +usb_net_tick(void *arg) +{ + struct usb_net * const un = arg; + + mutex_enter(&un->un_lock); + if (!un->un_stopping && !un->un_dying) { + /* Perform periodic stuff in process context */ + usb_add_task(un->un_udev, &un->un_ticktask, USB_TASKQ_DRIVER); + } + mutex_exit(&un->un_lock); +} + +static void +usb_net_tick_task(void *arg) +{ + struct usb_net * const un = arg; + + mutex_enter(&un->un_lock); + if (un->un_stopping || un->un_dying) { + mutex_exit(&un->un_lock); + return; + } + + struct ifnet * const ifp = usb_net_ifp(un); + struct mii_data * const mii = usb_net_mii(un); + + if (mii == NULL) { + mutex_exit(&un->un_lock); + return; + } + + un->un_refcnt++; + mutex_exit(&un->un_lock); + + if (un->un_timer != 0 && --un->un_timer == 0) + (*un->un_watchdog_cb)(ifp); + + mii_tick(mii); + + if (!un->un_link) + (*mii->mii_statchg)(ifp); + + mutex_enter(&un->un_lock); + if (--un->un_refcnt < 0) + cv_broadcast(&un->un_detachcv); + if (!un->un_stopping && !un->un_dying) + callout_schedule(&un->un_stat_ch, hz); + mutex_exit(&un->un_lock); +} + +/* Autoconf management. */ + +/* + * Setup 'struct usb_net *un'. + * + * Perform any setup that is global to all consumers of this interface. + */ +void +usb_net_attach(struct usb_net *un, const char *detname, + unsigned rx_list_cnt, unsigned tx_list_cnt) +{ + + usb_init_task(&un->un_ticktask, usb_net_tick_task, un, USB_TASKQ_MPSAFE); + callout_init(&un->un_stat_ch, CALLOUT_MPSAFE); + callout_setfunc(&un->un_stat_ch, usb_net_tick, un); + + mutex_init(&un->un_miilock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&un->un_txlock, MUTEX_DEFAULT, IPL_SOFTUSB); + mutex_init(&un->un_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB); + mutex_init(&un->un_lock, MUTEX_DEFAULT, IPL_NONE); + cv_init(&un->un_detachcv, detname); + + rnd_attach_source(&un->un_rndsrc, device_xname(un->un_dev), + RND_TYPE_NET, RND_FLAG_DEFAULT); + + usb_net_rx_list_alloc(un, rx_list_cnt); + usb_net_tx_list_alloc(un, tx_list_cnt); +} + +void +usb_net_detach(struct usb_net *un) +{ + + usb_net_rx_list_free(un); + usb_net_tx_list_free(un); + + rnd_detach_source(&un->un_rndsrc); + + callout_halt(&un->un_stat_ch, NULL); + callout_destroy(&un->un_stat_ch); + usb_rem_task_wait(un->un_udev, &un->un_ticktask, USB_TASKQ_DRIVER, NULL); + + cv_destroy(&un->un_detachcv); + mutex_destroy(&un->un_lock); + mutex_destroy(&un->un_rxlock); + mutex_destroy(&un->un_txlock); + mutex_destroy(&un->un_miilock); +} + +int +usb_net_activate(device_t self, devact_t act) +{ + struct usb_net * const un = device_private(self); + struct ifnet * const ifp = usb_net_ifp(un); + + switch (act) { + case DVACT_DEACTIVATE: + if_deactivate(ifp); + + mutex_enter(&un->un_lock); + un->un_dying = true; + mutex_exit(&un->un_lock); + + mutex_enter(&un->un_rxlock); + mutex_enter(&un->un_txlock); + un->un_stopping = true; + mutex_exit(&un->un_txlock); + mutex_exit(&un->un_rxlock); + + return 0; + default: + return EOPNOTSUPP; + } +} Index: usb_net.h =================================================================== RCS file: usb_net.h diff -N usb_net.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ usb_net.h 22 Jul 2019 09:46:41 -0000 @@ -0,0 +1,407 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 2019 Matthew R. Green + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DEV_USB_USB_NET_H +#define _DEV_USB_USB_NET_H + +/* + * Common data shared by all USB ethernet drivers (using these routines.) + * + * ethernet drivers: + * if_aue + * if_axe + * if_axen + * if_cdce + * if_cue + * if_kue + * if_mue + * if_smsc + * if_udav + * if_umb -- maybe? + * if_upl -- problematic + * if_ure + * if_url -- problematic + * if_urndis + */ + +#include + +/* + * Per-transfer data, initialised in usb_net_[rt]x_list_init(). + * + * Front-end must set uncd_tx_list_cnt and uncd_rx_list_cnt before calling + * list init, which the first time, will allocate the chain arrays, which + * must also be NULL to indicate the first call. + */ +struct usb_net; +struct usb_net_chain { + struct usb_net *unc_un; + struct usbd_xfer *unc_xfer; + uint8_t *unc_buf; +}; + +struct usb_net_cdata { + struct usb_net_chain *uncd_tx_chain; + struct usb_net_chain *uncd_rx_chain; + + unsigned uncd_rx_bufsz; + unsigned uncd_tx_bufsz; + unsigned uncd_rx_list_cnt; + unsigned uncd_tx_list_cnt; + + int uncd_tx_prod; + int uncd_tx_cnt; + int uncd_rx_cnt; +}; + +/* + * Extend this as necessary. axe(4) claims to want 6 total, but + * does not implement them. + */ +enum usb_net_ep { + USB_NET_ENDPT_RX, + USB_NET_ENDPT_TX, + USB_NET_ENDPT_INTR, + USB_NET_ENDPT_MAX, +}; + +typedef void (*usb_net_stop_cb)(struct ifnet *, int); +typedef void (*usb_net_watchdog_cb)(struct ifnet *); +typedef usbd_status (*usb_net_mii_read_reg_cb)(struct usb_net *, int reg, + int phy, uint16_t *val); +typedef usbd_status (*usb_net_mii_write_reg_cb)(struct usb_net *, int reg, + int phy, uint16_t val); + +struct usb_net { + void *un_sc; + device_t un_dev; + struct usbd_interface *un_iface; + struct usbd_device * un_udev; + krndsource_t un_rndsrc; + + struct ethercom un_ec; + struct mii_data un_mii; + int un_phyno; + + enum usb_net_ep un_ed[USB_NET_ENDPT_MAX]; + struct usbd_pipe *un_ep[USB_NET_ENDPT_MAX]; + + struct usb_net_cdata un_cdata; + struct callout un_stat_ch; + int un_if_flags; + + kmutex_t un_lock; + kmutex_t un_miilock; + kmutex_t un_rxlock; + kmutex_t un_txlock; + kcondvar_t un_detachcv; + + struct usb_task un_ticktask; + + int un_refcnt; + int un_timer; + + bool un_dying; + bool un_stopping; + bool un_link; + + struct timeval un_rx_notice; + struct timeval un_tx_notice; + + usb_net_stop_cb un_stop_cb; + usb_net_watchdog_cb un_watchdog_cb; + usb_net_mii_read_reg_cb un_read_reg_cb; + usb_net_mii_write_reg_cb un_write_reg_cb; +}; + +#define usb_net_ifp(un) (&(un)->un_ec.ec_if) +#define usb_net_mii(un) (&(un)->un_mii) + +/* + * Endpoint pipe management. + * + * usb_net_ep_open_pipes() creates the pipes that must have been found + * and set in un_ed[]. Zero is used to indiciate no pipe in the ed array. + * usb_net_ep_close_pipes() closes the relevant pipes. + */ +usbd_status usb_net_ep_open_pipes(struct usb_net *); +usbd_status usb_net_ep_stop_pipes(struct usb_net *); +void usb_net_ep_close_pipes(struct usb_net *); + +/* + * Chain management. + * + * usb_net_[rt]x_list_alloc() allocates chain array with count members. + * usb_net_[rt]x_list_free() frees chain array. + * usb_net_[rt]x_list_init() creates xfers for the chain + * usb_net_[rt]x_list_fini() destroys xfers for the chain + * + * Typical usage with pipes: + * attach() + * - usb_net_[rt]x_list_alloc() + * start(): + * - usb_net_ep_open_pipes() + * - usb_net_[rt]x_list_init() + * - usb_net_rx_start_pipes() + * stop(): + * - usb_net_ep_stop_pipes() + * - usb_net_[rt]x_list_fini() + * - usb_net_ep_close_pipes() + * detach(): + * - usb_net_[rt]x_list_free() + */ +void usb_net_rx_list_alloc(struct usb_net *, unsigned); +void usb_net_tx_list_alloc(struct usb_net *, unsigned); +void usb_net_rx_list_free(struct usb_net *); +void usb_net_tx_list_free(struct usb_net *); +int usb_net_rx_list_init(struct usb_net *, unsigned); +int usb_net_tx_list_init(struct usb_net *, unsigned); +void usb_net_rx_list_fini(struct usb_net *); +void usb_net_tx_list_fini(struct usb_net *); + +void usb_net_rx_start_pipes(struct usb_net *, usbd_callback); + +/* mii */ +void usb_net_lock_mii(struct usb_net *); +void usb_net_lock_mii_un_locked(struct usb_net *); +void usb_net_unlock_mii(struct usb_net *); +void usb_net_unlock_mii_un_locked(struct usb_net *); + +int usb_net_miibus_readreg(device_t, int, int, uint16_t *); +int usb_net_miibus_writereg(device_t, int, int, uint16_t); +void usb_net_miibus_statchg(struct ifnet *); + +/* autoconf */ +void usb_net_attach(struct usb_net *un, const char *, unsigned, unsigned); +void usb_net_detach(struct usb_net *un); +int usb_net_activate(device_t, devact_t); + +/* stop backend */ +void usb_net_stop(struct usb_net *, struct ifnet *, int); + + +/* + +common functionality + +DD - device dependant +DI - device indep + +init/init_locked: + - nothing if dying + - stop if running + - setup mac addr, filters, multicast + - very DD + - open rx/tx pipes + - maybe open intr pipe + - init rx/tx rings + - looks like can be mostly DI + - DD: can _chain be generalised? + - start rx pipes + - set running + - start stat_ch tick + +url_mbuf makes url(4) hard to make chain common +upl_mbuf similar for upl(4), which is worse and needs if_attach/if_input changes + +miibus: + - not all drivers do mii (cdce) + + calling ifmedia_init(): + - not for cdce, upl, urndis + - takes changecb and statcb + -- compare what does what + - most statcb can be converted to ether_mediastatus() + -- umb(4) is annoying + - changecb is ether_mediachange() sometimes + - DD setting link word/flag zero + - DD checking dying + - axen/mue/smsc/ure may reset multiple phys + - umb(4) does nothing + - ure(4) converts ENXIO into 0??? + +iff/iff_locked aka setmultii: + - mostly DD, could maybe share a function to do the 'while (enm != NULL) { .. }' loop + -- function will need a "do allmulti" return as well as success + - nothing for cdce, upl, urndis + +moving a bunch of init from attach/detach into usb_net_attach/detach + -- study last? + +rxeof: + - intro part looks DI: + - checking dying/cancelled/etc + - checking not normal completion + -- cdce/cue has a special log first time an error occurs after success + - get xfer status + - DD rxeof() has a loop or single run + - ends in DI drop lock, call enqueue, take lock, check dying/etc? + - when done, check dying/etc, if ok setup new transfer + +txeof: + - make look like axen, not cdce + - intro part looks DI: + - checking dying/cancelled/etc + - lower tx_cnt + - disable timer + - check status + -- maybe call start_locked(). + - for foo_mbuf versions, they m_freem() + - if error, maybe call clear endpoint stall + +start/encap: + - checks link/dying/flags + - some use OACTIVE to determine busy or not + - better like axen: + - set idx = tx_prod + - while tx_cnt < tx_list_cnt + - get mbuf to send + - call encap routine + - called with softc, mbuf and idex + -> softc -> usbnet.cdata + idx + - does DD stuff, + - consider having final usbd_setup_xfer/usbd_transfer->error+stop + move into start proper, to avoid having to ass in stop routine + - axe(4) encap does axe_tx_cnt++, axen(4) does it in start + - also cdce_encap, and some others + - move into start? think so + - cue calls it cue_send (kue, udav, upl, url too) + - url_send() has weirdo detach checking etc. + - dequeue if successful + - call bpf + - non foo_mbuf's call m_freem(m) here + - increase idx with % list_cnt + - increase tx_cnt + - set tx_prod = idx + - set 5 second timer + +ioctl. looks annoying to deal with. + - aue can lose its SIOCINITIFADDR + SIOCSIFMTU handlers + - cdce, cue, kue, upl too + - like the look of axen's SIOCSIFFLAGS over aue + - but make sure it runs foo_stop() if no UP|RUNNING + - has foo_if_flags to know what was previous status + - not all do, but make them all use this + - SIOCSIFFLAGS can call: + - stop, init, setmulti + - saves foo_if_flags + - default can call: + - setmulti | setcoe + - aue default has weirdo signal for a waiting task + - cdce, urndis eats ENETRESET + - cue has SIOCADDMULTI/SIOCDELMULTI case at the wrong level -- should be after + caling ether_ioctl() and checking its return + - kue too + - mue looks like axen already + - smsc has no special handler at all. calls calls ether_ioctl() and if it + gives SIOCADDMULTI/SIOCDELMULTI, setmulti. + - udav is worse. if ENETRESTART and running, setmulti + - url is like udav + +--done-start-- +stop/stop_locked: + - does DD "turn off" stuff + - sets timer = 0, link/LINK_FLAG = 0 + - sets stopping = true + - aue has stop task, can delete. now runs in close enough to process context? + - perhaps consider moving this generic for all if needed -- force test + - cue, udav, url also + - stops callout for stat change [not in all] + - aborts (some and then closes pipes, but don't do that, marking NULL): + - tx, rx, and maybe intr? + - see if_mue.c for a good way to handle multiple pipes + - free rx and tx resources: + - some have function to do this, most don't + - make generic function + - close pipes + - turn off IFF_RUNNING | OACTIVE + - XXX? do something with 'disable'? + +init is mostly DD + +activate: + -- likely can be fully DI + +foo_[rt]x_init_list(): + - make sure foo_cdata and foo_chain for all are good + -- looks mostly OK, but needs to become dynamically allocated + - input is tx_list_cnt, rx_list_cnt, sc, tx endpoint, rx endpoint, bufsize, maybe xfer flags + - for each desc: + setup chain sc [mbuf for old], xfer / buf for xfer + - xfer takes endpoint, size, ?flags, output &xfer + - tx: init tx_prod = tx_cnt = 0 + device specific diffs: + - aue_rx_list_init() calls aue_newbuf() to set foo_mbuf + - also cdce, cue, udav, upl, url, urndis + - rest no foo_mbuf + - smsc has tx_next/tx_free usage others don't + - ure_rx_list_init does not check for c->uc_xfer == NULL before create_xfer/get_buffer + -- should. can be partly filled if create returns an error + +tick/ticktask: + - tick (callout) does usb_add_task() (thread) for tick_real + - tick_real (aka tick_task): + - check for stopping/dying + - check for valid mii + - increase refcnt + - check timer, maybe call watchdog + - mii_tick() + - if link == 0, call statchg [not statcb] + - decrease refcnt, doing deteach broadcast trick + - reschedule tick + +miibus: + readreg/writereg: + - check dying/wrong phyno + - lock mii + - run DD operation + -- note that some transforms outside of miilock will need + to move inside the DD callback (eg, axen vs *val &= ~BMSR_EXTCAP;) + - unlock mii + - maybe return error + --> old api was void return, currently api is int + return but most drivers only return 0 (success) + statchg: + - some drivers have _link, some have _FLAG_LINK in _flags + - check ifp and mii are valid and running + - check mii->mii_media_status, set link based upon status + - if no link, do nothing (axe doesn't, check others) + - check status, set other DD bits; + -> this might be best to leave as mostly DD? + +init/init_locked: + -- all very DD + -- nothing to do + +--done-end-- + +*/ + +#endif /* _DEV_USB_USB_NET_H */ Index: if_axen.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/if_axen.c,v retrieving revision 1.50 diff -p -u -r1.50 if_axen.c --- if_axen.c 15 Jul 2019 03:14:22 -0000 1.50 +++ if_axen.c 22 Jul 2019 09:46:41 -0000 @@ -40,8 +40,6 @@ __KERNEL_RCSID(0, "$NetBSD: if_axen.c,v #include #include -#include - #include #include #include @@ -60,6 +58,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_axen.c,v #include #include #include +#include #include @@ -72,70 +71,17 @@ int axendebug = 0; #define DPRINTFN(n, x) #endif -struct axen_softc; - -struct axen_chain { - struct axen_softc *axen_sc; - struct usbd_xfer *axen_xfer; - uint8_t *axen_buf; -}; - -struct axen_cdata { - struct axen_chain axen_tx_chain[AXEN_TX_LIST_CNT]; - struct axen_chain axen_rx_chain[AXEN_RX_LIST_CNT]; - int axen_tx_prod; - int axen_tx_cnt; -}; - struct axen_softc { - device_t axen_dev; - struct ethercom axen_ec; - struct mii_data axen_mii; - krndsource_t rnd_source; - struct usbd_device * axen_udev; - struct usbd_interface * axen_iface; - - uint16_t axen_vendor; - uint16_t axen_product; - uint16_t axen_flags; - uint16_t axen_timer; - - int axen_ed[AXEN_ENDPT_MAX]; - struct usbd_pipe *axen_ep[AXEN_ENDPT_MAX]; - int axen_if_flags; - struct axen_cdata axen_cdata; - struct callout axen_stat_ch; - - int axen_refcnt; - bool axen_dying; - bool axen_stopping; + struct usb_net axen_un; bool axen_attached; - - struct usb_task axen_tick_task; - - kmutex_t axen_lock; - kmutex_t axen_mii_lock; - kmutex_t axen_rxlock; - kmutex_t axen_txlock; - kcondvar_t axen_detachcv; - - int axen_link; - - int axen_phyno; - struct timeval axen_rx_notice; - struct timeval axen_tx_notice; - u_int axen_rx_bufsz; - u_int axen_tx_bufsz; int axen_rev; - -#define sc_if axen_ec.ec_if }; -#define GET_MII(sc) (&(sc)->axen_mii) -#define GET_IFP(sc) (&(sc)->sc_if) +#define GET_MII(sc) (&(sc)->axen_un.un_mii) +#define GET_IFP(sc) (&(sc)->axen_un.un_ec.ec_if) struct axen_type { - struct usb_devno axen_dev; + struct usb_devno axen_devno; uint16_t axen_flags; #define AX178A 0x0001 /* AX88178a */ #define AX179 0x0002 /* AX88179 */ @@ -157,20 +103,15 @@ static const struct axen_type axen_devs[ static int axen_match(device_t, cfdata_t, void *); static void axen_attach(device_t, device_t, void *); static int axen_detach(device_t, int); -static int axen_activate(device_t, devact_t); CFATTACH_DECL_NEW(axen, sizeof(struct axen_softc), - axen_match, axen_attach, axen_detach, axen_activate); + axen_match, axen_attach, axen_detach, usb_net_activate); -static int axen_tx_list_init(struct axen_softc *); -static int axen_rx_list_init(struct axen_softc *); static struct mbuf *axen_newbuf(void); -static int axen_encap(struct axen_softc *, struct mbuf *, int); +static int axen_encap(struct usb_net *, 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 *); @@ -178,72 +119,29 @@ 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_setmulti(struct axen_softc *); 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 void axen_stop_cb(struct ifnet *, int); +static usbd_status axen_mii_read_reg(struct usb_net *, int, int, uint16_t *); +static usbd_status axen_mii_write_reg(struct usb_net *, int, int, uint16_t); static int axen_cmd(struct axen_softc *sc, int cmd, int index, int val, void *buf) { + struct usb_net * const un = &sc->axen_un; usb_device_request_t req; usbd_status err; - KASSERT(mutex_owned(&sc->axen_mii_lock)); + KASSERT(mutex_owned(&un->un_miilock)); - if (sc->axen_dying) + if (un->un_dying) return 0; if (AXEN_CMD_DIR(cmd)) @@ -255,7 +153,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 +165,55 @@ axen_cmd(struct axen_softc *sc, int cmd, return 0; } -static int -axen_miibus_readreg(device_t dev, int phy, int reg, uint16_t *val) +static usbd_status +axen_mii_read_reg(struct usb_net *un, int reg, int phy, uint16_t *val) { - struct axen_softc * const sc = device_private(dev); - usbd_status err; + struct axen_softc * const sc = un->un_sc; uint16_t data; + usbd_status err = axen_cmd(sc, AXEN_CMD_MII_READ_REG, reg, phy, &data); - mutex_enter(&sc->axen_lock); - if (sc->axen_dying || sc->axen_phyno != phy) { - mutex_exit(&sc->axen_lock); - return -1; - } - mutex_exit(&sc->axen_lock); - - axen_lock_mii(sc); - err = axen_cmd(sc, AXEN_CMD_MII_READ_REG, reg, phy, &data); - axen_unlock_mii(sc); - - if (err) { - aprint_error_dev(sc->axen_dev, "read PHY failed: %d\n", err); - return err; - } - - *val = le16toh(data); - DPRINTFN(2,("axen_miibus_readreg: phy 0x%x reg 0x%x val 0x%hx\n", - phy, reg, *val)); + if (!err) { + *val = le16toh(data); - if (reg == MII_BMSR) { - *val &= ~BMSR_EXTCAP; + if (reg == MII_BMSR) + *val &= ~BMSR_EXTCAP; } - return 0; + return err; } -static int -axen_miibus_writereg(device_t dev, int phy, int reg, uint16_t val) +static usbd_status +axen_mii_write_reg(struct usb_net *un, int reg, int phy, uint16_t val) { - struct axen_softc * const sc = device_private(dev); - usbd_status err; - uint16_t uval; - - mutex_enter(&sc->axen_lock); - if (sc->axen_dying || sc->axen_phyno != phy) { - mutex_exit(&sc->axen_lock); - return -1; - } - mutex_exit(&sc->axen_lock); + struct axen_softc * const sc = un->un_sc; + uint16_t uval = htole16(val); - uval = htole16(val); - - axen_lock_mii(sc); - err = axen_cmd(sc, AXEN_CMD_MII_WRITE_REG, reg, phy, &uval); - axen_unlock_mii(sc); - - DPRINTFN(2, ("axen_miibus_writereg: phy 0x%x reg 0x%x val 0x%04hx\n", - phy, reg, val)); - - if (err) { - aprint_error_dev(sc->axen_dev, "write PHY failed: %d\n", err); - return err; - } - - return 0; + return axen_cmd(sc, AXEN_CMD_MII_WRITE_REG, reg, phy, &uval); } static void axen_miibus_statchg(struct ifnet *ifp) { struct axen_softc * const sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); + struct usb_net * const un = &sc->axen_un; + struct mii_data * const mii = usb_net_mii(un); int err; uint16_t val; uint16_t wval; - if (sc->axen_dying) + if (un->un_dying) return; - sc->axen_link = 0; + un->un_link = false; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: - sc->axen_link++; + un->un_link = true; break; case IFM_1000_T: - sc->axen_link++; + un->un_link = true; break; default: break; @@ -361,7 +221,7 @@ axen_miibus_statchg(struct ifnet *ifp) } /* Lost link, do nothing. */ - if (sc->axen_link == 0) + if (!un->un_link) return; val = 0; @@ -384,11 +244,11 @@ axen_miibus_statchg(struct ifnet *ifp) DPRINTF(("%s: val=0x%x\n", __func__, val)); wval = htole16(val); - axen_lock_mii(sc); + usb_net_lock_mii(un); err = axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval); - axen_unlock_mii(sc); + usb_net_unlock_mii(un); if (err) { - aprint_error_dev(sc->axen_dev, "media change failed\n"); + aprint_error_dev(un->un_dev, "media change failed\n"); return; } } @@ -400,10 +260,11 @@ static int axen_ifmedia_upd(struct ifnet *ifp) { struct axen_softc * const sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); + struct usb_net * const un = &sc->axen_un; + struct mii_data * const mii = GET_MII(sc); int rc; - sc->axen_link = 0; + un->un_link = false; if (mii->mii_instance) { struct mii_softc *miisc; @@ -417,25 +278,12 @@ axen_ifmedia_upd(struct ifnet *ifp) 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; -} - static void -axen_iff_locked(struct axen_softc *sc) +axen_setmulti_locked(struct axen_softc *sc) { struct ifnet *ifp = GET_IFP(sc); - struct ethercom *ec = &sc->axen_ec; + struct usb_net * const un = &sc->axen_un; + struct ethercom *ec = &un->un_ec; struct ether_multi *enm; struct ether_multistep step; uint32_t h = 0; @@ -443,10 +291,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 +305,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 +316,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 +325,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 +334,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 +348,22 @@ allmulti: } static void -axen_iff(struct axen_softc *sc) +axen_setmulti(struct axen_softc *sc) { + struct usb_net * const un = &sc->axen_un; - axen_lock_mii(sc); - axen_iff_locked(sc); - axen_unlock_mii(sc); + usb_net_lock_mii(un); + axen_setmulti_locked(sc); + usb_net_unlock_mii(un); } static void axen_reset(struct axen_softc *sc) { + struct usb_net * const un = &sc->axen_un; - KASSERT(mutex_owned(&sc->axen_lock)); - if (sc->axen_dying) + KASSERT(mutex_owned(&un->un_lock)); + if (un->un_dying) return; /* XXX What to reset? */ @@ -521,11 +371,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 +378,7 @@ axen_get_eaddr(struct axen_softc *sc, vo return axen_cmd(sc, AXEN_CMD_MAC_READ_ETHER, 6, AXEN_CMD_MAC_NODE_ID, addr); #else + struct usb_net * const un = &sc->axen_un; int i, retry; uint8_t eeprom[20]; uint16_t csum; @@ -551,7 +397,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 +430,13 @@ axen_get_eaddr(struct axen_softc *sc, vo static void axen_ax88179_init(struct axen_softc *sc) { + struct usb_net * const un = &sc->axen_un; struct axen_qctrl qctrl; uint16_t ctl, temp; uint16_t wval; uint8_t val; - axen_lock_mii(sc); + usb_net_lock_mii(un); /* XXX: ? */ axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_UNK_05, &val); @@ -612,12 +459,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 +480,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 +520,9 @@ axen_ax88179_init(struct axen_softc *sc) qctrl.ifg = 0xff; break; default: - aprint_error_dev(sc->axen_dev, "unknown uplink bus:0x%02x\n", + aprint_error_dev(un->un_dev, "unknown uplink bus:0x%02x\n", val); - axen_unlock_mii(sc); + usb_net_unlock_mii(un); return; } axen_cmd(sc, AXEN_CMD_MAC_SET_RXSR, 5, AXEN_RX_BULKIN_QCTRL, &qctrl); @@ -710,31 +557,30 @@ axen_ax88179_init(struct axen_softc *sc) wval = htole16(ctl); DPRINTF(("axen: set to medium mode: 0x%04x\n", ctl)); axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval); - usbd_delay_ms(sc->axen_udev, 100); + usbd_delay_ms(un->un_udev, 100); axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MEDIUM_STATUS, &wval); DPRINTF(("axen: current medium mode: 0x%04x\n", le16toh(wval))); - axen_unlock_mii(sc); + usb_net_unlock_mii(un); #if 0 /* XXX: TBD.... */ #define GMII_LED_ACTIVE 0x1a #define GMII_PHY_PAGE_SEL 0x1e #define GMII_PHY_PAGE_SEL 0x1f #define GMII_PAGE_EXT 0x0007 - axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, GMII_PHY_PAGE_SEL, + usb_net_miibus_writereg(un->un_dev, un->un_phyno, GMII_PHY_PAGE_SEL, GMII_PAGE_EXT); - axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, GMII_PHY_PAGE, + usb_net_miibus_writereg(un->un_dev, un->un_phyno, GMII_PHY_PAGE, 0x002c); #endif #if 1 /* XXX: phy hack ? */ - axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x1F, 0x0005); - axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x0C, 0x0000); - axen_miibus_readreg(sc->axen_dev, sc->axen_phyno, 0x0001, &wval); - axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x01, - wval | 0x0080); - axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x1F, 0x0000); + usb_net_miibus_writereg(un->un_dev, un->un_phyno, 0x1F, 0x0005); + usb_net_miibus_writereg(un->un_dev, un->un_phyno, 0x0C, 0x0000); + usb_net_miibus_readreg(un->un_dev, un->un_phyno, 0x0001, &wval); + usb_net_miibus_writereg(un->un_dev, un->un_phyno, 0x01, wval | 0x0080); + usb_net_miibus_writereg(un->un_dev, un->un_phyno, 0x1F, 0x0000); #endif } @@ -742,10 +588,11 @@ static void axen_setcoe(struct axen_softc *sc) { struct ifnet *ifp = GET_IFP(sc); + struct usb_net * const un = &sc->axen_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) @@ -787,6 +634,7 @@ static void axen_attach(device_t parent, device_t self, void *aux) { struct axen_softc * const sc = device_private(self); + struct usb_net * const un = &sc->axen_un; struct usb_attach_arg *uaa = aux; struct usbd_device *dev = uaa->uaa_device; usbd_status err; @@ -797,14 +645,23 @@ axen_attach(device_t parent, device_t se char *devinfop; const char *devname = device_xname(self); struct ifnet *ifp; + uint16_t axen_flags; int i; + /* Switch to usb_net for device_private() */ + self->dv_private = un; + aprint_naive("\n"); aprint_normal("\n"); - sc->axen_dev = self; - sc->axen_udev = dev; - + un->un_dev = self; + un->un_udev = dev; + un->un_sc = sc; + un->un_stop_cb = axen_stop_cb; + un->un_watchdog_cb = axen_watchdog; + un->un_read_reg_cb = axen_mii_read_reg; + un->un_write_reg_cb = axen_mii_write_reg; + devinfop = usbd_devinfo_alloc(dev, 0); aprint_normal_dev(self, "%s\n", devinfop); usbd_devinfo_free(devinfop); @@ -816,95 +673,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[USB_NET_ENDPT_RX] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { - sc->axen_ed[AXEN_ENDPT_TX] = ed->bEndpointAddress; + un->un_ed[USB_NET_ENDPT_TX] = ed->bEndpointAddress; +#if 0 /* not used yet */ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { - sc->axen_ed[AXEN_ENDPT_INTR] = ed->bEndpointAddress; + un->un_ed[USB_NET_ENDPT_INTR] = ed->bEndpointAddress; +#endif } } /* Set these up now for axen_cmd(). */ - mutex_init(&sc->axen_mii_lock, MUTEX_DEFAULT, IPL_NONE); - mutex_init(&sc->axen_txlock, MUTEX_DEFAULT, IPL_SOFTUSB); - mutex_init(&sc->axen_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB); - mutex_init(&sc->axen_lock, MUTEX_DEFAULT, IPL_NONE); - cv_init(&sc->axen_detachcv, "axendet"); + usb_net_attach(un, "axendet", AXEN_RX_LIST_CNT, AXEN_TX_LIST_CNT); - sc->axen_phyno = AXEN_PHY_ID; - DPRINTF(("%s: phyno %d\n", device_xname(self), sc->axen_phyno)); + un->un_phyno = AXEN_PHY_ID; + DPRINTF(("%s: phyno %d\n", device_xname(self), un->un_phyno)); /* Get station address. */ - axen_lock_mii(sc); + usb_net_lock_mii(un); if (axen_get_eaddr(sc, &eaddr)) { - axen_unlock_mii(sc); + usb_net_unlock_mii(un); printf("EEPROM checksum error\n"); - cv_destroy(&sc->axen_detachcv); - mutex_destroy(&sc->axen_lock); - mutex_destroy(&sc->axen_rxlock); - mutex_destroy(&sc->axen_txlock); - mutex_destroy(&sc->axen_mii_lock); + usb_net_detach(un); return; } - axen_unlock_mii(sc); + usb_net_unlock_mii(un); axen_ax88179_init(sc); - /* - * An ASIX chip was detected. Inform the world. - */ - if (sc->axen_flags & AX178A) + /* An ASIX chip was detected. Inform the world. */ + if (axen_flags & AX178A) aprint_normal_dev(self, "AX88178a\n"); - else if (sc->axen_flags & AX179) + else if (axen_flags & AX179) aprint_normal_dev(self, "AX88179\n"); + else + aprint_normal_dev(self, "(unknown)\n"); aprint_normal_dev(self, "Ethernet address %s\n", ether_sprintf(eaddr)); /* Initialize interface info. */ - - ifp = &sc->sc_if; + ifp = GET_IFP(sc); ifp->if_softc = sc; strlcpy(ifp->if_xname, devname, IFNAMSIZ); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; @@ -916,7 +760,7 @@ axen_attach(device_t parent, device_t se IFQ_SET_READY(&ifp->if_snd); - sc->axen_ec.ec_capabilities = ETHERCAP_VLAN_MTU; + un->un_ec.ec_capabilities = ETHERCAP_VLAN_MTU; /* Adapter does not support TSOv6 (They call it LSOv2). */ ifp->if_capabilities |= IFCAP_TSOv4 | @@ -927,15 +771,15 @@ axen_attach(device_t parent, device_t se IFCAP_CSUM_UDPv6_Rx | IFCAP_CSUM_UDPv6_Tx; /* Initialize MII/media info. */ - mii = &sc->axen_mii; + mii = &un->un_mii; mii->mii_ifp = ifp; - mii->mii_readreg = axen_miibus_readreg; - mii->mii_writereg = axen_miibus_writereg; + mii->mii_readreg = usb_net_miibus_readreg; + mii->mii_writereg = usb_net_miibus_writereg; mii->mii_statchg = axen_miibus_statchg; mii->mii_flags = MIIF_AUTOTSLEEP; - sc->axen_ec.ec_mii = mii; - ifmedia_init(&mii->mii_media, 0, axen_ifmedia_upd, axen_ifmedia_sts); + un->un_ec.ec_mii = mii; + ifmedia_init(&mii->mii_media, 0, axen_ifmedia_upd, ether_mediastatus); mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); if (LIST_FIRST(&mii->mii_phys) == NULL) { @@ -947,15 +791,10 @@ axen_attach(device_t parent, device_t se /* Attach the interface. */ if_attach(ifp); ether_ifattach(ifp, eaddr); - rnd_attach_source(&sc->rnd_source, device_xname(sc->axen_dev), - RND_TYPE_NET, RND_FLAG_DEFAULT); - - callout_init(&sc->axen_stat_ch, CALLOUT_MPSAFE); - callout_setfunc(&sc->axen_stat_ch, axen_tick, sc); sc->axen_attached = true; - usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->axen_udev,sc->axen_dev); + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, un->un_udev, un->un_dev); if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); @@ -964,14 +803,15 @@ axen_attach(device_t parent, device_t se static int axen_detach(device_t self, int flags) { - struct axen_softc * const sc = device_private(self); - struct ifnet *ifp = GET_IFP(sc); + struct usb_net * const un = device_private(self); + struct axen_softc * const sc = un->un_sc; + struct ifnet * const ifp = usb_net_ifp(un); + + mutex_enter(&un->un_lock); + un->un_dying = true; + mutex_exit(&un->un_lock); - mutex_enter(&sc->axen_lock); - sc->axen_dying = true; - mutex_exit(&sc->axen_lock); - - DPRINTFN(2,("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); + DPRINTFN(2,("%s: %s: enter\n", device_xname(un->un_dev), __func__)); /* Detached before attached finished, so just bail out. */ if (!sc->axen_attached) @@ -979,80 +819,41 @@ axen_detach(device_t self, int flags) pmf_device_deregister(self); - callout_halt(&sc->axen_stat_ch, NULL); - usb_rem_task_wait(sc->axen_udev, &sc->axen_tick_task, - USB_TASKQ_DRIVER, NULL); - if (ifp->if_flags & IFF_RUNNING) { IFNET_LOCK(ifp); axen_stop(ifp, 1); IFNET_UNLOCK(ifp); } - mutex_enter(&sc->axen_lock); - sc->axen_refcnt--; - while (sc->axen_refcnt > 0) { + mutex_enter(&un->un_lock); + un->un_refcnt--; + while (un->un_refcnt > 0) { /* Wait for processes to go away */ - cv_wait(&sc->axen_detachcv, &sc->axen_lock); + cv_wait(&un->un_detachcv, &un->un_lock); } + mutex_exit(&un->un_lock); #ifdef DIAGNOSTIC - if (sc->axen_ep[AXEN_ENDPT_TX] != NULL || - sc->axen_ep[AXEN_ENDPT_RX] != NULL || - sc->axen_ep[AXEN_ENDPT_INTR] != NULL) + if (un->un_ep[USB_NET_ENDPT_TX] != NULL || + un->un_ep[USB_NET_ENDPT_RX] != NULL || + un->un_ep[USB_NET_ENDPT_INTR] != NULL) aprint_debug_dev(self, "detach has active endpoints\n"); #endif - mutex_exit(&sc->axen_lock); + usb_net_detach(un); - callout_destroy(&sc->axen_stat_ch); - rnd_detach_source(&sc->rnd_source); - mii_detach(&sc->axen_mii, MII_PHY_ANY, MII_OFFSET_ANY); - ifmedia_delete_instance(&sc->axen_mii.mii_media, IFM_INST_ANY); + mii_detach(&un->un_mii, MII_PHY_ANY, MII_OFFSET_ANY); + ifmedia_delete_instance(&un->un_mii.mii_media, IFM_INST_ANY); ether_ifdetach(ifp); if_detach(ifp); sc->axen_attached = false; - usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->axen_udev,sc->axen_dev); - - cv_destroy(&sc->axen_detachcv); - mutex_destroy(&sc->axen_lock); - mutex_destroy(&sc->axen_rxlock); - mutex_destroy(&sc->axen_txlock); - mutex_destroy(&sc->axen_mii_lock); + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, un->un_udev, un->un_dev); return 0; } -static int -axen_activate(device_t self, devact_t act) -{ - struct axen_softc * const sc = device_private(self); - struct ifnet *ifp = GET_IFP(sc); - - DPRINTFN(2,("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); - - switch (act) { - case DVACT_DEACTIVATE: - if_deactivate(ifp); - - mutex_enter(&sc->axen_lock); - sc->axen_dying = true; - mutex_exit(&sc->axen_lock); - - mutex_enter(&sc->axen_rxlock); - mutex_enter(&sc->axen_txlock); - sc->axen_stopping = true; - mutex_exit(&sc->axen_txlock); - mutex_exit(&sc->axen_rxlock); - - return 0; - default: - return EOPNOTSUPP; - } -} - static struct mbuf * axen_newbuf(void) { @@ -1074,59 +875,6 @@ axen_newbuf(void) 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); - } - } - - cd->axen_tx_prod = cd->axen_tx_cnt = 0; - - return 0; -} - /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. @@ -1134,10 +882,10 @@ axen_tx_list_init(struct axen_softc *sc) static void axen_rxeof(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 ifnet *ifp = GET_IFP(sc); - uint8_t *buf = c->axen_buf; + struct usb_net_chain *c = (struct usb_net_chain *)priv; + struct usb_net * const un = c->unc_un; + struct ifnet *ifp = &un->un_ec.ec_if; + uint8_t *buf = c->unc_buf; struct mbuf *m; uint32_t total_len; uint32_t rx_hdr, pkt_hdr; @@ -1146,30 +894,30 @@ axen_rxeof(struct usbd_xfer *xfer, void size_t pkt_len; size_t temp; - DPRINTFN(10,("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); + DPRINTFN(10,("%s: %s: enter\n", device_xname(un->un_dev), __func__)); - mutex_enter(&sc->axen_rxlock); + mutex_enter(&un->un_rxlock); - if (sc->axen_dying || sc->axen_stopping || + if (un->un_dying || un->un_stopping || status == USBD_INVAL || status == USBD_NOT_STARTED || status == USBD_CANCELLED || !(ifp->if_flags & IFF_RUNNING)) { - mutex_exit(&sc->axen_rxlock); + mutex_exit(&un->un_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", + 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(sc->axen_ep[AXEN_ENDPT_RX]); + usbd_clear_endpoint_stall_async(un->un_ep[USB_NET_ENDPT_RX]); goto done; } usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); if (total_len < 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; } @@ -1184,20 +932,20 @@ 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, + if (total_len > un->un_cdata.uncd_rx_bufsz) { + aprint_error_dev(un->un_dev, "rxeof: too large transfer (%u > %u)\n", - total_len, sc->axen_rx_bufsz); + total_len, un->un_cdata.uncd_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); + usbd_delay_ms(un->un_udev, 100); goto done; } @@ -1213,14 +961,14 @@ 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)); + device_xname(un->un_dev), pkt_count)); goto done; } #endif 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; @@ -1230,13 +978,13 @@ axen_rxeof(struct usbd_xfer *xfer, void 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; @@ -1257,14 +1005,14 @@ axen_rxeof(struct usbd_xfer *xfer, void 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); + mutex_exit(&un->un_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); + mutex_enter(&un->un_rxlock); + if (un->un_dying || un->un_stopping) { + mutex_exit(&un->un_rxlock); return; } @@ -1281,19 +1029,19 @@ nextpkt: } while (pkt_count > 0); done: - if (sc->axen_dying || sc->axen_stopping) { - mutex_exit(&sc->axen_rxlock); + if (un->un_dying || un->un_stopping) { + mutex_exit(&un->un_rxlock); return; } - mutex_exit(&sc->axen_rxlock); + mutex_exit(&un->un_rxlock); /* Setup new transfer. */ - usbd_setup_xfer(xfer, c, c->axen_buf, sc->axen_rx_bufsz, + usbd_setup_xfer(xfer, c, c->unc_buf, un->un_cdata.uncd_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__)); + DPRINTFN(10,("%s: %s: start rx\n",device_xname(un->un_dev),__func__)); } static int @@ -1348,21 +1096,21 @@ axen_csum_flags_rx(struct ifnet *ifp, ui 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); + struct usb_net_chain *c = (struct usb_net_chain *)priv; + struct usb_net * const un = c->unc_un; + struct usb_net_cdata *cd = &un->un_cdata; + struct ifnet * const ifp = usb_net_ifp(un); + + mutex_enter(&un->un_txlock); + if (un->un_stopping || un->un_dying) { + mutex_exit(&un->un_txlock); return; } - KASSERT(cd->axen_tx_cnt > 0); - cd->axen_tx_cnt--; + KASSERT(cd->uncd_tx_cnt > 0); + cd->uncd_tx_cnt--; - sc->axen_timer = 0; + un->un_timer = 0; switch (status) { case USBD_NOT_STARTED: @@ -1378,92 +1126,32 @@ axen_txeof(struct usbd_xfer *xfer, void default: ifp->if_oerrors++; - if (usbd_ratecheck(&sc->axen_tx_notice)) - aprint_error_dev(sc->axen_dev, "usb error on tx: %s\n", + 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(sc->axen_ep[AXEN_ENDPT_TX]); + usbd_clear_endpoint_stall_async(un->un_ep[USB_NET_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); + mutex_exit(&un->un_txlock); } static int -axen_encap(struct axen_softc *sc, struct mbuf *m, int idx) +axen_encap(struct usb_net *un, struct mbuf *m, int idx) { - struct ifnet *ifp = GET_IFP(sc); - struct axen_chain *c; + struct ifnet *ifp = usb_net_ifp(un); + struct usb_net_chain *c; usbd_status err; struct axen_sframe_hdr hdr; u_int length, boundary; - KASSERT(mutex_owned(&sc->axen_txlock)); + KASSERT(mutex_owned(&un->un_txlock)); - c = &sc->axen_cdata.axen_tx_chain[idx]; + c = &un->un_cdata.uncd_tx_chain[idx]; /* 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,29 +1164,29 @@ 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)); + memcpy(c->unc_buf, &hdr, sizeof(hdr)); + m_copydata(m, 0, m->m_pkthdr.len, c->unc_buf + sizeof(hdr)); - if (__predict_false(c->axen_xfer == NULL)) + if (__predict_false(c->unc_xfer == NULL)) return EIO; /* XXX plugged out or down */ - usbd_setup_xfer(c->axen_xfer, c, c->axen_buf, length, + usbd_setup_xfer(c->unc_xfer, c, c->unc_buf, length, USBD_FORCE_SHORT_XFER, 10000, axen_txeof); /* Transmit */ - err = usbd_transfer(c->axen_xfer); + err = usbd_transfer(c->unc_xfer); if (err != USBD_IN_PROGRESS) { /* XXXSMP IFNET_LOCK */ axen_stop(ifp, 0); @@ -1512,23 +1200,24 @@ static void axen_start_locked(struct ifnet *ifp) { struct axen_softc * const sc = ifp->if_softc; + struct usb_net * const un = &sc->axen_un; + struct usb_net_cdata *cd = &un->un_cdata; 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); + KASSERT(mutex_owned(&un->un_txlock)); + KASSERT(cd->uncd_tx_cnt <= AXEN_TX_LIST_CNT); - if (sc->axen_link == 0 || (ifp->if_flags & IFF_RUNNING) == 0) + if (!un->un_link || (ifp->if_flags & IFF_RUNNING) == 0) return; - idx = cd->axen_tx_prod; - while (cd->axen_tx_cnt < AXEN_TX_LIST_CNT) { + idx = cd->uncd_tx_prod; + while (cd->uncd_tx_cnt < AXEN_TX_LIST_CNT) { IFQ_POLL(&ifp->if_snd, m); if (m == NULL) break; - if (axen_encap(sc, m, idx)) { + if (axen_encap(un, m, idx)) { ifp->if_oerrors++; break; } @@ -1542,41 +1231,41 @@ axen_start_locked(struct ifnet *ifp) m_freem(m); idx = (idx + 1) % AXEN_TX_LIST_CNT; - cd->axen_tx_cnt++; + cd->uncd_tx_cnt++; } - cd->axen_tx_prod = idx; + cd->uncd_tx_prod = idx; /* * Set a timeout in case the chip goes out to lunch. */ - sc->axen_timer = 5; + un->un_timer = 5; } static void axen_start(struct ifnet *ifp) { struct axen_softc * const sc = ifp->if_softc; + struct usb_net * const un = &sc->axen_un; - mutex_enter(&sc->axen_txlock); - if (!sc->axen_stopping) + mutex_enter(&un->un_txlock); + if (!un->un_stopping) axen_start_locked(ifp); - mutex_exit(&sc->axen_txlock); + mutex_exit(&un->un_txlock); } static int axen_init_locked(struct ifnet *ifp) { struct axen_softc * const sc = ifp->if_softc; - struct axen_chain *c; + struct usb_net * const un = &sc->axen_un; usbd_status err; - int i; uint16_t rxmode; uint16_t wval; uint8_t bval; - KASSERT(mutex_owned(&sc->axen_lock)); + KASSERT(mutex_owned(&un->un_lock)); - if (sc->axen_dying) + if (un->un_dying) return EIO; /* Cancel pending I/O */ @@ -1585,7 +1274,7 @@ axen_init_locked(struct ifnet *ifp) /* Reset the ethernet interface. */ axen_reset(sc); - axen_lock_mii_sc_locked(sc); + usb_net_lock_mii_un_locked(un); /* XXX: ? */ bval = 0x01; @@ -1595,7 +1284,7 @@ axen_init_locked(struct ifnet *ifp) axen_setcoe(sc); /* Program promiscuous mode and multicast filters. */ - axen_iff_locked(sc); + axen_setmulti_locked(sc); /* Enable receiver, set RX mode */ axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); @@ -1604,58 +1293,36 @@ 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); + usb_net_unlock_mii_un_locked(un); /* 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]); + err = usb_net_ep_open_pipes(un); if (err) { - aprint_error_dev(sc->axen_dev, "open tx pipe failed: %s\n", + aprint_error_dev(un->un_dev, "open rx/tx pipes failed: %s\n", usbd_errstr(err)); return EIO; } /* Init RX ring. */ - if (axen_rx_list_init(sc)) { - aprint_error_dev(sc->axen_dev, "rx list init failed\n"); + if (usb_net_rx_list_init(un, 0)) { + aprint_error_dev(un->un_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"); + if (usb_net_tx_list_init(un, 0)) { + aprint_error_dev(un->un_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); + usb_net_rx_start_pipes(un, axen_rxeof); /* Indicate we are up and running. */ KASSERT(IFNET_LOCKED(ifp)); ifp->if_flags |= IFF_RUNNING; - callout_schedule(&sc->axen_stat_ch, hz); + callout_schedule(&un->un_stat_ch, hz); return 0; } @@ -1663,10 +1330,11 @@ static int axen_init(struct ifnet *ifp) { struct axen_softc * const sc = ifp->if_softc; + struct usb_net * const un = &sc->axen_un; - 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; } @@ -1675,6 +1343,7 @@ static int axen_ioctl(struct ifnet *ifp, u_long cmd, void *data) { struct axen_softc * const sc = ifp->if_softc; + struct usb_net * const un = &sc->axen_un; int error = 0; switch (cmd) { @@ -1690,18 +1359,18 @@ axen_ioctl(struct ifnet *ifp, u_long cmd axen_init(ifp); break; case IFF_UP | IFF_RUNNING: - if ((ifp->if_flags ^ sc->axen_if_flags) == IFF_PROMISC) { - axen_iff(sc); + if ((ifp->if_flags ^ un->un_if_flags) == IFF_PROMISC) { + axen_setmulti(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); + mutex_enter(&un->un_rxlock); + mutex_enter(&un->un_txlock); + un->un_if_flags = ifp->if_flags; + mutex_exit(&un->un_txlock); + mutex_exit(&un->un_rxlock); break; default: @@ -1712,12 +1381,12 @@ axen_ioctl(struct ifnet *ifp, u_long cmd switch (cmd) { case SIOCADDMULTI: case SIOCDELMULTI: - axen_iff(sc); + axen_setmulti(sc); break; case SIOCSIFCAP: - mutex_enter(&sc->axen_lock); + mutex_enter(&un->un_lock); axen_setcoe(sc); - mutex_exit(&sc->axen_lock); + mutex_exit(&un->un_lock); break; default: break; @@ -1732,15 +1401,16 @@ static void axen_watchdog(struct ifnet *ifp) { struct axen_softc * const sc = ifp->if_softc; - struct axen_chain *c; + struct usb_net * const un = &sc->axen_un; + struct usb_net_chain *c; usbd_status stat; ifp->if_oerrors++; - aprint_error_dev(sc->axen_dev, "watchdog timeout\n"); + aprint_error_dev(un->un_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); + c = &un->un_cdata.uncd_tx_chain[0]; + usbd_get_xfer_status(c->unc_xfer, NULL, NULL, NULL, &stat); + axen_txeof(c->unc_xfer, c, stat); if (!IFQ_IS_EMPTY(&ifp->if_snd)) axen_start(ifp); @@ -1751,125 +1421,42 @@ axen_watchdog(struct ifnet *ifp) * RX and TX lists. */ static void -axen_stop_locked(struct ifnet *ifp, int disable) +axen_stop_cb(struct ifnet *ifp, int disable) { struct axen_softc * const sc = ifp->if_softc; - struct axen_chain *c; - usbd_status err; - int i; + struct usb_net * const un = &sc->axen_un; uint16_t rxmode, wval; - KASSERT(mutex_owned(&sc->axen_lock)); - mutex_enter(&sc->axen_rxlock); - mutex_enter(&sc->axen_txlock); - sc->axen_stopping = true; - mutex_exit(&sc->axen_txlock); - mutex_exit(&sc->axen_rxlock); - axen_reset(sc); /* Disable receiver, set RX mode */ - axen_lock_mii_sc_locked(sc); + usb_net_lock_mii_un_locked(un); axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); rxmode = le16toh(wval); rxmode &= ~AXEN_RXCTL_START; wval = htole16(rxmode); axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); - axen_unlock_mii_sc_locked(sc); - - /* - * XXXSMP Would like to - * KASSERT(IFNET_LOCKED(ifp)) - * here but the locking order is: - * ifnet -> sc lock -> rxlock -> txlock - * and sc lock is already held. - */ - ifp->if_flags &= ~IFF_RUNNING; - sc->axen_timer = 0; - - callout_stop(&sc->axen_stat_ch); - sc->axen_link = 0; - - /* Stop transfers. */ - if (sc->axen_ep[AXEN_ENDPT_RX] != NULL) { - err = usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_RX]); - if (err) { - aprint_error_dev(sc->axen_dev, - "abort rx pipe failed: %s\n", usbd_errstr(err)); - } - } - - if (sc->axen_ep[AXEN_ENDPT_TX] != NULL) { - err = usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_TX]); - if (err) { - aprint_error_dev(sc->axen_dev, - "abort tx pipe failed: %s\n", usbd_errstr(err)); - } - } - - if (sc->axen_ep[AXEN_ENDPT_INTR] != NULL) { - err = usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_INTR]); - if (err) { - aprint_error_dev(sc->axen_dev, - "abort intr pipe failed: %s\n", usbd_errstr(err)); - } - } - - /* Free RX resources. */ - for (i = 0; i < AXEN_RX_LIST_CNT; i++) { - c = &sc->axen_cdata.axen_rx_chain[i]; - if (c->axen_xfer != NULL) { - usbd_destroy_xfer(c->axen_xfer); - c->axen_xfer = NULL; - } - } - - /* Free TX resources. */ - for (i = 0; i < AXEN_TX_LIST_CNT; i++) { - c = &sc->axen_cdata.axen_tx_chain[i]; - if (c->axen_xfer != NULL) { - usbd_destroy_xfer(c->axen_xfer); - c->axen_xfer = NULL; - } - } - - /* Close pipes. */ - if (sc->axen_ep[AXEN_ENDPT_RX] != NULL) { - err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_RX]); - if (err) { - aprint_error_dev(sc->axen_dev, - "close rx pipe failed: %s\n", usbd_errstr(err)); - } - sc->axen_ep[AXEN_ENDPT_RX] = NULL; - } + usb_net_unlock_mii_un_locked(un); +} - if (sc->axen_ep[AXEN_ENDPT_TX] != NULL) { - err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_TX]); - if (err) { - aprint_error_dev(sc->axen_dev, - "close tx pipe failed: %s\n", usbd_errstr(err)); - } - sc->axen_ep[AXEN_ENDPT_TX] = NULL; - } +static void +axen_stop_locked(struct ifnet *ifp, int disable) +{ + struct axen_softc * const sc = ifp->if_softc; + struct usb_net * const un = &sc->axen_un; - if (sc->axen_ep[AXEN_ENDPT_INTR] != NULL) { - err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_INTR]); - if (err) { - aprint_error_dev(sc->axen_dev, - "close intr pipe failed: %s\n", usbd_errstr(err)); - } - sc->axen_ep[AXEN_ENDPT_INTR] = NULL; - } + usb_net_stop(un, ifp, disable); } static void axen_stop(struct ifnet *ifp, int disable) { struct axen_softc * const sc = ifp->if_softc; + struct usb_net * const un = &sc->axen_un; - mutex_enter(&sc->axen_lock); + mutex_enter(&un->un_lock); axen_stop_locked(ifp, disable); - mutex_exit(&sc->axen_lock); + mutex_exit(&un->un_lock); } MODULE(MODULE_CLASS_DRIVER, if_axen, NULL); Index: if_axenreg.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/if_axenreg.h,v retrieving revision 1.14 diff -p -u -r1.14 if_axenreg.h --- if_axenreg.h 18 Jun 2019 09:34:57 -0000 1.14 +++ if_axenreg.h 22 Jul 2019 09:46:41 -0000 @@ -221,15 +221,6 @@ #define AXEN_CONFIG_NO 1 #define AXEN_IFACE_IDX 0 -/* - * The interrupt endpoint is currently unused - * by the ASIX part. - */ -#define AXEN_ENDPT_RX 0x0 -#define AXEN_ENDPT_TX 0x1 -#define AXEN_ENDPT_INTR 0x2 -#define AXEN_ENDPT_MAX 0x3 - struct axen_qctrl { uint8_t ctrl; uint8_t timer_low;