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