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
usbecm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
usbecm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
usbecm_m_stat(void * arg,uint_t stat,uint64_t * val)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
usbecm_m_start(void * arg)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
usbecm_m_stop(void * arg)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 
7193db80ed2SRaymond 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
usbecm_m_unicst(void * arg,const uint8_t * macaddr)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 
7743db80ed2SRaymond 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
usbecm_m_multicst(void * arg,boolean_t add,const uint8_t * m)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 
8133db80ed2SRaymond 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
usbecm_m_promisc(void * arg,boolean_t on)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 
8593db80ed2SRaymond 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
usbecm_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)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 
9053db80ed2SRaymond 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
usbecm_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)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*/
usbecm_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)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 *
usbecm_m_tx(void * arg,mblk_t * mp)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 
9833db80ed2SRaymond 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
usbecm_bulkin_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)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
usbecm_rx_start(usbecm_state_t * ecmp)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
usbecm_bulkout_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)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
usbecm_send_data(usbecm_state_t * ecmp,mblk_t * data)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
usbecm_send_zero_data(usbecm_state_t * ecmp)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
_init(void)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
_info(struct modinfo * modinfop)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
_fini(void)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
usbecm_pipe_start_polling(usbecm_state_t * ecmp)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
usbecm_intr_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)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
usbecm_intr_ex_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)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
usbecm_parse_intr_data(usbecm_state_t * ecmp,mblk_t * data)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
usbecm_restore_device_state(usbecm_state_t * ecmp)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
usbecm_reconnect_event_cb(dev_info_t * dip)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
usbecm_disconnect_event_cb(dev_info_t * dip)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
usbecm_create_pm_components(usbecm_state_t * ecmp)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
usbecm_cleanup(usbecm_state_t * ecmp)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) {
19473db80ed2SRaymond 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
usbecm_destroy_pm_components(usbecm_state_t * ecmp)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
usbecm_pm_set_busy(usbecm_state_t * ecmp)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
usbecm_pm_set_idle(usbecm_state_t * ecmp)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
usbecm_pwrlvl0(usbecm_state_t * ecmp)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
usbecm_pwrlvl1(usbecm_state_t * ecmp)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
usbecm_pwrlvl2(usbecm_state_t * ecmp)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
usbecm_pwrlvl3(usbecm_state_t * ecmp)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
usbecm_power(dev_info_t * dip,int comp,int level)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
usbecm_mac_init(usbecm_state_t * ecmp)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
usbecm_mac_fini(usbecm_state_t * ecmp)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
usbecm_resume(usbecm_state_t * ecmp)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
usbecm_suspend(usbecm_state_t * ecmp)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
label_to_mac(char * hex,unsigned char * mac)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
usbecm_get_descriptors(usbecm_state_t * ecmp)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
generate_ether_addr(uint8_t * mac_addr)26430035018cSRaymond Chen generate_ether_addr(uint8_t *mac_addr)
26440035018cSRaymond Chen {
26453db80ed2SRaymond 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  */
usbecm_find_bulk_in_out_eps(usbecm_state_t * ecmp,uint16_t ifc,usb_if_data_t * intf)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
usbecm_init_non_compatible_device(usbecm_state_t * ecmp)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
usbecm_is_compatible(usbecm_state_t * ecmp)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
usbecm_usb_init(usbecm_state_t * ecmp)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 "
2831*bbf21555SRichard Lowe 		    "usbecm(4D) 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
usbecm_open_pipes(usbecm_state_t * ecmp)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
usbecm_close_pipes(usbecm_state_t * ecmp)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
usbecm_ctrl_write(usbecm_state_t * ecmp,uchar_t request,uint16_t value,mblk_t ** data)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
usbecm_ctrl_read(usbecm_state_t * ecmp,uchar_t request,uint16_t value,mblk_t ** data,int len)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
usbecm_get_statistics(usbecm_state_t * ecmp,uint32_t fs,uint32_t * stat_data)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