usb_ethernet.c revision 193045
1184610Salfred/* $FreeBSD: head/sys/dev/usb/net/usb_ethernet.c 193045 2009-05-29 18:46:57Z thompsa $ */ 2184610Salfred/*- 3188412Sthompsa * Copyright (c) 2009 Andrew Thompson (thompsa@FreeBSD.org) 4184610Salfred * 5184610Salfred * Redistribution and use in source and binary forms, with or without 6184610Salfred * modification, are permitted provided that the following conditions 7184610Salfred * are met: 8184610Salfred * 1. Redistributions of source code must retain the above copyright 9184610Salfred * notice, this list of conditions and the following disclaimer. 10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 11184610Salfred * notice, this list of conditions and the following disclaimer in the 12184610Salfred * documentation and/or other materials provided with the distribution. 13184610Salfred * 14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184610Salfred * SUCH DAMAGE. 25184610Salfred */ 26184610Salfred 27188942Sthompsa#include <dev/usb/usb_mfunc.h> 28188942Sthompsa#include <dev/usb/usb_error.h> 29188942Sthompsa#include <dev/usb/usb_endian.h> 30188942Sthompsa#include <dev/usb/usb.h> 31188412Sthompsa 32188942Sthompsa#include <dev/usb/usb_core.h> 33188942Sthompsa#include <dev/usb/usb_process.h> 34188942Sthompsa#include <dev/usb/usb_busdma.h> 35188942Sthompsa#include <dev/usb/usb_request.h> 36188942Sthompsa#include <dev/usb/usb_util.h> 37188412Sthompsa 38188942Sthompsa#include <dev/usb/net/usb_ethernet.h> 39184610Salfred 40188412SthompsaSYSCTL_NODE(_net, OID_AUTO, ue, CTLFLAG_RD, 0, "USB Ethernet parameters"); 41184610Salfred 42188412Sthompsa#define UE_LOCK(_ue) mtx_lock((_ue)->ue_mtx) 43188412Sthompsa#define UE_UNLOCK(_ue) mtx_unlock((_ue)->ue_mtx) 44188412Sthompsa#define UE_LOCK_ASSERT(_ue, t) mtx_assert((_ue)->ue_mtx, t) 45188412Sthompsa 46188942SthompsaMODULE_DEPEND(uether, usb, 1, 1, 1); 47188942SthompsaMODULE_DEPEND(uether, miibus, 1, 1, 1); 48188552Sthompsa 49188412Sthompsastatic struct unrhdr *ueunit; 50188412Sthompsa 51193045Sthompsastatic usb_proc_callback_t ue_attach_post_task; 52193045Sthompsastatic usb_proc_callback_t ue_promisc_task; 53193045Sthompsastatic usb_proc_callback_t ue_setmulti_task; 54193045Sthompsastatic usb_proc_callback_t ue_ifmedia_task; 55193045Sthompsastatic usb_proc_callback_t ue_tick_task; 56193045Sthompsastatic usb_proc_callback_t ue_start_task; 57193045Sthompsastatic usb_proc_callback_t ue_stop_task; 58188412Sthompsa 59188412Sthompsastatic void ue_init(void *); 60188412Sthompsastatic void ue_start(struct ifnet *); 61188412Sthompsastatic int ue_ifmedia_upd(struct ifnet *); 62188412Sthompsastatic void ue_watchdog(void *); 63188412Sthompsa 64188412Sthompsa/* 65188412Sthompsa * Return values: 66188412Sthompsa * 0: success 67188412Sthompsa * Else: device has been detached 68188412Sthompsa */ 69188412Sthompsauint8_t 70192984Sthompsausb2_ether_pause(struct usb_ether *ue, unsigned int _ticks) 71184610Salfred{ 72188412Sthompsa if (usb2_proc_is_gone(&ue->ue_tq)) { 73188412Sthompsa /* nothing to do */ 74188412Sthompsa return (1); 75188412Sthompsa } 76188412Sthompsa usb2_pause_mtx(ue->ue_mtx, _ticks); 77188412Sthompsa return (0); 78188412Sthompsa} 79184610Salfred 80188412Sthompsastatic void 81192984Sthompsaue_queue_command(struct usb_ether *ue, 82193045Sthompsa usb_proc_callback_t *fn, 83192984Sthompsa struct usb_proc_msg *t0, struct usb_proc_msg *t1) 84188412Sthompsa{ 85192984Sthompsa struct usb_ether_cfg_task *task; 86188412Sthompsa 87188412Sthompsa UE_LOCK_ASSERT(ue, MA_OWNED); 88188412Sthompsa 89188412Sthompsa if (usb2_proc_is_gone(&ue->ue_tq)) { 90188412Sthompsa return; /* nothing to do */ 91184610Salfred } 92188412Sthompsa /* 93188412Sthompsa * NOTE: The task cannot get executed before we drop the 94188412Sthompsa * "sc_mtx" mutex. It is safe to update fields in the message 95188412Sthompsa * structure after that the message got queued. 96188412Sthompsa */ 97192984Sthompsa task = (struct usb_ether_cfg_task *) 98188412Sthompsa usb2_proc_msignal(&ue->ue_tq, t0, t1); 99188412Sthompsa 100188412Sthompsa /* Setup callback and self pointers */ 101188412Sthompsa task->hdr.pm_callback = fn; 102188412Sthompsa task->ue = ue; 103188412Sthompsa 104188412Sthompsa /* 105188412Sthompsa * Start and stop must be synchronous! 106188412Sthompsa */ 107188412Sthompsa if ((fn == ue_start_task) || (fn == ue_stop_task)) 108188412Sthompsa usb2_proc_mwait(&ue->ue_tq, t0, t1); 109184610Salfred} 110184610Salfred 111188412Sthompsastruct ifnet * 112192984Sthompsausb2_ether_getifp(struct usb_ether *ue) 113184610Salfred{ 114188412Sthompsa return (ue->ue_ifp); 115188412Sthompsa} 116184610Salfred 117188412Sthompsastruct mii_data * 118192984Sthompsausb2_ether_getmii(struct usb_ether *ue) 119188412Sthompsa{ 120188412Sthompsa return (device_get_softc(ue->ue_miibus)); 121188412Sthompsa} 122188412Sthompsa 123188412Sthompsavoid * 124192984Sthompsausb2_ether_getsc(struct usb_ether *ue) 125188412Sthompsa{ 126188412Sthompsa return (ue->ue_sc); 127188412Sthompsa} 128188412Sthompsa 129188412Sthompsastatic int 130188412Sthompsaue_sysctl_parent(SYSCTL_HANDLER_ARGS) 131188412Sthompsa{ 132192984Sthompsa struct usb_ether *ue = arg1; 133188412Sthompsa const char *name; 134188412Sthompsa 135188412Sthompsa name = device_get_nameunit(ue->ue_dev); 136188412Sthompsa return SYSCTL_OUT(req, name, strlen(name)); 137188412Sthompsa} 138188412Sthompsa 139188412Sthompsaint 140192984Sthompsausb2_ether_ifattach(struct usb_ether *ue) 141188412Sthompsa{ 142188412Sthompsa int error; 143188412Sthompsa 144188412Sthompsa /* check some critical parameters */ 145188412Sthompsa if ((ue->ue_dev == NULL) || 146188412Sthompsa (ue->ue_udev == NULL) || 147188412Sthompsa (ue->ue_mtx == NULL) || 148188412Sthompsa (ue->ue_methods == NULL)) 149188412Sthompsa return (EINVAL); 150188412Sthompsa 151188412Sthompsa error = usb2_proc_create(&ue->ue_tq, ue->ue_mtx, 152188412Sthompsa device_get_nameunit(ue->ue_dev), USB_PRI_MED); 153188412Sthompsa if (error) { 154188412Sthompsa device_printf(ue->ue_dev, "could not setup taskqueue\n"); 155188412Sthompsa goto error; 156188412Sthompsa } 157188412Sthompsa 158188412Sthompsa /* fork rest of the attach code */ 159188412Sthompsa UE_LOCK(ue); 160188412Sthompsa ue_queue_command(ue, ue_attach_post_task, 161188412Sthompsa &ue->ue_sync_task[0].hdr, 162188412Sthompsa &ue->ue_sync_task[1].hdr); 163188412Sthompsa UE_UNLOCK(ue); 164188412Sthompsa 165188412Sthompsaerror: 166188412Sthompsa return (error); 167188412Sthompsa} 168188412Sthompsa 169188412Sthompsastatic void 170192984Sthompsaue_attach_post_task(struct usb_proc_msg *_task) 171188412Sthompsa{ 172192984Sthompsa struct usb_ether_cfg_task *task = 173192984Sthompsa (struct usb_ether_cfg_task *)_task; 174192984Sthompsa struct usb_ether *ue = task->ue; 175188412Sthompsa struct ifnet *ifp; 176188412Sthompsa int error; 177188412Sthompsa char num[14]; /* sufficient for 32 bits */ 178188412Sthompsa 179188412Sthompsa /* first call driver's post attach routine */ 180188412Sthompsa ue->ue_methods->ue_attach_post(ue); 181188412Sthompsa 182188412Sthompsa UE_UNLOCK(ue); 183188412Sthompsa 184188412Sthompsa ue->ue_unit = alloc_unr(ueunit); 185188412Sthompsa usb2_callout_init_mtx(&ue->ue_watchdog, ue->ue_mtx, 0); 186188412Sthompsa sysctl_ctx_init(&ue->ue_sysctl_ctx); 187188412Sthompsa 188188412Sthompsa ifp = if_alloc(IFT_ETHER); 189184610Salfred if (ifp == NULL) { 190188412Sthompsa device_printf(ue->ue_dev, "could not allocate ifnet\n"); 191188412Sthompsa goto error; 192184610Salfred } 193184610Salfred 194188412Sthompsa ifp->if_softc = ue; 195188412Sthompsa if_initname(ifp, "ue", ue->ue_unit); 196188412Sthompsa ifp->if_mtu = ETHERMTU; 197188412Sthompsa ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 198188412Sthompsa if (ue->ue_methods->ue_ioctl != NULL) 199188412Sthompsa ifp->if_ioctl = ue->ue_methods->ue_ioctl; 200188412Sthompsa else 201188412Sthompsa ifp->if_ioctl = usb2_ether_ioctl; 202188412Sthompsa ifp->if_start = ue_start; 203188412Sthompsa ifp->if_init = ue_init; 204188412Sthompsa IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 205188412Sthompsa ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; 206188412Sthompsa IFQ_SET_READY(&ifp->if_snd); 207188412Sthompsa ue->ue_ifp = ifp; 208184610Salfred 209188412Sthompsa if (ue->ue_methods->ue_mii_upd != NULL && 210188412Sthompsa ue->ue_methods->ue_mii_sts != NULL) { 211188412Sthompsa mtx_lock(&Giant); /* device_xxx() depends on this */ 212188412Sthompsa error = mii_phy_probe(ue->ue_dev, &ue->ue_miibus, 213188412Sthompsa ue_ifmedia_upd, ue->ue_methods->ue_mii_sts); 214188412Sthompsa mtx_unlock(&Giant); 215188412Sthompsa if (error) { 216188412Sthompsa device_printf(ue->ue_dev, "MII without any PHY\n"); 217188412Sthompsa goto error; 218188412Sthompsa } 219188412Sthompsa } 220184610Salfred 221188412Sthompsa if_printf(ifp, "<USB Ethernet> on %s\n", device_get_nameunit(ue->ue_dev)); 222188412Sthompsa ether_ifattach(ifp, ue->ue_eaddr); 223188412Sthompsa 224188412Sthompsa snprintf(num, sizeof(num), "%u", ue->ue_unit); 225188412Sthompsa ue->ue_sysctl_oid = SYSCTL_ADD_NODE(&ue->ue_sysctl_ctx, 226188412Sthompsa &SYSCTL_NODE_CHILDREN(_net, ue), 227188412Sthompsa OID_AUTO, num, CTLFLAG_RD, NULL, ""); 228188412Sthompsa SYSCTL_ADD_PROC(&ue->ue_sysctl_ctx, 229188412Sthompsa SYSCTL_CHILDREN(ue->ue_sysctl_oid), OID_AUTO, 230188412Sthompsa "%parent", CTLFLAG_RD, ue, 0, 231188412Sthompsa ue_sysctl_parent, "A", "parent device"); 232188412Sthompsa 233188412Sthompsa UE_LOCK(ue); 234188412Sthompsa return; 235188412Sthompsa 236188412Sthompsaerror: 237188412Sthompsa free_unr(ueunit, ue->ue_unit); 238188412Sthompsa if (ue->ue_ifp != NULL) { 239188412Sthompsa if_free(ue->ue_ifp); 240188412Sthompsa ue->ue_ifp = NULL; 241184610Salfred } 242188412Sthompsa UE_LOCK(ue); 243188412Sthompsa return; 244188412Sthompsa} 245184610Salfred 246188412Sthompsavoid 247192984Sthompsausb2_ether_ifdetach(struct usb_ether *ue) 248188412Sthompsa{ 249188412Sthompsa struct ifnet *ifp; 250184610Salfred 251188412Sthompsa /* wait for any post attach or other command to complete */ 252188412Sthompsa usb2_proc_drain(&ue->ue_tq); 253184610Salfred 254188412Sthompsa /* read "ifnet" pointer after taskqueue drain */ 255188412Sthompsa ifp = ue->ue_ifp; 256184610Salfred 257188412Sthompsa if (ifp != NULL) { 258184610Salfred 259188412Sthompsa /* we are not running any more */ 260188412Sthompsa UE_LOCK(ue); 261188412Sthompsa ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 262188412Sthompsa UE_UNLOCK(ue); 263184610Salfred 264188412Sthompsa /* drain any callouts */ 265188412Sthompsa usb2_callout_drain(&ue->ue_watchdog); 266188412Sthompsa 267188412Sthompsa /* detach miibus */ 268188412Sthompsa if (ue->ue_miibus != NULL) { 269188412Sthompsa mtx_lock(&Giant); /* device_xxx() depends on this */ 270188412Sthompsa device_delete_child(ue->ue_dev, ue->ue_miibus); 271188412Sthompsa mtx_unlock(&Giant); 272184610Salfred } 273184610Salfred 274188412Sthompsa /* detach ethernet */ 275188412Sthompsa ether_ifdetach(ifp); 276184610Salfred 277188412Sthompsa /* free interface instance */ 278188412Sthompsa if_free(ifp); 279188412Sthompsa 280188412Sthompsa /* free sysctl */ 281188412Sthompsa sysctl_ctx_free(&ue->ue_sysctl_ctx); 282188412Sthompsa 283188412Sthompsa /* free unit */ 284188412Sthompsa free_unr(ueunit, ue->ue_unit); 285188412Sthompsa } 286188412Sthompsa 287188412Sthompsa /* free taskqueue, if any */ 288188412Sthompsa usb2_proc_free(&ue->ue_tq); 289188412Sthompsa} 290188412Sthompsa 291188412Sthompsauint8_t 292192984Sthompsausb2_ether_is_gone(struct usb_ether *ue) 293188412Sthompsa{ 294188412Sthompsa return (usb2_proc_is_gone(&ue->ue_tq)); 295188412Sthompsa} 296188412Sthompsa 297188412Sthompsastatic void 298188412Sthompsaue_init(void *arg) 299188412Sthompsa{ 300192984Sthompsa struct usb_ether *ue = arg; 301188412Sthompsa 302188412Sthompsa UE_LOCK(ue); 303188412Sthompsa ue_queue_command(ue, ue_start_task, 304188412Sthompsa &ue->ue_sync_task[0].hdr, 305188412Sthompsa &ue->ue_sync_task[1].hdr); 306188412Sthompsa UE_UNLOCK(ue); 307188412Sthompsa} 308188412Sthompsa 309188412Sthompsastatic void 310192984Sthompsaue_start_task(struct usb_proc_msg *_task) 311188412Sthompsa{ 312192984Sthompsa struct usb_ether_cfg_task *task = 313192984Sthompsa (struct usb_ether_cfg_task *)_task; 314192984Sthompsa struct usb_ether *ue = task->ue; 315188412Sthompsa struct ifnet *ifp = ue->ue_ifp; 316188412Sthompsa 317188412Sthompsa UE_LOCK_ASSERT(ue, MA_OWNED); 318188412Sthompsa 319188412Sthompsa ue->ue_methods->ue_init(ue); 320188412Sthompsa 321188412Sthompsa if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 322188412Sthompsa return; 323188412Sthompsa 324188412Sthompsa if (ue->ue_methods->ue_tick != NULL) 325188412Sthompsa usb2_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue); 326188412Sthompsa} 327188412Sthompsa 328188412Sthompsastatic void 329192984Sthompsaue_stop_task(struct usb_proc_msg *_task) 330188412Sthompsa{ 331192984Sthompsa struct usb_ether_cfg_task *task = 332192984Sthompsa (struct usb_ether_cfg_task *)_task; 333192984Sthompsa struct usb_ether *ue = task->ue; 334188412Sthompsa 335188412Sthompsa UE_LOCK_ASSERT(ue, MA_OWNED); 336188412Sthompsa 337188412Sthompsa usb2_callout_stop(&ue->ue_watchdog); 338188412Sthompsa 339188412Sthompsa ue->ue_methods->ue_stop(ue); 340188412Sthompsa} 341188412Sthompsa 342188412Sthompsastatic void 343188412Sthompsaue_start(struct ifnet *ifp) 344188412Sthompsa{ 345192984Sthompsa struct usb_ether *ue = ifp->if_softc; 346188412Sthompsa 347188412Sthompsa if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 348188412Sthompsa return; 349188412Sthompsa 350188412Sthompsa UE_LOCK(ue); 351188412Sthompsa ue->ue_methods->ue_start(ue); 352188412Sthompsa UE_UNLOCK(ue); 353188412Sthompsa} 354188412Sthompsa 355188412Sthompsastatic void 356192984Sthompsaue_promisc_task(struct usb_proc_msg *_task) 357188412Sthompsa{ 358192984Sthompsa struct usb_ether_cfg_task *task = 359192984Sthompsa (struct usb_ether_cfg_task *)_task; 360192984Sthompsa struct usb_ether *ue = task->ue; 361188412Sthompsa 362188412Sthompsa ue->ue_methods->ue_setpromisc(ue); 363188412Sthompsa} 364188412Sthompsa 365188412Sthompsastatic void 366192984Sthompsaue_setmulti_task(struct usb_proc_msg *_task) 367188412Sthompsa{ 368192984Sthompsa struct usb_ether_cfg_task *task = 369192984Sthompsa (struct usb_ether_cfg_task *)_task; 370192984Sthompsa struct usb_ether *ue = task->ue; 371188412Sthompsa 372188412Sthompsa ue->ue_methods->ue_setmulti(ue); 373188412Sthompsa} 374188412Sthompsa 375188412Sthompsastatic int 376188412Sthompsaue_ifmedia_upd(struct ifnet *ifp) 377188412Sthompsa{ 378192984Sthompsa struct usb_ether *ue = ifp->if_softc; 379188412Sthompsa 380188412Sthompsa /* Defer to process context */ 381188412Sthompsa UE_LOCK(ue); 382188412Sthompsa ue_queue_command(ue, ue_ifmedia_task, 383188412Sthompsa &ue->ue_media_task[0].hdr, 384188412Sthompsa &ue->ue_media_task[1].hdr); 385188412Sthompsa UE_UNLOCK(ue); 386188412Sthompsa 387188412Sthompsa return (0); 388188412Sthompsa} 389188412Sthompsa 390188412Sthompsastatic void 391192984Sthompsaue_ifmedia_task(struct usb_proc_msg *_task) 392188412Sthompsa{ 393192984Sthompsa struct usb_ether_cfg_task *task = 394192984Sthompsa (struct usb_ether_cfg_task *)_task; 395192984Sthompsa struct usb_ether *ue = task->ue; 396188412Sthompsa struct ifnet *ifp = ue->ue_ifp; 397188412Sthompsa 398188412Sthompsa ue->ue_methods->ue_mii_upd(ifp); 399188412Sthompsa} 400188412Sthompsa 401188412Sthompsastatic void 402188412Sthompsaue_watchdog(void *arg) 403188412Sthompsa{ 404192984Sthompsa struct usb_ether *ue = arg; 405188412Sthompsa struct ifnet *ifp = ue->ue_ifp; 406188412Sthompsa 407188412Sthompsa if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 408188412Sthompsa return; 409188412Sthompsa 410188412Sthompsa ue_queue_command(ue, ue_tick_task, 411188412Sthompsa &ue->ue_tick_task[0].hdr, 412188412Sthompsa &ue->ue_tick_task[1].hdr); 413188412Sthompsa 414188412Sthompsa usb2_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue); 415188412Sthompsa} 416188412Sthompsa 417188412Sthompsastatic void 418192984Sthompsaue_tick_task(struct usb_proc_msg *_task) 419188412Sthompsa{ 420192984Sthompsa struct usb_ether_cfg_task *task = 421192984Sthompsa (struct usb_ether_cfg_task *)_task; 422192984Sthompsa struct usb_ether *ue = task->ue; 423188412Sthompsa struct ifnet *ifp = ue->ue_ifp; 424188412Sthompsa 425188412Sthompsa if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 426188412Sthompsa return; 427188412Sthompsa 428188412Sthompsa ue->ue_methods->ue_tick(ue); 429188412Sthompsa} 430188412Sthompsa 431188412Sthompsaint 432188412Sthompsausb2_ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 433188412Sthompsa{ 434192984Sthompsa struct usb_ether *ue = ifp->if_softc; 435188412Sthompsa struct ifreq *ifr = (struct ifreq *)data; 436188412Sthompsa struct mii_data *mii; 437188412Sthompsa int error = 0; 438188412Sthompsa 439188412Sthompsa switch (command) { 440188412Sthompsa case SIOCSIFFLAGS: 441188412Sthompsa UE_LOCK(ue); 442188412Sthompsa if (ifp->if_flags & IFF_UP) { 443188412Sthompsa if (ifp->if_drv_flags & IFF_DRV_RUNNING) 444188412Sthompsa ue_queue_command(ue, ue_promisc_task, 445188412Sthompsa &ue->ue_promisc_task[0].hdr, 446188412Sthompsa &ue->ue_promisc_task[1].hdr); 447188412Sthompsa else 448188412Sthompsa ue_queue_command(ue, ue_start_task, 449188412Sthompsa &ue->ue_sync_task[0].hdr, 450188412Sthompsa &ue->ue_sync_task[1].hdr); 451188412Sthompsa } else { 452188412Sthompsa ue_queue_command(ue, ue_stop_task, 453188412Sthompsa &ue->ue_sync_task[0].hdr, 454188412Sthompsa &ue->ue_sync_task[1].hdr); 455184610Salfred } 456188412Sthompsa UE_UNLOCK(ue); 457188412Sthompsa break; 458188412Sthompsa case SIOCADDMULTI: 459188412Sthompsa case SIOCDELMULTI: 460188412Sthompsa UE_LOCK(ue); 461188412Sthompsa ue_queue_command(ue, ue_setmulti_task, 462188412Sthompsa &ue->ue_multi_task[0].hdr, 463188412Sthompsa &ue->ue_multi_task[1].hdr); 464188412Sthompsa UE_UNLOCK(ue); 465188412Sthompsa break; 466188412Sthompsa case SIOCGIFMEDIA: 467188412Sthompsa case SIOCSIFMEDIA: 468188412Sthompsa if (ue->ue_miibus != NULL) { 469188412Sthompsa mii = device_get_softc(ue->ue_miibus); 470188412Sthompsa error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 471188412Sthompsa } else 472188412Sthompsa error = ether_ioctl(ifp, command, data); 473188412Sthompsa break; 474188412Sthompsa default: 475188412Sthompsa error = ether_ioctl(ifp, command, data); 476188412Sthompsa break; 477184610Salfred } 478188412Sthompsa return (error); 479184610Salfred} 480188412Sthompsa 481188412Sthompsastatic int 482188412Sthompsausb2_ether_modevent(module_t mod, int type, void *data) 483188412Sthompsa{ 484188412Sthompsa 485188412Sthompsa switch (type) { 486188412Sthompsa case MOD_LOAD: 487188412Sthompsa ueunit = new_unrhdr(0, INT_MAX, NULL); 488188412Sthompsa break; 489188412Sthompsa case MOD_UNLOAD: 490188412Sthompsa break; 491188412Sthompsa default: 492188412Sthompsa return (EOPNOTSUPP); 493188412Sthompsa } 494188412Sthompsa return (0); 495188412Sthompsa} 496188412Sthompsastatic moduledata_t usb2_ether_mod = { 497188942Sthompsa "uether", 498188412Sthompsa usb2_ether_modevent, 499188412Sthompsa 0 500188412Sthompsa}; 501188412Sthompsa 502189528Sthompsastruct mbuf * 503189528Sthompsausb2_ether_newbuf(void) 504189528Sthompsa{ 505189528Sthompsa struct mbuf *m_new; 506189528Sthompsa 507189528Sthompsa m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 508189528Sthompsa if (m_new == NULL) 509189528Sthompsa return (NULL); 510189528Sthompsa m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 511189528Sthompsa 512189528Sthompsa m_adj(m_new, ETHER_ALIGN); 513189528Sthompsa return (m_new); 514189528Sthompsa} 515189528Sthompsa 516188412Sthompsaint 517192984Sthompsausb2_ether_rxmbuf(struct usb_ether *ue, struct mbuf *m, 518188412Sthompsa unsigned int len) 519188412Sthompsa{ 520188412Sthompsa struct ifnet *ifp = ue->ue_ifp; 521188412Sthompsa 522188412Sthompsa UE_LOCK_ASSERT(ue, MA_OWNED); 523188412Sthompsa 524188412Sthompsa /* finalize mbuf */ 525188412Sthompsa ifp->if_ipackets++; 526188412Sthompsa m->m_pkthdr.rcvif = ifp; 527188412Sthompsa m->m_pkthdr.len = m->m_len = len; 528188412Sthompsa 529188412Sthompsa /* enqueue for later when the lock can be released */ 530188412Sthompsa _IF_ENQUEUE(&ue->ue_rxq, m); 531188412Sthompsa return (0); 532188412Sthompsa} 533188412Sthompsa 534188412Sthompsaint 535192984Sthompsausb2_ether_rxbuf(struct usb_ether *ue, struct usb_page_cache *pc, 536188412Sthompsa unsigned int offset, unsigned int len) 537188412Sthompsa{ 538188412Sthompsa struct ifnet *ifp = ue->ue_ifp; 539188412Sthompsa struct mbuf *m; 540188412Sthompsa 541188412Sthompsa UE_LOCK_ASSERT(ue, MA_OWNED); 542188412Sthompsa 543189528Sthompsa if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) 544188412Sthompsa return (1); 545188412Sthompsa 546189528Sthompsa m = usb2_ether_newbuf(); 547188412Sthompsa if (m == NULL) { 548188412Sthompsa ifp->if_ierrors++; 549188412Sthompsa return (ENOMEM); 550188412Sthompsa } 551188412Sthompsa 552188412Sthompsa usb2_copy_out(pc, offset, mtod(m, uint8_t *), len); 553188412Sthompsa 554188412Sthompsa /* finalize mbuf */ 555188412Sthompsa ifp->if_ipackets++; 556188412Sthompsa m->m_pkthdr.rcvif = ifp; 557188412Sthompsa m->m_pkthdr.len = m->m_len = len; 558188412Sthompsa 559188412Sthompsa /* enqueue for later when the lock can be released */ 560188412Sthompsa _IF_ENQUEUE(&ue->ue_rxq, m); 561188412Sthompsa return (0); 562188412Sthompsa} 563188412Sthompsa 564188412Sthompsavoid 565192984Sthompsausb2_ether_rxflush(struct usb_ether *ue) 566188412Sthompsa{ 567188412Sthompsa struct ifnet *ifp = ue->ue_ifp; 568188412Sthompsa struct mbuf *m; 569188412Sthompsa 570188412Sthompsa UE_LOCK_ASSERT(ue, MA_OWNED); 571188412Sthompsa 572188412Sthompsa for (;;) { 573188412Sthompsa _IF_DEQUEUE(&ue->ue_rxq, m); 574188412Sthompsa if (m == NULL) 575188412Sthompsa break; 576188412Sthompsa 577188412Sthompsa /* 578188412Sthompsa * The USB xfer has been resubmitted so its safe to unlock now. 579188412Sthompsa */ 580188412Sthompsa UE_UNLOCK(ue); 581188412Sthompsa ifp->if_input(ifp, m); 582188412Sthompsa UE_LOCK(ue); 583188412Sthompsa } 584188412Sthompsa} 585188412Sthompsa 586188942SthompsaDECLARE_MODULE(uether, usb2_ether_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 587188942SthompsaMODULE_VERSION(uether, 1); 588