changeset 11900:3d14975ff34e

PSARC 2010/041 USB CDC ECM driver 6922046 Support USB CDC ECM driver
author Raymond Chen <Raymond.Chen@Sun.COM>
date Fri, 12 Mar 2010 09:19:50 +0800
parents b90a7691780e
children 4139b964b1ab
files usr/src/pkg/manifests/driver-network-usbecm.mf usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/usb/clients/usbecm/usbecm.c usr/src/uts/common/io/warlock/usbecm.wlcmd usr/src/uts/common/io/warlock/usbecm_with_usba.wlcmd usr/src/uts/common/sys/usb/clients/usbcdc/usb_cdc.h usr/src/uts/common/sys/usb/clients/usbecm/usbecm.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/usbecm/Makefile usr/src/uts/intel/warlock/Makefile usr/src/uts/sparc/Makefile.sparc.shared usr/src/uts/sparc/usbecm/Makefile usr/src/uts/sparc/warlock/Makefile
diffstat 14 files changed, 4053 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkg/manifests/driver-network-usbecm.mf	Fri Mar 12 09:19:50 2010 +0800
@@ -0,0 +1,49 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This package will install successfully into any zone, global or
+# non-global.  The files, directories, links, and hardlinks, however,
+# will only be installed into the global zone.
+#
+<include hollow_zone_pkg>
+set name=pkg.fmri value=pkg:/driver/network/usbecm@$(PKGVERS)
+set name=pkg.description value="CDC ECM USB-to-Ethernet driver"
+set name=pkg.summary value="CDC ECM USB-to-Ethernet driver"
+set name=info.classification value=org.opensolaris.category.2008:Drivers/Networking
+set name=variant.arch value=$(ARCH)
+set name=variant.opensolaris.zone value=global value=nonglobal
+dir path=kernel group=sys
+dir path=kernel/drv group=sys
+dir path=kernel/drv/$(ARCH64) group=sys
+driver name=usbecm perms="* 0666 root sys" \
+    alias=usb,class2.6.0 \
+    alias=usbif,class2.6 \
+    alias=usb430,a4a2
+file path=kernel/drv/$(ARCH64)/usbecm group=sys
+$(i386_ONLY)file path=kernel/drv/usbecm group=sys
+license cr_Sun license=cr_Sun
+license lic_CDDL license=lic_CDDL
--- a/usr/src/uts/common/Makefile.files	Thu Mar 11 16:01:06 2010 -0800
+++ b/usr/src/uts/common/Makefile.files	Fri Mar 12 09:19:50 2010 +0800
@@ -771,6 +771,8 @@
 
 USBFTDI_OBJS += usbser_uftdi.o uftdi_dsd.o
 
+USBECM_OBJS += usbecm.o
+
 WC_OBJS += wscons.o vcons.o
 
 VCONS_CONF_OBJS += vcons_conf.o
