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