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