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