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
uether_pause(struct usb_ether * ue,unsigned _ticks)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
ue_queue_command(struct usb_ether * ue,usb_proc_callback_t * fn,struct usb_proc_msg * t0,struct usb_proc_msg * t1)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
uether_getifp(struct usb_ether * ue)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 *
uether_getmii(struct usb_ether * ue)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 *
uether_getsc(struct usb_ether * ue)144a593f6b8SAndrew Thompson uether_getsc(struct usb_ether *ue)
14502ac6454SAndrew Thompson {
14602ac6454SAndrew Thompson return (ue->ue_sc);
14702ac6454SAndrew Thompson }
14802ac6454SAndrew Thompson
14902ac6454SAndrew Thompson static int
ue_sysctl_parent(SYSCTL_HANDLER_ARGS)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
uether_ifattach(struct usb_ether * ue)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
uether_ifattach_wait(struct usb_ether * ue)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
ue_attach_post_task(struct usb_proc_msg * _task)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
uether_ifdetach(struct usb_ether * ue)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();
31911a91178SJohn 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
uether_is_gone(struct usb_ether * ue)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
uether_init(void * arg)3465c6b53d8SPyun YongHyeon uether_init(void *arg)
3475c6b53d8SPyun YongHyeon {
3485c6b53d8SPyun YongHyeon
3495c6b53d8SPyun YongHyeon ue_init(arg);
3505c6b53d8SPyun YongHyeon }
3515c6b53d8SPyun YongHyeon
35202ac6454SAndrew Thompson static void
ue_init(void * arg)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
ue_start_task(struct usb_proc_msg * _task)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
ue_stop_task(struct usb_proc_msg * _task)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
uether_start(if_t ifp)3982fda7967SJustin Hibbits uether_start(if_t ifp)
3995c6b53d8SPyun YongHyeon {
4005c6b53d8SPyun YongHyeon
4015c6b53d8SPyun YongHyeon ue_start(ifp);
4025c6b53d8SPyun YongHyeon }
4035c6b53d8SPyun YongHyeon
40402ac6454SAndrew Thompson static void
ue_start(if_t ifp)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
ue_promisc_task(struct usb_proc_msg * _task)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
ue_setmulti_task(struct usb_proc_msg * _task)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
uether_ifmedia_upd(if_t ifp)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
ue_ifmedia_upd(if_t ifp)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
ue_ifmedia_task(struct usb_proc_msg * _task)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
ue_watchdog(void * arg)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
ue_tick_task(struct usb_proc_msg * _task)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
uether_ioctl(if_t ifp,u_long command,caddr_t data)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
uether_modevent(module_t mod,int type,void * data)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 *
uether_newbuf(void)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
uether_rxmbuf(struct usb_ether * ue,struct mbuf * m,unsigned len)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;
596*40ad87e9SKyle Evans if (len != 0) {
597*40ad87e9SKyle Evans /*
598*40ad87e9SKyle Evans * This is going to get it wrong for an mbuf chain, so let's
599*40ad87e9SKyle Evans * make sure we're not doing that.
600*40ad87e9SKyle Evans */
601*40ad87e9SKyle Evans MPASS(m->m_next == NULL);
60202ac6454SAndrew Thompson m->m_pkthdr.len = m->m_len = len;
603*40ad87e9SKyle Evans }
60402ac6454SAndrew Thompson
60502ac6454SAndrew Thompson /* enqueue for later when the lock can be released */
60635d3dd8bSGleb Smirnoff (void)mbufq_enqueue(&ue->ue_rxq, m);
60702ac6454SAndrew Thompson return (0);
60802ac6454SAndrew Thompson }
60902ac6454SAndrew Thompson
61002ac6454SAndrew Thompson int
uether_rxbuf(struct usb_ether * ue,struct usb_page_cache * pc,unsigned offset,unsigned len)611a593f6b8SAndrew Thompson uether_rxbuf(struct usb_ether *ue, struct usb_page_cache *pc,
61262d42655SHans Petter Selasky unsigned offset, unsigned len)
61302ac6454SAndrew Thompson {
6142fda7967SJustin Hibbits if_t ifp = ue->ue_ifp;
61502ac6454SAndrew Thompson struct mbuf *m;
61602ac6454SAndrew Thompson
61702ac6454SAndrew Thompson UE_LOCK_ASSERT(ue, MA_OWNED);
61802ac6454SAndrew Thompson
61975fd0939SAndrew Thompson if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN)
62002ac6454SAndrew Thompson return (1);
62102ac6454SAndrew Thompson
622a593f6b8SAndrew Thompson m = uether_newbuf();
62302ac6454SAndrew Thompson if (m == NULL) {
624ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
62502ac6454SAndrew Thompson return (ENOMEM);
62602ac6454SAndrew Thompson }
62702ac6454SAndrew Thompson
628a593f6b8SAndrew Thompson usbd_copy_out(pc, offset, mtod(m, uint8_t *), len);
62902ac6454SAndrew Thompson
63002ac6454SAndrew Thompson /* finalize mbuf */
631ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
63202ac6454SAndrew Thompson m->m_pkthdr.rcvif = ifp;
63302ac6454SAndrew Thompson m->m_pkthdr.len = m->m_len = len;
63402ac6454SAndrew Thompson
63502ac6454SAndrew Thompson /* enqueue for later when the lock can be released */
63635d3dd8bSGleb Smirnoff (void)mbufq_enqueue(&ue->ue_rxq, m);
63702ac6454SAndrew Thompson return (0);
63802ac6454SAndrew Thompson }
63902ac6454SAndrew Thompson
64002ac6454SAndrew Thompson void
uether_rxflush(struct usb_ether * ue)641a593f6b8SAndrew Thompson uether_rxflush(struct usb_ether *ue)
64202ac6454SAndrew Thompson {
6432fda7967SJustin Hibbits if_t ifp = ue->ue_ifp;
644b8a6e03fSGleb Smirnoff struct epoch_tracker et;
645b8a6e03fSGleb Smirnoff struct mbuf *m, *n;
64602ac6454SAndrew Thompson
64702ac6454SAndrew Thompson UE_LOCK_ASSERT(ue, MA_OWNED);
64802ac6454SAndrew Thompson
649b8a6e03fSGleb Smirnoff n = mbufq_flush(&ue->ue_rxq);
65002ac6454SAndrew Thompson UE_UNLOCK(ue);
651b8a6e03fSGleb Smirnoff NET_EPOCH_ENTER(et);
652b8a6e03fSGleb Smirnoff while ((m = n) != NULL) {
653b8a6e03fSGleb Smirnoff n = STAILQ_NEXT(m, m_stailqpkt);
654b8a6e03fSGleb Smirnoff m->m_nextpkt = NULL;
6552fda7967SJustin Hibbits if_input(ifp, m);
65602ac6454SAndrew Thompson }
657b8a6e03fSGleb Smirnoff NET_EPOCH_EXIT(et);
658b8a6e03fSGleb Smirnoff UE_LOCK(ue);
65902ac6454SAndrew Thompson }
66002ac6454SAndrew Thompson
66189856f7eSBjoern A. Zeeb /*
66289856f7eSBjoern A. Zeeb * USB net drivers are run by DRIVER_MODULE() thus SI_SUB_DRIVERS,
66389856f7eSBjoern A. Zeeb * SI_ORDER_MIDDLE. Run uether after that.
66489856f7eSBjoern A. Zeeb */
66589856f7eSBjoern A. Zeeb DECLARE_MODULE(uether, uether_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
66602ac6454SAndrew Thompson MODULE_VERSION(uether, 1);
667