if_ipheth.c revision 253670
1193326Sed/*- 2193326Sed * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. 3193326Sed * Copyright (c) 2009 Diego Giagio. All rights reserved. 4193326Sed * 5193326Sed * Redistribution and use in source and binary forms, with or without 6193326Sed * modification, are permitted provided that the following conditions 7193326Sed * are met: 8193326Sed * 1. Redistributions of source code must retain the above copyright 9193326Sed * notice, this list of conditions and the following disclaimer. 10193326Sed * 2. Redistributions in binary form must reproduce the above copyright 11193326Sed * notice, this list of conditions and the following disclaimer in the 12193326Sed * documentation and/or other materials provided with the distribution. 13193326Sed * 14212904Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15193326Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16193326Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17193326Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18224145Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19207619Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20249423Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21249423Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23249423Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24249423Sdim * SUCH DAMAGE. 25249423Sdim */ 26249423Sdim 27249423Sdim/* 28193326Sed * Thanks to Diego Giagio for figuring out the programming details for 29193326Sed * the Apple iPhone Ethernet driver. 30193326Sed */ 31218893Sdim 32226633Sdim#include <sys/cdefs.h> 33193326Sed__FBSDID("$FreeBSD: head/sys/dev/usb/net/if_ipheth.c 253670 2013-07-26 09:58:56Z hselasky $"); 34212904Sdim 35212904Sdim#include <sys/stdint.h> 36212904Sdim#include <sys/stddef.h> 37193326Sed#include <sys/param.h> 38193326Sed#include <sys/queue.h> 39193326Sed#include <sys/types.h> 40193326Sed#include <sys/systm.h> 41193326Sed#include <sys/kernel.h> 42193326Sed#include <sys/bus.h> 43193326Sed#include <sys/module.h> 44198092Srdivacky#include <sys/lock.h> 45193326Sed#include <sys/mutex.h> 46193326Sed#include <sys/condvar.h> 47193326Sed#include <sys/sysctl.h> 48234353Sdim#include <sys/sx.h> 49226633Sdim#include <sys/unistd.h> 50198092Srdivacky#include <sys/callout.h> 51193326Sed#include <sys/malloc.h> 52193326Sed#include <sys/priv.h> 53198092Srdivacky 54226633Sdim#include <dev/usb/usb.h> 55226633Sdim#include <dev/usb/usbdi.h> 56193326Sed#include <dev/usb/usbdi_util.h> 57193326Sed#include "usbdevs.h" 58193326Sed 59193326Sed#define USB_DEBUG_VAR ipheth_debug 60198092Srdivacky#include <dev/usb/usb_debug.h> 61212904Sdim#include <dev/usb/usb_process.h> 62212904Sdim 63198092Srdivacky#include <dev/usb/net/usb_ethernet.h> 64193326Sed#include <dev/usb/net/if_iphethvar.h> 65193326Sed 66193326Sedstatic device_probe_t ipheth_probe; 67198092Srdivackystatic device_attach_t ipheth_attach; 68193326Sedstatic device_detach_t ipheth_detach; 69193326Sed 70224145Sdimstatic usb_callback_t ipheth_bulk_write_callback; 71226633Sdimstatic usb_callback_t ipheth_bulk_read_callback; 72193326Sed 73193326Sedstatic uether_fn_t ipheth_attach_post; 74193326Sedstatic uether_fn_t ipheth_tick; 75234353Sdimstatic uether_fn_t ipheth_init; 76234353Sdimstatic uether_fn_t ipheth_stop; 77234353Sdimstatic uether_fn_t ipheth_start; 78198092Srdivackystatic uether_fn_t ipheth_setmulti; 79234353Sdimstatic uether_fn_t ipheth_setpromisc; 80193326Sed 81193326Sed#ifdef USB_DEBUG 82193326Sedstatic int ipheth_debug = 0; 83193326Sed 84193326Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet"); 85193326SedSYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RW, &ipheth_debug, 0, "Debug level"); 86193326Sed#endif 87193326Sed 88193326Sedstatic const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = { 89193326Sed 90198092Srdivacky [IPHETH_BULK_RX] = { 91234353Sdim .type = UE_BULK, 92218893Sdim .endpoint = UE_ADDR_ANY, 93234353Sdim .direction = UE_DIR_RX, 94218893Sdim .frames = IPHETH_RX_FRAMES_MAX, 95218893Sdim .bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES), 96218893Sdim .flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, 97218893Sdim .callback = ipheth_bulk_read_callback, 98218893Sdim .timeout = 0, /* no timeout */ 99218893Sdim }, 100234353Sdim 101207619Srdivacky [IPHETH_BULK_TX] = { 102207619Srdivacky .type = UE_BULK, 103207619Srdivacky .endpoint = UE_ADDR_ANY, 104207619Srdivacky .direction = UE_DIR_TX, 105207619Srdivacky .frames = IPHETH_TX_FRAMES_MAX, 106207619Srdivacky .bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE), 107207619Srdivacky .flags = {.force_short_xfer = 1,}, 108207619Srdivacky .callback = ipheth_bulk_write_callback, 109207619Srdivacky .timeout = IPHETH_TX_TIMEOUT, 110207619Srdivacky }, 111207619Srdivacky}; 112207619Srdivacky 113193326Sedstatic device_method_t ipheth_methods[] = { 114239462Sdim /* Device interface */ 115234353Sdim DEVMETHOD(device_probe, ipheth_probe), 116207619Srdivacky DEVMETHOD(device_attach, ipheth_attach), 117193326Sed DEVMETHOD(device_detach, ipheth_detach), 118193326Sed 119193326Sed DEVMETHOD_END 120198092Srdivacky}; 121193326Sed 122234353Sdimstatic driver_t ipheth_driver = { 123234353Sdim .name = "ipheth", 124234353Sdim .methods = ipheth_methods, 125234353Sdim .size = sizeof(struct ipheth_softc), 126234353Sdim}; 127234353Sdim 128234353Sdimstatic devclass_t ipheth_devclass; 129234353Sdim 130234353SdimDRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, 0); 131234353SdimMODULE_VERSION(ipheth, 1); 132234353SdimMODULE_DEPEND(ipheth, uether, 1, 1, 1); 133234353SdimMODULE_DEPEND(ipheth, usb, 1, 1, 1); 134234353SdimMODULE_DEPEND(ipheth, ether, 1, 1, 1); 135234353Sdim 136234353Sdimstatic const struct usb_ether_methods ipheth_ue_methods = { 137193326Sed .ue_attach_post = ipheth_attach_post, 138193326Sed .ue_start = ipheth_start, 139198092Srdivacky .ue_init = ipheth_init, 140234353Sdim .ue_tick = ipheth_tick, 141193326Sed .ue_stop = ipheth_stop, 142193326Sed .ue_setmulti = ipheth_setmulti, 143239462Sdim .ue_setpromisc = ipheth_setpromisc, 144239462Sdim}; 145239462Sdim 146239462Sdim#define IPHETH_ID(v,p,c,sc,pt) \ 147239462Sdim USB_VENDOR(v), USB_PRODUCT(p), \ 148239462Sdim USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \ 149239462Sdim USB_IFACE_PROTOCOL(pt) 150239462Sdim 151239462Sdimstatic const STRUCT_USB_HOST_ID ipheth_devs[] = { 152239462Sdim#if 0 153239462Sdim {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, 154239462Sdim IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 155239462Sdim IPHETH_USBINTF_PROTO)}, 156239462Sdim {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G, 157239462Sdim IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 158239462Sdim IPHETH_USBINTF_PROTO)}, 159239462Sdim {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS, 160239462Sdim IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 161239462Sdim IPHETH_USBINTF_PROTO)}, 162239462Sdim {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4, 163239462Sdim IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 164239462Sdim IPHETH_USBINTF_PROTO)}, 165239462Sdim {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S, 166239462Sdim IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 167234353Sdim IPHETH_USBINTF_PROTO)}, 168234353Sdim {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_5, 169234353Sdim IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 170239462Sdim IPHETH_USBINTF_PROTO)}, 171239462Sdim#else 172239462Sdim /* product agnostic interface match */ 173249423Sdim {USB_VENDOR(USB_VENDOR_APPLE), 174249423Sdim USB_IFACE_CLASS(IPHETH_USBINTF_CLASS), 175249423Sdim USB_IFACE_SUBCLASS(IPHETH_USBINTF_SUBCLASS), 176234353Sdim USB_IFACE_PROTOCOL(IPHETH_USBINTF_PROTO)}, 177239462Sdim#endif 178239462Sdim}; 179239462Sdim 180239462Sdimstatic int 181234353Sdimipheth_get_mac_addr(struct ipheth_softc *sc) 182234353Sdim{ 183239462Sdim struct usb_device_request req; 184234353Sdim int error; 185234353Sdim 186234353Sdim req.bmRequestType = UT_READ_VENDOR_DEVICE; 187234353Sdim req.bRequest = IPHETH_CMD_GET_MACADDR; 188234353Sdim req.wValue[0] = 0; 189234353Sdim req.wValue[1] = 0; 190234353Sdim req.wIndex[0] = sc->sc_iface_no; 191239462Sdim req.wIndex[1] = 0; 192239462Sdim req.wLength[0] = ETHER_ADDR_LEN; 193239462Sdim req.wLength[1] = 0; 194239462Sdim 195239462Sdim error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data); 196239462Sdim 197239462Sdim if (error) 198239462Sdim return (error); 199239462Sdim 200239462Sdim memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN); 201239462Sdim 202239462Sdim return (0); 203239462Sdim} 204239462Sdim 205239462Sdimstatic int 206239462Sdimipheth_probe(device_t dev) 207239462Sdim{ 208239462Sdim struct usb_attach_arg *uaa = device_get_ivars(dev); 209239462Sdim 210239462Sdim if (uaa->usb_mode != USB_MODE_HOST) 211239462Sdim return (ENXIO); 212239462Sdim 213239462Sdim return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa)); 214239462Sdim} 215239462Sdim 216239462Sdimstatic int 217239462Sdimipheth_attach(device_t dev) 218239462Sdim{ 219239462Sdim struct ipheth_softc *sc = device_get_softc(dev); 220239462Sdim struct usb_ether *ue = &sc->sc_ue; 221239462Sdim struct usb_attach_arg *uaa = device_get_ivars(dev); 222239462Sdim int error; 223234353Sdim 224239462Sdim sc->sc_iface_no = uaa->info.bIfaceIndex; 225234353Sdim 226239462Sdim device_set_usb_desc(dev); 227234353Sdim 228239462Sdim mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); 229239462Sdim 230239462Sdim error = usbd_set_alt_interface_index(uaa->device, 231239462Sdim uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM); 232243830Sdim if (error) { 233239462Sdim device_printf(dev, "Cannot set alternate setting\n"); 234239462Sdim goto detach; 235239462Sdim } 236239462Sdim error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no, 237234353Sdim sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx); 238234353Sdim if (error) { 239239462Sdim device_printf(dev, "Cannot setup USB transfers\n"); 240239462Sdim goto detach; 241249423Sdim } 242251662Sdim ue->ue_sc = sc; 243234353Sdim ue->ue_dev = dev; 244234353Sdim ue->ue_udev = uaa->device; 245239462Sdim ue->ue_mtx = &sc->sc_mtx; 246234353Sdim ue->ue_methods = &ipheth_ue_methods; 247234353Sdim 248234353Sdim error = ipheth_get_mac_addr(sc); 249234353Sdim if (error) { 250234353Sdim device_printf(dev, "Cannot get MAC address\n"); 251234353Sdim goto detach; 252234353Sdim } 253234353Sdim 254234353Sdim error = uether_ifattach(ue); 255239462Sdim if (error) { 256239462Sdim device_printf(dev, "could not attach interface\n"); 257234353Sdim goto detach; 258234353Sdim } 259234353Sdim return (0); /* success */ 260234353Sdim 261234353Sdimdetach: 262234353Sdim ipheth_detach(dev); 263234353Sdim return (ENXIO); /* failure */ 264234353Sdim} 265234353Sdim 266234353Sdimstatic int 267234353Sdimipheth_detach(device_t dev) 268234353Sdim{ 269234353Sdim struct ipheth_softc *sc = device_get_softc(dev); 270234353Sdim struct usb_ether *ue = &sc->sc_ue; 271234353Sdim 272234353Sdim /* stop all USB transfers first */ 273234353Sdim usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER); 274234353Sdim 275234353Sdim uether_ifdetach(ue); 276234353Sdim 277234353Sdim mtx_destroy(&sc->sc_mtx); 278234353Sdim 279234353Sdim return (0); 280234353Sdim} 281234353Sdim 282234353Sdimstatic void 283234353Sdimipheth_start(struct usb_ether *ue) 284239462Sdim{ 285239462Sdim struct ipheth_softc *sc = uether_getsc(ue); 286239462Sdim 287234353Sdim /* 288234353Sdim * Start the USB transfers, if not already started: 289234353Sdim */ 290234353Sdim usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]); 291239462Sdim usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]); 292239462Sdim} 293239462Sdim 294239462Sdimstatic void 295239462Sdimipheth_stop(struct usb_ether *ue) 296239462Sdim{ 297234353Sdim struct ipheth_softc *sc = uether_getsc(ue); 298234353Sdim 299234353Sdim /* 300234353Sdim * Stop the USB transfers, if not already stopped: 301239462Sdim */ 302234353Sdim usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]); 303239462Sdim usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]); 304239462Sdim} 305234353Sdim 306234353Sdimstatic void 307234353Sdimipheth_tick(struct usb_ether *ue) 308234353Sdim{ 309234353Sdim struct ipheth_softc *sc = uether_getsc(ue); 310234353Sdim struct usb_device_request req; 311234353Sdim int error; 312234353Sdim 313234353Sdim req.bmRequestType = UT_READ_VENDOR_DEVICE; 314234353Sdim req.bRequest = IPHETH_CMD_CARRIER_CHECK; 315234353Sdim req.wValue[0] = 0; 316234353Sdim req.wValue[1] = 0; 317234353Sdim req.wIndex[0] = sc->sc_iface_no; 318234353Sdim req.wIndex[1] = 0; 319234353Sdim req.wLength[0] = IPHETH_CTRL_BUF_SIZE; 320234353Sdim req.wLength[1] = 0; 321234353Sdim 322234353Sdim error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT); 323234353Sdim 324234353Sdim if (error) 325234353Sdim return; 326234353Sdim 327234353Sdim sc->sc_carrier_on = 328234353Sdim (sc->sc_data[0] == IPHETH_CARRIER_ON); 329234353Sdim} 330234353Sdim 331234353Sdimstatic void 332234353Sdimipheth_attach_post(struct usb_ether *ue) 333234353Sdim{ 334234353Sdim 335234353Sdim} 336234353Sdim 337234353Sdimstatic void 338234353Sdimipheth_init(struct usb_ether *ue) 339234353Sdim{ 340234353Sdim struct ipheth_softc *sc = uether_getsc(ue); 341239462Sdim struct ifnet *ifp = uether_getifp(ue); 342239462Sdim 343234353Sdim IPHETH_LOCK_ASSERT(sc, MA_OWNED); 344239462Sdim 345239462Sdim ifp->if_drv_flags |= IFF_DRV_RUNNING; 346251662Sdim 347234353Sdim /* stall data write direction, which depends on USB mode */ 348243830Sdim usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]); 349234353Sdim 350234353Sdim /* start data transfers */ 351234353Sdim ipheth_start(ue); 352234353Sdim} 353234353Sdim 354234353Sdimstatic void 355234353Sdimipheth_setmulti(struct usb_ether *ue) 356234353Sdim{ 357234353Sdim 358234353Sdim} 359234353Sdim 360234353Sdimstatic void 361234353Sdimipheth_setpromisc(struct usb_ether *ue) 362234353Sdim{ 363234353Sdim 364234353Sdim} 365234353Sdim 366234353Sdimstatic void 367234353Sdimipheth_free_queue(struct mbuf **ppm, uint8_t n) 368234353Sdim{ 369234353Sdim uint8_t x; 370234353Sdim 371234353Sdim for (x = 0; x != n; x++) { 372234353Sdim if (ppm[x] != NULL) { 373234353Sdim m_freem(ppm[x]); 374234353Sdim ppm[x] = NULL; 375234353Sdim } 376234353Sdim } 377234353Sdim} 378234353Sdim 379234353Sdimstatic void 380234353Sdimipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 381234353Sdim{ 382234353Sdim struct ipheth_softc *sc = usbd_xfer_softc(xfer); 383234353Sdim struct ifnet *ifp = uether_getifp(&sc->sc_ue); 384234353Sdim struct usb_page_cache *pc; 385234353Sdim struct mbuf *m; 386234353Sdim uint8_t x; 387234353Sdim int actlen; 388234353Sdim int aframes; 389234353Sdim 390234353Sdim usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 391234353Sdim 392234353Sdim DPRINTFN(1, "\n"); 393234353Sdim 394234353Sdim switch (USB_GET_STATE(xfer)) { 395234353Sdim case USB_ST_TRANSFERRED: 396234353Sdim DPRINTFN(11, "transfer complete: %u bytes in %u frames\n", 397234353Sdim actlen, aframes); 398234353Sdim 399234353Sdim ifp->if_opackets++; 400234353Sdim 401234353Sdim /* free all previous TX buffers */ 402234353Sdim ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX); 403234353Sdim 404234353Sdim /* FALLTHROUGH */ 405234353Sdim case USB_ST_SETUP: 406234353Sdimtr_setup: 407234353Sdim for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) { 408234353Sdim 409234353Sdim IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 410234353Sdim 411234353Sdim if (m == NULL) 412234353Sdim break; 413234353Sdim 414234353Sdim usbd_xfer_set_frame_offset(xfer, 415234353Sdim x * IPHETH_BUF_SIZE, x); 416234353Sdim 417234353Sdim pc = usbd_xfer_get_frame(xfer, x); 418234353Sdim 419234353Sdim sc->sc_tx_buf[x] = m; 420239462Sdim 421239462Sdim if (m->m_pkthdr.len > IPHETH_BUF_SIZE) 422239462Sdim m->m_pkthdr.len = IPHETH_BUF_SIZE; 423239462Sdim 424239462Sdim usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 425239462Sdim 426239462Sdim usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE); 427239462Sdim 428239462Sdim if (IPHETH_BUF_SIZE != m->m_pkthdr.len) { 429239462Sdim usbd_frame_zero(pc, m->m_pkthdr.len, 430239462Sdim IPHETH_BUF_SIZE - m->m_pkthdr.len); 431239462Sdim } 432239462Sdim 433239462Sdim /* 434239462Sdim * If there's a BPF listener, bounce a copy of 435239462Sdim * this frame to him: 436239462Sdim */ 437239462Sdim BPF_MTAP(ifp, m); 438239462Sdim } 439239462Sdim if (x != 0) { 440239462Sdim usbd_xfer_set_frames(xfer, x); 441239462Sdim 442239462Sdim usbd_transfer_submit(xfer); 443239462Sdim } 444239462Sdim break; 445239462Sdim 446239462Sdim default: /* Error */ 447239462Sdim DPRINTFN(11, "transfer error, %s\n", 448239462Sdim usbd_errstr(error)); 449239462Sdim 450239462Sdim /* free all previous TX buffers */ 451239462Sdim ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX); 452239462Sdim 453239462Sdim /* count output errors */ 454239462Sdim ifp->if_oerrors++; 455239462Sdim 456239462Sdim if (error != USB_ERR_CANCELLED) { 457239462Sdim /* try to clear stall first */ 458239462Sdim usbd_xfer_set_stall(xfer); 459239462Sdim goto tr_setup; 460239462Sdim } 461239462Sdim break; 462239462Sdim } 463239462Sdim} 464239462Sdim 465239462Sdimstatic void 466239462Sdimipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 467239462Sdim{ 468239462Sdim struct ipheth_softc *sc = usbd_xfer_softc(xfer); 469239462Sdim struct mbuf *m; 470239462Sdim uint8_t x; 471239462Sdim int actlen; 472239462Sdim int aframes; 473239462Sdim int len; 474239462Sdim 475239462Sdim usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 476239462Sdim 477239462Sdim switch (USB_GET_STATE(xfer)) { 478239462Sdim case USB_ST_TRANSFERRED: 479239462Sdim 480243830Sdim DPRINTF("received %u bytes in %u frames\n", actlen, aframes); 481239462Sdim 482239462Sdim for (x = 0; x != aframes; x++) { 483239462Sdim 484239462Sdim m = sc->sc_rx_buf[x]; 485239462Sdim sc->sc_rx_buf[x] = NULL; 486239462Sdim len = usbd_xfer_frame_len(xfer, x); 487239462Sdim 488239462Sdim if (len < (int)(sizeof(struct ether_header) + 489239462Sdim IPHETH_RX_ADJ)) { 490239462Sdim m_freem(m); 491239462Sdim continue; 492249423Sdim } 493251662Sdim 494239462Sdim m_adj(m, IPHETH_RX_ADJ); 495239462Sdim 496239462Sdim /* queue up mbuf */ 497239462Sdim uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ); 498239462Sdim } 499239462Sdim 500239462Sdim /* FALLTHROUGH */ 501239462Sdim case USB_ST_SETUP: 502239462Sdim 503239462Sdim for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) { 504239462Sdim if (sc->sc_rx_buf[x] == NULL) { 505239462Sdim m = uether_newbuf(); 506239462Sdim if (m == NULL) 507239462Sdim goto tr_stall; 508239462Sdim 509239462Sdim /* cancel alignment for ethernet */ 510239462Sdim m_adj(m, ETHER_ALIGN); 511239462Sdim 512239462Sdim sc->sc_rx_buf[x] = m; 513239462Sdim } else { 514239462Sdim m = sc->sc_rx_buf[x]; 515239462Sdim } 516239462Sdim 517239462Sdim usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); 518239462Sdim } 519239462Sdim /* set number of frames and start hardware */ 520239462Sdim usbd_xfer_set_frames(xfer, x); 521239462Sdim usbd_transfer_submit(xfer); 522239462Sdim /* flush any received frames */ 523239462Sdim uether_rxflush(&sc->sc_ue); 524239462Sdim break; 525239462Sdim 526239462Sdim default: /* Error */ 527239462Sdim DPRINTF("error = %s\n", usbd_errstr(error)); 528239462Sdim 529239462Sdim if (error != USB_ERR_CANCELLED) { 530239462Sdim tr_stall: 531239462Sdim /* try to clear stall first */ 532239462Sdim usbd_xfer_set_stall(xfer); 533239462Sdim usbd_xfer_set_frames(xfer, 0); 534239462Sdim usbd_transfer_submit(xfer); 535239462Sdim break; 536239462Sdim } 537239462Sdim /* need to free the RX-mbufs when we are cancelled */ 538239462Sdim ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX); 539239462Sdim break; 540239462Sdim } 541239462Sdim} 542239462Sdim