1*0035018cSRaymond Chen /* 2*0035018cSRaymond Chen * CDDL HEADER START 3*0035018cSRaymond Chen * 4*0035018cSRaymond Chen * The contents of this file are subject to the terms of the 5*0035018cSRaymond Chen * Common Development and Distribution License (the "License"). 6*0035018cSRaymond Chen * You may not use this file except in compliance with the License. 7*0035018cSRaymond Chen * 8*0035018cSRaymond Chen * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*0035018cSRaymond Chen * or http://www.opensolaris.org/os/licensing. 10*0035018cSRaymond Chen * See the License for the specific language governing permissions 11*0035018cSRaymond Chen * and limitations under the License. 12*0035018cSRaymond Chen * 13*0035018cSRaymond Chen * When distributing Covered Code, include this CDDL HEADER in each 14*0035018cSRaymond Chen * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*0035018cSRaymond Chen * If applicable, add the following below this CDDL HEADER, with the 16*0035018cSRaymond Chen * fields enclosed by brackets "[]" replaced with your own identifying 17*0035018cSRaymond Chen * information: Portions Copyright [yyyy] [name of copyright owner] 18*0035018cSRaymond Chen * 19*0035018cSRaymond Chen * CDDL HEADER END 20*0035018cSRaymond Chen */ 21*0035018cSRaymond Chen 22*0035018cSRaymond Chen /* 23*0035018cSRaymond Chen * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24*0035018cSRaymond Chen * Use is subject to license terms. 25*0035018cSRaymond Chen */ 26*0035018cSRaymond Chen 27*0035018cSRaymond Chen /* 28*0035018cSRaymond Chen * USB Ethernet Control Model 29*0035018cSRaymond Chen * 30*0035018cSRaymond Chen * USB-IF defines three ethernet network related specifications: EEM, 31*0035018cSRaymond Chen * ECM and NCM. This driver focuses specifically on ECM compatible 32*0035018cSRaymond Chen * devices. This kind of devices generally have one pair of bulk 33*0035018cSRaymond Chen * endpoints for in/out packet data and one interrupt endpoint for 34*0035018cSRaymond Chen * device notification. 35*0035018cSRaymond Chen * 36*0035018cSRaymond Chen * Devices which don't report ECM compatibility through descriptors but 37*0035018cSRaymond Chen * implement the ECM functions may also bind to this driver. This driver 38*0035018cSRaymond Chen * will try to find at least a bulk in endpoint and a bulk out endpoint 39*0035018cSRaymond Chen * in this case. If the non-compatible devices use vendor specific data 40*0035018cSRaymond Chen * format, this driver will not function. 41*0035018cSRaymond Chen * 42*0035018cSRaymond Chen * This driver is a normal USBA client driver. It's also a GLDv3 driver, 43*0035018cSRaymond Chen * which provides the necessary interfaces the GLDv3 framework requires. 44*0035018cSRaymond Chen * 45*0035018cSRaymond Chen */ 46*0035018cSRaymond Chen 47*0035018cSRaymond Chen #include <sys/types.h> 48*0035018cSRaymond Chen #include <sys/strsun.h> 49*0035018cSRaymond Chen #include <sys/ddi.h> 50*0035018cSRaymond Chen #include <sys/sunddi.h> 51*0035018cSRaymond Chen #include <sys/byteorder.h> 52*0035018cSRaymond Chen #include <sys/usb/usba/usbai_version.h> 53*0035018cSRaymond Chen #include <sys/usb/usba.h> 54*0035018cSRaymond Chen #include <sys/usb/usba/usba_types.h> 55*0035018cSRaymond Chen #include <sys/usb/clients/usbcdc/usb_cdc.h> 56*0035018cSRaymond Chen #include <sys/usb/clients/usbecm/usbecm.h> 57*0035018cSRaymond Chen #include <sys/mac_provider.h> 58*0035018cSRaymond Chen #include <sys/strsubr.h> 59*0035018cSRaymond Chen #include <sys/ethernet.h> 60*0035018cSRaymond Chen #include <sys/mac_ether.h> /* MAC_PLUGIN_IDENT_ETHER */ 61*0035018cSRaymond Chen #include <sys/random.h> /* random_get_bytes */ 62*0035018cSRaymond Chen #include <sys/sdt.h> /* sdt */ 63*0035018cSRaymond Chen #include <inet/nd.h> 64*0035018cSRaymond Chen 65*0035018cSRaymond Chen /* MAC callbacks */ 66*0035018cSRaymond Chen static int usbecm_m_stat(void *arg, uint_t stat, uint64_t *val); 67*0035018cSRaymond Chen static int usbecm_m_start(void *arg); 68*0035018cSRaymond Chen static void usbecm_m_stop(void *arg); 69*0035018cSRaymond Chen static int usbecm_m_unicst(void *arg, const uint8_t *macaddr); 70*0035018cSRaymond Chen static int usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m); 71*0035018cSRaymond Chen static int usbecm_m_promisc(void *arg, boolean_t on); 72*0035018cSRaymond Chen static void usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp); 73*0035018cSRaymond Chen static mblk_t *usbecm_m_tx(void *arg, mblk_t *mp); 74*0035018cSRaymond Chen static int usbecm_m_getprop(void *arg, const char *pr_name, 75*0035018cSRaymond Chen mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf); 76*0035018cSRaymond Chen static int usbecm_m_setprop(void *arg, const char *pr_name, 77*0035018cSRaymond Chen mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf); 78*0035018cSRaymond Chen 79*0035018cSRaymond Chen static int usbecm_usb_init(usbecm_state_t *ecmp); 80*0035018cSRaymond Chen static int usbecm_mac_init(usbecm_state_t *ecmp); 81*0035018cSRaymond Chen static int usbecm_mac_fini(usbecm_state_t *ecmp); 82*0035018cSRaymond Chen 83*0035018cSRaymond Chen 84*0035018cSRaymond Chen /* utils */ 85*0035018cSRaymond Chen static void generate_ether_addr(uint8_t *mac_addr); 86*0035018cSRaymond Chen static int usbecm_rx_start(usbecm_state_t *ecmp); 87*0035018cSRaymond Chen 88*0035018cSRaymond Chen static void usbecm_pipe_start_polling(usbecm_state_t *ecmp); 89*0035018cSRaymond Chen static void usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req); 90*0035018cSRaymond Chen static void usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req); 91*0035018cSRaymond Chen static void usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data); 92*0035018cSRaymond Chen 93*0035018cSRaymond Chen static int usbecm_reconnect_event_cb(dev_info_t *dip); 94*0035018cSRaymond Chen static int usbecm_disconnect_event_cb(dev_info_t *dip); 95*0035018cSRaymond Chen 96*0035018cSRaymond Chen static int usbecm_open_pipes(usbecm_state_t *ecmp); 97*0035018cSRaymond Chen static void usbecm_close_pipes(usbecm_state_t *ecmp); 98*0035018cSRaymond Chen 99*0035018cSRaymond Chen static int usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request, 100*0035018cSRaymond Chen uint16_t value, mblk_t **data, int len); 101*0035018cSRaymond Chen static int usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request, 102*0035018cSRaymond Chen uint16_t value, mblk_t **data); 103*0035018cSRaymond Chen static int usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data); 104*0035018cSRaymond Chen static int usbecm_send_zero_data(usbecm_state_t *ecmp); 105*0035018cSRaymond Chen static int usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs, 106*0035018cSRaymond Chen uint32_t *stat_data); 107*0035018cSRaymond Chen 108*0035018cSRaymond Chen static int usbecm_create_pm_components(usbecm_state_t *ecmp); 109*0035018cSRaymond Chen static void usbecm_destroy_pm_components(usbecm_state_t *ecmp); 110*0035018cSRaymond Chen static int usbecm_power(dev_info_t *dip, int comp, int level); 111*0035018cSRaymond Chen static void usbecm_pm_set_busy(usbecm_state_t *ecmp); 112*0035018cSRaymond Chen static void usbecm_pm_set_idle(usbecm_state_t *ecmp); 113*0035018cSRaymond Chen 114*0035018cSRaymond Chen static int usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 115*0035018cSRaymond Chen static int usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 116*0035018cSRaymond Chen 117*0035018cSRaymond Chen static int usbecm_suspend(usbecm_state_t *ecmp); 118*0035018cSRaymond Chen static int usbecm_resume(usbecm_state_t *ecmp); 119*0035018cSRaymond Chen static int usbecm_restore_device_state(usbecm_state_t *ecmp); 120*0035018cSRaymond Chen static void usbecm_cleanup(usbecm_state_t *ecmp); 121*0035018cSRaymond Chen 122*0035018cSRaymond Chen /* Driver identification */ 123*0035018cSRaymond Chen static char usbecm_ident[] = "usbecm 1.0"; 124*0035018cSRaymond Chen 125*0035018cSRaymond Chen /* Global state pointer for managing per-device soft states */ 126*0035018cSRaymond Chen void *usbecm_statep; 127*0035018cSRaymond Chen 128*0035018cSRaymond Chen /* print levels */ 129*0035018cSRaymond Chen static uint_t usbecm_errlevel = USB_LOG_L3; 130*0035018cSRaymond Chen static uint_t usbecm_errmask = 0xffffffff; 131*0035018cSRaymond Chen static uint_t usbecm_instance_debug = (uint_t)-1; 132*0035018cSRaymond Chen 133*0035018cSRaymond Chen /* 134*0035018cSRaymond Chen * to prevent upper layers packet flood from exhausting system 135*0035018cSRaymond Chen * resources(USBA does not set limitation of requests on a pipe), 136*0035018cSRaymond Chen * we set a upper limit for the transfer queue length. 137*0035018cSRaymond Chen */ 138*0035018cSRaymond Chen static int usbecm_tx_max = 32; 139*0035018cSRaymond Chen 140*0035018cSRaymond Chen #define SUN_SP_VENDOR_ID 0x0430 141*0035018cSRaymond Chen #define SUN_SP_PRODUCT_ID 0xa4a2 142*0035018cSRaymond Chen 143*0035018cSRaymond Chen static uint8_t usbecm_broadcast[ETHERADDRL] = { 144*0035018cSRaymond Chen 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 145*0035018cSRaymond Chen }; 146*0035018cSRaymond Chen 147*0035018cSRaymond Chen static usb_event_t usbecm_events = { 148*0035018cSRaymond Chen usbecm_disconnect_event_cb, 149*0035018cSRaymond Chen usbecm_reconnect_event_cb, 150*0035018cSRaymond Chen NULL, NULL 151*0035018cSRaymond Chen }; 152*0035018cSRaymond Chen 153*0035018cSRaymond Chen #define ECM_DS_OP_VALID(op) ((ecmp->ecm_ds_ops) && (ecmp->ecm_ds_ops->op)) 154*0035018cSRaymond Chen 155*0035018cSRaymond Chen /* 156*0035018cSRaymond Chen * MAC Call Back entries 157*0035018cSRaymond Chen */ 158*0035018cSRaymond Chen static mac_callbacks_t usbecm_m_callbacks = { 159*0035018cSRaymond Chen MC_IOCTL | MC_SETPROP | MC_GETPROP, 160*0035018cSRaymond Chen usbecm_m_stat, /* Get the value of a statistic */ 161*0035018cSRaymond Chen usbecm_m_start, /* Start the device */ 162*0035018cSRaymond Chen usbecm_m_stop, /* Stop the device */ 163*0035018cSRaymond Chen usbecm_m_promisc, /* Enable or disable promiscuous mode */ 164*0035018cSRaymond Chen usbecm_m_multicst, /* Enable or disable a multicast addr */ 165*0035018cSRaymond Chen usbecm_m_unicst, /* Set the unicast MAC address */ 166*0035018cSRaymond Chen usbecm_m_tx, /* Transmit a packet */ 167*0035018cSRaymond Chen NULL, 168*0035018cSRaymond Chen usbecm_m_ioctl, /* Process an unknown ioctl */ 169*0035018cSRaymond Chen NULL, /* mc_getcapab */ 170*0035018cSRaymond Chen NULL, /* mc_open */ 171*0035018cSRaymond Chen NULL, /* mc_close */ 172*0035018cSRaymond Chen usbecm_m_setprop, /* mc_setprop */ 173*0035018cSRaymond Chen usbecm_m_getprop, /* mc_getprop */ 174*0035018cSRaymond Chen NULL 175*0035018cSRaymond Chen }; 176*0035018cSRaymond Chen 177*0035018cSRaymond Chen 178*0035018cSRaymond Chen /* 179*0035018cSRaymond Chen * Module Loading Data & Entry Points 180*0035018cSRaymond Chen * Can't use DDI_DEFINE_STREAM_OPS, since it does 181*0035018cSRaymond Chen * not provide devo_power entry. 182*0035018cSRaymond Chen */ 183*0035018cSRaymond Chen static struct cb_ops cb_usbecm = { 184*0035018cSRaymond Chen nulldev, /* cb_open */ 185*0035018cSRaymond Chen nulldev, /* cb_close */ 186*0035018cSRaymond Chen nodev, /* cb_strategy */ 187*0035018cSRaymond Chen nodev, /* cb_print */ 188*0035018cSRaymond Chen nodev, /* cb_dump */ 189*0035018cSRaymond Chen nodev, /* cb_read */ 190*0035018cSRaymond Chen nodev, /* cb_write */ 191*0035018cSRaymond Chen nodev, /* cb_ioctl */ 192*0035018cSRaymond Chen nodev, /* cb_devmap */ 193*0035018cSRaymond Chen nodev, /* cb_mmap */ 194*0035018cSRaymond Chen nodev, /* cb_segmap */ 195*0035018cSRaymond Chen nochpoll, /* cb_chpoll */ 196*0035018cSRaymond Chen ddi_prop_op, /* cb_prop_op */ 197*0035018cSRaymond Chen NULL, /* cb_stream */ 198*0035018cSRaymond Chen D_MP, /* cb_flag */ 199*0035018cSRaymond Chen CB_REV, /* cb_rev */ 200*0035018cSRaymond Chen nodev, /* cb_aread */ 201*0035018cSRaymond Chen nodev, /* cb_awrite */ 202*0035018cSRaymond Chen }; 203*0035018cSRaymond Chen 204*0035018cSRaymond Chen static struct dev_ops usbecm_devops = { 205*0035018cSRaymond Chen DEVO_REV, /* devo_rev */ 206*0035018cSRaymond Chen 0, /* devo_refcnt */ 207*0035018cSRaymond Chen NULL, /* devo_getinfo */ 208*0035018cSRaymond Chen nulldev, /* devo_identify */ 209*0035018cSRaymond Chen nulldev, /* devo_probe */ 210*0035018cSRaymond Chen usbecm_attach, /* devo_attach */ 211*0035018cSRaymond Chen usbecm_detach, /* devo_detach */ 212*0035018cSRaymond Chen nodev, /* devo_reset */ 213*0035018cSRaymond Chen &(cb_usbecm), /* devo_cb_ops */ 214*0035018cSRaymond Chen (struct bus_ops *)NULL, /* devo_bus_ops */ 215*0035018cSRaymond Chen usbecm_power, /* devo_power */ 216*0035018cSRaymond Chen ddi_quiesce_not_needed /* devo_quiesce */ 217*0035018cSRaymond Chen }; 218*0035018cSRaymond Chen 219*0035018cSRaymond Chen static struct modldrv usbecm_modldrv = { 220*0035018cSRaymond Chen &mod_driverops, /* drv_modops */ 221*0035018cSRaymond Chen usbecm_ident, /* drv_linkinfo */ 222*0035018cSRaymond Chen &usbecm_devops /* drv_dev_ops */ 223*0035018cSRaymond Chen }; 224*0035018cSRaymond Chen 225*0035018cSRaymond Chen static struct modlinkage usbecm_ml = { 226*0035018cSRaymond Chen MODREV_1, /* ml_rev */ 227*0035018cSRaymond Chen &usbecm_modldrv, NULL /* ml_linkage */ 228*0035018cSRaymond Chen }; 229*0035018cSRaymond Chen 230*0035018cSRaymond Chen 231*0035018cSRaymond Chen /* 232*0035018cSRaymond Chen * Device operations 233*0035018cSRaymond Chen */ 234*0035018cSRaymond Chen /* 235*0035018cSRaymond Chen * Binding the driver to a device. 236*0035018cSRaymond Chen * 237*0035018cSRaymond Chen * Concurrency: Until usbecm_attach() returns with success, 238*0035018cSRaymond Chen * the only other entry point that can be executed is getinfo(). 239*0035018cSRaymond Chen * Thus no locking here yet. 240*0035018cSRaymond Chen */ 241*0035018cSRaymond Chen static int 242*0035018cSRaymond Chen usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 243*0035018cSRaymond Chen { 244*0035018cSRaymond Chen char strbuf[32]; 245*0035018cSRaymond Chen int instance; 246*0035018cSRaymond Chen int err; 247*0035018cSRaymond Chen usbecm_state_t *ecmp = NULL; 248*0035018cSRaymond Chen 249*0035018cSRaymond Chen switch (cmd) { 250*0035018cSRaymond Chen case DDI_ATTACH: 251*0035018cSRaymond Chen break; 252*0035018cSRaymond Chen 253*0035018cSRaymond Chen case DDI_RESUME: 254*0035018cSRaymond Chen ecmp = (usbecm_state_t *)ddi_get_soft_state(usbecm_statep, 255*0035018cSRaymond Chen ddi_get_instance(dip)); 256*0035018cSRaymond Chen 257*0035018cSRaymond Chen (void) usbecm_resume(ecmp); 258*0035018cSRaymond Chen 259*0035018cSRaymond Chen return (DDI_SUCCESS); 260*0035018cSRaymond Chen 261*0035018cSRaymond Chen default: 262*0035018cSRaymond Chen return (DDI_FAILURE); 263*0035018cSRaymond Chen } 264*0035018cSRaymond Chen 265*0035018cSRaymond Chen instance = ddi_get_instance(dip); 266*0035018cSRaymond Chen 267*0035018cSRaymond Chen if (ddi_soft_state_zalloc(usbecm_statep, instance) == DDI_SUCCESS) { 268*0035018cSRaymond Chen ecmp = ddi_get_soft_state(usbecm_statep, instance); 269*0035018cSRaymond Chen } 270*0035018cSRaymond Chen if (ecmp == NULL) { 271*0035018cSRaymond Chen cmn_err(CE_WARN, "usbecm_attach: fail to get soft state"); 272*0035018cSRaymond Chen 273*0035018cSRaymond Chen return (DDI_FAILURE); 274*0035018cSRaymond Chen } 275*0035018cSRaymond Chen 276*0035018cSRaymond Chen ecmp->ecm_dip = dip; 277*0035018cSRaymond Chen 278*0035018cSRaymond Chen ecmp->ecm_lh = usb_alloc_log_hdl(ecmp->ecm_dip, "usbecm", 279*0035018cSRaymond Chen &usbecm_errlevel, &usbecm_errmask, &usbecm_instance_debug, 0); 280*0035018cSRaymond Chen 281*0035018cSRaymond Chen if (usbecm_usb_init(ecmp) != USB_SUCCESS) { 282*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 283*0035018cSRaymond Chen "usbecm_attach: failed to init usb"); 284*0035018cSRaymond Chen 285*0035018cSRaymond Chen goto fail; 286*0035018cSRaymond Chen } 287*0035018cSRaymond Chen 288*0035018cSRaymond Chen if (ECM_DS_OP_VALID(ecm_ds_init)) { 289*0035018cSRaymond Chen if (ecmp->ecm_ds_ops->ecm_ds_init(ecmp) != USB_SUCCESS) { 290*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 291*0035018cSRaymond Chen "usbecm_attach: failed to init DS"); 292*0035018cSRaymond Chen 293*0035018cSRaymond Chen goto fail; 294*0035018cSRaymond Chen } 295*0035018cSRaymond Chen } 296*0035018cSRaymond Chen 297*0035018cSRaymond Chen if (usbecm_mac_init(ecmp) != DDI_SUCCESS) { 298*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 299*0035018cSRaymond Chen "usbecm_attach: failed to init mac"); 300*0035018cSRaymond Chen 301*0035018cSRaymond Chen goto fail; 302*0035018cSRaymond Chen } 303*0035018cSRaymond Chen ecmp->ecm_init_flags |= USBECM_INIT_MAC; 304*0035018cSRaymond Chen 305*0035018cSRaymond Chen /* 306*0035018cSRaymond Chen * Create minor node of type usb_net. Not necessary to create 307*0035018cSRaymond Chen * DDI_NT_NET since it's created in mac_register(). Otherwise, 308*0035018cSRaymond Chen * system will panic. 309*0035018cSRaymond Chen */ 310*0035018cSRaymond Chen (void) snprintf(strbuf, sizeof (strbuf), "usbecm%d", instance); 311*0035018cSRaymond Chen err = ddi_create_minor_node(dip, strbuf, S_IFCHR, 312*0035018cSRaymond Chen instance + 1, "usb_net", 0); 313*0035018cSRaymond Chen if (err != DDI_SUCCESS) { 314*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 315*0035018cSRaymond Chen "failed to create minor node"); 316*0035018cSRaymond Chen 317*0035018cSRaymond Chen goto fail; 318*0035018cSRaymond Chen } 319*0035018cSRaymond Chen 320*0035018cSRaymond Chen /* always busy. May change to a more precise PM in future */ 321*0035018cSRaymond Chen usbecm_pm_set_busy(ecmp); 322*0035018cSRaymond Chen 323*0035018cSRaymond Chen ddi_report_dev(dip); 324*0035018cSRaymond Chen 325*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, 326*0035018cSRaymond Chen "usbecm_attach: succeed!"); 327*0035018cSRaymond Chen 328*0035018cSRaymond Chen return (DDI_SUCCESS); 329*0035018cSRaymond Chen 330*0035018cSRaymond Chen fail: 331*0035018cSRaymond Chen USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh, 332*0035018cSRaymond Chen "usbecm_attach: Attach fail"); 333*0035018cSRaymond Chen 334*0035018cSRaymond Chen usbecm_cleanup(ecmp); 335*0035018cSRaymond Chen ddi_prop_remove_all(dip); 336*0035018cSRaymond Chen ddi_soft_state_free(usbecm_statep, instance); 337*0035018cSRaymond Chen 338*0035018cSRaymond Chen return (DDI_FAILURE); 339*0035018cSRaymond Chen 340*0035018cSRaymond Chen } 341*0035018cSRaymond Chen 342*0035018cSRaymond Chen 343*0035018cSRaymond Chen /* 344*0035018cSRaymond Chen * Detach the driver from a device. 345*0035018cSRaymond Chen * 346*0035018cSRaymond Chen * Concurrency: Will be called only after a successful attach 347*0035018cSRaymond Chen * (and not concurrently). 348*0035018cSRaymond Chen */ 349*0035018cSRaymond Chen static int 350*0035018cSRaymond Chen usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 351*0035018cSRaymond Chen { 352*0035018cSRaymond Chen usbecm_state_t *ecmp = NULL; 353*0035018cSRaymond Chen int instance; 354*0035018cSRaymond Chen 355*0035018cSRaymond Chen instance = ddi_get_instance(dip); 356*0035018cSRaymond Chen ecmp = ddi_get_soft_state(usbecm_statep, instance); 357*0035018cSRaymond Chen ASSERT(ecmp != NULL); 358*0035018cSRaymond Chen 359*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, 360*0035018cSRaymond Chen "usbecm_detach: entry "); 361*0035018cSRaymond Chen 362*0035018cSRaymond Chen switch (cmd) { 363*0035018cSRaymond Chen case DDI_DETACH: 364*0035018cSRaymond Chen break; 365*0035018cSRaymond Chen 366*0035018cSRaymond Chen case DDI_SUSPEND: 367*0035018cSRaymond Chen 368*0035018cSRaymond Chen return (usbecm_suspend(ecmp)); 369*0035018cSRaymond Chen 370*0035018cSRaymond Chen default: 371*0035018cSRaymond Chen return (DDI_FAILURE); 372*0035018cSRaymond Chen } 373*0035018cSRaymond Chen 374*0035018cSRaymond Chen usbecm_pm_set_idle(ecmp); 375*0035018cSRaymond Chen 376*0035018cSRaymond Chen if (ECM_DS_OP_VALID(ecm_ds_fini)) { 377*0035018cSRaymond Chen if (ecmp->ecm_ds_ops->ecm_ds_fini(ecmp) != USB_SUCCESS) { 378*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 379*0035018cSRaymond Chen "usbecm_detach: deinitialize DS fail!"); 380*0035018cSRaymond Chen 381*0035018cSRaymond Chen return (DDI_FAILURE); 382*0035018cSRaymond Chen } 383*0035018cSRaymond Chen } 384*0035018cSRaymond Chen 385*0035018cSRaymond Chen if (usbecm_mac_fini(ecmp) != 0) { 386*0035018cSRaymond Chen 387*0035018cSRaymond Chen return (DDI_FAILURE); 388*0035018cSRaymond Chen } 389*0035018cSRaymond Chen 390*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, 391*0035018cSRaymond Chen "usbecm_detach: exit"); 392*0035018cSRaymond Chen 393*0035018cSRaymond Chen usbecm_cleanup(ecmp); 394*0035018cSRaymond Chen ddi_soft_state_free(usbecm_statep, instance); 395*0035018cSRaymond Chen 396*0035018cSRaymond Chen return (DDI_SUCCESS); 397*0035018cSRaymond Chen } 398*0035018cSRaymond Chen 399*0035018cSRaymond Chen 400*0035018cSRaymond Chen /* 401*0035018cSRaymond Chen * Mac Call Back functions 402*0035018cSRaymond Chen */ 403*0035018cSRaymond Chen 404*0035018cSRaymond Chen /* 405*0035018cSRaymond Chen * Read device statistic information. 406*0035018cSRaymond Chen */ 407*0035018cSRaymond Chen static int 408*0035018cSRaymond Chen usbecm_m_stat(void *arg, uint_t stat, uint64_t *val) 409*0035018cSRaymond Chen { 410*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 411*0035018cSRaymond Chen uint32_t stats; 412*0035018cSRaymond Chen int rval; 413*0035018cSRaymond Chen uint32_t fs; 414*0035018cSRaymond Chen 415*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 416*0035018cSRaymond Chen "usbecm_m_stat: entry, stat=%d", stat); 417*0035018cSRaymond Chen 418*0035018cSRaymond Chen /* 419*0035018cSRaymond Chen * Some of the stats are MII specific. We try to 420*0035018cSRaymond Chen * resolve all the statistics we understand. If 421*0035018cSRaymond Chen * the usb device can't provide it, return ENOTSUP. 422*0035018cSRaymond Chen */ 423*0035018cSRaymond Chen switch (stat) { 424*0035018cSRaymond Chen case MAC_STAT_IFSPEED: 425*0035018cSRaymond Chen /* return link speed */ 426*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 427*0035018cSRaymond Chen if (ecmp->ecm_stat.es_downspeed) { 428*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_downspeed; 429*0035018cSRaymond Chen } else { 430*0035018cSRaymond Chen *val = 10 * 1000000ull; /* set a default value */ 431*0035018cSRaymond Chen } 432*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 433*0035018cSRaymond Chen 434*0035018cSRaymond Chen return (0); 435*0035018cSRaymond Chen case ETHER_STAT_LINK_DUPLEX: 436*0035018cSRaymond Chen *val = LINK_DUPLEX_FULL; 437*0035018cSRaymond Chen 438*0035018cSRaymond Chen return (0); 439*0035018cSRaymond Chen 440*0035018cSRaymond Chen case ETHER_STAT_SQE_ERRORS: 441*0035018cSRaymond Chen *val = 0; 442*0035018cSRaymond Chen 443*0035018cSRaymond Chen return (0); 444*0035018cSRaymond Chen 445*0035018cSRaymond Chen /* Map MAC/Ether stats to ECM statistics */ 446*0035018cSRaymond Chen case MAC_STAT_NORCVBUF: 447*0035018cSRaymond Chen fs = ECM_RCV_NO_BUFFER; 448*0035018cSRaymond Chen 449*0035018cSRaymond Chen break; 450*0035018cSRaymond Chen case MAC_STAT_NOXMTBUF: 451*0035018cSRaymond Chen fs = ECM_XMIT_ERROR; 452*0035018cSRaymond Chen 453*0035018cSRaymond Chen break; 454*0035018cSRaymond Chen case MAC_STAT_IERRORS: 455*0035018cSRaymond Chen fs = ECM_RCV_ERROR; 456*0035018cSRaymond Chen 457*0035018cSRaymond Chen break; 458*0035018cSRaymond Chen case MAC_STAT_OERRORS: 459*0035018cSRaymond Chen fs = ECM_XMIT_ERROR; 460*0035018cSRaymond Chen 461*0035018cSRaymond Chen break; 462*0035018cSRaymond Chen case MAC_STAT_RBYTES: 463*0035018cSRaymond Chen fs = ECM_DIRECTED_BYTES_RCV; 464*0035018cSRaymond Chen 465*0035018cSRaymond Chen break; 466*0035018cSRaymond Chen case MAC_STAT_IPACKETS: 467*0035018cSRaymond Chen fs = ECM_RCV_OK; /* frames */ 468*0035018cSRaymond Chen 469*0035018cSRaymond Chen break; 470*0035018cSRaymond Chen case MAC_STAT_OBYTES: 471*0035018cSRaymond Chen fs = ECM_DIRECTED_BYTES_XMIT; 472*0035018cSRaymond Chen 473*0035018cSRaymond Chen break; 474*0035018cSRaymond Chen case MAC_STAT_OPACKETS: 475*0035018cSRaymond Chen fs = ECM_XMIT_OK; /* frames */ 476*0035018cSRaymond Chen 477*0035018cSRaymond Chen break; 478*0035018cSRaymond Chen case MAC_STAT_MULTIRCV: 479*0035018cSRaymond Chen fs = ECM_MULTICAST_FRAMES_RCV; 480*0035018cSRaymond Chen 481*0035018cSRaymond Chen break; 482*0035018cSRaymond Chen case MAC_STAT_BRDCSTRCV: 483*0035018cSRaymond Chen fs = ECM_BROADCAST_FRAMES_RCV; 484*0035018cSRaymond Chen 485*0035018cSRaymond Chen break; 486*0035018cSRaymond Chen case MAC_STAT_MULTIXMT: 487*0035018cSRaymond Chen fs = ECM_MULTICAST_FRAMES_XMIT; 488*0035018cSRaymond Chen 489*0035018cSRaymond Chen break; 490*0035018cSRaymond Chen case MAC_STAT_BRDCSTXMT: 491*0035018cSRaymond Chen fs = ECM_BROADCAST_FRAMES_XMIT; 492*0035018cSRaymond Chen 493*0035018cSRaymond Chen break; 494*0035018cSRaymond Chen case MAC_STAT_COLLISIONS: 495*0035018cSRaymond Chen fs = ECM_XMIT_MAX_COLLISIONS; 496*0035018cSRaymond Chen 497*0035018cSRaymond Chen break; 498*0035018cSRaymond Chen case MAC_STAT_OVERFLOWS: 499*0035018cSRaymond Chen fs = ECM_RCV_OVERRUN; 500*0035018cSRaymond Chen 501*0035018cSRaymond Chen break; 502*0035018cSRaymond Chen case MAC_STAT_UNDERFLOWS: 503*0035018cSRaymond Chen fs = ECM_XMIT_UNDERRUN; 504*0035018cSRaymond Chen 505*0035018cSRaymond Chen break; 506*0035018cSRaymond Chen case ETHER_STAT_FCS_ERRORS: 507*0035018cSRaymond Chen fs = ECM_RCV_CRC_ERROR; 508*0035018cSRaymond Chen 509*0035018cSRaymond Chen break; 510*0035018cSRaymond Chen case ETHER_STAT_ALIGN_ERRORS: 511*0035018cSRaymond Chen fs = ECM_RCV_ERROR_ALIGNMENT; 512*0035018cSRaymond Chen 513*0035018cSRaymond Chen break; 514*0035018cSRaymond Chen case ETHER_STAT_DEFER_XMTS: 515*0035018cSRaymond Chen fs = ECM_XMIT_DEFERRED; 516*0035018cSRaymond Chen 517*0035018cSRaymond Chen break; 518*0035018cSRaymond Chen case ETHER_STAT_FIRST_COLLISIONS: 519*0035018cSRaymond Chen fs = ECM_XMIT_ONE_COLLISION; 520*0035018cSRaymond Chen 521*0035018cSRaymond Chen break; 522*0035018cSRaymond Chen case ETHER_STAT_MULTI_COLLISIONS: 523*0035018cSRaymond Chen fs = ECM_XMIT_MORE_COLLISIONS; 524*0035018cSRaymond Chen 525*0035018cSRaymond Chen break; 526*0035018cSRaymond Chen case ETHER_STAT_TX_LATE_COLLISIONS: 527*0035018cSRaymond Chen fs = ECM_XMIT_LATE_COLLISIONS; 528*0035018cSRaymond Chen 529*0035018cSRaymond Chen break; 530*0035018cSRaymond Chen 531*0035018cSRaymond Chen default: 532*0035018cSRaymond Chen return (ENOTSUP); 533*0035018cSRaymond Chen } 534*0035018cSRaymond Chen 535*0035018cSRaymond Chen /* 536*0035018cSRaymond Chen * we need to access device to get required stats, 537*0035018cSRaymond Chen * so check device state first 538*0035018cSRaymond Chen */ 539*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 540*0035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 541*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 542*0035018cSRaymond Chen "usbecm_m_stat: device not ONLINE"); 543*0035018cSRaymond Chen 544*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 545*0035018cSRaymond Chen 546*0035018cSRaymond Chen return (EIO); 547*0035018cSRaymond Chen } 548*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 549*0035018cSRaymond Chen 550*0035018cSRaymond Chen rval = usbecm_get_statistics(ecmp, 551*0035018cSRaymond Chen ECM_STAT_SELECTOR(fs), &stats); 552*0035018cSRaymond Chen if (rval != USB_SUCCESS) { 553*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 554*0035018cSRaymond Chen switch (stat) { 555*0035018cSRaymond Chen case MAC_STAT_IERRORS: 556*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_ierrors; 557*0035018cSRaymond Chen 558*0035018cSRaymond Chen break; 559*0035018cSRaymond Chen case MAC_STAT_OERRORS: 560*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_oerrors; 561*0035018cSRaymond Chen 562*0035018cSRaymond Chen break; 563*0035018cSRaymond Chen case MAC_STAT_RBYTES: 564*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_ibytes; 565*0035018cSRaymond Chen 566*0035018cSRaymond Chen break; 567*0035018cSRaymond Chen case MAC_STAT_IPACKETS: 568*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_ipackets; 569*0035018cSRaymond Chen 570*0035018cSRaymond Chen break; 571*0035018cSRaymond Chen case MAC_STAT_OBYTES: 572*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_obytes; 573*0035018cSRaymond Chen 574*0035018cSRaymond Chen break; 575*0035018cSRaymond Chen case MAC_STAT_OPACKETS: 576*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_opackets; 577*0035018cSRaymond Chen 578*0035018cSRaymond Chen break; 579*0035018cSRaymond Chen case MAC_STAT_MULTIRCV: 580*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_multircv; 581*0035018cSRaymond Chen 582*0035018cSRaymond Chen break; 583*0035018cSRaymond Chen case MAC_STAT_MULTIXMT: 584*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_multixmt; 585*0035018cSRaymond Chen 586*0035018cSRaymond Chen break; 587*0035018cSRaymond Chen case MAC_STAT_BRDCSTRCV: 588*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_brdcstrcv; 589*0035018cSRaymond Chen 590*0035018cSRaymond Chen break; 591*0035018cSRaymond Chen case MAC_STAT_BRDCSTXMT: 592*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_brdcstxmt; 593*0035018cSRaymond Chen 594*0035018cSRaymond Chen break; 595*0035018cSRaymond Chen case ETHER_STAT_MACXMT_ERRORS: 596*0035018cSRaymond Chen *val = ecmp->ecm_stat.es_macxmt_err; 597*0035018cSRaymond Chen break; 598*0035018cSRaymond Chen default: 599*0035018cSRaymond Chen *val = 0; 600*0035018cSRaymond Chen 601*0035018cSRaymond Chen break; 602*0035018cSRaymond Chen } 603*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 604*0035018cSRaymond Chen } else { 605*0035018cSRaymond Chen *val = stats; 606*0035018cSRaymond Chen } 607*0035018cSRaymond Chen 608*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 609*0035018cSRaymond Chen "usbecm_m_stat: end"); 610*0035018cSRaymond Chen 611*0035018cSRaymond Chen return (0); 612*0035018cSRaymond Chen } 613*0035018cSRaymond Chen 614*0035018cSRaymond Chen 615*0035018cSRaymond Chen /* 616*0035018cSRaymond Chen * Start the device: 617*0035018cSRaymond Chen * - Set proper altsettings of the data interface 618*0035018cSRaymond Chen * - Open status and data endpoints 619*0035018cSRaymond Chen * - Start status polling 620*0035018cSRaymond Chen * - Get bulk-in ep ready to receive data from ethernet 621*0035018cSRaymond Chen * 622*0035018cSRaymond Chen * Concurrency: Presumably fully concurrent, must lock. 623*0035018cSRaymond Chen */ 624*0035018cSRaymond Chen static int 625*0035018cSRaymond Chen usbecm_m_start(void *arg) 626*0035018cSRaymond Chen { 627*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 628*0035018cSRaymond Chen int rval; 629*0035018cSRaymond Chen 630*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 631*0035018cSRaymond Chen "usbecm_m_start: entry"); 632*0035018cSRaymond Chen 633*0035018cSRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 634*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 635*0035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 636*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 637*0035018cSRaymond Chen "usbecm_m_start: device not online"); 638*0035018cSRaymond Chen rval = ENODEV; 639*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 640*0035018cSRaymond Chen 641*0035018cSRaymond Chen goto fail; 642*0035018cSRaymond Chen } 643*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 644*0035018cSRaymond Chen 645*0035018cSRaymond Chen if (usbecm_open_pipes(ecmp) != USB_SUCCESS) { 646*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 647*0035018cSRaymond Chen "usbecm_m_start: open pipes fail"); 648*0035018cSRaymond Chen rval = EIO; 649*0035018cSRaymond Chen 650*0035018cSRaymond Chen goto fail; 651*0035018cSRaymond Chen } 652*0035018cSRaymond Chen 653*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 654*0035018cSRaymond Chen if (usbecm_rx_start(ecmp) != USB_SUCCESS) { 655*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 656*0035018cSRaymond Chen "usbecm_m_start: fail to start_rx"); 657*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 658*0035018cSRaymond Chen rval = EIO; 659*0035018cSRaymond Chen 660*0035018cSRaymond Chen goto fail; 661*0035018cSRaymond Chen } 662*0035018cSRaymond Chen ecmp->ecm_mac_state = USBECM_MAC_STARTED; 663*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 664*0035018cSRaymond Chen 665*0035018cSRaymond Chen /* set the device to receive all multicast/broadcast pkts */ 666*0035018cSRaymond Chen rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, 667*0035018cSRaymond Chen CDC_ECM_PKT_TYPE_DIRECTED | CDC_ECM_PKT_TYPE_ALL_MCAST | 668*0035018cSRaymond Chen CDC_ECM_PKT_TYPE_BCAST, NULL); 669*0035018cSRaymond Chen if (rval != USB_SUCCESS) { 670*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh, 671*0035018cSRaymond Chen "usbecm_m_start: set packet filters fail," 672*0035018cSRaymond Chen " rval=%d, continue", rval); 673*0035018cSRaymond Chen } 674*0035018cSRaymond Chen 675*0035018cSRaymond Chen if (ECM_DS_OP_VALID(ecm_ds_start)) { 676*0035018cSRaymond Chen if (ecmp->ecm_ds_ops->ecm_ds_start(ecmp) != USB_SUCCESS) { 677*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 678*0035018cSRaymond Chen "usbecm_m_start: Can't start hardware"); 679*0035018cSRaymond Chen 680*0035018cSRaymond Chen goto fail; 681*0035018cSRaymond Chen } 682*0035018cSRaymond Chen } 683*0035018cSRaymond Chen 684*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 685*0035018cSRaymond Chen 686*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 687*0035018cSRaymond Chen "usbecm_m_start: end"); 688*0035018cSRaymond Chen 689*0035018cSRaymond Chen /* 690*0035018cSRaymond Chen * To mark the link as RUNNING. 691*0035018cSRaymond Chen * 692*0035018cSRaymond Chen * ECM spec doesn't provide a way for host to get the status 693*0035018cSRaymond Chen * of the physical link initiatively. Only the device can 694*0035018cSRaymond Chen * report the link state through interrupt endpoints. 695*0035018cSRaymond Chen */ 696*0035018cSRaymond Chen mac_link_update(ecmp->ecm_mh, LINK_STATE_UP); 697*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 698*0035018cSRaymond Chen ecmp->ecm_stat.es_linkstate = LINK_STATE_UP; 699*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 700*0035018cSRaymond Chen 701*0035018cSRaymond Chen return (DDI_SUCCESS); 702*0035018cSRaymond Chen fail: 703*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 704*0035018cSRaymond Chen 705*0035018cSRaymond Chen return (rval); 706*0035018cSRaymond Chen } 707*0035018cSRaymond Chen 708*0035018cSRaymond Chen /* 709*0035018cSRaymond Chen * Stop the device. 710*0035018cSRaymond Chen */ 711*0035018cSRaymond Chen static void 712*0035018cSRaymond Chen usbecm_m_stop(void *arg) 713*0035018cSRaymond Chen { 714*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 715*0035018cSRaymond Chen 716*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 717*0035018cSRaymond Chen "usbecm_m_stop: entry"); 718*0035018cSRaymond Chen 719*0035018cSRaymond Chen usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 720*0035018cSRaymond Chen if (ECM_DS_OP_VALID(ecm_ds_stop)) { 721*0035018cSRaymond Chen if (ecmp->ecm_ds_ops->ecm_ds_stop(ecmp) != USB_SUCCESS) { 722*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 723*0035018cSRaymond Chen "usbecm_m_stop: fail to stop hardware"); 724*0035018cSRaymond Chen } 725*0035018cSRaymond Chen } 726*0035018cSRaymond Chen 727*0035018cSRaymond Chen usbecm_close_pipes(ecmp); 728*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 729*0035018cSRaymond Chen 730*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 731*0035018cSRaymond Chen ecmp->ecm_mac_state = USBECM_MAC_STOPPED; 732*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 733*0035018cSRaymond Chen 734*0035018cSRaymond Chen mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN); 735*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 736*0035018cSRaymond Chen ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN; 737*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 738*0035018cSRaymond Chen 739*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 740*0035018cSRaymond Chen "usbecm_m_stop: end"); 741*0035018cSRaymond Chen } 742*0035018cSRaymond Chen 743*0035018cSRaymond Chen /* 744*0035018cSRaymond Chen * Change the MAC address of the device. 745*0035018cSRaymond Chen */ 746*0035018cSRaymond Chen /*ARGSUSED*/ 747*0035018cSRaymond Chen static int 748*0035018cSRaymond Chen usbecm_m_unicst(void *arg, const uint8_t *macaddr) 749*0035018cSRaymond Chen { 750*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 751*0035018cSRaymond Chen uint16_t filter; 752*0035018cSRaymond Chen int rval; 753*0035018cSRaymond Chen 754*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 755*0035018cSRaymond Chen "usbecm_m_unicst: entry"); 756*0035018cSRaymond Chen 757*0035018cSRaymond Chen /* 758*0035018cSRaymond Chen * The device doesn't support to set a different MAC addr. 759*0035018cSRaymond Chen * Hence, it's not necessary to stop the device first if 760*0035018cSRaymond Chen * the mac addresses are identical. And we just set unicast 761*0035018cSRaymond Chen * filter only. 762*0035018cSRaymond Chen */ 763*0035018cSRaymond Chen if (bcmp(macaddr, ecmp->ecm_srcaddr, ETHERADDRL) != 0) { 764*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh, 765*0035018cSRaymond Chen "usbecm_m_unicst: not supported to set a" 766*0035018cSRaymond Chen " different MAC addr"); 767*0035018cSRaymond Chen 768*0035018cSRaymond Chen return (DDI_FAILURE); 769*0035018cSRaymond Chen } 770*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 771*0035018cSRaymond Chen filter = ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_DIRECTED; 772*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 773*0035018cSRaymond Chen 774*0035018cSRaymond Chen usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 775*0035018cSRaymond Chen rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, 776*0035018cSRaymond Chen filter, NULL); 777*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 778*0035018cSRaymond Chen 779*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 780*0035018cSRaymond Chen "usbecm_m_unicst: rval = %d", rval); 781*0035018cSRaymond Chen 782*0035018cSRaymond Chen /* some devices may not support this request, we just return success */ 783*0035018cSRaymond Chen return (DDI_SUCCESS); 784*0035018cSRaymond Chen } 785*0035018cSRaymond Chen 786*0035018cSRaymond Chen /* 787*0035018cSRaymond Chen * Enable/disable multicast. 788*0035018cSRaymond Chen */ 789*0035018cSRaymond Chen /*ARGSUSED*/ 790*0035018cSRaymond Chen static int 791*0035018cSRaymond Chen usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m) 792*0035018cSRaymond Chen { 793*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 794*0035018cSRaymond Chen uint16_t filter; 795*0035018cSRaymond Chen int rval = 0; 796*0035018cSRaymond Chen 797*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 798*0035018cSRaymond Chen "usbecm_m_multicst: entry"); 799*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 800*0035018cSRaymond Chen 801*0035018cSRaymond Chen /* 802*0035018cSRaymond Chen * To simplify the implementation, we support switching 803*0035018cSRaymond Chen * all multicast on/off feature only 804*0035018cSRaymond Chen */ 805*0035018cSRaymond Chen if (add == B_TRUE) { 806*0035018cSRaymond Chen ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_ALL_MCAST; 807*0035018cSRaymond Chen } else { 808*0035018cSRaymond Chen ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_ALL_MCAST; 809*0035018cSRaymond Chen } 810*0035018cSRaymond Chen filter = ecmp->ecm_pkt_flt; 811*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 812*0035018cSRaymond Chen 813*0035018cSRaymond Chen usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 814*0035018cSRaymond Chen if (ecmp->ecm_compatibility && 815*0035018cSRaymond Chen (ecmp->ecm_desc.wNumberMCFilters & 0x7F)) { 816*0035018cSRaymond Chen /* Device supports SetEthernetMulticastFilters request */ 817*0035018cSRaymond Chen rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, 818*0035018cSRaymond Chen filter, NULL); 819*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 820*0035018cSRaymond Chen "usbecm_m_multicst: rval = %d", rval); 821*0035018cSRaymond Chen } 822*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 823*0035018cSRaymond Chen 824*0035018cSRaymond Chen /* some devices may not support this request, we just return success */ 825*0035018cSRaymond Chen return (DDI_SUCCESS); 826*0035018cSRaymond Chen } 827*0035018cSRaymond Chen 828*0035018cSRaymond Chen /* 829*0035018cSRaymond Chen * Enable/disable promiscuous mode. 830*0035018cSRaymond Chen */ 831*0035018cSRaymond Chen static int 832*0035018cSRaymond Chen usbecm_m_promisc(void *arg, boolean_t on) 833*0035018cSRaymond Chen { 834*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 835*0035018cSRaymond Chen uint16_t filter; 836*0035018cSRaymond Chen int rval; 837*0035018cSRaymond Chen 838*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 839*0035018cSRaymond Chen "usbecm_m_promisc: entry"); 840*0035018cSRaymond Chen 841*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 842*0035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 843*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 844*0035018cSRaymond Chen "usbecm_m_promisc: device not ONLINE"); 845*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 846*0035018cSRaymond Chen 847*0035018cSRaymond Chen return (DDI_FAILURE); 848*0035018cSRaymond Chen } 849*0035018cSRaymond Chen 850*0035018cSRaymond Chen 851*0035018cSRaymond Chen if (on == B_TRUE) { 852*0035018cSRaymond Chen ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_PROMISC; 853*0035018cSRaymond Chen } else { 854*0035018cSRaymond Chen ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_PROMISC; 855*0035018cSRaymond Chen } 856*0035018cSRaymond Chen filter = ecmp->ecm_pkt_flt; 857*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 858*0035018cSRaymond Chen 859*0035018cSRaymond Chen usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 860*0035018cSRaymond Chen rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, 861*0035018cSRaymond Chen filter, NULL); 862*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 863*0035018cSRaymond Chen 864*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 865*0035018cSRaymond Chen "usbecm_m_promisc: rval=%d", rval); 866*0035018cSRaymond Chen 867*0035018cSRaymond Chen /* 868*0035018cSRaymond Chen * devices may not support this request, we just 869*0035018cSRaymond Chen * return success to let upper layer to do further 870*0035018cSRaymond Chen * operation. 871*0035018cSRaymond Chen */ 872*0035018cSRaymond Chen return (DDI_SUCCESS); 873*0035018cSRaymond Chen } 874*0035018cSRaymond Chen 875*0035018cSRaymond Chen /* 876*0035018cSRaymond Chen * IOCTL request: Does not do anything. Will be enhanced 877*0035018cSRaymond Chen * in future. 878*0035018cSRaymond Chen */ 879*0035018cSRaymond Chen static void 880*0035018cSRaymond Chen usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 881*0035018cSRaymond Chen { 882*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 883*0035018cSRaymond Chen struct iocblk *iocp; 884*0035018cSRaymond Chen int cmd; 885*0035018cSRaymond Chen 886*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 887*0035018cSRaymond Chen "usbecm_m_ioctl: entry"); 888*0035018cSRaymond Chen 889*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 890*0035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 891*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 892*0035018cSRaymond Chen "usbecm_m_ioctl: device not ONLINE"); 893*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 894*0035018cSRaymond Chen 895*0035018cSRaymond Chen miocnak(wq, mp, 0, EIO); 896*0035018cSRaymond Chen 897*0035018cSRaymond Chen return; 898*0035018cSRaymond Chen } 899*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 900*0035018cSRaymond Chen 901*0035018cSRaymond Chen iocp = (void *)mp->b_rptr; 902*0035018cSRaymond Chen iocp->ioc_error = 0; 903*0035018cSRaymond Chen cmd = iocp->ioc_cmd; 904*0035018cSRaymond Chen 905*0035018cSRaymond Chen usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 906*0035018cSRaymond Chen 907*0035018cSRaymond Chen switch (cmd) { 908*0035018cSRaymond Chen default: 909*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 910*0035018cSRaymond Chen "unknown cmd 0x%x", cmd); 911*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 912*0035018cSRaymond Chen miocnak(wq, mp, 0, EINVAL); 913*0035018cSRaymond Chen 914*0035018cSRaymond Chen return; 915*0035018cSRaymond Chen } 916*0035018cSRaymond Chen } 917*0035018cSRaymond Chen 918*0035018cSRaymond Chen /* 919*0035018cSRaymond Chen * callback functions for get/set properties 920*0035018cSRaymond Chen * Does not do anything. Will be enhanced to 921*0035018cSRaymond Chen * support set/get properties in future. 922*0035018cSRaymond Chen */ 923*0035018cSRaymond Chen /*ARGSUSED*/ 924*0035018cSRaymond Chen static int 925*0035018cSRaymond Chen usbecm_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 926*0035018cSRaymond Chen uint_t wldp_length, const void *wldp_buf) 927*0035018cSRaymond Chen { 928*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 929*0035018cSRaymond Chen int err = ENOTSUP; 930*0035018cSRaymond Chen 931*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 932*0035018cSRaymond Chen "usbecm_m_setprop: entry"); 933*0035018cSRaymond Chen 934*0035018cSRaymond Chen return (err); 935*0035018cSRaymond Chen } 936*0035018cSRaymond Chen 937*0035018cSRaymond Chen /*ARGSUSED*/ 938*0035018cSRaymond Chen static int usbecm_m_getprop(void *arg, const char *pr_name, 939*0035018cSRaymond Chen mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf) 940*0035018cSRaymond Chen { 941*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 942*0035018cSRaymond Chen int err = ENOTSUP; 943*0035018cSRaymond Chen 944*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 945*0035018cSRaymond Chen "usbecm_m_getprop: entry"); 946*0035018cSRaymond Chen 947*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 948*0035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 949*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 950*0035018cSRaymond Chen 951*0035018cSRaymond Chen return (EIO); 952*0035018cSRaymond Chen } 953*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 954*0035018cSRaymond Chen 955*0035018cSRaymond Chen return (err); 956*0035018cSRaymond Chen } 957*0035018cSRaymond Chen 958*0035018cSRaymond Chen /* 959*0035018cSRaymond Chen * Transmit a data frame. 960*0035018cSRaymond Chen */ 961*0035018cSRaymond Chen static mblk_t * 962*0035018cSRaymond Chen usbecm_m_tx(void *arg, mblk_t *mp) 963*0035018cSRaymond Chen { 964*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 965*0035018cSRaymond Chen mblk_t *next; 966*0035018cSRaymond Chen int count = 0; 967*0035018cSRaymond Chen 968*0035018cSRaymond Chen ASSERT(mp != NULL); 969*0035018cSRaymond Chen 970*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 971*0035018cSRaymond Chen "usbecm_m_tx: entry"); 972*0035018cSRaymond Chen 973*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 974*0035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 975*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 976*0035018cSRaymond Chen "usbecm_m_tx: device not ONLINE"); 977*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 978*0035018cSRaymond Chen 979*0035018cSRaymond Chen return (mp); 980*0035018cSRaymond Chen } 981*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 982*0035018cSRaymond Chen 983*0035018cSRaymond Chen usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 984*0035018cSRaymond Chen 985*0035018cSRaymond Chen /* 986*0035018cSRaymond Chen * To make use of the device maximum capability, 987*0035018cSRaymond Chen * concatenate msg blocks in a msg to ETHERMAX length. 988*0035018cSRaymond Chen */ 989*0035018cSRaymond Chen while (mp != NULL) { 990*0035018cSRaymond Chen next = mp->b_next; 991*0035018cSRaymond Chen mp->b_next = NULL; 992*0035018cSRaymond Chen 993*0035018cSRaymond Chen if (usbecm_send_data(ecmp, mp) != DDI_SUCCESS) { 994*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh, 995*0035018cSRaymond Chen "usbecm_m_tx: send data fail"); 996*0035018cSRaymond Chen 997*0035018cSRaymond Chen /* failure statistics */ 998*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 999*0035018cSRaymond Chen ecmp->ecm_stat.es_oerrors++; 1000*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1001*0035018cSRaymond Chen 1002*0035018cSRaymond Chen mp->b_next = next; 1003*0035018cSRaymond Chen 1004*0035018cSRaymond Chen break; 1005*0035018cSRaymond Chen } 1006*0035018cSRaymond Chen 1007*0035018cSRaymond Chen /* 1008*0035018cSRaymond Chen * To make it simple, we count all packets, no matter 1009*0035018cSRaymond Chen * the device supports ethernet statistics or not. 1010*0035018cSRaymond Chen */ 1011*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1012*0035018cSRaymond Chen ecmp->ecm_stat.es_opackets++; 1013*0035018cSRaymond Chen ecmp->ecm_stat.es_obytes += MBLKL(mp); 1014*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1015*0035018cSRaymond Chen 1016*0035018cSRaymond Chen freemsg(mp); /* free this msg upon success */ 1017*0035018cSRaymond Chen 1018*0035018cSRaymond Chen mp = next; 1019*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 1020*0035018cSRaymond Chen "usbecm_m_tx: %d msgs processed", ++count); 1021*0035018cSRaymond Chen } 1022*0035018cSRaymond Chen 1023*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 1024*0035018cSRaymond Chen 1025*0035018cSRaymond Chen return (mp); 1026*0035018cSRaymond Chen } 1027*0035018cSRaymond Chen 1028*0035018cSRaymond Chen /* 1029*0035018cSRaymond Chen * usbecm_bulkin_cb: 1030*0035018cSRaymond Chen * Bulk In regular and exeception callback; 1031*0035018cSRaymond Chen * USBA framework will call this callback 1032*0035018cSRaymond Chen * after deal with bulkin request. 1033*0035018cSRaymond Chen */ 1034*0035018cSRaymond Chen /*ARGSUSED*/ 1035*0035018cSRaymond Chen static void 1036*0035018cSRaymond Chen usbecm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) 1037*0035018cSRaymond Chen { 1038*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private; 1039*0035018cSRaymond Chen mblk_t *data, *mp; 1040*0035018cSRaymond Chen int data_len; 1041*0035018cSRaymond Chen int max_pkt_size = ecmp->ecm_bulkin_sz; 1042*0035018cSRaymond Chen 1043*0035018cSRaymond Chen data = req->bulk_data; 1044*0035018cSRaymond Chen data_len = (data) ? MBLKL(data) : 0; 1045*0035018cSRaymond Chen 1046*0035018cSRaymond Chen ASSERT(data->b_cont == NULL); 1047*0035018cSRaymond Chen 1048*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1049*0035018cSRaymond Chen 1050*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh, 1051*0035018cSRaymond Chen "usbecm_bulkin_cb: state=%d, len=%d", ecmp->ecm_bulkin_state, 1052*0035018cSRaymond Chen data_len); 1053*0035018cSRaymond Chen 1054*0035018cSRaymond Chen /* 1055*0035018cSRaymond Chen * may receive a zero length packet according 1056*0035018cSRaymond Chen * to USB short packet semantics 1057*0035018cSRaymond Chen */ 1058*0035018cSRaymond Chen if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) && 1059*0035018cSRaymond Chen (req->bulk_completion_reason == USB_CR_OK)) { 1060*0035018cSRaymond Chen if (data_len) { 1061*0035018cSRaymond Chen if (ecmp->ecm_rcv_queue == NULL) { 1062*0035018cSRaymond Chen ecmp->ecm_rcv_queue = data; 1063*0035018cSRaymond Chen } else { 1064*0035018cSRaymond Chen if ((msgsize(ecmp->ecm_rcv_queue) + data_len) 1065*0035018cSRaymond Chen > ETHERMAX) { 1066*0035018cSRaymond Chen /* 1067*0035018cSRaymond Chen * Exceed the ethernet maximum length, we think 1068*0035018cSRaymond Chen * something is wrong with this frame and hence 1069*0035018cSRaymond Chen * free older data. Accept new data instead. 1070*0035018cSRaymond Chen */ 1071*0035018cSRaymond Chen freemsg(ecmp->ecm_rcv_queue); 1072*0035018cSRaymond Chen ecmp->ecm_rcv_queue = data; 1073*0035018cSRaymond Chen } else { 1074*0035018cSRaymond Chen linkb(ecmp->ecm_rcv_queue, data); 1075*0035018cSRaymond Chen } 1076*0035018cSRaymond Chen } 1077*0035018cSRaymond Chen } else { 1078*0035018cSRaymond Chen /* 1079*0035018cSRaymond Chen * Do not put zero length packet to receive queue. 1080*0035018cSRaymond Chen * Otherwise, msgpullup will dupmsg() a zero length 1081*0035018cSRaymond Chen * mblk, which will cause memleaks. 1082*0035018cSRaymond Chen */ 1083*0035018cSRaymond Chen freemsg(data); 1084*0035018cSRaymond Chen } 1085*0035018cSRaymond Chen 1086*0035018cSRaymond Chen /* 1087*0035018cSRaymond Chen * ECM V1.2, section 3.3.1, a short(including zero length) 1088*0035018cSRaymond Chen * packet signifies end of frame. We can submit this frame 1089*0035018cSRaymond Chen * to upper layer now. 1090*0035018cSRaymond Chen */ 1091*0035018cSRaymond Chen if ((data_len < max_pkt_size) && 1092*0035018cSRaymond Chen (msgsize(ecmp->ecm_rcv_queue) > 0)) { 1093*0035018cSRaymond Chen mp = msgpullup(ecmp->ecm_rcv_queue, -1); 1094*0035018cSRaymond Chen freemsg(ecmp->ecm_rcv_queue); 1095*0035018cSRaymond Chen ecmp->ecm_rcv_queue = NULL; 1096*0035018cSRaymond Chen 1097*0035018cSRaymond Chen ecmp->ecm_stat.es_ipackets++; 1098*0035018cSRaymond Chen ecmp->ecm_stat.es_ibytes += msgsize(mp); 1099*0035018cSRaymond Chen if (mp && (mp->b_rptr[0] & 0x01)) { 1100*0035018cSRaymond Chen if (bcmp(mp->b_rptr, usbecm_broadcast, 1101*0035018cSRaymond Chen ETHERADDRL) != 0) { 1102*0035018cSRaymond Chen ecmp->ecm_stat.es_multircv++; 1103*0035018cSRaymond Chen } else { 1104*0035018cSRaymond Chen ecmp->ecm_stat.es_brdcstrcv++; 1105*0035018cSRaymond Chen } 1106*0035018cSRaymond Chen } 1107*0035018cSRaymond Chen 1108*0035018cSRaymond Chen if (mp) { 1109*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1110*0035018cSRaymond Chen mac_rx(ecmp->ecm_mh, NULL, mp); 1111*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1112*0035018cSRaymond Chen } 1113*0035018cSRaymond Chen } 1114*0035018cSRaymond Chen 1115*0035018cSRaymond Chen /* prevent USBA from freeing data along with the request */ 1116*0035018cSRaymond Chen req->bulk_data = NULL; 1117*0035018cSRaymond Chen } else if (req->bulk_completion_reason != USB_CR_OK) { 1118*0035018cSRaymond Chen ecmp->ecm_stat.es_ierrors++; 1119*0035018cSRaymond Chen } 1120*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1121*0035018cSRaymond Chen 1122*0035018cSRaymond Chen usb_free_bulk_req(req); 1123*0035018cSRaymond Chen 1124*0035018cSRaymond Chen /* receive more */ 1125*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1126*0035018cSRaymond Chen if (((ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) || 1127*0035018cSRaymond Chen (ecmp->ecm_bulkin_state == USBECM_PIPE_IDLE)) && 1128*0035018cSRaymond Chen (ecmp->ecm_dev_state == USB_DEV_ONLINE)) { 1129*0035018cSRaymond Chen if (usbecm_rx_start(ecmp) != USB_SUCCESS) { 1130*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 1131*0035018cSRaymond Chen "usbecm_bulkin_cb: restart rx fail " 1132*0035018cSRaymond Chen "ecmp_state = %d", ecmp->ecm_bulkin_state); 1133*0035018cSRaymond Chen } 1134*0035018cSRaymond Chen } else if (ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) { 1135*0035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE; 1136*0035018cSRaymond Chen } 1137*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1138*0035018cSRaymond Chen } 1139*0035018cSRaymond Chen 1140*0035018cSRaymond Chen /* 1141*0035018cSRaymond Chen * usbsecm_rx_start: 1142*0035018cSRaymond Chen * start data receipt 1143*0035018cSRaymond Chen */ 1144*0035018cSRaymond Chen static int 1145*0035018cSRaymond Chen usbecm_rx_start(usbecm_state_t *ecmp) 1146*0035018cSRaymond Chen { 1147*0035018cSRaymond Chen usb_bulk_req_t *br; 1148*0035018cSRaymond Chen int rval = USB_FAILURE; 1149*0035018cSRaymond Chen int data_len; 1150*0035018cSRaymond Chen 1151*0035018cSRaymond Chen ASSERT(mutex_owned(&ecmp->ecm_mutex)); 1152*0035018cSRaymond Chen 1153*0035018cSRaymond Chen DTRACE_PROBE2(usbecm_rx__start, int, ecmp->ecm_xfer_sz, 1154*0035018cSRaymond Chen int, ecmp->ecm_bulkin_sz); 1155*0035018cSRaymond Chen 1156*0035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_BUSY; 1157*0035018cSRaymond Chen data_len = ecmp->ecm_bulkin_sz; 1158*0035018cSRaymond Chen 1159*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1160*0035018cSRaymond Chen br = usb_alloc_bulk_req(ecmp->ecm_dip, data_len, USB_FLAGS_SLEEP); 1161*0035018cSRaymond Chen if (br == NULL) { 1162*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 1163*0035018cSRaymond Chen "usbsecm_rx_start: allocate bulk request failed"); 1164*0035018cSRaymond Chen 1165*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1166*0035018cSRaymond Chen 1167*0035018cSRaymond Chen return (USB_FAILURE); 1168*0035018cSRaymond Chen } 1169*0035018cSRaymond Chen /* initialize bulk in request. */ 1170*0035018cSRaymond Chen br->bulk_len = data_len; 1171*0035018cSRaymond Chen br->bulk_timeout = 0; 1172*0035018cSRaymond Chen br->bulk_cb = usbecm_bulkin_cb; 1173*0035018cSRaymond Chen br->bulk_exc_cb = usbecm_bulkin_cb; 1174*0035018cSRaymond Chen br->bulk_client_private = (usb_opaque_t)ecmp; 1175*0035018cSRaymond Chen br->bulk_attributes = USB_ATTRS_AUTOCLEARING 1176*0035018cSRaymond Chen | USB_ATTRS_SHORT_XFER_OK; 1177*0035018cSRaymond Chen 1178*0035018cSRaymond Chen rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkin_ph, br, 0); 1179*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1180*0035018cSRaymond Chen if (rval != USB_SUCCESS) { 1181*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 1182*0035018cSRaymond Chen "usbsecm_rx_start: bulk transfer failed %d", rval); 1183*0035018cSRaymond Chen usb_free_bulk_req(br); 1184*0035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE; 1185*0035018cSRaymond Chen } 1186*0035018cSRaymond Chen 1187*0035018cSRaymond Chen return (rval); 1188*0035018cSRaymond Chen } 1189*0035018cSRaymond Chen 1190*0035018cSRaymond Chen /* 1191*0035018cSRaymond Chen * usbecm_bulkout_cb: 1192*0035018cSRaymond Chen * Bulk Out regular and exeception callback; 1193*0035018cSRaymond Chen * USBA framework will call this callback function 1194*0035018cSRaymond Chen * after deal with bulkout request. 1195*0035018cSRaymond Chen */ 1196*0035018cSRaymond Chen /*ARGSUSED*/ 1197*0035018cSRaymond Chen static void 1198*0035018cSRaymond Chen usbecm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) 1199*0035018cSRaymond Chen { 1200*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private; 1201*0035018cSRaymond Chen int data_len; 1202*0035018cSRaymond Chen boolean_t need_update = B_FALSE; 1203*0035018cSRaymond Chen 1204*0035018cSRaymond Chen data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0; 1205*0035018cSRaymond Chen 1206*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh, 1207*0035018cSRaymond Chen "usbecm_bulkout_cb: data_len = %d, cr=%d", data_len, 1208*0035018cSRaymond Chen req->bulk_completion_reason); 1209*0035018cSRaymond Chen 1210*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1211*0035018cSRaymond Chen if ((data_len > 0) && (ecmp->ecm_tx_cnt > 0)) { 1212*0035018cSRaymond Chen if (ecmp->ecm_tx_cnt == usbecm_tx_max) { 1213*0035018cSRaymond Chen need_update = B_TRUE; 1214*0035018cSRaymond Chen } 1215*0035018cSRaymond Chen ecmp->ecm_tx_cnt--; 1216*0035018cSRaymond Chen } 1217*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1218*0035018cSRaymond Chen 1219*0035018cSRaymond Chen if (req->bulk_completion_reason && (data_len > 0)) { 1220*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1221*0035018cSRaymond Chen ecmp->ecm_stat.es_oerrors++; 1222*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1223*0035018cSRaymond Chen 1224*0035018cSRaymond Chen need_update = B_TRUE; 1225*0035018cSRaymond Chen } 1226*0035018cSRaymond Chen 1227*0035018cSRaymond Chen /* 1228*0035018cSRaymond Chen * notify MAC layer to retransfer the failed packet 1229*0035018cSRaymond Chen * Or notity MAC that we have more buffer now. 1230*0035018cSRaymond Chen */ 1231*0035018cSRaymond Chen if (need_update) { 1232*0035018cSRaymond Chen mac_tx_update(ecmp->ecm_mh); 1233*0035018cSRaymond Chen } 1234*0035018cSRaymond Chen 1235*0035018cSRaymond Chen usb_free_bulk_req(req); 1236*0035018cSRaymond Chen } 1237*0035018cSRaymond Chen 1238*0035018cSRaymond Chen static int 1239*0035018cSRaymond Chen usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data) 1240*0035018cSRaymond Chen { 1241*0035018cSRaymond Chen usb_bulk_req_t *br; 1242*0035018cSRaymond Chen int rval = USB_FAILURE; 1243*0035018cSRaymond Chen int data_len = MBLKL(data); 1244*0035018cSRaymond Chen int max_pkt_size; 1245*0035018cSRaymond Chen mblk_t *new_data = NULL; 1246*0035018cSRaymond Chen int new_data_len = 0; 1247*0035018cSRaymond Chen 1248*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 1249*0035018cSRaymond Chen "usbecm_send_data: length = %d, total len=%d", 1250*0035018cSRaymond Chen data_len, (int)msgdsize(data)); 1251*0035018cSRaymond Chen 1252*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1253*0035018cSRaymond Chen if (ecmp->ecm_tx_cnt >= usbecm_tx_max) { 1254*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 1255*0035018cSRaymond Chen "usbecm_send_data: (%d) exceeds TX max queue length", 1256*0035018cSRaymond Chen ecmp->ecm_tx_cnt); 1257*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1258*0035018cSRaymond Chen 1259*0035018cSRaymond Chen return (USB_FAILURE); 1260*0035018cSRaymond Chen } 1261*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1262*0035018cSRaymond Chen 1263*0035018cSRaymond Chen data_len = msgsize(data); 1264*0035018cSRaymond Chen if (data_len > ETHERMAX) { 1265*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1266*0035018cSRaymond Chen ecmp->ecm_stat.es_macxmt_err++; 1267*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1268*0035018cSRaymond Chen 1269*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 1270*0035018cSRaymond Chen "usbecm_send_data: packet too long, %d", data_len); 1271*0035018cSRaymond Chen 1272*0035018cSRaymond Chen return (USB_FAILURE); 1273*0035018cSRaymond Chen } 1274*0035018cSRaymond Chen 1275*0035018cSRaymond Chen if (data_len < ETHERMIN) { 1276*0035018cSRaymond Chen mblk_t *tmp; 1277*0035018cSRaymond Chen 1278*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 1279*0035018cSRaymond Chen "usbecm_send_data: short packet, padding to ETHERMIN"); 1280*0035018cSRaymond Chen 1281*0035018cSRaymond Chen new_data_len = ETHERMIN; 1282*0035018cSRaymond Chen if ((new_data = allocb(new_data_len, 0)) == NULL) { 1283*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 1284*0035018cSRaymond Chen "usbecm_send_data: fail to allocb"); 1285*0035018cSRaymond Chen 1286*0035018cSRaymond Chen return (USB_FAILURE); 1287*0035018cSRaymond Chen } 1288*0035018cSRaymond Chen bzero(new_data->b_wptr, new_data_len); 1289*0035018cSRaymond Chen for (tmp = data; tmp != NULL; tmp = tmp->b_cont) { 1290*0035018cSRaymond Chen bcopy(tmp->b_rptr, new_data->b_wptr, MBLKL(tmp)); 1291*0035018cSRaymond Chen new_data->b_wptr += MBLKL(tmp); 1292*0035018cSRaymond Chen } 1293*0035018cSRaymond Chen 1294*0035018cSRaymond Chen new_data->b_wptr = new_data->b_rptr + new_data_len; 1295*0035018cSRaymond Chen } 1296*0035018cSRaymond Chen 1297*0035018cSRaymond Chen br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP); 1298*0035018cSRaymond Chen if (br == NULL) { 1299*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 1300*0035018cSRaymond Chen "usbecm_send_data: alloc req failed."); 1301*0035018cSRaymond Chen 1302*0035018cSRaymond Chen return (USB_FAILURE); 1303*0035018cSRaymond Chen } 1304*0035018cSRaymond Chen 1305*0035018cSRaymond Chen /* initialize the bulk out request */ 1306*0035018cSRaymond Chen if (new_data) { 1307*0035018cSRaymond Chen br->bulk_data = msgpullup(new_data, -1); /* msg allocated! */ 1308*0035018cSRaymond Chen br->bulk_len = new_data_len; 1309*0035018cSRaymond Chen } else { 1310*0035018cSRaymond Chen br->bulk_data = msgpullup(data, -1); /* msg allocated! */ 1311*0035018cSRaymond Chen br->bulk_len = data_len; 1312*0035018cSRaymond Chen } 1313*0035018cSRaymond Chen 1314*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 1315*0035018cSRaymond Chen "usbecm_send_data: bulk_len = %d", br->bulk_len); 1316*0035018cSRaymond Chen 1317*0035018cSRaymond Chen br->bulk_timeout = USBECM_BULKOUT_TIMEOUT; 1318*0035018cSRaymond Chen br->bulk_cb = usbecm_bulkout_cb; 1319*0035018cSRaymond Chen br->bulk_exc_cb = usbecm_bulkout_cb; 1320*0035018cSRaymond Chen br->bulk_client_private = (usb_opaque_t)ecmp; 1321*0035018cSRaymond Chen br->bulk_attributes = USB_ATTRS_AUTOCLEARING; 1322*0035018cSRaymond Chen 1323*0035018cSRaymond Chen if (br->bulk_data != NULL) { 1324*0035018cSRaymond Chen if (br->bulk_data->b_rptr[0] & 0x01) { 1325*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1326*0035018cSRaymond Chen if (bcmp(br->bulk_data->b_rptr, usbecm_broadcast, 1327*0035018cSRaymond Chen ETHERADDRL) != 0) { 1328*0035018cSRaymond Chen ecmp->ecm_stat.es_multixmt++; 1329*0035018cSRaymond Chen } else { 1330*0035018cSRaymond Chen ecmp->ecm_stat.es_brdcstxmt++; 1331*0035018cSRaymond Chen } 1332*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1333*0035018cSRaymond Chen } 1334*0035018cSRaymond Chen rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0); 1335*0035018cSRaymond Chen } 1336*0035018cSRaymond Chen 1337*0035018cSRaymond Chen if (rval != USB_SUCCESS) { 1338*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 1339*0035018cSRaymond Chen "usbecm_send_data: Send Data failed."); 1340*0035018cSRaymond Chen 1341*0035018cSRaymond Chen /* 1342*0035018cSRaymond Chen * br->bulk_data should be freed because we allocated 1343*0035018cSRaymond Chen * it in this function. 1344*0035018cSRaymond Chen */ 1345*0035018cSRaymond Chen usb_free_bulk_req(br); 1346*0035018cSRaymond Chen 1347*0035018cSRaymond Chen } else { 1348*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1349*0035018cSRaymond Chen ecmp->ecm_tx_cnt++; 1350*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1351*0035018cSRaymond Chen 1352*0035018cSRaymond Chen /* 1353*0035018cSRaymond Chen * ECM V1.2, section 3.3.1, a short(including zero length) 1354*0035018cSRaymond Chen * packet signifies end of frame. We should send a zero length 1355*0035018cSRaymond Chen * packet to device if the total data lenght is multiple of 1356*0035018cSRaymond Chen * bulkout endpoint's max packet size. 1357*0035018cSRaymond Chen */ 1358*0035018cSRaymond Chen max_pkt_size = ecmp->ecm_bulk_out_ep->ep_descr.wMaxPacketSize; 1359*0035018cSRaymond Chen if ((data_len % max_pkt_size) == 0) { 1360*0035018cSRaymond Chen if ((rval = usbecm_send_zero_data(ecmp)) 1361*0035018cSRaymond Chen != USB_SUCCESS) { 1362*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 1363*0035018cSRaymond Chen "usbecm_send_data: fail to send padding"); 1364*0035018cSRaymond Chen } 1365*0035018cSRaymond Chen } 1366*0035018cSRaymond Chen } 1367*0035018cSRaymond Chen 1368*0035018cSRaymond Chen if (new_data) { 1369*0035018cSRaymond Chen freemsg(new_data); 1370*0035018cSRaymond Chen } 1371*0035018cSRaymond Chen 1372*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, 1373*0035018cSRaymond Chen "usbecm_send_data: len(%d) data sent, rval=%d", 1374*0035018cSRaymond Chen new_data_len ? new_data_len : data_len, rval); 1375*0035018cSRaymond Chen 1376*0035018cSRaymond Chen return (rval); 1377*0035018cSRaymond Chen } 1378*0035018cSRaymond Chen 1379*0035018cSRaymond Chen static int 1380*0035018cSRaymond Chen usbecm_send_zero_data(usbecm_state_t *ecmp) 1381*0035018cSRaymond Chen { 1382*0035018cSRaymond Chen usb_bulk_req_t *br; 1383*0035018cSRaymond Chen int rval = USB_FAILURE; 1384*0035018cSRaymond Chen 1385*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 1386*0035018cSRaymond Chen "usbecm_send_zero_data: entry"); 1387*0035018cSRaymond Chen 1388*0035018cSRaymond Chen br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP); 1389*0035018cSRaymond Chen if (br == NULL) { 1390*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 1391*0035018cSRaymond Chen "usbecm_send_data: alloc req failed."); 1392*0035018cSRaymond Chen 1393*0035018cSRaymond Chen return (USB_FAILURE); 1394*0035018cSRaymond Chen } 1395*0035018cSRaymond Chen 1396*0035018cSRaymond Chen /* initialize the bulk out request */ 1397*0035018cSRaymond Chen br->bulk_len = 0; 1398*0035018cSRaymond Chen br->bulk_timeout = USBECM_BULKOUT_TIMEOUT; 1399*0035018cSRaymond Chen br->bulk_cb = usbecm_bulkout_cb; 1400*0035018cSRaymond Chen br->bulk_exc_cb = usbecm_bulkout_cb; 1401*0035018cSRaymond Chen br->bulk_client_private = (usb_opaque_t)ecmp; 1402*0035018cSRaymond Chen br->bulk_attributes = USB_ATTRS_AUTOCLEARING; 1403*0035018cSRaymond Chen 1404*0035018cSRaymond Chen rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0); 1405*0035018cSRaymond Chen 1406*0035018cSRaymond Chen if (rval != USB_SUCCESS) { 1407*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 1408*0035018cSRaymond Chen "usbecm_send_zero_data: Send data failed, rval=%d", 1409*0035018cSRaymond Chen rval); 1410*0035018cSRaymond Chen 1411*0035018cSRaymond Chen /* 1412*0035018cSRaymond Chen * br->bulk_data should be freed because we allocated 1413*0035018cSRaymond Chen * it in this function. 1414*0035018cSRaymond Chen */ 1415*0035018cSRaymond Chen usb_free_bulk_req(br); 1416*0035018cSRaymond Chen 1417*0035018cSRaymond Chen } 1418*0035018cSRaymond Chen 1419*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 1420*0035018cSRaymond Chen "usbecm_send_zero_data: end"); 1421*0035018cSRaymond Chen 1422*0035018cSRaymond Chen return (rval); 1423*0035018cSRaymond Chen } 1424*0035018cSRaymond Chen 1425*0035018cSRaymond Chen /* 1426*0035018cSRaymond Chen * Loadable module configuration entry points 1427*0035018cSRaymond Chen */ 1428*0035018cSRaymond Chen 1429*0035018cSRaymond Chen /* 1430*0035018cSRaymond Chen * _init module entry point. 1431*0035018cSRaymond Chen * 1432*0035018cSRaymond Chen * Called when the module is being loaded into memory. 1433*0035018cSRaymond Chen */ 1434*0035018cSRaymond Chen int 1435*0035018cSRaymond Chen _init(void) 1436*0035018cSRaymond Chen { 1437*0035018cSRaymond Chen int err; 1438*0035018cSRaymond Chen 1439*0035018cSRaymond Chen err = ddi_soft_state_init(&usbecm_statep, sizeof (usbecm_state_t), 1); 1440*0035018cSRaymond Chen 1441*0035018cSRaymond Chen if (err != DDI_SUCCESS) 1442*0035018cSRaymond Chen return (err); 1443*0035018cSRaymond Chen 1444*0035018cSRaymond Chen mac_init_ops(&usbecm_devops, "usbecm"); 1445*0035018cSRaymond Chen err = mod_install(&usbecm_ml); 1446*0035018cSRaymond Chen 1447*0035018cSRaymond Chen if (err != DDI_SUCCESS) { 1448*0035018cSRaymond Chen mac_fini_ops(&usbecm_devops); 1449*0035018cSRaymond Chen ddi_soft_state_fini(&usbecm_statep); 1450*0035018cSRaymond Chen } 1451*0035018cSRaymond Chen 1452*0035018cSRaymond Chen return (err); 1453*0035018cSRaymond Chen } 1454*0035018cSRaymond Chen 1455*0035018cSRaymond Chen /* 1456*0035018cSRaymond Chen * _info module entry point. 1457*0035018cSRaymond Chen * 1458*0035018cSRaymond Chen * Called to obtain information about the module. 1459*0035018cSRaymond Chen */ 1460*0035018cSRaymond Chen int 1461*0035018cSRaymond Chen _info(struct modinfo *modinfop) 1462*0035018cSRaymond Chen { 1463*0035018cSRaymond Chen return (mod_info(&usbecm_ml, modinfop)); 1464*0035018cSRaymond Chen } 1465*0035018cSRaymond Chen 1466*0035018cSRaymond Chen /* 1467*0035018cSRaymond Chen * _fini module entry point. 1468*0035018cSRaymond Chen * 1469*0035018cSRaymond Chen * Called when the module is being unloaded. 1470*0035018cSRaymond Chen */ 1471*0035018cSRaymond Chen int 1472*0035018cSRaymond Chen _fini(void) 1473*0035018cSRaymond Chen { 1474*0035018cSRaymond Chen int err; 1475*0035018cSRaymond Chen 1476*0035018cSRaymond Chen err = mod_remove(&usbecm_ml); 1477*0035018cSRaymond Chen if (err == DDI_SUCCESS) { 1478*0035018cSRaymond Chen mac_fini_ops(&usbecm_devops); 1479*0035018cSRaymond Chen ddi_soft_state_fini(&usbecm_statep); 1480*0035018cSRaymond Chen } 1481*0035018cSRaymond Chen 1482*0035018cSRaymond Chen return (err); 1483*0035018cSRaymond Chen } 1484*0035018cSRaymond Chen 1485*0035018cSRaymond Chen /* 1486*0035018cSRaymond Chen * usbecm_pipe_start_polling: 1487*0035018cSRaymond Chen * start polling on the interrupt pipe 1488*0035018cSRaymond Chen */ 1489*0035018cSRaymond Chen static void 1490*0035018cSRaymond Chen usbecm_pipe_start_polling(usbecm_state_t *ecmp) 1491*0035018cSRaymond Chen { 1492*0035018cSRaymond Chen usb_intr_req_t *intr; 1493*0035018cSRaymond Chen int rval; 1494*0035018cSRaymond Chen 1495*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh, 1496*0035018cSRaymond Chen "usbecm_pipe_start_polling: "); 1497*0035018cSRaymond Chen 1498*0035018cSRaymond Chen if (ecmp->ecm_intr_ph == NULL) { 1499*0035018cSRaymond Chen 1500*0035018cSRaymond Chen return; 1501*0035018cSRaymond Chen } 1502*0035018cSRaymond Chen 1503*0035018cSRaymond Chen intr = usb_alloc_intr_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP); 1504*0035018cSRaymond Chen 1505*0035018cSRaymond Chen /* 1506*0035018cSRaymond Chen * If it is in interrupt context, usb_alloc_intr_req will return NULL if 1507*0035018cSRaymond Chen * called with SLEEP flag. 1508*0035018cSRaymond Chen */ 1509*0035018cSRaymond Chen if (!intr) { 1510*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 1511*0035018cSRaymond Chen "usbecm_pipe_start_polling: alloc req failed."); 1512*0035018cSRaymond Chen 1513*0035018cSRaymond Chen return; 1514*0035018cSRaymond Chen } 1515*0035018cSRaymond Chen 1516*0035018cSRaymond Chen /* initialize the interrupt request. */ 1517*0035018cSRaymond Chen intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK | 1518*0035018cSRaymond Chen USB_ATTRS_AUTOCLEARING; 1519*0035018cSRaymond Chen intr->intr_len = ecmp->ecm_intr_ep->ep_descr.wMaxPacketSize; 1520*0035018cSRaymond Chen intr->intr_client_private = (usb_opaque_t)ecmp; 1521*0035018cSRaymond Chen intr->intr_cb = usbecm_intr_cb; 1522*0035018cSRaymond Chen intr->intr_exc_cb = usbecm_intr_ex_cb; 1523*0035018cSRaymond Chen 1524*0035018cSRaymond Chen rval = usb_pipe_intr_xfer(ecmp->ecm_intr_ph, intr, USB_FLAGS_SLEEP); 1525*0035018cSRaymond Chen 1526*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1527*0035018cSRaymond Chen if (rval == USB_SUCCESS) { 1528*0035018cSRaymond Chen ecmp->ecm_intr_state = USBECM_PIPE_BUSY; 1529*0035018cSRaymond Chen } else { 1530*0035018cSRaymond Chen usb_free_intr_req(intr); 1531*0035018cSRaymond Chen ecmp->ecm_intr_state = USBECM_PIPE_IDLE; 1532*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, 1533*0035018cSRaymond Chen "usbecm_pipe_start_polling: failed (%d)", rval); 1534*0035018cSRaymond Chen } 1535*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1536*0035018cSRaymond Chen 1537*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, 1538*0035018cSRaymond Chen "usbecm_pipe_start_polling: end, rval=%d", rval); 1539*0035018cSRaymond Chen } 1540*0035018cSRaymond Chen 1541*0035018cSRaymond Chen 1542*0035018cSRaymond Chen /* 1543*0035018cSRaymond Chen * usbsecm_intr_cb: 1544*0035018cSRaymond Chen * interrupt pipe normal callback 1545*0035018cSRaymond Chen */ 1546*0035018cSRaymond Chen /*ARGSUSED*/ 1547*0035018cSRaymond Chen static void 1548*0035018cSRaymond Chen usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req) 1549*0035018cSRaymond Chen { 1550*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private; 1551*0035018cSRaymond Chen mblk_t *data = req->intr_data; 1552*0035018cSRaymond Chen int data_len; 1553*0035018cSRaymond Chen 1554*0035018cSRaymond Chen data_len = (data) ? MBLKL(data) : 0; 1555*0035018cSRaymond Chen 1556*0035018cSRaymond Chen DTRACE_PROBE2(usbecm_intr__cb, (usb_intr_req_t *), req, int, data_len); 1557*0035018cSRaymond Chen 1558*0035018cSRaymond Chen /* check data length */ 1559*0035018cSRaymond Chen if (data_len < 8) { 1560*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 1561*0035018cSRaymond Chen "usbsecm_intr_cb: %d packet too short", data_len); 1562*0035018cSRaymond Chen usb_free_intr_req(req); 1563*0035018cSRaymond Chen 1564*0035018cSRaymond Chen return; 1565*0035018cSRaymond Chen } 1566*0035018cSRaymond Chen req->intr_data = NULL; 1567*0035018cSRaymond Chen usb_free_intr_req(req); 1568*0035018cSRaymond Chen 1569*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1570*0035018cSRaymond Chen /* parse interrupt data -- notifications */ 1571*0035018cSRaymond Chen usbecm_parse_intr_data(ecmp, data); 1572*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1573*0035018cSRaymond Chen } 1574*0035018cSRaymond Chen 1575*0035018cSRaymond Chen 1576*0035018cSRaymond Chen /* 1577*0035018cSRaymond Chen * usbsecm_intr_ex_cb: 1578*0035018cSRaymond Chen * interrupt pipe exception callback 1579*0035018cSRaymond Chen */ 1580*0035018cSRaymond Chen /*ARGSUSED*/ 1581*0035018cSRaymond Chen static void 1582*0035018cSRaymond Chen usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req) 1583*0035018cSRaymond Chen { 1584*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private; 1585*0035018cSRaymond Chen usb_cr_t cr = req->intr_completion_reason; 1586*0035018cSRaymond Chen 1587*0035018cSRaymond Chen DTRACE_PROBE2(usbecm_intr_ex__cb, int, ecmp->ecm_dev_state, 1588*0035018cSRaymond Chen (usb_cr_t), cr); 1589*0035018cSRaymond Chen 1590*0035018cSRaymond Chen usb_free_intr_req(req); 1591*0035018cSRaymond Chen 1592*0035018cSRaymond Chen /* 1593*0035018cSRaymond Chen * If completion reason isn't USB_CR_PIPE_CLOSING and 1594*0035018cSRaymond Chen * USB_CR_STOPPED_POLLING, restart polling. 1595*0035018cSRaymond Chen */ 1596*0035018cSRaymond Chen if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) { 1597*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1598*0035018cSRaymond Chen 1599*0035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 1600*0035018cSRaymond Chen 1601*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 1602*0035018cSRaymond Chen "usbsecm_intr_ex_cb: state = %d", 1603*0035018cSRaymond Chen ecmp->ecm_dev_state); 1604*0035018cSRaymond Chen 1605*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1606*0035018cSRaymond Chen 1607*0035018cSRaymond Chen return; 1608*0035018cSRaymond Chen } 1609*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1610*0035018cSRaymond Chen 1611*0035018cSRaymond Chen usbecm_pipe_start_polling(ecmp); 1612*0035018cSRaymond Chen } 1613*0035018cSRaymond Chen } 1614*0035018cSRaymond Chen 1615*0035018cSRaymond Chen 1616*0035018cSRaymond Chen /* 1617*0035018cSRaymond Chen * usbsecm_parse_intr_data: 1618*0035018cSRaymond Chen * Parse data received from interrupt callback 1619*0035018cSRaymond Chen */ 1620*0035018cSRaymond Chen static void 1621*0035018cSRaymond Chen usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data) 1622*0035018cSRaymond Chen { 1623*0035018cSRaymond Chen uint8_t bmRequestType; 1624*0035018cSRaymond Chen uint8_t bNotification; 1625*0035018cSRaymond Chen uint16_t wValue; 1626*0035018cSRaymond Chen uint16_t wLength; 1627*0035018cSRaymond Chen int linkstate; 1628*0035018cSRaymond Chen 1629*0035018cSRaymond Chen bmRequestType = data->b_rptr[0]; 1630*0035018cSRaymond Chen bNotification = data->b_rptr[1]; 1631*0035018cSRaymond Chen /* 1632*0035018cSRaymond Chen * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1, 1633*0035018cSRaymond Chen * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0, 1634*0035018cSRaymond Chen * mLength is 2. So we directly get the value from the byte. 1635*0035018cSRaymond Chen */ 1636*0035018cSRaymond Chen wValue = data->b_rptr[2]; 1637*0035018cSRaymond Chen wLength = data->b_rptr[6]; 1638*0035018cSRaymond Chen 1639*0035018cSRaymond Chen if (ecmp->ecm_compatibility) { 1640*0035018cSRaymond Chen if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) { 1641*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 1642*0035018cSRaymond Chen "usbsecm_parse_intr_data: unknown request " 1643*0035018cSRaymond Chen "type - 0x%x", bmRequestType); 1644*0035018cSRaymond Chen 1645*0035018cSRaymond Chen freemsg(data); 1646*0035018cSRaymond Chen 1647*0035018cSRaymond Chen return; 1648*0035018cSRaymond Chen } 1649*0035018cSRaymond Chen } else { 1650*0035018cSRaymond Chen /* non-compatible device specific parsing */ 1651*0035018cSRaymond Chen if (ECM_DS_OP_VALID(ecm_ds_intr_cb)) { 1652*0035018cSRaymond Chen if (ecmp->ecm_ds_ops->ecm_ds_intr_cb(ecmp, data) 1653*0035018cSRaymond Chen != USB_SUCCESS) { 1654*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 1655*0035018cSRaymond Chen "usbsecm_parse_intr_data: unknown request" 1656*0035018cSRaymond Chen "type - 0x%x", bmRequestType); 1657*0035018cSRaymond Chen } 1658*0035018cSRaymond Chen } 1659*0035018cSRaymond Chen freemsg(data); 1660*0035018cSRaymond Chen 1661*0035018cSRaymond Chen return; 1662*0035018cSRaymond Chen } 1663*0035018cSRaymond Chen 1664*0035018cSRaymond Chen /* 1665*0035018cSRaymond Chen * Check the return value of compatible devices 1666*0035018cSRaymond Chen */ 1667*0035018cSRaymond Chen switch (bNotification) { 1668*0035018cSRaymond Chen case USB_CDC_NOTIFICATION_NETWORK_CONNECTION: 1669*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, 1670*0035018cSRaymond Chen "usbsecm_parse_intr_data: %s network!", 1671*0035018cSRaymond Chen wValue ? "connected to" :"disconnected from"); 1672*0035018cSRaymond Chen 1673*0035018cSRaymond Chen linkstate = wValue ? LINK_STATE_UP:LINK_STATE_DOWN; 1674*0035018cSRaymond Chen if (ecmp->ecm_stat.es_linkstate == linkstate) { 1675*0035018cSRaymond Chen /* no changes to previous state */ 1676*0035018cSRaymond Chen break; 1677*0035018cSRaymond Chen } 1678*0035018cSRaymond Chen 1679*0035018cSRaymond Chen ecmp->ecm_stat.es_linkstate = linkstate; 1680*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1681*0035018cSRaymond Chen mac_link_update(ecmp->ecm_mh, linkstate); 1682*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1683*0035018cSRaymond Chen 1684*0035018cSRaymond Chen break; 1685*0035018cSRaymond Chen case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE: 1686*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, 1687*0035018cSRaymond Chen "usbsecm_parse_intr_data: A response is a available."); 1688*0035018cSRaymond Chen 1689*0035018cSRaymond Chen break; 1690*0035018cSRaymond Chen case USB_CDC_NOTIFICATION_SPEED_CHANGE: 1691*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, 1692*0035018cSRaymond Chen "usbsecm_parse_intr_data: speed change"); 1693*0035018cSRaymond Chen 1694*0035018cSRaymond Chen /* check the parameter's length. */ 1695*0035018cSRaymond Chen if (wLength != 8) { 1696*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, 1697*0035018cSRaymond Chen "usbsecm_parse_intr_data: error data length."); 1698*0035018cSRaymond Chen } else { 1699*0035018cSRaymond Chen uint32_t us_rate, ds_rate; 1700*0035018cSRaymond Chen uint8_t *sp; 1701*0035018cSRaymond Chen 1702*0035018cSRaymond Chen sp = &data->b_rptr[8]; 1703*0035018cSRaymond Chen LE_TO_UINT32(sp, us_rate); 1704*0035018cSRaymond Chen sp = &data->b_rptr[12]; 1705*0035018cSRaymond Chen LE_TO_UINT32(sp, ds_rate); 1706*0035018cSRaymond Chen ecmp->ecm_stat.es_upspeed = us_rate; 1707*0035018cSRaymond Chen ecmp->ecm_stat.es_downspeed = ds_rate; 1708*0035018cSRaymond Chen } 1709*0035018cSRaymond Chen 1710*0035018cSRaymond Chen break; 1711*0035018cSRaymond Chen default: 1712*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, 1713*0035018cSRaymond Chen "usbsecm_parse_intr_data: unknown notification - 0x%x!", 1714*0035018cSRaymond Chen bNotification); 1715*0035018cSRaymond Chen 1716*0035018cSRaymond Chen break; 1717*0035018cSRaymond Chen } 1718*0035018cSRaymond Chen 1719*0035018cSRaymond Chen freemsg(data); 1720*0035018cSRaymond Chen } 1721*0035018cSRaymond Chen 1722*0035018cSRaymond Chen /* 1723*0035018cSRaymond Chen * usbecm_restore_device_state: 1724*0035018cSRaymond Chen * restore device state after CPR resume or reconnect 1725*0035018cSRaymond Chen */ 1726*0035018cSRaymond Chen static int 1727*0035018cSRaymond Chen usbecm_restore_device_state(usbecm_state_t *ecmp) 1728*0035018cSRaymond Chen { 1729*0035018cSRaymond Chen int state; 1730*0035018cSRaymond Chen 1731*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, 1732*0035018cSRaymond Chen "usbecm_restore_device_state: "); 1733*0035018cSRaymond Chen 1734*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1735*0035018cSRaymond Chen state = ecmp->ecm_dev_state; 1736*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1737*0035018cSRaymond Chen 1738*0035018cSRaymond Chen /* Check device status */ 1739*0035018cSRaymond Chen if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) { 1740*0035018cSRaymond Chen 1741*0035018cSRaymond Chen return (state); 1742*0035018cSRaymond Chen } 1743*0035018cSRaymond Chen 1744*0035018cSRaymond Chen /* Check if we are talking to the same device */ 1745*0035018cSRaymond Chen if (usb_check_same_device(ecmp->ecm_dip, ecmp->ecm_lh, USB_LOG_L0, 1746*0035018cSRaymond Chen -1, USB_CHK_ALL, NULL) != USB_SUCCESS) { 1747*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1748*0035018cSRaymond Chen state = ecmp->ecm_dev_state = USB_DEV_DISCONNECTED; 1749*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1750*0035018cSRaymond Chen 1751*0035018cSRaymond Chen return (state); 1752*0035018cSRaymond Chen } 1753*0035018cSRaymond Chen 1754*0035018cSRaymond Chen if (state == USB_DEV_DISCONNECTED) { 1755*0035018cSRaymond Chen USB_DPRINTF_L1(PRINT_MASK_EVENTS, ecmp->ecm_lh, 1756*0035018cSRaymond Chen "usbecm_restore_device_state: Device has been reconnected " 1757*0035018cSRaymond Chen "but data may have been lost"); 1758*0035018cSRaymond Chen } 1759*0035018cSRaymond Chen 1760*0035018cSRaymond Chen /* if MAC was started, restarted it */ 1761*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1762*0035018cSRaymond Chen if (ecmp->ecm_mac_state == USBECM_MAC_STARTED) { 1763*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_EVENTS, ecmp->ecm_lh, 1764*0035018cSRaymond Chen "usbecm_restore_device_state: MAC was started"); 1765*0035018cSRaymond Chen 1766*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1767*0035018cSRaymond Chen /* Do the same operation as usbecm_m_start() does */ 1768*0035018cSRaymond Chen if (usbecm_open_pipes(ecmp) != USB_SUCCESS) { 1769*0035018cSRaymond Chen 1770*0035018cSRaymond Chen return (state); 1771*0035018cSRaymond Chen } 1772*0035018cSRaymond Chen 1773*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1774*0035018cSRaymond Chen if (usbecm_rx_start(ecmp) != USB_SUCCESS) { 1775*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1776*0035018cSRaymond Chen 1777*0035018cSRaymond Chen return (state); 1778*0035018cSRaymond Chen } 1779*0035018cSRaymond Chen } 1780*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1781*0035018cSRaymond Chen 1782*0035018cSRaymond Chen /* 1783*0035018cSRaymond Chen * init device state 1784*0035018cSRaymond Chen */ 1785*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1786*0035018cSRaymond Chen state = ecmp->ecm_dev_state = USB_DEV_ONLINE; 1787*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1788*0035018cSRaymond Chen 1789*0035018cSRaymond Chen return (state); 1790*0035018cSRaymond Chen } 1791*0035018cSRaymond Chen 1792*0035018cSRaymond Chen /* 1793*0035018cSRaymond Chen * usbecm_reconnect_event_cb: 1794*0035018cSRaymond Chen * called upon when the device is hotplugged back 1795*0035018cSRaymond Chen */ 1796*0035018cSRaymond Chen /*ARGSUSED*/ 1797*0035018cSRaymond Chen static int 1798*0035018cSRaymond Chen usbecm_reconnect_event_cb(dev_info_t *dip) 1799*0035018cSRaymond Chen { 1800*0035018cSRaymond Chen usbecm_state_t *ecmp = 1801*0035018cSRaymond Chen (usbecm_state_t *)ddi_get_soft_state(usbecm_statep, 1802*0035018cSRaymond Chen ddi_get_instance(dip)); 1803*0035018cSRaymond Chen 1804*0035018cSRaymond Chen ASSERT(ecmp != NULL); 1805*0035018cSRaymond Chen 1806*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, 1807*0035018cSRaymond Chen "usbecm_reconnect_event_cb: entry"); 1808*0035018cSRaymond Chen 1809*0035018cSRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 1810*0035018cSRaymond Chen 1811*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1812*0035018cSRaymond Chen ASSERT(ecmp->ecm_dev_state == USB_DEV_DISCONNECTED); 1813*0035018cSRaymond Chen 1814*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1815*0035018cSRaymond Chen 1816*0035018cSRaymond Chen if (usbecm_restore_device_state(ecmp) != USB_DEV_ONLINE) { 1817*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 1818*0035018cSRaymond Chen 1819*0035018cSRaymond Chen return (USB_FAILURE); 1820*0035018cSRaymond Chen } 1821*0035018cSRaymond Chen 1822*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 1823*0035018cSRaymond Chen 1824*0035018cSRaymond Chen return (USB_SUCCESS); 1825*0035018cSRaymond Chen } 1826*0035018cSRaymond Chen 1827*0035018cSRaymond Chen 1828*0035018cSRaymond Chen /* 1829*0035018cSRaymond Chen * usbecm_disconnect_event_cb: 1830*0035018cSRaymond Chen * callback for disconnect events 1831*0035018cSRaymond Chen */ 1832*0035018cSRaymond Chen /*ARGSUSED*/ 1833*0035018cSRaymond Chen static int 1834*0035018cSRaymond Chen usbecm_disconnect_event_cb(dev_info_t *dip) 1835*0035018cSRaymond Chen { 1836*0035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)ddi_get_soft_state( 1837*0035018cSRaymond Chen usbecm_statep, ddi_get_instance(dip)); 1838*0035018cSRaymond Chen 1839*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, 1840*0035018cSRaymond Chen "usbecm_disconnect_event_cb: entry"); 1841*0035018cSRaymond Chen 1842*0035018cSRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 1843*0035018cSRaymond Chen 1844*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 1845*0035018cSRaymond Chen ecmp->ecm_dev_state = USB_DEV_DISCONNECTED; 1846*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 1847*0035018cSRaymond Chen 1848*0035018cSRaymond Chen usbecm_close_pipes(ecmp); 1849*0035018cSRaymond Chen 1850*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 1851*0035018cSRaymond Chen 1852*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, 1853*0035018cSRaymond Chen "usbecm_disconnect_event_cb: End"); 1854*0035018cSRaymond Chen 1855*0035018cSRaymond Chen return (USB_SUCCESS); 1856*0035018cSRaymond Chen } 1857*0035018cSRaymond Chen 1858*0035018cSRaymond Chen /* 1859*0035018cSRaymond Chen * power management 1860*0035018cSRaymond Chen * ---------------- 1861*0035018cSRaymond Chen * 1862*0035018cSRaymond Chen * usbecm_create_pm_components: 1863*0035018cSRaymond Chen * create PM components 1864*0035018cSRaymond Chen */ 1865*0035018cSRaymond Chen static int 1866*0035018cSRaymond Chen usbecm_create_pm_components(usbecm_state_t *ecmp) 1867*0035018cSRaymond Chen { 1868*0035018cSRaymond Chen dev_info_t *dip = ecmp->ecm_dip; 1869*0035018cSRaymond Chen usbecm_pm_t *pm; 1870*0035018cSRaymond Chen uint_t pwr_states; 1871*0035018cSRaymond Chen 1872*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 1873*0035018cSRaymond Chen "usbecm_create_pm_components: entry"); 1874*0035018cSRaymond Chen 1875*0035018cSRaymond Chen if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) { 1876*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 1877*0035018cSRaymond Chen "usbecm_create_pm_components: failed"); 1878*0035018cSRaymond Chen 1879*0035018cSRaymond Chen /* don't fail the attach process */ 1880*0035018cSRaymond Chen return (USB_SUCCESS); 1881*0035018cSRaymond Chen } 1882*0035018cSRaymond Chen 1883*0035018cSRaymond Chen pm = ecmp->ecm_pm = 1884*0035018cSRaymond Chen (usbecm_pm_t *)kmem_zalloc(sizeof (usbecm_pm_t), KM_SLEEP); 1885*0035018cSRaymond Chen 1886*0035018cSRaymond Chen pm->pm_pwr_states = (uint8_t)pwr_states; 1887*0035018cSRaymond Chen pm->pm_cur_power = USB_DEV_OS_FULL_PWR; 1888*0035018cSRaymond Chen pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip, 1889*0035018cSRaymond Chen USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS); 1890*0035018cSRaymond Chen 1891*0035018cSRaymond Chen (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 1892*0035018cSRaymond Chen 1893*0035018cSRaymond Chen return (USB_SUCCESS); 1894*0035018cSRaymond Chen } 1895*0035018cSRaymond Chen 1896*0035018cSRaymond Chen /* 1897*0035018cSRaymond Chen * usbecm_cleanup: 1898*0035018cSRaymond Chen * Release resources of current device during detach. 1899*0035018cSRaymond Chen */ 1900*0035018cSRaymond Chen static void 1901*0035018cSRaymond Chen usbecm_cleanup(usbecm_state_t *ecmp) 1902*0035018cSRaymond Chen { 1903*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, 1904*0035018cSRaymond Chen "usbecm_cleanup: "); 1905*0035018cSRaymond Chen 1906*0035018cSRaymond Chen if (ecmp == NULL) { 1907*0035018cSRaymond Chen 1908*0035018cSRaymond Chen return; 1909*0035018cSRaymond Chen } 1910*0035018cSRaymond Chen 1911*0035018cSRaymond Chen usbecm_close_pipes(ecmp); 1912*0035018cSRaymond Chen 1913*0035018cSRaymond Chen /* unregister callback function */ 1914*0035018cSRaymond Chen if (ecmp->ecm_init_flags & USBECM_INIT_EVENTS) { 1915*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, 1916*0035018cSRaymond Chen "usbecm_cleanup: unregister events"); 1917*0035018cSRaymond Chen 1918*0035018cSRaymond Chen usb_unregister_event_cbs(ecmp->ecm_dip, &usbecm_events); 1919*0035018cSRaymond Chen } 1920*0035018cSRaymond Chen 1921*0035018cSRaymond Chen /* destroy power management components */ 1922*0035018cSRaymond Chen if (ecmp->ecm_pm != NULL) { 1923*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, 1924*0035018cSRaymond Chen "usbecm_cleanup: destroy pm"); 1925*0035018cSRaymond Chen usbecm_destroy_pm_components(ecmp); 1926*0035018cSRaymond Chen } 1927*0035018cSRaymond Chen 1928*0035018cSRaymond Chen /* free description of device tree. */ 1929*0035018cSRaymond Chen if (ecmp->ecm_def_ph != NULL) { 1930*0035018cSRaymond Chen mutex_destroy(&ecmp->ecm_mutex); 1931*0035018cSRaymond Chen 1932*0035018cSRaymond Chen usb_free_descr_tree(ecmp->ecm_dip, ecmp->ecm_dev_data); 1933*0035018cSRaymond Chen ecmp->ecm_def_ph = NULL; 1934*0035018cSRaymond Chen } 1935*0035018cSRaymond Chen 1936*0035018cSRaymond Chen if (ecmp->ecm_lh != NULL) { 1937*0035018cSRaymond Chen usb_free_log_hdl(ecmp->ecm_lh); 1938*0035018cSRaymond Chen ecmp->ecm_lh = NULL; 1939*0035018cSRaymond Chen } 1940*0035018cSRaymond Chen 1941*0035018cSRaymond Chen /* detach client device */ 1942*0035018cSRaymond Chen if (ecmp->ecm_dev_data != NULL) { 1943*0035018cSRaymond Chen usb_client_detach(ecmp->ecm_dip, ecmp->ecm_dev_data); 1944*0035018cSRaymond Chen } 1945*0035018cSRaymond Chen 1946*0035018cSRaymond Chen if (ecmp->ecm_init_flags & USBECM_INIT_MAC) { 1947*0035018cSRaymond Chen usbecm_mac_fini(ecmp); 1948*0035018cSRaymond Chen } 1949*0035018cSRaymond Chen 1950*0035018cSRaymond Chen if (ecmp->ecm_init_flags & USBECM_INIT_SER) { 1951*0035018cSRaymond Chen usb_fini_serialization(ecmp->ecm_ser_acc); 1952*0035018cSRaymond Chen } 1953*0035018cSRaymond Chen 1954*0035018cSRaymond Chen ddi_prop_remove_all(ecmp->ecm_dip); 1955*0035018cSRaymond Chen ddi_remove_minor_node(ecmp->ecm_dip, NULL); 1956*0035018cSRaymond Chen } 1957*0035018cSRaymond Chen 1958*0035018cSRaymond Chen /* 1959*0035018cSRaymond Chen * usbecm_destroy_pm_components: 1960*0035018cSRaymond Chen * destroy PM components 1961*0035018cSRaymond Chen */ 1962*0035018cSRaymond Chen static void 1963*0035018cSRaymond Chen usbecm_destroy_pm_components(usbecm_state_t *ecmp) 1964*0035018cSRaymond Chen { 1965*0035018cSRaymond Chen usbecm_pm_t *pm = ecmp->ecm_pm; 1966*0035018cSRaymond Chen dev_info_t *dip = ecmp->ecm_dip; 1967*0035018cSRaymond Chen int rval; 1968*0035018cSRaymond Chen 1969*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 1970*0035018cSRaymond Chen "usbecm_destroy_pm_components: "); 1971*0035018cSRaymond Chen 1972*0035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_DISCONNECTED) { 1973*0035018cSRaymond Chen if (pm->pm_wakeup_enabled) { 1974*0035018cSRaymond Chen rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 1975*0035018cSRaymond Chen if (rval != DDI_SUCCESS) { 1976*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 1977*0035018cSRaymond Chen "usbecm_destroy_pm_components: " 1978*0035018cSRaymond Chen "raising power failed (%d)", rval); 1979*0035018cSRaymond Chen } 1980*0035018cSRaymond Chen 1981*0035018cSRaymond Chen rval = usb_handle_remote_wakeup(dip, 1982*0035018cSRaymond Chen USB_REMOTE_WAKEUP_DISABLE); 1983*0035018cSRaymond Chen if (rval != USB_SUCCESS) { 1984*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 1985*0035018cSRaymond Chen "usbecm_destroy_pm_components: " 1986*0035018cSRaymond Chen "disable remote wakeup failed (%d)", rval); 1987*0035018cSRaymond Chen } 1988*0035018cSRaymond Chen } 1989*0035018cSRaymond Chen 1990*0035018cSRaymond Chen (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF); 1991*0035018cSRaymond Chen } 1992*0035018cSRaymond Chen kmem_free((caddr_t)pm, sizeof (usbecm_pm_t)); 1993*0035018cSRaymond Chen ecmp->ecm_pm = NULL; 1994*0035018cSRaymond Chen } 1995*0035018cSRaymond Chen 1996*0035018cSRaymond Chen /* 1997*0035018cSRaymond Chen * usbecm_pm_set_busy: 1998*0035018cSRaymond Chen * mark device busy and raise power 1999*0035018cSRaymond Chen */ 2000*0035018cSRaymond Chen static void 2001*0035018cSRaymond Chen usbecm_pm_set_busy(usbecm_state_t *ecmp) 2002*0035018cSRaymond Chen { 2003*0035018cSRaymond Chen usbecm_pm_t *pm = ecmp->ecm_pm; 2004*0035018cSRaymond Chen dev_info_t *dip = ecmp->ecm_dip; 2005*0035018cSRaymond Chen int rval; 2006*0035018cSRaymond Chen 2007*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 2008*0035018cSRaymond Chen "usbecm_pm_set_busy: pm = 0x%p", (void *)pm); 2009*0035018cSRaymond Chen 2010*0035018cSRaymond Chen if (pm == NULL) { 2011*0035018cSRaymond Chen 2012*0035018cSRaymond Chen return; 2013*0035018cSRaymond Chen } 2014*0035018cSRaymond Chen 2015*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 2016*0035018cSRaymond Chen /* if already marked busy, just increment the counter */ 2017*0035018cSRaymond Chen if (pm->pm_busy_cnt++ > 0) { 2018*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2019*0035018cSRaymond Chen 2020*0035018cSRaymond Chen return; 2021*0035018cSRaymond Chen } 2022*0035018cSRaymond Chen 2023*0035018cSRaymond Chen (void) pm_busy_component(dip, 0); 2024*0035018cSRaymond Chen 2025*0035018cSRaymond Chen if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) { 2026*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2027*0035018cSRaymond Chen 2028*0035018cSRaymond Chen return; 2029*0035018cSRaymond Chen } 2030*0035018cSRaymond Chen 2031*0035018cSRaymond Chen /* need to raise power */ 2032*0035018cSRaymond Chen pm->pm_raise_power = B_TRUE; 2033*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2034*0035018cSRaymond Chen 2035*0035018cSRaymond Chen rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 2036*0035018cSRaymond Chen if (rval != DDI_SUCCESS) { 2037*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 2038*0035018cSRaymond Chen "usbecm_pm_set_busy: raising power failed"); 2039*0035018cSRaymond Chen } 2040*0035018cSRaymond Chen 2041*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 2042*0035018cSRaymond Chen pm->pm_raise_power = B_FALSE; 2043*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2044*0035018cSRaymond Chen } 2045*0035018cSRaymond Chen 2046*0035018cSRaymond Chen 2047*0035018cSRaymond Chen /* 2048*0035018cSRaymond Chen * usbecm_pm_set_idle: 2049*0035018cSRaymond Chen * mark device idle 2050*0035018cSRaymond Chen */ 2051*0035018cSRaymond Chen static void 2052*0035018cSRaymond Chen usbecm_pm_set_idle(usbecm_state_t *ecmp) 2053*0035018cSRaymond Chen { 2054*0035018cSRaymond Chen usbecm_pm_t *pm = ecmp->ecm_pm; 2055*0035018cSRaymond Chen dev_info_t *dip = ecmp->ecm_dip; 2056*0035018cSRaymond Chen 2057*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 2058*0035018cSRaymond Chen "usbecm_pm_set_idle: "); 2059*0035018cSRaymond Chen 2060*0035018cSRaymond Chen if (pm == NULL) { 2061*0035018cSRaymond Chen 2062*0035018cSRaymond Chen return; 2063*0035018cSRaymond Chen } 2064*0035018cSRaymond Chen 2065*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 2066*0035018cSRaymond Chen if (--pm->pm_busy_cnt > 0) { 2067*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2068*0035018cSRaymond Chen 2069*0035018cSRaymond Chen return; 2070*0035018cSRaymond Chen } 2071*0035018cSRaymond Chen 2072*0035018cSRaymond Chen if (pm) { 2073*0035018cSRaymond Chen (void) pm_idle_component(dip, 0); 2074*0035018cSRaymond Chen } 2075*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2076*0035018cSRaymond Chen } 2077*0035018cSRaymond Chen 2078*0035018cSRaymond Chen 2079*0035018cSRaymond Chen /* 2080*0035018cSRaymond Chen * usbecm_pwrlvl0: 2081*0035018cSRaymond Chen * Functions to handle power transition for OS levels 0 -> 3 2082*0035018cSRaymond Chen * The same level as OS state, different from USB state 2083*0035018cSRaymond Chen */ 2084*0035018cSRaymond Chen static int 2085*0035018cSRaymond Chen usbecm_pwrlvl0(usbecm_state_t *ecmp) 2086*0035018cSRaymond Chen { 2087*0035018cSRaymond Chen int rval; 2088*0035018cSRaymond Chen 2089*0035018cSRaymond Chen ASSERT(mutex_owned(&ecmp->ecm_mutex)); 2090*0035018cSRaymond Chen 2091*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 2092*0035018cSRaymond Chen "usbecm_pwrlvl0: "); 2093*0035018cSRaymond Chen 2094*0035018cSRaymond Chen switch (ecmp->ecm_dev_state) { 2095*0035018cSRaymond Chen case USB_DEV_ONLINE: 2096*0035018cSRaymond Chen /* issue USB D3 command to the device */ 2097*0035018cSRaymond Chen rval = usb_set_device_pwrlvl3(ecmp->ecm_dip); 2098*0035018cSRaymond Chen ASSERT(rval == USB_SUCCESS); 2099*0035018cSRaymond Chen if ((ecmp->ecm_intr_ph != NULL) && 2100*0035018cSRaymond Chen (ecmp->ecm_intr_state == USBECM_PIPE_BUSY)) { 2101*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2102*0035018cSRaymond Chen usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph, 2103*0035018cSRaymond Chen USB_FLAGS_SLEEP); 2104*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 2105*0035018cSRaymond Chen 2106*0035018cSRaymond Chen ecmp->ecm_intr_state = USBECM_PIPE_IDLE; 2107*0035018cSRaymond Chen } 2108*0035018cSRaymond Chen ecmp->ecm_dev_state = USB_DEV_PWRED_DOWN; 2109*0035018cSRaymond Chen ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF; 2110*0035018cSRaymond Chen 2111*0035018cSRaymond Chen /* FALLTHRU */ 2112*0035018cSRaymond Chen case USB_DEV_DISCONNECTED: 2113*0035018cSRaymond Chen case USB_DEV_SUSPENDED: 2114*0035018cSRaymond Chen /* allow a disconnect/cpr'ed device to go to lower power */ 2115*0035018cSRaymond Chen 2116*0035018cSRaymond Chen return (USB_SUCCESS); 2117*0035018cSRaymond Chen case USB_DEV_PWRED_DOWN: 2118*0035018cSRaymond Chen default: 2119*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 2120*0035018cSRaymond Chen "usbecm_pwrlvl0: illegal device state"); 2121*0035018cSRaymond Chen 2122*0035018cSRaymond Chen return (USB_FAILURE); 2123*0035018cSRaymond Chen } 2124*0035018cSRaymond Chen } 2125*0035018cSRaymond Chen 2126*0035018cSRaymond Chen 2127*0035018cSRaymond Chen /* 2128*0035018cSRaymond Chen * usbecm_pwrlvl1: 2129*0035018cSRaymond Chen * Functions to handle power transition for OS levels 1 -> 2 2130*0035018cSRaymond Chen */ 2131*0035018cSRaymond Chen static int 2132*0035018cSRaymond Chen usbecm_pwrlvl1(usbecm_state_t *ecmp) 2133*0035018cSRaymond Chen { 2134*0035018cSRaymond Chen /* issue USB D2 command to the device */ 2135*0035018cSRaymond Chen (void) usb_set_device_pwrlvl2(ecmp->ecm_dip); 2136*0035018cSRaymond Chen 2137*0035018cSRaymond Chen return (USB_FAILURE); 2138*0035018cSRaymond Chen } 2139*0035018cSRaymond Chen 2140*0035018cSRaymond Chen 2141*0035018cSRaymond Chen /* 2142*0035018cSRaymond Chen * usbecm_pwrlvl2: 2143*0035018cSRaymond Chen * Functions to handle power transition for OS levels 2 -> 1 2144*0035018cSRaymond Chen */ 2145*0035018cSRaymond Chen static int 2146*0035018cSRaymond Chen usbecm_pwrlvl2(usbecm_state_t *ecmp) 2147*0035018cSRaymond Chen { 2148*0035018cSRaymond Chen /* issue USB D1 command to the device */ 2149*0035018cSRaymond Chen (void) usb_set_device_pwrlvl1(ecmp->ecm_dip); 2150*0035018cSRaymond Chen 2151*0035018cSRaymond Chen return (USB_FAILURE); 2152*0035018cSRaymond Chen } 2153*0035018cSRaymond Chen 2154*0035018cSRaymond Chen 2155*0035018cSRaymond Chen /* 2156*0035018cSRaymond Chen * usbecm_pwrlvl3: 2157*0035018cSRaymond Chen * Functions to handle power transition for OS levels 3 -> 0 2158*0035018cSRaymond Chen * The same level as OS state, different from USB state 2159*0035018cSRaymond Chen */ 2160*0035018cSRaymond Chen static int 2161*0035018cSRaymond Chen usbecm_pwrlvl3(usbecm_state_t *ecmp) 2162*0035018cSRaymond Chen { 2163*0035018cSRaymond Chen int rval; 2164*0035018cSRaymond Chen 2165*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 2166*0035018cSRaymond Chen "usbecm_pwrlvl3: "); 2167*0035018cSRaymond Chen 2168*0035018cSRaymond Chen ASSERT(mutex_owned(&ecmp->ecm_mutex)); 2169*0035018cSRaymond Chen 2170*0035018cSRaymond Chen switch (ecmp->ecm_dev_state) { 2171*0035018cSRaymond Chen case USB_DEV_PWRED_DOWN: 2172*0035018cSRaymond Chen /* Issue USB D0 command to the device here */ 2173*0035018cSRaymond Chen rval = usb_set_device_pwrlvl0(ecmp->ecm_dip); 2174*0035018cSRaymond Chen ASSERT(rval == USB_SUCCESS); 2175*0035018cSRaymond Chen 2176*0035018cSRaymond Chen if (ecmp->ecm_intr_ph != NULL && 2177*0035018cSRaymond Chen ecmp->ecm_intr_state == USBECM_PIPE_IDLE) { 2178*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2179*0035018cSRaymond Chen usbecm_pipe_start_polling(ecmp); 2180*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 2181*0035018cSRaymond Chen } 2182*0035018cSRaymond Chen 2183*0035018cSRaymond Chen ecmp->ecm_dev_state = USB_DEV_ONLINE; 2184*0035018cSRaymond Chen ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR; 2185*0035018cSRaymond Chen 2186*0035018cSRaymond Chen /* FALLTHRU */ 2187*0035018cSRaymond Chen case USB_DEV_ONLINE: 2188*0035018cSRaymond Chen /* we are already in full power */ 2189*0035018cSRaymond Chen 2190*0035018cSRaymond Chen /* FALLTHRU */ 2191*0035018cSRaymond Chen case USB_DEV_DISCONNECTED: 2192*0035018cSRaymond Chen case USB_DEV_SUSPENDED: 2193*0035018cSRaymond Chen 2194*0035018cSRaymond Chen return (USB_SUCCESS); 2195*0035018cSRaymond Chen default: 2196*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 2197*0035018cSRaymond Chen "usbecm_pwrlvl3: illegal device state"); 2198*0035018cSRaymond Chen 2199*0035018cSRaymond Chen return (USB_FAILURE); 2200*0035018cSRaymond Chen } 2201*0035018cSRaymond Chen } 2202*0035018cSRaymond Chen 2203*0035018cSRaymond Chen /*ARGSUSED*/ 2204*0035018cSRaymond Chen static int 2205*0035018cSRaymond Chen usbecm_power(dev_info_t *dip, int comp, int level) 2206*0035018cSRaymond Chen { 2207*0035018cSRaymond Chen usbecm_state_t *ecmp; 2208*0035018cSRaymond Chen usbecm_pm_t *pm; 2209*0035018cSRaymond Chen int rval = USB_SUCCESS; 2210*0035018cSRaymond Chen 2211*0035018cSRaymond Chen ecmp = ddi_get_soft_state(usbecm_statep, ddi_get_instance(dip)); 2212*0035018cSRaymond Chen pm = ecmp->ecm_pm; 2213*0035018cSRaymond Chen 2214*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 2215*0035018cSRaymond Chen "usbecm_power: entry"); 2216*0035018cSRaymond Chen 2217*0035018cSRaymond Chen /* check if pm is NULL */ 2218*0035018cSRaymond Chen if (pm == NULL) { 2219*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 2220*0035018cSRaymond Chen "usbecm_power: pm is NULL."); 2221*0035018cSRaymond Chen 2222*0035018cSRaymond Chen return (USB_FAILURE); 2223*0035018cSRaymond Chen } 2224*0035018cSRaymond Chen 2225*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 2226*0035018cSRaymond Chen /* 2227*0035018cSRaymond Chen * check if we are transitioning to a legal power level 2228*0035018cSRaymond Chen */ 2229*0035018cSRaymond Chen if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) { 2230*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 2231*0035018cSRaymond Chen "usbecm_power: " 2232*0035018cSRaymond Chen "illegal power level %d, pwr_states=%x", 2233*0035018cSRaymond Chen level, pm->pm_pwr_states); 2234*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2235*0035018cSRaymond Chen 2236*0035018cSRaymond Chen return (USB_FAILURE); 2237*0035018cSRaymond Chen } 2238*0035018cSRaymond Chen 2239*0035018cSRaymond Chen /* 2240*0035018cSRaymond Chen * if we are about to raise power and asked to lower power, fail 2241*0035018cSRaymond Chen */ 2242*0035018cSRaymond Chen if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) { 2243*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 2244*0035018cSRaymond Chen "usbecm_power: wrong condition."); 2245*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2246*0035018cSRaymond Chen 2247*0035018cSRaymond Chen return (USB_FAILURE); 2248*0035018cSRaymond Chen } 2249*0035018cSRaymond Chen 2250*0035018cSRaymond Chen /* 2251*0035018cSRaymond Chen * Set the power status of device by request level. 2252*0035018cSRaymond Chen */ 2253*0035018cSRaymond Chen switch (level) { 2254*0035018cSRaymond Chen case USB_DEV_OS_PWR_OFF: 2255*0035018cSRaymond Chen rval = usbecm_pwrlvl0(ecmp); 2256*0035018cSRaymond Chen 2257*0035018cSRaymond Chen break; 2258*0035018cSRaymond Chen case USB_DEV_OS_PWR_1: 2259*0035018cSRaymond Chen rval = usbecm_pwrlvl1(ecmp); 2260*0035018cSRaymond Chen 2261*0035018cSRaymond Chen break; 2262*0035018cSRaymond Chen case USB_DEV_OS_PWR_2: 2263*0035018cSRaymond Chen rval = usbecm_pwrlvl2(ecmp); 2264*0035018cSRaymond Chen 2265*0035018cSRaymond Chen break; 2266*0035018cSRaymond Chen case USB_DEV_OS_FULL_PWR: 2267*0035018cSRaymond Chen rval = usbecm_pwrlvl3(ecmp); 2268*0035018cSRaymond Chen 2269*0035018cSRaymond Chen break; 2270*0035018cSRaymond Chen } 2271*0035018cSRaymond Chen 2272*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2273*0035018cSRaymond Chen 2274*0035018cSRaymond Chen return (rval); 2275*0035018cSRaymond Chen } 2276*0035018cSRaymond Chen 2277*0035018cSRaymond Chen /* 2278*0035018cSRaymond Chen * Register with the MAC layer. 2279*0035018cSRaymond Chen */ 2280*0035018cSRaymond Chen static int 2281*0035018cSRaymond Chen usbecm_mac_init(usbecm_state_t *ecmp) 2282*0035018cSRaymond Chen { 2283*0035018cSRaymond Chen mac_register_t *macp; 2284*0035018cSRaymond Chen int err; 2285*0035018cSRaymond Chen 2286*0035018cSRaymond Chen /* 2287*0035018cSRaymond Chen * Initialize mac structure 2288*0035018cSRaymond Chen */ 2289*0035018cSRaymond Chen macp = mac_alloc(MAC_VERSION); 2290*0035018cSRaymond Chen if (macp == NULL) { 2291*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2292*0035018cSRaymond Chen "failed to allocate MAC structure"); 2293*0035018cSRaymond Chen 2294*0035018cSRaymond Chen return (USB_FAILURE); 2295*0035018cSRaymond Chen } 2296*0035018cSRaymond Chen 2297*0035018cSRaymond Chen /* 2298*0035018cSRaymond Chen * Initialize pointer to device specific functions 2299*0035018cSRaymond Chen */ 2300*0035018cSRaymond Chen macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 2301*0035018cSRaymond Chen macp->m_driver = ecmp; 2302*0035018cSRaymond Chen macp->m_dip = ecmp->ecm_dip; 2303*0035018cSRaymond Chen 2304*0035018cSRaymond Chen macp->m_src_addr = ecmp->ecm_srcaddr; 2305*0035018cSRaymond Chen macp->m_callbacks = &usbecm_m_callbacks; 2306*0035018cSRaymond Chen macp->m_min_sdu = 0; 2307*0035018cSRaymond Chen macp->m_max_sdu = ETHERMTU; 2308*0035018cSRaymond Chen 2309*0035018cSRaymond Chen /* 2310*0035018cSRaymond Chen * Register the macp to mac 2311*0035018cSRaymond Chen */ 2312*0035018cSRaymond Chen err = mac_register(macp, &ecmp->ecm_mh); 2313*0035018cSRaymond Chen mac_free(macp); 2314*0035018cSRaymond Chen 2315*0035018cSRaymond Chen if (err != DDI_SUCCESS) { 2316*0035018cSRaymond Chen USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh, 2317*0035018cSRaymond Chen "failed to register MAC structure"); 2318*0035018cSRaymond Chen 2319*0035018cSRaymond Chen return (USB_FAILURE); 2320*0035018cSRaymond Chen } 2321*0035018cSRaymond Chen 2322*0035018cSRaymond Chen mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN); 2323*0035018cSRaymond Chen ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN; 2324*0035018cSRaymond Chen ecmp->ecm_tx_cnt = 0; 2325*0035018cSRaymond Chen 2326*0035018cSRaymond Chen return (USB_SUCCESS); 2327*0035018cSRaymond Chen } 2328*0035018cSRaymond Chen 2329*0035018cSRaymond Chen static int 2330*0035018cSRaymond Chen usbecm_mac_fini(usbecm_state_t *ecmp) 2331*0035018cSRaymond Chen { 2332*0035018cSRaymond Chen int rval = DDI_SUCCESS; 2333*0035018cSRaymond Chen 2334*0035018cSRaymond Chen if ((ecmp->ecm_init_flags & USBECM_INIT_MAC) == 0) { 2335*0035018cSRaymond Chen return (DDI_SUCCESS); 2336*0035018cSRaymond Chen } 2337*0035018cSRaymond Chen 2338*0035018cSRaymond Chen ecmp->ecm_init_flags &= ~USBECM_INIT_MAC; 2339*0035018cSRaymond Chen if ((rval = mac_disable(ecmp->ecm_mh)) != 0) { 2340*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2341*0035018cSRaymond Chen "failed to disable MAC"); 2342*0035018cSRaymond Chen 2343*0035018cSRaymond Chen return (rval); 2344*0035018cSRaymond Chen } 2345*0035018cSRaymond Chen 2346*0035018cSRaymond Chen (void) mac_unregister(ecmp->ecm_mh); 2347*0035018cSRaymond Chen 2348*0035018cSRaymond Chen return (rval); 2349*0035018cSRaymond Chen } 2350*0035018cSRaymond Chen 2351*0035018cSRaymond Chen static int 2352*0035018cSRaymond Chen usbecm_resume(usbecm_state_t *ecmp) 2353*0035018cSRaymond Chen { 2354*0035018cSRaymond Chen int current_state; 2355*0035018cSRaymond Chen int ret; 2356*0035018cSRaymond Chen 2357*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 2358*0035018cSRaymond Chen "usbecm_resume: "); 2359*0035018cSRaymond Chen 2360*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 2361*0035018cSRaymond Chen current_state = ecmp->ecm_dev_state; 2362*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2363*0035018cSRaymond Chen 2364*0035018cSRaymond Chen /* restore the status of device */ 2365*0035018cSRaymond Chen if (current_state != USB_DEV_ONLINE) { 2366*0035018cSRaymond Chen ret = usbecm_restore_device_state(ecmp); 2367*0035018cSRaymond Chen } else { 2368*0035018cSRaymond Chen ret = USB_DEV_ONLINE; 2369*0035018cSRaymond Chen } 2370*0035018cSRaymond Chen 2371*0035018cSRaymond Chen return (ret); 2372*0035018cSRaymond Chen } 2373*0035018cSRaymond Chen 2374*0035018cSRaymond Chen static int 2375*0035018cSRaymond Chen usbecm_suspend(usbecm_state_t *ecmp) 2376*0035018cSRaymond Chen { 2377*0035018cSRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 2378*0035018cSRaymond Chen 2379*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 2380*0035018cSRaymond Chen ecmp->ecm_dev_state = USB_DEV_SUSPENDED; 2381*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 2382*0035018cSRaymond Chen 2383*0035018cSRaymond Chen usbecm_close_pipes(ecmp); 2384*0035018cSRaymond Chen 2385*0035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 2386*0035018cSRaymond Chen 2387*0035018cSRaymond Chen return (0); 2388*0035018cSRaymond Chen } 2389*0035018cSRaymond Chen 2390*0035018cSRaymond Chen /* 2391*0035018cSRaymond Chen * Translate MAC address from string to 6 bytes array int value 2392*0035018cSRaymond Chen * Can't use ether_aton() since it requires format of x:x:x:x:x:x 2393*0035018cSRaymond Chen */ 2394*0035018cSRaymond Chen void 2395*0035018cSRaymond Chen label_to_mac(char *hex, unsigned char *mac) 2396*0035018cSRaymond Chen { 2397*0035018cSRaymond Chen int i; 2398*0035018cSRaymond Chen char c; 2399*0035018cSRaymond Chen 2400*0035018cSRaymond Chen /* can only count 6 bytes! */ 2401*0035018cSRaymond Chen for (i = 0; i < 6; i++) { 2402*0035018cSRaymond Chen /* upper 4 bits */ 2403*0035018cSRaymond Chen if (!isdigit(hex[2*i])) { 2404*0035018cSRaymond Chen c = (toupper(hex[2 * i]) - 'A' + 10); 2405*0035018cSRaymond Chen } else { 2406*0035018cSRaymond Chen c = (hex[2 * i] - '0'); 2407*0035018cSRaymond Chen } 2408*0035018cSRaymond Chen mac[i] = c * 16; 2409*0035018cSRaymond Chen 2410*0035018cSRaymond Chen /* lower 4 bits */ 2411*0035018cSRaymond Chen if (!isdigit(hex[2*i + 1])) { 2412*0035018cSRaymond Chen c = (toupper(hex[2 * i + 1]) - 'A' + 10); 2413*0035018cSRaymond Chen } else { 2414*0035018cSRaymond Chen c = hex[2 * i + 1] - '0'; 2415*0035018cSRaymond Chen } 2416*0035018cSRaymond Chen mac[i] += c; 2417*0035018cSRaymond Chen } 2418*0035018cSRaymond Chen } 2419*0035018cSRaymond Chen 2420*0035018cSRaymond Chen /* 2421*0035018cSRaymond Chen * usbecm_get_descriptors: 2422*0035018cSRaymond Chen * parse functional descriptors of ecm compatible device 2423*0035018cSRaymond Chen */ 2424*0035018cSRaymond Chen static int 2425*0035018cSRaymond Chen usbecm_get_descriptors(usbecm_state_t *ecmp) 2426*0035018cSRaymond Chen { 2427*0035018cSRaymond Chen int i; 2428*0035018cSRaymond Chen usb_cfg_data_t *cfg; 2429*0035018cSRaymond Chen usb_alt_if_data_t *altif; 2430*0035018cSRaymond Chen usb_cvs_data_t *cvs; 2431*0035018cSRaymond Chen int16_t master_if = -1, slave_if = -1; 2432*0035018cSRaymond Chen usb_cdc_ecm_descr_t ecm_desc; 2433*0035018cSRaymond Chen usb_ep_data_t *ep_data; 2434*0035018cSRaymond Chen usb_dev_descr_t *usb_dev_desc; 2435*0035018cSRaymond Chen 2436*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, 2437*0035018cSRaymond Chen "usbecm_get_descriptors: "); 2438*0035018cSRaymond Chen 2439*0035018cSRaymond Chen usb_dev_desc = ecmp->ecm_dev_data->dev_descr; 2440*0035018cSRaymond Chen 2441*0035018cSRaymond Chen /* 2442*0035018cSRaymond Chen * Special treatment of Sun's SP Ethernet device. 2443*0035018cSRaymond Chen */ 2444*0035018cSRaymond Chen if ((usb_dev_desc->idVendor == SUN_SP_VENDOR_ID) && 2445*0035018cSRaymond Chen (usb_dev_desc->idProduct == SUN_SP_PRODUCT_ID)) { 2446*0035018cSRaymond Chen if (usb_set_cfg(ecmp->ecm_dip, ecmp->ecm_cfg_index, 2447*0035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS) { 2448*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2449*0035018cSRaymond Chen "usbecm_get_descriptors: fail to set cfg "); 2450*0035018cSRaymond Chen } else { 2451*0035018cSRaymond Chen usb_free_dev_data(ecmp->ecm_dip, ecmp->ecm_dev_data); 2452*0035018cSRaymond Chen if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data, 2453*0035018cSRaymond Chen USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) { 2454*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2455*0035018cSRaymond Chen "usbecm_get_descriptors: fail to get" 2456*0035018cSRaymond Chen " dev_data"); 2457*0035018cSRaymond Chen 2458*0035018cSRaymond Chen return (USB_FAILURE); 2459*0035018cSRaymond Chen } 2460*0035018cSRaymond Chen } 2461*0035018cSRaymond Chen } 2462*0035018cSRaymond Chen 2463*0035018cSRaymond Chen cfg = ecmp->ecm_dev_data->dev_curr_cfg; 2464*0035018cSRaymond Chen 2465*0035018cSRaymond Chen /* set default control and data interface */ 2466*0035018cSRaymond Chen ecmp->ecm_ctrl_if_no = ecmp->ecm_data_if_no = 0; 2467*0035018cSRaymond Chen 2468*0035018cSRaymond Chen /* get current interfaces */ 2469*0035018cSRaymond Chen ecmp->ecm_ctrl_if_no = ecmp->ecm_dev_data->dev_curr_if; 2470*0035018cSRaymond Chen if (cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt == 0) { 2471*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2472*0035018cSRaymond Chen "usbecm_get_descriptors: elements in if_alt is %d", 2473*0035018cSRaymond Chen cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt); 2474*0035018cSRaymond Chen 2475*0035018cSRaymond Chen return (USB_FAILURE); 2476*0035018cSRaymond Chen } 2477*0035018cSRaymond Chen 2478*0035018cSRaymond Chen altif = &cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_alt[0]; 2479*0035018cSRaymond Chen 2480*0035018cSRaymond Chen /* 2481*0035018cSRaymond Chen * Based on CDC specification, ECM devices usually include the 2482*0035018cSRaymond Chen * following function descriptors: Header, Union and ECM 2483*0035018cSRaymond Chen * Contry Selection function descriptors. This loop search tree data 2484*0035018cSRaymond Chen * structure for each ecm class descriptor. 2485*0035018cSRaymond Chen */ 2486*0035018cSRaymond Chen for (i = 0; i < altif->altif_n_cvs; i++) { 2487*0035018cSRaymond Chen cvs = &altif->altif_cvs[i]; 2488*0035018cSRaymond Chen 2489*0035018cSRaymond Chen if ((cvs->cvs_buf == NULL) || 2490*0035018cSRaymond Chen (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) { 2491*0035018cSRaymond Chen continue; 2492*0035018cSRaymond Chen } 2493*0035018cSRaymond Chen 2494*0035018cSRaymond Chen switch (cvs->cvs_buf[2]) { 2495*0035018cSRaymond Chen case USB_CDC_DESCR_TYPE_HEADER: 2496*0035018cSRaymond Chen /* 2497*0035018cSRaymond Chen * parse header functional descriptor 2498*0035018cSRaymond Chen * Just to check integrity. 2499*0035018cSRaymond Chen */ 2500*0035018cSRaymond Chen if (cvs->cvs_buf_len != 5) { 2501*0035018cSRaymond Chen return (USB_FAILURE); 2502*0035018cSRaymond Chen } 2503*0035018cSRaymond Chen break; 2504*0035018cSRaymond Chen case USB_CDC_DESCR_TYPE_ETHERNET: 2505*0035018cSRaymond Chen /* parse ECM functional descriptor */ 2506*0035018cSRaymond Chen if (cvs->cvs_buf_len >= USB_CDC_ECM_LEN) { 2507*0035018cSRaymond Chen char buf[USB_MAXSTRINGLEN]; 2508*0035018cSRaymond Chen 2509*0035018cSRaymond Chen if (usb_parse_data("4cl2sc", cvs->cvs_buf, 2510*0035018cSRaymond Chen cvs->cvs_buf_len, (void *)&ecm_desc, 2511*0035018cSRaymond Chen (size_t)USB_CDC_ECM_LEN) < 2512*0035018cSRaymond Chen USB_CDC_ECM_LEN) { 2513*0035018cSRaymond Chen 2514*0035018cSRaymond Chen return (USB_FAILURE); 2515*0035018cSRaymond Chen } 2516*0035018cSRaymond Chen 2517*0035018cSRaymond Chen /* get the MAC address */ 2518*0035018cSRaymond Chen if (usb_get_string_descr(ecmp->ecm_dip, 2519*0035018cSRaymond Chen USB_LANG_ID, ecm_desc.iMACAddress, buf, 2520*0035018cSRaymond Chen USB_MAXSTRINGLEN) != USB_SUCCESS) { 2521*0035018cSRaymond Chen 2522*0035018cSRaymond Chen return (USB_FAILURE); 2523*0035018cSRaymond Chen } 2524*0035018cSRaymond Chen 2525*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, 2526*0035018cSRaymond Chen "usbecm_get_descriptors: macaddr=%s ", 2527*0035018cSRaymond Chen buf); 2528*0035018cSRaymond Chen 2529*0035018cSRaymond Chen /* expects 12 characters */ 2530*0035018cSRaymond Chen if (strlen(buf) < 12) { 2531*0035018cSRaymond Chen return (USB_FAILURE); 2532*0035018cSRaymond Chen } 2533*0035018cSRaymond Chen label_to_mac(buf, ecmp->ecm_srcaddr); 2534*0035018cSRaymond Chen 2535*0035018cSRaymond Chen bcopy(&ecm_desc, &ecmp->ecm_desc, 2536*0035018cSRaymond Chen USB_CDC_ECM_LEN); 2537*0035018cSRaymond Chen } 2538*0035018cSRaymond Chen break; 2539*0035018cSRaymond Chen case USB_CDC_DESCR_TYPE_UNION: 2540*0035018cSRaymond Chen /* parse Union functional descriptor. */ 2541*0035018cSRaymond Chen if (cvs->cvs_buf_len >= 5) { 2542*0035018cSRaymond Chen master_if = cvs->cvs_buf[3]; 2543*0035018cSRaymond Chen slave_if = cvs->cvs_buf[4]; 2544*0035018cSRaymond Chen } 2545*0035018cSRaymond Chen break; 2546*0035018cSRaymond Chen default: 2547*0035018cSRaymond Chen break; 2548*0035018cSRaymond Chen } 2549*0035018cSRaymond Chen } 2550*0035018cSRaymond Chen 2551*0035018cSRaymond Chen /* For usb ecm devices, it must satisfy the following options. */ 2552*0035018cSRaymond Chen if (cfg->cfg_n_if < 2) { 2553*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2554*0035018cSRaymond Chen "usbecm_get_descriptors: # of interfaces %d < 2", 2555*0035018cSRaymond Chen cfg->cfg_n_if); 2556*0035018cSRaymond Chen 2557*0035018cSRaymond Chen return (USB_FAILURE); 2558*0035018cSRaymond Chen } 2559*0035018cSRaymond Chen 2560*0035018cSRaymond Chen if (ecmp->ecm_data_if_no == 0 && 2561*0035018cSRaymond Chen slave_if != ecmp->ecm_data_if_no) { 2562*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2563*0035018cSRaymond Chen "usbecm_get_descriptors: Device has no call management " 2564*0035018cSRaymond Chen "descriptor and use Union Descriptor."); 2565*0035018cSRaymond Chen 2566*0035018cSRaymond Chen ecmp->ecm_data_if_no = slave_if; 2567*0035018cSRaymond Chen } 2568*0035018cSRaymond Chen 2569*0035018cSRaymond Chen if ((master_if != ecmp->ecm_ctrl_if_no) || 2570*0035018cSRaymond Chen (slave_if != ecmp->ecm_data_if_no)) { 2571*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2572*0035018cSRaymond Chen "usbecm_get_descriptors: control interface or " 2573*0035018cSRaymond Chen "data interface don't match."); 2574*0035018cSRaymond Chen 2575*0035018cSRaymond Chen return (USB_FAILURE); 2576*0035018cSRaymond Chen } 2577*0035018cSRaymond Chen 2578*0035018cSRaymond Chen if ((ecmp->ecm_ctrl_if_no >= cfg->cfg_n_if) || 2579*0035018cSRaymond Chen (ecmp->ecm_data_if_no >= cfg->cfg_n_if)) { 2580*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2581*0035018cSRaymond Chen "usbecm_get_descriptors: control interface %d or " 2582*0035018cSRaymond Chen "data interface %d out of range.", 2583*0035018cSRaymond Chen ecmp->ecm_ctrl_if_no, ecmp->ecm_data_if_no); 2584*0035018cSRaymond Chen 2585*0035018cSRaymond Chen return (USB_FAILURE); 2586*0035018cSRaymond Chen } 2587*0035018cSRaymond Chen 2588*0035018cSRaymond Chen /* ECM data interface has a minimal of two altsettings */ 2589*0035018cSRaymond Chen if (cfg->cfg_if[ecmp->ecm_data_if_no].if_n_alt < 2) { 2590*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2591*0035018cSRaymond Chen "usbecm_get_descriptors: elements in if_alt is %d," 2592*0035018cSRaymond Chen " MUST >= 2", cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt); 2593*0035018cSRaymond Chen 2594*0035018cSRaymond Chen return (USB_FAILURE); 2595*0035018cSRaymond Chen } 2596*0035018cSRaymond Chen 2597*0035018cSRaymond Chen /* control interface must have interrupt endpoint */ 2598*0035018cSRaymond Chen if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data, 2599*0035018cSRaymond Chen ecmp->ecm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR, 2600*0035018cSRaymond Chen USB_EP_DIR_IN)) == NULL) { 2601*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2602*0035018cSRaymond Chen "usbecm_get_descriptors: " 2603*0035018cSRaymond Chen "ctrl interface %d has no interrupt endpoint", 2604*0035018cSRaymond Chen ecmp->ecm_data_if_no); 2605*0035018cSRaymond Chen 2606*0035018cSRaymond Chen return (USB_FAILURE); 2607*0035018cSRaymond Chen } 2608*0035018cSRaymond Chen ecmp->ecm_intr_ep = ep_data; 2609*0035018cSRaymond Chen 2610*0035018cSRaymond Chen /* data interface alt 1 must have bulk in and out(ECM v1.2,p5) */ 2611*0035018cSRaymond Chen if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data, 2612*0035018cSRaymond Chen ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK, 2613*0035018cSRaymond Chen USB_EP_DIR_IN)) == NULL) { 2614*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2615*0035018cSRaymond Chen "usbecm_get_descriptors: " 2616*0035018cSRaymond Chen "data interface %d has no bulk in endpoint", 2617*0035018cSRaymond Chen ecmp->ecm_data_if_no); 2618*0035018cSRaymond Chen 2619*0035018cSRaymond Chen return (USB_FAILURE); 2620*0035018cSRaymond Chen } 2621*0035018cSRaymond Chen ecmp->ecm_bulk_in_ep = ep_data; 2622*0035018cSRaymond Chen 2623*0035018cSRaymond Chen if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data, 2624*0035018cSRaymond Chen ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK, 2625*0035018cSRaymond Chen USB_EP_DIR_OUT)) == NULL) { 2626*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2627*0035018cSRaymond Chen "usbecm_get_descriptors: " 2628*0035018cSRaymond Chen "data interface %d has no bulk out endpoint", 2629*0035018cSRaymond Chen ecmp->ecm_data_if_no); 2630*0035018cSRaymond Chen 2631*0035018cSRaymond Chen return (USB_FAILURE); 2632*0035018cSRaymond Chen } 2633*0035018cSRaymond Chen ecmp->ecm_bulk_out_ep = ep_data; 2634*0035018cSRaymond Chen 2635*0035018cSRaymond Chen /* set default value for ethernet packet filter */ 2636*0035018cSRaymond Chen ecmp->ecm_pkt_flt = CDC_ECM_PKT_TYPE_DIRECTED; 2637*0035018cSRaymond Chen 2638*0035018cSRaymond Chen return (USB_SUCCESS); 2639*0035018cSRaymond Chen } 2640*0035018cSRaymond Chen 2641*0035018cSRaymond Chen /* Generate IEEE802 style MAC address */ 2642*0035018cSRaymond Chen static void 2643*0035018cSRaymond Chen generate_ether_addr(uint8_t *mac_addr) 2644*0035018cSRaymond Chen { 2645*0035018cSRaymond Chen random_get_bytes(mac_addr, 6); 2646*0035018cSRaymond Chen mac_addr [0] &= 0xfe; /* unicast only */ 2647*0035018cSRaymond Chen mac_addr [0] |= 0x02; /* set locally administered bit */ 2648*0035018cSRaymond Chen } 2649*0035018cSRaymond Chen 2650*0035018cSRaymond Chen /* 2651*0035018cSRaymond Chen * Find a pair of bulk In/Out endpoints 2652*0035018cSRaymond Chen */ 2653*0035018cSRaymond Chen int usbecm_find_bulk_in_out_eps(usbecm_state_t *ecmp, 2654*0035018cSRaymond Chen uint16_t ifc, usb_if_data_t *intf) 2655*0035018cSRaymond Chen { 2656*0035018cSRaymond Chen uint16_t alt, alt_num; 2657*0035018cSRaymond Chen usb_ep_data_t *intr_ep = NULL; 2658*0035018cSRaymond Chen usb_ep_data_t *bulk_in, *bulk_out, *ep; 2659*0035018cSRaymond Chen 2660*0035018cSRaymond Chen alt_num = intf->if_n_alt; 2661*0035018cSRaymond Chen 2662*0035018cSRaymond Chen /* 2663*0035018cSRaymond Chen * for the non-compatible devices, to make it simple, we 2664*0035018cSRaymond Chen * suppose the devices have this kind of configuration: 2665*0035018cSRaymond Chen * INTR In EP(if exists) + BULK In + Bulk Out in the 2666*0035018cSRaymond Chen * same altsetting of the same interface 2667*0035018cSRaymond Chen */ 2668*0035018cSRaymond Chen for (alt = 0; alt < alt_num; alt++) { 2669*0035018cSRaymond Chen /* search pair of bulk in/out EPs */ 2670*0035018cSRaymond Chen if (((bulk_in = usb_lookup_ep_data(ecmp->ecm_dip, 2671*0035018cSRaymond Chen ecmp->ecm_dev_data, ifc, alt, 0, 2672*0035018cSRaymond Chen USB_EP_ATTR_BULK, 2673*0035018cSRaymond Chen USB_EP_DIR_IN)) == NULL) || 2674*0035018cSRaymond Chen (bulk_out = usb_lookup_ep_data(ecmp->ecm_dip, 2675*0035018cSRaymond Chen ecmp->ecm_dev_data, ifc, alt, 0, 2676*0035018cSRaymond Chen USB_EP_ATTR_BULK, 2677*0035018cSRaymond Chen USB_EP_DIR_OUT)) == NULL) { 2678*0035018cSRaymond Chen 2679*0035018cSRaymond Chen continue; 2680*0035018cSRaymond Chen } 2681*0035018cSRaymond Chen 2682*0035018cSRaymond Chen /* 2683*0035018cSRaymond Chen * search interrupt pipe. 2684*0035018cSRaymond Chen */ 2685*0035018cSRaymond Chen if ((ep = usb_lookup_ep_data(ecmp->ecm_dip, 2686*0035018cSRaymond Chen ecmp->ecm_dev_data, ifc, alt, 0, 2687*0035018cSRaymond Chen USB_EP_ATTR_INTR, USB_EP_DIR_IN)) != NULL) { 2688*0035018cSRaymond Chen intr_ep = ep; 2689*0035018cSRaymond Chen } 2690*0035018cSRaymond Chen 2691*0035018cSRaymond Chen 2692*0035018cSRaymond Chen ecmp->ecm_data_if_no = ifc; 2693*0035018cSRaymond Chen ecmp->ecm_data_if_alt = alt; 2694*0035018cSRaymond Chen ecmp->ecm_intr_ep = intr_ep; 2695*0035018cSRaymond Chen ecmp->ecm_ctrl_if_no = ifc; 2696*0035018cSRaymond Chen ecmp->ecm_bulk_in_ep = bulk_in; 2697*0035018cSRaymond Chen ecmp->ecm_bulk_out_ep = bulk_out; 2698*0035018cSRaymond Chen 2699*0035018cSRaymond Chen return (USB_SUCCESS); 2700*0035018cSRaymond Chen } 2701*0035018cSRaymond Chen 2702*0035018cSRaymond Chen return (USB_FAILURE); 2703*0035018cSRaymond Chen } 2704*0035018cSRaymond Chen 2705*0035018cSRaymond Chen static int 2706*0035018cSRaymond Chen usbecm_init_non_compatible_device(usbecm_state_t *ecmp) 2707*0035018cSRaymond Chen { 2708*0035018cSRaymond Chen usb_if_data_t *cur_if; 2709*0035018cSRaymond Chen uint16_t if_num, i; 2710*0035018cSRaymond Chen 2711*0035018cSRaymond Chen /* 2712*0035018cSRaymond Chen * If device don't conform to spec, search pairs of bulk in/out 2713*0035018cSRaymond Chen * endpoints and fill related structure. We suppose this driver 2714*0035018cSRaymond Chen * is bound to a interface. 2715*0035018cSRaymond Chen */ 2716*0035018cSRaymond Chen cur_if = ecmp->ecm_dev_data->dev_curr_cfg->cfg_if; 2717*0035018cSRaymond Chen if_num = ecmp->ecm_dev_data->dev_curr_cfg->cfg_n_if; 2718*0035018cSRaymond Chen 2719*0035018cSRaymond Chen /* search each interface which have bulk in and out */ 2720*0035018cSRaymond Chen for (i = 0; i < if_num; i++) { 2721*0035018cSRaymond Chen if (usbecm_find_bulk_in_out_eps(ecmp, i, 2722*0035018cSRaymond Chen cur_if) == USB_SUCCESS) { 2723*0035018cSRaymond Chen 2724*0035018cSRaymond Chen break; 2725*0035018cSRaymond Chen } 2726*0035018cSRaymond Chen cur_if++; 2727*0035018cSRaymond Chen } 2728*0035018cSRaymond Chen 2729*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, 2730*0035018cSRaymond Chen "usbecm_init_non_compatible_device: ctrl_if=%d," 2731*0035018cSRaymond Chen " data_if=%d, alt=%d", ecmp->ecm_ctrl_if_no, 2732*0035018cSRaymond Chen ecmp->ecm_data_if_no, ecmp->ecm_data_if_alt); 2733*0035018cSRaymond Chen 2734*0035018cSRaymond Chen return (USB_SUCCESS); 2735*0035018cSRaymond Chen } 2736*0035018cSRaymond Chen 2737*0035018cSRaymond Chen static boolean_t 2738*0035018cSRaymond Chen usbecm_is_compatible(usbecm_state_t *ecmp) 2739*0035018cSRaymond Chen { 2740*0035018cSRaymond Chen usb_cfg_data_t *cfg_data; 2741*0035018cSRaymond Chen usb_if_data_t *intf; 2742*0035018cSRaymond Chen usb_alt_if_data_t *alt; 2743*0035018cSRaymond Chen int alt_num, if_num, cfg_num; 2744*0035018cSRaymond Chen int i, j, cfg_index; 2745*0035018cSRaymond Chen 2746*0035018cSRaymond Chen cfg_num = ecmp->ecm_dev_data->dev_n_cfg; 2747*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, 2748*0035018cSRaymond Chen "usbecm_is_compatible: entry, cfg_num=%d", cfg_num); 2749*0035018cSRaymond Chen 2750*0035018cSRaymond Chen for (cfg_index = 0; cfg_index < cfg_num; cfg_index++) { 2751*0035018cSRaymond Chen cfg_data = &(ecmp->ecm_dev_data->dev_cfg[cfg_index]); 2752*0035018cSRaymond Chen 2753*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, 2754*0035018cSRaymond Chen "usbecm_is_compatible: cfg_index=%d, value=%d", 2755*0035018cSRaymond Chen cfg_index, cfg_data->cfg_descr.bConfigurationValue); 2756*0035018cSRaymond Chen 2757*0035018cSRaymond Chen intf = cfg_data->cfg_if; 2758*0035018cSRaymond Chen if_num = cfg_data->cfg_n_if; 2759*0035018cSRaymond Chen 2760*0035018cSRaymond Chen for (i = 0; i < if_num; i++) { 2761*0035018cSRaymond Chen alt_num = intf->if_n_alt; 2762*0035018cSRaymond Chen for (j = 0; j < alt_num; j++) { 2763*0035018cSRaymond Chen alt = &intf->if_alt[j]; 2764*0035018cSRaymond Chen if ((alt->altif_descr.bInterfaceClass == 0x02) && 2765*0035018cSRaymond Chen (alt->altif_descr.bInterfaceSubClass == 0x06)) { 2766*0035018cSRaymond Chen ecmp->ecm_cfg_index = cfg_index; 2767*0035018cSRaymond Chen 2768*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, 2769*0035018cSRaymond Chen "usbecm_is_compatible: cfg_index=%d", 2770*0035018cSRaymond Chen cfg_index); 2771*0035018cSRaymond Chen 2772*0035018cSRaymond Chen return (B_TRUE); 2773*0035018cSRaymond Chen } 2774*0035018cSRaymond Chen } 2775*0035018cSRaymond Chen intf++; 2776*0035018cSRaymond Chen } 2777*0035018cSRaymond Chen } 2778*0035018cSRaymond Chen 2779*0035018cSRaymond Chen return (B_FALSE); 2780*0035018cSRaymond Chen } 2781*0035018cSRaymond Chen 2782*0035018cSRaymond Chen 2783*0035018cSRaymond Chen static int 2784*0035018cSRaymond Chen usbecm_usb_init(usbecm_state_t *ecmp) 2785*0035018cSRaymond Chen { 2786*0035018cSRaymond Chen 2787*0035018cSRaymond Chen if (usb_client_attach(ecmp->ecm_dip, USBDRV_VERSION, 0) != 2788*0035018cSRaymond Chen USB_SUCCESS) { 2789*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2790*0035018cSRaymond Chen "usbecm_usb_init: fail to attach"); 2791*0035018cSRaymond Chen 2792*0035018cSRaymond Chen return (USB_FAILURE); 2793*0035018cSRaymond Chen } 2794*0035018cSRaymond Chen 2795*0035018cSRaymond Chen /* Get the configuration information of device */ 2796*0035018cSRaymond Chen if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data, 2797*0035018cSRaymond Chen USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) { 2798*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2799*0035018cSRaymond Chen "usbecm_usb_init: fail to get_dev_data"); 2800*0035018cSRaymond Chen 2801*0035018cSRaymond Chen return (USB_FAILURE); 2802*0035018cSRaymond Chen } 2803*0035018cSRaymond Chen ecmp->ecm_def_ph = ecmp->ecm_dev_data->dev_default_ph; 2804*0035018cSRaymond Chen ecmp->ecm_dev_state = USB_DEV_ONLINE; 2805*0035018cSRaymond Chen 2806*0035018cSRaymond Chen mutex_init(&ecmp->ecm_mutex, NULL, MUTEX_DRIVER, 2807*0035018cSRaymond Chen ecmp->ecm_dev_data->dev_iblock_cookie); 2808*0035018cSRaymond Chen 2809*0035018cSRaymond Chen if ((strcmp(ddi_binding_name(ecmp->ecm_dip), 2810*0035018cSRaymond Chen "usbif,class2.6") == 0) || 2811*0035018cSRaymond Chen ((strcmp(ddi_binding_name(ecmp->ecm_dip), 2812*0035018cSRaymond Chen "usb,class2.6.0") == 0))) { 2813*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2814*0035018cSRaymond Chen "usbecm_usb_init: A CDC ECM device is attached"); 2815*0035018cSRaymond Chen ecmp->ecm_compatibility = B_TRUE; 2816*0035018cSRaymond Chen } else if (usb_owns_device(ecmp->ecm_dip) && 2817*0035018cSRaymond Chen usbecm_is_compatible(ecmp)) { 2818*0035018cSRaymond Chen /* 2819*0035018cSRaymond Chen * Current Sun SP ECM device has two configurations. Hence 2820*0035018cSRaymond Chen * USBA doesn't create interface level compatible names 2821*0035018cSRaymond Chen * for it, see usba_ready_device_node(). We have to check 2822*0035018cSRaymond Chen * manually to see if compatible interfaces exist, when 2823*0035018cSRaymond Chen * the driver owns the entire device. 2824*0035018cSRaymond Chen */ 2825*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2826*0035018cSRaymond Chen "usbecm_usb_init: A CDC ECM device is attached"); 2827*0035018cSRaymond Chen ecmp->ecm_compatibility = B_TRUE; 2828*0035018cSRaymond Chen } else { 2829*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2830*0035018cSRaymond Chen "usbecm_usb_init: A nonstandard device is attached to " 2831*0035018cSRaymond Chen "usbecm(7D) driver. This device doesn't conform to " 2832*0035018cSRaymond Chen "usb cdc spec."); 2833*0035018cSRaymond Chen ecmp->ecm_compatibility = B_FALSE; 2834*0035018cSRaymond Chen 2835*0035018cSRaymond Chen /* generate a random MAC addr */ 2836*0035018cSRaymond Chen generate_ether_addr(ecmp->ecm_srcaddr); 2837*0035018cSRaymond Chen } 2838*0035018cSRaymond Chen 2839*0035018cSRaymond Chen if ((ecmp->ecm_compatibility == B_TRUE) && 2840*0035018cSRaymond Chen (usbecm_get_descriptors(ecmp) != USB_SUCCESS)) { 2841*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2842*0035018cSRaymond Chen "usbecm_usb_init: A compatible device is attached, but " 2843*0035018cSRaymond Chen "fail to get standard descriptors"); 2844*0035018cSRaymond Chen 2845*0035018cSRaymond Chen return (USB_FAILURE); 2846*0035018cSRaymond Chen } 2847*0035018cSRaymond Chen 2848*0035018cSRaymond Chen if (ecmp->ecm_compatibility == B_FALSE) { 2849*0035018cSRaymond Chen (void) usbecm_init_non_compatible_device(ecmp); 2850*0035018cSRaymond Chen } 2851*0035018cSRaymond Chen 2852*0035018cSRaymond Chen /* Create power management components */ 2853*0035018cSRaymond Chen if (usbecm_create_pm_components(ecmp) != USB_SUCCESS) { 2854*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2855*0035018cSRaymond Chen "usbecm_usb_init: create pm components failed."); 2856*0035018cSRaymond Chen 2857*0035018cSRaymond Chen return (USB_FAILURE); 2858*0035018cSRaymond Chen } 2859*0035018cSRaymond Chen 2860*0035018cSRaymond Chen /* Register to get callbacks for USB events */ 2861*0035018cSRaymond Chen if (usb_register_event_cbs(ecmp->ecm_dip, &usbecm_events, 0) 2862*0035018cSRaymond Chen != USB_SUCCESS) { 2863*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2864*0035018cSRaymond Chen "usbsecm_attach: register event callback failed."); 2865*0035018cSRaymond Chen 2866*0035018cSRaymond Chen return (USB_FAILURE); 2867*0035018cSRaymond Chen } 2868*0035018cSRaymond Chen ecmp->ecm_init_flags |= USBECM_INIT_EVENTS; 2869*0035018cSRaymond Chen 2870*0035018cSRaymond Chen 2871*0035018cSRaymond Chen /* Get max data size of bulk transfer */ 2872*0035018cSRaymond Chen if (usb_pipe_get_max_bulk_transfer_size(ecmp->ecm_dip, 2873*0035018cSRaymond Chen &ecmp->ecm_xfer_sz) != USB_SUCCESS) { 2874*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2875*0035018cSRaymond Chen "usbsecm_ds_attach: get max size of transfer failed."); 2876*0035018cSRaymond Chen 2877*0035018cSRaymond Chen return (USB_FAILURE); 2878*0035018cSRaymond Chen } 2879*0035018cSRaymond Chen 2880*0035018cSRaymond Chen 2881*0035018cSRaymond Chen ecmp->ecm_ser_acc = usb_init_serialization(ecmp->ecm_dip, 2882*0035018cSRaymond Chen USB_INIT_SER_CHECK_SAME_THREAD); 2883*0035018cSRaymond Chen ecmp->ecm_init_flags |= USBECM_INIT_SER; 2884*0035018cSRaymond Chen 2885*0035018cSRaymond Chen return (USB_SUCCESS); 2886*0035018cSRaymond Chen } 2887*0035018cSRaymond Chen 2888*0035018cSRaymond Chen 2889*0035018cSRaymond Chen /* 2890*0035018cSRaymond Chen * Open operation pipes. Each ECM device should have Bulk In, Bulk Out 2891*0035018cSRaymond Chen * and Interrupt In endpoints 2892*0035018cSRaymond Chen */ 2893*0035018cSRaymond Chen static int 2894*0035018cSRaymond Chen usbecm_open_pipes(usbecm_state_t *ecmp) 2895*0035018cSRaymond Chen { 2896*0035018cSRaymond Chen int rval = USB_SUCCESS; 2897*0035018cSRaymond Chen usb_ep_data_t *in_data, *out_data, *intr_pipe; 2898*0035018cSRaymond Chen usb_pipe_policy_t policy; 2899*0035018cSRaymond Chen int altif; 2900*0035018cSRaymond Chen 2901*0035018cSRaymond Chen ASSERT(!mutex_owned(&ecmp->ecm_mutex)); 2902*0035018cSRaymond Chen 2903*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh, 2904*0035018cSRaymond Chen "usbsecm_open_pipes: ecmp = 0x%p", (void *)ecmp); 2905*0035018cSRaymond Chen 2906*0035018cSRaymond Chen if (ecmp->ecm_compatibility == B_TRUE) { 2907*0035018cSRaymond Chen /* compatible device has minimum of 2 altsetting, select alt 1 */ 2908*0035018cSRaymond Chen altif = 1; 2909*0035018cSRaymond Chen } else { 2910*0035018cSRaymond Chen altif = ecmp->ecm_data_if_alt; 2911*0035018cSRaymond Chen } 2912*0035018cSRaymond Chen intr_pipe = ecmp->ecm_intr_ep; 2913*0035018cSRaymond Chen in_data = ecmp->ecm_bulk_in_ep; 2914*0035018cSRaymond Chen out_data = ecmp->ecm_bulk_out_ep; 2915*0035018cSRaymond Chen 2916*0035018cSRaymond Chen /* Bulk in and out must exist simultaneously. */ 2917*0035018cSRaymond Chen if ((in_data == NULL) || (out_data == NULL)) { 2918*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 2919*0035018cSRaymond Chen "usbsecm_open_pipes: look up bulk pipe failed in " 2920*0035018cSRaymond Chen "interface %d ", 2921*0035018cSRaymond Chen ecmp->ecm_data_if_no); 2922*0035018cSRaymond Chen 2923*0035018cSRaymond Chen return (USB_FAILURE); 2924*0035018cSRaymond Chen } 2925*0035018cSRaymond Chen /* 2926*0035018cSRaymond Chen * If device conform to ecm spec, it must have an interrupt pipe 2927*0035018cSRaymond Chen * for this device. 2928*0035018cSRaymond Chen */ 2929*0035018cSRaymond Chen if (ecmp->ecm_compatibility == B_TRUE && intr_pipe == NULL) { 2930*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 2931*0035018cSRaymond Chen "usbecm_open_pipes: look up interrupt pipe failed in " 2932*0035018cSRaymond Chen "interface %d", ecmp->ecm_ctrl_if_no); 2933*0035018cSRaymond Chen 2934*0035018cSRaymond Chen return (USB_FAILURE); 2935*0035018cSRaymond Chen } 2936*0035018cSRaymond Chen 2937*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, 2938*0035018cSRaymond Chen "usbsecm_open_pipes: open intr %02x, bulkin %02x bulkout %02x", 2939*0035018cSRaymond Chen intr_pipe?intr_pipe->ep_descr.bEndpointAddress:0, 2940*0035018cSRaymond Chen in_data->ep_descr.bEndpointAddress, 2941*0035018cSRaymond Chen out_data->ep_descr.bEndpointAddress); 2942*0035018cSRaymond Chen 2943*0035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, 2944*0035018cSRaymond Chen "usbsecm_open_pipes: set data if(%d) alt(%d) ", 2945*0035018cSRaymond Chen ecmp->ecm_data_if_no, altif); 2946*0035018cSRaymond Chen 2947*0035018cSRaymond Chen if ((rval = usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no, 2948*0035018cSRaymond Chen altif, USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) { 2949*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2950*0035018cSRaymond Chen "usbecm_open_pipes: set alternate failed (%d)", 2951*0035018cSRaymond Chen rval); 2952*0035018cSRaymond Chen 2953*0035018cSRaymond Chen return (rval); 2954*0035018cSRaymond Chen } 2955*0035018cSRaymond Chen 2956*0035018cSRaymond Chen policy.pp_max_async_reqs = 2; 2957*0035018cSRaymond Chen 2958*0035018cSRaymond Chen /* Open bulk in endpoint */ 2959*0035018cSRaymond Chen if (usb_pipe_open(ecmp->ecm_dip, &in_data->ep_descr, &policy, 2960*0035018cSRaymond Chen USB_FLAGS_SLEEP, &ecmp->ecm_bulkin_ph) != USB_SUCCESS) { 2961*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 2962*0035018cSRaymond Chen "usbecm_open_pipes: open bulkin pipe failed!"); 2963*0035018cSRaymond Chen 2964*0035018cSRaymond Chen return (USB_FAILURE); 2965*0035018cSRaymond Chen } 2966*0035018cSRaymond Chen 2967*0035018cSRaymond Chen /* Open bulk out endpoint */ 2968*0035018cSRaymond Chen if (usb_pipe_open(ecmp->ecm_dip, &out_data->ep_descr, &policy, 2969*0035018cSRaymond Chen USB_FLAGS_SLEEP, &ecmp->ecm_bulkout_ph) != USB_SUCCESS) { 2970*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 2971*0035018cSRaymond Chen "usbecm_open_pipes: open bulkout pipe failed!"); 2972*0035018cSRaymond Chen 2973*0035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, 2974*0035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, NULL); 2975*0035018cSRaymond Chen 2976*0035018cSRaymond Chen return (USB_FAILURE); 2977*0035018cSRaymond Chen } 2978*0035018cSRaymond Chen 2979*0035018cSRaymond Chen /* Open interrupt endpoint if found. */ 2980*0035018cSRaymond Chen if (intr_pipe != NULL) { 2981*0035018cSRaymond Chen if (usb_pipe_open(ecmp->ecm_dip, &intr_pipe->ep_descr, &policy, 2982*0035018cSRaymond Chen USB_FLAGS_SLEEP, &ecmp->ecm_intr_ph) != USB_SUCCESS) { 2983*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 2984*0035018cSRaymond Chen "usbecm_open_pipes: " 2985*0035018cSRaymond Chen "open intr pipe failed"); 2986*0035018cSRaymond Chen 2987*0035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, 2988*0035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, NULL); 2989*0035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph, 2990*0035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, NULL); 2991*0035018cSRaymond Chen 2992*0035018cSRaymond Chen return (USB_FAILURE); 2993*0035018cSRaymond Chen } 2994*0035018cSRaymond Chen } 2995*0035018cSRaymond Chen 2996*0035018cSRaymond Chen /* initialize the pipe related data */ 2997*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 2998*0035018cSRaymond Chen ecmp->ecm_bulkin_sz = in_data->ep_descr.wMaxPacketSize; 2999*0035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE; 3000*0035018cSRaymond Chen ecmp->ecm_bulkout_state = USBECM_PIPE_IDLE; 3001*0035018cSRaymond Chen if (ecmp->ecm_intr_ph != NULL) { 3002*0035018cSRaymond Chen ecmp->ecm_intr_state = USBECM_PIPE_IDLE; 3003*0035018cSRaymond Chen } 3004*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 3005*0035018cSRaymond Chen 3006*0035018cSRaymond Chen if (ecmp->ecm_intr_ph != NULL) { 3007*0035018cSRaymond Chen 3008*0035018cSRaymond Chen usbecm_pipe_start_polling(ecmp); 3009*0035018cSRaymond Chen } 3010*0035018cSRaymond Chen 3011*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh, 3012*0035018cSRaymond Chen "usbsecm_open_pipes: end"); 3013*0035018cSRaymond Chen 3014*0035018cSRaymond Chen return (rval); 3015*0035018cSRaymond Chen } 3016*0035018cSRaymond Chen 3017*0035018cSRaymond Chen 3018*0035018cSRaymond Chen /* 3019*0035018cSRaymond Chen * usbsecm_close_pipes: 3020*0035018cSRaymond Chen * Close pipes 3021*0035018cSRaymond Chen * Each device could include three pipes: bulk in, bulk out and interrupt. 3022*0035018cSRaymond Chen */ 3023*0035018cSRaymond Chen static void 3024*0035018cSRaymond Chen usbecm_close_pipes(usbecm_state_t *ecmp) 3025*0035018cSRaymond Chen { 3026*0035018cSRaymond Chen 3027*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 3028*0035018cSRaymond Chen 3029*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, 3030*0035018cSRaymond Chen "usbsecm_close_pipes: ecm_bulkin_state = %d", 3031*0035018cSRaymond Chen ecmp->ecm_bulkin_state); 3032*0035018cSRaymond Chen 3033*0035018cSRaymond Chen /* 3034*0035018cSRaymond Chen * Check the status of the pipes. If pipe is closing or closed, 3035*0035018cSRaymond Chen * return directly. 3036*0035018cSRaymond Chen */ 3037*0035018cSRaymond Chen if ((ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSED) || 3038*0035018cSRaymond Chen (ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSING)) { 3039*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CLOSE, ecmp->ecm_lh, 3040*0035018cSRaymond Chen "usbsecm_close_pipes: pipe is closing or has closed"); 3041*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 3042*0035018cSRaymond Chen 3043*0035018cSRaymond Chen return; 3044*0035018cSRaymond Chen } 3045*0035018cSRaymond Chen 3046*0035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSING; 3047*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 3048*0035018cSRaymond Chen 3049*0035018cSRaymond Chen /* reset the data interface's altsetting to 0 */ 3050*0035018cSRaymond Chen if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) && 3051*0035018cSRaymond Chen (usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no, 3052*0035018cSRaymond Chen 0, USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS)) { 3053*0035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 3054*0035018cSRaymond Chen "usbecm_close_pipes: reset alternate failed "); 3055*0035018cSRaymond Chen } 3056*0035018cSRaymond Chen 3057*0035018cSRaymond Chen /* Close pipes */ 3058*0035018cSRaymond Chen usb_pipe_reset(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, 3059*0035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, 0); 3060*0035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, 3061*0035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, 0); 3062*0035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph, 3063*0035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, 0); 3064*0035018cSRaymond Chen 3065*0035018cSRaymond Chen if (ecmp->ecm_intr_ph != NULL) { 3066*0035018cSRaymond Chen usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph, 3067*0035018cSRaymond Chen USB_FLAGS_SLEEP); 3068*0035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_intr_ph, 3069*0035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, 0); 3070*0035018cSRaymond Chen } 3071*0035018cSRaymond Chen 3072*0035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 3073*0035018cSRaymond Chen /* Reset the status of pipes to closed */ 3074*0035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSED; 3075*0035018cSRaymond Chen ecmp->ecm_bulkin_ph = NULL; 3076*0035018cSRaymond Chen ecmp->ecm_bulkout_state = USBECM_PIPE_CLOSED; 3077*0035018cSRaymond Chen ecmp->ecm_bulkout_ph = NULL; 3078*0035018cSRaymond Chen if (ecmp->ecm_intr_ph != NULL) { 3079*0035018cSRaymond Chen ecmp->ecm_intr_state = USBECM_PIPE_CLOSED; 3080*0035018cSRaymond Chen ecmp->ecm_intr_ph = NULL; 3081*0035018cSRaymond Chen } 3082*0035018cSRaymond Chen 3083*0035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 3084*0035018cSRaymond Chen 3085*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, 3086*0035018cSRaymond Chen "usbsecm_close_pipes: pipes have been closed."); 3087*0035018cSRaymond Chen } 3088*0035018cSRaymond Chen 3089*0035018cSRaymond Chen 3090*0035018cSRaymond Chen static int 3091*0035018cSRaymond Chen usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request, 3092*0035018cSRaymond Chen uint16_t value, mblk_t **data) 3093*0035018cSRaymond Chen { 3094*0035018cSRaymond Chen usb_ctrl_setup_t setup; 3095*0035018cSRaymond Chen usb_cb_flags_t cb_flags; 3096*0035018cSRaymond Chen usb_cr_t cr; 3097*0035018cSRaymond Chen int rval; 3098*0035018cSRaymond Chen 3099*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh, 3100*0035018cSRaymond Chen "usbecm_ctrl_write: "); 3101*0035018cSRaymond Chen 3102*0035018cSRaymond Chen /* initialize the control request. */ 3103*0035018cSRaymond Chen setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV | 3104*0035018cSRaymond Chen USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; 3105*0035018cSRaymond Chen setup.bRequest = request; 3106*0035018cSRaymond Chen setup.wValue = value; 3107*0035018cSRaymond Chen setup.wIndex = ecmp->ecm_ctrl_if_no; 3108*0035018cSRaymond Chen setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0; 3109*0035018cSRaymond Chen setup.attrs = 0; 3110*0035018cSRaymond Chen 3111*0035018cSRaymond Chen rval = usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data, 3112*0035018cSRaymond Chen &cr, &cb_flags, 0); 3113*0035018cSRaymond Chen 3114*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh, 3115*0035018cSRaymond Chen "usbecm_ctrl_write: rval = %d", rval); 3116*0035018cSRaymond Chen 3117*0035018cSRaymond Chen return (rval); 3118*0035018cSRaymond Chen } 3119*0035018cSRaymond Chen 3120*0035018cSRaymond Chen static int 3121*0035018cSRaymond Chen usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request, 3122*0035018cSRaymond Chen uint16_t value, mblk_t **data, int len) 3123*0035018cSRaymond Chen { 3124*0035018cSRaymond Chen usb_ctrl_setup_t setup; 3125*0035018cSRaymond Chen usb_cb_flags_t cb_flags; 3126*0035018cSRaymond Chen usb_cr_t cr; 3127*0035018cSRaymond Chen 3128*0035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh, 3129*0035018cSRaymond Chen "usbecm_ctrl_read: "); 3130*0035018cSRaymond Chen 3131*0035018cSRaymond Chen /* initialize the control request. */ 3132*0035018cSRaymond Chen setup.bmRequestType = USB_DEV_REQ_DEV_TO_HOST | 3133*0035018cSRaymond Chen USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; 3134*0035018cSRaymond Chen setup.bRequest = request; 3135*0035018cSRaymond Chen setup.wValue = value; 3136*0035018cSRaymond Chen setup.wIndex = ecmp->ecm_ctrl_if_no; 3137*0035018cSRaymond Chen setup.wLength = (uint16_t)len; 3138*0035018cSRaymond Chen setup.attrs = 0; 3139*0035018cSRaymond Chen 3140*0035018cSRaymond Chen return (usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data, 3141*0035018cSRaymond Chen &cr, &cb_flags, 0)); 3142*0035018cSRaymond Chen } 3143*0035018cSRaymond Chen 3144*0035018cSRaymond Chen /* Get specific statistic data from device */ 3145*0035018cSRaymond Chen static int 3146*0035018cSRaymond Chen usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs, uint32_t *stat_data) 3147*0035018cSRaymond Chen { 3148*0035018cSRaymond Chen mblk_t *data = NULL; 3149*0035018cSRaymond Chen uint32_t stat; 3150*0035018cSRaymond Chen 3151*0035018cSRaymond Chen /* first check to see if this stat is collected by device */ 3152*0035018cSRaymond Chen if ((ecmp->ecm_compatibility == B_TRUE) && 3153*0035018cSRaymond Chen (ecmp->ecm_desc.bmEthernetStatistics & ECM_STAT_CAP_MASK(fs))) { 3154*0035018cSRaymond Chen if (usbecm_ctrl_read(ecmp, CDC_ECM_GET_ETH_STAT, 3155*0035018cSRaymond Chen ecmp->ecm_ctrl_if_no, &data, 4) != USB_SUCCESS) { 3156*0035018cSRaymond Chen 3157*0035018cSRaymond Chen return (USB_FAILURE); 3158*0035018cSRaymond Chen } 3159*0035018cSRaymond Chen stat = (data->b_rptr[3] << 24) | (data->b_rptr[2] << 16) | 3160*0035018cSRaymond Chen (data->b_rptr[1] << 8) | (data->b_rptr[0]); 3161*0035018cSRaymond Chen *stat_data = stat; 3162*0035018cSRaymond Chen 3163*0035018cSRaymond Chen freemsg(data); 3164*0035018cSRaymond Chen 3165*0035018cSRaymond Chen return (USB_SUCCESS); 3166*0035018cSRaymond Chen } 3167*0035018cSRaymond Chen 3168*0035018cSRaymond Chen return (USB_FAILURE); 3169*0035018cSRaymond Chen } 3170