if_ipheth.c revision 276701
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 276701 2015-01-05 15:04:17Z hselasky $"); 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> 47257241Sglebius#include <sys/socket.h> 48213805Shselasky#include <sys/sysctl.h> 49213805Shselasky#include <sys/sx.h> 50213805Shselasky#include <sys/unistd.h> 51213805Shselasky#include <sys/callout.h> 52213805Shselasky#include <sys/malloc.h> 53213805Shselasky#include <sys/priv.h> 54213805Shselasky 55257241Sglebius#include <net/if.h> 56257241Sglebius#include <net/if_var.h> 57257241Sglebius 58213805Shselasky#include <dev/usb/usb.h> 59213805Shselasky#include <dev/usb/usbdi.h> 60213805Shselasky#include <dev/usb/usbdi_util.h> 61213805Shselasky#include "usbdevs.h" 62213805Shselasky 63213805Shselasky#define USB_DEBUG_VAR ipheth_debug 64213805Shselasky#include <dev/usb/usb_debug.h> 65213805Shselasky#include <dev/usb/usb_process.h> 66213805Shselasky 67213805Shselasky#include <dev/usb/net/usb_ethernet.h> 68213805Shselasky#include <dev/usb/net/if_iphethvar.h> 69213805Shselasky 70213805Shselaskystatic device_probe_t ipheth_probe; 71213805Shselaskystatic device_attach_t ipheth_attach; 72213805Shselaskystatic device_detach_t ipheth_detach; 73213805Shselasky 74213805Shselaskystatic usb_callback_t ipheth_bulk_write_callback; 75213805Shselaskystatic usb_callback_t ipheth_bulk_read_callback; 76213805Shselasky 77213805Shselaskystatic uether_fn_t ipheth_attach_post; 78213805Shselaskystatic uether_fn_t ipheth_tick; 79213805Shselaskystatic uether_fn_t ipheth_init; 80213805Shselaskystatic uether_fn_t ipheth_stop; 81213805Shselaskystatic uether_fn_t ipheth_start; 82213805Shselaskystatic uether_fn_t ipheth_setmulti; 83213805Shselaskystatic uether_fn_t ipheth_setpromisc; 84213805Shselasky 85213805Shselasky#ifdef USB_DEBUG 86213805Shselaskystatic int ipheth_debug = 0; 87213805Shselasky 88227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet"); 89276701ShselaskySYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RWTUN, &ipheth_debug, 0, "Debug level"); 90213805Shselasky#endif 91213805Shselasky 92213805Shselaskystatic const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = { 93213805Shselasky 94213805Shselasky [IPHETH_BULK_RX] = { 95213805Shselasky .type = UE_BULK, 96213805Shselasky .endpoint = UE_ADDR_ANY, 97213805Shselasky .direction = UE_DIR_RX, 98213805Shselasky .frames = IPHETH_RX_FRAMES_MAX, 99213805Shselasky .bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES), 100213805Shselasky .flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, 101213805Shselasky .callback = ipheth_bulk_read_callback, 102213805Shselasky .timeout = 0, /* no timeout */ 103213805Shselasky }, 104213805Shselasky 105213805Shselasky [IPHETH_BULK_TX] = { 106213805Shselasky .type = UE_BULK, 107213805Shselasky .endpoint = UE_ADDR_ANY, 108213805Shselasky .direction = UE_DIR_TX, 109213805Shselasky .frames = IPHETH_TX_FRAMES_MAX, 110213805Shselasky .bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE), 111213805Shselasky .flags = {.force_short_xfer = 1,}, 112213805Shselasky .callback = ipheth_bulk_write_callback, 113213805Shselasky .timeout = IPHETH_TX_TIMEOUT, 114213805Shselasky }, 115213805Shselasky}; 116213805Shselasky 117213805Shselaskystatic device_method_t ipheth_methods[] = { 118213805Shselasky /* Device interface */ 119213805Shselasky DEVMETHOD(device_probe, ipheth_probe), 120213805Shselasky DEVMETHOD(device_attach, ipheth_attach), 121213805Shselasky DEVMETHOD(device_detach, ipheth_detach), 122213805Shselasky 123246128Ssbz DEVMETHOD_END 124213805Shselasky}; 125213805Shselasky 126213805Shselaskystatic driver_t ipheth_driver = { 127213805Shselasky .name = "ipheth", 128213805Shselasky .methods = ipheth_methods, 129213805Shselasky .size = sizeof(struct ipheth_softc), 130213805Shselasky}; 131213805Shselasky 132213805Shselaskystatic devclass_t ipheth_devclass; 133213805Shselasky 134213805ShselaskyDRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, 0); 135213805ShselaskyMODULE_VERSION(ipheth, 1); 136213805ShselaskyMODULE_DEPEND(ipheth, uether, 1, 1, 1); 137213805ShselaskyMODULE_DEPEND(ipheth, usb, 1, 1, 1); 138213805ShselaskyMODULE_DEPEND(ipheth, ether, 1, 1, 1); 139213805Shselasky 140213805Shselaskystatic const struct usb_ether_methods ipheth_ue_methods = { 141213805Shselasky .ue_attach_post = ipheth_attach_post, 142213805Shselasky .ue_start = ipheth_start, 143213805Shselasky .ue_init = ipheth_init, 144213805Shselasky .ue_tick = ipheth_tick, 145213805Shselasky .ue_stop = ipheth_stop, 146213805Shselasky .ue_setmulti = ipheth_setmulti, 147213805Shselasky .ue_setpromisc = ipheth_setpromisc, 148213805Shselasky}; 149213805Shselasky 150213805Shselasky#define IPHETH_ID(v,p,c,sc,pt) \ 151213805Shselasky USB_VENDOR(v), USB_PRODUCT(p), \ 152213805Shselasky USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \ 153213805Shselasky USB_IFACE_PROTOCOL(pt) 154213805Shselasky 155223486Shselaskystatic const STRUCT_USB_HOST_ID ipheth_devs[] = { 156253670Shselasky#if 0 157213805Shselasky {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, 158213805Shselasky IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 159213805Shselasky IPHETH_USBINTF_PROTO)}, 160213805Shselasky {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G, 161213805Shselasky IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 162213805Shselasky IPHETH_USBINTF_PROTO)}, 163213805Shselasky {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS, 164213805Shselasky IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 165213805Shselasky IPHETH_USBINTF_PROTO)}, 166213805Shselasky {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4, 167213805Shselasky IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 168213805Shselasky IPHETH_USBINTF_PROTO)}, 169251109Seadler {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S, 170251109Seadler IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 171251109Seadler IPHETH_USBINTF_PROTO)}, 172241793Seadler {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_5, 173241793Seadler IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, 174241793Seadler IPHETH_USBINTF_PROTO)}, 175253670Shselasky#else 176253670Shselasky /* product agnostic interface match */ 177253670Shselasky {USB_VENDOR(USB_VENDOR_APPLE), 178253670Shselasky USB_IFACE_CLASS(IPHETH_USBINTF_CLASS), 179253670Shselasky USB_IFACE_SUBCLASS(IPHETH_USBINTF_SUBCLASS), 180253670Shselasky USB_IFACE_PROTOCOL(IPHETH_USBINTF_PROTO)}, 181253670Shselasky#endif 182213805Shselasky}; 183213805Shselasky 184213805Shselaskystatic int 185213805Shselaskyipheth_get_mac_addr(struct ipheth_softc *sc) 186213805Shselasky{ 187213805Shselasky struct usb_device_request req; 188213805Shselasky int error; 189213805Shselasky 190213805Shselasky req.bmRequestType = UT_READ_VENDOR_DEVICE; 191213805Shselasky req.bRequest = IPHETH_CMD_GET_MACADDR; 192213805Shselasky req.wValue[0] = 0; 193213805Shselasky req.wValue[1] = 0; 194213805Shselasky req.wIndex[0] = sc->sc_iface_no; 195213805Shselasky req.wIndex[1] = 0; 196213805Shselasky req.wLength[0] = ETHER_ADDR_LEN; 197213805Shselasky req.wLength[1] = 0; 198213805Shselasky 199213805Shselasky error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data); 200213805Shselasky 201213805Shselasky if (error) 202213805Shselasky return (error); 203213805Shselasky 204213805Shselasky memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN); 205213805Shselasky 206213805Shselasky return (0); 207213805Shselasky} 208213805Shselasky 209213805Shselaskystatic int 210213805Shselaskyipheth_probe(device_t dev) 211213805Shselasky{ 212213805Shselasky struct usb_attach_arg *uaa = device_get_ivars(dev); 213213805Shselasky 214213805Shselasky if (uaa->usb_mode != USB_MODE_HOST) 215213805Shselasky return (ENXIO); 216213805Shselasky 217213805Shselasky return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa)); 218213805Shselasky} 219213805Shselasky 220213805Shselaskystatic int 221213805Shselaskyipheth_attach(device_t dev) 222213805Shselasky{ 223213805Shselasky struct ipheth_softc *sc = device_get_softc(dev); 224213805Shselasky struct usb_ether *ue = &sc->sc_ue; 225213805Shselasky struct usb_attach_arg *uaa = device_get_ivars(dev); 226213805Shselasky int error; 227213805Shselasky 228213805Shselasky sc->sc_iface_no = uaa->info.bIfaceIndex; 229213805Shselasky 230213805Shselasky device_set_usb_desc(dev); 231213805Shselasky 232213805Shselasky mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); 233213805Shselasky 234213805Shselasky error = usbd_set_alt_interface_index(uaa->device, 235213805Shselasky uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM); 236213805Shselasky if (error) { 237213805Shselasky device_printf(dev, "Cannot set alternate setting\n"); 238213805Shselasky goto detach; 239213805Shselasky } 240213805Shselasky error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no, 241213805Shselasky sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx); 242213805Shselasky if (error) { 243213805Shselasky device_printf(dev, "Cannot setup USB transfers\n"); 244213805Shselasky goto detach; 245213805Shselasky } 246213805Shselasky ue->ue_sc = sc; 247213805Shselasky ue->ue_dev = dev; 248213805Shselasky ue->ue_udev = uaa->device; 249213805Shselasky ue->ue_mtx = &sc->sc_mtx; 250213805Shselasky ue->ue_methods = &ipheth_ue_methods; 251213805Shselasky 252213805Shselasky error = ipheth_get_mac_addr(sc); 253213805Shselasky if (error) { 254213805Shselasky device_printf(dev, "Cannot get MAC address\n"); 255213805Shselasky goto detach; 256213805Shselasky } 257213805Shselasky 258213805Shselasky error = uether_ifattach(ue); 259213805Shselasky if (error) { 260213805Shselasky device_printf(dev, "could not attach interface\n"); 261213805Shselasky goto detach; 262213805Shselasky } 263213805Shselasky return (0); /* success */ 264213805Shselasky 265213805Shselaskydetach: 266213805Shselasky ipheth_detach(dev); 267213805Shselasky return (ENXIO); /* failure */ 268213805Shselasky} 269213805Shselasky 270213805Shselaskystatic int 271213805Shselaskyipheth_detach(device_t dev) 272213805Shselasky{ 273213805Shselasky struct ipheth_softc *sc = device_get_softc(dev); 274213805Shselasky struct usb_ether *ue = &sc->sc_ue; 275213805Shselasky 276213805Shselasky /* stop all USB transfers first */ 277213805Shselasky usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER); 278213805Shselasky 279213805Shselasky uether_ifdetach(ue); 280213805Shselasky 281213805Shselasky mtx_destroy(&sc->sc_mtx); 282213805Shselasky 283213805Shselasky return (0); 284213805Shselasky} 285213805Shselasky 286213805Shselaskystatic void 287213805Shselaskyipheth_start(struct usb_ether *ue) 288213805Shselasky{ 289213805Shselasky struct ipheth_softc *sc = uether_getsc(ue); 290213805Shselasky 291213805Shselasky /* 292213805Shselasky * Start the USB transfers, if not already started: 293213805Shselasky */ 294213805Shselasky usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]); 295213805Shselasky usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]); 296213805Shselasky} 297213805Shselasky 298213805Shselaskystatic void 299213805Shselaskyipheth_stop(struct usb_ether *ue) 300213805Shselasky{ 301213805Shselasky struct ipheth_softc *sc = uether_getsc(ue); 302213805Shselasky 303213805Shselasky /* 304213805Shselasky * Stop the USB transfers, if not already stopped: 305213805Shselasky */ 306213805Shselasky usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]); 307213805Shselasky usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]); 308213805Shselasky} 309213805Shselasky 310213805Shselaskystatic void 311213805Shselaskyipheth_tick(struct usb_ether *ue) 312213805Shselasky{ 313213805Shselasky struct ipheth_softc *sc = uether_getsc(ue); 314213805Shselasky struct usb_device_request req; 315213805Shselasky int error; 316213805Shselasky 317213805Shselasky req.bmRequestType = UT_READ_VENDOR_DEVICE; 318213805Shselasky req.bRequest = IPHETH_CMD_CARRIER_CHECK; 319213805Shselasky req.wValue[0] = 0; 320213805Shselasky req.wValue[1] = 0; 321213805Shselasky req.wIndex[0] = sc->sc_iface_no; 322213805Shselasky req.wIndex[1] = 0; 323213805Shselasky req.wLength[0] = IPHETH_CTRL_BUF_SIZE; 324213805Shselasky req.wLength[1] = 0; 325213805Shselasky 326213805Shselasky error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT); 327213805Shselasky 328213805Shselasky if (error) 329213805Shselasky return; 330213805Shselasky 331213805Shselasky sc->sc_carrier_on = 332213805Shselasky (sc->sc_data[0] == IPHETH_CARRIER_ON); 333213805Shselasky} 334213805Shselasky 335213805Shselaskystatic void 336213805Shselaskyipheth_attach_post(struct usb_ether *ue) 337213805Shselasky{ 338213805Shselasky 339213805Shselasky} 340213805Shselasky 341213805Shselaskystatic void 342213805Shselaskyipheth_init(struct usb_ether *ue) 343213805Shselasky{ 344213805Shselasky struct ipheth_softc *sc = uether_getsc(ue); 345213805Shselasky struct ifnet *ifp = uether_getifp(ue); 346213805Shselasky 347213805Shselasky IPHETH_LOCK_ASSERT(sc, MA_OWNED); 348213805Shselasky 349213805Shselasky ifp->if_drv_flags |= IFF_DRV_RUNNING; 350213805Shselasky 351213805Shselasky /* stall data write direction, which depends on USB mode */ 352213805Shselasky usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]); 353213805Shselasky 354213805Shselasky /* start data transfers */ 355213805Shselasky ipheth_start(ue); 356213805Shselasky} 357213805Shselasky 358213805Shselaskystatic void 359213805Shselaskyipheth_setmulti(struct usb_ether *ue) 360213805Shselasky{ 361213805Shselasky 362213805Shselasky} 363213805Shselasky 364213805Shselaskystatic void 365213805Shselaskyipheth_setpromisc(struct usb_ether *ue) 366213805Shselasky{ 367213805Shselasky 368213805Shselasky} 369213805Shselasky 370213805Shselaskystatic void 371213805Shselaskyipheth_free_queue(struct mbuf **ppm, uint8_t n) 372213805Shselasky{ 373213805Shselasky uint8_t x; 374213805Shselasky 375213805Shselasky for (x = 0; x != n; x++) { 376213805Shselasky if (ppm[x] != NULL) { 377213805Shselasky m_freem(ppm[x]); 378213805Shselasky ppm[x] = NULL; 379213805Shselasky } 380213805Shselasky } 381213805Shselasky} 382213805Shselasky 383213805Shselaskystatic void 384213805Shselaskyipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 385213805Shselasky{ 386213805Shselasky struct ipheth_softc *sc = usbd_xfer_softc(xfer); 387213805Shselasky struct ifnet *ifp = uether_getifp(&sc->sc_ue); 388213805Shselasky struct usb_page_cache *pc; 389213805Shselasky struct mbuf *m; 390213805Shselasky uint8_t x; 391213805Shselasky int actlen; 392213805Shselasky int aframes; 393213805Shselasky 394213805Shselasky usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 395213805Shselasky 396213805Shselasky DPRINTFN(1, "\n"); 397213805Shselasky 398213805Shselasky switch (USB_GET_STATE(xfer)) { 399213805Shselasky case USB_ST_TRANSFERRED: 400213805Shselasky DPRINTFN(11, "transfer complete: %u bytes in %u frames\n", 401213805Shselasky actlen, aframes); 402213805Shselasky 403271832Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 404213805Shselasky 405213805Shselasky /* free all previous TX buffers */ 406213805Shselasky ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX); 407213805Shselasky 408213805Shselasky /* FALLTHROUGH */ 409213805Shselasky case USB_ST_SETUP: 410213805Shselaskytr_setup: 411213805Shselasky for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) { 412213805Shselasky 413213805Shselasky IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 414213805Shselasky 415213805Shselasky if (m == NULL) 416213805Shselasky break; 417213805Shselasky 418213805Shselasky usbd_xfer_set_frame_offset(xfer, 419213805Shselasky x * IPHETH_BUF_SIZE, x); 420213805Shselasky 421213805Shselasky pc = usbd_xfer_get_frame(xfer, x); 422213805Shselasky 423213805Shselasky sc->sc_tx_buf[x] = m; 424213805Shselasky 425213805Shselasky if (m->m_pkthdr.len > IPHETH_BUF_SIZE) 426213805Shselasky m->m_pkthdr.len = IPHETH_BUF_SIZE; 427213805Shselasky 428213805Shselasky usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 429213805Shselasky 430213805Shselasky usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE); 431213805Shselasky 432213805Shselasky if (IPHETH_BUF_SIZE != m->m_pkthdr.len) { 433213805Shselasky usbd_frame_zero(pc, m->m_pkthdr.len, 434213805Shselasky IPHETH_BUF_SIZE - m->m_pkthdr.len); 435213805Shselasky } 436213805Shselasky 437213805Shselasky /* 438213805Shselasky * If there's a BPF listener, bounce a copy of 439213805Shselasky * this frame to him: 440213805Shselasky */ 441213805Shselasky BPF_MTAP(ifp, m); 442213805Shselasky } 443213805Shselasky if (x != 0) { 444213805Shselasky usbd_xfer_set_frames(xfer, x); 445213805Shselasky 446213805Shselasky usbd_transfer_submit(xfer); 447213805Shselasky } 448213805Shselasky break; 449213805Shselasky 450213805Shselasky default: /* Error */ 451213805Shselasky DPRINTFN(11, "transfer error, %s\n", 452213805Shselasky usbd_errstr(error)); 453213805Shselasky 454213805Shselasky /* free all previous TX buffers */ 455213805Shselasky ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX); 456213805Shselasky 457213805Shselasky /* count output errors */ 458271832Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 459213805Shselasky 460213805Shselasky if (error != USB_ERR_CANCELLED) { 461213805Shselasky /* try to clear stall first */ 462213805Shselasky usbd_xfer_set_stall(xfer); 463213805Shselasky goto tr_setup; 464213805Shselasky } 465213805Shselasky break; 466213805Shselasky } 467213805Shselasky} 468213805Shselasky 469213805Shselaskystatic void 470213805Shselaskyipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 471213805Shselasky{ 472213805Shselasky struct ipheth_softc *sc = usbd_xfer_softc(xfer); 473213805Shselasky struct mbuf *m; 474213805Shselasky uint8_t x; 475213805Shselasky int actlen; 476213805Shselasky int aframes; 477213805Shselasky int len; 478213805Shselasky 479213805Shselasky usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 480213805Shselasky 481213805Shselasky switch (USB_GET_STATE(xfer)) { 482213805Shselasky case USB_ST_TRANSFERRED: 483213805Shselasky 484213805Shselasky DPRINTF("received %u bytes in %u frames\n", actlen, aframes); 485213805Shselasky 486213805Shselasky for (x = 0; x != aframes; x++) { 487213805Shselasky 488213805Shselasky m = sc->sc_rx_buf[x]; 489213805Shselasky sc->sc_rx_buf[x] = NULL; 490213805Shselasky len = usbd_xfer_frame_len(xfer, x); 491213805Shselasky 492233774Shselasky if (len < (int)(sizeof(struct ether_header) + 493213805Shselasky IPHETH_RX_ADJ)) { 494213805Shselasky m_freem(m); 495213805Shselasky continue; 496213805Shselasky } 497213805Shselasky 498213805Shselasky m_adj(m, IPHETH_RX_ADJ); 499213805Shselasky 500213805Shselasky /* queue up mbuf */ 501213805Shselasky uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ); 502213805Shselasky } 503213805Shselasky 504213805Shselasky /* FALLTHROUGH */ 505213805Shselasky case USB_ST_SETUP: 506213805Shselasky 507213805Shselasky for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) { 508213805Shselasky if (sc->sc_rx_buf[x] == NULL) { 509213805Shselasky m = uether_newbuf(); 510213805Shselasky if (m == NULL) 511213805Shselasky goto tr_stall; 512213805Shselasky 513213805Shselasky /* cancel alignment for ethernet */ 514213805Shselasky m_adj(m, ETHER_ALIGN); 515213805Shselasky 516213805Shselasky sc->sc_rx_buf[x] = m; 517213805Shselasky } else { 518213805Shselasky m = sc->sc_rx_buf[x]; 519213805Shselasky } 520213805Shselasky 521213805Shselasky usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); 522213805Shselasky } 523213805Shselasky /* set number of frames and start hardware */ 524213805Shselasky usbd_xfer_set_frames(xfer, x); 525213805Shselasky usbd_transfer_submit(xfer); 526213805Shselasky /* flush any received frames */ 527213805Shselasky uether_rxflush(&sc->sc_ue); 528213805Shselasky break; 529213805Shselasky 530213805Shselasky default: /* Error */ 531213805Shselasky DPRINTF("error = %s\n", usbd_errstr(error)); 532213805Shselasky 533213805Shselasky if (error != USB_ERR_CANCELLED) { 534213805Shselasky tr_stall: 535213805Shselasky /* try to clear stall first */ 536213805Shselasky usbd_xfer_set_stall(xfer); 537213805Shselasky usbd_xfer_set_frames(xfer, 0); 538213805Shselasky usbd_transfer_submit(xfer); 539213805Shselasky break; 540213805Shselasky } 541213805Shselasky /* need to free the RX-mbufs when we are cancelled */ 542213805Shselasky ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX); 543213805Shselasky break; 544213805Shselasky } 545213805Shselasky} 546