xref: /freebsd-src/sys/dev/usb/net/usb_ethernet.c (revision 11a9117871e6037ae7b8011b243939322efce569)
102ac6454SAndrew Thompson /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
402ac6454SAndrew Thompson  * Copyright (c) 2009 Andrew Thompson (thompsa@FreeBSD.org)
502ac6454SAndrew Thompson  *
602ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
702ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
802ac6454SAndrew Thompson  * are met:
902ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
1002ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1102ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1202ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1302ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1402ac6454SAndrew Thompson  *
1502ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1602ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1702ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1802ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1902ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2002ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2102ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2202ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2302ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2402ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2502ac6454SAndrew Thompson  * SUCH DAMAGE.
2602ac6454SAndrew Thompson  */
2702ac6454SAndrew Thompson 
28ed6d949aSAndrew Thompson #include <sys/param.h>
29ed6d949aSAndrew Thompson #include <sys/systm.h>
30ed6d949aSAndrew Thompson #include <sys/bus.h>
31ed6d949aSAndrew Thompson #include <sys/condvar.h>
325c6b53d8SPyun YongHyeon #include <sys/kernel.h>
335c6b53d8SPyun YongHyeon #include <sys/lock.h>
345c6b53d8SPyun YongHyeon #include <sys/malloc.h>
355c6b53d8SPyun YongHyeon #include <sys/mbuf.h>
365c6b53d8SPyun YongHyeon #include <sys/module.h>
375c6b53d8SPyun YongHyeon #include <sys/mutex.h>
385c6b53d8SPyun YongHyeon #include <sys/socket.h>
395c6b53d8SPyun YongHyeon #include <sys/sockio.h>
40ed6d949aSAndrew Thompson #include <sys/sysctl.h>
41ed6d949aSAndrew Thompson #include <sys/sx.h>
425c6b53d8SPyun YongHyeon 
435c6b53d8SPyun YongHyeon #include <net/if.h>
4476039bc8SGleb Smirnoff #include <net/if_var.h>
455c6b53d8SPyun YongHyeon #include <net/ethernet.h>
465c6b53d8SPyun YongHyeon #include <net/if_types.h>
475c6b53d8SPyun YongHyeon #include <net/if_media.h>
485c6b53d8SPyun YongHyeon #include <net/if_vlan_var.h>
495c6b53d8SPyun YongHyeon 
505c6b53d8SPyun YongHyeon #include <dev/mii/mii.h>
515c6b53d8SPyun YongHyeon #include <dev/mii/miivar.h>
52ed6d949aSAndrew Thompson 
5302ac6454SAndrew Thompson #include <dev/usb/usb.h>
54ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
5502ac6454SAndrew Thompson 
5602ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
5702ac6454SAndrew Thompson #include <dev/usb/net/usb_ethernet.h>
5802ac6454SAndrew Thompson 
59f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_net, OID_AUTO, ue, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
606472ac3dSEd Schouten     "USB Ethernet parameters");
6102ac6454SAndrew Thompson 
6202ac6454SAndrew Thompson #define	UE_LOCK(_ue)		mtx_lock((_ue)->ue_mtx)
6302ac6454SAndrew Thompson #define	UE_UNLOCK(_ue)		mtx_unlock((_ue)->ue_mtx)
6402ac6454SAndrew Thompson #define	UE_LOCK_ASSERT(_ue, t)	mtx_assert((_ue)->ue_mtx, t)
6502ac6454SAndrew Thompson 
6602ac6454SAndrew Thompson MODULE_DEPEND(uether, usb, 1, 1, 1);
6702ac6454SAndrew Thompson MODULE_DEPEND(uether, miibus, 1, 1, 1);
6802ac6454SAndrew Thompson 
6902ac6454SAndrew Thompson static struct unrhdr *ueunit;
7002ac6454SAndrew Thompson 
71e0a69b51SAndrew Thompson static usb_proc_callback_t ue_attach_post_task;
72e0a69b51SAndrew Thompson static usb_proc_callback_t ue_promisc_task;
73e0a69b51SAndrew Thompson static usb_proc_callback_t ue_setmulti_task;
74e0a69b51SAndrew Thompson static usb_proc_callback_t ue_ifmedia_task;
75e0a69b51SAndrew Thompson static usb_proc_callback_t ue_tick_task;
76e0a69b51SAndrew Thompson static usb_proc_callback_t ue_start_task;
77e0a69b51SAndrew Thompson static usb_proc_callback_t ue_stop_task;
7802ac6454SAndrew Thompson 
7902ac6454SAndrew Thompson static void	ue_init(void *);
802fda7967SJustin Hibbits static void	ue_start(if_t);
812fda7967SJustin Hibbits static int	ue_ifmedia_upd(if_t);
8202ac6454SAndrew Thompson static void	ue_watchdog(void *);
8302ac6454SAndrew Thompson 
8402ac6454SAndrew Thompson /*
8502ac6454SAndrew Thompson  * Return values:
8602ac6454SAndrew Thompson  *    0: success
8702ac6454SAndrew Thompson  * Else: device has been detached
8802ac6454SAndrew Thompson  */
8902ac6454SAndrew Thompson uint8_t
9062d42655SHans Petter Selasky uether_pause(struct usb_ether *ue, unsigned _ticks)
9102ac6454SAndrew Thompson {
92a593f6b8SAndrew Thompson 	if (usb_proc_is_gone(&ue->ue_tq)) {
9302ac6454SAndrew Thompson 		/* nothing to do */
9402ac6454SAndrew Thompson 		return (1);
9502ac6454SAndrew Thompson 	}
96a593f6b8SAndrew Thompson 	usb_pause_mtx(ue->ue_mtx, _ticks);
9702ac6454SAndrew Thompson 	return (0);
9802ac6454SAndrew Thompson }
9902ac6454SAndrew Thompson 
10002ac6454SAndrew Thompson static void
101760bc48eSAndrew Thompson ue_queue_command(struct usb_ether *ue,
102e0a69b51SAndrew Thompson     usb_proc_callback_t *fn,
103760bc48eSAndrew Thompson     struct usb_proc_msg *t0, struct usb_proc_msg *t1)
10402ac6454SAndrew Thompson {
105760bc48eSAndrew Thompson 	struct usb_ether_cfg_task *task;
10602ac6454SAndrew Thompson 
10702ac6454SAndrew Thompson 	UE_LOCK_ASSERT(ue, MA_OWNED);
10802ac6454SAndrew Thompson 
109a593f6b8SAndrew Thompson 	if (usb_proc_is_gone(&ue->ue_tq)) {
11002ac6454SAndrew Thompson 		return;         /* nothing to do */
11102ac6454SAndrew Thompson 	}
11202ac6454SAndrew Thompson 	/*
11302ac6454SAndrew Thompson 	 * NOTE: The task cannot get executed before we drop the
11402ac6454SAndrew Thompson 	 * "sc_mtx" mutex. It is safe to update fields in the message
11502ac6454SAndrew Thompson 	 * structure after that the message got queued.
11602ac6454SAndrew Thompson 	 */
117760bc48eSAndrew Thompson 	task = (struct usb_ether_cfg_task *)
118a593f6b8SAndrew Thompson 	  usb_proc_msignal(&ue->ue_tq, t0, t1);
11902ac6454SAndrew Thompson 
12002ac6454SAndrew Thompson 	/* Setup callback and self pointers */
12102ac6454SAndrew Thompson 	task->hdr.pm_callback = fn;
12202ac6454SAndrew Thompson 	task->ue = ue;
12302ac6454SAndrew Thompson 
12402ac6454SAndrew Thompson 	/*
12502ac6454SAndrew Thompson 	 * Start and stop must be synchronous!
12602ac6454SAndrew Thompson 	 */
12702ac6454SAndrew Thompson 	if ((fn == ue_start_task) || (fn == ue_stop_task))
128a593f6b8SAndrew Thompson 		usb_proc_mwait(&ue->ue_tq, t0, t1);
12902ac6454SAndrew Thompson }
13002ac6454SAndrew Thompson 
1312fda7967SJustin Hibbits if_t
132a593f6b8SAndrew Thompson uether_getifp(struct usb_ether *ue)
13302ac6454SAndrew Thompson {
13402ac6454SAndrew Thompson 	return (ue->ue_ifp);
13502ac6454SAndrew Thompson }
13602ac6454SAndrew Thompson 
13702ac6454SAndrew Thompson struct mii_data *
138a593f6b8SAndrew Thompson uether_getmii(struct usb_ether *ue)
13902ac6454SAndrew Thompson {
14002ac6454SAndrew Thompson 	return (device_get_softc(ue->ue_miibus));
14102ac6454SAndrew Thompson }
14202ac6454SAndrew Thompson 
14302ac6454SAndrew Thompson void *
144a593f6b8SAndrew Thompson uether_getsc(struct usb_ether *ue)
14502ac6454SAndrew Thompson {
14602ac6454SAndrew Thompson 	return (ue->ue_sc);
14702ac6454SAndrew Thompson }
14802ac6454SAndrew Thompson 
14902ac6454SAndrew Thompson static int
15002ac6454SAndrew Thompson ue_sysctl_parent(SYSCTL_HANDLER_ARGS)
15102ac6454SAndrew Thompson {
152760bc48eSAndrew Thompson 	struct usb_ether *ue = arg1;
15302ac6454SAndrew Thompson 	const char *name;
15402ac6454SAndrew Thompson 
15502ac6454SAndrew Thompson 	name = device_get_nameunit(ue->ue_dev);
1568ddeeebfSIan Lepore 	return SYSCTL_OUT_STR(req, name);
15702ac6454SAndrew Thompson }
15802ac6454SAndrew Thompson 
15902ac6454SAndrew Thompson int
160a593f6b8SAndrew Thompson uether_ifattach(struct usb_ether *ue)
16102ac6454SAndrew Thompson {
16202ac6454SAndrew Thompson 	int error;
16302ac6454SAndrew Thompson 
16402ac6454SAndrew Thompson 	/* check some critical parameters */
16502ac6454SAndrew Thompson 	if ((ue->ue_dev == NULL) ||
16602ac6454SAndrew Thompson 	    (ue->ue_udev == NULL) ||
16702ac6454SAndrew Thompson 	    (ue->ue_mtx == NULL) ||
16802ac6454SAndrew Thompson 	    (ue->ue_methods == NULL))
16902ac6454SAndrew Thompson 		return (EINVAL);
17002ac6454SAndrew Thompson 
171a593f6b8SAndrew Thompson 	error = usb_proc_create(&ue->ue_tq, ue->ue_mtx,
17202ac6454SAndrew Thompson 	    device_get_nameunit(ue->ue_dev), USB_PRI_MED);
17302ac6454SAndrew Thompson 	if (error) {
17402ac6454SAndrew Thompson 		device_printf(ue->ue_dev, "could not setup taskqueue\n");
17502ac6454SAndrew Thompson 		goto error;
17602ac6454SAndrew Thompson 	}
17702ac6454SAndrew Thompson 
17802ac6454SAndrew Thompson 	/* fork rest of the attach code */
17902ac6454SAndrew Thompson 	UE_LOCK(ue);
18002ac6454SAndrew Thompson 	ue_queue_command(ue, ue_attach_post_task,
18102ac6454SAndrew Thompson 	    &ue->ue_sync_task[0].hdr,
18202ac6454SAndrew Thompson 	    &ue->ue_sync_task[1].hdr);
18302ac6454SAndrew Thompson 	UE_UNLOCK(ue);
18402ac6454SAndrew Thompson 
18502ac6454SAndrew Thompson error:
18602ac6454SAndrew Thompson 	return (error);
18702ac6454SAndrew Thompson }
18802ac6454SAndrew Thompson 
1890c89167cSHans Petter Selasky void
1900c89167cSHans Petter Selasky uether_ifattach_wait(struct usb_ether *ue)
1910c89167cSHans Petter Selasky {
1920c89167cSHans Petter Selasky 
1930c89167cSHans Petter Selasky 	UE_LOCK(ue);
1940c89167cSHans Petter Selasky 	usb_proc_mwait(&ue->ue_tq,
1950c89167cSHans Petter Selasky 	    &ue->ue_sync_task[0].hdr,
1960c89167cSHans Petter Selasky 	    &ue->ue_sync_task[1].hdr);
1970c89167cSHans Petter Selasky 	UE_UNLOCK(ue);
1980c89167cSHans Petter Selasky }
1990c89167cSHans Petter Selasky 
20002ac6454SAndrew Thompson static void
201760bc48eSAndrew Thompson ue_attach_post_task(struct usb_proc_msg *_task)
20202ac6454SAndrew Thompson {
203760bc48eSAndrew Thompson 	struct usb_ether_cfg_task *task =
204760bc48eSAndrew Thompson 	    (struct usb_ether_cfg_task *)_task;
205760bc48eSAndrew Thompson 	struct usb_ether *ue = task->ue;
2062fda7967SJustin Hibbits 	if_t ifp;
20702ac6454SAndrew Thompson 	int error;
20802ac6454SAndrew Thompson 	char num[14];			/* sufficient for 32 bits */
20902ac6454SAndrew Thompson 
21002ac6454SAndrew Thompson 	/* first call driver's post attach routine */
21102ac6454SAndrew Thompson 	ue->ue_methods->ue_attach_post(ue);
21202ac6454SAndrew Thompson 
21302ac6454SAndrew Thompson 	UE_UNLOCK(ue);
21402ac6454SAndrew Thompson 
21502ac6454SAndrew Thompson 	ue->ue_unit = alloc_unr(ueunit);
216a593f6b8SAndrew Thompson 	usb_callout_init_mtx(&ue->ue_watchdog, ue->ue_mtx, 0);
21702ac6454SAndrew Thompson 	sysctl_ctx_init(&ue->ue_sysctl_ctx);
218f6549df6SHans Petter Selasky 	mbufq_init(&ue->ue_rxq, 0 /* unlimited length */);
21902ac6454SAndrew Thompson 
2205c6b53d8SPyun YongHyeon 	error = 0;
221458a36d5SCraig Rodrigues 	CURVNET_SET_QUIET(vnet0);
22202ac6454SAndrew Thompson 	ifp = if_alloc(IFT_ETHER);
2232fda7967SJustin Hibbits 	if_setsoftc(ifp, ue);
22402ac6454SAndrew Thompson 	if_initname(ifp, "ue", ue->ue_unit);
2255c6b53d8SPyun YongHyeon 	if (ue->ue_methods->ue_attach_post_sub != NULL) {
2265c6b53d8SPyun YongHyeon 		ue->ue_ifp = ifp;
2275c6b53d8SPyun YongHyeon 		error = ue->ue_methods->ue_attach_post_sub(ue);
2285c6b53d8SPyun YongHyeon 	} else {
2292fda7967SJustin Hibbits 		if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
23002ac6454SAndrew Thompson 		if (ue->ue_methods->ue_ioctl != NULL)
2312fda7967SJustin Hibbits 			if_setioctlfn(ifp, ue->ue_methods->ue_ioctl);
23202ac6454SAndrew Thompson 		else
2332fda7967SJustin Hibbits 			if_setioctlfn(ifp, uether_ioctl);
2342fda7967SJustin Hibbits 		if_setstartfn(ifp, ue_start);
2352fda7967SJustin Hibbits 		if_setinitfn(ifp, ue_init);
2362fda7967SJustin Hibbits 		if_setsendqlen(ifp, ifqmaxlen);
2372fda7967SJustin Hibbits 		if_setsendqready(ifp);
23802ac6454SAndrew Thompson 		ue->ue_ifp = ifp;
23902ac6454SAndrew Thompson 
24002ac6454SAndrew Thompson 		if (ue->ue_methods->ue_mii_upd != NULL &&
24102ac6454SAndrew Thompson 		    ue->ue_methods->ue_mii_sts != NULL) {
242c6df6f53SWarner Losh 			bus_topo_lock();
243d6c65d27SMarius Strobl 			error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp,
244d6c65d27SMarius Strobl 			    ue_ifmedia_upd, ue->ue_methods->ue_mii_sts,
245d6c65d27SMarius Strobl 			    BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0);
246c6df6f53SWarner Losh 			bus_topo_unlock();
2475c6b53d8SPyun YongHyeon 		}
2485c6b53d8SPyun YongHyeon 	}
2495c6b53d8SPyun YongHyeon 
25002ac6454SAndrew Thompson 	if (error) {
251d6c65d27SMarius Strobl 		device_printf(ue->ue_dev, "attaching PHYs failed\n");
2525c6b53d8SPyun YongHyeon 		goto fail;
25302ac6454SAndrew Thompson 	}
25402ac6454SAndrew Thompson 
25502ac6454SAndrew Thompson 	if_printf(ifp, "<USB Ethernet> on %s\n", device_get_nameunit(ue->ue_dev));
25602ac6454SAndrew Thompson 	ether_ifattach(ifp, ue->ue_eaddr);
2575c6b53d8SPyun YongHyeon 	/* Tell upper layer we support VLAN oversized frames. */
2582fda7967SJustin Hibbits 	if (if_getcapabilities(ifp) & IFCAP_VLAN_MTU)
2592fda7967SJustin Hibbits 		if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
26002ac6454SAndrew Thompson 
261458a36d5SCraig Rodrigues 	CURVNET_RESTORE();
262458a36d5SCraig Rodrigues 
26302ac6454SAndrew Thompson 	snprintf(num, sizeof(num), "%u", ue->ue_unit);
26402ac6454SAndrew Thompson 	ue->ue_sysctl_oid = SYSCTL_ADD_NODE(&ue->ue_sysctl_ctx,
26502ac6454SAndrew Thompson 	    &SYSCTL_NODE_CHILDREN(_net, ue),
266f8d2b1f3SPawel Biernacki 	    OID_AUTO, num, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
26702ac6454SAndrew Thompson 	SYSCTL_ADD_PROC(&ue->ue_sysctl_ctx,
268f8d2b1f3SPawel Biernacki 	    SYSCTL_CHILDREN(ue->ue_sysctl_oid), OID_AUTO, "%parent",
269f8d2b1f3SPawel Biernacki 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, ue, 0,
27002ac6454SAndrew Thompson 	    ue_sysctl_parent, "A", "parent device");
27102ac6454SAndrew Thompson 
27202ac6454SAndrew Thompson 	UE_LOCK(ue);
27302ac6454SAndrew Thompson 	return;
27402ac6454SAndrew Thompson 
2755c6b53d8SPyun YongHyeon fail:
276458a36d5SCraig Rodrigues 	CURVNET_RESTORE();
277f6549df6SHans Petter Selasky 
278f6549df6SHans Petter Selasky 	/* drain mbuf queue */
279f6549df6SHans Petter Selasky 	mbufq_drain(&ue->ue_rxq);
280f6549df6SHans Petter Selasky 
281f6549df6SHans Petter Selasky 	/* free unit */
28202ac6454SAndrew Thompson 	free_unr(ueunit, ue->ue_unit);
28302ac6454SAndrew Thompson 	if (ue->ue_ifp != NULL) {
28402ac6454SAndrew Thompson 		if_free(ue->ue_ifp);
28502ac6454SAndrew Thompson 		ue->ue_ifp = NULL;
28602ac6454SAndrew Thompson 	}
28702ac6454SAndrew Thompson 	UE_LOCK(ue);
28802ac6454SAndrew Thompson 	return;
28902ac6454SAndrew Thompson }
29002ac6454SAndrew Thompson 
29102ac6454SAndrew Thompson void
292a593f6b8SAndrew Thompson uether_ifdetach(struct usb_ether *ue)
29302ac6454SAndrew Thompson {
2942fda7967SJustin Hibbits 	if_t ifp;
29502ac6454SAndrew Thompson 
29602ac6454SAndrew Thompson 	/* wait for any post attach or other command to complete */
297a593f6b8SAndrew Thompson 	usb_proc_drain(&ue->ue_tq);
29802ac6454SAndrew Thompson 
29902ac6454SAndrew Thompson 	/* read "ifnet" pointer after taskqueue drain */
30002ac6454SAndrew Thompson 	ifp = ue->ue_ifp;
30102ac6454SAndrew Thompson 
30202ac6454SAndrew Thompson 	if (ifp != NULL) {
30302ac6454SAndrew Thompson 		/* we are not running any more */
30402ac6454SAndrew Thompson 		UE_LOCK(ue);
3052fda7967SJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
30602ac6454SAndrew Thompson 		UE_UNLOCK(ue);
30702ac6454SAndrew Thompson 
30802ac6454SAndrew Thompson 		/* drain any callouts */
309a593f6b8SAndrew Thompson 		usb_callout_drain(&ue->ue_watchdog);
31002ac6454SAndrew Thompson 
3114eac63afSHans Petter Selasky 		/*
3124eac63afSHans Petter Selasky 		 * Detach ethernet first to stop miibus calls from
3134eac63afSHans Petter Selasky 		 * user-space:
3144eac63afSHans Petter Selasky 		 */
3154eac63afSHans Petter Selasky 		ether_ifdetach(ifp);
3164eac63afSHans Petter Selasky 
31702ac6454SAndrew Thompson 		/* detach miibus */
318c6df6f53SWarner Losh 		bus_topo_lock();
319*11a91178SJohn Baldwin 		bus_generic_detach(ue->ue_dev);
320c6df6f53SWarner Losh 		bus_topo_unlock();
32102ac6454SAndrew Thompson 
32202ac6454SAndrew Thompson 		/* free interface instance */
32302ac6454SAndrew Thompson 		if_free(ifp);
32402ac6454SAndrew Thompson 
32502ac6454SAndrew Thompson 		/* free sysctl */
32602ac6454SAndrew Thompson 		sysctl_ctx_free(&ue->ue_sysctl_ctx);
32702ac6454SAndrew Thompson 
328f6549df6SHans Petter Selasky 		/* drain mbuf queue */
329f6549df6SHans Petter Selasky 		mbufq_drain(&ue->ue_rxq);
330f6549df6SHans Petter Selasky 
33102ac6454SAndrew Thompson 		/* free unit */
33202ac6454SAndrew Thompson 		free_unr(ueunit, ue->ue_unit);
33302ac6454SAndrew Thompson 	}
33402ac6454SAndrew Thompson 
33502ac6454SAndrew Thompson 	/* free taskqueue, if any */
336a593f6b8SAndrew Thompson 	usb_proc_free(&ue->ue_tq);
33702ac6454SAndrew Thompson }
33802ac6454SAndrew Thompson 
33902ac6454SAndrew Thompson uint8_t
340a593f6b8SAndrew Thompson uether_is_gone(struct usb_ether *ue)
34102ac6454SAndrew Thompson {
342a593f6b8SAndrew Thompson 	return (usb_proc_is_gone(&ue->ue_tq));
34302ac6454SAndrew Thompson }
34402ac6454SAndrew Thompson 
3455c6b53d8SPyun YongHyeon void
3465c6b53d8SPyun YongHyeon uether_init(void *arg)
3475c6b53d8SPyun YongHyeon {
3485c6b53d8SPyun YongHyeon 
3495c6b53d8SPyun YongHyeon 	ue_init(arg);
3505c6b53d8SPyun YongHyeon }
3515c6b53d8SPyun YongHyeon 
35202ac6454SAndrew Thompson static void
35302ac6454SAndrew Thompson ue_init(void *arg)
35402ac6454SAndrew Thompson {
355760bc48eSAndrew Thompson 	struct usb_ether *ue = arg;
35602ac6454SAndrew Thompson 
35702ac6454SAndrew Thompson 	UE_LOCK(ue);
35802ac6454SAndrew Thompson 	ue_queue_command(ue, ue_start_task,
35902ac6454SAndrew Thompson 	    &ue->ue_sync_task[0].hdr,
36002ac6454SAndrew Thompson 	    &ue->ue_sync_task[1].hdr);
36102ac6454SAndrew Thompson 	UE_UNLOCK(ue);
36202ac6454SAndrew Thompson }
36302ac6454SAndrew Thompson 
36402ac6454SAndrew Thompson static void
365760bc48eSAndrew Thompson ue_start_task(struct usb_proc_msg *_task)
36602ac6454SAndrew Thompson {
367760bc48eSAndrew Thompson 	struct usb_ether_cfg_task *task =
368760bc48eSAndrew Thompson 	    (struct usb_ether_cfg_task *)_task;
369760bc48eSAndrew Thompson 	struct usb_ether *ue = task->ue;
3702fda7967SJustin Hibbits 	if_t ifp = ue->ue_ifp;
37102ac6454SAndrew Thompson 
37202ac6454SAndrew Thompson 	UE_LOCK_ASSERT(ue, MA_OWNED);
37302ac6454SAndrew Thompson 
37402ac6454SAndrew Thompson 	ue->ue_methods->ue_init(ue);
37502ac6454SAndrew Thompson 
3762fda7967SJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
37702ac6454SAndrew Thompson 		return;
37802ac6454SAndrew Thompson 
37902ac6454SAndrew Thompson 	if (ue->ue_methods->ue_tick != NULL)
380a593f6b8SAndrew Thompson 		usb_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue);
38102ac6454SAndrew Thompson }
38202ac6454SAndrew Thompson 
38302ac6454SAndrew Thompson static void
384760bc48eSAndrew Thompson ue_stop_task(struct usb_proc_msg *_task)
38502ac6454SAndrew Thompson {
386760bc48eSAndrew Thompson 	struct usb_ether_cfg_task *task =
387760bc48eSAndrew Thompson 	    (struct usb_ether_cfg_task *)_task;
388760bc48eSAndrew Thompson 	struct usb_ether *ue = task->ue;
38902ac6454SAndrew Thompson 
39002ac6454SAndrew Thompson 	UE_LOCK_ASSERT(ue, MA_OWNED);
39102ac6454SAndrew Thompson 
392a593f6b8SAndrew Thompson 	usb_callout_stop(&ue->ue_watchdog);
39302ac6454SAndrew Thompson 
39402ac6454SAndrew Thompson 	ue->ue_methods->ue_stop(ue);
39502ac6454SAndrew Thompson }
39602ac6454SAndrew Thompson 
3975c6b53d8SPyun YongHyeon void
3982fda7967SJustin Hibbits uether_start(if_t ifp)
3995c6b53d8SPyun YongHyeon {
4005c6b53d8SPyun YongHyeon 
4015c6b53d8SPyun YongHyeon 	ue_start(ifp);
4025c6b53d8SPyun YongHyeon }
4035c6b53d8SPyun YongHyeon 
40402ac6454SAndrew Thompson static void
4052fda7967SJustin Hibbits ue_start(if_t ifp)
40602ac6454SAndrew Thompson {
4072fda7967SJustin Hibbits 	struct usb_ether *ue = if_getsoftc(ifp);
40802ac6454SAndrew Thompson 
4092fda7967SJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
41002ac6454SAndrew Thompson 		return;
41102ac6454SAndrew Thompson 
41202ac6454SAndrew Thompson 	UE_LOCK(ue);
41302ac6454SAndrew Thompson 	ue->ue_methods->ue_start(ue);
41402ac6454SAndrew Thompson 	UE_UNLOCK(ue);
41502ac6454SAndrew Thompson }
41602ac6454SAndrew Thompson 
41702ac6454SAndrew Thompson static void
418760bc48eSAndrew Thompson ue_promisc_task(struct usb_proc_msg *_task)
41902ac6454SAndrew Thompson {
420760bc48eSAndrew Thompson 	struct usb_ether_cfg_task *task =
421760bc48eSAndrew Thompson 	    (struct usb_ether_cfg_task *)_task;
422760bc48eSAndrew Thompson 	struct usb_ether *ue = task->ue;
42302ac6454SAndrew Thompson 
42402ac6454SAndrew Thompson 	ue->ue_methods->ue_setpromisc(ue);
42502ac6454SAndrew Thompson }
42602ac6454SAndrew Thompson 
42702ac6454SAndrew Thompson static void
428760bc48eSAndrew Thompson ue_setmulti_task(struct usb_proc_msg *_task)
42902ac6454SAndrew Thompson {
430760bc48eSAndrew Thompson 	struct usb_ether_cfg_task *task =
431760bc48eSAndrew Thompson 	    (struct usb_ether_cfg_task *)_task;
432760bc48eSAndrew Thompson 	struct usb_ether *ue = task->ue;
43302ac6454SAndrew Thompson 
43402ac6454SAndrew Thompson 	ue->ue_methods->ue_setmulti(ue);
43502ac6454SAndrew Thompson }
43602ac6454SAndrew Thompson 
4375c6b53d8SPyun YongHyeon int
4382fda7967SJustin Hibbits uether_ifmedia_upd(if_t ifp)
4395c6b53d8SPyun YongHyeon {
4405c6b53d8SPyun YongHyeon 
4415c6b53d8SPyun YongHyeon 	return (ue_ifmedia_upd(ifp));
4425c6b53d8SPyun YongHyeon }
4435c6b53d8SPyun YongHyeon 
44402ac6454SAndrew Thompson static int
4452fda7967SJustin Hibbits ue_ifmedia_upd(if_t ifp)
44602ac6454SAndrew Thompson {
4472fda7967SJustin Hibbits 	struct usb_ether *ue = if_getsoftc(ifp);
44802ac6454SAndrew Thompson 
44902ac6454SAndrew Thompson 	/* Defer to process context */
45002ac6454SAndrew Thompson 	UE_LOCK(ue);
45102ac6454SAndrew Thompson 	ue_queue_command(ue, ue_ifmedia_task,
45202ac6454SAndrew Thompson 	    &ue->ue_media_task[0].hdr,
45302ac6454SAndrew Thompson 	    &ue->ue_media_task[1].hdr);
45402ac6454SAndrew Thompson 	UE_UNLOCK(ue);
45502ac6454SAndrew Thompson 
45602ac6454SAndrew Thompson 	return (0);
45702ac6454SAndrew Thompson }
45802ac6454SAndrew Thompson 
45902ac6454SAndrew Thompson static void
460760bc48eSAndrew Thompson ue_ifmedia_task(struct usb_proc_msg *_task)
46102ac6454SAndrew Thompson {
462760bc48eSAndrew Thompson 	struct usb_ether_cfg_task *task =
463760bc48eSAndrew Thompson 	    (struct usb_ether_cfg_task *)_task;
464760bc48eSAndrew Thompson 	struct usb_ether *ue = task->ue;
4652fda7967SJustin Hibbits 	if_t ifp = ue->ue_ifp;
46602ac6454SAndrew Thompson 
46702ac6454SAndrew Thompson 	ue->ue_methods->ue_mii_upd(ifp);
46802ac6454SAndrew Thompson }
46902ac6454SAndrew Thompson 
47002ac6454SAndrew Thompson static void
47102ac6454SAndrew Thompson ue_watchdog(void *arg)
47202ac6454SAndrew Thompson {
473760bc48eSAndrew Thompson 	struct usb_ether *ue = arg;
4742fda7967SJustin Hibbits 	if_t ifp = ue->ue_ifp;
47502ac6454SAndrew Thompson 
4762fda7967SJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
47702ac6454SAndrew Thompson 		return;
47802ac6454SAndrew Thompson 
47902ac6454SAndrew Thompson 	ue_queue_command(ue, ue_tick_task,
48002ac6454SAndrew Thompson 	    &ue->ue_tick_task[0].hdr,
48102ac6454SAndrew Thompson 	    &ue->ue_tick_task[1].hdr);
48202ac6454SAndrew Thompson 
483a593f6b8SAndrew Thompson 	usb_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue);
48402ac6454SAndrew Thompson }
48502ac6454SAndrew Thompson 
48602ac6454SAndrew Thompson static void
487760bc48eSAndrew Thompson ue_tick_task(struct usb_proc_msg *_task)
48802ac6454SAndrew Thompson {
489760bc48eSAndrew Thompson 	struct usb_ether_cfg_task *task =
490760bc48eSAndrew Thompson 	    (struct usb_ether_cfg_task *)_task;
491760bc48eSAndrew Thompson 	struct usb_ether *ue = task->ue;
4922fda7967SJustin Hibbits 	if_t ifp = ue->ue_ifp;
49302ac6454SAndrew Thompson 
4942fda7967SJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
49502ac6454SAndrew Thompson 		return;
49602ac6454SAndrew Thompson 
49702ac6454SAndrew Thompson 	ue->ue_methods->ue_tick(ue);
49802ac6454SAndrew Thompson }
49902ac6454SAndrew Thompson 
50002ac6454SAndrew Thompson int
5012fda7967SJustin Hibbits uether_ioctl(if_t ifp, u_long command, caddr_t data)
50202ac6454SAndrew Thompson {
5032fda7967SJustin Hibbits 	struct usb_ether *ue = if_getsoftc(ifp);
50402ac6454SAndrew Thompson 	struct ifreq *ifr = (struct ifreq *)data;
50502ac6454SAndrew Thompson 	struct mii_data *mii;
50602ac6454SAndrew Thompson 	int error = 0;
50702ac6454SAndrew Thompson 
50802ac6454SAndrew Thompson 	switch (command) {
50902ac6454SAndrew Thompson 	case SIOCSIFFLAGS:
51002ac6454SAndrew Thompson 		UE_LOCK(ue);
5112fda7967SJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
5122fda7967SJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
51302ac6454SAndrew Thompson 				ue_queue_command(ue, ue_promisc_task,
51402ac6454SAndrew Thompson 				    &ue->ue_promisc_task[0].hdr,
51502ac6454SAndrew Thompson 				    &ue->ue_promisc_task[1].hdr);
51602ac6454SAndrew Thompson 			else
51702ac6454SAndrew Thompson 				ue_queue_command(ue, ue_start_task,
51802ac6454SAndrew Thompson 				    &ue->ue_sync_task[0].hdr,
51902ac6454SAndrew Thompson 				    &ue->ue_sync_task[1].hdr);
52002ac6454SAndrew Thompson 		} else {
52102ac6454SAndrew Thompson 			ue_queue_command(ue, ue_stop_task,
52202ac6454SAndrew Thompson 			    &ue->ue_sync_task[0].hdr,
52302ac6454SAndrew Thompson 			    &ue->ue_sync_task[1].hdr);
52402ac6454SAndrew Thompson 		}
52502ac6454SAndrew Thompson 		UE_UNLOCK(ue);
52602ac6454SAndrew Thompson 		break;
52702ac6454SAndrew Thompson 	case SIOCADDMULTI:
52802ac6454SAndrew Thompson 	case SIOCDELMULTI:
52902ac6454SAndrew Thompson 		UE_LOCK(ue);
53002ac6454SAndrew Thompson 		ue_queue_command(ue, ue_setmulti_task,
53102ac6454SAndrew Thompson 		    &ue->ue_multi_task[0].hdr,
53202ac6454SAndrew Thompson 		    &ue->ue_multi_task[1].hdr);
53302ac6454SAndrew Thompson 		UE_UNLOCK(ue);
53402ac6454SAndrew Thompson 		break;
53502ac6454SAndrew Thompson 	case SIOCGIFMEDIA:
53602ac6454SAndrew Thompson 	case SIOCSIFMEDIA:
53702ac6454SAndrew Thompson 		if (ue->ue_miibus != NULL) {
53802ac6454SAndrew Thompson 			mii = device_get_softc(ue->ue_miibus);
53902ac6454SAndrew Thompson 			error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
54002ac6454SAndrew Thompson 		} else
54102ac6454SAndrew Thompson 			error = ether_ioctl(ifp, command, data);
54202ac6454SAndrew Thompson 		break;
54302ac6454SAndrew Thompson 	default:
54402ac6454SAndrew Thompson 		error = ether_ioctl(ifp, command, data);
54502ac6454SAndrew Thompson 		break;
54602ac6454SAndrew Thompson 	}
54702ac6454SAndrew Thompson 	return (error);
54802ac6454SAndrew Thompson }
54902ac6454SAndrew Thompson 
55002ac6454SAndrew Thompson static int
551a593f6b8SAndrew Thompson uether_modevent(module_t mod, int type, void *data)
55202ac6454SAndrew Thompson {
55302ac6454SAndrew Thompson 
55402ac6454SAndrew Thompson 	switch (type) {
55502ac6454SAndrew Thompson 	case MOD_LOAD:
55602ac6454SAndrew Thompson 		ueunit = new_unrhdr(0, INT_MAX, NULL);
55702ac6454SAndrew Thompson 		break;
55802ac6454SAndrew Thompson 	case MOD_UNLOAD:
55902ac6454SAndrew Thompson 		break;
56002ac6454SAndrew Thompson 	default:
56102ac6454SAndrew Thompson 		return (EOPNOTSUPP);
56202ac6454SAndrew Thompson 	}
56302ac6454SAndrew Thompson 	return (0);
56402ac6454SAndrew Thompson }
565a593f6b8SAndrew Thompson static moduledata_t uether_mod = {
56602ac6454SAndrew Thompson 	"uether",
567a593f6b8SAndrew Thompson 	uether_modevent,
5689823d527SKevin Lo 	0
56902ac6454SAndrew Thompson };
57002ac6454SAndrew Thompson 
57175fd0939SAndrew Thompson struct mbuf *
572a593f6b8SAndrew Thompson uether_newbuf(void)
57375fd0939SAndrew Thompson {
57475fd0939SAndrew Thompson 	struct mbuf *m_new;
57575fd0939SAndrew Thompson 
576c6499eccSGleb Smirnoff 	m_new = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
57775fd0939SAndrew Thompson 	if (m_new == NULL)
57875fd0939SAndrew Thompson 		return (NULL);
57975fd0939SAndrew Thompson 	m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
58075fd0939SAndrew Thompson 
58175fd0939SAndrew Thompson 	m_adj(m_new, ETHER_ALIGN);
58275fd0939SAndrew Thompson 	return (m_new);
58375fd0939SAndrew Thompson }
58475fd0939SAndrew Thompson 
58502ac6454SAndrew Thompson int
586a593f6b8SAndrew Thompson uether_rxmbuf(struct usb_ether *ue, struct mbuf *m,
58762d42655SHans Petter Selasky     unsigned len)
58802ac6454SAndrew Thompson {
5892fda7967SJustin Hibbits 	if_t ifp = ue->ue_ifp;
59002ac6454SAndrew Thompson 
59102ac6454SAndrew Thompson 	UE_LOCK_ASSERT(ue, MA_OWNED);
59202ac6454SAndrew Thompson 
59302ac6454SAndrew Thompson 	/* finalize mbuf */
594ecc70d3fSGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
59502ac6454SAndrew Thompson 	m->m_pkthdr.rcvif = ifp;
59602ac6454SAndrew Thompson 	m->m_pkthdr.len = m->m_len = len;
59702ac6454SAndrew Thompson 
59802ac6454SAndrew Thompson 	/* enqueue for later when the lock can be released */
59935d3dd8bSGleb Smirnoff 	(void)mbufq_enqueue(&ue->ue_rxq, m);
60002ac6454SAndrew Thompson 	return (0);
60102ac6454SAndrew Thompson }
60202ac6454SAndrew Thompson 
60302ac6454SAndrew Thompson int
604a593f6b8SAndrew Thompson uether_rxbuf(struct usb_ether *ue, struct usb_page_cache *pc,
60562d42655SHans Petter Selasky     unsigned offset, unsigned len)
60602ac6454SAndrew Thompson {
6072fda7967SJustin Hibbits 	if_t ifp = ue->ue_ifp;
60802ac6454SAndrew Thompson 	struct mbuf *m;
60902ac6454SAndrew Thompson 
61002ac6454SAndrew Thompson 	UE_LOCK_ASSERT(ue, MA_OWNED);
61102ac6454SAndrew Thompson 
61275fd0939SAndrew Thompson 	if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN)
61302ac6454SAndrew Thompson 		return (1);
61402ac6454SAndrew Thompson 
615a593f6b8SAndrew Thompson 	m = uether_newbuf();
61602ac6454SAndrew Thompson 	if (m == NULL) {
617ecc70d3fSGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
61802ac6454SAndrew Thompson 		return (ENOMEM);
61902ac6454SAndrew Thompson 	}
62002ac6454SAndrew Thompson 
621a593f6b8SAndrew Thompson 	usbd_copy_out(pc, offset, mtod(m, uint8_t *), len);
62202ac6454SAndrew Thompson 
62302ac6454SAndrew Thompson 	/* finalize mbuf */
624ecc70d3fSGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
62502ac6454SAndrew Thompson 	m->m_pkthdr.rcvif = ifp;
62602ac6454SAndrew Thompson 	m->m_pkthdr.len = m->m_len = len;
62702ac6454SAndrew Thompson 
62802ac6454SAndrew Thompson 	/* enqueue for later when the lock can be released */
62935d3dd8bSGleb Smirnoff 	(void)mbufq_enqueue(&ue->ue_rxq, m);
63002ac6454SAndrew Thompson 	return (0);
63102ac6454SAndrew Thompson }
63202ac6454SAndrew Thompson 
63302ac6454SAndrew Thompson void
634a593f6b8SAndrew Thompson uether_rxflush(struct usb_ether *ue)
63502ac6454SAndrew Thompson {
6362fda7967SJustin Hibbits 	if_t ifp = ue->ue_ifp;
637b8a6e03fSGleb Smirnoff 	struct epoch_tracker et;
638b8a6e03fSGleb Smirnoff 	struct mbuf *m, *n;
63902ac6454SAndrew Thompson 
64002ac6454SAndrew Thompson 	UE_LOCK_ASSERT(ue, MA_OWNED);
64102ac6454SAndrew Thompson 
642b8a6e03fSGleb Smirnoff 	n = mbufq_flush(&ue->ue_rxq);
64302ac6454SAndrew Thompson 	UE_UNLOCK(ue);
644b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
645b8a6e03fSGleb Smirnoff 	while ((m = n) != NULL) {
646b8a6e03fSGleb Smirnoff 		n = STAILQ_NEXT(m, m_stailqpkt);
647b8a6e03fSGleb Smirnoff 		m->m_nextpkt = NULL;
6482fda7967SJustin Hibbits 		if_input(ifp, m);
64902ac6454SAndrew Thompson 	}
650b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
651b8a6e03fSGleb Smirnoff 	UE_LOCK(ue);
65202ac6454SAndrew Thompson }
65302ac6454SAndrew Thompson 
65489856f7eSBjoern A. Zeeb /*
65589856f7eSBjoern A. Zeeb  * USB net drivers are run by DRIVER_MODULE() thus SI_SUB_DRIVERS,
65689856f7eSBjoern A. Zeeb  * SI_ORDER_MIDDLE.  Run uether after that.
65789856f7eSBjoern A. Zeeb  */
65889856f7eSBjoern A. Zeeb DECLARE_MODULE(uether, uether_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
65902ac6454SAndrew Thompson MODULE_VERSION(uether, 1);
660