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