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