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
44int hpcsvc_debug = 0;
45
46static 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
112typedef struct hpc_bus_entry hpc_bus_entry_t;
113typedef struct hpc_slot_entry hpc_slot_entry_t;
114typedef struct hpc_event_entry hpc_event_entry_t;
115
116struct 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
128struct 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
189struct 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
213typedef struct hpc_callback_entry hpc_callback_entry_t;
214
215struct 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 */
237static kmutex_t hpc_bus_mutex;
238static hpc_bus_entry_t *hpc_bus_list_head;
239
240
241/*
242 * Forward function declarations.
243 */
244static hpc_bus_entry_t *hpc_find_bus_by_name(char *name);
245static void hpc_slot_event_dispatcher(hpc_bus_entry_t *busp);
246
247
248/*
249 * loadable module definitions:
250 */
251extern struct mod_ops mod_miscops;
252
253static struct modlmisc modlmisc = {
254	&mod_miscops,	/* Type of module */
255	"hot-plug controller services"
256};
257
258static struct modlinkage modlinkage = {
259	MODREV_1, (void *)&modlmisc, NULL
260};
261
262int
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
279int
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
291int
292_info(struct modinfo *modinfop)
293{
294	return (mod_info(&modlinkage, modinfop));
295}
296
297
298
299hpc_slot_ops_t *
300hpc_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
309void
310hpc_free_slot_ops(hpc_slot_ops_t *ops)
311{
312	kmem_free((void *)ops, sizeof (hpc_slot_ops_t));
313}
314
315
316/*ARGSUSED2*/
317int
318hpc_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
383int
384hpc_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*/
438int
439hpc_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
547int
548hpc_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
668int
669hpc_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
690int
691hpc_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*/
711int
712hpc_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
765int
766hpc_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
779int
780hpc_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
793int
794hpc_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
807int
808hpc_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
821int
822hpc_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 */
838static void
839hpc_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
907static hpc_bus_entry_t *
908hpc_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
919boolean_t
920hpc_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
933extern void prom_printf(const char *, ...);
934
935static void
936debug(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