1184610Salfred/* $NetBSD: if_cdce.c,v 1.4 2004/10/24 12:50:54 augustss Exp $ */ 2184610Salfred 3184610Salfred/*- 4184610Salfred * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul <wpaul@windriver.com> 5184610Salfred * Copyright (c) 2003-2005 Craig Boston 6184610Salfred * Copyright (c) 2004 Daniel Hartmeier 7188412Sthompsa * Copyright (c) 2009 Hans Petter Selasky 8184610Salfred * All rights reserved. 9184610Salfred * 10184610Salfred * Redistribution and use in source and binary forms, with or without 11184610Salfred * modification, are permitted provided that the following conditions 12184610Salfred * are met: 13184610Salfred * 1. Redistributions of source code must retain the above copyright 14184610Salfred * notice, this list of conditions and the following disclaimer. 15184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 16184610Salfred * notice, this list of conditions and the following disclaimer in the 17184610Salfred * documentation and/or other materials provided with the distribution. 18184610Salfred * 3. All advertising materials mentioning features or use of this software 19184610Salfred * must display the following acknowledgement: 20184610Salfred * This product includes software developed by Bill Paul. 21184610Salfred * 4. Neither the name of the author nor the names of any co-contributors 22184610Salfred * may be used to endorse or promote products derived from this software 23184610Salfred * without specific prior written permission. 24184610Salfred * 25184610Salfred * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 26184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul, THE VOICES IN HIS HEAD OR 29184610Salfred * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 30184610Salfred * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 31184610Salfred * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 32184610Salfred * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 33184610Salfred * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34184610Salfred * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 35184610Salfred * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36184610Salfred */ 37184610Salfred 38184610Salfred/* 39184610Salfred * USB Communication Device Class (Ethernet Networking Control Model) 40184610Salfred * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf 41184610Salfred */ 42184610Salfred 43197563Sthompsa/* 44197563Sthompsa * USB Network Control Model (NCM) 45197563Sthompsa * http://www.usb.org/developers/devclass_docs/NCM10.zip 46197563Sthompsa */ 47197563Sthompsa 48184610Salfred#include <sys/cdefs.h> 49184610Salfred__FBSDID("$FreeBSD: releng/11.0/sys/dev/usb/net/if_cdce.c 292080 2015-12-11 05:28:00Z imp $"); 50184610Salfred 51194677Sthompsa#include <sys/stdint.h> 52194677Sthompsa#include <sys/stddef.h> 53194677Sthompsa#include <sys/param.h> 54194677Sthompsa#include <sys/queue.h> 55194677Sthompsa#include <sys/types.h> 56194677Sthompsa#include <sys/systm.h> 57257176Sglebius#include <sys/socket.h> 58194677Sthompsa#include <sys/kernel.h> 59194677Sthompsa#include <sys/bus.h> 60194677Sthompsa#include <sys/module.h> 61194677Sthompsa#include <sys/lock.h> 62194677Sthompsa#include <sys/mutex.h> 63194677Sthompsa#include <sys/condvar.h> 64194677Sthompsa#include <sys/sysctl.h> 65194677Sthompsa#include <sys/sx.h> 66194677Sthompsa#include <sys/unistd.h> 67194677Sthompsa#include <sys/callout.h> 68194677Sthompsa#include <sys/malloc.h> 69194677Sthompsa#include <sys/priv.h> 70194677Sthompsa 71257176Sglebius#include <net/if.h> 72257176Sglebius#include <net/if_var.h> 73257176Sglebius 74188942Sthompsa#include <dev/usb/usb.h> 75194677Sthompsa#include <dev/usb/usbdi.h> 76194677Sthompsa#include <dev/usb/usbdi_util.h> 77188942Sthompsa#include <dev/usb/usb_cdc.h> 78194677Sthompsa#include "usbdevs.h" 79184610Salfred 80184610Salfred#define USB_DEBUG_VAR cdce_debug 81194677Sthompsa#include <dev/usb/usb_debug.h> 82188942Sthompsa#include <dev/usb/usb_process.h> 83269584Sn_hibma#include <dev/usb/usb_msctest.h> 84194677Sthompsa#include "usb_if.h" 85184610Salfred 86188942Sthompsa#include <dev/usb/net/usb_ethernet.h> 87188942Sthompsa#include <dev/usb/net/if_cdcereg.h> 88184610Salfred 89184610Salfredstatic device_probe_t cdce_probe; 90184610Salfredstatic device_attach_t cdce_attach; 91184610Salfredstatic device_detach_t cdce_detach; 92184610Salfredstatic device_suspend_t cdce_suspend; 93184610Salfredstatic device_resume_t cdce_resume; 94188942Sthompsastatic usb_handle_request_t cdce_handle_request; 95184610Salfred 96193045Sthompsastatic usb_callback_t cdce_bulk_write_callback; 97193045Sthompsastatic usb_callback_t cdce_bulk_read_callback; 98193045Sthompsastatic usb_callback_t cdce_intr_read_callback; 99193045Sthompsastatic usb_callback_t cdce_intr_write_callback; 100184610Salfred 101197563Sthompsa#if CDCE_HAVE_NCM 102197563Sthompsastatic usb_callback_t cdce_ncm_bulk_write_callback; 103197563Sthompsastatic usb_callback_t cdce_ncm_bulk_read_callback; 104197563Sthompsa#endif 105197563Sthompsa 106193045Sthompsastatic uether_fn_t cdce_attach_post; 107193045Sthompsastatic uether_fn_t cdce_init; 108193045Sthompsastatic uether_fn_t cdce_stop; 109193045Sthompsastatic uether_fn_t cdce_start; 110193045Sthompsastatic uether_fn_t cdce_setmulti; 111193045Sthompsastatic uether_fn_t cdce_setpromisc; 112188412Sthompsa 113185948Sthompsastatic uint32_t cdce_m_crc32(struct mbuf *, uint32_t, uint32_t); 114184610Salfred 115207077Sthompsa#ifdef USB_DEBUG 116184610Salfredstatic int cdce_debug = 0; 117213809Shselaskystatic int cdce_tx_interval = 0; 118184610Salfred 119227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet"); 120276701ShselaskySYSCTL_INT(_hw_usb_cdce, OID_AUTO, debug, CTLFLAG_RWTUN, &cdce_debug, 0, 121188412Sthompsa "Debug level"); 122276701ShselaskySYSCTL_INT(_hw_usb_cdce, OID_AUTO, interval, CTLFLAG_RWTUN, &cdce_tx_interval, 0, 123213809Shselasky "NCM transmit interval in ms"); 124184610Salfred#endif 125184610Salfred 126192984Sthompsastatic const struct usb_config cdce_config[CDCE_N_TRANSFER] = { 127184610Salfred 128190734Sthompsa [CDCE_BULK_RX] = { 129184610Salfred .type = UE_BULK, 130184610Salfred .endpoint = UE_ADDR_ANY, 131190734Sthompsa .direction = UE_DIR_RX, 132184610Salfred .if_index = 0, 133190734Sthompsa .frames = CDCE_FRAMES_MAX, 134190734Sthompsa .bufsize = (CDCE_FRAMES_MAX * MCLBYTES), 135190734Sthompsa .flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, 136190734Sthompsa .callback = cdce_bulk_read_callback, 137190734Sthompsa .timeout = 0, /* no timeout */ 138192499Sthompsa .usb_mode = USB_MODE_DUAL, /* both modes */ 139184610Salfred }, 140184610Salfred 141190734Sthompsa [CDCE_BULK_TX] = { 142184610Salfred .type = UE_BULK, 143184610Salfred .endpoint = UE_ADDR_ANY, 144190734Sthompsa .direction = UE_DIR_TX, 145184610Salfred .if_index = 0, 146190734Sthompsa .frames = CDCE_FRAMES_MAX, 147190734Sthompsa .bufsize = (CDCE_FRAMES_MAX * MCLBYTES), 148190734Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, 149190734Sthompsa .callback = cdce_bulk_write_callback, 150190734Sthompsa .timeout = 10000, /* 10 seconds */ 151192499Sthompsa .usb_mode = USB_MODE_DUAL, /* both modes */ 152184610Salfred }, 153184610Salfred 154190734Sthompsa [CDCE_INTR_RX] = { 155184610Salfred .type = UE_INTERRUPT, 156184610Salfred .endpoint = UE_ADDR_ANY, 157190734Sthompsa .direction = UE_DIR_RX, 158184610Salfred .if_index = 1, 159190734Sthompsa .bufsize = CDCE_IND_SIZE_MAX, 160190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, 161190734Sthompsa .callback = cdce_intr_read_callback, 162190734Sthompsa .timeout = 0, 163190734Sthompsa .usb_mode = USB_MODE_HOST, 164184610Salfred }, 165190734Sthompsa 166190734Sthompsa [CDCE_INTR_TX] = { 167190734Sthompsa .type = UE_INTERRUPT, 168190734Sthompsa .endpoint = UE_ADDR_ANY, 169190734Sthompsa .direction = UE_DIR_TX, 170190734Sthompsa .if_index = 1, 171190734Sthompsa .bufsize = CDCE_IND_SIZE_MAX, 172190734Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, 173190734Sthompsa .callback = cdce_intr_write_callback, 174190734Sthompsa .timeout = 10000, /* 10 seconds */ 175190734Sthompsa .usb_mode = USB_MODE_DEVICE, 176190734Sthompsa }, 177184610Salfred}; 178184610Salfred 179197563Sthompsa#if CDCE_HAVE_NCM 180197563Sthompsastatic const struct usb_config cdce_ncm_config[CDCE_N_TRANSFER] = { 181197563Sthompsa 182197563Sthompsa [CDCE_BULK_RX] = { 183197563Sthompsa .type = UE_BULK, 184197563Sthompsa .endpoint = UE_ADDR_ANY, 185197563Sthompsa .direction = UE_DIR_RX, 186197563Sthompsa .if_index = 0, 187197563Sthompsa .frames = CDCE_NCM_RX_FRAMES_MAX, 188197563Sthompsa .bufsize = (CDCE_NCM_RX_FRAMES_MAX * CDCE_NCM_RX_MAXLEN), 189197563Sthompsa .flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,}, 190197563Sthompsa .callback = cdce_ncm_bulk_read_callback, 191197563Sthompsa .timeout = 0, /* no timeout */ 192197563Sthompsa .usb_mode = USB_MODE_DUAL, /* both modes */ 193197563Sthompsa }, 194197563Sthompsa 195197563Sthompsa [CDCE_BULK_TX] = { 196197563Sthompsa .type = UE_BULK, 197197563Sthompsa .endpoint = UE_ADDR_ANY, 198197563Sthompsa .direction = UE_DIR_TX, 199197563Sthompsa .if_index = 0, 200197563Sthompsa .frames = CDCE_NCM_TX_FRAMES_MAX, 201197563Sthompsa .bufsize = (CDCE_NCM_TX_FRAMES_MAX * CDCE_NCM_TX_MAXLEN), 202213809Shselasky .flags = {.pipe_bof = 1,}, 203197563Sthompsa .callback = cdce_ncm_bulk_write_callback, 204197563Sthompsa .timeout = 10000, /* 10 seconds */ 205197563Sthompsa .usb_mode = USB_MODE_DUAL, /* both modes */ 206197563Sthompsa }, 207197563Sthompsa 208197563Sthompsa [CDCE_INTR_RX] = { 209197563Sthompsa .type = UE_INTERRUPT, 210197563Sthompsa .endpoint = UE_ADDR_ANY, 211197563Sthompsa .direction = UE_DIR_RX, 212197563Sthompsa .if_index = 1, 213197563Sthompsa .bufsize = CDCE_IND_SIZE_MAX, 214197563Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, 215197563Sthompsa .callback = cdce_intr_read_callback, 216197563Sthompsa .timeout = 0, 217197563Sthompsa .usb_mode = USB_MODE_HOST, 218197563Sthompsa }, 219197563Sthompsa 220197563Sthompsa [CDCE_INTR_TX] = { 221197563Sthompsa .type = UE_INTERRUPT, 222197563Sthompsa .endpoint = UE_ADDR_ANY, 223197563Sthompsa .direction = UE_DIR_TX, 224197563Sthompsa .if_index = 1, 225197563Sthompsa .bufsize = CDCE_IND_SIZE_MAX, 226197563Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, 227197563Sthompsa .callback = cdce_intr_write_callback, 228197563Sthompsa .timeout = 10000, /* 10 seconds */ 229197563Sthompsa .usb_mode = USB_MODE_DEVICE, 230197563Sthompsa }, 231197563Sthompsa}; 232197563Sthompsa#endif 233197563Sthompsa 234184610Salfredstatic device_method_t cdce_methods[] = { 235184610Salfred /* USB interface */ 236188942Sthompsa DEVMETHOD(usb_handle_request, cdce_handle_request), 237184610Salfred 238184610Salfred /* Device interface */ 239184610Salfred DEVMETHOD(device_probe, cdce_probe), 240184610Salfred DEVMETHOD(device_attach, cdce_attach), 241184610Salfred DEVMETHOD(device_detach, cdce_detach), 242184610Salfred DEVMETHOD(device_suspend, cdce_suspend), 243184610Salfred DEVMETHOD(device_resume, cdce_resume), 244184610Salfred 245246128Ssbz DEVMETHOD_END 246184610Salfred}; 247184610Salfred 248184610Salfredstatic driver_t cdce_driver = { 249184610Salfred .name = "cdce", 250184610Salfred .methods = cdce_methods, 251184610Salfred .size = sizeof(struct cdce_softc), 252184610Salfred}; 253184610Salfred 254184610Salfredstatic devclass_t cdce_devclass; 255269584Sn_hibmastatic eventhandler_tag cdce_etag; 256184610Salfred 257269584Sn_hibmastatic int cdce_driver_loaded(struct module *, int, void *); 258269584Sn_hibma 259269584Sn_hibmastatic const STRUCT_USB_HOST_ID cdce_switch_devs[] = { 260269584Sn_hibma {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E3272_INIT, MSC_EJECT_HUAWEI2)}, 261269584Sn_hibma}; 262269584Sn_hibma 263223486Shselaskystatic const STRUCT_USB_HOST_ID cdce_host_devs[] = { 264184610Salfred {USB_VPI(USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632, CDCE_FLAG_NO_UNION)}, 265184610Salfred {USB_VPI(USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250, CDCE_FLAG_NO_UNION)}, 266184610Salfred {USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX, CDCE_FLAG_NO_UNION)}, 267184610Salfred {USB_VPI(USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00, CDCE_FLAG_NO_UNION)}, 268184610Salfred {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 269184610Salfred {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 270184610Salfred {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET, CDCE_FLAG_NO_UNION)}, 271184610Salfred {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501, CDCE_FLAG_NO_UNION)}, 272184610Salfred {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500, CDCE_FLAG_ZAURUS)}, 273184610Salfred {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 274184610Salfred {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLA300, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 275184610Salfred {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC700, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 276184610Salfred {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC750, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 277269584Sn_hibma 278269584Sn_hibma {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), 279269584Sn_hibma USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x16), 280269584Sn_hibma USB_DRIVER_INFO(0)}, 281269584Sn_hibma {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), 282269584Sn_hibma USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x46), 283269584Sn_hibma USB_DRIVER_INFO(0)}, 284269584Sn_hibma {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), 285269584Sn_hibma USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x76), 286269584Sn_hibma USB_DRIVER_INFO(0)}, 287223486Shselasky}; 288196492Salfred 289223486Shselaskystatic const STRUCT_USB_DUAL_ID cdce_dual_devs[] = { 290196492Salfred {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0)}, 291196492Salfred {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_MOBILE_DIRECT_LINE_MODEL, 0)}, 292197563Sthompsa {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_NETWORK_CONTROL_MODEL, 0)}, 293184610Salfred}; 294184610Salfred 295292080SimpDRIVER_MODULE(cdce, uhub, cdce_driver, cdce_devclass, cdce_driver_loaded, 0); 296292080SimpMODULE_VERSION(cdce, 1); 297292080SimpMODULE_DEPEND(cdce, uether, 1, 1, 1); 298292080SimpMODULE_DEPEND(cdce, usb, 1, 1, 1); 299292080SimpMODULE_DEPEND(cdce, ether, 1, 1, 1); 300292080SimpUSB_PNP_DEVICE_INFO(cdce_switch_devs); 301292080SimpUSB_PNP_HOST_INFO(cdce_host_devs); 302292080SimpUSB_PNP_DUAL_INFO(cdce_dual_devs); 303292080Simp 304292080Simpstatic const struct usb_ether_methods cdce_ue_methods = { 305292080Simp .ue_attach_post = cdce_attach_post, 306292080Simp .ue_start = cdce_start, 307292080Simp .ue_init = cdce_init, 308292080Simp .ue_stop = cdce_stop, 309292080Simp .ue_setmulti = cdce_setmulti, 310292080Simp .ue_setpromisc = cdce_setpromisc, 311292080Simp}; 312292080Simp 313197563Sthompsa#if CDCE_HAVE_NCM 314197563Sthompsa/*------------------------------------------------------------------------* 315197563Sthompsa * cdce_ncm_init 316197563Sthompsa * 317197563Sthompsa * Return values: 318197563Sthompsa * 0: Success 319197563Sthompsa * Else: Failure 320197563Sthompsa *------------------------------------------------------------------------*/ 321197563Sthompsastatic uint8_t 322197563Sthompsacdce_ncm_init(struct cdce_softc *sc) 323197563Sthompsa{ 324197563Sthompsa struct usb_ncm_parameters temp; 325197563Sthompsa struct usb_device_request req; 326213809Shselasky struct usb_ncm_func_descriptor *ufd; 327213809Shselasky uint8_t value[8]; 328197563Sthompsa int err; 329197563Sthompsa 330213809Shselasky ufd = usbd_find_descriptor(sc->sc_ue.ue_udev, NULL, 331233774Shselasky sc->sc_ifaces_index[1], UDESC_CS_INTERFACE, 0xFF, 332233774Shselasky UCDC_NCM_FUNC_DESC_SUBTYPE, 0xFF); 333213809Shselasky 334213809Shselasky /* verify length of NCM functional descriptor */ 335213809Shselasky if (ufd != NULL) { 336213809Shselasky if (ufd->bLength < sizeof(*ufd)) 337213809Shselasky ufd = NULL; 338213809Shselasky else 339213809Shselasky DPRINTFN(1, "Found NCM functional descriptor.\n"); 340213809Shselasky } 341213809Shselasky 342197563Sthompsa req.bmRequestType = UT_READ_CLASS_INTERFACE; 343197563Sthompsa req.bRequest = UCDC_NCM_GET_NTB_PARAMETERS; 344197563Sthompsa USETW(req.wValue, 0); 345197563Sthompsa req.wIndex[0] = sc->sc_ifaces_index[1]; 346197563Sthompsa req.wIndex[1] = 0; 347197563Sthompsa USETW(req.wLength, sizeof(temp)); 348197563Sthompsa 349197563Sthompsa err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, 350197563Sthompsa &temp, 0, NULL, 1000 /* ms */); 351197563Sthompsa if (err) 352197563Sthompsa return (1); 353197563Sthompsa 354197563Sthompsa /* Read correct set of parameters according to device mode */ 355197563Sthompsa 356197563Sthompsa if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { 357212133Sthompsa sc->sc_ncm.rx_max = UGETDW(temp.dwNtbInMaxSize); 358212133Sthompsa sc->sc_ncm.tx_max = UGETDW(temp.dwNtbOutMaxSize); 359197563Sthompsa sc->sc_ncm.tx_remainder = UGETW(temp.wNdpOutPayloadRemainder); 360197563Sthompsa sc->sc_ncm.tx_modulus = UGETW(temp.wNdpOutDivisor); 361197563Sthompsa sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpOutAlignment); 362213809Shselasky sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams); 363197563Sthompsa } else { 364212133Sthompsa sc->sc_ncm.rx_max = UGETDW(temp.dwNtbOutMaxSize); 365212133Sthompsa sc->sc_ncm.tx_max = UGETDW(temp.dwNtbInMaxSize); 366197563Sthompsa sc->sc_ncm.tx_remainder = UGETW(temp.wNdpInPayloadRemainder); 367197563Sthompsa sc->sc_ncm.tx_modulus = UGETW(temp.wNdpInDivisor); 368197563Sthompsa sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpInAlignment); 369213809Shselasky sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams); 370197563Sthompsa } 371197563Sthompsa 372197563Sthompsa /* Verify maximum receive length */ 373197563Sthompsa 374213809Shselasky if ((sc->sc_ncm.rx_max < 32) || 375197563Sthompsa (sc->sc_ncm.rx_max > CDCE_NCM_RX_MAXLEN)) { 376197563Sthompsa DPRINTFN(1, "Using default maximum receive length\n"); 377197563Sthompsa sc->sc_ncm.rx_max = CDCE_NCM_RX_MAXLEN; 378197563Sthompsa } 379197563Sthompsa 380197563Sthompsa /* Verify maximum transmit length */ 381197563Sthompsa 382213809Shselasky if ((sc->sc_ncm.tx_max < 32) || 383197563Sthompsa (sc->sc_ncm.tx_max > CDCE_NCM_TX_MAXLEN)) { 384197563Sthompsa DPRINTFN(1, "Using default maximum transmit length\n"); 385197563Sthompsa sc->sc_ncm.tx_max = CDCE_NCM_TX_MAXLEN; 386197563Sthompsa } 387197563Sthompsa 388197563Sthompsa /* 389197563Sthompsa * Verify that the structure alignment is: 390197563Sthompsa * - power of two 391197563Sthompsa * - not greater than the maximum transmit length 392197563Sthompsa * - not less than four bytes 393197563Sthompsa */ 394213809Shselasky if ((sc->sc_ncm.tx_struct_align < 4) || 395197563Sthompsa (sc->sc_ncm.tx_struct_align != 396197563Sthompsa ((-sc->sc_ncm.tx_struct_align) & sc->sc_ncm.tx_struct_align)) || 397197563Sthompsa (sc->sc_ncm.tx_struct_align >= sc->sc_ncm.tx_max)) { 398197563Sthompsa DPRINTFN(1, "Using default other alignment: 4 bytes\n"); 399197563Sthompsa sc->sc_ncm.tx_struct_align = 4; 400197563Sthompsa } 401197563Sthompsa 402197563Sthompsa /* 403197563Sthompsa * Verify that the payload alignment is: 404197563Sthompsa * - power of two 405197563Sthompsa * - not greater than the maximum transmit length 406197563Sthompsa * - not less than four bytes 407197563Sthompsa */ 408213809Shselasky if ((sc->sc_ncm.tx_modulus < 4) || 409197563Sthompsa (sc->sc_ncm.tx_modulus != 410197563Sthompsa ((-sc->sc_ncm.tx_modulus) & sc->sc_ncm.tx_modulus)) || 411197563Sthompsa (sc->sc_ncm.tx_modulus >= sc->sc_ncm.tx_max)) { 412197563Sthompsa DPRINTFN(1, "Using default transmit modulus: 4 bytes\n"); 413197563Sthompsa sc->sc_ncm.tx_modulus = 4; 414197563Sthompsa } 415197563Sthompsa 416197563Sthompsa /* Verify that the payload remainder */ 417197563Sthompsa 418213809Shselasky if ((sc->sc_ncm.tx_remainder >= sc->sc_ncm.tx_modulus)) { 419197563Sthompsa DPRINTFN(1, "Using default transmit remainder: 0 bytes\n"); 420197563Sthompsa sc->sc_ncm.tx_remainder = 0; 421197563Sthompsa } 422197563Sthompsa 423213809Shselasky /* 424213809Shselasky * Offset the TX remainder so that IP packet payload starts at 425213809Shselasky * the tx_modulus. This is not too clear in the specification. 426213809Shselasky */ 427213809Shselasky 428213809Shselasky sc->sc_ncm.tx_remainder = 429213809Shselasky (sc->sc_ncm.tx_remainder - ETHER_HDR_LEN) & 430213809Shselasky (sc->sc_ncm.tx_modulus - 1); 431213809Shselasky 432213809Shselasky /* Verify max datagrams */ 433213809Shselasky 434213809Shselasky if (sc->sc_ncm.tx_nframe == 0 || 435213809Shselasky sc->sc_ncm.tx_nframe > (CDCE_NCM_SUBFRAMES_MAX - 1)) { 436213809Shselasky DPRINTFN(1, "Using default max " 437213809Shselasky "subframes: %u units\n", CDCE_NCM_SUBFRAMES_MAX - 1); 438213809Shselasky /* need to reserve one entry for zero padding */ 439213809Shselasky sc->sc_ncm.tx_nframe = (CDCE_NCM_SUBFRAMES_MAX - 1); 440213809Shselasky } 441213809Shselasky 442197563Sthompsa /* Additional configuration, will fail in device side mode, which is OK. */ 443197563Sthompsa 444197563Sthompsa req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 445197563Sthompsa req.bRequest = UCDC_NCM_SET_NTB_INPUT_SIZE; 446197563Sthompsa USETW(req.wValue, 0); 447197563Sthompsa req.wIndex[0] = sc->sc_ifaces_index[1]; 448197563Sthompsa req.wIndex[1] = 0; 449197563Sthompsa 450213809Shselasky if (ufd != NULL && 451213809Shselasky (ufd->bmNetworkCapabilities & UCDC_NCM_CAP_MAX_DGRAM)) { 452213809Shselasky USETW(req.wLength, 8); 453213809Shselasky USETDW(value, sc->sc_ncm.rx_max); 454213809Shselasky USETW(value + 4, (CDCE_NCM_SUBFRAMES_MAX - 1)); 455213809Shselasky USETW(value + 6, 0); 456213809Shselasky } else { 457213809Shselasky USETW(req.wLength, 4); 458213809Shselasky USETDW(value, sc->sc_ncm.rx_max); 459213809Shselasky } 460213809Shselasky 461197563Sthompsa err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, 462197563Sthompsa &value, 0, NULL, 1000 /* ms */); 463197563Sthompsa if (err) { 464197563Sthompsa DPRINTFN(1, "Setting input size " 465197563Sthompsa "to %u failed.\n", sc->sc_ncm.rx_max); 466197563Sthompsa } 467197563Sthompsa 468197563Sthompsa req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 469197563Sthompsa req.bRequest = UCDC_NCM_SET_CRC_MODE; 470197563Sthompsa USETW(req.wValue, 0); /* no CRC */ 471197563Sthompsa req.wIndex[0] = sc->sc_ifaces_index[1]; 472197563Sthompsa req.wIndex[1] = 0; 473197563Sthompsa USETW(req.wLength, 0); 474197563Sthompsa 475197563Sthompsa err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, 476197563Sthompsa NULL, 0, NULL, 1000 /* ms */); 477197563Sthompsa if (err) { 478197563Sthompsa DPRINTFN(1, "Setting CRC mode to off failed.\n"); 479197563Sthompsa } 480197563Sthompsa 481197563Sthompsa req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 482197563Sthompsa req.bRequest = UCDC_NCM_SET_NTB_FORMAT; 483197563Sthompsa USETW(req.wValue, 0); /* NTB-16 */ 484197563Sthompsa req.wIndex[0] = sc->sc_ifaces_index[1]; 485197563Sthompsa req.wIndex[1] = 0; 486197563Sthompsa USETW(req.wLength, 0); 487197563Sthompsa 488197563Sthompsa err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, 489197563Sthompsa NULL, 0, NULL, 1000 /* ms */); 490197563Sthompsa if (err) { 491197563Sthompsa DPRINTFN(1, "Setting NTB format to 16-bit failed.\n"); 492197563Sthompsa } 493197563Sthompsa 494197563Sthompsa return (0); /* success */ 495197563Sthompsa} 496197563Sthompsa#endif 497197563Sthompsa 498269584Sn_hibmastatic void 499269584Sn_hibmacdce_test_autoinst(void *arg, struct usb_device *udev, 500269584Sn_hibma struct usb_attach_arg *uaa) 501269584Sn_hibma{ 502269584Sn_hibma struct usb_interface *iface; 503269584Sn_hibma struct usb_interface_descriptor *id; 504269584Sn_hibma 505269584Sn_hibma if (uaa->dev_state != UAA_DEV_READY) 506269584Sn_hibma return; 507269584Sn_hibma 508269584Sn_hibma iface = usbd_get_iface(udev, 0); 509269584Sn_hibma if (iface == NULL) 510269584Sn_hibma return; 511269584Sn_hibma id = iface->idesc; 512269584Sn_hibma if (id == NULL || id->bInterfaceClass != UICLASS_MASS) 513269584Sn_hibma return; 514269584Sn_hibma if (usbd_lookup_id_by_uaa(cdce_switch_devs, sizeof(cdce_switch_devs), uaa)) 515269584Sn_hibma return; /* no device match */ 516269584Sn_hibma 517269584Sn_hibma if (usb_msc_eject(udev, 0, USB_GET_DRIVER_INFO(uaa)) == 0) { 518269584Sn_hibma /* success, mark the udev as disappearing */ 519269584Sn_hibma uaa->dev_state = UAA_DEV_EJECTING; 520269584Sn_hibma } 521269584Sn_hibma} 522269584Sn_hibma 523184610Salfredstatic int 524269584Sn_hibmacdce_driver_loaded(struct module *mod, int what, void *arg) 525269584Sn_hibma{ 526269584Sn_hibma switch (what) { 527269584Sn_hibma case MOD_LOAD: 528269584Sn_hibma /* register our autoinstall handler */ 529269584Sn_hibma cdce_etag = EVENTHANDLER_REGISTER(usb_dev_configured, 530269584Sn_hibma cdce_test_autoinst, NULL, EVENTHANDLER_PRI_ANY); 531269584Sn_hibma return (0); 532269584Sn_hibma case MOD_UNLOAD: 533269584Sn_hibma EVENTHANDLER_DEREGISTER(usb_dev_configured, cdce_etag); 534269584Sn_hibma return (0); 535269584Sn_hibma default: 536269584Sn_hibma return (EOPNOTSUPP); 537269584Sn_hibma } 538269584Sn_hibma} 539269584Sn_hibma 540269584Sn_hibmastatic int 541184610Salfredcdce_probe(device_t dev) 542184610Salfred{ 543192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 544223486Shselasky int error; 545184610Salfred 546223486Shselasky error = usbd_lookup_id_by_uaa(cdce_host_devs, sizeof(cdce_host_devs), uaa); 547223486Shselasky if (error) 548223486Shselasky error = usbd_lookup_id_by_uaa(cdce_dual_devs, sizeof(cdce_dual_devs), uaa); 549223486Shselasky return (error); 550184610Salfred} 551184610Salfred 552188412Sthompsastatic void 553192984Sthompsacdce_attach_post(struct usb_ether *ue) 554188412Sthompsa{ 555188412Sthompsa /* no-op */ 556188412Sthompsa return; 557188412Sthompsa} 558188412Sthompsa 559184610Salfredstatic int 560184610Salfredcdce_attach(device_t dev) 561184610Salfred{ 562184610Salfred struct cdce_softc *sc = device_get_softc(dev); 563192984Sthompsa struct usb_ether *ue = &sc->sc_ue; 564192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 565192984Sthompsa struct usb_interface *iface; 566192984Sthompsa const struct usb_cdc_union_descriptor *ud; 567192984Sthompsa const struct usb_interface_descriptor *id; 568192984Sthompsa const struct usb_cdc_ethernet_descriptor *ued; 569197563Sthompsa const struct usb_config *pcfg; 570246037Sjhb uint32_t seed; 571184610Salfred int error; 572184610Salfred uint8_t i; 573197563Sthompsa uint8_t data_iface_no; 574184610Salfred char eaddr_str[5 * ETHER_ADDR_LEN]; /* approx */ 575184610Salfred 576184610Salfred sc->sc_flags = USB_GET_DRIVER_INFO(uaa); 577197563Sthompsa sc->sc_ue.ue_udev = uaa->device; 578184610Salfred 579194228Sthompsa device_set_usb_desc(dev); 580184610Salfred 581188412Sthompsa mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); 582184610Salfred 583194271Sthompsa ud = usbd_find_descriptor 584184610Salfred (uaa->device, NULL, uaa->info.bIfaceIndex, 585233774Shselasky UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_UNION, 0xFF); 586184610Salfred 587197563Sthompsa if ((ud == NULL) || (ud->bLength < sizeof(*ud)) || 588197563Sthompsa (sc->sc_flags & CDCE_FLAG_NO_UNION)) { 589197563Sthompsa DPRINTFN(1, "No union descriptor!\n"); 590197563Sthompsa sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex; 591197563Sthompsa sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; 592197563Sthompsa goto alloc_transfers; 593184610Salfred } 594197563Sthompsa data_iface_no = ud->bSlaveInterface[0]; 595184610Salfred 596184610Salfred for (i = 0;; i++) { 597184610Salfred 598194228Sthompsa iface = usbd_get_iface(uaa->device, i); 599184610Salfred 600184610Salfred if (iface) { 601184610Salfred 602194228Sthompsa id = usbd_get_interface_descriptor(iface); 603184610Salfred 604197563Sthompsa if (id && (id->bInterfaceNumber == data_iface_no)) { 605184610Salfred sc->sc_ifaces_index[0] = i; 606184610Salfred sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; 607194228Sthompsa usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 608184610Salfred break; 609184610Salfred } 610184610Salfred } else { 611199816Sthompsa device_printf(dev, "no data interface found\n"); 612184610Salfred goto detach; 613184610Salfred } 614184610Salfred } 615184610Salfred 616184610Salfred /* 617184610Salfred * <quote> 618184610Salfred * 619184610Salfred * The Data Class interface of a networking device shall have 620184610Salfred * a minimum of two interface settings. The first setting 621184610Salfred * (the default interface setting) includes no endpoints and 622184610Salfred * therefore no networking traffic is exchanged whenever the 623184610Salfred * default interface setting is selected. One or more 624184610Salfred * additional interface settings are used for normal 625184610Salfred * operation, and therefore each includes a pair of endpoints 626184610Salfred * (one IN, and one OUT) to exchange network traffic. Select 627184610Salfred * an alternate interface setting to initialize the network 628184610Salfred * aspects of the device and to enable the exchange of 629184610Salfred * network traffic. 630184610Salfred * 631184610Salfred * </quote> 632184610Salfred * 633184610Salfred * Some devices, most notably cable modems, include interface 634184610Salfred * settings that have no IN or OUT endpoint, therefore loop 635184610Salfred * through the list of all available interface settings 636184610Salfred * looking for one with both IN and OUT endpoints. 637184610Salfred */ 638184610Salfred 639184610Salfredalloc_transfers: 640184610Salfred 641197563Sthompsa pcfg = cdce_config; /* Default Configuration */ 642197563Sthompsa 643188412Sthompsa for (i = 0; i != 32; i++) { 644184610Salfred 645197563Sthompsa error = usbd_set_alt_interface_index(uaa->device, 646197563Sthompsa sc->sc_ifaces_index[0], i); 647197563Sthompsa if (error) 648197563Sthompsa break; 649197563Sthompsa#if CDCE_HAVE_NCM 650197563Sthompsa if ((i == 0) && (cdce_ncm_init(sc) == 0)) 651197563Sthompsa pcfg = cdce_ncm_config; 652197563Sthompsa#endif 653197563Sthompsa error = usbd_transfer_setup(uaa->device, 654197563Sthompsa sc->sc_ifaces_index, sc->sc_xfer, 655197563Sthompsa pcfg, CDCE_N_TRANSFER, sc, &sc->sc_mtx); 656184610Salfred 657197563Sthompsa if (error == 0) 658184610Salfred break; 659184610Salfred } 660184610Salfred 661197563Sthompsa if (error || (i == 32)) { 662197563Sthompsa device_printf(dev, "No valid alternate " 663199816Sthompsa "setting found\n"); 664197563Sthompsa goto detach; 665197563Sthompsa } 666197563Sthompsa 667194271Sthompsa ued = usbd_find_descriptor 668184610Salfred (uaa->device, NULL, uaa->info.bIfaceIndex, 669233774Shselasky UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_ENF, 0xFF); 670184610Salfred 671188412Sthompsa if ((ued == NULL) || (ued->bLength < sizeof(*ued))) { 672184610Salfred error = USB_ERR_INVAL; 673184610Salfred } else { 674194228Sthompsa error = usbd_req_get_string_any(uaa->device, NULL, 675188412Sthompsa eaddr_str, sizeof(eaddr_str), ued->iMacAddress); 676184610Salfred } 677184610Salfred 678184610Salfred if (error) { 679184610Salfred 680184610Salfred /* fake MAC address */ 681184610Salfred 682184610Salfred device_printf(dev, "faking MAC address\n"); 683246037Sjhb seed = ticks; 684188412Sthompsa sc->sc_ue.ue_eaddr[0] = 0x2a; 685246037Sjhb memcpy(&sc->sc_ue.ue_eaddr[1], &seed, sizeof(uint32_t)); 686188412Sthompsa sc->sc_ue.ue_eaddr[5] = device_get_unit(dev); 687184610Salfred 688184610Salfred } else { 689184610Salfred 690213809Shselasky memset(sc->sc_ue.ue_eaddr, 0, sizeof(sc->sc_ue.ue_eaddr)); 691184610Salfred 692188412Sthompsa for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) { 693184610Salfred 694184610Salfred char c = eaddr_str[i]; 695184610Salfred 696188412Sthompsa if ('0' <= c && c <= '9') 697184610Salfred c -= '0'; 698188412Sthompsa else if (c != 0) 699184610Salfred c -= 'A' - 10; 700188412Sthompsa else 701184610Salfred break; 702184610Salfred 703184610Salfred c &= 0xf; 704184610Salfred 705188412Sthompsa if ((i & 1) == 0) 706184610Salfred c <<= 4; 707188412Sthompsa sc->sc_ue.ue_eaddr[i / 2] |= c; 708184610Salfred } 709184610Salfred 710192499Sthompsa if (uaa->usb_mode == USB_MODE_DEVICE) { 711184610Salfred /* 712184610Salfred * Do not use the same MAC address like the peer ! 713184610Salfred */ 714188412Sthompsa sc->sc_ue.ue_eaddr[5] ^= 0xFF; 715184610Salfred } 716184610Salfred } 717184610Salfred 718188412Sthompsa ue->ue_sc = sc; 719188412Sthompsa ue->ue_dev = dev; 720188412Sthompsa ue->ue_udev = uaa->device; 721188412Sthompsa ue->ue_mtx = &sc->sc_mtx; 722188412Sthompsa ue->ue_methods = &cdce_ue_methods; 723184610Salfred 724194228Sthompsa error = uether_ifattach(ue); 725188412Sthompsa if (error) { 726188412Sthompsa device_printf(dev, "could not attach interface\n"); 727184610Salfred goto detach; 728184610Salfred } 729184610Salfred return (0); /* success */ 730184610Salfred 731184610Salfreddetach: 732184610Salfred cdce_detach(dev); 733184610Salfred return (ENXIO); /* failure */ 734184610Salfred} 735184610Salfred 736184610Salfredstatic int 737184610Salfredcdce_detach(device_t dev) 738184610Salfred{ 739184610Salfred struct cdce_softc *sc = device_get_softc(dev); 740192984Sthompsa struct usb_ether *ue = &sc->sc_ue; 741184610Salfred 742184610Salfred /* stop all USB transfers first */ 743194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, CDCE_N_TRANSFER); 744194228Sthompsa uether_ifdetach(ue); 745184610Salfred mtx_destroy(&sc->sc_mtx); 746184610Salfred 747184610Salfred return (0); 748184610Salfred} 749184610Salfred 750184610Salfredstatic void 751192984Sthompsacdce_start(struct usb_ether *ue) 752184610Salfred{ 753194228Sthompsa struct cdce_softc *sc = uether_getsc(ue); 754184610Salfred 755188412Sthompsa /* 756188412Sthompsa * Start the USB transfers, if not already started: 757188412Sthompsa */ 758194228Sthompsa usbd_transfer_start(sc->sc_xfer[CDCE_BULK_TX]); 759194228Sthompsa usbd_transfer_start(sc->sc_xfer[CDCE_BULK_RX]); 760184610Salfred} 761184610Salfred 762184610Salfredstatic void 763188412Sthompsacdce_free_queue(struct mbuf **ppm, uint8_t n) 764184610Salfred{ 765188412Sthompsa uint8_t x; 766188412Sthompsa for (x = 0; x != n; x++) { 767188412Sthompsa if (ppm[x] != NULL) { 768188412Sthompsa m_freem(ppm[x]); 769188412Sthompsa ppm[x] = NULL; 770184610Salfred } 771184610Salfred } 772184610Salfred} 773184610Salfred 774184610Salfredstatic void 775194677Sthompsacdce_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 776184610Salfred{ 777194677Sthompsa struct cdce_softc *sc = usbd_xfer_softc(xfer); 778194228Sthompsa struct ifnet *ifp = uether_getifp(&sc->sc_ue); 779184610Salfred struct mbuf *m; 780184610Salfred struct mbuf *mt; 781188412Sthompsa uint32_t crc; 782188412Sthompsa uint8_t x; 783194677Sthompsa int actlen, aframes; 784184610Salfred 785194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 786194677Sthompsa 787188412Sthompsa DPRINTFN(1, "\n"); 788188412Sthompsa 789184610Salfred switch (USB_GET_STATE(xfer)) { 790184610Salfred case USB_ST_TRANSFERRED: 791194677Sthompsa DPRINTFN(11, "transfer complete: %u bytes in %u frames\n", 792194677Sthompsa actlen, aframes); 793184610Salfred 794271832Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 795184610Salfred 796188412Sthompsa /* free all previous TX buffers */ 797188412Sthompsa cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX); 798184610Salfred 799188412Sthompsa /* FALLTHROUGH */ 800184610Salfred case USB_ST_SETUP: 801184610Salfredtr_setup: 802188412Sthompsa for (x = 0; x != CDCE_FRAMES_MAX; x++) { 803184610Salfred 804184610Salfred IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 805184610Salfred 806188412Sthompsa if (m == NULL) 807184610Salfred break; 808188412Sthompsa 809188412Sthompsa if (sc->sc_flags & CDCE_FLAG_ZAURUS) { 810188412Sthompsa /* 811188412Sthompsa * Zaurus wants a 32-bit CRC appended 812188412Sthompsa * to every frame 813188412Sthompsa */ 814188412Sthompsa 815188412Sthompsa crc = cdce_m_crc32(m, 0, m->m_pkthdr.len); 816188412Sthompsa crc = htole32(crc); 817188412Sthompsa 818188412Sthompsa if (!m_append(m, 4, (void *)&crc)) { 819188412Sthompsa m_freem(m); 820271832Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 821188412Sthompsa continue; 822188412Sthompsa } 823184610Salfred } 824188412Sthompsa if (m->m_len != m->m_pkthdr.len) { 825243857Sglebius mt = m_defrag(m, M_NOWAIT); 826184610Salfred if (mt == NULL) { 827184610Salfred m_freem(m); 828271832Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 829184610Salfred continue; 830184610Salfred } 831184610Salfred m = mt; 832184610Salfred } 833188412Sthompsa if (m->m_pkthdr.len > MCLBYTES) { 834188412Sthompsa m->m_pkthdr.len = MCLBYTES; 835188412Sthompsa } 836188412Sthompsa sc->sc_tx_buf[x] = m; 837194677Sthompsa usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); 838184610Salfred 839184610Salfred /* 840188412Sthompsa * If there's a BPF listener, bounce a copy of 841188412Sthompsa * this frame to him: 842184610Salfred */ 843184610Salfred BPF_MTAP(ifp, m); 844184610Salfred } 845188412Sthompsa if (x != 0) { 846194677Sthompsa usbd_xfer_set_frames(xfer, x); 847194677Sthompsa 848194228Sthompsa usbd_transfer_submit(xfer); 849184610Salfred } 850184610Salfred break; 851184610Salfred 852184610Salfred default: /* Error */ 853184610Salfred DPRINTFN(11, "transfer error, %s\n", 854194677Sthompsa usbd_errstr(error)); 855184610Salfred 856188412Sthompsa /* free all previous TX buffers */ 857188412Sthompsa cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX); 858184610Salfred 859188412Sthompsa /* count output errors */ 860271832Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 861184610Salfred 862194677Sthompsa if (error != USB_ERR_CANCELLED) { 863277420Sbr if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { 864277420Sbr /* try to clear stall first */ 865277420Sbr usbd_xfer_set_stall(xfer); 866277420Sbr } 867277422Sbr goto tr_setup; 868184610Salfred } 869184610Salfred break; 870184610Salfred } 871184610Salfred} 872184610Salfred 873184610Salfredstatic int32_t 874184610Salfredcdce_m_crc32_cb(void *arg, void *src, uint32_t count) 875184610Salfred{ 876188412Sthompsa uint32_t *p_crc = arg; 877184610Salfred 878184610Salfred *p_crc = crc32_raw(src, count, *p_crc); 879184610Salfred return (0); 880184610Salfred} 881184610Salfred 882184610Salfredstatic uint32_t 883184610Salfredcdce_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len) 884184610Salfred{ 885184610Salfred uint32_t crc = 0xFFFFFFFF; 886188412Sthompsa int error; 887184610Salfred 888188412Sthompsa error = m_apply(m, src_offset, src_len, cdce_m_crc32_cb, &crc); 889184610Salfred return (crc ^ 0xFFFFFFFF); 890184610Salfred} 891184610Salfred 892184610Salfredstatic void 893192984Sthompsacdce_init(struct usb_ether *ue) 894184610Salfred{ 895194228Sthompsa struct cdce_softc *sc = uether_getsc(ue); 896194228Sthompsa struct ifnet *ifp = uether_getifp(ue); 897184610Salfred 898188412Sthompsa CDCE_LOCK_ASSERT(sc, MA_OWNED); 899184610Salfred 900188412Sthompsa ifp->if_drv_flags |= IFF_DRV_RUNNING; 901184610Salfred 902188412Sthompsa /* start interrupt transfer */ 903194228Sthompsa usbd_transfer_start(sc->sc_xfer[CDCE_INTR_RX]); 904194228Sthompsa usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]); 905188412Sthompsa 906277298Sbr /* 907277298Sbr * Stall data write direction, which depends on USB mode. 908277298Sbr * 909277298Sbr * Some USB host stacks (e.g. Mac OS X) don't clears stall 910277298Sbr * bit as it should, so set it in our host mode only. 911277298Sbr */ 912277298Sbr if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) 913277298Sbr usbd_xfer_set_stall(sc->sc_xfer[CDCE_BULK_TX]); 914188412Sthompsa 915188412Sthompsa /* start data transfers */ 916188412Sthompsa cdce_start(ue); 917188412Sthompsa} 918188412Sthompsa 919188412Sthompsastatic void 920192984Sthompsacdce_stop(struct usb_ether *ue) 921188412Sthompsa{ 922194228Sthompsa struct cdce_softc *sc = uether_getsc(ue); 923194228Sthompsa struct ifnet *ifp = uether_getifp(ue); 924188412Sthompsa 925188412Sthompsa CDCE_LOCK_ASSERT(sc, MA_OWNED); 926188412Sthompsa 927188412Sthompsa ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 928188412Sthompsa 929184610Salfred /* 930184610Salfred * stop all the transfers, if not already stopped: 931184610Salfred */ 932194228Sthompsa usbd_transfer_stop(sc->sc_xfer[CDCE_BULK_RX]); 933194228Sthompsa usbd_transfer_stop(sc->sc_xfer[CDCE_BULK_TX]); 934194228Sthompsa usbd_transfer_stop(sc->sc_xfer[CDCE_INTR_RX]); 935194228Sthompsa usbd_transfer_stop(sc->sc_xfer[CDCE_INTR_TX]); 936184610Salfred} 937184610Salfred 938188412Sthompsastatic void 939192984Sthompsacdce_setmulti(struct usb_ether *ue) 940188412Sthompsa{ 941188412Sthompsa /* no-op */ 942188412Sthompsa return; 943188412Sthompsa} 944188412Sthompsa 945188412Sthompsastatic void 946192984Sthompsacdce_setpromisc(struct usb_ether *ue) 947188412Sthompsa{ 948188412Sthompsa /* no-op */ 949188412Sthompsa return; 950188412Sthompsa} 951188412Sthompsa 952184610Salfredstatic int 953184610Salfredcdce_suspend(device_t dev) 954184610Salfred{ 955184610Salfred device_printf(dev, "Suspending\n"); 956184610Salfred return (0); 957184610Salfred} 958184610Salfred 959184610Salfredstatic int 960184610Salfredcdce_resume(device_t dev) 961184610Salfred{ 962184610Salfred device_printf(dev, "Resuming\n"); 963184610Salfred return (0); 964184610Salfred} 965184610Salfred 966184610Salfredstatic void 967194677Sthompsacdce_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 968184610Salfred{ 969194677Sthompsa struct cdce_softc *sc = usbd_xfer_softc(xfer); 970184610Salfred struct mbuf *m; 971188412Sthompsa uint8_t x; 972233774Shselasky int actlen; 973233774Shselasky int aframes; 974233774Shselasky int len; 975184610Salfred 976194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 977194677Sthompsa 978184610Salfred switch (USB_GET_STATE(xfer)) { 979184610Salfred case USB_ST_TRANSFERRED: 980184610Salfred 981194677Sthompsa DPRINTF("received %u bytes in %u frames\n", actlen, aframes); 982184610Salfred 983194677Sthompsa for (x = 0; x != aframes; x++) { 984184610Salfred 985188412Sthompsa m = sc->sc_rx_buf[x]; 986188412Sthompsa sc->sc_rx_buf[x] = NULL; 987194682Sthompsa len = usbd_xfer_frame_len(xfer, x); 988188412Sthompsa 989188412Sthompsa /* Strip off CRC added by Zaurus, if any */ 990194677Sthompsa if ((sc->sc_flags & CDCE_FLAG_ZAURUS) && len >= 14) 991194677Sthompsa len -= 4; 992188412Sthompsa 993233774Shselasky if (len < (int)sizeof(struct ether_header)) { 994188412Sthompsa m_freem(m); 995188412Sthompsa continue; 996184610Salfred } 997188412Sthompsa /* queue up mbuf */ 998194677Sthompsa uether_rxmbuf(&sc->sc_ue, m, len); 999184610Salfred } 1000184610Salfred 1001188412Sthompsa /* FALLTHROUGH */ 1002184610Salfred case USB_ST_SETUP: 1003188412Sthompsa /* 1004188412Sthompsa * TODO: Implement support for multi frame transfers, 1005188412Sthompsa * when the USB hardware supports it. 1006184610Salfred */ 1007188412Sthompsa for (x = 0; x != 1; x++) { 1008188412Sthompsa if (sc->sc_rx_buf[x] == NULL) { 1009194228Sthompsa m = uether_newbuf(); 1010188412Sthompsa if (m == NULL) 1011188412Sthompsa goto tr_stall; 1012188412Sthompsa sc->sc_rx_buf[x] = m; 1013184610Salfred } else { 1014188412Sthompsa m = sc->sc_rx_buf[x]; 1015184610Salfred } 1016184610Salfred 1017194677Sthompsa usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); 1018184610Salfred } 1019188412Sthompsa /* set number of frames and start hardware */ 1020194677Sthompsa usbd_xfer_set_frames(xfer, x); 1021194228Sthompsa usbd_transfer_submit(xfer); 1022188412Sthompsa /* flush any received frames */ 1023194228Sthompsa uether_rxflush(&sc->sc_ue); 1024184610Salfred break; 1025184610Salfred 1026184610Salfred default: /* Error */ 1027184610Salfred DPRINTF("error = %s\n", 1028194677Sthompsa usbd_errstr(error)); 1029184610Salfred 1030194677Sthompsa if (error != USB_ERR_CANCELLED) { 1031188412Sthompsatr_stall: 1032277420Sbr if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { 1033277420Sbr /* try to clear stall first */ 1034277420Sbr usbd_xfer_set_stall(xfer); 1035277420Sbr usbd_xfer_set_frames(xfer, 0); 1036277420Sbr usbd_transfer_submit(xfer); 1037277420Sbr } 1038188412Sthompsa break; 1039184610Salfred } 1040184610Salfred 1041188412Sthompsa /* need to free the RX-mbufs when we are cancelled */ 1042188412Sthompsa cdce_free_queue(sc->sc_rx_buf, CDCE_FRAMES_MAX); 1043184610Salfred break; 1044184610Salfred } 1045184610Salfred} 1046184610Salfred 1047184610Salfredstatic void 1048194677Sthompsacdce_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 1049184610Salfred{ 1050277420Sbr struct cdce_softc *sc = usbd_xfer_softc(xfer); 1051194677Sthompsa int actlen; 1052194677Sthompsa 1053194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 1054194677Sthompsa 1055184610Salfred switch (USB_GET_STATE(xfer)) { 1056184610Salfred case USB_ST_TRANSFERRED: 1057184610Salfred 1058194677Sthompsa DPRINTF("Received %d bytes\n", actlen); 1059184610Salfred 1060184610Salfred /* TODO: decode some indications */ 1061184610Salfred 1062188412Sthompsa /* FALLTHROUGH */ 1063184610Salfred case USB_ST_SETUP: 1064184610Salfredtr_setup: 1065194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 1066194228Sthompsa usbd_transfer_submit(xfer); 1067184610Salfred break; 1068184610Salfred 1069184610Salfred default: /* Error */ 1070194677Sthompsa if (error != USB_ERR_CANCELLED) { 1071184610Salfred /* start clear stall */ 1072277420Sbr if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) 1073277420Sbr usbd_xfer_set_stall(xfer); 1074277422Sbr goto tr_setup; 1075184610Salfred } 1076184610Salfred break; 1077184610Salfred } 1078184610Salfred} 1079184610Salfred 1080184610Salfredstatic void 1081194677Sthompsacdce_intr_write_callback(struct usb_xfer *xfer, usb_error_t error) 1082184610Salfred{ 1083277298Sbr struct cdce_softc *sc = usbd_xfer_softc(xfer); 1084277298Sbr struct usb_cdc_notification req; 1085277298Sbr struct usb_page_cache *pc; 1086277298Sbr uint32_t speed; 1087194677Sthompsa int actlen; 1088194677Sthompsa 1089194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 1090194677Sthompsa 1091184610Salfred switch (USB_GET_STATE(xfer)) { 1092184610Salfred case USB_ST_TRANSFERRED: 1093184610Salfred 1094194677Sthompsa DPRINTF("Transferred %d bytes\n", actlen); 1095184610Salfred 1096277420Sbr switch (sc->sc_notify_state) { 1097277422Sbr case CDCE_NOTIFY_NETWORK_CONNECTION: 1098277420Sbr sc->sc_notify_state = CDCE_NOTIFY_SPEED_CHANGE; 1099277420Sbr break; 1100277422Sbr case CDCE_NOTIFY_SPEED_CHANGE: 1101277420Sbr sc->sc_notify_state = CDCE_NOTIFY_DONE; 1102277420Sbr break; 1103277420Sbr default: 1104277420Sbr break; 1105277420Sbr } 1106277420Sbr 1107188412Sthompsa /* FALLTHROUGH */ 1108184610Salfred case USB_ST_SETUP: 1109184610Salfredtr_setup: 1110277298Sbr /* 1111277298Sbr * Inform host about connection. Required according to USB CDC 1112277298Sbr * specification and communicating to Mac OS X USB host stack. 1113277298Sbr * Some of the values seems ignored by Mac OS X though. 1114277298Sbr */ 1115277298Sbr if (sc->sc_notify_state == CDCE_NOTIFY_NETWORK_CONNECTION) { 1116277298Sbr req.bmRequestType = UCDC_NOTIFICATION; 1117277298Sbr req.bNotification = UCDC_N_NETWORK_CONNECTION; 1118277298Sbr req.wIndex[0] = sc->sc_ifaces_index[1]; 1119277298Sbr req.wIndex[1] = 0; 1120277298Sbr USETW(req.wValue, 1); /* Connected */ 1121277298Sbr USETW(req.wLength, 0); 1122277298Sbr 1123277298Sbr pc = usbd_xfer_get_frame(xfer, 0); 1124277298Sbr usbd_copy_in(pc, 0, &req, sizeof(req)); 1125277298Sbr usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 1126277298Sbr usbd_xfer_set_frames(xfer, 1); 1127277298Sbr usbd_transfer_submit(xfer); 1128277298Sbr 1129277298Sbr } else if (sc->sc_notify_state == CDCE_NOTIFY_SPEED_CHANGE) { 1130277298Sbr req.bmRequestType = UCDC_NOTIFICATION; 1131277298Sbr req.bNotification = UCDC_N_CONNECTION_SPEED_CHANGE; 1132277298Sbr req.wIndex[0] = sc->sc_ifaces_index[1]; 1133277298Sbr req.wIndex[1] = 0; 1134277298Sbr USETW(req.wValue, 0); 1135277298Sbr USETW(req.wLength, 8); 1136277298Sbr 1137277298Sbr /* Peak theoretical bulk trasfer rate in bits/s */ 1138277420Sbr if (usbd_get_speed(sc->sc_ue.ue_udev) != USB_SPEED_FULL) 1139277298Sbr speed = (13 * 512 * 8 * 1000 * 8); 1140277298Sbr else 1141277298Sbr speed = (19 * 64 * 1 * 1000 * 8); 1142277298Sbr 1143277298Sbr USETDW(req.data + 0, speed); /* Upstream bit rate */ 1144277298Sbr USETDW(req.data + 4, speed); /* Downstream bit rate */ 1145277298Sbr 1146277298Sbr pc = usbd_xfer_get_frame(xfer, 0); 1147277298Sbr usbd_copy_in(pc, 0, &req, sizeof(req)); 1148277298Sbr usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 1149277298Sbr usbd_xfer_set_frames(xfer, 1); 1150277298Sbr usbd_transfer_submit(xfer); 1151277298Sbr } 1152184610Salfred break; 1153184610Salfred 1154184610Salfred default: /* Error */ 1155194677Sthompsa if (error != USB_ERR_CANCELLED) { 1156277420Sbr if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { 1157277420Sbr /* start clear stall */ 1158277420Sbr usbd_xfer_set_stall(xfer); 1159277420Sbr } 1160277422Sbr goto tr_setup; 1161184610Salfred } 1162184610Salfred break; 1163184610Salfred } 1164184610Salfred} 1165184610Salfred 1166184610Salfredstatic int 1167184610Salfredcdce_handle_request(device_t dev, 1168277420Sbr const void *preq, void **pptr, uint16_t *plen, 1169195121Sthompsa uint16_t offset, uint8_t *pstate) 1170184610Salfred{ 1171277420Sbr struct cdce_softc *sc = device_get_softc(dev); 1172277420Sbr const struct usb_device_request *req = preq; 1173277420Sbr uint8_t is_complete = *pstate; 1174277420Sbr 1175277420Sbr /* 1176277420Sbr * When Mac OS X resumes after suspending it expects 1177277420Sbr * to be notified again after this request. 1178277420Sbr */ 1179277420Sbr if (req->bmRequestType == UT_WRITE_CLASS_INTERFACE && \ 1180277420Sbr req->bRequest == UCDC_NCM_SET_ETHERNET_PACKET_FILTER) { 1181277420Sbr 1182277420Sbr if (is_complete == 1) { 1183277420Sbr mtx_lock(&sc->sc_mtx); 1184277420Sbr sc->sc_notify_state = CDCE_NOTIFY_SPEED_CHANGE; 1185277420Sbr usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]); 1186277420Sbr mtx_unlock(&sc->sc_mtx); 1187277420Sbr } 1188277420Sbr 1189277420Sbr return (0); 1190277420Sbr } 1191277420Sbr 1192184610Salfred return (ENXIO); /* use builtin handler */ 1193184610Salfred} 1194197563Sthompsa 1195197563Sthompsa#if CDCE_HAVE_NCM 1196213809Shselaskystatic void 1197213809Shselaskycdce_ncm_tx_zero(struct usb_page_cache *pc, 1198213809Shselasky uint32_t start, uint32_t end) 1199213809Shselasky{ 1200213809Shselasky if (start >= CDCE_NCM_TX_MAXLEN) 1201213809Shselasky return; 1202213809Shselasky if (end > CDCE_NCM_TX_MAXLEN) 1203213809Shselasky end = CDCE_NCM_TX_MAXLEN; 1204213809Shselasky 1205213809Shselasky usbd_frame_zero(pc, start, end - start); 1206213809Shselasky} 1207213809Shselasky 1208197563Sthompsastatic uint8_t 1209197563Sthompsacdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index) 1210197563Sthompsa{ 1211197563Sthompsa struct cdce_softc *sc = usbd_xfer_softc(xfer); 1212197563Sthompsa struct ifnet *ifp = uether_getifp(&sc->sc_ue); 1213197563Sthompsa struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, index); 1214197563Sthompsa struct mbuf *m; 1215197563Sthompsa uint32_t rem; 1216197563Sthompsa uint32_t offset; 1217197563Sthompsa uint32_t last_offset; 1218213809Shselasky uint16_t n; 1219213809Shselasky uint8_t retval; 1220197563Sthompsa 1221197563Sthompsa usbd_xfer_set_frame_offset(xfer, index * CDCE_NCM_TX_MAXLEN, index); 1222197563Sthompsa 1223197563Sthompsa offset = sizeof(sc->sc_ncm.hdr) + 1224197563Sthompsa sizeof(sc->sc_ncm.dpt) + sizeof(sc->sc_ncm.dp); 1225197563Sthompsa 1226197563Sthompsa /* Store last valid offset before alignment */ 1227197563Sthompsa last_offset = offset; 1228197563Sthompsa 1229213809Shselasky /* Align offset */ 1230213809Shselasky offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder, 1231213809Shselasky offset, sc->sc_ncm.tx_modulus); 1232197563Sthompsa 1233213809Shselasky /* Zero pad */ 1234213809Shselasky cdce_ncm_tx_zero(pc, last_offset, offset); 1235197563Sthompsa 1236213809Shselasky /* buffer full */ 1237213809Shselasky retval = 2; 1238213809Shselasky 1239213809Shselasky for (n = 0; n != sc->sc_ncm.tx_nframe; n++) { 1240213809Shselasky 1241197563Sthompsa /* check if end of transmit buffer is reached */ 1242197563Sthompsa 1243197563Sthompsa if (offset >= sc->sc_ncm.tx_max) 1244197563Sthompsa break; 1245197563Sthompsa 1246197563Sthompsa /* compute maximum buffer size */ 1247197563Sthompsa 1248197563Sthompsa rem = sc->sc_ncm.tx_max - offset; 1249197563Sthompsa 1250197563Sthompsa IFQ_DRV_DEQUEUE(&(ifp->if_snd), m); 1251197563Sthompsa 1252213809Shselasky if (m == NULL) { 1253213809Shselasky /* buffer not full */ 1254213809Shselasky retval = 1; 1255197563Sthompsa break; 1256213809Shselasky } 1257197563Sthompsa 1258233774Shselasky if (m->m_pkthdr.len > (int)rem) { 1259197563Sthompsa if (n == 0) { 1260197563Sthompsa /* The frame won't fit in our buffer */ 1261197563Sthompsa DPRINTFN(1, "Frame too big to be transmitted!\n"); 1262197563Sthompsa m_freem(m); 1263271832Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1264197563Sthompsa n--; 1265197563Sthompsa continue; 1266197563Sthompsa } 1267197563Sthompsa /* Wait till next buffer becomes ready */ 1268197563Sthompsa IFQ_DRV_PREPEND(&(ifp->if_snd), m); 1269197563Sthompsa break; 1270197563Sthompsa } 1271197563Sthompsa usbd_m_copy_in(pc, offset, m, 0, m->m_pkthdr.len); 1272197563Sthompsa 1273197563Sthompsa USETW(sc->sc_ncm.dp[n].wFrameLength, m->m_pkthdr.len); 1274197563Sthompsa USETW(sc->sc_ncm.dp[n].wFrameIndex, offset); 1275197563Sthompsa 1276197563Sthompsa /* Update offset */ 1277197563Sthompsa offset += m->m_pkthdr.len; 1278197563Sthompsa 1279197563Sthompsa /* Store last valid offset before alignment */ 1280197563Sthompsa last_offset = offset; 1281197563Sthompsa 1282213809Shselasky /* Align offset */ 1283213809Shselasky offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder, 1284213809Shselasky offset, sc->sc_ncm.tx_modulus); 1285197563Sthompsa 1286213809Shselasky /* Zero pad */ 1287213809Shselasky cdce_ncm_tx_zero(pc, last_offset, offset); 1288213809Shselasky 1289197563Sthompsa /* 1290197563Sthompsa * If there's a BPF listener, bounce a copy 1291197563Sthompsa * of this frame to him: 1292197563Sthompsa */ 1293197563Sthompsa BPF_MTAP(ifp, m); 1294197563Sthompsa 1295197563Sthompsa /* Free mbuf */ 1296197563Sthompsa 1297197563Sthompsa m_freem(m); 1298197563Sthompsa 1299197563Sthompsa /* Pre-increment interface counter */ 1300197563Sthompsa 1301271832Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 1302197563Sthompsa } 1303197563Sthompsa 1304197563Sthompsa if (n == 0) 1305213809Shselasky return (0); 1306197563Sthompsa 1307197563Sthompsa rem = (sizeof(sc->sc_ncm.dpt) + (4 * n) + 4); 1308197563Sthompsa 1309197563Sthompsa USETW(sc->sc_ncm.dpt.wLength, rem); 1310197563Sthompsa 1311197563Sthompsa /* zero the rest of the data pointer entries */ 1312197563Sthompsa for (; n != CDCE_NCM_SUBFRAMES_MAX; n++) { 1313197563Sthompsa USETW(sc->sc_ncm.dp[n].wFrameLength, 0); 1314197563Sthompsa USETW(sc->sc_ncm.dp[n].wFrameIndex, 0); 1315197563Sthompsa } 1316197563Sthompsa 1317213809Shselasky offset = last_offset; 1318213809Shselasky 1319213809Shselasky /* Align offset */ 1320213809Shselasky offset = CDCE_NCM_ALIGN(0, offset, CDCE_NCM_TX_MINLEN); 1321213809Shselasky 1322213809Shselasky /* Optimise, save bandwidth and force short termination */ 1323213809Shselasky if (offset >= sc->sc_ncm.tx_max) 1324213809Shselasky offset = sc->sc_ncm.tx_max; 1325213809Shselasky else 1326213809Shselasky offset ++; 1327213809Shselasky 1328213809Shselasky /* Zero pad */ 1329213809Shselasky cdce_ncm_tx_zero(pc, last_offset, offset); 1330213809Shselasky 1331197563Sthompsa /* set frame length */ 1332213809Shselasky usbd_xfer_set_frame_len(xfer, index, offset); 1333197563Sthompsa 1334197563Sthompsa /* Fill out 16-bit header */ 1335197563Sthompsa sc->sc_ncm.hdr.dwSignature[0] = 'N'; 1336197563Sthompsa sc->sc_ncm.hdr.dwSignature[1] = 'C'; 1337197563Sthompsa sc->sc_ncm.hdr.dwSignature[2] = 'M'; 1338197563Sthompsa sc->sc_ncm.hdr.dwSignature[3] = 'H'; 1339197563Sthompsa USETW(sc->sc_ncm.hdr.wHeaderLength, sizeof(sc->sc_ncm.hdr)); 1340213809Shselasky USETW(sc->sc_ncm.hdr.wBlockLength, offset); 1341197563Sthompsa USETW(sc->sc_ncm.hdr.wSequence, sc->sc_ncm.tx_seq); 1342197563Sthompsa USETW(sc->sc_ncm.hdr.wDptIndex, sizeof(sc->sc_ncm.hdr)); 1343197563Sthompsa 1344197563Sthompsa sc->sc_ncm.tx_seq++; 1345197563Sthompsa 1346197563Sthompsa /* Fill out 16-bit frame table header */ 1347197563Sthompsa sc->sc_ncm.dpt.dwSignature[0] = 'N'; 1348197563Sthompsa sc->sc_ncm.dpt.dwSignature[1] = 'C'; 1349197563Sthompsa sc->sc_ncm.dpt.dwSignature[2] = 'M'; 1350200307Sthompsa sc->sc_ncm.dpt.dwSignature[3] = '0'; 1351197563Sthompsa USETW(sc->sc_ncm.dpt.wNextNdpIndex, 0); /* reserved */ 1352197563Sthompsa 1353197563Sthompsa usbd_copy_in(pc, 0, &(sc->sc_ncm.hdr), sizeof(sc->sc_ncm.hdr)); 1354197563Sthompsa usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr), &(sc->sc_ncm.dpt), 1355197563Sthompsa sizeof(sc->sc_ncm.dpt)); 1356197563Sthompsa usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr) + sizeof(sc->sc_ncm.dpt), 1357197563Sthompsa &(sc->sc_ncm.dp), sizeof(sc->sc_ncm.dp)); 1358213809Shselasky return (retval); 1359197563Sthompsa} 1360197563Sthompsa 1361197563Sthompsastatic void 1362197563Sthompsacdce_ncm_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 1363197563Sthompsa{ 1364197563Sthompsa struct cdce_softc *sc = usbd_xfer_softc(xfer); 1365197563Sthompsa struct ifnet *ifp = uether_getifp(&sc->sc_ue); 1366197563Sthompsa uint16_t x; 1367213809Shselasky uint8_t temp; 1368197563Sthompsa int actlen; 1369197563Sthompsa int aframes; 1370197563Sthompsa 1371197563Sthompsa switch (USB_GET_STATE(xfer)) { 1372197563Sthompsa case USB_ST_TRANSFERRED: 1373197563Sthompsa 1374197563Sthompsa usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 1375197563Sthompsa 1376197563Sthompsa DPRINTFN(10, "transfer complete: " 1377197563Sthompsa "%u bytes in %u frames\n", actlen, aframes); 1378197563Sthompsa 1379197563Sthompsa case USB_ST_SETUP: 1380197563Sthompsa for (x = 0; x != CDCE_NCM_TX_FRAMES_MAX; x++) { 1381213809Shselasky temp = cdce_ncm_fill_tx_frames(xfer, x); 1382213809Shselasky if (temp == 0) 1383197563Sthompsa break; 1384213809Shselasky if (temp == 1) { 1385213809Shselasky x++; 1386213809Shselasky break; 1387213809Shselasky } 1388197563Sthompsa } 1389197563Sthompsa 1390197563Sthompsa if (x != 0) { 1391213809Shselasky#ifdef USB_DEBUG 1392213809Shselasky usbd_xfer_set_interval(xfer, cdce_tx_interval); 1393213809Shselasky#endif 1394197563Sthompsa usbd_xfer_set_frames(xfer, x); 1395197563Sthompsa usbd_transfer_submit(xfer); 1396197563Sthompsa } 1397197563Sthompsa break; 1398197563Sthompsa 1399197563Sthompsa default: /* Error */ 1400197563Sthompsa DPRINTFN(10, "Transfer error: %s\n", 1401197563Sthompsa usbd_errstr(error)); 1402197563Sthompsa 1403197563Sthompsa /* update error counter */ 1404271832Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1405197563Sthompsa 1406197563Sthompsa if (error != USB_ERR_CANCELLED) { 1407277420Sbr if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { 1408277420Sbr /* try to clear stall first */ 1409277420Sbr usbd_xfer_set_stall(xfer); 1410277420Sbr usbd_xfer_set_frames(xfer, 0); 1411277420Sbr usbd_transfer_submit(xfer); 1412277420Sbr } 1413197563Sthompsa } 1414197563Sthompsa break; 1415197563Sthompsa } 1416197563Sthompsa} 1417197563Sthompsa 1418197563Sthompsastatic void 1419197563Sthompsacdce_ncm_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 1420197563Sthompsa{ 1421197563Sthompsa struct cdce_softc *sc = usbd_xfer_softc(xfer); 1422197563Sthompsa struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, 0); 1423197563Sthompsa struct ifnet *ifp = uether_getifp(&sc->sc_ue); 1424197563Sthompsa struct mbuf *m; 1425197563Sthompsa int sumdata; 1426197563Sthompsa int sumlen; 1427197563Sthompsa int actlen; 1428197563Sthompsa int aframes; 1429197563Sthompsa int temp; 1430197563Sthompsa int nframes; 1431197563Sthompsa int x; 1432197563Sthompsa int offset; 1433197563Sthompsa 1434197563Sthompsa switch (USB_GET_STATE(xfer)) { 1435197563Sthompsa case USB_ST_TRANSFERRED: 1436197563Sthompsa 1437197563Sthompsa usbd_xfer_status(xfer, &actlen, &sumlen, &aframes, NULL); 1438197563Sthompsa 1439197563Sthompsa DPRINTFN(1, "received %u bytes in %u frames\n", 1440197563Sthompsa actlen, aframes); 1441197563Sthompsa 1442233774Shselasky if (actlen < (int)(sizeof(sc->sc_ncm.hdr) + 1443197563Sthompsa sizeof(sc->sc_ncm.dpt))) { 1444197563Sthompsa DPRINTFN(1, "frame too short\n"); 1445200307Sthompsa goto tr_setup; 1446197563Sthompsa } 1447197563Sthompsa usbd_copy_out(pc, 0, &(sc->sc_ncm.hdr), 1448197563Sthompsa sizeof(sc->sc_ncm.hdr)); 1449197563Sthompsa 1450197563Sthompsa if ((sc->sc_ncm.hdr.dwSignature[0] != 'N') || 1451197563Sthompsa (sc->sc_ncm.hdr.dwSignature[1] != 'C') || 1452197563Sthompsa (sc->sc_ncm.hdr.dwSignature[2] != 'M') || 1453197563Sthompsa (sc->sc_ncm.hdr.dwSignature[3] != 'H')) { 1454200307Sthompsa DPRINTFN(1, "invalid HDR signature: " 1455200307Sthompsa "0x%02x:0x%02x:0x%02x:0x%02x\n", 1456200307Sthompsa sc->sc_ncm.hdr.dwSignature[0], 1457200307Sthompsa sc->sc_ncm.hdr.dwSignature[1], 1458200307Sthompsa sc->sc_ncm.hdr.dwSignature[2], 1459200307Sthompsa sc->sc_ncm.hdr.dwSignature[3]); 1460197563Sthompsa goto tr_stall; 1461197563Sthompsa } 1462197563Sthompsa temp = UGETW(sc->sc_ncm.hdr.wBlockLength); 1463197563Sthompsa if (temp > sumlen) { 1464197563Sthompsa DPRINTFN(1, "unsupported block length %u/%u\n", 1465197563Sthompsa temp, sumlen); 1466197563Sthompsa goto tr_stall; 1467197563Sthompsa } 1468197563Sthompsa temp = UGETW(sc->sc_ncm.hdr.wDptIndex); 1469233774Shselasky if ((int)(temp + sizeof(sc->sc_ncm.dpt)) > actlen) { 1470200307Sthompsa DPRINTFN(1, "invalid DPT index: 0x%04x\n", temp); 1471197563Sthompsa goto tr_stall; 1472197563Sthompsa } 1473197563Sthompsa usbd_copy_out(pc, temp, &(sc->sc_ncm.dpt), 1474197563Sthompsa sizeof(sc->sc_ncm.dpt)); 1475197563Sthompsa 1476197563Sthompsa if ((sc->sc_ncm.dpt.dwSignature[0] != 'N') || 1477197563Sthompsa (sc->sc_ncm.dpt.dwSignature[1] != 'C') || 1478197563Sthompsa (sc->sc_ncm.dpt.dwSignature[2] != 'M') || 1479200307Sthompsa (sc->sc_ncm.dpt.dwSignature[3] != '0')) { 1480200307Sthompsa DPRINTFN(1, "invalid DPT signature" 1481200307Sthompsa "0x%02x:0x%02x:0x%02x:0x%02x\n", 1482200307Sthompsa sc->sc_ncm.dpt.dwSignature[0], 1483200307Sthompsa sc->sc_ncm.dpt.dwSignature[1], 1484200307Sthompsa sc->sc_ncm.dpt.dwSignature[2], 1485200307Sthompsa sc->sc_ncm.dpt.dwSignature[3]); 1486197563Sthompsa goto tr_stall; 1487197563Sthompsa } 1488197563Sthompsa nframes = UGETW(sc->sc_ncm.dpt.wLength) / 4; 1489197563Sthompsa 1490197563Sthompsa /* Subtract size of header and last zero padded entry */ 1491197563Sthompsa if (nframes >= (2 + 1)) 1492197563Sthompsa nframes -= (2 + 1); 1493197563Sthompsa else 1494197563Sthompsa nframes = 0; 1495197563Sthompsa 1496197563Sthompsa DPRINTFN(1, "nframes = %u\n", nframes); 1497197563Sthompsa 1498197563Sthompsa temp += sizeof(sc->sc_ncm.dpt); 1499197563Sthompsa 1500197563Sthompsa if ((temp + (4 * nframes)) > actlen) 1501197563Sthompsa goto tr_stall; 1502197563Sthompsa 1503197563Sthompsa if (nframes > CDCE_NCM_SUBFRAMES_MAX) { 1504197563Sthompsa DPRINTFN(1, "Truncating number of frames from %u to %u\n", 1505197563Sthompsa nframes, CDCE_NCM_SUBFRAMES_MAX); 1506197563Sthompsa nframes = CDCE_NCM_SUBFRAMES_MAX; 1507197563Sthompsa } 1508197563Sthompsa usbd_copy_out(pc, temp, &(sc->sc_ncm.dp), (4 * nframes)); 1509197563Sthompsa 1510197563Sthompsa sumdata = 0; 1511197563Sthompsa 1512197563Sthompsa for (x = 0; x != nframes; x++) { 1513197563Sthompsa 1514197563Sthompsa offset = UGETW(sc->sc_ncm.dp[x].wFrameIndex); 1515197563Sthompsa temp = UGETW(sc->sc_ncm.dp[x].wFrameLength); 1516198153Sthompsa 1517198153Sthompsa if ((offset == 0) || 1518233774Shselasky (temp < (int)sizeof(struct ether_header)) || 1519198153Sthompsa (temp > (MCLBYTES - ETHER_ALIGN))) { 1520198153Sthompsa DPRINTFN(1, "NULL frame detected at %d\n", x); 1521197563Sthompsa m = NULL; 1522198153Sthompsa /* silently ignore this frame */ 1523198153Sthompsa continue; 1524198153Sthompsa } else if ((offset + temp) > actlen) { 1525198153Sthompsa DPRINTFN(1, "invalid frame " 1526198153Sthompsa "detected at %d\n", x); 1527198153Sthompsa m = NULL; 1528198153Sthompsa /* silently ignore this frame */ 1529198153Sthompsa continue; 1530233774Shselasky } else if (temp > (int)(MHLEN - ETHER_ALIGN)) { 1531243857Sglebius m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 1532197563Sthompsa } else { 1533243857Sglebius m = m_gethdr(M_NOWAIT, MT_DATA); 1534197563Sthompsa } 1535197563Sthompsa 1536197563Sthompsa DPRINTFN(16, "frame %u, offset = %u, length = %u \n", 1537197563Sthompsa x, offset, temp); 1538197563Sthompsa 1539197563Sthompsa /* check if we have a buffer */ 1540197563Sthompsa if (m) { 1541290441Shselasky m->m_len = m->m_pkthdr.len = temp + ETHER_ALIGN; 1542197563Sthompsa m_adj(m, ETHER_ALIGN); 1543197563Sthompsa 1544197563Sthompsa usbd_copy_out(pc, offset, m->m_data, temp); 1545197563Sthompsa 1546197563Sthompsa /* enqueue */ 1547197563Sthompsa uether_rxmbuf(&sc->sc_ue, m, temp); 1548197563Sthompsa 1549197563Sthompsa sumdata += temp; 1550197563Sthompsa } else { 1551271832Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 1552197563Sthompsa } 1553197563Sthompsa } 1554197563Sthompsa 1555197563Sthompsa DPRINTFN(1, "Efficiency: %u/%u bytes\n", sumdata, actlen); 1556197563Sthompsa 1557197563Sthompsa case USB_ST_SETUP: 1558200307Sthompsatr_setup: 1559197563Sthompsa usbd_xfer_set_frame_len(xfer, 0, sc->sc_ncm.rx_max); 1560197563Sthompsa usbd_xfer_set_frames(xfer, 1); 1561197563Sthompsa usbd_transfer_submit(xfer); 1562197563Sthompsa uether_rxflush(&sc->sc_ue); /* must be last */ 1563197563Sthompsa break; 1564197563Sthompsa 1565197563Sthompsa default: /* Error */ 1566197563Sthompsa DPRINTFN(1, "error = %s\n", 1567197563Sthompsa usbd_errstr(error)); 1568197563Sthompsa 1569197563Sthompsa if (error != USB_ERR_CANCELLED) { 1570197563Sthompsatr_stall: 1571277420Sbr if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { 1572277420Sbr /* try to clear stall first */ 1573277420Sbr usbd_xfer_set_stall(xfer); 1574277420Sbr usbd_xfer_set_frames(xfer, 0); 1575277420Sbr usbd_transfer_submit(xfer); 1576277420Sbr } 1577197563Sthompsa } 1578197563Sthompsa break; 1579197563Sthompsa } 1580197563Sthompsa} 1581197563Sthompsa#endif 1582