--- a/usr/src/uts/common/Makefile.rules	Thu Mar 11 16:01:06 2010 -0800
+++ b/usr/src/uts/common/Makefile.rules	Fri Mar 12 09:19:50 2010 +0800
@@ -1186,6 +1186,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/usb/clients/usbecm/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/usb/hcd/openhci/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -2365,6 +2369,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/usb/clients/wusb_ca/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/usb/clients/usbecm/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/usb/hcd/openhci/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/clients/usbecm/usbecm.c	Fri Mar 12 09:19:50 2010 +0800
@@ -0,0 +1,3169 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * USB Ethernet Control Model
+ *
+ * USB-IF defines three ethernet network related specifications: EEM,
+ * ECM and NCM. This driver focuses specifically on ECM compatible
+ * devices. This kind of devices generally have one pair of bulk
+ * endpoints for in/out packet data and one interrupt endpoint for
+ * device notification.
+ *
+ * Devices which don't report ECM compatibility through descriptors but
+ * implement the ECM functions may also bind to this driver. This driver
+ * will try to find at least a bulk in endpoint and a bulk out endpoint
+ * in this case. If the non-compatible devices use vendor specific data
+ * format, this driver will not function.
+ *
+ * This driver is a normal USBA client driver. It's also a GLDv3 driver,
+ * which provides the necessary interfaces the GLDv3 framework requires.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/strsun.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/byteorder.h>
+#include <sys/usb/usba/usbai_version.h>
+#include <sys/usb/usba.h>
+#include <sys/usb/usba/usba_types.h>
+#include <sys/usb/clients/usbcdc/usb_cdc.h>
+#include <sys/usb/clients/usbecm/usbecm.h>
+#include <sys/mac_provider.h>
+#include <sys/strsubr.h>
+#include <sys/ethernet.h>
+#include <sys/mac_ether.h> /* MAC_PLUGIN_IDENT_ETHER */
+#include <sys/random.h> /* random_get_bytes */
+#include <sys/sdt.h>	/* sdt */
+#include <inet/nd.h>
+
+/* MAC callbacks */
+static int	usbecm_m_stat(void *arg, uint_t stat, uint64_t *val);
+static int	usbecm_m_start(void *arg);
+static void	usbecm_m_stop(void *arg);
+static int	usbecm_m_unicst(void *arg, const uint8_t *macaddr);
+static int	usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m);
+static int	usbecm_m_promisc(void *arg, boolean_t on);
+static void	usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
+static mblk_t	*usbecm_m_tx(void *arg, mblk_t *mp);
+static int	usbecm_m_getprop(void *arg, const char *pr_name,
+    mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf);
+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);
+
+static int	usbecm_usb_init(usbecm_state_t *ecmp);
+static int	usbecm_mac_init(usbecm_state_t *ecmp);
+static int	usbecm_mac_fini(usbecm_state_t *ecmp);
+
+
+/* utils */
+static void	generate_ether_addr(uint8_t *mac_addr);
+static int	usbecm_rx_start(usbecm_state_t *ecmp);
+
+static void	usbecm_pipe_start_polling(usbecm_state_t *ecmp);
+static void	usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
+static void	usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
+static void	usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data);
+
+static int	usbecm_reconnect_event_cb(dev_info_t *dip);
+static int	usbecm_disconnect_event_cb(dev_info_t *dip);
+
+static int	usbecm_open_pipes(usbecm_state_t *ecmp);
+static void	usbecm_close_pipes(usbecm_state_t *ecmp);
+
+static int	usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request,
+    uint16_t value, mblk_t **data, int len);
+static int	usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request,
+    uint16_t value, mblk_t **data);
+static int	usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data);
+static int	usbecm_send_zero_data(usbecm_state_t *ecmp);
+static int	usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs,
+    uint32_t *stat_data);
+
+static int	usbecm_create_pm_components(usbecm_state_t *ecmp);
+static void	usbecm_destroy_pm_components(usbecm_state_t *ecmp);
+static int	usbecm_power(dev_info_t *dip, int comp, int level);
+static void	usbecm_pm_set_busy(usbecm_state_t *ecmp);
+static void	usbecm_pm_set_idle(usbecm_state_t *ecmp);
+
+static int	usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int	usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+
+static int	usbecm_suspend(usbecm_state_t *ecmp);
+static int	usbecm_resume(usbecm_state_t *ecmp);
+static int	usbecm_restore_device_state(usbecm_state_t *ecmp);
+static void	usbecm_cleanup(usbecm_state_t *ecmp);
+
+/* Driver identification */
+static char usbecm_ident[] = "usbecm 1.0";
+
+/* Global state pointer for managing per-device soft states */
+void *usbecm_statep;
+
+/* print levels */
+static uint_t   usbecm_errlevel = USB_LOG_L3;
+static uint_t   usbecm_errmask = 0xffffffff;
+static uint_t   usbecm_instance_debug = (uint_t)-1;
+
+/*
+ * to prevent upper layers packet flood from exhausting system
+ * resources(USBA does not set limitation of requests on a pipe),
+ * we set a upper limit for the transfer queue length.
+ */
+static	int	usbecm_tx_max = 32;
+
+#define	SUN_SP_VENDOR_ID	0x0430
+#define	SUN_SP_PRODUCT_ID	0xa4a2
+
+static uint8_t	usbecm_broadcast[ETHERADDRL] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static usb_event_t usbecm_events = {
+	usbecm_disconnect_event_cb,
+	usbecm_reconnect_event_cb,
+	NULL, NULL
+};
+
+#define	ECM_DS_OP_VALID(op) ((ecmp->ecm_ds_ops) && (ecmp->ecm_ds_ops->op))
+
+/*
+ * MAC Call Back entries
+ */
+static mac_callbacks_t usbecm_m_callbacks = {
+	MC_IOCTL | MC_SETPROP | MC_GETPROP,
+	usbecm_m_stat,		/* Get the value of a statistic */
+	usbecm_m_start,		/* Start the device */
+	usbecm_m_stop,		/* Stop the device */
+	usbecm_m_promisc,	/* Enable or disable promiscuous mode */
+	usbecm_m_multicst,	/* Enable or disable a multicast addr */
+	usbecm_m_unicst,	/* Set the unicast MAC address */
+	usbecm_m_tx,		/* Transmit a packet */
+	NULL,
+	usbecm_m_ioctl,		/* Process an unknown ioctl */
+	NULL,			/* mc_getcapab */
+	NULL,			/* mc_open */
+	NULL,			/* mc_close */
+	usbecm_m_setprop, 	/* mc_setprop */
+	usbecm_m_getprop,	/* mc_getprop */
+	NULL
+};
+
+
+/*
+ *  Module Loading Data & Entry Points
+ *     Can't use DDI_DEFINE_STREAM_OPS, since it does
+ *     not provide devo_power entry.
+ */
+static struct cb_ops cb_usbecm = {
+	nulldev,		/* cb_open */
+	nulldev,		/* cb_close */
+	nodev,			/* cb_strategy */
+	nodev,			/* cb_print */
+	nodev,			/* cb_dump */
+	nodev,			/* cb_read */
+	nodev,			/* cb_write */
+	nodev,			/* cb_ioctl */
+	nodev,			/* cb_devmap */
+	nodev,			/* cb_mmap */
+	nodev,			/* cb_segmap */
+	nochpoll,		/* cb_chpoll */
+	ddi_prop_op,		/* cb_prop_op */
+	NULL,			/* cb_stream */
+	D_MP,			/* cb_flag */
+	CB_REV,			/* cb_rev */
+	nodev,			/* cb_aread */
+	nodev,			/* cb_awrite */
+};
+
+static struct dev_ops usbecm_devops = {
+	DEVO_REV,		/* devo_rev */
+	0,			/* devo_refcnt */
+	NULL,			/* devo_getinfo */
+	nulldev,		/* devo_identify */
+	nulldev,		/* devo_probe */
+	usbecm_attach,		/* devo_attach */
+	usbecm_detach,		/* devo_detach */
+	nodev,			/* devo_reset */
+	&(cb_usbecm),		/* devo_cb_ops */
+	(struct bus_ops *)NULL,	/* devo_bus_ops */
+	usbecm_power,		/* devo_power */
+	ddi_quiesce_not_needed	/* devo_quiesce */
+};
+
+static struct modldrv usbecm_modldrv = {
+	&mod_driverops,		/* drv_modops */
+	usbecm_ident,		/* drv_linkinfo */
+	&usbecm_devops		/* drv_dev_ops */
+};
+
+static struct modlinkage usbecm_ml = {
+	MODREV_1,		/* ml_rev */
+	&usbecm_modldrv, NULL	/* ml_linkage */
+};
+
+
+/*
+ * Device operations
+ */
+/*
+ * Binding the driver to a device.
+ *
+ * Concurrency: Until usbecm_attach() returns with success,
+ * the only other entry point that can be executed is getinfo().
+ * Thus no locking here yet.
+ */
+static int
+usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	char strbuf[32];
+	int instance;
+	int err;
+	usbecm_state_t *ecmp = NULL;
+
+	switch (cmd) {
+	case DDI_ATTACH:
+		break;
+
+	case DDI_RESUME:
+		ecmp = (usbecm_state_t *)ddi_get_soft_state(usbecm_statep,
+		    ddi_get_instance(dip));
+
+		(void) usbecm_resume(ecmp);
+
+		return (DDI_SUCCESS);
+
+	default:
+		return (DDI_FAILURE);
+	}
+
+	instance = ddi_get_instance(dip);
+
+	if (ddi_soft_state_zalloc(usbecm_statep, instance) == DDI_SUCCESS) {
+		ecmp = ddi_get_soft_state(usbecm_statep, instance);
+	}
+	if (ecmp == NULL) {
+		cmn_err(CE_WARN, "usbecm_attach: fail to get soft state");
+
+		return (DDI_FAILURE);
+	}
+
+	ecmp->ecm_dip = dip;
+
+	ecmp->ecm_lh = usb_alloc_log_hdl(ecmp->ecm_dip, "usbecm",
+	    &usbecm_errlevel, &usbecm_errmask, &usbecm_instance_debug, 0);
+
+	if (usbecm_usb_init(ecmp) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_attach: failed to init usb");
+
+		goto fail;
+	}
+
+	if (ECM_DS_OP_VALID(ecm_ds_init)) {
+		if (ecmp->ecm_ds_ops->ecm_ds_init(ecmp) != USB_SUCCESS) {
+			USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+			    "usbecm_attach: failed to init DS");
+
+			goto fail;
+		}
+	}
+
+	if (usbecm_mac_init(ecmp) != DDI_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_attach: failed to init mac");
+
+		goto fail;
+	}
+	ecmp->ecm_init_flags |= USBECM_INIT_MAC;
+
+	/*
+	 * Create minor node of type usb_net. Not necessary to create
+	 * DDI_NT_NET since it's created in mac_register(). Otherwise,
+	 * system will panic.
+	 */
+	(void) snprintf(strbuf, sizeof (strbuf), "usbecm%d", instance);
+	err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
+	    instance + 1, "usb_net", 0);
+	if (err != DDI_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "failed to create minor node");
+
+		goto fail;
+	}
+
+	/* always busy. May change to a more precise PM in future */
+	usbecm_pm_set_busy(ecmp);
+
+	ddi_report_dev(dip);
+
+	USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
+	    "usbecm_attach: succeed!");
+
+	return (DDI_SUCCESS);
+
+fail:
+	USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh,
+	    "usbecm_attach: Attach fail");
+
+	usbecm_cleanup(ecmp);
+	ddi_prop_remove_all(dip);
+	ddi_soft_state_free(usbecm_statep, instance);
+
+	return (DDI_FAILURE);
+
+}
+
+
+/*
+ * Detach the driver from a device.
+ *
+ * Concurrency: Will be called only after a successful attach
+ * (and not concurrently).
+ */
+static int
+usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	usbecm_state_t *ecmp = NULL;
+	int instance;
+
+	instance = ddi_get_instance(dip);
+	ecmp = ddi_get_soft_state(usbecm_statep, instance);
+	ASSERT(ecmp != NULL);
+
+	USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
+	    "usbecm_detach: entry ");
+
+	switch (cmd) {
+	case DDI_DETACH:
+		break;
+
+	case DDI_SUSPEND:
+
+		return (usbecm_suspend(ecmp));
+
+	default:
+		return (DDI_FAILURE);
+	}
+
+	usbecm_pm_set_idle(ecmp);
+
+	if (ECM_DS_OP_VALID(ecm_ds_fini)) {
+		if (ecmp->ecm_ds_ops->ecm_ds_fini(ecmp) != USB_SUCCESS) {
+			USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+			    "usbecm_detach: deinitialize DS fail!");
+
+			return (DDI_FAILURE);
+		}
+	}
+
+	if (usbecm_mac_fini(ecmp) != 0) {
+
+		return (DDI_FAILURE);
+	}
+
+	USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
+	    "usbecm_detach: exit");
+
+	usbecm_cleanup(ecmp);
+	ddi_soft_state_free(usbecm_statep, instance);
+
+	return (DDI_SUCCESS);
+}
+
+
+/*
+ * Mac Call Back functions
+ */
+
+/*
+ * Read device statistic information.
+ */
+static int
+usbecm_m_stat(void *arg, uint_t stat, uint64_t *val)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
+	uint32_t	stats;
+	int		rval;
+	uint32_t	fs;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_stat: entry, stat=%d", stat);
+
+	/*
+	 * Some of the stats are MII specific. We try to
+	 * resolve all the statistics we understand. If
+	 * the usb device can't provide it, return ENOTSUP.
+	 */
+	switch (stat) {
+	case MAC_STAT_IFSPEED:
+		/* return link speed */
+		mutex_enter(&ecmp->ecm_mutex);
+		if (ecmp->ecm_stat.es_downspeed) {
+			*val = ecmp->ecm_stat.es_downspeed;
+		} else {
+			*val = 10 * 1000000ull; /* set a default value */
+		}
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return (0);
+	case ETHER_STAT_LINK_DUPLEX:
+		*val = LINK_DUPLEX_FULL;
+
+		return (0);
+
+	case ETHER_STAT_SQE_ERRORS:
+		*val = 0;
+
+		return (0);
+
+	/* Map MAC/Ether stats to ECM statistics */
+	case MAC_STAT_NORCVBUF:
+		fs = ECM_RCV_NO_BUFFER;
+
+		break;
+	case MAC_STAT_NOXMTBUF:
+		fs = ECM_XMIT_ERROR;
+
+		break;
+	case MAC_STAT_IERRORS:
+		fs = ECM_RCV_ERROR;
+
+		break;
+	case MAC_STAT_OERRORS:
+		fs = ECM_XMIT_ERROR;
+
+		break;
+	case MAC_STAT_RBYTES:
+		fs = ECM_DIRECTED_BYTES_RCV;
+
+		break;
+	case MAC_STAT_IPACKETS:
+		fs = ECM_RCV_OK; /* frames */
+
+		break;
+	case MAC_STAT_OBYTES:
+		fs = ECM_DIRECTED_BYTES_XMIT;
+
+		break;
+	case MAC_STAT_OPACKETS:
+		fs = ECM_XMIT_OK; /* frames */
+
+		break;
+	case MAC_STAT_MULTIRCV:
+		fs = ECM_MULTICAST_FRAMES_RCV;
+
+		break;
+	case MAC_STAT_BRDCSTRCV:
+		fs = ECM_BROADCAST_FRAMES_RCV;
+
+		break;
+	case MAC_STAT_MULTIXMT:
+		fs = ECM_MULTICAST_FRAMES_XMIT;
+
+		break;
+	case MAC_STAT_BRDCSTXMT:
+		fs = ECM_BROADCAST_FRAMES_XMIT;
+
+		break;
+	case MAC_STAT_COLLISIONS:
+		fs = ECM_XMIT_MAX_COLLISIONS;
+
+		break;
+	case MAC_STAT_OVERFLOWS:
+		fs = ECM_RCV_OVERRUN;
+
+		break;
+	case MAC_STAT_UNDERFLOWS:
+		fs = ECM_XMIT_UNDERRUN;
+
+		break;
+	case ETHER_STAT_FCS_ERRORS:
+		fs = ECM_RCV_CRC_ERROR;
+
+		break;
+	case ETHER_STAT_ALIGN_ERRORS:
+		fs = ECM_RCV_ERROR_ALIGNMENT;
+
+		break;
+	case ETHER_STAT_DEFER_XMTS:
+		fs = ECM_XMIT_DEFERRED;
+
+		break;
+	case ETHER_STAT_FIRST_COLLISIONS:
+		fs = ECM_XMIT_ONE_COLLISION;
+
+		break;
+	case ETHER_STAT_MULTI_COLLISIONS:
+		fs = ECM_XMIT_MORE_COLLISIONS;
+
+		break;
+	case ETHER_STAT_TX_LATE_COLLISIONS:
+		fs = ECM_XMIT_LATE_COLLISIONS;
+
+		break;
+
+	default:
+		return (ENOTSUP);
+	}
+
+	/*
+	 * we need to access device to get required stats,
+	 * so check device state first
+	 */
+	mutex_enter(&ecmp->ecm_mutex);
+	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_m_stat: device not ONLINE");
+
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return (EIO);
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+
+	rval = usbecm_get_statistics(ecmp,
+	    ECM_STAT_SELECTOR(fs), &stats);
+	if (rval != USB_SUCCESS) {
+		mutex_enter(&ecmp->ecm_mutex);
+		switch (stat) {
+		case MAC_STAT_IERRORS:
+			*val = ecmp->ecm_stat.es_ierrors;
+
+			break;
+		case MAC_STAT_OERRORS:
+			*val = ecmp->ecm_stat.es_oerrors;
+
+			break;
+		case MAC_STAT_RBYTES:
+			*val = ecmp->ecm_stat.es_ibytes;
+
+			break;
+		case MAC_STAT_IPACKETS:
+			*val = ecmp->ecm_stat.es_ipackets;
+
+			break;
+		case MAC_STAT_OBYTES:
+			*val = ecmp->ecm_stat.es_obytes;
+
+			break;
+		case MAC_STAT_OPACKETS:
+			*val = ecmp->ecm_stat.es_opackets;
+
+			break;
+		case MAC_STAT_MULTIRCV:
+			*val = ecmp->ecm_stat.es_multircv;
+
+			break;
+		case MAC_STAT_MULTIXMT:
+			*val = ecmp->ecm_stat.es_multixmt;
+
+			break;
+		case MAC_STAT_BRDCSTRCV:
+			*val = ecmp->ecm_stat.es_brdcstrcv;
+
+			break;
+		case MAC_STAT_BRDCSTXMT:
+			*val = ecmp->ecm_stat.es_brdcstxmt;
+
+			break;
+		case ETHER_STAT_MACXMT_ERRORS:
+			*val = ecmp->ecm_stat.es_macxmt_err;
+			break;
+		default:
+			*val = 0;
+
+			break;
+		}
+		mutex_exit(&ecmp->ecm_mutex);
+	} else {
+		*val = stats;
+	}
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_stat: end");
+
+	return (0);
+}
+
+
+/*
+ * Start the device:
+ *	- Set proper altsettings of the data interface
+ *	- Open status and data endpoints
+ *	- Start status polling
+ *	- Get bulk-in ep ready to receive data from ethernet
+ *
+ * Concurrency: Presumably fully concurrent, must lock.
+ */
+static int
+usbecm_m_start(void *arg)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
+	int rval;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_start: entry");
+
+	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
+	mutex_enter(&ecmp->ecm_mutex);
+	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_m_start: device not online");
+		rval = ENODEV;
+		mutex_exit(&ecmp->ecm_mutex);
+
+		goto fail;
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+
+	if (usbecm_open_pipes(ecmp) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_m_start: open pipes fail");
+		rval = EIO;
+
+		goto fail;
+	}
+
+	mutex_enter(&ecmp->ecm_mutex);
+	if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_m_start: fail to start_rx");
+		mutex_exit(&ecmp->ecm_mutex);
+		rval = EIO;
+
+		goto fail;
+	}
+	ecmp->ecm_mac_state = USBECM_MAC_STARTED;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	/* set the device to receive all multicast/broadcast pkts */
+	rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
+	    CDC_ECM_PKT_TYPE_DIRECTED | CDC_ECM_PKT_TYPE_ALL_MCAST |
+	    CDC_ECM_PKT_TYPE_BCAST, NULL);
+	if (rval != USB_SUCCESS) {
+		USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_m_start: set packet filters fail,"
+		    " rval=%d, continue", rval);
+	}
+
+	if (ECM_DS_OP_VALID(ecm_ds_start)) {
+		if (ecmp->ecm_ds_ops->ecm_ds_start(ecmp) != USB_SUCCESS) {
+			USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+			    "usbecm_m_start: Can't start hardware");
+
+			goto fail;
+		}
+	}
+
+	usb_release_access(ecmp->ecm_ser_acc);
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_start: end");
+
+	/*
+	 * To mark the link as RUNNING.
+	 *
+	 * ECM spec doesn't provide a way for host to get the status
+	 * of the physical link initiatively. Only the device can
+	 * report the link state through interrupt endpoints.
+	 */
+	mac_link_update(ecmp->ecm_mh, LINK_STATE_UP);
+	mutex_enter(&ecmp->ecm_mutex);
+	ecmp->ecm_stat.es_linkstate = LINK_STATE_UP;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	return (DDI_SUCCESS);
+fail:
+	usb_release_access(ecmp->ecm_ser_acc);
+
+	return (rval);
+}
+
+/*
+ * Stop the device.
+ */
+static void
+usbecm_m_stop(void *arg)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_stop: entry");
+
+	usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
+	if (ECM_DS_OP_VALID(ecm_ds_stop)) {
+		if (ecmp->ecm_ds_ops->ecm_ds_stop(ecmp) != USB_SUCCESS) {
+			USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+			    "usbecm_m_stop: fail to stop hardware");
+		}
+	}
+
+	usbecm_close_pipes(ecmp);
+	usb_release_access(ecmp->ecm_ser_acc);
+
+	mutex_enter(&ecmp->ecm_mutex);
+	ecmp->ecm_mac_state = USBECM_MAC_STOPPED;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN);
+	mutex_enter(&ecmp->ecm_mutex);
+	ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_stop: end");
+}
+
+/*
+ * Change the MAC address of the device.
+ */
+/*ARGSUSED*/
+static int
+usbecm_m_unicst(void *arg, const uint8_t *macaddr)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
+	uint16_t	filter;
+	int		rval;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_unicst: entry");
+
+	/*
+	 * The device doesn't support to set a different MAC addr.
+	 * Hence, it's not necessary to stop the device first if
+	 * the mac addresses are identical. And we just set unicast
+	 * filter only.
+	 */
+	if (bcmp(macaddr, ecmp->ecm_srcaddr, ETHERADDRL) != 0) {
+		USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_m_unicst: not supported to set a"
+		    " different MAC addr");
+
+		return (DDI_FAILURE);
+	}
+	mutex_enter(&ecmp->ecm_mutex);
+	filter = ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_DIRECTED;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
+	rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
+	    filter, NULL);
+	usb_release_access(ecmp->ecm_ser_acc);
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_unicst: rval = %d", rval);
+
+	/* some devices may not support this request, we just return success */
+	return (DDI_SUCCESS);
+}
+
+/*
+ * Enable/disable multicast.
+ */
+/*ARGSUSED*/
+static int
+usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
+	uint16_t	filter;
+	int	rval = 0;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_multicst: entry");
+	mutex_enter(&ecmp->ecm_mutex);
+
+	/*
+	 * To simplify the implementation, we support switching
+	 * all multicast on/off feature only
+	 */
+	if (add == B_TRUE) {
+		ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_ALL_MCAST;
+	} else {
+		ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_ALL_MCAST;
+	}
+	filter = ecmp->ecm_pkt_flt;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
+	if (ecmp->ecm_compatibility &&
+	    (ecmp->ecm_desc.wNumberMCFilters & 0x7F)) {
+	/* Device supports SetEthernetMulticastFilters request */
+		rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
+		    filter, NULL);
+		USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_m_multicst: rval = %d", rval);
+	}
+	usb_release_access(ecmp->ecm_ser_acc);
+
+	/* some devices may not support this request, we just return success */
+	return (DDI_SUCCESS);
+}
+
+/*
+ * Enable/disable promiscuous mode.
+ */
+static int
+usbecm_m_promisc(void *arg, boolean_t on)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
+	uint16_t	filter;
+	int		rval;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_promisc: entry");
+
+	mutex_enter(&ecmp->ecm_mutex);
+	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_m_promisc: device not ONLINE");
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return (DDI_FAILURE);
+	}
+
+
+	if (on == B_TRUE) {
+		ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_PROMISC;
+	} else {
+		ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_PROMISC;
+	}
+	filter = ecmp->ecm_pkt_flt;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
+	rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
+	    filter, NULL);
+	usb_release_access(ecmp->ecm_ser_acc);
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_promisc: rval=%d", rval);
+
+	/*
+	 * devices may not support this request, we just
+	 * return success to let upper layer to do further
+	 * operation.
+	 */
+	return (DDI_SUCCESS);
+}
+
+/*
+ * IOCTL request: Does not do anything. Will be enhanced
+ *	in future.
+ */
+static void
+usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
+	struct iocblk   *iocp;
+	int cmd;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_ioctl: entry");
+
+	mutex_enter(&ecmp->ecm_mutex);
+	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_m_ioctl: device not ONLINE");
+		mutex_exit(&ecmp->ecm_mutex);
+
+		miocnak(wq, mp, 0, EIO);
+
+		return;
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+
+	iocp = (void *)mp->b_rptr;
+	iocp->ioc_error = 0;
+	cmd = iocp->ioc_cmd;
+
+	usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
+
+	switch (cmd) {
+	default:
+		USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "unknown cmd 0x%x", cmd);
+		usb_release_access(ecmp->ecm_ser_acc);
+		miocnak(wq, mp, 0, EINVAL);
+
+		return;
+	}
+}
+
+/*
+ * callback functions for get/set properties
+ *	Does not do anything. Will be enhanced to
+ *	support set/get properties in future.
+ */
+/*ARGSUSED*/
+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)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
+	int err = ENOTSUP;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_setprop: entry");
+
+	return (err);
+}
+
+/*ARGSUSED*/
+static int usbecm_m_getprop(void *arg, const char *pr_name,
+    mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
+	int err = ENOTSUP;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_getprop: entry");
+
+	mutex_enter(&ecmp->ecm_mutex);
+	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return (EIO);
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+
+	return (err);
+}
+
+/*
+ * Transmit a data frame.
+ */
+static mblk_t *
+usbecm_m_tx(void *arg, mblk_t *mp)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
+	mblk_t *next;
+	int count = 0;
+
+	ASSERT(mp != NULL);
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_m_tx: entry");
+
+	mutex_enter(&ecmp->ecm_mutex);
+	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_m_tx: device not ONLINE");
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return (mp);
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+
+	usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
+
+	/*
+	 * To make use of the device maximum capability,
+	 * concatenate msg blocks in a msg to ETHERMAX length.
+	 */
+	while (mp != NULL) {
+		next = mp->b_next;
+		mp->b_next = NULL;
+
+		if (usbecm_send_data(ecmp, mp) != DDI_SUCCESS) {
+			USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
+			    "usbecm_m_tx: send data fail");
+
+			/* failure statistics */
+			mutex_enter(&ecmp->ecm_mutex);
+			ecmp->ecm_stat.es_oerrors++;
+			mutex_exit(&ecmp->ecm_mutex);
+
+			mp->b_next = next;
+
+			break;
+		}
+
+		/*
+		 * To make it simple, we count all packets, no matter
+		 * the device supports ethernet statistics or not.
+		 */
+		mutex_enter(&ecmp->ecm_mutex);
+		ecmp->ecm_stat.es_opackets++;
+		ecmp->ecm_stat.es_obytes += MBLKL(mp);
+		mutex_exit(&ecmp->ecm_mutex);
+
+		freemsg(mp); /* free this msg upon success */
+
+		mp = next;
+		USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_m_tx: %d msgs processed", ++count);
+	}
+
+	usb_release_access(ecmp->ecm_ser_acc);
+
+	return (mp);
+}
+
+/*
+ * usbecm_bulkin_cb:
+ *	Bulk In regular and exeception callback;
+ *	USBA framework will call this callback
+ *	after deal with bulkin request.
+ */
+/*ARGSUSED*/
+static void
+usbecm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+	usbecm_state_t	*ecmp = (usbecm_state_t *)req->bulk_client_private;
+	mblk_t		*data, *mp;
+	int		data_len;
+	int		max_pkt_size = ecmp->ecm_bulkin_sz;
+
+	data = req->bulk_data;
+	data_len = (data) ? MBLKL(data) : 0;
+
+	ASSERT(data->b_cont == NULL);
+
+	mutex_enter(&ecmp->ecm_mutex);
+
+	USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh,
+	    "usbecm_bulkin_cb: state=%d, len=%d", ecmp->ecm_bulkin_state,
+	    data_len);
+
+	/*
+	 * may receive a zero length packet according
+	 * to USB short packet semantics
+	 */
+	if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) &&
+	    (req->bulk_completion_reason == USB_CR_OK)) {
+		if (data_len) {
+			if (ecmp->ecm_rcv_queue == NULL) {
+				ecmp->ecm_rcv_queue = data;
+			} else {
+				if ((msgsize(ecmp->ecm_rcv_queue) + data_len)
+				    > ETHERMAX) {
+				/*
+				 * Exceed the ethernet maximum length, we think
+				 * something is wrong with this frame and hence
+				 * free older data. Accept new data instead.
+				 */
+					freemsg(ecmp->ecm_rcv_queue);
+					ecmp->ecm_rcv_queue = data;
+				} else {
+					linkb(ecmp->ecm_rcv_queue, data);
+				}
+			}
+		} else {
+		/*
+		 * Do not put zero length packet to receive queue.
+		 * Otherwise, msgpullup will dupmsg() a zero length
+		 * mblk, which will cause memleaks.
+		 */
+			freemsg(data);
+		}
+
+		/*
+		 * ECM V1.2, section 3.3.1, a short(including zero length)
+		 * packet signifies end of frame. We can submit this frame
+		 * to upper layer now.
+		 */
+		if ((data_len < max_pkt_size) &&
+		    (msgsize(ecmp->ecm_rcv_queue) > 0)) {
+			mp = msgpullup(ecmp->ecm_rcv_queue, -1);
+			freemsg(ecmp->ecm_rcv_queue);
+			ecmp->ecm_rcv_queue = NULL;
+
+			ecmp->ecm_stat.es_ipackets++;
+			ecmp->ecm_stat.es_ibytes += msgsize(mp);
+			if (mp && (mp->b_rptr[0] & 0x01)) {
+				if (bcmp(mp->b_rptr, usbecm_broadcast,
+				    ETHERADDRL) != 0) {
+					ecmp->ecm_stat.es_multircv++;
+				} else {
+					ecmp->ecm_stat.es_brdcstrcv++;
+				}
+			}
+
+			if (mp) {
+				mutex_exit(&ecmp->ecm_mutex);
+				mac_rx(ecmp->ecm_mh, NULL, mp);
+				mutex_enter(&ecmp->ecm_mutex);
+			}
+		}
+
+		/* prevent USBA from freeing data along with the request */
+		req->bulk_data = NULL;
+	} else if (req->bulk_completion_reason != USB_CR_OK) {
+		ecmp->ecm_stat.es_ierrors++;
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+
+	usb_free_bulk_req(req);
+
+	/* receive more */
+	mutex_enter(&ecmp->ecm_mutex);
+	if (((ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) ||
+	    (ecmp->ecm_bulkin_state == USBECM_PIPE_IDLE)) &&
+	    (ecmp->ecm_dev_state == USB_DEV_ONLINE)) {
+		if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
+			USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
+			    "usbecm_bulkin_cb: restart rx fail "
+			    "ecmp_state = %d", ecmp->ecm_bulkin_state);
+		}
+	} else if (ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) {
+		ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+}
+
+/*
+ * usbsecm_rx_start:
+ *	start data receipt
+ */
+static int
+usbecm_rx_start(usbecm_state_t *ecmp)
+{
+	usb_bulk_req_t	*br;
+	int		rval = USB_FAILURE;
+	int		data_len;
+
+	ASSERT(mutex_owned(&ecmp->ecm_mutex));
+
+	DTRACE_PROBE2(usbecm_rx__start, int, ecmp->ecm_xfer_sz,
+	    int, ecmp->ecm_bulkin_sz);
+
+	ecmp->ecm_bulkin_state = USBECM_PIPE_BUSY;
+	data_len = ecmp->ecm_bulkin_sz;
+
+	mutex_exit(&ecmp->ecm_mutex);
+	br = usb_alloc_bulk_req(ecmp->ecm_dip, data_len, USB_FLAGS_SLEEP);
+	if (br == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
+		    "usbsecm_rx_start: allocate bulk request failed");
+
+		mutex_enter(&ecmp->ecm_mutex);
+
+		return (USB_FAILURE);
+	}
+	/* initialize bulk in request. */
+	br->bulk_len = data_len;
+	br->bulk_timeout = 0;
+	br->bulk_cb = usbecm_bulkin_cb;
+	br->bulk_exc_cb = usbecm_bulkin_cb;
+	br->bulk_client_private = (usb_opaque_t)ecmp;
+	br->bulk_attributes = USB_ATTRS_AUTOCLEARING
+	    | USB_ATTRS_SHORT_XFER_OK;
+
+	rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkin_ph, br, 0);
+	mutex_enter(&ecmp->ecm_mutex);
+	if (rval != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
+		    "usbsecm_rx_start: bulk transfer failed %d", rval);
+		usb_free_bulk_req(br);
+		ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
+	}
+
+	return (rval);
+}
+
+/*
+ * usbecm_bulkout_cb:
+ *	Bulk Out regular and exeception callback;
+ *	USBA framework will call this callback function
+ *	after deal with bulkout request.
+ */
+/*ARGSUSED*/
+static void
+usbecm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private;
+	int		data_len;
+	boolean_t	need_update = B_FALSE;
+
+	data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0;
+
+	USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh,
+	    "usbecm_bulkout_cb: data_len = %d, cr=%d", data_len,
+	    req->bulk_completion_reason);
+
+	mutex_enter(&ecmp->ecm_mutex);
+	if ((data_len > 0) && (ecmp->ecm_tx_cnt > 0)) {
+		if (ecmp->ecm_tx_cnt == usbecm_tx_max) {
+			need_update = B_TRUE;
+		}
+		ecmp->ecm_tx_cnt--;
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+
+	if (req->bulk_completion_reason && (data_len > 0)) {
+		mutex_enter(&ecmp->ecm_mutex);
+		ecmp->ecm_stat.es_oerrors++;
+		mutex_exit(&ecmp->ecm_mutex);
+
+		need_update = B_TRUE;
+	}
+
+	/*
+	 * notify MAC layer to retransfer the failed packet
+	 * Or notity MAC that we have more buffer now.
+	 */
+	if (need_update) {
+		mac_tx_update(ecmp->ecm_mh);
+	}
+
+	usb_free_bulk_req(req);
+}
+
+static int
+usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data)
+{
+	usb_bulk_req_t	*br;
+	int		rval = USB_FAILURE;
+	int		data_len = MBLKL(data);
+	int		max_pkt_size;
+	mblk_t		*new_data = NULL;
+	int		new_data_len = 0;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_send_data: length = %d, total len=%d",
+	    data_len, (int)msgdsize(data));
+
+	mutex_enter(&ecmp->ecm_mutex);
+	if (ecmp->ecm_tx_cnt >= usbecm_tx_max) {
+		USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_send_data: (%d) exceeds TX max queue length",
+		    ecmp->ecm_tx_cnt);
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return (USB_FAILURE);
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+
+	data_len = msgsize(data);
+	if (data_len > ETHERMAX) {
+		mutex_enter(&ecmp->ecm_mutex);
+		ecmp->ecm_stat.es_macxmt_err++;
+		mutex_exit(&ecmp->ecm_mutex);
+
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_send_data: packet too long, %d", data_len);
+
+		return (USB_FAILURE);
+	}
+
+	if (data_len < ETHERMIN) {
+		mblk_t *tmp;
+
+		USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_send_data: short packet, padding to ETHERMIN");
+
+		new_data_len = ETHERMIN;
+		if ((new_data = allocb(new_data_len, 0)) == NULL) {
+			USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+			    "usbecm_send_data: fail to allocb");
+
+			return (USB_FAILURE);
+		}
+		bzero(new_data->b_wptr, new_data_len);
+		for (tmp = data; tmp != NULL; tmp = tmp->b_cont) {
+			bcopy(tmp->b_rptr, new_data->b_wptr, MBLKL(tmp));
+			new_data->b_wptr += MBLKL(tmp);
+		}
+
+		new_data->b_wptr = new_data->b_rptr + new_data_len;
+	}
+
+	br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
+	if (br == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_send_data: alloc req failed.");
+
+		return (USB_FAILURE);
+	}
+
+	/* initialize the bulk out request */
+	if (new_data) {
+		br->bulk_data = msgpullup(new_data, -1); /* msg allocated! */
+		br->bulk_len = new_data_len;
+	} else {
+		br->bulk_data = msgpullup(data, -1); /* msg allocated! */
+		br->bulk_len = data_len;
+	}
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_send_data: bulk_len = %d", br->bulk_len);
+
+	br->bulk_timeout = USBECM_BULKOUT_TIMEOUT;
+	br->bulk_cb = usbecm_bulkout_cb;
+	br->bulk_exc_cb = usbecm_bulkout_cb;
+	br->bulk_client_private = (usb_opaque_t)ecmp;
+	br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
+
+	if (br->bulk_data != NULL) {
+		if (br->bulk_data->b_rptr[0] & 0x01) {
+			mutex_enter(&ecmp->ecm_mutex);
+			if (bcmp(br->bulk_data->b_rptr, usbecm_broadcast,
+			    ETHERADDRL) != 0) {
+				ecmp->ecm_stat.es_multixmt++;
+			} else {
+				ecmp->ecm_stat.es_brdcstxmt++;
+			}
+			mutex_exit(&ecmp->ecm_mutex);
+		}
+		rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0);
+	}
+
+	if (rval != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_send_data: Send Data failed.");
+
+		/*
+		 * br->bulk_data should be freed because we allocated
+		 * it in this function.
+		 */
+		usb_free_bulk_req(br);
+
+	} else {
+		mutex_enter(&ecmp->ecm_mutex);
+		ecmp->ecm_tx_cnt++;
+		mutex_exit(&ecmp->ecm_mutex);
+
+		/*
+		 * ECM V1.2, section 3.3.1, a short(including zero length)
+		 * packet signifies end of frame. We should send a zero length
+		 * packet to device if the total data lenght is multiple of
+		 * bulkout endpoint's max packet size.
+		 */
+		max_pkt_size = ecmp->ecm_bulk_out_ep->ep_descr.wMaxPacketSize;
+		if ((data_len % max_pkt_size) == 0) {
+			if ((rval = usbecm_send_zero_data(ecmp))
+			    != USB_SUCCESS) {
+				USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+				    "usbecm_send_data: fail to send padding");
+			}
+		}
+	}
+
+	if (new_data) {
+		freemsg(new_data);
+	}
+
+	USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
+	    "usbecm_send_data: len(%d) data sent, rval=%d",
+	    new_data_len ? new_data_len : data_len, rval);
+
+	return (rval);
+}
+
+static int
+usbecm_send_zero_data(usbecm_state_t *ecmp)
+{
+	usb_bulk_req_t	*br;
+	int		rval = USB_FAILURE;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_send_zero_data: entry");
+
+	br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
+	if (br == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_send_data: alloc req failed.");
+
+		return (USB_FAILURE);
+	}
+
+	/* initialize the bulk out request */
+	br->bulk_len = 0;
+	br->bulk_timeout = USBECM_BULKOUT_TIMEOUT;
+	br->bulk_cb = usbecm_bulkout_cb;
+	br->bulk_exc_cb = usbecm_bulkout_cb;
+	br->bulk_client_private = (usb_opaque_t)ecmp;
+	br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
+
+	rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0);
+
+	if (rval != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
+		    "usbecm_send_zero_data: Send data failed, rval=%d",
+		    rval);
+
+		/*
+		 * br->bulk_data should be freed because we allocated
+		 * it in this function.
+		 */
+		usb_free_bulk_req(br);
+
+	}
+
+	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
+	    "usbecm_send_zero_data: end");
+
+	return (rval);
+}
+
+/*
+ * Loadable module configuration entry points
+ */
+
+/*
+ * _init module entry point.
+ *
+ * Called when the module is being loaded into memory.
+ */
+int
+_init(void)
+{
+	int err;
+
+	err = ddi_soft_state_init(&usbecm_statep, sizeof (usbecm_state_t), 1);
+
+	if (err != DDI_SUCCESS)
+		return (err);
+
+	mac_init_ops(&usbecm_devops, "usbecm");
+	err = mod_install(&usbecm_ml);
+
+	if (err != DDI_SUCCESS) {
+		mac_fini_ops(&usbecm_devops);
+		ddi_soft_state_fini(&usbecm_statep);
+	}
+
+	return (err);
+}
+
+/*
+ * _info module entry point.
+ *
+ * Called to obtain information about the module.
+ */
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&usbecm_ml, modinfop));
+}
+
+/*
+ * _fini module entry point.
+ *
+ * Called when the module is being unloaded.
+ */
+int
+_fini(void)
+{
+	int err;
+
+	err = mod_remove(&usbecm_ml);
+	if (err == DDI_SUCCESS) {
+		mac_fini_ops(&usbecm_devops);
+		ddi_soft_state_fini(&usbecm_statep);
+	}
+
+	return (err);
+}
+
+/*
+ * usbecm_pipe_start_polling:
+ *	start polling on the interrupt pipe
+ */
+static void
+usbecm_pipe_start_polling(usbecm_state_t *ecmp)
+{
+	usb_intr_req_t	*intr;
+	int		rval;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
+	    "usbecm_pipe_start_polling: ");
+
+	if (ecmp->ecm_intr_ph == NULL) {
+
+		return;
+	}
+
+	intr = usb_alloc_intr_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
+
+	/*
+	 * If it is in interrupt context, usb_alloc_intr_req will return NULL if
+	 * called with SLEEP flag.
+	 */
+	if (!intr) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
+		    "usbecm_pipe_start_polling: alloc req failed.");
+
+		return;
+	}
+
+	/* initialize the interrupt request. */
+	intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
+	    USB_ATTRS_AUTOCLEARING;
+	intr->intr_len = ecmp->ecm_intr_ep->ep_descr.wMaxPacketSize;
+	intr->intr_client_private = (usb_opaque_t)ecmp;
+	intr->intr_cb = usbecm_intr_cb;
+	intr->intr_exc_cb = usbecm_intr_ex_cb;
+
+	rval = usb_pipe_intr_xfer(ecmp->ecm_intr_ph, intr, USB_FLAGS_SLEEP);
+
+	mutex_enter(&ecmp->ecm_mutex);
+	if (rval == USB_SUCCESS) {
+		ecmp->ecm_intr_state = USBECM_PIPE_BUSY;
+	} else {
+		usb_free_intr_req(intr);
+		ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
+		USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
+		    "usbecm_pipe_start_polling: failed (%d)", rval);
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+
+	USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
+	    "usbecm_pipe_start_polling: end, rval=%d", rval);
+}
+
+
+/*
+ * usbsecm_intr_cb:
+ *	interrupt pipe normal callback
+ */
+/*ARGSUSED*/
+static void
+usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private;
+	mblk_t		*data = req->intr_data;
+	int		data_len;
+
+	data_len = (data) ? MBLKL(data) : 0;
+
+	DTRACE_PROBE2(usbecm_intr__cb, (usb_intr_req_t *), req, int, data_len);
+
+	/* check data length */
+	if (data_len < 8) {
+		USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
+		    "usbsecm_intr_cb: %d packet too short", data_len);
+		usb_free_intr_req(req);
+
+		return;
+	}
+	req->intr_data = NULL;
+	usb_free_intr_req(req);
+
+	mutex_enter(&ecmp->ecm_mutex);
+	/* parse interrupt data -- notifications */
+	usbecm_parse_intr_data(ecmp, data);
+	mutex_exit(&ecmp->ecm_mutex);
+}
+
+
+/*
+ * usbsecm_intr_ex_cb:
+ *	interrupt pipe exception callback
+ */
+/*ARGSUSED*/
+static void
+usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
+{
+	usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private;
+	usb_cr_t	cr = req->intr_completion_reason;
+
+	DTRACE_PROBE2(usbecm_intr_ex__cb, int, ecmp->ecm_dev_state,
+	    (usb_cr_t), cr);
+
+	usb_free_intr_req(req);
+
+	/*
+	 * If completion reason isn't USB_CR_PIPE_CLOSING and
+	 * USB_CR_STOPPED_POLLING, restart polling.
+	 */
+	if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) {
+		mutex_enter(&ecmp->ecm_mutex);
+
+		if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
+
+			USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
+			    "usbsecm_intr_ex_cb: state = %d",
+			    ecmp->ecm_dev_state);
+
+			mutex_exit(&ecmp->ecm_mutex);
+
+			return;
+		}
+		mutex_exit(&ecmp->ecm_mutex);
+
+		usbecm_pipe_start_polling(ecmp);
+	}
+}
+
+
+/*
+ * usbsecm_parse_intr_data:
+ *	Parse data received from interrupt callback
+ */
+static void
+usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data)
+{
+	uint8_t		bmRequestType;
+	uint8_t		bNotification;
+	uint16_t	wValue;
+	uint16_t	wLength;
+	int		linkstate;
+
+	bmRequestType = data->b_rptr[0];
+	bNotification = data->b_rptr[1];
+	/*
+	 * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1,
+	 * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0,
+	 * mLength is 2. So we directly get the value from the byte.
+	 */
+	wValue = data->b_rptr[2];
+	wLength = data->b_rptr[6];
+
+	if (ecmp->ecm_compatibility) {
+		if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) {
+			USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
+			    "usbsecm_parse_intr_data: unknown request "
+			    "type - 0x%x", bmRequestType);
+
+			freemsg(data);
+
+			return;
+		}
+	} else {
+		/* non-compatible device specific parsing */
+		if (ECM_DS_OP_VALID(ecm_ds_intr_cb)) {
+			if (ecmp->ecm_ds_ops->ecm_ds_intr_cb(ecmp, data)
+			    != USB_SUCCESS) {
+				USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
+				    "usbsecm_parse_intr_data: unknown request"
+				    "type - 0x%x", bmRequestType);
+			}
+		}
+		freemsg(data);
+
+		return;
+	}
+
+	/*
+	 * Check the return value of compatible devices
+	 */
+	switch (bNotification) {
+	case USB_CDC_NOTIFICATION_NETWORK_CONNECTION:
+		USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
+		    "usbsecm_parse_intr_data: %s network!",
+		    wValue ? "connected to" :"disconnected from");
+
+		linkstate = wValue ? LINK_STATE_UP:LINK_STATE_DOWN;
+		if (ecmp->ecm_stat.es_linkstate == linkstate) {
+		/* no changes to previous state */
+			break;
+		}
+
+		ecmp->ecm_stat.es_linkstate = linkstate;
+		mutex_exit(&ecmp->ecm_mutex);
+		mac_link_update(ecmp->ecm_mh, linkstate);
+		mutex_enter(&ecmp->ecm_mutex);
+
+		break;
+	case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE:
+		USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
+		    "usbsecm_parse_intr_data: A response is a available.");
+
+		break;
+	case USB_CDC_NOTIFICATION_SPEED_CHANGE:
+		USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
+		    "usbsecm_parse_intr_data: speed change");
+
+		/* check the parameter's length. */
+		if (wLength != 8) {
+			USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
+			    "usbsecm_parse_intr_data: error data length.");
+		} else {
+			uint32_t	us_rate, ds_rate;
+			uint8_t		*sp;
+
+			sp = &data->b_rptr[8];
+			LE_TO_UINT32(sp, us_rate);
+			sp = &data->b_rptr[12];
+			LE_TO_UINT32(sp, ds_rate);
+			ecmp->ecm_stat.es_upspeed = us_rate;
+			ecmp->ecm_stat.es_downspeed = ds_rate;
+		}
+
+		break;
+	default:
+		USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
+		    "usbsecm_parse_intr_data: unknown notification - 0x%x!",
+		    bNotification);
+
+		break;
+	}
+
+	freemsg(data);
+}
+
+/*
+ * usbecm_restore_device_state:
+ *	restore device state after CPR resume or reconnect
+ */
+static int
+usbecm_restore_device_state(usbecm_state_t *ecmp)
+{
+	int	state;
+
+	USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
+	    "usbecm_restore_device_state: ");
+
+	mutex_enter(&ecmp->ecm_mutex);
+	state = ecmp->ecm_dev_state;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	/* Check device status */
+	if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) {
+
+		return (state);
+	}
+
+	/* Check if we are talking to the same device */
+	if (usb_check_same_device(ecmp->ecm_dip, ecmp->ecm_lh, USB_LOG_L0,
+	    -1, USB_CHK_ALL, NULL) != USB_SUCCESS) {
+		mutex_enter(&ecmp->ecm_mutex);
+		state = ecmp->ecm_dev_state = USB_DEV_DISCONNECTED;
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return (state);
+	}
+
+	if (state == USB_DEV_DISCONNECTED) {
+		USB_DPRINTF_L1(PRINT_MASK_EVENTS, ecmp->ecm_lh,
+		    "usbecm_restore_device_state: Device has been reconnected "
+		    "but data may have been lost");
+	}
+
+	/* if MAC was started, restarted it */
+	mutex_enter(&ecmp->ecm_mutex);
+	if (ecmp->ecm_mac_state == USBECM_MAC_STARTED) {
+		USB_DPRINTF_L3(PRINT_MASK_EVENTS, ecmp->ecm_lh,
+		    "usbecm_restore_device_state: MAC was started");
+
+		mutex_exit(&ecmp->ecm_mutex);
+		/* Do the same operation as usbecm_m_start() does */
+		if (usbecm_open_pipes(ecmp) != USB_SUCCESS) {
+
+			return (state);
+		}
+
+		mutex_enter(&ecmp->ecm_mutex);
+		if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
+			mutex_exit(&ecmp->ecm_mutex);
+
+			return (state);
+		}
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+
+	/*
+	 * init device state
+	 */
+	mutex_enter(&ecmp->ecm_mutex);
+	state = ecmp->ecm_dev_state = USB_DEV_ONLINE;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	return (state);
+}
+
+/*
+ * usbecm_reconnect_event_cb:
+ *     called upon when the device is hotplugged back
+ */
+/*ARGSUSED*/
+static int
+usbecm_reconnect_event_cb(dev_info_t *dip)
+{
+	usbecm_state_t	*ecmp =
+	    (usbecm_state_t *)ddi_get_soft_state(usbecm_statep,
+	    ddi_get_instance(dip));
+
+	ASSERT(ecmp != NULL);
+
+	USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
+	    "usbecm_reconnect_event_cb: entry");
+
+	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
+
+	mutex_enter(&ecmp->ecm_mutex);
+	ASSERT(ecmp->ecm_dev_state == USB_DEV_DISCONNECTED);
+
+	mutex_exit(&ecmp->ecm_mutex);
+
+	if (usbecm_restore_device_state(ecmp) != USB_DEV_ONLINE) {
+		usb_release_access(ecmp->ecm_ser_acc);
+
+		return (USB_FAILURE);
+	}
+
+	usb_release_access(ecmp->ecm_ser_acc);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * usbecm_disconnect_event_cb:
+ *	callback for disconnect events
+ */
+/*ARGSUSED*/
+static int
+usbecm_disconnect_event_cb(dev_info_t *dip)
+{
+	usbecm_state_t	*ecmp = (usbecm_state_t *)ddi_get_soft_state(
+	    usbecm_statep, ddi_get_instance(dip));
+
+	USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
+	    "usbecm_disconnect_event_cb: entry");
+
+	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
+
+	mutex_enter(&ecmp->ecm_mutex);
+	ecmp->ecm_dev_state = USB_DEV_DISCONNECTED;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	usbecm_close_pipes(ecmp);
+
+	usb_release_access(ecmp->ecm_ser_acc);
+
+	USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
+	    "usbecm_disconnect_event_cb: End");
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * power management
+ * ----------------
+ *
+ * usbecm_create_pm_components:
+ *	create PM components
+ */
+static int
+usbecm_create_pm_components(usbecm_state_t *ecmp)
+{
+	dev_info_t	*dip = ecmp->ecm_dip;
+	usbecm_pm_t	*pm;
+	uint_t		pwr_states;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
+	    "usbecm_create_pm_components: entry");
+
+	if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
+		    "usbecm_create_pm_components: failed");
+
+		/* don't fail the attach process */
+		return (USB_SUCCESS);
+	}
+
+	pm = ecmp->ecm_pm =
+	    (usbecm_pm_t *)kmem_zalloc(sizeof (usbecm_pm_t), KM_SLEEP);
+
+	pm->pm_pwr_states = (uint8_t)pwr_states;
+	pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
+	pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip,
+	    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS);
+
+	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * usbecm_cleanup:
+ *	Release resources of current device during detach.
+ */
+static void
+usbecm_cleanup(usbecm_state_t *ecmp)
+{
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
+	    "usbecm_cleanup: ");
+
+	if (ecmp == NULL) {
+
+		return;
+	}
+
+	usbecm_close_pipes(ecmp);
+
+	/* unregister callback function */
+	if (ecmp->ecm_init_flags & USBECM_INIT_EVENTS) {
+		USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
+		    "usbecm_cleanup: unregister events");
+
+		usb_unregister_event_cbs(ecmp->ecm_dip, &usbecm_events);
+	}
+
+	/* destroy power management components */
+	if (ecmp->ecm_pm != NULL) {
+		USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
+		    "usbecm_cleanup: destroy pm");
+		usbecm_destroy_pm_components(ecmp);
+	}
+
+	/* free description of device tree. */
+	if (ecmp->ecm_def_ph != NULL) {
+		mutex_destroy(&ecmp->ecm_mutex);
+
+		usb_free_descr_tree(ecmp->ecm_dip, ecmp->ecm_dev_data);
+		ecmp->ecm_def_ph = NULL;
+	}
+
+	if (ecmp->ecm_lh != NULL) {
+		usb_free_log_hdl(ecmp->ecm_lh);
+		ecmp->ecm_lh = NULL;
+	}
+
+	/* detach client device */
+	if (ecmp->ecm_dev_data != NULL) {
+		usb_client_detach(ecmp->ecm_dip, ecmp->ecm_dev_data);
+	}
+
+	if (ecmp->ecm_init_flags & USBECM_INIT_MAC) {
+		usbecm_mac_fini(ecmp);
+	}
+
+	if (ecmp->ecm_init_flags & USBECM_INIT_SER) {
+		usb_fini_serialization(ecmp->ecm_ser_acc);
+	}
+
+	ddi_prop_remove_all(ecmp->ecm_dip);
+	ddi_remove_minor_node(ecmp->ecm_dip, NULL);
+}
+
+/*
+ * usbecm_destroy_pm_components:
+ *	destroy PM components
+ */
+static void
+usbecm_destroy_pm_components(usbecm_state_t *ecmp)
+{
+	usbecm_pm_t	*pm = ecmp->ecm_pm;
+	dev_info_t	*dip = ecmp->ecm_dip;
+	int		rval;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
+	    "usbecm_destroy_pm_components: ");
+
+	if (ecmp->ecm_dev_state != USB_DEV_DISCONNECTED) {
+		if (pm->pm_wakeup_enabled) {
+			rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+			if (rval != DDI_SUCCESS) {
+				USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
+				    "usbecm_destroy_pm_components: "
+				    "raising power failed (%d)", rval);
+			}
+
+			rval = usb_handle_remote_wakeup(dip,
+			    USB_REMOTE_WAKEUP_DISABLE);
+			if (rval != USB_SUCCESS) {
+				USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
+				    "usbecm_destroy_pm_components: "
+				    "disable remote wakeup failed (%d)", rval);
+			}
+		}
+
+		(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
+	}
+	kmem_free((caddr_t)pm, sizeof (usbecm_pm_t));
+	ecmp->ecm_pm = NULL;
+}
+
+/*
+ * usbecm_pm_set_busy:
+ *	mark device busy and raise power
+ */
+static void
+usbecm_pm_set_busy(usbecm_state_t *ecmp)
+{
+	usbecm_pm_t	*pm = ecmp->ecm_pm;
+	dev_info_t	*dip = ecmp->ecm_dip;
+	int		rval;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
+	    "usbecm_pm_set_busy: pm = 0x%p", (void *)pm);
+
+	if (pm == NULL) {
+
+		return;
+	}
+
+	mutex_enter(&ecmp->ecm_mutex);
+	/* if already marked busy, just increment the counter */
+	if (pm->pm_busy_cnt++ > 0) {
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return;
+	}
+
+	(void) pm_busy_component(dip, 0);
+
+	if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) {
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return;
+	}
+
+	/* need to raise power	*/
+	pm->pm_raise_power = B_TRUE;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+	if (rval != DDI_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
+		    "usbecm_pm_set_busy: raising power failed");
+	}
+
+	mutex_enter(&ecmp->ecm_mutex);
+	pm->pm_raise_power = B_FALSE;
+	mutex_exit(&ecmp->ecm_mutex);
+}
+
+
+/*
+ * usbecm_pm_set_idle:
+ *	mark device idle
+ */
+static void
+usbecm_pm_set_idle(usbecm_state_t *ecmp)
+{
+	usbecm_pm_t	*pm = ecmp->ecm_pm;
+	dev_info_t	*dip = ecmp->ecm_dip;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
+	    "usbecm_pm_set_idle: ");
+
+	if (pm == NULL) {
+
+		return;
+	}
+
+	mutex_enter(&ecmp->ecm_mutex);
+	if (--pm->pm_busy_cnt > 0) {
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return;
+	}
+
+	if (pm) {
+		(void) pm_idle_component(dip, 0);
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+}
+
+
+/*
+ * usbecm_pwrlvl0:
+ *	Functions to handle power transition for OS levels 0 -> 3
+ *	The same level as OS state, different from USB state
+ */
+static int
+usbecm_pwrlvl0(usbecm_state_t *ecmp)
+{
+	int		rval;
+
+	ASSERT(mutex_owned(&ecmp->ecm_mutex));
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
+	    "usbecm_pwrlvl0: ");
+
+	switch (ecmp->ecm_dev_state) {
+	case USB_DEV_ONLINE:
+		/* issue USB D3 command to the device */
+		rval = usb_set_device_pwrlvl3(ecmp->ecm_dip);
+		ASSERT(rval == USB_SUCCESS);
+		if ((ecmp->ecm_intr_ph != NULL) &&
+		    (ecmp->ecm_intr_state == USBECM_PIPE_BUSY)) {
+			mutex_exit(&ecmp->ecm_mutex);
+			usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph,
+			    USB_FLAGS_SLEEP);
+			mutex_enter(&ecmp->ecm_mutex);
+
+			ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
+		}
+		ecmp->ecm_dev_state = USB_DEV_PWRED_DOWN;
+		ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF;
+
+		/* FALLTHRU */
+	case USB_DEV_DISCONNECTED:
+	case USB_DEV_SUSPENDED:
+		/* allow a disconnect/cpr'ed device to go to lower power */
+
+		return (USB_SUCCESS);
+	case USB_DEV_PWRED_DOWN:
+	default:
+		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
+		    "usbecm_pwrlvl0: illegal device state");
+
+		return (USB_FAILURE);
+	}
+}
+
+
+/*
+ * usbecm_pwrlvl1:
+ *	Functions to handle power transition for OS levels 1 -> 2
+ */
+static int
+usbecm_pwrlvl1(usbecm_state_t *ecmp)
+{
+	/* issue USB D2 command to the device */
+	(void) usb_set_device_pwrlvl2(ecmp->ecm_dip);
+
+	return (USB_FAILURE);
+}
+
+
+/*
+ * usbecm_pwrlvl2:
+ *	Functions to handle power transition for OS levels 2 -> 1
+ */
+static int
+usbecm_pwrlvl2(usbecm_state_t *ecmp)
+{
+	/* issue USB D1 command to the device */
+	(void) usb_set_device_pwrlvl1(ecmp->ecm_dip);
+
+	return (USB_FAILURE);
+}
+
+
+/*
+ * usbecm_pwrlvl3:
+ *	Functions to handle power transition for OS levels 3 -> 0
+ *	The same level as OS state, different from USB state
+ */
+static int
+usbecm_pwrlvl3(usbecm_state_t *ecmp)
+{
+	int		rval;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
+	    "usbecm_pwrlvl3: ");
+
+	ASSERT(mutex_owned(&ecmp->ecm_mutex));
+
+	switch (ecmp->ecm_dev_state) {
+	case USB_DEV_PWRED_DOWN:
+		/* Issue USB D0 command to the device here */
+		rval = usb_set_device_pwrlvl0(ecmp->ecm_dip);
+		ASSERT(rval == USB_SUCCESS);
+
+		if (ecmp->ecm_intr_ph != NULL &&
+		    ecmp->ecm_intr_state == USBECM_PIPE_IDLE) {
+			mutex_exit(&ecmp->ecm_mutex);
+			usbecm_pipe_start_polling(ecmp);
+			mutex_enter(&ecmp->ecm_mutex);
+		}
+
+		ecmp->ecm_dev_state = USB_DEV_ONLINE;
+		ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
+
+		/* FALLTHRU */
+	case USB_DEV_ONLINE:
+		/* we are already in full power */
+
+		/* FALLTHRU */
+	case USB_DEV_DISCONNECTED:
+	case USB_DEV_SUSPENDED:
+
+		return (USB_SUCCESS);
+	default:
+		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
+		    "usbecm_pwrlvl3: illegal device state");
+
+		return (USB_FAILURE);
+	}
+}
+
+/*ARGSUSED*/
+static int
+usbecm_power(dev_info_t *dip, int comp, int level)
+{
+	usbecm_state_t	*ecmp;
+	usbecm_pm_t	*pm;
+	int		rval = USB_SUCCESS;
+
+	ecmp = ddi_get_soft_state(usbecm_statep, ddi_get_instance(dip));
+	pm = ecmp->ecm_pm;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
+	    "usbecm_power: entry");
+
+	/* check if pm is NULL */
+	if (pm == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
+		    "usbecm_power: pm is NULL.");
+
+		return (USB_FAILURE);
+	}
+
+	mutex_enter(&ecmp->ecm_mutex);
+	/*
+	 * check if we are transitioning to a legal power level
+	 */
+	if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) {
+		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
+		    "usbecm_power: "
+		    "illegal power level %d, pwr_states=%x",
+		    level, pm->pm_pwr_states);
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return (USB_FAILURE);
+	}
+
+	/*
+	 * if we are about to raise power and asked to lower power, fail
+	 */
+	if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) {
+		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
+		    "usbecm_power: wrong condition.");
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return (USB_FAILURE);
+	}
+
+	/*
+	 * Set the power status of device by request level.
+	 */
+	switch (level) {
+	case USB_DEV_OS_PWR_OFF:
+		rval = usbecm_pwrlvl0(ecmp);
+
+		break;
+	case USB_DEV_OS_PWR_1:
+		rval = usbecm_pwrlvl1(ecmp);
+
+		break;
+	case USB_DEV_OS_PWR_2:
+		rval = usbecm_pwrlvl2(ecmp);
+
+		break;
+	case USB_DEV_OS_FULL_PWR:
+		rval = usbecm_pwrlvl3(ecmp);
+
+		break;
+	}
+
+	mutex_exit(&ecmp->ecm_mutex);
+
+	return (rval);
+}
+
+/*
+ * Register with the MAC layer.
+ */
+static int
+usbecm_mac_init(usbecm_state_t *ecmp)
+{
+	mac_register_t *macp;
+	int err;
+
+	/*
+	 * Initialize mac structure
+	 */
+	macp = mac_alloc(MAC_VERSION);
+	if (macp == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "failed to allocate MAC structure");
+
+		return (USB_FAILURE);
+	}
+
+	/*
+	 * Initialize pointer to device specific functions
+	 */
+	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
+	macp->m_driver = ecmp;
+	macp->m_dip = ecmp->ecm_dip;
+
+	macp->m_src_addr = ecmp->ecm_srcaddr;
+	macp->m_callbacks = &usbecm_m_callbacks;
+	macp->m_min_sdu = 0;
+	macp->m_max_sdu = ETHERMTU;
+
+	/*
+	 * Register the macp to mac
+	 */
+	err = mac_register(macp, &ecmp->ecm_mh);
+	mac_free(macp);
+
+	if (err != DDI_SUCCESS) {
+		USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "failed to register MAC structure");
+
+		return (USB_FAILURE);
+	}
+
+	mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN);
+	ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN;
+	ecmp->ecm_tx_cnt = 0;
+
+	return (USB_SUCCESS);
+}
+
+static int
+usbecm_mac_fini(usbecm_state_t *ecmp)
+{
+	int rval = DDI_SUCCESS;
+
+	if ((ecmp->ecm_init_flags & USBECM_INIT_MAC) == 0) {
+		return (DDI_SUCCESS);
+	}
+
+	ecmp->ecm_init_flags &= ~USBECM_INIT_MAC;
+	if ((rval = mac_disable(ecmp->ecm_mh)) != 0) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "failed to disable MAC");
+
+		return (rval);
+	}
+
+	(void) mac_unregister(ecmp->ecm_mh);
+
+	return (rval);
+}
+
+static int
+usbecm_resume(usbecm_state_t *ecmp)
+{
+	int		current_state;
+	int		ret;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
+	    "usbecm_resume: ");
+
+	mutex_enter(&ecmp->ecm_mutex);
+	current_state = ecmp->ecm_dev_state;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	/* restore the status of device */
+	if (current_state != USB_DEV_ONLINE) {
+		ret = usbecm_restore_device_state(ecmp);
+	} else {
+		ret = USB_DEV_ONLINE;
+	}
+
+	return (ret);
+}
+
+static int
+usbecm_suspend(usbecm_state_t *ecmp)
+{
+	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
+
+	mutex_enter(&ecmp->ecm_mutex);
+	ecmp->ecm_dev_state = USB_DEV_SUSPENDED;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	usbecm_close_pipes(ecmp);
+
+	usb_release_access(ecmp->ecm_ser_acc);
+
+	return (0);
+}
+
+/*
+ * Translate MAC address from string to 6 bytes array int value
+ * Can't use ether_aton() since it requires format of x:x:x:x:x:x
+ */
+void
+label_to_mac(char *hex, unsigned char *mac)
+{
+	int i;
+	char c;
+
+	/* can only count 6 bytes! */
+	for (i = 0; i < 6; i++) {
+		/* upper 4 bits */
+		if (!isdigit(hex[2*i])) {
+			c = (toupper(hex[2 * i]) - 'A' + 10);
+		} else {
+			c = (hex[2 * i] - '0');
+		}
+		mac[i] = c * 16;
+
+		/* lower 4 bits */
+		if (!isdigit(hex[2*i + 1])) {
+			c = (toupper(hex[2 * i + 1]) - 'A' + 10);
+		} else {
+			c = hex[2 * i + 1] - '0';
+		}
+		mac[i] += c;
+	}
+}
+
+/*
+ * usbecm_get_descriptors:
+ *	parse functional descriptors of ecm compatible device
+ */
+static int
+usbecm_get_descriptors(usbecm_state_t *ecmp)
+{
+	int			i;
+	usb_cfg_data_t		*cfg;
+	usb_alt_if_data_t	*altif;
+	usb_cvs_data_t		*cvs;
+	int16_t			master_if = -1, slave_if = -1;
+	usb_cdc_ecm_descr_t	ecm_desc;
+	usb_ep_data_t		*ep_data;
+	usb_dev_descr_t		*usb_dev_desc;
+
+	USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
+	    "usbecm_get_descriptors: ");
+
+	usb_dev_desc = ecmp->ecm_dev_data->dev_descr;
+
+	/*
+	 * Special treatment of Sun's SP Ethernet device.
+	 */
+	if ((usb_dev_desc->idVendor == SUN_SP_VENDOR_ID) &&
+	    (usb_dev_desc->idProduct == SUN_SP_PRODUCT_ID)) {
+		if (usb_set_cfg(ecmp->ecm_dip, ecmp->ecm_cfg_index,
+		    USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS) {
+			USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+			    "usbecm_get_descriptors: fail to set cfg ");
+		} else {
+			usb_free_dev_data(ecmp->ecm_dip, ecmp->ecm_dev_data);
+			if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data,
+			    USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
+				USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+				    "usbecm_get_descriptors: fail to get"
+				    " dev_data");
+
+				return (USB_FAILURE);
+			}
+		}
+	}
+
+	cfg = ecmp->ecm_dev_data->dev_curr_cfg;
+
+	/* set default control and data interface */
+	ecmp->ecm_ctrl_if_no = ecmp->ecm_data_if_no = 0;
+
+	/* get current interfaces */
+	ecmp->ecm_ctrl_if_no = ecmp->ecm_dev_data->dev_curr_if;
+	if (cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt == 0) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_get_descriptors: elements in if_alt is %d",
+		    cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt);
+
+		return (USB_FAILURE);
+	}
+
+	altif = &cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_alt[0];
+
+	/*
+	 * Based on CDC specification, ECM devices usually include the
+	 * following function descriptors: Header, Union and ECM
+	 * Contry Selection function descriptors. This loop search tree data
+	 * structure for each ecm class descriptor.
+	 */
+	for (i = 0; i < altif->altif_n_cvs; i++) {
+		cvs = &altif->altif_cvs[i];
+
+		if ((cvs->cvs_buf == NULL) ||
+		    (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) {
+			continue;
+		}
+
+		switch (cvs->cvs_buf[2]) {
+		case USB_CDC_DESCR_TYPE_HEADER:
+			/*
+			 * parse header functional descriptor
+			 * Just to check integrity.
+			 */
+			if (cvs->cvs_buf_len != 5) {
+				return (USB_FAILURE);
+			}
+			break;
+		case USB_CDC_DESCR_TYPE_ETHERNET:
+			/* parse ECM functional descriptor */
+			if (cvs->cvs_buf_len >= USB_CDC_ECM_LEN) {
+				char buf[USB_MAXSTRINGLEN];
+
+				if (usb_parse_data("4cl2sc", cvs->cvs_buf,
+				    cvs->cvs_buf_len, (void *)&ecm_desc,
+				    (size_t)USB_CDC_ECM_LEN) <
+				    USB_CDC_ECM_LEN) {
+
+					return (USB_FAILURE);
+				}
+
+				/* get the MAC address */
+				if (usb_get_string_descr(ecmp->ecm_dip,
+				    USB_LANG_ID, ecm_desc.iMACAddress, buf,
+				    USB_MAXSTRINGLEN) != USB_SUCCESS) {
+
+					return (USB_FAILURE);
+				}
+
+				USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
+				    "usbecm_get_descriptors: macaddr=%s ",
+				    buf);
+
+				/* expects 12 characters */
+				if (strlen(buf) < 12) {
+					return (USB_FAILURE);
+				}
+				label_to_mac(buf, ecmp->ecm_srcaddr);
+
+				bcopy(&ecm_desc, &ecmp->ecm_desc,
+				    USB_CDC_ECM_LEN);
+			}
+			break;
+		case USB_CDC_DESCR_TYPE_UNION:
+			/* parse Union functional descriptor. */
+			if (cvs->cvs_buf_len >= 5) {
+				master_if = cvs->cvs_buf[3];
+				slave_if = cvs->cvs_buf[4];
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* For usb ecm devices, it must satisfy the following options. */
+	if (cfg->cfg_n_if < 2) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_get_descriptors: # of interfaces %d < 2",
+		    cfg->cfg_n_if);
+
+		return (USB_FAILURE);
+	}
+
+	if (ecmp->ecm_data_if_no == 0 &&
+	    slave_if != ecmp->ecm_data_if_no) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_get_descriptors: Device has no call management "
+		    "descriptor and use Union Descriptor.");
+
+		ecmp->ecm_data_if_no = slave_if;
+	}
+
+	if ((master_if != ecmp->ecm_ctrl_if_no) ||
+	    (slave_if != ecmp->ecm_data_if_no)) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_get_descriptors: control interface or "
+		    "data interface don't match.");
+
+		return (USB_FAILURE);
+	}
+
+	if ((ecmp->ecm_ctrl_if_no >= cfg->cfg_n_if) ||
+	    (ecmp->ecm_data_if_no >= cfg->cfg_n_if)) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_get_descriptors: control interface %d or "
+		    "data interface %d out of range.",
+		    ecmp->ecm_ctrl_if_no, ecmp->ecm_data_if_no);
+
+		return (USB_FAILURE);
+	}
+
+	/* ECM data interface has a minimal of two altsettings */
+	if (cfg->cfg_if[ecmp->ecm_data_if_no].if_n_alt < 2) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_get_descriptors: elements in if_alt is %d,"
+		    " MUST >= 2", cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt);
+
+		return (USB_FAILURE);
+	}
+
+	/* control interface must have interrupt endpoint */
+	if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
+	    ecmp->ecm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR,
+	    USB_EP_DIR_IN)) == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_get_descriptors: "
+		    "ctrl interface %d has no interrupt endpoint",
+		    ecmp->ecm_data_if_no);
+
+		return (USB_FAILURE);
+	}
+	ecmp->ecm_intr_ep = ep_data;
+
+	/* data interface alt 1 must have bulk in and out(ECM v1.2,p5) */
+	if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
+	    ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK,
+	    USB_EP_DIR_IN)) == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_get_descriptors: "
+		    "data interface %d has no bulk in endpoint",
+		    ecmp->ecm_data_if_no);
+
+		return (USB_FAILURE);
+	}
+	ecmp->ecm_bulk_in_ep = ep_data;
+
+	if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
+	    ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK,
+	    USB_EP_DIR_OUT)) == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_get_descriptors: "
+		    "data interface %d has no bulk out endpoint",
+		    ecmp->ecm_data_if_no);
+
+		return (USB_FAILURE);
+	}
+	ecmp->ecm_bulk_out_ep = ep_data;
+
+	/* set default value for ethernet packet filter */
+	ecmp->ecm_pkt_flt = CDC_ECM_PKT_TYPE_DIRECTED;
+
+	return (USB_SUCCESS);
+}
+
+/* Generate IEEE802 style MAC address */
+static void
+generate_ether_addr(uint8_t *mac_addr)
+{
+	random_get_bytes(mac_addr, 6);
+	mac_addr [0] &= 0xfe;	/* unicast only */
+	mac_addr [0] |= 0x02;	/* set locally administered bit */
+}
+
+/*
+ * Find a pair of bulk In/Out endpoints
+ */
+int usbecm_find_bulk_in_out_eps(usbecm_state_t *ecmp,
+    uint16_t ifc, usb_if_data_t *intf)
+{
+	uint16_t alt, alt_num;
+	usb_ep_data_t *intr_ep = NULL;
+	usb_ep_data_t *bulk_in, *bulk_out, *ep;
+
+	alt_num = intf->if_n_alt;
+
+	/*
+	 * for the non-compatible devices, to make it simple, we
+	 * suppose the devices have this kind of configuration:
+	 *	INTR In EP(if exists) + BULK In + Bulk Out in the
+	 *	same altsetting of the same interface
+	 */
+	for (alt = 0; alt < alt_num; alt++) {
+		/* search pair of bulk in/out EPs */
+		if (((bulk_in = usb_lookup_ep_data(ecmp->ecm_dip,
+		    ecmp->ecm_dev_data, ifc, alt, 0,
+		    USB_EP_ATTR_BULK,
+		    USB_EP_DIR_IN)) == NULL) ||
+		    (bulk_out = usb_lookup_ep_data(ecmp->ecm_dip,
+		    ecmp->ecm_dev_data, ifc, alt, 0,
+		    USB_EP_ATTR_BULK,
+		    USB_EP_DIR_OUT)) == NULL) {
+
+			continue;
+		}
+
+		/*
+		 * search interrupt pipe.
+		 */
+		if ((ep = usb_lookup_ep_data(ecmp->ecm_dip,
+		    ecmp->ecm_dev_data, ifc, alt, 0,
+		    USB_EP_ATTR_INTR, USB_EP_DIR_IN)) != NULL) {
+			intr_ep = ep;
+		}
+
+
+		ecmp->ecm_data_if_no = ifc;
+		ecmp->ecm_data_if_alt = alt;
+		ecmp->ecm_intr_ep = intr_ep;
+		ecmp->ecm_ctrl_if_no = ifc;
+		ecmp->ecm_bulk_in_ep = bulk_in;
+		ecmp->ecm_bulk_out_ep = bulk_out;
+
+		return (USB_SUCCESS);
+	}
+
+	return (USB_FAILURE);
+}
+
+static int
+usbecm_init_non_compatible_device(usbecm_state_t *ecmp)
+{
+	usb_if_data_t *cur_if;
+	uint16_t if_num, i;
+
+	/*
+	 * If device don't conform to spec, search pairs of bulk in/out
+	 * endpoints and fill related structure. We suppose this driver
+	 * is bound to a interface.
+	 */
+	cur_if = ecmp->ecm_dev_data->dev_curr_cfg->cfg_if;
+	if_num = ecmp->ecm_dev_data->dev_curr_cfg->cfg_n_if;
+
+	/* search each interface which have bulk in and out */
+	for (i = 0; i < if_num; i++) {
+		if (usbecm_find_bulk_in_out_eps(ecmp, i,
+		    cur_if) == USB_SUCCESS) {
+
+			break;
+		}
+		cur_if++;
+	}
+
+	USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
+	    "usbecm_init_non_compatible_device: ctrl_if=%d,"
+	    " data_if=%d, alt=%d", ecmp->ecm_ctrl_if_no,
+	    ecmp->ecm_data_if_no, ecmp->ecm_data_if_alt);
+
+	return (USB_SUCCESS);
+}
+
+static boolean_t
+usbecm_is_compatible(usbecm_state_t *ecmp)
+{
+	usb_cfg_data_t *cfg_data;
+	usb_if_data_t *intf;
+	usb_alt_if_data_t *alt;
+	int alt_num, if_num, cfg_num;
+	int i, j, cfg_index;
+
+	cfg_num = ecmp->ecm_dev_data->dev_n_cfg;
+	USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
+	    "usbecm_is_compatible: entry, cfg_num=%d", cfg_num);
+
+	for (cfg_index = 0; cfg_index < cfg_num; cfg_index++) {
+		cfg_data = &(ecmp->ecm_dev_data->dev_cfg[cfg_index]);
+
+		USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_is_compatible: cfg_index=%d, value=%d",
+		    cfg_index, cfg_data->cfg_descr.bConfigurationValue);
+
+		intf = cfg_data->cfg_if;
+		if_num = cfg_data->cfg_n_if;
+
+		for (i = 0; i < if_num; i++) {
+			alt_num = intf->if_n_alt;
+			for (j = 0; j < alt_num; j++) {
+			alt = &intf->if_alt[j];
+			if ((alt->altif_descr.bInterfaceClass == 0x02) &&
+			    (alt->altif_descr.bInterfaceSubClass == 0x06)) {
+				ecmp->ecm_cfg_index = cfg_index;
+
+				USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
+				    "usbecm_is_compatible: cfg_index=%d",
+				    cfg_index);
+
+				return (B_TRUE);
+			}
+			}
+			intf++;
+		}
+	}
+
+	return (B_FALSE);
+}
+
+
+static int
+usbecm_usb_init(usbecm_state_t *ecmp)
+{
+
+	if (usb_client_attach(ecmp->ecm_dip, USBDRV_VERSION, 0) !=
+	    USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		"usbecm_usb_init: fail to attach");
+
+		return (USB_FAILURE);
+	}
+
+	/* Get the configuration information of device */
+	if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data,
+	    USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		"usbecm_usb_init: fail to get_dev_data");
+
+		return (USB_FAILURE);
+	}
+	ecmp->ecm_def_ph = ecmp->ecm_dev_data->dev_default_ph;
+	ecmp->ecm_dev_state = USB_DEV_ONLINE;
+
+	mutex_init(&ecmp->ecm_mutex, NULL, MUTEX_DRIVER,
+	    ecmp->ecm_dev_data->dev_iblock_cookie);
+
+	if ((strcmp(ddi_binding_name(ecmp->ecm_dip),
+	    "usbif,class2.6") == 0) ||
+	    ((strcmp(ddi_binding_name(ecmp->ecm_dip),
+	    "usb,class2.6.0") == 0))) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_usb_init: A CDC ECM device is attached");
+		ecmp->ecm_compatibility = B_TRUE;
+	} else if (usb_owns_device(ecmp->ecm_dip) &&
+	    usbecm_is_compatible(ecmp)) {
+		/*
+		 * Current Sun SP ECM device has two configurations. Hence
+		 * USBA doesn't create interface level compatible names
+		 * for it, see usba_ready_device_node(). We have to check
+		 * manually to see if compatible interfaces exist, when
+		 * the driver owns the entire device.
+		 */
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_usb_init: A CDC ECM device is attached");
+		ecmp->ecm_compatibility = B_TRUE;
+	} else {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_usb_init: A nonstandard device is attached to "
+		    "usbecm(7D) driver. This device doesn't conform to "
+		    "usb cdc spec.");
+		ecmp->ecm_compatibility = B_FALSE;
+
+		/* generate a random MAC addr */
+		generate_ether_addr(ecmp->ecm_srcaddr);
+	}
+
+	if ((ecmp->ecm_compatibility == B_TRUE) &&
+	    (usbecm_get_descriptors(ecmp) != USB_SUCCESS)) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_usb_init: A compatible device is attached, but "
+		    "fail to get standard descriptors");
+
+		return (USB_FAILURE);
+	}
+
+	if (ecmp->ecm_compatibility == B_FALSE) {
+		(void) usbecm_init_non_compatible_device(ecmp);
+	}
+
+	/* Create power management components */
+	if (usbecm_create_pm_components(ecmp) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_usb_init: create pm components failed.");
+
+		return (USB_FAILURE);
+	}
+
+	/* Register to get callbacks for USB events */
+	if (usb_register_event_cbs(ecmp->ecm_dip, &usbecm_events, 0)
+	    != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbsecm_attach: register event callback failed.");
+
+		return (USB_FAILURE);
+	}
+	ecmp->ecm_init_flags |= USBECM_INIT_EVENTS;
+
+
+	/* Get max data size of bulk transfer */
+	if (usb_pipe_get_max_bulk_transfer_size(ecmp->ecm_dip,
+	    &ecmp->ecm_xfer_sz) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbsecm_ds_attach: get max size of transfer failed.");
+
+		return (USB_FAILURE);
+	}
+
+
+	ecmp->ecm_ser_acc = usb_init_serialization(ecmp->ecm_dip,
+	    USB_INIT_SER_CHECK_SAME_THREAD);
+	ecmp->ecm_init_flags |= USBECM_INIT_SER;
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * Open operation pipes. Each ECM device should have Bulk In, Bulk Out
+ * and Interrupt In endpoints
+ */
+static int
+usbecm_open_pipes(usbecm_state_t *ecmp)
+{
+	int		rval = USB_SUCCESS;
+	usb_ep_data_t	*in_data, *out_data, *intr_pipe;
+	usb_pipe_policy_t policy;
+	int		altif;
+
+	ASSERT(!mutex_owned(&ecmp->ecm_mutex));
+
+	USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
+	    "usbsecm_open_pipes: ecmp = 0x%p", (void *)ecmp);
+
+	if (ecmp->ecm_compatibility == B_TRUE) {
+	/* compatible device has minimum of 2 altsetting, select alt 1 */
+		altif = 1;
+	} else {
+		altif = ecmp->ecm_data_if_alt;
+	}
+	intr_pipe = ecmp->ecm_intr_ep;
+	in_data = ecmp->ecm_bulk_in_ep;
+	out_data = ecmp->ecm_bulk_out_ep;
+
+	/* Bulk in and out must exist simultaneously. */
+	if ((in_data == NULL) || (out_data == NULL)) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
+		    "usbsecm_open_pipes: look up bulk pipe failed in "
+		    "interface %d ",
+		    ecmp->ecm_data_if_no);
+
+		return (USB_FAILURE);
+	}
+	/*
+	 * If device conform to ecm spec, it must have an interrupt pipe
+	 * for this device.
+	 */
+	if (ecmp->ecm_compatibility == B_TRUE && intr_pipe == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
+		    "usbecm_open_pipes: look up interrupt pipe failed in "
+		    "interface %d", ecmp->ecm_ctrl_if_no);
+
+		return (USB_FAILURE);
+	}
+
+	USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
+	    "usbsecm_open_pipes: open intr %02x, bulkin %02x bulkout %02x",
+	    intr_pipe?intr_pipe->ep_descr.bEndpointAddress:0,
+	    in_data->ep_descr.bEndpointAddress,
+	    out_data->ep_descr.bEndpointAddress);
+
+	USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
+	    "usbsecm_open_pipes: set data if(%d) alt(%d) ",
+	    ecmp->ecm_data_if_no, altif);
+
+	if ((rval = usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no,
+	    altif, USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_open_pipes: set alternate failed (%d)",
+		    rval);
+
+		return (rval);
+	}
+
+	policy.pp_max_async_reqs = 2;
+
+	/* Open bulk in endpoint */
+	if (usb_pipe_open(ecmp->ecm_dip, &in_data->ep_descr, &policy,
+	    USB_FLAGS_SLEEP, &ecmp->ecm_bulkin_ph) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
+		    "usbecm_open_pipes: open bulkin pipe failed!");
+
+		return (USB_FAILURE);
+	}
+
+	/* Open bulk out endpoint */
+	if (usb_pipe_open(ecmp->ecm_dip, &out_data->ep_descr, &policy,
+	    USB_FLAGS_SLEEP, &ecmp->ecm_bulkout_ph) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
+		    "usbecm_open_pipes: open bulkout pipe failed!");
+
+		usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
+		    USB_FLAGS_SLEEP, NULL, NULL);
+
+		return (USB_FAILURE);
+	}
+
+	/* Open interrupt endpoint if found. */
+	if (intr_pipe != NULL) {
+		if (usb_pipe_open(ecmp->ecm_dip, &intr_pipe->ep_descr, &policy,
+		    USB_FLAGS_SLEEP, &ecmp->ecm_intr_ph) != USB_SUCCESS) {
+			USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
+			    "usbecm_open_pipes: "
+			    "open intr pipe failed");
+
+			usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
+			    USB_FLAGS_SLEEP, NULL, NULL);
+			usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph,
+			    USB_FLAGS_SLEEP, NULL, NULL);
+
+			return (USB_FAILURE);
+		}
+	}
+
+	/* initialize the pipe related data */
+	mutex_enter(&ecmp->ecm_mutex);
+	ecmp->ecm_bulkin_sz = in_data->ep_descr.wMaxPacketSize;
+	ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
+	ecmp->ecm_bulkout_state = USBECM_PIPE_IDLE;
+	if (ecmp->ecm_intr_ph != NULL) {
+		ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
+	}
+	mutex_exit(&ecmp->ecm_mutex);
+
+	if (ecmp->ecm_intr_ph != NULL) {
+
+		usbecm_pipe_start_polling(ecmp);
+	}
+
+	USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
+	    "usbsecm_open_pipes: end");
+
+	return (rval);
+}
+
+
+/*
+ * usbsecm_close_pipes:
+ *	Close pipes
+ *	Each device could include three pipes: bulk in, bulk out and interrupt.
+ */
+static void
+usbecm_close_pipes(usbecm_state_t *ecmp)
+{
+
+	mutex_enter(&ecmp->ecm_mutex);
+
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
+	    "usbsecm_close_pipes: ecm_bulkin_state = %d",
+	    ecmp->ecm_bulkin_state);
+
+	/*
+	 * Check the status of the pipes. If pipe is closing or closed,
+	 * return directly.
+	 */
+	if ((ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSED) ||
+	    (ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSING)) {
+		USB_DPRINTF_L2(PRINT_MASK_CLOSE, ecmp->ecm_lh,
+		    "usbsecm_close_pipes: pipe is closing or has closed");
+		mutex_exit(&ecmp->ecm_mutex);
+
+		return;
+	}
+
+	ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSING;
+	mutex_exit(&ecmp->ecm_mutex);
+
+	/* reset the data interface's altsetting to 0 */
+	if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) &&
+	    (usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no,
+	    0, USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS)) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
+		    "usbecm_close_pipes: reset alternate failed ");
+	}
+
+	/* Close pipes */
+	usb_pipe_reset(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
+	    USB_FLAGS_SLEEP, NULL, 0);
+	usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
+	    USB_FLAGS_SLEEP, NULL, 0);
+	usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph,
+	    USB_FLAGS_SLEEP, NULL, 0);
+
+	if (ecmp->ecm_intr_ph != NULL) {
+		usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph,
+		    USB_FLAGS_SLEEP);
+		usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_intr_ph,
+		    USB_FLAGS_SLEEP, NULL, 0);
+	}
+
+	mutex_enter(&ecmp->ecm_mutex);
+	/* Reset the status of pipes to closed */
+	ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSED;
+	ecmp->ecm_bulkin_ph = NULL;
+	ecmp->ecm_bulkout_state = USBECM_PIPE_CLOSED;
+	ecmp->ecm_bulkout_ph = NULL;
+	if (ecmp->ecm_intr_ph != NULL) {
+		ecmp->ecm_intr_state = USBECM_PIPE_CLOSED;
+		ecmp->ecm_intr_ph = NULL;
+	}
+
+	mutex_exit(&ecmp->ecm_mutex);
+
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
+	    "usbsecm_close_pipes: pipes have been closed.");
+}
+
+
+static int
+usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request,
+    uint16_t value, mblk_t **data)
+{
+	usb_ctrl_setup_t setup;
+	usb_cb_flags_t	cb_flags;
+	usb_cr_t	cr;
+	int		rval;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
+	    "usbecm_ctrl_write: ");
+
+	/* initialize the control request. */
+	setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV |
+	    USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
+	setup.bRequest = request;
+	setup.wValue = value;
+	setup.wIndex = ecmp->ecm_ctrl_if_no;
+	setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0;
+	setup.attrs = 0;
+
+	rval = usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data,
+	    &cr, &cb_flags, 0);
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
+	    "usbecm_ctrl_write: rval = %d", rval);
+
+	return (rval);
+}
+
+static int
+usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request,
+    uint16_t value, mblk_t **data, int len)
+{
+	usb_ctrl_setup_t setup;
+	usb_cb_flags_t	cb_flags;
+	usb_cr_t	cr;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
+	    "usbecm_ctrl_read: ");
+
+	/* initialize the control request. */
+	setup.bmRequestType = USB_DEV_REQ_DEV_TO_HOST |
+	    USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
+	setup.bRequest = request;
+	setup.wValue = value;
+	setup.wIndex = ecmp->ecm_ctrl_if_no;
+	setup.wLength = (uint16_t)len;
+	setup.attrs = 0;
+
+	return (usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data,
+	    &cr, &cb_flags, 0));
+}
+
+/* Get specific statistic data from device */
+static int
+usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs, uint32_t *stat_data)
+{
+	mblk_t *data = NULL;
+	uint32_t stat;
+
+	/* first check to see if this stat is collected by device */
+	if ((ecmp->ecm_compatibility == B_TRUE) &&
+	    (ecmp->ecm_desc.bmEthernetStatistics & ECM_STAT_CAP_MASK(fs))) {
+		if (usbecm_ctrl_read(ecmp, CDC_ECM_GET_ETH_STAT,
+		    ecmp->ecm_ctrl_if_no, &data, 4) != USB_SUCCESS) {
+
+			return (USB_FAILURE);
+		}
+		stat = (data->b_rptr[3] << 24) | (data->b_rptr[2] << 16) |
+		    (data->b_rptr[1] << 8) | (data->b_rptr[0]);
+		*stat_data = stat;
+
+		freemsg(data);
+
+		return (USB_SUCCESS);
+	}
+
+	return (USB_FAILURE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/warlock/usbecm.wlcmd	Fri Mar 12 09:19:50 2010 +0800
@@ -0,0 +1,72 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+one usbecm_state
+
+### specify the root functions
+
+root usbecm_bulkin_cb
+root usbecm_bulkout_cb
+root usbecm_intr_cb
+root usbecm_intr_ex_cb
+
+### specify usbecm function pointers
+
+
+root usbecm_bulkin_cb
+root usbecm_bulkout_cb
+root usbecm_intr_cb
+root usbecm_intr_ex_cb
+
+# temporary
+root usbecm_pm_set_busy
+root usbecm_pm_set_idle
+root usbecm_power
+
+root usbecm_m_stop
+root usbecm_m_start
+root usbecm_m_unicst
+root usbecm_m_multicst
+root usbecm_m_promisc
+root usbecm_m_ioctl
+root usbecm_m_tx
+root usbecm_m_getprop
+root usbecm_m_setprop
+root usbecm_m_stat
+
+root usbecm_disconnect_event_cb
+root usbecm_reconnect_event_cb
+
+add bus_ops::bus_add_eventcall targets warlock_dummy
+add bus_ops::bus_get_eventcookie targets warlock_dummy
+add bus_ops::bus_post_event targets warlock_dummy
+add bus_ops::bus_remove_eventcall targets warlock_dummy
+add bus_ops::bus_intr_ctl targets warlock_dummy
+add bus_ops::bus_config targets warlock_dummy
+add bus_ops::bus_unconfig targets warlock_dummy
+
+add usbecm_ds_ops::ecm_ds_init targets warlock_dummy
+add usbecm_ds_ops::ecm_ds_fini targets warlock_dummy
+add usbecm_ds_ops::ecm_ds_start targets warlock_dummy
+add usbecm_ds_ops::ecm_ds_stop targets warlock_dummy
+add usbecm_ds_ops::ecm_ds_intr_cb targets warlock_dummy
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/warlock/usbecm_with_usba.wlcmd	Fri Mar 12 09:19:50 2010 +0800
@@ -0,0 +1,187 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+one ohci_state
+one ehci_state
+one uhci_state
+one usbecm_state
+
+### specify the root functions
+
+root	usb_console_output_init
+root	usb_console_output_fini
+root	usb_console_output_enter
+root	usb_console_output_exit
+root	usb_console_write
+root	usb_console_input_enter
+root	usb_console_input_exit
+root	usb_console_input_fini
+root	usb_console_input_init
+root	usb_console_read
+root	usb_get_dev_descr
+root	usb_get_if_number
+root	usb_parse_CV_cfg_descr
+root	usb_parse_CV_ep_descr
+root	usb_parse_CV_if_descr
+root    usb_parse_comp_ep_descr
+root	usb_pipe_get_private
+root	usb_get_current_frame_number
+root	usb_get_max_isoc_pkts
+root	usb_pipe_set_private
+root	usba_ready_interface_node
+root	usba_free_hcdi_ops
+root	usba_async_req_raise_power
+root	usba_async_req_lower_power
+root	usb_req_raise_power
+root	usb_req_lower_power
+root	usb_set_device_pwrlvl0
+root	usb_set_device_pwrlvl1
+root	usb_set_device_pwrlvl2
+root	usb_set_device_pwrlvl3
+root	usb_is_pm_enabled
+root	usb_async_req
+root	usb_pipe_bulk_transfer_size
+root 	usb_get_ep_data
+root	usba_pipe_get_policy
+root	usb_pipe_ctrl_xfer_wait
+root	usb_pipe_drain_reqs
+root    usb_try_serialize_access
+root	usb_clr_feature
+root    usb_clear_feature
+root    usb_get_alt_if
+root    usb_get_ep_descr
+root    usb_get_if_descr
+root    usb_log
+root    usb_pipe_isoc_xfer
+root    usb_pipe_stop_isoc_polling
+root    usb_set_alt_if
+root    usb_set_cfg
+root    usb_get_cfg
+root	usb_ep_num
+root	usb_get_status
+root	usb_pipe_reset
+root	usb_log_descr_tree
+root	usb_print_descr_tree
+root    usb_rval2errno
+root	usb_register_hotplug_cbs
+root	usb_get_current_cfgidx
+root    usb_register_client
+root	usb_reset_device
+root	usb_ugen_power
+root	usb_ugen_attach
+root	usb_ugen_close
+root	usb_ugen_detach
+root	usb_ugen_disconnect_ev_cb
+root	usb_ugen_get_hdl
+root	usb_ugen_open
+root	usb_ugen_poll
+root	usb_ugen_read
+root	usb_ugen_reconnect_ev_cb
+root	usb_ugen_write
+root	usb_register_dev_driver
+root	usb_unregister_dev_driver
+
+root	hcdi_autoclearing
+root	hcdi_cb_thread
+root    hcdi_shared_cb_thread
+
+
+root	hubd_restore_state_cb
+root    hubd_disconnect_event_cb
+root    hubd_post_resume_event_cb
+root    hubd_pre_suspend_event_cb
+root    hubd_reconnect_event_cb
+root	hubd_hotplug_thread
+root	hubd_reset_thread
+root	hubd_cpr_post_user_callb
+root    hubd_root_hub_cleanup_thread
+root	hubd_bus_power
+
+root	usba_common_power
+root	usba_common_register_events
+root	usba_ready_interface_association_node
+root	usba_pipe_do_async_func_thread
+root	usba_pipe_sync_reset
+root    usba_get_hc_dma_attr
+root    usba_hcdi_get_req_private
+root    usba_hcdi_set_req_private
+root    usba_dbuf_tail
+root	usba_hubdi_power
+root	usba_hubdi_root_hub_power
+root	usba_get_hotplug_stats
+root	usba_reset_hotplug_stats
+root	usba_ascii_string_descr
+root	usba_move_list
+root    usba_taskq_destroy
+root	usba_mk_mctl
+root	usb_fail_checkpoint
+
+root	ohci_intr
+root	ehci_intr
+root    ohci_quiesce
+root    uhci_quiesce
+root    ehci_quiesce
+
+### specify the usbecm root functions
+root usbecm_power
+root usbecm_disconnect_event_cb
+root usbecm_reconnect_event_cb
+
+root	usbecm_m_stop
+root	usbecm_m_start
+root	usbecm_m_stat
+root	usbecm_m_promisc
+root	usbecm_m_multicst
+root	usbecm_m_unicst
+root	usbecm_m_tx
+root	usbecm_m_ioctl
+root	usbecm_m_getprop
+root	usbecm_m_setprop
+
+# temporary
+root	usbecm_pm_set_busy
+root	usbecm_pm_set_idle
+
+add hubd::h_cleanup_child	targets warlock_dummy
+add usb_ctrl_req::ctrl_cb	targets warlock_dummy
+add usb_ctrl_req::ctrl_exc_cb	targets warlock_dummy
+add usba_pipe_async_req::callback	targets warlock_dummy
+add usba_pipe_async_req::sync_func	targets warlock_dummy
+add usba_pm_req::cb			targets warlock_dummy
+
+add ohci_trans_wrapper::tw_handle_td targets ohci_handle_ctrl_td
+add ohci_trans_wrapper::tw_handle_td targets ohci_handle_bulk_td
+add ohci_trans_wrapper::tw_handle_td targets ohci_handle_intr_td
+add ohci_trans_wrapper::tw_handle_td targets ohci_handle_isoc_td
+
+add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_bulk_qtd
+add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_intr_qtd
+add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_ctrl_qtd
+
+add usbecm_ds_ops::ecm_ds_init targets warlock_dummy
+add usbecm_ds_ops::ecm_ds_fini targets warlock_dummy
+add usbecm_ds_ops::ecm_ds_start targets warlock_dummy
+add usbecm_ds_ops::ecm_ds_stop targets warlock_dummy
+add usbecm_ds_ops::ecm_ds_intr_cb targets warlock_dummy
--- a/usr/src/uts/common/sys/usb/clients/usbcdc/usb_cdc.h	Thu Mar 11 16:01:06 2010 -0800
+++ b/usr/src/uts/common/sys/usb/clients/usbcdc/usb_cdc.h	Fri Mar 12 09:19:50 2010 +0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -29,7 +29,6 @@
 
 #include <sys/types.h>
 #include <sys/dditypes.h>
-#include <sys/usb/clients/usbser/usbser_dsdi.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -96,6 +95,19 @@
 	/* more slave interafce may follow */
 } usb_cdc_union_descr_t;
 
+/* Ethernet Control Model Functional Descriptor */
+typedef struct usb_cdc_ecm_descr {
+	uint8_t		bFunctionalLength;
+	uint8_t		bDescriptorType;
+	uint8_t		bDescriptorSubtype;
+	uint8_t		iMACAddress;
+	uint32_t	bmEthernetStatistics;
+	uint16_t	wMaxSegmentSize;
+	uint16_t	wNumberMCFilters;
+	uint8_t		bNumberPowerFilters;
+} usb_cdc_ecm_descr_t;
+
+
 /*
  * Class-specific requests
  */
@@ -115,6 +127,7 @@
 } usb_cdc_line_coding_t;
 
 #define	USB_CDC_LINE_CODING_LEN			7
