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