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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)292 _info(struct modinfo *modinfop)
293 {
294 return (mod_info(&modlinkage, modinfop));
295 }
296
297
298
299 hpc_slot_ops_t *
hpc_alloc_slot_ops(int flag)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
hpc_free_slot_ops(hpc_slot_ops_t * ops)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
hpc_nexus_register_bus(dev_info_t * dip,int (* callback)(dev_info_t * dip,hpc_slot_t hdl,hpc_slot_info_t * slot_info,int slot_state),uint_t flags)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
hpc_nexus_unregister_bus(dev_info_t * dip)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
hpc_slot_register(dev_info_t * hpc_dip,char * bus,hpc_slot_info_t * infop,hpc_slot_t * handlep,hpc_slot_ops_t * opsp,caddr_t ops_arg,uint_t flags)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
hpc_slot_unregister(hpc_slot_t * handlep)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
hpc_install_event_handler(hpc_slot_t handle,uint_t event_mask,int (* event_handler)(caddr_t,uint_t),caddr_t arg)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
hpc_remove_event_handler(hpc_slot_t handle)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
hpc_slot_event_notify(hpc_slot_t handle,uint_t event,uint_t flags)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
hpc_nexus_connect(hpc_slot_t handle,void * data,uint_t flags)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
hpc_nexus_disconnect(hpc_slot_t handle,void * data,uint_t flags)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
hpc_nexus_insert(hpc_slot_t handle,void * data,uint_t flags)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
hpc_nexus_remove(hpc_slot_t handle,void * data,uint_t flags)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
hpc_nexus_control(hpc_slot_t handle,int request,caddr_t arg)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
hpc_slot_event_dispatcher(hpc_bus_entry_t * busp)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 *
hpc_find_bus_by_name(char * path)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
hpc_bus_registered(hpc_slot_t slot_hdl)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
debug(char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)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
946