+#define	USB_CDC_ECM_LEN				13
 
 #define	USB_CDC_STOP_BITS_1			0
 #define	USB_CDC_STOP_BITS_1_5			1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/usb/clients/usbecm/usbecm.h	Fri Mar 12 09:19:50 2010 +0800
@@ -0,0 +1,277 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_USB_USBETH_H
+#define	_SYS_USB_USBETH_H
+
+
+#include <sys/types.h>
+#include <sys/dditypes.h>
+#include <sys/mac.h>
+#include <sys/note.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct usbecm_state usbecm_state_t;
+
+
+/*
+ * PM support
+ */
+typedef struct usbecm_power {
+	uint8_t		pm_wakeup_enabled;	/* remote wakeup enabled */
+	uint8_t		pm_pwr_states;	/* bit mask of power states */
+	boolean_t	pm_raise_power;	/* driver is about to raise power */
+	uint8_t		pm_cur_power;	/* current power level */
+	uint_t		pm_busy_cnt;	/* number of set_busy requests */
+} usbecm_pm_t;
+
+struct usbecm_statistics {
+	uint32_t	es_upspeed;	/* Upstream bit rate, bps */
+	uint32_t	es_downspeed;	/* Downstream bit rate, bps */
+	int		es_linkstate;	/* link state */
+	uint64_t	es_ipackets;
+	uint64_t	es_opackets;
+	uint64_t	es_ibytes;
+	uint64_t	es_obytes;
+	uint64_t	es_ierrors;	/* received frames with errors */
+	uint64_t	es_oerrors;	/* transmitted frames with errors */
+	uint64_t	es_multircv;	/* received multicast frames */
+	uint64_t	es_multixmt;	/* transmitted multicast frames */
+	uint64_t	es_brdcstrcv;
+	uint64_t	es_brdcstxmt;
+	uint64_t	es_macxmt_err;
+};
+
+struct usbecm_ds_ops {
+	/* Device specific initialization and deinitialization */
+	int (*ecm_ds_init)(usbecm_state_t *);
+	int (*ecm_ds_fini)(usbecm_state_t *);
+
+	int (*ecm_ds_start)(usbecm_state_t *);
+	int (*ecm_ds_stop)(usbecm_state_t *);
+	int (*ecm_ds_unicst)(usbecm_state_t *);
+	int (*ecm_ds_promisc)(usbecm_state_t *);
+	int (*ecm_ds_multicst)(usbecm_state_t *);
+	mblk_t *(*ecm_ds_tx)(usbecm_state_t *, mblk_t *);
+
+	int (*ecm_ds_intr_cb)(usbecm_state_t *, mblk_t *);
+	int (*ecm_ds_bulkin_cb)(usbecm_state_t *, mblk_t *);
+	int (*ecm_ds_bulkout_cb)(usbecm_state_t *, mblk_t *);
+};
+
+/*
+ * per bulk in/out structure
+ */
+struct usbecm_state {
+	kmutex_t		ecm_mutex;		/* structure lock */
+	dev_info_t		*ecm_dip;		/* device info */
+	usb_client_dev_data_t	*ecm_dev_data;		/* registration data */
+	usb_pipe_handle_t	ecm_def_ph;		/* default pipe hdl */
+	usb_log_handle_t	ecm_lh;			/* USBA log handle */
+	int			ecm_dev_state;		/* USB device state */
+	size_t			ecm_xfer_sz;		/* bulk xfer size */
+	size_t			ecm_bulkin_sz;
+	usbecm_pm_t		*ecm_pm;		/* PM support */
+	mac_handle_t		ecm_mh;			/* mac handle */
+	usb_serialization_t	ecm_ser_acc;	/* serialization object */
+
+	uint_t			ecm_cfg_index;	/* config contains ECM ifc */
+	uint16_t		ecm_ctrl_if_no;
+	uint16_t		ecm_data_if_no;
+	uint16_t		ecm_data_if_alt; /* non-compatible device */
+
+	usb_ep_data_t		*ecm_intr_ep;
+	usb_ep_data_t		*ecm_bulk_in_ep;
+	usb_ep_data_t		*ecm_bulk_out_ep;
+
+	boolean_t		ecm_compatibility;	/* if conform to spec */
+	usb_cdc_ecm_descr_t	ecm_desc;	/* if conform to spec */
+
+	uint8_t			ecm_srcaddr[6];	/* source MAC addr */
+	uint16_t		ecm_pkt_flt;	/* pkt flt bitmap ECM1.2 T.8 */
+
+	usb_pipe_handle_t	ecm_bulkout_ph;
+	int			ecm_bulkout_state;
+	usb_pipe_handle_t	ecm_bulkin_ph;
+	int			ecm_bulkin_state;
+	usb_pipe_handle_t	ecm_intr_ph;
+	int			ecm_intr_state;
+	struct usbecm_statistics	ecm_stat;
+	uint32_t		ecm_init_flags;
+	int			ecm_mac_state;
+	mblk_t			*ecm_rcv_queue; /* receive queue */
+	int			ecm_tx_cnt;
+
+	void			*ecm_priv; /* device private data */
+	struct usbecm_ds_ops    *ecm_ds_ops;
+};
+
+
+_NOTE(MUTEX_PROTECTS_DATA(usbecm_state::ecm_mutex, usbecm_state))
+_NOTE(MUTEX_PROTECTS_DATA(usbecm_state::ecm_mutex, usbecm_statistics))
+
+_NOTE(DATA_READABLE_WITHOUT_LOCK(usbecm_state::{
+	ecm_dip
+	ecm_dev_data
+	ecm_def_ph
+	ecm_lh
+	ecm_dev_state
+	ecm_xfer_sz
+	ecm_compatibility
+	ecm_pm
+	ecm_mh
+	ecm_bulkin_ph
+	ecm_bulkout_ph
+	ecm_intr_ph
+	ecm_ser_acc
+	ecm_ctrl_if_no
+	ecm_data_if_no
+	ecm_data_if_alt
+	ecm_desc
+	ecm_bulk_in_ep
+	ecm_intr_ep
+	ecm_bulk_out_ep
+	ecm_bulkin_sz
+	ecm_priv
+	ecm_ds_ops
+}))
+
+_NOTE(SCHEME_PROTECTS_DATA("unshared data", mblk_t iocblk))
+_NOTE(SCHEME_PROTECTS_DATA("unshared data", usb_bulk_req_t usb_intr_req_t))
+
+/* pipe state */
+enum {
+	USBECM_PIPE_CLOSED,			/* pipe is closed */
+	USBECM_PIPE_IDLE,			/* open but no requests */
+	USBECM_PIPE_BUSY,			/* servicing request */
+	USBECM_PIPE_CLOSING			/* pipe is closing */
+};
+
+enum {
+	USBECM_MAC_STOPPED = 0,
+	USBECM_MAC_STARTED,
+};
+
+/* various tunables */
+enum {
+	USBECM_BULKOUT_TIMEOUT		= 15,	/* bulkout timeout */
+	USBECM_BULKIN_TIMEOUT		= 0	/* bulkin timeout */
+};
+
+/* hardware definitions */
+enum {
+	USBSACM_REQ_OUT	= USB_DEV_REQ_TYPE_CLASS| USB_DEV_REQ_HOST_TO_DEV,
+	USBSACM_REQ_IN	= USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_DEV_TO_HOST,
+	USBSACM_REQ_WRITE_IF		= USBSACM_REQ_OUT | USB_DEV_REQ_RCPT_IF,
+	USBSACM_REQ_READ_IF		= USBSACM_REQ_IN | USB_DEV_REQ_RCPT_IF
+};
+
+#define	USBECM_INIT_EVENTS	(0x01 << 0)
+#define	USBECM_INIT_SER		(0x01 << 1)
+#define	USBECM_INIT_MAC		(0x01 << 2)
+
+/* Bit offset for ECM statistics capabilities, CDC ECM Rev 1.2, Table 4 */
+#define	ECM_XMIT_OK			0
+#define	ECM_RCV_OK			1
+#define	ECM_XMIT_ERROR			2
+#define	ECM_RCV_ERROR			3
+#define	ECM_RCV_NO_BUFFER		4
+#define	ECM_DIRECTED_BYTES_XMIT		5
+#define	ECM_DIRECTED_FRAMES_XMIT	6
+#define	ECM_MULTICAST_BYTES_XMIT	7
+#define	ECM_MULTICAST_FRAMES_XMIT	8
+#define	ECM_BROADCAST_BYTES_XMIT	9
+#define	ECM_BROADCAST_FRAMES_XMIT	10
+#define	ECM_DIRECTED_BYTES_RCV		11
+#define	ECM_DIRECTED_FRAMES_RCV		12
+#define	ECM_MULTICAST_BYTES_RCV		13
+#define	ECM_MULTICAST_FRAMES_RCV	14
+#define	ECM_BROADCAST_BYTES_RCV		15
+#define	ECM_BROADCAST_FRAMES_RCV	16
+#define	ECM_RCV_CRC_ERROR		17
+#define	ECM_TRANSMIT_QUEUE_LENGTH	18
+#define	ECM_RCV_ERROR_ALIGNMENT		19
+#define	ECM_XMIT_ONE_COLLISION		20
+#define	ECM_XMIT_MORE_COLLISIONS	21
+#define	ECM_XMIT_DEFERRED		22
+#define	ECM_XMIT_MAX_COLLISIONS		23
+#define	ECM_RCV_OVERRUN			24
+#define	ECM_XMIT_UNDERRUN		25
+#define	ECM_XMIT_HEARTBEAT_FAILURE	26
+#define	ECM_XMIT_TIMES_CRS_LOST		27
+#define	ECM_XMIT_LATE_COLLISIONS	28
+
+#define	ECM_STAT_CAP_MASK(x)	(1UL << (x))		/* Table 4 */
+#define	ECM_STAT_SELECTOR(x)	((x) + 1)	/* Table 9 */
+
+/* ECM class-specific request codes, Table 6 */
+#define	CDC_ECM_SET_ETH_MCAST_FLT	0x40
+#define	CDC_ECM_SET_ETH_PM_FLT		0x41
+#define	CDC_ECM_GET_ETH_PM_FLT		0x42
+#define	CDC_ECM_SET_ETH_PKT_FLT		0x43
+#define	CDC_ECM_GET_ETH_STAT		0x44
+
+/* ECM Ethernet Pakcet Filter Bitmap, Table 8 */
+#define	CDC_ECM_PKT_TYPE_PROMISC	(1<<0)
+#define	CDC_ECM_PKT_TYPE_ALL_MCAST	(1<<1) /* all multicast */
+#define	CDC_ECM_PKT_TYPE_DIRECTED	(1<<2)
+#define	CDC_ECM_PKT_TYPE_BCAST		(1<<3) /* broadcast */
+#define	CDC_ECM_PKT_TYPE_MCAST		(1<<4) /* multicast */
+
+#define	PRINT_MASK_ATTA		0x00000001
+#define	PRINT_MASK_CLOSE	0x00000002
+#define	PRINT_MASK_OPEN		0x00000004
+#define	PRINT_MASK_EVENTS	0x00000008
+#define	PRINT_MASK_PM		0x00000010
+#define	PRINT_MASK_CB		0x00000020
+#define	PRINT_MASK_OPS		0x00000040
+#define	PRINT_MASK_ALL		0xFFFFFFFF
+
+/* Turn a little endian byte array to a uint32_t */
+#define	LE_TO_UINT32(src, des)	{ \
+	uint32_t tmp; \
+	des = src[3]; \
+	des = des << 24; \
+	tmp = src[2]; \
+	des |= tmp << 16; \
+	tmp = src[1]; \
+	des |= tmp << 8; \
+	des |= src[0]; \
+}
+
+#define	isdigit(c)	((c) >= '0' && c <= '9')
+#define	toupper(C)	(((C) >= 'a' && (C) <= 'z')? ((C) - 'a' + 'A'): (C))
+
+/* #define	NELEM(a)	(sizeof (a) / sizeof (*(a))) */
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_USB_USBETH_H */
--- a/usr/src/uts/intel/Makefile.intel.shared	Thu Mar 11 16:01:06 2010 -0800
+++ b/usr/src/uts/intel/Makefile.intel.shared	Fri Mar 12 09:19:50 2010 +0800
@@ -484,6 +484,7 @@
 DRV_KMODS	+= usbftdi
 DRV_KMODS	+= wusb_df
 DRV_KMODS	+= wusb_ca
