1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * hot-plug services module 28 */ 29 30 #include <sys/modctl.h> 31 #include <sys/kmem.h> 32 #include <sys/sunddi.h> 33 #include <sys/sunndi.h> 34 #include <sys/disp.h> 35 #include <sys/stat.h> 36 #include <sys/hotplug/hpcsvc.h> 37 #include <sys/callb.h> 38 39 /* 40 * debug macros: 41 */ 42 #if defined(DEBUG) 43 44 int hpcsvc_debug = 0; 45 46 static void debug(char *, uintptr_t, uintptr_t, uintptr_t, 47 uintptr_t, uintptr_t); 48 49 #define DEBUG0(fmt) \ 50 debug(fmt, 0, 0, 0, 0, 0); 51 #define DEBUG1(fmt, a1) \ 52 debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0); 53 #define DEBUG2(fmt, a1, a2) \ 54 debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0); 55 #define DEBUG3(fmt, a1, a2, a3) \ 56 debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0); 57 #else 58 #define DEBUG0(fmt) 59 #define DEBUG1(fmt, a1) 60 #define DEBUG2(fmt, a1, a2) 61 #define DEBUG3(fmt, a1, a2, a3) 62 #endif 63 64 /* 65 * Definitions for the bus node registration list: 66 * 67 * The hot-plug service module maintains a linked list of items 68 * representing the device bus nodes that have been registered via 69 * hpc_nexus_register, or identified as candidates for registration 70 * by the bus argument to hpc_slot_register. 71 * 72 * The head of the linked listed is stored in hpc_bus_list_head. Insertions 73 * and removals from the list should be locked with mutex hpc_bus_mutex. 74 * 75 * Items in the list are allocated/freed with the macros hpc_alloc_bus_entry() 76 * and hpc_free_bus_entry(). 77 * 78 * Each item in the list contains the following fields: 79 * 80 * bus_dip - pointer to devinfo node of the registering bus 81 * 82 * bus_name - device path name of the bus (ie /pci@1f,4000) 83 * 84 * bus_callback - bus nexus driver callback function registered 85 * with the bus 86 * 87 * bus_registered - a boolean value which is true if the bus has 88 * been registered with hpc_nexus_register, false otherwise 89 * 90 * bus_mutex - mutex lock to be held while updating this list entry 91 * 92 * bus_slot_list - linked list of the slots registered for this 93 * bus node (see slot list details below) 94 * 95 * bus_thread - kernel thread for running slot event handlers for 96 * slots associated with this bus 97 * 98 * bus_thread_cv - condition variable for synchronization between 99 * the service routines and the thread for running slot 100 * event handlers 101 * 102 * bus_thread_exit - a boolean value used to instruct the thread 103 * for invoking the slot event handlers to exit 104 * 105 * bus_slot_event_list_head - the head of the linked list of instances 106 * of slot event handlers to be run 107 * handlers to be invoked 108 * 109 * bus_next - pointer to next list entry 110 */ 111 112 typedef struct hpc_bus_entry hpc_bus_entry_t; 113 typedef struct hpc_slot_entry hpc_slot_entry_t; 114 typedef struct hpc_event_entry hpc_event_entry_t; 115 116 struct hpc_event_entry { 117 hpc_slot_entry_t *slotp; 118 int event; 119 hpc_event_entry_t *next; 120 }; 121 122 #define hpc_alloc_event_entry() \ 123 (hpc_event_entry_t *)kmem_zalloc(sizeof (hpc_event_entry_t), KM_SLEEP) 124 125 #define hpc_free_event_entry(a) \ 126 kmem_free((a), sizeof (hpc_event_entry_t)) 127 128 struct hpc_bus_entry { 129 dev_info_t *bus_dip; 130 char bus_name[MAXPATHLEN + 1]; 131 boolean_t bus_registered; 132 kmutex_t bus_mutex; 133 int (* bus_callback)(dev_info_t *dip, hpc_slot_t hdl, 134 hpc_slot_info_t *slot_info, int slot_state); 135 hpc_slot_entry_t *bus_slot_list; 136 kthread_t *bus_thread; 137 kcondvar_t bus_thread_cv; 138 boolean_t bus_thread_exit; 139 hpc_event_entry_t *bus_slot_event_list_head; 140 hpc_bus_entry_t *bus_next; 141 }; 142 143 #define hpc_alloc_bus_entry() \ 144 (hpc_bus_entry_t *)kmem_zalloc(sizeof (hpc_bus_entry_t), KM_SLEEP) 145 146 #define hpc_free_bus_entry(a) \ 147 kmem_free((a), sizeof (hpc_bus_entry_t)) 148 149 150 /* 151 * Definitions for the per-bus node slot registration list: 152 * 153 * For each bus node in the bus list, the hot-plug service module maintains 154 * a doubly linked link list of items representing the slots that have been 155 * registered (by hot-plug controllers) for that bus. 156 * 157 * The head of the linked listed is stored in bus_slot_list field of the bus 158 * node. Insertions and removals from this list should locked with the mutex 159 * in the bus_mutex field of the bus node. 160 * 161 * Items in the list are allocated/freed with the macros hpc_alloc_slot_entry() 162 * and hpc_free_slot_entry(). 163 * 164 * Each item in the list contains the following fields: 165 * 166 * slot_handle - handle for slot (hpc_slot_t) 167 * 168 * slot_info - information registered with the slot (hpc_slot_info_t) 169 * 170 * slot_ops - ops vector registered with the slot (hpc_slot_ops_t) 171 * 172 * slot_ops_arg - argument to be passed to ops routines (caddr_t) 173 * 174 * slot_event_handler - handler registered for slot events 175 * 176 * slot_event_handler_arg - argument to be passed to event handler 177 * 178 * slot_event_mask - the set of events for which the event handler 179 * gets invoked 180 * 181 * slot_bus - pointer to bus node for the slot 182 * 183 * slot_hpc_dip - devinfo node pointer to the HPC driver instance 184 * that controls this slot 185 * 186 * slot_{prev,next} - point to {previous,next} node in the list 187 */ 188 189 struct hpc_slot_entry { 190 hpc_slot_t slot_handle; 191 hpc_slot_info_t slot_info; /* should be static & copied */ 192 hpc_slot_ops_t slot_ops; 193 caddr_t slot_ops_arg; 194 int (* slot_event_handler)(caddr_t, uint_t); 195 caddr_t slot_event_handler_arg; 196 uint_t slot_event_mask; 197 hpc_bus_entry_t *slot_bus; 198 dev_info_t *slot_hpc_dip; 199 hpc_slot_entry_t *slot_next, *slot_prev; 200 }; 201 202 #define hpc_alloc_slot_entry() \ 203 (hpc_slot_entry_t *)kmem_zalloc(sizeof (hpc_slot_entry_t), KM_SLEEP) 204 205 #define hpc_free_slot_entry(a) \ 206 kmem_free((a), sizeof (hpc_slot_entry_t)) 207 208 209 /* 210 * Definitions for slot registration callback table. 211 */ 212 213 typedef struct hpc_callback_entry hpc_callback_entry_t; 214 215 struct hpc_callback_entry { 216 int (* callback)(dev_info_t *dip, hpc_slot_t hdl, 217 hpc_slot_info_t *slot_info, int slot_state); 218 dev_info_t *dip; 219 hpc_slot_t hdl; 220 hpc_slot_info_t *slot_info; 221 int slot_state; 222 hpc_callback_entry_t *next; 223 }; 224 225 #define hpc_alloc_callback_entry() \ 226 (hpc_callback_entry_t *) \ 227 kmem_zalloc(sizeof (hpc_callback_entry_t), KM_SLEEP) 228 229 #define hpc_free_callback_entry(a) \ 230 kmem_free((a), sizeof (hpc_callback_entry_t)) 231 232 233 234 /* 235 * Mutex lock for bus registration table and table head. 236 */ 237 static kmutex_t hpc_bus_mutex; 238 static hpc_bus_entry_t *hpc_bus_list_head; 239 240 241 /* 242 * Forward function declarations. 243 */ 244 static hpc_bus_entry_t *hpc_find_bus_by_name(char *name); 245 static void hpc_slot_event_dispatcher(hpc_bus_entry_t *busp); 246 247 248 /* 249 * loadable module definitions: 250 */ 251 extern struct mod_ops mod_miscops; 252 253 static struct modlmisc modlmisc = { 254 &mod_miscops, /* Type of module */ 255 "hot-plug controller services" 256 }; 257 258 static struct modlinkage modlinkage = { 259 MODREV_1, { (void *)&modlmisc, NULL } 260 }; 261 262 int 263 _init(void) 264 { 265 int e; 266 267 mutex_init(&hpc_bus_mutex, NULL, MUTEX_DRIVER, NULL); 268 269 /* 270 * Install the module. 271 */ 272 e = mod_install(&modlinkage); 273 if (e != 0) { 274 mutex_destroy(&hpc_bus_mutex); 275 } 276 return (e); 277 } 278 279 int 280 _fini(void) 281 { 282 int e; 283 284 e = mod_remove(&modlinkage); 285 if (e == 0) { 286 mutex_destroy(&hpc_bus_mutex); 287 } 288 return (e); 289 } 290 291 int 292 _info(struct modinfo *modinfop) 293 { 294 return (mod_info(&modlinkage, modinfop)); 295 } 296 297 298 299 hpc_slot_ops_t * 300 hpc_alloc_slot_ops(int flag) 301 { 302 hpc_slot_ops_t *ops; 303 304 ops = (hpc_slot_ops_t *)kmem_zalloc(sizeof (hpc_slot_ops_t), flag); 305 return (ops); 306 } 307 308 309 void 310 hpc_free_slot_ops(hpc_slot_ops_t *ops) 311 { 312 kmem_free((void *)ops, sizeof (hpc_slot_ops_t)); 313 } 314 315 316 /*ARGSUSED2*/ 317 int 318 hpc_nexus_register_bus(dev_info_t *dip, 319 int (* callback)(dev_info_t *dip, hpc_slot_t hdl, 320 hpc_slot_info_t *slot_info, int slot_state), uint_t flags) 321 { 322 hpc_bus_entry_t *busp; 323 hpc_slot_entry_t *slotp; 324 char bus_path[MAXPATHLEN + 1]; 325 326 DEBUG2("hpc_nexus_register_bus: %s%d", 327 ddi_node_name(dip), ddi_get_instance(dip)); 328 mutex_enter(&hpc_bus_mutex); 329 (void) ddi_pathname(dip, bus_path); 330 busp = hpc_find_bus_by_name(bus_path); 331 if (busp == NULL) { 332 333 /* 334 * Initialize the new bus node and link it at the head 335 * of the bus list. 336 */ 337 DEBUG0("hpc_nexus_register_bus: not in bus list"); 338 busp = hpc_alloc_bus_entry(); 339 busp->bus_dip = dip; 340 busp->bus_registered = B_TRUE; 341 (void) strcpy(busp->bus_name, bus_path); 342 mutex_init(&busp->bus_mutex, NULL, MUTEX_DRIVER, NULL); 343 busp->bus_callback = callback; 344 busp->bus_slot_list = NULL; 345 busp->bus_next = hpc_bus_list_head; 346 hpc_bus_list_head = busp; 347 348 } else { 349 350 /* 351 * The bus is in the bus list but isn't registered yet. 352 * Mark it as registered, and run the registration callbacks 353 * for it slots. 354 */ 355 DEBUG0("hpc_nexus_register_bus: in list, but not registered"); 356 mutex_enter(&busp->bus_mutex); 357 if (busp->bus_registered == B_TRUE) { 358 mutex_exit(&busp->bus_mutex); 359 mutex_exit(&hpc_bus_mutex); 360 return (HPC_ERR_BUS_DUPLICATE); 361 } 362 busp->bus_dip = dip; 363 busp->bus_callback = callback; 364 busp->bus_registered = B_TRUE; 365 366 mutex_exit(&busp->bus_mutex); 367 mutex_exit(&hpc_bus_mutex); 368 if (callback) { 369 DEBUG0("hpc_nexus_register_bus: running callbacks"); 370 for (slotp = busp->bus_slot_list; slotp; 371 slotp = slotp->slot_next) { 372 (void) callback(dip, slotp, &slotp->slot_info, 373 HPC_SLOT_ONLINE); 374 } 375 } 376 return (HPC_SUCCESS); 377 } 378 mutex_exit(&hpc_bus_mutex); 379 return (HPC_SUCCESS); 380 } 381 382 383 int 384 hpc_nexus_unregister_bus(dev_info_t *dip) 385 { 386 hpc_bus_entry_t *busp, *busp_prev; 387 hpc_slot_entry_t *slotp; 388 389 /* 390 * Search the list for the bus node and remove it. 391 */ 392 DEBUG2("hpc_nexus_unregister_bus: %s%d", 393 ddi_node_name(dip), ddi_get_instance(dip)); 394 mutex_enter(&hpc_bus_mutex); 395 for (busp = hpc_bus_list_head; busp != NULL; busp_prev = busp, 396 busp = busp->bus_next) { 397 if (busp->bus_dip == dip) 398 break; 399 } 400 if (busp == NULL) { 401 mutex_exit(&hpc_bus_mutex); 402 return (HPC_ERR_BUS_NOTREGISTERED); 403 } 404 405 /* 406 * If the bus has slots, mark the bus as unregistered, otherwise 407 * remove the bus entry from the list. 408 */ 409 mutex_enter(&busp->bus_mutex); 410 if (busp->bus_slot_list == NULL) { 411 if (busp == hpc_bus_list_head) 412 hpc_bus_list_head = busp->bus_next; 413 else 414 busp_prev->bus_next = busp->bus_next; 415 mutex_exit(&busp->bus_mutex); 416 mutex_destroy(&busp->bus_mutex); 417 hpc_free_bus_entry(busp); 418 mutex_exit(&hpc_bus_mutex); 419 return (HPC_SUCCESS); 420 } 421 422 /* 423 * unregister event handlers for all the slots on this bus. 424 */ 425 for (slotp = busp->bus_slot_list; slotp != NULL; 426 slotp = slotp->slot_next) { 427 slotp->slot_event_handler = NULL; 428 slotp->slot_event_handler_arg = NULL; 429 } 430 busp->bus_registered = B_FALSE; 431 mutex_exit(&busp->bus_mutex); 432 mutex_exit(&hpc_bus_mutex); 433 return (HPC_SUCCESS); 434 } 435 436 437 /*ARGSUSED5*/ 438 int 439 hpc_slot_register(dev_info_t *hpc_dip, char *bus, hpc_slot_info_t *infop, 440 hpc_slot_t *handlep, hpc_slot_ops_t *opsp, 441 caddr_t ops_arg, uint_t flags) 442 { 443 hpc_bus_entry_t *busp; 444 hpc_slot_entry_t *slotp, *slot_list_head; 445 boolean_t run_callback = B_FALSE; 446 int (* callback)(dev_info_t *dip, hpc_slot_t hdl, 447 hpc_slot_info_t *slot_info, int slot_state); 448 dev_info_t *dip; 449 kthread_t *t; 450 451 /* 452 * Validate the arguments. 453 */ 454 DEBUG1("hpc_slot_register: %s", bus); 455 if (handlep == NULL || infop == NULL || opsp == NULL || hpc_dip == NULL) 456 return (HPC_ERR_INVALID); 457 458 /* 459 * The bus for the slot may or may not be in the bus list. If it's 460 * not, we create a node for the bus in the bus list and mark it as 461 * not registered. 462 */ 463 mutex_enter(&hpc_bus_mutex); 464 busp = hpc_find_bus_by_name(bus); 465 if (busp == NULL) { 466 467 /* 468 * Initialize the new bus node and link it at the 469 * head of the bus list. 470 */ 471 DEBUG1("hpc_slot_register: %s not in bus list", bus); 472 busp = hpc_alloc_bus_entry(); 473 busp->bus_registered = B_FALSE; 474 (void) strcpy(busp->bus_name, bus); 475 mutex_init(&busp->bus_mutex, NULL, MUTEX_DRIVER, NULL); 476 busp->bus_slot_list = NULL; 477 busp->bus_next = hpc_bus_list_head; 478 hpc_bus_list_head = busp; 479 480 } else { 481 if (busp->bus_registered == B_TRUE) { 482 run_callback = B_TRUE; 483 callback = busp->bus_callback; 484 dip = busp->bus_dip; 485 } 486 } 487 488 mutex_enter(&busp->bus_mutex); 489 slot_list_head = busp->bus_slot_list; 490 if (slot_list_head == NULL) { 491 492 /* 493 * The slot list was empty, so this is the first slot 494 * registered for the bus. Create a per-bus thread 495 * for running the slot event handlers. 496 */ 497 DEBUG0("hpc_slot_register: creating event callback thread"); 498 cv_init(&busp->bus_thread_cv, NULL, CV_DRIVER, NULL); 499 busp->bus_thread_exit = B_FALSE; 500 t = thread_create(NULL, 0, hpc_slot_event_dispatcher, 501 (caddr_t)busp, 0, &p0, TS_RUN, minclsyspri); 502 busp->bus_thread = t; 503 } 504 505 /* 506 * Create and initialize a new entry in the slot list for the bus. 507 */ 508 slotp = hpc_alloc_slot_entry(); 509 slotp->slot_handle = (hpc_slot_t)slotp; 510 slotp->slot_info = *infop; 511 slotp->slot_ops = *opsp; 512 slotp->slot_ops_arg = ops_arg; 513 slotp->slot_bus = busp; 514 slotp->slot_hpc_dip = hpc_dip; 515 slotp->slot_prev = NULL; 516 busp->bus_slot_list = slotp; 517 slotp->slot_next = slot_list_head; 518 if (slot_list_head != NULL) 519 slot_list_head->slot_prev = slotp; 520 mutex_exit(&busp->bus_mutex); 521 mutex_exit(&hpc_bus_mutex); 522 523 /* 524 * Return the handle to the caller prior to return to avoid recursion. 525 */ 526 *handlep = (hpc_slot_t)slotp; 527 528 /* 529 * If the bus was registered, we run the callback registered by 530 * the bus node. 531 */ 532 if (run_callback) { 533 DEBUG0("hpc_slot_register: running callback"); 534 535 (void) callback(dip, slotp, infop, HPC_SLOT_ONLINE); 536 } 537 538 /* 539 * Keep hpc driver in memory 540 */ 541 (void) ndi_hold_driver(hpc_dip); 542 543 return (HPC_SUCCESS); 544 } 545 546 547 int 548 hpc_slot_unregister(hpc_slot_t *handlep) 549 { 550 hpc_slot_entry_t *slotp; 551 hpc_bus_entry_t *busp, *busp_prev; 552 boolean_t run_callback; 553 int (* callback)(dev_info_t *dip, hpc_slot_t hdl, 554 hpc_slot_info_t *slot_info, int slot_state); 555 int r; 556 dev_info_t *dip; 557 char *bus_name; 558 559 DEBUG0("hpc_slot_unregister:"); 560 561 ASSERT(handlep != NULL); 562 563 /* validate the handle */ 564 slotp = (hpc_slot_entry_t *)*handlep; 565 if ((slotp == NULL) || slotp->slot_handle != *handlep) 566 return (HPC_ERR_INVALID); 567 568 /* 569 * Get the bus list entry from the slot to grap the mutex for 570 * the slot list of the bus. 571 */ 572 mutex_enter(&hpc_bus_mutex); 573 busp = slotp->slot_bus; 574 DEBUG2("hpc_slot_unregister: handlep=%x, slotp=%x", handlep, slotp); 575 if (busp == NULL) { 576 mutex_exit(&hpc_bus_mutex); 577 return (HPC_ERR_SLOT_NOTREGISTERED); 578 } 579 580 /* 581 * Determine if we need to run the slot offline callback and 582 * save the data necessary to do so. 583 */ 584 callback = busp->bus_callback; 585 run_callback = (busp->bus_registered == B_TRUE) && (callback != NULL); 586 dip = busp->bus_dip; 587 bus_name = busp->bus_name; 588 589 /* 590 * Run the slot offline callback if necessary. 591 */ 592 if (run_callback) { 593 mutex_exit(&hpc_bus_mutex); 594 DEBUG0("hpc_slot_unregister: running callback"); 595 r = callback(dip, (hpc_slot_t)slotp, &slotp->slot_info, 596 HPC_SLOT_OFFLINE); 597 DEBUG1("hpc_slot_unregister: callback returned %x", r); 598 if (r != HPC_SUCCESS) 599 return (HPC_ERR_FAILED); 600 mutex_enter(&hpc_bus_mutex); 601 } 602 603 /* 604 * Remove the slot from list and free the memory associated with it. 605 */ 606 mutex_enter(&busp->bus_mutex); 607 DEBUG1("hpc_slot_unregister: freeing slot, bus_slot_list=%x", 608 busp->bus_slot_list); 609 if (slotp->slot_prev != NULL) 610 slotp->slot_prev->slot_next = slotp->slot_next; 611 if (slotp->slot_next != NULL) 612 slotp->slot_next->slot_prev = slotp->slot_prev; 613 if (slotp == busp->bus_slot_list) 614 busp->bus_slot_list = slotp->slot_next; 615 616 /* 617 * Release hold from slot registration 618 */ 619 ndi_rele_driver(slotp->slot_hpc_dip); 620 621 /* Free the memory associated with the slot entry structure */ 622 hpc_free_slot_entry(slotp); 623 624 /* 625 * If the slot list is empty then stop the event handler thread. 626 */ 627 if (busp->bus_slot_list == NULL) { 628 DEBUG0("hpc_slot_unregister: stopping thread"); 629 busp->bus_thread_exit = B_TRUE; 630 cv_signal(&busp->bus_thread_cv); 631 DEBUG0("hpc_slot_unregister: waiting for thread to exit"); 632 cv_wait(&busp->bus_thread_cv, &busp->bus_mutex); 633 DEBUG0("hpc_slot_unregister: thread exit"); 634 cv_destroy(&busp->bus_thread_cv); 635 } 636 637 /* 638 * If the bus is unregistered and this is the last slot for this bus 639 * then remove the entry from the bus list. 640 */ 641 if (busp->bus_registered == B_FALSE && busp->bus_slot_list == NULL) { 642 /* locate the previous entry in the bus list */ 643 for (busp = hpc_bus_list_head; busp != NULL; busp_prev = busp, 644 busp = busp->bus_next) 645 if (strcmp(bus_name, busp->bus_name) == 0) 646 break; 647 648 if (busp == hpc_bus_list_head) 649 hpc_bus_list_head = busp->bus_next; 650 else 651 busp_prev->bus_next = busp->bus_next; 652 653 mutex_exit(&busp->bus_mutex); 654 mutex_destroy(&busp->bus_mutex); 655 hpc_free_bus_entry(busp); 656 } else 657 mutex_exit(&busp->bus_mutex); 658 mutex_exit(&hpc_bus_mutex); 659 660 /* 661 * reset the slot handle. 662 */ 663 *handlep = NULL; 664 return (HPC_SUCCESS); 665 } 666 667 668 int 669 hpc_install_event_handler(hpc_slot_t handle, uint_t event_mask, 670 int (*event_handler)(caddr_t, uint_t), caddr_t arg) 671 { 672 hpc_slot_entry_t *slotp; 673 hpc_bus_entry_t *busp; 674 675 DEBUG3("hpc_install_event_handler: handle=%x, mask=%x, arg=%x", 676 handle, event_mask, arg); 677 ASSERT((handle != NULL) && (event_handler != NULL)); 678 slotp = (hpc_slot_entry_t *)handle; 679 busp = slotp->slot_bus; 680 ASSERT(slotp == slotp->slot_handle); 681 mutex_enter(&busp->bus_mutex); 682 slotp->slot_event_mask = event_mask; 683 slotp->slot_event_handler = event_handler; 684 slotp->slot_event_handler_arg = arg; 685 mutex_exit(&busp->bus_mutex); 686 return (HPC_SUCCESS); 687 } 688 689 690 int 691 hpc_remove_event_handler(hpc_slot_t handle) 692 { 693 hpc_slot_entry_t *slotp; 694 hpc_bus_entry_t *busp; 695 696 DEBUG1("hpc_remove_event_handler: handle=%x", handle); 697 ASSERT(handle != NULL); 698 slotp = (hpc_slot_entry_t *)handle; 699 ASSERT(slotp == slotp->slot_handle); 700 busp = slotp->slot_bus; 701 mutex_enter(&busp->bus_mutex); 702 slotp->slot_event_mask = 0; 703 slotp->slot_event_handler = NULL; 704 slotp->slot_event_handler_arg = NULL; 705 mutex_exit(&busp->bus_mutex); 706 return (HPC_SUCCESS); 707 } 708 709 710 /*ARGSUSED2*/ 711 int 712 hpc_slot_event_notify(hpc_slot_t handle, uint_t event, uint_t flags) 713 { 714 hpc_slot_entry_t *slotp; 715 hpc_bus_entry_t *busp; 716 hpc_event_entry_t *eventp; 717 718 DEBUG2("hpc_slot_event_notify: handle=%x event=%x", handle, event); 719 ASSERT(handle != NULL); 720 slotp = (hpc_slot_entry_t *)handle; 721 ASSERT(slotp == slotp->slot_handle); 722 723 if (slotp->slot_event_handler == NULL) 724 return (HPC_EVENT_UNCLAIMED); 725 726 /* 727 * If the request is to handle the event synchronously, then call 728 * the event handler without queuing the event. 729 */ 730 if (flags == HPC_EVENT_SYNCHRONOUS) { 731 caddr_t arg; 732 int (* func)(caddr_t, uint_t); 733 734 func = slotp->slot_event_handler; 735 arg = slotp->slot_event_handler_arg; 736 return (func(arg, event)); 737 } 738 /* 739 * Insert the event into the bus slot event handler list and 740 * signal the bus slot event handler dispatch thread. 741 */ 742 busp = slotp->slot_bus; 743 mutex_enter(&busp->bus_mutex); 744 745 if (busp->bus_slot_event_list_head == NULL) { 746 eventp = busp->bus_slot_event_list_head = 747 hpc_alloc_event_entry(); 748 } else { 749 for (eventp = busp->bus_slot_event_list_head; 750 eventp->next != NULL; eventp = eventp->next) 751 ; 752 eventp->next = hpc_alloc_event_entry(); 753 eventp = eventp->next; 754 } 755 eventp->slotp = slotp; 756 eventp->event = event; 757 eventp->next = NULL; 758 DEBUG2("hpc_slot_event_notify: busp=%x event=%x", busp, event); 759 cv_signal(&busp->bus_thread_cv); 760 mutex_exit(&busp->bus_mutex); 761 return (HPC_EVENT_CLAIMED); 762 } 763 764 765 int 766 hpc_nexus_connect(hpc_slot_t handle, void *data, uint_t flags) 767 { 768 hpc_slot_entry_t *slotp; 769 770 ASSERT(handle != NULL); 771 slotp = (hpc_slot_entry_t *)handle; 772 if (slotp->slot_ops.hpc_op_connect) 773 return (slotp->slot_ops.hpc_op_connect(slotp->slot_ops_arg, 774 handle, data, flags)); 775 return (HPC_ERR_FAILED); 776 } 777 778 779 int 780 hpc_nexus_disconnect(hpc_slot_t handle, void *data, uint_t flags) 781 { 782 hpc_slot_entry_t *slotp; 783 784 ASSERT(handle != NULL); 785 slotp = (hpc_slot_entry_t *)handle; 786 if (slotp->slot_ops.hpc_op_disconnect) 787 return (slotp->slot_ops.hpc_op_disconnect(slotp->slot_ops_arg, 788 handle, data, flags)); 789 return (HPC_ERR_FAILED); 790 } 791 792 793 int 794 hpc_nexus_insert(hpc_slot_t handle, void *data, uint_t flags) 795 { 796 hpc_slot_entry_t *slotp; 797 798 ASSERT(handle != NULL); 799 slotp = (hpc_slot_entry_t *)handle; 800 if (slotp->slot_ops.hpc_op_insert) 801 return (slotp->slot_ops.hpc_op_insert(slotp->slot_ops_arg, 802 handle, data, flags)); 803 return (HPC_ERR_FAILED); 804 } 805 806 807 int 808 hpc_nexus_remove(hpc_slot_t handle, void *data, uint_t flags) 809 { 810 hpc_slot_entry_t *slotp; 811 812 ASSERT(handle != NULL); 813 slotp = (hpc_slot_entry_t *)handle; 814 if (slotp->slot_ops.hpc_op_remove) 815 return (slotp->slot_ops.hpc_op_remove(slotp->slot_ops_arg, 816 handle, data, flags)); 817 return (HPC_ERR_FAILED); 818 } 819 820 821 int 822 hpc_nexus_control(hpc_slot_t handle, int request, caddr_t arg) 823 { 824 hpc_slot_entry_t *slotp; 825 826 ASSERT(handle != NULL); 827 slotp = (hpc_slot_entry_t *)handle; 828 if (slotp->slot_ops.hpc_op_control) 829 return (slotp->slot_ops.hpc_op_control(slotp->slot_ops_arg, 830 handle, request, arg)); 831 return (HPC_ERR_FAILED); 832 } 833 834 /* 835 * The following function is run from the bus entries slot event handling 836 * thread. 837 */ 838 static void 839 hpc_slot_event_dispatcher(hpc_bus_entry_t *busp) 840 { 841 hpc_event_entry_t *eventp; 842 hpc_slot_entry_t *slotp; 843 int event; 844 caddr_t arg; 845 int (* func)(caddr_t, uint_t); 846 callb_cpr_t cprinfo; 847 848 /* 849 * The creator of this thread is waiting to be signaled that 850 * the thread has been started. 851 */ 852 DEBUG1("hpc_slot_event_dispatcher: busp=%x", busp); 853 854 CALLB_CPR_INIT(&cprinfo, &busp->bus_mutex, callb_generic_cpr, 855 "hpc_slot_event_dispatcher"); 856 857 mutex_enter(&busp->bus_mutex); 858 /* 859 * Wait for events to queue and then process them. 860 */ 861 for (;;) { 862 863 /* 864 * Note we only hold the mutex while determining 865 * the number of entries that have been added to 866 * the event list, while updating the event list 867 * after processing the event list entries. 868 */ 869 if (busp->bus_slot_event_list_head == NULL) { 870 CALLB_CPR_SAFE_BEGIN(&cprinfo); 871 cv_wait(&busp->bus_thread_cv, &busp->bus_mutex); 872 CALLB_CPR_SAFE_END(&cprinfo, &busp->bus_mutex); 873 if (busp->bus_thread_exit) 874 break; 875 continue; 876 } 877 878 /* 879 * We have an event handler instance in the list to 880 * process. Remove the head of the list, saving the 881 * information required to run the event handler. 882 * Then run the event handler while the bus mutex 883 * is released. 884 */ 885 eventp = busp->bus_slot_event_list_head; 886 slotp = eventp->slotp; 887 event = eventp->event; 888 func = slotp->slot_event_handler; 889 arg = slotp->slot_event_handler_arg; 890 busp->bus_slot_event_list_head = eventp->next; 891 hpc_free_event_entry(eventp); 892 mutex_exit(&busp->bus_mutex); 893 func(arg, event); 894 mutex_enter(&busp->bus_mutex); 895 896 if (busp->bus_thread_exit) 897 break; 898 } 899 900 DEBUG0("hpc_slot_event_dispatcher: thread_exit"); 901 cv_signal(&busp->bus_thread_cv); 902 CALLB_CPR_EXIT(&cprinfo); 903 thread_exit(); 904 } 905 906 907 static hpc_bus_entry_t * 908 hpc_find_bus_by_name(char *path) 909 { 910 hpc_bus_entry_t *busp; 911 912 for (busp = hpc_bus_list_head; busp != NULL; busp = busp->bus_next) { 913 if (strcmp(path, busp->bus_name) == 0) 914 break; 915 } 916 return (busp); 917 } 918 919 boolean_t 920 hpc_bus_registered(hpc_slot_t slot_hdl) 921 { 922 hpc_slot_entry_t *slotp; 923 hpc_bus_entry_t *busp; 924 925 slotp = (hpc_slot_entry_t *)slot_hdl; 926 busp = slotp->slot_bus; 927 return (busp->bus_registered); 928 } 929 930 931 #ifdef DEBUG 932 933 extern void prom_printf(const char *, ...); 934 935 static void 936 debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3, 937 uintptr_t a4, uintptr_t a5) 938 { 939 if (hpcsvc_debug != 0) { 940 cmn_err(CE_CONT, "hpcsvc: "); 941 cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); 942 cmn_err(CE_CONT, "\n"); 943 } 944 } 945 #endif