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 NDI hotplug interfaces 28*26947304SEvan Yan */ 29*26947304SEvan Yan 30*26947304SEvan Yan #include <sys/note.h> 31*26947304SEvan Yan #include <sys/sysmacros.h> 32*26947304SEvan Yan #include <sys/types.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/sunndi.h> 41*26947304SEvan Yan #include <sys/ndi_impldefs.h> 42*26947304SEvan Yan #include <sys/ddi.h> 43*26947304SEvan Yan #include <sys/disp.h> 44*26947304SEvan Yan #include <sys/stat.h> 45*26947304SEvan Yan #include <sys/callb.h> 46*26947304SEvan Yan #include <sys/sysevent.h> 47*26947304SEvan Yan #include <sys/sysevent/eventdefs.h> 48*26947304SEvan Yan #include <sys/sysevent/dr.h> 49*26947304SEvan Yan #include <sys/taskq.h> 50*26947304SEvan Yan 51*26947304SEvan Yan /* Local functions prototype */ 52*26947304SEvan Yan static void ddihp_cn_run_event(void *arg); 53*26947304SEvan Yan static int ddihp_cn_req_handler(ddi_hp_cn_handle_t *hdlp, 54*26947304SEvan Yan ddi_hp_cn_state_t target_state); 55*26947304SEvan Yan 56*26947304SEvan Yan /* 57*26947304SEvan Yan * Global functions (called by hotplug controller or nexus drivers) 58*26947304SEvan Yan */ 59*26947304SEvan Yan 60*26947304SEvan Yan /* 61*26947304SEvan Yan * Register the Hotplug Connection (CN) 62*26947304SEvan Yan */ 63*26947304SEvan Yan int 64*26947304SEvan Yan ndi_hp_register(dev_info_t *dip, ddi_hp_cn_info_t *info_p) 65*26947304SEvan Yan { 66*26947304SEvan Yan ddi_hp_cn_handle_t *hdlp; 67*26947304SEvan Yan int count; 68*26947304SEvan Yan 69*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ndi_hp_register: dip %p, info_p %p\n", 70*26947304SEvan Yan (void *)dip, (void *)info_p)); 71*26947304SEvan Yan 72*26947304SEvan Yan ASSERT(!servicing_interrupt()); 73*26947304SEvan Yan if (servicing_interrupt()) 74*26947304SEvan Yan return (NDI_FAILURE); 75*26947304SEvan Yan 76*26947304SEvan Yan /* Validate the arguments */ 77*26947304SEvan Yan if ((dip == NULL) || (info_p == NULL)) 78*26947304SEvan Yan return (NDI_EINVAL); 79*26947304SEvan Yan 80*26947304SEvan Yan if (!NEXUS_HAS_HP_OP(dip)) { 81*26947304SEvan Yan return (NDI_ENOTSUP); 82*26947304SEvan Yan } 83*26947304SEvan Yan /* Lock before access */ 84*26947304SEvan Yan ndi_devi_enter(dip, &count); 85*26947304SEvan Yan 86*26947304SEvan Yan hdlp = ddihp_cn_name_to_handle(dip, info_p->cn_name); 87*26947304SEvan Yan if (hdlp) { 88*26947304SEvan Yan /* This cn_name is already registered. */ 89*26947304SEvan Yan ndi_devi_exit(dip, count); 90*26947304SEvan Yan 91*26947304SEvan Yan return (NDI_SUCCESS); 92*26947304SEvan Yan } 93*26947304SEvan Yan /* 94*26947304SEvan Yan * Create and initialize hotplug Connection handle 95*26947304SEvan Yan */ 96*26947304SEvan Yan hdlp = (ddi_hp_cn_handle_t *)kmem_zalloc( 97*26947304SEvan Yan (sizeof (ddi_hp_cn_handle_t)), KM_SLEEP); 98*26947304SEvan Yan 99*26947304SEvan Yan /* Copy the Connection information */ 100*26947304SEvan Yan hdlp->cn_dip = dip; 101*26947304SEvan Yan bcopy(info_p, &(hdlp->cn_info), sizeof (*info_p)); 102*26947304SEvan Yan 103*26947304SEvan Yan /* Copy cn_name */ 104*26947304SEvan Yan hdlp->cn_info.cn_name = ddi_strdup(info_p->cn_name, KM_SLEEP); 105*26947304SEvan Yan 106*26947304SEvan Yan if (ddihp_cn_getstate(hdlp) != DDI_SUCCESS) { 107*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ndi_hp_register: dip %p, hdlp %p" 108*26947304SEvan Yan "ddi_cn_getstate failed\n", (void *)dip, (void *)hdlp)); 109*26947304SEvan Yan 110*26947304SEvan Yan goto fail; 111*26947304SEvan Yan } 112*26947304SEvan Yan 113*26947304SEvan Yan /* 114*26947304SEvan Yan * Append the handle to the list 115*26947304SEvan Yan */ 116*26947304SEvan Yan DDIHP_LIST_APPEND(ddi_hp_cn_handle_t, (DEVI(dip)->devi_hp_hdlp), 117*26947304SEvan Yan hdlp); 118*26947304SEvan Yan 119*26947304SEvan Yan ndi_devi_exit(dip, count); 120*26947304SEvan Yan 121*26947304SEvan Yan return (NDI_SUCCESS); 122*26947304SEvan Yan 123*26947304SEvan Yan fail: 124*26947304SEvan Yan kmem_free(hdlp->cn_info.cn_name, strlen(hdlp->cn_info.cn_name) + 1); 125*26947304SEvan Yan kmem_free(hdlp, sizeof (ddi_hp_cn_handle_t)); 126*26947304SEvan Yan ndi_devi_exit(dip, count); 127*26947304SEvan Yan 128*26947304SEvan Yan return (NDI_FAILURE); 129*26947304SEvan Yan } 130*26947304SEvan Yan 131*26947304SEvan Yan /* 132*26947304SEvan Yan * Unregister a Hotplug Connection (CN) 133*26947304SEvan Yan */ 134*26947304SEvan Yan int 135*26947304SEvan Yan ndi_hp_unregister(dev_info_t *dip, char *cn_name) 136*26947304SEvan Yan { 137*26947304SEvan Yan ddi_hp_cn_handle_t *hdlp; 138*26947304SEvan Yan int count; 139*26947304SEvan Yan int ret; 140*26947304SEvan Yan 141*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ndi_hp_unregister: dip %p, cn name %s\n", 142*26947304SEvan Yan (void *)dip, cn_name)); 143*26947304SEvan Yan 144*26947304SEvan Yan ASSERT(!servicing_interrupt()); 145*26947304SEvan Yan if (servicing_interrupt()) 146*26947304SEvan Yan return (NDI_FAILURE); 147*26947304SEvan Yan 148*26947304SEvan Yan /* Validate the arguments */ 149*26947304SEvan Yan if ((dip == NULL) || (cn_name == NULL)) 150*26947304SEvan Yan return (NDI_EINVAL); 151*26947304SEvan Yan 152*26947304SEvan Yan ndi_devi_enter(dip, &count); 153*26947304SEvan Yan 154*26947304SEvan Yan hdlp = ddihp_cn_name_to_handle(dip, cn_name); 155*26947304SEvan Yan if (hdlp == NULL) { 156*26947304SEvan Yan ndi_devi_exit(dip, count); 157*26947304SEvan Yan return (NDI_EINVAL); 158*26947304SEvan Yan } 159*26947304SEvan Yan 160*26947304SEvan Yan switch (ddihp_cn_unregister(hdlp)) { 161*26947304SEvan Yan case DDI_SUCCESS: 162*26947304SEvan Yan ret = NDI_SUCCESS; 163*26947304SEvan Yan break; 164*26947304SEvan Yan case DDI_EINVAL: 165*26947304SEvan Yan ret = NDI_EINVAL; 166*26947304SEvan Yan break; 167*26947304SEvan Yan case DDI_EBUSY: 168*26947304SEvan Yan ret = NDI_BUSY; 169*26947304SEvan Yan break; 170*26947304SEvan Yan default: 171*26947304SEvan Yan ret = NDI_FAILURE; 172*26947304SEvan Yan break; 173*26947304SEvan Yan } 174*26947304SEvan Yan 175*26947304SEvan Yan ndi_devi_exit(dip, count); 176*26947304SEvan Yan 177*26947304SEvan Yan return (ret); 178*26947304SEvan Yan } 179*26947304SEvan Yan 180*26947304SEvan Yan /* 181*26947304SEvan Yan * Notify the Hotplug Connection (CN) to change state. 182*26947304SEvan Yan * Flag: 183*26947304SEvan Yan * DDI_HP_REQ_SYNC Return after the change is finished. 184*26947304SEvan Yan * DDI_HP_REQ_ASYNC Return after the request is dispatched. 185*26947304SEvan Yan */ 186*26947304SEvan Yan int 187*26947304SEvan Yan ndi_hp_state_change_req(dev_info_t *dip, char *cn_name, 188*26947304SEvan Yan ddi_hp_cn_state_t state, uint_t flag) 189*26947304SEvan Yan { 190*26947304SEvan Yan ddi_hp_cn_async_event_entry_t *eventp; 191*26947304SEvan Yan 192*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ndi_hp_state_change_req: dip %p " 193*26947304SEvan Yan "cn_name: %s, state %x, flag %x\n", 194*26947304SEvan Yan (void *)dip, cn_name, state, flag)); 195*26947304SEvan Yan 196*26947304SEvan Yan /* Validate the arguments */ 197*26947304SEvan Yan if (dip == NULL || cn_name == NULL) 198*26947304SEvan Yan return (NDI_EINVAL); 199*26947304SEvan Yan 200*26947304SEvan Yan if (!NEXUS_HAS_HP_OP(dip)) { 201*26947304SEvan Yan return (NDI_ENOTSUP); 202*26947304SEvan Yan } 203*26947304SEvan Yan /* 204*26947304SEvan Yan * If the request is to handle the event synchronously, then call 205*26947304SEvan Yan * the event handler without queuing the event. 206*26947304SEvan Yan */ 207*26947304SEvan Yan if (flag & DDI_HP_REQ_SYNC) { 208*26947304SEvan Yan ddi_hp_cn_handle_t *hdlp; 209*26947304SEvan Yan int count; 210*26947304SEvan Yan int ret; 211*26947304SEvan Yan 212*26947304SEvan Yan ASSERT(!servicing_interrupt()); 213*26947304SEvan Yan if (servicing_interrupt()) 214*26947304SEvan Yan return (NDI_FAILURE); 215*26947304SEvan Yan 216*26947304SEvan Yan ndi_devi_enter(dip, &count); 217*26947304SEvan Yan 218*26947304SEvan Yan hdlp = ddihp_cn_name_to_handle(dip, cn_name); 219*26947304SEvan Yan if (hdlp == NULL) { 220*26947304SEvan Yan ndi_devi_exit(dip, count); 221*26947304SEvan Yan 222*26947304SEvan Yan return (NDI_EINVAL); 223*26947304SEvan Yan } 224*26947304SEvan Yan 225*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ndi_hp_state_change_req: hdlp %p " 226*26947304SEvan Yan "calling ddihp_cn_req_handler() directly to handle " 227*26947304SEvan Yan "target_state %x\n", (void *)hdlp, state)); 228*26947304SEvan Yan 229*26947304SEvan Yan ret = ddihp_cn_req_handler(hdlp, state); 230*26947304SEvan Yan 231*26947304SEvan Yan ndi_devi_exit(dip, count); 232*26947304SEvan Yan 233*26947304SEvan Yan return (ret); 234*26947304SEvan Yan } 235*26947304SEvan Yan 236*26947304SEvan Yan eventp = kmem_zalloc(sizeof (ddi_hp_cn_async_event_entry_t), 237*26947304SEvan Yan KM_NOSLEEP); 238*26947304SEvan Yan if (eventp == NULL) 239*26947304SEvan Yan return (NDI_NOMEM); 240*26947304SEvan Yan 241*26947304SEvan Yan eventp->cn_name = ddi_strdup(cn_name, KM_NOSLEEP); 242*26947304SEvan Yan if (eventp->cn_name == NULL) { 243*26947304SEvan Yan kmem_free(eventp, sizeof (ddi_hp_cn_async_event_entry_t)); 244*26947304SEvan Yan return (NDI_NOMEM); 245*26947304SEvan Yan } 246*26947304SEvan Yan eventp->dip = dip; 247*26947304SEvan Yan eventp->target_state = state; 248*26947304SEvan Yan 249*26947304SEvan Yan /* 250*26947304SEvan Yan * Hold the parent's ref so that it won't disappear when the taskq is 251*26947304SEvan Yan * scheduled to run. 252*26947304SEvan Yan */ 253*26947304SEvan Yan ndi_hold_devi(dip); 254*26947304SEvan Yan 255*26947304SEvan Yan if (!taskq_dispatch(system_taskq, ddihp_cn_run_event, eventp, 256*26947304SEvan Yan TQ_NOSLEEP)) { 257*26947304SEvan Yan ndi_rele_devi(dip); 258*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ndi_hp_state_change_req: " 259*26947304SEvan Yan "taskq_dispatch failed! dip %p " 260*26947304SEvan Yan "target_state %x\n", (void *)dip, state)); 261*26947304SEvan Yan return (NDI_NOMEM); 262*26947304SEvan Yan } 263*26947304SEvan Yan 264*26947304SEvan Yan return (NDI_CLAIMED); 265*26947304SEvan Yan } 266*26947304SEvan Yan 267*26947304SEvan Yan /* 268*26947304SEvan Yan * Walk the link of Hotplug Connection handles of a dip: 269*26947304SEvan Yan * DEVI(dip)->devi_hp_hdlp->[link of connections] 270*26947304SEvan Yan */ 271*26947304SEvan Yan void 272*26947304SEvan Yan ndi_hp_walk_cn(dev_info_t *dip, int (*f)(ddi_hp_cn_info_t *, 273*26947304SEvan Yan void *), void *arg) 274*26947304SEvan Yan { 275*26947304SEvan Yan int count; 276*26947304SEvan Yan ddi_hp_cn_handle_t *head, *curr, *prev; 277*26947304SEvan Yan 278*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ndi_hp_walk_cn: dip %p arg %p\n", 279*26947304SEvan Yan (void *)dip, arg)); 280*26947304SEvan Yan 281*26947304SEvan Yan ASSERT(!servicing_interrupt()); 282*26947304SEvan Yan if (servicing_interrupt()) 283*26947304SEvan Yan return; 284*26947304SEvan Yan 285*26947304SEvan Yan /* Validate the arguments */ 286*26947304SEvan Yan if (dip == NULL) 287*26947304SEvan Yan return; 288*26947304SEvan Yan 289*26947304SEvan Yan ndi_devi_enter(dip, &count); 290*26947304SEvan Yan 291*26947304SEvan Yan head = DEVI(dip)->devi_hp_hdlp; 292*26947304SEvan Yan curr = head; 293*26947304SEvan Yan prev = NULL; 294*26947304SEvan Yan while (curr != NULL) { 295*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ndi_hp_walk_cn: dip %p " 296*26947304SEvan Yan "current cn_name: %s\n", 297*26947304SEvan Yan (void *)dip, curr->cn_info.cn_name)); 298*26947304SEvan Yan switch ((*f)(&(curr->cn_info), arg)) { 299*26947304SEvan Yan case DDI_WALK_TERMINATE: 300*26947304SEvan Yan ndi_devi_exit(dip, count); 301*26947304SEvan Yan 302*26947304SEvan Yan return; 303*26947304SEvan Yan case DDI_WALK_CONTINUE: 304*26947304SEvan Yan default: 305*26947304SEvan Yan if (DEVI(dip)->devi_hp_hdlp != head) { 306*26947304SEvan Yan /* 307*26947304SEvan Yan * The current node is head and it is removed 308*26947304SEvan Yan * by last call to (*f)() 309*26947304SEvan Yan */ 310*26947304SEvan Yan head = DEVI(dip)->devi_hp_hdlp; 311*26947304SEvan Yan curr = head; 312*26947304SEvan Yan prev = NULL; 313*26947304SEvan Yan } else if (prev && prev->next != curr) { 314*26947304SEvan Yan /* 315*26947304SEvan Yan * The current node is a middle node or tail 316*26947304SEvan Yan * node and it is removed by last call to 317*26947304SEvan Yan * (*f)() 318*26947304SEvan Yan */ 319*26947304SEvan Yan curr = prev->next; 320*26947304SEvan Yan } else { 321*26947304SEvan Yan /* no removal accurred on curr node */ 322*26947304SEvan Yan prev = curr; 323*26947304SEvan Yan curr = curr->next; 324*26947304SEvan Yan } 325*26947304SEvan Yan } 326*26947304SEvan Yan } 327*26947304SEvan Yan ndi_devi_exit(dip, count); 328*26947304SEvan Yan } 329*26947304SEvan Yan 330*26947304SEvan Yan /* 331*26947304SEvan Yan * Local functions (called within this file) 332*26947304SEvan Yan */ 333*26947304SEvan Yan 334*26947304SEvan Yan /* 335*26947304SEvan Yan * Wrapper function for ddihp_cn_req_handler() called from taskq 336*26947304SEvan Yan */ 337*26947304SEvan Yan static void 338*26947304SEvan Yan ddihp_cn_run_event(void *arg) 339*26947304SEvan Yan { 340*26947304SEvan Yan ddi_hp_cn_async_event_entry_t *eventp = 341*26947304SEvan Yan (ddi_hp_cn_async_event_entry_t *)arg; 342*26947304SEvan Yan dev_info_t *dip = eventp->dip; 343*26947304SEvan Yan ddi_hp_cn_handle_t *hdlp; 344*26947304SEvan Yan int count; 345*26947304SEvan Yan 346*26947304SEvan Yan /* Lock before access */ 347*26947304SEvan Yan ndi_devi_enter(dip, &count); 348*26947304SEvan Yan 349*26947304SEvan Yan hdlp = ddihp_cn_name_to_handle(dip, eventp->cn_name); 350*26947304SEvan Yan if (hdlp) { 351*26947304SEvan Yan (void) ddihp_cn_req_handler(hdlp, eventp->target_state); 352*26947304SEvan Yan } else { 353*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_run_event: no handle for " 354*26947304SEvan Yan "cn_name: %s dip %p. Request for target_state %x is" 355*26947304SEvan Yan " dropped. \n", 356*26947304SEvan Yan eventp->cn_name, (void *)dip, eventp->target_state)); 357*26947304SEvan Yan } 358*26947304SEvan Yan 359*26947304SEvan Yan ndi_devi_exit(dip, count); 360*26947304SEvan Yan 361*26947304SEvan Yan /* Release the devi's ref that is held from interrupt context. */ 362*26947304SEvan Yan ndi_rele_devi((dev_info_t *)DEVI(dip)); 363*26947304SEvan Yan kmem_free(eventp->cn_name, strlen(eventp->cn_name) + 1); 364*26947304SEvan Yan kmem_free(eventp, sizeof (ddi_hp_cn_async_event_entry_t)); 365*26947304SEvan Yan } 366*26947304SEvan Yan 367*26947304SEvan Yan /* 368*26947304SEvan Yan * Handle state change request of a Hotplug Connection (CN) 369*26947304SEvan Yan */ 370*26947304SEvan Yan static int 371*26947304SEvan Yan ddihp_cn_req_handler(ddi_hp_cn_handle_t *hdlp, 372*26947304SEvan Yan ddi_hp_cn_state_t target_state) 373*26947304SEvan Yan { 374*26947304SEvan Yan dev_info_t *dip = hdlp->cn_dip; 375*26947304SEvan Yan int ret = DDI_SUCCESS; 376*26947304SEvan Yan 377*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_req_handler:" 378*26947304SEvan Yan " hdlp %p, target_state %x\n", 379*26947304SEvan Yan (void *)hdlp, target_state)); 380*26947304SEvan Yan 381*26947304SEvan Yan ASSERT(DEVI_BUSY_OWNED(dip)); 382*26947304SEvan Yan 383*26947304SEvan Yan if (ddihp_cn_getstate(hdlp) != DDI_SUCCESS) { 384*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_req_handler: dip %p, " 385*26947304SEvan Yan "hdlp %p ddi_cn_getstate failed\n", (void *)dip, 386*26947304SEvan Yan (void *)hdlp)); 387*26947304SEvan Yan 388*26947304SEvan Yan return (NDI_UNCLAIMED); 389*26947304SEvan Yan } 390*26947304SEvan Yan if (hdlp->cn_info.cn_state != target_state) { 391*26947304SEvan Yan ddi_hp_cn_state_t result_state = 0; 392*26947304SEvan Yan 393*26947304SEvan Yan DDIHP_CN_OPS(hdlp, DDI_HPOP_CN_CHANGE_STATE, 394*26947304SEvan Yan (void *)&target_state, (void *)&result_state, ret); 395*26947304SEvan Yan 396*26947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_req_handler: dip %p, " 397*26947304SEvan Yan "hdlp %p changed state to %x, ret=%x\n", 398*26947304SEvan Yan (void *)dip, (void *)hdlp, result_state, ret)); 399*26947304SEvan Yan } 400*26947304SEvan Yan 401*26947304SEvan Yan if (ret == DDI_SUCCESS) 402*26947304SEvan Yan return (NDI_CLAIMED); 403*26947304SEvan Yan else 404*26947304SEvan Yan return (NDI_UNCLAIMED); 405*26947304SEvan Yan } 406