xref: /onnv-gate/usr/src/uts/common/io/usb/clients/usbecm/usbecm.c (revision 11902:129cd0e04cae)
111900SRaymond.Chen@Sun.COM /*
211900SRaymond.Chen@Sun.COM  * CDDL HEADER START
311900SRaymond.Chen@Sun.COM  *
411900SRaymond.Chen@Sun.COM  * The contents of this file are subject to the terms of the
511900SRaymond.Chen@Sun.COM  * Common Development and Distribution License (the "License").
611900SRaymond.Chen@Sun.COM  * You may not use this file except in compliance with the License.
711900SRaymond.Chen@Sun.COM  *
811900SRaymond.Chen@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911900SRaymond.Chen@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011900SRaymond.Chen@Sun.COM  * See the License for the specific language governing permissions
1111900SRaymond.Chen@Sun.COM  * and limitations under the License.
1211900SRaymond.Chen@Sun.COM  *
1311900SRaymond.Chen@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411900SRaymond.Chen@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511900SRaymond.Chen@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611900SRaymond.Chen@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711900SRaymond.Chen@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811900SRaymond.Chen@Sun.COM  *
1911900SRaymond.Chen@Sun.COM  * CDDL HEADER END
2011900SRaymond.Chen@Sun.COM  */
2111900SRaymond.Chen@Sun.COM 
2211900SRaymond.Chen@Sun.COM /*
2311900SRaymond.Chen@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2411900SRaymond.Chen@Sun.COM  * Use is subject to license terms.
2511900SRaymond.Chen@Sun.COM  */
2611900SRaymond.Chen@Sun.COM 
2711900SRaymond.Chen@Sun.COM /*
2811900SRaymond.Chen@Sun.COM  * USB Ethernet Control Model
2911900SRaymond.Chen@Sun.COM  *
3011900SRaymond.Chen@Sun.COM  * USB-IF defines three ethernet network related specifications: EEM,
3111900SRaymond.Chen@Sun.COM  * ECM and NCM. This driver focuses specifically on ECM compatible
3211900SRaymond.Chen@Sun.COM  * devices. This kind of devices generally have one pair of bulk
3311900SRaymond.Chen@Sun.COM  * endpoints for in/out packet data and one interrupt endpoint for
3411900SRaymond.Chen@Sun.COM  * device notification.
3511900SRaymond.Chen@Sun.COM  *
3611900SRaymond.Chen@Sun.COM  * Devices which don't report ECM compatibility through descriptors but
3711900SRaymond.Chen@Sun.COM  * implement the ECM functions may also bind to this driver. This driver
3811900SRaymond.Chen@Sun.COM  * will try to find at least a bulk in endpoint and a bulk out endpoint
3911900SRaymond.Chen@Sun.COM  * in this case. If the non-compatible devices use vendor specific data
4011900SRaymond.Chen@Sun.COM  * format, this driver will not function.
4111900SRaymond.Chen@Sun.COM  *
4211900SRaymond.Chen@Sun.COM  * This driver is a normal USBA client driver. It's also a GLDv3 driver,
4311900SRaymond.Chen@Sun.COM  * which provides the necessary interfaces the GLDv3 framework requires.
4411900SRaymond.Chen@Sun.COM  *
4511900SRaymond.Chen@Sun.COM  */
4611900SRaymond.Chen@Sun.COM 
4711900SRaymond.Chen@Sun.COM #include <sys/types.h>
4811900SRaymond.Chen@Sun.COM #include <sys/strsun.h>
4911900SRaymond.Chen@Sun.COM #include <sys/ddi.h>
5011900SRaymond.Chen@Sun.COM #include <sys/sunddi.h>
5111900SRaymond.Chen@Sun.COM #include <sys/byteorder.h>
5211900SRaymond.Chen@Sun.COM #include <sys/usb/usba/usbai_version.h>
5311900SRaymond.Chen@Sun.COM #include <sys/usb/usba.h>
5411900SRaymond.Chen@Sun.COM #include <sys/usb/usba/usba_types.h>
5511900SRaymond.Chen@Sun.COM #include <sys/usb/clients/usbcdc/usb_cdc.h>
5611900SRaymond.Chen@Sun.COM #include <sys/usb/clients/usbecm/usbecm.h>
5711900SRaymond.Chen@Sun.COM #include <sys/mac_provider.h>
5811900SRaymond.Chen@Sun.COM #include <sys/strsubr.h>
5911900SRaymond.Chen@Sun.COM #include <sys/ethernet.h>
6011900SRaymond.Chen@Sun.COM #include <sys/mac_ether.h> /* MAC_PLUGIN_IDENT_ETHER */
6111900SRaymond.Chen@Sun.COM #include <sys/random.h> /* random_get_bytes */
6211900SRaymond.Chen@Sun.COM #include <sys/sdt.h>	/* sdt */
6311900SRaymond.Chen@Sun.COM #include <inet/nd.h>
6411900SRaymond.Chen@Sun.COM 
6511900SRaymond.Chen@Sun.COM /* MAC callbacks */
6611900SRaymond.Chen@Sun.COM static int	usbecm_m_stat(void *arg, uint_t stat, uint64_t *val);
6711900SRaymond.Chen@Sun.COM static int	usbecm_m_start(void *arg);
6811900SRaymond.Chen@Sun.COM static void	usbecm_m_stop(void *arg);
6911900SRaymond.Chen@Sun.COM static int	usbecm_m_unicst(void *arg, const uint8_t *macaddr);
7011900SRaymond.Chen@Sun.COM static int	usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m);
7111900SRaymond.Chen@Sun.COM static int	usbecm_m_promisc(void *arg, boolean_t on);
7211900SRaymond.Chen@Sun.COM static void	usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
7311900SRaymond.Chen@Sun.COM static mblk_t	*usbecm_m_tx(void *arg, mblk_t *mp);
7411900SRaymond.Chen@Sun.COM static int	usbecm_m_getprop(void *arg, const char *pr_name,
7511900SRaymond.Chen@Sun.COM     mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf);
7611900SRaymond.Chen@Sun.COM static int	usbecm_m_setprop(void *arg, const char *pr_name,
7711900SRaymond.Chen@Sun.COM     mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf);
7811900SRaymond.Chen@Sun.COM 
7911900SRaymond.Chen@Sun.COM static int	usbecm_usb_init(usbecm_state_t *ecmp);
8011900SRaymond.Chen@Sun.COM static int	usbecm_mac_init(usbecm_state_t *ecmp);
8111900SRaymond.Chen@Sun.COM static int	usbecm_mac_fini(usbecm_state_t *ecmp);
8211900SRaymond.Chen@Sun.COM 
8311900SRaymond.Chen@Sun.COM 
8411900SRaymond.Chen@Sun.COM /* utils */
8511900SRaymond.Chen@Sun.COM static void	generate_ether_addr(uint8_t *mac_addr);
8611900SRaymond.Chen@Sun.COM static int	usbecm_rx_start(usbecm_state_t *ecmp);
8711900SRaymond.Chen@Sun.COM 
8811900SRaymond.Chen@Sun.COM static void	usbecm_pipe_start_polling(usbecm_state_t *ecmp);
8911900SRaymond.Chen@Sun.COM static void	usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
9011900SRaymond.Chen@Sun.COM static void	usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
9111900SRaymond.Chen@Sun.COM static void	usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data);
9211900SRaymond.Chen@Sun.COM 
9311900SRaymond.Chen@Sun.COM static int	usbecm_reconnect_event_cb(dev_info_t *dip);
9411900SRaymond.Chen@Sun.COM static int	usbecm_disconnect_event_cb(dev_info_t *dip);
9511900SRaymond.Chen@Sun.COM 
9611900SRaymond.Chen@Sun.COM static int	usbecm_open_pipes(usbecm_state_t *ecmp);
9711900SRaymond.Chen@Sun.COM static void	usbecm_close_pipes(usbecm_state_t *ecmp);
9811900SRaymond.Chen@Sun.COM 
9911900SRaymond.Chen@Sun.COM static int	usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request,
10011900SRaymond.Chen@Sun.COM     uint16_t value, mblk_t **data, int len);
10111900SRaymond.Chen@Sun.COM static int	usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request,
10211900SRaymond.Chen@Sun.COM     uint16_t value, mblk_t **data);
10311900SRaymond.Chen@Sun.COM static int	usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data);
10411900SRaymond.Chen@Sun.COM static int	usbecm_send_zero_data(usbecm_state_t *ecmp);
10511900SRaymond.Chen@Sun.COM static int	usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs,
10611900SRaymond.Chen@Sun.COM     uint32_t *stat_data);
10711900SRaymond.Chen@Sun.COM 
10811900SRaymond.Chen@Sun.COM static int	usbecm_create_pm_components(usbecm_state_t *ecmp);
10911900SRaymond.Chen@Sun.COM static void	usbecm_destroy_pm_components(usbecm_state_t *ecmp);
11011900SRaymond.Chen@Sun.COM static int	usbecm_power(dev_info_t *dip, int comp, int level);
11111900SRaymond.Chen@Sun.COM static void	usbecm_pm_set_busy(usbecm_state_t *ecmp);
11211900SRaymond.Chen@Sun.COM static void	usbecm_pm_set_idle(usbecm_state_t *ecmp);
11311900SRaymond.Chen@Sun.COM 
11411900SRaymond.Chen@Sun.COM static int	usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
11511900SRaymond.Chen@Sun.COM static int	usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
11611900SRaymond.Chen@Sun.COM 
11711900SRaymond.Chen@Sun.COM static int	usbecm_suspend(usbecm_state_t *ecmp);
11811900SRaymond.Chen@Sun.COM static int	usbecm_resume(usbecm_state_t *ecmp);
11911900SRaymond.Chen@Sun.COM static int	usbecm_restore_device_state(usbecm_state_t *ecmp);
12011900SRaymond.Chen@Sun.COM static void	usbecm_cleanup(usbecm_state_t *ecmp);
12111900SRaymond.Chen@Sun.COM 
12211900SRaymond.Chen@Sun.COM /* Driver identification */
12311900SRaymond.Chen@Sun.COM static char usbecm_ident[] = "usbecm 1.0";
12411900SRaymond.Chen@Sun.COM 
12511900SRaymond.Chen@Sun.COM /* Global state pointer for managing per-device soft states */
12611900SRaymond.Chen@Sun.COM void *usbecm_statep;
12711900SRaymond.Chen@Sun.COM 
12811900SRaymond.Chen@Sun.COM /* print levels */
12911900SRaymond.Chen@Sun.COM static uint_t   usbecm_errlevel = USB_LOG_L3;
13011900SRaymond.Chen@Sun.COM static uint_t   usbecm_errmask = 0xffffffff;
13111900SRaymond.Chen@Sun.COM static uint_t   usbecm_instance_debug = (uint_t)-1;
13211900SRaymond.Chen@Sun.COM 
13311900SRaymond.Chen@Sun.COM /*
13411900SRaymond.Chen@Sun.COM  * to prevent upper layers packet flood from exhausting system
13511900SRaymond.Chen@Sun.COM  * resources(USBA does not set limitation of requests on a pipe),
13611900SRaymond.Chen@Sun.COM  * we set a upper limit for the transfer queue length.
13711900SRaymond.Chen@Sun.COM  */
13811900SRaymond.Chen@Sun.COM static	int	usbecm_tx_max = 32;
13911900SRaymond.Chen@Sun.COM 
14011900SRaymond.Chen@Sun.COM #define	SUN_SP_VENDOR_ID	0x0430
14111900SRaymond.Chen@Sun.COM #define	SUN_SP_PRODUCT_ID	0xa4a2
14211900SRaymond.Chen@Sun.COM 
14311900SRaymond.Chen@Sun.COM static uint8_t	usbecm_broadcast[ETHERADDRL] = {
14411900SRaymond.Chen@Sun.COM 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
14511900SRaymond.Chen@Sun.COM };
14611900SRaymond.Chen@Sun.COM 
14711900SRaymond.Chen@Sun.COM static usb_event_t usbecm_events = {
14811900SRaymond.Chen@Sun.COM 	usbecm_disconnect_event_cb,
14911900SRaymond.Chen@Sun.COM 	usbecm_reconnect_event_cb,
15011900SRaymond.Chen@Sun.COM 	NULL, NULL
15111900SRaymond.Chen@Sun.COM };
15211900SRaymond.Chen@Sun.COM 
15311900SRaymond.Chen@Sun.COM #define	ECM_DS_OP_VALID(op) ((ecmp->ecm_ds_ops) && (ecmp->ecm_ds_ops->op))
15411900SRaymond.Chen@Sun.COM 
15511900SRaymond.Chen@Sun.COM /*
15611900SRaymond.Chen@Sun.COM  * MAC Call Back entries
15711900SRaymond.Chen@Sun.COM  */
15811900SRaymond.Chen@Sun.COM static mac_callbacks_t usbecm_m_callbacks = {
15911900SRaymond.Chen@Sun.COM 	MC_IOCTL | MC_SETPROP | MC_GETPROP,
16011900SRaymond.Chen@Sun.COM 	usbecm_m_stat,		/* Get the value of a statistic */
16111900SRaymond.Chen@Sun.COM 	usbecm_m_start,		/* Start the device */
16211900SRaymond.Chen@Sun.COM 	usbecm_m_stop,		/* Stop the device */
16311900SRaymond.Chen@Sun.COM 	usbecm_m_promisc,	/* Enable or disable promiscuous mode */
16411900SRaymond.Chen@Sun.COM 	usbecm_m_multicst,	/* Enable or disable a multicast addr */
16511900SRaymond.Chen@Sun.COM 	usbecm_m_unicst,	/* Set the unicast MAC address */
16611900SRaymond.Chen@Sun.COM 	usbecm_m_tx,		/* Transmit a packet */
16711900SRaymond.Chen@Sun.COM 	NULL,
16811900SRaymond.Chen@Sun.COM 	usbecm_m_ioctl,		/* Process an unknown ioctl */
16911900SRaymond.Chen@Sun.COM 	NULL,			/* mc_getcapab */
17011900SRaymond.Chen@Sun.COM 	NULL,			/* mc_open */
17111900SRaymond.Chen@Sun.COM 	NULL,			/* mc_close */
17211900SRaymond.Chen@Sun.COM 	usbecm_m_setprop, 	/* mc_setprop */
17311900SRaymond.Chen@Sun.COM 	usbecm_m_getprop,	/* mc_getprop */
17411900SRaymond.Chen@Sun.COM 	NULL
17511900SRaymond.Chen@Sun.COM };
17611900SRaymond.Chen@Sun.COM 
17711900SRaymond.Chen@Sun.COM 
17811900SRaymond.Chen@Sun.COM /*
17911900SRaymond.Chen@Sun.COM  *  Module Loading Data & Entry Points
18011900SRaymond.Chen@Sun.COM  *     Can't use DDI_DEFINE_STREAM_OPS, since it does
18111900SRaymond.Chen@Sun.COM  *     not provide devo_power entry.
18211900SRaymond.Chen@Sun.COM  */
18311900SRaymond.Chen@Sun.COM static struct cb_ops cb_usbecm = {
18411900SRaymond.Chen@Sun.COM 	nulldev,		/* cb_open */
18511900SRaymond.Chen@Sun.COM 	nulldev,		/* cb_close */
18611900SRaymond.Chen@Sun.COM 	nodev,			/* cb_strategy */
18711900SRaymond.Chen@Sun.COM 	nodev,			/* cb_print */
18811900SRaymond.Chen@Sun.COM 	nodev,			/* cb_dump */
18911900SRaymond.Chen@Sun.COM 	nodev,			/* cb_read */
19011900SRaymond.Chen@Sun.COM 	nodev,			/* cb_write */
19111900SRaymond.Chen@Sun.COM 	nodev,			/* cb_ioctl */
19211900SRaymond.Chen@Sun.COM 	nodev,			/* cb_devmap */
19311900SRaymond.Chen@Sun.COM 	nodev,			/* cb_mmap */
19411900SRaymond.Chen@Sun.COM 	nodev,			/* cb_segmap */
19511900SRaymond.Chen@Sun.COM 	nochpoll,		/* cb_chpoll */
19611900SRaymond.Chen@Sun.COM 	ddi_prop_op,		/* cb_prop_op */
19711900SRaymond.Chen@Sun.COM 	NULL,			/* cb_stream */
19811900SRaymond.Chen@Sun.COM 	D_MP,			/* cb_flag */
19911900SRaymond.Chen@Sun.COM 	CB_REV,			/* cb_rev */
20011900SRaymond.Chen@Sun.COM 	nodev,			/* cb_aread */
20111900SRaymond.Chen@Sun.COM 	nodev,			/* cb_awrite */
20211900SRaymond.Chen@Sun.COM };
20311900SRaymond.Chen@Sun.COM 
20411900SRaymond.Chen@Sun.COM static struct dev_ops usbecm_devops = {
20511900SRaymond.Chen@Sun.COM 	DEVO_REV,		/* devo_rev */
20611900SRaymond.Chen@Sun.COM 	0,			/* devo_refcnt */
20711900SRaymond.Chen@Sun.COM 	NULL,			/* devo_getinfo */
20811900SRaymond.Chen@Sun.COM 	nulldev,		/* devo_identify */
20911900SRaymond.Chen@Sun.COM 	nulldev,		/* devo_probe */
21011900SRaymond.Chen@Sun.COM 	usbecm_attach,		/* devo_attach */
21111900SRaymond.Chen@Sun.COM 	usbecm_detach,		/* devo_detach */
21211900SRaymond.Chen@Sun.COM 	nodev,			/* devo_reset */
21311900SRaymond.Chen@Sun.COM 	&(cb_usbecm),		/* devo_cb_ops */
21411900SRaymond.Chen@Sun.COM 	(struct bus_ops *)NULL,	/* devo_bus_ops */
21511900SRaymond.Chen@Sun.COM 	usbecm_power,		/* devo_power */
21611900SRaymond.Chen@Sun.COM 	ddi_quiesce_not_needed	/* devo_quiesce */
21711900SRaymond.Chen@Sun.COM };
21811900SRaymond.Chen@Sun.COM 
21911900SRaymond.Chen@Sun.COM static struct modldrv usbecm_modldrv = {
22011900SRaymond.Chen@Sun.COM 	&mod_driverops,		/* drv_modops */
22111900SRaymond.Chen@Sun.COM 	usbecm_ident,		/* drv_linkinfo */
22211900SRaymond.Chen@Sun.COM 	&usbecm_devops		/* drv_dev_ops */
22311900SRaymond.Chen@Sun.COM };
22411900SRaymond.Chen@Sun.COM 
22511900SRaymond.Chen@Sun.COM static struct modlinkage usbecm_ml = {
22611900SRaymond.Chen@Sun.COM 	MODREV_1,		/* ml_rev */
22711900SRaymond.Chen@Sun.COM 	&usbecm_modldrv, NULL	/* ml_linkage */
22811900SRaymond.Chen@Sun.COM };
22911900SRaymond.Chen@Sun.COM 
23011900SRaymond.Chen@Sun.COM 
23111900SRaymond.Chen@Sun.COM /*
23211900SRaymond.Chen@Sun.COM  * Device operations
23311900SRaymond.Chen@Sun.COM  */
23411900SRaymond.Chen@Sun.COM /*
23511900SRaymond.Chen@Sun.COM  * Binding the driver to a device.
23611900SRaymond.Chen@Sun.COM  *
23711900SRaymond.Chen@Sun.COM  * Concurrency: Until usbecm_attach() returns with success,
23811900SRaymond.Chen@Sun.COM  * the only other entry point that can be executed is getinfo().
23911900SRaymond.Chen@Sun.COM  * Thus no locking here yet.
24011900SRaymond.Chen@Sun.COM  */
24111900SRaymond.Chen@Sun.COM static int
usbecm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)24211900SRaymond.Chen@Sun.COM usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
24311900SRaymond.Chen@Sun.COM {
24411900SRaymond.Chen@Sun.COM 	char strbuf[32];
24511900SRaymond.Chen@Sun.COM 	int instance;
24611900SRaymond.Chen@Sun.COM 	int err;
24711900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = NULL;
24811900SRaymond.Chen@Sun.COM 
24911900SRaymond.Chen@Sun.COM 	switch (cmd) {
25011900SRaymond.Chen@Sun.COM 	case DDI_ATTACH:
25111900SRaymond.Chen@Sun.COM 		break;
25211900SRaymond.Chen@Sun.COM 
25311900SRaymond.Chen@Sun.COM 	case DDI_RESUME:
25411900SRaymond.Chen@Sun.COM 		ecmp = (usbecm_state_t *)ddi_get_soft_state(usbecm_statep,
25511900SRaymond.Chen@Sun.COM 		    ddi_get_instance(dip));
25611900SRaymond.Chen@Sun.COM 
25711900SRaymond.Chen@Sun.COM 		(void) usbecm_resume(ecmp);
25811900SRaymond.Chen@Sun.COM 
25911900SRaymond.Chen@Sun.COM 		return (DDI_SUCCESS);
26011900SRaymond.Chen@Sun.COM 
26111900SRaymond.Chen@Sun.COM 	default:
26211900SRaymond.Chen@Sun.COM 		return (DDI_FAILURE);
26311900SRaymond.Chen@Sun.COM 	}
26411900SRaymond.Chen@Sun.COM 
26511900SRaymond.Chen@Sun.COM 	instance = ddi_get_instance(dip);
26611900SRaymond.Chen@Sun.COM 
26711900SRaymond.Chen@Sun.COM 	if (ddi_soft_state_zalloc(usbecm_statep, instance) == DDI_SUCCESS) {
26811900SRaymond.Chen@Sun.COM 		ecmp = ddi_get_soft_state(usbecm_statep, instance);
26911900SRaymond.Chen@Sun.COM 	}
27011900SRaymond.Chen@Sun.COM 	if (ecmp == NULL) {
27111900SRaymond.Chen@Sun.COM 		cmn_err(CE_WARN, "usbecm_attach: fail to get soft state");
27211900SRaymond.Chen@Sun.COM 
27311900SRaymond.Chen@Sun.COM 		return (DDI_FAILURE);
27411900SRaymond.Chen@Sun.COM 	}
27511900SRaymond.Chen@Sun.COM 
27611900SRaymond.Chen@Sun.COM 	ecmp->ecm_dip = dip;
27711900SRaymond.Chen@Sun.COM 
27811900SRaymond.Chen@Sun.COM 	ecmp->ecm_lh = usb_alloc_log_hdl(ecmp->ecm_dip, "usbecm",
27911900SRaymond.Chen@Sun.COM 	    &usbecm_errlevel, &usbecm_errmask, &usbecm_instance_debug, 0);
28011900SRaymond.Chen@Sun.COM 
28111900SRaymond.Chen@Sun.COM 	if (usbecm_usb_init(ecmp) != USB_SUCCESS) {
28211900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
28311900SRaymond.Chen@Sun.COM 		    "usbecm_attach: failed to init usb");
28411900SRaymond.Chen@Sun.COM 
28511900SRaymond.Chen@Sun.COM 		goto fail;
28611900SRaymond.Chen@Sun.COM 	}
28711900SRaymond.Chen@Sun.COM 
28811900SRaymond.Chen@Sun.COM 	if (ECM_DS_OP_VALID(ecm_ds_init)) {
28911900SRaymond.Chen@Sun.COM 		if (ecmp->ecm_ds_ops->ecm_ds_init(ecmp) != USB_SUCCESS) {
29011900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
29111900SRaymond.Chen@Sun.COM 			    "usbecm_attach: failed to init DS");
29211900SRaymond.Chen@Sun.COM 
29311900SRaymond.Chen@Sun.COM 			goto fail;
29411900SRaymond.Chen@Sun.COM 		}
29511900SRaymond.Chen@Sun.COM 	}
29611900SRaymond.Chen@Sun.COM 
29711900SRaymond.Chen@Sun.COM 	if (usbecm_mac_init(ecmp) != DDI_SUCCESS) {
29811900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
29911900SRaymond.Chen@Sun.COM 		    "usbecm_attach: failed to init mac");
30011900SRaymond.Chen@Sun.COM 
30111900SRaymond.Chen@Sun.COM 		goto fail;
30211900SRaymond.Chen@Sun.COM 	}
30311900SRaymond.Chen@Sun.COM 	ecmp->ecm_init_flags |= USBECM_INIT_MAC;
30411900SRaymond.Chen@Sun.COM 
30511900SRaymond.Chen@Sun.COM 	/*
30611900SRaymond.Chen@Sun.COM 	 * Create minor node of type usb_net. Not necessary to create
30711900SRaymond.Chen@Sun.COM 	 * DDI_NT_NET since it's created in mac_register(). Otherwise,
30811900SRaymond.Chen@Sun.COM 	 * system will panic.
30911900SRaymond.Chen@Sun.COM 	 */
31011900SRaymond.Chen@Sun.COM 	(void) snprintf(strbuf, sizeof (strbuf), "usbecm%d", instance);
31111900SRaymond.Chen@Sun.COM 	err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
31211900SRaymond.Chen@Sun.COM 	    instance + 1, "usb_net", 0);
31311900SRaymond.Chen@Sun.COM 	if (err != DDI_SUCCESS) {
31411900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
31511900SRaymond.Chen@Sun.COM 		    "failed to create minor node");
31611900SRaymond.Chen@Sun.COM 
31711900SRaymond.Chen@Sun.COM 		goto fail;
31811900SRaymond.Chen@Sun.COM 	}
31911900SRaymond.Chen@Sun.COM 
32011900SRaymond.Chen@Sun.COM 	/* always busy. May change to a more precise PM in future */
32111900SRaymond.Chen@Sun.COM 	usbecm_pm_set_busy(ecmp);
32211900SRaymond.Chen@Sun.COM 
32311900SRaymond.Chen@Sun.COM 	ddi_report_dev(dip);
32411900SRaymond.Chen@Sun.COM 
32511900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
32611900SRaymond.Chen@Sun.COM 	    "usbecm_attach: succeed!");
32711900SRaymond.Chen@Sun.COM 
32811900SRaymond.Chen@Sun.COM 	return (DDI_SUCCESS);
32911900SRaymond.Chen@Sun.COM 
33011900SRaymond.Chen@Sun.COM fail:
33111900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh,
33211900SRaymond.Chen@Sun.COM 	    "usbecm_attach: Attach fail");
33311900SRaymond.Chen@Sun.COM 
33411900SRaymond.Chen@Sun.COM 	usbecm_cleanup(ecmp);
33511900SRaymond.Chen@Sun.COM 	ddi_prop_remove_all(dip);
33611900SRaymond.Chen@Sun.COM 	ddi_soft_state_free(usbecm_statep, instance);
33711900SRaymond.Chen@Sun.COM 
33811900SRaymond.Chen@Sun.COM 	return (DDI_FAILURE);
33911900SRaymond.Chen@Sun.COM 
34011900SRaymond.Chen@Sun.COM }
34111900SRaymond.Chen@Sun.COM 
34211900SRaymond.Chen@Sun.COM 
34311900SRaymond.Chen@Sun.COM /*
34411900SRaymond.Chen@Sun.COM  * Detach the driver from a device.
34511900SRaymond.Chen@Sun.COM  *
34611900SRaymond.Chen@Sun.COM  * Concurrency: Will be called only after a successful attach
34711900SRaymond.Chen@Sun.COM  * (and not concurrently).
34811900SRaymond.Chen@Sun.COM  */
34911900SRaymond.Chen@Sun.COM static int
usbecm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)35011900SRaymond.Chen@Sun.COM usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
35111900SRaymond.Chen@Sun.COM {
35211900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = NULL;
35311900SRaymond.Chen@Sun.COM 	int instance;
35411900SRaymond.Chen@Sun.COM 
35511900SRaymond.Chen@Sun.COM 	instance = ddi_get_instance(dip);
35611900SRaymond.Chen@Sun.COM 	ecmp = ddi_get_soft_state(usbecm_statep, instance);
35711900SRaymond.Chen@Sun.COM 	ASSERT(ecmp != NULL);
35811900SRaymond.Chen@Sun.COM 
35911900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
36011900SRaymond.Chen@Sun.COM 	    "usbecm_detach: entry ");
36111900SRaymond.Chen@Sun.COM 
36211900SRaymond.Chen@Sun.COM 	switch (cmd) {
36311900SRaymond.Chen@Sun.COM 	case DDI_DETACH:
36411900SRaymond.Chen@Sun.COM 		break;
36511900SRaymond.Chen@Sun.COM 
36611900SRaymond.Chen@Sun.COM 	case DDI_SUSPEND:
36711900SRaymond.Chen@Sun.COM 
36811900SRaymond.Chen@Sun.COM 		return (usbecm_suspend(ecmp));
36911900SRaymond.Chen@Sun.COM 
37011900SRaymond.Chen@Sun.COM 	default:
37111900SRaymond.Chen@Sun.COM 		return (DDI_FAILURE);
37211900SRaymond.Chen@Sun.COM 	}
37311900SRaymond.Chen@Sun.COM 
37411900SRaymond.Chen@Sun.COM 	usbecm_pm_set_idle(ecmp);
37511900SRaymond.Chen@Sun.COM 
37611900SRaymond.Chen@Sun.COM 	if (ECM_DS_OP_VALID(ecm_ds_fini)) {
37711900SRaymond.Chen@Sun.COM 		if (ecmp->ecm_ds_ops->ecm_ds_fini(ecmp) != USB_SUCCESS) {
37811900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
37911900SRaymond.Chen@Sun.COM 			    "usbecm_detach: deinitialize DS fail!");
38011900SRaymond.Chen@Sun.COM 
38111900SRaymond.Chen@Sun.COM 			return (DDI_FAILURE);
38211900SRaymond.Chen@Sun.COM 		}
38311900SRaymond.Chen@Sun.COM 	}
38411900SRaymond.Chen@Sun.COM 
38511900SRaymond.Chen@Sun.COM 	if (usbecm_mac_fini(ecmp) != 0) {
38611900SRaymond.Chen@Sun.COM 
38711900SRaymond.Chen@Sun.COM 		return (DDI_FAILURE);
38811900SRaymond.Chen@Sun.COM 	}
38911900SRaymond.Chen@Sun.COM 
39011900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
39111900SRaymond.Chen@Sun.COM 	    "usbecm_detach: exit");
39211900SRaymond.Chen@Sun.COM 
39311900SRaymond.Chen@Sun.COM 	usbecm_cleanup(ecmp);
39411900SRaymond.Chen@Sun.COM 	ddi_soft_state_free(usbecm_statep, instance);
39511900SRaymond.Chen@Sun.COM 
39611900SRaymond.Chen@Sun.COM 	return (DDI_SUCCESS);
39711900SRaymond.Chen@Sun.COM }
39811900SRaymond.Chen@Sun.COM 
39911900SRaymond.Chen@Sun.COM 
40011900SRaymond.Chen@Sun.COM /*
40111900SRaymond.Chen@Sun.COM  * Mac Call Back functions
40211900SRaymond.Chen@Sun.COM  */
40311900SRaymond.Chen@Sun.COM 
40411900SRaymond.Chen@Sun.COM /*
40511900SRaymond.Chen@Sun.COM  * Read device statistic information.
40611900SRaymond.Chen@Sun.COM  */
40711900SRaymond.Chen@Sun.COM static int
usbecm_m_stat(void * arg,uint_t stat,uint64_t * val)40811900SRaymond.Chen@Sun.COM usbecm_m_stat(void *arg, uint_t stat, uint64_t *val)
40911900SRaymond.Chen@Sun.COM {
41011900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
41111900SRaymond.Chen@Sun.COM 	uint32_t	stats;
41211900SRaymond.Chen@Sun.COM 	int		rval;
41311900SRaymond.Chen@Sun.COM 	uint32_t	fs;
41411900SRaymond.Chen@Sun.COM 
41511900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
41611900SRaymond.Chen@Sun.COM 	    "usbecm_m_stat: entry, stat=%d", stat);
41711900SRaymond.Chen@Sun.COM 
41811900SRaymond.Chen@Sun.COM 	/*
41911900SRaymond.Chen@Sun.COM 	 * Some of the stats are MII specific. We try to
42011900SRaymond.Chen@Sun.COM 	 * resolve all the statistics we understand. If
42111900SRaymond.Chen@Sun.COM 	 * the usb device can't provide it, return ENOTSUP.
42211900SRaymond.Chen@Sun.COM 	 */
42311900SRaymond.Chen@Sun.COM 	switch (stat) {
42411900SRaymond.Chen@Sun.COM 	case MAC_STAT_IFSPEED:
42511900SRaymond.Chen@Sun.COM 		/* return link speed */
42611900SRaymond.Chen@Sun.COM 		mutex_enter(&ecmp->ecm_mutex);
42711900SRaymond.Chen@Sun.COM 		if (ecmp->ecm_stat.es_downspeed) {
42811900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_downspeed;
42911900SRaymond.Chen@Sun.COM 		} else {
43011900SRaymond.Chen@Sun.COM 			*val = 10 * 1000000ull; /* set a default value */
43111900SRaymond.Chen@Sun.COM 		}
43211900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
43311900SRaymond.Chen@Sun.COM 
43411900SRaymond.Chen@Sun.COM 		return (0);
43511900SRaymond.Chen@Sun.COM 	case ETHER_STAT_LINK_DUPLEX:
43611900SRaymond.Chen@Sun.COM 		*val = LINK_DUPLEX_FULL;
43711900SRaymond.Chen@Sun.COM 
43811900SRaymond.Chen@Sun.COM 		return (0);
43911900SRaymond.Chen@Sun.COM 
44011900SRaymond.Chen@Sun.COM 	case ETHER_STAT_SQE_ERRORS:
44111900SRaymond.Chen@Sun.COM 		*val = 0;
44211900SRaymond.Chen@Sun.COM 
44311900SRaymond.Chen@Sun.COM 		return (0);
44411900SRaymond.Chen@Sun.COM 
44511900SRaymond.Chen@Sun.COM 	/* Map MAC/Ether stats to ECM statistics */
44611900SRaymond.Chen@Sun.COM 	case MAC_STAT_NORCVBUF:
44711900SRaymond.Chen@Sun.COM 		fs = ECM_RCV_NO_BUFFER;
44811900SRaymond.Chen@Sun.COM 
44911900SRaymond.Chen@Sun.COM 		break;
45011900SRaymond.Chen@Sun.COM 	case MAC_STAT_NOXMTBUF:
45111900SRaymond.Chen@Sun.COM 		fs = ECM_XMIT_ERROR;
45211900SRaymond.Chen@Sun.COM 
45311900SRaymond.Chen@Sun.COM 		break;
45411900SRaymond.Chen@Sun.COM 	case MAC_STAT_IERRORS:
45511900SRaymond.Chen@Sun.COM 		fs = ECM_RCV_ERROR;
45611900SRaymond.Chen@Sun.COM 
45711900SRaymond.Chen@Sun.COM 		break;
45811900SRaymond.Chen@Sun.COM 	case MAC_STAT_OERRORS:
45911900SRaymond.Chen@Sun.COM 		fs = ECM_XMIT_ERROR;
46011900SRaymond.Chen@Sun.COM 
46111900SRaymond.Chen@Sun.COM 		break;
46211900SRaymond.Chen@Sun.COM 	case MAC_STAT_RBYTES:
46311900SRaymond.Chen@Sun.COM 		fs = ECM_DIRECTED_BYTES_RCV;
46411900SRaymond.Chen@Sun.COM 
46511900SRaymond.Chen@Sun.COM 		break;
46611900SRaymond.Chen@Sun.COM 	case MAC_STAT_IPACKETS:
46711900SRaymond.Chen@Sun.COM 		fs = ECM_RCV_OK; /* frames */
46811900SRaymond.Chen@Sun.COM 
46911900SRaymond.Chen@Sun.COM 		break;
47011900SRaymond.Chen@Sun.COM 	case MAC_STAT_OBYTES:
47111900SRaymond.Chen@Sun.COM 		fs = ECM_DIRECTED_BYTES_XMIT;
47211900SRaymond.Chen@Sun.COM 
47311900SRaymond.Chen@Sun.COM 		break;
47411900SRaymond.Chen@Sun.COM 	case MAC_STAT_OPACKETS:
47511900SRaymond.Chen@Sun.COM 		fs = ECM_XMIT_OK; /* frames */
47611900SRaymond.Chen@Sun.COM 
47711900SRaymond.Chen@Sun.COM 		break;
47811900SRaymond.Chen@Sun.COM 	case MAC_STAT_MULTIRCV:
47911900SRaymond.Chen@Sun.COM 		fs = ECM_MULTICAST_FRAMES_RCV;
48011900SRaymond.Chen@Sun.COM 
48111900SRaymond.Chen@Sun.COM 		break;
48211900SRaymond.Chen@Sun.COM 	case MAC_STAT_BRDCSTRCV:
48311900SRaymond.Chen@Sun.COM 		fs = ECM_BROADCAST_FRAMES_RCV;
48411900SRaymond.Chen@Sun.COM 
48511900SRaymond.Chen@Sun.COM 		break;
48611900SRaymond.Chen@Sun.COM 	case MAC_STAT_MULTIXMT:
48711900SRaymond.Chen@Sun.COM 		fs = ECM_MULTICAST_FRAMES_XMIT;
48811900SRaymond.Chen@Sun.COM 
48911900SRaymond.Chen@Sun.COM 		break;
49011900SRaymond.Chen@Sun.COM 	case MAC_STAT_BRDCSTXMT:
49111900SRaymond.Chen@Sun.COM 		fs = ECM_BROADCAST_FRAMES_XMIT;
49211900SRaymond.Chen@Sun.COM 
49311900SRaymond.Chen@Sun.COM 		break;
49411900SRaymond.Chen@Sun.COM 	case MAC_STAT_COLLISIONS:
49511900SRaymond.Chen@Sun.COM 		fs = ECM_XMIT_MAX_COLLISIONS;
49611900SRaymond.Chen@Sun.COM 
49711900SRaymond.Chen@Sun.COM 		break;
49811900SRaymond.Chen@Sun.COM 	case MAC_STAT_OVERFLOWS:
49911900SRaymond.Chen@Sun.COM 		fs = ECM_RCV_OVERRUN;
50011900SRaymond.Chen@Sun.COM 
50111900SRaymond.Chen@Sun.COM 		break;
50211900SRaymond.Chen@Sun.COM 	case MAC_STAT_UNDERFLOWS:
50311900SRaymond.Chen@Sun.COM 		fs = ECM_XMIT_UNDERRUN;
50411900SRaymond.Chen@Sun.COM 
50511900SRaymond.Chen@Sun.COM 		break;
50611900SRaymond.Chen@Sun.COM 	case ETHER_STAT_FCS_ERRORS:
50711900SRaymond.Chen@Sun.COM 		fs = ECM_RCV_CRC_ERROR;
50811900SRaymond.Chen@Sun.COM 
50911900SRaymond.Chen@Sun.COM 		break;
51011900SRaymond.Chen@Sun.COM 	case ETHER_STAT_ALIGN_ERRORS:
51111900SRaymond.Chen@Sun.COM 		fs = ECM_RCV_ERROR_ALIGNMENT;
51211900SRaymond.Chen@Sun.COM 
51311900SRaymond.Chen@Sun.COM 		break;
51411900SRaymond.Chen@Sun.COM 	case ETHER_STAT_DEFER_XMTS:
51511900SRaymond.Chen@Sun.COM 		fs = ECM_XMIT_DEFERRED;
51611900SRaymond.Chen@Sun.COM 
51711900SRaymond.Chen@Sun.COM 		break;
51811900SRaymond.Chen@Sun.COM 	case ETHER_STAT_FIRST_COLLISIONS:
51911900SRaymond.Chen@Sun.COM 		fs = ECM_XMIT_ONE_COLLISION;
52011900SRaymond.Chen@Sun.COM 
52111900SRaymond.Chen@Sun.COM 		break;
52211900SRaymond.Chen@Sun.COM 	case ETHER_STAT_MULTI_COLLISIONS:
52311900SRaymond.Chen@Sun.COM 		fs = ECM_XMIT_MORE_COLLISIONS;
52411900SRaymond.Chen@Sun.COM 
52511900SRaymond.Chen@Sun.COM 		break;
52611900SRaymond.Chen@Sun.COM 	case ETHER_STAT_TX_LATE_COLLISIONS:
52711900SRaymond.Chen@Sun.COM 		fs = ECM_XMIT_LATE_COLLISIONS;
52811900SRaymond.Chen@Sun.COM 
52911900SRaymond.Chen@Sun.COM 		break;
53011900SRaymond.Chen@Sun.COM 
53111900SRaymond.Chen@Sun.COM 	default:
53211900SRaymond.Chen@Sun.COM 		return (ENOTSUP);
53311900SRaymond.Chen@Sun.COM 	}
53411900SRaymond.Chen@Sun.COM 
53511900SRaymond.Chen@Sun.COM 	/*
53611900SRaymond.Chen@Sun.COM 	 * we need to access device to get required stats,
53711900SRaymond.Chen@Sun.COM 	 * so check device state first
53811900SRaymond.Chen@Sun.COM 	 */
53911900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
54011900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
54111900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
54211900SRaymond.Chen@Sun.COM 		    "usbecm_m_stat: device not ONLINE");
54311900SRaymond.Chen@Sun.COM 
54411900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
54511900SRaymond.Chen@Sun.COM 
54611900SRaymond.Chen@Sun.COM 		return (EIO);
54711900SRaymond.Chen@Sun.COM 	}
54811900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
54911900SRaymond.Chen@Sun.COM 
55011900SRaymond.Chen@Sun.COM 	rval = usbecm_get_statistics(ecmp,
55111900SRaymond.Chen@Sun.COM 	    ECM_STAT_SELECTOR(fs), &stats);
55211900SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
55311900SRaymond.Chen@Sun.COM 		mutex_enter(&ecmp->ecm_mutex);
55411900SRaymond.Chen@Sun.COM 		switch (stat) {
55511900SRaymond.Chen@Sun.COM 		case MAC_STAT_IERRORS:
55611900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_ierrors;
55711900SRaymond.Chen@Sun.COM 
55811900SRaymond.Chen@Sun.COM 			break;
55911900SRaymond.Chen@Sun.COM 		case MAC_STAT_OERRORS:
56011900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_oerrors;
56111900SRaymond.Chen@Sun.COM 
56211900SRaymond.Chen@Sun.COM 			break;
56311900SRaymond.Chen@Sun.COM 		case MAC_STAT_RBYTES:
56411900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_ibytes;
56511900SRaymond.Chen@Sun.COM 
56611900SRaymond.Chen@Sun.COM 			break;
56711900SRaymond.Chen@Sun.COM 		case MAC_STAT_IPACKETS:
56811900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_ipackets;
56911900SRaymond.Chen@Sun.COM 
57011900SRaymond.Chen@Sun.COM 			break;
57111900SRaymond.Chen@Sun.COM 		case MAC_STAT_OBYTES:
57211900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_obytes;
57311900SRaymond.Chen@Sun.COM 
57411900SRaymond.Chen@Sun.COM 			break;
57511900SRaymond.Chen@Sun.COM 		case MAC_STAT_OPACKETS:
57611900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_opackets;
57711900SRaymond.Chen@Sun.COM 
57811900SRaymond.Chen@Sun.COM 			break;
57911900SRaymond.Chen@Sun.COM 		case MAC_STAT_MULTIRCV:
58011900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_multircv;
58111900SRaymond.Chen@Sun.COM 
58211900SRaymond.Chen@Sun.COM 			break;
58311900SRaymond.Chen@Sun.COM 		case MAC_STAT_MULTIXMT:
58411900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_multixmt;
58511900SRaymond.Chen@Sun.COM 
58611900SRaymond.Chen@Sun.COM 			break;
58711900SRaymond.Chen@Sun.COM 		case MAC_STAT_BRDCSTRCV:
58811900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_brdcstrcv;
58911900SRaymond.Chen@Sun.COM 
59011900SRaymond.Chen@Sun.COM 			break;
59111900SRaymond.Chen@Sun.COM 		case MAC_STAT_BRDCSTXMT:
59211900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_brdcstxmt;
59311900SRaymond.Chen@Sun.COM 
59411900SRaymond.Chen@Sun.COM 			break;
59511900SRaymond.Chen@Sun.COM 		case ETHER_STAT_MACXMT_ERRORS:
59611900SRaymond.Chen@Sun.COM 			*val = ecmp->ecm_stat.es_macxmt_err;
59711900SRaymond.Chen@Sun.COM 			break;
59811900SRaymond.Chen@Sun.COM 		default:
59911900SRaymond.Chen@Sun.COM 			*val = 0;
60011900SRaymond.Chen@Sun.COM 
60111900SRaymond.Chen@Sun.COM 			break;
60211900SRaymond.Chen@Sun.COM 		}
60311900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
60411900SRaymond.Chen@Sun.COM 	} else {
60511900SRaymond.Chen@Sun.COM 		*val = stats;
60611900SRaymond.Chen@Sun.COM 	}
60711900SRaymond.Chen@Sun.COM 
60811900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
60911900SRaymond.Chen@Sun.COM 	    "usbecm_m_stat: end");
61011900SRaymond.Chen@Sun.COM 
61111900SRaymond.Chen@Sun.COM 	return (0);
61211900SRaymond.Chen@Sun.COM }
61311900SRaymond.Chen@Sun.COM 
61411900SRaymond.Chen@Sun.COM 
61511900SRaymond.Chen@Sun.COM /*
61611900SRaymond.Chen@Sun.COM  * Start the device:
61711900SRaymond.Chen@Sun.COM  *	- Set proper altsettings of the data interface
61811900SRaymond.Chen@Sun.COM  *	- Open status and data endpoints
61911900SRaymond.Chen@Sun.COM  *	- Start status polling
62011900SRaymond.Chen@Sun.COM  *	- Get bulk-in ep ready to receive data from ethernet
62111900SRaymond.Chen@Sun.COM  *
62211900SRaymond.Chen@Sun.COM  * Concurrency: Presumably fully concurrent, must lock.
62311900SRaymond.Chen@Sun.COM  */
62411900SRaymond.Chen@Sun.COM static int
usbecm_m_start(void * arg)62511900SRaymond.Chen@Sun.COM usbecm_m_start(void *arg)
62611900SRaymond.Chen@Sun.COM {
62711900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
62811900SRaymond.Chen@Sun.COM 	int rval;
62911900SRaymond.Chen@Sun.COM 
63011900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
63111900SRaymond.Chen@Sun.COM 	    "usbecm_m_start: entry");
63211900SRaymond.Chen@Sun.COM 
63311900SRaymond.Chen@Sun.COM 	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
63411900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
63511900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
63611900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
63711900SRaymond.Chen@Sun.COM 		    "usbecm_m_start: device not online");
63811900SRaymond.Chen@Sun.COM 		rval = ENODEV;
63911900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
64011900SRaymond.Chen@Sun.COM 
64111900SRaymond.Chen@Sun.COM 		goto fail;
64211900SRaymond.Chen@Sun.COM 	}
64311900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
64411900SRaymond.Chen@Sun.COM 
64511900SRaymond.Chen@Sun.COM 	if (usbecm_open_pipes(ecmp) != USB_SUCCESS) {
64611900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
64711900SRaymond.Chen@Sun.COM 		    "usbecm_m_start: open pipes fail");
64811900SRaymond.Chen@Sun.COM 		rval = EIO;
64911900SRaymond.Chen@Sun.COM 
65011900SRaymond.Chen@Sun.COM 		goto fail;
65111900SRaymond.Chen@Sun.COM 	}
65211900SRaymond.Chen@Sun.COM 
65311900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
65411900SRaymond.Chen@Sun.COM 	if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
65511900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
65611900SRaymond.Chen@Sun.COM 		    "usbecm_m_start: fail to start_rx");
65711900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
65811900SRaymond.Chen@Sun.COM 		rval = EIO;
65911900SRaymond.Chen@Sun.COM 
66011900SRaymond.Chen@Sun.COM 		goto fail;
66111900SRaymond.Chen@Sun.COM 	}
66211900SRaymond.Chen@Sun.COM 	ecmp->ecm_mac_state = USBECM_MAC_STARTED;
66311900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
66411900SRaymond.Chen@Sun.COM 
66511900SRaymond.Chen@Sun.COM 	/* set the device to receive all multicast/broadcast pkts */
66611900SRaymond.Chen@Sun.COM 	rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
66711900SRaymond.Chen@Sun.COM 	    CDC_ECM_PKT_TYPE_DIRECTED | CDC_ECM_PKT_TYPE_ALL_MCAST |
66811900SRaymond.Chen@Sun.COM 	    CDC_ECM_PKT_TYPE_BCAST, NULL);
66911900SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
67011900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
67111900SRaymond.Chen@Sun.COM 		    "usbecm_m_start: set packet filters fail,"
67211900SRaymond.Chen@Sun.COM 		    " rval=%d, continue", rval);
67311900SRaymond.Chen@Sun.COM 	}
67411900SRaymond.Chen@Sun.COM 
67511900SRaymond.Chen@Sun.COM 	if (ECM_DS_OP_VALID(ecm_ds_start)) {
67611900SRaymond.Chen@Sun.COM 		if (ecmp->ecm_ds_ops->ecm_ds_start(ecmp) != USB_SUCCESS) {
67711900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
67811900SRaymond.Chen@Sun.COM 			    "usbecm_m_start: Can't start hardware");
67911900SRaymond.Chen@Sun.COM 
68011900SRaymond.Chen@Sun.COM 			goto fail;
68111900SRaymond.Chen@Sun.COM 		}
68211900SRaymond.Chen@Sun.COM 	}
68311900SRaymond.Chen@Sun.COM 
68411900SRaymond.Chen@Sun.COM 	usb_release_access(ecmp->ecm_ser_acc);
68511900SRaymond.Chen@Sun.COM 
68611900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
68711900SRaymond.Chen@Sun.COM 	    "usbecm_m_start: end");
68811900SRaymond.Chen@Sun.COM 
68911900SRaymond.Chen@Sun.COM 	/*
69011900SRaymond.Chen@Sun.COM 	 * To mark the link as RUNNING.
69111900SRaymond.Chen@Sun.COM 	 *
69211900SRaymond.Chen@Sun.COM 	 * ECM spec doesn't provide a way for host to get the status
69311900SRaymond.Chen@Sun.COM 	 * of the physical link initiatively. Only the device can
69411900SRaymond.Chen@Sun.COM 	 * report the link state through interrupt endpoints.
69511900SRaymond.Chen@Sun.COM 	 */
69611900SRaymond.Chen@Sun.COM 	mac_link_update(ecmp->ecm_mh, LINK_STATE_UP);
69711900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
69811900SRaymond.Chen@Sun.COM 	ecmp->ecm_stat.es_linkstate = LINK_STATE_UP;
69911900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
70011900SRaymond.Chen@Sun.COM 
70111900SRaymond.Chen@Sun.COM 	return (DDI_SUCCESS);
70211900SRaymond.Chen@Sun.COM fail:
70311900SRaymond.Chen@Sun.COM 	usb_release_access(ecmp->ecm_ser_acc);
70411900SRaymond.Chen@Sun.COM 
70511900SRaymond.Chen@Sun.COM 	return (rval);
70611900SRaymond.Chen@Sun.COM }
70711900SRaymond.Chen@Sun.COM 
70811900SRaymond.Chen@Sun.COM /*
70911900SRaymond.Chen@Sun.COM  * Stop the device.
71011900SRaymond.Chen@Sun.COM  */
71111900SRaymond.Chen@Sun.COM static void
usbecm_m_stop(void * arg)71211900SRaymond.Chen@Sun.COM usbecm_m_stop(void *arg)
71311900SRaymond.Chen@Sun.COM {
71411900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
71511900SRaymond.Chen@Sun.COM 
71611900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
71711900SRaymond.Chen@Sun.COM 	    "usbecm_m_stop: entry");
71811900SRaymond.Chen@Sun.COM 
719*11902SRaymond.Chen@Sun.COM 	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
72011900SRaymond.Chen@Sun.COM 	if (ECM_DS_OP_VALID(ecm_ds_stop)) {
72111900SRaymond.Chen@Sun.COM 		if (ecmp->ecm_ds_ops->ecm_ds_stop(ecmp) != USB_SUCCESS) {
72211900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
72311900SRaymond.Chen@Sun.COM 			    "usbecm_m_stop: fail to stop hardware");
72411900SRaymond.Chen@Sun.COM 		}
72511900SRaymond.Chen@Sun.COM 	}
72611900SRaymond.Chen@Sun.COM 
72711900SRaymond.Chen@Sun.COM 	usbecm_close_pipes(ecmp);
72811900SRaymond.Chen@Sun.COM 	usb_release_access(ecmp->ecm_ser_acc);
72911900SRaymond.Chen@Sun.COM 
73011900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
73111900SRaymond.Chen@Sun.COM 	ecmp->ecm_mac_state = USBECM_MAC_STOPPED;
73211900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
73311900SRaymond.Chen@Sun.COM 
73411900SRaymond.Chen@Sun.COM 	mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN);
73511900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
73611900SRaymond.Chen@Sun.COM 	ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN;
73711900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
73811900SRaymond.Chen@Sun.COM 
73911900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
74011900SRaymond.Chen@Sun.COM 	    "usbecm_m_stop: end");
74111900SRaymond.Chen@Sun.COM }
74211900SRaymond.Chen@Sun.COM 
74311900SRaymond.Chen@Sun.COM /*
74411900SRaymond.Chen@Sun.COM  * Change the MAC address of the device.
74511900SRaymond.Chen@Sun.COM  */
74611900SRaymond.Chen@Sun.COM /*ARGSUSED*/
74711900SRaymond.Chen@Sun.COM static int
usbecm_m_unicst(void * arg,const uint8_t * macaddr)74811900SRaymond.Chen@Sun.COM usbecm_m_unicst(void *arg, const uint8_t *macaddr)
74911900SRaymond.Chen@Sun.COM {
75011900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
75111900SRaymond.Chen@Sun.COM 	uint16_t	filter;
75211900SRaymond.Chen@Sun.COM 	int		rval;
75311900SRaymond.Chen@Sun.COM 
75411900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
75511900SRaymond.Chen@Sun.COM 	    "usbecm_m_unicst: entry");
75611900SRaymond.Chen@Sun.COM 
75711900SRaymond.Chen@Sun.COM 	/*
75811900SRaymond.Chen@Sun.COM 	 * The device doesn't support to set a different MAC addr.
75911900SRaymond.Chen@Sun.COM 	 * Hence, it's not necessary to stop the device first if
76011900SRaymond.Chen@Sun.COM 	 * the mac addresses are identical. And we just set unicast
76111900SRaymond.Chen@Sun.COM 	 * filter only.
76211900SRaymond.Chen@Sun.COM 	 */
76311900SRaymond.Chen@Sun.COM 	if (bcmp(macaddr, ecmp->ecm_srcaddr, ETHERADDRL) != 0) {
76411900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
76511900SRaymond.Chen@Sun.COM 		    "usbecm_m_unicst: not supported to set a"
76611900SRaymond.Chen@Sun.COM 		    " different MAC addr");
76711900SRaymond.Chen@Sun.COM 
76811900SRaymond.Chen@Sun.COM 		return (DDI_FAILURE);
76911900SRaymond.Chen@Sun.COM 	}
77011900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
77111900SRaymond.Chen@Sun.COM 	filter = ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_DIRECTED;
77211900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
77311900SRaymond.Chen@Sun.COM 
774*11902SRaymond.Chen@Sun.COM 	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
77511900SRaymond.Chen@Sun.COM 	rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
77611900SRaymond.Chen@Sun.COM 	    filter, NULL);
77711900SRaymond.Chen@Sun.COM 	usb_release_access(ecmp->ecm_ser_acc);
77811900SRaymond.Chen@Sun.COM 
77911900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
78011900SRaymond.Chen@Sun.COM 	    "usbecm_m_unicst: rval = %d", rval);
78111900SRaymond.Chen@Sun.COM 
78211900SRaymond.Chen@Sun.COM 	/* some devices may not support this request, we just return success */
78311900SRaymond.Chen@Sun.COM 	return (DDI_SUCCESS);
78411900SRaymond.Chen@Sun.COM }
78511900SRaymond.Chen@Sun.COM 
78611900SRaymond.Chen@Sun.COM /*
78711900SRaymond.Chen@Sun.COM  * Enable/disable multicast.
78811900SRaymond.Chen@Sun.COM  */
78911900SRaymond.Chen@Sun.COM /*ARGSUSED*/
79011900SRaymond.Chen@Sun.COM static int
usbecm_m_multicst(void * arg,boolean_t add,const uint8_t * m)79111900SRaymond.Chen@Sun.COM usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m)
79211900SRaymond.Chen@Sun.COM {
79311900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
79411900SRaymond.Chen@Sun.COM 	uint16_t	filter;
79511900SRaymond.Chen@Sun.COM 	int	rval = 0;
79611900SRaymond.Chen@Sun.COM 
79711900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
79811900SRaymond.Chen@Sun.COM 	    "usbecm_m_multicst: entry");
79911900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
80011900SRaymond.Chen@Sun.COM 
80111900SRaymond.Chen@Sun.COM 	/*
80211900SRaymond.Chen@Sun.COM 	 * To simplify the implementation, we support switching
80311900SRaymond.Chen@Sun.COM 	 * all multicast on/off feature only
80411900SRaymond.Chen@Sun.COM 	 */
80511900SRaymond.Chen@Sun.COM 	if (add == B_TRUE) {
80611900SRaymond.Chen@Sun.COM 		ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_ALL_MCAST;
80711900SRaymond.Chen@Sun.COM 	} else {
80811900SRaymond.Chen@Sun.COM 		ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_ALL_MCAST;
80911900SRaymond.Chen@Sun.COM 	}
81011900SRaymond.Chen@Sun.COM 	filter = ecmp->ecm_pkt_flt;
81111900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
81211900SRaymond.Chen@Sun.COM 
813*11902SRaymond.Chen@Sun.COM 	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
81411900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_compatibility &&
81511900SRaymond.Chen@Sun.COM 	    (ecmp->ecm_desc.wNumberMCFilters & 0x7F)) {
81611900SRaymond.Chen@Sun.COM 	/* Device supports SetEthernetMulticastFilters request */
81711900SRaymond.Chen@Sun.COM 		rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
81811900SRaymond.Chen@Sun.COM 		    filter, NULL);
81911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
82011900SRaymond.Chen@Sun.COM 		    "usbecm_m_multicst: rval = %d", rval);
82111900SRaymond.Chen@Sun.COM 	}
82211900SRaymond.Chen@Sun.COM 	usb_release_access(ecmp->ecm_ser_acc);
82311900SRaymond.Chen@Sun.COM 
82411900SRaymond.Chen@Sun.COM 	/* some devices may not support this request, we just return success */
82511900SRaymond.Chen@Sun.COM 	return (DDI_SUCCESS);
82611900SRaymond.Chen@Sun.COM }
82711900SRaymond.Chen@Sun.COM 
82811900SRaymond.Chen@Sun.COM /*
82911900SRaymond.Chen@Sun.COM  * Enable/disable promiscuous mode.
83011900SRaymond.Chen@Sun.COM  */
83111900SRaymond.Chen@Sun.COM static int
usbecm_m_promisc(void * arg,boolean_t on)83211900SRaymond.Chen@Sun.COM usbecm_m_promisc(void *arg, boolean_t on)
83311900SRaymond.Chen@Sun.COM {
83411900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
83511900SRaymond.Chen@Sun.COM 	uint16_t	filter;
83611900SRaymond.Chen@Sun.COM 	int		rval;
83711900SRaymond.Chen@Sun.COM 
83811900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
83911900SRaymond.Chen@Sun.COM 	    "usbecm_m_promisc: entry");
84011900SRaymond.Chen@Sun.COM 
84111900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
84211900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
84311900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
84411900SRaymond.Chen@Sun.COM 		    "usbecm_m_promisc: device not ONLINE");
84511900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
84611900SRaymond.Chen@Sun.COM 
84711900SRaymond.Chen@Sun.COM 		return (DDI_FAILURE);
84811900SRaymond.Chen@Sun.COM 	}
84911900SRaymond.Chen@Sun.COM 
85011900SRaymond.Chen@Sun.COM 
85111900SRaymond.Chen@Sun.COM 	if (on == B_TRUE) {
85211900SRaymond.Chen@Sun.COM 		ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_PROMISC;
85311900SRaymond.Chen@Sun.COM 	} else {
85411900SRaymond.Chen@Sun.COM 		ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_PROMISC;
85511900SRaymond.Chen@Sun.COM 	}
85611900SRaymond.Chen@Sun.COM 	filter = ecmp->ecm_pkt_flt;
85711900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
85811900SRaymond.Chen@Sun.COM 
859*11902SRaymond.Chen@Sun.COM 	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
86011900SRaymond.Chen@Sun.COM 	rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
86111900SRaymond.Chen@Sun.COM 	    filter, NULL);
86211900SRaymond.Chen@Sun.COM 	usb_release_access(ecmp->ecm_ser_acc);
86311900SRaymond.Chen@Sun.COM 
86411900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
86511900SRaymond.Chen@Sun.COM 	    "usbecm_m_promisc: rval=%d", rval);
86611900SRaymond.Chen@Sun.COM 
86711900SRaymond.Chen@Sun.COM 	/*
86811900SRaymond.Chen@Sun.COM 	 * devices may not support this request, we just
86911900SRaymond.Chen@Sun.COM 	 * return success to let upper layer to do further
87011900SRaymond.Chen@Sun.COM 	 * operation.
87111900SRaymond.Chen@Sun.COM 	 */
87211900SRaymond.Chen@Sun.COM 	return (DDI_SUCCESS);
87311900SRaymond.Chen@Sun.COM }
87411900SRaymond.Chen@Sun.COM 
87511900SRaymond.Chen@Sun.COM /*
87611900SRaymond.Chen@Sun.COM  * IOCTL request: Does not do anything. Will be enhanced
87711900SRaymond.Chen@Sun.COM  *	in future.
87811900SRaymond.Chen@Sun.COM  */
87911900SRaymond.Chen@Sun.COM static void
usbecm_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)88011900SRaymond.Chen@Sun.COM usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
88111900SRaymond.Chen@Sun.COM {
88211900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
88311900SRaymond.Chen@Sun.COM 	struct iocblk   *iocp;
88411900SRaymond.Chen@Sun.COM 	int cmd;
88511900SRaymond.Chen@Sun.COM 
88611900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
88711900SRaymond.Chen@Sun.COM 	    "usbecm_m_ioctl: entry");
88811900SRaymond.Chen@Sun.COM 
88911900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
89011900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
89111900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
89211900SRaymond.Chen@Sun.COM 		    "usbecm_m_ioctl: device not ONLINE");
89311900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
89411900SRaymond.Chen@Sun.COM 
89511900SRaymond.Chen@Sun.COM 		miocnak(wq, mp, 0, EIO);
89611900SRaymond.Chen@Sun.COM 
89711900SRaymond.Chen@Sun.COM 		return;
89811900SRaymond.Chen@Sun.COM 	}
89911900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
90011900SRaymond.Chen@Sun.COM 
90111900SRaymond.Chen@Sun.COM 	iocp = (void *)mp->b_rptr;
90211900SRaymond.Chen@Sun.COM 	iocp->ioc_error = 0;
90311900SRaymond.Chen@Sun.COM 	cmd = iocp->ioc_cmd;
90411900SRaymond.Chen@Sun.COM 
905*11902SRaymond.Chen@Sun.COM 	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
90611900SRaymond.Chen@Sun.COM 
90711900SRaymond.Chen@Sun.COM 	switch (cmd) {
90811900SRaymond.Chen@Sun.COM 	default:
90911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
91011900SRaymond.Chen@Sun.COM 		    "unknown cmd 0x%x", cmd);
91111900SRaymond.Chen@Sun.COM 		usb_release_access(ecmp->ecm_ser_acc);
91211900SRaymond.Chen@Sun.COM 		miocnak(wq, mp, 0, EINVAL);
91311900SRaymond.Chen@Sun.COM 
91411900SRaymond.Chen@Sun.COM 		return;
91511900SRaymond.Chen@Sun.COM 	}
91611900SRaymond.Chen@Sun.COM }
91711900SRaymond.Chen@Sun.COM 
91811900SRaymond.Chen@Sun.COM /*
91911900SRaymond.Chen@Sun.COM  * callback functions for get/set properties
92011900SRaymond.Chen@Sun.COM  *	Does not do anything. Will be enhanced to
92111900SRaymond.Chen@Sun.COM  *	support set/get properties in future.
92211900SRaymond.Chen@Sun.COM  */
92311900SRaymond.Chen@Sun.COM /*ARGSUSED*/
92411900SRaymond.Chen@Sun.COM 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)92511900SRaymond.Chen@Sun.COM usbecm_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
92611900SRaymond.Chen@Sun.COM     uint_t wldp_length, const void *wldp_buf)
92711900SRaymond.Chen@Sun.COM {
92811900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
92911900SRaymond.Chen@Sun.COM 	int err = ENOTSUP;
93011900SRaymond.Chen@Sun.COM 
93111900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
93211900SRaymond.Chen@Sun.COM 	    "usbecm_m_setprop: entry");
93311900SRaymond.Chen@Sun.COM 
93411900SRaymond.Chen@Sun.COM 	return (err);
93511900SRaymond.Chen@Sun.COM }
93611900SRaymond.Chen@Sun.COM 
93711900SRaymond.Chen@Sun.COM /*ARGSUSED*/
usbecm_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)93811900SRaymond.Chen@Sun.COM static int usbecm_m_getprop(void *arg, const char *pr_name,
93911900SRaymond.Chen@Sun.COM     mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf)
94011900SRaymond.Chen@Sun.COM {
94111900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
94211900SRaymond.Chen@Sun.COM 	int err = ENOTSUP;
94311900SRaymond.Chen@Sun.COM 
94411900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
94511900SRaymond.Chen@Sun.COM 	    "usbecm_m_getprop: entry");
94611900SRaymond.Chen@Sun.COM 
94711900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
94811900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
94911900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
95011900SRaymond.Chen@Sun.COM 
95111900SRaymond.Chen@Sun.COM 		return (EIO);
95211900SRaymond.Chen@Sun.COM 	}
95311900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
95411900SRaymond.Chen@Sun.COM 
95511900SRaymond.Chen@Sun.COM 	return (err);
95611900SRaymond.Chen@Sun.COM }
95711900SRaymond.Chen@Sun.COM 
95811900SRaymond.Chen@Sun.COM /*
95911900SRaymond.Chen@Sun.COM  * Transmit a data frame.
96011900SRaymond.Chen@Sun.COM  */
96111900SRaymond.Chen@Sun.COM static mblk_t *
usbecm_m_tx(void * arg,mblk_t * mp)96211900SRaymond.Chen@Sun.COM usbecm_m_tx(void *arg, mblk_t *mp)
96311900SRaymond.Chen@Sun.COM {
96411900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)arg;
96511900SRaymond.Chen@Sun.COM 	mblk_t *next;
96611900SRaymond.Chen@Sun.COM 	int count = 0;
96711900SRaymond.Chen@Sun.COM 
96811900SRaymond.Chen@Sun.COM 	ASSERT(mp != NULL);
96911900SRaymond.Chen@Sun.COM 
97011900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
97111900SRaymond.Chen@Sun.COM 	    "usbecm_m_tx: entry");
97211900SRaymond.Chen@Sun.COM 
97311900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
97411900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
97511900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
97611900SRaymond.Chen@Sun.COM 		    "usbecm_m_tx: device not ONLINE");
97711900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
97811900SRaymond.Chen@Sun.COM 
97911900SRaymond.Chen@Sun.COM 		return (mp);
98011900SRaymond.Chen@Sun.COM 	}
98111900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
98211900SRaymond.Chen@Sun.COM 
983*11902SRaymond.Chen@Sun.COM 	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
98411900SRaymond.Chen@Sun.COM 
98511900SRaymond.Chen@Sun.COM 	/*
98611900SRaymond.Chen@Sun.COM 	 * To make use of the device maximum capability,
98711900SRaymond.Chen@Sun.COM 	 * concatenate msg blocks in a msg to ETHERMAX length.
98811900SRaymond.Chen@Sun.COM 	 */
98911900SRaymond.Chen@Sun.COM 	while (mp != NULL) {
99011900SRaymond.Chen@Sun.COM 		next = mp->b_next;
99111900SRaymond.Chen@Sun.COM 		mp->b_next = NULL;
99211900SRaymond.Chen@Sun.COM 
99311900SRaymond.Chen@Sun.COM 		if (usbecm_send_data(ecmp, mp) != DDI_SUCCESS) {
99411900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
99511900SRaymond.Chen@Sun.COM 			    "usbecm_m_tx: send data fail");
99611900SRaymond.Chen@Sun.COM 
99711900SRaymond.Chen@Sun.COM 			/* failure statistics */
99811900SRaymond.Chen@Sun.COM 			mutex_enter(&ecmp->ecm_mutex);
99911900SRaymond.Chen@Sun.COM 			ecmp->ecm_stat.es_oerrors++;
100011900SRaymond.Chen@Sun.COM 			mutex_exit(&ecmp->ecm_mutex);
100111900SRaymond.Chen@Sun.COM 
100211900SRaymond.Chen@Sun.COM 			mp->b_next = next;
100311900SRaymond.Chen@Sun.COM 
100411900SRaymond.Chen@Sun.COM 			break;
100511900SRaymond.Chen@Sun.COM 		}
100611900SRaymond.Chen@Sun.COM 
100711900SRaymond.Chen@Sun.COM 		/*
100811900SRaymond.Chen@Sun.COM 		 * To make it simple, we count all packets, no matter
100911900SRaymond.Chen@Sun.COM 		 * the device supports ethernet statistics or not.
101011900SRaymond.Chen@Sun.COM 		 */
101111900SRaymond.Chen@Sun.COM 		mutex_enter(&ecmp->ecm_mutex);
101211900SRaymond.Chen@Sun.COM 		ecmp->ecm_stat.es_opackets++;
101311900SRaymond.Chen@Sun.COM 		ecmp->ecm_stat.es_obytes += MBLKL(mp);
101411900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
101511900SRaymond.Chen@Sun.COM 
101611900SRaymond.Chen@Sun.COM 		freemsg(mp); /* free this msg upon success */
101711900SRaymond.Chen@Sun.COM 
101811900SRaymond.Chen@Sun.COM 		mp = next;
101911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
102011900SRaymond.Chen@Sun.COM 		    "usbecm_m_tx: %d msgs processed", ++count);
102111900SRaymond.Chen@Sun.COM 	}
102211900SRaymond.Chen@Sun.COM 
102311900SRaymond.Chen@Sun.COM 	usb_release_access(ecmp->ecm_ser_acc);
102411900SRaymond.Chen@Sun.COM 
102511900SRaymond.Chen@Sun.COM 	return (mp);
102611900SRaymond.Chen@Sun.COM }
102711900SRaymond.Chen@Sun.COM 
102811900SRaymond.Chen@Sun.COM /*
102911900SRaymond.Chen@Sun.COM  * usbecm_bulkin_cb:
103011900SRaymond.Chen@Sun.COM  *	Bulk In regular and exeception callback;
103111900SRaymond.Chen@Sun.COM  *	USBA framework will call this callback
103211900SRaymond.Chen@Sun.COM  *	after deal with bulkin request.
103311900SRaymond.Chen@Sun.COM  */
103411900SRaymond.Chen@Sun.COM /*ARGSUSED*/
103511900SRaymond.Chen@Sun.COM static void
usbecm_bulkin_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)103611900SRaymond.Chen@Sun.COM usbecm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
103711900SRaymond.Chen@Sun.COM {
103811900SRaymond.Chen@Sun.COM 	usbecm_state_t	*ecmp = (usbecm_state_t *)req->bulk_client_private;
103911900SRaymond.Chen@Sun.COM 	mblk_t		*data, *mp;
104011900SRaymond.Chen@Sun.COM 	int		data_len;
104111900SRaymond.Chen@Sun.COM 	int		max_pkt_size = ecmp->ecm_bulkin_sz;
104211900SRaymond.Chen@Sun.COM 
104311900SRaymond.Chen@Sun.COM 	data = req->bulk_data;
104411900SRaymond.Chen@Sun.COM 	data_len = (data) ? MBLKL(data) : 0;
104511900SRaymond.Chen@Sun.COM 
104611900SRaymond.Chen@Sun.COM 	ASSERT(data->b_cont == NULL);
104711900SRaymond.Chen@Sun.COM 
104811900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
104911900SRaymond.Chen@Sun.COM 
105011900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh,
105111900SRaymond.Chen@Sun.COM 	    "usbecm_bulkin_cb: state=%d, len=%d", ecmp->ecm_bulkin_state,
105211900SRaymond.Chen@Sun.COM 	    data_len);
105311900SRaymond.Chen@Sun.COM 
105411900SRaymond.Chen@Sun.COM 	/*
105511900SRaymond.Chen@Sun.COM 	 * may receive a zero length packet according
105611900SRaymond.Chen@Sun.COM 	 * to USB short packet semantics
105711900SRaymond.Chen@Sun.COM 	 */
105811900SRaymond.Chen@Sun.COM 	if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) &&
105911900SRaymond.Chen@Sun.COM 	    (req->bulk_completion_reason == USB_CR_OK)) {
106011900SRaymond.Chen@Sun.COM 		if (data_len) {
106111900SRaymond.Chen@Sun.COM 			if (ecmp->ecm_rcv_queue == NULL) {
106211900SRaymond.Chen@Sun.COM 				ecmp->ecm_rcv_queue = data;
106311900SRaymond.Chen@Sun.COM 			} else {
106411900SRaymond.Chen@Sun.COM 				if ((msgsize(ecmp->ecm_rcv_queue) + data_len)
106511900SRaymond.Chen@Sun.COM 				    > ETHERMAX) {
106611900SRaymond.Chen@Sun.COM 				/*
106711900SRaymond.Chen@Sun.COM 				 * Exceed the ethernet maximum length, we think
106811900SRaymond.Chen@Sun.COM 				 * something is wrong with this frame and hence
106911900SRaymond.Chen@Sun.COM 				 * free older data. Accept new data instead.
107011900SRaymond.Chen@Sun.COM 				 */
107111900SRaymond.Chen@Sun.COM 					freemsg(ecmp->ecm_rcv_queue);
107211900SRaymond.Chen@Sun.COM 					ecmp->ecm_rcv_queue = data;
107311900SRaymond.Chen@Sun.COM 				} else {
107411900SRaymond.Chen@Sun.COM 					linkb(ecmp->ecm_rcv_queue, data);
107511900SRaymond.Chen@Sun.COM 				}
107611900SRaymond.Chen@Sun.COM 			}
107711900SRaymond.Chen@Sun.COM 		} else {
107811900SRaymond.Chen@Sun.COM 		/*
107911900SRaymond.Chen@Sun.COM 		 * Do not put zero length packet to receive queue.
108011900SRaymond.Chen@Sun.COM 		 * Otherwise, msgpullup will dupmsg() a zero length
108111900SRaymond.Chen@Sun.COM 		 * mblk, which will cause memleaks.
108211900SRaymond.Chen@Sun.COM 		 */
108311900SRaymond.Chen@Sun.COM 			freemsg(data);
108411900SRaymond.Chen@Sun.COM 		}
108511900SRaymond.Chen@Sun.COM 
108611900SRaymond.Chen@Sun.COM 		/*
108711900SRaymond.Chen@Sun.COM 		 * ECM V1.2, section 3.3.1, a short(including zero length)
108811900SRaymond.Chen@Sun.COM 		 * packet signifies end of frame. We can submit this frame
108911900SRaymond.Chen@Sun.COM 		 * to upper layer now.
109011900SRaymond.Chen@Sun.COM 		 */
109111900SRaymond.Chen@Sun.COM 		if ((data_len < max_pkt_size) &&
109211900SRaymond.Chen@Sun.COM 		    (msgsize(ecmp->ecm_rcv_queue) > 0)) {
109311900SRaymond.Chen@Sun.COM 			mp = msgpullup(ecmp->ecm_rcv_queue, -1);
109411900SRaymond.Chen@Sun.COM 			freemsg(ecmp->ecm_rcv_queue);
109511900SRaymond.Chen@Sun.COM 			ecmp->ecm_rcv_queue = NULL;
109611900SRaymond.Chen@Sun.COM 
109711900SRaymond.Chen@Sun.COM 			ecmp->ecm_stat.es_ipackets++;
109811900SRaymond.Chen@Sun.COM 			ecmp->ecm_stat.es_ibytes += msgsize(mp);
109911900SRaymond.Chen@Sun.COM 			if (mp && (mp->b_rptr[0] & 0x01)) {
110011900SRaymond.Chen@Sun.COM 				if (bcmp(mp->b_rptr, usbecm_broadcast,
110111900SRaymond.Chen@Sun.COM 				    ETHERADDRL) != 0) {
110211900SRaymond.Chen@Sun.COM 					ecmp->ecm_stat.es_multircv++;
110311900SRaymond.Chen@Sun.COM 				} else {
110411900SRaymond.Chen@Sun.COM 					ecmp->ecm_stat.es_brdcstrcv++;
110511900SRaymond.Chen@Sun.COM 				}
110611900SRaymond.Chen@Sun.COM 			}
110711900SRaymond.Chen@Sun.COM 
110811900SRaymond.Chen@Sun.COM 			if (mp) {
110911900SRaymond.Chen@Sun.COM 				mutex_exit(&ecmp->ecm_mutex);
111011900SRaymond.Chen@Sun.COM 				mac_rx(ecmp->ecm_mh, NULL, mp);
111111900SRaymond.Chen@Sun.COM 				mutex_enter(&ecmp->ecm_mutex);
111211900SRaymond.Chen@Sun.COM 			}
111311900SRaymond.Chen@Sun.COM 		}
111411900SRaymond.Chen@Sun.COM 
111511900SRaymond.Chen@Sun.COM 		/* prevent USBA from freeing data along with the request */
111611900SRaymond.Chen@Sun.COM 		req->bulk_data = NULL;
111711900SRaymond.Chen@Sun.COM 	} else if (req->bulk_completion_reason != USB_CR_OK) {
111811900SRaymond.Chen@Sun.COM 		ecmp->ecm_stat.es_ierrors++;
111911900SRaymond.Chen@Sun.COM 	}
112011900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
112111900SRaymond.Chen@Sun.COM 
112211900SRaymond.Chen@Sun.COM 	usb_free_bulk_req(req);
112311900SRaymond.Chen@Sun.COM 
112411900SRaymond.Chen@Sun.COM 	/* receive more */
112511900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
112611900SRaymond.Chen@Sun.COM 	if (((ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) ||
112711900SRaymond.Chen@Sun.COM 	    (ecmp->ecm_bulkin_state == USBECM_PIPE_IDLE)) &&
112811900SRaymond.Chen@Sun.COM 	    (ecmp->ecm_dev_state == USB_DEV_ONLINE)) {
112911900SRaymond.Chen@Sun.COM 		if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
113011900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
113111900SRaymond.Chen@Sun.COM 			    "usbecm_bulkin_cb: restart rx fail "
113211900SRaymond.Chen@Sun.COM 			    "ecmp_state = %d", ecmp->ecm_bulkin_state);
113311900SRaymond.Chen@Sun.COM 		}
113411900SRaymond.Chen@Sun.COM 	} else if (ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) {
113511900SRaymond.Chen@Sun.COM 		ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
113611900SRaymond.Chen@Sun.COM 	}
113711900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
113811900SRaymond.Chen@Sun.COM }
113911900SRaymond.Chen@Sun.COM 
114011900SRaymond.Chen@Sun.COM /*
114111900SRaymond.Chen@Sun.COM  * usbsecm_rx_start:
114211900SRaymond.Chen@Sun.COM  *	start data receipt
114311900SRaymond.Chen@Sun.COM  */
114411900SRaymond.Chen@Sun.COM static int
usbecm_rx_start(usbecm_state_t * ecmp)114511900SRaymond.Chen@Sun.COM usbecm_rx_start(usbecm_state_t *ecmp)
114611900SRaymond.Chen@Sun.COM {
114711900SRaymond.Chen@Sun.COM 	usb_bulk_req_t	*br;
114811900SRaymond.Chen@Sun.COM 	int		rval = USB_FAILURE;
114911900SRaymond.Chen@Sun.COM 	int		data_len;
115011900SRaymond.Chen@Sun.COM 
115111900SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&ecmp->ecm_mutex));
115211900SRaymond.Chen@Sun.COM 
115311900SRaymond.Chen@Sun.COM 	DTRACE_PROBE2(usbecm_rx__start, int, ecmp->ecm_xfer_sz,
115411900SRaymond.Chen@Sun.COM 	    int, ecmp->ecm_bulkin_sz);
115511900SRaymond.Chen@Sun.COM 
115611900SRaymond.Chen@Sun.COM 	ecmp->ecm_bulkin_state = USBECM_PIPE_BUSY;
115711900SRaymond.Chen@Sun.COM 	data_len = ecmp->ecm_bulkin_sz;
115811900SRaymond.Chen@Sun.COM 
115911900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
116011900SRaymond.Chen@Sun.COM 	br = usb_alloc_bulk_req(ecmp->ecm_dip, data_len, USB_FLAGS_SLEEP);
116111900SRaymond.Chen@Sun.COM 	if (br == NULL) {
116211900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
116311900SRaymond.Chen@Sun.COM 		    "usbsecm_rx_start: allocate bulk request failed");
116411900SRaymond.Chen@Sun.COM 
116511900SRaymond.Chen@Sun.COM 		mutex_enter(&ecmp->ecm_mutex);
116611900SRaymond.Chen@Sun.COM 
116711900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
116811900SRaymond.Chen@Sun.COM 	}
116911900SRaymond.Chen@Sun.COM 	/* initialize bulk in request. */
117011900SRaymond.Chen@Sun.COM 	br->bulk_len = data_len;
117111900SRaymond.Chen@Sun.COM 	br->bulk_timeout = 0;
117211900SRaymond.Chen@Sun.COM 	br->bulk_cb = usbecm_bulkin_cb;
117311900SRaymond.Chen@Sun.COM 	br->bulk_exc_cb = usbecm_bulkin_cb;
117411900SRaymond.Chen@Sun.COM 	br->bulk_client_private = (usb_opaque_t)ecmp;
117511900SRaymond.Chen@Sun.COM 	br->bulk_attributes = USB_ATTRS_AUTOCLEARING
117611900SRaymond.Chen@Sun.COM 	    | USB_ATTRS_SHORT_XFER_OK;
117711900SRaymond.Chen@Sun.COM 
117811900SRaymond.Chen@Sun.COM 	rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkin_ph, br, 0);
117911900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
118011900SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
118111900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
118211900SRaymond.Chen@Sun.COM 		    "usbsecm_rx_start: bulk transfer failed %d", rval);
118311900SRaymond.Chen@Sun.COM 		usb_free_bulk_req(br);
118411900SRaymond.Chen@Sun.COM 		ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
118511900SRaymond.Chen@Sun.COM 	}
118611900SRaymond.Chen@Sun.COM 
118711900SRaymond.Chen@Sun.COM 	return (rval);
118811900SRaymond.Chen@Sun.COM }
118911900SRaymond.Chen@Sun.COM 
119011900SRaymond.Chen@Sun.COM /*
119111900SRaymond.Chen@Sun.COM  * usbecm_bulkout_cb:
119211900SRaymond.Chen@Sun.COM  *	Bulk Out regular and exeception callback;
119311900SRaymond.Chen@Sun.COM  *	USBA framework will call this callback function
119411900SRaymond.Chen@Sun.COM  *	after deal with bulkout request.
119511900SRaymond.Chen@Sun.COM  */
119611900SRaymond.Chen@Sun.COM /*ARGSUSED*/
119711900SRaymond.Chen@Sun.COM static void
usbecm_bulkout_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)119811900SRaymond.Chen@Sun.COM usbecm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
119911900SRaymond.Chen@Sun.COM {
120011900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private;
120111900SRaymond.Chen@Sun.COM 	int		data_len;
120211900SRaymond.Chen@Sun.COM 	boolean_t	need_update = B_FALSE;
120311900SRaymond.Chen@Sun.COM 
120411900SRaymond.Chen@Sun.COM 	data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0;
120511900SRaymond.Chen@Sun.COM 
120611900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh,
120711900SRaymond.Chen@Sun.COM 	    "usbecm_bulkout_cb: data_len = %d, cr=%d", data_len,
120811900SRaymond.Chen@Sun.COM 	    req->bulk_completion_reason);
120911900SRaymond.Chen@Sun.COM 
121011900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
121111900SRaymond.Chen@Sun.COM 	if ((data_len > 0) && (ecmp->ecm_tx_cnt > 0)) {
121211900SRaymond.Chen@Sun.COM 		if (ecmp->ecm_tx_cnt == usbecm_tx_max) {
121311900SRaymond.Chen@Sun.COM 			need_update = B_TRUE;
121411900SRaymond.Chen@Sun.COM 		}
121511900SRaymond.Chen@Sun.COM 		ecmp->ecm_tx_cnt--;
121611900SRaymond.Chen@Sun.COM 	}
121711900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
121811900SRaymond.Chen@Sun.COM 
121911900SRaymond.Chen@Sun.COM 	if (req->bulk_completion_reason && (data_len > 0)) {
122011900SRaymond.Chen@Sun.COM 		mutex_enter(&ecmp->ecm_mutex);
122111900SRaymond.Chen@Sun.COM 		ecmp->ecm_stat.es_oerrors++;
122211900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
122311900SRaymond.Chen@Sun.COM 
122411900SRaymond.Chen@Sun.COM 		need_update = B_TRUE;
122511900SRaymond.Chen@Sun.COM 	}
122611900SRaymond.Chen@Sun.COM 
122711900SRaymond.Chen@Sun.COM 	/*
122811900SRaymond.Chen@Sun.COM 	 * notify MAC layer to retransfer the failed packet
122911900SRaymond.Chen@Sun.COM 	 * Or notity MAC that we have more buffer now.
123011900SRaymond.Chen@Sun.COM 	 */
123111900SRaymond.Chen@Sun.COM 	if (need_update) {
123211900SRaymond.Chen@Sun.COM 		mac_tx_update(ecmp->ecm_mh);
123311900SRaymond.Chen@Sun.COM 	}
123411900SRaymond.Chen@Sun.COM 
123511900SRaymond.Chen@Sun.COM 	usb_free_bulk_req(req);
123611900SRaymond.Chen@Sun.COM }
123711900SRaymond.Chen@Sun.COM 
123811900SRaymond.Chen@Sun.COM static int
usbecm_send_data(usbecm_state_t * ecmp,mblk_t * data)123911900SRaymond.Chen@Sun.COM usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data)
124011900SRaymond.Chen@Sun.COM {
124111900SRaymond.Chen@Sun.COM 	usb_bulk_req_t	*br;
124211900SRaymond.Chen@Sun.COM 	int		rval = USB_FAILURE;
124311900SRaymond.Chen@Sun.COM 	int		data_len = MBLKL(data);
124411900SRaymond.Chen@Sun.COM 	int		max_pkt_size;
124511900SRaymond.Chen@Sun.COM 	mblk_t		*new_data = NULL;
124611900SRaymond.Chen@Sun.COM 	int		new_data_len = 0;
124711900SRaymond.Chen@Sun.COM 
124811900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
124911900SRaymond.Chen@Sun.COM 	    "usbecm_send_data: length = %d, total len=%d",
125011900SRaymond.Chen@Sun.COM 	    data_len, (int)msgdsize(data));
125111900SRaymond.Chen@Sun.COM 
125211900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
125311900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_tx_cnt >= usbecm_tx_max) {
125411900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
125511900SRaymond.Chen@Sun.COM 		    "usbecm_send_data: (%d) exceeds TX max queue length",
125611900SRaymond.Chen@Sun.COM 		    ecmp->ecm_tx_cnt);
125711900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
125811900SRaymond.Chen@Sun.COM 
125911900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
126011900SRaymond.Chen@Sun.COM 	}
126111900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
126211900SRaymond.Chen@Sun.COM 
126311900SRaymond.Chen@Sun.COM 	data_len = msgsize(data);
126411900SRaymond.Chen@Sun.COM 	if (data_len > ETHERMAX) {
126511900SRaymond.Chen@Sun.COM 		mutex_enter(&ecmp->ecm_mutex);
126611900SRaymond.Chen@Sun.COM 		ecmp->ecm_stat.es_macxmt_err++;
126711900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
126811900SRaymond.Chen@Sun.COM 
126911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
127011900SRaymond.Chen@Sun.COM 		    "usbecm_send_data: packet too long, %d", data_len);
127111900SRaymond.Chen@Sun.COM 
127211900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
127311900SRaymond.Chen@Sun.COM 	}
127411900SRaymond.Chen@Sun.COM 
127511900SRaymond.Chen@Sun.COM 	if (data_len < ETHERMIN) {
127611900SRaymond.Chen@Sun.COM 		mblk_t *tmp;
127711900SRaymond.Chen@Sun.COM 
127811900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
127911900SRaymond.Chen@Sun.COM 		    "usbecm_send_data: short packet, padding to ETHERMIN");
128011900SRaymond.Chen@Sun.COM 
128111900SRaymond.Chen@Sun.COM 		new_data_len = ETHERMIN;
128211900SRaymond.Chen@Sun.COM 		if ((new_data = allocb(new_data_len, 0)) == NULL) {
128311900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
128411900SRaymond.Chen@Sun.COM 			    "usbecm_send_data: fail to allocb");
128511900SRaymond.Chen@Sun.COM 
128611900SRaymond.Chen@Sun.COM 			return (USB_FAILURE);
128711900SRaymond.Chen@Sun.COM 		}
128811900SRaymond.Chen@Sun.COM 		bzero(new_data->b_wptr, new_data_len);
128911900SRaymond.Chen@Sun.COM 		for (tmp = data; tmp != NULL; tmp = tmp->b_cont) {
129011900SRaymond.Chen@Sun.COM 			bcopy(tmp->b_rptr, new_data->b_wptr, MBLKL(tmp));
129111900SRaymond.Chen@Sun.COM 			new_data->b_wptr += MBLKL(tmp);
129211900SRaymond.Chen@Sun.COM 		}
129311900SRaymond.Chen@Sun.COM 
129411900SRaymond.Chen@Sun.COM 		new_data->b_wptr = new_data->b_rptr + new_data_len;
129511900SRaymond.Chen@Sun.COM 	}
129611900SRaymond.Chen@Sun.COM 
129711900SRaymond.Chen@Sun.COM 	br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
129811900SRaymond.Chen@Sun.COM 	if (br == NULL) {
129911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
130011900SRaymond.Chen@Sun.COM 		    "usbecm_send_data: alloc req failed.");
130111900SRaymond.Chen@Sun.COM 
130211900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
130311900SRaymond.Chen@Sun.COM 	}
130411900SRaymond.Chen@Sun.COM 
130511900SRaymond.Chen@Sun.COM 	/* initialize the bulk out request */
130611900SRaymond.Chen@Sun.COM 	if (new_data) {
130711900SRaymond.Chen@Sun.COM 		br->bulk_data = msgpullup(new_data, -1); /* msg allocated! */
130811900SRaymond.Chen@Sun.COM 		br->bulk_len = new_data_len;
130911900SRaymond.Chen@Sun.COM 	} else {
131011900SRaymond.Chen@Sun.COM 		br->bulk_data = msgpullup(data, -1); /* msg allocated! */
131111900SRaymond.Chen@Sun.COM 		br->bulk_len = data_len;
131211900SRaymond.Chen@Sun.COM 	}
131311900SRaymond.Chen@Sun.COM 
131411900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
131511900SRaymond.Chen@Sun.COM 	    "usbecm_send_data: bulk_len = %d", br->bulk_len);
131611900SRaymond.Chen@Sun.COM 
131711900SRaymond.Chen@Sun.COM 	br->bulk_timeout = USBECM_BULKOUT_TIMEOUT;
131811900SRaymond.Chen@Sun.COM 	br->bulk_cb = usbecm_bulkout_cb;
131911900SRaymond.Chen@Sun.COM 	br->bulk_exc_cb = usbecm_bulkout_cb;
132011900SRaymond.Chen@Sun.COM 	br->bulk_client_private = (usb_opaque_t)ecmp;
132111900SRaymond.Chen@Sun.COM 	br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
132211900SRaymond.Chen@Sun.COM 
132311900SRaymond.Chen@Sun.COM 	if (br->bulk_data != NULL) {
132411900SRaymond.Chen@Sun.COM 		if (br->bulk_data->b_rptr[0] & 0x01) {
132511900SRaymond.Chen@Sun.COM 			mutex_enter(&ecmp->ecm_mutex);
132611900SRaymond.Chen@Sun.COM 			if (bcmp(br->bulk_data->b_rptr, usbecm_broadcast,
132711900SRaymond.Chen@Sun.COM 			    ETHERADDRL) != 0) {
132811900SRaymond.Chen@Sun.COM 				ecmp->ecm_stat.es_multixmt++;
132911900SRaymond.Chen@Sun.COM 			} else {
133011900SRaymond.Chen@Sun.COM 				ecmp->ecm_stat.es_brdcstxmt++;
133111900SRaymond.Chen@Sun.COM 			}
133211900SRaymond.Chen@Sun.COM 			mutex_exit(&ecmp->ecm_mutex);
133311900SRaymond.Chen@Sun.COM 		}
133411900SRaymond.Chen@Sun.COM 		rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0);
133511900SRaymond.Chen@Sun.COM 	}
133611900SRaymond.Chen@Sun.COM 
133711900SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
133811900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
133911900SRaymond.Chen@Sun.COM 		    "usbecm_send_data: Send Data failed.");
134011900SRaymond.Chen@Sun.COM 
134111900SRaymond.Chen@Sun.COM 		/*
134211900SRaymond.Chen@Sun.COM 		 * br->bulk_data should be freed because we allocated
134311900SRaymond.Chen@Sun.COM 		 * it in this function.
134411900SRaymond.Chen@Sun.COM 		 */
134511900SRaymond.Chen@Sun.COM 		usb_free_bulk_req(br);
134611900SRaymond.Chen@Sun.COM 
134711900SRaymond.Chen@Sun.COM 	} else {
134811900SRaymond.Chen@Sun.COM 		mutex_enter(&ecmp->ecm_mutex);
134911900SRaymond.Chen@Sun.COM 		ecmp->ecm_tx_cnt++;
135011900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
135111900SRaymond.Chen@Sun.COM 
135211900SRaymond.Chen@Sun.COM 		/*
135311900SRaymond.Chen@Sun.COM 		 * ECM V1.2, section 3.3.1, a short(including zero length)
135411900SRaymond.Chen@Sun.COM 		 * packet signifies end of frame. We should send a zero length
135511900SRaymond.Chen@Sun.COM 		 * packet to device if the total data lenght is multiple of
135611900SRaymond.Chen@Sun.COM 		 * bulkout endpoint's max packet size.
135711900SRaymond.Chen@Sun.COM 		 */
135811900SRaymond.Chen@Sun.COM 		max_pkt_size = ecmp->ecm_bulk_out_ep->ep_descr.wMaxPacketSize;
135911900SRaymond.Chen@Sun.COM 		if ((data_len % max_pkt_size) == 0) {
136011900SRaymond.Chen@Sun.COM 			if ((rval = usbecm_send_zero_data(ecmp))
136111900SRaymond.Chen@Sun.COM 			    != USB_SUCCESS) {
136211900SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
136311900SRaymond.Chen@Sun.COM 				    "usbecm_send_data: fail to send padding");
136411900SRaymond.Chen@Sun.COM 			}
136511900SRaymond.Chen@Sun.COM 		}
136611900SRaymond.Chen@Sun.COM 	}
136711900SRaymond.Chen@Sun.COM 
136811900SRaymond.Chen@Sun.COM 	if (new_data) {
136911900SRaymond.Chen@Sun.COM 		freemsg(new_data);
137011900SRaymond.Chen@Sun.COM 	}
137111900SRaymond.Chen@Sun.COM 
137211900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
137311900SRaymond.Chen@Sun.COM 	    "usbecm_send_data: len(%d) data sent, rval=%d",
137411900SRaymond.Chen@Sun.COM 	    new_data_len ? new_data_len : data_len, rval);
137511900SRaymond.Chen@Sun.COM 
137611900SRaymond.Chen@Sun.COM 	return (rval);
137711900SRaymond.Chen@Sun.COM }
137811900SRaymond.Chen@Sun.COM 
137911900SRaymond.Chen@Sun.COM static int
usbecm_send_zero_data(usbecm_state_t * ecmp)138011900SRaymond.Chen@Sun.COM usbecm_send_zero_data(usbecm_state_t *ecmp)
138111900SRaymond.Chen@Sun.COM {
138211900SRaymond.Chen@Sun.COM 	usb_bulk_req_t	*br;
138311900SRaymond.Chen@Sun.COM 	int		rval = USB_FAILURE;
138411900SRaymond.Chen@Sun.COM 
138511900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
138611900SRaymond.Chen@Sun.COM 	    "usbecm_send_zero_data: entry");
138711900SRaymond.Chen@Sun.COM 
138811900SRaymond.Chen@Sun.COM 	br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
138911900SRaymond.Chen@Sun.COM 	if (br == NULL) {
139011900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
139111900SRaymond.Chen@Sun.COM 		    "usbecm_send_data: alloc req failed.");
139211900SRaymond.Chen@Sun.COM 
139311900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
139411900SRaymond.Chen@Sun.COM 	}
139511900SRaymond.Chen@Sun.COM 
139611900SRaymond.Chen@Sun.COM 	/* initialize the bulk out request */
139711900SRaymond.Chen@Sun.COM 	br->bulk_len = 0;
139811900SRaymond.Chen@Sun.COM 	br->bulk_timeout = USBECM_BULKOUT_TIMEOUT;
139911900SRaymond.Chen@Sun.COM 	br->bulk_cb = usbecm_bulkout_cb;
140011900SRaymond.Chen@Sun.COM 	br->bulk_exc_cb = usbecm_bulkout_cb;
140111900SRaymond.Chen@Sun.COM 	br->bulk_client_private = (usb_opaque_t)ecmp;
140211900SRaymond.Chen@Sun.COM 	br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
140311900SRaymond.Chen@Sun.COM 
140411900SRaymond.Chen@Sun.COM 	rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0);
140511900SRaymond.Chen@Sun.COM 
140611900SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
140711900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
140811900SRaymond.Chen@Sun.COM 		    "usbecm_send_zero_data: Send data failed, rval=%d",
140911900SRaymond.Chen@Sun.COM 		    rval);
141011900SRaymond.Chen@Sun.COM 
141111900SRaymond.Chen@Sun.COM 		/*
141211900SRaymond.Chen@Sun.COM 		 * br->bulk_data should be freed because we allocated
141311900SRaymond.Chen@Sun.COM 		 * it in this function.
141411900SRaymond.Chen@Sun.COM 		 */
141511900SRaymond.Chen@Sun.COM 		usb_free_bulk_req(br);
141611900SRaymond.Chen@Sun.COM 
141711900SRaymond.Chen@Sun.COM 	}
141811900SRaymond.Chen@Sun.COM 
141911900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
142011900SRaymond.Chen@Sun.COM 	    "usbecm_send_zero_data: end");
142111900SRaymond.Chen@Sun.COM 
142211900SRaymond.Chen@Sun.COM 	return (rval);
142311900SRaymond.Chen@Sun.COM }
142411900SRaymond.Chen@Sun.COM 
142511900SRaymond.Chen@Sun.COM /*
142611900SRaymond.Chen@Sun.COM  * Loadable module configuration entry points
142711900SRaymond.Chen@Sun.COM  */
142811900SRaymond.Chen@Sun.COM 
142911900SRaymond.Chen@Sun.COM /*
143011900SRaymond.Chen@Sun.COM  * _init module entry point.
143111900SRaymond.Chen@Sun.COM  *
143211900SRaymond.Chen@Sun.COM  * Called when the module is being loaded into memory.
143311900SRaymond.Chen@Sun.COM  */
143411900SRaymond.Chen@Sun.COM int
_init(void)143511900SRaymond.Chen@Sun.COM _init(void)
143611900SRaymond.Chen@Sun.COM {
143711900SRaymond.Chen@Sun.COM 	int err;
143811900SRaymond.Chen@Sun.COM 
143911900SRaymond.Chen@Sun.COM 	err = ddi_soft_state_init(&usbecm_statep, sizeof (usbecm_state_t), 1);
144011900SRaymond.Chen@Sun.COM 
144111900SRaymond.Chen@Sun.COM 	if (err != DDI_SUCCESS)
144211900SRaymond.Chen@Sun.COM 		return (err);
144311900SRaymond.Chen@Sun.COM 
144411900SRaymond.Chen@Sun.COM 	mac_init_ops(&usbecm_devops, "usbecm");
144511900SRaymond.Chen@Sun.COM 	err = mod_install(&usbecm_ml);
144611900SRaymond.Chen@Sun.COM 
144711900SRaymond.Chen@Sun.COM 	if (err != DDI_SUCCESS) {
144811900SRaymond.Chen@Sun.COM 		mac_fini_ops(&usbecm_devops);
144911900SRaymond.Chen@Sun.COM 		ddi_soft_state_fini(&usbecm_statep);
145011900SRaymond.Chen@Sun.COM 	}
145111900SRaymond.Chen@Sun.COM 
145211900SRaymond.Chen@Sun.COM 	return (err);
145311900SRaymond.Chen@Sun.COM }
145411900SRaymond.Chen@Sun.COM 
145511900SRaymond.Chen@Sun.COM /*
145611900SRaymond.Chen@Sun.COM  * _info module entry point.
145711900SRaymond.Chen@Sun.COM  *
145811900SRaymond.Chen@Sun.COM  * Called to obtain information about the module.
145911900SRaymond.Chen@Sun.COM  */
146011900SRaymond.Chen@Sun.COM int
_info(struct modinfo * modinfop)146111900SRaymond.Chen@Sun.COM _info(struct modinfo *modinfop)
146211900SRaymond.Chen@Sun.COM {
146311900SRaymond.Chen@Sun.COM 	return (mod_info(&usbecm_ml, modinfop));
146411900SRaymond.Chen@Sun.COM }
146511900SRaymond.Chen@Sun.COM 
146611900SRaymond.Chen@Sun.COM /*
146711900SRaymond.Chen@Sun.COM  * _fini module entry point.
146811900SRaymond.Chen@Sun.COM  *
146911900SRaymond.Chen@Sun.COM  * Called when the module is being unloaded.
147011900SRaymond.Chen@Sun.COM  */
147111900SRaymond.Chen@Sun.COM int
_fini(void)147211900SRaymond.Chen@Sun.COM _fini(void)
147311900SRaymond.Chen@Sun.COM {
147411900SRaymond.Chen@Sun.COM 	int err;
147511900SRaymond.Chen@Sun.COM 
147611900SRaymond.Chen@Sun.COM 	err = mod_remove(&usbecm_ml);
147711900SRaymond.Chen@Sun.COM 	if (err == DDI_SUCCESS) {
147811900SRaymond.Chen@Sun.COM 		mac_fini_ops(&usbecm_devops);
147911900SRaymond.Chen@Sun.COM 		ddi_soft_state_fini(&usbecm_statep);
148011900SRaymond.Chen@Sun.COM 	}
148111900SRaymond.Chen@Sun.COM 
148211900SRaymond.Chen@Sun.COM 	return (err);
148311900SRaymond.Chen@Sun.COM }
148411900SRaymond.Chen@Sun.COM 
148511900SRaymond.Chen@Sun.COM /*
148611900SRaymond.Chen@Sun.COM  * usbecm_pipe_start_polling:
148711900SRaymond.Chen@Sun.COM  *	start polling on the interrupt pipe
148811900SRaymond.Chen@Sun.COM  */
148911900SRaymond.Chen@Sun.COM static void
usbecm_pipe_start_polling(usbecm_state_t * ecmp)149011900SRaymond.Chen@Sun.COM usbecm_pipe_start_polling(usbecm_state_t *ecmp)
149111900SRaymond.Chen@Sun.COM {
149211900SRaymond.Chen@Sun.COM 	usb_intr_req_t	*intr;
149311900SRaymond.Chen@Sun.COM 	int		rval;
149411900SRaymond.Chen@Sun.COM 
149511900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
149611900SRaymond.Chen@Sun.COM 	    "usbecm_pipe_start_polling: ");
149711900SRaymond.Chen@Sun.COM 
149811900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_intr_ph == NULL) {
149911900SRaymond.Chen@Sun.COM 
150011900SRaymond.Chen@Sun.COM 		return;
150111900SRaymond.Chen@Sun.COM 	}
150211900SRaymond.Chen@Sun.COM 
150311900SRaymond.Chen@Sun.COM 	intr = usb_alloc_intr_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
150411900SRaymond.Chen@Sun.COM 
150511900SRaymond.Chen@Sun.COM 	/*
150611900SRaymond.Chen@Sun.COM 	 * If it is in interrupt context, usb_alloc_intr_req will return NULL if
150711900SRaymond.Chen@Sun.COM 	 * called with SLEEP flag.
150811900SRaymond.Chen@Sun.COM 	 */
150911900SRaymond.Chen@Sun.COM 	if (!intr) {
151011900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
151111900SRaymond.Chen@Sun.COM 		    "usbecm_pipe_start_polling: alloc req failed.");
151211900SRaymond.Chen@Sun.COM 
151311900SRaymond.Chen@Sun.COM 		return;
151411900SRaymond.Chen@Sun.COM 	}
151511900SRaymond.Chen@Sun.COM 
151611900SRaymond.Chen@Sun.COM 	/* initialize the interrupt request. */
151711900SRaymond.Chen@Sun.COM 	intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
151811900SRaymond.Chen@Sun.COM 	    USB_ATTRS_AUTOCLEARING;
151911900SRaymond.Chen@Sun.COM 	intr->intr_len = ecmp->ecm_intr_ep->ep_descr.wMaxPacketSize;
152011900SRaymond.Chen@Sun.COM 	intr->intr_client_private = (usb_opaque_t)ecmp;
152111900SRaymond.Chen@Sun.COM 	intr->intr_cb = usbecm_intr_cb;
152211900SRaymond.Chen@Sun.COM 	intr->intr_exc_cb = usbecm_intr_ex_cb;
152311900SRaymond.Chen@Sun.COM 
152411900SRaymond.Chen@Sun.COM 	rval = usb_pipe_intr_xfer(ecmp->ecm_intr_ph, intr, USB_FLAGS_SLEEP);
152511900SRaymond.Chen@Sun.COM 
152611900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
152711900SRaymond.Chen@Sun.COM 	if (rval == USB_SUCCESS) {
152811900SRaymond.Chen@Sun.COM 		ecmp->ecm_intr_state = USBECM_PIPE_BUSY;
152911900SRaymond.Chen@Sun.COM 	} else {
153011900SRaymond.Chen@Sun.COM 		usb_free_intr_req(intr);
153111900SRaymond.Chen@Sun.COM 		ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
153211900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
153311900SRaymond.Chen@Sun.COM 		    "usbecm_pipe_start_polling: failed (%d)", rval);
153411900SRaymond.Chen@Sun.COM 	}
153511900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
153611900SRaymond.Chen@Sun.COM 
153711900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
153811900SRaymond.Chen@Sun.COM 	    "usbecm_pipe_start_polling: end, rval=%d", rval);
153911900SRaymond.Chen@Sun.COM }
154011900SRaymond.Chen@Sun.COM 
154111900SRaymond.Chen@Sun.COM 
154211900SRaymond.Chen@Sun.COM /*
154311900SRaymond.Chen@Sun.COM  * usbsecm_intr_cb:
154411900SRaymond.Chen@Sun.COM  *	interrupt pipe normal callback
154511900SRaymond.Chen@Sun.COM  */
154611900SRaymond.Chen@Sun.COM /*ARGSUSED*/
154711900SRaymond.Chen@Sun.COM static void
usbecm_intr_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)154811900SRaymond.Chen@Sun.COM usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
154911900SRaymond.Chen@Sun.COM {
155011900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private;
155111900SRaymond.Chen@Sun.COM 	mblk_t		*data = req->intr_data;
155211900SRaymond.Chen@Sun.COM 	int		data_len;
155311900SRaymond.Chen@Sun.COM 
155411900SRaymond.Chen@Sun.COM 	data_len = (data) ? MBLKL(data) : 0;
155511900SRaymond.Chen@Sun.COM 
155611900SRaymond.Chen@Sun.COM 	DTRACE_PROBE2(usbecm_intr__cb, (usb_intr_req_t *), req, int, data_len);
155711900SRaymond.Chen@Sun.COM 
155811900SRaymond.Chen@Sun.COM 	/* check data length */
155911900SRaymond.Chen@Sun.COM 	if (data_len < 8) {
156011900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
156111900SRaymond.Chen@Sun.COM 		    "usbsecm_intr_cb: %d packet too short", data_len);
156211900SRaymond.Chen@Sun.COM 		usb_free_intr_req(req);
156311900SRaymond.Chen@Sun.COM 
156411900SRaymond.Chen@Sun.COM 		return;
156511900SRaymond.Chen@Sun.COM 	}
156611900SRaymond.Chen@Sun.COM 	req->intr_data = NULL;
156711900SRaymond.Chen@Sun.COM 	usb_free_intr_req(req);
156811900SRaymond.Chen@Sun.COM 
156911900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
157011900SRaymond.Chen@Sun.COM 	/* parse interrupt data -- notifications */
157111900SRaymond.Chen@Sun.COM 	usbecm_parse_intr_data(ecmp, data);
157211900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
157311900SRaymond.Chen@Sun.COM }
157411900SRaymond.Chen@Sun.COM 
157511900SRaymond.Chen@Sun.COM 
157611900SRaymond.Chen@Sun.COM /*
157711900SRaymond.Chen@Sun.COM  * usbsecm_intr_ex_cb:
157811900SRaymond.Chen@Sun.COM  *	interrupt pipe exception callback
157911900SRaymond.Chen@Sun.COM  */
158011900SRaymond.Chen@Sun.COM /*ARGSUSED*/
158111900SRaymond.Chen@Sun.COM static void
usbecm_intr_ex_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)158211900SRaymond.Chen@Sun.COM usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
158311900SRaymond.Chen@Sun.COM {
158411900SRaymond.Chen@Sun.COM 	usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private;
158511900SRaymond.Chen@Sun.COM 	usb_cr_t	cr = req->intr_completion_reason;
158611900SRaymond.Chen@Sun.COM 
158711900SRaymond.Chen@Sun.COM 	DTRACE_PROBE2(usbecm_intr_ex__cb, int, ecmp->ecm_dev_state,
158811900SRaymond.Chen@Sun.COM 	    (usb_cr_t), cr);
158911900SRaymond.Chen@Sun.COM 
159011900SRaymond.Chen@Sun.COM 	usb_free_intr_req(req);
159111900SRaymond.Chen@Sun.COM 
159211900SRaymond.Chen@Sun.COM 	/*
159311900SRaymond.Chen@Sun.COM 	 * If completion reason isn't USB_CR_PIPE_CLOSING and
159411900SRaymond.Chen@Sun.COM 	 * USB_CR_STOPPED_POLLING, restart polling.
159511900SRaymond.Chen@Sun.COM 	 */
159611900SRaymond.Chen@Sun.COM 	if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) {
159711900SRaymond.Chen@Sun.COM 		mutex_enter(&ecmp->ecm_mutex);
159811900SRaymond.Chen@Sun.COM 
159911900SRaymond.Chen@Sun.COM 		if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
160011900SRaymond.Chen@Sun.COM 
160111900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
160211900SRaymond.Chen@Sun.COM 			    "usbsecm_intr_ex_cb: state = %d",
160311900SRaymond.Chen@Sun.COM 			    ecmp->ecm_dev_state);
160411900SRaymond.Chen@Sun.COM 
160511900SRaymond.Chen@Sun.COM 			mutex_exit(&ecmp->ecm_mutex);
160611900SRaymond.Chen@Sun.COM 
160711900SRaymond.Chen@Sun.COM 			return;
160811900SRaymond.Chen@Sun.COM 		}
160911900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
161011900SRaymond.Chen@Sun.COM 
161111900SRaymond.Chen@Sun.COM 		usbecm_pipe_start_polling(ecmp);
161211900SRaymond.Chen@Sun.COM 	}
161311900SRaymond.Chen@Sun.COM }
161411900SRaymond.Chen@Sun.COM 
161511900SRaymond.Chen@Sun.COM 
161611900SRaymond.Chen@Sun.COM /*
161711900SRaymond.Chen@Sun.COM  * usbsecm_parse_intr_data:
161811900SRaymond.Chen@Sun.COM  *	Parse data received from interrupt callback
161911900SRaymond.Chen@Sun.COM  */
162011900SRaymond.Chen@Sun.COM static void
usbecm_parse_intr_data(usbecm_state_t * ecmp,mblk_t * data)162111900SRaymond.Chen@Sun.COM usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data)
162211900SRaymond.Chen@Sun.COM {
162311900SRaymond.Chen@Sun.COM 	uint8_t		bmRequestType;
162411900SRaymond.Chen@Sun.COM 	uint8_t		bNotification;
162511900SRaymond.Chen@Sun.COM 	uint16_t	wValue;
162611900SRaymond.Chen@Sun.COM 	uint16_t	wLength;
162711900SRaymond.Chen@Sun.COM 	int		linkstate;
162811900SRaymond.Chen@Sun.COM 
162911900SRaymond.Chen@Sun.COM 	bmRequestType = data->b_rptr[0];
163011900SRaymond.Chen@Sun.COM 	bNotification = data->b_rptr[1];
163111900SRaymond.Chen@Sun.COM 	/*
163211900SRaymond.Chen@Sun.COM 	 * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1,
163311900SRaymond.Chen@Sun.COM 	 * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0,
163411900SRaymond.Chen@Sun.COM 	 * mLength is 2. So we directly get the value from the byte.
163511900SRaymond.Chen@Sun.COM 	 */
163611900SRaymond.Chen@Sun.COM 	wValue = data->b_rptr[2];
163711900SRaymond.Chen@Sun.COM 	wLength = data->b_rptr[6];
163811900SRaymond.Chen@Sun.COM 
163911900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_compatibility) {
164011900SRaymond.Chen@Sun.COM 		if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) {
164111900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
164211900SRaymond.Chen@Sun.COM 			    "usbsecm_parse_intr_data: unknown request "
164311900SRaymond.Chen@Sun.COM 			    "type - 0x%x", bmRequestType);
164411900SRaymond.Chen@Sun.COM 
164511900SRaymond.Chen@Sun.COM 			freemsg(data);
164611900SRaymond.Chen@Sun.COM 
164711900SRaymond.Chen@Sun.COM 			return;
164811900SRaymond.Chen@Sun.COM 		}
164911900SRaymond.Chen@Sun.COM 	} else {
165011900SRaymond.Chen@Sun.COM 		/* non-compatible device specific parsing */
165111900SRaymond.Chen@Sun.COM 		if (ECM_DS_OP_VALID(ecm_ds_intr_cb)) {
165211900SRaymond.Chen@Sun.COM 			if (ecmp->ecm_ds_ops->ecm_ds_intr_cb(ecmp, data)
165311900SRaymond.Chen@Sun.COM 			    != USB_SUCCESS) {
165411900SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
165511900SRaymond.Chen@Sun.COM 				    "usbsecm_parse_intr_data: unknown request"
165611900SRaymond.Chen@Sun.COM 				    "type - 0x%x", bmRequestType);
165711900SRaymond.Chen@Sun.COM 			}
165811900SRaymond.Chen@Sun.COM 		}
165911900SRaymond.Chen@Sun.COM 		freemsg(data);
166011900SRaymond.Chen@Sun.COM 
166111900SRaymond.Chen@Sun.COM 		return;
166211900SRaymond.Chen@Sun.COM 	}
166311900SRaymond.Chen@Sun.COM 
166411900SRaymond.Chen@Sun.COM 	/*
166511900SRaymond.Chen@Sun.COM 	 * Check the return value of compatible devices
166611900SRaymond.Chen@Sun.COM 	 */
166711900SRaymond.Chen@Sun.COM 	switch (bNotification) {
166811900SRaymond.Chen@Sun.COM 	case USB_CDC_NOTIFICATION_NETWORK_CONNECTION:
166911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
167011900SRaymond.Chen@Sun.COM 		    "usbsecm_parse_intr_data: %s network!",
167111900SRaymond.Chen@Sun.COM 		    wValue ? "connected to" :"disconnected from");
167211900SRaymond.Chen@Sun.COM 
167311900SRaymond.Chen@Sun.COM 		linkstate = wValue ? LINK_STATE_UP:LINK_STATE_DOWN;
167411900SRaymond.Chen@Sun.COM 		if (ecmp->ecm_stat.es_linkstate == linkstate) {
167511900SRaymond.Chen@Sun.COM 		/* no changes to previous state */
167611900SRaymond.Chen@Sun.COM 			break;
167711900SRaymond.Chen@Sun.COM 		}
167811900SRaymond.Chen@Sun.COM 
167911900SRaymond.Chen@Sun.COM 		ecmp->ecm_stat.es_linkstate = linkstate;
168011900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
168111900SRaymond.Chen@Sun.COM 		mac_link_update(ecmp->ecm_mh, linkstate);
168211900SRaymond.Chen@Sun.COM 		mutex_enter(&ecmp->ecm_mutex);
168311900SRaymond.Chen@Sun.COM 
168411900SRaymond.Chen@Sun.COM 		break;
168511900SRaymond.Chen@Sun.COM 	case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE:
168611900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
168711900SRaymond.Chen@Sun.COM 		    "usbsecm_parse_intr_data: A response is a available.");
168811900SRaymond.Chen@Sun.COM 
168911900SRaymond.Chen@Sun.COM 		break;
169011900SRaymond.Chen@Sun.COM 	case USB_CDC_NOTIFICATION_SPEED_CHANGE:
169111900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
169211900SRaymond.Chen@Sun.COM 		    "usbsecm_parse_intr_data: speed change");
169311900SRaymond.Chen@Sun.COM 
169411900SRaymond.Chen@Sun.COM 		/* check the parameter's length. */
169511900SRaymond.Chen@Sun.COM 		if (wLength != 8) {
169611900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
169711900SRaymond.Chen@Sun.COM 			    "usbsecm_parse_intr_data: error data length.");
169811900SRaymond.Chen@Sun.COM 		} else {
169911900SRaymond.Chen@Sun.COM 			uint32_t	us_rate, ds_rate;
170011900SRaymond.Chen@Sun.COM 			uint8_t		*sp;
170111900SRaymond.Chen@Sun.COM 
170211900SRaymond.Chen@Sun.COM 			sp = &data->b_rptr[8];
170311900SRaymond.Chen@Sun.COM 			LE_TO_UINT32(sp, us_rate);
170411900SRaymond.Chen@Sun.COM 			sp = &data->b_rptr[12];
170511900SRaymond.Chen@Sun.COM 			LE_TO_UINT32(sp, ds_rate);
170611900SRaymond.Chen@Sun.COM 			ecmp->ecm_stat.es_upspeed = us_rate;
170711900SRaymond.Chen@Sun.COM 			ecmp->ecm_stat.es_downspeed = ds_rate;
170811900SRaymond.Chen@Sun.COM 		}
170911900SRaymond.Chen@Sun.COM 
171011900SRaymond.Chen@Sun.COM 		break;
171111900SRaymond.Chen@Sun.COM 	default:
171211900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
171311900SRaymond.Chen@Sun.COM 		    "usbsecm_parse_intr_data: unknown notification - 0x%x!",
171411900SRaymond.Chen@Sun.COM 		    bNotification);
171511900SRaymond.Chen@Sun.COM 
171611900SRaymond.Chen@Sun.COM 		break;
171711900SRaymond.Chen@Sun.COM 	}
171811900SRaymond.Chen@Sun.COM 
171911900SRaymond.Chen@Sun.COM 	freemsg(data);
172011900SRaymond.Chen@Sun.COM }
172111900SRaymond.Chen@Sun.COM 
172211900SRaymond.Chen@Sun.COM /*
172311900SRaymond.Chen@Sun.COM  * usbecm_restore_device_state:
172411900SRaymond.Chen@Sun.COM  *	restore device state after CPR resume or reconnect
172511900SRaymond.Chen@Sun.COM  */
172611900SRaymond.Chen@Sun.COM static int
usbecm_restore_device_state(usbecm_state_t * ecmp)172711900SRaymond.Chen@Sun.COM usbecm_restore_device_state(usbecm_state_t *ecmp)
172811900SRaymond.Chen@Sun.COM {
172911900SRaymond.Chen@Sun.COM 	int	state;
173011900SRaymond.Chen@Sun.COM 
173111900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
173211900SRaymond.Chen@Sun.COM 	    "usbecm_restore_device_state: ");
173311900SRaymond.Chen@Sun.COM 
173411900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
173511900SRaymond.Chen@Sun.COM 	state = ecmp->ecm_dev_state;
173611900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
173711900SRaymond.Chen@Sun.COM 
173811900SRaymond.Chen@Sun.COM 	/* Check device status */
173911900SRaymond.Chen@Sun.COM 	if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) {
174011900SRaymond.Chen@Sun.COM 
174111900SRaymond.Chen@Sun.COM 		return (state);
174211900SRaymond.Chen@Sun.COM 	}
174311900SRaymond.Chen@Sun.COM 
174411900SRaymond.Chen@Sun.COM 	/* Check if we are talking to the same device */
174511900SRaymond.Chen@Sun.COM 	if (usb_check_same_device(ecmp->ecm_dip, ecmp->ecm_lh, USB_LOG_L0,
174611900SRaymond.Chen@Sun.COM 	    -1, USB_CHK_ALL, NULL) != USB_SUCCESS) {
174711900SRaymond.Chen@Sun.COM 		mutex_enter(&ecmp->ecm_mutex);
174811900SRaymond.Chen@Sun.COM 		state = ecmp->ecm_dev_state = USB_DEV_DISCONNECTED;
174911900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
175011900SRaymond.Chen@Sun.COM 
175111900SRaymond.Chen@Sun.COM 		return (state);
175211900SRaymond.Chen@Sun.COM 	}
175311900SRaymond.Chen@Sun.COM 
175411900SRaymond.Chen@Sun.COM 	if (state == USB_DEV_DISCONNECTED) {
175511900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L1(PRINT_MASK_EVENTS, ecmp->ecm_lh,
175611900SRaymond.Chen@Sun.COM 		    "usbecm_restore_device_state: Device has been reconnected "
175711900SRaymond.Chen@Sun.COM 		    "but data may have been lost");
175811900SRaymond.Chen@Sun.COM 	}
175911900SRaymond.Chen@Sun.COM 
176011900SRaymond.Chen@Sun.COM 	/* if MAC was started, restarted it */
176111900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
176211900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_mac_state == USBECM_MAC_STARTED) {
176311900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_EVENTS, ecmp->ecm_lh,
176411900SRaymond.Chen@Sun.COM 		    "usbecm_restore_device_state: MAC was started");
176511900SRaymond.Chen@Sun.COM 
176611900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
176711900SRaymond.Chen@Sun.COM 		/* Do the same operation as usbecm_m_start() does */
176811900SRaymond.Chen@Sun.COM 		if (usbecm_open_pipes(ecmp) != USB_SUCCESS) {
176911900SRaymond.Chen@Sun.COM 
177011900SRaymond.Chen@Sun.COM 			return (state);
177111900SRaymond.Chen@Sun.COM 		}
177211900SRaymond.Chen@Sun.COM 
177311900SRaymond.Chen@Sun.COM 		mutex_enter(&ecmp->ecm_mutex);
177411900SRaymond.Chen@Sun.COM 		if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
177511900SRaymond.Chen@Sun.COM 			mutex_exit(&ecmp->ecm_mutex);
177611900SRaymond.Chen@Sun.COM 
177711900SRaymond.Chen@Sun.COM 			return (state);
177811900SRaymond.Chen@Sun.COM 		}
177911900SRaymond.Chen@Sun.COM 	}
178011900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
178111900SRaymond.Chen@Sun.COM 
178211900SRaymond.Chen@Sun.COM 	/*
178311900SRaymond.Chen@Sun.COM 	 * init device state
178411900SRaymond.Chen@Sun.COM 	 */
178511900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
178611900SRaymond.Chen@Sun.COM 	state = ecmp->ecm_dev_state = USB_DEV_ONLINE;
178711900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
178811900SRaymond.Chen@Sun.COM 
178911900SRaymond.Chen@Sun.COM 	return (state);
179011900SRaymond.Chen@Sun.COM }
179111900SRaymond.Chen@Sun.COM 
179211900SRaymond.Chen@Sun.COM /*
179311900SRaymond.Chen@Sun.COM  * usbecm_reconnect_event_cb:
179411900SRaymond.Chen@Sun.COM  *     called upon when the device is hotplugged back
179511900SRaymond.Chen@Sun.COM  */
179611900SRaymond.Chen@Sun.COM /*ARGSUSED*/
179711900SRaymond.Chen@Sun.COM static int
usbecm_reconnect_event_cb(dev_info_t * dip)179811900SRaymond.Chen@Sun.COM usbecm_reconnect_event_cb(dev_info_t *dip)
179911900SRaymond.Chen@Sun.COM {
180011900SRaymond.Chen@Sun.COM 	usbecm_state_t	*ecmp =
180111900SRaymond.Chen@Sun.COM 	    (usbecm_state_t *)ddi_get_soft_state(usbecm_statep,
180211900SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip));
180311900SRaymond.Chen@Sun.COM 
180411900SRaymond.Chen@Sun.COM 	ASSERT(ecmp != NULL);
180511900SRaymond.Chen@Sun.COM 
180611900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
180711900SRaymond.Chen@Sun.COM 	    "usbecm_reconnect_event_cb: entry");
180811900SRaymond.Chen@Sun.COM 
180911900SRaymond.Chen@Sun.COM 	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
181011900SRaymond.Chen@Sun.COM 
181111900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
181211900SRaymond.Chen@Sun.COM 	ASSERT(ecmp->ecm_dev_state == USB_DEV_DISCONNECTED);
181311900SRaymond.Chen@Sun.COM 
181411900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
181511900SRaymond.Chen@Sun.COM 
181611900SRaymond.Chen@Sun.COM 	if (usbecm_restore_device_state(ecmp) != USB_DEV_ONLINE) {
181711900SRaymond.Chen@Sun.COM 		usb_release_access(ecmp->ecm_ser_acc);
181811900SRaymond.Chen@Sun.COM 
181911900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
182011900SRaymond.Chen@Sun.COM 	}
182111900SRaymond.Chen@Sun.COM 
182211900SRaymond.Chen@Sun.COM 	usb_release_access(ecmp->ecm_ser_acc);
182311900SRaymond.Chen@Sun.COM 
182411900SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
182511900SRaymond.Chen@Sun.COM }
182611900SRaymond.Chen@Sun.COM 
182711900SRaymond.Chen@Sun.COM 
182811900SRaymond.Chen@Sun.COM /*
182911900SRaymond.Chen@Sun.COM  * usbecm_disconnect_event_cb:
183011900SRaymond.Chen@Sun.COM  *	callback for disconnect events
183111900SRaymond.Chen@Sun.COM  */
183211900SRaymond.Chen@Sun.COM /*ARGSUSED*/
183311900SRaymond.Chen@Sun.COM static int
usbecm_disconnect_event_cb(dev_info_t * dip)183411900SRaymond.Chen@Sun.COM usbecm_disconnect_event_cb(dev_info_t *dip)
183511900SRaymond.Chen@Sun.COM {
183611900SRaymond.Chen@Sun.COM 	usbecm_state_t	*ecmp = (usbecm_state_t *)ddi_get_soft_state(
183711900SRaymond.Chen@Sun.COM 	    usbecm_statep, ddi_get_instance(dip));
183811900SRaymond.Chen@Sun.COM 
183911900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
184011900SRaymond.Chen@Sun.COM 	    "usbecm_disconnect_event_cb: entry");
184111900SRaymond.Chen@Sun.COM 
184211900SRaymond.Chen@Sun.COM 	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
184311900SRaymond.Chen@Sun.COM 
184411900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
184511900SRaymond.Chen@Sun.COM 	ecmp->ecm_dev_state = USB_DEV_DISCONNECTED;
184611900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
184711900SRaymond.Chen@Sun.COM 
184811900SRaymond.Chen@Sun.COM 	usbecm_close_pipes(ecmp);
184911900SRaymond.Chen@Sun.COM 
185011900SRaymond.Chen@Sun.COM 	usb_release_access(ecmp->ecm_ser_acc);
185111900SRaymond.Chen@Sun.COM 
185211900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
185311900SRaymond.Chen@Sun.COM 	    "usbecm_disconnect_event_cb: End");
185411900SRaymond.Chen@Sun.COM 
185511900SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
185611900SRaymond.Chen@Sun.COM }
185711900SRaymond.Chen@Sun.COM 
185811900SRaymond.Chen@Sun.COM /*
185911900SRaymond.Chen@Sun.COM  * power management
186011900SRaymond.Chen@Sun.COM  * ----------------
186111900SRaymond.Chen@Sun.COM  *
186211900SRaymond.Chen@Sun.COM  * usbecm_create_pm_components:
186311900SRaymond.Chen@Sun.COM  *	create PM components
186411900SRaymond.Chen@Sun.COM  */
186511900SRaymond.Chen@Sun.COM static int
usbecm_create_pm_components(usbecm_state_t * ecmp)186611900SRaymond.Chen@Sun.COM usbecm_create_pm_components(usbecm_state_t *ecmp)
186711900SRaymond.Chen@Sun.COM {
186811900SRaymond.Chen@Sun.COM 	dev_info_t	*dip = ecmp->ecm_dip;
186911900SRaymond.Chen@Sun.COM 	usbecm_pm_t	*pm;
187011900SRaymond.Chen@Sun.COM 	uint_t		pwr_states;
187111900SRaymond.Chen@Sun.COM 
187211900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
187311900SRaymond.Chen@Sun.COM 	    "usbecm_create_pm_components: entry");
187411900SRaymond.Chen@Sun.COM 
187511900SRaymond.Chen@Sun.COM 	if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) {
187611900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
187711900SRaymond.Chen@Sun.COM 		    "usbecm_create_pm_components: failed");
187811900SRaymond.Chen@Sun.COM 
187911900SRaymond.Chen@Sun.COM 		/* don't fail the attach process */
188011900SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
188111900SRaymond.Chen@Sun.COM 	}
188211900SRaymond.Chen@Sun.COM 
188311900SRaymond.Chen@Sun.COM 	pm = ecmp->ecm_pm =
188411900SRaymond.Chen@Sun.COM 	    (usbecm_pm_t *)kmem_zalloc(sizeof (usbecm_pm_t), KM_SLEEP);
188511900SRaymond.Chen@Sun.COM 
188611900SRaymond.Chen@Sun.COM 	pm->pm_pwr_states = (uint8_t)pwr_states;
188711900SRaymond.Chen@Sun.COM 	pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
188811900SRaymond.Chen@Sun.COM 	pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip,
188911900SRaymond.Chen@Sun.COM 	    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS);
189011900SRaymond.Chen@Sun.COM 
189111900SRaymond.Chen@Sun.COM 	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
189211900SRaymond.Chen@Sun.COM 
189311900SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
189411900SRaymond.Chen@Sun.COM }
189511900SRaymond.Chen@Sun.COM 
189611900SRaymond.Chen@Sun.COM /*
189711900SRaymond.Chen@Sun.COM  * usbecm_cleanup:
189811900SRaymond.Chen@Sun.COM  *	Release resources of current device during detach.
189911900SRaymond.Chen@Sun.COM  */
190011900SRaymond.Chen@Sun.COM static void
usbecm_cleanup(usbecm_state_t * ecmp)190111900SRaymond.Chen@Sun.COM usbecm_cleanup(usbecm_state_t *ecmp)
190211900SRaymond.Chen@Sun.COM {
190311900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
190411900SRaymond.Chen@Sun.COM 	    "usbecm_cleanup: ");
190511900SRaymond.Chen@Sun.COM 
190611900SRaymond.Chen@Sun.COM 	if (ecmp == NULL) {
190711900SRaymond.Chen@Sun.COM 
190811900SRaymond.Chen@Sun.COM 		return;
190911900SRaymond.Chen@Sun.COM 	}
191011900SRaymond.Chen@Sun.COM 
191111900SRaymond.Chen@Sun.COM 	usbecm_close_pipes(ecmp);
191211900SRaymond.Chen@Sun.COM 
191311900SRaymond.Chen@Sun.COM 	/* unregister callback function */
191411900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_init_flags & USBECM_INIT_EVENTS) {
191511900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
191611900SRaymond.Chen@Sun.COM 		    "usbecm_cleanup: unregister events");
191711900SRaymond.Chen@Sun.COM 
191811900SRaymond.Chen@Sun.COM 		usb_unregister_event_cbs(ecmp->ecm_dip, &usbecm_events);
191911900SRaymond.Chen@Sun.COM 	}
192011900SRaymond.Chen@Sun.COM 
192111900SRaymond.Chen@Sun.COM 	/* destroy power management components */
192211900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_pm != NULL) {
192311900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
192411900SRaymond.Chen@Sun.COM 		    "usbecm_cleanup: destroy pm");
192511900SRaymond.Chen@Sun.COM 		usbecm_destroy_pm_components(ecmp);
192611900SRaymond.Chen@Sun.COM 	}
192711900SRaymond.Chen@Sun.COM 
192811900SRaymond.Chen@Sun.COM 	/* free description of device tree. */
192911900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_def_ph != NULL) {
193011900SRaymond.Chen@Sun.COM 		mutex_destroy(&ecmp->ecm_mutex);
193111900SRaymond.Chen@Sun.COM 
193211900SRaymond.Chen@Sun.COM 		usb_free_descr_tree(ecmp->ecm_dip, ecmp->ecm_dev_data);
193311900SRaymond.Chen@Sun.COM 		ecmp->ecm_def_ph = NULL;
193411900SRaymond.Chen@Sun.COM 	}
193511900SRaymond.Chen@Sun.COM 
193611900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_lh != NULL) {
193711900SRaymond.Chen@Sun.COM 		usb_free_log_hdl(ecmp->ecm_lh);
193811900SRaymond.Chen@Sun.COM 		ecmp->ecm_lh = NULL;
193911900SRaymond.Chen@Sun.COM 	}
194011900SRaymond.Chen@Sun.COM 
194111900SRaymond.Chen@Sun.COM 	/* detach client device */
194211900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_dev_data != NULL) {
194311900SRaymond.Chen@Sun.COM 		usb_client_detach(ecmp->ecm_dip, ecmp->ecm_dev_data);
194411900SRaymond.Chen@Sun.COM 	}
194511900SRaymond.Chen@Sun.COM 
194611900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_init_flags & USBECM_INIT_MAC) {
1947*11902SRaymond.Chen@Sun.COM 		(void) usbecm_mac_fini(ecmp);
194811900SRaymond.Chen@Sun.COM 	}
194911900SRaymond.Chen@Sun.COM 
195011900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_init_flags & USBECM_INIT_SER) {
195111900SRaymond.Chen@Sun.COM 		usb_fini_serialization(ecmp->ecm_ser_acc);
195211900SRaymond.Chen@Sun.COM 	}
195311900SRaymond.Chen@Sun.COM 
195411900SRaymond.Chen@Sun.COM 	ddi_prop_remove_all(ecmp->ecm_dip);
195511900SRaymond.Chen@Sun.COM 	ddi_remove_minor_node(ecmp->ecm_dip, NULL);
195611900SRaymond.Chen@Sun.COM }
195711900SRaymond.Chen@Sun.COM 
195811900SRaymond.Chen@Sun.COM /*
195911900SRaymond.Chen@Sun.COM  * usbecm_destroy_pm_components:
196011900SRaymond.Chen@Sun.COM  *	destroy PM components
196111900SRaymond.Chen@Sun.COM  */
196211900SRaymond.Chen@Sun.COM static void
usbecm_destroy_pm_components(usbecm_state_t * ecmp)196311900SRaymond.Chen@Sun.COM usbecm_destroy_pm_components(usbecm_state_t *ecmp)
196411900SRaymond.Chen@Sun.COM {
196511900SRaymond.Chen@Sun.COM 	usbecm_pm_t	*pm = ecmp->ecm_pm;
196611900SRaymond.Chen@Sun.COM 	dev_info_t	*dip = ecmp->ecm_dip;
196711900SRaymond.Chen@Sun.COM 	int		rval;
196811900SRaymond.Chen@Sun.COM 
196911900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
197011900SRaymond.Chen@Sun.COM 	    "usbecm_destroy_pm_components: ");
197111900SRaymond.Chen@Sun.COM 
197211900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_dev_state != USB_DEV_DISCONNECTED) {
197311900SRaymond.Chen@Sun.COM 		if (pm->pm_wakeup_enabled) {
197411900SRaymond.Chen@Sun.COM 			rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
197511900SRaymond.Chen@Sun.COM 			if (rval != DDI_SUCCESS) {
197611900SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
197711900SRaymond.Chen@Sun.COM 				    "usbecm_destroy_pm_components: "
197811900SRaymond.Chen@Sun.COM 				    "raising power failed (%d)", rval);
197911900SRaymond.Chen@Sun.COM 			}
198011900SRaymond.Chen@Sun.COM 
198111900SRaymond.Chen@Sun.COM 			rval = usb_handle_remote_wakeup(dip,
198211900SRaymond.Chen@Sun.COM 			    USB_REMOTE_WAKEUP_DISABLE);
198311900SRaymond.Chen@Sun.COM 			if (rval != USB_SUCCESS) {
198411900SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
198511900SRaymond.Chen@Sun.COM 				    "usbecm_destroy_pm_components: "
198611900SRaymond.Chen@Sun.COM 				    "disable remote wakeup failed (%d)", rval);
198711900SRaymond.Chen@Sun.COM 			}
198811900SRaymond.Chen@Sun.COM 		}
198911900SRaymond.Chen@Sun.COM 
199011900SRaymond.Chen@Sun.COM 		(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
199111900SRaymond.Chen@Sun.COM 	}
199211900SRaymond.Chen@Sun.COM 	kmem_free((caddr_t)pm, sizeof (usbecm_pm_t));
199311900SRaymond.Chen@Sun.COM 	ecmp->ecm_pm = NULL;
199411900SRaymond.Chen@Sun.COM }
199511900SRaymond.Chen@Sun.COM 
199611900SRaymond.Chen@Sun.COM /*
199711900SRaymond.Chen@Sun.COM  * usbecm_pm_set_busy:
199811900SRaymond.Chen@Sun.COM  *	mark device busy and raise power
199911900SRaymond.Chen@Sun.COM  */
200011900SRaymond.Chen@Sun.COM static void
usbecm_pm_set_busy(usbecm_state_t * ecmp)200111900SRaymond.Chen@Sun.COM usbecm_pm_set_busy(usbecm_state_t *ecmp)
200211900SRaymond.Chen@Sun.COM {
200311900SRaymond.Chen@Sun.COM 	usbecm_pm_t	*pm = ecmp->ecm_pm;
200411900SRaymond.Chen@Sun.COM 	dev_info_t	*dip = ecmp->ecm_dip;
200511900SRaymond.Chen@Sun.COM 	int		rval;
200611900SRaymond.Chen@Sun.COM 
200711900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
200811900SRaymond.Chen@Sun.COM 	    "usbecm_pm_set_busy: pm = 0x%p", (void *)pm);
200911900SRaymond.Chen@Sun.COM 
201011900SRaymond.Chen@Sun.COM 	if (pm == NULL) {
201111900SRaymond.Chen@Sun.COM 
201211900SRaymond.Chen@Sun.COM 		return;
201311900SRaymond.Chen@Sun.COM 	}
201411900SRaymond.Chen@Sun.COM 
201511900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
201611900SRaymond.Chen@Sun.COM 	/* if already marked busy, just increment the counter */
201711900SRaymond.Chen@Sun.COM 	if (pm->pm_busy_cnt++ > 0) {
201811900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
201911900SRaymond.Chen@Sun.COM 
202011900SRaymond.Chen@Sun.COM 		return;
202111900SRaymond.Chen@Sun.COM 	}
202211900SRaymond.Chen@Sun.COM 
202311900SRaymond.Chen@Sun.COM 	(void) pm_busy_component(dip, 0);
202411900SRaymond.Chen@Sun.COM 
202511900SRaymond.Chen@Sun.COM 	if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) {
202611900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
202711900SRaymond.Chen@Sun.COM 
202811900SRaymond.Chen@Sun.COM 		return;
202911900SRaymond.Chen@Sun.COM 	}
203011900SRaymond.Chen@Sun.COM 
203111900SRaymond.Chen@Sun.COM 	/* need to raise power	*/
203211900SRaymond.Chen@Sun.COM 	pm->pm_raise_power = B_TRUE;
203311900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
203411900SRaymond.Chen@Sun.COM 
203511900SRaymond.Chen@Sun.COM 	rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
203611900SRaymond.Chen@Sun.COM 	if (rval != DDI_SUCCESS) {
203711900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
203811900SRaymond.Chen@Sun.COM 		    "usbecm_pm_set_busy: raising power failed");
203911900SRaymond.Chen@Sun.COM 	}
204011900SRaymond.Chen@Sun.COM 
204111900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
204211900SRaymond.Chen@Sun.COM 	pm->pm_raise_power = B_FALSE;
204311900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
204411900SRaymond.Chen@Sun.COM }
204511900SRaymond.Chen@Sun.COM 
204611900SRaymond.Chen@Sun.COM 
204711900SRaymond.Chen@Sun.COM /*
204811900SRaymond.Chen@Sun.COM  * usbecm_pm_set_idle:
204911900SRaymond.Chen@Sun.COM  *	mark device idle
205011900SRaymond.Chen@Sun.COM  */
205111900SRaymond.Chen@Sun.COM static void
usbecm_pm_set_idle(usbecm_state_t * ecmp)205211900SRaymond.Chen@Sun.COM usbecm_pm_set_idle(usbecm_state_t *ecmp)
205311900SRaymond.Chen@Sun.COM {
205411900SRaymond.Chen@Sun.COM 	usbecm_pm_t	*pm = ecmp->ecm_pm;
205511900SRaymond.Chen@Sun.COM 	dev_info_t	*dip = ecmp->ecm_dip;
205611900SRaymond.Chen@Sun.COM 
205711900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
205811900SRaymond.Chen@Sun.COM 	    "usbecm_pm_set_idle: ");
205911900SRaymond.Chen@Sun.COM 
206011900SRaymond.Chen@Sun.COM 	if (pm == NULL) {
206111900SRaymond.Chen@Sun.COM 
206211900SRaymond.Chen@Sun.COM 		return;
206311900SRaymond.Chen@Sun.COM 	}
206411900SRaymond.Chen@Sun.COM 
206511900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
206611900SRaymond.Chen@Sun.COM 	if (--pm->pm_busy_cnt > 0) {
206711900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
206811900SRaymond.Chen@Sun.COM 
206911900SRaymond.Chen@Sun.COM 		return;
207011900SRaymond.Chen@Sun.COM 	}
207111900SRaymond.Chen@Sun.COM 
207211900SRaymond.Chen@Sun.COM 	if (pm) {
207311900SRaymond.Chen@Sun.COM 		(void) pm_idle_component(dip, 0);
207411900SRaymond.Chen@Sun.COM 	}
207511900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
207611900SRaymond.Chen@Sun.COM }
207711900SRaymond.Chen@Sun.COM 
207811900SRaymond.Chen@Sun.COM 
207911900SRaymond.Chen@Sun.COM /*
208011900SRaymond.Chen@Sun.COM  * usbecm_pwrlvl0:
208111900SRaymond.Chen@Sun.COM  *	Functions to handle power transition for OS levels 0 -> 3
208211900SRaymond.Chen@Sun.COM  *	The same level as OS state, different from USB state
208311900SRaymond.Chen@Sun.COM  */
208411900SRaymond.Chen@Sun.COM static int
usbecm_pwrlvl0(usbecm_state_t * ecmp)208511900SRaymond.Chen@Sun.COM usbecm_pwrlvl0(usbecm_state_t *ecmp)
208611900SRaymond.Chen@Sun.COM {
208711900SRaymond.Chen@Sun.COM 	int		rval;
208811900SRaymond.Chen@Sun.COM 
208911900SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&ecmp->ecm_mutex));
209011900SRaymond.Chen@Sun.COM 
209111900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
209211900SRaymond.Chen@Sun.COM 	    "usbecm_pwrlvl0: ");
209311900SRaymond.Chen@Sun.COM 
209411900SRaymond.Chen@Sun.COM 	switch (ecmp->ecm_dev_state) {
209511900SRaymond.Chen@Sun.COM 	case USB_DEV_ONLINE:
209611900SRaymond.Chen@Sun.COM 		/* issue USB D3 command to the device */
209711900SRaymond.Chen@Sun.COM 		rval = usb_set_device_pwrlvl3(ecmp->ecm_dip);
209811900SRaymond.Chen@Sun.COM 		ASSERT(rval == USB_SUCCESS);
209911900SRaymond.Chen@Sun.COM 		if ((ecmp->ecm_intr_ph != NULL) &&
210011900SRaymond.Chen@Sun.COM 		    (ecmp->ecm_intr_state == USBECM_PIPE_BUSY)) {
210111900SRaymond.Chen@Sun.COM 			mutex_exit(&ecmp->ecm_mutex);
210211900SRaymond.Chen@Sun.COM 			usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph,
210311900SRaymond.Chen@Sun.COM 			    USB_FLAGS_SLEEP);
210411900SRaymond.Chen@Sun.COM 			mutex_enter(&ecmp->ecm_mutex);
210511900SRaymond.Chen@Sun.COM 
210611900SRaymond.Chen@Sun.COM 			ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
210711900SRaymond.Chen@Sun.COM 		}
210811900SRaymond.Chen@Sun.COM 		ecmp->ecm_dev_state = USB_DEV_PWRED_DOWN;
210911900SRaymond.Chen@Sun.COM 		ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF;
211011900SRaymond.Chen@Sun.COM 
211111900SRaymond.Chen@Sun.COM 		/* FALLTHRU */
211211900SRaymond.Chen@Sun.COM 	case USB_DEV_DISCONNECTED:
211311900SRaymond.Chen@Sun.COM 	case USB_DEV_SUSPENDED:
211411900SRaymond.Chen@Sun.COM 		/* allow a disconnect/cpr'ed device to go to lower power */
211511900SRaymond.Chen@Sun.COM 
211611900SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
211711900SRaymond.Chen@Sun.COM 	case USB_DEV_PWRED_DOWN:
211811900SRaymond.Chen@Sun.COM 	default:
211911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
212011900SRaymond.Chen@Sun.COM 		    "usbecm_pwrlvl0: illegal device state");
212111900SRaymond.Chen@Sun.COM 
212211900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
212311900SRaymond.Chen@Sun.COM 	}
212411900SRaymond.Chen@Sun.COM }
212511900SRaymond.Chen@Sun.COM 
212611900SRaymond.Chen@Sun.COM 
212711900SRaymond.Chen@Sun.COM /*
212811900SRaymond.Chen@Sun.COM  * usbecm_pwrlvl1:
212911900SRaymond.Chen@Sun.COM  *	Functions to handle power transition for OS levels 1 -> 2
213011900SRaymond.Chen@Sun.COM  */
213111900SRaymond.Chen@Sun.COM static int
usbecm_pwrlvl1(usbecm_state_t * ecmp)213211900SRaymond.Chen@Sun.COM usbecm_pwrlvl1(usbecm_state_t *ecmp)
213311900SRaymond.Chen@Sun.COM {
213411900SRaymond.Chen@Sun.COM 	/* issue USB D2 command to the device */
213511900SRaymond.Chen@Sun.COM 	(void) usb_set_device_pwrlvl2(ecmp->ecm_dip);
213611900SRaymond.Chen@Sun.COM 
213711900SRaymond.Chen@Sun.COM 	return (USB_FAILURE);
213811900SRaymond.Chen@Sun.COM }
213911900SRaymond.Chen@Sun.COM 
214011900SRaymond.Chen@Sun.COM 
214111900SRaymond.Chen@Sun.COM /*
214211900SRaymond.Chen@Sun.COM  * usbecm_pwrlvl2:
214311900SRaymond.Chen@Sun.COM  *	Functions to handle power transition for OS levels 2 -> 1
214411900SRaymond.Chen@Sun.COM  */
214511900SRaymond.Chen@Sun.COM static int
usbecm_pwrlvl2(usbecm_state_t * ecmp)214611900SRaymond.Chen@Sun.COM usbecm_pwrlvl2(usbecm_state_t *ecmp)
214711900SRaymond.Chen@Sun.COM {
214811900SRaymond.Chen@Sun.COM 	/* issue USB D1 command to the device */
214911900SRaymond.Chen@Sun.COM 	(void) usb_set_device_pwrlvl1(ecmp->ecm_dip);
215011900SRaymond.Chen@Sun.COM 
215111900SRaymond.Chen@Sun.COM 	return (USB_FAILURE);
215211900SRaymond.Chen@Sun.COM }
215311900SRaymond.Chen@Sun.COM 
215411900SRaymond.Chen@Sun.COM 
215511900SRaymond.Chen@Sun.COM /*
215611900SRaymond.Chen@Sun.COM  * usbecm_pwrlvl3:
215711900SRaymond.Chen@Sun.COM  *	Functions to handle power transition for OS levels 3 -> 0
215811900SRaymond.Chen@Sun.COM  *	The same level as OS state, different from USB state
215911900SRaymond.Chen@Sun.COM  */
216011900SRaymond.Chen@Sun.COM static int
usbecm_pwrlvl3(usbecm_state_t * ecmp)216111900SRaymond.Chen@Sun.COM usbecm_pwrlvl3(usbecm_state_t *ecmp)
216211900SRaymond.Chen@Sun.COM {
216311900SRaymond.Chen@Sun.COM 	int		rval;
216411900SRaymond.Chen@Sun.COM 
216511900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
216611900SRaymond.Chen@Sun.COM 	    "usbecm_pwrlvl3: ");
216711900SRaymond.Chen@Sun.COM 
216811900SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&ecmp->ecm_mutex));
216911900SRaymond.Chen@Sun.COM 
217011900SRaymond.Chen@Sun.COM 	switch (ecmp->ecm_dev_state) {
217111900SRaymond.Chen@Sun.COM 	case USB_DEV_PWRED_DOWN:
217211900SRaymond.Chen@Sun.COM 		/* Issue USB D0 command to the device here */
217311900SRaymond.Chen@Sun.COM 		rval = usb_set_device_pwrlvl0(ecmp->ecm_dip);
217411900SRaymond.Chen@Sun.COM 		ASSERT(rval == USB_SUCCESS);
217511900SRaymond.Chen@Sun.COM 
217611900SRaymond.Chen@Sun.COM 		if (ecmp->ecm_intr_ph != NULL &&
217711900SRaymond.Chen@Sun.COM 		    ecmp->ecm_intr_state == USBECM_PIPE_IDLE) {
217811900SRaymond.Chen@Sun.COM 			mutex_exit(&ecmp->ecm_mutex);
217911900SRaymond.Chen@Sun.COM 			usbecm_pipe_start_polling(ecmp);
218011900SRaymond.Chen@Sun.COM 			mutex_enter(&ecmp->ecm_mutex);
218111900SRaymond.Chen@Sun.COM 		}
218211900SRaymond.Chen@Sun.COM 
218311900SRaymond.Chen@Sun.COM 		ecmp->ecm_dev_state = USB_DEV_ONLINE;
218411900SRaymond.Chen@Sun.COM 		ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
218511900SRaymond.Chen@Sun.COM 
218611900SRaymond.Chen@Sun.COM 		/* FALLTHRU */
218711900SRaymond.Chen@Sun.COM 	case USB_DEV_ONLINE:
218811900SRaymond.Chen@Sun.COM 		/* we are already in full power */
218911900SRaymond.Chen@Sun.COM 
219011900SRaymond.Chen@Sun.COM 		/* FALLTHRU */
219111900SRaymond.Chen@Sun.COM 	case USB_DEV_DISCONNECTED:
219211900SRaymond.Chen@Sun.COM 	case USB_DEV_SUSPENDED:
219311900SRaymond.Chen@Sun.COM 
219411900SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
219511900SRaymond.Chen@Sun.COM 	default:
219611900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
219711900SRaymond.Chen@Sun.COM 		    "usbecm_pwrlvl3: illegal device state");
219811900SRaymond.Chen@Sun.COM 
219911900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
220011900SRaymond.Chen@Sun.COM 	}
220111900SRaymond.Chen@Sun.COM }
220211900SRaymond.Chen@Sun.COM 
220311900SRaymond.Chen@Sun.COM /*ARGSUSED*/
220411900SRaymond.Chen@Sun.COM static int
usbecm_power(dev_info_t * dip,int comp,int level)220511900SRaymond.Chen@Sun.COM usbecm_power(dev_info_t *dip, int comp, int level)
220611900SRaymond.Chen@Sun.COM {
220711900SRaymond.Chen@Sun.COM 	usbecm_state_t	*ecmp;
220811900SRaymond.Chen@Sun.COM 	usbecm_pm_t	*pm;
220911900SRaymond.Chen@Sun.COM 	int		rval = USB_SUCCESS;
221011900SRaymond.Chen@Sun.COM 
221111900SRaymond.Chen@Sun.COM 	ecmp = ddi_get_soft_state(usbecm_statep, ddi_get_instance(dip));
221211900SRaymond.Chen@Sun.COM 	pm = ecmp->ecm_pm;
221311900SRaymond.Chen@Sun.COM 
221411900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
221511900SRaymond.Chen@Sun.COM 	    "usbecm_power: entry");
221611900SRaymond.Chen@Sun.COM 
221711900SRaymond.Chen@Sun.COM 	/* check if pm is NULL */
221811900SRaymond.Chen@Sun.COM 	if (pm == NULL) {
221911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
222011900SRaymond.Chen@Sun.COM 		    "usbecm_power: pm is NULL.");
222111900SRaymond.Chen@Sun.COM 
222211900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
222311900SRaymond.Chen@Sun.COM 	}
222411900SRaymond.Chen@Sun.COM 
222511900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
222611900SRaymond.Chen@Sun.COM 	/*
222711900SRaymond.Chen@Sun.COM 	 * check if we are transitioning to a legal power level
222811900SRaymond.Chen@Sun.COM 	 */
222911900SRaymond.Chen@Sun.COM 	if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) {
223011900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
223111900SRaymond.Chen@Sun.COM 		    "usbecm_power: "
223211900SRaymond.Chen@Sun.COM 		    "illegal power level %d, pwr_states=%x",
223311900SRaymond.Chen@Sun.COM 		    level, pm->pm_pwr_states);
223411900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
223511900SRaymond.Chen@Sun.COM 
223611900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
223711900SRaymond.Chen@Sun.COM 	}
223811900SRaymond.Chen@Sun.COM 
223911900SRaymond.Chen@Sun.COM 	/*
224011900SRaymond.Chen@Sun.COM 	 * if we are about to raise power and asked to lower power, fail
224111900SRaymond.Chen@Sun.COM 	 */
224211900SRaymond.Chen@Sun.COM 	if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) {
224311900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
224411900SRaymond.Chen@Sun.COM 		    "usbecm_power: wrong condition.");
224511900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
224611900SRaymond.Chen@Sun.COM 
224711900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
224811900SRaymond.Chen@Sun.COM 	}
224911900SRaymond.Chen@Sun.COM 
225011900SRaymond.Chen@Sun.COM 	/*
225111900SRaymond.Chen@Sun.COM 	 * Set the power status of device by request level.
225211900SRaymond.Chen@Sun.COM 	 */
225311900SRaymond.Chen@Sun.COM 	switch (level) {
225411900SRaymond.Chen@Sun.COM 	case USB_DEV_OS_PWR_OFF:
225511900SRaymond.Chen@Sun.COM 		rval = usbecm_pwrlvl0(ecmp);
225611900SRaymond.Chen@Sun.COM 
225711900SRaymond.Chen@Sun.COM 		break;
225811900SRaymond.Chen@Sun.COM 	case USB_DEV_OS_PWR_1:
225911900SRaymond.Chen@Sun.COM 		rval = usbecm_pwrlvl1(ecmp);
226011900SRaymond.Chen@Sun.COM 
226111900SRaymond.Chen@Sun.COM 		break;
226211900SRaymond.Chen@Sun.COM 	case USB_DEV_OS_PWR_2:
226311900SRaymond.Chen@Sun.COM 		rval = usbecm_pwrlvl2(ecmp);
226411900SRaymond.Chen@Sun.COM 
226511900SRaymond.Chen@Sun.COM 		break;
226611900SRaymond.Chen@Sun.COM 	case USB_DEV_OS_FULL_PWR:
226711900SRaymond.Chen@Sun.COM 		rval = usbecm_pwrlvl3(ecmp);
226811900SRaymond.Chen@Sun.COM 
226911900SRaymond.Chen@Sun.COM 		break;
227011900SRaymond.Chen@Sun.COM 	}
227111900SRaymond.Chen@Sun.COM 
227211900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
227311900SRaymond.Chen@Sun.COM 
227411900SRaymond.Chen@Sun.COM 	return (rval);
227511900SRaymond.Chen@Sun.COM }
227611900SRaymond.Chen@Sun.COM 
227711900SRaymond.Chen@Sun.COM /*
227811900SRaymond.Chen@Sun.COM  * Register with the MAC layer.
227911900SRaymond.Chen@Sun.COM  */
228011900SRaymond.Chen@Sun.COM static int
usbecm_mac_init(usbecm_state_t * ecmp)228111900SRaymond.Chen@Sun.COM usbecm_mac_init(usbecm_state_t *ecmp)
228211900SRaymond.Chen@Sun.COM {
228311900SRaymond.Chen@Sun.COM 	mac_register_t *macp;
228411900SRaymond.Chen@Sun.COM 	int err;
228511900SRaymond.Chen@Sun.COM 
228611900SRaymond.Chen@Sun.COM 	/*
228711900SRaymond.Chen@Sun.COM 	 * Initialize mac structure
228811900SRaymond.Chen@Sun.COM 	 */
228911900SRaymond.Chen@Sun.COM 	macp = mac_alloc(MAC_VERSION);
229011900SRaymond.Chen@Sun.COM 	if (macp == NULL) {
229111900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
229211900SRaymond.Chen@Sun.COM 		    "failed to allocate MAC structure");
229311900SRaymond.Chen@Sun.COM 
229411900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
229511900SRaymond.Chen@Sun.COM 	}
229611900SRaymond.Chen@Sun.COM 
229711900SRaymond.Chen@Sun.COM 	/*
229811900SRaymond.Chen@Sun.COM 	 * Initialize pointer to device specific functions
229911900SRaymond.Chen@Sun.COM 	 */
230011900SRaymond.Chen@Sun.COM 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
230111900SRaymond.Chen@Sun.COM 	macp->m_driver = ecmp;
230211900SRaymond.Chen@Sun.COM 	macp->m_dip = ecmp->ecm_dip;
230311900SRaymond.Chen@Sun.COM 
230411900SRaymond.Chen@Sun.COM 	macp->m_src_addr = ecmp->ecm_srcaddr;
230511900SRaymond.Chen@Sun.COM 	macp->m_callbacks = &usbecm_m_callbacks;
230611900SRaymond.Chen@Sun.COM 	macp->m_min_sdu = 0;
230711900SRaymond.Chen@Sun.COM 	macp->m_max_sdu = ETHERMTU;
230811900SRaymond.Chen@Sun.COM 
230911900SRaymond.Chen@Sun.COM 	/*
231011900SRaymond.Chen@Sun.COM 	 * Register the macp to mac
231111900SRaymond.Chen@Sun.COM 	 */
231211900SRaymond.Chen@Sun.COM 	err = mac_register(macp, &ecmp->ecm_mh);
231311900SRaymond.Chen@Sun.COM 	mac_free(macp);
231411900SRaymond.Chen@Sun.COM 
231511900SRaymond.Chen@Sun.COM 	if (err != DDI_SUCCESS) {
231611900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh,
231711900SRaymond.Chen@Sun.COM 		    "failed to register MAC structure");
231811900SRaymond.Chen@Sun.COM 
231911900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
232011900SRaymond.Chen@Sun.COM 	}
232111900SRaymond.Chen@Sun.COM 
232211900SRaymond.Chen@Sun.COM 	mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN);
232311900SRaymond.Chen@Sun.COM 	ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN;
232411900SRaymond.Chen@Sun.COM 	ecmp->ecm_tx_cnt = 0;
232511900SRaymond.Chen@Sun.COM 
232611900SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
232711900SRaymond.Chen@Sun.COM }
232811900SRaymond.Chen@Sun.COM 
232911900SRaymond.Chen@Sun.COM static int
usbecm_mac_fini(usbecm_state_t * ecmp)233011900SRaymond.Chen@Sun.COM usbecm_mac_fini(usbecm_state_t *ecmp)
233111900SRaymond.Chen@Sun.COM {
233211900SRaymond.Chen@Sun.COM 	int rval = DDI_SUCCESS;
233311900SRaymond.Chen@Sun.COM 
233411900SRaymond.Chen@Sun.COM 	if ((ecmp->ecm_init_flags & USBECM_INIT_MAC) == 0) {
233511900SRaymond.Chen@Sun.COM 		return (DDI_SUCCESS);
233611900SRaymond.Chen@Sun.COM 	}
233711900SRaymond.Chen@Sun.COM 
233811900SRaymond.Chen@Sun.COM 	ecmp->ecm_init_flags &= ~USBECM_INIT_MAC;
233911900SRaymond.Chen@Sun.COM 	if ((rval = mac_disable(ecmp->ecm_mh)) != 0) {
234011900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
234111900SRaymond.Chen@Sun.COM 		    "failed to disable MAC");
234211900SRaymond.Chen@Sun.COM 
234311900SRaymond.Chen@Sun.COM 		return (rval);
234411900SRaymond.Chen@Sun.COM 	}
234511900SRaymond.Chen@Sun.COM 
234611900SRaymond.Chen@Sun.COM 	(void) mac_unregister(ecmp->ecm_mh);
234711900SRaymond.Chen@Sun.COM 
234811900SRaymond.Chen@Sun.COM 	return (rval);
234911900SRaymond.Chen@Sun.COM }
235011900SRaymond.Chen@Sun.COM 
235111900SRaymond.Chen@Sun.COM static int
usbecm_resume(usbecm_state_t * ecmp)235211900SRaymond.Chen@Sun.COM usbecm_resume(usbecm_state_t *ecmp)
235311900SRaymond.Chen@Sun.COM {
235411900SRaymond.Chen@Sun.COM 	int		current_state;
235511900SRaymond.Chen@Sun.COM 	int		ret;
235611900SRaymond.Chen@Sun.COM 
235711900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
235811900SRaymond.Chen@Sun.COM 	    "usbecm_resume: ");
235911900SRaymond.Chen@Sun.COM 
236011900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
236111900SRaymond.Chen@Sun.COM 	current_state = ecmp->ecm_dev_state;
236211900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
236311900SRaymond.Chen@Sun.COM 
236411900SRaymond.Chen@Sun.COM 	/* restore the status of device */
236511900SRaymond.Chen@Sun.COM 	if (current_state != USB_DEV_ONLINE) {
236611900SRaymond.Chen@Sun.COM 		ret = usbecm_restore_device_state(ecmp);
236711900SRaymond.Chen@Sun.COM 	} else {
236811900SRaymond.Chen@Sun.COM 		ret = USB_DEV_ONLINE;
236911900SRaymond.Chen@Sun.COM 	}
237011900SRaymond.Chen@Sun.COM 
237111900SRaymond.Chen@Sun.COM 	return (ret);
237211900SRaymond.Chen@Sun.COM }
237311900SRaymond.Chen@Sun.COM 
237411900SRaymond.Chen@Sun.COM static int
usbecm_suspend(usbecm_state_t * ecmp)237511900SRaymond.Chen@Sun.COM usbecm_suspend(usbecm_state_t *ecmp)
237611900SRaymond.Chen@Sun.COM {
237711900SRaymond.Chen@Sun.COM 	(void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
237811900SRaymond.Chen@Sun.COM 
237911900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
238011900SRaymond.Chen@Sun.COM 	ecmp->ecm_dev_state = USB_DEV_SUSPENDED;
238111900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
238211900SRaymond.Chen@Sun.COM 
238311900SRaymond.Chen@Sun.COM 	usbecm_close_pipes(ecmp);
238411900SRaymond.Chen@Sun.COM 
238511900SRaymond.Chen@Sun.COM 	usb_release_access(ecmp->ecm_ser_acc);
238611900SRaymond.Chen@Sun.COM 
238711900SRaymond.Chen@Sun.COM 	return (0);
238811900SRaymond.Chen@Sun.COM }
238911900SRaymond.Chen@Sun.COM 
239011900SRaymond.Chen@Sun.COM /*
239111900SRaymond.Chen@Sun.COM  * Translate MAC address from string to 6 bytes array int value
239211900SRaymond.Chen@Sun.COM  * Can't use ether_aton() since it requires format of x:x:x:x:x:x
239311900SRaymond.Chen@Sun.COM  */
239411900SRaymond.Chen@Sun.COM void
label_to_mac(char * hex,unsigned char * mac)239511900SRaymond.Chen@Sun.COM label_to_mac(char *hex, unsigned char *mac)
239611900SRaymond.Chen@Sun.COM {
239711900SRaymond.Chen@Sun.COM 	int i;
239811900SRaymond.Chen@Sun.COM 	char c;
239911900SRaymond.Chen@Sun.COM 
240011900SRaymond.Chen@Sun.COM 	/* can only count 6 bytes! */
240111900SRaymond.Chen@Sun.COM 	for (i = 0; i < 6; i++) {
240211900SRaymond.Chen@Sun.COM 		/* upper 4 bits */
240311900SRaymond.Chen@Sun.COM 		if (!isdigit(hex[2*i])) {
240411900SRaymond.Chen@Sun.COM 			c = (toupper(hex[2 * i]) - 'A' + 10);
240511900SRaymond.Chen@Sun.COM 		} else {
240611900SRaymond.Chen@Sun.COM 			c = (hex[2 * i] - '0');
240711900SRaymond.Chen@Sun.COM 		}
240811900SRaymond.Chen@Sun.COM 		mac[i] = c * 16;
240911900SRaymond.Chen@Sun.COM 
241011900SRaymond.Chen@Sun.COM 		/* lower 4 bits */
241111900SRaymond.Chen@Sun.COM 		if (!isdigit(hex[2*i + 1])) {
241211900SRaymond.Chen@Sun.COM 			c = (toupper(hex[2 * i + 1]) - 'A' + 10);
241311900SRaymond.Chen@Sun.COM 		} else {
241411900SRaymond.Chen@Sun.COM 			c = hex[2 * i + 1] - '0';
241511900SRaymond.Chen@Sun.COM 		}
241611900SRaymond.Chen@Sun.COM 		mac[i] += c;
241711900SRaymond.Chen@Sun.COM 	}
241811900SRaymond.Chen@Sun.COM }
241911900SRaymond.Chen@Sun.COM 
242011900SRaymond.Chen@Sun.COM /*
242111900SRaymond.Chen@Sun.COM  * usbecm_get_descriptors:
242211900SRaymond.Chen@Sun.COM  *	parse functional descriptors of ecm compatible device
242311900SRaymond.Chen@Sun.COM  */
242411900SRaymond.Chen@Sun.COM static int
usbecm_get_descriptors(usbecm_state_t * ecmp)242511900SRaymond.Chen@Sun.COM usbecm_get_descriptors(usbecm_state_t *ecmp)
242611900SRaymond.Chen@Sun.COM {
242711900SRaymond.Chen@Sun.COM 	int			i;
242811900SRaymond.Chen@Sun.COM 	usb_cfg_data_t		*cfg;
242911900SRaymond.Chen@Sun.COM 	usb_alt_if_data_t	*altif;
243011900SRaymond.Chen@Sun.COM 	usb_cvs_data_t		*cvs;
243111900SRaymond.Chen@Sun.COM 	int16_t			master_if = -1, slave_if = -1;
243211900SRaymond.Chen@Sun.COM 	usb_cdc_ecm_descr_t	ecm_desc;
243311900SRaymond.Chen@Sun.COM 	usb_ep_data_t		*ep_data;
243411900SRaymond.Chen@Sun.COM 	usb_dev_descr_t		*usb_dev_desc;
243511900SRaymond.Chen@Sun.COM 
243611900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
243711900SRaymond.Chen@Sun.COM 	    "usbecm_get_descriptors: ");
243811900SRaymond.Chen@Sun.COM 
243911900SRaymond.Chen@Sun.COM 	usb_dev_desc = ecmp->ecm_dev_data->dev_descr;
244011900SRaymond.Chen@Sun.COM 
244111900SRaymond.Chen@Sun.COM 	/*
244211900SRaymond.Chen@Sun.COM 	 * Special treatment of Sun's SP Ethernet device.
244311900SRaymond.Chen@Sun.COM 	 */
244411900SRaymond.Chen@Sun.COM 	if ((usb_dev_desc->idVendor == SUN_SP_VENDOR_ID) &&
244511900SRaymond.Chen@Sun.COM 	    (usb_dev_desc->idProduct == SUN_SP_PRODUCT_ID)) {
244611900SRaymond.Chen@Sun.COM 		if (usb_set_cfg(ecmp->ecm_dip, ecmp->ecm_cfg_index,
244711900SRaymond.Chen@Sun.COM 		    USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS) {
244811900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
244911900SRaymond.Chen@Sun.COM 			    "usbecm_get_descriptors: fail to set cfg ");
245011900SRaymond.Chen@Sun.COM 		} else {
245111900SRaymond.Chen@Sun.COM 			usb_free_dev_data(ecmp->ecm_dip, ecmp->ecm_dev_data);
245211900SRaymond.Chen@Sun.COM 			if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data,
245311900SRaymond.Chen@Sun.COM 			    USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
245411900SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
245511900SRaymond.Chen@Sun.COM 				    "usbecm_get_descriptors: fail to get"
245611900SRaymond.Chen@Sun.COM 				    " dev_data");
245711900SRaymond.Chen@Sun.COM 
245811900SRaymond.Chen@Sun.COM 				return (USB_FAILURE);
245911900SRaymond.Chen@Sun.COM 			}
246011900SRaymond.Chen@Sun.COM 		}
246111900SRaymond.Chen@Sun.COM 	}
246211900SRaymond.Chen@Sun.COM 
246311900SRaymond.Chen@Sun.COM 	cfg = ecmp->ecm_dev_data->dev_curr_cfg;
246411900SRaymond.Chen@Sun.COM 
246511900SRaymond.Chen@Sun.COM 	/* set default control and data interface */
246611900SRaymond.Chen@Sun.COM 	ecmp->ecm_ctrl_if_no = ecmp->ecm_data_if_no = 0;
246711900SRaymond.Chen@Sun.COM 
246811900SRaymond.Chen@Sun.COM 	/* get current interfaces */
246911900SRaymond.Chen@Sun.COM 	ecmp->ecm_ctrl_if_no = ecmp->ecm_dev_data->dev_curr_if;
247011900SRaymond.Chen@Sun.COM 	if (cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt == 0) {
247111900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
247211900SRaymond.Chen@Sun.COM 		    "usbecm_get_descriptors: elements in if_alt is %d",
247311900SRaymond.Chen@Sun.COM 		    cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt);
247411900SRaymond.Chen@Sun.COM 
247511900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
247611900SRaymond.Chen@Sun.COM 	}
247711900SRaymond.Chen@Sun.COM 
247811900SRaymond.Chen@Sun.COM 	altif = &cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_alt[0];
247911900SRaymond.Chen@Sun.COM 
248011900SRaymond.Chen@Sun.COM 	/*
248111900SRaymond.Chen@Sun.COM 	 * Based on CDC specification, ECM devices usually include the
248211900SRaymond.Chen@Sun.COM 	 * following function descriptors: Header, Union and ECM
248311900SRaymond.Chen@Sun.COM 	 * Contry Selection function descriptors. This loop search tree data
248411900SRaymond.Chen@Sun.COM 	 * structure for each ecm class descriptor.
248511900SRaymond.Chen@Sun.COM 	 */
248611900SRaymond.Chen@Sun.COM 	for (i = 0; i < altif->altif_n_cvs; i++) {
248711900SRaymond.Chen@Sun.COM 		cvs = &altif->altif_cvs[i];
248811900SRaymond.Chen@Sun.COM 
248911900SRaymond.Chen@Sun.COM 		if ((cvs->cvs_buf == NULL) ||
249011900SRaymond.Chen@Sun.COM 		    (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) {
249111900SRaymond.Chen@Sun.COM 			continue;
249211900SRaymond.Chen@Sun.COM 		}
249311900SRaymond.Chen@Sun.COM 
249411900SRaymond.Chen@Sun.COM 		switch (cvs->cvs_buf[2]) {
249511900SRaymond.Chen@Sun.COM 		case USB_CDC_DESCR_TYPE_HEADER:
249611900SRaymond.Chen@Sun.COM 			/*
249711900SRaymond.Chen@Sun.COM 			 * parse header functional descriptor
249811900SRaymond.Chen@Sun.COM 			 * Just to check integrity.
249911900SRaymond.Chen@Sun.COM 			 */
250011900SRaymond.Chen@Sun.COM 			if (cvs->cvs_buf_len != 5) {
250111900SRaymond.Chen@Sun.COM 				return (USB_FAILURE);
250211900SRaymond.Chen@Sun.COM 			}
250311900SRaymond.Chen@Sun.COM 			break;
250411900SRaymond.Chen@Sun.COM 		case USB_CDC_DESCR_TYPE_ETHERNET:
250511900SRaymond.Chen@Sun.COM 			/* parse ECM functional descriptor */
250611900SRaymond.Chen@Sun.COM 			if (cvs->cvs_buf_len >= USB_CDC_ECM_LEN) {
250711900SRaymond.Chen@Sun.COM 				char buf[USB_MAXSTRINGLEN];
250811900SRaymond.Chen@Sun.COM 
250911900SRaymond.Chen@Sun.COM 				if (usb_parse_data("4cl2sc", cvs->cvs_buf,
251011900SRaymond.Chen@Sun.COM 				    cvs->cvs_buf_len, (void *)&ecm_desc,
251111900SRaymond.Chen@Sun.COM 				    (size_t)USB_CDC_ECM_LEN) <
251211900SRaymond.Chen@Sun.COM 				    USB_CDC_ECM_LEN) {
251311900SRaymond.Chen@Sun.COM 
251411900SRaymond.Chen@Sun.COM 					return (USB_FAILURE);
251511900SRaymond.Chen@Sun.COM 				}
251611900SRaymond.Chen@Sun.COM 
251711900SRaymond.Chen@Sun.COM 				/* get the MAC address */
251811900SRaymond.Chen@Sun.COM 				if (usb_get_string_descr(ecmp->ecm_dip,
251911900SRaymond.Chen@Sun.COM 				    USB_LANG_ID, ecm_desc.iMACAddress, buf,
252011900SRaymond.Chen@Sun.COM 				    USB_MAXSTRINGLEN) != USB_SUCCESS) {
252111900SRaymond.Chen@Sun.COM 
252211900SRaymond.Chen@Sun.COM 					return (USB_FAILURE);
252311900SRaymond.Chen@Sun.COM 				}
252411900SRaymond.Chen@Sun.COM 
252511900SRaymond.Chen@Sun.COM 				USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
252611900SRaymond.Chen@Sun.COM 				    "usbecm_get_descriptors: macaddr=%s ",
252711900SRaymond.Chen@Sun.COM 				    buf);
252811900SRaymond.Chen@Sun.COM 
252911900SRaymond.Chen@Sun.COM 				/* expects 12 characters */
253011900SRaymond.Chen@Sun.COM 				if (strlen(buf) < 12) {
253111900SRaymond.Chen@Sun.COM 					return (USB_FAILURE);
253211900SRaymond.Chen@Sun.COM 				}
253311900SRaymond.Chen@Sun.COM 				label_to_mac(buf, ecmp->ecm_srcaddr);
253411900SRaymond.Chen@Sun.COM 
253511900SRaymond.Chen@Sun.COM 				bcopy(&ecm_desc, &ecmp->ecm_desc,
253611900SRaymond.Chen@Sun.COM 				    USB_CDC_ECM_LEN);
253711900SRaymond.Chen@Sun.COM 			}
253811900SRaymond.Chen@Sun.COM 			break;
253911900SRaymond.Chen@Sun.COM 		case USB_CDC_DESCR_TYPE_UNION:
254011900SRaymond.Chen@Sun.COM 			/* parse Union functional descriptor. */
254111900SRaymond.Chen@Sun.COM 			if (cvs->cvs_buf_len >= 5) {
254211900SRaymond.Chen@Sun.COM 				master_if = cvs->cvs_buf[3];
254311900SRaymond.Chen@Sun.COM 				slave_if = cvs->cvs_buf[4];
254411900SRaymond.Chen@Sun.COM 			}
254511900SRaymond.Chen@Sun.COM 			break;
254611900SRaymond.Chen@Sun.COM 		default:
254711900SRaymond.Chen@Sun.COM 			break;
254811900SRaymond.Chen@Sun.COM 		}
254911900SRaymond.Chen@Sun.COM 	}
255011900SRaymond.Chen@Sun.COM 
255111900SRaymond.Chen@Sun.COM 	/* For usb ecm devices, it must satisfy the following options. */
255211900SRaymond.Chen@Sun.COM 	if (cfg->cfg_n_if < 2) {
255311900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
255411900SRaymond.Chen@Sun.COM 		    "usbecm_get_descriptors: # of interfaces %d < 2",
255511900SRaymond.Chen@Sun.COM 		    cfg->cfg_n_if);
255611900SRaymond.Chen@Sun.COM 
255711900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
255811900SRaymond.Chen@Sun.COM 	}
255911900SRaymond.Chen@Sun.COM 
256011900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_data_if_no == 0 &&
256111900SRaymond.Chen@Sun.COM 	    slave_if != ecmp->ecm_data_if_no) {
256211900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
256311900SRaymond.Chen@Sun.COM 		    "usbecm_get_descriptors: Device has no call management "
256411900SRaymond.Chen@Sun.COM 		    "descriptor and use Union Descriptor.");
256511900SRaymond.Chen@Sun.COM 
256611900SRaymond.Chen@Sun.COM 		ecmp->ecm_data_if_no = slave_if;
256711900SRaymond.Chen@Sun.COM 	}
256811900SRaymond.Chen@Sun.COM 
256911900SRaymond.Chen@Sun.COM 	if ((master_if != ecmp->ecm_ctrl_if_no) ||
257011900SRaymond.Chen@Sun.COM 	    (slave_if != ecmp->ecm_data_if_no)) {
257111900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
257211900SRaymond.Chen@Sun.COM 		    "usbecm_get_descriptors: control interface or "
257311900SRaymond.Chen@Sun.COM 		    "data interface don't match.");
257411900SRaymond.Chen@Sun.COM 
257511900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
257611900SRaymond.Chen@Sun.COM 	}
257711900SRaymond.Chen@Sun.COM 
257811900SRaymond.Chen@Sun.COM 	if ((ecmp->ecm_ctrl_if_no >= cfg->cfg_n_if) ||
257911900SRaymond.Chen@Sun.COM 	    (ecmp->ecm_data_if_no >= cfg->cfg_n_if)) {
258011900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
258111900SRaymond.Chen@Sun.COM 		    "usbecm_get_descriptors: control interface %d or "
258211900SRaymond.Chen@Sun.COM 		    "data interface %d out of range.",
258311900SRaymond.Chen@Sun.COM 		    ecmp->ecm_ctrl_if_no, ecmp->ecm_data_if_no);
258411900SRaymond.Chen@Sun.COM 
258511900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
258611900SRaymond.Chen@Sun.COM 	}
258711900SRaymond.Chen@Sun.COM 
258811900SRaymond.Chen@Sun.COM 	/* ECM data interface has a minimal of two altsettings */
258911900SRaymond.Chen@Sun.COM 	if (cfg->cfg_if[ecmp->ecm_data_if_no].if_n_alt < 2) {
259011900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
259111900SRaymond.Chen@Sun.COM 		    "usbecm_get_descriptors: elements in if_alt is %d,"
259211900SRaymond.Chen@Sun.COM 		    " MUST >= 2", cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt);
259311900SRaymond.Chen@Sun.COM 
259411900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
259511900SRaymond.Chen@Sun.COM 	}
259611900SRaymond.Chen@Sun.COM 
259711900SRaymond.Chen@Sun.COM 	/* control interface must have interrupt endpoint */
259811900SRaymond.Chen@Sun.COM 	if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
259911900SRaymond.Chen@Sun.COM 	    ecmp->ecm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR,
260011900SRaymond.Chen@Sun.COM 	    USB_EP_DIR_IN)) == NULL) {
260111900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
260211900SRaymond.Chen@Sun.COM 		    "usbecm_get_descriptors: "
260311900SRaymond.Chen@Sun.COM 		    "ctrl interface %d has no interrupt endpoint",
260411900SRaymond.Chen@Sun.COM 		    ecmp->ecm_data_if_no);
260511900SRaymond.Chen@Sun.COM 
260611900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
260711900SRaymond.Chen@Sun.COM 	}
260811900SRaymond.Chen@Sun.COM 	ecmp->ecm_intr_ep = ep_data;
260911900SRaymond.Chen@Sun.COM 
261011900SRaymond.Chen@Sun.COM 	/* data interface alt 1 must have bulk in and out(ECM v1.2,p5) */
261111900SRaymond.Chen@Sun.COM 	if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
261211900SRaymond.Chen@Sun.COM 	    ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK,
261311900SRaymond.Chen@Sun.COM 	    USB_EP_DIR_IN)) == NULL) {
261411900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
261511900SRaymond.Chen@Sun.COM 		    "usbecm_get_descriptors: "
261611900SRaymond.Chen@Sun.COM 		    "data interface %d has no bulk in endpoint",
261711900SRaymond.Chen@Sun.COM 		    ecmp->ecm_data_if_no);
261811900SRaymond.Chen@Sun.COM 
261911900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
262011900SRaymond.Chen@Sun.COM 	}
262111900SRaymond.Chen@Sun.COM 	ecmp->ecm_bulk_in_ep = ep_data;
262211900SRaymond.Chen@Sun.COM 
262311900SRaymond.Chen@Sun.COM 	if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
262411900SRaymond.Chen@Sun.COM 	    ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK,
262511900SRaymond.Chen@Sun.COM 	    USB_EP_DIR_OUT)) == NULL) {
262611900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
262711900SRaymond.Chen@Sun.COM 		    "usbecm_get_descriptors: "
262811900SRaymond.Chen@Sun.COM 		    "data interface %d has no bulk out endpoint",
262911900SRaymond.Chen@Sun.COM 		    ecmp->ecm_data_if_no);
263011900SRaymond.Chen@Sun.COM 
263111900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
263211900SRaymond.Chen@Sun.COM 	}
263311900SRaymond.Chen@Sun.COM 	ecmp->ecm_bulk_out_ep = ep_data;
263411900SRaymond.Chen@Sun.COM 
263511900SRaymond.Chen@Sun.COM 	/* set default value for ethernet packet filter */
263611900SRaymond.Chen@Sun.COM 	ecmp->ecm_pkt_flt = CDC_ECM_PKT_TYPE_DIRECTED;
263711900SRaymond.Chen@Sun.COM 
263811900SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
263911900SRaymond.Chen@Sun.COM }
264011900SRaymond.Chen@Sun.COM 
264111900SRaymond.Chen@Sun.COM /* Generate IEEE802 style MAC address */
264211900SRaymond.Chen@Sun.COM static void
generate_ether_addr(uint8_t * mac_addr)264311900SRaymond.Chen@Sun.COM generate_ether_addr(uint8_t *mac_addr)
264411900SRaymond.Chen@Sun.COM {
2645*11902SRaymond.Chen@Sun.COM 	(void) random_get_bytes(mac_addr, 6);
264611900SRaymond.Chen@Sun.COM 	mac_addr [0] &= 0xfe;	/* unicast only */
264711900SRaymond.Chen@Sun.COM 	mac_addr [0] |= 0x02;	/* set locally administered bit */
264811900SRaymond.Chen@Sun.COM }
264911900SRaymond.Chen@Sun.COM 
265011900SRaymond.Chen@Sun.COM /*
265111900SRaymond.Chen@Sun.COM  * Find a pair of bulk In/Out endpoints
265211900SRaymond.Chen@Sun.COM  */
usbecm_find_bulk_in_out_eps(usbecm_state_t * ecmp,uint16_t ifc,usb_if_data_t * intf)265311900SRaymond.Chen@Sun.COM int usbecm_find_bulk_in_out_eps(usbecm_state_t *ecmp,
265411900SRaymond.Chen@Sun.COM     uint16_t ifc, usb_if_data_t *intf)
265511900SRaymond.Chen@Sun.COM {
265611900SRaymond.Chen@Sun.COM 	uint16_t alt, alt_num;
265711900SRaymond.Chen@Sun.COM 	usb_ep_data_t *intr_ep = NULL;
265811900SRaymond.Chen@Sun.COM 	usb_ep_data_t *bulk_in, *bulk_out, *ep;
265911900SRaymond.Chen@Sun.COM 
266011900SRaymond.Chen@Sun.COM 	alt_num = intf->if_n_alt;
266111900SRaymond.Chen@Sun.COM 
266211900SRaymond.Chen@Sun.COM 	/*
266311900SRaymond.Chen@Sun.COM 	 * for the non-compatible devices, to make it simple, we
266411900SRaymond.Chen@Sun.COM 	 * suppose the devices have this kind of configuration:
266511900SRaymond.Chen@Sun.COM 	 *	INTR In EP(if exists) + BULK In + Bulk Out in the
266611900SRaymond.Chen@Sun.COM 	 *	same altsetting of the same interface
266711900SRaymond.Chen@Sun.COM 	 */
266811900SRaymond.Chen@Sun.COM 	for (alt = 0; alt < alt_num; alt++) {
266911900SRaymond.Chen@Sun.COM 		/* search pair of bulk in/out EPs */
267011900SRaymond.Chen@Sun.COM 		if (((bulk_in = usb_lookup_ep_data(ecmp->ecm_dip,
267111900SRaymond.Chen@Sun.COM 		    ecmp->ecm_dev_data, ifc, alt, 0,
267211900SRaymond.Chen@Sun.COM 		    USB_EP_ATTR_BULK,
267311900SRaymond.Chen@Sun.COM 		    USB_EP_DIR_IN)) == NULL) ||
267411900SRaymond.Chen@Sun.COM 		    (bulk_out = usb_lookup_ep_data(ecmp->ecm_dip,
267511900SRaymond.Chen@Sun.COM 		    ecmp->ecm_dev_data, ifc, alt, 0,
267611900SRaymond.Chen@Sun.COM 		    USB_EP_ATTR_BULK,
267711900SRaymond.Chen@Sun.COM 		    USB_EP_DIR_OUT)) == NULL) {
267811900SRaymond.Chen@Sun.COM 
267911900SRaymond.Chen@Sun.COM 			continue;
268011900SRaymond.Chen@Sun.COM 		}
268111900SRaymond.Chen@Sun.COM 
268211900SRaymond.Chen@Sun.COM 		/*
268311900SRaymond.Chen@Sun.COM 		 * search interrupt pipe.
268411900SRaymond.Chen@Sun.COM 		 */
268511900SRaymond.Chen@Sun.COM 		if ((ep = usb_lookup_ep_data(ecmp->ecm_dip,
268611900SRaymond.Chen@Sun.COM 		    ecmp->ecm_dev_data, ifc, alt, 0,
268711900SRaymond.Chen@Sun.COM 		    USB_EP_ATTR_INTR, USB_EP_DIR_IN)) != NULL) {
268811900SRaymond.Chen@Sun.COM 			intr_ep = ep;
268911900SRaymond.Chen@Sun.COM 		}
269011900SRaymond.Chen@Sun.COM 
269111900SRaymond.Chen@Sun.COM 
269211900SRaymond.Chen@Sun.COM 		ecmp->ecm_data_if_no = ifc;
269311900SRaymond.Chen@Sun.COM 		ecmp->ecm_data_if_alt = alt;
269411900SRaymond.Chen@Sun.COM 		ecmp->ecm_intr_ep = intr_ep;
269511900SRaymond.Chen@Sun.COM 		ecmp->ecm_ctrl_if_no = ifc;
269611900SRaymond.Chen@Sun.COM 		ecmp->ecm_bulk_in_ep = bulk_in;
269711900SRaymond.Chen@Sun.COM 		ecmp->ecm_bulk_out_ep = bulk_out;
269811900SRaymond.Chen@Sun.COM 
269911900SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
270011900SRaymond.Chen@Sun.COM 	}
270111900SRaymond.Chen@Sun.COM 
270211900SRaymond.Chen@Sun.COM 	return (USB_FAILURE);
270311900SRaymond.Chen@Sun.COM }
270411900SRaymond.Chen@Sun.COM 
270511900SRaymond.Chen@Sun.COM static int
usbecm_init_non_compatible_device(usbecm_state_t * ecmp)270611900SRaymond.Chen@Sun.COM usbecm_init_non_compatible_device(usbecm_state_t *ecmp)
270711900SRaymond.Chen@Sun.COM {
270811900SRaymond.Chen@Sun.COM 	usb_if_data_t *cur_if;
270911900SRaymond.Chen@Sun.COM 	uint16_t if_num, i;
271011900SRaymond.Chen@Sun.COM 
271111900SRaymond.Chen@Sun.COM 	/*
271211900SRaymond.Chen@Sun.COM 	 * If device don't conform to spec, search pairs of bulk in/out
271311900SRaymond.Chen@Sun.COM 	 * endpoints and fill related structure. We suppose this driver
271411900SRaymond.Chen@Sun.COM 	 * is bound to a interface.
271511900SRaymond.Chen@Sun.COM 	 */
271611900SRaymond.Chen@Sun.COM 	cur_if = ecmp->ecm_dev_data->dev_curr_cfg->cfg_if;
271711900SRaymond.Chen@Sun.COM 	if_num = ecmp->ecm_dev_data->dev_curr_cfg->cfg_n_if;
271811900SRaymond.Chen@Sun.COM 
271911900SRaymond.Chen@Sun.COM 	/* search each interface which have bulk in and out */
272011900SRaymond.Chen@Sun.COM 	for (i = 0; i < if_num; i++) {
272111900SRaymond.Chen@Sun.COM 		if (usbecm_find_bulk_in_out_eps(ecmp, i,
272211900SRaymond.Chen@Sun.COM 		    cur_if) == USB_SUCCESS) {
272311900SRaymond.Chen@Sun.COM 
272411900SRaymond.Chen@Sun.COM 			break;
272511900SRaymond.Chen@Sun.COM 		}
272611900SRaymond.Chen@Sun.COM 		cur_if++;
272711900SRaymond.Chen@Sun.COM 	}
272811900SRaymond.Chen@Sun.COM 
272911900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
273011900SRaymond.Chen@Sun.COM 	    "usbecm_init_non_compatible_device: ctrl_if=%d,"
273111900SRaymond.Chen@Sun.COM 	    " data_if=%d, alt=%d", ecmp->ecm_ctrl_if_no,
273211900SRaymond.Chen@Sun.COM 	    ecmp->ecm_data_if_no, ecmp->ecm_data_if_alt);
273311900SRaymond.Chen@Sun.COM 
273411900SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
273511900SRaymond.Chen@Sun.COM }
273611900SRaymond.Chen@Sun.COM 
273711900SRaymond.Chen@Sun.COM static boolean_t
usbecm_is_compatible(usbecm_state_t * ecmp)273811900SRaymond.Chen@Sun.COM usbecm_is_compatible(usbecm_state_t *ecmp)
273911900SRaymond.Chen@Sun.COM {
274011900SRaymond.Chen@Sun.COM 	usb_cfg_data_t *cfg_data;
274111900SRaymond.Chen@Sun.COM 	usb_if_data_t *intf;
274211900SRaymond.Chen@Sun.COM 	usb_alt_if_data_t *alt;
274311900SRaymond.Chen@Sun.COM 	int alt_num, if_num, cfg_num;
274411900SRaymond.Chen@Sun.COM 	int i, j, cfg_index;
274511900SRaymond.Chen@Sun.COM 
274611900SRaymond.Chen@Sun.COM 	cfg_num = ecmp->ecm_dev_data->dev_n_cfg;
274711900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
274811900SRaymond.Chen@Sun.COM 	    "usbecm_is_compatible: entry, cfg_num=%d", cfg_num);
274911900SRaymond.Chen@Sun.COM 
275011900SRaymond.Chen@Sun.COM 	for (cfg_index = 0; cfg_index < cfg_num; cfg_index++) {
275111900SRaymond.Chen@Sun.COM 		cfg_data = &(ecmp->ecm_dev_data->dev_cfg[cfg_index]);
275211900SRaymond.Chen@Sun.COM 
275311900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
275411900SRaymond.Chen@Sun.COM 		    "usbecm_is_compatible: cfg_index=%d, value=%d",
275511900SRaymond.Chen@Sun.COM 		    cfg_index, cfg_data->cfg_descr.bConfigurationValue);
275611900SRaymond.Chen@Sun.COM 
275711900SRaymond.Chen@Sun.COM 		intf = cfg_data->cfg_if;
275811900SRaymond.Chen@Sun.COM 		if_num = cfg_data->cfg_n_if;
275911900SRaymond.Chen@Sun.COM 
276011900SRaymond.Chen@Sun.COM 		for (i = 0; i < if_num; i++) {
276111900SRaymond.Chen@Sun.COM 			alt_num = intf->if_n_alt;
276211900SRaymond.Chen@Sun.COM 			for (j = 0; j < alt_num; j++) {
276311900SRaymond.Chen@Sun.COM 			alt = &intf->if_alt[j];
276411900SRaymond.Chen@Sun.COM 			if ((alt->altif_descr.bInterfaceClass == 0x02) &&
276511900SRaymond.Chen@Sun.COM 			    (alt->altif_descr.bInterfaceSubClass == 0x06)) {
276611900SRaymond.Chen@Sun.COM 				ecmp->ecm_cfg_index = cfg_index;
276711900SRaymond.Chen@Sun.COM 
276811900SRaymond.Chen@Sun.COM 				USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
276911900SRaymond.Chen@Sun.COM 				    "usbecm_is_compatible: cfg_index=%d",
277011900SRaymond.Chen@Sun.COM 				    cfg_index);
277111900SRaymond.Chen@Sun.COM 
277211900SRaymond.Chen@Sun.COM 				return (B_TRUE);
277311900SRaymond.Chen@Sun.COM 			}
277411900SRaymond.Chen@Sun.COM 			}
277511900SRaymond.Chen@Sun.COM 			intf++;
277611900SRaymond.Chen@Sun.COM 		}
277711900SRaymond.Chen@Sun.COM 	}
277811900SRaymond.Chen@Sun.COM 
277911900SRaymond.Chen@Sun.COM 	return (B_FALSE);
278011900SRaymond.Chen@Sun.COM }
278111900SRaymond.Chen@Sun.COM 
278211900SRaymond.Chen@Sun.COM 
278311900SRaymond.Chen@Sun.COM static int
usbecm_usb_init(usbecm_state_t * ecmp)278411900SRaymond.Chen@Sun.COM usbecm_usb_init(usbecm_state_t *ecmp)
278511900SRaymond.Chen@Sun.COM {
278611900SRaymond.Chen@Sun.COM 
278711900SRaymond.Chen@Sun.COM 	if (usb_client_attach(ecmp->ecm_dip, USBDRV_VERSION, 0) !=
278811900SRaymond.Chen@Sun.COM 	    USB_SUCCESS) {
278911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
279011900SRaymond.Chen@Sun.COM 		"usbecm_usb_init: fail to attach");
279111900SRaymond.Chen@Sun.COM 
279211900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
279311900SRaymond.Chen@Sun.COM 	}
279411900SRaymond.Chen@Sun.COM 
279511900SRaymond.Chen@Sun.COM 	/* Get the configuration information of device */
279611900SRaymond.Chen@Sun.COM 	if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data,
279711900SRaymond.Chen@Sun.COM 	    USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
279811900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
279911900SRaymond.Chen@Sun.COM 		"usbecm_usb_init: fail to get_dev_data");
280011900SRaymond.Chen@Sun.COM 
280111900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
280211900SRaymond.Chen@Sun.COM 	}
280311900SRaymond.Chen@Sun.COM 	ecmp->ecm_def_ph = ecmp->ecm_dev_data->dev_default_ph;
280411900SRaymond.Chen@Sun.COM 	ecmp->ecm_dev_state = USB_DEV_ONLINE;
280511900SRaymond.Chen@Sun.COM 
280611900SRaymond.Chen@Sun.COM 	mutex_init(&ecmp->ecm_mutex, NULL, MUTEX_DRIVER,
280711900SRaymond.Chen@Sun.COM 	    ecmp->ecm_dev_data->dev_iblock_cookie);
280811900SRaymond.Chen@Sun.COM 
280911900SRaymond.Chen@Sun.COM 	if ((strcmp(ddi_binding_name(ecmp->ecm_dip),
281011900SRaymond.Chen@Sun.COM 	    "usbif,class2.6") == 0) ||
281111900SRaymond.Chen@Sun.COM 	    ((strcmp(ddi_binding_name(ecmp->ecm_dip),
281211900SRaymond.Chen@Sun.COM 	    "usb,class2.6.0") == 0))) {
281311900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
281411900SRaymond.Chen@Sun.COM 		    "usbecm_usb_init: A CDC ECM device is attached");
281511900SRaymond.Chen@Sun.COM 		ecmp->ecm_compatibility = B_TRUE;
281611900SRaymond.Chen@Sun.COM 	} else if (usb_owns_device(ecmp->ecm_dip) &&
281711900SRaymond.Chen@Sun.COM 	    usbecm_is_compatible(ecmp)) {
281811900SRaymond.Chen@Sun.COM 		/*
281911900SRaymond.Chen@Sun.COM 		 * Current Sun SP ECM device has two configurations. Hence
282011900SRaymond.Chen@Sun.COM 		 * USBA doesn't create interface level compatible names
282111900SRaymond.Chen@Sun.COM 		 * for it, see usba_ready_device_node(). We have to check
282211900SRaymond.Chen@Sun.COM 		 * manually to see if compatible interfaces exist, when
282311900SRaymond.Chen@Sun.COM 		 * the driver owns the entire device.
282411900SRaymond.Chen@Sun.COM 		 */
282511900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
282611900SRaymond.Chen@Sun.COM 		    "usbecm_usb_init: A CDC ECM device is attached");
282711900SRaymond.Chen@Sun.COM 		ecmp->ecm_compatibility = B_TRUE;
282811900SRaymond.Chen@Sun.COM 	} else {
282911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
283011900SRaymond.Chen@Sun.COM 		    "usbecm_usb_init: A nonstandard device is attached to "
283111900SRaymond.Chen@Sun.COM 		    "usbecm(7D) driver. This device doesn't conform to "
283211900SRaymond.Chen@Sun.COM 		    "usb cdc spec.");
283311900SRaymond.Chen@Sun.COM 		ecmp->ecm_compatibility = B_FALSE;
283411900SRaymond.Chen@Sun.COM 
283511900SRaymond.Chen@Sun.COM 		/* generate a random MAC addr */
283611900SRaymond.Chen@Sun.COM 		generate_ether_addr(ecmp->ecm_srcaddr);
283711900SRaymond.Chen@Sun.COM 	}
283811900SRaymond.Chen@Sun.COM 
283911900SRaymond.Chen@Sun.COM 	if ((ecmp->ecm_compatibility == B_TRUE) &&
284011900SRaymond.Chen@Sun.COM 	    (usbecm_get_descriptors(ecmp) != USB_SUCCESS)) {
284111900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
284211900SRaymond.Chen@Sun.COM 		    "usbecm_usb_init: A compatible device is attached, but "
284311900SRaymond.Chen@Sun.COM 		    "fail to get standard descriptors");
284411900SRaymond.Chen@Sun.COM 
284511900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
284611900SRaymond.Chen@Sun.COM 	}
284711900SRaymond.Chen@Sun.COM 
284811900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_compatibility == B_FALSE) {
284911900SRaymond.Chen@Sun.COM 		(void) usbecm_init_non_compatible_device(ecmp);
285011900SRaymond.Chen@Sun.COM 	}
285111900SRaymond.Chen@Sun.COM 
285211900SRaymond.Chen@Sun.COM 	/* Create power management components */
285311900SRaymond.Chen@Sun.COM 	if (usbecm_create_pm_components(ecmp) != USB_SUCCESS) {
285411900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
285511900SRaymond.Chen@Sun.COM 		    "usbecm_usb_init: create pm components failed.");
285611900SRaymond.Chen@Sun.COM 
285711900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
285811900SRaymond.Chen@Sun.COM 	}
285911900SRaymond.Chen@Sun.COM 
286011900SRaymond.Chen@Sun.COM 	/* Register to get callbacks for USB events */
286111900SRaymond.Chen@Sun.COM 	if (usb_register_event_cbs(ecmp->ecm_dip, &usbecm_events, 0)
286211900SRaymond.Chen@Sun.COM 	    != USB_SUCCESS) {
286311900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
286411900SRaymond.Chen@Sun.COM 		    "usbsecm_attach: register event callback failed.");
286511900SRaymond.Chen@Sun.COM 
286611900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
286711900SRaymond.Chen@Sun.COM 	}
286811900SRaymond.Chen@Sun.COM 	ecmp->ecm_init_flags |= USBECM_INIT_EVENTS;
286911900SRaymond.Chen@Sun.COM 
287011900SRaymond.Chen@Sun.COM 
287111900SRaymond.Chen@Sun.COM 	/* Get max data size of bulk transfer */
287211900SRaymond.Chen@Sun.COM 	if (usb_pipe_get_max_bulk_transfer_size(ecmp->ecm_dip,
287311900SRaymond.Chen@Sun.COM 	    &ecmp->ecm_xfer_sz) != USB_SUCCESS) {
287411900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
287511900SRaymond.Chen@Sun.COM 		    "usbsecm_ds_attach: get max size of transfer failed.");
287611900SRaymond.Chen@Sun.COM 
287711900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
287811900SRaymond.Chen@Sun.COM 	}
287911900SRaymond.Chen@Sun.COM 
288011900SRaymond.Chen@Sun.COM 
288111900SRaymond.Chen@Sun.COM 	ecmp->ecm_ser_acc = usb_init_serialization(ecmp->ecm_dip,
288211900SRaymond.Chen@Sun.COM 	    USB_INIT_SER_CHECK_SAME_THREAD);
288311900SRaymond.Chen@Sun.COM 	ecmp->ecm_init_flags |= USBECM_INIT_SER;
288411900SRaymond.Chen@Sun.COM 
288511900SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
288611900SRaymond.Chen@Sun.COM }
288711900SRaymond.Chen@Sun.COM 
288811900SRaymond.Chen@Sun.COM 
288911900SRaymond.Chen@Sun.COM /*
289011900SRaymond.Chen@Sun.COM  * Open operation pipes. Each ECM device should have Bulk In, Bulk Out
289111900SRaymond.Chen@Sun.COM  * and Interrupt In endpoints
289211900SRaymond.Chen@Sun.COM  */
289311900SRaymond.Chen@Sun.COM static int
usbecm_open_pipes(usbecm_state_t * ecmp)289411900SRaymond.Chen@Sun.COM usbecm_open_pipes(usbecm_state_t *ecmp)
289511900SRaymond.Chen@Sun.COM {
289611900SRaymond.Chen@Sun.COM 	int		rval = USB_SUCCESS;
289711900SRaymond.Chen@Sun.COM 	usb_ep_data_t	*in_data, *out_data, *intr_pipe;
289811900SRaymond.Chen@Sun.COM 	usb_pipe_policy_t policy;
289911900SRaymond.Chen@Sun.COM 	int		altif;
290011900SRaymond.Chen@Sun.COM 
290111900SRaymond.Chen@Sun.COM 	ASSERT(!mutex_owned(&ecmp->ecm_mutex));
290211900SRaymond.Chen@Sun.COM 
290311900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
290411900SRaymond.Chen@Sun.COM 	    "usbsecm_open_pipes: ecmp = 0x%p", (void *)ecmp);
290511900SRaymond.Chen@Sun.COM 
290611900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_compatibility == B_TRUE) {
290711900SRaymond.Chen@Sun.COM 	/* compatible device has minimum of 2 altsetting, select alt 1 */
290811900SRaymond.Chen@Sun.COM 		altif = 1;
290911900SRaymond.Chen@Sun.COM 	} else {
291011900SRaymond.Chen@Sun.COM 		altif = ecmp->ecm_data_if_alt;
291111900SRaymond.Chen@Sun.COM 	}
291211900SRaymond.Chen@Sun.COM 	intr_pipe = ecmp->ecm_intr_ep;
291311900SRaymond.Chen@Sun.COM 	in_data = ecmp->ecm_bulk_in_ep;
291411900SRaymond.Chen@Sun.COM 	out_data = ecmp->ecm_bulk_out_ep;
291511900SRaymond.Chen@Sun.COM 
291611900SRaymond.Chen@Sun.COM 	/* Bulk in and out must exist simultaneously. */
291711900SRaymond.Chen@Sun.COM 	if ((in_data == NULL) || (out_data == NULL)) {
291811900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
291911900SRaymond.Chen@Sun.COM 		    "usbsecm_open_pipes: look up bulk pipe failed in "
292011900SRaymond.Chen@Sun.COM 		    "interface %d ",
292111900SRaymond.Chen@Sun.COM 		    ecmp->ecm_data_if_no);
292211900SRaymond.Chen@Sun.COM 
292311900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
292411900SRaymond.Chen@Sun.COM 	}
292511900SRaymond.Chen@Sun.COM 	/*
292611900SRaymond.Chen@Sun.COM 	 * If device conform to ecm spec, it must have an interrupt pipe
292711900SRaymond.Chen@Sun.COM 	 * for this device.
292811900SRaymond.Chen@Sun.COM 	 */
292911900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_compatibility == B_TRUE && intr_pipe == NULL) {
293011900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
293111900SRaymond.Chen@Sun.COM 		    "usbecm_open_pipes: look up interrupt pipe failed in "
293211900SRaymond.Chen@Sun.COM 		    "interface %d", ecmp->ecm_ctrl_if_no);
293311900SRaymond.Chen@Sun.COM 
293411900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
293511900SRaymond.Chen@Sun.COM 	}
293611900SRaymond.Chen@Sun.COM 
293711900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
293811900SRaymond.Chen@Sun.COM 	    "usbsecm_open_pipes: open intr %02x, bulkin %02x bulkout %02x",
293911900SRaymond.Chen@Sun.COM 	    intr_pipe?intr_pipe->ep_descr.bEndpointAddress:0,
294011900SRaymond.Chen@Sun.COM 	    in_data->ep_descr.bEndpointAddress,
294111900SRaymond.Chen@Sun.COM 	    out_data->ep_descr.bEndpointAddress);
294211900SRaymond.Chen@Sun.COM 
294311900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
294411900SRaymond.Chen@Sun.COM 	    "usbsecm_open_pipes: set data if(%d) alt(%d) ",
294511900SRaymond.Chen@Sun.COM 	    ecmp->ecm_data_if_no, altif);
294611900SRaymond.Chen@Sun.COM 
294711900SRaymond.Chen@Sun.COM 	if ((rval = usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no,
294811900SRaymond.Chen@Sun.COM 	    altif, USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) {
294911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
295011900SRaymond.Chen@Sun.COM 		    "usbecm_open_pipes: set alternate failed (%d)",
295111900SRaymond.Chen@Sun.COM 		    rval);
295211900SRaymond.Chen@Sun.COM 
295311900SRaymond.Chen@Sun.COM 		return (rval);
295411900SRaymond.Chen@Sun.COM 	}
295511900SRaymond.Chen@Sun.COM 
295611900SRaymond.Chen@Sun.COM 	policy.pp_max_async_reqs = 2;
295711900SRaymond.Chen@Sun.COM 
295811900SRaymond.Chen@Sun.COM 	/* Open bulk in endpoint */
295911900SRaymond.Chen@Sun.COM 	if (usb_pipe_open(ecmp->ecm_dip, &in_data->ep_descr, &policy,
296011900SRaymond.Chen@Sun.COM 	    USB_FLAGS_SLEEP, &ecmp->ecm_bulkin_ph) != USB_SUCCESS) {
296111900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
296211900SRaymond.Chen@Sun.COM 		    "usbecm_open_pipes: open bulkin pipe failed!");
296311900SRaymond.Chen@Sun.COM 
296411900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
296511900SRaymond.Chen@Sun.COM 	}
296611900SRaymond.Chen@Sun.COM 
296711900SRaymond.Chen@Sun.COM 	/* Open bulk out endpoint */
296811900SRaymond.Chen@Sun.COM 	if (usb_pipe_open(ecmp->ecm_dip, &out_data->ep_descr, &policy,
296911900SRaymond.Chen@Sun.COM 	    USB_FLAGS_SLEEP, &ecmp->ecm_bulkout_ph) != USB_SUCCESS) {
297011900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
297111900SRaymond.Chen@Sun.COM 		    "usbecm_open_pipes: open bulkout pipe failed!");
297211900SRaymond.Chen@Sun.COM 
297311900SRaymond.Chen@Sun.COM 		usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
297411900SRaymond.Chen@Sun.COM 		    USB_FLAGS_SLEEP, NULL, NULL);
297511900SRaymond.Chen@Sun.COM 
297611900SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
297711900SRaymond.Chen@Sun.COM 	}
297811900SRaymond.Chen@Sun.COM 
297911900SRaymond.Chen@Sun.COM 	/* Open interrupt endpoint if found. */
298011900SRaymond.Chen@Sun.COM 	if (intr_pipe != NULL) {
298111900SRaymond.Chen@Sun.COM 		if (usb_pipe_open(ecmp->ecm_dip, &intr_pipe->ep_descr, &policy,
298211900SRaymond.Chen@Sun.COM 		    USB_FLAGS_SLEEP, &ecmp->ecm_intr_ph) != USB_SUCCESS) {
298311900SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
298411900SRaymond.Chen@Sun.COM 			    "usbecm_open_pipes: "
298511900SRaymond.Chen@Sun.COM 			    "open intr pipe failed");
298611900SRaymond.Chen@Sun.COM 
298711900SRaymond.Chen@Sun.COM 			usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
298811900SRaymond.Chen@Sun.COM 			    USB_FLAGS_SLEEP, NULL, NULL);
298911900SRaymond.Chen@Sun.COM 			usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph,
299011900SRaymond.Chen@Sun.COM 			    USB_FLAGS_SLEEP, NULL, NULL);
299111900SRaymond.Chen@Sun.COM 
299211900SRaymond.Chen@Sun.COM 			return (USB_FAILURE);
299311900SRaymond.Chen@Sun.COM 		}
299411900SRaymond.Chen@Sun.COM 	}
299511900SRaymond.Chen@Sun.COM 
299611900SRaymond.Chen@Sun.COM 	/* initialize the pipe related data */
299711900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
299811900SRaymond.Chen@Sun.COM 	ecmp->ecm_bulkin_sz = in_data->ep_descr.wMaxPacketSize;
299911900SRaymond.Chen@Sun.COM 	ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
300011900SRaymond.Chen@Sun.COM 	ecmp->ecm_bulkout_state = USBECM_PIPE_IDLE;
300111900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_intr_ph != NULL) {
300211900SRaymond.Chen@Sun.COM 		ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
300311900SRaymond.Chen@Sun.COM 	}
300411900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
300511900SRaymond.Chen@Sun.COM 
300611900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_intr_ph != NULL) {
300711900SRaymond.Chen@Sun.COM 
300811900SRaymond.Chen@Sun.COM 		usbecm_pipe_start_polling(ecmp);
300911900SRaymond.Chen@Sun.COM 	}
301011900SRaymond.Chen@Sun.COM 
301111900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
301211900SRaymond.Chen@Sun.COM 	    "usbsecm_open_pipes: end");
301311900SRaymond.Chen@Sun.COM 
301411900SRaymond.Chen@Sun.COM 	return (rval);
301511900SRaymond.Chen@Sun.COM }
301611900SRaymond.Chen@Sun.COM 
301711900SRaymond.Chen@Sun.COM 
301811900SRaymond.Chen@Sun.COM /*
301911900SRaymond.Chen@Sun.COM  * usbsecm_close_pipes:
302011900SRaymond.Chen@Sun.COM  *	Close pipes
302111900SRaymond.Chen@Sun.COM  *	Each device could include three pipes: bulk in, bulk out and interrupt.
302211900SRaymond.Chen@Sun.COM  */
302311900SRaymond.Chen@Sun.COM static void
usbecm_close_pipes(usbecm_state_t * ecmp)302411900SRaymond.Chen@Sun.COM usbecm_close_pipes(usbecm_state_t *ecmp)
302511900SRaymond.Chen@Sun.COM {
302611900SRaymond.Chen@Sun.COM 
302711900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
302811900SRaymond.Chen@Sun.COM 
302911900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
303011900SRaymond.Chen@Sun.COM 	    "usbsecm_close_pipes: ecm_bulkin_state = %d",
303111900SRaymond.Chen@Sun.COM 	    ecmp->ecm_bulkin_state);
303211900SRaymond.Chen@Sun.COM 
303311900SRaymond.Chen@Sun.COM 	/*
303411900SRaymond.Chen@Sun.COM 	 * Check the status of the pipes. If pipe is closing or closed,
303511900SRaymond.Chen@Sun.COM 	 * return directly.
303611900SRaymond.Chen@Sun.COM 	 */
303711900SRaymond.Chen@Sun.COM 	if ((ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSED) ||
303811900SRaymond.Chen@Sun.COM 	    (ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSING)) {
303911900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CLOSE, ecmp->ecm_lh,
304011900SRaymond.Chen@Sun.COM 		    "usbsecm_close_pipes: pipe is closing or has closed");
304111900SRaymond.Chen@Sun.COM 		mutex_exit(&ecmp->ecm_mutex);
304211900SRaymond.Chen@Sun.COM 
304311900SRaymond.Chen@Sun.COM 		return;
304411900SRaymond.Chen@Sun.COM 	}
304511900SRaymond.Chen@Sun.COM 
304611900SRaymond.Chen@Sun.COM 	ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSING;
304711900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
304811900SRaymond.Chen@Sun.COM 
304911900SRaymond.Chen@Sun.COM 	/* reset the data interface's altsetting to 0 */
305011900SRaymond.Chen@Sun.COM 	if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) &&
305111900SRaymond.Chen@Sun.COM 	    (usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no,
305211900SRaymond.Chen@Sun.COM 	    0, USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS)) {
305311900SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
305411900SRaymond.Chen@Sun.COM 		    "usbecm_close_pipes: reset alternate failed ");
305511900SRaymond.Chen@Sun.COM 	}
305611900SRaymond.Chen@Sun.COM 
305711900SRaymond.Chen@Sun.COM 	/* Close pipes */
305811900SRaymond.Chen@Sun.COM 	usb_pipe_reset(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
305911900SRaymond.Chen@Sun.COM 	    USB_FLAGS_SLEEP, NULL, 0);
306011900SRaymond.Chen@Sun.COM 	usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
306111900SRaymond.Chen@Sun.COM 	    USB_FLAGS_SLEEP, NULL, 0);
306211900SRaymond.Chen@Sun.COM 	usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph,
306311900SRaymond.Chen@Sun.COM 	    USB_FLAGS_SLEEP, NULL, 0);
306411900SRaymond.Chen@Sun.COM 
306511900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_intr_ph != NULL) {
306611900SRaymond.Chen@Sun.COM 		usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph,
306711900SRaymond.Chen@Sun.COM 		    USB_FLAGS_SLEEP);
306811900SRaymond.Chen@Sun.COM 		usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_intr_ph,
306911900SRaymond.Chen@Sun.COM 		    USB_FLAGS_SLEEP, NULL, 0);
307011900SRaymond.Chen@Sun.COM 	}
307111900SRaymond.Chen@Sun.COM 
307211900SRaymond.Chen@Sun.COM 	mutex_enter(&ecmp->ecm_mutex);
307311900SRaymond.Chen@Sun.COM 	/* Reset the status of pipes to closed */
307411900SRaymond.Chen@Sun.COM 	ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSED;
307511900SRaymond.Chen@Sun.COM 	ecmp->ecm_bulkin_ph = NULL;
307611900SRaymond.Chen@Sun.COM 	ecmp->ecm_bulkout_state = USBECM_PIPE_CLOSED;
307711900SRaymond.Chen@Sun.COM 	ecmp->ecm_bulkout_ph = NULL;
307811900SRaymond.Chen@Sun.COM 	if (ecmp->ecm_intr_ph != NULL) {
307911900SRaymond.Chen@Sun.COM 		ecmp->ecm_intr_state = USBECM_PIPE_CLOSED;
308011900SRaymond.Chen@Sun.COM 		ecmp->ecm_intr_ph = NULL;
308111900SRaymond.Chen@Sun.COM 	}
308211900SRaymond.Chen@Sun.COM 
308311900SRaymond.Chen@Sun.COM 	mutex_exit(&ecmp->ecm_mutex);
308411900SRaymond.Chen@Sun.COM 
308511900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
308611900SRaymond.Chen@Sun.COM 	    "usbsecm_close_pipes: pipes have been closed.");
308711900SRaymond.Chen@Sun.COM }
308811900SRaymond.Chen@Sun.COM 
308911900SRaymond.Chen@Sun.COM 
309011900SRaymond.Chen@Sun.COM static int
usbecm_ctrl_write(usbecm_state_t * ecmp,uchar_t request,uint16_t value,mblk_t ** data)309111900SRaymond.Chen@Sun.COM usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request,
309211900SRaymond.Chen@Sun.COM     uint16_t value, mblk_t **data)
309311900SRaymond.Chen@Sun.COM {
309411900SRaymond.Chen@Sun.COM 	usb_ctrl_setup_t setup;
309511900SRaymond.Chen@Sun.COM 	usb_cb_flags_t	cb_flags;
309611900SRaymond.Chen@Sun.COM 	usb_cr_t	cr;
309711900SRaymond.Chen@Sun.COM 	int		rval;
309811900SRaymond.Chen@Sun.COM 
309911900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
310011900SRaymond.Chen@Sun.COM 	    "usbecm_ctrl_write: ");
310111900SRaymond.Chen@Sun.COM 
310211900SRaymond.Chen@Sun.COM 	/* initialize the control request. */
310311900SRaymond.Chen@Sun.COM 	setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV |
310411900SRaymond.Chen@Sun.COM 	    USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
310511900SRaymond.Chen@Sun.COM 	setup.bRequest = request;
310611900SRaymond.Chen@Sun.COM 	setup.wValue = value;
310711900SRaymond.Chen@Sun.COM 	setup.wIndex = ecmp->ecm_ctrl_if_no;
310811900SRaymond.Chen@Sun.COM 	setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0;
310911900SRaymond.Chen@Sun.COM 	setup.attrs = 0;
311011900SRaymond.Chen@Sun.COM 
311111900SRaymond.Chen@Sun.COM 	rval = usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data,
311211900SRaymond.Chen@Sun.COM 	    &cr, &cb_flags, 0);
311311900SRaymond.Chen@Sun.COM 
311411900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
311511900SRaymond.Chen@Sun.COM 	    "usbecm_ctrl_write: rval = %d", rval);
311611900SRaymond.Chen@Sun.COM 
311711900SRaymond.Chen@Sun.COM 	return (rval);
311811900SRaymond.Chen@Sun.COM }
311911900SRaymond.Chen@Sun.COM 
312011900SRaymond.Chen@Sun.COM static int
usbecm_ctrl_read(usbecm_state_t * ecmp,uchar_t request,uint16_t value,mblk_t ** data,int len)312111900SRaymond.Chen@Sun.COM usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request,
312211900SRaymond.Chen@Sun.COM     uint16_t value, mblk_t **data, int len)
312311900SRaymond.Chen@Sun.COM {
312411900SRaymond.Chen@Sun.COM 	usb_ctrl_setup_t setup;
312511900SRaymond.Chen@Sun.COM 	usb_cb_flags_t	cb_flags;
312611900SRaymond.Chen@Sun.COM 	usb_cr_t	cr;
312711900SRaymond.Chen@Sun.COM 
312811900SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
312911900SRaymond.Chen@Sun.COM 	    "usbecm_ctrl_read: ");
313011900SRaymond.Chen@Sun.COM 
313111900SRaymond.Chen@Sun.COM 	/* initialize the control request. */
313211900SRaymond.Chen@Sun.COM 	setup.bmRequestType = USB_DEV_REQ_DEV_TO_HOST |
313311900SRaymond.Chen@Sun.COM 	    USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
313411900SRaymond.Chen@Sun.COM 	setup.bRequest = request;
313511900SRaymond.Chen@Sun.COM 	setup.wValue = value;
313611900SRaymond.Chen@Sun.COM 	setup.wIndex = ecmp->ecm_ctrl_if_no;
313711900SRaymond.Chen@Sun.COM 	setup.wLength = (uint16_t)len;
313811900SRaymond.Chen@Sun.COM 	setup.attrs = 0;
313911900SRaymond.Chen@Sun.COM 
314011900SRaymond.Chen@Sun.COM 	return (usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data,
314111900SRaymond.Chen@Sun.COM 	    &cr, &cb_flags, 0));
314211900SRaymond.Chen@Sun.COM }
314311900SRaymond.Chen@Sun.COM 
314411900SRaymond.Chen@Sun.COM /* Get specific statistic data from device */
314511900SRaymond.Chen@Sun.COM static int
usbecm_get_statistics(usbecm_state_t * ecmp,uint32_t fs,uint32_t * stat_data)314611900SRaymond.Chen@Sun.COM usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs, uint32_t *stat_data)
314711900SRaymond.Chen@Sun.COM {
314811900SRaymond.Chen@Sun.COM 	mblk_t *data = NULL;
314911900SRaymond.Chen@Sun.COM 	uint32_t stat;
315011900SRaymond.Chen@Sun.COM 
315111900SRaymond.Chen@Sun.COM 	/* first check to see if this stat is collected by device */
315211900SRaymond.Chen@Sun.COM 	if ((ecmp->ecm_compatibility == B_TRUE) &&
315311900SRaymond.Chen@Sun.COM 	    (ecmp->ecm_desc.bmEthernetStatistics & ECM_STAT_CAP_MASK(fs))) {
315411900SRaymond.Chen@Sun.COM 		if (usbecm_ctrl_read(ecmp, CDC_ECM_GET_ETH_STAT,
315511900SRaymond.Chen@Sun.COM 		    ecmp->ecm_ctrl_if_no, &data, 4) != USB_SUCCESS) {
315611900SRaymond.Chen@Sun.COM 
315711900SRaymond.Chen@Sun.COM 			return (USB_FAILURE);
315811900SRaymond.Chen@Sun.COM 		}
315911900SRaymond.Chen@Sun.COM 		stat = (data->b_rptr[3] << 24) | (data->b_rptr[2] << 16) |
316011900SRaymond.Chen@Sun.COM 		    (data->b_rptr[1] << 8) | (data->b_rptr[0]);
316111900SRaymond.Chen@Sun.COM 		*stat_data = stat;
316211900SRaymond.Chen@Sun.COM 
316311900SRaymond.Chen@Sun.COM 		freemsg(data);
316411900SRaymond.Chen@Sun.COM 
316511900SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
316611900SRaymond.Chen@Sun.COM 	}
316711900SRaymond.Chen@Sun.COM 
316811900SRaymond.Chen@Sun.COM 	return (USB_FAILURE);
316911900SRaymond.Chen@Sun.COM }
3170