if_ipheth.c revision 227309
1213805Shselasky/*- 2213805Shselasky * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. 3213805Shselasky * Copyright (c) 2009 Diego Giagio. All rights reserved. 4213805Shselasky * 5213805Shselasky * Redistribution and use in source and binary forms, with or without 6213805Shselasky * modification, are permitted provided that the following conditions 7213805Shselasky * are met: 8213805Shselasky * 1. Redistributions of source code must retain the above copyright 9213805Shselasky * notice, this list of conditions and the following disclaimer. 10213805Shselasky * 2. Redistributions in binary form must reproduce the above copyright 11213805Shselasky * notice, this list of conditions and the following disclaimer in the 12213805Shselasky * documentation and/or other materials provided with the distribution. 13213805Shselasky * 14213805Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15213805Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16213805Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17213805Shselasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18213805Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19213805Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20213805Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21213805Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22213805Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23213805Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24213805Shselasky * SUCH DAMAGE. 25213805Shselasky */ 26213805Shselasky 27213805Shselasky/* 28213805Shselasky * Thanks to Diego Giagio for figuring out the programming details for 29213805Shselasky * the Apple iPhone Ethernet driver. 30213805Shselasky */ 31213805Shselasky 32213805Shselasky#include <sys/cdefs.h> 33213805Shselasky__FBSDID("$FreeBSD: head/sys/dev/usb/net/if_ipheth.c 227309 2011-11-07 15:43:11Z ed $"); 34213805Shselasky 35213805Shselasky#include <sys/stdint.h> 36213805Shselasky#include <sys/stddef.h> 37213805Shselasky#include <sys/param.h> 38213805Shselasky#include <sys/queue.h> 39213805Shselasky#include <sys/types.h> 40213805Shselasky#include <sys/systm.h> 41213805Shselasky#include <sys/kernel.h> 42213805Shselasky#include <sys/bus.h> 43213805Shselasky#include <sys/module.h> 44213805Shselasky#include <sys/lock.h> 45213805Shselasky#include <sys/mutex.h> 46213805Shselasky#include <sys/condvar.h> 47213805Shselasky#include <sys/sysctl.h> 48213805Shselasky#include <sys/sx.h> 49213805Shselasky#include <sys/unistd.h> 50213805Shselasky#include <sys/callout.h> 51213805Shselasky#include <sys/malloc.h> 52213805Shselasky#include <sys/priv.h> 53213805Shselasky 54213805Shselasky#include <dev/usb/usb.h> 55213805Shselasky#include <dev/usb/usbdi.h> 56213805Shselasky#include <dev/usb/usbdi_util.h> 57213805Shselasky#include "usbdevs.h" 58213805Shselasky 59213805Shselasky#define USB_DEBUG_VAR ipheth_debug 60213805Shselasky#include <dev/usb/usb_debug.h> 61213805Shselasky#include <dev/usb/usb_process.h> 62213805Shselasky 63213805Shselasky#include <dev/usb/net/usb_ethernet.h> 64213805Shselasky#include <dev/usb/net/if_iphethvar.h> 65213805Shselasky 66213805Shselaskystatic device_probe_t ipheth_probe; 67213805Shselaskystatic device_attach_t ipheth_attach; 68213805Shselaskystatic device_detach_t ipheth_detach; 69213805Shselasky 70213805Shselaskystatic usb_callback_t ipheth_bulk_write_callback; 71213805Shselaskystatic usb_callback_t ipheth_bulk_read_callback; 72213805Shselasky 73213805Shselaskystatic uether_fn_t ipheth_attach_post; 74213805Shselaskystatic uether_fn_t ipheth_tick; 75213805Shselaskystatic uether_fn_t ipheth_init; 76213805Shselaskystatic uether_fn_t ipheth_stop; 77213805Shselaskystatic uether_fn_t ipheth_start; 78213805Shselaskystatic uether_fn_t ipheth_setmulti; 79213805Shselaskystatic uether_fn_t ipheth_setpromisc; 80213805Shselasky 81213805Shselasky#ifdef USB_DEBUG 82213805Shselaskystatic int ipheth_debug = 0; 83213805Shselasky 84227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet"); 85213805ShselaskySYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RW, &ipheth_debug, 0, "Debug level"); 86213805Shselasky#endif 87213805Shselasky 88213805Shselaskystatic const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = { 89213805Shselasky 90213805Shselasky [IPHETH_BULK_RX] = { 91213805Shselasky .type = UE_BULK, 92213805Shselasky .endpoint = UE_ADDR_ANY, 93213805Shselasky .direction = UE_DIR_RX, 94213805Shselasky .frames = IPHETH_RX_FRAMES_MAX, 95213805Shselasky .bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES), 96213805Shselasky .flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, 97213805Shselasky .callback = ipheth_bulk_read_callback, 98213805Shselasky .timeout = 0, /* no timeout */ 99213805Shselasky }, 100213805Shselasky 101213805Shselasky [IPHETH_BULK_TX] = { 102213805Shselasky .type = UE_BULK, 103213805Shselasky .endpoint = UE_ADDR_ANY, 104213805Shselasky .direction = UE_DIR_TX, 105213805Shselasky .frames = IPHETH_TX_FRAMES_MAX, 106213805Shselasky .bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE), 107213805Shselasky .flags = {.force_short_xfer = 1,}, 108213805Shselasky .callback = ipheth_bulk_write_callback, 109213805Shselasky .timeout = IPHETH_TX_TIMEOUT, 110213805Shselasky }, 111213805Shselasky}; 112213805Shselasky 113213805Shselaskystatic device_method_t ipheth_methods[] = { 114213805Shselasky /* Device interface */ 115213805Shselasky DEVMETHOD(device_probe, ipheth_probe), 116213805Shselasky DEVMETHOD(device_attach, ipheth_attach), 117213805Shselasky DEVMETHOD(device_detach, ipheth_detach), 118213805Shselasky 119213805Shselasky {0, 0} 120213805Shselasky}; 121213805Shselasky 122213805Shselaskystatic driver_t ipheth_driver = { 123213805Shselasky .name = "ipheth", 124213805Shselasky .methods = ipheth_methods, 125213805Shselasky .size = sizeof(struct ipheth_softc), 126213805Shselasky}; 127213805Shselasky 128213805Shselaskystatic devclass_t ipheth_devclass; 129213805Shselasky 130213805ShselaskyDRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, 0); 131213805ShselaskyMODULE_VERSION(ipheth, 1); 132213805ShselaskyMODULE_DEPEND(ipheth, uether, 1, 1, 1); 133213805ShselaskyMODULE_DEPEND(ipheth, usb, 1, 1, 1); 134213805ShselaskyMODULE_DEPEND(ipheth, ether, 1, 1, 1); 135213805Shselasky 136213805Shselaskystatic const struct usb_ether_methods ipheth_ue_methods = { 137213805Shselasky .ue_attach_post = ipheth_attach_post, 138213805Shselasky .ue_start = ipheth_start, 139213805Shselasky .ue_init = ipheth_init, 140213805Shselasky .ue_tick = ipheth_tick, 141213805Shselasky .ue_stop = ipheth_stop, 142213805Shselasky .ue_setmulti = ipheth_setmulti, 143213805Shselasky .ue_setpromisc = ipheth_setpromisc, 144213805Shselasky}; 145213805Shselasky 146213805Shselasky#define IPHETH_ID(v,p,c,sc,pt) \ 147213805Shselasky USB_VENDOR(v), USB_PRODUCT(p), \ 148213805Shselasky USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \ 149213805Shselasky USB_IFACE_PROTOCOL(pt) 150213805Shselasky 151223486Shselaskystatic const STRUCT_USB_HOST_ID ipheth_devs[] = { 152213805Shselasky {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, 153213805Shselasky IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 154213805Shselasky IPHETH_USBINTF_PROTO)}, 155213805Shselasky {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G, 156213805Shselasky IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 157213805Shselasky IPHETH_USBINTF_PROTO)}, 158213805Shselasky {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS, 159213805Shselasky IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 160213805Shselasky IPHETH_USBINTF_PROTO)}, 161213805Shselasky {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4, 162213805Shselasky IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 163213805Shselasky IPHETH_USBINTF_PROTO)}, 164213805Shselasky}; 165213805Shselasky 166213805Shselaskystatic int 167213805Shselaskyipheth_get_mac_addr(struct ipheth_softc *sc) 168213805Shselasky{ 169213805Shselasky struct usb_device_request req; 170213805Shselasky int error; 171213805Shselasky 172213805Shselasky req.bmRequestType = UT_READ_VENDOR_DEVICE; 173213805Shselasky req.bRequest = IPHETH_CMD_GET_MACADDR; 174213805Shselasky req.wValue[0] = 0; 175213805Shselasky req.wValue[1] = 0; 176213805Shselasky req.wIndex[0] = sc->sc_iface_no; 177213805Shselasky req.wIndex[1] = 0; 178213805Shselasky req.wLength[0] = ETHER_ADDR_LEN; 179213805Shselasky req.wLength[1] = 0; 180213805Shselasky 181213805Shselasky error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data); 182213805Shselasky 183213805Shselasky if (error) 184213805Shselasky return (error); 185213805Shselasky 186213805Shselasky memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN); 187213805Shselasky 188213805Shselasky return (0); 189213805Shselasky} 190213805Shselasky 191213805Shselaskystatic int 192213805Shselaskyipheth_probe(device_t dev) 193213805Shselasky{ 194213805Shselasky struct usb_attach_arg *uaa = device_get_ivars(dev); 195213805Shselasky 196213805Shselasky if (uaa->usb_mode != USB_MODE_HOST) 197213805Shselasky return (ENXIO); 198213805Shselasky 199213805Shselasky return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa)); 200213805Shselasky} 201213805Shselasky 202213805Shselaskystatic int 203213805Shselaskyipheth_attach(device_t dev) 204213805Shselasky{ 205213805Shselasky struct ipheth_softc *sc = device_get_softc(dev); 206213805Shselasky struct usb_ether *ue = &sc->sc_ue; 207213805Shselasky struct usb_attach_arg *uaa = device_get_ivars(dev); 208213805Shselasky int error; 209213805Shselasky 210213805Shselasky sc->sc_iface_no = uaa->info.bIfaceIndex; 211213805Shselasky 212213805Shselasky device_set_usb_desc(dev); 213213805Shselasky 214213805Shselasky mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); 215213805Shselasky 216213805Shselasky error = usbd_set_alt_interface_index(uaa->device, 217213805Shselasky uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM); 218213805Shselasky if (error) { 219213805Shselasky device_printf(dev, "Cannot set alternate setting\n"); 220213805Shselasky goto detach; 221213805Shselasky } 222213805Shselasky error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no, 223213805Shselasky sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx); 224213805Shselasky if (error) { 225213805Shselasky device_printf(dev, "Cannot setup USB transfers\n"); 226213805Shselasky goto detach; 227213805Shselasky } 228213805Shselasky ue->ue_sc = sc; 229213805Shselasky ue->ue_dev = dev; 230213805Shselasky ue->ue_udev = uaa->device; 231213805Shselasky ue->ue_mtx = &sc->sc_mtx; 232213805Shselasky ue->ue_methods = &ipheth_ue_methods; 233213805Shselasky 234213805Shselasky error = ipheth_get_mac_addr(sc); 235213805Shselasky if (error) { 236213805Shselasky device_printf(dev, "Cannot get MAC address\n"); 237213805Shselasky goto detach; 238213805Shselasky } 239213805Shselasky 240213805Shselasky error = uether_ifattach(ue); 241213805Shselasky if (error) { 242213805Shselasky device_printf(dev, "could not attach interface\n"); 243213805Shselasky goto detach; 244213805Shselasky } 245213805Shselasky return (0); /* success */ 246213805Shselasky 247213805Shselaskydetach: 248213805Shselasky ipheth_detach(dev); 249213805Shselasky return (ENXIO); /* failure */ 250213805Shselasky} 251213805Shselasky 252213805Shselaskystatic int 253213805Shselaskyipheth_detach(device_t dev) 254213805Shselasky{ 255213805Shselasky struct ipheth_softc *sc = device_get_softc(dev); 256213805Shselasky struct usb_ether *ue = &sc->sc_ue; 257213805Shselasky 258213805Shselasky /* stop all USB transfers first */ 259213805Shselasky usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER); 260213805Shselasky 261213805Shselasky uether_ifdetach(ue); 262213805Shselasky 263213805Shselasky mtx_destroy(&sc->sc_mtx); 264213805Shselasky 265213805Shselasky return (0); 266213805Shselasky} 267213805Shselasky 268213805Shselaskystatic void 269213805Shselaskyipheth_start(struct usb_ether *ue) 270213805Shselasky{ 271213805Shselasky struct ipheth_softc *sc = uether_getsc(ue); 272213805Shselasky 273213805Shselasky /* 274213805Shselasky * Start the USB transfers, if not already started: 275213805Shselasky */ 276213805Shselasky usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]); 277213805Shselasky usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]); 278213805Shselasky} 279213805Shselasky 280213805Shselaskystatic void 281213805Shselaskyipheth_stop(struct usb_ether *ue) 282213805Shselasky{ 283213805Shselasky struct ipheth_softc *sc = uether_getsc(ue); 284213805Shselasky 285213805Shselasky /* 286213805Shselasky * Stop the USB transfers, if not already stopped: 287213805Shselasky */ 288213805Shselasky usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]); 289213805Shselasky usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]); 290213805Shselasky} 291213805Shselasky 292213805Shselaskystatic void 293213805Shselaskyipheth_tick(struct usb_ether *ue) 294213805Shselasky{ 295213805Shselasky struct ipheth_softc *sc = uether_getsc(ue); 296213805Shselasky struct usb_device_request req; 297213805Shselasky int error; 298213805Shselasky 299213805Shselasky req.bmRequestType = UT_READ_VENDOR_DEVICE; 300213805Shselasky req.bRequest = IPHETH_CMD_CARRIER_CHECK; 301213805Shselasky req.wValue[0] = 0; 302213805Shselasky req.wValue[1] = 0; 303213805Shselasky req.wIndex[0] = sc->sc_iface_no; 304213805Shselasky req.wIndex[1] = 0; 305213805Shselasky req.wLength[0] = IPHETH_CTRL_BUF_SIZE; 306213805Shselasky req.wLength[1] = 0; 307213805Shselasky 308213805Shselasky error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT); 309213805Shselasky 310213805Shselasky if (error) 311213805Shselasky return; 312213805Shselasky 313213805Shselasky sc->sc_carrier_on = 314213805Shselasky (sc->sc_data[0] == IPHETH_CARRIER_ON); 315213805Shselasky} 316213805Shselasky 317213805Shselaskystatic void 318213805Shselaskyipheth_attach_post(struct usb_ether *ue) 319213805Shselasky{ 320213805Shselasky 321213805Shselasky} 322213805Shselasky 323213805Shselaskystatic void 324213805Shselaskyipheth_init(struct usb_ether *ue) 325213805Shselasky{ 326213805Shselasky struct ipheth_softc *sc = uether_getsc(ue); 327213805Shselasky struct ifnet *ifp = uether_getifp(ue); 328213805Shselasky 329213805Shselasky IPHETH_LOCK_ASSERT(sc, MA_OWNED); 330213805Shselasky 331213805Shselasky ifp->if_drv_flags |= IFF_DRV_RUNNING; 332213805Shselasky 333213805Shselasky /* stall data write direction, which depends on USB mode */ 334213805Shselasky usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]); 335213805Shselasky 336213805Shselasky /* start data transfers */ 337213805Shselasky ipheth_start(ue); 338213805Shselasky} 339213805Shselasky 340213805Shselaskystatic void 341213805Shselaskyipheth_setmulti(struct usb_ether *ue) 342213805Shselasky{ 343213805Shselasky 344213805Shselasky} 345213805Shselasky 346213805Shselaskystatic void 347213805Shselaskyipheth_setpromisc(struct usb_ether *ue) 348213805Shselasky{ 349213805Shselasky 350213805Shselasky} 351213805Shselasky 352213805Shselaskystatic void 353213805Shselaskyipheth_free_queue(struct mbuf **ppm, uint8_t n) 354213805Shselasky{ 355213805Shselasky uint8_t x; 356213805Shselasky 357213805Shselasky for (x = 0; x != n; x++) { 358213805Shselasky if (ppm[x] != NULL) { 359213805Shselasky m_freem(ppm[x]); 360213805Shselasky ppm[x] = NULL; 361213805Shselasky } 362213805Shselasky } 363213805Shselasky} 364213805Shselasky 365213805Shselaskystatic void 366213805Shselaskyipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 367213805Shselasky{ 368213805Shselasky struct ipheth_softc *sc = usbd_xfer_softc(xfer); 369213805Shselasky struct ifnet *ifp = uether_getifp(&sc->sc_ue); 370213805Shselasky struct usb_page_cache *pc; 371213805Shselasky struct mbuf *m; 372213805Shselasky uint8_t x; 373213805Shselasky int actlen; 374213805Shselasky int aframes; 375213805Shselasky 376213805Shselasky usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 377213805Shselasky 378213805Shselasky DPRINTFN(1, "\n"); 379213805Shselasky 380213805Shselasky switch (USB_GET_STATE(xfer)) { 381213805Shselasky case USB_ST_TRANSFERRED: 382213805Shselasky DPRINTFN(11, "transfer complete: %u bytes in %u frames\n", 383213805Shselasky actlen, aframes); 384213805Shselasky 385213805Shselasky ifp->if_opackets++; 386213805Shselasky 387213805Shselasky /* free all previous TX buffers */ 388213805Shselasky ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX); 389213805Shselasky 390213805Shselasky /* FALLTHROUGH */ 391213805Shselasky case USB_ST_SETUP: 392213805Shselaskytr_setup: 393213805Shselasky for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) { 394213805Shselasky 395213805Shselasky IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 396213805Shselasky 397213805Shselasky if (m == NULL) 398213805Shselasky break; 399213805Shselasky 400213805Shselasky usbd_xfer_set_frame_offset(xfer, 401213805Shselasky x * IPHETH_BUF_SIZE, x); 402213805Shselasky 403213805Shselasky pc = usbd_xfer_get_frame(xfer, x); 404213805Shselasky 405213805Shselasky sc->sc_tx_buf[x] = m; 406213805Shselasky 407213805Shselasky if (m->m_pkthdr.len > IPHETH_BUF_SIZE) 408213805Shselasky m->m_pkthdr.len = IPHETH_BUF_SIZE; 409213805Shselasky 410213805Shselasky usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 411213805Shselasky 412213805Shselasky usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE); 413213805Shselasky 414213805Shselasky if (IPHETH_BUF_SIZE != m->m_pkthdr.len) { 415213805Shselasky usbd_frame_zero(pc, m->m_pkthdr.len, 416213805Shselasky IPHETH_BUF_SIZE - m->m_pkthdr.len); 417213805Shselasky } 418213805Shselasky 419213805Shselasky /* 420213805Shselasky * If there's a BPF listener, bounce a copy of 421213805Shselasky * this frame to him: 422213805Shselasky */ 423213805Shselasky BPF_MTAP(ifp, m); 424213805Shselasky } 425213805Shselasky if (x != 0) { 426213805Shselasky usbd_xfer_set_frames(xfer, x); 427213805Shselasky 428213805Shselasky usbd_transfer_submit(xfer); 429213805Shselasky } 430213805Shselasky break; 431213805Shselasky 432213805Shselasky default: /* Error */ 433213805Shselasky DPRINTFN(11, "transfer error, %s\n", 434213805Shselasky usbd_errstr(error)); 435213805Shselasky 436213805Shselasky /* free all previous TX buffers */ 437213805Shselasky ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX); 438213805Shselasky 439213805Shselasky /* count output errors */ 440213805Shselasky ifp->if_oerrors++; 441213805Shselasky 442213805Shselasky if (error != USB_ERR_CANCELLED) { 443213805Shselasky /* try to clear stall first */ 444213805Shselasky usbd_xfer_set_stall(xfer); 445213805Shselasky goto tr_setup; 446213805Shselasky } 447213805Shselasky break; 448213805Shselasky } 449213805Shselasky} 450213805Shselasky 451213805Shselaskystatic void 452213805Shselaskyipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 453213805Shselasky{ 454213805Shselasky struct ipheth_softc *sc = usbd_xfer_softc(xfer); 455213805Shselasky struct mbuf *m; 456213805Shselasky uint8_t x; 457213805Shselasky int actlen; 458213805Shselasky int aframes; 459213805Shselasky int len; 460213805Shselasky 461213805Shselasky usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 462213805Shselasky 463213805Shselasky switch (USB_GET_STATE(xfer)) { 464213805Shselasky case USB_ST_TRANSFERRED: 465213805Shselasky 466213805Shselasky DPRINTF("received %u bytes in %u frames\n", actlen, aframes); 467213805Shselasky 468213805Shselasky for (x = 0; x != aframes; x++) { 469213805Shselasky 470213805Shselasky m = sc->sc_rx_buf[x]; 471213805Shselasky sc->sc_rx_buf[x] = NULL; 472213805Shselasky len = usbd_xfer_frame_len(xfer, x); 473213805Shselasky 474213805Shselasky if (len < (sizeof(struct ether_header) + 475213805Shselasky IPHETH_RX_ADJ)) { 476213805Shselasky m_freem(m); 477213805Shselasky continue; 478213805Shselasky } 479213805Shselasky 480213805Shselasky m_adj(m, IPHETH_RX_ADJ); 481213805Shselasky 482213805Shselasky /* queue up mbuf */ 483213805Shselasky uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ); 484213805Shselasky } 485213805Shselasky 486213805Shselasky /* FALLTHROUGH */ 487213805Shselasky case USB_ST_SETUP: 488213805Shselasky 489213805Shselasky for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) { 490213805Shselasky if (sc->sc_rx_buf[x] == NULL) { 491213805Shselasky m = uether_newbuf(); 492213805Shselasky if (m == NULL) 493213805Shselasky goto tr_stall; 494213805Shselasky 495213805Shselasky /* cancel alignment for ethernet */ 496213805Shselasky m_adj(m, ETHER_ALIGN); 497213805Shselasky 498213805Shselasky sc->sc_rx_buf[x] = m; 499213805Shselasky } else { 500213805Shselasky m = sc->sc_rx_buf[x]; 501213805Shselasky } 502213805Shselasky 503213805Shselasky usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); 504213805Shselasky } 505213805Shselasky /* set number of frames and start hardware */ 506213805Shselasky usbd_xfer_set_frames(xfer, x); 507213805Shselasky usbd_transfer_submit(xfer); 508213805Shselasky /* flush any received frames */ 509213805Shselasky uether_rxflush(&sc->sc_ue); 510213805Shselasky break; 511213805Shselasky 512213805Shselasky default: /* Error */ 513213805Shselasky DPRINTF("error = %s\n", usbd_errstr(error)); 514213805Shselasky 515213805Shselasky if (error != USB_ERR_CANCELLED) { 516213805Shselasky tr_stall: 517213805Shselasky /* try to clear stall first */ 518213805Shselasky usbd_xfer_set_stall(xfer); 519213805Shselasky usbd_xfer_set_frames(xfer, 0); 520213805Shselasky usbd_transfer_submit(xfer); 521213805Shselasky break; 522213805Shselasky } 523213805Shselasky /* need to free the RX-mbufs when we are cancelled */ 524213805Shselasky ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX); 525213805Shselasky break; 526213805Shselasky } 527213805Shselasky} 528