10035018cSRaymond Chen /* 20035018cSRaymond Chen * CDDL HEADER START 30035018cSRaymond Chen * 40035018cSRaymond Chen * The contents of this file are subject to the terms of the 50035018cSRaymond Chen * Common Development and Distribution License (the "License"). 60035018cSRaymond Chen * You may not use this file except in compliance with the License. 70035018cSRaymond Chen * 80035018cSRaymond Chen * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90035018cSRaymond Chen * or http://www.opensolaris.org/os/licensing. 100035018cSRaymond Chen * See the License for the specific language governing permissions 110035018cSRaymond Chen * and limitations under the License. 120035018cSRaymond Chen * 130035018cSRaymond Chen * When distributing Covered Code, include this CDDL HEADER in each 140035018cSRaymond Chen * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150035018cSRaymond Chen * If applicable, add the following below this CDDL HEADER, with the 160035018cSRaymond Chen * fields enclosed by brackets "[]" replaced with your own identifying 170035018cSRaymond Chen * information: Portions Copyright [yyyy] [name of copyright owner] 180035018cSRaymond Chen * 190035018cSRaymond Chen * CDDL HEADER END 200035018cSRaymond Chen */ 210035018cSRaymond Chen 220035018cSRaymond Chen /* 230035018cSRaymond Chen * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 240035018cSRaymond Chen * Use is subject to license terms. 250035018cSRaymond Chen */ 260035018cSRaymond Chen 270035018cSRaymond Chen /* 280035018cSRaymond Chen * USB Ethernet Control Model 290035018cSRaymond Chen * 300035018cSRaymond Chen * USB-IF defines three ethernet network related specifications: EEM, 310035018cSRaymond Chen * ECM and NCM. This driver focuses specifically on ECM compatible 320035018cSRaymond Chen * devices. This kind of devices generally have one pair of bulk 330035018cSRaymond Chen * endpoints for in/out packet data and one interrupt endpoint for 340035018cSRaymond Chen * device notification. 350035018cSRaymond Chen * 360035018cSRaymond Chen * Devices which don't report ECM compatibility through descriptors but 370035018cSRaymond Chen * implement the ECM functions may also bind to this driver. This driver 380035018cSRaymond Chen * will try to find at least a bulk in endpoint and a bulk out endpoint 390035018cSRaymond Chen * in this case. If the non-compatible devices use vendor specific data 400035018cSRaymond Chen * format, this driver will not function. 410035018cSRaymond Chen * 420035018cSRaymond Chen * This driver is a normal USBA client driver. It's also a GLDv3 driver, 430035018cSRaymond Chen * which provides the necessary interfaces the GLDv3 framework requires. 440035018cSRaymond Chen * 450035018cSRaymond Chen */ 460035018cSRaymond Chen 470035018cSRaymond Chen #include <sys/types.h> 480035018cSRaymond Chen #include <sys/strsun.h> 490035018cSRaymond Chen #include <sys/ddi.h> 500035018cSRaymond Chen #include <sys/sunddi.h> 510035018cSRaymond Chen #include <sys/byteorder.h> 520035018cSRaymond Chen #include <sys/usb/usba/usbai_version.h> 530035018cSRaymond Chen #include <sys/usb/usba.h> 540035018cSRaymond Chen #include <sys/usb/usba/usba_types.h> 550035018cSRaymond Chen #include <sys/usb/clients/usbcdc/usb_cdc.h> 560035018cSRaymond Chen #include <sys/usb/clients/usbecm/usbecm.h> 570035018cSRaymond Chen #include <sys/mac_provider.h> 580035018cSRaymond Chen #include <sys/strsubr.h> 590035018cSRaymond Chen #include <sys/ethernet.h> 600035018cSRaymond Chen #include <sys/mac_ether.h> /* MAC_PLUGIN_IDENT_ETHER */ 610035018cSRaymond Chen #include <sys/random.h> /* random_get_bytes */ 620035018cSRaymond Chen #include <sys/sdt.h> /* sdt */ 630035018cSRaymond Chen #include <inet/nd.h> 640035018cSRaymond Chen 650035018cSRaymond Chen /* MAC callbacks */ 660035018cSRaymond Chen static int usbecm_m_stat(void *arg, uint_t stat, uint64_t *val); 670035018cSRaymond Chen static int usbecm_m_start(void *arg); 680035018cSRaymond Chen static void usbecm_m_stop(void *arg); 690035018cSRaymond Chen static int usbecm_m_unicst(void *arg, const uint8_t *macaddr); 700035018cSRaymond Chen static int usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m); 710035018cSRaymond Chen static int usbecm_m_promisc(void *arg, boolean_t on); 720035018cSRaymond Chen static void usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp); 730035018cSRaymond Chen static mblk_t *usbecm_m_tx(void *arg, mblk_t *mp); 740035018cSRaymond Chen static int usbecm_m_getprop(void *arg, const char *pr_name, 750035018cSRaymond Chen mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf); 760035018cSRaymond Chen static int usbecm_m_setprop(void *arg, const char *pr_name, 770035018cSRaymond Chen mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf); 780035018cSRaymond Chen 790035018cSRaymond Chen static int usbecm_usb_init(usbecm_state_t *ecmp); 800035018cSRaymond Chen static int usbecm_mac_init(usbecm_state_t *ecmp); 810035018cSRaymond Chen static int usbecm_mac_fini(usbecm_state_t *ecmp); 820035018cSRaymond Chen 830035018cSRaymond Chen 840035018cSRaymond Chen /* utils */ 850035018cSRaymond Chen static void generate_ether_addr(uint8_t *mac_addr); 860035018cSRaymond Chen static int usbecm_rx_start(usbecm_state_t *ecmp); 870035018cSRaymond Chen 880035018cSRaymond Chen static void usbecm_pipe_start_polling(usbecm_state_t *ecmp); 890035018cSRaymond Chen static void usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req); 900035018cSRaymond Chen static void usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req); 910035018cSRaymond Chen static void usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data); 920035018cSRaymond Chen 930035018cSRaymond Chen static int usbecm_reconnect_event_cb(dev_info_t *dip); 940035018cSRaymond Chen static int usbecm_disconnect_event_cb(dev_info_t *dip); 950035018cSRaymond Chen 960035018cSRaymond Chen static int usbecm_open_pipes(usbecm_state_t *ecmp); 970035018cSRaymond Chen static void usbecm_close_pipes(usbecm_state_t *ecmp); 980035018cSRaymond Chen 990035018cSRaymond Chen static int usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request, 1000035018cSRaymond Chen uint16_t value, mblk_t **data, int len); 1010035018cSRaymond Chen static int usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request, 1020035018cSRaymond Chen uint16_t value, mblk_t **data); 1030035018cSRaymond Chen static int usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data); 1040035018cSRaymond Chen static int usbecm_send_zero_data(usbecm_state_t *ecmp); 1050035018cSRaymond Chen static int usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs, 1060035018cSRaymond Chen uint32_t *stat_data); 1070035018cSRaymond Chen 1080035018cSRaymond Chen static int usbecm_create_pm_components(usbecm_state_t *ecmp); 1090035018cSRaymond Chen static void usbecm_destroy_pm_components(usbecm_state_t *ecmp); 1100035018cSRaymond Chen static int usbecm_power(dev_info_t *dip, int comp, int level); 1110035018cSRaymond Chen static void usbecm_pm_set_busy(usbecm_state_t *ecmp); 1120035018cSRaymond Chen static void usbecm_pm_set_idle(usbecm_state_t *ecmp); 1130035018cSRaymond Chen 1140035018cSRaymond Chen static int usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 1150035018cSRaymond Chen static int usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 1160035018cSRaymond Chen 1170035018cSRaymond Chen static int usbecm_suspend(usbecm_state_t *ecmp); 1180035018cSRaymond Chen static int usbecm_resume(usbecm_state_t *ecmp); 1190035018cSRaymond Chen static int usbecm_restore_device_state(usbecm_state_t *ecmp); 1200035018cSRaymond Chen static void usbecm_cleanup(usbecm_state_t *ecmp); 1210035018cSRaymond Chen 1220035018cSRaymond Chen /* Driver identification */ 1230035018cSRaymond Chen static char usbecm_ident[] = "usbecm 1.0"; 1240035018cSRaymond Chen 1250035018cSRaymond Chen /* Global state pointer for managing per-device soft states */ 1260035018cSRaymond Chen void *usbecm_statep; 1270035018cSRaymond Chen 1280035018cSRaymond Chen /* print levels */ 1290035018cSRaymond Chen static uint_t usbecm_errlevel = USB_LOG_L3; 1300035018cSRaymond Chen static uint_t usbecm_errmask = 0xffffffff; 1310035018cSRaymond Chen static uint_t usbecm_instance_debug = (uint_t)-1; 1320035018cSRaymond Chen 1330035018cSRaymond Chen /* 1340035018cSRaymond Chen * to prevent upper layers packet flood from exhausting system 1350035018cSRaymond Chen * resources(USBA does not set limitation of requests on a pipe), 1360035018cSRaymond Chen * we set a upper limit for the transfer queue length. 1370035018cSRaymond Chen */ 1380035018cSRaymond Chen static int usbecm_tx_max = 32; 1390035018cSRaymond Chen 1400035018cSRaymond Chen #define SUN_SP_VENDOR_ID 0x0430 1410035018cSRaymond Chen #define SUN_SP_PRODUCT_ID 0xa4a2 1420035018cSRaymond Chen 1430035018cSRaymond Chen static uint8_t usbecm_broadcast[ETHERADDRL] = { 1440035018cSRaymond Chen 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 1450035018cSRaymond Chen }; 1460035018cSRaymond Chen 1470035018cSRaymond Chen static usb_event_t usbecm_events = { 1480035018cSRaymond Chen usbecm_disconnect_event_cb, 1490035018cSRaymond Chen usbecm_reconnect_event_cb, 1500035018cSRaymond Chen NULL, NULL 1510035018cSRaymond Chen }; 1520035018cSRaymond Chen 1530035018cSRaymond Chen #define ECM_DS_OP_VALID(op) ((ecmp->ecm_ds_ops) && (ecmp->ecm_ds_ops->op)) 1540035018cSRaymond Chen 1550035018cSRaymond Chen /* 1560035018cSRaymond Chen * MAC Call Back entries 1570035018cSRaymond Chen */ 1580035018cSRaymond Chen static mac_callbacks_t usbecm_m_callbacks = { 1590035018cSRaymond Chen MC_IOCTL | MC_SETPROP | MC_GETPROP, 1600035018cSRaymond Chen usbecm_m_stat, /* Get the value of a statistic */ 1610035018cSRaymond Chen usbecm_m_start, /* Start the device */ 1620035018cSRaymond Chen usbecm_m_stop, /* Stop the device */ 1630035018cSRaymond Chen usbecm_m_promisc, /* Enable or disable promiscuous mode */ 1640035018cSRaymond Chen usbecm_m_multicst, /* Enable or disable a multicast addr */ 1650035018cSRaymond Chen usbecm_m_unicst, /* Set the unicast MAC address */ 1660035018cSRaymond Chen usbecm_m_tx, /* Transmit a packet */ 1670035018cSRaymond Chen NULL, 1680035018cSRaymond Chen usbecm_m_ioctl, /* Process an unknown ioctl */ 1690035018cSRaymond Chen NULL, /* mc_getcapab */ 1700035018cSRaymond Chen NULL, /* mc_open */ 1710035018cSRaymond Chen NULL, /* mc_close */ 1720035018cSRaymond Chen usbecm_m_setprop, /* mc_setprop */ 1730035018cSRaymond Chen usbecm_m_getprop, /* mc_getprop */ 1740035018cSRaymond Chen NULL 1750035018cSRaymond Chen }; 1760035018cSRaymond Chen 1770035018cSRaymond Chen 1780035018cSRaymond Chen /* 1790035018cSRaymond Chen * Module Loading Data & Entry Points 1800035018cSRaymond Chen * Can't use DDI_DEFINE_STREAM_OPS, since it does 1810035018cSRaymond Chen * not provide devo_power entry. 1820035018cSRaymond Chen */ 1830035018cSRaymond Chen static struct cb_ops cb_usbecm = { 1840035018cSRaymond Chen nulldev, /* cb_open */ 1850035018cSRaymond Chen nulldev, /* cb_close */ 1860035018cSRaymond Chen nodev, /* cb_strategy */ 1870035018cSRaymond Chen nodev, /* cb_print */ 1880035018cSRaymond Chen nodev, /* cb_dump */ 1890035018cSRaymond Chen nodev, /* cb_read */ 1900035018cSRaymond Chen nodev, /* cb_write */ 1910035018cSRaymond Chen nodev, /* cb_ioctl */ 1920035018cSRaymond Chen nodev, /* cb_devmap */ 1930035018cSRaymond Chen nodev, /* cb_mmap */ 1940035018cSRaymond Chen nodev, /* cb_segmap */ 1950035018cSRaymond Chen nochpoll, /* cb_chpoll */ 1960035018cSRaymond Chen ddi_prop_op, /* cb_prop_op */ 1970035018cSRaymond Chen NULL, /* cb_stream */ 1980035018cSRaymond Chen D_MP, /* cb_flag */ 1990035018cSRaymond Chen CB_REV, /* cb_rev */ 2000035018cSRaymond Chen nodev, /* cb_aread */ 2010035018cSRaymond Chen nodev, /* cb_awrite */ 2020035018cSRaymond Chen }; 2030035018cSRaymond Chen 2040035018cSRaymond Chen static struct dev_ops usbecm_devops = { 2050035018cSRaymond Chen DEVO_REV, /* devo_rev */ 2060035018cSRaymond Chen 0, /* devo_refcnt */ 2070035018cSRaymond Chen NULL, /* devo_getinfo */ 2080035018cSRaymond Chen nulldev, /* devo_identify */ 2090035018cSRaymond Chen nulldev, /* devo_probe */ 2100035018cSRaymond Chen usbecm_attach, /* devo_attach */ 2110035018cSRaymond Chen usbecm_detach, /* devo_detach */ 2120035018cSRaymond Chen nodev, /* devo_reset */ 2130035018cSRaymond Chen &(cb_usbecm), /* devo_cb_ops */ 2140035018cSRaymond Chen (struct bus_ops *)NULL, /* devo_bus_ops */ 2150035018cSRaymond Chen usbecm_power, /* devo_power */ 2160035018cSRaymond Chen ddi_quiesce_not_needed /* devo_quiesce */ 2170035018cSRaymond Chen }; 2180035018cSRaymond Chen 2190035018cSRaymond Chen static struct modldrv usbecm_modldrv = { 2200035018cSRaymond Chen &mod_driverops, /* drv_modops */ 2210035018cSRaymond Chen usbecm_ident, /* drv_linkinfo */ 2220035018cSRaymond Chen &usbecm_devops /* drv_dev_ops */ 2230035018cSRaymond Chen }; 2240035018cSRaymond Chen 2250035018cSRaymond Chen static struct modlinkage usbecm_ml = { 2260035018cSRaymond Chen MODREV_1, /* ml_rev */ 2270035018cSRaymond Chen &usbecm_modldrv, NULL /* ml_linkage */ 2280035018cSRaymond Chen }; 2290035018cSRaymond Chen 2300035018cSRaymond Chen 2310035018cSRaymond Chen /* 2320035018cSRaymond Chen * Device operations 2330035018cSRaymond Chen */ 2340035018cSRaymond Chen /* 2350035018cSRaymond Chen * Binding the driver to a device. 2360035018cSRaymond Chen * 2370035018cSRaymond Chen * Concurrency: Until usbecm_attach() returns with success, 2380035018cSRaymond Chen * the only other entry point that can be executed is getinfo(). 2390035018cSRaymond Chen * Thus no locking here yet. 2400035018cSRaymond Chen */ 2410035018cSRaymond Chen static int 2420035018cSRaymond Chen usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 2430035018cSRaymond Chen { 2440035018cSRaymond Chen char strbuf[32]; 2450035018cSRaymond Chen int instance; 2460035018cSRaymond Chen int err; 2470035018cSRaymond Chen usbecm_state_t *ecmp = NULL; 2480035018cSRaymond Chen 2490035018cSRaymond Chen switch (cmd) { 2500035018cSRaymond Chen case DDI_ATTACH: 2510035018cSRaymond Chen break; 2520035018cSRaymond Chen 2530035018cSRaymond Chen case DDI_RESUME: 2540035018cSRaymond Chen ecmp = (usbecm_state_t *)ddi_get_soft_state(usbecm_statep, 2550035018cSRaymond Chen ddi_get_instance(dip)); 2560035018cSRaymond Chen 2570035018cSRaymond Chen (void) usbecm_resume(ecmp); 2580035018cSRaymond Chen 2590035018cSRaymond Chen return (DDI_SUCCESS); 2600035018cSRaymond Chen 2610035018cSRaymond Chen default: 2620035018cSRaymond Chen return (DDI_FAILURE); 2630035018cSRaymond Chen } 2640035018cSRaymond Chen 2650035018cSRaymond Chen instance = ddi_get_instance(dip); 2660035018cSRaymond Chen 2670035018cSRaymond Chen if (ddi_soft_state_zalloc(usbecm_statep, instance) == DDI_SUCCESS) { 2680035018cSRaymond Chen ecmp = ddi_get_soft_state(usbecm_statep, instance); 2690035018cSRaymond Chen } 2700035018cSRaymond Chen if (ecmp == NULL) { 2710035018cSRaymond Chen cmn_err(CE_WARN, "usbecm_attach: fail to get soft state"); 2720035018cSRaymond Chen 2730035018cSRaymond Chen return (DDI_FAILURE); 2740035018cSRaymond Chen } 2750035018cSRaymond Chen 2760035018cSRaymond Chen ecmp->ecm_dip = dip; 2770035018cSRaymond Chen 2780035018cSRaymond Chen ecmp->ecm_lh = usb_alloc_log_hdl(ecmp->ecm_dip, "usbecm", 2790035018cSRaymond Chen &usbecm_errlevel, &usbecm_errmask, &usbecm_instance_debug, 0); 2800035018cSRaymond Chen 2810035018cSRaymond Chen if (usbecm_usb_init(ecmp) != USB_SUCCESS) { 2820035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2830035018cSRaymond Chen "usbecm_attach: failed to init usb"); 2840035018cSRaymond Chen 2850035018cSRaymond Chen goto fail; 2860035018cSRaymond Chen } 2870035018cSRaymond Chen 2880035018cSRaymond Chen if (ECM_DS_OP_VALID(ecm_ds_init)) { 2890035018cSRaymond Chen if (ecmp->ecm_ds_ops->ecm_ds_init(ecmp) != USB_SUCCESS) { 2900035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2910035018cSRaymond Chen "usbecm_attach: failed to init DS"); 2920035018cSRaymond Chen 2930035018cSRaymond Chen goto fail; 2940035018cSRaymond Chen } 2950035018cSRaymond Chen } 2960035018cSRaymond Chen 2970035018cSRaymond Chen if (usbecm_mac_init(ecmp) != DDI_SUCCESS) { 2980035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 2990035018cSRaymond Chen "usbecm_attach: failed to init mac"); 3000035018cSRaymond Chen 3010035018cSRaymond Chen goto fail; 3020035018cSRaymond Chen } 3030035018cSRaymond Chen ecmp->ecm_init_flags |= USBECM_INIT_MAC; 3040035018cSRaymond Chen 3050035018cSRaymond Chen /* 3060035018cSRaymond Chen * Create minor node of type usb_net. Not necessary to create 3070035018cSRaymond Chen * DDI_NT_NET since it's created in mac_register(). Otherwise, 3080035018cSRaymond Chen * system will panic. 3090035018cSRaymond Chen */ 3100035018cSRaymond Chen (void) snprintf(strbuf, sizeof (strbuf), "usbecm%d", instance); 3110035018cSRaymond Chen err = ddi_create_minor_node(dip, strbuf, S_IFCHR, 3120035018cSRaymond Chen instance + 1, "usb_net", 0); 3130035018cSRaymond Chen if (err != DDI_SUCCESS) { 3140035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 3150035018cSRaymond Chen "failed to create minor node"); 3160035018cSRaymond Chen 3170035018cSRaymond Chen goto fail; 3180035018cSRaymond Chen } 3190035018cSRaymond Chen 3200035018cSRaymond Chen /* always busy. May change to a more precise PM in future */ 3210035018cSRaymond Chen usbecm_pm_set_busy(ecmp); 3220035018cSRaymond Chen 3230035018cSRaymond Chen ddi_report_dev(dip); 3240035018cSRaymond Chen 3250035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, 3260035018cSRaymond Chen "usbecm_attach: succeed!"); 3270035018cSRaymond Chen 3280035018cSRaymond Chen return (DDI_SUCCESS); 3290035018cSRaymond Chen 3300035018cSRaymond Chen fail: 3310035018cSRaymond Chen USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh, 3320035018cSRaymond Chen "usbecm_attach: Attach fail"); 3330035018cSRaymond Chen 3340035018cSRaymond Chen usbecm_cleanup(ecmp); 3350035018cSRaymond Chen ddi_prop_remove_all(dip); 3360035018cSRaymond Chen ddi_soft_state_free(usbecm_statep, instance); 3370035018cSRaymond Chen 3380035018cSRaymond Chen return (DDI_FAILURE); 3390035018cSRaymond Chen 3400035018cSRaymond Chen } 3410035018cSRaymond Chen 3420035018cSRaymond Chen 3430035018cSRaymond Chen /* 3440035018cSRaymond Chen * Detach the driver from a device. 3450035018cSRaymond Chen * 3460035018cSRaymond Chen * Concurrency: Will be called only after a successful attach 3470035018cSRaymond Chen * (and not concurrently). 3480035018cSRaymond Chen */ 3490035018cSRaymond Chen static int 3500035018cSRaymond Chen usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 3510035018cSRaymond Chen { 3520035018cSRaymond Chen usbecm_state_t *ecmp = NULL; 3530035018cSRaymond Chen int instance; 3540035018cSRaymond Chen 3550035018cSRaymond Chen instance = ddi_get_instance(dip); 3560035018cSRaymond Chen ecmp = ddi_get_soft_state(usbecm_statep, instance); 3570035018cSRaymond Chen ASSERT(ecmp != NULL); 3580035018cSRaymond Chen 3590035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, 3600035018cSRaymond Chen "usbecm_detach: entry "); 3610035018cSRaymond Chen 3620035018cSRaymond Chen switch (cmd) { 3630035018cSRaymond Chen case DDI_DETACH: 3640035018cSRaymond Chen break; 3650035018cSRaymond Chen 3660035018cSRaymond Chen case DDI_SUSPEND: 3670035018cSRaymond Chen 3680035018cSRaymond Chen return (usbecm_suspend(ecmp)); 3690035018cSRaymond Chen 3700035018cSRaymond Chen default: 3710035018cSRaymond Chen return (DDI_FAILURE); 3720035018cSRaymond Chen } 3730035018cSRaymond Chen 3740035018cSRaymond Chen usbecm_pm_set_idle(ecmp); 3750035018cSRaymond Chen 3760035018cSRaymond Chen if (ECM_DS_OP_VALID(ecm_ds_fini)) { 3770035018cSRaymond Chen if (ecmp->ecm_ds_ops->ecm_ds_fini(ecmp) != USB_SUCCESS) { 3780035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 3790035018cSRaymond Chen "usbecm_detach: deinitialize DS fail!"); 3800035018cSRaymond Chen 3810035018cSRaymond Chen return (DDI_FAILURE); 3820035018cSRaymond Chen } 3830035018cSRaymond Chen } 3840035018cSRaymond Chen 3850035018cSRaymond Chen if (usbecm_mac_fini(ecmp) != 0) { 3860035018cSRaymond Chen 3870035018cSRaymond Chen return (DDI_FAILURE); 3880035018cSRaymond Chen } 3890035018cSRaymond Chen 3900035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, 3910035018cSRaymond Chen "usbecm_detach: exit"); 3920035018cSRaymond Chen 3930035018cSRaymond Chen usbecm_cleanup(ecmp); 3940035018cSRaymond Chen ddi_soft_state_free(usbecm_statep, instance); 3950035018cSRaymond Chen 3960035018cSRaymond Chen return (DDI_SUCCESS); 3970035018cSRaymond Chen } 3980035018cSRaymond Chen 3990035018cSRaymond Chen 4000035018cSRaymond Chen /* 4010035018cSRaymond Chen * Mac Call Back functions 4020035018cSRaymond Chen */ 4030035018cSRaymond Chen 4040035018cSRaymond Chen /* 4050035018cSRaymond Chen * Read device statistic information. 4060035018cSRaymond Chen */ 4070035018cSRaymond Chen static int 4080035018cSRaymond Chen usbecm_m_stat(void *arg, uint_t stat, uint64_t *val) 4090035018cSRaymond Chen { 4100035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 4110035018cSRaymond Chen uint32_t stats; 4120035018cSRaymond Chen int rval; 4130035018cSRaymond Chen uint32_t fs; 4140035018cSRaymond Chen 4150035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 4160035018cSRaymond Chen "usbecm_m_stat: entry, stat=%d", stat); 4170035018cSRaymond Chen 4180035018cSRaymond Chen /* 4190035018cSRaymond Chen * Some of the stats are MII specific. We try to 4200035018cSRaymond Chen * resolve all the statistics we understand. If 4210035018cSRaymond Chen * the usb device can't provide it, return ENOTSUP. 4220035018cSRaymond Chen */ 4230035018cSRaymond Chen switch (stat) { 4240035018cSRaymond Chen case MAC_STAT_IFSPEED: 4250035018cSRaymond Chen /* return link speed */ 4260035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 4270035018cSRaymond Chen if (ecmp->ecm_stat.es_downspeed) { 4280035018cSRaymond Chen *val = ecmp->ecm_stat.es_downspeed; 4290035018cSRaymond Chen } else { 4300035018cSRaymond Chen *val = 10 * 1000000ull; /* set a default value */ 4310035018cSRaymond Chen } 4320035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 4330035018cSRaymond Chen 4340035018cSRaymond Chen return (0); 4350035018cSRaymond Chen case ETHER_STAT_LINK_DUPLEX: 4360035018cSRaymond Chen *val = LINK_DUPLEX_FULL; 4370035018cSRaymond Chen 4380035018cSRaymond Chen return (0); 4390035018cSRaymond Chen 4400035018cSRaymond Chen case ETHER_STAT_SQE_ERRORS: 4410035018cSRaymond Chen *val = 0; 4420035018cSRaymond Chen 4430035018cSRaymond Chen return (0); 4440035018cSRaymond Chen 4450035018cSRaymond Chen /* Map MAC/Ether stats to ECM statistics */ 4460035018cSRaymond Chen case MAC_STAT_NORCVBUF: 4470035018cSRaymond Chen fs = ECM_RCV_NO_BUFFER; 4480035018cSRaymond Chen 4490035018cSRaymond Chen break; 4500035018cSRaymond Chen case MAC_STAT_NOXMTBUF: 4510035018cSRaymond Chen fs = ECM_XMIT_ERROR; 4520035018cSRaymond Chen 4530035018cSRaymond Chen break; 4540035018cSRaymond Chen case MAC_STAT_IERRORS: 4550035018cSRaymond Chen fs = ECM_RCV_ERROR; 4560035018cSRaymond Chen 4570035018cSRaymond Chen break; 4580035018cSRaymond Chen case MAC_STAT_OERRORS: 4590035018cSRaymond Chen fs = ECM_XMIT_ERROR; 4600035018cSRaymond Chen 4610035018cSRaymond Chen break; 4620035018cSRaymond Chen case MAC_STAT_RBYTES: 4630035018cSRaymond Chen fs = ECM_DIRECTED_BYTES_RCV; 4640035018cSRaymond Chen 4650035018cSRaymond Chen break; 4660035018cSRaymond Chen case MAC_STAT_IPACKETS: 4670035018cSRaymond Chen fs = ECM_RCV_OK; /* frames */ 4680035018cSRaymond Chen 4690035018cSRaymond Chen break; 4700035018cSRaymond Chen case MAC_STAT_OBYTES: 4710035018cSRaymond Chen fs = ECM_DIRECTED_BYTES_XMIT; 4720035018cSRaymond Chen 4730035018cSRaymond Chen break; 4740035018cSRaymond Chen case MAC_STAT_OPACKETS: 4750035018cSRaymond Chen fs = ECM_XMIT_OK; /* frames */ 4760035018cSRaymond Chen 4770035018cSRaymond Chen break; 4780035018cSRaymond Chen case MAC_STAT_MULTIRCV: 4790035018cSRaymond Chen fs = ECM_MULTICAST_FRAMES_RCV; 4800035018cSRaymond Chen 4810035018cSRaymond Chen break; 4820035018cSRaymond Chen case MAC_STAT_BRDCSTRCV: 4830035018cSRaymond Chen fs = ECM_BROADCAST_FRAMES_RCV; 4840035018cSRaymond Chen 4850035018cSRaymond Chen break; 4860035018cSRaymond Chen case MAC_STAT_MULTIXMT: 4870035018cSRaymond Chen fs = ECM_MULTICAST_FRAMES_XMIT; 4880035018cSRaymond Chen 4890035018cSRaymond Chen break; 4900035018cSRaymond Chen case MAC_STAT_BRDCSTXMT: 4910035018cSRaymond Chen fs = ECM_BROADCAST_FRAMES_XMIT; 4920035018cSRaymond Chen 4930035018cSRaymond Chen break; 4940035018cSRaymond Chen case MAC_STAT_COLLISIONS: 4950035018cSRaymond Chen fs = ECM_XMIT_MAX_COLLISIONS; 4960035018cSRaymond Chen 4970035018cSRaymond Chen break; 4980035018cSRaymond Chen case MAC_STAT_OVERFLOWS: 4990035018cSRaymond Chen fs = ECM_RCV_OVERRUN; 5000035018cSRaymond Chen 5010035018cSRaymond Chen break; 5020035018cSRaymond Chen case MAC_STAT_UNDERFLOWS: 5030035018cSRaymond Chen fs = ECM_XMIT_UNDERRUN; 5040035018cSRaymond Chen 5050035018cSRaymond Chen break; 5060035018cSRaymond Chen case ETHER_STAT_FCS_ERRORS: 5070035018cSRaymond Chen fs = ECM_RCV_CRC_ERROR; 5080035018cSRaymond Chen 5090035018cSRaymond Chen break; 5100035018cSRaymond Chen case ETHER_STAT_ALIGN_ERRORS: 5110035018cSRaymond Chen fs = ECM_RCV_ERROR_ALIGNMENT; 5120035018cSRaymond Chen 5130035018cSRaymond Chen break; 5140035018cSRaymond Chen case ETHER_STAT_DEFER_XMTS: 5150035018cSRaymond Chen fs = ECM_XMIT_DEFERRED; 5160035018cSRaymond Chen 5170035018cSRaymond Chen break; 5180035018cSRaymond Chen case ETHER_STAT_FIRST_COLLISIONS: 5190035018cSRaymond Chen fs = ECM_XMIT_ONE_COLLISION; 5200035018cSRaymond Chen 5210035018cSRaymond Chen break; 5220035018cSRaymond Chen case ETHER_STAT_MULTI_COLLISIONS: 5230035018cSRaymond Chen fs = ECM_XMIT_MORE_COLLISIONS; 5240035018cSRaymond Chen 5250035018cSRaymond Chen break; 5260035018cSRaymond Chen case ETHER_STAT_TX_LATE_COLLISIONS: 5270035018cSRaymond Chen fs = ECM_XMIT_LATE_COLLISIONS; 5280035018cSRaymond Chen 5290035018cSRaymond Chen break; 5300035018cSRaymond Chen 5310035018cSRaymond Chen default: 5320035018cSRaymond Chen return (ENOTSUP); 5330035018cSRaymond Chen } 5340035018cSRaymond Chen 5350035018cSRaymond Chen /* 5360035018cSRaymond Chen * we need to access device to get required stats, 5370035018cSRaymond Chen * so check device state first 5380035018cSRaymond Chen */ 5390035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 5400035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 5410035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 5420035018cSRaymond Chen "usbecm_m_stat: device not ONLINE"); 5430035018cSRaymond Chen 5440035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 5450035018cSRaymond Chen 5460035018cSRaymond Chen return (EIO); 5470035018cSRaymond Chen } 5480035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 5490035018cSRaymond Chen 5500035018cSRaymond Chen rval = usbecm_get_statistics(ecmp, 5510035018cSRaymond Chen ECM_STAT_SELECTOR(fs), &stats); 5520035018cSRaymond Chen if (rval != USB_SUCCESS) { 5530035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 5540035018cSRaymond Chen switch (stat) { 5550035018cSRaymond Chen case MAC_STAT_IERRORS: 5560035018cSRaymond Chen *val = ecmp->ecm_stat.es_ierrors; 5570035018cSRaymond Chen 5580035018cSRaymond Chen break; 5590035018cSRaymond Chen case MAC_STAT_OERRORS: 5600035018cSRaymond Chen *val = ecmp->ecm_stat.es_oerrors; 5610035018cSRaymond Chen 5620035018cSRaymond Chen break; 5630035018cSRaymond Chen case MAC_STAT_RBYTES: 5640035018cSRaymond Chen *val = ecmp->ecm_stat.es_ibytes; 5650035018cSRaymond Chen 5660035018cSRaymond Chen break; 5670035018cSRaymond Chen case MAC_STAT_IPACKETS: 5680035018cSRaymond Chen *val = ecmp->ecm_stat.es_ipackets; 5690035018cSRaymond Chen 5700035018cSRaymond Chen break; 5710035018cSRaymond Chen case MAC_STAT_OBYTES: 5720035018cSRaymond Chen *val = ecmp->ecm_stat.es_obytes; 5730035018cSRaymond Chen 5740035018cSRaymond Chen break; 5750035018cSRaymond Chen case MAC_STAT_OPACKETS: 5760035018cSRaymond Chen *val = ecmp->ecm_stat.es_opackets; 5770035018cSRaymond Chen 5780035018cSRaymond Chen break; 5790035018cSRaymond Chen case MAC_STAT_MULTIRCV: 5800035018cSRaymond Chen *val = ecmp->ecm_stat.es_multircv; 5810035018cSRaymond Chen 5820035018cSRaymond Chen break; 5830035018cSRaymond Chen case MAC_STAT_MULTIXMT: 5840035018cSRaymond Chen *val = ecmp->ecm_stat.es_multixmt; 5850035018cSRaymond Chen 5860035018cSRaymond Chen break; 5870035018cSRaymond Chen case MAC_STAT_BRDCSTRCV: 5880035018cSRaymond Chen *val = ecmp->ecm_stat.es_brdcstrcv; 5890035018cSRaymond Chen 5900035018cSRaymond Chen break; 5910035018cSRaymond Chen case MAC_STAT_BRDCSTXMT: 5920035018cSRaymond Chen *val = ecmp->ecm_stat.es_brdcstxmt; 5930035018cSRaymond Chen 5940035018cSRaymond Chen break; 5950035018cSRaymond Chen case ETHER_STAT_MACXMT_ERRORS: 5960035018cSRaymond Chen *val = ecmp->ecm_stat.es_macxmt_err; 5970035018cSRaymond Chen break; 5980035018cSRaymond Chen default: 5990035018cSRaymond Chen *val = 0; 6000035018cSRaymond Chen 6010035018cSRaymond Chen break; 6020035018cSRaymond Chen } 6030035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 6040035018cSRaymond Chen } else { 6050035018cSRaymond Chen *val = stats; 6060035018cSRaymond Chen } 6070035018cSRaymond Chen 6080035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 6090035018cSRaymond Chen "usbecm_m_stat: end"); 6100035018cSRaymond Chen 6110035018cSRaymond Chen return (0); 6120035018cSRaymond Chen } 6130035018cSRaymond Chen 6140035018cSRaymond Chen 6150035018cSRaymond Chen /* 6160035018cSRaymond Chen * Start the device: 6170035018cSRaymond Chen * - Set proper altsettings of the data interface 6180035018cSRaymond Chen * - Open status and data endpoints 6190035018cSRaymond Chen * - Start status polling 6200035018cSRaymond Chen * - Get bulk-in ep ready to receive data from ethernet 6210035018cSRaymond Chen * 6220035018cSRaymond Chen * Concurrency: Presumably fully concurrent, must lock. 6230035018cSRaymond Chen */ 6240035018cSRaymond Chen static int 6250035018cSRaymond Chen usbecm_m_start(void *arg) 6260035018cSRaymond Chen { 6270035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 6280035018cSRaymond Chen int rval; 6290035018cSRaymond Chen 6300035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 6310035018cSRaymond Chen "usbecm_m_start: entry"); 6320035018cSRaymond Chen 6330035018cSRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 6340035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 6350035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 6360035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 6370035018cSRaymond Chen "usbecm_m_start: device not online"); 6380035018cSRaymond Chen rval = ENODEV; 6390035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 6400035018cSRaymond Chen 6410035018cSRaymond Chen goto fail; 6420035018cSRaymond Chen } 6430035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 6440035018cSRaymond Chen 6450035018cSRaymond Chen if (usbecm_open_pipes(ecmp) != USB_SUCCESS) { 6460035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 6470035018cSRaymond Chen "usbecm_m_start: open pipes fail"); 6480035018cSRaymond Chen rval = EIO; 6490035018cSRaymond Chen 6500035018cSRaymond Chen goto fail; 6510035018cSRaymond Chen } 6520035018cSRaymond Chen 6530035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 6540035018cSRaymond Chen if (usbecm_rx_start(ecmp) != USB_SUCCESS) { 6550035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 6560035018cSRaymond Chen "usbecm_m_start: fail to start_rx"); 6570035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 6580035018cSRaymond Chen rval = EIO; 6590035018cSRaymond Chen 6600035018cSRaymond Chen goto fail; 6610035018cSRaymond Chen } 6620035018cSRaymond Chen ecmp->ecm_mac_state = USBECM_MAC_STARTED; 6630035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 6640035018cSRaymond Chen 6650035018cSRaymond Chen /* set the device to receive all multicast/broadcast pkts */ 6660035018cSRaymond Chen rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, 6670035018cSRaymond Chen CDC_ECM_PKT_TYPE_DIRECTED | CDC_ECM_PKT_TYPE_ALL_MCAST | 6680035018cSRaymond Chen CDC_ECM_PKT_TYPE_BCAST, NULL); 6690035018cSRaymond Chen if (rval != USB_SUCCESS) { 6700035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh, 6710035018cSRaymond Chen "usbecm_m_start: set packet filters fail," 6720035018cSRaymond Chen " rval=%d, continue", rval); 6730035018cSRaymond Chen } 6740035018cSRaymond Chen 6750035018cSRaymond Chen if (ECM_DS_OP_VALID(ecm_ds_start)) { 6760035018cSRaymond Chen if (ecmp->ecm_ds_ops->ecm_ds_start(ecmp) != USB_SUCCESS) { 6770035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 6780035018cSRaymond Chen "usbecm_m_start: Can't start hardware"); 6790035018cSRaymond Chen 6800035018cSRaymond Chen goto fail; 6810035018cSRaymond Chen } 6820035018cSRaymond Chen } 6830035018cSRaymond Chen 6840035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 6850035018cSRaymond Chen 6860035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 6870035018cSRaymond Chen "usbecm_m_start: end"); 6880035018cSRaymond Chen 6890035018cSRaymond Chen /* 6900035018cSRaymond Chen * To mark the link as RUNNING. 6910035018cSRaymond Chen * 6920035018cSRaymond Chen * ECM spec doesn't provide a way for host to get the status 6930035018cSRaymond Chen * of the physical link initiatively. Only the device can 6940035018cSRaymond Chen * report the link state through interrupt endpoints. 6950035018cSRaymond Chen */ 6960035018cSRaymond Chen mac_link_update(ecmp->ecm_mh, LINK_STATE_UP); 6970035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 6980035018cSRaymond Chen ecmp->ecm_stat.es_linkstate = LINK_STATE_UP; 6990035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 7000035018cSRaymond Chen 7010035018cSRaymond Chen return (DDI_SUCCESS); 7020035018cSRaymond Chen fail: 7030035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 7040035018cSRaymond Chen 7050035018cSRaymond Chen return (rval); 7060035018cSRaymond Chen } 7070035018cSRaymond Chen 7080035018cSRaymond Chen /* 7090035018cSRaymond Chen * Stop the device. 7100035018cSRaymond Chen */ 7110035018cSRaymond Chen static void 7120035018cSRaymond Chen usbecm_m_stop(void *arg) 7130035018cSRaymond Chen { 7140035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 7150035018cSRaymond Chen 7160035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 7170035018cSRaymond Chen "usbecm_m_stop: entry"); 7180035018cSRaymond Chen 719*3db80ed2SRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 7200035018cSRaymond Chen if (ECM_DS_OP_VALID(ecm_ds_stop)) { 7210035018cSRaymond Chen if (ecmp->ecm_ds_ops->ecm_ds_stop(ecmp) != USB_SUCCESS) { 7220035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 7230035018cSRaymond Chen "usbecm_m_stop: fail to stop hardware"); 7240035018cSRaymond Chen } 7250035018cSRaymond Chen } 7260035018cSRaymond Chen 7270035018cSRaymond Chen usbecm_close_pipes(ecmp); 7280035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 7290035018cSRaymond Chen 7300035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 7310035018cSRaymond Chen ecmp->ecm_mac_state = USBECM_MAC_STOPPED; 7320035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 7330035018cSRaymond Chen 7340035018cSRaymond Chen mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN); 7350035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 7360035018cSRaymond Chen ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN; 7370035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 7380035018cSRaymond Chen 7390035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 7400035018cSRaymond Chen "usbecm_m_stop: end"); 7410035018cSRaymond Chen } 7420035018cSRaymond Chen 7430035018cSRaymond Chen /* 7440035018cSRaymond Chen * Change the MAC address of the device. 7450035018cSRaymond Chen */ 7460035018cSRaymond Chen /*ARGSUSED*/ 7470035018cSRaymond Chen static int 7480035018cSRaymond Chen usbecm_m_unicst(void *arg, const uint8_t *macaddr) 7490035018cSRaymond Chen { 7500035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 7510035018cSRaymond Chen uint16_t filter; 7520035018cSRaymond Chen int rval; 7530035018cSRaymond Chen 7540035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 7550035018cSRaymond Chen "usbecm_m_unicst: entry"); 7560035018cSRaymond Chen 7570035018cSRaymond Chen /* 7580035018cSRaymond Chen * The device doesn't support to set a different MAC addr. 7590035018cSRaymond Chen * Hence, it's not necessary to stop the device first if 7600035018cSRaymond Chen * the mac addresses are identical. And we just set unicast 7610035018cSRaymond Chen * filter only. 7620035018cSRaymond Chen */ 7630035018cSRaymond Chen if (bcmp(macaddr, ecmp->ecm_srcaddr, ETHERADDRL) != 0) { 7640035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh, 7650035018cSRaymond Chen "usbecm_m_unicst: not supported to set a" 7660035018cSRaymond Chen " different MAC addr"); 7670035018cSRaymond Chen 7680035018cSRaymond Chen return (DDI_FAILURE); 7690035018cSRaymond Chen } 7700035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 7710035018cSRaymond Chen filter = ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_DIRECTED; 7720035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 7730035018cSRaymond Chen 774*3db80ed2SRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 7750035018cSRaymond Chen rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, 7760035018cSRaymond Chen filter, NULL); 7770035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 7780035018cSRaymond Chen 7790035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 7800035018cSRaymond Chen "usbecm_m_unicst: rval = %d", rval); 7810035018cSRaymond Chen 7820035018cSRaymond Chen /* some devices may not support this request, we just return success */ 7830035018cSRaymond Chen return (DDI_SUCCESS); 7840035018cSRaymond Chen } 7850035018cSRaymond Chen 7860035018cSRaymond Chen /* 7870035018cSRaymond Chen * Enable/disable multicast. 7880035018cSRaymond Chen */ 7890035018cSRaymond Chen /*ARGSUSED*/ 7900035018cSRaymond Chen static int 7910035018cSRaymond Chen usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m) 7920035018cSRaymond Chen { 7930035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 7940035018cSRaymond Chen uint16_t filter; 7950035018cSRaymond Chen int rval = 0; 7960035018cSRaymond Chen 7970035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 7980035018cSRaymond Chen "usbecm_m_multicst: entry"); 7990035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 8000035018cSRaymond Chen 8010035018cSRaymond Chen /* 8020035018cSRaymond Chen * To simplify the implementation, we support switching 8030035018cSRaymond Chen * all multicast on/off feature only 8040035018cSRaymond Chen */ 8050035018cSRaymond Chen if (add == B_TRUE) { 8060035018cSRaymond Chen ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_ALL_MCAST; 8070035018cSRaymond Chen } else { 8080035018cSRaymond Chen ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_ALL_MCAST; 8090035018cSRaymond Chen } 8100035018cSRaymond Chen filter = ecmp->ecm_pkt_flt; 8110035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 8120035018cSRaymond Chen 813*3db80ed2SRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 8140035018cSRaymond Chen if (ecmp->ecm_compatibility && 8150035018cSRaymond Chen (ecmp->ecm_desc.wNumberMCFilters & 0x7F)) { 8160035018cSRaymond Chen /* Device supports SetEthernetMulticastFilters request */ 8170035018cSRaymond Chen rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, 8180035018cSRaymond Chen filter, NULL); 8190035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 8200035018cSRaymond Chen "usbecm_m_multicst: rval = %d", rval); 8210035018cSRaymond Chen } 8220035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 8230035018cSRaymond Chen 8240035018cSRaymond Chen /* some devices may not support this request, we just return success */ 8250035018cSRaymond Chen return (DDI_SUCCESS); 8260035018cSRaymond Chen } 8270035018cSRaymond Chen 8280035018cSRaymond Chen /* 8290035018cSRaymond Chen * Enable/disable promiscuous mode. 8300035018cSRaymond Chen */ 8310035018cSRaymond Chen static int 8320035018cSRaymond Chen usbecm_m_promisc(void *arg, boolean_t on) 8330035018cSRaymond Chen { 8340035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 8350035018cSRaymond Chen uint16_t filter; 8360035018cSRaymond Chen int rval; 8370035018cSRaymond Chen 8380035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 8390035018cSRaymond Chen "usbecm_m_promisc: entry"); 8400035018cSRaymond Chen 8410035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 8420035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 8430035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 8440035018cSRaymond Chen "usbecm_m_promisc: device not ONLINE"); 8450035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 8460035018cSRaymond Chen 8470035018cSRaymond Chen return (DDI_FAILURE); 8480035018cSRaymond Chen } 8490035018cSRaymond Chen 8500035018cSRaymond Chen 8510035018cSRaymond Chen if (on == B_TRUE) { 8520035018cSRaymond Chen ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_PROMISC; 8530035018cSRaymond Chen } else { 8540035018cSRaymond Chen ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_PROMISC; 8550035018cSRaymond Chen } 8560035018cSRaymond Chen filter = ecmp->ecm_pkt_flt; 8570035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 8580035018cSRaymond Chen 859*3db80ed2SRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 8600035018cSRaymond Chen rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, 8610035018cSRaymond Chen filter, NULL); 8620035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 8630035018cSRaymond Chen 8640035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 8650035018cSRaymond Chen "usbecm_m_promisc: rval=%d", rval); 8660035018cSRaymond Chen 8670035018cSRaymond Chen /* 8680035018cSRaymond Chen * devices may not support this request, we just 8690035018cSRaymond Chen * return success to let upper layer to do further 8700035018cSRaymond Chen * operation. 8710035018cSRaymond Chen */ 8720035018cSRaymond Chen return (DDI_SUCCESS); 8730035018cSRaymond Chen } 8740035018cSRaymond Chen 8750035018cSRaymond Chen /* 8760035018cSRaymond Chen * IOCTL request: Does not do anything. Will be enhanced 8770035018cSRaymond Chen * in future. 8780035018cSRaymond Chen */ 8790035018cSRaymond Chen static void 8800035018cSRaymond Chen usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 8810035018cSRaymond Chen { 8820035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 8830035018cSRaymond Chen struct iocblk *iocp; 8840035018cSRaymond Chen int cmd; 8850035018cSRaymond Chen 8860035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 8870035018cSRaymond Chen "usbecm_m_ioctl: entry"); 8880035018cSRaymond Chen 8890035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 8900035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 8910035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 8920035018cSRaymond Chen "usbecm_m_ioctl: device not ONLINE"); 8930035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 8940035018cSRaymond Chen 8950035018cSRaymond Chen miocnak(wq, mp, 0, EIO); 8960035018cSRaymond Chen 8970035018cSRaymond Chen return; 8980035018cSRaymond Chen } 8990035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 9000035018cSRaymond Chen 9010035018cSRaymond Chen iocp = (void *)mp->b_rptr; 9020035018cSRaymond Chen iocp->ioc_error = 0; 9030035018cSRaymond Chen cmd = iocp->ioc_cmd; 9040035018cSRaymond Chen 905*3db80ed2SRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 9060035018cSRaymond Chen 9070035018cSRaymond Chen switch (cmd) { 9080035018cSRaymond Chen default: 9090035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 9100035018cSRaymond Chen "unknown cmd 0x%x", cmd); 9110035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 9120035018cSRaymond Chen miocnak(wq, mp, 0, EINVAL); 9130035018cSRaymond Chen 9140035018cSRaymond Chen return; 9150035018cSRaymond Chen } 9160035018cSRaymond Chen } 9170035018cSRaymond Chen 9180035018cSRaymond Chen /* 9190035018cSRaymond Chen * callback functions for get/set properties 9200035018cSRaymond Chen * Does not do anything. Will be enhanced to 9210035018cSRaymond Chen * support set/get properties in future. 9220035018cSRaymond Chen */ 9230035018cSRaymond Chen /*ARGSUSED*/ 9240035018cSRaymond Chen static int 9250035018cSRaymond Chen usbecm_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 9260035018cSRaymond Chen uint_t wldp_length, const void *wldp_buf) 9270035018cSRaymond Chen { 9280035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 9290035018cSRaymond Chen int err = ENOTSUP; 9300035018cSRaymond Chen 9310035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 9320035018cSRaymond Chen "usbecm_m_setprop: entry"); 9330035018cSRaymond Chen 9340035018cSRaymond Chen return (err); 9350035018cSRaymond Chen } 9360035018cSRaymond Chen 9370035018cSRaymond Chen /*ARGSUSED*/ 9380035018cSRaymond Chen static int usbecm_m_getprop(void *arg, const char *pr_name, 9390035018cSRaymond Chen mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf) 9400035018cSRaymond Chen { 9410035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 9420035018cSRaymond Chen int err = ENOTSUP; 9430035018cSRaymond Chen 9440035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 9450035018cSRaymond Chen "usbecm_m_getprop: entry"); 9460035018cSRaymond Chen 9470035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 9480035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 9490035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 9500035018cSRaymond Chen 9510035018cSRaymond Chen return (EIO); 9520035018cSRaymond Chen } 9530035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 9540035018cSRaymond Chen 9550035018cSRaymond Chen return (err); 9560035018cSRaymond Chen } 9570035018cSRaymond Chen 9580035018cSRaymond Chen /* 9590035018cSRaymond Chen * Transmit a data frame. 9600035018cSRaymond Chen */ 9610035018cSRaymond Chen static mblk_t * 9620035018cSRaymond Chen usbecm_m_tx(void *arg, mblk_t *mp) 9630035018cSRaymond Chen { 9640035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)arg; 9650035018cSRaymond Chen mblk_t *next; 9660035018cSRaymond Chen int count = 0; 9670035018cSRaymond Chen 9680035018cSRaymond Chen ASSERT(mp != NULL); 9690035018cSRaymond Chen 9700035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 9710035018cSRaymond Chen "usbecm_m_tx: entry"); 9720035018cSRaymond Chen 9730035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 9740035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 9750035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 9760035018cSRaymond Chen "usbecm_m_tx: device not ONLINE"); 9770035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 9780035018cSRaymond Chen 9790035018cSRaymond Chen return (mp); 9800035018cSRaymond Chen } 9810035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 9820035018cSRaymond Chen 983*3db80ed2SRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 9840035018cSRaymond Chen 9850035018cSRaymond Chen /* 9860035018cSRaymond Chen * To make use of the device maximum capability, 9870035018cSRaymond Chen * concatenate msg blocks in a msg to ETHERMAX length. 9880035018cSRaymond Chen */ 9890035018cSRaymond Chen while (mp != NULL) { 9900035018cSRaymond Chen next = mp->b_next; 9910035018cSRaymond Chen mp->b_next = NULL; 9920035018cSRaymond Chen 9930035018cSRaymond Chen if (usbecm_send_data(ecmp, mp) != DDI_SUCCESS) { 9940035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh, 9950035018cSRaymond Chen "usbecm_m_tx: send data fail"); 9960035018cSRaymond Chen 9970035018cSRaymond Chen /* failure statistics */ 9980035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 9990035018cSRaymond Chen ecmp->ecm_stat.es_oerrors++; 10000035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 10010035018cSRaymond Chen 10020035018cSRaymond Chen mp->b_next = next; 10030035018cSRaymond Chen 10040035018cSRaymond Chen break; 10050035018cSRaymond Chen } 10060035018cSRaymond Chen 10070035018cSRaymond Chen /* 10080035018cSRaymond Chen * To make it simple, we count all packets, no matter 10090035018cSRaymond Chen * the device supports ethernet statistics or not. 10100035018cSRaymond Chen */ 10110035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 10120035018cSRaymond Chen ecmp->ecm_stat.es_opackets++; 10130035018cSRaymond Chen ecmp->ecm_stat.es_obytes += MBLKL(mp); 10140035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 10150035018cSRaymond Chen 10160035018cSRaymond Chen freemsg(mp); /* free this msg upon success */ 10170035018cSRaymond Chen 10180035018cSRaymond Chen mp = next; 10190035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 10200035018cSRaymond Chen "usbecm_m_tx: %d msgs processed", ++count); 10210035018cSRaymond Chen } 10220035018cSRaymond Chen 10230035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 10240035018cSRaymond Chen 10250035018cSRaymond Chen return (mp); 10260035018cSRaymond Chen } 10270035018cSRaymond Chen 10280035018cSRaymond Chen /* 10290035018cSRaymond Chen * usbecm_bulkin_cb: 10300035018cSRaymond Chen * Bulk In regular and exeception callback; 10310035018cSRaymond Chen * USBA framework will call this callback 10320035018cSRaymond Chen * after deal with bulkin request. 10330035018cSRaymond Chen */ 10340035018cSRaymond Chen /*ARGSUSED*/ 10350035018cSRaymond Chen static void 10360035018cSRaymond Chen usbecm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) 10370035018cSRaymond Chen { 10380035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private; 10390035018cSRaymond Chen mblk_t *data, *mp; 10400035018cSRaymond Chen int data_len; 10410035018cSRaymond Chen int max_pkt_size = ecmp->ecm_bulkin_sz; 10420035018cSRaymond Chen 10430035018cSRaymond Chen data = req->bulk_data; 10440035018cSRaymond Chen data_len = (data) ? MBLKL(data) : 0; 10450035018cSRaymond Chen 10460035018cSRaymond Chen ASSERT(data->b_cont == NULL); 10470035018cSRaymond Chen 10480035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 10490035018cSRaymond Chen 10500035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh, 10510035018cSRaymond Chen "usbecm_bulkin_cb: state=%d, len=%d", ecmp->ecm_bulkin_state, 10520035018cSRaymond Chen data_len); 10530035018cSRaymond Chen 10540035018cSRaymond Chen /* 10550035018cSRaymond Chen * may receive a zero length packet according 10560035018cSRaymond Chen * to USB short packet semantics 10570035018cSRaymond Chen */ 10580035018cSRaymond Chen if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) && 10590035018cSRaymond Chen (req->bulk_completion_reason == USB_CR_OK)) { 10600035018cSRaymond Chen if (data_len) { 10610035018cSRaymond Chen if (ecmp->ecm_rcv_queue == NULL) { 10620035018cSRaymond Chen ecmp->ecm_rcv_queue = data; 10630035018cSRaymond Chen } else { 10640035018cSRaymond Chen if ((msgsize(ecmp->ecm_rcv_queue) + data_len) 10650035018cSRaymond Chen > ETHERMAX) { 10660035018cSRaymond Chen /* 10670035018cSRaymond Chen * Exceed the ethernet maximum length, we think 10680035018cSRaymond Chen * something is wrong with this frame and hence 10690035018cSRaymond Chen * free older data. Accept new data instead. 10700035018cSRaymond Chen */ 10710035018cSRaymond Chen freemsg(ecmp->ecm_rcv_queue); 10720035018cSRaymond Chen ecmp->ecm_rcv_queue = data; 10730035018cSRaymond Chen } else { 10740035018cSRaymond Chen linkb(ecmp->ecm_rcv_queue, data); 10750035018cSRaymond Chen } 10760035018cSRaymond Chen } 10770035018cSRaymond Chen } else { 10780035018cSRaymond Chen /* 10790035018cSRaymond Chen * Do not put zero length packet to receive queue. 10800035018cSRaymond Chen * Otherwise, msgpullup will dupmsg() a zero length 10810035018cSRaymond Chen * mblk, which will cause memleaks. 10820035018cSRaymond Chen */ 10830035018cSRaymond Chen freemsg(data); 10840035018cSRaymond Chen } 10850035018cSRaymond Chen 10860035018cSRaymond Chen /* 10870035018cSRaymond Chen * ECM V1.2, section 3.3.1, a short(including zero length) 10880035018cSRaymond Chen * packet signifies end of frame. We can submit this frame 10890035018cSRaymond Chen * to upper layer now. 10900035018cSRaymond Chen */ 10910035018cSRaymond Chen if ((data_len < max_pkt_size) && 10920035018cSRaymond Chen (msgsize(ecmp->ecm_rcv_queue) > 0)) { 10930035018cSRaymond Chen mp = msgpullup(ecmp->ecm_rcv_queue, -1); 10940035018cSRaymond Chen freemsg(ecmp->ecm_rcv_queue); 10950035018cSRaymond Chen ecmp->ecm_rcv_queue = NULL; 10960035018cSRaymond Chen 10970035018cSRaymond Chen ecmp->ecm_stat.es_ipackets++; 10980035018cSRaymond Chen ecmp->ecm_stat.es_ibytes += msgsize(mp); 10990035018cSRaymond Chen if (mp && (mp->b_rptr[0] & 0x01)) { 11000035018cSRaymond Chen if (bcmp(mp->b_rptr, usbecm_broadcast, 11010035018cSRaymond Chen ETHERADDRL) != 0) { 11020035018cSRaymond Chen ecmp->ecm_stat.es_multircv++; 11030035018cSRaymond Chen } else { 11040035018cSRaymond Chen ecmp->ecm_stat.es_brdcstrcv++; 11050035018cSRaymond Chen } 11060035018cSRaymond Chen } 11070035018cSRaymond Chen 11080035018cSRaymond Chen if (mp) { 11090035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 11100035018cSRaymond Chen mac_rx(ecmp->ecm_mh, NULL, mp); 11110035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 11120035018cSRaymond Chen } 11130035018cSRaymond Chen } 11140035018cSRaymond Chen 11150035018cSRaymond Chen /* prevent USBA from freeing data along with the request */ 11160035018cSRaymond Chen req->bulk_data = NULL; 11170035018cSRaymond Chen } else if (req->bulk_completion_reason != USB_CR_OK) { 11180035018cSRaymond Chen ecmp->ecm_stat.es_ierrors++; 11190035018cSRaymond Chen } 11200035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 11210035018cSRaymond Chen 11220035018cSRaymond Chen usb_free_bulk_req(req); 11230035018cSRaymond Chen 11240035018cSRaymond Chen /* receive more */ 11250035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 11260035018cSRaymond Chen if (((ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) || 11270035018cSRaymond Chen (ecmp->ecm_bulkin_state == USBECM_PIPE_IDLE)) && 11280035018cSRaymond Chen (ecmp->ecm_dev_state == USB_DEV_ONLINE)) { 11290035018cSRaymond Chen if (usbecm_rx_start(ecmp) != USB_SUCCESS) { 11300035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 11310035018cSRaymond Chen "usbecm_bulkin_cb: restart rx fail " 11320035018cSRaymond Chen "ecmp_state = %d", ecmp->ecm_bulkin_state); 11330035018cSRaymond Chen } 11340035018cSRaymond Chen } else if (ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) { 11350035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE; 11360035018cSRaymond Chen } 11370035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 11380035018cSRaymond Chen } 11390035018cSRaymond Chen 11400035018cSRaymond Chen /* 11410035018cSRaymond Chen * usbsecm_rx_start: 11420035018cSRaymond Chen * start data receipt 11430035018cSRaymond Chen */ 11440035018cSRaymond Chen static int 11450035018cSRaymond Chen usbecm_rx_start(usbecm_state_t *ecmp) 11460035018cSRaymond Chen { 11470035018cSRaymond Chen usb_bulk_req_t *br; 11480035018cSRaymond Chen int rval = USB_FAILURE; 11490035018cSRaymond Chen int data_len; 11500035018cSRaymond Chen 11510035018cSRaymond Chen ASSERT(mutex_owned(&ecmp->ecm_mutex)); 11520035018cSRaymond Chen 11530035018cSRaymond Chen DTRACE_PROBE2(usbecm_rx__start, int, ecmp->ecm_xfer_sz, 11540035018cSRaymond Chen int, ecmp->ecm_bulkin_sz); 11550035018cSRaymond Chen 11560035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_BUSY; 11570035018cSRaymond Chen data_len = ecmp->ecm_bulkin_sz; 11580035018cSRaymond Chen 11590035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 11600035018cSRaymond Chen br = usb_alloc_bulk_req(ecmp->ecm_dip, data_len, USB_FLAGS_SLEEP); 11610035018cSRaymond Chen if (br == NULL) { 11620035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 11630035018cSRaymond Chen "usbsecm_rx_start: allocate bulk request failed"); 11640035018cSRaymond Chen 11650035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 11660035018cSRaymond Chen 11670035018cSRaymond Chen return (USB_FAILURE); 11680035018cSRaymond Chen } 11690035018cSRaymond Chen /* initialize bulk in request. */ 11700035018cSRaymond Chen br->bulk_len = data_len; 11710035018cSRaymond Chen br->bulk_timeout = 0; 11720035018cSRaymond Chen br->bulk_cb = usbecm_bulkin_cb; 11730035018cSRaymond Chen br->bulk_exc_cb = usbecm_bulkin_cb; 11740035018cSRaymond Chen br->bulk_client_private = (usb_opaque_t)ecmp; 11750035018cSRaymond Chen br->bulk_attributes = USB_ATTRS_AUTOCLEARING 11760035018cSRaymond Chen | USB_ATTRS_SHORT_XFER_OK; 11770035018cSRaymond Chen 11780035018cSRaymond Chen rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkin_ph, br, 0); 11790035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 11800035018cSRaymond Chen if (rval != USB_SUCCESS) { 11810035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 11820035018cSRaymond Chen "usbsecm_rx_start: bulk transfer failed %d", rval); 11830035018cSRaymond Chen usb_free_bulk_req(br); 11840035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE; 11850035018cSRaymond Chen } 11860035018cSRaymond Chen 11870035018cSRaymond Chen return (rval); 11880035018cSRaymond Chen } 11890035018cSRaymond Chen 11900035018cSRaymond Chen /* 11910035018cSRaymond Chen * usbecm_bulkout_cb: 11920035018cSRaymond Chen * Bulk Out regular and exeception callback; 11930035018cSRaymond Chen * USBA framework will call this callback function 11940035018cSRaymond Chen * after deal with bulkout request. 11950035018cSRaymond Chen */ 11960035018cSRaymond Chen /*ARGSUSED*/ 11970035018cSRaymond Chen static void 11980035018cSRaymond Chen usbecm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) 11990035018cSRaymond Chen { 12000035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private; 12010035018cSRaymond Chen int data_len; 12020035018cSRaymond Chen boolean_t need_update = B_FALSE; 12030035018cSRaymond Chen 12040035018cSRaymond Chen data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0; 12050035018cSRaymond Chen 12060035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh, 12070035018cSRaymond Chen "usbecm_bulkout_cb: data_len = %d, cr=%d", data_len, 12080035018cSRaymond Chen req->bulk_completion_reason); 12090035018cSRaymond Chen 12100035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 12110035018cSRaymond Chen if ((data_len > 0) && (ecmp->ecm_tx_cnt > 0)) { 12120035018cSRaymond Chen if (ecmp->ecm_tx_cnt == usbecm_tx_max) { 12130035018cSRaymond Chen need_update = B_TRUE; 12140035018cSRaymond Chen } 12150035018cSRaymond Chen ecmp->ecm_tx_cnt--; 12160035018cSRaymond Chen } 12170035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 12180035018cSRaymond Chen 12190035018cSRaymond Chen if (req->bulk_completion_reason && (data_len > 0)) { 12200035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 12210035018cSRaymond Chen ecmp->ecm_stat.es_oerrors++; 12220035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 12230035018cSRaymond Chen 12240035018cSRaymond Chen need_update = B_TRUE; 12250035018cSRaymond Chen } 12260035018cSRaymond Chen 12270035018cSRaymond Chen /* 12280035018cSRaymond Chen * notify MAC layer to retransfer the failed packet 12290035018cSRaymond Chen * Or notity MAC that we have more buffer now. 12300035018cSRaymond Chen */ 12310035018cSRaymond Chen if (need_update) { 12320035018cSRaymond Chen mac_tx_update(ecmp->ecm_mh); 12330035018cSRaymond Chen } 12340035018cSRaymond Chen 12350035018cSRaymond Chen usb_free_bulk_req(req); 12360035018cSRaymond Chen } 12370035018cSRaymond Chen 12380035018cSRaymond Chen static int 12390035018cSRaymond Chen usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data) 12400035018cSRaymond Chen { 12410035018cSRaymond Chen usb_bulk_req_t *br; 12420035018cSRaymond Chen int rval = USB_FAILURE; 12430035018cSRaymond Chen int data_len = MBLKL(data); 12440035018cSRaymond Chen int max_pkt_size; 12450035018cSRaymond Chen mblk_t *new_data = NULL; 12460035018cSRaymond Chen int new_data_len = 0; 12470035018cSRaymond Chen 12480035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 12490035018cSRaymond Chen "usbecm_send_data: length = %d, total len=%d", 12500035018cSRaymond Chen data_len, (int)msgdsize(data)); 12510035018cSRaymond Chen 12520035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 12530035018cSRaymond Chen if (ecmp->ecm_tx_cnt >= usbecm_tx_max) { 12540035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 12550035018cSRaymond Chen "usbecm_send_data: (%d) exceeds TX max queue length", 12560035018cSRaymond Chen ecmp->ecm_tx_cnt); 12570035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 12580035018cSRaymond Chen 12590035018cSRaymond Chen return (USB_FAILURE); 12600035018cSRaymond Chen } 12610035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 12620035018cSRaymond Chen 12630035018cSRaymond Chen data_len = msgsize(data); 12640035018cSRaymond Chen if (data_len > ETHERMAX) { 12650035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 12660035018cSRaymond Chen ecmp->ecm_stat.es_macxmt_err++; 12670035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 12680035018cSRaymond Chen 12690035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 12700035018cSRaymond Chen "usbecm_send_data: packet too long, %d", data_len); 12710035018cSRaymond Chen 12720035018cSRaymond Chen return (USB_FAILURE); 12730035018cSRaymond Chen } 12740035018cSRaymond Chen 12750035018cSRaymond Chen if (data_len < ETHERMIN) { 12760035018cSRaymond Chen mblk_t *tmp; 12770035018cSRaymond Chen 12780035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 12790035018cSRaymond Chen "usbecm_send_data: short packet, padding to ETHERMIN"); 12800035018cSRaymond Chen 12810035018cSRaymond Chen new_data_len = ETHERMIN; 12820035018cSRaymond Chen if ((new_data = allocb(new_data_len, 0)) == NULL) { 12830035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 12840035018cSRaymond Chen "usbecm_send_data: fail to allocb"); 12850035018cSRaymond Chen 12860035018cSRaymond Chen return (USB_FAILURE); 12870035018cSRaymond Chen } 12880035018cSRaymond Chen bzero(new_data->b_wptr, new_data_len); 12890035018cSRaymond Chen for (tmp = data; tmp != NULL; tmp = tmp->b_cont) { 12900035018cSRaymond Chen bcopy(tmp->b_rptr, new_data->b_wptr, MBLKL(tmp)); 12910035018cSRaymond Chen new_data->b_wptr += MBLKL(tmp); 12920035018cSRaymond Chen } 12930035018cSRaymond Chen 12940035018cSRaymond Chen new_data->b_wptr = new_data->b_rptr + new_data_len; 12950035018cSRaymond Chen } 12960035018cSRaymond Chen 12970035018cSRaymond Chen br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP); 12980035018cSRaymond Chen if (br == NULL) { 12990035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 13000035018cSRaymond Chen "usbecm_send_data: alloc req failed."); 13010035018cSRaymond Chen 13020035018cSRaymond Chen return (USB_FAILURE); 13030035018cSRaymond Chen } 13040035018cSRaymond Chen 13050035018cSRaymond Chen /* initialize the bulk out request */ 13060035018cSRaymond Chen if (new_data) { 13070035018cSRaymond Chen br->bulk_data = msgpullup(new_data, -1); /* msg allocated! */ 13080035018cSRaymond Chen br->bulk_len = new_data_len; 13090035018cSRaymond Chen } else { 13100035018cSRaymond Chen br->bulk_data = msgpullup(data, -1); /* msg allocated! */ 13110035018cSRaymond Chen br->bulk_len = data_len; 13120035018cSRaymond Chen } 13130035018cSRaymond Chen 13140035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 13150035018cSRaymond Chen "usbecm_send_data: bulk_len = %d", br->bulk_len); 13160035018cSRaymond Chen 13170035018cSRaymond Chen br->bulk_timeout = USBECM_BULKOUT_TIMEOUT; 13180035018cSRaymond Chen br->bulk_cb = usbecm_bulkout_cb; 13190035018cSRaymond Chen br->bulk_exc_cb = usbecm_bulkout_cb; 13200035018cSRaymond Chen br->bulk_client_private = (usb_opaque_t)ecmp; 13210035018cSRaymond Chen br->bulk_attributes = USB_ATTRS_AUTOCLEARING; 13220035018cSRaymond Chen 13230035018cSRaymond Chen if (br->bulk_data != NULL) { 13240035018cSRaymond Chen if (br->bulk_data->b_rptr[0] & 0x01) { 13250035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 13260035018cSRaymond Chen if (bcmp(br->bulk_data->b_rptr, usbecm_broadcast, 13270035018cSRaymond Chen ETHERADDRL) != 0) { 13280035018cSRaymond Chen ecmp->ecm_stat.es_multixmt++; 13290035018cSRaymond Chen } else { 13300035018cSRaymond Chen ecmp->ecm_stat.es_brdcstxmt++; 13310035018cSRaymond Chen } 13320035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 13330035018cSRaymond Chen } 13340035018cSRaymond Chen rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0); 13350035018cSRaymond Chen } 13360035018cSRaymond Chen 13370035018cSRaymond Chen if (rval != USB_SUCCESS) { 13380035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 13390035018cSRaymond Chen "usbecm_send_data: Send Data failed."); 13400035018cSRaymond Chen 13410035018cSRaymond Chen /* 13420035018cSRaymond Chen * br->bulk_data should be freed because we allocated 13430035018cSRaymond Chen * it in this function. 13440035018cSRaymond Chen */ 13450035018cSRaymond Chen usb_free_bulk_req(br); 13460035018cSRaymond Chen 13470035018cSRaymond Chen } else { 13480035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 13490035018cSRaymond Chen ecmp->ecm_tx_cnt++; 13500035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 13510035018cSRaymond Chen 13520035018cSRaymond Chen /* 13530035018cSRaymond Chen * ECM V1.2, section 3.3.1, a short(including zero length) 13540035018cSRaymond Chen * packet signifies end of frame. We should send a zero length 13550035018cSRaymond Chen * packet to device if the total data lenght is multiple of 13560035018cSRaymond Chen * bulkout endpoint's max packet size. 13570035018cSRaymond Chen */ 13580035018cSRaymond Chen max_pkt_size = ecmp->ecm_bulk_out_ep->ep_descr.wMaxPacketSize; 13590035018cSRaymond Chen if ((data_len % max_pkt_size) == 0) { 13600035018cSRaymond Chen if ((rval = usbecm_send_zero_data(ecmp)) 13610035018cSRaymond Chen != USB_SUCCESS) { 13620035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 13630035018cSRaymond Chen "usbecm_send_data: fail to send padding"); 13640035018cSRaymond Chen } 13650035018cSRaymond Chen } 13660035018cSRaymond Chen } 13670035018cSRaymond Chen 13680035018cSRaymond Chen if (new_data) { 13690035018cSRaymond Chen freemsg(new_data); 13700035018cSRaymond Chen } 13710035018cSRaymond Chen 13720035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, 13730035018cSRaymond Chen "usbecm_send_data: len(%d) data sent, rval=%d", 13740035018cSRaymond Chen new_data_len ? new_data_len : data_len, rval); 13750035018cSRaymond Chen 13760035018cSRaymond Chen return (rval); 13770035018cSRaymond Chen } 13780035018cSRaymond Chen 13790035018cSRaymond Chen static int 13800035018cSRaymond Chen usbecm_send_zero_data(usbecm_state_t *ecmp) 13810035018cSRaymond Chen { 13820035018cSRaymond Chen usb_bulk_req_t *br; 13830035018cSRaymond Chen int rval = USB_FAILURE; 13840035018cSRaymond Chen 13850035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 13860035018cSRaymond Chen "usbecm_send_zero_data: entry"); 13870035018cSRaymond Chen 13880035018cSRaymond Chen br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP); 13890035018cSRaymond Chen if (br == NULL) { 13900035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 13910035018cSRaymond Chen "usbecm_send_data: alloc req failed."); 13920035018cSRaymond Chen 13930035018cSRaymond Chen return (USB_FAILURE); 13940035018cSRaymond Chen } 13950035018cSRaymond Chen 13960035018cSRaymond Chen /* initialize the bulk out request */ 13970035018cSRaymond Chen br->bulk_len = 0; 13980035018cSRaymond Chen br->bulk_timeout = USBECM_BULKOUT_TIMEOUT; 13990035018cSRaymond Chen br->bulk_cb = usbecm_bulkout_cb; 14000035018cSRaymond Chen br->bulk_exc_cb = usbecm_bulkout_cb; 14010035018cSRaymond Chen br->bulk_client_private = (usb_opaque_t)ecmp; 14020035018cSRaymond Chen br->bulk_attributes = USB_ATTRS_AUTOCLEARING; 14030035018cSRaymond Chen 14040035018cSRaymond Chen rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0); 14050035018cSRaymond Chen 14060035018cSRaymond Chen if (rval != USB_SUCCESS) { 14070035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, 14080035018cSRaymond Chen "usbecm_send_zero_data: Send data failed, rval=%d", 14090035018cSRaymond Chen rval); 14100035018cSRaymond Chen 14110035018cSRaymond Chen /* 14120035018cSRaymond Chen * br->bulk_data should be freed because we allocated 14130035018cSRaymond Chen * it in this function. 14140035018cSRaymond Chen */ 14150035018cSRaymond Chen usb_free_bulk_req(br); 14160035018cSRaymond Chen 14170035018cSRaymond Chen } 14180035018cSRaymond Chen 14190035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, 14200035018cSRaymond Chen "usbecm_send_zero_data: end"); 14210035018cSRaymond Chen 14220035018cSRaymond Chen return (rval); 14230035018cSRaymond Chen } 14240035018cSRaymond Chen 14250035018cSRaymond Chen /* 14260035018cSRaymond Chen * Loadable module configuration entry points 14270035018cSRaymond Chen */ 14280035018cSRaymond Chen 14290035018cSRaymond Chen /* 14300035018cSRaymond Chen * _init module entry point. 14310035018cSRaymond Chen * 14320035018cSRaymond Chen * Called when the module is being loaded into memory. 14330035018cSRaymond Chen */ 14340035018cSRaymond Chen int 14350035018cSRaymond Chen _init(void) 14360035018cSRaymond Chen { 14370035018cSRaymond Chen int err; 14380035018cSRaymond Chen 14390035018cSRaymond Chen err = ddi_soft_state_init(&usbecm_statep, sizeof (usbecm_state_t), 1); 14400035018cSRaymond Chen 14410035018cSRaymond Chen if (err != DDI_SUCCESS) 14420035018cSRaymond Chen return (err); 14430035018cSRaymond Chen 14440035018cSRaymond Chen mac_init_ops(&usbecm_devops, "usbecm"); 14450035018cSRaymond Chen err = mod_install(&usbecm_ml); 14460035018cSRaymond Chen 14470035018cSRaymond Chen if (err != DDI_SUCCESS) { 14480035018cSRaymond Chen mac_fini_ops(&usbecm_devops); 14490035018cSRaymond Chen ddi_soft_state_fini(&usbecm_statep); 14500035018cSRaymond Chen } 14510035018cSRaymond Chen 14520035018cSRaymond Chen return (err); 14530035018cSRaymond Chen } 14540035018cSRaymond Chen 14550035018cSRaymond Chen /* 14560035018cSRaymond Chen * _info module entry point. 14570035018cSRaymond Chen * 14580035018cSRaymond Chen * Called to obtain information about the module. 14590035018cSRaymond Chen */ 14600035018cSRaymond Chen int 14610035018cSRaymond Chen _info(struct modinfo *modinfop) 14620035018cSRaymond Chen { 14630035018cSRaymond Chen return (mod_info(&usbecm_ml, modinfop)); 14640035018cSRaymond Chen } 14650035018cSRaymond Chen 14660035018cSRaymond Chen /* 14670035018cSRaymond Chen * _fini module entry point. 14680035018cSRaymond Chen * 14690035018cSRaymond Chen * Called when the module is being unloaded. 14700035018cSRaymond Chen */ 14710035018cSRaymond Chen int 14720035018cSRaymond Chen _fini(void) 14730035018cSRaymond Chen { 14740035018cSRaymond Chen int err; 14750035018cSRaymond Chen 14760035018cSRaymond Chen err = mod_remove(&usbecm_ml); 14770035018cSRaymond Chen if (err == DDI_SUCCESS) { 14780035018cSRaymond Chen mac_fini_ops(&usbecm_devops); 14790035018cSRaymond Chen ddi_soft_state_fini(&usbecm_statep); 14800035018cSRaymond Chen } 14810035018cSRaymond Chen 14820035018cSRaymond Chen return (err); 14830035018cSRaymond Chen } 14840035018cSRaymond Chen 14850035018cSRaymond Chen /* 14860035018cSRaymond Chen * usbecm_pipe_start_polling: 14870035018cSRaymond Chen * start polling on the interrupt pipe 14880035018cSRaymond Chen */ 14890035018cSRaymond Chen static void 14900035018cSRaymond Chen usbecm_pipe_start_polling(usbecm_state_t *ecmp) 14910035018cSRaymond Chen { 14920035018cSRaymond Chen usb_intr_req_t *intr; 14930035018cSRaymond Chen int rval; 14940035018cSRaymond Chen 14950035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh, 14960035018cSRaymond Chen "usbecm_pipe_start_polling: "); 14970035018cSRaymond Chen 14980035018cSRaymond Chen if (ecmp->ecm_intr_ph == NULL) { 14990035018cSRaymond Chen 15000035018cSRaymond Chen return; 15010035018cSRaymond Chen } 15020035018cSRaymond Chen 15030035018cSRaymond Chen intr = usb_alloc_intr_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP); 15040035018cSRaymond Chen 15050035018cSRaymond Chen /* 15060035018cSRaymond Chen * If it is in interrupt context, usb_alloc_intr_req will return NULL if 15070035018cSRaymond Chen * called with SLEEP flag. 15080035018cSRaymond Chen */ 15090035018cSRaymond Chen if (!intr) { 15100035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 15110035018cSRaymond Chen "usbecm_pipe_start_polling: alloc req failed."); 15120035018cSRaymond Chen 15130035018cSRaymond Chen return; 15140035018cSRaymond Chen } 15150035018cSRaymond Chen 15160035018cSRaymond Chen /* initialize the interrupt request. */ 15170035018cSRaymond Chen intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK | 15180035018cSRaymond Chen USB_ATTRS_AUTOCLEARING; 15190035018cSRaymond Chen intr->intr_len = ecmp->ecm_intr_ep->ep_descr.wMaxPacketSize; 15200035018cSRaymond Chen intr->intr_client_private = (usb_opaque_t)ecmp; 15210035018cSRaymond Chen intr->intr_cb = usbecm_intr_cb; 15220035018cSRaymond Chen intr->intr_exc_cb = usbecm_intr_ex_cb; 15230035018cSRaymond Chen 15240035018cSRaymond Chen rval = usb_pipe_intr_xfer(ecmp->ecm_intr_ph, intr, USB_FLAGS_SLEEP); 15250035018cSRaymond Chen 15260035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 15270035018cSRaymond Chen if (rval == USB_SUCCESS) { 15280035018cSRaymond Chen ecmp->ecm_intr_state = USBECM_PIPE_BUSY; 15290035018cSRaymond Chen } else { 15300035018cSRaymond Chen usb_free_intr_req(intr); 15310035018cSRaymond Chen ecmp->ecm_intr_state = USBECM_PIPE_IDLE; 15320035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, 15330035018cSRaymond Chen "usbecm_pipe_start_polling: failed (%d)", rval); 15340035018cSRaymond Chen } 15350035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 15360035018cSRaymond Chen 15370035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, 15380035018cSRaymond Chen "usbecm_pipe_start_polling: end, rval=%d", rval); 15390035018cSRaymond Chen } 15400035018cSRaymond Chen 15410035018cSRaymond Chen 15420035018cSRaymond Chen /* 15430035018cSRaymond Chen * usbsecm_intr_cb: 15440035018cSRaymond Chen * interrupt pipe normal callback 15450035018cSRaymond Chen */ 15460035018cSRaymond Chen /*ARGSUSED*/ 15470035018cSRaymond Chen static void 15480035018cSRaymond Chen usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req) 15490035018cSRaymond Chen { 15500035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private; 15510035018cSRaymond Chen mblk_t *data = req->intr_data; 15520035018cSRaymond Chen int data_len; 15530035018cSRaymond Chen 15540035018cSRaymond Chen data_len = (data) ? MBLKL(data) : 0; 15550035018cSRaymond Chen 15560035018cSRaymond Chen DTRACE_PROBE2(usbecm_intr__cb, (usb_intr_req_t *), req, int, data_len); 15570035018cSRaymond Chen 15580035018cSRaymond Chen /* check data length */ 15590035018cSRaymond Chen if (data_len < 8) { 15600035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 15610035018cSRaymond Chen "usbsecm_intr_cb: %d packet too short", data_len); 15620035018cSRaymond Chen usb_free_intr_req(req); 15630035018cSRaymond Chen 15640035018cSRaymond Chen return; 15650035018cSRaymond Chen } 15660035018cSRaymond Chen req->intr_data = NULL; 15670035018cSRaymond Chen usb_free_intr_req(req); 15680035018cSRaymond Chen 15690035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 15700035018cSRaymond Chen /* parse interrupt data -- notifications */ 15710035018cSRaymond Chen usbecm_parse_intr_data(ecmp, data); 15720035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 15730035018cSRaymond Chen } 15740035018cSRaymond Chen 15750035018cSRaymond Chen 15760035018cSRaymond Chen /* 15770035018cSRaymond Chen * usbsecm_intr_ex_cb: 15780035018cSRaymond Chen * interrupt pipe exception callback 15790035018cSRaymond Chen */ 15800035018cSRaymond Chen /*ARGSUSED*/ 15810035018cSRaymond Chen static void 15820035018cSRaymond Chen usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req) 15830035018cSRaymond Chen { 15840035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private; 15850035018cSRaymond Chen usb_cr_t cr = req->intr_completion_reason; 15860035018cSRaymond Chen 15870035018cSRaymond Chen DTRACE_PROBE2(usbecm_intr_ex__cb, int, ecmp->ecm_dev_state, 15880035018cSRaymond Chen (usb_cr_t), cr); 15890035018cSRaymond Chen 15900035018cSRaymond Chen usb_free_intr_req(req); 15910035018cSRaymond Chen 15920035018cSRaymond Chen /* 15930035018cSRaymond Chen * If completion reason isn't USB_CR_PIPE_CLOSING and 15940035018cSRaymond Chen * USB_CR_STOPPED_POLLING, restart polling. 15950035018cSRaymond Chen */ 15960035018cSRaymond Chen if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) { 15970035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 15980035018cSRaymond Chen 15990035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { 16000035018cSRaymond Chen 16010035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 16020035018cSRaymond Chen "usbsecm_intr_ex_cb: state = %d", 16030035018cSRaymond Chen ecmp->ecm_dev_state); 16040035018cSRaymond Chen 16050035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 16060035018cSRaymond Chen 16070035018cSRaymond Chen return; 16080035018cSRaymond Chen } 16090035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 16100035018cSRaymond Chen 16110035018cSRaymond Chen usbecm_pipe_start_polling(ecmp); 16120035018cSRaymond Chen } 16130035018cSRaymond Chen } 16140035018cSRaymond Chen 16150035018cSRaymond Chen 16160035018cSRaymond Chen /* 16170035018cSRaymond Chen * usbsecm_parse_intr_data: 16180035018cSRaymond Chen * Parse data received from interrupt callback 16190035018cSRaymond Chen */ 16200035018cSRaymond Chen static void 16210035018cSRaymond Chen usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data) 16220035018cSRaymond Chen { 16230035018cSRaymond Chen uint8_t bmRequestType; 16240035018cSRaymond Chen uint8_t bNotification; 16250035018cSRaymond Chen uint16_t wValue; 16260035018cSRaymond Chen uint16_t wLength; 16270035018cSRaymond Chen int linkstate; 16280035018cSRaymond Chen 16290035018cSRaymond Chen bmRequestType = data->b_rptr[0]; 16300035018cSRaymond Chen bNotification = data->b_rptr[1]; 16310035018cSRaymond Chen /* 16320035018cSRaymond Chen * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1, 16330035018cSRaymond Chen * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0, 16340035018cSRaymond Chen * mLength is 2. So we directly get the value from the byte. 16350035018cSRaymond Chen */ 16360035018cSRaymond Chen wValue = data->b_rptr[2]; 16370035018cSRaymond Chen wLength = data->b_rptr[6]; 16380035018cSRaymond Chen 16390035018cSRaymond Chen if (ecmp->ecm_compatibility) { 16400035018cSRaymond Chen if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) { 16410035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 16420035018cSRaymond Chen "usbsecm_parse_intr_data: unknown request " 16430035018cSRaymond Chen "type - 0x%x", bmRequestType); 16440035018cSRaymond Chen 16450035018cSRaymond Chen freemsg(data); 16460035018cSRaymond Chen 16470035018cSRaymond Chen return; 16480035018cSRaymond Chen } 16490035018cSRaymond Chen } else { 16500035018cSRaymond Chen /* non-compatible device specific parsing */ 16510035018cSRaymond Chen if (ECM_DS_OP_VALID(ecm_ds_intr_cb)) { 16520035018cSRaymond Chen if (ecmp->ecm_ds_ops->ecm_ds_intr_cb(ecmp, data) 16530035018cSRaymond Chen != USB_SUCCESS) { 16540035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, 16550035018cSRaymond Chen "usbsecm_parse_intr_data: unknown request" 16560035018cSRaymond Chen "type - 0x%x", bmRequestType); 16570035018cSRaymond Chen } 16580035018cSRaymond Chen } 16590035018cSRaymond Chen freemsg(data); 16600035018cSRaymond Chen 16610035018cSRaymond Chen return; 16620035018cSRaymond Chen } 16630035018cSRaymond Chen 16640035018cSRaymond Chen /* 16650035018cSRaymond Chen * Check the return value of compatible devices 16660035018cSRaymond Chen */ 16670035018cSRaymond Chen switch (bNotification) { 16680035018cSRaymond Chen case USB_CDC_NOTIFICATION_NETWORK_CONNECTION: 16690035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, 16700035018cSRaymond Chen "usbsecm_parse_intr_data: %s network!", 16710035018cSRaymond Chen wValue ? "connected to" :"disconnected from"); 16720035018cSRaymond Chen 16730035018cSRaymond Chen linkstate = wValue ? LINK_STATE_UP:LINK_STATE_DOWN; 16740035018cSRaymond Chen if (ecmp->ecm_stat.es_linkstate == linkstate) { 16750035018cSRaymond Chen /* no changes to previous state */ 16760035018cSRaymond Chen break; 16770035018cSRaymond Chen } 16780035018cSRaymond Chen 16790035018cSRaymond Chen ecmp->ecm_stat.es_linkstate = linkstate; 16800035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 16810035018cSRaymond Chen mac_link_update(ecmp->ecm_mh, linkstate); 16820035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 16830035018cSRaymond Chen 16840035018cSRaymond Chen break; 16850035018cSRaymond Chen case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE: 16860035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, 16870035018cSRaymond Chen "usbsecm_parse_intr_data: A response is a available."); 16880035018cSRaymond Chen 16890035018cSRaymond Chen break; 16900035018cSRaymond Chen case USB_CDC_NOTIFICATION_SPEED_CHANGE: 16910035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, 16920035018cSRaymond Chen "usbsecm_parse_intr_data: speed change"); 16930035018cSRaymond Chen 16940035018cSRaymond Chen /* check the parameter's length. */ 16950035018cSRaymond Chen if (wLength != 8) { 16960035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, 16970035018cSRaymond Chen "usbsecm_parse_intr_data: error data length."); 16980035018cSRaymond Chen } else { 16990035018cSRaymond Chen uint32_t us_rate, ds_rate; 17000035018cSRaymond Chen uint8_t *sp; 17010035018cSRaymond Chen 17020035018cSRaymond Chen sp = &data->b_rptr[8]; 17030035018cSRaymond Chen LE_TO_UINT32(sp, us_rate); 17040035018cSRaymond Chen sp = &data->b_rptr[12]; 17050035018cSRaymond Chen LE_TO_UINT32(sp, ds_rate); 17060035018cSRaymond Chen ecmp->ecm_stat.es_upspeed = us_rate; 17070035018cSRaymond Chen ecmp->ecm_stat.es_downspeed = ds_rate; 17080035018cSRaymond Chen } 17090035018cSRaymond Chen 17100035018cSRaymond Chen break; 17110035018cSRaymond Chen default: 17120035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, 17130035018cSRaymond Chen "usbsecm_parse_intr_data: unknown notification - 0x%x!", 17140035018cSRaymond Chen bNotification); 17150035018cSRaymond Chen 17160035018cSRaymond Chen break; 17170035018cSRaymond Chen } 17180035018cSRaymond Chen 17190035018cSRaymond Chen freemsg(data); 17200035018cSRaymond Chen } 17210035018cSRaymond Chen 17220035018cSRaymond Chen /* 17230035018cSRaymond Chen * usbecm_restore_device_state: 17240035018cSRaymond Chen * restore device state after CPR resume or reconnect 17250035018cSRaymond Chen */ 17260035018cSRaymond Chen static int 17270035018cSRaymond Chen usbecm_restore_device_state(usbecm_state_t *ecmp) 17280035018cSRaymond Chen { 17290035018cSRaymond Chen int state; 17300035018cSRaymond Chen 17310035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, 17320035018cSRaymond Chen "usbecm_restore_device_state: "); 17330035018cSRaymond Chen 17340035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 17350035018cSRaymond Chen state = ecmp->ecm_dev_state; 17360035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 17370035018cSRaymond Chen 17380035018cSRaymond Chen /* Check device status */ 17390035018cSRaymond Chen if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) { 17400035018cSRaymond Chen 17410035018cSRaymond Chen return (state); 17420035018cSRaymond Chen } 17430035018cSRaymond Chen 17440035018cSRaymond Chen /* Check if we are talking to the same device */ 17450035018cSRaymond Chen if (usb_check_same_device(ecmp->ecm_dip, ecmp->ecm_lh, USB_LOG_L0, 17460035018cSRaymond Chen -1, USB_CHK_ALL, NULL) != USB_SUCCESS) { 17470035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 17480035018cSRaymond Chen state = ecmp->ecm_dev_state = USB_DEV_DISCONNECTED; 17490035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 17500035018cSRaymond Chen 17510035018cSRaymond Chen return (state); 17520035018cSRaymond Chen } 17530035018cSRaymond Chen 17540035018cSRaymond Chen if (state == USB_DEV_DISCONNECTED) { 17550035018cSRaymond Chen USB_DPRINTF_L1(PRINT_MASK_EVENTS, ecmp->ecm_lh, 17560035018cSRaymond Chen "usbecm_restore_device_state: Device has been reconnected " 17570035018cSRaymond Chen "but data may have been lost"); 17580035018cSRaymond Chen } 17590035018cSRaymond Chen 17600035018cSRaymond Chen /* if MAC was started, restarted it */ 17610035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 17620035018cSRaymond Chen if (ecmp->ecm_mac_state == USBECM_MAC_STARTED) { 17630035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_EVENTS, ecmp->ecm_lh, 17640035018cSRaymond Chen "usbecm_restore_device_state: MAC was started"); 17650035018cSRaymond Chen 17660035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 17670035018cSRaymond Chen /* Do the same operation as usbecm_m_start() does */ 17680035018cSRaymond Chen if (usbecm_open_pipes(ecmp) != USB_SUCCESS) { 17690035018cSRaymond Chen 17700035018cSRaymond Chen return (state); 17710035018cSRaymond Chen } 17720035018cSRaymond Chen 17730035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 17740035018cSRaymond Chen if (usbecm_rx_start(ecmp) != USB_SUCCESS) { 17750035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 17760035018cSRaymond Chen 17770035018cSRaymond Chen return (state); 17780035018cSRaymond Chen } 17790035018cSRaymond Chen } 17800035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 17810035018cSRaymond Chen 17820035018cSRaymond Chen /* 17830035018cSRaymond Chen * init device state 17840035018cSRaymond Chen */ 17850035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 17860035018cSRaymond Chen state = ecmp->ecm_dev_state = USB_DEV_ONLINE; 17870035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 17880035018cSRaymond Chen 17890035018cSRaymond Chen return (state); 17900035018cSRaymond Chen } 17910035018cSRaymond Chen 17920035018cSRaymond Chen /* 17930035018cSRaymond Chen * usbecm_reconnect_event_cb: 17940035018cSRaymond Chen * called upon when the device is hotplugged back 17950035018cSRaymond Chen */ 17960035018cSRaymond Chen /*ARGSUSED*/ 17970035018cSRaymond Chen static int 17980035018cSRaymond Chen usbecm_reconnect_event_cb(dev_info_t *dip) 17990035018cSRaymond Chen { 18000035018cSRaymond Chen usbecm_state_t *ecmp = 18010035018cSRaymond Chen (usbecm_state_t *)ddi_get_soft_state(usbecm_statep, 18020035018cSRaymond Chen ddi_get_instance(dip)); 18030035018cSRaymond Chen 18040035018cSRaymond Chen ASSERT(ecmp != NULL); 18050035018cSRaymond Chen 18060035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, 18070035018cSRaymond Chen "usbecm_reconnect_event_cb: entry"); 18080035018cSRaymond Chen 18090035018cSRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 18100035018cSRaymond Chen 18110035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 18120035018cSRaymond Chen ASSERT(ecmp->ecm_dev_state == USB_DEV_DISCONNECTED); 18130035018cSRaymond Chen 18140035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 18150035018cSRaymond Chen 18160035018cSRaymond Chen if (usbecm_restore_device_state(ecmp) != USB_DEV_ONLINE) { 18170035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 18180035018cSRaymond Chen 18190035018cSRaymond Chen return (USB_FAILURE); 18200035018cSRaymond Chen } 18210035018cSRaymond Chen 18220035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 18230035018cSRaymond Chen 18240035018cSRaymond Chen return (USB_SUCCESS); 18250035018cSRaymond Chen } 18260035018cSRaymond Chen 18270035018cSRaymond Chen 18280035018cSRaymond Chen /* 18290035018cSRaymond Chen * usbecm_disconnect_event_cb: 18300035018cSRaymond Chen * callback for disconnect events 18310035018cSRaymond Chen */ 18320035018cSRaymond Chen /*ARGSUSED*/ 18330035018cSRaymond Chen static int 18340035018cSRaymond Chen usbecm_disconnect_event_cb(dev_info_t *dip) 18350035018cSRaymond Chen { 18360035018cSRaymond Chen usbecm_state_t *ecmp = (usbecm_state_t *)ddi_get_soft_state( 18370035018cSRaymond Chen usbecm_statep, ddi_get_instance(dip)); 18380035018cSRaymond Chen 18390035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, 18400035018cSRaymond Chen "usbecm_disconnect_event_cb: entry"); 18410035018cSRaymond Chen 18420035018cSRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 18430035018cSRaymond Chen 18440035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 18450035018cSRaymond Chen ecmp->ecm_dev_state = USB_DEV_DISCONNECTED; 18460035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 18470035018cSRaymond Chen 18480035018cSRaymond Chen usbecm_close_pipes(ecmp); 18490035018cSRaymond Chen 18500035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 18510035018cSRaymond Chen 18520035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, 18530035018cSRaymond Chen "usbecm_disconnect_event_cb: End"); 18540035018cSRaymond Chen 18550035018cSRaymond Chen return (USB_SUCCESS); 18560035018cSRaymond Chen } 18570035018cSRaymond Chen 18580035018cSRaymond Chen /* 18590035018cSRaymond Chen * power management 18600035018cSRaymond Chen * ---------------- 18610035018cSRaymond Chen * 18620035018cSRaymond Chen * usbecm_create_pm_components: 18630035018cSRaymond Chen * create PM components 18640035018cSRaymond Chen */ 18650035018cSRaymond Chen static int 18660035018cSRaymond Chen usbecm_create_pm_components(usbecm_state_t *ecmp) 18670035018cSRaymond Chen { 18680035018cSRaymond Chen dev_info_t *dip = ecmp->ecm_dip; 18690035018cSRaymond Chen usbecm_pm_t *pm; 18700035018cSRaymond Chen uint_t pwr_states; 18710035018cSRaymond Chen 18720035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 18730035018cSRaymond Chen "usbecm_create_pm_components: entry"); 18740035018cSRaymond Chen 18750035018cSRaymond Chen if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) { 18760035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 18770035018cSRaymond Chen "usbecm_create_pm_components: failed"); 18780035018cSRaymond Chen 18790035018cSRaymond Chen /* don't fail the attach process */ 18800035018cSRaymond Chen return (USB_SUCCESS); 18810035018cSRaymond Chen } 18820035018cSRaymond Chen 18830035018cSRaymond Chen pm = ecmp->ecm_pm = 18840035018cSRaymond Chen (usbecm_pm_t *)kmem_zalloc(sizeof (usbecm_pm_t), KM_SLEEP); 18850035018cSRaymond Chen 18860035018cSRaymond Chen pm->pm_pwr_states = (uint8_t)pwr_states; 18870035018cSRaymond Chen pm->pm_cur_power = USB_DEV_OS_FULL_PWR; 18880035018cSRaymond Chen pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip, 18890035018cSRaymond Chen USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS); 18900035018cSRaymond Chen 18910035018cSRaymond Chen (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 18920035018cSRaymond Chen 18930035018cSRaymond Chen return (USB_SUCCESS); 18940035018cSRaymond Chen } 18950035018cSRaymond Chen 18960035018cSRaymond Chen /* 18970035018cSRaymond Chen * usbecm_cleanup: 18980035018cSRaymond Chen * Release resources of current device during detach. 18990035018cSRaymond Chen */ 19000035018cSRaymond Chen static void 19010035018cSRaymond Chen usbecm_cleanup(usbecm_state_t *ecmp) 19020035018cSRaymond Chen { 19030035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, 19040035018cSRaymond Chen "usbecm_cleanup: "); 19050035018cSRaymond Chen 19060035018cSRaymond Chen if (ecmp == NULL) { 19070035018cSRaymond Chen 19080035018cSRaymond Chen return; 19090035018cSRaymond Chen } 19100035018cSRaymond Chen 19110035018cSRaymond Chen usbecm_close_pipes(ecmp); 19120035018cSRaymond Chen 19130035018cSRaymond Chen /* unregister callback function */ 19140035018cSRaymond Chen if (ecmp->ecm_init_flags & USBECM_INIT_EVENTS) { 19150035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, 19160035018cSRaymond Chen "usbecm_cleanup: unregister events"); 19170035018cSRaymond Chen 19180035018cSRaymond Chen usb_unregister_event_cbs(ecmp->ecm_dip, &usbecm_events); 19190035018cSRaymond Chen } 19200035018cSRaymond Chen 19210035018cSRaymond Chen /* destroy power management components */ 19220035018cSRaymond Chen if (ecmp->ecm_pm != NULL) { 19230035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, 19240035018cSRaymond Chen "usbecm_cleanup: destroy pm"); 19250035018cSRaymond Chen usbecm_destroy_pm_components(ecmp); 19260035018cSRaymond Chen } 19270035018cSRaymond Chen 19280035018cSRaymond Chen /* free description of device tree. */ 19290035018cSRaymond Chen if (ecmp->ecm_def_ph != NULL) { 19300035018cSRaymond Chen mutex_destroy(&ecmp->ecm_mutex); 19310035018cSRaymond Chen 19320035018cSRaymond Chen usb_free_descr_tree(ecmp->ecm_dip, ecmp->ecm_dev_data); 19330035018cSRaymond Chen ecmp->ecm_def_ph = NULL; 19340035018cSRaymond Chen } 19350035018cSRaymond Chen 19360035018cSRaymond Chen if (ecmp->ecm_lh != NULL) { 19370035018cSRaymond Chen usb_free_log_hdl(ecmp->ecm_lh); 19380035018cSRaymond Chen ecmp->ecm_lh = NULL; 19390035018cSRaymond Chen } 19400035018cSRaymond Chen 19410035018cSRaymond Chen /* detach client device */ 19420035018cSRaymond Chen if (ecmp->ecm_dev_data != NULL) { 19430035018cSRaymond Chen usb_client_detach(ecmp->ecm_dip, ecmp->ecm_dev_data); 19440035018cSRaymond Chen } 19450035018cSRaymond Chen 19460035018cSRaymond Chen if (ecmp->ecm_init_flags & USBECM_INIT_MAC) { 1947*3db80ed2SRaymond Chen (void) usbecm_mac_fini(ecmp); 19480035018cSRaymond Chen } 19490035018cSRaymond Chen 19500035018cSRaymond Chen if (ecmp->ecm_init_flags & USBECM_INIT_SER) { 19510035018cSRaymond Chen usb_fini_serialization(ecmp->ecm_ser_acc); 19520035018cSRaymond Chen } 19530035018cSRaymond Chen 19540035018cSRaymond Chen ddi_prop_remove_all(ecmp->ecm_dip); 19550035018cSRaymond Chen ddi_remove_minor_node(ecmp->ecm_dip, NULL); 19560035018cSRaymond Chen } 19570035018cSRaymond Chen 19580035018cSRaymond Chen /* 19590035018cSRaymond Chen * usbecm_destroy_pm_components: 19600035018cSRaymond Chen * destroy PM components 19610035018cSRaymond Chen */ 19620035018cSRaymond Chen static void 19630035018cSRaymond Chen usbecm_destroy_pm_components(usbecm_state_t *ecmp) 19640035018cSRaymond Chen { 19650035018cSRaymond Chen usbecm_pm_t *pm = ecmp->ecm_pm; 19660035018cSRaymond Chen dev_info_t *dip = ecmp->ecm_dip; 19670035018cSRaymond Chen int rval; 19680035018cSRaymond Chen 19690035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 19700035018cSRaymond Chen "usbecm_destroy_pm_components: "); 19710035018cSRaymond Chen 19720035018cSRaymond Chen if (ecmp->ecm_dev_state != USB_DEV_DISCONNECTED) { 19730035018cSRaymond Chen if (pm->pm_wakeup_enabled) { 19740035018cSRaymond Chen rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 19750035018cSRaymond Chen if (rval != DDI_SUCCESS) { 19760035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 19770035018cSRaymond Chen "usbecm_destroy_pm_components: " 19780035018cSRaymond Chen "raising power failed (%d)", rval); 19790035018cSRaymond Chen } 19800035018cSRaymond Chen 19810035018cSRaymond Chen rval = usb_handle_remote_wakeup(dip, 19820035018cSRaymond Chen USB_REMOTE_WAKEUP_DISABLE); 19830035018cSRaymond Chen if (rval != USB_SUCCESS) { 19840035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 19850035018cSRaymond Chen "usbecm_destroy_pm_components: " 19860035018cSRaymond Chen "disable remote wakeup failed (%d)", rval); 19870035018cSRaymond Chen } 19880035018cSRaymond Chen } 19890035018cSRaymond Chen 19900035018cSRaymond Chen (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF); 19910035018cSRaymond Chen } 19920035018cSRaymond Chen kmem_free((caddr_t)pm, sizeof (usbecm_pm_t)); 19930035018cSRaymond Chen ecmp->ecm_pm = NULL; 19940035018cSRaymond Chen } 19950035018cSRaymond Chen 19960035018cSRaymond Chen /* 19970035018cSRaymond Chen * usbecm_pm_set_busy: 19980035018cSRaymond Chen * mark device busy and raise power 19990035018cSRaymond Chen */ 20000035018cSRaymond Chen static void 20010035018cSRaymond Chen usbecm_pm_set_busy(usbecm_state_t *ecmp) 20020035018cSRaymond Chen { 20030035018cSRaymond Chen usbecm_pm_t *pm = ecmp->ecm_pm; 20040035018cSRaymond Chen dev_info_t *dip = ecmp->ecm_dip; 20050035018cSRaymond Chen int rval; 20060035018cSRaymond Chen 20070035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 20080035018cSRaymond Chen "usbecm_pm_set_busy: pm = 0x%p", (void *)pm); 20090035018cSRaymond Chen 20100035018cSRaymond Chen if (pm == NULL) { 20110035018cSRaymond Chen 20120035018cSRaymond Chen return; 20130035018cSRaymond Chen } 20140035018cSRaymond Chen 20150035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 20160035018cSRaymond Chen /* if already marked busy, just increment the counter */ 20170035018cSRaymond Chen if (pm->pm_busy_cnt++ > 0) { 20180035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 20190035018cSRaymond Chen 20200035018cSRaymond Chen return; 20210035018cSRaymond Chen } 20220035018cSRaymond Chen 20230035018cSRaymond Chen (void) pm_busy_component(dip, 0); 20240035018cSRaymond Chen 20250035018cSRaymond Chen if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) { 20260035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 20270035018cSRaymond Chen 20280035018cSRaymond Chen return; 20290035018cSRaymond Chen } 20300035018cSRaymond Chen 20310035018cSRaymond Chen /* need to raise power */ 20320035018cSRaymond Chen pm->pm_raise_power = B_TRUE; 20330035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 20340035018cSRaymond Chen 20350035018cSRaymond Chen rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 20360035018cSRaymond Chen if (rval != DDI_SUCCESS) { 20370035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 20380035018cSRaymond Chen "usbecm_pm_set_busy: raising power failed"); 20390035018cSRaymond Chen } 20400035018cSRaymond Chen 20410035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 20420035018cSRaymond Chen pm->pm_raise_power = B_FALSE; 20430035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 20440035018cSRaymond Chen } 20450035018cSRaymond Chen 20460035018cSRaymond Chen 20470035018cSRaymond Chen /* 20480035018cSRaymond Chen * usbecm_pm_set_idle: 20490035018cSRaymond Chen * mark device idle 20500035018cSRaymond Chen */ 20510035018cSRaymond Chen static void 20520035018cSRaymond Chen usbecm_pm_set_idle(usbecm_state_t *ecmp) 20530035018cSRaymond Chen { 20540035018cSRaymond Chen usbecm_pm_t *pm = ecmp->ecm_pm; 20550035018cSRaymond Chen dev_info_t *dip = ecmp->ecm_dip; 20560035018cSRaymond Chen 20570035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 20580035018cSRaymond Chen "usbecm_pm_set_idle: "); 20590035018cSRaymond Chen 20600035018cSRaymond Chen if (pm == NULL) { 20610035018cSRaymond Chen 20620035018cSRaymond Chen return; 20630035018cSRaymond Chen } 20640035018cSRaymond Chen 20650035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 20660035018cSRaymond Chen if (--pm->pm_busy_cnt > 0) { 20670035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 20680035018cSRaymond Chen 20690035018cSRaymond Chen return; 20700035018cSRaymond Chen } 20710035018cSRaymond Chen 20720035018cSRaymond Chen if (pm) { 20730035018cSRaymond Chen (void) pm_idle_component(dip, 0); 20740035018cSRaymond Chen } 20750035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 20760035018cSRaymond Chen } 20770035018cSRaymond Chen 20780035018cSRaymond Chen 20790035018cSRaymond Chen /* 20800035018cSRaymond Chen * usbecm_pwrlvl0: 20810035018cSRaymond Chen * Functions to handle power transition for OS levels 0 -> 3 20820035018cSRaymond Chen * The same level as OS state, different from USB state 20830035018cSRaymond Chen */ 20840035018cSRaymond Chen static int 20850035018cSRaymond Chen usbecm_pwrlvl0(usbecm_state_t *ecmp) 20860035018cSRaymond Chen { 20870035018cSRaymond Chen int rval; 20880035018cSRaymond Chen 20890035018cSRaymond Chen ASSERT(mutex_owned(&ecmp->ecm_mutex)); 20900035018cSRaymond Chen 20910035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 20920035018cSRaymond Chen "usbecm_pwrlvl0: "); 20930035018cSRaymond Chen 20940035018cSRaymond Chen switch (ecmp->ecm_dev_state) { 20950035018cSRaymond Chen case USB_DEV_ONLINE: 20960035018cSRaymond Chen /* issue USB D3 command to the device */ 20970035018cSRaymond Chen rval = usb_set_device_pwrlvl3(ecmp->ecm_dip); 20980035018cSRaymond Chen ASSERT(rval == USB_SUCCESS); 20990035018cSRaymond Chen if ((ecmp->ecm_intr_ph != NULL) && 21000035018cSRaymond Chen (ecmp->ecm_intr_state == USBECM_PIPE_BUSY)) { 21010035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 21020035018cSRaymond Chen usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph, 21030035018cSRaymond Chen USB_FLAGS_SLEEP); 21040035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 21050035018cSRaymond Chen 21060035018cSRaymond Chen ecmp->ecm_intr_state = USBECM_PIPE_IDLE; 21070035018cSRaymond Chen } 21080035018cSRaymond Chen ecmp->ecm_dev_state = USB_DEV_PWRED_DOWN; 21090035018cSRaymond Chen ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF; 21100035018cSRaymond Chen 21110035018cSRaymond Chen /* FALLTHRU */ 21120035018cSRaymond Chen case USB_DEV_DISCONNECTED: 21130035018cSRaymond Chen case USB_DEV_SUSPENDED: 21140035018cSRaymond Chen /* allow a disconnect/cpr'ed device to go to lower power */ 21150035018cSRaymond Chen 21160035018cSRaymond Chen return (USB_SUCCESS); 21170035018cSRaymond Chen case USB_DEV_PWRED_DOWN: 21180035018cSRaymond Chen default: 21190035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 21200035018cSRaymond Chen "usbecm_pwrlvl0: illegal device state"); 21210035018cSRaymond Chen 21220035018cSRaymond Chen return (USB_FAILURE); 21230035018cSRaymond Chen } 21240035018cSRaymond Chen } 21250035018cSRaymond Chen 21260035018cSRaymond Chen 21270035018cSRaymond Chen /* 21280035018cSRaymond Chen * usbecm_pwrlvl1: 21290035018cSRaymond Chen * Functions to handle power transition for OS levels 1 -> 2 21300035018cSRaymond Chen */ 21310035018cSRaymond Chen static int 21320035018cSRaymond Chen usbecm_pwrlvl1(usbecm_state_t *ecmp) 21330035018cSRaymond Chen { 21340035018cSRaymond Chen /* issue USB D2 command to the device */ 21350035018cSRaymond Chen (void) usb_set_device_pwrlvl2(ecmp->ecm_dip); 21360035018cSRaymond Chen 21370035018cSRaymond Chen return (USB_FAILURE); 21380035018cSRaymond Chen } 21390035018cSRaymond Chen 21400035018cSRaymond Chen 21410035018cSRaymond Chen /* 21420035018cSRaymond Chen * usbecm_pwrlvl2: 21430035018cSRaymond Chen * Functions to handle power transition for OS levels 2 -> 1 21440035018cSRaymond Chen */ 21450035018cSRaymond Chen static int 21460035018cSRaymond Chen usbecm_pwrlvl2(usbecm_state_t *ecmp) 21470035018cSRaymond Chen { 21480035018cSRaymond Chen /* issue USB D1 command to the device */ 21490035018cSRaymond Chen (void) usb_set_device_pwrlvl1(ecmp->ecm_dip); 21500035018cSRaymond Chen 21510035018cSRaymond Chen return (USB_FAILURE); 21520035018cSRaymond Chen } 21530035018cSRaymond Chen 21540035018cSRaymond Chen 21550035018cSRaymond Chen /* 21560035018cSRaymond Chen * usbecm_pwrlvl3: 21570035018cSRaymond Chen * Functions to handle power transition for OS levels 3 -> 0 21580035018cSRaymond Chen * The same level as OS state, different from USB state 21590035018cSRaymond Chen */ 21600035018cSRaymond Chen static int 21610035018cSRaymond Chen usbecm_pwrlvl3(usbecm_state_t *ecmp) 21620035018cSRaymond Chen { 21630035018cSRaymond Chen int rval; 21640035018cSRaymond Chen 21650035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 21660035018cSRaymond Chen "usbecm_pwrlvl3: "); 21670035018cSRaymond Chen 21680035018cSRaymond Chen ASSERT(mutex_owned(&ecmp->ecm_mutex)); 21690035018cSRaymond Chen 21700035018cSRaymond Chen switch (ecmp->ecm_dev_state) { 21710035018cSRaymond Chen case USB_DEV_PWRED_DOWN: 21720035018cSRaymond Chen /* Issue USB D0 command to the device here */ 21730035018cSRaymond Chen rval = usb_set_device_pwrlvl0(ecmp->ecm_dip); 21740035018cSRaymond Chen ASSERT(rval == USB_SUCCESS); 21750035018cSRaymond Chen 21760035018cSRaymond Chen if (ecmp->ecm_intr_ph != NULL && 21770035018cSRaymond Chen ecmp->ecm_intr_state == USBECM_PIPE_IDLE) { 21780035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 21790035018cSRaymond Chen usbecm_pipe_start_polling(ecmp); 21800035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 21810035018cSRaymond Chen } 21820035018cSRaymond Chen 21830035018cSRaymond Chen ecmp->ecm_dev_state = USB_DEV_ONLINE; 21840035018cSRaymond Chen ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR; 21850035018cSRaymond Chen 21860035018cSRaymond Chen /* FALLTHRU */ 21870035018cSRaymond Chen case USB_DEV_ONLINE: 21880035018cSRaymond Chen /* we are already in full power */ 21890035018cSRaymond Chen 21900035018cSRaymond Chen /* FALLTHRU */ 21910035018cSRaymond Chen case USB_DEV_DISCONNECTED: 21920035018cSRaymond Chen case USB_DEV_SUSPENDED: 21930035018cSRaymond Chen 21940035018cSRaymond Chen return (USB_SUCCESS); 21950035018cSRaymond Chen default: 21960035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 21970035018cSRaymond Chen "usbecm_pwrlvl3: illegal device state"); 21980035018cSRaymond Chen 21990035018cSRaymond Chen return (USB_FAILURE); 22000035018cSRaymond Chen } 22010035018cSRaymond Chen } 22020035018cSRaymond Chen 22030035018cSRaymond Chen /*ARGSUSED*/ 22040035018cSRaymond Chen static int 22050035018cSRaymond Chen usbecm_power(dev_info_t *dip, int comp, int level) 22060035018cSRaymond Chen { 22070035018cSRaymond Chen usbecm_state_t *ecmp; 22080035018cSRaymond Chen usbecm_pm_t *pm; 22090035018cSRaymond Chen int rval = USB_SUCCESS; 22100035018cSRaymond Chen 22110035018cSRaymond Chen ecmp = ddi_get_soft_state(usbecm_statep, ddi_get_instance(dip)); 22120035018cSRaymond Chen pm = ecmp->ecm_pm; 22130035018cSRaymond Chen 22140035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 22150035018cSRaymond Chen "usbecm_power: entry"); 22160035018cSRaymond Chen 22170035018cSRaymond Chen /* check if pm is NULL */ 22180035018cSRaymond Chen if (pm == NULL) { 22190035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 22200035018cSRaymond Chen "usbecm_power: pm is NULL."); 22210035018cSRaymond Chen 22220035018cSRaymond Chen return (USB_FAILURE); 22230035018cSRaymond Chen } 22240035018cSRaymond Chen 22250035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 22260035018cSRaymond Chen /* 22270035018cSRaymond Chen * check if we are transitioning to a legal power level 22280035018cSRaymond Chen */ 22290035018cSRaymond Chen if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) { 22300035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 22310035018cSRaymond Chen "usbecm_power: " 22320035018cSRaymond Chen "illegal power level %d, pwr_states=%x", 22330035018cSRaymond Chen level, pm->pm_pwr_states); 22340035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 22350035018cSRaymond Chen 22360035018cSRaymond Chen return (USB_FAILURE); 22370035018cSRaymond Chen } 22380035018cSRaymond Chen 22390035018cSRaymond Chen /* 22400035018cSRaymond Chen * if we are about to raise power and asked to lower power, fail 22410035018cSRaymond Chen */ 22420035018cSRaymond Chen if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) { 22430035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, 22440035018cSRaymond Chen "usbecm_power: wrong condition."); 22450035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 22460035018cSRaymond Chen 22470035018cSRaymond Chen return (USB_FAILURE); 22480035018cSRaymond Chen } 22490035018cSRaymond Chen 22500035018cSRaymond Chen /* 22510035018cSRaymond Chen * Set the power status of device by request level. 22520035018cSRaymond Chen */ 22530035018cSRaymond Chen switch (level) { 22540035018cSRaymond Chen case USB_DEV_OS_PWR_OFF: 22550035018cSRaymond Chen rval = usbecm_pwrlvl0(ecmp); 22560035018cSRaymond Chen 22570035018cSRaymond Chen break; 22580035018cSRaymond Chen case USB_DEV_OS_PWR_1: 22590035018cSRaymond Chen rval = usbecm_pwrlvl1(ecmp); 22600035018cSRaymond Chen 22610035018cSRaymond Chen break; 22620035018cSRaymond Chen case USB_DEV_OS_PWR_2: 22630035018cSRaymond Chen rval = usbecm_pwrlvl2(ecmp); 22640035018cSRaymond Chen 22650035018cSRaymond Chen break; 22660035018cSRaymond Chen case USB_DEV_OS_FULL_PWR: 22670035018cSRaymond Chen rval = usbecm_pwrlvl3(ecmp); 22680035018cSRaymond Chen 22690035018cSRaymond Chen break; 22700035018cSRaymond Chen } 22710035018cSRaymond Chen 22720035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 22730035018cSRaymond Chen 22740035018cSRaymond Chen return (rval); 22750035018cSRaymond Chen } 22760035018cSRaymond Chen 22770035018cSRaymond Chen /* 22780035018cSRaymond Chen * Register with the MAC layer. 22790035018cSRaymond Chen */ 22800035018cSRaymond Chen static int 22810035018cSRaymond Chen usbecm_mac_init(usbecm_state_t *ecmp) 22820035018cSRaymond Chen { 22830035018cSRaymond Chen mac_register_t *macp; 22840035018cSRaymond Chen int err; 22850035018cSRaymond Chen 22860035018cSRaymond Chen /* 22870035018cSRaymond Chen * Initialize mac structure 22880035018cSRaymond Chen */ 22890035018cSRaymond Chen macp = mac_alloc(MAC_VERSION); 22900035018cSRaymond Chen if (macp == NULL) { 22910035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 22920035018cSRaymond Chen "failed to allocate MAC structure"); 22930035018cSRaymond Chen 22940035018cSRaymond Chen return (USB_FAILURE); 22950035018cSRaymond Chen } 22960035018cSRaymond Chen 22970035018cSRaymond Chen /* 22980035018cSRaymond Chen * Initialize pointer to device specific functions 22990035018cSRaymond Chen */ 23000035018cSRaymond Chen macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 23010035018cSRaymond Chen macp->m_driver = ecmp; 23020035018cSRaymond Chen macp->m_dip = ecmp->ecm_dip; 23030035018cSRaymond Chen 23040035018cSRaymond Chen macp->m_src_addr = ecmp->ecm_srcaddr; 23050035018cSRaymond Chen macp->m_callbacks = &usbecm_m_callbacks; 23060035018cSRaymond Chen macp->m_min_sdu = 0; 23070035018cSRaymond Chen macp->m_max_sdu = ETHERMTU; 23080035018cSRaymond Chen 23090035018cSRaymond Chen /* 23100035018cSRaymond Chen * Register the macp to mac 23110035018cSRaymond Chen */ 23120035018cSRaymond Chen err = mac_register(macp, &ecmp->ecm_mh); 23130035018cSRaymond Chen mac_free(macp); 23140035018cSRaymond Chen 23150035018cSRaymond Chen if (err != DDI_SUCCESS) { 23160035018cSRaymond Chen USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh, 23170035018cSRaymond Chen "failed to register MAC structure"); 23180035018cSRaymond Chen 23190035018cSRaymond Chen return (USB_FAILURE); 23200035018cSRaymond Chen } 23210035018cSRaymond Chen 23220035018cSRaymond Chen mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN); 23230035018cSRaymond Chen ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN; 23240035018cSRaymond Chen ecmp->ecm_tx_cnt = 0; 23250035018cSRaymond Chen 23260035018cSRaymond Chen return (USB_SUCCESS); 23270035018cSRaymond Chen } 23280035018cSRaymond Chen 23290035018cSRaymond Chen static int 23300035018cSRaymond Chen usbecm_mac_fini(usbecm_state_t *ecmp) 23310035018cSRaymond Chen { 23320035018cSRaymond Chen int rval = DDI_SUCCESS; 23330035018cSRaymond Chen 23340035018cSRaymond Chen if ((ecmp->ecm_init_flags & USBECM_INIT_MAC) == 0) { 23350035018cSRaymond Chen return (DDI_SUCCESS); 23360035018cSRaymond Chen } 23370035018cSRaymond Chen 23380035018cSRaymond Chen ecmp->ecm_init_flags &= ~USBECM_INIT_MAC; 23390035018cSRaymond Chen if ((rval = mac_disable(ecmp->ecm_mh)) != 0) { 23400035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 23410035018cSRaymond Chen "failed to disable MAC"); 23420035018cSRaymond Chen 23430035018cSRaymond Chen return (rval); 23440035018cSRaymond Chen } 23450035018cSRaymond Chen 23460035018cSRaymond Chen (void) mac_unregister(ecmp->ecm_mh); 23470035018cSRaymond Chen 23480035018cSRaymond Chen return (rval); 23490035018cSRaymond Chen } 23500035018cSRaymond Chen 23510035018cSRaymond Chen static int 23520035018cSRaymond Chen usbecm_resume(usbecm_state_t *ecmp) 23530035018cSRaymond Chen { 23540035018cSRaymond Chen int current_state; 23550035018cSRaymond Chen int ret; 23560035018cSRaymond Chen 23570035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, 23580035018cSRaymond Chen "usbecm_resume: "); 23590035018cSRaymond Chen 23600035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 23610035018cSRaymond Chen current_state = ecmp->ecm_dev_state; 23620035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 23630035018cSRaymond Chen 23640035018cSRaymond Chen /* restore the status of device */ 23650035018cSRaymond Chen if (current_state != USB_DEV_ONLINE) { 23660035018cSRaymond Chen ret = usbecm_restore_device_state(ecmp); 23670035018cSRaymond Chen } else { 23680035018cSRaymond Chen ret = USB_DEV_ONLINE; 23690035018cSRaymond Chen } 23700035018cSRaymond Chen 23710035018cSRaymond Chen return (ret); 23720035018cSRaymond Chen } 23730035018cSRaymond Chen 23740035018cSRaymond Chen static int 23750035018cSRaymond Chen usbecm_suspend(usbecm_state_t *ecmp) 23760035018cSRaymond Chen { 23770035018cSRaymond Chen (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); 23780035018cSRaymond Chen 23790035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 23800035018cSRaymond Chen ecmp->ecm_dev_state = USB_DEV_SUSPENDED; 23810035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 23820035018cSRaymond Chen 23830035018cSRaymond Chen usbecm_close_pipes(ecmp); 23840035018cSRaymond Chen 23850035018cSRaymond Chen usb_release_access(ecmp->ecm_ser_acc); 23860035018cSRaymond Chen 23870035018cSRaymond Chen return (0); 23880035018cSRaymond Chen } 23890035018cSRaymond Chen 23900035018cSRaymond Chen /* 23910035018cSRaymond Chen * Translate MAC address from string to 6 bytes array int value 23920035018cSRaymond Chen * Can't use ether_aton() since it requires format of x:x:x:x:x:x 23930035018cSRaymond Chen */ 23940035018cSRaymond Chen void 23950035018cSRaymond Chen label_to_mac(char *hex, unsigned char *mac) 23960035018cSRaymond Chen { 23970035018cSRaymond Chen int i; 23980035018cSRaymond Chen char c; 23990035018cSRaymond Chen 24000035018cSRaymond Chen /* can only count 6 bytes! */ 24010035018cSRaymond Chen for (i = 0; i < 6; i++) { 24020035018cSRaymond Chen /* upper 4 bits */ 24030035018cSRaymond Chen if (!isdigit(hex[2*i])) { 24040035018cSRaymond Chen c = (toupper(hex[2 * i]) - 'A' + 10); 24050035018cSRaymond Chen } else { 24060035018cSRaymond Chen c = (hex[2 * i] - '0'); 24070035018cSRaymond Chen } 24080035018cSRaymond Chen mac[i] = c * 16; 24090035018cSRaymond Chen 24100035018cSRaymond Chen /* lower 4 bits */ 24110035018cSRaymond Chen if (!isdigit(hex[2*i + 1])) { 24120035018cSRaymond Chen c = (toupper(hex[2 * i + 1]) - 'A' + 10); 24130035018cSRaymond Chen } else { 24140035018cSRaymond Chen c = hex[2 * i + 1] - '0'; 24150035018cSRaymond Chen } 24160035018cSRaymond Chen mac[i] += c; 24170035018cSRaymond Chen } 24180035018cSRaymond Chen } 24190035018cSRaymond Chen 24200035018cSRaymond Chen /* 24210035018cSRaymond Chen * usbecm_get_descriptors: 24220035018cSRaymond Chen * parse functional descriptors of ecm compatible device 24230035018cSRaymond Chen */ 24240035018cSRaymond Chen static int 24250035018cSRaymond Chen usbecm_get_descriptors(usbecm_state_t *ecmp) 24260035018cSRaymond Chen { 24270035018cSRaymond Chen int i; 24280035018cSRaymond Chen usb_cfg_data_t *cfg; 24290035018cSRaymond Chen usb_alt_if_data_t *altif; 24300035018cSRaymond Chen usb_cvs_data_t *cvs; 24310035018cSRaymond Chen int16_t master_if = -1, slave_if = -1; 24320035018cSRaymond Chen usb_cdc_ecm_descr_t ecm_desc; 24330035018cSRaymond Chen usb_ep_data_t *ep_data; 24340035018cSRaymond Chen usb_dev_descr_t *usb_dev_desc; 24350035018cSRaymond Chen 24360035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, 24370035018cSRaymond Chen "usbecm_get_descriptors: "); 24380035018cSRaymond Chen 24390035018cSRaymond Chen usb_dev_desc = ecmp->ecm_dev_data->dev_descr; 24400035018cSRaymond Chen 24410035018cSRaymond Chen /* 24420035018cSRaymond Chen * Special treatment of Sun's SP Ethernet device. 24430035018cSRaymond Chen */ 24440035018cSRaymond Chen if ((usb_dev_desc->idVendor == SUN_SP_VENDOR_ID) && 24450035018cSRaymond Chen (usb_dev_desc->idProduct == SUN_SP_PRODUCT_ID)) { 24460035018cSRaymond Chen if (usb_set_cfg(ecmp->ecm_dip, ecmp->ecm_cfg_index, 24470035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS) { 24480035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 24490035018cSRaymond Chen "usbecm_get_descriptors: fail to set cfg "); 24500035018cSRaymond Chen } else { 24510035018cSRaymond Chen usb_free_dev_data(ecmp->ecm_dip, ecmp->ecm_dev_data); 24520035018cSRaymond Chen if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data, 24530035018cSRaymond Chen USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) { 24540035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 24550035018cSRaymond Chen "usbecm_get_descriptors: fail to get" 24560035018cSRaymond Chen " dev_data"); 24570035018cSRaymond Chen 24580035018cSRaymond Chen return (USB_FAILURE); 24590035018cSRaymond Chen } 24600035018cSRaymond Chen } 24610035018cSRaymond Chen } 24620035018cSRaymond Chen 24630035018cSRaymond Chen cfg = ecmp->ecm_dev_data->dev_curr_cfg; 24640035018cSRaymond Chen 24650035018cSRaymond Chen /* set default control and data interface */ 24660035018cSRaymond Chen ecmp->ecm_ctrl_if_no = ecmp->ecm_data_if_no = 0; 24670035018cSRaymond Chen 24680035018cSRaymond Chen /* get current interfaces */ 24690035018cSRaymond Chen ecmp->ecm_ctrl_if_no = ecmp->ecm_dev_data->dev_curr_if; 24700035018cSRaymond Chen if (cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt == 0) { 24710035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 24720035018cSRaymond Chen "usbecm_get_descriptors: elements in if_alt is %d", 24730035018cSRaymond Chen cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt); 24740035018cSRaymond Chen 24750035018cSRaymond Chen return (USB_FAILURE); 24760035018cSRaymond Chen } 24770035018cSRaymond Chen 24780035018cSRaymond Chen altif = &cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_alt[0]; 24790035018cSRaymond Chen 24800035018cSRaymond Chen /* 24810035018cSRaymond Chen * Based on CDC specification, ECM devices usually include the 24820035018cSRaymond Chen * following function descriptors: Header, Union and ECM 24830035018cSRaymond Chen * Contry Selection function descriptors. This loop search tree data 24840035018cSRaymond Chen * structure for each ecm class descriptor. 24850035018cSRaymond Chen */ 24860035018cSRaymond Chen for (i = 0; i < altif->altif_n_cvs; i++) { 24870035018cSRaymond Chen cvs = &altif->altif_cvs[i]; 24880035018cSRaymond Chen 24890035018cSRaymond Chen if ((cvs->cvs_buf == NULL) || 24900035018cSRaymond Chen (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) { 24910035018cSRaymond Chen continue; 24920035018cSRaymond Chen } 24930035018cSRaymond Chen 24940035018cSRaymond Chen switch (cvs->cvs_buf[2]) { 24950035018cSRaymond Chen case USB_CDC_DESCR_TYPE_HEADER: 24960035018cSRaymond Chen /* 24970035018cSRaymond Chen * parse header functional descriptor 24980035018cSRaymond Chen * Just to check integrity. 24990035018cSRaymond Chen */ 25000035018cSRaymond Chen if (cvs->cvs_buf_len != 5) { 25010035018cSRaymond Chen return (USB_FAILURE); 25020035018cSRaymond Chen } 25030035018cSRaymond Chen break; 25040035018cSRaymond Chen case USB_CDC_DESCR_TYPE_ETHERNET: 25050035018cSRaymond Chen /* parse ECM functional descriptor */ 25060035018cSRaymond Chen if (cvs->cvs_buf_len >= USB_CDC_ECM_LEN) { 25070035018cSRaymond Chen char buf[USB_MAXSTRINGLEN]; 25080035018cSRaymond Chen 25090035018cSRaymond Chen if (usb_parse_data("4cl2sc", cvs->cvs_buf, 25100035018cSRaymond Chen cvs->cvs_buf_len, (void *)&ecm_desc, 25110035018cSRaymond Chen (size_t)USB_CDC_ECM_LEN) < 25120035018cSRaymond Chen USB_CDC_ECM_LEN) { 25130035018cSRaymond Chen 25140035018cSRaymond Chen return (USB_FAILURE); 25150035018cSRaymond Chen } 25160035018cSRaymond Chen 25170035018cSRaymond Chen /* get the MAC address */ 25180035018cSRaymond Chen if (usb_get_string_descr(ecmp->ecm_dip, 25190035018cSRaymond Chen USB_LANG_ID, ecm_desc.iMACAddress, buf, 25200035018cSRaymond Chen USB_MAXSTRINGLEN) != USB_SUCCESS) { 25210035018cSRaymond Chen 25220035018cSRaymond Chen return (USB_FAILURE); 25230035018cSRaymond Chen } 25240035018cSRaymond Chen 25250035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, 25260035018cSRaymond Chen "usbecm_get_descriptors: macaddr=%s ", 25270035018cSRaymond Chen buf); 25280035018cSRaymond Chen 25290035018cSRaymond Chen /* expects 12 characters */ 25300035018cSRaymond Chen if (strlen(buf) < 12) { 25310035018cSRaymond Chen return (USB_FAILURE); 25320035018cSRaymond Chen } 25330035018cSRaymond Chen label_to_mac(buf, ecmp->ecm_srcaddr); 25340035018cSRaymond Chen 25350035018cSRaymond Chen bcopy(&ecm_desc, &ecmp->ecm_desc, 25360035018cSRaymond Chen USB_CDC_ECM_LEN); 25370035018cSRaymond Chen } 25380035018cSRaymond Chen break; 25390035018cSRaymond Chen case USB_CDC_DESCR_TYPE_UNION: 25400035018cSRaymond Chen /* parse Union functional descriptor. */ 25410035018cSRaymond Chen if (cvs->cvs_buf_len >= 5) { 25420035018cSRaymond Chen master_if = cvs->cvs_buf[3]; 25430035018cSRaymond Chen slave_if = cvs->cvs_buf[4]; 25440035018cSRaymond Chen } 25450035018cSRaymond Chen break; 25460035018cSRaymond Chen default: 25470035018cSRaymond Chen break; 25480035018cSRaymond Chen } 25490035018cSRaymond Chen } 25500035018cSRaymond Chen 25510035018cSRaymond Chen /* For usb ecm devices, it must satisfy the following options. */ 25520035018cSRaymond Chen if (cfg->cfg_n_if < 2) { 25530035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 25540035018cSRaymond Chen "usbecm_get_descriptors: # of interfaces %d < 2", 25550035018cSRaymond Chen cfg->cfg_n_if); 25560035018cSRaymond Chen 25570035018cSRaymond Chen return (USB_FAILURE); 25580035018cSRaymond Chen } 25590035018cSRaymond Chen 25600035018cSRaymond Chen if (ecmp->ecm_data_if_no == 0 && 25610035018cSRaymond Chen slave_if != ecmp->ecm_data_if_no) { 25620035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 25630035018cSRaymond Chen "usbecm_get_descriptors: Device has no call management " 25640035018cSRaymond Chen "descriptor and use Union Descriptor."); 25650035018cSRaymond Chen 25660035018cSRaymond Chen ecmp->ecm_data_if_no = slave_if; 25670035018cSRaymond Chen } 25680035018cSRaymond Chen 25690035018cSRaymond Chen if ((master_if != ecmp->ecm_ctrl_if_no) || 25700035018cSRaymond Chen (slave_if != ecmp->ecm_data_if_no)) { 25710035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 25720035018cSRaymond Chen "usbecm_get_descriptors: control interface or " 25730035018cSRaymond Chen "data interface don't match."); 25740035018cSRaymond Chen 25750035018cSRaymond Chen return (USB_FAILURE); 25760035018cSRaymond Chen } 25770035018cSRaymond Chen 25780035018cSRaymond Chen if ((ecmp->ecm_ctrl_if_no >= cfg->cfg_n_if) || 25790035018cSRaymond Chen (ecmp->ecm_data_if_no >= cfg->cfg_n_if)) { 25800035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 25810035018cSRaymond Chen "usbecm_get_descriptors: control interface %d or " 25820035018cSRaymond Chen "data interface %d out of range.", 25830035018cSRaymond Chen ecmp->ecm_ctrl_if_no, ecmp->ecm_data_if_no); 25840035018cSRaymond Chen 25850035018cSRaymond Chen return (USB_FAILURE); 25860035018cSRaymond Chen } 25870035018cSRaymond Chen 25880035018cSRaymond Chen /* ECM data interface has a minimal of two altsettings */ 25890035018cSRaymond Chen if (cfg->cfg_if[ecmp->ecm_data_if_no].if_n_alt < 2) { 25900035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 25910035018cSRaymond Chen "usbecm_get_descriptors: elements in if_alt is %d," 25920035018cSRaymond Chen " MUST >= 2", cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt); 25930035018cSRaymond Chen 25940035018cSRaymond Chen return (USB_FAILURE); 25950035018cSRaymond Chen } 25960035018cSRaymond Chen 25970035018cSRaymond Chen /* control interface must have interrupt endpoint */ 25980035018cSRaymond Chen if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data, 25990035018cSRaymond Chen ecmp->ecm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR, 26000035018cSRaymond Chen USB_EP_DIR_IN)) == NULL) { 26010035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 26020035018cSRaymond Chen "usbecm_get_descriptors: " 26030035018cSRaymond Chen "ctrl interface %d has no interrupt endpoint", 26040035018cSRaymond Chen ecmp->ecm_data_if_no); 26050035018cSRaymond Chen 26060035018cSRaymond Chen return (USB_FAILURE); 26070035018cSRaymond Chen } 26080035018cSRaymond Chen ecmp->ecm_intr_ep = ep_data; 26090035018cSRaymond Chen 26100035018cSRaymond Chen /* data interface alt 1 must have bulk in and out(ECM v1.2,p5) */ 26110035018cSRaymond Chen if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data, 26120035018cSRaymond Chen ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK, 26130035018cSRaymond Chen USB_EP_DIR_IN)) == NULL) { 26140035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 26150035018cSRaymond Chen "usbecm_get_descriptors: " 26160035018cSRaymond Chen "data interface %d has no bulk in endpoint", 26170035018cSRaymond Chen ecmp->ecm_data_if_no); 26180035018cSRaymond Chen 26190035018cSRaymond Chen return (USB_FAILURE); 26200035018cSRaymond Chen } 26210035018cSRaymond Chen ecmp->ecm_bulk_in_ep = ep_data; 26220035018cSRaymond Chen 26230035018cSRaymond Chen if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data, 26240035018cSRaymond Chen ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK, 26250035018cSRaymond Chen USB_EP_DIR_OUT)) == NULL) { 26260035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 26270035018cSRaymond Chen "usbecm_get_descriptors: " 26280035018cSRaymond Chen "data interface %d has no bulk out endpoint", 26290035018cSRaymond Chen ecmp->ecm_data_if_no); 26300035018cSRaymond Chen 26310035018cSRaymond Chen return (USB_FAILURE); 26320035018cSRaymond Chen } 26330035018cSRaymond Chen ecmp->ecm_bulk_out_ep = ep_data; 26340035018cSRaymond Chen 26350035018cSRaymond Chen /* set default value for ethernet packet filter */ 26360035018cSRaymond Chen ecmp->ecm_pkt_flt = CDC_ECM_PKT_TYPE_DIRECTED; 26370035018cSRaymond Chen 26380035018cSRaymond Chen return (USB_SUCCESS); 26390035018cSRaymond Chen } 26400035018cSRaymond Chen 26410035018cSRaymond Chen /* Generate IEEE802 style MAC address */ 26420035018cSRaymond Chen static void 26430035018cSRaymond Chen generate_ether_addr(uint8_t *mac_addr) 26440035018cSRaymond Chen { 2645*3db80ed2SRaymond Chen (void) random_get_bytes(mac_addr, 6); 26460035018cSRaymond Chen mac_addr [0] &= 0xfe; /* unicast only */ 26470035018cSRaymond Chen mac_addr [0] |= 0x02; /* set locally administered bit */ 26480035018cSRaymond Chen } 26490035018cSRaymond Chen 26500035018cSRaymond Chen /* 26510035018cSRaymond Chen * Find a pair of bulk In/Out endpoints 26520035018cSRaymond Chen */ 26530035018cSRaymond Chen int usbecm_find_bulk_in_out_eps(usbecm_state_t *ecmp, 26540035018cSRaymond Chen uint16_t ifc, usb_if_data_t *intf) 26550035018cSRaymond Chen { 26560035018cSRaymond Chen uint16_t alt, alt_num; 26570035018cSRaymond Chen usb_ep_data_t *intr_ep = NULL; 26580035018cSRaymond Chen usb_ep_data_t *bulk_in, *bulk_out, *ep; 26590035018cSRaymond Chen 26600035018cSRaymond Chen alt_num = intf->if_n_alt; 26610035018cSRaymond Chen 26620035018cSRaymond Chen /* 26630035018cSRaymond Chen * for the non-compatible devices, to make it simple, we 26640035018cSRaymond Chen * suppose the devices have this kind of configuration: 26650035018cSRaymond Chen * INTR In EP(if exists) + BULK In + Bulk Out in the 26660035018cSRaymond Chen * same altsetting of the same interface 26670035018cSRaymond Chen */ 26680035018cSRaymond Chen for (alt = 0; alt < alt_num; alt++) { 26690035018cSRaymond Chen /* search pair of bulk in/out EPs */ 26700035018cSRaymond Chen if (((bulk_in = usb_lookup_ep_data(ecmp->ecm_dip, 26710035018cSRaymond Chen ecmp->ecm_dev_data, ifc, alt, 0, 26720035018cSRaymond Chen USB_EP_ATTR_BULK, 26730035018cSRaymond Chen USB_EP_DIR_IN)) == NULL) || 26740035018cSRaymond Chen (bulk_out = usb_lookup_ep_data(ecmp->ecm_dip, 26750035018cSRaymond Chen ecmp->ecm_dev_data, ifc, alt, 0, 26760035018cSRaymond Chen USB_EP_ATTR_BULK, 26770035018cSRaymond Chen USB_EP_DIR_OUT)) == NULL) { 26780035018cSRaymond Chen 26790035018cSRaymond Chen continue; 26800035018cSRaymond Chen } 26810035018cSRaymond Chen 26820035018cSRaymond Chen /* 26830035018cSRaymond Chen * search interrupt pipe. 26840035018cSRaymond Chen */ 26850035018cSRaymond Chen if ((ep = usb_lookup_ep_data(ecmp->ecm_dip, 26860035018cSRaymond Chen ecmp->ecm_dev_data, ifc, alt, 0, 26870035018cSRaymond Chen USB_EP_ATTR_INTR, USB_EP_DIR_IN)) != NULL) { 26880035018cSRaymond Chen intr_ep = ep; 26890035018cSRaymond Chen } 26900035018cSRaymond Chen 26910035018cSRaymond Chen 26920035018cSRaymond Chen ecmp->ecm_data_if_no = ifc; 26930035018cSRaymond Chen ecmp->ecm_data_if_alt = alt; 26940035018cSRaymond Chen ecmp->ecm_intr_ep = intr_ep; 26950035018cSRaymond Chen ecmp->ecm_ctrl_if_no = ifc; 26960035018cSRaymond Chen ecmp->ecm_bulk_in_ep = bulk_in; 26970035018cSRaymond Chen ecmp->ecm_bulk_out_ep = bulk_out; 26980035018cSRaymond Chen 26990035018cSRaymond Chen return (USB_SUCCESS); 27000035018cSRaymond Chen } 27010035018cSRaymond Chen 27020035018cSRaymond Chen return (USB_FAILURE); 27030035018cSRaymond Chen } 27040035018cSRaymond Chen 27050035018cSRaymond Chen static int 27060035018cSRaymond Chen usbecm_init_non_compatible_device(usbecm_state_t *ecmp) 27070035018cSRaymond Chen { 27080035018cSRaymond Chen usb_if_data_t *cur_if; 27090035018cSRaymond Chen uint16_t if_num, i; 27100035018cSRaymond Chen 27110035018cSRaymond Chen /* 27120035018cSRaymond Chen * If device don't conform to spec, search pairs of bulk in/out 27130035018cSRaymond Chen * endpoints and fill related structure. We suppose this driver 27140035018cSRaymond Chen * is bound to a interface. 27150035018cSRaymond Chen */ 27160035018cSRaymond Chen cur_if = ecmp->ecm_dev_data->dev_curr_cfg->cfg_if; 27170035018cSRaymond Chen if_num = ecmp->ecm_dev_data->dev_curr_cfg->cfg_n_if; 27180035018cSRaymond Chen 27190035018cSRaymond Chen /* search each interface which have bulk in and out */ 27200035018cSRaymond Chen for (i = 0; i < if_num; i++) { 27210035018cSRaymond Chen if (usbecm_find_bulk_in_out_eps(ecmp, i, 27220035018cSRaymond Chen cur_if) == USB_SUCCESS) { 27230035018cSRaymond Chen 27240035018cSRaymond Chen break; 27250035018cSRaymond Chen } 27260035018cSRaymond Chen cur_if++; 27270035018cSRaymond Chen } 27280035018cSRaymond Chen 27290035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, 27300035018cSRaymond Chen "usbecm_init_non_compatible_device: ctrl_if=%d," 27310035018cSRaymond Chen " data_if=%d, alt=%d", ecmp->ecm_ctrl_if_no, 27320035018cSRaymond Chen ecmp->ecm_data_if_no, ecmp->ecm_data_if_alt); 27330035018cSRaymond Chen 27340035018cSRaymond Chen return (USB_SUCCESS); 27350035018cSRaymond Chen } 27360035018cSRaymond Chen 27370035018cSRaymond Chen static boolean_t 27380035018cSRaymond Chen usbecm_is_compatible(usbecm_state_t *ecmp) 27390035018cSRaymond Chen { 27400035018cSRaymond Chen usb_cfg_data_t *cfg_data; 27410035018cSRaymond Chen usb_if_data_t *intf; 27420035018cSRaymond Chen usb_alt_if_data_t *alt; 27430035018cSRaymond Chen int alt_num, if_num, cfg_num; 27440035018cSRaymond Chen int i, j, cfg_index; 27450035018cSRaymond Chen 27460035018cSRaymond Chen cfg_num = ecmp->ecm_dev_data->dev_n_cfg; 27470035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, 27480035018cSRaymond Chen "usbecm_is_compatible: entry, cfg_num=%d", cfg_num); 27490035018cSRaymond Chen 27500035018cSRaymond Chen for (cfg_index = 0; cfg_index < cfg_num; cfg_index++) { 27510035018cSRaymond Chen cfg_data = &(ecmp->ecm_dev_data->dev_cfg[cfg_index]); 27520035018cSRaymond Chen 27530035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, 27540035018cSRaymond Chen "usbecm_is_compatible: cfg_index=%d, value=%d", 27550035018cSRaymond Chen cfg_index, cfg_data->cfg_descr.bConfigurationValue); 27560035018cSRaymond Chen 27570035018cSRaymond Chen intf = cfg_data->cfg_if; 27580035018cSRaymond Chen if_num = cfg_data->cfg_n_if; 27590035018cSRaymond Chen 27600035018cSRaymond Chen for (i = 0; i < if_num; i++) { 27610035018cSRaymond Chen alt_num = intf->if_n_alt; 27620035018cSRaymond Chen for (j = 0; j < alt_num; j++) { 27630035018cSRaymond Chen alt = &intf->if_alt[j]; 27640035018cSRaymond Chen if ((alt->altif_descr.bInterfaceClass == 0x02) && 27650035018cSRaymond Chen (alt->altif_descr.bInterfaceSubClass == 0x06)) { 27660035018cSRaymond Chen ecmp->ecm_cfg_index = cfg_index; 27670035018cSRaymond Chen 27680035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, 27690035018cSRaymond Chen "usbecm_is_compatible: cfg_index=%d", 27700035018cSRaymond Chen cfg_index); 27710035018cSRaymond Chen 27720035018cSRaymond Chen return (B_TRUE); 27730035018cSRaymond Chen } 27740035018cSRaymond Chen } 27750035018cSRaymond Chen intf++; 27760035018cSRaymond Chen } 27770035018cSRaymond Chen } 27780035018cSRaymond Chen 27790035018cSRaymond Chen return (B_FALSE); 27800035018cSRaymond Chen } 27810035018cSRaymond Chen 27820035018cSRaymond Chen 27830035018cSRaymond Chen static int 27840035018cSRaymond Chen usbecm_usb_init(usbecm_state_t *ecmp) 27850035018cSRaymond Chen { 27860035018cSRaymond Chen 27870035018cSRaymond Chen if (usb_client_attach(ecmp->ecm_dip, USBDRV_VERSION, 0) != 27880035018cSRaymond Chen USB_SUCCESS) { 27890035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 27900035018cSRaymond Chen "usbecm_usb_init: fail to attach"); 27910035018cSRaymond Chen 27920035018cSRaymond Chen return (USB_FAILURE); 27930035018cSRaymond Chen } 27940035018cSRaymond Chen 27950035018cSRaymond Chen /* Get the configuration information of device */ 27960035018cSRaymond Chen if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data, 27970035018cSRaymond Chen USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) { 27980035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 27990035018cSRaymond Chen "usbecm_usb_init: fail to get_dev_data"); 28000035018cSRaymond Chen 28010035018cSRaymond Chen return (USB_FAILURE); 28020035018cSRaymond Chen } 28030035018cSRaymond Chen ecmp->ecm_def_ph = ecmp->ecm_dev_data->dev_default_ph; 28040035018cSRaymond Chen ecmp->ecm_dev_state = USB_DEV_ONLINE; 28050035018cSRaymond Chen 28060035018cSRaymond Chen mutex_init(&ecmp->ecm_mutex, NULL, MUTEX_DRIVER, 28070035018cSRaymond Chen ecmp->ecm_dev_data->dev_iblock_cookie); 28080035018cSRaymond Chen 28090035018cSRaymond Chen if ((strcmp(ddi_binding_name(ecmp->ecm_dip), 28100035018cSRaymond Chen "usbif,class2.6") == 0) || 28110035018cSRaymond Chen ((strcmp(ddi_binding_name(ecmp->ecm_dip), 28120035018cSRaymond Chen "usb,class2.6.0") == 0))) { 28130035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 28140035018cSRaymond Chen "usbecm_usb_init: A CDC ECM device is attached"); 28150035018cSRaymond Chen ecmp->ecm_compatibility = B_TRUE; 28160035018cSRaymond Chen } else if (usb_owns_device(ecmp->ecm_dip) && 28170035018cSRaymond Chen usbecm_is_compatible(ecmp)) { 28180035018cSRaymond Chen /* 28190035018cSRaymond Chen * Current Sun SP ECM device has two configurations. Hence 28200035018cSRaymond Chen * USBA doesn't create interface level compatible names 28210035018cSRaymond Chen * for it, see usba_ready_device_node(). We have to check 28220035018cSRaymond Chen * manually to see if compatible interfaces exist, when 28230035018cSRaymond Chen * the driver owns the entire device. 28240035018cSRaymond Chen */ 28250035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 28260035018cSRaymond Chen "usbecm_usb_init: A CDC ECM device is attached"); 28270035018cSRaymond Chen ecmp->ecm_compatibility = B_TRUE; 28280035018cSRaymond Chen } else { 28290035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 28300035018cSRaymond Chen "usbecm_usb_init: A nonstandard device is attached to " 28310035018cSRaymond Chen "usbecm(7D) driver. This device doesn't conform to " 28320035018cSRaymond Chen "usb cdc spec."); 28330035018cSRaymond Chen ecmp->ecm_compatibility = B_FALSE; 28340035018cSRaymond Chen 28350035018cSRaymond Chen /* generate a random MAC addr */ 28360035018cSRaymond Chen generate_ether_addr(ecmp->ecm_srcaddr); 28370035018cSRaymond Chen } 28380035018cSRaymond Chen 28390035018cSRaymond Chen if ((ecmp->ecm_compatibility == B_TRUE) && 28400035018cSRaymond Chen (usbecm_get_descriptors(ecmp) != USB_SUCCESS)) { 28410035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 28420035018cSRaymond Chen "usbecm_usb_init: A compatible device is attached, but " 28430035018cSRaymond Chen "fail to get standard descriptors"); 28440035018cSRaymond Chen 28450035018cSRaymond Chen return (USB_FAILURE); 28460035018cSRaymond Chen } 28470035018cSRaymond Chen 28480035018cSRaymond Chen if (ecmp->ecm_compatibility == B_FALSE) { 28490035018cSRaymond Chen (void) usbecm_init_non_compatible_device(ecmp); 28500035018cSRaymond Chen } 28510035018cSRaymond Chen 28520035018cSRaymond Chen /* Create power management components */ 28530035018cSRaymond Chen if (usbecm_create_pm_components(ecmp) != USB_SUCCESS) { 28540035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 28550035018cSRaymond Chen "usbecm_usb_init: create pm components failed."); 28560035018cSRaymond Chen 28570035018cSRaymond Chen return (USB_FAILURE); 28580035018cSRaymond Chen } 28590035018cSRaymond Chen 28600035018cSRaymond Chen /* Register to get callbacks for USB events */ 28610035018cSRaymond Chen if (usb_register_event_cbs(ecmp->ecm_dip, &usbecm_events, 0) 28620035018cSRaymond Chen != USB_SUCCESS) { 28630035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 28640035018cSRaymond Chen "usbsecm_attach: register event callback failed."); 28650035018cSRaymond Chen 28660035018cSRaymond Chen return (USB_FAILURE); 28670035018cSRaymond Chen } 28680035018cSRaymond Chen ecmp->ecm_init_flags |= USBECM_INIT_EVENTS; 28690035018cSRaymond Chen 28700035018cSRaymond Chen 28710035018cSRaymond Chen /* Get max data size of bulk transfer */ 28720035018cSRaymond Chen if (usb_pipe_get_max_bulk_transfer_size(ecmp->ecm_dip, 28730035018cSRaymond Chen &ecmp->ecm_xfer_sz) != USB_SUCCESS) { 28740035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 28750035018cSRaymond Chen "usbsecm_ds_attach: get max size of transfer failed."); 28760035018cSRaymond Chen 28770035018cSRaymond Chen return (USB_FAILURE); 28780035018cSRaymond Chen } 28790035018cSRaymond Chen 28800035018cSRaymond Chen 28810035018cSRaymond Chen ecmp->ecm_ser_acc = usb_init_serialization(ecmp->ecm_dip, 28820035018cSRaymond Chen USB_INIT_SER_CHECK_SAME_THREAD); 28830035018cSRaymond Chen ecmp->ecm_init_flags |= USBECM_INIT_SER; 28840035018cSRaymond Chen 28850035018cSRaymond Chen return (USB_SUCCESS); 28860035018cSRaymond Chen } 28870035018cSRaymond Chen 28880035018cSRaymond Chen 28890035018cSRaymond Chen /* 28900035018cSRaymond Chen * Open operation pipes. Each ECM device should have Bulk In, Bulk Out 28910035018cSRaymond Chen * and Interrupt In endpoints 28920035018cSRaymond Chen */ 28930035018cSRaymond Chen static int 28940035018cSRaymond Chen usbecm_open_pipes(usbecm_state_t *ecmp) 28950035018cSRaymond Chen { 28960035018cSRaymond Chen int rval = USB_SUCCESS; 28970035018cSRaymond Chen usb_ep_data_t *in_data, *out_data, *intr_pipe; 28980035018cSRaymond Chen usb_pipe_policy_t policy; 28990035018cSRaymond Chen int altif; 29000035018cSRaymond Chen 29010035018cSRaymond Chen ASSERT(!mutex_owned(&ecmp->ecm_mutex)); 29020035018cSRaymond Chen 29030035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh, 29040035018cSRaymond Chen "usbsecm_open_pipes: ecmp = 0x%p", (void *)ecmp); 29050035018cSRaymond Chen 29060035018cSRaymond Chen if (ecmp->ecm_compatibility == B_TRUE) { 29070035018cSRaymond Chen /* compatible device has minimum of 2 altsetting, select alt 1 */ 29080035018cSRaymond Chen altif = 1; 29090035018cSRaymond Chen } else { 29100035018cSRaymond Chen altif = ecmp->ecm_data_if_alt; 29110035018cSRaymond Chen } 29120035018cSRaymond Chen intr_pipe = ecmp->ecm_intr_ep; 29130035018cSRaymond Chen in_data = ecmp->ecm_bulk_in_ep; 29140035018cSRaymond Chen out_data = ecmp->ecm_bulk_out_ep; 29150035018cSRaymond Chen 29160035018cSRaymond Chen /* Bulk in and out must exist simultaneously. */ 29170035018cSRaymond Chen if ((in_data == NULL) || (out_data == NULL)) { 29180035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 29190035018cSRaymond Chen "usbsecm_open_pipes: look up bulk pipe failed in " 29200035018cSRaymond Chen "interface %d ", 29210035018cSRaymond Chen ecmp->ecm_data_if_no); 29220035018cSRaymond Chen 29230035018cSRaymond Chen return (USB_FAILURE); 29240035018cSRaymond Chen } 29250035018cSRaymond Chen /* 29260035018cSRaymond Chen * If device conform to ecm spec, it must have an interrupt pipe 29270035018cSRaymond Chen * for this device. 29280035018cSRaymond Chen */ 29290035018cSRaymond Chen if (ecmp->ecm_compatibility == B_TRUE && intr_pipe == NULL) { 29300035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 29310035018cSRaymond Chen "usbecm_open_pipes: look up interrupt pipe failed in " 29320035018cSRaymond Chen "interface %d", ecmp->ecm_ctrl_if_no); 29330035018cSRaymond Chen 29340035018cSRaymond Chen return (USB_FAILURE); 29350035018cSRaymond Chen } 29360035018cSRaymond Chen 29370035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, 29380035018cSRaymond Chen "usbsecm_open_pipes: open intr %02x, bulkin %02x bulkout %02x", 29390035018cSRaymond Chen intr_pipe?intr_pipe->ep_descr.bEndpointAddress:0, 29400035018cSRaymond Chen in_data->ep_descr.bEndpointAddress, 29410035018cSRaymond Chen out_data->ep_descr.bEndpointAddress); 29420035018cSRaymond Chen 29430035018cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, 29440035018cSRaymond Chen "usbsecm_open_pipes: set data if(%d) alt(%d) ", 29450035018cSRaymond Chen ecmp->ecm_data_if_no, altif); 29460035018cSRaymond Chen 29470035018cSRaymond Chen if ((rval = usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no, 29480035018cSRaymond Chen altif, USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) { 29490035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 29500035018cSRaymond Chen "usbecm_open_pipes: set alternate failed (%d)", 29510035018cSRaymond Chen rval); 29520035018cSRaymond Chen 29530035018cSRaymond Chen return (rval); 29540035018cSRaymond Chen } 29550035018cSRaymond Chen 29560035018cSRaymond Chen policy.pp_max_async_reqs = 2; 29570035018cSRaymond Chen 29580035018cSRaymond Chen /* Open bulk in endpoint */ 29590035018cSRaymond Chen if (usb_pipe_open(ecmp->ecm_dip, &in_data->ep_descr, &policy, 29600035018cSRaymond Chen USB_FLAGS_SLEEP, &ecmp->ecm_bulkin_ph) != USB_SUCCESS) { 29610035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 29620035018cSRaymond Chen "usbecm_open_pipes: open bulkin pipe failed!"); 29630035018cSRaymond Chen 29640035018cSRaymond Chen return (USB_FAILURE); 29650035018cSRaymond Chen } 29660035018cSRaymond Chen 29670035018cSRaymond Chen /* Open bulk out endpoint */ 29680035018cSRaymond Chen if (usb_pipe_open(ecmp->ecm_dip, &out_data->ep_descr, &policy, 29690035018cSRaymond Chen USB_FLAGS_SLEEP, &ecmp->ecm_bulkout_ph) != USB_SUCCESS) { 29700035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 29710035018cSRaymond Chen "usbecm_open_pipes: open bulkout pipe failed!"); 29720035018cSRaymond Chen 29730035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, 29740035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, NULL); 29750035018cSRaymond Chen 29760035018cSRaymond Chen return (USB_FAILURE); 29770035018cSRaymond Chen } 29780035018cSRaymond Chen 29790035018cSRaymond Chen /* Open interrupt endpoint if found. */ 29800035018cSRaymond Chen if (intr_pipe != NULL) { 29810035018cSRaymond Chen if (usb_pipe_open(ecmp->ecm_dip, &intr_pipe->ep_descr, &policy, 29820035018cSRaymond Chen USB_FLAGS_SLEEP, &ecmp->ecm_intr_ph) != USB_SUCCESS) { 29830035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, 29840035018cSRaymond Chen "usbecm_open_pipes: " 29850035018cSRaymond Chen "open intr pipe failed"); 29860035018cSRaymond Chen 29870035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, 29880035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, NULL); 29890035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph, 29900035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, NULL); 29910035018cSRaymond Chen 29920035018cSRaymond Chen return (USB_FAILURE); 29930035018cSRaymond Chen } 29940035018cSRaymond Chen } 29950035018cSRaymond Chen 29960035018cSRaymond Chen /* initialize the pipe related data */ 29970035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 29980035018cSRaymond Chen ecmp->ecm_bulkin_sz = in_data->ep_descr.wMaxPacketSize; 29990035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE; 30000035018cSRaymond Chen ecmp->ecm_bulkout_state = USBECM_PIPE_IDLE; 30010035018cSRaymond Chen if (ecmp->ecm_intr_ph != NULL) { 30020035018cSRaymond Chen ecmp->ecm_intr_state = USBECM_PIPE_IDLE; 30030035018cSRaymond Chen } 30040035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 30050035018cSRaymond Chen 30060035018cSRaymond Chen if (ecmp->ecm_intr_ph != NULL) { 30070035018cSRaymond Chen 30080035018cSRaymond Chen usbecm_pipe_start_polling(ecmp); 30090035018cSRaymond Chen } 30100035018cSRaymond Chen 30110035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh, 30120035018cSRaymond Chen "usbsecm_open_pipes: end"); 30130035018cSRaymond Chen 30140035018cSRaymond Chen return (rval); 30150035018cSRaymond Chen } 30160035018cSRaymond Chen 30170035018cSRaymond Chen 30180035018cSRaymond Chen /* 30190035018cSRaymond Chen * usbsecm_close_pipes: 30200035018cSRaymond Chen * Close pipes 30210035018cSRaymond Chen * Each device could include three pipes: bulk in, bulk out and interrupt. 30220035018cSRaymond Chen */ 30230035018cSRaymond Chen static void 30240035018cSRaymond Chen usbecm_close_pipes(usbecm_state_t *ecmp) 30250035018cSRaymond Chen { 30260035018cSRaymond Chen 30270035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 30280035018cSRaymond Chen 30290035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, 30300035018cSRaymond Chen "usbsecm_close_pipes: ecm_bulkin_state = %d", 30310035018cSRaymond Chen ecmp->ecm_bulkin_state); 30320035018cSRaymond Chen 30330035018cSRaymond Chen /* 30340035018cSRaymond Chen * Check the status of the pipes. If pipe is closing or closed, 30350035018cSRaymond Chen * return directly. 30360035018cSRaymond Chen */ 30370035018cSRaymond Chen if ((ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSED) || 30380035018cSRaymond Chen (ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSING)) { 30390035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_CLOSE, ecmp->ecm_lh, 30400035018cSRaymond Chen "usbsecm_close_pipes: pipe is closing or has closed"); 30410035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 30420035018cSRaymond Chen 30430035018cSRaymond Chen return; 30440035018cSRaymond Chen } 30450035018cSRaymond Chen 30460035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSING; 30470035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 30480035018cSRaymond Chen 30490035018cSRaymond Chen /* reset the data interface's altsetting to 0 */ 30500035018cSRaymond Chen if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) && 30510035018cSRaymond Chen (usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no, 30520035018cSRaymond Chen 0, USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS)) { 30530035018cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, 30540035018cSRaymond Chen "usbecm_close_pipes: reset alternate failed "); 30550035018cSRaymond Chen } 30560035018cSRaymond Chen 30570035018cSRaymond Chen /* Close pipes */ 30580035018cSRaymond Chen usb_pipe_reset(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, 30590035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, 0); 30600035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, 30610035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, 0); 30620035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph, 30630035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, 0); 30640035018cSRaymond Chen 30650035018cSRaymond Chen if (ecmp->ecm_intr_ph != NULL) { 30660035018cSRaymond Chen usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph, 30670035018cSRaymond Chen USB_FLAGS_SLEEP); 30680035018cSRaymond Chen usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_intr_ph, 30690035018cSRaymond Chen USB_FLAGS_SLEEP, NULL, 0); 30700035018cSRaymond Chen } 30710035018cSRaymond Chen 30720035018cSRaymond Chen mutex_enter(&ecmp->ecm_mutex); 30730035018cSRaymond Chen /* Reset the status of pipes to closed */ 30740035018cSRaymond Chen ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSED; 30750035018cSRaymond Chen ecmp->ecm_bulkin_ph = NULL; 30760035018cSRaymond Chen ecmp->ecm_bulkout_state = USBECM_PIPE_CLOSED; 30770035018cSRaymond Chen ecmp->ecm_bulkout_ph = NULL; 30780035018cSRaymond Chen if (ecmp->ecm_intr_ph != NULL) { 30790035018cSRaymond Chen ecmp->ecm_intr_state = USBECM_PIPE_CLOSED; 30800035018cSRaymond Chen ecmp->ecm_intr_ph = NULL; 30810035018cSRaymond Chen } 30820035018cSRaymond Chen 30830035018cSRaymond Chen mutex_exit(&ecmp->ecm_mutex); 30840035018cSRaymond Chen 30850035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, 30860035018cSRaymond Chen "usbsecm_close_pipes: pipes have been closed."); 30870035018cSRaymond Chen } 30880035018cSRaymond Chen 30890035018cSRaymond Chen 30900035018cSRaymond Chen static int 30910035018cSRaymond Chen usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request, 30920035018cSRaymond Chen uint16_t value, mblk_t **data) 30930035018cSRaymond Chen { 30940035018cSRaymond Chen usb_ctrl_setup_t setup; 30950035018cSRaymond Chen usb_cb_flags_t cb_flags; 30960035018cSRaymond Chen usb_cr_t cr; 30970035018cSRaymond Chen int rval; 30980035018cSRaymond Chen 30990035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh, 31000035018cSRaymond Chen "usbecm_ctrl_write: "); 31010035018cSRaymond Chen 31020035018cSRaymond Chen /* initialize the control request. */ 31030035018cSRaymond Chen setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV | 31040035018cSRaymond Chen USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; 31050035018cSRaymond Chen setup.bRequest = request; 31060035018cSRaymond Chen setup.wValue = value; 31070035018cSRaymond Chen setup.wIndex = ecmp->ecm_ctrl_if_no; 31080035018cSRaymond Chen setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0; 31090035018cSRaymond Chen setup.attrs = 0; 31100035018cSRaymond Chen 31110035018cSRaymond Chen rval = usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data, 31120035018cSRaymond Chen &cr, &cb_flags, 0); 31130035018cSRaymond Chen 31140035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh, 31150035018cSRaymond Chen "usbecm_ctrl_write: rval = %d", rval); 31160035018cSRaymond Chen 31170035018cSRaymond Chen return (rval); 31180035018cSRaymond Chen } 31190035018cSRaymond Chen 31200035018cSRaymond Chen static int 31210035018cSRaymond Chen usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request, 31220035018cSRaymond Chen uint16_t value, mblk_t **data, int len) 31230035018cSRaymond Chen { 31240035018cSRaymond Chen usb_ctrl_setup_t setup; 31250035018cSRaymond Chen usb_cb_flags_t cb_flags; 31260035018cSRaymond Chen usb_cr_t cr; 31270035018cSRaymond Chen 31280035018cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh, 31290035018cSRaymond Chen "usbecm_ctrl_read: "); 31300035018cSRaymond Chen 31310035018cSRaymond Chen /* initialize the control request. */ 31320035018cSRaymond Chen setup.bmRequestType = USB_DEV_REQ_DEV_TO_HOST | 31330035018cSRaymond Chen USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; 31340035018cSRaymond Chen setup.bRequest = request; 31350035018cSRaymond Chen setup.wValue = value; 31360035018cSRaymond Chen setup.wIndex = ecmp->ecm_ctrl_if_no; 31370035018cSRaymond Chen setup.wLength = (uint16_t)len; 31380035018cSRaymond Chen setup.attrs = 0; 31390035018cSRaymond Chen 31400035018cSRaymond Chen return (usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data, 31410035018cSRaymond Chen &cr, &cb_flags, 0)); 31420035018cSRaymond Chen } 31430035018cSRaymond Chen 31440035018cSRaymond Chen /* Get specific statistic data from device */ 31450035018cSRaymond Chen static int 31460035018cSRaymond Chen usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs, uint32_t *stat_data) 31470035018cSRaymond Chen { 31480035018cSRaymond Chen mblk_t *data = NULL; 31490035018cSRaymond Chen uint32_t stat; 31500035018cSRaymond Chen 31510035018cSRaymond Chen /* first check to see if this stat is collected by device */ 31520035018cSRaymond Chen if ((ecmp->ecm_compatibility == B_TRUE) && 31530035018cSRaymond Chen (ecmp->ecm_desc.bmEthernetStatistics & ECM_STAT_CAP_MASK(fs))) { 31540035018cSRaymond Chen if (usbecm_ctrl_read(ecmp, CDC_ECM_GET_ETH_STAT, 31550035018cSRaymond Chen ecmp->ecm_ctrl_if_no, &data, 4) != USB_SUCCESS) { 31560035018cSRaymond Chen 31570035018cSRaymond Chen return (USB_FAILURE); 31580035018cSRaymond Chen } 31590035018cSRaymond Chen stat = (data->b_rptr[3] << 24) | (data->b_rptr[2] << 16) | 31600035018cSRaymond Chen (data->b_rptr[1] << 8) | (data->b_rptr[0]); 31610035018cSRaymond Chen *stat_data = stat; 31620035018cSRaymond Chen 31630035018cSRaymond Chen freemsg(data); 31640035018cSRaymond Chen 31650035018cSRaymond Chen return (USB_SUCCESS); 31660035018cSRaymond Chen } 31670035018cSRaymond Chen 31680035018cSRaymond Chen return (USB_FAILURE); 31690035018cSRaymond Chen } 3170