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