xref: /netbsd-src/sys/dev/usb/usbnet.c (revision 938459337bdffd017e9459447cc909fdaada30d2)
1*93845933Smlelstv /*	$NetBSD: usbnet.c,v 1.121 2024/11/10 11:53:04 mlelstv Exp $	*/
2773ec77dSmrg 
3773ec77dSmrg /*
4773ec77dSmrg  * Copyright (c) 2019 Matthew R. Green
5773ec77dSmrg  * All rights reserved.
6773ec77dSmrg  *
7773ec77dSmrg  * Redistribution and use in source and binary forms, with or without
8773ec77dSmrg  * modification, are permitted provided that the following conditions
9773ec77dSmrg  * are met:
10773ec77dSmrg  * 1. Redistributions of source code must retain the above copyright
11773ec77dSmrg  *    notice, this list of conditions and the following disclaimer.
12773ec77dSmrg  * 2. Redistributions in binary form must reproduce the above copyright
13773ec77dSmrg  *    notice, this list of conditions and the following disclaimer in the
14773ec77dSmrg  *    documentation and/or other materials provided with the distribution.
15773ec77dSmrg  *
16773ec77dSmrg  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17773ec77dSmrg  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18773ec77dSmrg  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19773ec77dSmrg  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20773ec77dSmrg  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21773ec77dSmrg  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22773ec77dSmrg  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23773ec77dSmrg  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24773ec77dSmrg  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25773ec77dSmrg  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26773ec77dSmrg  * SUCH DAMAGE.
27773ec77dSmrg  */
28773ec77dSmrg 
29773ec77dSmrg /*
30c9e9d49aSmrg  * Common code shared between USB network drivers.
31773ec77dSmrg  */
32773ec77dSmrg 
33773ec77dSmrg #include <sys/cdefs.h>
34*93845933Smlelstv __KERNEL_RCSID(0, "$NetBSD: usbnet.c,v 1.121 2024/11/10 11:53:04 mlelstv Exp $");
35773ec77dSmrg 
36773ec77dSmrg #include <sys/param.h>
37773ec77dSmrg #include <sys/kernel.h>
38773ec77dSmrg #include <sys/kmem.h>
39773ec77dSmrg #include <sys/module.h>
40543abf08Smrg #include <sys/atomic.h>
41773ec77dSmrg 
42773ec77dSmrg #include <dev/usb/usbnet.h>
43fa8dd7efSmrg #include <dev/usb/usbhist.h>
44773ec77dSmrg 
45dbbc1953Smrg struct usbnet_cdata {
46dbbc1953Smrg 	struct usbnet_chain	*uncd_tx_chain;
47dbbc1953Smrg 	struct usbnet_chain	*uncd_rx_chain;
48dbbc1953Smrg 
49dbbc1953Smrg 	int			uncd_tx_prod;
50dbbc1953Smrg 	int			uncd_tx_cnt;
51dbbc1953Smrg };
52dbbc1953Smrg 
53dbbc1953Smrg struct usbnet_private {
54dbbc1953Smrg 	/*
55edcedbe9Sriastradh 	 * - unp_miilock protects the MII / media data and tick scheduling.
56dbbc1953Smrg 	 * - unp_rxlock protects the rx path and its data
57dbbc1953Smrg 	 * - unp_txlock protects the tx path and its data
58bcd7a20dSmrg 	 *
59bcd7a20dSmrg 	 * the lock ordering is:
60edcedbe9Sriastradh 	 *	ifnet lock -> unp_miilock
618cff504dSriastradh 	 *		   -> unp_rxlock
628c65f880Sriastradh 	 *		   -> unp_txlock
63f59d8c97Sriastradh 	 *		   -> unp_mcastlock
64dbbc1953Smrg 	 */
65edcedbe9Sriastradh 	kmutex_t		unp_miilock;
66dbbc1953Smrg 	kmutex_t		unp_rxlock;
67dbbc1953Smrg 	kmutex_t		unp_txlock;
68dbbc1953Smrg 
69f59d8c97Sriastradh 	kmutex_t		unp_mcastlock;
70f59d8c97Sriastradh 	bool			unp_mcastactive;
71f59d8c97Sriastradh 
72dbbc1953Smrg 	struct usbnet_cdata	unp_cdata;
73dbbc1953Smrg 
74dbbc1953Smrg 	struct ethercom		unp_ec;
75dbbc1953Smrg 	struct mii_data		unp_mii;
76dbbc1953Smrg 	struct usb_task		unp_ticktask;
77dbbc1953Smrg 	struct callout		unp_stat_ch;
78dbbc1953Smrg 	struct usbd_pipe	*unp_ep[USBNET_ENDPT_MAX];
79dbbc1953Smrg 
804c7e9da1Sriastradh 	volatile bool		unp_dying;
818c65f880Sriastradh 	bool			unp_stopped;
828c65f880Sriastradh 	bool			unp_rxstopped;
838c65f880Sriastradh 	bool			unp_txstopped;
84dbbc1953Smrg 	bool			unp_attached;
8505d2a7a3Sriastradh 	bool			unp_ifp_attached;
86dbbc1953Smrg 	bool			unp_link;
87dbbc1953Smrg 
88dbbc1953Smrg 	int			unp_timer;
8970b25bc9Smsaitoh 	unsigned short		unp_if_flags;
90543abf08Smrg 	unsigned		unp_number;
91dbbc1953Smrg 
92dbbc1953Smrg 	krndsource_t		unp_rndsrc;
93dbbc1953Smrg 
94dbbc1953Smrg 	struct timeval		unp_rx_notice;
95dbbc1953Smrg 	struct timeval		unp_tx_notice;
96dbbc1953Smrg 	struct timeval		unp_intr_notice;
97dbbc1953Smrg };
98dbbc1953Smrg 
99dbbc1953Smrg #define un_cdata(un)	(&(un)->un_pri->unp_cdata)
100dbbc1953Smrg 
101543abf08Smrg volatile unsigned usbnet_number;
102543abf08Smrg 
1035c872ef1Sriastradh static void usbnet_isowned_rx(struct usbnet *);
1045c872ef1Sriastradh static void usbnet_isowned_tx(struct usbnet *);
1055c872ef1Sriastradh 
106103e1634Sriastradh static inline void
107edcedbe9Sriastradh usbnet_isowned_mii(struct usbnet *un)
1087714090bSriastradh {
109edcedbe9Sriastradh 	KASSERT(mutex_owned(&un->un_pri->unp_miilock));
1107714090bSriastradh }
1117714090bSriastradh 
112773ec77dSmrg static int usbnet_modcmd(modcmd_t, void *);
113773ec77dSmrg 
114699d292aSmrg #ifdef USB_DEBUG
115699d292aSmrg #ifndef USBNET_DEBUG
116699d292aSmrg #define usbnetdebug 0
117699d292aSmrg #else
1180801a2c6Smrg static int usbnetdebug = 0;
119fa8dd7efSmrg 
120699d292aSmrg SYSCTL_SETUP(sysctl_hw_usbnet_setup, "sysctl hw.usbnet setup")
121699d292aSmrg {
122699d292aSmrg 	int err;
123699d292aSmrg 	const struct sysctlnode *rnode;
124699d292aSmrg 	const struct sysctlnode *cnode;
125699d292aSmrg 
126699d292aSmrg 	err = sysctl_createv(clog, 0, NULL, &rnode,
127699d292aSmrg 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "usbnet",
128699d292aSmrg 	    SYSCTL_DESCR("usbnet global controls"),
129699d292aSmrg 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
130699d292aSmrg 
131699d292aSmrg 	if (err)
132699d292aSmrg 		goto fail;
133699d292aSmrg 
134699d292aSmrg 	/* control debugging printfs */
135699d292aSmrg 	err = sysctl_createv(clog, 0, &rnode, &cnode,
136699d292aSmrg 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
137699d292aSmrg 	    "debug", SYSCTL_DESCR("Enable debugging output"),
138699d292aSmrg 	    NULL, 0, &usbnetdebug, sizeof(usbnetdebug), CTL_CREATE, CTL_EOL);
139699d292aSmrg 	if (err)
140699d292aSmrg 		goto fail;
141699d292aSmrg 
142699d292aSmrg 	return;
143699d292aSmrg fail:
144699d292aSmrg 	aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err);
145699d292aSmrg }
146699d292aSmrg 
147699d292aSmrg #endif /* USBNET_DEBUG */
148699d292aSmrg #endif /* USB_DEBUG */
149699d292aSmrg 
150699d292aSmrg #define DPRINTF(FMT,A,B,C,D)	USBHIST_LOGN(usbnetdebug,1,FMT,A,B,C,D)
151699d292aSmrg #define DPRINTFN(N,FMT,A,B,C,D)	USBHIST_LOGN(usbnetdebug,N,FMT,A,B,C,D)
152699d292aSmrg #define USBNETHIST_FUNC()	USBHIST_FUNC()
153699d292aSmrg #define USBNETHIST_CALLED(name)	USBHIST_CALLED(usbnetdebug)
1549e463835Smrg #define USBNETHIST_CALLARGS(FMT,A,B,C,D) \
1559e463835Smrg 				USBHIST_CALLARGS(usbnetdebug,FMT,A,B,C,D)
156543abf08Smrg #define USBNETHIST_CALLARGSN(N,FMT,A,B,C,D) \
157543abf08Smrg 				USBHIST_CALLARGSN(usbnetdebug,N,FMT,A,B,C,D)
158773ec77dSmrg 
159d066f229Smrg /* Callback vectors. */
160d066f229Smrg 
161d066f229Smrg static void
162d066f229Smrg uno_stop(struct usbnet *un, struct ifnet *ifp, int disable)
163d066f229Smrg {
164552e8ffbSriastradh 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
165d066f229Smrg 	if (un->un_ops->uno_stop)
166d066f229Smrg 		(*un->un_ops->uno_stop)(ifp, disable);
167d066f229Smrg }
168d066f229Smrg 
169d066f229Smrg static int
170d066f229Smrg uno_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
171d066f229Smrg {
172d8ab8603Sriastradh 
173d9c770e7Sriastradh 	KASSERTMSG(cmd != SIOCADDMULTI, "%s", ifp->if_xname);
174d9c770e7Sriastradh 	KASSERTMSG(cmd != SIOCDELMULTI, "%s", ifp->if_xname);
175d8ab8603Sriastradh 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
176d8ab8603Sriastradh 
177d066f229Smrg 	if (un->un_ops->uno_ioctl)
178d066f229Smrg 		return (*un->un_ops->uno_ioctl)(ifp, cmd, data);
179d066f229Smrg 	return 0;
180d066f229Smrg }
181d066f229Smrg 
182d066f229Smrg static int
183d066f229Smrg uno_override_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
184d066f229Smrg {
185d8ab8603Sriastradh 
186d8ab8603Sriastradh 	switch (cmd) {
187d8ab8603Sriastradh 	case SIOCADDMULTI:
188d8ab8603Sriastradh 	case SIOCDELMULTI:
189d8ab8603Sriastradh 		break;
190d8ab8603Sriastradh 	default:
191d8ab8603Sriastradh 		KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
192d8ab8603Sriastradh 	}
193d8ab8603Sriastradh 
194d066f229Smrg 	return (*un->un_ops->uno_override_ioctl)(ifp, cmd, data);
195d066f229Smrg }
196d066f229Smrg 
197d066f229Smrg static int
198d066f229Smrg uno_init(struct usbnet *un, struct ifnet *ifp)
199d066f229Smrg {
200a2ab7acfSriastradh 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
20105a3622eSriastradh 	return un->un_ops->uno_init ? (*un->un_ops->uno_init)(ifp) : 0;
202d066f229Smrg }
203d066f229Smrg 
204d066f229Smrg static int
205d066f229Smrg uno_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val)
206d066f229Smrg {
207edcedbe9Sriastradh 	usbnet_isowned_mii(un);
208d066f229Smrg 	return (*un->un_ops->uno_read_reg)(un, phy, reg, val);
209d066f229Smrg }
210d066f229Smrg 
211d066f229Smrg static int
212d066f229Smrg uno_write_reg(struct usbnet *un, int phy, int reg, uint16_t val)
213d066f229Smrg {
214edcedbe9Sriastradh 	usbnet_isowned_mii(un);
215d066f229Smrg 	return (*un->un_ops->uno_write_reg)(un, phy, reg, val);
216d066f229Smrg }
217d066f229Smrg 
218d066f229Smrg static void
219d066f229Smrg uno_mii_statchg(struct usbnet *un, struct ifnet *ifp)
220d066f229Smrg {
221edcedbe9Sriastradh 	usbnet_isowned_mii(un);
222d066f229Smrg 	(*un->un_ops->uno_statchg)(ifp);
223d066f229Smrg }
224d066f229Smrg 
225d066f229Smrg static unsigned
226d066f229Smrg uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
227d066f229Smrg {
2287a9a30c5Sthorpej 	usbnet_isowned_tx(un);
229d066f229Smrg 	return (*un->un_ops->uno_tx_prepare)(un, m, c);
230d066f229Smrg }
231d066f229Smrg 
232d066f229Smrg static void
233f2c22b6bSmrg uno_rx_loop(struct usbnet *un, struct usbnet_chain *c, uint32_t total_len)
234d066f229Smrg {
2357a9a30c5Sthorpej 	usbnet_isowned_rx(un);
236f2c22b6bSmrg 	(*un->un_ops->uno_rx_loop)(un, c, total_len);
237f2c22b6bSmrg }
238f2c22b6bSmrg 
239f2c22b6bSmrg static void
240f2c22b6bSmrg uno_tick(struct usbnet *un)
241f2c22b6bSmrg {
242f2c22b6bSmrg 	if (un->un_ops->uno_tick)
243f2c22b6bSmrg 		(*un->un_ops->uno_tick)(un);
244d066f229Smrg }
245d066f229Smrg 
246d066f229Smrg static void
247d066f229Smrg uno_intr(struct usbnet *un, usbd_status status)
248d066f229Smrg {
249d066f229Smrg 	if (un->un_ops->uno_intr)
250d066f229Smrg 		(*un->un_ops->uno_intr)(un, status);
251d066f229Smrg }
252d066f229Smrg 
253773ec77dSmrg /* Interrupt handling. */
254773ec77dSmrg 
255773ec77dSmrg static struct mbuf *
256305fe6fdSmrg usbnet_newbuf(size_t buflen)
257773ec77dSmrg {
258773ec77dSmrg 	struct mbuf *m;
259773ec77dSmrg 
26085774ea6Sriastradh 	if (buflen > MCLBYTES - ETHER_ALIGN)
261d1cd546dSriastradh 		return NULL;
262d1cd546dSriastradh 
263773ec77dSmrg 	MGETHDR(m, M_DONTWAIT, MT_DATA);
264773ec77dSmrg 	if (m == NULL)
265773ec77dSmrg 		return NULL;
266773ec77dSmrg 
267305fe6fdSmrg 	if (buflen > MHLEN - ETHER_ALIGN) {
268773ec77dSmrg 		MCLGET(m, M_DONTWAIT);
269773ec77dSmrg 		if (!(m->m_flags & M_EXT)) {
270773ec77dSmrg 			m_freem(m);
271773ec77dSmrg 			return NULL;
272773ec77dSmrg 		}
273305fe6fdSmrg 	}
274773ec77dSmrg 
27585774ea6Sriastradh 	m->m_len = m->m_pkthdr.len = ETHER_ALIGN + buflen;
276773ec77dSmrg 	m_adj(m, ETHER_ALIGN);
277773ec77dSmrg 
278773ec77dSmrg 	return m;
279773ec77dSmrg }
280773ec77dSmrg 
281773ec77dSmrg /*
282773ec77dSmrg  * usbnet_rxeof() is designed to be the done callback for rx completion.
283773ec77dSmrg  * it provides generic setup and finalisation, calls a different usbnet
284773ec77dSmrg  * rx_loop callback in the middle, which can use usbnet_enqueue() to
28576b0c5e3Smrg  * enqueue a packet for higher levels (or usbnet_input() if previously
28676b0c5e3Smrg  * using if_input() path.)
287773ec77dSmrg  */
288773ec77dSmrg void
289773ec77dSmrg usbnet_enqueue(struct usbnet * const un, uint8_t *buf, size_t buflen,
29076b0c5e3Smrg 	       int csum_flags, uint32_t csum_data, int mbuf_flags)
291773ec77dSmrg {
292543abf08Smrg 	USBNETHIST_FUNC();
293dbbc1953Smrg 	struct ifnet * const ifp = usbnet_ifp(un);
294543abf08Smrg 	struct usbnet_private * const unp __unused = un->un_pri;
295773ec77dSmrg 	struct mbuf *m;
296773ec77dSmrg 
297d3dde16cSchristos 	USBNETHIST_CALLARGSN(5, "%jd: enter: len=%ju csf %#jx mbf %#jx",
298543abf08Smrg 	    unp->unp_number, buflen, csum_flags, mbuf_flags);
299543abf08Smrg 
300b28f9034Smrg 	usbnet_isowned_rx(un);
301773ec77dSmrg 
302305fe6fdSmrg 	m = usbnet_newbuf(buflen);
303773ec77dSmrg 	if (m == NULL) {
304d3dde16cSchristos 		DPRINTF("%jd: no memory", unp->unp_number, 0, 0, 0);
305cdaa0e91Sthorpej 		if_statinc(ifp, if_ierrors);
306773ec77dSmrg 		return;
307773ec77dSmrg 	}
308*93845933Smlelstv 	MCLAIM(m, &unp->unp_ec.ec_rx_mowner);
309773ec77dSmrg 
310773ec77dSmrg 	m_set_rcvif(m, ifp);
31176b0c5e3Smrg 	m->m_pkthdr.csum_flags = csum_flags;
31276b0c5e3Smrg 	m->m_pkthdr.csum_data = csum_data;
31376b0c5e3Smrg 	m->m_flags |= mbuf_flags;
314305fe6fdSmrg 	memcpy(mtod(m, uint8_t *), buf, buflen);
315773ec77dSmrg 
316773ec77dSmrg 	/* push the packet up */
317773ec77dSmrg 	if_percpuq_enqueue(ifp->if_percpuq, m);
318773ec77dSmrg }
319773ec77dSmrg 
32076b0c5e3Smrg void
32176b0c5e3Smrg usbnet_input(struct usbnet * const un, uint8_t *buf, size_t buflen)
32276b0c5e3Smrg {
323543abf08Smrg 	USBNETHIST_FUNC();
32476b0c5e3Smrg 	struct ifnet * const ifp = usbnet_ifp(un);
325543abf08Smrg 	struct usbnet_private * const unp __unused = un->un_pri;
32676b0c5e3Smrg 	struct mbuf *m;
32776b0c5e3Smrg 
328d3dde16cSchristos 	USBNETHIST_CALLARGSN(5, "%jd: enter: buf %#jx len %ju",
329543abf08Smrg 	    unp->unp_number, (uintptr_t)buf, buflen, 0);
330543abf08Smrg 
331b28f9034Smrg 	usbnet_isowned_rx(un);
33276b0c5e3Smrg 
333305fe6fdSmrg 	m = usbnet_newbuf(buflen);
33476b0c5e3Smrg 	if (m == NULL) {
335cdaa0e91Sthorpej 		if_statinc(ifp, if_ierrors);
33676b0c5e3Smrg 		return;
33776b0c5e3Smrg 	}
338*93845933Smlelstv 	MCLAIM(m, &unp->unp_ec.ec_rx_mowner);
33976b0c5e3Smrg 
34076b0c5e3Smrg 	m_set_rcvif(m, ifp);
34176b0c5e3Smrg 	memcpy(mtod(m, char *), buf, buflen);
34276b0c5e3Smrg 
34376b0c5e3Smrg 	/* push the packet up */
34476b0c5e3Smrg 	if_input(ifp, m);
34576b0c5e3Smrg }
34676b0c5e3Smrg 
347773ec77dSmrg /*
348773ec77dSmrg  * A frame has been uploaded: pass the resulting mbuf chain up to
349773ec77dSmrg  * the higher level protocols.
350773ec77dSmrg  */
351773ec77dSmrg static void
352773ec77dSmrg usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
353773ec77dSmrg {
354543abf08Smrg 	USBNETHIST_FUNC();
355dbbc1953Smrg 	struct usbnet_chain * const c = priv;
356773ec77dSmrg 	struct usbnet * const un = c->unc_un;
357dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
358773ec77dSmrg 	uint32_t total_len;
359773ec77dSmrg 
360d3dde16cSchristos 	USBNETHIST_CALLARGSN(5, "%jd: enter: status %#jx xfer %#jx",
361543abf08Smrg 	    unp->unp_number, status, (uintptr_t)xfer, 0);
362543abf08Smrg 
363dbbc1953Smrg 	mutex_enter(&unp->unp_rxlock);
364773ec77dSmrg 
3658c65f880Sriastradh 	if (usbnet_isdying(un) || unp->unp_rxstopped ||
366773ec77dSmrg 	    status == USBD_INVAL || status == USBD_NOT_STARTED ||
367802e09eeSriastradh 	    status == USBD_CANCELLED)
368773ec77dSmrg 		goto out;
369773ec77dSmrg 
370773ec77dSmrg 	if (status != USBD_NORMAL_COMPLETION) {
371dbbc1953Smrg 		if (usbd_ratecheck(&unp->unp_rx_notice))
37268918b85Sjakllsch 			device_printf(un->un_dev, "usb errors on rx: %s\n",
373773ec77dSmrg 			    usbd_errstr(status));
374773ec77dSmrg 		if (status == USBD_STALLED)
375dbbc1953Smrg 			usbd_clear_endpoint_stall_async(unp->unp_ep[USBNET_ENDPT_RX]);
376773ec77dSmrg 		goto done;
377773ec77dSmrg 	}
378773ec77dSmrg 
379773ec77dSmrg 	usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
380773ec77dSmrg 
381dbbc1953Smrg 	if (total_len > un->un_rx_bufsz) {
3824ccc274eSmlelstv 		device_printf(un->un_dev,
383773ec77dSmrg 		    "rxeof: too large transfer (%u > %u)\n",
384dbbc1953Smrg 		    total_len, un->un_rx_bufsz);
385773ec77dSmrg 		goto done;
386773ec77dSmrg 	}
387773ec77dSmrg 
388f2c22b6bSmrg 	uno_rx_loop(un, c, total_len);
389b28f9034Smrg 	usbnet_isowned_rx(un);
390773ec77dSmrg 
391773ec77dSmrg done:
3928c65f880Sriastradh 	if (usbnet_isdying(un) || unp->unp_rxstopped)
393773ec77dSmrg 		goto out;
394773ec77dSmrg 
395dbbc1953Smrg 	mutex_exit(&unp->unp_rxlock);
396773ec77dSmrg 
397773ec77dSmrg 	/* Setup new transfer. */
398dbbc1953Smrg 	usbd_setup_xfer(xfer, c, c->unc_buf, un->un_rx_bufsz,
399dbbc1953Smrg 	    un->un_rx_xfer_flags, USBD_NO_TIMEOUT, usbnet_rxeof);
400773ec77dSmrg 	usbd_transfer(xfer);
401773ec77dSmrg 	return;
402773ec77dSmrg 
403773ec77dSmrg out:
404dbbc1953Smrg 	mutex_exit(&unp->unp_rxlock);
405773ec77dSmrg }
406773ec77dSmrg 
407773ec77dSmrg static void
408773ec77dSmrg usbnet_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
409773ec77dSmrg {
410fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
411dbbc1953Smrg 	struct usbnet_chain * const c = priv;
412773ec77dSmrg 	struct usbnet * const un = c->unc_un;
413dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
414dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
415773ec77dSmrg 	struct ifnet * const ifp = usbnet_ifp(un);
416773ec77dSmrg 
417d3dde16cSchristos 	USBNETHIST_CALLARGSN(5, "%jd: enter: status %#jx xfer %#jx",
418543abf08Smrg 	    unp->unp_number, status, (uintptr_t)xfer, 0);
419543abf08Smrg 
420dbbc1953Smrg 	mutex_enter(&unp->unp_txlock);
4218c65f880Sriastradh 	if (unp->unp_txstopped || usbnet_isdying(un)) {
422dbbc1953Smrg 		mutex_exit(&unp->unp_txlock);
423773ec77dSmrg 		return;
424773ec77dSmrg 	}
425773ec77dSmrg 
426773ec77dSmrg 	KASSERT(cd->uncd_tx_cnt > 0);
427773ec77dSmrg 	cd->uncd_tx_cnt--;
428773ec77dSmrg 
429dbbc1953Smrg 	unp->unp_timer = 0;
430773ec77dSmrg 
431773ec77dSmrg 	switch (status) {
432773ec77dSmrg 	case USBD_NOT_STARTED:
433773ec77dSmrg 	case USBD_CANCELLED:
434773ec77dSmrg 		break;
435773ec77dSmrg 
436773ec77dSmrg 	case USBD_NORMAL_COMPLETION:
437cdaa0e91Sthorpej 		if_statinc(ifp, if_opackets);
438773ec77dSmrg 		break;
439773ec77dSmrg 
440773ec77dSmrg 	default:
441773ec77dSmrg 
442cdaa0e91Sthorpej 		if_statinc(ifp, if_oerrors);
443dbbc1953Smrg 		if (usbd_ratecheck(&unp->unp_tx_notice))
44468918b85Sjakllsch 			device_printf(un->un_dev, "usb error on tx: %s\n",
445773ec77dSmrg 			    usbd_errstr(status));
446773ec77dSmrg 		if (status == USBD_STALLED)
447dbbc1953Smrg 			usbd_clear_endpoint_stall_async(unp->unp_ep[USBNET_ENDPT_TX]);
448773ec77dSmrg 		break;
449773ec77dSmrg 	}
450773ec77dSmrg 
451dbbc1953Smrg 	mutex_exit(&unp->unp_txlock);
452773ec77dSmrg 
453773ec77dSmrg 	if (status == USBD_NORMAL_COMPLETION && !IFQ_IS_EMPTY(&ifp->if_snd))
454773ec77dSmrg 		(*ifp->if_start)(ifp);
455773ec77dSmrg }
456773ec77dSmrg 
457773ec77dSmrg static void
458dbbc1953Smrg usbnet_pipe_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
459fa8dd7efSmrg {
4600801a2c6Smrg 	USBNETHIST_FUNC();
461dbbc1953Smrg 	struct usbnet * const un = priv;
462dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
463719db767Sriastradh 	struct usbnet_intr * const uni __unused = un->un_intr;
464fa8dd7efSmrg 
465bc98c509Sriastradh 	if (usbnet_isdying(un) ||
466fa8dd7efSmrg 	    status == USBD_INVAL || status == USBD_NOT_STARTED ||
46786170d4cSriastradh 	    status == USBD_CANCELLED) {
468bc98c509Sriastradh 		USBNETHIST_CALLARGS("%jd: uni %#jx dying %#jx status %#jx",
4690801a2c6Smrg 		    unp->unp_number, (uintptr_t)uni,
470bc98c509Sriastradh 		    usbnet_isdying(un), status);
471fa8dd7efSmrg 		return;
4720801a2c6Smrg 	}
473fa8dd7efSmrg 
474fa8dd7efSmrg 	if (status != USBD_NORMAL_COMPLETION) {
475dbbc1953Smrg 		if (usbd_ratecheck(&unp->unp_intr_notice)) {
4764ccc274eSmlelstv 			device_printf(un->un_dev, "usb error on intr: %s\n",
477fa8dd7efSmrg 			    usbd_errstr(status));
478fa8dd7efSmrg 		}
479fa8dd7efSmrg 		if (status == USBD_STALLED)
480dbbc1953Smrg 			usbd_clear_endpoint_stall_async(unp->unp_ep[USBNET_ENDPT_INTR]);
481d3dde16cSchristos 		USBNETHIST_CALLARGS("%jd: not normal status %#jx",
4820801a2c6Smrg 		    unp->unp_number, status, 0, 0);
483fa8dd7efSmrg 		return;
484fa8dd7efSmrg 	}
485fa8dd7efSmrg 
486d066f229Smrg 	uno_intr(un, status);
487fa8dd7efSmrg }
488fa8dd7efSmrg 
489fa8dd7efSmrg static void
490773ec77dSmrg usbnet_start_locked(struct ifnet *ifp)
491773ec77dSmrg {
4920801a2c6Smrg 	USBNETHIST_FUNC();
493773ec77dSmrg 	struct usbnet * const un = ifp->if_softc;
494dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
495dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
496773ec77dSmrg 	struct mbuf *m;
497773ec77dSmrg 	unsigned length;
4980801a2c6Smrg 	bool done_transmit = false;
49994f3f4a3Srin 	int idx, count;
500773ec77dSmrg 
501d3dde16cSchristos 	USBNETHIST_CALLARGS("%jd: tx_cnt %jd list_cnt %jd link %jd",
5020801a2c6Smrg 	    unp->unp_number, cd->uncd_tx_cnt, un->un_tx_list_cnt,
5030801a2c6Smrg 	    unp->unp_link);
5040801a2c6Smrg 
505b28f9034Smrg 	usbnet_isowned_tx(un);
506dbbc1953Smrg 	KASSERT(cd->uncd_tx_cnt <= un->un_tx_list_cnt);
5077e70ab3fSriastradh 	KASSERT(!unp->unp_txstopped);
508773ec77dSmrg 
5097e70ab3fSriastradh 	if (!unp->unp_link) {
5107e70ab3fSriastradh 		DPRINTF("start called no link (%jx)",
5117e70ab3fSriastradh 		    unp->unp_link, 0, 0, 0);
512773ec77dSmrg 		return;
513f2c22b6bSmrg 	}
514773ec77dSmrg 
515543abf08Smrg 	if (cd->uncd_tx_cnt == un->un_tx_list_cnt) {
516d3dde16cSchristos 		DPRINTF("start called, tx busy (%#jx == %#jx)",
517543abf08Smrg 		    cd->uncd_tx_cnt, un->un_tx_list_cnt, 0, 0);
518543abf08Smrg 		return;
519543abf08Smrg 	}
520543abf08Smrg 
521773ec77dSmrg 	idx = cd->uncd_tx_prod;
52294f3f4a3Srin 	count = 0;
523dbbc1953Smrg 	while (cd->uncd_tx_cnt < un->un_tx_list_cnt) {
524773ec77dSmrg 		IFQ_POLL(&ifp->if_snd, m);
525543abf08Smrg 		if (m == NULL) {
526543abf08Smrg 			DPRINTF("start called, queue empty", 0, 0, 0, 0);
527773ec77dSmrg 			break;
528543abf08Smrg 		}
529c9e9d49aSmrg 		KASSERT(m->m_pkthdr.len <= un->un_tx_bufsz);
530773ec77dSmrg 
531d066f229Smrg 		struct usbnet_chain *c = &cd->uncd_tx_chain[idx];
532773ec77dSmrg 
533d066f229Smrg 		length = uno_tx_prepare(un, m, c);
534773ec77dSmrg 		if (length == 0) {
535543abf08Smrg 			DPRINTF("uno_tx_prepare gave zero length", 0, 0, 0, 0);
536cdaa0e91Sthorpej 			if_statinc(ifp, if_oerrors);
537773ec77dSmrg 			break;
538773ec77dSmrg 		}
539773ec77dSmrg 
540773ec77dSmrg 		if (__predict_false(c->unc_xfer == NULL)) {
541543abf08Smrg 			DPRINTF("unc_xfer is NULL", 0, 0, 0, 0);
542cdaa0e91Sthorpej 			if_statinc(ifp, if_oerrors);
543773ec77dSmrg 			break;
544773ec77dSmrg 		}
545773ec77dSmrg 
546773ec77dSmrg 		usbd_setup_xfer(c->unc_xfer, c, c->unc_buf, length,
547dbbc1953Smrg 		    un->un_tx_xfer_flags, 10000, usbnet_txeof);
548773ec77dSmrg 
549773ec77dSmrg 		/* Transmit */
550773ec77dSmrg 		usbd_status err = usbd_transfer(c->unc_xfer);
551773ec77dSmrg 		if (err != USBD_IN_PROGRESS) {
55213fbb093Schristos 			DPRINTF("usbd_transfer on %#jx for %ju bytes: %jd",
553543abf08Smrg 			    (uintptr_t)c->unc_buf, length, err, 0);
554cdaa0e91Sthorpej 			if_statinc(ifp, if_oerrors);
555773ec77dSmrg 			break;
556773ec77dSmrg 		}
5570801a2c6Smrg 		done_transmit = true;
558773ec77dSmrg 
559773ec77dSmrg 		IFQ_DEQUEUE(&ifp->if_snd, m);
560773ec77dSmrg 
561773ec77dSmrg 		/*
562773ec77dSmrg 		 * If there's a BPF listener, bounce a copy of this frame
563773ec77dSmrg 		 * to him.
564773ec77dSmrg 		 */
565773ec77dSmrg 		bpf_mtap(ifp, m, BPF_D_OUT);
566773ec77dSmrg 		m_freem(m);
567773ec77dSmrg 
568dbbc1953Smrg 		idx = (idx + 1) % un->un_tx_list_cnt;
569773ec77dSmrg 		cd->uncd_tx_cnt++;
57094f3f4a3Srin 		count++;
571773ec77dSmrg 	}
572773ec77dSmrg 	cd->uncd_tx_prod = idx;
573773ec77dSmrg 
57413fbb093Schristos 	DPRINTF("finished with start; tx_cnt %jd list_cnt %jd link %jd",
5750801a2c6Smrg 	    cd->uncd_tx_cnt, un->un_tx_list_cnt, unp->unp_link, 0);
5760801a2c6Smrg 
577773ec77dSmrg 	/*
578773ec77dSmrg 	 * Set a timeout in case the chip goes out to lunch.
579773ec77dSmrg 	 */
5800801a2c6Smrg 	if (done_transmit)
581dbbc1953Smrg 		unp->unp_timer = 5;
58294f3f4a3Srin 
58394f3f4a3Srin 	if (count != 0)
58494f3f4a3Srin 		rnd_add_uint32(&unp->unp_rndsrc, count);
585773ec77dSmrg }
586773ec77dSmrg 
587773ec77dSmrg static void
5887a9a30c5Sthorpej usbnet_if_start(struct ifnet *ifp)
589773ec77dSmrg {
590773ec77dSmrg 	struct usbnet * const un = ifp->if_softc;
591dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
592773ec77dSmrg 
5930801a2c6Smrg 	USBNETHIST_FUNC();
5948c65f880Sriastradh 	USBNETHIST_CALLARGS("%jd: txstopped %jd",
5958c65f880Sriastradh 	    unp->unp_number, unp->unp_txstopped, 0, 0);
5960801a2c6Smrg 
597dbbc1953Smrg 	mutex_enter(&unp->unp_txlock);
5988c65f880Sriastradh 	if (!unp->unp_txstopped)
599773ec77dSmrg 		usbnet_start_locked(ifp);
600dbbc1953Smrg 	mutex_exit(&unp->unp_txlock);
601773ec77dSmrg }
602773ec77dSmrg 
603773ec77dSmrg /*
604773ec77dSmrg  * Chain management.
605773ec77dSmrg  *
606773ec77dSmrg  * RX and TX are identical. Keep them that way.
607773ec77dSmrg  */
608773ec77dSmrg 
609773ec77dSmrg /* Start of common RX functions */
610773ec77dSmrg 
611773ec77dSmrg static size_t
612b28f9034Smrg usbnet_rx_list_size(struct usbnet_cdata * const cd, struct usbnet * const un)
613773ec77dSmrg {
614dbbc1953Smrg 	return sizeof(*cd->uncd_rx_chain) * un->un_rx_list_cnt;
615773ec77dSmrg }
616773ec77dSmrg 
617773ec77dSmrg static void
618b28f9034Smrg usbnet_rx_list_alloc(struct usbnet * const un)
619773ec77dSmrg {
620dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
621773ec77dSmrg 
622dbbc1953Smrg 	cd->uncd_rx_chain = kmem_zalloc(usbnet_rx_list_size(cd, un), KM_SLEEP);
623773ec77dSmrg }
624773ec77dSmrg 
625773ec77dSmrg static void
626b28f9034Smrg usbnet_rx_list_free(struct usbnet * const un)
627773ec77dSmrg {
628dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
629773ec77dSmrg 
630773ec77dSmrg 	if (cd->uncd_rx_chain) {
631dbbc1953Smrg 		kmem_free(cd->uncd_rx_chain, usbnet_rx_list_size(cd, un));
632773ec77dSmrg 		cd->uncd_rx_chain = NULL;
633773ec77dSmrg 	}
634773ec77dSmrg }
635773ec77dSmrg 
636773ec77dSmrg static int
637b28f9034Smrg usbnet_rx_list_init(struct usbnet * const un)
638773ec77dSmrg {
639dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
640dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
641773ec77dSmrg 
642dbbc1953Smrg 	for (size_t i = 0; i < un->un_rx_list_cnt; i++) {
643773ec77dSmrg 		struct usbnet_chain *c = &cd->uncd_rx_chain[i];
644773ec77dSmrg 
645773ec77dSmrg 		c->unc_un = un;
646773ec77dSmrg 		if (c->unc_xfer == NULL) {
647dbbc1953Smrg 			int err = usbd_create_xfer(unp->unp_ep[USBNET_ENDPT_RX],
648dbbc1953Smrg 			    un->un_rx_bufsz, un->un_rx_xfer_flags, 0,
649d066f229Smrg 			    &c->unc_xfer);
650773ec77dSmrg 			if (err)
651773ec77dSmrg 				return err;
652773ec77dSmrg 			c->unc_buf = usbd_get_buffer(c->unc_xfer);
653773ec77dSmrg 		}
654773ec77dSmrg 	}
655773ec77dSmrg 
656773ec77dSmrg 	return 0;
657773ec77dSmrg }
658773ec77dSmrg 
659773ec77dSmrg static void
660b28f9034Smrg usbnet_rx_list_fini(struct usbnet * const un)
661773ec77dSmrg {
662dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
663773ec77dSmrg 
664dbbc1953Smrg 	for (size_t i = 0; i < un->un_rx_list_cnt; i++) {
665773ec77dSmrg 		struct usbnet_chain *c = &cd->uncd_rx_chain[i];
666773ec77dSmrg 
667773ec77dSmrg 		if (c->unc_xfer != NULL) {
668773ec77dSmrg 			usbd_destroy_xfer(c->unc_xfer);
669773ec77dSmrg 			c->unc_xfer = NULL;
670773ec77dSmrg 			c->unc_buf = NULL;
671773ec77dSmrg 		}
672773ec77dSmrg 	}
673773ec77dSmrg }
674773ec77dSmrg 
675773ec77dSmrg /* End of common RX functions */
676773ec77dSmrg 
677773ec77dSmrg static void
678305fe6fdSmrg usbnet_rx_start_pipes(struct usbnet * const un)
679773ec77dSmrg {
680dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
681dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
682773ec77dSmrg 
683dbbc1953Smrg 	mutex_enter(&unp->unp_rxlock);
6848c65f880Sriastradh 	KASSERT(unp->unp_rxstopped);
6858c65f880Sriastradh 	unp->unp_rxstopped = false;
686773ec77dSmrg 
687dbbc1953Smrg 	for (size_t i = 0; i < un->un_rx_list_cnt; i++) {
688773ec77dSmrg 		struct usbnet_chain *c = &cd->uncd_rx_chain[i];
689773ec77dSmrg 
690dbbc1953Smrg 		usbd_setup_xfer(c->unc_xfer, c, c->unc_buf, un->un_rx_bufsz,
691305fe6fdSmrg 		    un->un_rx_xfer_flags, USBD_NO_TIMEOUT, usbnet_rxeof);
692773ec77dSmrg 		usbd_transfer(c->unc_xfer);
693773ec77dSmrg 	}
694773ec77dSmrg 
695dbbc1953Smrg 	mutex_exit(&unp->unp_rxlock);
696773ec77dSmrg }
697773ec77dSmrg 
698773ec77dSmrg /* Start of common TX functions */
699773ec77dSmrg 
700773ec77dSmrg static size_t
701b28f9034Smrg usbnet_tx_list_size(struct usbnet_cdata * const cd, struct usbnet * const un)
702773ec77dSmrg {
703dbbc1953Smrg 	return sizeof(*cd->uncd_tx_chain) * un->un_tx_list_cnt;
704773ec77dSmrg }
705773ec77dSmrg 
706773ec77dSmrg static void
707b28f9034Smrg usbnet_tx_list_alloc(struct usbnet * const un)
708773ec77dSmrg {
709dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
710773ec77dSmrg 
711dbbc1953Smrg 	cd->uncd_tx_chain = kmem_zalloc(usbnet_tx_list_size(cd, un), KM_SLEEP);
712773ec77dSmrg }
713773ec77dSmrg 
714773ec77dSmrg static void
715b28f9034Smrg usbnet_tx_list_free(struct usbnet * const un)
716773ec77dSmrg {
717dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
718773ec77dSmrg 
719773ec77dSmrg 	if (cd->uncd_tx_chain) {
720dbbc1953Smrg 		kmem_free(cd->uncd_tx_chain, usbnet_tx_list_size(cd, un));
721773ec77dSmrg 		cd->uncd_tx_chain = NULL;
722773ec77dSmrg 	}
723773ec77dSmrg }
724773ec77dSmrg 
725773ec77dSmrg static int
726b28f9034Smrg usbnet_tx_list_init(struct usbnet * const un)
727773ec77dSmrg {
728dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
729dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
730773ec77dSmrg 
731dbbc1953Smrg 	for (size_t i = 0; i < un->un_tx_list_cnt; i++) {
732773ec77dSmrg 		struct usbnet_chain *c = &cd->uncd_tx_chain[i];
733773ec77dSmrg 
734773ec77dSmrg 		c->unc_un = un;
735773ec77dSmrg 		if (c->unc_xfer == NULL) {
736dbbc1953Smrg 			int err = usbd_create_xfer(unp->unp_ep[USBNET_ENDPT_TX],
737dbbc1953Smrg 			    un->un_tx_bufsz, un->un_tx_xfer_flags, 0,
738d066f229Smrg 			    &c->unc_xfer);
739773ec77dSmrg 			if (err)
740773ec77dSmrg 				return err;
741773ec77dSmrg 			c->unc_buf = usbd_get_buffer(c->unc_xfer);
742773ec77dSmrg 		}
743773ec77dSmrg 	}
744773ec77dSmrg 
745773ec77dSmrg 	return 0;
746773ec77dSmrg }
747773ec77dSmrg 
748773ec77dSmrg static void
749b28f9034Smrg usbnet_tx_list_fini(struct usbnet * const un)
750773ec77dSmrg {
751dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
752773ec77dSmrg 
753dbbc1953Smrg 	for (size_t i = 0; i < un->un_tx_list_cnt; i++) {
754773ec77dSmrg 		struct usbnet_chain *c = &cd->uncd_tx_chain[i];
755773ec77dSmrg 
756773ec77dSmrg 		if (c->unc_xfer != NULL) {
757773ec77dSmrg 			usbd_destroy_xfer(c->unc_xfer);
758773ec77dSmrg 			c->unc_xfer = NULL;
759773ec77dSmrg 			c->unc_buf = NULL;
760773ec77dSmrg 		}
761773ec77dSmrg 	}
762543abf08Smrg 	cd->uncd_tx_prod = cd->uncd_tx_cnt = 0;
763773ec77dSmrg }
764773ec77dSmrg 
765773ec77dSmrg /* End of common TX functions */
766773ec77dSmrg 
767773ec77dSmrg /* Endpoint pipe management. */
768773ec77dSmrg 
769773ec77dSmrg static void
770b28f9034Smrg usbnet_ep_close_pipes(struct usbnet * const un)
771773ec77dSmrg {
772dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
773dbbc1953Smrg 
774dbbc1953Smrg 	for (size_t i = 0; i < __arraycount(unp->unp_ep); i++) {
775dbbc1953Smrg 		if (unp->unp_ep[i] == NULL)
776773ec77dSmrg 			continue;
77740e1019eSriastradh 		usbd_close_pipe(unp->unp_ep[i]);
778dbbc1953Smrg 		unp->unp_ep[i] = NULL;
779773ec77dSmrg 	}
780773ec77dSmrg }
781773ec77dSmrg 
782773ec77dSmrg static usbd_status
783b28f9034Smrg usbnet_ep_open_pipes(struct usbnet * const un)
784773ec77dSmrg {
785dbbc1953Smrg 	struct usbnet_intr * const uni = un->un_intr;
786dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
787dbbc1953Smrg 
788dbbc1953Smrg 	for (size_t i = 0; i < __arraycount(unp->unp_ep); i++) {
789fa8dd7efSmrg 		usbd_status err;
790fa8dd7efSmrg 
791773ec77dSmrg 		if (un->un_ed[i] == 0)
792773ec77dSmrg 			continue;
793fa8dd7efSmrg 
794dbbc1953Smrg 		if (i == USBNET_ENDPT_INTR && uni) {
795fa8dd7efSmrg 			err = usbd_open_pipe_intr(un->un_iface, un->un_ed[i],
796dbbc1953Smrg 			    USBD_EXCLUSIVE_USE | USBD_MPSAFE, &unp->unp_ep[i], un,
797dbbc1953Smrg 			    uni->uni_buf, uni->uni_bufsz, usbnet_pipe_intr,
798dbbc1953Smrg 			    uni->uni_interval);
799fa8dd7efSmrg 		} else {
800fa8dd7efSmrg 			err = usbd_open_pipe(un->un_iface, un->un_ed[i],
801dbbc1953Smrg 			    USBD_EXCLUSIVE_USE | USBD_MPSAFE, &unp->unp_ep[i]);
802fa8dd7efSmrg 		}
803773ec77dSmrg 		if (err) {
804773ec77dSmrg 			usbnet_ep_close_pipes(un);
805773ec77dSmrg 			return err;
806773ec77dSmrg 		}
807773ec77dSmrg 	}
808773ec77dSmrg 
809773ec77dSmrg 	return USBD_NORMAL_COMPLETION;
810773ec77dSmrg }
811773ec77dSmrg 
812065aa2e9Sriastradh static void
813b28f9034Smrg usbnet_ep_stop_pipes(struct usbnet * const un)
814773ec77dSmrg {
815dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
816dbbc1953Smrg 
817dbbc1953Smrg 	for (size_t i = 0; i < __arraycount(unp->unp_ep); i++) {
818dbbc1953Smrg 		if (unp->unp_ep[i] == NULL)
819773ec77dSmrg 			continue;
820065aa2e9Sriastradh 		usbd_abort_pipe(unp->unp_ep[i]);
821773ec77dSmrg 	}
822773ec77dSmrg }
823773ec77dSmrg 
82493ec4d73Sriastradh static int
825d066f229Smrg usbnet_init_rx_tx(struct usbnet * const un)
826773ec77dSmrg {
827fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
828dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
829773ec77dSmrg 	struct ifnet * const ifp = usbnet_ifp(un);
830773ec77dSmrg 	usbd_status err;
831fa8dd7efSmrg 	int error = 0;
832fa8dd7efSmrg 
833552e8ffbSriastradh 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
834703c8e30Sriastradh 
8354c7e9da1Sriastradh 	if (usbnet_isdying(un)) {
836fa8dd7efSmrg 		return EIO;
837fa8dd7efSmrg 	}
8387a9a30c5Sthorpej 
839773ec77dSmrg 	/* Open RX and TX pipes. */
840773ec77dSmrg 	err = usbnet_ep_open_pipes(un);
841773ec77dSmrg 	if (err) {
842773ec77dSmrg 		aprint_error_dev(un->un_dev, "open rx/tx pipes failed: %s\n",
843773ec77dSmrg 		    usbd_errstr(err));
844fa8dd7efSmrg 		error = EIO;
845fa8dd7efSmrg 		goto out;
846773ec77dSmrg 	}
847773ec77dSmrg 
848773ec77dSmrg 	/* Init RX ring. */
849d066f229Smrg 	if (usbnet_rx_list_init(un)) {
850773ec77dSmrg 		aprint_error_dev(un->un_dev, "rx list init failed\n");
851fa8dd7efSmrg 		error = ENOBUFS;
852fa8dd7efSmrg 		goto out;
853773ec77dSmrg 	}
854773ec77dSmrg 
855773ec77dSmrg 	/* Init TX ring. */
856d066f229Smrg 	if (usbnet_tx_list_init(un)) {
857773ec77dSmrg 		aprint_error_dev(un->un_dev, "tx list init failed\n");
858fa8dd7efSmrg 		error = ENOBUFS;
859fa8dd7efSmrg 		goto out;
860773ec77dSmrg 	}
861773ec77dSmrg 
862773ec77dSmrg 	/* Indicate we are up and running. */
863552e8ffbSriastradh 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
864773ec77dSmrg 	ifp->if_flags |= IFF_RUNNING;
865773ec77dSmrg 
866f59d8c97Sriastradh 	/*
867f59d8c97Sriastradh 	 * If the hardware has a multicast filter, program it and then
868f59d8c97Sriastradh 	 * allow updates to it while we're running.
869f59d8c97Sriastradh 	 */
870f59d8c97Sriastradh 	if (un->un_ops->uno_mcast) {
871f59d8c97Sriastradh 		mutex_enter(&unp->unp_mcastlock);
8726586a9d4Sriastradh 		KASSERTMSG(!unp->unp_mcastactive, "%s", ifp->if_xname);
8736586a9d4Sriastradh 		unp->unp_if_flags = ifp->if_flags;
874f59d8c97Sriastradh 		(*un->un_ops->uno_mcast)(ifp);
875f59d8c97Sriastradh 		unp->unp_mcastactive = true;
876f59d8c97Sriastradh 		mutex_exit(&unp->unp_mcastlock);
877f59d8c97Sriastradh 	}
878f59d8c97Sriastradh 
8798c65f880Sriastradh 	/* Allow transmit.  */
8808c65f880Sriastradh 	mutex_enter(&unp->unp_txlock);
8818c65f880Sriastradh 	KASSERT(unp->unp_txstopped);
8828c65f880Sriastradh 	unp->unp_txstopped = false;
8838c65f880Sriastradh 	mutex_exit(&unp->unp_txlock);
8848c65f880Sriastradh 
88575757ff5Sriastradh 	/* Start up the receive pipe(s). */
88675757ff5Sriastradh 	usbnet_rx_start_pipes(un);
88775757ff5Sriastradh 
8888c65f880Sriastradh 	/* Kick off the watchdog/stats/mii tick.  */
889edcedbe9Sriastradh 	mutex_enter(&unp->unp_miilock);
8908c65f880Sriastradh 	unp->unp_stopped = false;
891dbbc1953Smrg 	callout_schedule(&unp->unp_stat_ch, hz);
892edcedbe9Sriastradh 	mutex_exit(&unp->unp_miilock);
893773ec77dSmrg 
894fa8dd7efSmrg out:
895fa8dd7efSmrg 	if (error) {
896773ec77dSmrg 		usbnet_rx_list_fini(un);
897773ec77dSmrg 		usbnet_tx_list_fini(un);
898773ec77dSmrg 		usbnet_ep_close_pipes(un);
899fa8dd7efSmrg 	}
900773ec77dSmrg 
9014169cb4dSriastradh 	/*
9024169cb4dSriastradh 	 * For devices without any media autodetection, treat success
9034169cb4dSriastradh 	 * here as an active link.
9044169cb4dSriastradh 	 */
9058cff504dSriastradh 	if (un->un_ops->uno_statchg == NULL) {
906edcedbe9Sriastradh 		mutex_enter(&unp->unp_miilock);
9074169cb4dSriastradh 		usbnet_set_link(un, error == 0);
908edcedbe9Sriastradh 		mutex_exit(&unp->unp_miilock);
9098cff504dSriastradh 	}
910fa8dd7efSmrg 
911fa8dd7efSmrg 	return error;
912773ec77dSmrg }
913773ec77dSmrg 
914773ec77dSmrg /* MII management. */
915773ec77dSmrg 
91647bf81dfSriastradh static int
917d066f229Smrg usbnet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
918773ec77dSmrg {
9199e463835Smrg 	USBNETHIST_FUNC();
920773ec77dSmrg 	struct usbnet * const un = device_private(dev);
92165dae968Smrg 	int err;
922773ec77dSmrg 
923edcedbe9Sriastradh 	/* MII layer ensures miilock is held. */
924edcedbe9Sriastradh 	usbnet_isowned_mii(un);
9257a9a30c5Sthorpej 
9264c7e9da1Sriastradh 	if (usbnet_isdying(un)) {
927773ec77dSmrg 		return EIO;
928773ec77dSmrg 	}
929773ec77dSmrg 
930d066f229Smrg 	err = uno_read_reg(un, phy, reg, val);
931773ec77dSmrg 	if (err) {
932d3dde16cSchristos 		USBNETHIST_CALLARGS("%jd: read PHY failed: %jd",
9334c7e9da1Sriastradh 		    un->un_pri->unp_number, err, 0, 0);
93465dae968Smrg 		return err;
935773ec77dSmrg 	}
936773ec77dSmrg 
937773ec77dSmrg 	return 0;
938773ec77dSmrg }
939773ec77dSmrg 
94047bf81dfSriastradh static int
941d066f229Smrg usbnet_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
942773ec77dSmrg {
9439e463835Smrg 	USBNETHIST_FUNC();
944773ec77dSmrg 	struct usbnet * const un = device_private(dev);
94565dae968Smrg 	int err;
946773ec77dSmrg 
947edcedbe9Sriastradh 	/* MII layer ensures miilock is held. */
948edcedbe9Sriastradh 	usbnet_isowned_mii(un);
9497a9a30c5Sthorpej 
9504c7e9da1Sriastradh 	if (usbnet_isdying(un)) {
951773ec77dSmrg 		return EIO;
952773ec77dSmrg 	}
953773ec77dSmrg 
954d066f229Smrg 	err = uno_write_reg(un, phy, reg, val);
955773ec77dSmrg 	if (err) {
956d3dde16cSchristos 		USBNETHIST_CALLARGS("%jd: write PHY failed: %jd",
9574c7e9da1Sriastradh 		    un->un_pri->unp_number, err, 0, 0);
95865dae968Smrg 		return err;
959773ec77dSmrg 	}
960773ec77dSmrg 
961773ec77dSmrg 	return 0;
962773ec77dSmrg }
963773ec77dSmrg 
96447bf81dfSriastradh static void
965d066f229Smrg usbnet_mii_statchg(struct ifnet *ifp)
966773ec77dSmrg {
967fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
968773ec77dSmrg 	struct usbnet * const un = ifp->if_softc;
969773ec77dSmrg 
970edcedbe9Sriastradh 	/* MII layer ensures miilock is held. */
971edcedbe9Sriastradh 	usbnet_isowned_mii(un);
9727a9a30c5Sthorpej 
973d066f229Smrg 	uno_mii_statchg(un, ifp);
974773ec77dSmrg }
975773ec77dSmrg 
976773ec77dSmrg static int
977773ec77dSmrg usbnet_media_upd(struct ifnet *ifp)
978773ec77dSmrg {
979fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
980773ec77dSmrg 	struct usbnet * const un = ifp->if_softc;
981dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
982773ec77dSmrg 	struct mii_data * const mii = usbnet_mii(un);
983773ec77dSmrg 
984edcedbe9Sriastradh 	/* ifmedia layer ensures miilock is held. */
985edcedbe9Sriastradh 	usbnet_isowned_mii(un);
9867a9a30c5Sthorpej 
98736ee3f20Sriastradh 	/* ifmedia changes only with IFNET_LOCK held.  */
98836ee3f20Sriastradh 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
98936ee3f20Sriastradh 
9904c7e9da1Sriastradh 	if (usbnet_isdying(un))
991773ec77dSmrg 		return EIO;
992773ec77dSmrg 
993dbbc1953Smrg 	unp->unp_link = false;
994773ec77dSmrg 
995773ec77dSmrg 	if (mii->mii_instance) {
996773ec77dSmrg 		struct mii_softc *miisc;
997773ec77dSmrg 
998773ec77dSmrg 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
999773ec77dSmrg 			mii_phy_reset(miisc);
1000773ec77dSmrg 	}
1001773ec77dSmrg 
1002773ec77dSmrg 	return ether_mediachange(ifp);
1003773ec77dSmrg }
1004773ec77dSmrg 
1005773ec77dSmrg /* ioctl */
1006773ec77dSmrg 
10076586a9d4Sriastradh /*
10086586a9d4Sriastradh  * usbnet_ifflags_cb(ec)
10096586a9d4Sriastradh  *
10106586a9d4Sriastradh  *	Called by if_ethersubr when interface flags change
10116586a9d4Sriastradh  *	(SIOCSIFFLAGS), or ethernet capabilities change
10126586a9d4Sriastradh  *	(SIOCSETHERCAP), on a running interface.
10136586a9d4Sriastradh  */
1014773ec77dSmrg static int
1015773ec77dSmrg usbnet_ifflags_cb(struct ethercom *ec)
1016773ec77dSmrg {
1017fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
1018773ec77dSmrg 	struct ifnet *ifp = &ec->ec_if;
1019773ec77dSmrg 	struct usbnet *un = ifp->if_softc;
1020dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
1021773ec77dSmrg 
102257635420Sriastradh 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
102357635420Sriastradh 
102470b25bc9Smsaitoh 	const u_short changed = ifp->if_flags ^ unp->unp_if_flags;
10254d2a5263Sriastradh 
10264d2a5263Sriastradh 	/*
10274d2a5263Sriastradh 	 * If any user-settable flags have changed other than
10284d2a5263Sriastradh 	 * IFF_DEBUG, just reset the interface.
10294d2a5263Sriastradh 	 */
10304d2a5263Sriastradh 	if ((changed & ~(IFF_CANTCHANGE | IFF_DEBUG)) != 0)
10314d2a5263Sriastradh 		return ENETRESET;
10324d2a5263Sriastradh 
10334d2a5263Sriastradh 	/*
10344d2a5263Sriastradh 	 * Otherwise, cache the flags change so we can read the flags
10354a682b0fSriastradh 	 * under unp_mcastlock for multicast updates in SIOCADDMULTI or
10364d2a5263Sriastradh 	 * SIOCDELMULTI without IFNET_LOCK.
10374d2a5263Sriastradh 	 */
1038a06a0449Sriastradh 	mutex_enter(&unp->unp_mcastlock);
1039dbbc1953Smrg 	unp->unp_if_flags = ifp->if_flags;
1040a06a0449Sriastradh 	mutex_exit(&unp->unp_mcastlock);
10414d2a5263Sriastradh 
1042a06a0449Sriastradh 	/*
10434d2a5263Sriastradh 	 * If we're switching on or off promiscuous mode, reprogram the
10444d2a5263Sriastradh 	 * hardware multicast filter now.
1045a06a0449Sriastradh 	 *
10464d2a5263Sriastradh 	 * XXX Actually, reset the interface, because some usbnet
10474d2a5263Sriastradh 	 * drivers (e.g., aue(4)) initialize the hardware differently
10484d2a5263Sriastradh 	 * in uno_init depending on IFF_PROMISC.  But some (again,
10494d2a5263Sriastradh 	 * aue(4)) _also_ need to know whether IFF_PROMISC is set in
10504d2a5263Sriastradh 	 * uno_mcast and do something different with it there.  Maybe
10514d2a5263Sriastradh 	 * the logic can be unified, but it will require an audit and
10524d2a5263Sriastradh 	 * testing of all the usbnet drivers.
1053a06a0449Sriastradh 	 */
1054a06a0449Sriastradh 	if (changed & IFF_PROMISC)
10554d2a5263Sriastradh 		return ENETRESET;
1056773ec77dSmrg 
10574d2a5263Sriastradh 	return 0;
1058773ec77dSmrg }
1059773ec77dSmrg 
1060a06a0449Sriastradh bool
1061a06a0449Sriastradh usbnet_ispromisc(struct usbnet *un)
1062a06a0449Sriastradh {
1063a06a0449Sriastradh 	struct ifnet * const ifp = usbnet_ifp(un);
1064a06a0449Sriastradh 	struct usbnet_private * const unp = un->un_pri;
1065a06a0449Sriastradh 
1066a06a0449Sriastradh 	KASSERTMSG(mutex_owned(&unp->unp_mcastlock) || IFNET_LOCKED(ifp),
1067a06a0449Sriastradh 	    "%s", ifp->if_xname);
1068a06a0449Sriastradh 
1069a06a0449Sriastradh 	return unp->unp_if_flags & IFF_PROMISC;
1070a06a0449Sriastradh }
1071a06a0449Sriastradh 
1072773ec77dSmrg static int
10737a9a30c5Sthorpej usbnet_if_ioctl(struct ifnet *ifp, u_long cmd, void *data)
1074773ec77dSmrg {
1075543abf08Smrg 	USBNETHIST_FUNC();
1076773ec77dSmrg 	struct usbnet * const un = ifp->if_softc;
1077543abf08Smrg 	struct usbnet_private * const unp __unused = un->un_pri;
1078773ec77dSmrg 	int error;
1079773ec77dSmrg 
1080d3dde16cSchristos 	USBNETHIST_CALLARGSN(11, "%jd: enter %#jx data %#jx",
1081543abf08Smrg 	    unp->unp_number, cmd, (uintptr_t)data, 0);
1082543abf08Smrg 
1083d066f229Smrg 	if (un->un_ops->uno_override_ioctl)
1084d066f229Smrg 		return uno_override_ioctl(un, ifp, cmd, data);
108576b0c5e3Smrg 
1086773ec77dSmrg 	error = ether_ioctl(ifp, cmd, data);
108766170f1aSriastradh 	if (error == ENETRESET) {
108866170f1aSriastradh 		switch (cmd) {
108966170f1aSriastradh 		case SIOCADDMULTI:
109066170f1aSriastradh 		case SIOCDELMULTI:
1091f59d8c97Sriastradh 			/*
1092f59d8c97Sriastradh 			 * If there's a hardware multicast filter, and
1093f59d8c97Sriastradh 			 * it has been programmed by usbnet_init_rx_tx
1094f59d8c97Sriastradh 			 * and is active, update it now.  Otherwise,
1095f59d8c97Sriastradh 			 * drop the update on the floor -- it will be
1096f59d8c97Sriastradh 			 * observed by usbnet_init_rx_tx next time we
1097f59d8c97Sriastradh 			 * bring the interface up.
1098f59d8c97Sriastradh 			 */
1099f59d8c97Sriastradh 			if (un->un_ops->uno_mcast) {
1100f59d8c97Sriastradh 				mutex_enter(&unp->unp_mcastlock);
1101f59d8c97Sriastradh 				if (unp->unp_mcastactive)
1102f59d8c97Sriastradh 					(*un->un_ops->uno_mcast)(ifp);
1103f59d8c97Sriastradh 				mutex_exit(&unp->unp_mcastlock);
1104f59d8c97Sriastradh 			}
110566170f1aSriastradh 			error = 0;
110666170f1aSriastradh 			break;
110766170f1aSriastradh 		default:
1108d066f229Smrg 			error = uno_ioctl(un, ifp, cmd, data);
110966170f1aSriastradh 		}
111066170f1aSriastradh 	}
1111773ec77dSmrg 
1112773ec77dSmrg 	return error;
1113773ec77dSmrg }
1114773ec77dSmrg 
1115773ec77dSmrg /*
1116773ec77dSmrg  * Generic stop network function:
1117773ec77dSmrg  *	- mark as stopping
1118773ec77dSmrg  *	- call DD routine to stop the device
1119773ec77dSmrg  *	- turn off running, timer, statchg callout, link
1120773ec77dSmrg  *	- stop transfers
1121773ec77dSmrg  *	- free RX and TX resources
1122773ec77dSmrg  *	- close pipes
1123773ec77dSmrg  *
11247a9a30c5Sthorpej  * usbnet_if_stop() is for the if_stop handler.
1125773ec77dSmrg  */
112648639f89Sriastradh static void
1127773ec77dSmrg usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
1128773ec77dSmrg {
1129dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
113060e74a58Sriastradh 	struct mii_data * const mii = usbnet_mii(un);
1131dbbc1953Smrg 
1132fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
1133fa8dd7efSmrg 
1134552e8ffbSriastradh 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
1135c1b31cabSriastradh 	KASSERTMSG(ifp->if_flags & IFF_RUNNING, "%s", ifp->if_xname);
11367a9a30c5Sthorpej 
11375a426beeSriastradh 	/*
1138f59d8c97Sriastradh 	 * For drivers with hardware multicast filter update callbacks:
1139f59d8c97Sriastradh 	 * Prevent concurrent access to the hardware registers by
11408cff504dSriastradh 	 * multicast filter updates, which happens without IFNET_LOCK.
1141f59d8c97Sriastradh 	 */
1142f59d8c97Sriastradh 	if (un->un_ops->uno_mcast) {
1143f59d8c97Sriastradh 		mutex_enter(&unp->unp_mcastlock);
11446586a9d4Sriastradh 		KASSERTMSG(unp->unp_mcastactive, "%p", ifp->if_xname);
1145f59d8c97Sriastradh 		unp->unp_mcastactive = false;
11466586a9d4Sriastradh 		unp->unp_if_flags = 0;
1147f59d8c97Sriastradh 		mutex_exit(&unp->unp_mcastlock);
1148f59d8c97Sriastradh 	}
1149f59d8c97Sriastradh 
1150f59d8c97Sriastradh 	/*
11515a426beeSriastradh 	 * Prevent new activity (rescheduling ticks, xfers, &c.) and
11525a426beeSriastradh 	 * clear the watchdog timer.
11535a426beeSriastradh 	 */
1154edcedbe9Sriastradh 	mutex_enter(&unp->unp_miilock);
11558c65f880Sriastradh 	unp->unp_stopped = true;
1156edcedbe9Sriastradh 	mutex_exit(&unp->unp_miilock);
11578c65f880Sriastradh 
1158dbbc1953Smrg 	mutex_enter(&unp->unp_rxlock);
11598c65f880Sriastradh 	unp->unp_rxstopped = true;
11608c65f880Sriastradh 	mutex_exit(&unp->unp_rxlock);
11618c65f880Sriastradh 
1162dbbc1953Smrg 	mutex_enter(&unp->unp_txlock);
11638c65f880Sriastradh 	unp->unp_txstopped = true;
11645a426beeSriastradh 	unp->unp_timer = 0;
1165dbbc1953Smrg 	mutex_exit(&unp->unp_txlock);
1166773ec77dSmrg 
1167d9ce0ea7Sriastradh 	/*
1168d9ce0ea7Sriastradh 	 * Stop the timer first, then the task -- if the timer was
1169d9ce0ea7Sriastradh 	 * already firing, we stop the task or wait for it complete
11708cff504dSriastradh 	 * only after it last fired.  Setting unp_stopped prevents the
1171d9ce0ea7Sriastradh 	 * timer task from being scheduled again.
1172d9ce0ea7Sriastradh 	 */
11738cff504dSriastradh 	callout_halt(&unp->unp_stat_ch, NULL);
11740801a2c6Smrg 	usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
11758cff504dSriastradh 	    NULL);
1176773ec77dSmrg 
117760e74a58Sriastradh 	/*
117860e74a58Sriastradh 	 * Now that we have stopped calling mii_tick, bring the MII
117960e74a58Sriastradh 	 * state machine down.
118060e74a58Sriastradh 	 */
11818cff504dSriastradh 	if (mii) {
1182edcedbe9Sriastradh 		mutex_enter(&unp->unp_miilock);
118360e74a58Sriastradh 		mii_down(mii);
1184edcedbe9Sriastradh 		mutex_exit(&unp->unp_miilock);
11858cff504dSriastradh 	}
118660e74a58Sriastradh 
1187847d2eafSriastradh 	/* Stop transfers. */
1188847d2eafSriastradh 	usbnet_ep_stop_pipes(un);
1189847d2eafSriastradh 
1190d9ce0ea7Sriastradh 	/*
1191d9ce0ea7Sriastradh 	 * Now that the software is quiescent, ask the driver to stop
1192d9ce0ea7Sriastradh 	 * the hardware.  The driver's uno_stop routine now has
1193d9ce0ea7Sriastradh 	 * exclusive access to any registers that might previously have
1194d9ce0ea7Sriastradh 	 * been used by to ifmedia, mii, or ioctl callbacks.
119542232f3bSriastradh 	 *
119642232f3bSriastradh 	 * Don't bother if the device is being detached, though -- if
119742232f3bSriastradh 	 * it's been unplugged then there's no point in trying to touch
119842232f3bSriastradh 	 * the registers.
1199d9ce0ea7Sriastradh 	 */
1200ec36326eSriastradh 	if (!usbnet_isdying(un))
1201d9ce0ea7Sriastradh 		uno_stop(un, ifp, disable);
1202d9ce0ea7Sriastradh 
1203773ec77dSmrg 	/* Free RX/TX resources. */
1204773ec77dSmrg 	usbnet_rx_list_fini(un);
1205773ec77dSmrg 	usbnet_tx_list_fini(un);
1206773ec77dSmrg 
1207773ec77dSmrg 	/* Close pipes. */
1208773ec77dSmrg 	usbnet_ep_close_pipes(un);
12097a9a30c5Sthorpej 
1210703c8e30Sriastradh 	/* Everything is quesced now. */
1211552e8ffbSriastradh 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
121275757ff5Sriastradh 	ifp->if_flags &= ~IFF_RUNNING;
1213773ec77dSmrg }
1214773ec77dSmrg 
1215773ec77dSmrg static void
12167a9a30c5Sthorpej usbnet_if_stop(struct ifnet *ifp, int disable)
1217773ec77dSmrg {
1218773ec77dSmrg 	struct usbnet * const un = ifp->if_softc;
1219773ec77dSmrg 
122057635420Sriastradh 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
122157635420Sriastradh 
12228d74eae9Sriastradh 	/*
12238d74eae9Sriastradh 	 * If we're already stopped, nothing to do.
12248d74eae9Sriastradh 	 *
12258d74eae9Sriastradh 	 * XXX This should be an assertion, but it may require some
12268d74eae9Sriastradh 	 * analysis -- and possibly some tweaking -- of sys/net to
12278d74eae9Sriastradh 	 * ensure.
12288d74eae9Sriastradh 	 */
12298d74eae9Sriastradh 	if ((ifp->if_flags & IFF_RUNNING) == 0)
12308d74eae9Sriastradh 		return;
12318d74eae9Sriastradh 
1232773ec77dSmrg 	usbnet_stop(un, ifp, disable);
1233773ec77dSmrg }
1234773ec77dSmrg 
1235773ec77dSmrg /*
1236773ec77dSmrg  * Generic tick task function.
1237773ec77dSmrg  *
1238773ec77dSmrg  * usbnet_tick() is triggered from a callout, and triggers a call to
1239773ec77dSmrg  * usbnet_tick_task() from the usb_task subsystem.
1240773ec77dSmrg  */
1241773ec77dSmrg static void
1242773ec77dSmrg usbnet_tick(void *arg)
1243773ec77dSmrg {
12440801a2c6Smrg 	USBNETHIST_FUNC();
1245773ec77dSmrg 	struct usbnet * const un = arg;
1246dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
1247773ec77dSmrg 
1248d3dde16cSchristos 	USBNETHIST_CALLARGSN(10, "%jd: enter", unp->unp_number, 0, 0, 0);
12490801a2c6Smrg 
1250773ec77dSmrg 	/* Perform periodic stuff in process context */
1251dbbc1953Smrg 	usb_add_task(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER);
1252773ec77dSmrg }
1253773ec77dSmrg 
1254773ec77dSmrg static void
1255773ec77dSmrg usbnet_watchdog(struct ifnet *ifp)
1256773ec77dSmrg {
1257543abf08Smrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
1258773ec77dSmrg 	struct usbnet * const un = ifp->if_softc;
1259be798422Smrg 	struct usbnet_private * const unp = un->un_pri;
1260dbbc1953Smrg 	struct usbnet_cdata * const cd = un_cdata(un);
1261773ec77dSmrg 
1262cdaa0e91Sthorpej 	if_statinc(ifp, if_oerrors);
126368918b85Sjakllsch 	device_printf(un->un_dev, "watchdog timeout\n");
1264773ec77dSmrg 
1265773ec77dSmrg 	if (cd->uncd_tx_cnt > 0) {
126613fbb093Schristos 		DPRINTF("uncd_tx_cnt=%ju non zero, aborting pipe", 0, 0, 0, 0);
1267065aa2e9Sriastradh 		usbd_abort_pipe(unp->unp_ep[USBNET_ENDPT_TX]);
1268543abf08Smrg 		if (cd->uncd_tx_cnt != 0)
126913fbb093Schristos 			DPRINTF("uncd_tx_cnt now %ju", cd->uncd_tx_cnt, 0, 0, 0);
1270773ec77dSmrg 	}
1271773ec77dSmrg 
1272773ec77dSmrg 	if (!IFQ_IS_EMPTY(&ifp->if_snd))
1273773ec77dSmrg 		(*ifp->if_start)(ifp);
1274773ec77dSmrg }
1275773ec77dSmrg 
1276773ec77dSmrg static void
1277773ec77dSmrg usbnet_tick_task(void *arg)
1278773ec77dSmrg {
12790801a2c6Smrg 	USBNETHIST_FUNC();
1280773ec77dSmrg 	struct usbnet * const un = arg;
1281dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
1282773ec77dSmrg 	struct ifnet * const ifp = usbnet_ifp(un);
1283773ec77dSmrg 	struct mii_data * const mii = usbnet_mii(un);
1284773ec77dSmrg 
12854edd6c22Sriastradh 	USBNETHIST_CALLARGSN(8, "%jd: enter", unp->unp_number, 0, 0, 0);
1286773ec77dSmrg 
1287d4cda951Sriastradh 	mutex_enter(&unp->unp_txlock);
1288d4cda951Sriastradh 	const bool timeout = unp->unp_timer != 0 && --unp->unp_timer == 0;
1289d4cda951Sriastradh 	mutex_exit(&unp->unp_txlock);
1290d4cda951Sriastradh 	if (timeout)
1291773ec77dSmrg 		usbnet_watchdog(ifp);
1292773ec77dSmrg 
1293771538fcSriastradh 	/* Call driver if requested. */
1294771538fcSriastradh 	uno_tick(un);
1295771538fcSriastradh 
1296edcedbe9Sriastradh 	mutex_enter(&unp->unp_miilock);
1297d3dde16cSchristos 	DPRINTFN(8, "mii %#jx ifp %#jx", (uintptr_t)mii, (uintptr_t)ifp, 0, 0);
12983029dd48Smaya 	if (mii) {
1299773ec77dSmrg 		mii_tick(mii);
1300dbbc1953Smrg 		if (!unp->unp_link)
1301773ec77dSmrg 			(*mii->mii_statchg)(ifp);
13023029dd48Smaya 	}
1303773ec77dSmrg 
13048c65f880Sriastradh 	if (!unp->unp_stopped && !usbnet_isdying(un))
1305dbbc1953Smrg 		callout_schedule(&unp->unp_stat_ch, hz);
1306edcedbe9Sriastradh 	mutex_exit(&unp->unp_miilock);
1307773ec77dSmrg }
1308773ec77dSmrg 
1309773ec77dSmrg static int
13107a9a30c5Sthorpej usbnet_if_init(struct ifnet *ifp)
1311773ec77dSmrg {
1312fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
1313773ec77dSmrg 	struct usbnet * const un = ifp->if_softc;
13142cb1302aSriastradh 	int error;
1315773ec77dSmrg 
131657635420Sriastradh 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
131757635420Sriastradh 
1318fd68bbd0Sriastradh 	/*
1319fd68bbd0Sriastradh 	 * Prevent anyone from bringing the interface back up once
1320fd68bbd0Sriastradh 	 * we're detaching.
1321fd68bbd0Sriastradh 	 */
1322839d7b20Sriastradh 	if (usbnet_isdying(un))
1323fd68bbd0Sriastradh 		return EIO;
1324fd68bbd0Sriastradh 
13258d74eae9Sriastradh 	/*
1326c1b31cabSriastradh 	 * If we're already running, stop the interface first -- we're
1327c1b31cabSriastradh 	 * reinitializing it.
13288d74eae9Sriastradh 	 *
1329c1b31cabSriastradh 	 * XXX Grody for sys/net to call if_init to reinitialize.  This
1330c1b31cabSriastradh 	 * should be an assertion, not a branch, but it will require
1331c1b31cabSriastradh 	 * some tweaking of sys/net to avoid.  See also the comment in
1332f22ed7e5Sandvar 	 * usbnet_ifflags_cb about if_init vs uno_mcast on reinitialize.
13338d74eae9Sriastradh 	 */
13348d74eae9Sriastradh 	if (ifp->if_flags & IFF_RUNNING)
1335c1b31cabSriastradh 		usbnet_stop(un, ifp, /*disable*/1/*XXX???*/);
1336c1b31cabSriastradh 	KASSERTMSG((ifp->if_flags & IFF_RUNNING) == 0, "%s", ifp->if_xname);
13378d74eae9Sriastradh 
13382cb1302aSriastradh 	error = uno_init(un, ifp);
133993ec4d73Sriastradh 	if (error)
13408cff504dSriastradh 		return error;
134193ec4d73Sriastradh 	error = usbnet_init_rx_tx(un);
134293ec4d73Sriastradh 	if (error)
13432cb1302aSriastradh 		return error;
13448cff504dSriastradh 
13458cff504dSriastradh 	return 0;
1346773ec77dSmrg }
1347773ec77dSmrg 
1348b28f9034Smrg 
1349dbbc1953Smrg /* Various accessors. */
1350dbbc1953Smrg 
1351dbbc1953Smrg void
1352dbbc1953Smrg usbnet_set_link(struct usbnet *un, bool link)
1353dbbc1953Smrg {
1354edcedbe9Sriastradh 	usbnet_isowned_mii(un);
1355dbbc1953Smrg 	un->un_pri->unp_link = link;
1356dbbc1953Smrg }
1357dbbc1953Smrg 
1358dbbc1953Smrg struct ifnet *
1359dbbc1953Smrg usbnet_ifp(struct usbnet *un)
1360dbbc1953Smrg {
1361dbbc1953Smrg 	return &un->un_pri->unp_ec.ec_if;
1362dbbc1953Smrg }
1363dbbc1953Smrg 
1364dbbc1953Smrg struct ethercom *
1365dbbc1953Smrg usbnet_ec(struct usbnet *un)
1366dbbc1953Smrg {
1367dbbc1953Smrg 	return &un->un_pri->unp_ec;
1368dbbc1953Smrg }
1369dbbc1953Smrg 
1370dbbc1953Smrg struct mii_data *
1371dbbc1953Smrg usbnet_mii(struct usbnet *un)
1372dbbc1953Smrg {
1373dbbc1953Smrg 	return un->un_pri->unp_ec.ec_mii;
1374dbbc1953Smrg }
1375dbbc1953Smrg 
1376dbbc1953Smrg krndsource_t *
1377dbbc1953Smrg usbnet_rndsrc(struct usbnet *un)
1378dbbc1953Smrg {
1379dbbc1953Smrg 	return &un->un_pri->unp_rndsrc;
1380dbbc1953Smrg }
1381dbbc1953Smrg 
1382dbbc1953Smrg void *
1383dbbc1953Smrg usbnet_softc(struct usbnet *un)
1384dbbc1953Smrg {
1385dbbc1953Smrg 	return un->un_sc;
1386dbbc1953Smrg }
1387dbbc1953Smrg 
1388dbbc1953Smrg bool
1389dbbc1953Smrg usbnet_havelink(struct usbnet *un)
1390dbbc1953Smrg {
1391dbbc1953Smrg 	return un->un_pri->unp_link;
1392dbbc1953Smrg }
1393dbbc1953Smrg 
1394dbbc1953Smrg bool
1395dbbc1953Smrg usbnet_isdying(struct usbnet *un)
1396dbbc1953Smrg {
13974c7e9da1Sriastradh 	return atomic_load_relaxed(&un->un_pri->unp_dying);
1398dbbc1953Smrg }
1399dbbc1953Smrg 
1400dbbc1953Smrg 
1401dbbc1953Smrg /* Locking. */
1402dbbc1953Smrg 
14035c872ef1Sriastradh static void
14045c872ef1Sriastradh usbnet_isowned_rx(struct usbnet *un)
1405dbbc1953Smrg {
14065c872ef1Sriastradh 	KASSERT(mutex_owned(&un->un_pri->unp_rxlock));
1407dbbc1953Smrg }
1408dbbc1953Smrg 
14095c872ef1Sriastradh static void
14105c872ef1Sriastradh usbnet_isowned_tx(struct usbnet *un)
1411dbbc1953Smrg {
14125c872ef1Sriastradh 	KASSERT(mutex_owned(&un->un_pri->unp_txlock));
1413dbbc1953Smrg }
1414dbbc1953Smrg 
1415773ec77dSmrg /* Autoconf management. */
1416773ec77dSmrg 
141776b0c5e3Smrg static bool
1418b28f9034Smrg usbnet_empty_eaddr(struct usbnet * const un)
141976b0c5e3Smrg {
142076b0c5e3Smrg 	return (un->un_eaddr[0] == 0 && un->un_eaddr[1] == 0 &&
142176b0c5e3Smrg 		un->un_eaddr[2] == 0 && un->un_eaddr[3] == 0 &&
142276b0c5e3Smrg 		un->un_eaddr[4] == 0 && un->un_eaddr[5] == 0);
142376b0c5e3Smrg }
142476b0c5e3Smrg 
1425773ec77dSmrg /*
1426773ec77dSmrg  * usbnet_attach() and usbnet_attach_ifp() perform setup of the relevant
1427773ec77dSmrg  * 'usbnet'.  The first is enough to enable device access (eg, endpoints
1428773ec77dSmrg  * are connected and commands can be sent), and the second connects the
1429773ec77dSmrg  * device to the system networking.
1430773ec77dSmrg  *
143159adb0c0Sandvar  * Always call usbnet_detach(), even if usbnet_attach_ifp() is skipped.
1432773ec77dSmrg  * Also usable as driver detach directly.
143376b0c5e3Smrg  *
143476b0c5e3Smrg  * To skip ethernet configuration (eg, point-to-point), make sure that
143576b0c5e3Smrg  * the un_eaddr[] is fully zero.
1436773ec77dSmrg  */
1437dbbc1953Smrg 
1438773ec77dSmrg void
14390b4ab8ceSriastradh usbnet_attach(struct usbnet *un)
1440773ec77dSmrg {
1441fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
1442773ec77dSmrg 
1443d066f229Smrg 	/* Required inputs.  */
1444d066f229Smrg 	KASSERT(un->un_ops->uno_tx_prepare);
1445d066f229Smrg 	KASSERT(un->un_ops->uno_rx_loop);
1446dbbc1953Smrg 	KASSERT(un->un_rx_bufsz);
1447dbbc1953Smrg 	KASSERT(un->un_tx_bufsz);
1448dbbc1953Smrg 	KASSERT(un->un_rx_list_cnt);
1449dbbc1953Smrg 	KASSERT(un->un_tx_list_cnt);
1450773ec77dSmrg 
1451c9e9d49aSmrg 	/* Unfortunate fact.  */
1452c9e9d49aSmrg 	KASSERT(un == device_private(un->un_dev));
1453c9e9d49aSmrg 
1454dbbc1953Smrg 	un->un_pri = kmem_zalloc(sizeof(*un->un_pri), KM_SLEEP);
1455dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
1456d066f229Smrg 
145766170f1aSriastradh 	usb_init_task(&unp->unp_ticktask, usbnet_tick_task, un,
145866170f1aSriastradh 	    USB_TASKQ_MPSAFE);
1459dbbc1953Smrg 	callout_init(&unp->unp_stat_ch, CALLOUT_MPSAFE);
1460dbbc1953Smrg 	callout_setfunc(&unp->unp_stat_ch, usbnet_tick, un);
1461773ec77dSmrg 
1462dbbc1953Smrg 	mutex_init(&unp->unp_txlock, MUTEX_DEFAULT, IPL_SOFTUSB);
1463dbbc1953Smrg 	mutex_init(&unp->unp_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB);
1464edcedbe9Sriastradh 	mutex_init(&unp->unp_miilock, MUTEX_DEFAULT, IPL_NONE);
1465f59d8c97Sriastradh 	mutex_init(&unp->unp_mcastlock, MUTEX_DEFAULT, IPL_SOFTCLOCK);
1466773ec77dSmrg 
1467dbbc1953Smrg 	rnd_attach_source(&unp->unp_rndsrc, device_xname(un->un_dev),
1468773ec77dSmrg 	    RND_TYPE_NET, RND_FLAG_DEFAULT);
1469773ec77dSmrg 
1470d066f229Smrg 	usbnet_rx_list_alloc(un);
1471d066f229Smrg 	usbnet_tx_list_alloc(un);
1472773ec77dSmrg 
1473543abf08Smrg 	unp->unp_number = atomic_inc_uint_nv(&usbnet_number);
1474543abf08Smrg 
14758c65f880Sriastradh 	unp->unp_stopped = true;
14768c65f880Sriastradh 	unp->unp_rxstopped = true;
14778c65f880Sriastradh 	unp->unp_txstopped = true;
1478dbbc1953Smrg 	unp->unp_attached = true;
1479773ec77dSmrg }
1480773ec77dSmrg 
1481773ec77dSmrg static void
148265dae968Smrg usbnet_attach_mii(struct usbnet *un, const struct usbnet_mii *unm)
1483773ec77dSmrg {
1484fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
1485dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
1486dbbc1953Smrg 	struct mii_data * const mii = &unp->unp_mii;
1487dbbc1953Smrg 	struct ifnet * const ifp = usbnet_ifp(un);
1488773ec77dSmrg 
1489d066f229Smrg 	KASSERT(un->un_ops->uno_read_reg);
1490d066f229Smrg 	KASSERT(un->un_ops->uno_write_reg);
1491d066f229Smrg 	KASSERT(un->un_ops->uno_statchg);
1492d066f229Smrg 
1493773ec77dSmrg 	mii->mii_ifp = ifp;
1494d066f229Smrg 	mii->mii_readreg = usbnet_mii_readreg;
1495d066f229Smrg 	mii->mii_writereg = usbnet_mii_writereg;
1496d066f229Smrg 	mii->mii_statchg = usbnet_mii_statchg;
1497773ec77dSmrg 	mii->mii_flags = MIIF_AUTOTSLEEP;
1498773ec77dSmrg 
1499dbbc1953Smrg 	usbnet_ec(un)->ec_mii = mii;
15007a9a30c5Sthorpej 	ifmedia_init_with_lock(&mii->mii_media, 0,
1501edcedbe9Sriastradh 	    usbnet_media_upd, ether_mediastatus, &unp->unp_miilock);
150265dae968Smrg 	mii_attach(un->un_dev, mii, unm->un_mii_capmask, unm->un_mii_phyloc,
150365dae968Smrg 	    unm->un_mii_offset, unm->un_mii_flags);
1504773ec77dSmrg 
1505773ec77dSmrg 	if (LIST_FIRST(&mii->mii_phys) == NULL) {
1506773ec77dSmrg 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
1507773ec77dSmrg 		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
1508773ec77dSmrg 	} else
1509773ec77dSmrg 		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
1510773ec77dSmrg }
1511773ec77dSmrg 
1512773ec77dSmrg void
1513773ec77dSmrg usbnet_attach_ifp(struct usbnet *un,
1514773ec77dSmrg 		  unsigned if_flags,		/* additional if_flags */
1515699d292aSmrg 		  unsigned if_extflags,		/* additional if_extflags */
151665dae968Smrg 		  const struct usbnet_mii *unm)	/* additional mii_attach flags */
1517773ec77dSmrg {
1518fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
1519dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
1520dbbc1953Smrg 	struct ifnet * const ifp = usbnet_ifp(un);
1521773ec77dSmrg 
1522dbbc1953Smrg 	KASSERT(unp->unp_attached);
152305d2a7a3Sriastradh 	KASSERT(!unp->unp_ifp_attached);
1524773ec77dSmrg 
152505d2a7a3Sriastradh 	ifp->if_softc = un;
1526773ec77dSmrg 	strlcpy(ifp->if_xname, device_xname(un->un_dev), IFNAMSIZ);
1527773ec77dSmrg 	ifp->if_flags = if_flags;
1528773ec77dSmrg 	ifp->if_extflags = IFEF_MPSAFE | if_extflags;
15297a9a30c5Sthorpej 	ifp->if_ioctl = usbnet_if_ioctl;
15307a9a30c5Sthorpej 	ifp->if_start = usbnet_if_start;
15317a9a30c5Sthorpej 	ifp->if_init = usbnet_if_init;
15327a9a30c5Sthorpej 	ifp->if_stop = usbnet_if_stop;
1533773ec77dSmrg 
153465dae968Smrg 	if (unm)
153565dae968Smrg 		usbnet_attach_mii(un, unm);
1536fa8dd7efSmrg 	else
1537dbbc1953Smrg 		unp->unp_link = true;
1538773ec77dSmrg 
1539773ec77dSmrg 	/* Attach the interface. */
1540076e3579Sriastradh 	if_initialize(ifp);
1541be798422Smrg 	if (ifp->_if_input == NULL)
1542be798422Smrg 		ifp->if_percpuq = if_percpuq_create(ifp);
1543be798422Smrg 	if_register(ifp);
154405d2a7a3Sriastradh 	unp->unp_ifp_attached = true;
154576b0c5e3Smrg 
154676b0c5e3Smrg 	/*
154776b0c5e3Smrg 	 * If ethernet address is all zero, skip ether_ifattach() and
154876b0c5e3Smrg 	 * instead attach bpf here..
154976b0c5e3Smrg 	 */
155076b0c5e3Smrg 	if (!usbnet_empty_eaddr(un)) {
1551dbbc1953Smrg 		ether_set_ifflags_cb(&unp->unp_ec, usbnet_ifflags_cb);
1552599c0927Smrg 		aprint_normal_dev(un->un_dev, "Ethernet address %s\n",
1553599c0927Smrg 		    ether_sprintf(un->un_eaddr));
1554773ec77dSmrg 		ether_ifattach(ifp, un->un_eaddr);
155576b0c5e3Smrg 	} else {
155676b0c5e3Smrg 		if_alloc_sadl(ifp);
155776b0c5e3Smrg 		bpf_attach(ifp, DLT_RAW, 0);
155876b0c5e3Smrg 	}
1559f3e2d55bSmrg 
1560be798422Smrg 	/* Now ready, and attached. */
1561be798422Smrg 	IFQ_SET_READY(&ifp->if_snd);
1562be798422Smrg 
1563f3e2d55bSmrg 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, un->un_udev, un->un_dev);
1564f3e2d55bSmrg 
1565f3e2d55bSmrg 	if (!pmf_device_register(un->un_dev, NULL, NULL))
1566f3e2d55bSmrg 		aprint_error_dev(un->un_dev, "couldn't establish power handler\n");
1567773ec77dSmrg }
1568773ec77dSmrg 
1569773ec77dSmrg int
1570773ec77dSmrg usbnet_detach(device_t self, int flags)
1571773ec77dSmrg {
1572fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
1573773ec77dSmrg 	struct usbnet * const un = device_private(self);
1574749ea25dSmrg 	struct usbnet_private * const unp = un->un_pri;
1575749ea25dSmrg 
1576749ea25dSmrg 	/* Detached before attached finished, so just bail out. */
1577749ea25dSmrg 	if (unp == NULL || !unp->unp_attached)
1578749ea25dSmrg 		return 0;
1579749ea25dSmrg 
1580dbbc1953Smrg 	struct ifnet * const ifp = usbnet_ifp(un);
1581dbbc1953Smrg 	struct mii_data * const mii = usbnet_mii(un);
1582773ec77dSmrg 
1583fd68bbd0Sriastradh 	/*
1584fd68bbd0Sriastradh 	 * Prevent new activity.  After we stop the interface, it
1585fd68bbd0Sriastradh 	 * cannot be brought back up.
1586fd68bbd0Sriastradh 	 */
15874c7e9da1Sriastradh 	atomic_store_relaxed(&unp->unp_dying, true);
1588773ec77dSmrg 
1589fd68bbd0Sriastradh 	/*
1590fd68bbd0Sriastradh 	 * If we're still running on the network, stop and wait for all
1591fd68bbd0Sriastradh 	 * asynchronous activity to finish.
1592d7d8fa7bSriastradh 	 *
1593d7d8fa7bSriastradh 	 * If usbnet_attach_ifp never ran, IFNET_LOCK won't work, but
1594d7d8fa7bSriastradh 	 * no activity is possible, so just skip this part.
1595fd68bbd0Sriastradh 	 */
1596d7d8fa7bSriastradh 	if (unp->unp_ifp_attached) {
1597773ec77dSmrg 		IFNET_LOCK(ifp);
1598774acb4aSriastradh 		if (ifp->if_flags & IFF_RUNNING) {
15997a9a30c5Sthorpej 			usbnet_if_stop(ifp, 1);
1600773ec77dSmrg 		}
1601774acb4aSriastradh 		IFNET_UNLOCK(ifp);
1602d7d8fa7bSriastradh 	}
1603773ec77dSmrg 
1604c0ae7fccSriastradh 	/*
1605c0ae7fccSriastradh 	 * The callout and tick task can't be scheduled anew at this
1606c0ae7fccSriastradh 	 * point, and usbnet_if_stop has waited for them to complete.
1607c0ae7fccSriastradh 	 */
1608c0ae7fccSriastradh 	KASSERT(!callout_pending(&unp->unp_stat_ch));
1609c0ae7fccSriastradh 	KASSERT(!usb_task_pending(un->un_udev, &unp->unp_ticktask));
1610c0ae7fccSriastradh 
1611773ec77dSmrg 	if (mii) {
1612773ec77dSmrg 		mii_detach(mii, MII_PHY_ANY, MII_OFFSET_ANY);
16134947e861Sthorpej 		ifmedia_fini(&mii->mii_media);
1614773ec77dSmrg 	}
161505d2a7a3Sriastradh 	if (unp->unp_ifp_attached) {
161676b0c5e3Smrg 		if (!usbnet_empty_eaddr(un))
1617773ec77dSmrg 			ether_ifdetach(ifp);
161876b0c5e3Smrg 		else
161976b0c5e3Smrg 			bpf_detach(ifp);
1620773ec77dSmrg 		if_detach(ifp);
1621773ec77dSmrg 	}
1622ccd4e408Sriastradh 	usbnet_ec(un)->ec_mii = NULL;
1623773ec77dSmrg 
1624719b8cc5Sriastradh 	usbnet_rx_list_free(un);
1625719b8cc5Sriastradh 	usbnet_tx_list_free(un);
1626719b8cc5Sriastradh 
1627719b8cc5Sriastradh 	rnd_detach_source(&unp->unp_rndsrc);
1628719b8cc5Sriastradh 
1629f59d8c97Sriastradh 	mutex_destroy(&unp->unp_mcastlock);
1630edcedbe9Sriastradh 	mutex_destroy(&unp->unp_miilock);
1631dbbc1953Smrg 	mutex_destroy(&unp->unp_rxlock);
1632dbbc1953Smrg 	mutex_destroy(&unp->unp_txlock);
1633773ec77dSmrg 
1634313c62bbSriastradh 	callout_destroy(&unp->unp_stat_ch);
1635313c62bbSriastradh 
1636773ec77dSmrg 	pmf_device_deregister(un->un_dev);
1637773ec77dSmrg 
1638486844a4Sriastradh 	/*
1639486844a4Sriastradh 	 * Notify userland that we're going away, if we arrived in the
1640486844a4Sriastradh 	 * first place.
1641486844a4Sriastradh 	 */
1642486844a4Sriastradh 	if (unp->unp_ifp_attached) {
1643486844a4Sriastradh 		usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, un->un_udev,
1644486844a4Sriastradh 		    un->un_dev);
1645486844a4Sriastradh 	}
1646773ec77dSmrg 
1647dbbc1953Smrg 	kmem_free(unp, sizeof(*unp));
16480801a2c6Smrg 	un->un_pri = NULL;
1649dbbc1953Smrg 
1650773ec77dSmrg 	return 0;
1651773ec77dSmrg }
1652773ec77dSmrg 
1653773ec77dSmrg int
1654773ec77dSmrg usbnet_activate(device_t self, devact_t act)
1655773ec77dSmrg {
1656fa8dd7efSmrg 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
1657773ec77dSmrg 	struct usbnet * const un = device_private(self);
1658dbbc1953Smrg 	struct usbnet_private * const unp = un->un_pri;
1659773ec77dSmrg 	struct ifnet * const ifp = usbnet_ifp(un);
1660773ec77dSmrg 
1661773ec77dSmrg 	switch (act) {
1662773ec77dSmrg 	case DVACT_DEACTIVATE:
1663773ec77dSmrg 		if_deactivate(ifp);
1664773ec77dSmrg 
16654c7e9da1Sriastradh 		atomic_store_relaxed(&unp->unp_dying, true);
1666773ec77dSmrg 
1667edcedbe9Sriastradh 		mutex_enter(&unp->unp_miilock);
16688c65f880Sriastradh 		unp->unp_stopped = true;
1669edcedbe9Sriastradh 		mutex_exit(&unp->unp_miilock);
16708c65f880Sriastradh 
1671dbbc1953Smrg 		mutex_enter(&unp->unp_rxlock);
16728c65f880Sriastradh 		unp->unp_rxstopped = true;
1673dbbc1953Smrg 		mutex_exit(&unp->unp_rxlock);
1674773ec77dSmrg 
16758c65f880Sriastradh 		mutex_enter(&unp->unp_txlock);
16768c65f880Sriastradh 		unp->unp_txstopped = true;
16778c65f880Sriastradh 		mutex_exit(&unp->unp_txlock);
16788c65f880Sriastradh 
1679773ec77dSmrg 		return 0;
1680773ec77dSmrg 	default:
1681773ec77dSmrg 		return EOPNOTSUPP;
1682773ec77dSmrg 	}
1683773ec77dSmrg }
1684773ec77dSmrg 
1685773ec77dSmrg MODULE(MODULE_CLASS_MISC, usbnet, NULL);
1686773ec77dSmrg 
1687773ec77dSmrg static int
1688773ec77dSmrg usbnet_modcmd(modcmd_t cmd, void *arg)
1689773ec77dSmrg {
1690773ec77dSmrg 	switch (cmd) {
1691773ec77dSmrg 	case MODULE_CMD_INIT:
1692fa8dd7efSmrg 		return 0;
1693773ec77dSmrg 	case MODULE_CMD_FINI:
1694773ec77dSmrg 		return 0;
1695773ec77dSmrg 	case MODULE_CMD_STAT:
1696773ec77dSmrg 	case MODULE_CMD_AUTOUNLOAD:
1697773ec77dSmrg 	default:
1698773ec77dSmrg 		return ENOTTY;
1699773ec77dSmrg 	}
1700773ec77dSmrg }
1701