xref: /titanic_52/usr/src/uts/common/os/ddi_hp_impl.c (revision 4f6e674fbbae301788090311a9ecf340d0ef7f8b)
1*26947304SEvan Yan /*
2*26947304SEvan Yan  * CDDL HEADER START
3*26947304SEvan Yan  *
4*26947304SEvan Yan  * The contents of this file are subject to the terms of the
5*26947304SEvan Yan  * Common Development and Distribution License (the "License").
6*26947304SEvan Yan  * You may not use this file except in compliance with the License.
7*26947304SEvan Yan  *
8*26947304SEvan Yan  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*26947304SEvan Yan  * or http://www.opensolaris.org/os/licensing.
10*26947304SEvan Yan  * See the License for the specific language governing permissions
11*26947304SEvan Yan  * and limitations under the License.
12*26947304SEvan Yan  *
13*26947304SEvan Yan  * When distributing Covered Code, include this CDDL HEADER in each
14*26947304SEvan Yan  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*26947304SEvan Yan  * If applicable, add the following below this CDDL HEADER, with the
16*26947304SEvan Yan  * fields enclosed by brackets "[]" replaced with your own identifying
17*26947304SEvan Yan  * information: Portions Copyright [yyyy] [name of copyright owner]
18*26947304SEvan Yan  *
19*26947304SEvan Yan  * CDDL HEADER END
20*26947304SEvan Yan  */
21*26947304SEvan Yan /*
22*26947304SEvan Yan  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23*26947304SEvan Yan  * Use is subject to license terms.
24*26947304SEvan Yan  */
25*26947304SEvan Yan 
26*26947304SEvan Yan /*
27*26947304SEvan Yan  * Sun DDI hotplug implementation specific functions
28*26947304SEvan Yan  */
29*26947304SEvan Yan 
30*26947304SEvan Yan #include <sys/sysmacros.h>
31*26947304SEvan Yan #include <sys/types.h>
32*26947304SEvan Yan #include <sys/file.h>
33*26947304SEvan Yan #include <sys/param.h>
34*26947304SEvan Yan #include <sys/systm.h>
35*26947304SEvan Yan #include <sys/kmem.h>
36*26947304SEvan Yan #include <sys/cmn_err.h>
37*26947304SEvan Yan #include <sys/debug.h>
38*26947304SEvan Yan #include <sys/avintr.h>
39*26947304SEvan Yan #include <sys/autoconf.h>
40*26947304SEvan Yan #include <sys/ddi.h>
41*26947304SEvan Yan #include <sys/sunndi.h>
42*26947304SEvan Yan #include <sys/ndi_impldefs.h>
43*26947304SEvan Yan #include <sys/sysevent.h>
44*26947304SEvan Yan #include <sys/sysevent/eventdefs.h>
45*26947304SEvan Yan #include <sys/sysevent/dr.h>
46*26947304SEvan Yan #include <sys/fs/dv_node.h>
47*26947304SEvan Yan 
48*26947304SEvan Yan /*
49*26947304SEvan Yan  * Local function prototypes
50*26947304SEvan Yan  */
51*26947304SEvan Yan /* Connector operations */
52*26947304SEvan Yan static int ddihp_cn_pre_change_state(ddi_hp_cn_handle_t *hdlp,
53*26947304SEvan Yan     ddi_hp_cn_state_t target_state);
54*26947304SEvan Yan static int ddihp_cn_post_change_state(ddi_hp_cn_handle_t *hdlp,
55*26947304SEvan Yan     ddi_hp_cn_state_t new_state);
56*26947304SEvan Yan static int ddihp_cn_handle_state_change(ddi_hp_cn_handle_t *hdlp);
57*26947304SEvan Yan static int ddihp_cn_change_children_state(ddi_hp_cn_handle_t *hdlp,
58*26947304SEvan Yan     boolean_t online);
59*26947304SEvan Yan /* Port operations */
60*26947304SEvan Yan static int ddihp_port_change_state(ddi_hp_cn_handle_t *hdlp,
61*26947304SEvan Yan     ddi_hp_cn_state_t target_state);
62*26947304SEvan Yan static int ddihp_port_upgrade_state(ddi_hp_cn_handle_t *hdlp,
63*26947304SEvan Yan     ddi_hp_cn_state_t target_state);
64*26947304SEvan Yan static int ddihp_port_downgrade_state(ddi_hp_cn_handle_t *hdlp,
65*26947304SEvan Yan     ddi_hp_cn_state_t target_state);
66*26947304SEvan Yan /* Misc routines */
67*26947304SEvan Yan static void ddihp_update_last_change(ddi_hp_cn_handle_t *hdlp);
68*26947304SEvan Yan static boolean_t ddihp_check_status_prop(dev_info_t *dip);
69*26947304SEvan Yan 
70*26947304SEvan Yan /*
71*26947304SEvan Yan  * Global functions (called within hotplug framework)
72*26947304SEvan Yan  */
73*26947304SEvan Yan 
74*26947304SEvan Yan /*
75*26947304SEvan Yan  * Implement modctl() commands for hotplug.
76*26947304SEvan Yan  * Called by modctl_hp() in modctl.c
77*26947304SEvan Yan  */
78*26947304SEvan Yan int
79*26947304SEvan Yan ddihp_modctl(int hp_op, char *path, char *cn_name, uintptr_t arg,
80*26947304SEvan Yan     uintptr_t rval)
81*26947304SEvan Yan {
82*26947304SEvan Yan 	dev_info_t		*dip;
83*26947304SEvan Yan 	ddi_hp_cn_handle_t	*hdlp;
84*26947304SEvan Yan 	ddi_hp_op_t		op = (ddi_hp_op_t)hp_op;
85*26947304SEvan Yan 	int			count, rv, error;
86*26947304SEvan Yan 
87*26947304SEvan Yan 	/* Get the dip of nexus node */
88*26947304SEvan Yan 	dip = e_ddi_hold_devi_by_path(path, 0);
89*26947304SEvan Yan 
90*26947304SEvan Yan 	if (dip == NULL)
91*26947304SEvan Yan 		return (ENXIO);
92*26947304SEvan Yan 
93*26947304SEvan Yan 	DDI_HP_IMPLDBG((CE_CONT, "ddihp_modctl: dip %p op %x path %s "
94*26947304SEvan Yan 	    "cn_name %s arg %p rval %p\n", (void *)dip, hp_op, path, cn_name,
95*26947304SEvan Yan 	    (void *)arg, (void *)rval));
96*26947304SEvan Yan 
97*26947304SEvan Yan 	if (!NEXUS_HAS_HP_OP(dip)) {
98*26947304SEvan Yan 		ddi_release_devi(dip);
99*26947304SEvan Yan 		return (ENOTSUP);
100*26947304SEvan Yan 	}
101*26947304SEvan Yan 
102*26947304SEvan Yan 	/* Lock before access */
103*26947304SEvan Yan 	ndi_devi_enter(dip, &count);
104*26947304SEvan Yan 
105*26947304SEvan Yan 	hdlp = ddihp_cn_name_to_handle(dip, cn_name);
106*26947304SEvan Yan 
107*26947304SEvan Yan 	if (hp_op == DDI_HPOP_CN_CREATE_PORT) {
108*26947304SEvan Yan 		if (hdlp != NULL) {
109*26947304SEvan Yan 			/* this port already exists. */
110*26947304SEvan Yan 			error = EEXIST;
111*26947304SEvan Yan 
112*26947304SEvan Yan 			goto done;
113*26947304SEvan Yan 		}
114*26947304SEvan Yan 		rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
115*26947304SEvan Yan 		    dip, cn_name, op, NULL, NULL);
116*26947304SEvan Yan 	} else {
117*26947304SEvan Yan 		if (hdlp == NULL) {
118*26947304SEvan Yan 			/* Invalid Connection name */
119*26947304SEvan Yan 			error = ENXIO;
120*26947304SEvan Yan 
121*26947304SEvan Yan 			goto done;
122*26947304SEvan Yan 		}
123*26947304SEvan Yan 		if (hp_op == DDI_HPOP_CN_CHANGE_STATE) {
124*26947304SEvan Yan 			ddi_hp_cn_state_t target_state = (ddi_hp_cn_state_t)arg;
125*26947304SEvan Yan 			ddi_hp_cn_state_t result_state = 0;
126*26947304SEvan Yan 
127*26947304SEvan Yan 			DDIHP_CN_OPS(hdlp, op, (void *)&target_state,
128*26947304SEvan Yan 			    (void *)&result_state, rv);
129*26947304SEvan Yan 
130*26947304SEvan Yan 			DDI_HP_IMPLDBG((CE_CONT, "ddihp_modctl: target_state="
131*26947304SEvan Yan 			    "%x, result_state=%x, rv=%x \n",
132*26947304SEvan Yan 			    target_state, result_state, rv));
133*26947304SEvan Yan 		} else {
134*26947304SEvan Yan 			DDIHP_CN_OPS(hdlp, op, (void *)arg, (void *)rval, rv);
135*26947304SEvan Yan 		}
136*26947304SEvan Yan 	}
137*26947304SEvan Yan 	switch (rv) {
138*26947304SEvan Yan 	case DDI_SUCCESS:
139*26947304SEvan Yan 		error = 0;
140*26947304SEvan Yan 		break;
141*26947304SEvan Yan 	case DDI_EINVAL:
142*26947304SEvan Yan 		error = EINVAL;
143*26947304SEvan Yan 		break;
144*26947304SEvan Yan 	case DDI_EBUSY:
145*26947304SEvan Yan 		error = EBUSY;
146*26947304SEvan Yan 		break;
147*26947304SEvan Yan 	case DDI_ENOTSUP:
148*26947304SEvan Yan 		error = ENOTSUP;
149*26947304SEvan Yan 		break;
150*26947304SEvan Yan 	case DDI_ENOMEM:
151*26947304SEvan Yan 		error = ENOMEM;
152*26947304SEvan Yan 		break;
153*26947304SEvan Yan 	default:
154*26947304SEvan Yan 		error = EIO;
155*26947304SEvan Yan 	}
156*26947304SEvan Yan 
157*26947304SEvan Yan done:
158*26947304SEvan Yan 	ndi_devi_exit(dip, count);
159*26947304SEvan Yan 
160*26947304SEvan Yan 	ddi_release_devi(dip);
161*26947304SEvan Yan 
162*26947304SEvan Yan 	return (error);
163*26947304SEvan Yan }
164*26947304SEvan Yan 
165*26947304SEvan Yan /*
166*26947304SEvan Yan  * Return the state of Hotplug Connection (CN)
167*26947304SEvan Yan  */
168*26947304SEvan Yan int
169*26947304SEvan Yan ddihp_cn_getstate(ddi_hp_cn_handle_t *hdlp)
170*26947304SEvan Yan {
171*26947304SEvan Yan 	ddi_hp_cn_state_t	new_state;
172*26947304SEvan Yan 	int			ret;
173*26947304SEvan Yan 
174*26947304SEvan Yan 	DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: pdip %p hdlp %p\n",
175*26947304SEvan Yan 	    (void *)hdlp->cn_dip, (void *)hdlp));
176*26947304SEvan Yan 
177*26947304SEvan Yan 	ASSERT(DEVI_BUSY_OWNED(hdlp->cn_dip));
178*26947304SEvan Yan 
179*26947304SEvan Yan 	DDIHP_CN_OPS(hdlp, DDI_HPOP_CN_GET_STATE,
180*26947304SEvan Yan 	    NULL, (void *)&new_state, ret);
181*26947304SEvan Yan 	if (ret != DDI_SUCCESS) {
182*26947304SEvan Yan 		DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: "
183*26947304SEvan Yan 		    "CN %p getstate command failed\n", (void *)hdlp));
184*26947304SEvan Yan 
185*26947304SEvan Yan 		return (ret);
186*26947304SEvan Yan 	}
187*26947304SEvan Yan 
188*26947304SEvan Yan 	DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: hdlp %p "
189*26947304SEvan Yan 	    "current Connection state %x new Connection state %x\n",
190*26947304SEvan Yan 	    (void *)hdlp, hdlp->cn_info.cn_state, new_state));
191*26947304SEvan Yan 
192*26947304SEvan Yan 	if (new_state != hdlp->cn_info.cn_state) {
193*26947304SEvan Yan 		hdlp->cn_info.cn_state = new_state;
194*26947304SEvan Yan 		ddihp_update_last_change(hdlp);
195*26947304SEvan Yan 	}
196*26947304SEvan Yan 
197*26947304SEvan Yan 	return (ret);
198*26947304SEvan Yan }
199*26947304SEvan Yan 
200*26947304SEvan Yan /*
201*26947304SEvan Yan  * Implementation function for unregistering the Hotplug Connection (CN)
202*26947304SEvan Yan  */
203*26947304SEvan Yan int
204*26947304SEvan Yan ddihp_cn_unregister(ddi_hp_cn_handle_t *hdlp)
205*26947304SEvan Yan {
206*26947304SEvan Yan 	dev_info_t	*dip = hdlp->cn_dip;
207*26947304SEvan Yan 
208*26947304SEvan Yan 	DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_unregister: hdlp %p\n",
209*26947304SEvan Yan 	    (void *)hdlp));
210*26947304SEvan Yan 
211*26947304SEvan Yan 	ASSERT(DEVI_BUSY_OWNED(dip));
212*26947304SEvan Yan 
213*26947304SEvan Yan 	(void) ddihp_cn_getstate(hdlp);
214*26947304SEvan Yan 
215*26947304SEvan Yan 	if (hdlp->cn_info.cn_state > DDI_HP_CN_STATE_OFFLINE) {
216*26947304SEvan Yan 		DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_unregister: dip %p, hdlp %p "
217*26947304SEvan Yan 		    "state %x. Device busy, failed to unregister connection!\n",
218*26947304SEvan Yan 		    (void *)dip, (void *)hdlp, hdlp->cn_info.cn_state));
219*26947304SEvan Yan 
220*26947304SEvan Yan 		return (DDI_EBUSY);
221*26947304SEvan Yan 	}
222*26947304SEvan Yan 
223*26947304SEvan Yan 	/* unlink the handle */
224*26947304SEvan Yan 	DDIHP_LIST_REMOVE(ddi_hp_cn_handle_t, (DEVI(dip)->devi_hp_hdlp), hdlp);
225*26947304SEvan Yan 
226*26947304SEvan Yan 	kmem_free(hdlp->cn_info.cn_name, strlen(hdlp->cn_info.cn_name) + 1);
227*26947304SEvan Yan 	kmem_free(hdlp, sizeof (ddi_hp_cn_handle_t));
228*26947304SEvan Yan 	return (DDI_SUCCESS);
229*26947304SEvan Yan }
230*26947304SEvan Yan 
231*26947304SEvan Yan /*
232*26947304SEvan Yan  * For a given Connection name and the dip node where the Connection is
233*26947304SEvan Yan  * supposed to be, find the corresponding hotplug handle.
234*26947304SEvan Yan  */
235*26947304SEvan Yan ddi_hp_cn_handle_t *
236*26947304SEvan Yan ddihp_cn_name_to_handle(dev_info_t *dip, char *cn_name)
237*26947304SEvan Yan {
238*26947304SEvan Yan 	ddi_hp_cn_handle_t *hdlp;
239*26947304SEvan Yan 
240*26947304SEvan Yan 	ASSERT(DEVI_BUSY_OWNED(dip));
241*26947304SEvan Yan 
242*26947304SEvan Yan 	DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
243*26947304SEvan Yan 	    "dip %p cn_name to find: %s", (void *)dip, cn_name));
244*26947304SEvan Yan 	for (hdlp = DEVI(dip)->devi_hp_hdlp; hdlp; hdlp = hdlp->next) {
245*26947304SEvan Yan 		DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
246*26947304SEvan Yan 		    "current cn_name: %s", hdlp->cn_info.cn_name));
247*26947304SEvan Yan 
248*26947304SEvan Yan 		if (strcmp(cn_name, hdlp->cn_info.cn_name) == 0) {
249*26947304SEvan Yan 			/* found */
250*26947304SEvan Yan 			return (hdlp);
251*26947304SEvan Yan 		}
252*26947304SEvan Yan 	}
253*26947304SEvan Yan 	DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
254*26947304SEvan Yan 	    "failed to find cn_name"));
255*26947304SEvan Yan 	return (NULL);
256*26947304SEvan Yan }
257*26947304SEvan Yan 
258*26947304SEvan Yan /*
259*26947304SEvan Yan  * Process the hotplug operations for Connector and also create Port
260*26947304SEvan Yan  * upon user command.
261*26947304SEvan Yan  */
262*26947304SEvan Yan int
263*26947304SEvan Yan ddihp_connector_ops(ddi_hp_cn_handle_t *hdlp, ddi_hp_op_t op,
264*26947304SEvan Yan     void *arg, void *result)
265*26947304SEvan Yan {
266*26947304SEvan Yan 	int			rv = DDI_SUCCESS;
267*26947304SEvan Yan 	dev_info_t		*dip = hdlp->cn_dip;
268*26947304SEvan Yan 
269*26947304SEvan Yan 	ASSERT(DEVI_BUSY_OWNED(dip));
270*26947304SEvan Yan 
271*26947304SEvan Yan 	DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: pdip=%p op=%x "
272*26947304SEvan Yan 	    "hdlp=%p arg=%p\n", (void *)dip, op, (void *)hdlp, arg));
273*26947304SEvan Yan 
274*26947304SEvan Yan 	if (op == DDI_HPOP_CN_CHANGE_STATE) {
275*26947304SEvan Yan 		ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
276*26947304SEvan Yan 
277*26947304SEvan Yan 		rv = ddihp_cn_pre_change_state(hdlp, target_state);
278*26947304SEvan Yan 		if (rv != DDI_SUCCESS) {
279*26947304SEvan Yan 			/* the state is not changed */
280*26947304SEvan Yan 			*((ddi_hp_cn_state_t *)result) =
281*26947304SEvan Yan 			    hdlp->cn_info.cn_state;
282*26947304SEvan Yan 			return (rv);
283*26947304SEvan Yan 		}
284*26947304SEvan Yan 	}
285*26947304SEvan Yan 	ASSERT(NEXUS_HAS_HP_OP(dip));
286*26947304SEvan Yan 	rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
287*26947304SEvan Yan 	    dip, hdlp->cn_info.cn_name, op, arg, result);
288*26947304SEvan Yan 
289*26947304SEvan Yan 	if (rv != DDI_SUCCESS) {
290*26947304SEvan Yan 		DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: "
291*26947304SEvan Yan 		    "bus_hp_op failed: pdip=%p cn_name:%s op=%x "
292*26947304SEvan Yan 		    "hdlp=%p arg=%p\n", (void *)dip, hdlp->cn_info.cn_name,
293*26947304SEvan Yan 		    op, (void *)hdlp, arg));
294*26947304SEvan Yan 	}
295*26947304SEvan Yan 	if (op == DDI_HPOP_CN_CHANGE_STATE) {
296*26947304SEvan Yan 		int rv_post;
297*26947304SEvan Yan 
298*26947304SEvan Yan 		DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: "
299*26947304SEvan Yan 		    "old_state=%x, new_state=%x, rv=%x\n",
300*26947304SEvan Yan 		    hdlp->cn_info.cn_state, *(ddi_hp_cn_state_t *)result, rv));
301*26947304SEvan Yan 
302*26947304SEvan Yan 		/*
303*26947304SEvan Yan 		 * After state change op is successfully done or
304*26947304SEvan Yan 		 * failed at some stages, continue to do some jobs.
305*26947304SEvan Yan 		 */
306*26947304SEvan Yan 		rv_post = ddihp_cn_post_change_state(hdlp,
307*26947304SEvan Yan 		    *(ddi_hp_cn_state_t *)result);
308*26947304SEvan Yan 
309*26947304SEvan Yan 		if (rv_post != DDI_SUCCESS)
310*26947304SEvan Yan 			rv = rv_post;
311*26947304SEvan Yan 	}
312*26947304SEvan Yan 
313*26947304SEvan Yan 	return (rv);
314*26947304SEvan Yan }
315*26947304SEvan Yan 
316*26947304SEvan Yan /*
317*26947304SEvan Yan  * Process the hotplug op for Port
318*26947304SEvan Yan  */
319*26947304SEvan Yan int
320*26947304SEvan Yan ddihp_port_ops(ddi_hp_cn_handle_t *hdlp, ddi_hp_op_t op,
321*26947304SEvan Yan     void *arg, void *result)
322*26947304SEvan Yan {
323*26947304SEvan Yan 	int		ret = DDI_SUCCESS;
324*26947304SEvan Yan 
325*26947304SEvan Yan 	ASSERT(DEVI_BUSY_OWNED(hdlp->cn_dip));
326*26947304SEvan Yan 
327*26947304SEvan Yan 	DDI_HP_IMPLDBG((CE_CONT, "ddihp_port_ops: pdip=%p op=%x hdlp=%p "
328*26947304SEvan Yan 	    "arg=%p\n", (void *)hdlp->cn_dip, op, (void *)hdlp, arg));
329*26947304SEvan Yan 
330*26947304SEvan Yan 	switch (op) {
331*26947304SEvan Yan 	case DDI_HPOP_CN_GET_STATE:
332*26947304SEvan Yan 	{
333*26947304SEvan Yan 		int state;
334*26947304SEvan Yan 
335*26947304SEvan Yan 		state = hdlp->cn_info.cn_state;
336*26947304SEvan Yan 
337*26947304SEvan Yan 		if (hdlp->cn_info.cn_child == NULL) {
338*26947304SEvan Yan 			/* No child. Either present or empty. */
339*26947304SEvan Yan 			if (state >= DDI_HP_CN_STATE_PORT_PRESENT)
340*26947304SEvan Yan 				state = DDI_HP_CN_STATE_PORT_PRESENT;
341*26947304SEvan Yan 			else
342*26947304SEvan Yan 				state = DDI_HP_CN_STATE_PORT_EMPTY;
343*26947304SEvan Yan 
344*26947304SEvan Yan 		} else { /* There is a child of this Port */
345*26947304SEvan Yan 
346*26947304SEvan Yan 			/* Check DEVI(dip)->devi_node_state */
347*26947304SEvan Yan 			switch (i_ddi_node_state(hdlp->cn_info.cn_child)) {
348*26947304SEvan Yan 			case	DS_INVAL:
349*26947304SEvan Yan 			case	DS_PROTO:
350*26947304SEvan Yan 			case	DS_LINKED:
351*26947304SEvan Yan 			case	DS_BOUND:
352*26947304SEvan Yan 			case	DS_INITIALIZED:
353*26947304SEvan Yan 			case	DS_PROBED:
354*26947304SEvan Yan 				state = DDI_HP_CN_STATE_OFFLINE;
355*26947304SEvan Yan 				break;
356*26947304SEvan Yan 			case	DS_ATTACHED:
357*26947304SEvan Yan 				state = DDI_HP_CN_STATE_MAINTENANCE;
358*26947304SEvan Yan 				break;
359*26947304SEvan Yan 			case	DS_READY:
360*26947304SEvan Yan 				state = DDI_HP_CN_STATE_ONLINE;
361*26947304SEvan Yan 				break;
362*26947304SEvan Yan 			default:
363*26947304SEvan Yan 				/* should never reach here */
364*26947304SEvan Yan 				ASSERT("unknown devinfo state");
365*26947304SEvan Yan 			}
366*26947304SEvan Yan 			/*
367*26947304SEvan Yan 			 * Check DEVI(dip)->devi_state in case the node is
368*26947304SEvan Yan 			 * downgraded or quiesced.
369*26947304SEvan Yan 			 */
370*26947304SEvan Yan 			if (state == DDI_HP_CN_STATE_ONLINE &&
371*26947304SEvan Yan 			    ddi_get_devstate(hdlp->cn_info.cn_child) !=
372*26947304SEvan Yan 			    DDI_DEVSTATE_UP)
373*26947304SEvan Yan 				state = DDI_HP_CN_STATE_MAINTENANCE;
374*26947304SEvan Yan 		}
375*26947304SEvan Yan 
376*26947304SEvan Yan 		*((ddi_hp_cn_state_t *)result) = state;
377*26947304SEvan Yan 
378*26947304SEvan Yan 		break;
379*26947304SEvan Yan 	}
380*26947304SEvan Yan 	case DDI_HPOP_CN_CHANGE_STATE:
381*26947304SEvan Yan 	{
382*26947304SEvan Yan 		ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
383*26947304SEvan Yan 		ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
384*26947304SEvan Yan 
385*26947304SEvan Yan 		ret = ddihp_port_change_state(hdlp, target_state);
386*26947304SEvan Yan 		if (curr_state != hdlp->cn_info.cn_state) {
387*26947304SEvan Yan 			ddihp_update_last_change(hdlp);
388*26947304SEvan Yan 		}
389*26947304SEvan Yan 		*((ddi_hp_cn_state_t *)result) = hdlp->cn_info.cn_state;
390*26947304SEvan Yan 
391*26947304SEvan Yan 		break;
392*26947304SEvan Yan 	}
393*26947304SEvan Yan 	case DDI_HPOP_CN_REMOVE_PORT:
394*26947304SEvan Yan 	{
395*26947304SEvan Yan 		(void) ddihp_cn_getstate(hdlp);
396*26947304SEvan Yan 
397*26947304SEvan Yan 		if (hdlp->cn_info.cn_state != DDI_HP_CN_STATE_PORT_EMPTY) {
398*26947304SEvan Yan 			/* Only empty PORT can be removed by commands */
399*26947304SEvan Yan 			ret = DDI_EBUSY;
400*26947304SEvan Yan 
401*26947304SEvan Yan 			break;
402*26947304SEvan Yan 		}
403*26947304SEvan Yan 
404*26947304SEvan Yan 		ret = ddihp_cn_unregister(hdlp);
405*26947304SEvan Yan 		break;
406*26947304SEvan Yan 	}
407*26947304SEvan Yan 	default:
408*26947304SEvan Yan 		ret = DDI_ENOTSUP;
409*26947304SEvan Yan 		break;
410*26947304SEvan Yan 	}
411*26947304SEvan Yan 
412*26947304SEvan Yan 	return (ret);
413*26947304SEvan Yan }
414*26947304SEvan Yan 
415*26947304SEvan Yan /*
416*26947304SEvan Yan  * Generate the system event with a possible hint
417*26947304SEvan Yan  */
418*26947304SEvan Yan /* ARGSUSED */
419*26947304SEvan Yan void
420*26947304SEvan Yan ddihp_cn_gen_sysevent(ddi_hp_cn_handle_t *hdlp,
421*26947304SEvan Yan     ddi_hp_cn_sysevent_t event_sub_class, int hint, int kmflag)
422*26947304SEvan Yan {
423*26947304SEvan Yan 	dev_info_t	*dip = hdlp->cn_dip;
424*26947304SEvan Yan 	char		*cn_path, *ap_id;
425*26947304SEvan Yan 	char		*ev_subclass = NULL;
426*26947304SEvan Yan 	nvlist_t	*ev_attr_list = NULL;
427*26947304SEvan Yan 	sysevent_id_t	eid;
428*26947304SEvan Yan 	int		ap_id_len, err;
429*26947304SEvan Yan 
430*26947304SEvan Yan 	cn_path = kmem_zalloc(MAXPATHLEN, kmflag);
431*26947304SEvan Yan 	if (cn_path == NULL) {
432*26947304SEvan Yan 		cmn_err(CE_WARN,
433*26947304SEvan Yan 		    "%s%d: Failed to allocate memory for hotplug"
434*26947304SEvan Yan 		    " connection: %s\n",
435*26947304SEvan Yan 		    ddi_driver_name(dip), ddi_get_instance(dip),
436*26947304SEvan Yan 		    hdlp->cn_info.cn_name);
437*26947304SEvan Yan 
438*26947304SEvan Yan 		return;
439*26947304SEvan Yan 	}
440*26947304SEvan Yan 
441*26947304SEvan Yan 	/*
442*26947304SEvan Yan 	 * Minor device name will be bus path
443*26947304SEvan Yan 	 * concatenated with connection name.
444*26947304SEvan Yan 	 * One of consumers of the sysevent will pass it
445*26947304SEvan Yan 	 * to cfgadm as AP ID.
446*26947304SEvan Yan 	 */
447*26947304SEvan Yan 	(void) strcpy(cn_path, "/devices");
448*26947304SEvan Yan 	(void) ddi_pathname(dip, cn_path + strlen("/devices"));
449*26947304SEvan Yan 
450*26947304SEvan Yan 	ap_id_len = strlen(cn_path) + strlen(":") +
451*26947304SEvan Yan 	    strlen(hdlp->cn_info.cn_name) + 1;
452*26947304SEvan Yan 	ap_id = kmem_zalloc(ap_id_len, kmflag);
453*26947304SEvan Yan 	if (ap_id == NULL) {
454*26947304SEvan Yan 		cmn_err(CE_WARN,
455*26947304SEvan Yan 		    "%s%d: Failed to allocate memory for AP ID: %s:%s\n",
456*26947304SEvan Yan 		    ddi_driver_name(dip), ddi_get_instance(dip),
457*26947304SEvan Yan 		    cn_path, hdlp->cn_info.cn_name);
458*26947304SEvan Yan 		kmem_free(cn_path, MAXPATHLEN);
459*26947304SEvan Yan 
460*26947304SEvan Yan 		return;
461*26947304SEvan Yan 	}
462*26947304SEvan Yan 
463*26947304SEvan Yan 	(void) strcpy(ap_id, cn_path);
464*26947304SEvan Yan 	(void) strcat(ap_id, ":");
465*26947304SEvan Yan 	(void) strcat(ap_id, hdlp->cn_info.cn_name);
466*26947304SEvan Yan 	kmem_free(cn_path, MAXPATHLEN);
467*26947304SEvan Yan 
468*26947304SEvan Yan 	err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag);
469*26947304SEvan Yan 
470*26947304SEvan Yan 	if (err != 0) {
471*26947304SEvan Yan 		cmn_err(CE_WARN,
472*26947304SEvan Yan 		    "%s%d: Failed to allocate memory for event subclass %d\n",
473*26947304SEvan Yan 		    ddi_driver_name(dip), ddi_get_instance(dip),
474*26947304SEvan Yan 		    event_sub_class);
475*26947304SEvan Yan 		kmem_free(ap_id, ap_id_len);
476*26947304SEvan Yan 
477*26947304SEvan Yan 		return;
478*26947304SEvan Yan 	}
479*26947304SEvan Yan 
480*26947304SEvan Yan 	switch (event_sub_class) {
481*26947304SEvan Yan 	case DDI_HP_CN_STATE_CHANGE:
482*26947304SEvan Yan 		ev_subclass = ESC_DR_AP_STATE_CHANGE;
483*26947304SEvan Yan 
484*26947304SEvan Yan 		switch (hint) {
485*26947304SEvan Yan 		case SE_NO_HINT:	/* fall through */
486*26947304SEvan Yan 		case SE_HINT_INSERT:	/* fall through */
487*26947304SEvan Yan 		case SE_HINT_REMOVE:
488*26947304SEvan Yan 			err = nvlist_add_string(ev_attr_list, DR_HINT,
489*26947304SEvan Yan 			    SE_HINT2STR(hint));
490*26947304SEvan Yan 
491*26947304SEvan Yan 			if (err != 0) {
492*26947304SEvan Yan 				cmn_err(CE_WARN, "%s%d: Failed to add attr [%s]"
493*26947304SEvan Yan 				    " for %s event\n", ddi_driver_name(dip),
494*26947304SEvan Yan 				    ddi_get_instance(dip), DR_HINT,
495*26947304SEvan Yan 				    ESC_DR_AP_STATE_CHANGE);
496*26947304SEvan Yan 
497*26947304SEvan Yan 				goto done;
498*26947304SEvan Yan 			}
499*26947304SEvan Yan 			break;
500*26947304SEvan Yan 
501*26947304SEvan Yan 		default:
502*26947304SEvan Yan 			cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent\n",
503*26947304SEvan Yan 			    ddi_driver_name(dip), ddi_get_instance(dip));
504*26947304SEvan Yan 
505*26947304SEvan Yan 			goto done;
506*26947304SEvan Yan 		}
507*26947304SEvan Yan 
508*26947304SEvan Yan 		break;
509*26947304SEvan Yan 
510*26947304SEvan Yan 	/* event sub class: DDI_HP_CN_REQ */
511*26947304SEvan Yan 	case DDI_HP_CN_REQ:
512*26947304SEvan Yan 		ev_subclass = ESC_DR_REQ;
513*26947304SEvan Yan 
514*26947304SEvan Yan 		switch (hint) {
515*26947304SEvan Yan 		case SE_INVESTIGATE_RES: /* fall through */
516*26947304SEvan Yan 		case SE_INCOMING_RES:	/* fall through */
517*26947304SEvan Yan 		case SE_OUTGOING_RES:	/* fall through */
518*26947304SEvan Yan 			err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE,
519*26947304SEvan Yan 			    SE_REQ2STR(hint));
520*26947304SEvan Yan 
521*26947304SEvan Yan 			if (err != 0) {
522*26947304SEvan Yan 				cmn_err(CE_WARN,
523*26947304SEvan Yan 				    "%s%d: Failed to add attr [%s] for %s \n"
524*26947304SEvan Yan 				    "event", ddi_driver_name(dip),
525*26947304SEvan Yan 				    ddi_get_instance(dip),
526*26947304SEvan Yan 				    DR_REQ_TYPE, ESC_DR_REQ);
527*26947304SEvan Yan 
528*26947304SEvan Yan 				goto done;
529*26947304SEvan Yan 			}
530*26947304SEvan Yan 			break;
531*26947304SEvan Yan 
532*26947304SEvan Yan 		default:
533*26947304SEvan Yan 			cmn_err(CE_WARN, "%s%d:  Unknown hint on sysevent\n",
534*26947304SEvan Yan 			    ddi_driver_name(dip), ddi_get_instance(dip));
535*26947304SEvan Yan 
536*26947304SEvan Yan 			goto done;
537*26947304SEvan Yan 		}
538*26947304SEvan Yan 
539*26947304SEvan Yan 		break;
540*26947304SEvan Yan 
541*26947304SEvan Yan 	default:
542*26947304SEvan Yan 		cmn_err(CE_WARN, "%s%d:  Unknown Event subclass\n",
543*26947304SEvan Yan 		    ddi_driver_name(dip), ddi_get_instance(dip));
544*26947304SEvan Yan 
545*26947304SEvan Yan 		goto done;
546*26947304SEvan Yan 	}
547*26947304SEvan Yan 
548*26947304SEvan Yan 	/*
549*26947304SEvan Yan 	 * Add Hotplug Connection (CN) as attribute (common attribute)
550*26947304SEvan Yan 	 */
551*26947304SEvan Yan 	err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id);
552*26947304SEvan Yan 	if (err != 0) {
553*26947304SEvan Yan 		cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event\n",
554*26947304SEvan Yan 		    ddi_driver_name(dip), ddi_get_instance(dip),
555*26947304SEvan Yan 		    DR_AP_ID, EC_DR);
556*26947304SEvan Yan 
557*26947304SEvan Yan 		goto done;
558*26947304SEvan Yan 	}
559*26947304SEvan Yan 
560*26947304SEvan Yan 	/*
561*26947304SEvan Yan 	 * Log this event with sysevent framework.
562*26947304SEvan Yan 	 */
563*26947304SEvan Yan 	err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, EC_DR,
564*26947304SEvan Yan 	    ev_subclass, ev_attr_list, &eid,
565*26947304SEvan Yan 	    ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP));
566*26947304SEvan Yan 
567*26947304SEvan Yan 	if (err != 0) {
568*26947304SEvan Yan 		cmn_err(CE_WARN, "%s%d: Failed to log %s event\n",
569*26947304SEvan Yan 		    ddi_driver_name(dip), ddi_get_instance(dip), EC_DR);
570*26947304SEvan Yan 	}
571*26947304SEvan Yan 
572*26947304SEvan Yan done:
573*26947304SEvan Yan 	nvlist_free(ev_attr_list);
574*26947304SEvan Yan 	kmem_free(ap_id, ap_id_len);
575*26947304SEvan Yan }
576*26947304SEvan Yan 
577*26947304SEvan Yan /*
578*26947304SEvan Yan  * Local functions (called within this file)
579*26947304SEvan Yan  */
580*26947304SEvan Yan 
581*26947304SEvan Yan /*
582*26947304SEvan Yan  * Connector operations
583*26947304SEvan Yan  */
584*26947304SEvan Yan 
585*26947304SEvan Yan /*
586*26947304SEvan Yan  * Prepare to change state for a Connector: offline, unprobe, etc.
587*26947304SEvan Yan  */
588*26947304SEvan Yan static int
589*26947304SEvan Yan ddihp_cn_pre_change_state(ddi_hp_cn_handle_t *hdlp,
590*26947304SEvan Yan     ddi_hp_cn_state_t target_state)
591*26947304SEvan Yan {
592*26947304SEvan Yan 	ddi_hp_cn_state_t	curr_state = hdlp->cn_info.cn_state;
593*26947304SEvan Yan 	dev_info_t		*dip = hdlp->cn_dip;
594*26947304SEvan Yan 	int			rv = DDI_SUCCESS;
595*26947304SEvan Yan 
596*26947304SEvan Yan 	if (curr_state > target_state &&
597*26947304SEvan Yan 	    curr_state == DDI_HP_CN_STATE_ENABLED) {
598*26947304SEvan Yan 		/*
599*26947304SEvan Yan 		 * If the Connection goes to a lower state from ENABLED,
600*26947304SEvan Yan 		 *  then offline all children under it.
601*26947304SEvan Yan 		 */
602*26947304SEvan Yan 		rv = ddihp_cn_change_children_state(hdlp, B_FALSE);
603*26947304SEvan Yan 		if (rv != DDI_SUCCESS) {
604*26947304SEvan Yan 			cmn_err(CE_WARN,
605*26947304SEvan Yan 			    "(%s%d): "
606*26947304SEvan Yan 			    "failed to unconfigure the device in the"
607*26947304SEvan Yan 			    " Connection %s\n", ddi_driver_name(dip),
608*26947304SEvan Yan 			    ddi_get_instance(dip),
609*26947304SEvan Yan 			    hdlp->cn_info.cn_name);
610*26947304SEvan Yan 
611*26947304SEvan Yan 			return (rv);
612*26947304SEvan Yan 		}
613*26947304SEvan Yan 		ASSERT(NEXUS_HAS_HP_OP(dip));
614*26947304SEvan Yan 		/*
615*26947304SEvan Yan 		 * Remove all the children and their ports
616*26947304SEvan Yan 		 * after they are offlined.
617*26947304SEvan Yan 		 */
618*26947304SEvan Yan 		rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
619*26947304SEvan Yan 		    dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_UNPROBE,
620*26947304SEvan Yan 		    NULL, NULL);
621*26947304SEvan Yan 		if (rv != DDI_SUCCESS) {
622*26947304SEvan Yan 			cmn_err(CE_WARN,
623*26947304SEvan Yan 			    "(%s%d): failed"
624*26947304SEvan Yan 			    " to unprobe the device in the Connector"
625*26947304SEvan Yan 			    " %s\n", ddi_driver_name(dip),
626*26947304SEvan Yan 			    ddi_get_instance(dip),
627*26947304SEvan Yan 			    hdlp->cn_info.cn_name);
628*26947304SEvan Yan 
629*26947304SEvan Yan 			return (rv);
630*26947304SEvan Yan 		}
631*26947304SEvan Yan 
632*26947304SEvan Yan 		DDI_HP_NEXDBG((CE_CONT,
633*26947304SEvan Yan 		    "ddihp_connector_ops (%s%d): device"
634*26947304SEvan Yan 		    " is unconfigured and unprobed in Connector %s\n",
635*26947304SEvan Yan 		    ddi_driver_name(dip), ddi_get_instance(dip),
636*26947304SEvan Yan 		    hdlp->cn_info.cn_name));
637*26947304SEvan Yan 	}
638*26947304SEvan Yan 
639*26947304SEvan Yan 	return (rv);
640*26947304SEvan Yan }
641*26947304SEvan Yan 
642*26947304SEvan Yan /*
643*26947304SEvan Yan  * Jobs after change state of a Connector: update last change time,
644*26947304SEvan Yan  * probe, online, sysevent, etc.
645*26947304SEvan Yan  */
646*26947304SEvan Yan static int
647*26947304SEvan Yan ddihp_cn_post_change_state(ddi_hp_cn_handle_t *hdlp,
648*26947304SEvan Yan     ddi_hp_cn_state_t new_state)
649*26947304SEvan Yan {
650*26947304SEvan Yan 	int			rv = DDI_SUCCESS;
651*26947304SEvan Yan 	ddi_hp_cn_state_t	curr_state = hdlp->cn_info.cn_state;
652*26947304SEvan Yan 
653*26947304SEvan Yan 	/* Update the state in handle */
654*26947304SEvan Yan 	if (new_state != curr_state) {
655*26947304SEvan Yan 		hdlp->cn_info.cn_state = new_state;
656*26947304SEvan Yan 		ddihp_update_last_change(hdlp);
657*26947304SEvan Yan 	}
658*26947304SEvan Yan 
659*26947304SEvan Yan 	if (curr_state < new_state &&
660*26947304SEvan Yan 	    new_state == DDI_HP_CN_STATE_ENABLED) {
661*26947304SEvan Yan 		/*
662*26947304SEvan Yan 		 * Probe and online devices if state is
663*26947304SEvan Yan 		 * upgraded to ENABLED.
664*26947304SEvan Yan 		 */
665*26947304SEvan Yan 		rv = ddihp_cn_handle_state_change(hdlp);
666*26947304SEvan Yan 	}
667*26947304SEvan Yan 	if (curr_state != hdlp->cn_info.cn_state) {
668*26947304SEvan Yan 		/*
669*26947304SEvan Yan 		 * For Connector, generate a sysevent on
670*26947304SEvan Yan 		 * state change.
671*26947304SEvan Yan 		 */
672*26947304SEvan Yan 		ddihp_cn_gen_sysevent(hdlp, DDI_HP_CN_STATE_CHANGE,
673*26947304SEvan Yan 		    SE_NO_HINT, KM_SLEEP);
674*26947304SEvan Yan 	}
675*26947304SEvan Yan 
676*26947304SEvan Yan 	return (rv);
677*26947304SEvan Yan }
678*26947304SEvan Yan 
679*26947304SEvan Yan /*
680*26947304SEvan Yan  * Handle Connector state change.
681*26947304SEvan Yan  *
682*26947304SEvan Yan  * This function is called after connector is upgraded to ENABLED sate.
683*26947304SEvan Yan  * It probes the device plugged in the connector to setup devinfo nodes
684*26947304SEvan Yan  * and then online the nodes.
685*26947304SEvan Yan  */
686*26947304SEvan Yan static int
687*26947304SEvan Yan ddihp_cn_handle_state_change(ddi_hp_cn_handle_t *hdlp)
688*26947304SEvan Yan {
689*26947304SEvan Yan 	dev_info_t		*dip = hdlp->cn_dip;
690*26947304SEvan Yan 	int			rv = DDI_SUCCESS;
691*26947304SEvan Yan 
692*26947304SEvan Yan 	ASSERT(DEVI_BUSY_OWNED(dip));
693*26947304SEvan Yan 	ASSERT(NEXUS_HAS_HP_OP(dip));
694*26947304SEvan Yan 	/*
695*26947304SEvan Yan 	 * If the Connection went to state ENABLED from a lower state,
696*26947304SEvan Yan 	 * probe it.
697*26947304SEvan Yan 	 */
698*26947304SEvan Yan 	rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
699*26947304SEvan Yan 	    dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_PROBE, NULL, NULL);
700*26947304SEvan Yan 
701*26947304SEvan Yan 	if (rv != DDI_SUCCESS) {
702*26947304SEvan Yan 		ddi_hp_cn_state_t	target_state = DDI_HP_CN_STATE_POWERED;
703*26947304SEvan Yan 		ddi_hp_cn_state_t	result_state = 0;
704*26947304SEvan Yan 
705*26947304SEvan Yan 		/*
706*26947304SEvan Yan 		 * Probe failed. Disable the connector so that it can
707*26947304SEvan Yan 		 * be enabled again by a later try from userland.
708*26947304SEvan Yan 		 */
709*26947304SEvan Yan 		(void) (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
710*26947304SEvan Yan 		    dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_CHANGE_STATE,
711*26947304SEvan Yan 		    (void *)&target_state, (void *)&result_state);
712*26947304SEvan Yan 
713*26947304SEvan Yan 		if (result_state && result_state != hdlp->cn_info.cn_state) {
714*26947304SEvan Yan 			hdlp->cn_info.cn_state = result_state;
715*26947304SEvan Yan 			ddihp_update_last_change(hdlp);
716*26947304SEvan Yan 		}
717*26947304SEvan Yan 
718*26947304SEvan Yan 		cmn_err(CE_WARN,
719*26947304SEvan Yan 		    "(%s%d): failed to probe the Connection %s\n",
720*26947304SEvan Yan 		    ddi_driver_name(dip), ddi_get_instance(dip),
721*26947304SEvan Yan 		    hdlp->cn_info.cn_name);
722*26947304SEvan Yan 
723*26947304SEvan Yan 		return (rv);
724*26947304SEvan Yan 	}
725*26947304SEvan Yan 	/*
726*26947304SEvan Yan 	 * Try to online all the children of CN.
727*26947304SEvan Yan 	 */
728*26947304SEvan Yan 	(void) ddihp_cn_change_children_state(hdlp, B_TRUE);
729*26947304SEvan Yan 
730*26947304SEvan Yan 	DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_event_handler (%s%d): "
731*26947304SEvan Yan 	    "device is configured in the Connection %s\n",
732*26947304SEvan Yan 	    ddi_driver_name(dip), ddi_get_instance(dip),
733*26947304SEvan Yan 	    hdlp->cn_info.cn_name));
734*26947304SEvan Yan 	return (rv);
735*26947304SEvan Yan }
736*26947304SEvan Yan 
737*26947304SEvan Yan /*
738*26947304SEvan Yan  * Online/Offline all the children under the Hotplug Connection (CN)
739*26947304SEvan Yan  *
740*26947304SEvan Yan  * Do online operation when the online parameter is true; otherwise do offline.
741*26947304SEvan Yan  */
742*26947304SEvan Yan static int
743*26947304SEvan Yan ddihp_cn_change_children_state(ddi_hp_cn_handle_t *hdlp, boolean_t online)
744*26947304SEvan Yan {
745*26947304SEvan Yan 	dev_info_t		*dip = hdlp->cn_dip;
746*26947304SEvan Yan 	dev_info_t		*cdip;
747*26947304SEvan Yan 	ddi_hp_cn_handle_t	*h;
748*26947304SEvan Yan 	int			rv = DDI_SUCCESS;
749*26947304SEvan Yan 
750*26947304SEvan Yan 	DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_change_children_state:"
751*26947304SEvan Yan 	    " dip %p hdlp %p, online %x\n",
752*26947304SEvan Yan 	    (void *)dip, (void *)hdlp, online));
753*26947304SEvan Yan 
754*26947304SEvan Yan 	ASSERT(DEVI_BUSY_OWNED(dip));
755*26947304SEvan Yan 
756*26947304SEvan Yan 	/*
757*26947304SEvan Yan 	 * Return invalid if Connection state is < DDI_HP_CN_STATE_ENABLED
758*26947304SEvan Yan 	 * when try to online children.
759*26947304SEvan Yan 	 */
760*26947304SEvan Yan 	if (online && hdlp->cn_info.cn_state < DDI_HP_CN_STATE_ENABLED) {
761*26947304SEvan Yan 		DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_change_children_state: "
762*26947304SEvan Yan 		    "Connector %p is not in probed state\n", (void *)hdlp));
763*26947304SEvan Yan 
764*26947304SEvan Yan 		return (DDI_EINVAL);
765*26947304SEvan Yan 	}
766*26947304SEvan Yan 
767*26947304SEvan Yan 	/* Now, online/offline all the devices depending on the Connector */
768*26947304SEvan Yan 
769*26947304SEvan Yan 	if (!online) {
770*26947304SEvan Yan 		/*
771*26947304SEvan Yan 		 * For offline operation we need to firstly clean up devfs
772*26947304SEvan Yan 		 * so as not to prevent driver detach.
773*26947304SEvan Yan 		 */
774*26947304SEvan Yan 		(void) devfs_clean(dip, NULL, DV_CLEAN_FORCE);
775*26947304SEvan Yan 	}
776*26947304SEvan Yan 	for (h = DEVI(dip)->devi_hp_hdlp; h; h = h->next) {
777*26947304SEvan Yan 		if (h->cn_info.cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT)
778*26947304SEvan Yan 			continue;
779*26947304SEvan Yan 
780*26947304SEvan Yan 		if (h->cn_info.cn_num_dpd_on !=
781*26947304SEvan Yan 		    hdlp->cn_info.cn_num)
782*26947304SEvan Yan 			continue;
783*26947304SEvan Yan 
784*26947304SEvan Yan 		cdip = h->cn_info.cn_child;
785*26947304SEvan Yan 		ASSERT(cdip);
786*26947304SEvan Yan 		if (online) {
787*26947304SEvan Yan 			/* online children */
788*26947304SEvan Yan 			if (!ddihp_check_status_prop(dip))
789*26947304SEvan Yan 				continue;
790*26947304SEvan Yan 
791*26947304SEvan Yan 			if (ndi_devi_online(cdip,
792*26947304SEvan Yan 			    NDI_ONLINE_ATTACH | NDI_CONFIG) != NDI_SUCCESS) {
793*26947304SEvan Yan 				cmn_err(CE_WARN,
794*26947304SEvan Yan 				    "(%s%d):"
795*26947304SEvan Yan 				    " failed to attach driver for a device"
796*26947304SEvan Yan 				    " (%s%d) under the Connection %s\n",
797*26947304SEvan Yan 				    ddi_driver_name(dip), ddi_get_instance(dip),
798*26947304SEvan Yan 				    ddi_driver_name(cdip),
799*26947304SEvan Yan 				    ddi_get_instance(cdip),
800*26947304SEvan Yan 				    hdlp->cn_info.cn_name);
801*26947304SEvan Yan 				/*
802*26947304SEvan Yan 				 * One of the devices failed to online, but we
803*26947304SEvan Yan 				 * want to continue to online the rest siblings
804*26947304SEvan Yan 				 * after mark the failure here.
805*26947304SEvan Yan 				 */
806*26947304SEvan Yan 				rv = DDI_FAILURE;
807*26947304SEvan Yan 
808*26947304SEvan Yan 				continue;
809*26947304SEvan Yan 			}
810*26947304SEvan Yan 		} else {
811*26947304SEvan Yan 			/* offline children */
812*26947304SEvan Yan 			if (ndi_devi_offline(cdip, NDI_UNCONFIG) !=
813*26947304SEvan Yan 			    NDI_SUCCESS) {
814*26947304SEvan Yan 				cmn_err(CE_WARN,
815*26947304SEvan Yan 				    "(%s%d):"
816*26947304SEvan Yan 				    " failed to dettach driver for the device"
817*26947304SEvan Yan 				    " (%s%d) in the Connection %s\n",
818*26947304SEvan Yan 				    ddi_driver_name(dip), ddi_get_instance(dip),
819*26947304SEvan Yan 				    ddi_driver_name(cdip),
820*26947304SEvan Yan 				    ddi_get_instance(cdip),
821*26947304SEvan Yan 				    hdlp->cn_info.cn_name);
822*26947304SEvan Yan 
823*26947304SEvan Yan 				return (DDI_EBUSY);
824*26947304SEvan Yan 			}
825*26947304SEvan Yan 		}
826*26947304SEvan Yan 	}
827*26947304SEvan Yan 
828*26947304SEvan Yan 	return (rv);
829*26947304SEvan Yan }
830*26947304SEvan Yan 
831*26947304SEvan Yan /*
832*26947304SEvan Yan  * Port operations
833*26947304SEvan Yan  */
834*26947304SEvan Yan 
835*26947304SEvan Yan /*
836*26947304SEvan Yan  * Change Port state to target_state.
837*26947304SEvan Yan  */
838*26947304SEvan Yan static int
839*26947304SEvan Yan ddihp_port_change_state(ddi_hp_cn_handle_t *hdlp,
840*26947304SEvan Yan     ddi_hp_cn_state_t target_state)
841*26947304SEvan Yan {
842*26947304SEvan Yan 	ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
843*26947304SEvan Yan 
844*26947304SEvan Yan 	if (target_state < DDI_HP_CN_STATE_PORT_EMPTY ||
845*26947304SEvan Yan 	    target_state > DDI_HP_CN_STATE_ONLINE) {
846*26947304SEvan Yan 
847*26947304SEvan Yan 		return (DDI_EINVAL);
848*26947304SEvan Yan 	}
849*26947304SEvan Yan 
850*26947304SEvan Yan 	if (curr_state < target_state)
851*26947304SEvan Yan 		return (ddihp_port_upgrade_state(hdlp, target_state));
852*26947304SEvan Yan 	else if (curr_state > target_state)
853*26947304SEvan Yan 		return (ddihp_port_downgrade_state(hdlp, target_state));
854*26947304SEvan Yan 	else
855*26947304SEvan Yan 		return (DDI_SUCCESS);
856*26947304SEvan Yan }
857*26947304SEvan Yan 
858*26947304SEvan Yan /*
859*26947304SEvan Yan  * Upgrade port state to target_state.
860*26947304SEvan Yan  */
861*26947304SEvan Yan static int
862*26947304SEvan Yan ddihp_port_upgrade_state(ddi_hp_cn_handle_t *hdlp,
863*26947304SEvan Yan     ddi_hp_cn_state_t target_state)
864*26947304SEvan Yan {
865*26947304SEvan Yan 	ddi_hp_cn_state_t	curr_state, new_state, result_state;
866*26947304SEvan Yan 	dev_info_t		*cdip;
867*26947304SEvan Yan 	int			rv = DDI_SUCCESS;
868*26947304SEvan Yan 
869*26947304SEvan Yan 	curr_state = hdlp->cn_info.cn_state;
870*26947304SEvan Yan 	while (curr_state < target_state) {
871*26947304SEvan Yan 		switch (curr_state) {
872*26947304SEvan Yan 		case DDI_HP_CN_STATE_PORT_EMPTY:
873*26947304SEvan Yan 			/* Check the existence of the corresponding hardware */
874*26947304SEvan Yan 			new_state = DDI_HP_CN_STATE_PORT_PRESENT;
875*26947304SEvan Yan 			rv = ddihp_connector_ops(hdlp,
876*26947304SEvan Yan 			    DDI_HPOP_CN_CHANGE_STATE,
877*26947304SEvan Yan 			    (void *)&new_state, (void *)&result_state);
878*26947304SEvan Yan 			if (rv == DDI_SUCCESS) {
879*26947304SEvan Yan 				hdlp->cn_info.cn_state =
880*26947304SEvan Yan 				    result_state;
881*26947304SEvan Yan 			}
882*26947304SEvan Yan 			break;
883*26947304SEvan Yan 		case DDI_HP_CN_STATE_PORT_PRESENT:
884*26947304SEvan Yan 			/* Read-only probe the corresponding hardware. */
885*26947304SEvan Yan 			new_state = DDI_HP_CN_STATE_OFFLINE;
886*26947304SEvan Yan 			rv = ddihp_connector_ops(hdlp,
887*26947304SEvan Yan 			    DDI_HPOP_CN_CHANGE_STATE,
888*26947304SEvan Yan 			    (void *)&new_state, &cdip);
889*26947304SEvan Yan 			if (rv == DDI_SUCCESS) {
890*26947304SEvan Yan 				hdlp->cn_info.cn_state =
891*26947304SEvan Yan 				    DDI_HP_CN_STATE_OFFLINE;
892*26947304SEvan Yan 
893*26947304SEvan Yan 				ASSERT(hdlp->cn_info.cn_child == NULL);
894*26947304SEvan Yan 				hdlp->cn_info.cn_child = cdip;
895*26947304SEvan Yan 			}
896*26947304SEvan Yan 			break;
897*26947304SEvan Yan 		case DDI_HP_CN_STATE_OFFLINE:
898*26947304SEvan Yan 			/* fall through */
899*26947304SEvan Yan 		case DDI_HP_CN_STATE_MAINTENANCE:
900*26947304SEvan Yan 
901*26947304SEvan Yan 			cdip = hdlp->cn_info.cn_child;
902*26947304SEvan Yan 
903*26947304SEvan Yan 			rv = ndi_devi_online(cdip,
904*26947304SEvan Yan 			    NDI_ONLINE_ATTACH | NDI_CONFIG);
905*26947304SEvan Yan 			if (rv == NDI_SUCCESS) {
906*26947304SEvan Yan 				hdlp->cn_info.cn_state =
907*26947304SEvan Yan 				    DDI_HP_CN_STATE_ONLINE;
908*26947304SEvan Yan 				rv = DDI_SUCCESS;
909*26947304SEvan Yan 			} else {
910*26947304SEvan Yan 				rv = DDI_FAILURE;
911*26947304SEvan Yan 				DDI_HP_IMPLDBG((CE_CONT,
912*26947304SEvan Yan 				    "ddihp_port_upgrade_state: "
913*26947304SEvan Yan 				    "failed to online device %p at port: %s\n",
914*26947304SEvan Yan 				    (void *)cdip, hdlp->cn_info.cn_name));
915*26947304SEvan Yan 			}
916*26947304SEvan Yan 			break;
917*26947304SEvan Yan 		case DDI_HP_CN_STATE_ONLINE:
918*26947304SEvan Yan 
919*26947304SEvan Yan 			break;
920*26947304SEvan Yan 		default:
921*26947304SEvan Yan 			/* should never reach here */
922*26947304SEvan Yan 			ASSERT("unknown devinfo state");
923*26947304SEvan Yan 		}
924*26947304SEvan Yan 		curr_state = hdlp->cn_info.cn_state;
925*26947304SEvan Yan 		if (rv != DDI_SUCCESS) {
926*26947304SEvan Yan 			DDI_HP_IMPLDBG((CE_CONT, "ddihp_port_upgrade_state: "
927*26947304SEvan Yan 			    "failed curr_state=%x, target_state=%x \n",
928*26947304SEvan Yan 			    curr_state, target_state));
929*26947304SEvan Yan 			return (rv);
930*26947304SEvan Yan 		}
931*26947304SEvan Yan 	}
932*26947304SEvan Yan 
933*26947304SEvan Yan 	return (rv);
934*26947304SEvan Yan }
935*26947304SEvan Yan 
936*26947304SEvan Yan /*
937*26947304SEvan Yan  * Downgrade state to target_state
938*26947304SEvan Yan  */
939*26947304SEvan Yan static int
940*26947304SEvan Yan ddihp_port_downgrade_state(ddi_hp_cn_handle_t *hdlp,
941*26947304SEvan Yan     ddi_hp_cn_state_t target_state)
942*26947304SEvan Yan {
943*26947304SEvan Yan 	ddi_hp_cn_state_t	curr_state, new_state, result_state;
944*26947304SEvan Yan 	dev_info_t		*dip = hdlp->cn_dip;
945*26947304SEvan Yan 	dev_info_t		*cdip;
946*26947304SEvan Yan 	int			rv = DDI_SUCCESS;
947*26947304SEvan Yan 
948*26947304SEvan Yan 	curr_state = hdlp->cn_info.cn_state;
949*26947304SEvan Yan 	while (curr_state > target_state) {
950*26947304SEvan Yan 
951*26947304SEvan Yan 		switch (curr_state) {
952*26947304SEvan Yan 		case DDI_HP_CN_STATE_PORT_EMPTY:
953*26947304SEvan Yan 
954*26947304SEvan Yan 			break;
955*26947304SEvan Yan 		case DDI_HP_CN_STATE_PORT_PRESENT:
956*26947304SEvan Yan 			/* Check the existence of the corresponding hardware */
957*26947304SEvan Yan 			new_state = DDI_HP_CN_STATE_PORT_EMPTY;
958*26947304SEvan Yan 			rv = ddihp_connector_ops(hdlp,
959*26947304SEvan Yan 			    DDI_HPOP_CN_CHANGE_STATE,
960*26947304SEvan Yan 			    (void *)&new_state, (void *)&result_state);
961*26947304SEvan Yan 			if (rv == DDI_SUCCESS)
962*26947304SEvan Yan 				hdlp->cn_info.cn_state =
963*26947304SEvan Yan 				    result_state;
964*26947304SEvan Yan 
965*26947304SEvan Yan 			break;
966*26947304SEvan Yan 		case DDI_HP_CN_STATE_OFFLINE:
967*26947304SEvan Yan 			/*
968*26947304SEvan Yan 			 * Read-only unprobe the corresponding hardware:
969*26947304SEvan Yan 			 * 1. release the assigned resource;
970*26947304SEvan Yan 			 * 2. remove the node pointed by the port's cn_child
971*26947304SEvan Yan 			 */
972*26947304SEvan Yan 			new_state = DDI_HP_CN_STATE_PORT_PRESENT;
973*26947304SEvan Yan 			rv = ddihp_connector_ops(hdlp,
974*26947304SEvan Yan 			    DDI_HPOP_CN_CHANGE_STATE,
975*26947304SEvan Yan 			    (void *)&new_state, (void *)&result_state);
976*26947304SEvan Yan 			if (rv == DDI_SUCCESS)
977*26947304SEvan Yan 				hdlp->cn_info.cn_state =
978*26947304SEvan Yan 				    DDI_HP_CN_STATE_PORT_PRESENT;
979*26947304SEvan Yan 			break;
980*26947304SEvan Yan 		case DDI_HP_CN_STATE_MAINTENANCE:
981*26947304SEvan Yan 			/* fall through. */
982*26947304SEvan Yan 		case DDI_HP_CN_STATE_ONLINE:
983*26947304SEvan Yan 			cdip = hdlp->cn_info.cn_child;
984*26947304SEvan Yan 
985*26947304SEvan Yan 			(void) devfs_clean(dip, NULL, DV_CLEAN_FORCE);
986*26947304SEvan Yan 			rv = ndi_devi_offline(cdip, NDI_UNCONFIG);
987*26947304SEvan Yan 			if (rv == NDI_SUCCESS) {
988*26947304SEvan Yan 				hdlp->cn_info.cn_state =
989*26947304SEvan Yan 				    DDI_HP_CN_STATE_OFFLINE;
990*26947304SEvan Yan 				rv = DDI_SUCCESS;
991*26947304SEvan Yan 			} else {
992*26947304SEvan Yan 				rv = DDI_EBUSY;
993*26947304SEvan Yan 				DDI_HP_IMPLDBG((CE_CONT,
994*26947304SEvan Yan 				    "ddihp_port_downgrade_state: failed "
995*26947304SEvan Yan 				    "to offline node, rv=%x, cdip=%p \n",
996*26947304SEvan Yan 				    rv, (void *)cdip));
997*26947304SEvan Yan 			}
998*26947304SEvan Yan 
999*26947304SEvan Yan 			break;
1000*26947304SEvan Yan 		default:
1001*26947304SEvan Yan 			/* should never reach here */
1002*26947304SEvan Yan 			ASSERT("unknown devinfo state");
1003*26947304SEvan Yan 		}
1004*26947304SEvan Yan 		curr_state = hdlp->cn_info.cn_state;
1005*26947304SEvan Yan 		if (rv != DDI_SUCCESS) {
1006*26947304SEvan Yan 			DDI_HP_IMPLDBG((CE_CONT,
1007*26947304SEvan Yan 			    "ddihp_port_downgrade_state: failed "
1008*26947304SEvan Yan 			    "curr_state=%x, target_state=%x \n",
1009*26947304SEvan Yan 			    curr_state, target_state));
1010*26947304SEvan Yan 			return (rv);
1011*26947304SEvan Yan 		}
1012*26947304SEvan Yan 	}
1013*26947304SEvan Yan 
1014*26947304SEvan Yan 	return (rv);
1015*26947304SEvan Yan }
1016*26947304SEvan Yan 
1017*26947304SEvan Yan /*
1018*26947304SEvan Yan  * Misc routines
1019*26947304SEvan Yan  */
1020*26947304SEvan Yan 
1021*26947304SEvan Yan /* Update the last state change time */
1022*26947304SEvan Yan static void
1023*26947304SEvan Yan ddihp_update_last_change(ddi_hp_cn_handle_t *hdlp)
1024*26947304SEvan Yan {
1025*26947304SEvan Yan 	time_t			time;
1026*26947304SEvan Yan 
1027*26947304SEvan Yan 	if (drv_getparm(TIME, (void *)&time) != DDI_SUCCESS)
1028*26947304SEvan Yan 		hdlp->cn_info.cn_last_change = (time_t)-1;
1029*26947304SEvan Yan 	else
1030*26947304SEvan Yan 		hdlp->cn_info.cn_last_change = (time32_t)time;
1031*26947304SEvan Yan }
1032*26947304SEvan Yan 
1033*26947304SEvan Yan /*
1034*26947304SEvan Yan  * Check the device for a 'status' property.  A conforming device
1035*26947304SEvan Yan  * should have a status of "okay", "disabled", "fail", or "fail-xxx".
1036*26947304SEvan Yan  *
1037*26947304SEvan Yan  * Return FALSE for a conforming device that is disabled or faulted.
1038*26947304SEvan Yan  * Return TRUE in every other case.
1039*26947304SEvan Yan  *
1040*26947304SEvan Yan  * 'status' property is NOT a bus specific property. It is defined in page 184,
1041*26947304SEvan Yan  * IEEE 1275 spec. The full name of the spec is "IEEE Standard for
1042*26947304SEvan Yan  * Boot (Initialization Configuration) Firmware: Core Requirements and
1043*26947304SEvan Yan  * Practices".
1044*26947304SEvan Yan  */
1045*26947304SEvan Yan static boolean_t
1046*26947304SEvan Yan ddihp_check_status_prop(dev_info_t *dip)
1047*26947304SEvan Yan {
1048*26947304SEvan Yan 	char		*status_prop;
1049*26947304SEvan Yan 	boolean_t	rv = B_TRUE;
1050*26947304SEvan Yan 
1051*26947304SEvan Yan 	/* try to get the 'status' property */
1052*26947304SEvan Yan 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1053*26947304SEvan Yan 	    "status", &status_prop) == DDI_PROP_SUCCESS) {
1054*26947304SEvan Yan 		/*
1055*26947304SEvan Yan 		 * test if the status is "disabled", "fail", or
1056*26947304SEvan Yan 		 * "fail-xxx".
1057*26947304SEvan Yan 		 */
1058*26947304SEvan Yan 		if (strcmp(status_prop, "disabled") == 0) {
1059*26947304SEvan Yan 			rv = B_FALSE;
1060*26947304SEvan Yan 			DDI_HP_IMPLDBG((CE_CONT, "ddihp_check_status_prop "
1061*26947304SEvan Yan 			    "(%s%d): device is in disabled state",
1062*26947304SEvan Yan 			    ddi_driver_name(dip), ddi_get_instance(dip)));
1063*26947304SEvan Yan 		} else if (strncmp(status_prop, "fail", 4) == 0) {
1064*26947304SEvan Yan 			rv = B_FALSE;
1065*26947304SEvan Yan 			cmn_err(CE_WARN,
1066*26947304SEvan Yan 			    "hotplug (%s%d): device is in fault state (%s)\n",
1067*26947304SEvan Yan 			    ddi_driver_name(dip), ddi_get_instance(dip),
1068*26947304SEvan Yan 			    status_prop);
1069*26947304SEvan Yan 		}
1070*26947304SEvan Yan 
1071*26947304SEvan Yan 		ddi_prop_free(status_prop);
1072*26947304SEvan Yan 	}
1073*26947304SEvan Yan 
1074*26947304SEvan Yan 	return (rv);
1075*26947304SEvan Yan }
1076