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$"); 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> 57194677Sthompsa#include <sys/kernel.h> 58194677Sthompsa#include <sys/bus.h> 59194677Sthompsa#include <sys/module.h> 60194677Sthompsa#include <sys/lock.h> 61194677Sthompsa#include <sys/mutex.h> 62194677Sthompsa#include <sys/condvar.h> 63194677Sthompsa#include <sys/sysctl.h> 64194677Sthompsa#include <sys/sx.h> 65194677Sthompsa#include <sys/unistd.h> 66194677Sthompsa#include <sys/callout.h> 67194677Sthompsa#include <sys/malloc.h> 68194677Sthompsa#include <sys/priv.h> 69194677Sthompsa 70188942Sthompsa#include <dev/usb/usb.h> 71194677Sthompsa#include <dev/usb/usbdi.h> 72194677Sthompsa#include <dev/usb/usbdi_util.h> 73188942Sthompsa#include <dev/usb/usb_cdc.h> 74194677Sthompsa#include "usbdevs.h" 75184610Salfred 76184610Salfred#define USB_DEBUG_VAR cdce_debug 77194677Sthompsa#include <dev/usb/usb_debug.h> 78188942Sthompsa#include <dev/usb/usb_process.h> 79194677Sthompsa#include "usb_if.h" 80184610Salfred 81188942Sthompsa#include <dev/usb/net/usb_ethernet.h> 82188942Sthompsa#include <dev/usb/net/if_cdcereg.h> 83184610Salfred 84184610Salfredstatic device_probe_t cdce_probe; 85184610Salfredstatic device_attach_t cdce_attach; 86184610Salfredstatic device_detach_t cdce_detach; 87184610Salfredstatic device_suspend_t cdce_suspend; 88184610Salfredstatic device_resume_t cdce_resume; 89188942Sthompsastatic usb_handle_request_t cdce_handle_request; 90184610Salfred 91193045Sthompsastatic usb_callback_t cdce_bulk_write_callback; 92193045Sthompsastatic usb_callback_t cdce_bulk_read_callback; 93193045Sthompsastatic usb_callback_t cdce_intr_read_callback; 94193045Sthompsastatic usb_callback_t cdce_intr_write_callback; 95184610Salfred 96197563Sthompsa#if CDCE_HAVE_NCM 97197563Sthompsastatic usb_callback_t cdce_ncm_bulk_write_callback; 98197563Sthompsastatic usb_callback_t cdce_ncm_bulk_read_callback; 99197563Sthompsa#endif 100197563Sthompsa 101193045Sthompsastatic uether_fn_t cdce_attach_post; 102193045Sthompsastatic uether_fn_t cdce_init; 103193045Sthompsastatic uether_fn_t cdce_stop; 104193045Sthompsastatic uether_fn_t cdce_start; 105193045Sthompsastatic uether_fn_t cdce_setmulti; 106193045Sthompsastatic uether_fn_t cdce_setpromisc; 107188412Sthompsa 108185948Sthompsastatic uint32_t cdce_m_crc32(struct mbuf *, uint32_t, uint32_t); 109184610Salfred 110207077Sthompsa#ifdef USB_DEBUG 111184610Salfredstatic int cdce_debug = 0; 112213809Shselaskystatic int cdce_tx_interval = 0; 113184610Salfred 114248085Smariusstatic SYSCTL_NODE(_hw_usb, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet"); 115192502SthompsaSYSCTL_INT(_hw_usb_cdce, OID_AUTO, debug, CTLFLAG_RW, &cdce_debug, 0, 116188412Sthompsa "Debug level"); 117213809ShselaskySYSCTL_INT(_hw_usb_cdce, OID_AUTO, interval, CTLFLAG_RW, &cdce_tx_interval, 0, 118213809Shselasky "NCM transmit interval in ms"); 119184610Salfred#endif 120184610Salfred 121192984Sthompsastatic const struct usb_config cdce_config[CDCE_N_TRANSFER] = { 122184610Salfred 123190734Sthompsa [CDCE_BULK_RX] = { 124184610Salfred .type = UE_BULK, 125184610Salfred .endpoint = UE_ADDR_ANY, 126190734Sthompsa .direction = UE_DIR_RX, 127184610Salfred .if_index = 0, 128190734Sthompsa .frames = CDCE_FRAMES_MAX, 129190734Sthompsa .bufsize = (CDCE_FRAMES_MAX * MCLBYTES), 130190734Sthompsa .flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, 131190734Sthompsa .callback = cdce_bulk_read_callback, 132190734Sthompsa .timeout = 0, /* no timeout */ 133192499Sthompsa .usb_mode = USB_MODE_DUAL, /* both modes */ 134184610Salfred }, 135184610Salfred 136190734Sthompsa [CDCE_BULK_TX] = { 137184610Salfred .type = UE_BULK, 138184610Salfred .endpoint = UE_ADDR_ANY, 139190734Sthompsa .direction = UE_DIR_TX, 140184610Salfred .if_index = 0, 141190734Sthompsa .frames = CDCE_FRAMES_MAX, 142190734Sthompsa .bufsize = (CDCE_FRAMES_MAX * MCLBYTES), 143190734Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, 144190734Sthompsa .callback = cdce_bulk_write_callback, 145190734Sthompsa .timeout = 10000, /* 10 seconds */ 146192499Sthompsa .usb_mode = USB_MODE_DUAL, /* both modes */ 147184610Salfred }, 148184610Salfred 149190734Sthompsa [CDCE_INTR_RX] = { 150184610Salfred .type = UE_INTERRUPT, 151184610Salfred .endpoint = UE_ADDR_ANY, 152190734Sthompsa .direction = UE_DIR_RX, 153184610Salfred .if_index = 1, 154190734Sthompsa .bufsize = CDCE_IND_SIZE_MAX, 155190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, 156190734Sthompsa .callback = cdce_intr_read_callback, 157190734Sthompsa .timeout = 0, 158190734Sthompsa .usb_mode = USB_MODE_HOST, 159184610Salfred }, 160190734Sthompsa 161190734Sthompsa [CDCE_INTR_TX] = { 162190734Sthompsa .type = UE_INTERRUPT, 163190734Sthompsa .endpoint = UE_ADDR_ANY, 164190734Sthompsa .direction = UE_DIR_TX, 165190734Sthompsa .if_index = 1, 166190734Sthompsa .bufsize = CDCE_IND_SIZE_MAX, 167190734Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, 168190734Sthompsa .callback = cdce_intr_write_callback, 169190734Sthompsa .timeout = 10000, /* 10 seconds */ 170190734Sthompsa .usb_mode = USB_MODE_DEVICE, 171190734Sthompsa }, 172184610Salfred}; 173184610Salfred 174197563Sthompsa#if CDCE_HAVE_NCM 175197563Sthompsastatic const struct usb_config cdce_ncm_config[CDCE_N_TRANSFER] = { 176197563Sthompsa 177197563Sthompsa [CDCE_BULK_RX] = { 178197563Sthompsa .type = UE_BULK, 179197563Sthompsa .endpoint = UE_ADDR_ANY, 180197563Sthompsa .direction = UE_DIR_RX, 181197563Sthompsa .if_index = 0, 182197563Sthompsa .frames = CDCE_NCM_RX_FRAMES_MAX, 183197563Sthompsa .bufsize = (CDCE_NCM_RX_FRAMES_MAX * CDCE_NCM_RX_MAXLEN), 184197563Sthompsa .flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,}, 185197563Sthompsa .callback = cdce_ncm_bulk_read_callback, 186197563Sthompsa .timeout = 0, /* no timeout */ 187197563Sthompsa .usb_mode = USB_MODE_DUAL, /* both modes */ 188197563Sthompsa }, 189197563Sthompsa 190197563Sthompsa [CDCE_BULK_TX] = { 191197563Sthompsa .type = UE_BULK, 192197563Sthompsa .endpoint = UE_ADDR_ANY, 193197563Sthompsa .direction = UE_DIR_TX, 194197563Sthompsa .if_index = 0, 195197563Sthompsa .frames = CDCE_NCM_TX_FRAMES_MAX, 196197563Sthompsa .bufsize = (CDCE_NCM_TX_FRAMES_MAX * CDCE_NCM_TX_MAXLEN), 197213809Shselasky .flags = {.pipe_bof = 1,}, 198197563Sthompsa .callback = cdce_ncm_bulk_write_callback, 199197563Sthompsa .timeout = 10000, /* 10 seconds */ 200197563Sthompsa .usb_mode = USB_MODE_DUAL, /* both modes */ 201197563Sthompsa }, 202197563Sthompsa 203197563Sthompsa [CDCE_INTR_RX] = { 204197563Sthompsa .type = UE_INTERRUPT, 205197563Sthompsa .endpoint = UE_ADDR_ANY, 206197563Sthompsa .direction = UE_DIR_RX, 207197563Sthompsa .if_index = 1, 208197563Sthompsa .bufsize = CDCE_IND_SIZE_MAX, 209197563Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, 210197563Sthompsa .callback = cdce_intr_read_callback, 211197563Sthompsa .timeout = 0, 212197563Sthompsa .usb_mode = USB_MODE_HOST, 213197563Sthompsa }, 214197563Sthompsa 215197563Sthompsa [CDCE_INTR_TX] = { 216197563Sthompsa .type = UE_INTERRUPT, 217197563Sthompsa .endpoint = UE_ADDR_ANY, 218197563Sthompsa .direction = UE_DIR_TX, 219197563Sthompsa .if_index = 1, 220197563Sthompsa .bufsize = CDCE_IND_SIZE_MAX, 221197563Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, 222197563Sthompsa .callback = cdce_intr_write_callback, 223197563Sthompsa .timeout = 10000, /* 10 seconds */ 224197563Sthompsa .usb_mode = USB_MODE_DEVICE, 225197563Sthompsa }, 226197563Sthompsa}; 227197563Sthompsa#endif 228197563Sthompsa 229184610Salfredstatic device_method_t cdce_methods[] = { 230184610Salfred /* USB interface */ 231188942Sthompsa DEVMETHOD(usb_handle_request, cdce_handle_request), 232184610Salfred 233184610Salfred /* Device interface */ 234184610Salfred DEVMETHOD(device_probe, cdce_probe), 235184610Salfred DEVMETHOD(device_attach, cdce_attach), 236184610Salfred DEVMETHOD(device_detach, cdce_detach), 237184610Salfred DEVMETHOD(device_suspend, cdce_suspend), 238184610Salfred DEVMETHOD(device_resume, cdce_resume), 239184610Salfred 240184610Salfred {0, 0} 241184610Salfred}; 242184610Salfred 243184610Salfredstatic driver_t cdce_driver = { 244184610Salfred .name = "cdce", 245184610Salfred .methods = cdce_methods, 246184610Salfred .size = sizeof(struct cdce_softc), 247184610Salfred}; 248184610Salfred 249184610Salfredstatic devclass_t cdce_devclass; 250184610Salfred 251189275SthompsaDRIVER_MODULE(cdce, uhub, cdce_driver, cdce_devclass, NULL, 0); 252184610SalfredMODULE_VERSION(cdce, 1); 253188942SthompsaMODULE_DEPEND(cdce, uether, 1, 1, 1); 254188942SthompsaMODULE_DEPEND(cdce, usb, 1, 1, 1); 255184610SalfredMODULE_DEPEND(cdce, ether, 1, 1, 1); 256184610Salfred 257192984Sthompsastatic const struct usb_ether_methods cdce_ue_methods = { 258188412Sthompsa .ue_attach_post = cdce_attach_post, 259188412Sthompsa .ue_start = cdce_start, 260188412Sthompsa .ue_init = cdce_init, 261188412Sthompsa .ue_stop = cdce_stop, 262188412Sthompsa .ue_setmulti = cdce_setmulti, 263188412Sthompsa .ue_setpromisc = cdce_setpromisc, 264188412Sthompsa}; 265188412Sthompsa 266223486Shselaskystatic const STRUCT_USB_HOST_ID cdce_host_devs[] = { 267184610Salfred {USB_VPI(USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632, CDCE_FLAG_NO_UNION)}, 268184610Salfred {USB_VPI(USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250, CDCE_FLAG_NO_UNION)}, 269184610Salfred {USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX, CDCE_FLAG_NO_UNION)}, 270184610Salfred {USB_VPI(USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00, CDCE_FLAG_NO_UNION)}, 271184610Salfred {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 272184610Salfred {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 273184610Salfred {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET, CDCE_FLAG_NO_UNION)}, 274184610Salfred {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501, CDCE_FLAG_NO_UNION)}, 275184610Salfred {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500, CDCE_FLAG_ZAURUS)}, 276184610Salfred {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 277184610Salfred {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLA300, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 278184610Salfred {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC700, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 279184610Salfred {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC750, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, 280223486Shselasky}; 281196492Salfred 282223486Shselaskystatic const STRUCT_USB_DUAL_ID cdce_dual_devs[] = { 283196492Salfred {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0)}, 284196492Salfred {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_MOBILE_DIRECT_LINE_MODEL, 0)}, 285197563Sthompsa {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_NETWORK_CONTROL_MODEL, 0)}, 286184610Salfred}; 287184610Salfred 288197563Sthompsa#if CDCE_HAVE_NCM 289197563Sthompsa/*------------------------------------------------------------------------* 290197563Sthompsa * cdce_ncm_init 291197563Sthompsa * 292197563Sthompsa * Return values: 293197563Sthompsa * 0: Success 294197563Sthompsa * Else: Failure 295197563Sthompsa *------------------------------------------------------------------------*/ 296197563Sthompsastatic uint8_t 297197563Sthompsacdce_ncm_init(struct cdce_softc *sc) 298197563Sthompsa{ 299197563Sthompsa struct usb_ncm_parameters temp; 300197563Sthompsa struct usb_device_request req; 301213809Shselasky struct usb_ncm_func_descriptor *ufd; 302213809Shselasky uint8_t value[8]; 303197563Sthompsa int err; 304197563Sthompsa 305213809Shselasky ufd = usbd_find_descriptor(sc->sc_ue.ue_udev, NULL, 306235000Shselasky sc->sc_ifaces_index[1], UDESC_CS_INTERFACE, 0xFF, 307235000Shselasky UCDC_NCM_FUNC_DESC_SUBTYPE, 0xFF); 308213809Shselasky 309213809Shselasky /* verify length of NCM functional descriptor */ 310213809Shselasky if (ufd != NULL) { 311213809Shselasky if (ufd->bLength < sizeof(*ufd)) 312213809Shselasky ufd = NULL; 313213809Shselasky else 314213809Shselasky DPRINTFN(1, "Found NCM functional descriptor.\n"); 315213809Shselasky } 316213809Shselasky 317197563Sthompsa req.bmRequestType = UT_READ_CLASS_INTERFACE; 318197563Sthompsa req.bRequest = UCDC_NCM_GET_NTB_PARAMETERS; 319197563Sthompsa USETW(req.wValue, 0); 320197563Sthompsa req.wIndex[0] = sc->sc_ifaces_index[1]; 321197563Sthompsa req.wIndex[1] = 0; 322197563Sthompsa USETW(req.wLength, sizeof(temp)); 323197563Sthompsa 324197563Sthompsa err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, 325197563Sthompsa &temp, 0, NULL, 1000 /* ms */); 326197563Sthompsa if (err) 327197563Sthompsa return (1); 328197563Sthompsa 329197563Sthompsa /* Read correct set of parameters according to device mode */ 330197563Sthompsa 331197563Sthompsa if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { 332212133Sthompsa sc->sc_ncm.rx_max = UGETDW(temp.dwNtbInMaxSize); 333212133Sthompsa sc->sc_ncm.tx_max = UGETDW(temp.dwNtbOutMaxSize); 334197563Sthompsa sc->sc_ncm.tx_remainder = UGETW(temp.wNdpOutPayloadRemainder); 335197563Sthompsa sc->sc_ncm.tx_modulus = UGETW(temp.wNdpOutDivisor); 336197563Sthompsa sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpOutAlignment); 337213809Shselasky sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams); 338197563Sthompsa } else { 339212133Sthompsa sc->sc_ncm.rx_max = UGETDW(temp.dwNtbOutMaxSize); 340212133Sthompsa sc->sc_ncm.tx_max = UGETDW(temp.dwNtbInMaxSize); 341197563Sthompsa sc->sc_ncm.tx_remainder = UGETW(temp.wNdpInPayloadRemainder); 342197563Sthompsa sc->sc_ncm.tx_modulus = UGETW(temp.wNdpInDivisor); 343197563Sthompsa sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpInAlignment); 344213809Shselasky sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams); 345197563Sthompsa } 346197563Sthompsa 347197563Sthompsa /* Verify maximum receive length */ 348197563Sthompsa 349213809Shselasky if ((sc->sc_ncm.rx_max < 32) || 350197563Sthompsa (sc->sc_ncm.rx_max > CDCE_NCM_RX_MAXLEN)) { 351197563Sthompsa DPRINTFN(1, "Using default maximum receive length\n"); 352197563Sthompsa sc->sc_ncm.rx_max = CDCE_NCM_RX_MAXLEN; 353197563Sthompsa } 354197563Sthompsa 355197563Sthompsa /* Verify maximum transmit length */ 356197563Sthompsa 357213809Shselasky if ((sc->sc_ncm.tx_max < 32) || 358197563Sthompsa (sc->sc_ncm.tx_max > CDCE_NCM_TX_MAXLEN)) { 359197563Sthompsa DPRINTFN(1, "Using default maximum transmit length\n"); 360197563Sthompsa sc->sc_ncm.tx_max = CDCE_NCM_TX_MAXLEN; 361197563Sthompsa } 362197563Sthompsa 363197563Sthompsa /* 364197563Sthompsa * Verify that the structure alignment is: 365197563Sthompsa * - power of two 366197563Sthompsa * - not greater than the maximum transmit length 367197563Sthompsa * - not less than four bytes 368197563Sthompsa */ 369213809Shselasky if ((sc->sc_ncm.tx_struct_align < 4) || 370197563Sthompsa (sc->sc_ncm.tx_struct_align != 371197563Sthompsa ((-sc->sc_ncm.tx_struct_align) & sc->sc_ncm.tx_struct_align)) || 372197563Sthompsa (sc->sc_ncm.tx_struct_align >= sc->sc_ncm.tx_max)) { 373197563Sthompsa DPRINTFN(1, "Using default other alignment: 4 bytes\n"); 374197563Sthompsa sc->sc_ncm.tx_struct_align = 4; 375197563Sthompsa } 376197563Sthompsa 377197563Sthompsa /* 378197563Sthompsa * Verify that the payload alignment is: 379197563Sthompsa * - power of two 380197563Sthompsa * - not greater than the maximum transmit length 381197563Sthompsa * - not less than four bytes 382197563Sthompsa */ 383213809Shselasky if ((sc->sc_ncm.tx_modulus < 4) || 384197563Sthompsa (sc->sc_ncm.tx_modulus != 385197563Sthompsa ((-sc->sc_ncm.tx_modulus) & sc->sc_ncm.tx_modulus)) || 386197563Sthompsa (sc->sc_ncm.tx_modulus >= sc->sc_ncm.tx_max)) { 387197563Sthompsa DPRINTFN(1, "Using default transmit modulus: 4 bytes\n"); 388197563Sthompsa sc->sc_ncm.tx_modulus = 4; 389197563Sthompsa } 390197563Sthompsa 391197563Sthompsa /* Verify that the payload remainder */ 392197563Sthompsa 393213809Shselasky if ((sc->sc_ncm.tx_remainder >= sc->sc_ncm.tx_modulus)) { 394197563Sthompsa DPRINTFN(1, "Using default transmit remainder: 0 bytes\n"); 395197563Sthompsa sc->sc_ncm.tx_remainder = 0; 396197563Sthompsa } 397197563Sthompsa 398213809Shselasky /* 399213809Shselasky * Offset the TX remainder so that IP packet payload starts at 400213809Shselasky * the tx_modulus. This is not too clear in the specification. 401213809Shselasky */ 402213809Shselasky 403213809Shselasky sc->sc_ncm.tx_remainder = 404213809Shselasky (sc->sc_ncm.tx_remainder - ETHER_HDR_LEN) & 405213809Shselasky (sc->sc_ncm.tx_modulus - 1); 406213809Shselasky 407213809Shselasky /* Verify max datagrams */ 408213809Shselasky 409213809Shselasky if (sc->sc_ncm.tx_nframe == 0 || 410213809Shselasky sc->sc_ncm.tx_nframe > (CDCE_NCM_SUBFRAMES_MAX - 1)) { 411213809Shselasky DPRINTFN(1, "Using default max " 412213809Shselasky "subframes: %u units\n", CDCE_NCM_SUBFRAMES_MAX - 1); 413213809Shselasky /* need to reserve one entry for zero padding */ 414213809Shselasky sc->sc_ncm.tx_nframe = (CDCE_NCM_SUBFRAMES_MAX - 1); 415213809Shselasky } 416213809Shselasky 417197563Sthompsa /* Additional configuration, will fail in device side mode, which is OK. */ 418197563Sthompsa 419197563Sthompsa req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 420197563Sthompsa req.bRequest = UCDC_NCM_SET_NTB_INPUT_SIZE; 421197563Sthompsa USETW(req.wValue, 0); 422197563Sthompsa req.wIndex[0] = sc->sc_ifaces_index[1]; 423197563Sthompsa req.wIndex[1] = 0; 424197563Sthompsa 425213809Shselasky if (ufd != NULL && 426213809Shselasky (ufd->bmNetworkCapabilities & UCDC_NCM_CAP_MAX_DGRAM)) { 427213809Shselasky USETW(req.wLength, 8); 428213809Shselasky USETDW(value, sc->sc_ncm.rx_max); 429213809Shselasky USETW(value + 4, (CDCE_NCM_SUBFRAMES_MAX - 1)); 430213809Shselasky USETW(value + 6, 0); 431213809Shselasky } else { 432213809Shselasky USETW(req.wLength, 4); 433213809Shselasky USETDW(value, sc->sc_ncm.rx_max); 434213809Shselasky } 435213809Shselasky 436197563Sthompsa err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, 437197563Sthompsa &value, 0, NULL, 1000 /* ms */); 438197563Sthompsa if (err) { 439197563Sthompsa DPRINTFN(1, "Setting input size " 440197563Sthompsa "to %u failed.\n", sc->sc_ncm.rx_max); 441197563Sthompsa } 442197563Sthompsa 443197563Sthompsa req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 444197563Sthompsa req.bRequest = UCDC_NCM_SET_CRC_MODE; 445197563Sthompsa USETW(req.wValue, 0); /* no CRC */ 446197563Sthompsa req.wIndex[0] = sc->sc_ifaces_index[1]; 447197563Sthompsa req.wIndex[1] = 0; 448197563Sthompsa USETW(req.wLength, 0); 449197563Sthompsa 450197563Sthompsa err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, 451197563Sthompsa NULL, 0, NULL, 1000 /* ms */); 452197563Sthompsa if (err) { 453197563Sthompsa DPRINTFN(1, "Setting CRC mode to off failed.\n"); 454197563Sthompsa } 455197563Sthompsa 456197563Sthompsa req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 457197563Sthompsa req.bRequest = UCDC_NCM_SET_NTB_FORMAT; 458197563Sthompsa USETW(req.wValue, 0); /* NTB-16 */ 459197563Sthompsa req.wIndex[0] = sc->sc_ifaces_index[1]; 460197563Sthompsa req.wIndex[1] = 0; 461197563Sthompsa USETW(req.wLength, 0); 462197563Sthompsa 463197563Sthompsa err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, 464197563Sthompsa NULL, 0, NULL, 1000 /* ms */); 465197563Sthompsa if (err) { 466197563Sthompsa DPRINTFN(1, "Setting NTB format to 16-bit failed.\n"); 467197563Sthompsa } 468197563Sthompsa 469197563Sthompsa return (0); /* success */ 470197563Sthompsa} 471197563Sthompsa#endif 472197563Sthompsa 473184610Salfredstatic int 474184610Salfredcdce_probe(device_t dev) 475184610Salfred{ 476192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 477223486Shselasky int error; 478184610Salfred 479223486Shselasky error = usbd_lookup_id_by_uaa(cdce_host_devs, sizeof(cdce_host_devs), uaa); 480223486Shselasky if (error) 481223486Shselasky error = usbd_lookup_id_by_uaa(cdce_dual_devs, sizeof(cdce_dual_devs), uaa); 482223486Shselasky return (error); 483184610Salfred} 484184610Salfred 485188412Sthompsastatic void 486192984Sthompsacdce_attach_post(struct usb_ether *ue) 487188412Sthompsa{ 488188412Sthompsa /* no-op */ 489188412Sthompsa return; 490188412Sthompsa} 491188412Sthompsa 492184610Salfredstatic int 493184610Salfredcdce_attach(device_t dev) 494184610Salfred{ 495184610Salfred struct cdce_softc *sc = device_get_softc(dev); 496192984Sthompsa struct usb_ether *ue = &sc->sc_ue; 497192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 498192984Sthompsa struct usb_interface *iface; 499192984Sthompsa const struct usb_cdc_union_descriptor *ud; 500192984Sthompsa const struct usb_interface_descriptor *id; 501192984Sthompsa const struct usb_cdc_ethernet_descriptor *ued; 502197563Sthompsa const struct usb_config *pcfg; 503247555Sjhb uint32_t seed; 504184610Salfred int error; 505184610Salfred uint8_t i; 506197563Sthompsa uint8_t data_iface_no; 507184610Salfred char eaddr_str[5 * ETHER_ADDR_LEN]; /* approx */ 508184610Salfred 509184610Salfred sc->sc_flags = USB_GET_DRIVER_INFO(uaa); 510197563Sthompsa sc->sc_ue.ue_udev = uaa->device; 511184610Salfred 512194228Sthompsa device_set_usb_desc(dev); 513184610Salfred 514188412Sthompsa mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); 515184610Salfred 516194271Sthompsa ud = usbd_find_descriptor 517184610Salfred (uaa->device, NULL, uaa->info.bIfaceIndex, 518235000Shselasky UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_UNION, 0xFF); 519184610Salfred 520197563Sthompsa if ((ud == NULL) || (ud->bLength < sizeof(*ud)) || 521197563Sthompsa (sc->sc_flags & CDCE_FLAG_NO_UNION)) { 522197563Sthompsa DPRINTFN(1, "No union descriptor!\n"); 523197563Sthompsa sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex; 524197563Sthompsa sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; 525197563Sthompsa goto alloc_transfers; 526184610Salfred } 527197563Sthompsa data_iface_no = ud->bSlaveInterface[0]; 528184610Salfred 529184610Salfred for (i = 0;; i++) { 530184610Salfred 531194228Sthompsa iface = usbd_get_iface(uaa->device, i); 532184610Salfred 533184610Salfred if (iface) { 534184610Salfred 535194228Sthompsa id = usbd_get_interface_descriptor(iface); 536184610Salfred 537197563Sthompsa if (id && (id->bInterfaceNumber == data_iface_no)) { 538184610Salfred sc->sc_ifaces_index[0] = i; 539184610Salfred sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; 540194228Sthompsa usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 541184610Salfred break; 542184610Salfred } 543184610Salfred } else { 544199816Sthompsa device_printf(dev, "no data interface found\n"); 545184610Salfred goto detach; 546184610Salfred } 547184610Salfred } 548184610Salfred 549184610Salfred /* 550184610Salfred * <quote> 551184610Salfred * 552184610Salfred * The Data Class interface of a networking device shall have 553184610Salfred * a minimum of two interface settings. The first setting 554184610Salfred * (the default interface setting) includes no endpoints and 555184610Salfred * therefore no networking traffic is exchanged whenever the 556184610Salfred * default interface setting is selected. One or more 557184610Salfred * additional interface settings are used for normal 558184610Salfred * operation, and therefore each includes a pair of endpoints 559184610Salfred * (one IN, and one OUT) to exchange network traffic. Select 560184610Salfred * an alternate interface setting to initialize the network 561184610Salfred * aspects of the device and to enable the exchange of 562184610Salfred * network traffic. 563184610Salfred * 564184610Salfred * </quote> 565184610Salfred * 566184610Salfred * Some devices, most notably cable modems, include interface 567184610Salfred * settings that have no IN or OUT endpoint, therefore loop 568184610Salfred * through the list of all available interface settings 569184610Salfred * looking for one with both IN and OUT endpoints. 570184610Salfred */ 571184610Salfred 572184610Salfredalloc_transfers: 573184610Salfred 574197563Sthompsa pcfg = cdce_config; /* Default Configuration */ 575197563Sthompsa 576188412Sthompsa for (i = 0; i != 32; i++) { 577184610Salfred 578197563Sthompsa error = usbd_set_alt_interface_index(uaa->device, 579197563Sthompsa sc->sc_ifaces_index[0], i); 580197563Sthompsa if (error) 581197563Sthompsa break; 582197563Sthompsa#if CDCE_HAVE_NCM 583197563Sthompsa if ((i == 0) && (cdce_ncm_init(sc) == 0)) 584197563Sthompsa pcfg = cdce_ncm_config; 585197563Sthompsa#endif 586197563Sthompsa error = usbd_transfer_setup(uaa->device, 587197563Sthompsa sc->sc_ifaces_index, sc->sc_xfer, 588197563Sthompsa pcfg, CDCE_N_TRANSFER, sc, &sc->sc_mtx); 589184610Salfred 590197563Sthompsa if (error == 0) 591184610Salfred break; 592184610Salfred } 593184610Salfred 594197563Sthompsa if (error || (i == 32)) { 595197563Sthompsa device_printf(dev, "No valid alternate " 596199816Sthompsa "setting found\n"); 597197563Sthompsa goto detach; 598197563Sthompsa } 599197563Sthompsa 600194271Sthompsa ued = usbd_find_descriptor 601184610Salfred (uaa->device, NULL, uaa->info.bIfaceIndex, 602235000Shselasky UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_ENF, 0xFF); 603184610Salfred 604188412Sthompsa if ((ued == NULL) || (ued->bLength < sizeof(*ued))) { 605184610Salfred error = USB_ERR_INVAL; 606184610Salfred } else { 607194228Sthompsa error = usbd_req_get_string_any(uaa->device, NULL, 608188412Sthompsa eaddr_str, sizeof(eaddr_str), ued->iMacAddress); 609184610Salfred } 610184610Salfred 611184610Salfred if (error) { 612184610Salfred 613184610Salfred /* fake MAC address */ 614184610Salfred 615184610Salfred device_printf(dev, "faking MAC address\n"); 616247555Sjhb seed = ticks; 617188412Sthompsa sc->sc_ue.ue_eaddr[0] = 0x2a; 618247555Sjhb memcpy(&sc->sc_ue.ue_eaddr[1], &seed, sizeof(uint32_t)); 619188412Sthompsa sc->sc_ue.ue_eaddr[5] = device_get_unit(dev); 620184610Salfred 621184610Salfred } else { 622184610Salfred 623213809Shselasky memset(sc->sc_ue.ue_eaddr, 0, sizeof(sc->sc_ue.ue_eaddr)); 624184610Salfred 625188412Sthompsa for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) { 626184610Salfred 627184610Salfred char c = eaddr_str[i]; 628184610Salfred 629188412Sthompsa if ('0' <= c && c <= '9') 630184610Salfred c -= '0'; 631188412Sthompsa else if (c != 0) 632184610Salfred c -= 'A' - 10; 633188412Sthompsa else 634184610Salfred break; 635184610Salfred 636184610Salfred c &= 0xf; 637184610Salfred 638188412Sthompsa if ((i & 1) == 0) 639184610Salfred c <<= 4; 640188412Sthompsa sc->sc_ue.ue_eaddr[i / 2] |= c; 641184610Salfred } 642184610Salfred 643192499Sthompsa if (uaa->usb_mode == USB_MODE_DEVICE) { 644184610Salfred /* 645184610Salfred * Do not use the same MAC address like the peer ! 646184610Salfred */ 647188412Sthompsa sc->sc_ue.ue_eaddr[5] ^= 0xFF; 648184610Salfred } 649184610Salfred } 650184610Salfred 651188412Sthompsa ue->ue_sc = sc; 652188412Sthompsa ue->ue_dev = dev; 653188412Sthompsa ue->ue_udev = uaa->device; 654188412Sthompsa ue->ue_mtx = &sc->sc_mtx; 655188412Sthompsa ue->ue_methods = &cdce_ue_methods; 656184610Salfred 657194228Sthompsa error = uether_ifattach(ue); 658188412Sthompsa if (error) { 659188412Sthompsa device_printf(dev, "could not attach interface\n"); 660184610Salfred goto detach; 661184610Salfred } 662184610Salfred return (0); /* success */ 663184610Salfred 664184610Salfreddetach: 665184610Salfred cdce_detach(dev); 666184610Salfred return (ENXIO); /* failure */ 667184610Salfred} 668184610Salfred 669184610Salfredstatic int 670184610Salfredcdce_detach(device_t dev) 671184610Salfred{ 672184610Salfred struct cdce_softc *sc = device_get_softc(dev); 673192984Sthompsa struct usb_ether *ue = &sc->sc_ue; 674184610Salfred 675184610Salfred /* stop all USB transfers first */ 676194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, CDCE_N_TRANSFER); 677194228Sthompsa uether_ifdetach(ue); 678184610Salfred mtx_destroy(&sc->sc_mtx); 679184610Salfred 680184610Salfred return (0); 681184610Salfred} 682184610Salfred 683184610Salfredstatic void 684192984Sthompsacdce_start(struct usb_ether *ue) 685184610Salfred{ 686194228Sthompsa struct cdce_softc *sc = uether_getsc(ue); 687184610Salfred 688188412Sthompsa /* 689188412Sthompsa * Start the USB transfers, if not already started: 690188412Sthompsa */ 691194228Sthompsa usbd_transfer_start(sc->sc_xfer[CDCE_BULK_TX]); 692194228Sthompsa usbd_transfer_start(sc->sc_xfer[CDCE_BULK_RX]); 693184610Salfred} 694184610Salfred 695184610Salfredstatic void 696188412Sthompsacdce_free_queue(struct mbuf **ppm, uint8_t n) 697184610Salfred{ 698188412Sthompsa uint8_t x; 699188412Sthompsa for (x = 0; x != n; x++) { 700188412Sthompsa if (ppm[x] != NULL) { 701188412Sthompsa m_freem(ppm[x]); 702188412Sthompsa ppm[x] = NULL; 703184610Salfred } 704184610Salfred } 705184610Salfred} 706184610Salfred 707184610Salfredstatic void 708194677Sthompsacdce_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 709184610Salfred{ 710194677Sthompsa struct cdce_softc *sc = usbd_xfer_softc(xfer); 711194228Sthompsa struct ifnet *ifp = uether_getifp(&sc->sc_ue); 712184610Salfred struct mbuf *m; 713184610Salfred struct mbuf *mt; 714188412Sthompsa uint32_t crc; 715188412Sthompsa uint8_t x; 716194677Sthompsa int actlen, aframes; 717184610Salfred 718194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 719194677Sthompsa 720188412Sthompsa DPRINTFN(1, "\n"); 721188412Sthompsa 722184610Salfred switch (USB_GET_STATE(xfer)) { 723184610Salfred case USB_ST_TRANSFERRED: 724194677Sthompsa DPRINTFN(11, "transfer complete: %u bytes in %u frames\n", 725194677Sthompsa actlen, aframes); 726184610Salfred 727188412Sthompsa ifp->if_opackets++; 728184610Salfred 729188412Sthompsa /* free all previous TX buffers */ 730188412Sthompsa cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX); 731184610Salfred 732188412Sthompsa /* FALLTHROUGH */ 733184610Salfred case USB_ST_SETUP: 734184610Salfredtr_setup: 735188412Sthompsa for (x = 0; x != CDCE_FRAMES_MAX; x++) { 736184610Salfred 737184610Salfred IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 738184610Salfred 739188412Sthompsa if (m == NULL) 740184610Salfred break; 741188412Sthompsa 742188412Sthompsa if (sc->sc_flags & CDCE_FLAG_ZAURUS) { 743188412Sthompsa /* 744188412Sthompsa * Zaurus wants a 32-bit CRC appended 745188412Sthompsa * to every frame 746188412Sthompsa */ 747188412Sthompsa 748188412Sthompsa crc = cdce_m_crc32(m, 0, m->m_pkthdr.len); 749188412Sthompsa crc = htole32(crc); 750188412Sthompsa 751188412Sthompsa if (!m_append(m, 4, (void *)&crc)) { 752188412Sthompsa m_freem(m); 753188412Sthompsa ifp->if_oerrors++; 754188412Sthompsa continue; 755188412Sthompsa } 756184610Salfred } 757188412Sthompsa if (m->m_len != m->m_pkthdr.len) { 758248078Smarius mt = m_defrag(m, M_NOWAIT); 759184610Salfred if (mt == NULL) { 760184610Salfred m_freem(m); 761184610Salfred ifp->if_oerrors++; 762184610Salfred continue; 763184610Salfred } 764184610Salfred m = mt; 765184610Salfred } 766188412Sthompsa if (m->m_pkthdr.len > MCLBYTES) { 767188412Sthompsa m->m_pkthdr.len = MCLBYTES; 768188412Sthompsa } 769188412Sthompsa sc->sc_tx_buf[x] = m; 770194677Sthompsa usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); 771184610Salfred 772184610Salfred /* 773188412Sthompsa * If there's a BPF listener, bounce a copy of 774188412Sthompsa * this frame to him: 775184610Salfred */ 776184610Salfred BPF_MTAP(ifp, m); 777184610Salfred } 778188412Sthompsa if (x != 0) { 779194677Sthompsa usbd_xfer_set_frames(xfer, x); 780194677Sthompsa 781194228Sthompsa usbd_transfer_submit(xfer); 782184610Salfred } 783184610Salfred break; 784184610Salfred 785184610Salfred default: /* Error */ 786184610Salfred DPRINTFN(11, "transfer error, %s\n", 787194677Sthompsa usbd_errstr(error)); 788184610Salfred 789188412Sthompsa /* free all previous TX buffers */ 790188412Sthompsa cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX); 791184610Salfred 792188412Sthompsa /* count output errors */ 793184610Salfred ifp->if_oerrors++; 794184610Salfred 795194677Sthompsa if (error != USB_ERR_CANCELLED) { 796184610Salfred /* try to clear stall first */ 797194677Sthompsa usbd_xfer_set_stall(xfer); 798184610Salfred goto tr_setup; 799184610Salfred } 800184610Salfred break; 801184610Salfred } 802184610Salfred} 803184610Salfred 804184610Salfredstatic int32_t 805184610Salfredcdce_m_crc32_cb(void *arg, void *src, uint32_t count) 806184610Salfred{ 807188412Sthompsa uint32_t *p_crc = arg; 808184610Salfred 809184610Salfred *p_crc = crc32_raw(src, count, *p_crc); 810184610Salfred return (0); 811184610Salfred} 812184610Salfred 813184610Salfredstatic uint32_t 814184610Salfredcdce_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len) 815184610Salfred{ 816184610Salfred uint32_t crc = 0xFFFFFFFF; 817188412Sthompsa int error; 818184610Salfred 819188412Sthompsa error = m_apply(m, src_offset, src_len, cdce_m_crc32_cb, &crc); 820184610Salfred return (crc ^ 0xFFFFFFFF); 821184610Salfred} 822184610Salfred 823184610Salfredstatic void 824192984Sthompsacdce_init(struct usb_ether *ue) 825184610Salfred{ 826194228Sthompsa struct cdce_softc *sc = uether_getsc(ue); 827194228Sthompsa struct ifnet *ifp = uether_getifp(ue); 828184610Salfred 829188412Sthompsa CDCE_LOCK_ASSERT(sc, MA_OWNED); 830184610Salfred 831188412Sthompsa ifp->if_drv_flags |= IFF_DRV_RUNNING; 832184610Salfred 833188412Sthompsa /* start interrupt transfer */ 834194228Sthompsa usbd_transfer_start(sc->sc_xfer[CDCE_INTR_RX]); 835194228Sthompsa usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]); 836188412Sthompsa 837188412Sthompsa /* stall data write direction, which depends on USB mode */ 838194677Sthompsa usbd_xfer_set_stall(sc->sc_xfer[CDCE_BULK_TX]); 839188412Sthompsa 840188412Sthompsa /* start data transfers */ 841188412Sthompsa cdce_start(ue); 842188412Sthompsa} 843188412Sthompsa 844188412Sthompsastatic void 845192984Sthompsacdce_stop(struct usb_ether *ue) 846188412Sthompsa{ 847194228Sthompsa struct cdce_softc *sc = uether_getsc(ue); 848194228Sthompsa struct ifnet *ifp = uether_getifp(ue); 849188412Sthompsa 850188412Sthompsa CDCE_LOCK_ASSERT(sc, MA_OWNED); 851188412Sthompsa 852188412Sthompsa ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 853188412Sthompsa 854184610Salfred /* 855184610Salfred * stop all the transfers, if not already stopped: 856184610Salfred */ 857194228Sthompsa usbd_transfer_stop(sc->sc_xfer[CDCE_BULK_RX]); 858194228Sthompsa usbd_transfer_stop(sc->sc_xfer[CDCE_BULK_TX]); 859194228Sthompsa usbd_transfer_stop(sc->sc_xfer[CDCE_INTR_RX]); 860194228Sthompsa usbd_transfer_stop(sc->sc_xfer[CDCE_INTR_TX]); 861184610Salfred} 862184610Salfred 863188412Sthompsastatic void 864192984Sthompsacdce_setmulti(struct usb_ether *ue) 865188412Sthompsa{ 866188412Sthompsa /* no-op */ 867188412Sthompsa return; 868188412Sthompsa} 869188412Sthompsa 870188412Sthompsastatic void 871192984Sthompsacdce_setpromisc(struct usb_ether *ue) 872188412Sthompsa{ 873188412Sthompsa /* no-op */ 874188412Sthompsa return; 875188412Sthompsa} 876188412Sthompsa 877184610Salfredstatic int 878184610Salfredcdce_suspend(device_t dev) 879184610Salfred{ 880184610Salfred device_printf(dev, "Suspending\n"); 881184610Salfred return (0); 882184610Salfred} 883184610Salfred 884184610Salfredstatic int 885184610Salfredcdce_resume(device_t dev) 886184610Salfred{ 887184610Salfred device_printf(dev, "Resuming\n"); 888184610Salfred return (0); 889184610Salfred} 890184610Salfred 891184610Salfredstatic void 892194677Sthompsacdce_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 893184610Salfred{ 894194677Sthompsa struct cdce_softc *sc = usbd_xfer_softc(xfer); 895184610Salfred struct mbuf *m; 896188412Sthompsa uint8_t x; 897235000Shselasky int actlen; 898235000Shselasky int aframes; 899235000Shselasky int len; 900184610Salfred 901194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 902194677Sthompsa 903184610Salfred switch (USB_GET_STATE(xfer)) { 904184610Salfred case USB_ST_TRANSFERRED: 905184610Salfred 906194677Sthompsa DPRINTF("received %u bytes in %u frames\n", actlen, aframes); 907184610Salfred 908194677Sthompsa for (x = 0; x != aframes; x++) { 909184610Salfred 910188412Sthompsa m = sc->sc_rx_buf[x]; 911188412Sthompsa sc->sc_rx_buf[x] = NULL; 912194682Sthompsa len = usbd_xfer_frame_len(xfer, x); 913188412Sthompsa 914188412Sthompsa /* Strip off CRC added by Zaurus, if any */ 915194677Sthompsa if ((sc->sc_flags & CDCE_FLAG_ZAURUS) && len >= 14) 916194677Sthompsa len -= 4; 917188412Sthompsa 918235000Shselasky if (len < (int)sizeof(struct ether_header)) { 919188412Sthompsa m_freem(m); 920188412Sthompsa continue; 921184610Salfred } 922188412Sthompsa /* queue up mbuf */ 923194677Sthompsa uether_rxmbuf(&sc->sc_ue, m, len); 924184610Salfred } 925184610Salfred 926188412Sthompsa /* FALLTHROUGH */ 927184610Salfred case USB_ST_SETUP: 928188412Sthompsa /* 929188412Sthompsa * TODO: Implement support for multi frame transfers, 930188412Sthompsa * when the USB hardware supports it. 931184610Salfred */ 932188412Sthompsa for (x = 0; x != 1; x++) { 933188412Sthompsa if (sc->sc_rx_buf[x] == NULL) { 934194228Sthompsa m = uether_newbuf(); 935188412Sthompsa if (m == NULL) 936188412Sthompsa goto tr_stall; 937188412Sthompsa sc->sc_rx_buf[x] = m; 938184610Salfred } else { 939188412Sthompsa m = sc->sc_rx_buf[x]; 940184610Salfred } 941184610Salfred 942194677Sthompsa usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); 943184610Salfred } 944188412Sthompsa /* set number of frames and start hardware */ 945194677Sthompsa usbd_xfer_set_frames(xfer, x); 946194228Sthompsa usbd_transfer_submit(xfer); 947188412Sthompsa /* flush any received frames */ 948194228Sthompsa uether_rxflush(&sc->sc_ue); 949184610Salfred break; 950184610Salfred 951184610Salfred default: /* Error */ 952184610Salfred DPRINTF("error = %s\n", 953194677Sthompsa usbd_errstr(error)); 954184610Salfred 955194677Sthompsa if (error != USB_ERR_CANCELLED) { 956188412Sthompsatr_stall: 957184610Salfred /* try to clear stall first */ 958194677Sthompsa usbd_xfer_set_stall(xfer); 959194677Sthompsa usbd_xfer_set_frames(xfer, 0); 960194228Sthompsa usbd_transfer_submit(xfer); 961188412Sthompsa break; 962184610Salfred } 963184610Salfred 964188412Sthompsa /* need to free the RX-mbufs when we are cancelled */ 965188412Sthompsa cdce_free_queue(sc->sc_rx_buf, CDCE_FRAMES_MAX); 966184610Salfred break; 967184610Salfred } 968184610Salfred} 969184610Salfred 970184610Salfredstatic void 971194677Sthompsacdce_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 972184610Salfred{ 973194677Sthompsa int actlen; 974194677Sthompsa 975194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 976194677Sthompsa 977184610Salfred switch (USB_GET_STATE(xfer)) { 978184610Salfred case USB_ST_TRANSFERRED: 979184610Salfred 980194677Sthompsa DPRINTF("Received %d bytes\n", actlen); 981184610Salfred 982184610Salfred /* TODO: decode some indications */ 983184610Salfred 984188412Sthompsa /* FALLTHROUGH */ 985184610Salfred case USB_ST_SETUP: 986184610Salfredtr_setup: 987194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 988194228Sthompsa usbd_transfer_submit(xfer); 989184610Salfred break; 990184610Salfred 991184610Salfred default: /* Error */ 992194677Sthompsa if (error != USB_ERR_CANCELLED) { 993184610Salfred /* start clear stall */ 994194677Sthompsa usbd_xfer_set_stall(xfer); 995184610Salfred goto tr_setup; 996184610Salfred } 997184610Salfred break; 998184610Salfred } 999184610Salfred} 1000184610Salfred 1001184610Salfredstatic void 1002194677Sthompsacdce_intr_write_callback(struct usb_xfer *xfer, usb_error_t error) 1003184610Salfred{ 1004194677Sthompsa int actlen; 1005194677Sthompsa 1006194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 1007194677Sthompsa 1008184610Salfred switch (USB_GET_STATE(xfer)) { 1009184610Salfred case USB_ST_TRANSFERRED: 1010184610Salfred 1011194677Sthompsa DPRINTF("Transferred %d bytes\n", actlen); 1012184610Salfred 1013188412Sthompsa /* FALLTHROUGH */ 1014184610Salfred case USB_ST_SETUP: 1015184610Salfredtr_setup: 1016184610Salfred#if 0 1017194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, XXX); 1018194228Sthompsa usbd_transfer_submit(xfer); 1019184610Salfred#endif 1020184610Salfred break; 1021184610Salfred 1022184610Salfred default: /* Error */ 1023194677Sthompsa if (error != USB_ERR_CANCELLED) { 1024184610Salfred /* start clear stall */ 1025194677Sthompsa usbd_xfer_set_stall(xfer); 1026184610Salfred goto tr_setup; 1027184610Salfred } 1028184610Salfred break; 1029184610Salfred } 1030184610Salfred} 1031184610Salfred 1032184610Salfredstatic int 1033184610Salfredcdce_handle_request(device_t dev, 1034184610Salfred const void *req, void **pptr, uint16_t *plen, 1035195121Sthompsa uint16_t offset, uint8_t *pstate) 1036184610Salfred{ 1037184610Salfred return (ENXIO); /* use builtin handler */ 1038184610Salfred} 1039197563Sthompsa 1040197563Sthompsa#if CDCE_HAVE_NCM 1041213809Shselaskystatic void 1042213809Shselaskycdce_ncm_tx_zero(struct usb_page_cache *pc, 1043213809Shselasky uint32_t start, uint32_t end) 1044213809Shselasky{ 1045213809Shselasky if (start >= CDCE_NCM_TX_MAXLEN) 1046213809Shselasky return; 1047213809Shselasky if (end > CDCE_NCM_TX_MAXLEN) 1048213809Shselasky end = CDCE_NCM_TX_MAXLEN; 1049213809Shselasky 1050213809Shselasky usbd_frame_zero(pc, start, end - start); 1051213809Shselasky} 1052213809Shselasky 1053197563Sthompsastatic uint8_t 1054197563Sthompsacdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index) 1055197563Sthompsa{ 1056197563Sthompsa struct cdce_softc *sc = usbd_xfer_softc(xfer); 1057197563Sthompsa struct ifnet *ifp = uether_getifp(&sc->sc_ue); 1058197563Sthompsa struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, index); 1059197563Sthompsa struct mbuf *m; 1060197563Sthompsa uint32_t rem; 1061197563Sthompsa uint32_t offset; 1062197563Sthompsa uint32_t last_offset; 1063213809Shselasky uint16_t n; 1064213809Shselasky uint8_t retval; 1065197563Sthompsa 1066197563Sthompsa usbd_xfer_set_frame_offset(xfer, index * CDCE_NCM_TX_MAXLEN, index); 1067197563Sthompsa 1068197563Sthompsa offset = sizeof(sc->sc_ncm.hdr) + 1069197563Sthompsa sizeof(sc->sc_ncm.dpt) + sizeof(sc->sc_ncm.dp); 1070197563Sthompsa 1071197563Sthompsa /* Store last valid offset before alignment */ 1072197563Sthompsa last_offset = offset; 1073197563Sthompsa 1074213809Shselasky /* Align offset */ 1075213809Shselasky offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder, 1076213809Shselasky offset, sc->sc_ncm.tx_modulus); 1077197563Sthompsa 1078213809Shselasky /* Zero pad */ 1079213809Shselasky cdce_ncm_tx_zero(pc, last_offset, offset); 1080197563Sthompsa 1081213809Shselasky /* buffer full */ 1082213809Shselasky retval = 2; 1083213809Shselasky 1084213809Shselasky for (n = 0; n != sc->sc_ncm.tx_nframe; n++) { 1085213809Shselasky 1086197563Sthompsa /* check if end of transmit buffer is reached */ 1087197563Sthompsa 1088197563Sthompsa if (offset >= sc->sc_ncm.tx_max) 1089197563Sthompsa break; 1090197563Sthompsa 1091197563Sthompsa /* compute maximum buffer size */ 1092197563Sthompsa 1093197563Sthompsa rem = sc->sc_ncm.tx_max - offset; 1094197563Sthompsa 1095197563Sthompsa IFQ_DRV_DEQUEUE(&(ifp->if_snd), m); 1096197563Sthompsa 1097213809Shselasky if (m == NULL) { 1098213809Shselasky /* buffer not full */ 1099213809Shselasky retval = 1; 1100197563Sthompsa break; 1101213809Shselasky } 1102197563Sthompsa 1103235000Shselasky if (m->m_pkthdr.len > (int)rem) { 1104197563Sthompsa if (n == 0) { 1105197563Sthompsa /* The frame won't fit in our buffer */ 1106197563Sthompsa DPRINTFN(1, "Frame too big to be transmitted!\n"); 1107197563Sthompsa m_freem(m); 1108197563Sthompsa ifp->if_oerrors++; 1109197563Sthompsa n--; 1110197563Sthompsa continue; 1111197563Sthompsa } 1112197563Sthompsa /* Wait till next buffer becomes ready */ 1113197563Sthompsa IFQ_DRV_PREPEND(&(ifp->if_snd), m); 1114197563Sthompsa break; 1115197563Sthompsa } 1116197563Sthompsa usbd_m_copy_in(pc, offset, m, 0, m->m_pkthdr.len); 1117197563Sthompsa 1118197563Sthompsa USETW(sc->sc_ncm.dp[n].wFrameLength, m->m_pkthdr.len); 1119197563Sthompsa USETW(sc->sc_ncm.dp[n].wFrameIndex, offset); 1120197563Sthompsa 1121197563Sthompsa /* Update offset */ 1122197563Sthompsa offset += m->m_pkthdr.len; 1123197563Sthompsa 1124197563Sthompsa /* Store last valid offset before alignment */ 1125197563Sthompsa last_offset = offset; 1126197563Sthompsa 1127213809Shselasky /* Align offset */ 1128213809Shselasky offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder, 1129213809Shselasky offset, sc->sc_ncm.tx_modulus); 1130197563Sthompsa 1131213809Shselasky /* Zero pad */ 1132213809Shselasky cdce_ncm_tx_zero(pc, last_offset, offset); 1133213809Shselasky 1134197563Sthompsa /* 1135197563Sthompsa * If there's a BPF listener, bounce a copy 1136197563Sthompsa * of this frame to him: 1137197563Sthompsa */ 1138197563Sthompsa BPF_MTAP(ifp, m); 1139197563Sthompsa 1140197563Sthompsa /* Free mbuf */ 1141197563Sthompsa 1142197563Sthompsa m_freem(m); 1143197563Sthompsa 1144197563Sthompsa /* Pre-increment interface counter */ 1145197563Sthompsa 1146197563Sthompsa ifp->if_opackets++; 1147197563Sthompsa } 1148197563Sthompsa 1149197563Sthompsa if (n == 0) 1150213809Shselasky return (0); 1151197563Sthompsa 1152197563Sthompsa rem = (sizeof(sc->sc_ncm.dpt) + (4 * n) + 4); 1153197563Sthompsa 1154197563Sthompsa USETW(sc->sc_ncm.dpt.wLength, rem); 1155197563Sthompsa 1156197563Sthompsa /* zero the rest of the data pointer entries */ 1157197563Sthompsa for (; n != CDCE_NCM_SUBFRAMES_MAX; n++) { 1158197563Sthompsa USETW(sc->sc_ncm.dp[n].wFrameLength, 0); 1159197563Sthompsa USETW(sc->sc_ncm.dp[n].wFrameIndex, 0); 1160197563Sthompsa } 1161197563Sthompsa 1162213809Shselasky offset = last_offset; 1163213809Shselasky 1164213809Shselasky /* Align offset */ 1165213809Shselasky offset = CDCE_NCM_ALIGN(0, offset, CDCE_NCM_TX_MINLEN); 1166213809Shselasky 1167213809Shselasky /* Optimise, save bandwidth and force short termination */ 1168213809Shselasky if (offset >= sc->sc_ncm.tx_max) 1169213809Shselasky offset = sc->sc_ncm.tx_max; 1170213809Shselasky else 1171213809Shselasky offset ++; 1172213809Shselasky 1173213809Shselasky /* Zero pad */ 1174213809Shselasky cdce_ncm_tx_zero(pc, last_offset, offset); 1175213809Shselasky 1176197563Sthompsa /* set frame length */ 1177213809Shselasky usbd_xfer_set_frame_len(xfer, index, offset); 1178197563Sthompsa 1179197563Sthompsa /* Fill out 16-bit header */ 1180197563Sthompsa sc->sc_ncm.hdr.dwSignature[0] = 'N'; 1181197563Sthompsa sc->sc_ncm.hdr.dwSignature[1] = 'C'; 1182197563Sthompsa sc->sc_ncm.hdr.dwSignature[2] = 'M'; 1183197563Sthompsa sc->sc_ncm.hdr.dwSignature[3] = 'H'; 1184197563Sthompsa USETW(sc->sc_ncm.hdr.wHeaderLength, sizeof(sc->sc_ncm.hdr)); 1185213809Shselasky USETW(sc->sc_ncm.hdr.wBlockLength, offset); 1186197563Sthompsa USETW(sc->sc_ncm.hdr.wSequence, sc->sc_ncm.tx_seq); 1187197563Sthompsa USETW(sc->sc_ncm.hdr.wDptIndex, sizeof(sc->sc_ncm.hdr)); 1188197563Sthompsa 1189197563Sthompsa sc->sc_ncm.tx_seq++; 1190197563Sthompsa 1191197563Sthompsa /* Fill out 16-bit frame table header */ 1192197563Sthompsa sc->sc_ncm.dpt.dwSignature[0] = 'N'; 1193197563Sthompsa sc->sc_ncm.dpt.dwSignature[1] = 'C'; 1194197563Sthompsa sc->sc_ncm.dpt.dwSignature[2] = 'M'; 1195200307Sthompsa sc->sc_ncm.dpt.dwSignature[3] = '0'; 1196197563Sthompsa USETW(sc->sc_ncm.dpt.wNextNdpIndex, 0); /* reserved */ 1197197563Sthompsa 1198197563Sthompsa usbd_copy_in(pc, 0, &(sc->sc_ncm.hdr), sizeof(sc->sc_ncm.hdr)); 1199197563Sthompsa usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr), &(sc->sc_ncm.dpt), 1200197563Sthompsa sizeof(sc->sc_ncm.dpt)); 1201197563Sthompsa usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr) + sizeof(sc->sc_ncm.dpt), 1202197563Sthompsa &(sc->sc_ncm.dp), sizeof(sc->sc_ncm.dp)); 1203213809Shselasky return (retval); 1204197563Sthompsa} 1205197563Sthompsa 1206197563Sthompsastatic void 1207197563Sthompsacdce_ncm_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 1208197563Sthompsa{ 1209197563Sthompsa struct cdce_softc *sc = usbd_xfer_softc(xfer); 1210197563Sthompsa struct ifnet *ifp = uether_getifp(&sc->sc_ue); 1211197563Sthompsa uint16_t x; 1212213809Shselasky uint8_t temp; 1213197563Sthompsa int actlen; 1214197563Sthompsa int aframes; 1215197563Sthompsa 1216197563Sthompsa switch (USB_GET_STATE(xfer)) { 1217197563Sthompsa case USB_ST_TRANSFERRED: 1218197563Sthompsa 1219197563Sthompsa usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 1220197563Sthompsa 1221197563Sthompsa DPRINTFN(10, "transfer complete: " 1222197563Sthompsa "%u bytes in %u frames\n", actlen, aframes); 1223197563Sthompsa 1224197563Sthompsa case USB_ST_SETUP: 1225197563Sthompsa for (x = 0; x != CDCE_NCM_TX_FRAMES_MAX; x++) { 1226213809Shselasky temp = cdce_ncm_fill_tx_frames(xfer, x); 1227213809Shselasky if (temp == 0) 1228197563Sthompsa break; 1229213809Shselasky if (temp == 1) { 1230213809Shselasky x++; 1231213809Shselasky break; 1232213809Shselasky } 1233197563Sthompsa } 1234197563Sthompsa 1235197563Sthompsa if (x != 0) { 1236213809Shselasky#ifdef USB_DEBUG 1237213809Shselasky usbd_xfer_set_interval(xfer, cdce_tx_interval); 1238213809Shselasky#endif 1239197563Sthompsa usbd_xfer_set_frames(xfer, x); 1240197563Sthompsa usbd_transfer_submit(xfer); 1241197563Sthompsa } 1242197563Sthompsa break; 1243197563Sthompsa 1244197563Sthompsa default: /* Error */ 1245197563Sthompsa DPRINTFN(10, "Transfer error: %s\n", 1246197563Sthompsa usbd_errstr(error)); 1247197563Sthompsa 1248197563Sthompsa /* update error counter */ 1249197563Sthompsa ifp->if_oerrors += 1; 1250197563Sthompsa 1251197563Sthompsa if (error != USB_ERR_CANCELLED) { 1252197563Sthompsa /* try to clear stall first */ 1253197563Sthompsa usbd_xfer_set_stall(xfer); 1254197563Sthompsa usbd_xfer_set_frames(xfer, 0); 1255197563Sthompsa usbd_transfer_submit(xfer); 1256197563Sthompsa } 1257197563Sthompsa break; 1258197563Sthompsa } 1259197563Sthompsa} 1260197563Sthompsa 1261197563Sthompsastatic void 1262197563Sthompsacdce_ncm_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 1263197563Sthompsa{ 1264197563Sthompsa struct cdce_softc *sc = usbd_xfer_softc(xfer); 1265197563Sthompsa struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, 0); 1266197563Sthompsa struct ifnet *ifp = uether_getifp(&sc->sc_ue); 1267197563Sthompsa struct mbuf *m; 1268197563Sthompsa int sumdata; 1269197563Sthompsa int sumlen; 1270197563Sthompsa int actlen; 1271197563Sthompsa int aframes; 1272197563Sthompsa int temp; 1273197563Sthompsa int nframes; 1274197563Sthompsa int x; 1275197563Sthompsa int offset; 1276197563Sthompsa 1277197563Sthompsa switch (USB_GET_STATE(xfer)) { 1278197563Sthompsa case USB_ST_TRANSFERRED: 1279197563Sthompsa 1280197563Sthompsa usbd_xfer_status(xfer, &actlen, &sumlen, &aframes, NULL); 1281197563Sthompsa 1282197563Sthompsa DPRINTFN(1, "received %u bytes in %u frames\n", 1283197563Sthompsa actlen, aframes); 1284197563Sthompsa 1285235000Shselasky if (actlen < (int)(sizeof(sc->sc_ncm.hdr) + 1286197563Sthompsa sizeof(sc->sc_ncm.dpt))) { 1287197563Sthompsa DPRINTFN(1, "frame too short\n"); 1288200307Sthompsa goto tr_setup; 1289197563Sthompsa } 1290197563Sthompsa usbd_copy_out(pc, 0, &(sc->sc_ncm.hdr), 1291197563Sthompsa sizeof(sc->sc_ncm.hdr)); 1292197563Sthompsa 1293197563Sthompsa if ((sc->sc_ncm.hdr.dwSignature[0] != 'N') || 1294197563Sthompsa (sc->sc_ncm.hdr.dwSignature[1] != 'C') || 1295197563Sthompsa (sc->sc_ncm.hdr.dwSignature[2] != 'M') || 1296197563Sthompsa (sc->sc_ncm.hdr.dwSignature[3] != 'H')) { 1297200307Sthompsa DPRINTFN(1, "invalid HDR signature: " 1298200307Sthompsa "0x%02x:0x%02x:0x%02x:0x%02x\n", 1299200307Sthompsa sc->sc_ncm.hdr.dwSignature[0], 1300200307Sthompsa sc->sc_ncm.hdr.dwSignature[1], 1301200307Sthompsa sc->sc_ncm.hdr.dwSignature[2], 1302200307Sthompsa sc->sc_ncm.hdr.dwSignature[3]); 1303197563Sthompsa goto tr_stall; 1304197563Sthompsa } 1305197563Sthompsa temp = UGETW(sc->sc_ncm.hdr.wBlockLength); 1306197563Sthompsa if (temp > sumlen) { 1307197563Sthompsa DPRINTFN(1, "unsupported block length %u/%u\n", 1308197563Sthompsa temp, sumlen); 1309197563Sthompsa goto tr_stall; 1310197563Sthompsa } 1311197563Sthompsa temp = UGETW(sc->sc_ncm.hdr.wDptIndex); 1312235000Shselasky if ((int)(temp + sizeof(sc->sc_ncm.dpt)) > actlen) { 1313200307Sthompsa DPRINTFN(1, "invalid DPT index: 0x%04x\n", temp); 1314197563Sthompsa goto tr_stall; 1315197563Sthompsa } 1316197563Sthompsa usbd_copy_out(pc, temp, &(sc->sc_ncm.dpt), 1317197563Sthompsa sizeof(sc->sc_ncm.dpt)); 1318197563Sthompsa 1319197563Sthompsa if ((sc->sc_ncm.dpt.dwSignature[0] != 'N') || 1320197563Sthompsa (sc->sc_ncm.dpt.dwSignature[1] != 'C') || 1321197563Sthompsa (sc->sc_ncm.dpt.dwSignature[2] != 'M') || 1322200307Sthompsa (sc->sc_ncm.dpt.dwSignature[3] != '0')) { 1323200307Sthompsa DPRINTFN(1, "invalid DPT signature" 1324200307Sthompsa "0x%02x:0x%02x:0x%02x:0x%02x\n", 1325200307Sthompsa sc->sc_ncm.dpt.dwSignature[0], 1326200307Sthompsa sc->sc_ncm.dpt.dwSignature[1], 1327200307Sthompsa sc->sc_ncm.dpt.dwSignature[2], 1328200307Sthompsa sc->sc_ncm.dpt.dwSignature[3]); 1329197563Sthompsa goto tr_stall; 1330197563Sthompsa } 1331197563Sthompsa nframes = UGETW(sc->sc_ncm.dpt.wLength) / 4; 1332197563Sthompsa 1333197563Sthompsa /* Subtract size of header and last zero padded entry */ 1334197563Sthompsa if (nframes >= (2 + 1)) 1335197563Sthompsa nframes -= (2 + 1); 1336197563Sthompsa else 1337197563Sthompsa nframes = 0; 1338197563Sthompsa 1339197563Sthompsa DPRINTFN(1, "nframes = %u\n", nframes); 1340197563Sthompsa 1341197563Sthompsa temp += sizeof(sc->sc_ncm.dpt); 1342197563Sthompsa 1343197563Sthompsa if ((temp + (4 * nframes)) > actlen) 1344197563Sthompsa goto tr_stall; 1345197563Sthompsa 1346197563Sthompsa if (nframes > CDCE_NCM_SUBFRAMES_MAX) { 1347197563Sthompsa DPRINTFN(1, "Truncating number of frames from %u to %u\n", 1348197563Sthompsa nframes, CDCE_NCM_SUBFRAMES_MAX); 1349197563Sthompsa nframes = CDCE_NCM_SUBFRAMES_MAX; 1350197563Sthompsa } 1351197563Sthompsa usbd_copy_out(pc, temp, &(sc->sc_ncm.dp), (4 * nframes)); 1352197563Sthompsa 1353197563Sthompsa sumdata = 0; 1354197563Sthompsa 1355197563Sthompsa for (x = 0; x != nframes; x++) { 1356197563Sthompsa 1357197563Sthompsa offset = UGETW(sc->sc_ncm.dp[x].wFrameIndex); 1358197563Sthompsa temp = UGETW(sc->sc_ncm.dp[x].wFrameLength); 1359198153Sthompsa 1360198153Sthompsa if ((offset == 0) || 1361235000Shselasky (temp < (int)sizeof(struct ether_header)) || 1362198153Sthompsa (temp > (MCLBYTES - ETHER_ALIGN))) { 1363198153Sthompsa DPRINTFN(1, "NULL frame detected at %d\n", x); 1364197563Sthompsa m = NULL; 1365198153Sthompsa /* silently ignore this frame */ 1366198153Sthompsa continue; 1367198153Sthompsa } else if ((offset + temp) > actlen) { 1368198153Sthompsa DPRINTFN(1, "invalid frame " 1369198153Sthompsa "detected at %d\n", x); 1370198153Sthompsa m = NULL; 1371198153Sthompsa /* silently ignore this frame */ 1372198153Sthompsa continue; 1373235000Shselasky } else if (temp > (int)(MHLEN - ETHER_ALIGN)) { 1374248078Smarius m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 1375197563Sthompsa } else { 1376248078Smarius m = m_gethdr(M_NOWAIT, MT_DATA); 1377197563Sthompsa } 1378197563Sthompsa 1379197563Sthompsa DPRINTFN(16, "frame %u, offset = %u, length = %u \n", 1380197563Sthompsa x, offset, temp); 1381197563Sthompsa 1382197563Sthompsa /* check if we have a buffer */ 1383197563Sthompsa if (m) { 1384197563Sthompsa m_adj(m, ETHER_ALIGN); 1385197563Sthompsa 1386197563Sthompsa usbd_copy_out(pc, offset, m->m_data, temp); 1387197563Sthompsa 1388197563Sthompsa /* enqueue */ 1389197563Sthompsa uether_rxmbuf(&sc->sc_ue, m, temp); 1390197563Sthompsa 1391197563Sthompsa sumdata += temp; 1392197563Sthompsa } else { 1393197563Sthompsa ifp->if_ierrors++; 1394197563Sthompsa } 1395197563Sthompsa } 1396197563Sthompsa 1397197563Sthompsa DPRINTFN(1, "Efficiency: %u/%u bytes\n", sumdata, actlen); 1398197563Sthompsa 1399197563Sthompsa case USB_ST_SETUP: 1400200307Sthompsatr_setup: 1401197563Sthompsa usbd_xfer_set_frame_len(xfer, 0, sc->sc_ncm.rx_max); 1402197563Sthompsa usbd_xfer_set_frames(xfer, 1); 1403197563Sthompsa usbd_transfer_submit(xfer); 1404197563Sthompsa uether_rxflush(&sc->sc_ue); /* must be last */ 1405197563Sthompsa break; 1406197563Sthompsa 1407197563Sthompsa default: /* Error */ 1408197563Sthompsa DPRINTFN(1, "error = %s\n", 1409197563Sthompsa usbd_errstr(error)); 1410197563Sthompsa 1411197563Sthompsa if (error != USB_ERR_CANCELLED) { 1412197563Sthompsatr_stall: 1413197563Sthompsa /* try to clear stall first */ 1414197563Sthompsa usbd_xfer_set_stall(xfer); 1415197563Sthompsa usbd_xfer_set_frames(xfer, 0); 1416197563Sthompsa usbd_transfer_submit(xfer); 1417197563Sthompsa } 1418197563Sthompsa break; 1419197563Sthompsa } 1420197563Sthompsa} 1421197563Sthompsa#endif 1422