+DRV_KMODS	+= usbecm
 
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= usbser_edge
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/usbecm/Makefile	Fri Mar 12 09:19:50 2010 +0800
@@ -0,0 +1,132 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/intel/usbecm/Makefile
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+#	This makefile drives the production of Abstract Control Model of
+#	USB Communication Devices Class dirver.
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= usbecm
+OBJECTS		= $(USBECM_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(USBECM_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+WARLOCK_OUT	= $(USBECM_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+WLCMD_DIR	= $(UTSBASE)/common/io/warlock
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+LDFLAGS         += -dy -Nmisc/usba -Nmisc/mac
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+.KEEP_STATE:
+
+all:		$(ALL_DEPS)
+
+def:		$(DEF_DEPS)
+
+clean:		$(CLEAN_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+clobber:	$(CLOBBER_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+#
+#	Defines for local commands.
+#
+WLCC		= wlcc
+TOUCH		= touch
+WARLOCK		= warlock
+TEST		= test
+
+#
+#	warlock
+#
+WARLOCK_CMD	= $(WLCMD_DIR)/$(MODULE).wlcmd
+
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
+UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
+OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
+EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
+
+warlock: $(WARLOCK_OK) warlock_with_usba
+
+%.ll: $(UTSBASE)/common/io/usb/clients/usbecm/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
+
+$(WARLOCK_OK): $(WARLOCK_OUT) $(WLCMD_DIR)/usbecm.wlcmd warlock_ddi.files
+	$(WARLOCK) -c $(WLCMD_DIR)/usbecm.wlcmd $(WARLOCK_OUT)  \
+		-l ../warlock/ddi_dki_impl.ll
+	$(TOUCH) $@
+
+warlock_with_usba: $(WLCMD_DIR)/usbecm_with_usba.wlcmd $(WARLOCK_OUT) \
+	usba_files ohci_files uhci_files ehci_files \
+	warlock_ddi.files
+	$(WARLOCK) -c $(WLCMD_DIR)/usbecm_with_usba.wlcmd \
+		$(USBA_FILES) $(OHCI_FILES) $(EHCI_FILES) $(UHCI_FILES) \
+		$(WARLOCK_OUT) -l ../warlock/ddi_dki_impl.ll
+
+usba_files:
+	@cd ../usba;pwd; $(MAKE) warlock
+
+uhci_files:
+	@cd ../uhci;pwd; $(MAKE) warlock
+
+ohci_files:
+	@cd ../ohci;pwd; $(MAKE) warlock
+
+ehci_files:
+	@cd ../ehci;pwd; $(MAKE) warlock
+
+warlock_ddi.files:
+	cd ../warlock; pwd; $(MAKE) warlock
--- a/usr/src/uts/intel/warlock/Makefile	Thu Mar 11 16:01:06 2010 -0800
+++ b/usr/src/uts/intel/warlock/Makefile	Fri Mar 12 09:19:50 2010 +0800
@@ -20,7 +20,7 @@
 #
 
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # uts/intel/warlock/Makefile
@@ -79,6 +79,7 @@
 	@cd ../usbsksp; $(MAKE) clean; $(MAKE) warlock
 	@cd ../usbsprl; $(MAKE) clean; $(MAKE) warlock
 	@cd ../usbsacm; $(MAKE) clean; $(MAKE) warlock
+	@cd ../usbecm; $(MAKE) clean; $(MAKE) warlock
 	@cd ../usbskel; $(MAKE) clean; $(MAKE) warlock
 $(CLOSED_BUILD)	@cd $(CLOSED)/uts/intel/usbser_edge; \
 		$(MAKE) clean; $(MAKE) warlock
--- a/usr/src/uts/sparc/Makefile.sparc.shared	Thu Mar 11 16:01:06 2010 -0800
+++ b/usr/src/uts/sparc/Makefile.sparc.shared	Fri Mar 12 09:19:50 2010 +0800
@@ -279,6 +279,7 @@
 DRV_KMODS	+= usbvc
 DRV_KMODS	+= usbftdi
 DRV_KMODS	+= wusb_df hwahc hwarc wusb_ca
+DRV_KMODS	+= usbecm
 DRV_KMODS	+= hci1394 av1394 scsa1394 dcam1394
 DRV_KMODS	+= sbp2
 DRV_KMODS	+= ib ibd rdsib sdp iser daplt hermon tavor
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/usbecm/Makefile	Fri Mar 12 09:19:50 2010 +0800
@@ -0,0 +1,137 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/sparc/usbecm/Makefile
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+#	This makefile drives the production of Abstract Control Model of
+#	USB Communication Devices Class dirver.
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= usbecm
+OBJECTS		= $(USBECM_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(USBECM_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+WARLOCK_OUT	= $(USBECM_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+WLCMD_DIR	= $(UTSBASE)/common/io/warlock
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+LDFLAGS         += -dy -Nmisc/usba -Nmisc/mac
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+.KEEP_STATE:
+
+all:		$(ALL_DEPS)
+
+def:		$(DEF_DEPS)
+
+clean:		$(CLEAN_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+clobber:	$(CLOBBER_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
+
+#
+#	Defines for local commands.
+#
+WLCC		= wlcc
+TOUCH		= touch
+WARLOCK		= warlock
+TEST		= test
+
+#
+#	warlock
+#
+WARLOCK_CMD	= $(WLCMD_DIR)/$(MODULE).wlcmd
+
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
+UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
+OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
+EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
+
+warlock: $(WARLOCK_OK) warlock_with_usba
+
+%.ll: $(UTSBASE)/common/io/usb/clients/usbecm/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
+
+$(WARLOCK_OK): $(WARLOCK_OUT) $(WLCMD_DIR)/usbecm.wlcmd warlock_ddi.files
+	$(WARLOCK) -c $(WLCMD_DIR)/usbecm.wlcmd $(WARLOCK_OUT)  \
+		-l ../warlock/ddi_dki_impl.ll
+	$(TOUCH) $@
+
+warlock_with_usba: $(WLCMD_DIR)/usbecm_with_usba.wlcmd $(WARLOCK_OUT) \
+	usba_files ohci_files uhci_files ehci_files \
+	warlock_ddi.files
+	$(WARLOCK) -c $(WLCMD_DIR)/usbecm_with_usba.wlcmd \
+		$(USBA_FILES) $(OHCI_FILES) $(EHCI_FILES) $(UHCI_FILES) \
+		$(WARLOCK_OUT) -l ../warlock/ddi_dki_impl.ll
+
+usba_files:
+	@cd ../usba;pwd; $(MAKE) warlock
+
+uhci_files:
+	@cd ../uhci;pwd; $(MAKE) warlock
+
+ohci_files:
+	@cd ../ohci;pwd; $(MAKE) warlock
+
+ehci_files:
+	@cd ../ehci;pwd; $(MAKE) warlock
+
+warlock_ddi.files:
+	cd ../warlock; pwd; $(MAKE) warlock
--- a/usr/src/uts/sparc/warlock/Makefile	Thu Mar 11 16:01:06 2010 -0800
+++ b/usr/src/uts/sparc/warlock/Makefile	Fri Mar 12 09:19:50 2010 +0800
@@ -18,7 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #	sparc architecture dependent
@@ -80,6 +80,7 @@
 	@cd ../usbsksp; $(MAKE) clean; $(MAKE) warlock
 	@cd ../usbsprl; $(MAKE) clean; $(MAKE) warlock
 	@cd ../usbsacm; $(MAKE) clean; $(MAKE) warlock
+	@cd ../usbecm; $(MAKE) clean; $(MAKE) warlock
 	@cd ../usbskel; $(MAKE) clean; $(MAKE) warlock
 $(CLOSED_BUILD)	@cd $(CLOSED)/uts/sparc/usbser_edge; \
 		$(MAKE) clean; $(MAKE) warlock