4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Sun DDI hotplug implementation specific functions
30 #include <sys/sysmacros.h>
31 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
36 #include <sys/cmn_err.h>
37 #include <sys/debug.h>
38 #include <sys/avintr.h>
39 #include <sys/autoconf.h>
41 #include <sys/sunndi.h>
42 #include <sys/ndi_impldefs.h>
43 #include <sys/sysevent.h>
44 #include <sys/sysevent/eventdefs.h>
45 #include <sys/sysevent/dr.h>
46 #include <sys/fs/dv_node.h>
49 * Local function prototypes
51 /* Connector operations */
52 static int ddihp_cn_pre_change_state(ddi_hp_cn_handle_t
*hdlp
,
53 ddi_hp_cn_state_t target_state
);
54 static int ddihp_cn_post_change_state(ddi_hp_cn_handle_t
*hdlp
,
55 ddi_hp_cn_state_t new_state
);
56 static int ddihp_cn_handle_state_change(ddi_hp_cn_handle_t
*hdlp
);
57 static int ddihp_cn_change_children_state(ddi_hp_cn_handle_t
*hdlp
,
60 static int ddihp_port_change_state(ddi_hp_cn_handle_t
*hdlp
,
61 ddi_hp_cn_state_t target_state
);
62 static int ddihp_port_upgrade_state(ddi_hp_cn_handle_t
*hdlp
,
63 ddi_hp_cn_state_t target_state
);
64 static int ddihp_port_downgrade_state(ddi_hp_cn_handle_t
*hdlp
,
65 ddi_hp_cn_state_t target_state
);
67 static void ddihp_update_last_change(ddi_hp_cn_handle_t
*hdlp
);
68 static boolean_t
ddihp_check_status_prop(dev_info_t
*dip
);
71 * Global functions (called within hotplug framework)
75 * Implement modctl() commands for hotplug.
76 * Called by modctl_hp() in modctl.c
79 ddihp_modctl(int hp_op
, char *path
, char *cn_name
, uintptr_t arg
,
83 ddi_hp_cn_handle_t
*hdlp
;
84 ddi_hp_op_t op
= (ddi_hp_op_t
)hp_op
;
87 /* Get the dip of nexus node */
88 dip
= e_ddi_hold_devi_by_path(path
, 0);
93 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_modctl: dip %p op %x path %s "
94 "cn_name %s arg %p rval %p\n", (void *)dip
, hp_op
, path
, cn_name
,
95 (void *)arg
, (void *)rval
));
97 if (!NEXUS_HAS_HP_OP(dip
)) {
98 ddi_release_devi(dip
);
102 /* Lock before access */
103 ndi_devi_enter(dip
, &count
);
105 hdlp
= ddihp_cn_name_to_handle(dip
, cn_name
);
107 if (hp_op
== DDI_HPOP_CN_CREATE_PORT
) {
109 /* this port already exists. */
114 rv
= (*(DEVI(dip
)->devi_ops
->devo_bus_ops
->bus_hp_op
))(
115 dip
, cn_name
, op
, NULL
, NULL
);
118 /* Invalid Connection name */
123 if (hp_op
== DDI_HPOP_CN_CHANGE_STATE
) {
124 ddi_hp_cn_state_t target_state
= (ddi_hp_cn_state_t
)arg
;
125 ddi_hp_cn_state_t result_state
= 0;
127 DDIHP_CN_OPS(hdlp
, op
, (void *)&target_state
,
128 (void *)&result_state
, rv
);
130 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_modctl: target_state="
131 "%x, result_state=%x, rv=%x \n",
132 target_state
, result_state
, rv
));
134 DDIHP_CN_OPS(hdlp
, op
, (void *)arg
, (void *)rval
, rv
);
158 ndi_devi_exit(dip
, count
);
160 ddi_release_devi(dip
);
166 * Return the state of Hotplug Connection (CN)
169 ddihp_cn_getstate(ddi_hp_cn_handle_t
*hdlp
)
171 ddi_hp_cn_state_t new_state
;
174 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_cn_getstate: pdip %p hdlp %p\n",
175 (void *)hdlp
->cn_dip
, (void *)hdlp
));
177 ASSERT(DEVI_BUSY_OWNED(hdlp
->cn_dip
));
179 DDIHP_CN_OPS(hdlp
, DDI_HPOP_CN_GET_STATE
,
180 NULL
, (void *)&new_state
, ret
);
181 if (ret
!= DDI_SUCCESS
) {
182 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_cn_getstate: "
183 "CN %p getstate command failed\n", (void *)hdlp
));
188 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_cn_getstate: hdlp %p "
189 "current Connection state %x new Connection state %x\n",
190 (void *)hdlp
, hdlp
->cn_info
.cn_state
, new_state
));
192 if (new_state
!= hdlp
->cn_info
.cn_state
) {
193 hdlp
->cn_info
.cn_state
= new_state
;
194 ddihp_update_last_change(hdlp
);
201 * Implementation function for unregistering the Hotplug Connection (CN)
204 ddihp_cn_unregister(ddi_hp_cn_handle_t
*hdlp
)
206 dev_info_t
*dip
= hdlp
->cn_dip
;
208 DDI_HP_NEXDBG((CE_CONT
, "ddihp_cn_unregister: hdlp %p\n",
211 ASSERT(DEVI_BUSY_OWNED(dip
));
213 (void) ddihp_cn_getstate(hdlp
);
215 if (hdlp
->cn_info
.cn_state
> DDI_HP_CN_STATE_OFFLINE
) {
216 DDI_HP_NEXDBG((CE_CONT
, "ddihp_cn_unregister: dip %p, hdlp %p "
217 "state %x. Device busy, failed to unregister connection!\n",
218 (void *)dip
, (void *)hdlp
, hdlp
->cn_info
.cn_state
));
223 /* unlink the handle */
224 DDIHP_LIST_REMOVE(ddi_hp_cn_handle_t
, (DEVI(dip
)->devi_hp_hdlp
), hdlp
);
226 kmem_free(hdlp
->cn_info
.cn_name
, strlen(hdlp
->cn_info
.cn_name
) + 1);
227 kmem_free(hdlp
, sizeof (ddi_hp_cn_handle_t
));
228 return (DDI_SUCCESS
);
232 * For a given Connection name and the dip node where the Connection is
233 * supposed to be, find the corresponding hotplug handle.
236 ddihp_cn_name_to_handle(dev_info_t
*dip
, char *cn_name
)
238 ddi_hp_cn_handle_t
*hdlp
;
240 ASSERT(DEVI_BUSY_OWNED(dip
));
242 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_cn_name_to_handle: "
243 "dip %p cn_name to find: %s", (void *)dip
, cn_name
));
244 for (hdlp
= DEVI(dip
)->devi_hp_hdlp
; hdlp
; hdlp
= hdlp
->next
) {
245 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_cn_name_to_handle: "
246 "current cn_name: %s", hdlp
->cn_info
.cn_name
));
248 if (strcmp(cn_name
, hdlp
->cn_info
.cn_name
) == 0) {
253 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_cn_name_to_handle: "
254 "failed to find cn_name"));
259 * Process the hotplug operations for Connector and also create Port
263 ddihp_connector_ops(ddi_hp_cn_handle_t
*hdlp
, ddi_hp_op_t op
,
264 void *arg
, void *result
)
266 int rv
= DDI_SUCCESS
;
267 dev_info_t
*dip
= hdlp
->cn_dip
;
269 ASSERT(DEVI_BUSY_OWNED(dip
));
271 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_connector_ops: pdip=%p op=%x "
272 "hdlp=%p arg=%p\n", (void *)dip
, op
, (void *)hdlp
, arg
));
274 if (op
== DDI_HPOP_CN_CHANGE_STATE
) {
275 ddi_hp_cn_state_t target_state
= *(ddi_hp_cn_state_t
*)arg
;
277 rv
= ddihp_cn_pre_change_state(hdlp
, target_state
);
278 if (rv
!= DDI_SUCCESS
) {
279 /* the state is not changed */
280 *((ddi_hp_cn_state_t
*)result
) =
281 hdlp
->cn_info
.cn_state
;
285 ASSERT(NEXUS_HAS_HP_OP(dip
));
286 rv
= (*(DEVI(dip
)->devi_ops
->devo_bus_ops
->bus_hp_op
))(
287 dip
, hdlp
->cn_info
.cn_name
, op
, arg
, result
);
289 if (rv
!= DDI_SUCCESS
) {
290 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_connector_ops: "
291 "bus_hp_op failed: pdip=%p cn_name:%s op=%x "
292 "hdlp=%p arg=%p\n", (void *)dip
, hdlp
->cn_info
.cn_name
,
293 op
, (void *)hdlp
, arg
));
295 if (op
== DDI_HPOP_CN_CHANGE_STATE
) {
298 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_connector_ops: "
299 "old_state=%x, new_state=%x, rv=%x\n",
300 hdlp
->cn_info
.cn_state
, *(ddi_hp_cn_state_t
*)result
, rv
));
303 * After state change op is successfully done or
304 * failed at some stages, continue to do some jobs.
306 rv_post
= ddihp_cn_post_change_state(hdlp
,
307 *(ddi_hp_cn_state_t
*)result
);
309 if (rv_post
!= DDI_SUCCESS
)
317 * Process the hotplug op for Port
320 ddihp_port_ops(ddi_hp_cn_handle_t
*hdlp
, ddi_hp_op_t op
,
321 void *arg
, void *result
)
323 int ret
= DDI_SUCCESS
;
325 ASSERT(DEVI_BUSY_OWNED(hdlp
->cn_dip
));
327 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_port_ops: pdip=%p op=%x hdlp=%p "
328 "arg=%p\n", (void *)hdlp
->cn_dip
, op
, (void *)hdlp
, arg
));
331 case DDI_HPOP_CN_GET_STATE
:
335 state
= hdlp
->cn_info
.cn_state
;
337 if (hdlp
->cn_info
.cn_child
== NULL
) {
338 /* No child. Either present or empty. */
339 if (state
>= DDI_HP_CN_STATE_PORT_PRESENT
)
340 state
= DDI_HP_CN_STATE_PORT_PRESENT
;
342 state
= DDI_HP_CN_STATE_PORT_EMPTY
;
344 } else { /* There is a child of this Port */
346 /* Check DEVI(dip)->devi_node_state */
347 switch (i_ddi_node_state(hdlp
->cn_info
.cn_child
)) {
354 state
= DDI_HP_CN_STATE_OFFLINE
;
357 state
= DDI_HP_CN_STATE_MAINTENANCE
;
360 state
= DDI_HP_CN_STATE_ONLINE
;
363 /* should never reach here */
364 ASSERT("unknown devinfo state");
367 * Check DEVI(dip)->devi_state in case the node is
368 * downgraded or quiesced.
370 if (state
== DDI_HP_CN_STATE_ONLINE
&&
371 ddi_get_devstate(hdlp
->cn_info
.cn_child
) !=
373 state
= DDI_HP_CN_STATE_MAINTENANCE
;
376 *((ddi_hp_cn_state_t
*)result
) = state
;
380 case DDI_HPOP_CN_CHANGE_STATE
:
382 ddi_hp_cn_state_t target_state
= *(ddi_hp_cn_state_t
*)arg
;
383 ddi_hp_cn_state_t curr_state
= hdlp
->cn_info
.cn_state
;
385 ret
= ddihp_port_change_state(hdlp
, target_state
);
386 if (curr_state
!= hdlp
->cn_info
.cn_state
) {
387 ddihp_update_last_change(hdlp
);
389 *((ddi_hp_cn_state_t
*)result
) = hdlp
->cn_info
.cn_state
;
393 case DDI_HPOP_CN_REMOVE_PORT
:
395 (void) ddihp_cn_getstate(hdlp
);
397 if (hdlp
->cn_info
.cn_state
!= DDI_HP_CN_STATE_PORT_EMPTY
) {
398 /* Only empty PORT can be removed by commands */
404 ret
= ddihp_cn_unregister(hdlp
);
416 * Generate the system event with a possible hint
420 ddihp_cn_gen_sysevent(ddi_hp_cn_handle_t
*hdlp
,
421 ddi_hp_cn_sysevent_t event_sub_class
, int hint
, int kmflag
)
423 dev_info_t
*dip
= hdlp
->cn_dip
;
424 char *cn_path
, *ap_id
;
425 char *ev_subclass
= NULL
;
426 nvlist_t
*ev_attr_list
= NULL
;
430 cn_path
= kmem_zalloc(MAXPATHLEN
, kmflag
);
431 if (cn_path
== NULL
) {
433 "%s%d: Failed to allocate memory for hotplug"
435 ddi_driver_name(dip
), ddi_get_instance(dip
),
436 hdlp
->cn_info
.cn_name
);
442 * Minor device name will be bus path
443 * concatenated with connection name.
444 * One of consumers of the sysevent will pass it
445 * to cfgadm as AP ID.
447 (void) strcpy(cn_path
, "/devices");
448 (void) ddi_pathname(dip
, cn_path
+ strlen("/devices"));
450 ap_id_len
= strlen(cn_path
) + strlen(":") +
451 strlen(hdlp
->cn_info
.cn_name
) + 1;
452 ap_id
= kmem_zalloc(ap_id_len
, kmflag
);
455 "%s%d: Failed to allocate memory for AP ID: %s:%s\n",
456 ddi_driver_name(dip
), ddi_get_instance(dip
),
457 cn_path
, hdlp
->cn_info
.cn_name
);
458 kmem_free(cn_path
, MAXPATHLEN
);
463 (void) strcpy(ap_id
, cn_path
);
464 (void) strcat(ap_id
, ":");
465 (void) strcat(ap_id
, hdlp
->cn_info
.cn_name
);
466 kmem_free(cn_path
, MAXPATHLEN
);
468 err
= nvlist_alloc(&ev_attr_list
, NV_UNIQUE_NAME_TYPE
, kmflag
);
472 "%s%d: Failed to allocate memory for event subclass %d\n",
473 ddi_driver_name(dip
), ddi_get_instance(dip
),
475 kmem_free(ap_id
, ap_id_len
);
480 switch (event_sub_class
) {
481 case DDI_HP_CN_STATE_CHANGE
:
482 ev_subclass
= ESC_DR_AP_STATE_CHANGE
;
485 case SE_NO_HINT
: /* fall through */
486 case SE_HINT_INSERT
: /* fall through */
488 err
= nvlist_add_string(ev_attr_list
, DR_HINT
,
492 cmn_err(CE_WARN
, "%s%d: Failed to add attr [%s]"
493 " for %s event\n", ddi_driver_name(dip
),
494 ddi_get_instance(dip
), DR_HINT
,
495 ESC_DR_AP_STATE_CHANGE
);
502 cmn_err(CE_WARN
, "%s%d: Unknown hint on sysevent\n",
503 ddi_driver_name(dip
), ddi_get_instance(dip
));
510 /* event sub class: DDI_HP_CN_REQ */
512 ev_subclass
= ESC_DR_REQ
;
515 case SE_INVESTIGATE_RES
: /* fall through */
516 case SE_INCOMING_RES
: /* fall through */
517 case SE_OUTGOING_RES
: /* fall through */
518 err
= nvlist_add_string(ev_attr_list
, DR_REQ_TYPE
,
523 "%s%d: Failed to add attr [%s] for %s \n"
524 "event", ddi_driver_name(dip
),
525 ddi_get_instance(dip
),
526 DR_REQ_TYPE
, ESC_DR_REQ
);
533 cmn_err(CE_WARN
, "%s%d: Unknown hint on sysevent\n",
534 ddi_driver_name(dip
), ddi_get_instance(dip
));
542 cmn_err(CE_WARN
, "%s%d: Unknown Event subclass\n",
543 ddi_driver_name(dip
), ddi_get_instance(dip
));
549 * Add Hotplug Connection (CN) as attribute (common attribute)
551 err
= nvlist_add_string(ev_attr_list
, DR_AP_ID
, ap_id
);
553 cmn_err(CE_WARN
, "%s%d: Failed to add attr [%s] for %s event\n",
554 ddi_driver_name(dip
), ddi_get_instance(dip
),
561 * Log this event with sysevent framework.
563 err
= ddi_log_sysevent(dip
, DDI_VENDOR_SUNW
, EC_DR
,
564 ev_subclass
, ev_attr_list
, &eid
,
565 ((kmflag
== KM_SLEEP
) ? DDI_SLEEP
: DDI_NOSLEEP
));
568 cmn_err(CE_WARN
, "%s%d: Failed to log %s event\n",
569 ddi_driver_name(dip
), ddi_get_instance(dip
), EC_DR
);
573 nvlist_free(ev_attr_list
);
574 kmem_free(ap_id
, ap_id_len
);
578 * Local functions (called within this file)
582 * Connector operations
586 * Prepare to change state for a Connector: offline, unprobe, etc.
589 ddihp_cn_pre_change_state(ddi_hp_cn_handle_t
*hdlp
,
590 ddi_hp_cn_state_t target_state
)
592 ddi_hp_cn_state_t curr_state
= hdlp
->cn_info
.cn_state
;
593 dev_info_t
*dip
= hdlp
->cn_dip
;
594 int rv
= DDI_SUCCESS
;
596 if (curr_state
> target_state
&&
597 curr_state
== DDI_HP_CN_STATE_ENABLED
) {
599 * If the Connection goes to a lower state from ENABLED,
600 * then offline all children under it.
602 rv
= ddihp_cn_change_children_state(hdlp
, B_FALSE
);
603 if (rv
!= DDI_SUCCESS
) {
606 "failed to unconfigure the device in the"
607 " Connection %s\n", ddi_driver_name(dip
),
608 ddi_get_instance(dip
),
609 hdlp
->cn_info
.cn_name
);
613 ASSERT(NEXUS_HAS_HP_OP(dip
));
615 * Remove all the children and their ports
616 * after they are offlined.
618 rv
= (*(DEVI(dip
)->devi_ops
->devo_bus_ops
->bus_hp_op
))(
619 dip
, hdlp
->cn_info
.cn_name
, DDI_HPOP_CN_UNPROBE
,
621 if (rv
!= DDI_SUCCESS
) {
624 " to unprobe the device in the Connector"
625 " %s\n", ddi_driver_name(dip
),
626 ddi_get_instance(dip
),
627 hdlp
->cn_info
.cn_name
);
632 DDI_HP_NEXDBG((CE_CONT
,
633 "ddihp_connector_ops (%s%d): device"
634 " is unconfigured and unprobed in Connector %s\n",
635 ddi_driver_name(dip
), ddi_get_instance(dip
),
636 hdlp
->cn_info
.cn_name
));
643 * Jobs after change state of a Connector: update last change time,
644 * probe, online, sysevent, etc.
647 ddihp_cn_post_change_state(ddi_hp_cn_handle_t
*hdlp
,
648 ddi_hp_cn_state_t new_state
)
650 int rv
= DDI_SUCCESS
;
651 ddi_hp_cn_state_t curr_state
= hdlp
->cn_info
.cn_state
;
653 /* Update the state in handle */
654 if (new_state
!= curr_state
) {
655 hdlp
->cn_info
.cn_state
= new_state
;
656 ddihp_update_last_change(hdlp
);
659 if (curr_state
< new_state
&&
660 new_state
== DDI_HP_CN_STATE_ENABLED
) {
662 * Probe and online devices if state is
663 * upgraded to ENABLED.
665 rv
= ddihp_cn_handle_state_change(hdlp
);
667 if (curr_state
!= hdlp
->cn_info
.cn_state
) {
669 * For Connector, generate a sysevent on
672 ddihp_cn_gen_sysevent(hdlp
, DDI_HP_CN_STATE_CHANGE
,
673 SE_NO_HINT
, KM_SLEEP
);
680 * Handle Connector state change.
682 * This function is called after connector is upgraded to ENABLED sate.
683 * It probes the device plugged in the connector to setup devinfo nodes
684 * and then online the nodes.
687 ddihp_cn_handle_state_change(ddi_hp_cn_handle_t
*hdlp
)
689 dev_info_t
*dip
= hdlp
->cn_dip
;
690 int rv
= DDI_SUCCESS
;
692 ASSERT(DEVI_BUSY_OWNED(dip
));
693 ASSERT(NEXUS_HAS_HP_OP(dip
));
695 * If the Connection went to state ENABLED from a lower state,
698 rv
= (*(DEVI(dip
)->devi_ops
->devo_bus_ops
->bus_hp_op
))(
699 dip
, hdlp
->cn_info
.cn_name
, DDI_HPOP_CN_PROBE
, NULL
, NULL
);
701 if (rv
!= DDI_SUCCESS
) {
702 ddi_hp_cn_state_t target_state
= DDI_HP_CN_STATE_POWERED
;
703 ddi_hp_cn_state_t result_state
= 0;
706 * Probe failed. Disable the connector so that it can
707 * be enabled again by a later try from userland.
709 (void) (*(DEVI(dip
)->devi_ops
->devo_bus_ops
->bus_hp_op
))(
710 dip
, hdlp
->cn_info
.cn_name
, DDI_HPOP_CN_CHANGE_STATE
,
711 (void *)&target_state
, (void *)&result_state
);
713 if (result_state
&& result_state
!= hdlp
->cn_info
.cn_state
) {
714 hdlp
->cn_info
.cn_state
= result_state
;
715 ddihp_update_last_change(hdlp
);
719 "(%s%d): failed to probe the Connection %s\n",
720 ddi_driver_name(dip
), ddi_get_instance(dip
),
721 hdlp
->cn_info
.cn_name
);
726 * Try to online all the children of CN.
728 (void) ddihp_cn_change_children_state(hdlp
, B_TRUE
);
730 DDI_HP_NEXDBG((CE_CONT
, "ddihp_cn_event_handler (%s%d): "
731 "device is configured in the Connection %s\n",
732 ddi_driver_name(dip
), ddi_get_instance(dip
),
733 hdlp
->cn_info
.cn_name
));
738 * Online/Offline all the children under the Hotplug Connection (CN)
740 * Do online operation when the online parameter is true; otherwise do offline.
743 ddihp_cn_change_children_state(ddi_hp_cn_handle_t
*hdlp
, boolean_t online
)
745 dev_info_t
*dip
= hdlp
->cn_dip
;
747 ddi_hp_cn_handle_t
*h
;
748 int rv
= DDI_SUCCESS
;
750 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_cn_change_children_state:"
751 " dip %p hdlp %p, online %x\n",
752 (void *)dip
, (void *)hdlp
, online
));
754 ASSERT(DEVI_BUSY_OWNED(dip
));
757 * Return invalid if Connection state is < DDI_HP_CN_STATE_ENABLED
758 * when try to online children.
760 if (online
&& hdlp
->cn_info
.cn_state
< DDI_HP_CN_STATE_ENABLED
) {
761 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_cn_change_children_state: "
762 "Connector %p is not in probed state\n", (void *)hdlp
));
767 /* Now, online/offline all the devices depending on the Connector */
771 * For offline operation we need to firstly clean up devfs
772 * so as not to prevent driver detach.
774 (void) devfs_clean(dip
, NULL
, DV_CLEAN_FORCE
);
776 for (h
= DEVI(dip
)->devi_hp_hdlp
; h
; h
= h
->next
) {
777 if (h
->cn_info
.cn_type
!= DDI_HP_CN_TYPE_VIRTUAL_PORT
)
780 if (h
->cn_info
.cn_num_dpd_on
!=
781 hdlp
->cn_info
.cn_num
)
784 cdip
= h
->cn_info
.cn_child
;
787 /* online children */
788 if (!ddihp_check_status_prop(dip
))
791 if (ndi_devi_online(cdip
,
792 NDI_ONLINE_ATTACH
| NDI_CONFIG
) != NDI_SUCCESS
) {
795 " failed to attach driver for a device"
796 " (%s%d) under the Connection %s\n",
797 ddi_driver_name(dip
), ddi_get_instance(dip
),
798 ddi_driver_name(cdip
),
799 ddi_get_instance(cdip
),
800 hdlp
->cn_info
.cn_name
);
802 * One of the devices failed to online, but we
803 * want to continue to online the rest siblings
804 * after mark the failure here.
811 /* offline children */
812 if (ndi_devi_offline(cdip
, NDI_UNCONFIG
) !=
816 " failed to dettach driver for the device"
817 " (%s%d) in the Connection %s\n",
818 ddi_driver_name(dip
), ddi_get_instance(dip
),
819 ddi_driver_name(cdip
),
820 ddi_get_instance(cdip
),
821 hdlp
->cn_info
.cn_name
);
836 * Change Port state to target_state.
839 ddihp_port_change_state(ddi_hp_cn_handle_t
*hdlp
,
840 ddi_hp_cn_state_t target_state
)
842 ddi_hp_cn_state_t curr_state
= hdlp
->cn_info
.cn_state
;
844 if (target_state
< DDI_HP_CN_STATE_PORT_EMPTY
||
845 target_state
> DDI_HP_CN_STATE_ONLINE
) {
850 if (curr_state
< target_state
)
851 return (ddihp_port_upgrade_state(hdlp
, target_state
));
852 else if (curr_state
> target_state
)
853 return (ddihp_port_downgrade_state(hdlp
, target_state
));
855 return (DDI_SUCCESS
);
859 * Upgrade port state to target_state.
862 ddihp_port_upgrade_state(ddi_hp_cn_handle_t
*hdlp
,
863 ddi_hp_cn_state_t target_state
)
865 ddi_hp_cn_state_t curr_state
, new_state
, result_state
;
867 int rv
= DDI_SUCCESS
;
869 curr_state
= hdlp
->cn_info
.cn_state
;
870 while (curr_state
< target_state
) {
871 switch (curr_state
) {
872 case DDI_HP_CN_STATE_PORT_EMPTY
:
873 /* Check the existence of the corresponding hardware */
874 new_state
= DDI_HP_CN_STATE_PORT_PRESENT
;
875 rv
= ddihp_connector_ops(hdlp
,
876 DDI_HPOP_CN_CHANGE_STATE
,
877 (void *)&new_state
, (void *)&result_state
);
878 if (rv
== DDI_SUCCESS
) {
879 hdlp
->cn_info
.cn_state
=
883 case DDI_HP_CN_STATE_PORT_PRESENT
:
884 /* Read-only probe the corresponding hardware. */
885 new_state
= DDI_HP_CN_STATE_OFFLINE
;
886 rv
= ddihp_connector_ops(hdlp
,
887 DDI_HPOP_CN_CHANGE_STATE
,
888 (void *)&new_state
, &cdip
);
889 if (rv
== DDI_SUCCESS
) {
890 hdlp
->cn_info
.cn_state
=
891 DDI_HP_CN_STATE_OFFLINE
;
893 ASSERT(hdlp
->cn_info
.cn_child
== NULL
);
894 hdlp
->cn_info
.cn_child
= cdip
;
897 case DDI_HP_CN_STATE_OFFLINE
:
899 case DDI_HP_CN_STATE_MAINTENANCE
:
901 cdip
= hdlp
->cn_info
.cn_child
;
903 rv
= ndi_devi_online(cdip
,
904 NDI_ONLINE_ATTACH
| NDI_CONFIG
);
905 if (rv
== NDI_SUCCESS
) {
906 hdlp
->cn_info
.cn_state
=
907 DDI_HP_CN_STATE_ONLINE
;
911 DDI_HP_IMPLDBG((CE_CONT
,
912 "ddihp_port_upgrade_state: "
913 "failed to online device %p at port: %s\n",
914 (void *)cdip
, hdlp
->cn_info
.cn_name
));
917 case DDI_HP_CN_STATE_ONLINE
:
921 /* should never reach here */
922 ASSERT("unknown devinfo state");
924 curr_state
= hdlp
->cn_info
.cn_state
;
925 if (rv
!= DDI_SUCCESS
) {
926 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_port_upgrade_state: "
927 "failed curr_state=%x, target_state=%x \n",
928 curr_state
, target_state
));
937 * Downgrade state to target_state
940 ddihp_port_downgrade_state(ddi_hp_cn_handle_t
*hdlp
,
941 ddi_hp_cn_state_t target_state
)
943 ddi_hp_cn_state_t curr_state
, new_state
, result_state
;
944 dev_info_t
*dip
= hdlp
->cn_dip
;
946 int rv
= DDI_SUCCESS
;
948 curr_state
= hdlp
->cn_info
.cn_state
;
949 while (curr_state
> target_state
) {
951 switch (curr_state
) {
952 case DDI_HP_CN_STATE_PORT_EMPTY
:
955 case DDI_HP_CN_STATE_PORT_PRESENT
:
956 /* Check the existence of the corresponding hardware */
957 new_state
= DDI_HP_CN_STATE_PORT_EMPTY
;
958 rv
= ddihp_connector_ops(hdlp
,
959 DDI_HPOP_CN_CHANGE_STATE
,
960 (void *)&new_state
, (void *)&result_state
);
961 if (rv
== DDI_SUCCESS
)
962 hdlp
->cn_info
.cn_state
=
966 case DDI_HP_CN_STATE_OFFLINE
:
968 * Read-only unprobe the corresponding hardware:
969 * 1. release the assigned resource;
970 * 2. remove the node pointed by the port's cn_child
972 new_state
= DDI_HP_CN_STATE_PORT_PRESENT
;
973 rv
= ddihp_connector_ops(hdlp
,
974 DDI_HPOP_CN_CHANGE_STATE
,
975 (void *)&new_state
, (void *)&result_state
);
976 if (rv
== DDI_SUCCESS
)
977 hdlp
->cn_info
.cn_state
=
978 DDI_HP_CN_STATE_PORT_PRESENT
;
980 case DDI_HP_CN_STATE_MAINTENANCE
:
982 case DDI_HP_CN_STATE_ONLINE
:
983 cdip
= hdlp
->cn_info
.cn_child
;
985 (void) devfs_clean(dip
, NULL
, DV_CLEAN_FORCE
);
986 rv
= ndi_devi_offline(cdip
, NDI_UNCONFIG
);
987 if (rv
== NDI_SUCCESS
) {
988 hdlp
->cn_info
.cn_state
=
989 DDI_HP_CN_STATE_OFFLINE
;
993 DDI_HP_IMPLDBG((CE_CONT
,
994 "ddihp_port_downgrade_state: failed "
995 "to offline node, rv=%x, cdip=%p \n",
1001 /* should never reach here */
1002 ASSERT("unknown devinfo state");
1004 curr_state
= hdlp
->cn_info
.cn_state
;
1005 if (rv
!= DDI_SUCCESS
) {
1006 DDI_HP_IMPLDBG((CE_CONT
,
1007 "ddihp_port_downgrade_state: failed "
1008 "curr_state=%x, target_state=%x \n",
1009 curr_state
, target_state
));
1021 /* Update the last state change time */
1023 ddihp_update_last_change(ddi_hp_cn_handle_t
*hdlp
)
1027 if (drv_getparm(TIME
, (void *)&time
) != DDI_SUCCESS
)
1028 hdlp
->cn_info
.cn_last_change
= (time_t)-1;
1030 hdlp
->cn_info
.cn_last_change
= (time32_t
)time
;
1034 * Check the device for a 'status' property. A conforming device
1035 * should have a status of "okay", "disabled", "fail", or "fail-xxx".
1037 * Return FALSE for a conforming device that is disabled or faulted.
1038 * Return TRUE in every other case.
1040 * 'status' property is NOT a bus specific property. It is defined in page 184,
1041 * IEEE 1275 spec. The full name of the spec is "IEEE Standard for
1042 * Boot (Initialization Configuration) Firmware: Core Requirements and
1046 ddihp_check_status_prop(dev_info_t
*dip
)
1049 boolean_t rv
= B_TRUE
;
1051 /* try to get the 'status' property */
1052 if (ddi_prop_lookup_string(DDI_DEV_T_ANY
, dip
, DDI_PROP_DONTPASS
,
1053 "status", &status_prop
) == DDI_PROP_SUCCESS
) {
1055 * test if the status is "disabled", "fail", or
1058 if (strcmp(status_prop
, "disabled") == 0) {
1060 DDI_HP_IMPLDBG((CE_CONT
, "ddihp_check_status_prop "
1061 "(%s%d): device is in disabled state",
1062 ddi_driver_name(dip
), ddi_get_instance(dip
)));
1063 } else if (strncmp(status_prop
, "fail", 4) == 0) {
1066 "hotplug (%s%d): device is in fault state (%s)\n",
1067 ddi_driver_name(dip
), ddi_get_instance(dip
),
1071 ddi_prop_free(status_prop
);