2022-08-20 15:28:25 +03:00
|
|
|
/* $NetBSD: usbnet.c,v 1.98 2022/08/20 12:28:25 riastradh Exp $ */
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2019-08-12 02:55:43 +03:00
|
|
|
* Common code shared between USB network drivers.
|
2019-07-31 12:13:16 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2022-08-20 15:28:25 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: usbnet.c,v 1.98 2022/08/20 12:28:25 riastradh Exp $");
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/kmem.h>
|
|
|
|
#include <sys/module.h>
|
2019-08-23 07:29:28 +03:00
|
|
|
#include <sys/atomic.h>
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
#include <dev/usb/usbnet.h>
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
#include <dev/usb/usbhist.h>
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata {
|
|
|
|
struct usbnet_chain *uncd_tx_chain;
|
|
|
|
struct usbnet_chain *uncd_rx_chain;
|
|
|
|
|
|
|
|
int uncd_tx_prod;
|
|
|
|
int uncd_tx_cnt;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct usbnet_private {
|
|
|
|
/*
|
2020-03-16 02:04:50 +03:00
|
|
|
* - unp_core_lock protects most of this structure, the public one,
|
|
|
|
* and the MII / media data.
|
2019-08-10 05:17:36 +03:00
|
|
|
* - unp_rxlock protects the rx path and its data
|
|
|
|
* - unp_txlock protects the tx path and its data
|
2019-09-09 10:20:16 +03:00
|
|
|
*
|
|
|
|
* the lock ordering is:
|
2020-03-16 02:04:50 +03:00
|
|
|
* ifnet lock -> unp_core_lock -> unp_rxlock -> unp_txlock
|
2022-03-03 08:53:23 +03:00
|
|
|
* -> unp_mcastlock
|
2020-03-16 02:04:50 +03:00
|
|
|
* - ifnet lock is not needed for unp_core_lock, but if ifnet lock is
|
2019-11-06 10:30:59 +03:00
|
|
|
* involved, it must be taken first
|
2019-08-10 05:17:36 +03:00
|
|
|
*/
|
2020-03-16 02:04:50 +03:00
|
|
|
kmutex_t unp_core_lock;
|
2019-08-10 05:17:36 +03:00
|
|
|
kmutex_t unp_rxlock;
|
|
|
|
kmutex_t unp_txlock;
|
|
|
|
|
2022-03-03 08:53:23 +03:00
|
|
|
kmutex_t unp_mcastlock;
|
|
|
|
bool unp_mcastactive;
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata unp_cdata;
|
|
|
|
|
|
|
|
struct ethercom unp_ec;
|
|
|
|
struct mii_data unp_mii;
|
|
|
|
struct usb_task unp_ticktask;
|
|
|
|
struct callout unp_stat_ch;
|
|
|
|
struct usbd_pipe *unp_ep[USBNET_ENDPT_MAX];
|
|
|
|
|
2022-03-03 08:50:39 +03:00
|
|
|
volatile bool unp_dying;
|
2019-08-10 05:17:36 +03:00
|
|
|
bool unp_stopping;
|
|
|
|
bool unp_attached;
|
2022-03-03 08:47:14 +03:00
|
|
|
bool unp_ifp_attached;
|
2019-08-10 05:17:36 +03:00
|
|
|
bool unp_link;
|
|
|
|
|
|
|
|
int unp_timer;
|
2019-09-13 10:55:05 +03:00
|
|
|
unsigned short unp_if_flags;
|
2019-08-23 07:29:28 +03:00
|
|
|
unsigned unp_number;
|
2019-08-10 05:17:36 +03:00
|
|
|
|
|
|
|
krndsource_t unp_rndsrc;
|
|
|
|
|
|
|
|
struct timeval unp_rx_notice;
|
|
|
|
struct timeval unp_tx_notice;
|
|
|
|
struct timeval unp_intr_notice;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define un_cdata(un) (&(un)->un_pri->unp_cdata)
|
|
|
|
|
2019-08-23 07:29:28 +03:00
|
|
|
volatile unsigned usbnet_number;
|
|
|
|
|
2022-03-03 08:52:20 +03:00
|
|
|
static void usbnet_isowned_rx(struct usbnet *);
|
|
|
|
static void usbnet_isowned_tx(struct usbnet *);
|
|
|
|
|
2022-08-16 03:44:20 +03:00
|
|
|
static inline void
|
2022-03-03 08:54:52 +03:00
|
|
|
usbnet_isowned_core(struct usbnet *un)
|
|
|
|
{
|
2022-08-16 03:44:20 +03:00
|
|
|
KASSERT(mutex_owned(&un->un_pri->unp_core_lock));
|
2022-03-03 08:54:52 +03:00
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
static int usbnet_modcmd(modcmd_t, void *);
|
|
|
|
|
2019-08-01 02:47:16 +03:00
|
|
|
#ifdef USB_DEBUG
|
|
|
|
#ifndef USBNET_DEBUG
|
|
|
|
#define usbnetdebug 0
|
|
|
|
#else
|
2019-09-08 21:59:32 +03:00
|
|
|
static int usbnetdebug = 0;
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
|
2019-08-01 02:47:16 +03:00
|
|
|
SYSCTL_SETUP(sysctl_hw_usbnet_setup, "sysctl hw.usbnet setup")
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
const struct sysctlnode *rnode;
|
|
|
|
const struct sysctlnode *cnode;
|
|
|
|
|
|
|
|
err = sysctl_createv(clog, 0, NULL, &rnode,
|
|
|
|
CTLFLAG_PERMANENT, CTLTYPE_NODE, "usbnet",
|
|
|
|
SYSCTL_DESCR("usbnet global controls"),
|
|
|
|
NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/* control debugging printfs */
|
|
|
|
err = sysctl_createv(clog, 0, &rnode, &cnode,
|
|
|
|
CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
|
|
|
|
"debug", SYSCTL_DESCR("Enable debugging output"),
|
|
|
|
NULL, 0, &usbnetdebug, sizeof(usbnetdebug), CTL_CREATE, CTL_EOL);
|
|
|
|
if (err)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
return;
|
|
|
|
fail:
|
|
|
|
aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* USBNET_DEBUG */
|
|
|
|
#endif /* USB_DEBUG */
|
|
|
|
|
|
|
|
#define DPRINTF(FMT,A,B,C,D) USBHIST_LOGN(usbnetdebug,1,FMT,A,B,C,D)
|
|
|
|
#define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(usbnetdebug,N,FMT,A,B,C,D)
|
|
|
|
#define USBNETHIST_FUNC() USBHIST_FUNC()
|
|
|
|
#define USBNETHIST_CALLED(name) USBHIST_CALLED(usbnetdebug)
|
2019-08-19 09:35:14 +03:00
|
|
|
#define USBNETHIST_CALLARGS(FMT,A,B,C,D) \
|
|
|
|
USBHIST_CALLARGS(usbnetdebug,FMT,A,B,C,D)
|
2019-08-23 07:29:28 +03:00
|
|
|
#define USBNETHIST_CALLARGSN(N,FMT,A,B,C,D) \
|
|
|
|
USBHIST_CALLARGSN(usbnetdebug,N,FMT,A,B,C,D)
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
/* Callback vectors. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
uno_stop(struct usbnet *un, struct ifnet *ifp, int disable)
|
|
|
|
{
|
2022-08-08 02:49:30 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
if (un->un_ops->uno_stop)
|
|
|
|
(*un->un_ops->uno_stop)(ifp, disable);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uno_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
|
|
|
|
{
|
2022-03-03 08:50:12 +03:00
|
|
|
|
2022-03-03 08:51:06 +03:00
|
|
|
KASSERTMSG(cmd != SIOCADDMULTI, "%s", ifp->if_xname);
|
|
|
|
KASSERTMSG(cmd != SIOCDELMULTI, "%s", ifp->if_xname);
|
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
2022-03-03 08:50:12 +03:00
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
if (un->un_ops->uno_ioctl)
|
|
|
|
return (*un->un_ops->uno_ioctl)(ifp, cmd, data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uno_override_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
|
|
|
|
{
|
2022-03-03 08:50:12 +03:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SIOCADDMULTI:
|
|
|
|
case SIOCDELMULTI:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
|
|
|
}
|
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
return (*un->un_ops->uno_override_ioctl)(ifp, cmd, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uno_init(struct usbnet *un, struct ifnet *ifp)
|
|
|
|
{
|
2022-03-03 08:50:05 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
2022-03-03 08:56:18 +03:00
|
|
|
return un->un_ops->uno_init ? (*un->un_ops->uno_init)(ifp) : 0;
|
2019-08-09 04:17:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uno_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
return (*un->un_ops->uno_read_reg)(un, phy, reg, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uno_write_reg(struct usbnet *un, int phy, int reg, uint16_t val)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
return (*un->un_ops->uno_write_reg)(un, phy, reg, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
uno_mii_statchg(struct usbnet *un, struct ifnet *ifp)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
(*un->un_ops->uno_statchg)(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_tx(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
return (*un->un_ops->uno_tx_prepare)(un, m, c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-15 08:52:23 +03:00
|
|
|
uno_rx_loop(struct usbnet *un, struct usbnet_chain *c, uint32_t total_len)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_rx(un);
|
2019-08-15 08:52:23 +03:00
|
|
|
(*un->un_ops->uno_rx_loop)(un, c, total_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
uno_tick(struct usbnet *un)
|
2019-08-09 04:17:33 +03:00
|
|
|
{
|
2019-08-15 08:52:23 +03:00
|
|
|
if (un->un_ops->uno_tick)
|
|
|
|
(*un->un_ops->uno_tick)(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
uno_intr(struct usbnet *un, usbd_status status)
|
|
|
|
{
|
|
|
|
if (un->un_ops->uno_intr)
|
|
|
|
(*un->un_ops->uno_intr)(un, status);
|
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/* Interrupt handling. */
|
|
|
|
|
|
|
|
static struct mbuf *
|
2019-08-16 11:38:21 +03:00
|
|
|
usbnet_newbuf(size_t buflen)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
|
usbnet(9): Fix mbuf alignment and narrow bounds check.
In usbnet.c rev. 1.16, usbnet_newbuf was first passed a buffer length
to verify it fits within MCLBYTES. It also changed m_adj to go
before, not after, setting m_len and m_pkthdr.len -- which had the
effect of making the m_adj a no-op, because after MGETHDR the mbuf
has zero length and m_adj stops at the length of the mbuf, so nothing
was aligned as intended.
To make this aligned as intended, we require the buffer length to be
_below_ MCLBYTES, by ETHER_ALIGN, so there's room for the ethernet
header in a maximum-length payload. Once we do that, it is safe to
initialize m_len = m_pkthdr.len = ETHER_ALIGN + buflen, which is
below the actual size of the mbuf (MHLEN or MCLBYTES, depending), and
_then_ do m_adj to align the pointer.
2022-08-12 14:25:45 +03:00
|
|
|
if (buflen > MCLBYTES - ETHER_ALIGN)
|
2020-08-28 20:05:32 +03:00
|
|
|
return NULL;
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2019-08-16 11:38:21 +03:00
|
|
|
if (buflen > MHLEN - ETHER_ALIGN) {
|
|
|
|
MCLGET(m, M_DONTWAIT);
|
|
|
|
if (!(m->m_flags & M_EXT)) {
|
|
|
|
m_freem(m);
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
usbnet(9): Fix mbuf alignment and narrow bounds check.
In usbnet.c rev. 1.16, usbnet_newbuf was first passed a buffer length
to verify it fits within MCLBYTES. It also changed m_adj to go
before, not after, setting m_len and m_pkthdr.len -- which had the
effect of making the m_adj a no-op, because after MGETHDR the mbuf
has zero length and m_adj stops at the length of the mbuf, so nothing
was aligned as intended.
To make this aligned as intended, we require the buffer length to be
_below_ MCLBYTES, by ETHER_ALIGN, so there's room for the ethernet
header in a maximum-length payload. Once we do that, it is safe to
initialize m_len = m_pkthdr.len = ETHER_ALIGN + buflen, which is
below the actual size of the mbuf (MHLEN or MCLBYTES, depending), and
_then_ do m_adj to align the pointer.
2022-08-12 14:25:45 +03:00
|
|
|
m->m_len = m->m_pkthdr.len = ETHER_ALIGN + buflen;
|
2019-07-31 12:13:16 +03:00
|
|
|
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
|
2019-08-06 03:19:57 +03:00
|
|
|
* enqueue a packet for higher levels (or usbnet_input() if previously
|
|
|
|
* using if_input() path.)
|
2019-07-31 12:13:16 +03:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
usbnet_enqueue(struct usbnet * const un, uint8_t *buf, size_t buflen,
|
2019-08-06 03:19:57 +03:00
|
|
|
int csum_flags, uint32_t csum_data, int mbuf_flags)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-23 07:29:28 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
2019-08-23 07:29:28 +03:00
|
|
|
struct usbnet_private * const unp __unused = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct mbuf *m;
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(5, "%jd: enter: len=%ju csf %#jx mbf %#jx",
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number, buflen, csum_flags, mbuf_flags);
|
|
|
|
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_isowned_rx(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-16 11:38:21 +03:00
|
|
|
m = usbnet_newbuf(buflen);
|
2019-07-31 12:13:16 +03:00
|
|
|
if (m == NULL) {
|
2020-03-13 21:17:40 +03:00
|
|
|
DPRINTF("%jd: no memory", unp->unp_number, 0, 0, 0);
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_ierrors);
|
2019-07-31 12:13:16 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_set_rcvif(m, ifp);
|
2019-08-06 03:19:57 +03:00
|
|
|
m->m_pkthdr.csum_flags = csum_flags;
|
|
|
|
m->m_pkthdr.csum_data = csum_data;
|
|
|
|
m->m_flags |= mbuf_flags;
|
2019-08-16 11:38:21 +03:00
|
|
|
memcpy(mtod(m, uint8_t *), buf, buflen);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/* push the packet up */
|
|
|
|
if_percpuq_enqueue(ifp->if_percpuq, m);
|
|
|
|
}
|
|
|
|
|
2019-08-06 03:19:57 +03:00
|
|
|
void
|
|
|
|
usbnet_input(struct usbnet * const un, uint8_t *buf, size_t buflen)
|
|
|
|
{
|
2019-08-23 07:29:28 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-08-06 03:19:57 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
2019-08-23 07:29:28 +03:00
|
|
|
struct usbnet_private * const unp __unused = un->un_pri;
|
2019-08-06 03:19:57 +03:00
|
|
|
struct mbuf *m;
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(5, "%jd: enter: buf %#jx len %ju",
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number, (uintptr_t)buf, buflen, 0);
|
|
|
|
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_isowned_rx(un);
|
2019-08-06 03:19:57 +03:00
|
|
|
|
2019-08-16 11:38:21 +03:00
|
|
|
m = usbnet_newbuf(buflen);
|
2019-08-06 03:19:57 +03:00
|
|
|
if (m == NULL) {
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_ierrors);
|
2019-08-06 03:19:57 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_set_rcvif(m, ifp);
|
|
|
|
memcpy(mtod(m, char *), buf, buflen);
|
|
|
|
|
|
|
|
/* push the packet up */
|
|
|
|
if_input(ifp, m);
|
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/*
|
|
|
|
* A frame has been uploaded: pass the resulting mbuf chain up to
|
|
|
|
* the higher level protocols.
|
|
|
|
*/
|
|
|
|
static void
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-23 07:29:28 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_chain * const c = priv;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = c->unc_un;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
uint32_t total_len;
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(5, "%jd: enter: status %#jx xfer %#jx",
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number, status, (uintptr_t)xfer, 0);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:50:39 +03:00
|
|
|
if (usbnet_isdying(un) || unp->unp_stopping ||
|
2019-07-31 12:13:16 +03:00
|
|
|
status == USBD_INVAL || status == USBD_NOT_STARTED ||
|
2022-03-03 08:47:43 +03:00
|
|
|
status == USBD_CANCELLED)
|
2019-07-31 12:13:16 +03:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (status != USBD_NORMAL_COMPLETION) {
|
2019-08-10 05:17:36 +03:00
|
|
|
if (usbd_ratecheck(&unp->unp_rx_notice))
|
2021-03-01 20:41:00 +03:00
|
|
|
device_printf(un->un_dev, "usb errors on rx: %s\n",
|
2019-07-31 12:13:16 +03:00
|
|
|
usbd_errstr(status));
|
|
|
|
if (status == USBD_STALLED)
|
2019-08-10 05:17:36 +03:00
|
|
|
usbd_clear_endpoint_stall_async(unp->unp_ep[USBNET_ENDPT_RX]);
|
2019-07-31 12:13:16 +03:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
if (total_len > un->un_rx_bufsz) {
|
2019-07-31 12:13:16 +03:00
|
|
|
aprint_error_dev(un->un_dev,
|
|
|
|
"rxeof: too large transfer (%u > %u)\n",
|
2019-08-10 05:17:36 +03:00
|
|
|
total_len, un->un_rx_bufsz);
|
2019-07-31 12:13:16 +03:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2019-08-15 08:52:23 +03:00
|
|
|
uno_rx_loop(un, c, total_len);
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_isowned_rx(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
done:
|
2022-03-03 08:50:39 +03:00
|
|
|
if (usbnet_isdying(un) || unp->unp_stopping)
|
2019-07-31 12:13:16 +03:00
|
|
|
goto out;
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/* Setup new transfer. */
|
2019-08-10 05:17:36 +03:00
|
|
|
usbd_setup_xfer(xfer, c, c->unc_buf, un->un_rx_bufsz,
|
|
|
|
un->un_rx_xfer_flags, USBD_NO_TIMEOUT, usbnet_rxeof);
|
2019-07-31 12:13:16 +03:00
|
|
|
usbd_transfer(xfer);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
usbnet_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_chain * const c = priv;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = c->unc_un;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(5, "%jd: enter: status %#jx xfer %#jx",
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number, status, (uintptr_t)xfer, 0);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_txlock);
|
2022-03-03 08:50:39 +03:00
|
|
|
if (unp->unp_stopping || usbnet_isdying(un)) {
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_txlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
KASSERT(cd->uncd_tx_cnt > 0);
|
|
|
|
cd->uncd_tx_cnt--;
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_timer = 0;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
switch (status) {
|
|
|
|
case USBD_NOT_STARTED:
|
|
|
|
case USBD_CANCELLED:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBD_NORMAL_COMPLETION:
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_opackets);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_oerrors);
|
2019-08-10 05:17:36 +03:00
|
|
|
if (usbd_ratecheck(&unp->unp_tx_notice))
|
2021-03-01 20:41:00 +03:00
|
|
|
device_printf(un->un_dev, "usb error on tx: %s\n",
|
2019-07-31 12:13:16 +03:00
|
|
|
usbd_errstr(status));
|
|
|
|
if (status == USBD_STALLED)
|
2019-08-10 05:17:36 +03:00
|
|
|
usbd_clear_endpoint_stall_async(unp->unp_ep[USBNET_ENDPT_TX]);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_txlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (status == USBD_NORMAL_COMPLETION && !IFQ_IS_EMPTY(&ifp->if_snd))
|
|
|
|
(*ifp->if_start)(ifp);
|
|
|
|
}
|
|
|
|
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
static void
|
2019-08-10 05:17:36 +03:00
|
|
|
usbnet_pipe_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
{
|
2019-09-08 21:59:32 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet * const un = priv;
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
struct usbnet_intr * const uni = un->un_intr;
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
|
2022-03-03 08:50:39 +03:00
|
|
|
if (uni == NULL || usbnet_isdying(un) || unp->unp_stopping ||
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
status == USBD_INVAL || status == USBD_NOT_STARTED ||
|
2022-03-03 08:47:50 +03:00
|
|
|
status == USBD_CANCELLED) {
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: uni %#jx d/s %#jx status %#jx",
|
2019-09-08 21:59:32 +03:00
|
|
|
unp->unp_number, (uintptr_t)uni,
|
2022-03-03 08:50:39 +03:00
|
|
|
(usbnet_isdying(un) << 8) | unp->unp_stopping, status);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
return;
|
2019-09-08 21:59:32 +03:00
|
|
|
}
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
|
|
|
|
if (status != USBD_NORMAL_COMPLETION) {
|
2019-08-10 05:17:36 +03:00
|
|
|
if (usbd_ratecheck(&unp->unp_intr_notice)) {
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
aprint_error_dev(un->un_dev, "usb error on intr: %s\n",
|
|
|
|
usbd_errstr(status));
|
|
|
|
}
|
|
|
|
if (status == USBD_STALLED)
|
2019-08-10 05:17:36 +03:00
|
|
|
usbd_clear_endpoint_stall_async(unp->unp_ep[USBNET_ENDPT_INTR]);
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: not normal status %#jx",
|
2019-09-08 21:59:32 +03:00
|
|
|
unp->unp_number, status, 0, 0);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
uno_intr(un, status);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
static void
|
|
|
|
usbnet_start_locked(struct ifnet *ifp)
|
|
|
|
{
|
2019-09-08 21:59:32 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct mbuf *m;
|
|
|
|
unsigned length;
|
2019-09-08 21:59:32 +03:00
|
|
|
bool done_transmit = false;
|
2021-04-25 08:15:20 +03:00
|
|
|
int idx, count;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: tx_cnt %jd list_cnt %jd link %jd",
|
2019-09-08 21:59:32 +03:00
|
|
|
unp->unp_number, cd->uncd_tx_cnt, un->un_tx_list_cnt,
|
|
|
|
unp->unp_link);
|
|
|
|
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_isowned_tx(un);
|
2019-08-10 05:17:36 +03:00
|
|
|
KASSERT(cd->uncd_tx_cnt <= un->un_tx_list_cnt);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-15 08:52:23 +03:00
|
|
|
if (!unp->unp_link || (ifp->if_flags & IFF_RUNNING) == 0) {
|
2020-03-14 06:01:36 +03:00
|
|
|
DPRINTF("start called no link (%jx) or running (flags %jx)",
|
2019-08-15 08:52:23 +03:00
|
|
|
unp->unp_link, ifp->if_flags, 0, 0);
|
2019-07-31 12:13:16 +03:00
|
|
|
return;
|
2019-08-15 08:52:23 +03:00
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-23 07:29:28 +03:00
|
|
|
if (cd->uncd_tx_cnt == un->un_tx_list_cnt) {
|
2020-03-13 21:17:40 +03:00
|
|
|
DPRINTF("start called, tx busy (%#jx == %#jx)",
|
2019-08-23 07:29:28 +03:00
|
|
|
cd->uncd_tx_cnt, un->un_tx_list_cnt, 0, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
idx = cd->uncd_tx_prod;
|
2021-04-25 08:15:20 +03:00
|
|
|
count = 0;
|
2019-08-10 05:17:36 +03:00
|
|
|
while (cd->uncd_tx_cnt < un->un_tx_list_cnt) {
|
2019-07-31 12:13:16 +03:00
|
|
|
IFQ_POLL(&ifp->if_snd, m);
|
2019-08-23 07:29:28 +03:00
|
|
|
if (m == NULL) {
|
|
|
|
DPRINTF("start called, queue empty", 0, 0, 0, 0);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
2019-08-23 07:29:28 +03:00
|
|
|
}
|
2019-08-12 02:55:43 +03:00
|
|
|
KASSERT(m->m_pkthdr.len <= un->un_tx_bufsz);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
struct usbnet_chain *c = &cd->uncd_tx_chain[idx];
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
length = uno_tx_prepare(un, m, c);
|
2019-07-31 12:13:16 +03:00
|
|
|
if (length == 0) {
|
2019-08-23 07:29:28 +03:00
|
|
|
DPRINTF("uno_tx_prepare gave zero length", 0, 0, 0, 0);
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_oerrors);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (__predict_false(c->unc_xfer == NULL)) {
|
2019-08-23 07:29:28 +03:00
|
|
|
DPRINTF("unc_xfer is NULL", 0, 0, 0, 0);
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_oerrors);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
usbd_setup_xfer(c->unc_xfer, c, c->unc_buf, length,
|
2019-08-10 05:17:36 +03:00
|
|
|
un->un_tx_xfer_flags, 10000, usbnet_txeof);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/* Transmit */
|
|
|
|
usbd_status err = usbd_transfer(c->unc_xfer);
|
|
|
|
if (err != USBD_IN_PROGRESS) {
|
2020-03-14 06:01:36 +03:00
|
|
|
DPRINTF("usbd_transfer on %#jx for %ju bytes: %jd",
|
2019-08-23 07:29:28 +03:00
|
|
|
(uintptr_t)c->unc_buf, length, err, 0);
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_oerrors);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
|
|
|
}
|
2019-09-08 21:59:32 +03:00
|
|
|
done_transmit = true;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
idx = (idx + 1) % un->un_tx_list_cnt;
|
2019-07-31 12:13:16 +03:00
|
|
|
cd->uncd_tx_cnt++;
|
2021-04-25 08:15:20 +03:00
|
|
|
count++;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
cd->uncd_tx_prod = idx;
|
|
|
|
|
2020-03-14 06:01:36 +03:00
|
|
|
DPRINTF("finished with start; tx_cnt %jd list_cnt %jd link %jd",
|
2019-09-08 21:59:32 +03:00
|
|
|
cd->uncd_tx_cnt, un->un_tx_list_cnt, unp->unp_link, 0);
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/*
|
|
|
|
* Set a timeout in case the chip goes out to lunch.
|
|
|
|
*/
|
2019-09-08 21:59:32 +03:00
|
|
|
if (done_transmit)
|
|
|
|
unp->unp_timer = 5;
|
2021-04-25 08:15:20 +03:00
|
|
|
|
|
|
|
if (count != 0)
|
|
|
|
rnd_add_uint32(&unp->unp_rndsrc, count);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_if_start(struct ifnet *ifp)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-09-08 21:59:32 +03:00
|
|
|
USBNETHIST_FUNC();
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: stopping %jd",
|
2019-09-08 21:59:32 +03:00
|
|
|
unp->unp_number, unp->unp_stopping, 0, 0);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_txlock);
|
|
|
|
if (!unp->unp_stopping)
|
2019-07-31 12:13:16 +03:00
|
|
|
usbnet_start_locked(ifp);
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_txlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Chain management.
|
|
|
|
*
|
|
|
|
* RX and TX are identical. Keep them that way.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Start of common RX functions */
|
|
|
|
|
|
|
|
static size_t
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_rx_list_size(struct usbnet_cdata * const cd, struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
return sizeof(*cd->uncd_rx_chain) * un->un_rx_list_cnt;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_rx_list_alloc(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
cd->uncd_rx_chain = kmem_zalloc(usbnet_rx_list_size(cd, un), KM_SLEEP);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_rx_list_free(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (cd->uncd_rx_chain) {
|
2019-08-10 05:17:36 +03:00
|
|
|
kmem_free(cd->uncd_rx_chain, usbnet_rx_list_size(cd, un));
|
2019-07-31 12:13:16 +03:00
|
|
|
cd->uncd_rx_chain = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_rx_list_init(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
for (size_t i = 0; i < un->un_rx_list_cnt; i++) {
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet_chain *c = &cd->uncd_rx_chain[i];
|
|
|
|
|
|
|
|
c->unc_un = un;
|
|
|
|
if (c->unc_xfer == NULL) {
|
2019-08-10 05:17:36 +03:00
|
|
|
int err = usbd_create_xfer(unp->unp_ep[USBNET_ENDPT_RX],
|
|
|
|
un->un_rx_bufsz, un->un_rx_xfer_flags, 0,
|
2019-08-09 04:17:33 +03:00
|
|
|
&c->unc_xfer);
|
2019-07-31 12:13:16 +03:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
c->unc_buf = usbd_get_buffer(c->unc_xfer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_rx_list_fini(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
for (size_t i = 0; i < un->un_rx_list_cnt; i++) {
|
2019-07-31 12:13:16 +03:00
|
|
|
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
|
2019-08-16 11:38:21 +03:00
|
|
|
usbnet_rx_start_pipes(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_rxlock);
|
|
|
|
mutex_enter(&unp->unp_txlock);
|
|
|
|
unp->unp_stopping = false;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
for (size_t i = 0; i < un->un_rx_list_cnt; i++) {
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet_chain *c = &cd->uncd_rx_chain[i];
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
usbd_setup_xfer(c->unc_xfer, c, c->unc_buf, un->un_rx_bufsz,
|
2019-08-16 11:38:21 +03:00
|
|
|
un->un_rx_xfer_flags, USBD_NO_TIMEOUT, usbnet_rxeof);
|
2019-07-31 12:13:16 +03:00
|
|
|
usbd_transfer(c->unc_xfer);
|
2019-08-03 18:58:14 +03:00
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_txlock);
|
|
|
|
mutex_exit(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Start of common TX functions */
|
|
|
|
|
|
|
|
static size_t
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_tx_list_size(struct usbnet_cdata * const cd, struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
return sizeof(*cd->uncd_tx_chain) * un->un_tx_list_cnt;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_tx_list_alloc(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
cd->uncd_tx_chain = kmem_zalloc(usbnet_tx_list_size(cd, un), KM_SLEEP);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_tx_list_free(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (cd->uncd_tx_chain) {
|
2019-08-10 05:17:36 +03:00
|
|
|
kmem_free(cd->uncd_tx_chain, usbnet_tx_list_size(cd, un));
|
2019-07-31 12:13:16 +03:00
|
|
|
cd->uncd_tx_chain = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_tx_list_init(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
for (size_t i = 0; i < un->un_tx_list_cnt; i++) {
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet_chain *c = &cd->uncd_tx_chain[i];
|
|
|
|
|
|
|
|
c->unc_un = un;
|
|
|
|
if (c->unc_xfer == NULL) {
|
2019-08-10 05:17:36 +03:00
|
|
|
int err = usbd_create_xfer(unp->unp_ep[USBNET_ENDPT_TX],
|
|
|
|
un->un_tx_bufsz, un->un_tx_xfer_flags, 0,
|
2019-08-09 04:17:33 +03:00
|
|
|
&c->unc_xfer);
|
2019-07-31 12:13:16 +03:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
c->unc_buf = usbd_get_buffer(c->unc_xfer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_tx_list_fini(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
for (size_t i = 0; i < un->un_tx_list_cnt; i++) {
|
2019-07-31 12:13:16 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2019-08-23 07:29:28 +03:00
|
|
|
cd->uncd_tx_prod = cd->uncd_tx_cnt = 0;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* End of common TX functions */
|
|
|
|
|
|
|
|
/* Endpoint pipe management. */
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_ep_close_pipes(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < __arraycount(unp->unp_ep); i++) {
|
|
|
|
if (unp->unp_ep[i] == NULL)
|
2019-07-31 12:13:16 +03:00
|
|
|
continue;
|
2022-03-03 09:06:52 +03:00
|
|
|
usbd_close_pipe(unp->unp_ep[i]);
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_ep[i] = NULL;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static usbd_status
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_ep_open_pipes(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_intr * const uni = un->un_intr;
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < __arraycount(unp->unp_ep); i++) {
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
usbd_status err;
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
if (un->un_ed[i] == 0)
|
|
|
|
continue;
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
if (i == USBNET_ENDPT_INTR && uni) {
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
err = usbd_open_pipe_intr(un->un_iface, un->un_ed[i],
|
2019-08-10 05:17:36 +03:00
|
|
|
USBD_EXCLUSIVE_USE | USBD_MPSAFE, &unp->unp_ep[i], un,
|
|
|
|
uni->uni_buf, uni->uni_bufsz, usbnet_pipe_intr,
|
|
|
|
uni->uni_interval);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
} else {
|
|
|
|
err = usbd_open_pipe(un->un_iface, un->un_ed[i],
|
2019-08-10 05:17:36 +03:00
|
|
|
USBD_EXCLUSIVE_USE | USBD_MPSAFE, &unp->unp_ep[i]);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
if (err) {
|
|
|
|
usbnet_ep_close_pipes(un);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return USBD_NORMAL_COMPLETION;
|
|
|
|
}
|
|
|
|
|
2022-03-03 09:05:38 +03:00
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_ep_stop_pipes(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < __arraycount(unp->unp_ep); i++) {
|
|
|
|
if (unp->unp_ep[i] == NULL)
|
2019-07-31 12:13:16 +03:00
|
|
|
continue;
|
2022-03-03 09:05:38 +03:00
|
|
|
usbd_abort_pipe(unp->unp_ep[i]);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-03 08:56:09 +03:00
|
|
|
static int
|
2019-08-09 04:17:33 +03:00
|
|
|
usbnet_init_rx_tx(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
|
|
|
usbd_status err;
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
int error = 0;
|
|
|
|
|
2022-08-08 02:49:30 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
2022-03-03 08:47:36 +03:00
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
|
2022-03-03 08:50:39 +03:00
|
|
|
if (usbnet_isdying(un)) {
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
return EIO;
|
|
|
|
}
|
2020-03-16 02:04:50 +03:00
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/* 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));
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
error = EIO;
|
|
|
|
goto out;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Init RX ring. */
|
2019-08-09 04:17:33 +03:00
|
|
|
if (usbnet_rx_list_init(un)) {
|
2019-07-31 12:13:16 +03:00
|
|
|
aprint_error_dev(un->un_dev, "rx list init failed\n");
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
error = ENOBUFS;
|
|
|
|
goto out;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Init TX ring. */
|
2019-08-09 04:17:33 +03:00
|
|
|
if (usbnet_tx_list_init(un)) {
|
2019-07-31 12:13:16 +03:00
|
|
|
aprint_error_dev(un->un_dev, "tx list init failed\n");
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
error = ENOBUFS;
|
|
|
|
goto out;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Indicate we are up and running. */
|
2022-08-08 02:49:30 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
2019-07-31 12:13:16 +03:00
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
|
|
|
2022-03-03 08:53:23 +03:00
|
|
|
/*
|
|
|
|
* If the hardware has a multicast filter, program it and then
|
|
|
|
* allow updates to it while we're running.
|
|
|
|
*/
|
|
|
|
if (un->un_ops->uno_mcast) {
|
|
|
|
mutex_enter(&unp->unp_mcastlock);
|
|
|
|
(*un->un_ops->uno_mcast)(ifp);
|
|
|
|
unp->unp_mcastactive = true;
|
|
|
|
mutex_exit(&unp->unp_mcastlock);
|
|
|
|
}
|
|
|
|
|
2022-03-03 08:46:58 +03:00
|
|
|
/* Start up the receive pipe(s). */
|
|
|
|
usbnet_rx_start_pipes(un);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
callout_schedule(&unp->unp_stat_ch, hz);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
out:
|
|
|
|
if (error) {
|
|
|
|
usbnet_rx_list_fini(un);
|
|
|
|
usbnet_tx_list_fini(un);
|
|
|
|
usbnet_ep_close_pipes(un);
|
|
|
|
}
|
|
|
|
|
2022-03-03 08:55:52 +03:00
|
|
|
/*
|
|
|
|
* For devices without any media autodetection, treat success
|
|
|
|
* here as an active link.
|
|
|
|
*/
|
|
|
|
if (un->un_ops->uno_statchg == NULL)
|
|
|
|
usbnet_set_link(un, error == 0);
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
return error;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
/* MII management. */
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:54:28 +03:00
|
|
|
static int
|
2019-08-09 04:17:33 +03:00
|
|
|
usbnet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-19 09:35:14 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = device_private(dev);
|
2019-08-20 09:37:06 +03:00
|
|
|
int err;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
/* MII layer ensures core_lock is held. */
|
|
|
|
usbnet_isowned_core(un);
|
|
|
|
|
2022-03-03 08:50:39 +03:00
|
|
|
if (usbnet_isdying(un)) {
|
2019-07-31 12:13:16 +03:00
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
err = uno_read_reg(un, phy, reg, val);
|
2019-07-31 12:13:16 +03:00
|
|
|
if (err) {
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: read PHY failed: %jd",
|
2022-03-03 08:50:39 +03:00
|
|
|
un->un_pri->unp_number, err, 0, 0);
|
2019-08-20 09:37:06 +03:00
|
|
|
return err;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-03 08:54:28 +03:00
|
|
|
static int
|
2019-08-09 04:17:33 +03:00
|
|
|
usbnet_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-19 09:35:14 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = device_private(dev);
|
2019-08-20 09:37:06 +03:00
|
|
|
int err;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
/* MII layer ensures core_lock is held. */
|
|
|
|
usbnet_isowned_core(un);
|
|
|
|
|
2022-03-03 08:50:39 +03:00
|
|
|
if (usbnet_isdying(un)) {
|
2019-07-31 12:13:16 +03:00
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
err = uno_write_reg(un, phy, reg, val);
|
2019-07-31 12:13:16 +03:00
|
|
|
if (err) {
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: write PHY failed: %jd",
|
2022-03-03 08:50:39 +03:00
|
|
|
un->un_pri->unp_number, err, 0, 0);
|
2019-08-20 09:37:06 +03:00
|
|
|
return err;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-03 08:54:28 +03:00
|
|
|
static void
|
2019-08-09 04:17:33 +03:00
|
|
|
usbnet_mii_statchg(struct ifnet *ifp)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
/* MII layer ensures core_lock is held. */
|
|
|
|
usbnet_isowned_core(un);
|
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
uno_mii_statchg(un, ifp);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
usbnet_media_upd(struct ifnet *ifp)
|
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct mii_data * const mii = usbnet_mii(un);
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
/* ifmedia layer ensures core_lock is held. */
|
|
|
|
usbnet_isowned_core(un);
|
|
|
|
|
2022-03-03 08:48:30 +03:00
|
|
|
/* ifmedia changes only with IFNET_LOCK held. */
|
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
|
|
|
|
2022-03-03 08:50:39 +03:00
|
|
|
if (usbnet_isdying(un))
|
2019-07-31 12:13:16 +03:00
|
|
|
return EIO;
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_link = false;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (mii->mii_instance) {
|
|
|
|
struct mii_softc *miisc;
|
|
|
|
|
|
|
|
LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
|
|
|
|
mii_phy_reset(miisc);
|
|
|
|
}
|
|
|
|
|
2022-08-20 15:28:25 +03:00
|
|
|
if (unp->unp_stopping)
|
|
|
|
return 0;
|
2019-07-31 12:13:16 +03:00
|
|
|
return ether_mediachange(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ioctl */
|
|
|
|
|
|
|
|
static int
|
|
|
|
usbnet_ifflags_cb(struct ethercom *ec)
|
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct ifnet *ifp = &ec->ec_if;
|
|
|
|
struct usbnet *un = ifp->if_softc;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
int rv = 0;
|
|
|
|
|
2022-03-03 08:47:28 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
|
|
|
|
2019-09-13 10:55:05 +03:00
|
|
|
const u_short changed = ifp->if_flags ^ unp->unp_if_flags;
|
2019-07-31 12:13:16 +03:00
|
|
|
if ((changed & ~(IFF_CANTCHANGE | IFF_DEBUG)) == 0) {
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_if_flags = ifp->if_flags;
|
2019-07-31 12:13:16 +03:00
|
|
|
if ((changed & IFF_PROMISC) != 0)
|
|
|
|
rv = ENETRESET;
|
|
|
|
} else {
|
|
|
|
rv = ENETRESET;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_if_ioctl(struct ifnet *ifp, u_long cmd, void *data)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-23 07:29:28 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-23 07:29:28 +03:00
|
|
|
struct usbnet_private * const unp __unused = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
int error;
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(11, "%jd: enter %#jx data %#jx",
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number, cmd, (uintptr_t)data, 0);
|
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
if (un->un_ops->uno_override_ioctl)
|
|
|
|
return uno_override_ioctl(un, ifp, cmd, data);
|
2019-08-06 03:19:57 +03:00
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
error = ether_ioctl(ifp, cmd, data);
|
usbnet: Defer hardware multicast filter updates to USB task.
Breaks deadlock:
- usbnet_detach holds usbnet lock, awaits kpause in ure_reset
- callout holds softclock `lock' (sequential softints, blocks kpause
wakeup), awaits softnet_lock in tcp_timer_keep, frag6_fasttimo, &c.
- soclose holds softnet_lock, awaits usbnet lock in SIOCDELMULTI
This change breaks the deadlock by not passing the SIOCADDMULTI or
SIOCDELMULTI ioctl synchronously to the driver, which typically takes
the usbnet lock.
With this change, the ethernet layer still maintains the list of
multicast addresses synchronously, but we defer the driver logic that
updates the hardware multicast filter to an asynchronous USB task
without softnet_lock held.
This doesn't cause exactly the same ioctl to be sent to the driver --
usbnet just sends SIOCDELMULTI with an all-zero struct ifreq, and
might drop some ioctls if issued in quick succession. This is OK
because none of the drivers actually distinguish between SIOCADDMULTI
and SIOCDELMULTI, or examine the argument; the drivers just commit
whatever multicast addresses are listed in the ethercom.
Other than the different ioctl submitted, there is no change to the
ABI or locking scheme of usbnet, so this is safe to pull up to
netbsd-9. This means we unfortunately can't guarantee that if a
process issues SIOCADDMULTI and then sendto, the multicast filter
update will be done by the time of the sendto -- and, more
importantly, the packets received in reply to it. But failing to
guarantee that is better than deadlocking! Later changes on HEAD
will restore the synchronous multicast filter updates with much more
extensive ABI changes and API simplifications in usbnet(9).
Proposed on tech-net:
https://mail-index.netbsd.org/tech-net/2021/12/30/msg008164.html
XXX pullup-9
2022-01-30 00:37:07 +03:00
|
|
|
if (error == ENETRESET) {
|
|
|
|
switch (cmd) {
|
|
|
|
case SIOCADDMULTI:
|
|
|
|
case SIOCDELMULTI:
|
2022-03-03 08:53:23 +03:00
|
|
|
/*
|
|
|
|
* If there's a hardware multicast filter, and
|
|
|
|
* it has been programmed by usbnet_init_rx_tx
|
|
|
|
* and is active, update it now. Otherwise,
|
|
|
|
* drop the update on the floor -- it will be
|
|
|
|
* observed by usbnet_init_rx_tx next time we
|
|
|
|
* bring the interface up.
|
|
|
|
*/
|
|
|
|
if (un->un_ops->uno_mcast) {
|
|
|
|
mutex_enter(&unp->unp_mcastlock);
|
|
|
|
if (unp->unp_mcastactive)
|
|
|
|
(*un->un_ops->uno_mcast)(ifp);
|
|
|
|
mutex_exit(&unp->unp_mcastlock);
|
|
|
|
}
|
usbnet: Defer hardware multicast filter updates to USB task.
Breaks deadlock:
- usbnet_detach holds usbnet lock, awaits kpause in ure_reset
- callout holds softclock `lock' (sequential softints, blocks kpause
wakeup), awaits softnet_lock in tcp_timer_keep, frag6_fasttimo, &c.
- soclose holds softnet_lock, awaits usbnet lock in SIOCDELMULTI
This change breaks the deadlock by not passing the SIOCADDMULTI or
SIOCDELMULTI ioctl synchronously to the driver, which typically takes
the usbnet lock.
With this change, the ethernet layer still maintains the list of
multicast addresses synchronously, but we defer the driver logic that
updates the hardware multicast filter to an asynchronous USB task
without softnet_lock held.
This doesn't cause exactly the same ioctl to be sent to the driver --
usbnet just sends SIOCDELMULTI with an all-zero struct ifreq, and
might drop some ioctls if issued in quick succession. This is OK
because none of the drivers actually distinguish between SIOCADDMULTI
and SIOCDELMULTI, or examine the argument; the drivers just commit
whatever multicast addresses are listed in the ethercom.
Other than the different ioctl submitted, there is no change to the
ABI or locking scheme of usbnet, so this is safe to pull up to
netbsd-9. This means we unfortunately can't guarantee that if a
process issues SIOCADDMULTI and then sendto, the multicast filter
update will be done by the time of the sendto -- and, more
importantly, the packets received in reply to it. But failing to
guarantee that is better than deadlocking! Later changes on HEAD
will restore the synchronous multicast filter updates with much more
extensive ABI changes and API simplifications in usbnet(9).
Proposed on tech-net:
https://mail-index.netbsd.org/tech-net/2021/12/30/msg008164.html
XXX pullup-9
2022-01-30 00:37:07 +03:00
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error = uno_ioctl(un, ifp, cmd, data);
|
|
|
|
}
|
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
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
|
|
|
|
*
|
2020-03-16 02:04:50 +03:00
|
|
|
* usbnet_if_stop() is for the if_stop handler.
|
2019-07-31 12:13:16 +03:00
|
|
|
*/
|
2022-03-03 08:55:29 +03:00
|
|
|
static void
|
2019-07-31 12:13:16 +03:00
|
|
|
usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
|
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
|
|
|
|
2022-08-08 02:49:30 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
|
|
|
|
2022-03-03 08:53:23 +03:00
|
|
|
/*
|
|
|
|
* For drivers with hardware multicast filter update callbacks:
|
|
|
|
* Prevent concurrent access to the hardware registers by
|
|
|
|
* multicast filter updates, which happens without IFNET_LOCK
|
|
|
|
* or the usbnet core lock.
|
|
|
|
*/
|
|
|
|
if (un->un_ops->uno_mcast) {
|
|
|
|
mutex_enter(&unp->unp_mcastlock);
|
|
|
|
unp->unp_mcastactive = false;
|
|
|
|
mutex_exit(&unp->unp_mcastlock);
|
|
|
|
}
|
|
|
|
|
2022-03-03 08:49:37 +03:00
|
|
|
/*
|
|
|
|
* Prevent new activity (rescheduling ticks, xfers, &c.) and
|
|
|
|
* clear the watchdog timer.
|
|
|
|
*/
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_rxlock);
|
|
|
|
mutex_enter(&unp->unp_txlock);
|
|
|
|
unp->unp_stopping = true;
|
2022-03-03 08:49:37 +03:00
|
|
|
unp->unp_timer = 0;
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_txlock);
|
|
|
|
mutex_exit(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:48:22 +03:00
|
|
|
/*
|
|
|
|
* Stop the timer first, then the task -- if the timer was
|
|
|
|
* already firing, we stop the task or wait for it complete
|
|
|
|
* only after if last fired. Setting unp_stopping prevents the
|
|
|
|
* timer task from being scheduled again.
|
|
|
|
*/
|
|
|
|
callout_halt(&unp->unp_stat_ch, &unp->unp_core_lock);
|
|
|
|
usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
|
|
|
|
&unp->unp_core_lock);
|
|
|
|
|
2022-03-03 08:56:44 +03:00
|
|
|
/* Stop transfers. */
|
|
|
|
usbnet_ep_stop_pipes(un);
|
|
|
|
|
2022-03-03 08:48:22 +03:00
|
|
|
/*
|
|
|
|
* Now that the software is quiescent, ask the driver to stop
|
|
|
|
* the hardware. The driver's uno_stop routine now has
|
|
|
|
* exclusive access to any registers that might previously have
|
|
|
|
* been used by to ifmedia, mii, or ioctl callbacks.
|
2022-03-03 08:49:58 +03:00
|
|
|
*
|
|
|
|
* Don't bother if the device is being detached, though -- if
|
|
|
|
* it's been unplugged then there's no point in trying to touch
|
|
|
|
* the registers.
|
2022-03-03 08:48:22 +03:00
|
|
|
*/
|
2022-03-03 08:50:39 +03:00
|
|
|
if (!usbnet_isdying(un))
|
2022-03-03 08:49:58 +03:00
|
|
|
uno_stop(un, ifp, disable);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/* Free RX/TX resources. */
|
|
|
|
usbnet_rx_list_fini(un);
|
|
|
|
usbnet_tx_list_fini(un);
|
|
|
|
|
|
|
|
/* Close pipes. */
|
|
|
|
usbnet_ep_close_pipes(un);
|
2020-03-16 02:04:50 +03:00
|
|
|
|
2022-03-03 08:47:36 +03:00
|
|
|
/* Everything is quesced now. */
|
2022-08-08 02:49:30 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
2022-03-03 08:46:58 +03:00
|
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_if_stop(struct ifnet *ifp, int disable)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:47:28 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
|
|
|
|
2022-03-03 08:55:10 +03:00
|
|
|
/*
|
|
|
|
* If we're already stopped, nothing to do.
|
|
|
|
*
|
|
|
|
* XXX This should be an assertion, but it may require some
|
|
|
|
* analysis -- and possibly some tweaking -- of sys/net to
|
|
|
|
* ensure.
|
|
|
|
*/
|
|
|
|
if ((ifp->if_flags & IFF_RUNNING) == 0)
|
|
|
|
return;
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_enter(&unp->unp_core_lock);
|
2019-07-31 12:13:16 +03:00
|
|
|
usbnet_stop(un, ifp, disable);
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_exit(&unp->unp_core_lock);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2019-09-08 21:59:32 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = arg;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(10, "%jd: enter", unp->unp_number, 0, 0, 0);
|
2019-09-08 21:59:32 +03:00
|
|
|
|
2022-03-03 08:49:22 +03:00
|
|
|
/* Perform periodic stuff in process context */
|
|
|
|
usb_add_task(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
usbnet_watchdog(struct ifnet *ifp)
|
|
|
|
{
|
2019-08-23 07:29:28 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-18 12:29:38 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_oerrors);
|
2021-03-01 20:41:00 +03:00
|
|
|
device_printf(un->un_dev, "watchdog timeout\n");
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (cd->uncd_tx_cnt > 0) {
|
2020-03-14 06:01:36 +03:00
|
|
|
DPRINTF("uncd_tx_cnt=%ju non zero, aborting pipe", 0, 0, 0, 0);
|
2022-03-03 09:05:38 +03:00
|
|
|
usbd_abort_pipe(unp->unp_ep[USBNET_ENDPT_TX]);
|
2019-08-23 07:29:28 +03:00
|
|
|
if (cd->uncd_tx_cnt != 0)
|
2020-03-14 06:01:36 +03:00
|
|
|
DPRINTF("uncd_tx_cnt now %ju", cd->uncd_tx_cnt, 0, 0, 0);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!IFQ_IS_EMPTY(&ifp->if_snd))
|
|
|
|
(*ifp->if_start)(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
usbnet_tick_task(void *arg)
|
|
|
|
{
|
2019-09-08 21:59:32 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = arg;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
|
|
|
struct mii_data * const mii = usbnet_mii(un);
|
|
|
|
|
2022-03-03 08:49:29 +03:00
|
|
|
USBNETHIST_CALLARGSN(8, "%jd: enter", unp->unp_number, 0, 0, 0);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:47:21 +03:00
|
|
|
mutex_enter(&unp->unp_txlock);
|
|
|
|
const bool timeout = unp->unp_timer != 0 && --unp->unp_timer == 0;
|
|
|
|
mutex_exit(&unp->unp_txlock);
|
|
|
|
if (timeout)
|
2019-07-31 12:13:16 +03:00
|
|
|
usbnet_watchdog(ifp);
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
DPRINTFN(8, "mii %#jx ifp %#jx", (uintptr_t)mii, (uintptr_t)ifp, 0, 0);
|
2019-12-14 18:40:43 +03:00
|
|
|
if (mii) {
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_enter(&unp->unp_core_lock);
|
2019-12-14 18:40:43 +03:00
|
|
|
mii_tick(mii);
|
|
|
|
if (!unp->unp_link)
|
|
|
|
(*mii->mii_statchg)(ifp);
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_exit(&unp->unp_core_lock);
|
2019-12-14 18:40:43 +03:00
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-15 08:52:23 +03:00
|
|
|
/* Call driver if requested. */
|
|
|
|
uno_tick(un);
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_enter(&unp->unp_core_lock);
|
2022-03-03 08:50:39 +03:00
|
|
|
if (!unp->unp_stopping && !usbnet_isdying(un))
|
2019-08-10 05:17:36 +03:00
|
|
|
callout_schedule(&unp->unp_stat_ch, hz);
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_exit(&unp->unp_core_lock);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_if_init(struct ifnet *ifp)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2022-03-03 08:50:22 +03:00
|
|
|
int error;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:47:28 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
|
|
|
|
2022-03-03 08:48:37 +03:00
|
|
|
/*
|
|
|
|
* Prevent anyone from bringing the interface back up once
|
|
|
|
* we're detaching.
|
|
|
|
*/
|
2022-03-03 08:50:47 +03:00
|
|
|
if (usbnet_isdying(un))
|
2022-03-03 08:48:37 +03:00
|
|
|
return EIO;
|
|
|
|
|
2022-03-03 08:55:10 +03:00
|
|
|
/*
|
|
|
|
* If we're already running, nothing to do.
|
|
|
|
*
|
|
|
|
* XXX This should be an assertion, but it may require some
|
|
|
|
* analysis -- and possibly some tweaking -- of sys/net to
|
|
|
|
* ensure.
|
|
|
|
*/
|
|
|
|
if (ifp->if_flags & IFF_RUNNING)
|
|
|
|
return 0;
|
|
|
|
|
2022-03-03 08:50:22 +03:00
|
|
|
mutex_enter(&un->un_pri->unp_core_lock);
|
|
|
|
error = uno_init(un, ifp);
|
2022-03-03 08:56:09 +03:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
error = usbnet_init_rx_tx(un);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
out: mutex_exit(&un->un_pri->unp_core_lock);
|
2022-03-03 08:50:22 +03:00
|
|
|
|
|
|
|
return error;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
2019-08-11 04:31:19 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
/* Various accessors. */
|
|
|
|
|
|
|
|
void
|
|
|
|
usbnet_set_link(struct usbnet *un, bool link)
|
|
|
|
{
|
|
|
|
un->un_pri->unp_link = link;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ifnet *
|
|
|
|
usbnet_ifp(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return &un->un_pri->unp_ec.ec_if;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ethercom *
|
|
|
|
usbnet_ec(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return &un->un_pri->unp_ec;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct mii_data *
|
|
|
|
usbnet_mii(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return un->un_pri->unp_ec.ec_mii;
|
|
|
|
}
|
|
|
|
|
|
|
|
krndsource_t *
|
|
|
|
usbnet_rndsrc(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return &un->un_pri->unp_rndsrc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
usbnet_softc(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return un->un_sc;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
usbnet_havelink(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return un->un_pri->unp_link;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
usbnet_isdying(struct usbnet *un)
|
|
|
|
{
|
2022-03-03 08:50:39 +03:00
|
|
|
return atomic_load_relaxed(&un->un_pri->unp_dying);
|
2019-08-10 05:17:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Locking. */
|
|
|
|
|
2022-03-03 08:52:20 +03:00
|
|
|
static void
|
|
|
|
usbnet_isowned_rx(struct usbnet *un)
|
2019-08-10 05:17:36 +03:00
|
|
|
{
|
2022-03-03 08:52:20 +03:00
|
|
|
KASSERT(mutex_owned(&un->un_pri->unp_rxlock));
|
2019-08-10 05:17:36 +03:00
|
|
|
}
|
|
|
|
|
2022-03-03 08:52:20 +03:00
|
|
|
static void
|
|
|
|
usbnet_isowned_tx(struct usbnet *un)
|
2019-08-10 05:17:36 +03:00
|
|
|
{
|
2022-03-03 08:52:20 +03:00
|
|
|
KASSERT(mutex_owned(&un->un_pri->unp_txlock));
|
2019-08-10 05:17:36 +03:00
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/* Autoconf management. */
|
|
|
|
|
2019-08-06 03:19:57 +03:00
|
|
|
static bool
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_empty_eaddr(struct usbnet * const un)
|
2019-08-06 03:19:57 +03:00
|
|
|
{
|
|
|
|
return (un->un_eaddr[0] == 0 && un->un_eaddr[1] == 0 &&
|
|
|
|
un->un_eaddr[2] == 0 && un->un_eaddr[3] == 0 &&
|
|
|
|
un->un_eaddr[4] == 0 && un->un_eaddr[5] == 0);
|
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/*
|
|
|
|
* 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.
|
2019-08-06 03:19:57 +03:00
|
|
|
*
|
|
|
|
* To skip ethernet configuration (eg, point-to-point), make sure that
|
|
|
|
* the un_eaddr[] is fully zero.
|
2019-07-31 12:13:16 +03:00
|
|
|
*/
|
2019-08-10 05:17:36 +03:00
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
void
|
2022-03-03 08:56:28 +03:00
|
|
|
usbnet_attach(struct usbnet *un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
/* Required inputs. */
|
|
|
|
KASSERT(un->un_ops->uno_tx_prepare);
|
|
|
|
KASSERT(un->un_ops->uno_rx_loop);
|
2019-08-10 05:17:36 +03:00
|
|
|
KASSERT(un->un_rx_bufsz);
|
|
|
|
KASSERT(un->un_tx_bufsz);
|
|
|
|
KASSERT(un->un_rx_list_cnt);
|
|
|
|
KASSERT(un->un_tx_list_cnt);
|
|
|
|
|
2019-08-12 02:55:43 +03:00
|
|
|
/* Unfortunate fact. */
|
|
|
|
KASSERT(un == device_private(un->un_dev));
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
un->un_pri = kmem_zalloc(sizeof(*un->un_pri), KM_SLEEP);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
usbnet: Defer hardware multicast filter updates to USB task.
Breaks deadlock:
- usbnet_detach holds usbnet lock, awaits kpause in ure_reset
- callout holds softclock `lock' (sequential softints, blocks kpause
wakeup), awaits softnet_lock in tcp_timer_keep, frag6_fasttimo, &c.
- soclose holds softnet_lock, awaits usbnet lock in SIOCDELMULTI
This change breaks the deadlock by not passing the SIOCADDMULTI or
SIOCDELMULTI ioctl synchronously to the driver, which typically takes
the usbnet lock.
With this change, the ethernet layer still maintains the list of
multicast addresses synchronously, but we defer the driver logic that
updates the hardware multicast filter to an asynchronous USB task
without softnet_lock held.
This doesn't cause exactly the same ioctl to be sent to the driver --
usbnet just sends SIOCDELMULTI with an all-zero struct ifreq, and
might drop some ioctls if issued in quick succession. This is OK
because none of the drivers actually distinguish between SIOCADDMULTI
and SIOCDELMULTI, or examine the argument; the drivers just commit
whatever multicast addresses are listed in the ethercom.
Other than the different ioctl submitted, there is no change to the
ABI or locking scheme of usbnet, so this is safe to pull up to
netbsd-9. This means we unfortunately can't guarantee that if a
process issues SIOCADDMULTI and then sendto, the multicast filter
update will be done by the time of the sendto -- and, more
importantly, the packets received in reply to it. But failing to
guarantee that is better than deadlocking! Later changes on HEAD
will restore the synchronous multicast filter updates with much more
extensive ABI changes and API simplifications in usbnet(9).
Proposed on tech-net:
https://mail-index.netbsd.org/tech-net/2021/12/30/msg008164.html
XXX pullup-9
2022-01-30 00:37:07 +03:00
|
|
|
usb_init_task(&unp->unp_ticktask, usbnet_tick_task, un,
|
|
|
|
USB_TASKQ_MPSAFE);
|
2019-08-10 05:17:36 +03:00
|
|
|
callout_init(&unp->unp_stat_ch, CALLOUT_MPSAFE);
|
|
|
|
callout_setfunc(&unp->unp_stat_ch, usbnet_tick, un);
|
|
|
|
|
|
|
|
mutex_init(&unp->unp_txlock, MUTEX_DEFAULT, IPL_SOFTUSB);
|
|
|
|
mutex_init(&unp->unp_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB);
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_init(&unp->unp_core_lock, MUTEX_DEFAULT, IPL_NONE);
|
2022-03-03 08:53:23 +03:00
|
|
|
mutex_init(&unp->unp_mcastlock, MUTEX_DEFAULT, IPL_SOFTCLOCK);
|
2019-08-10 05:17:36 +03:00
|
|
|
|
|
|
|
rnd_attach_source(&unp->unp_rndsrc, device_xname(un->un_dev),
|
2019-07-31 12:13:16 +03:00
|
|
|
RND_TYPE_NET, RND_FLAG_DEFAULT);
|
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
usbnet_rx_list_alloc(un);
|
|
|
|
usbnet_tx_list_alloc(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number = atomic_inc_uint_nv(&usbnet_number);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_attached = true;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-20 09:37:06 +03:00
|
|
|
usbnet_attach_mii(struct usbnet *un, const struct usbnet_mii *unm)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
struct mii_data * const mii = &unp->unp_mii;
|
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
KASSERT(un->un_ops->uno_read_reg);
|
|
|
|
KASSERT(un->un_ops->uno_write_reg);
|
|
|
|
KASSERT(un->un_ops->uno_statchg);
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
mii->mii_ifp = ifp;
|
2019-08-09 04:17:33 +03:00
|
|
|
mii->mii_readreg = usbnet_mii_readreg;
|
|
|
|
mii->mii_writereg = usbnet_mii_writereg;
|
|
|
|
mii->mii_statchg = usbnet_mii_statchg;
|
2019-07-31 12:13:16 +03:00
|
|
|
mii->mii_flags = MIIF_AUTOTSLEEP;
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
usbnet_ec(un)->ec_mii = mii;
|
2020-03-16 02:04:50 +03:00
|
|
|
ifmedia_init_with_lock(&mii->mii_media, 0,
|
2022-08-16 03:44:20 +03:00
|
|
|
usbnet_media_upd, ether_mediastatus, &unp->unp_core_lock);
|
2019-08-20 09:37:06 +03:00
|
|
|
mii_attach(un->un_dev, mii, unm->un_mii_capmask, unm->un_mii_phyloc,
|
2022-08-16 03:44:20 +03:00
|
|
|
unm->un_mii_offset, unm->un_mii_flags);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
usbnet_attach_ifp(struct usbnet *un,
|
|
|
|
unsigned if_flags, /* additional if_flags */
|
2019-08-01 02:47:16 +03:00
|
|
|
unsigned if_extflags, /* additional if_extflags */
|
2019-08-20 09:37:06 +03:00
|
|
|
const struct usbnet_mii *unm) /* additional mii_attach flags */
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
KASSERT(unp->unp_attached);
|
2022-03-03 08:47:14 +03:00
|
|
|
KASSERT(!unp->unp_ifp_attached);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:47:14 +03:00
|
|
|
ifp->if_softc = un;
|
2019-07-31 12:13:16 +03:00
|
|
|
strlcpy(ifp->if_xname, device_xname(un->un_dev), IFNAMSIZ);
|
|
|
|
ifp->if_flags = if_flags;
|
|
|
|
ifp->if_extflags = IFEF_MPSAFE | if_extflags;
|
2020-03-16 02:04:50 +03:00
|
|
|
ifp->if_ioctl = usbnet_if_ioctl;
|
|
|
|
ifp->if_start = usbnet_if_start;
|
|
|
|
ifp->if_init = usbnet_if_init;
|
|
|
|
ifp->if_stop = usbnet_if_stop;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-20 09:37:06 +03:00
|
|
|
if (unm)
|
|
|
|
usbnet_attach_mii(un, unm);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
else
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_link = true;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/* Attach the interface. */
|
if_attach and if_initialize cannot fail, don't test return value
These were originally made failable back in 2017 when if_initialize
allocated a softint in every interface for link state changes, so
that it could fail gracefully instead of panicking:
https://mail-index.NetBSD.org/source-changes/2017/10/23/msg089053.html
However, this spawned many seldom- or never-tested error branches,
which are risky to have around. And that softint in every interface
has since been replaced by a single global workqueue, because link
state changes require thread context but not low latency or high
throughput:
https://mail-index.NetBSD.org/source-changes/2020/02/06/msg113759.html
So there is no longer any reason for if_initialize to fail. (The
subroutine if_stats_init can't fail because percpu_alloc can't fail
either.)
There is a snag: the softint_establish in if_percpuq_create could
fail, potentially leading to bad consequences later on trying to use
the softint. This change doesn't introduce any new bugs because of
the snag -- if_percpuq_attach was already broken. However, the snag
can be better addressed without spawning error branches, either by
using a single softint or making softints less scarce.
(Separate commit will change the signatures of if_attach and
if_initialize to return void, scheduled to ride whatever is the next
convenient kernel bump.)
Patch and testing on amd64 and evbmips64-eb by maya@; commit message
soliloquy, and compile-testing on evbppc/i386/earmv7hf, by me.
2021-06-16 03:21:17 +03:00
|
|
|
if_initialize(ifp);
|
2019-08-18 12:29:38 +03:00
|
|
|
if (ifp->_if_input == NULL)
|
|
|
|
ifp->if_percpuq = if_percpuq_create(ifp);
|
|
|
|
if_register(ifp);
|
2022-03-03 08:47:14 +03:00
|
|
|
unp->unp_ifp_attached = true;
|
2019-08-06 03:19:57 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If ethernet address is all zero, skip ether_ifattach() and
|
|
|
|
* instead attach bpf here..
|
|
|
|
*/
|
|
|
|
if (!usbnet_empty_eaddr(un)) {
|
2019-08-10 05:17:36 +03:00
|
|
|
ether_set_ifflags_cb(&unp->unp_ec, usbnet_ifflags_cb);
|
2019-08-06 04:42:22 +03:00
|
|
|
aprint_normal_dev(un->un_dev, "Ethernet address %s\n",
|
|
|
|
ether_sprintf(un->un_eaddr));
|
2019-08-06 03:19:57 +03:00
|
|
|
ether_ifattach(ifp, un->un_eaddr);
|
|
|
|
} else {
|
|
|
|
if_alloc_sadl(ifp);
|
|
|
|
bpf_attach(ifp, DLT_RAW, 0);
|
|
|
|
}
|
2019-08-07 04:47:18 +03:00
|
|
|
|
2019-08-18 12:29:38 +03:00
|
|
|
/* Now ready, and attached. */
|
|
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
|
|
|
2019-08-07 04:47:18 +03:00
|
|
|
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");
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
usbnet_detach(device_t self, int flags)
|
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = device_private(self);
|
2019-08-28 09:07:21 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
|
|
|
/* Detached before attached finished, so just bail out. */
|
|
|
|
if (unp == NULL || !unp->unp_attached)
|
|
|
|
return 0;
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
|
|
|
struct mii_data * const mii = usbnet_mii(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:48:37 +03:00
|
|
|
/*
|
|
|
|
* Prevent new activity. After we stop the interface, it
|
|
|
|
* cannot be brought back up.
|
|
|
|
*/
|
2022-03-03 08:50:39 +03:00
|
|
|
atomic_store_relaxed(&unp->unp_dying, true);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:48:37 +03:00
|
|
|
/*
|
|
|
|
* If we're still running on the network, stop and wait for all
|
|
|
|
* asynchronous activity to finish.
|
2022-03-03 08:49:44 +03:00
|
|
|
*
|
|
|
|
* If usbnet_attach_ifp never ran, IFNET_LOCK won't work, but
|
|
|
|
* no activity is possible, so just skip this part.
|
2022-03-03 08:48:37 +03:00
|
|
|
*/
|
2022-03-03 08:49:44 +03:00
|
|
|
if (unp->unp_ifp_attached) {
|
|
|
|
IFNET_LOCK(ifp);
|
|
|
|
if (ifp->if_flags & IFF_RUNNING) {
|
|
|
|
usbnet_if_stop(ifp, 1);
|
|
|
|
}
|
|
|
|
IFNET_UNLOCK(ifp);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
2022-03-03 08:48:45 +03:00
|
|
|
/*
|
|
|
|
* The callout and tick task can't be scheduled anew at this
|
|
|
|
* point, and usbnet_if_stop has waited for them to complete.
|
|
|
|
*/
|
|
|
|
KASSERT(!callout_pending(&unp->unp_stat_ch));
|
|
|
|
KASSERT(!usb_task_pending(un->un_udev, &unp->unp_ticktask));
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
if (mii) {
|
|
|
|
mii_detach(mii, MII_PHY_ANY, MII_OFFSET_ANY);
|
2020-02-04 08:44:14 +03:00
|
|
|
ifmedia_fini(&mii->mii_media);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
2022-03-03 08:47:14 +03:00
|
|
|
if (unp->unp_ifp_attached) {
|
2019-08-06 03:19:57 +03:00
|
|
|
if (!usbnet_empty_eaddr(un))
|
|
|
|
ether_ifdetach(ifp);
|
|
|
|
else
|
|
|
|
bpf_detach(ifp);
|
2019-07-31 12:13:16 +03:00
|
|
|
if_detach(ifp);
|
|
|
|
}
|
2019-12-03 08:01:45 +03:00
|
|
|
usbnet_ec(un)->ec_mii = NULL;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:48:52 +03:00
|
|
|
usbnet_rx_list_free(un);
|
|
|
|
usbnet_tx_list_free(un);
|
|
|
|
|
|
|
|
rnd_detach_source(&unp->unp_rndsrc);
|
|
|
|
|
2022-03-03 08:53:23 +03:00
|
|
|
mutex_destroy(&unp->unp_mcastlock);
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_destroy(&unp->unp_core_lock);
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_destroy(&unp->unp_rxlock);
|
|
|
|
mutex_destroy(&unp->unp_txlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:49:00 +03:00
|
|
|
callout_destroy(&unp->unp_stat_ch);
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
pmf_device_deregister(un->un_dev);
|
|
|
|
|
2022-03-03 08:49:07 +03:00
|
|
|
/*
|
|
|
|
* Notify userland that we're going away, if we arrived in the
|
|
|
|
* first place.
|
|
|
|
*/
|
|
|
|
if (unp->unp_ifp_attached) {
|
|
|
|
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, un->un_udev,
|
|
|
|
un->un_dev);
|
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
kmem_free(unp, sizeof(*unp));
|
2019-09-08 21:59:32 +03:00
|
|
|
un->un_pri = NULL;
|
2019-08-10 05:17:36 +03:00
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
usbnet_activate(device_t self, devact_t act)
|
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = device_private(self);
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
|
|
|
|
|
|
|
switch (act) {
|
|
|
|
case DVACT_DEACTIVATE:
|
|
|
|
if_deactivate(ifp);
|
|
|
|
|
2022-03-03 08:50:39 +03:00
|
|
|
atomic_store_relaxed(&unp->unp_dying, true);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_rxlock);
|
|
|
|
mutex_enter(&unp->unp_txlock);
|
|
|
|
unp->unp_stopping = true;
|
|
|
|
mutex_exit(&unp->unp_txlock);
|
|
|
|
mutex_exit(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
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:
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
return 0;
|
2019-07-31 12:13:16 +03:00
|
|
|
case MODULE_CMD_FINI:
|
|
|
|
return 0;
|
|
|
|
case MODULE_CMD_STAT:
|
|
|
|
case MODULE_CMD_AUTOUNLOAD:
|
|
|
|
default:
|
|
|
|
return ENOTTY;
|
|
|
|
}
|
|
|
|
}
|