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