1104477Ssam/*- 2104477Ssam * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. 3104477Ssam * Copyright (c) 2009 Diego Giagio. All rights reserved. 4104477Ssam * 5104477Ssam * Redistribution and use in source and binary forms, with or without 6104477Ssam * modification, are permitted provided that the following conditions 7104477Ssam * are met: 8104477Ssam * 1. Redistributions of source code must retain the above copyright 9120915Ssam * notice, this list of conditions and the following disclaimer. 10104477Ssam * 2. Redistributions in binary form must reproduce the above copyright 11104477Ssam * notice, this list of conditions and the following disclaimer in the 12104477Ssam * documentation and/or other materials provided with the distribution. 13104477Ssam * 14104477Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15104477Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16104477Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17104477Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18104477Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19104477Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20104477Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21104477Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22104477Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23104477Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24104477Ssam * SUCH DAMAGE. 25104477Ssam */ 26104477Ssam 27104477Ssam/* 28104477Ssam * Thanks to Diego Giagio for figuring out the programming details for 29104477Ssam * the Apple iPhone Ethernet driver. 30104477Ssam */ 31104477Ssam 32104477Ssam#include <sys/cdefs.h> 33104477Ssam__FBSDID("$FreeBSD$"); 34104477Ssam 35104477Ssam#include <sys/stdint.h> 36104477Ssam#include <sys/stddef.h> 37104477Ssam#include <sys/param.h> 38104477Ssam#include <sys/queue.h> 39104477Ssam#include <sys/types.h> 40104477Ssam#include <sys/systm.h> 41104477Ssam#include <sys/kernel.h> 42104477Ssam#include <sys/bus.h> 43119418Sobrien#include <sys/module.h> 44119418Sobrien#include <sys/lock.h> 45119418Sobrien#include <sys/mutex.h> 46104477Ssam#include <sys/condvar.h> 47120915Ssam#include <sys/sysctl.h> 48104477Ssam#include <sys/sx.h> 49112124Ssam#include <sys/unistd.h> 50104477Ssam#include <sys/callout.h> 51104477Ssam#include <sys/malloc.h> 52104477Ssam#include <sys/priv.h> 53104477Ssam 54104477Ssam#include <dev/usb/usb.h> 55104477Ssam#include <dev/usb/usbdi.h> 56104477Ssam#include <dev/usb/usbdi_util.h> 57129879Sphk#include "usbdevs.h" 58104477Ssam 59104477Ssam#define USB_DEBUG_VAR ipheth_debug 60104477Ssam#include <dev/usb/usb_debug.h> 61104477Ssam#include <dev/usb/usb_process.h> 62104477Ssam 63104477Ssam#include <dev/usb/net/usb_ethernet.h> 64104477Ssam#include <dev/usb/net/if_iphethvar.h> 65104477Ssam 66104477Ssamstatic device_probe_t ipheth_probe; 67104477Ssamstatic device_attach_t ipheth_attach; 68104477Ssamstatic device_detach_t ipheth_detach; 69104477Ssam 70104477Ssamstatic usb_callback_t ipheth_bulk_write_callback; 71104477Ssamstatic usb_callback_t ipheth_bulk_read_callback; 72104477Ssam 73104477Ssamstatic uether_fn_t ipheth_attach_post; 74104477Ssamstatic uether_fn_t ipheth_tick; 75119280Simpstatic uether_fn_t ipheth_init; 76119280Simpstatic uether_fn_t ipheth_stop; 77112124Ssamstatic uether_fn_t ipheth_start; 78112124Ssamstatic uether_fn_t ipheth_setmulti; 79112124Ssamstatic uether_fn_t ipheth_setpromisc; 80112124Ssam 81104477Ssam#ifdef USB_DEBUG 82104477Ssamstatic int ipheth_debug = 0; 83104477Ssam 84104477Ssamstatic SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet"); 85104477SsamSYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RW, &ipheth_debug, 0, "Debug level"); 86104477Ssam#endif 87104477Ssam 88104477Ssamstatic const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = { 89104477Ssam 90104477Ssam [IPHETH_BULK_RX] = { 91104477Ssam .type = UE_BULK, 92104477Ssam .endpoint = UE_ADDR_ANY, 93104477Ssam .direction = UE_DIR_RX, 94104477Ssam .frames = IPHETH_RX_FRAMES_MAX, 95104477Ssam .bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES), 96104477Ssam .flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, 97104477Ssam .callback = ipheth_bulk_read_callback, 98104477Ssam .timeout = 0, /* no timeout */ 99104477Ssam }, 100104477Ssam 101104477Ssam [IPHETH_BULK_TX] = { 102104477Ssam .type = UE_BULK, 103104477Ssam .endpoint = UE_ADDR_ANY, 104104477Ssam .direction = UE_DIR_TX, 105104477Ssam .frames = IPHETH_TX_FRAMES_MAX, 106104477Ssam .bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE), 107104477Ssam .flags = {.force_short_xfer = 1,}, 108104477Ssam .callback = ipheth_bulk_write_callback, 109104477Ssam .timeout = IPHETH_TX_TIMEOUT, 110104477Ssam }, 111104477Ssam}; 112104477Ssam 113104477Ssamstatic device_method_t ipheth_methods[] = { 114104477Ssam /* Device interface */ 115104477Ssam DEVMETHOD(device_probe, ipheth_probe), 116104477Ssam DEVMETHOD(device_attach, ipheth_attach), 117105251Smarkm DEVMETHOD(device_detach, ipheth_detach), 118112124Ssam 119112124Ssam DEVMETHOD_END 120112124Ssam}; 121104477Ssam 122104477Ssamstatic driver_t ipheth_driver = { 123104477Ssam .name = "ipheth", 124104477Ssam .methods = ipheth_methods, 125104477Ssam .size = sizeof(struct ipheth_softc), 126104477Ssam}; 127104477Ssam 128104477Ssamstatic devclass_t ipheth_devclass; 129104477Ssam 130104477SsamDRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, 0); 131104477SsamMODULE_VERSION(ipheth, 1); 132104477SsamMODULE_DEPEND(ipheth, uether, 1, 1, 1); 133104477SsamMODULE_DEPEND(ipheth, usb, 1, 1, 1); 134104477SsamMODULE_DEPEND(ipheth, ether, 1, 1, 1); 135104477Ssam 136104477Ssamstatic const struct usb_ether_methods ipheth_ue_methods = { 137104477Ssam .ue_attach_post = ipheth_attach_post, 138104477Ssam .ue_start = ipheth_start, 139104477Ssam .ue_init = ipheth_init, 140104477Ssam .ue_tick = ipheth_tick, 141104477Ssam .ue_stop = ipheth_stop, 142104477Ssam .ue_setmulti = ipheth_setmulti, 143104477Ssam .ue_setpromisc = ipheth_setpromisc, 144104477Ssam}; 145104477Ssam 146104477Ssam#define IPHETH_ID(v,p,c,sc,pt) \ 147104477Ssam USB_VENDOR(v), USB_PRODUCT(p), \ 148104477Ssam USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \ 149104477Ssam USB_IFACE_PROTOCOL(pt) 150104477Ssam 151104477Ssamstatic const STRUCT_USB_HOST_ID ipheth_devs[] = { 152104477Ssam#if 0 153104477Ssam {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, 154131575Sstefanf IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 155104477Ssam IPHETH_USBINTF_PROTO)}, 156104477Ssam {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G, 157104477Ssam IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 158104477Ssam IPHETH_USBINTF_PROTO)}, 159104477Ssam {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS, 160104477Ssam IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 161104477Ssam IPHETH_USBINTF_PROTO)}, 162104477Ssam {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4, 163131575Sstefanf IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 164104477Ssam IPHETH_USBINTF_PROTO)}, 165104477Ssam {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S, 166104477Ssam IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 167104477Ssam IPHETH_USBINTF_PROTO)}, 168104477Ssam {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_5, 169104477Ssam IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 170104477Ssam IPHETH_USBINTF_PROTO)}, 171104477Ssam#else 172109596Ssam /* product agnostic interface match */ 173109596Ssam {USB_VENDOR(USB_VENDOR_APPLE), 174104477Ssam USB_IFACE_CLASS(IPHETH_USBINTF_CLASS), 175104477Ssam USB_IFACE_SUBCLASS(IPHETH_USBINTF_SUBCLASS), 176109596Ssam USB_IFACE_PROTOCOL(IPHETH_USBINTF_PROTO)}, 177109596Ssam#endif 178104477Ssam}; 179104477Ssam 180104477Ssamstatic int 181109596Ssamipheth_get_mac_addr(struct ipheth_softc *sc) 182109596Ssam{ 183112121Ssam struct usb_device_request req; 184109596Ssam int error; 185109596Ssam 186104477Ssam req.bmRequestType = UT_READ_VENDOR_DEVICE; 187104477Ssam req.bRequest = IPHETH_CMD_GET_MACADDR; 188104477Ssam req.wValue[0] = 0; 189104477Ssam req.wValue[1] = 0; 190104477Ssam req.wIndex[0] = sc->sc_iface_no; 191104477Ssam req.wIndex[1] = 0; 192104477Ssam req.wLength[0] = ETHER_ADDR_LEN; 193104477Ssam req.wLength[1] = 0; 194104477Ssam 195104477Ssam error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data); 196104477Ssam 197104477Ssam if (error) 198104477Ssam return (error); 199104477Ssam 200120915Ssam memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN); 201120915Ssam 202104477Ssam return (0); 203104477Ssam} 204104477Ssam 205104477Ssamstatic int 206104477Ssamipheth_probe(device_t dev) 207104477Ssam{ 208104477Ssam struct usb_attach_arg *uaa = device_get_ivars(dev); 209104477Ssam 210104477Ssam if (uaa->usb_mode != USB_MODE_HOST) 211104477Ssam return (ENXIO); 212104477Ssam 213104477Ssam return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa)); 214104477Ssam} 215104477Ssam 216104477Ssamstatic int 217104477Ssamipheth_attach(device_t dev) 218104477Ssam{ 219104477Ssam struct ipheth_softc *sc = device_get_softc(dev); 220104477Ssam struct usb_ether *ue = &sc->sc_ue; 221104477Ssam struct usb_attach_arg *uaa = device_get_ivars(dev); 222104477Ssam int error; 223104477Ssam 224104477Ssam sc->sc_iface_no = uaa->info.bIfaceIndex; 225104477Ssam 226104477Ssam device_set_usb_desc(dev); 227104477Ssam 228120915Ssam mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); 229120915Ssam 230104477Ssam error = usbd_set_alt_interface_index(uaa->device, 231104477Ssam uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM); 232104477Ssam if (error) { 233104477Ssam device_printf(dev, "Cannot set alternate setting\n"); 234104477Ssam goto detach; 235104477Ssam } 236104477Ssam error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no, 237104477Ssam sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx); 238104477Ssam if (error) { 239104477Ssam device_printf(dev, "Cannot setup USB transfers\n"); 240104477Ssam goto detach; 241104477Ssam } 242104477Ssam ue->ue_sc = sc; 243104477Ssam ue->ue_dev = dev; 244104477Ssam ue->ue_udev = uaa->device; 245104477Ssam ue->ue_mtx = &sc->sc_mtx; 246112124Ssam ue->ue_methods = &ipheth_ue_methods; 247112124Ssam 248112124Ssam error = ipheth_get_mac_addr(sc); 249112124Ssam if (error) { 250112124Ssam device_printf(dev, "Cannot get MAC address\n"); 251112124Ssam goto detach; 252104477Ssam } 253104477Ssam 254104477Ssam error = uether_ifattach(ue); 255104477Ssam if (error) { 256104477Ssam device_printf(dev, "could not attach interface\n"); 257104477Ssam goto detach; 258104477Ssam } 259104477Ssam return (0); /* success */ 260104477Ssam 261104477Ssamdetach: 262104477Ssam ipheth_detach(dev); 263104477Ssam return (ENXIO); /* failure */ 264104477Ssam} 265104477Ssam 266104477Ssamstatic int 267104477Ssamipheth_detach(device_t dev) 268104477Ssam{ 269115748Ssam struct ipheth_softc *sc = device_get_softc(dev); 270104477Ssam struct usb_ether *ue = &sc->sc_ue; 271104477Ssam 272104477Ssam /* stop all USB transfers first */ 273104477Ssam usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER); 274120915Ssam 275104477Ssam uether_ifdetach(ue); 276104477Ssam 277104477Ssam mtx_destroy(&sc->sc_mtx); 278120915Ssam 279120915Ssam return (0); 280120915Ssam} 281104477Ssam 282104477Ssamstatic void 283104477Ssamipheth_start(struct usb_ether *ue) 284104477Ssam{ 285104477Ssam struct ipheth_softc *sc = uether_getsc(ue); 286104477Ssam 287104477Ssam /* 288104477Ssam * Start the USB transfers, if not already started: 289104477Ssam */ 290104477Ssam usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]); 291120915Ssam usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]); 292120915Ssam} 293120915Ssam 294120915Ssamstatic void 295120915Ssamipheth_stop(struct usb_ether *ue) 296120915Ssam{ 297120915Ssam struct ipheth_softc *sc = uether_getsc(ue); 298120915Ssam 299104477Ssam /* 300104477Ssam * Stop the USB transfers, if not already stopped: 301104477Ssam */ 302104477Ssam usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]); 303104477Ssam usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]); 304104477Ssam} 305104477Ssam 306104477Ssamstatic void 307104477Ssamipheth_tick(struct usb_ether *ue) 308104477Ssam{ 309104477Ssam struct ipheth_softc *sc = uether_getsc(ue); 310104477Ssam struct usb_device_request req; 311104477Ssam int error; 312104477Ssam 313104477Ssam req.bmRequestType = UT_READ_VENDOR_DEVICE; 314104477Ssam req.bRequest = IPHETH_CMD_CARRIER_CHECK; 315104477Ssam req.wValue[0] = 0; 316104477Ssam req.wValue[1] = 0; 317104477Ssam req.wIndex[0] = sc->sc_iface_no; 318104477Ssam req.wIndex[1] = 0; 319104477Ssam req.wLength[0] = IPHETH_CTRL_BUF_SIZE; 320104477Ssam req.wLength[1] = 0; 321104477Ssam 322104477Ssam error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT); 323104477Ssam 324127135Snjl if (error) 325127135Snjl return; 326104477Ssam 327104477Ssam sc->sc_carrier_on = 328104477Ssam (sc->sc_data[0] == IPHETH_CARRIER_ON); 329104477Ssam} 330104477Ssam 331104477Ssamstatic void 332104477Ssamipheth_attach_post(struct usb_ether *ue) 333104477Ssam{ 334104477Ssam 335127135Snjl} 336127135Snjl 337104477Ssamstatic void 338104477Ssamipheth_init(struct usb_ether *ue) 339104477Ssam{ 340104477Ssam struct ipheth_softc *sc = uether_getsc(ue); 341104477Ssam struct ifnet *ifp = uether_getifp(ue); 342104477Ssam 343104477Ssam IPHETH_LOCK_ASSERT(sc, MA_OWNED); 344104477Ssam 345104477Ssam ifp->if_drv_flags |= IFF_DRV_RUNNING; 346104477Ssam 347104477Ssam /* stall data write direction, which depends on USB mode */ 348104477Ssam usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]); 349104477Ssam 350104477Ssam /* start data transfers */ 351104477Ssam ipheth_start(ue); 352104477Ssam} 353104477Ssam 354104477Ssamstatic void 355104477Ssamipheth_setmulti(struct usb_ether *ue) 356104477Ssam{ 357104477Ssam 358104477Ssam} 359104477Ssam 360117126Sscottlstatic void 361117126Sscottlipheth_setpromisc(struct usb_ether *ue) 362104477Ssam{ 363104477Ssam 364104477Ssam} 365104477Ssam 366104477Ssamstatic void 367104477Ssamipheth_free_queue(struct mbuf **ppm, uint8_t n) 368104477Ssam{ 369104477Ssam uint8_t x; 370104477Ssam 371104477Ssam for (x = 0; x != n; x++) { 372104477Ssam if (ppm[x] != NULL) { 373104477Ssam m_freem(ppm[x]); 374104477Ssam ppm[x] = NULL; 375104477Ssam } 376104477Ssam } 377104477Ssam} 378104477Ssam 379104477Ssamstatic void 380104477Ssamipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 381104477Ssam{ 382104477Ssam struct ipheth_softc *sc = usbd_xfer_softc(xfer); 383104477Ssam struct ifnet *ifp = uether_getifp(&sc->sc_ue); 384104477Ssam struct usb_page_cache *pc; 385104477Ssam struct mbuf *m; 386104477Ssam uint8_t x; 387104477Ssam int actlen; 388104477Ssam int aframes; 389104477Ssam 390123824Ssam usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 391123824Ssam 392123824Ssam DPRINTFN(1, "\n"); 393123824Ssam 394104477Ssam switch (USB_GET_STATE(xfer)) { 395104477Ssam case USB_ST_TRANSFERRED: 396104477Ssam DPRINTFN(11, "transfer complete: %u bytes in %u frames\n", 397104477Ssam actlen, aframes); 398104477Ssam 399104477Ssam ifp->if_opackets++; 400104477Ssam 401104477Ssam /* free all previous TX buffers */ 402104477Ssam ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX); 403104477Ssam 404104477Ssam /* FALLTHROUGH */ 405104477Ssam case USB_ST_SETUP: 406104477Ssamtr_setup: 407104477Ssam for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) { 408104477Ssam 409104477Ssam IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 410104477Ssam 411104477Ssam if (m == NULL) 412104477Ssam break; 413104477Ssam 414120915Ssam usbd_xfer_set_frame_offset(xfer, 415120915Ssam x * IPHETH_BUF_SIZE, x); 416120915Ssam 417120915Ssam pc = usbd_xfer_get_frame(xfer, x); 418104477Ssam 419104477Ssam sc->sc_tx_buf[x] = m; 420104477Ssam 421104477Ssam if (m->m_pkthdr.len > IPHETH_BUF_SIZE) 422104477Ssam m->m_pkthdr.len = IPHETH_BUF_SIZE; 423104477Ssam 424104477Ssam usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 425104477Ssam 426104477Ssam usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE); 427104477Ssam 428104477Ssam if (IPHETH_BUF_SIZE != m->m_pkthdr.len) { 429104477Ssam usbd_frame_zero(pc, m->m_pkthdr.len, 430104477Ssam IPHETH_BUF_SIZE - m->m_pkthdr.len); 431104477Ssam } 432104477Ssam 433104477Ssam /* 434104477Ssam * If there's a BPF listener, bounce a copy of 435104477Ssam * this frame to him: 436104477Ssam */ 437104477Ssam BPF_MTAP(ifp, m); 438127135Snjl } 439127135Snjl if (x != 0) { 440104477Ssam usbd_xfer_set_frames(xfer, x); 441104477Ssam 442104477Ssam usbd_transfer_submit(xfer); 443104477Ssam } 444104477Ssam break; 445104477Ssam 446104477Ssam default: /* Error */ 447104477Ssam DPRINTFN(11, "transfer error, %s\n", 448115748Ssam usbd_errstr(error)); 449104477Ssam 450104477Ssam /* free all previous TX buffers */ 451104477Ssam ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX); 452104477Ssam 453104477Ssam /* count output errors */ 454104477Ssam ifp->if_oerrors++; 455104477Ssam 456104477Ssam if (error != USB_ERR_CANCELLED) { 457104477Ssam /* try to clear stall first */ 458104477Ssam usbd_xfer_set_stall(xfer); 459104477Ssam goto tr_setup; 460104477Ssam } 461104477Ssam break; 462104477Ssam } 463104477Ssam} 464104477Ssam 465104477Ssamstatic void 466104477Ssamipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 467104477Ssam{ 468104477Ssam struct ipheth_softc *sc = usbd_xfer_softc(xfer); 469104477Ssam struct mbuf *m; 470104477Ssam uint8_t x; 471104477Ssam int actlen; 472104477Ssam int aframes; 473104477Ssam int len; 474104477Ssam 475104477Ssam usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 476104477Ssam 477104477Ssam switch (USB_GET_STATE(xfer)) { 478104477Ssam case USB_ST_TRANSFERRED: 479104477Ssam 480104477Ssam DPRINTF("received %u bytes in %u frames\n", actlen, aframes); 481104477Ssam 482104477Ssam for (x = 0; x != aframes; x++) { 483104477Ssam 484104477Ssam m = sc->sc_rx_buf[x]; 485104477Ssam sc->sc_rx_buf[x] = NULL; 486104477Ssam len = usbd_xfer_frame_len(xfer, x); 487104477Ssam 488104477Ssam if (len < (int)(sizeof(struct ether_header) + 489120915Ssam IPHETH_RX_ADJ)) { 490120915Ssam m_freem(m); 491120915Ssam continue; 492120915Ssam } 493104477Ssam 494104477Ssam m_adj(m, IPHETH_RX_ADJ); 495104477Ssam 496104477Ssam /* queue up mbuf */ 497104477Ssam uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ); 498104477Ssam } 499104477Ssam 500104477Ssam /* FALLTHROUGH */ 501104477Ssam case USB_ST_SETUP: 502104477Ssam 503104477Ssam for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) { 504104477Ssam if (sc->sc_rx_buf[x] == NULL) { 505104477Ssam m = uether_newbuf(); 506104477Ssam if (m == NULL) 507104477Ssam goto tr_stall; 508104477Ssam 509104477Ssam /* cancel alignment for ethernet */ 510104477Ssam m_adj(m, ETHER_ALIGN); 511104477Ssam 512104477Ssam sc->sc_rx_buf[x] = m; 513104477Ssam } else { 514119137Ssam m = sc->sc_rx_buf[x]; 515104477Ssam } 516104477Ssam 517104477Ssam usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); 518104477Ssam } 519104477Ssam /* set number of frames and start hardware */ 520104477Ssam usbd_xfer_set_frames(xfer, x); 521104477Ssam usbd_transfer_submit(xfer); 522104477Ssam /* flush any received frames */ 523104477Ssam uether_rxflush(&sc->sc_ue); 524104477Ssam break; 525104477Ssam 526104477Ssam default: /* Error */ 527104477Ssam DPRINTF("error = %s\n", usbd_errstr(error)); 528104477Ssam 529104477Ssam if (error != USB_ERR_CANCELLED) { 530104477Ssam tr_stall: 531104477Ssam /* try to clear stall first */ 532104477Ssam usbd_xfer_set_stall(xfer); 533104477Ssam usbd_xfer_set_frames(xfer, 0); 534104477Ssam usbd_transfer_submit(xfer); 535104477Ssam break; 536104477Ssam } 537104477Ssam /* need to free the RX-mbufs when we are cancelled */ 538104477Ssam ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX); 539104477Ssam break; 540104477Ssam } 541104477Ssam} 542104477Ssam