/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* hot-plug services module
*/
#include <sys/modctl.h>
#include <sys/kmem.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/disp.h>
#include <sys/stat.h>
#include <sys/hotplug/hpcsvc.h>
#include <sys/callb.h>
/*
* debug macros:
*/
#if defined(DEBUG)
int hpcsvc_debug = 0;
static void debug(char *, uintptr_t, uintptr_t, uintptr_t,
uintptr_t, uintptr_t);
#define DEBUG0(fmt) \
debug(fmt, 0, 0, 0, 0, 0);
#define DEBUG1(fmt, a1) \
debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0);
#define DEBUG2(fmt, a1, a2) \
debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0);
#define DEBUG3(fmt, a1, a2, a3) \
debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0);
#else
#define DEBUG0(fmt)
#define DEBUG1(fmt, a1)
#define DEBUG2(fmt, a1, a2)
#define DEBUG3(fmt, a1, a2, a3)
#endif
/*
* Definitions for the bus node registration list:
*
* The hot-plug service module maintains a linked list of items
* representing the device bus nodes that have been registered via
* hpc_nexus_register, or identified as candidates for registration
* by the bus argument to hpc_slot_register.
*
* The head of the linked listed is stored in hpc_bus_list_head. Insertions
* and removals from the list should be locked with mutex hpc_bus_mutex.
*
* Items in the list are allocated/freed with the macros hpc_alloc_bus_entry()
* and hpc_free_bus_entry().
*
* Each item in the list contains the following fields:
*
* bus_dip - pointer to devinfo node of the registering bus
*
* bus_name - device path name of the bus (ie /pci@1f,4000)
*
* bus_callback - bus nexus driver callback function registered
* with the bus
*
* bus_registered - a boolean value which is true if the bus has
* been registered with hpc_nexus_register, false otherwise
*
* bus_mutex - mutex lock to be held while updating this list entry
*
* bus_slot_list - linked list of the slots registered for this
* bus node (see slot list details below)
*
* bus_thread - kernel thread for running slot event handlers for
* slots associated with this bus
*
* bus_thread_cv - condition variable for synchronization between
* the service routines and the thread for running slot
* event handlers
*
* bus_thread_exit - a boolean value used to instruct the thread
* for invoking the slot event handlers to exit
*
* bus_slot_event_list_head - the head of the linked list of instances
* of slot event handlers to be run
* handlers to be invoked
*
* bus_next - pointer to next list entry
*/
typedef struct hpc_bus_entry hpc_bus_entry_t;
typedef struct hpc_slot_entry hpc_slot_entry_t;
typedef struct hpc_event_entry hpc_event_entry_t;
struct hpc_event_entry {
hpc_slot_entry_t *slotp;
int event;
hpc_event_entry_t *next;
};
#define hpc_alloc_event_entry() \
(hpc_event_entry_t *)kmem_zalloc(sizeof (hpc_event_entry_t), KM_SLEEP)
#define hpc_free_event_entry(a) \
kmem_free((a), sizeof (hpc_event_entry_t))
struct hpc_bus_entry {
dev_info_t *bus_dip;
char bus_name[MAXPATHLEN + 1];
boolean_t bus_registered;
kmutex_t bus_mutex;
int (* bus_callback)(dev_info_t *dip, hpc_slot_t hdl,
hpc_slot_info_t *slot_info, int slot_state);
hpc_slot_entry_t *bus_slot_list;
kthread_t *bus_thread;
kcondvar_t bus_thread_cv;
boolean_t bus_thread_exit;
hpc_event_entry_t *bus_slot_event_list_head;
hpc_bus_entry_t *bus_next;
};
#define hpc_alloc_bus_entry() \
(hpc_bus_entry_t *)kmem_zalloc(sizeof (hpc_bus_entry_t), KM_SLEEP)
#define hpc_free_bus_entry(a) \
kmem_free((a), sizeof (hpc_bus_entry_t))
/*
* Definitions for the per-bus node slot registration list:
*
* For each bus node in the bus list, the hot-plug service module maintains
* a doubly linked link list of items representing the slots that have been
* registered (by hot-plug controllers) for that bus.
*
* The head of the linked listed is stored in bus_slot_list field of the bus
* node. Insertions and removals from this list should locked with the mutex
* in the bus_mutex field of the bus node.
*
* Items in the list are allocated/freed with the macros hpc_alloc_slot_entry()
* and hpc_free_slot_entry().
*
* Each item in the list contains the following fields:
*
* slot_handle - handle for slot (hpc_slot_t)
*
* slot_info - information registered with the slot (hpc_slot_info_t)
*
* slot_ops - ops vector registered with the slot (hpc_slot_ops_t)
*
* slot_ops_arg - argument to be passed to ops routines (caddr_t)
*
* slot_event_handler - handler registered for slot events
*
* slot_event_handler_arg - argument to be passed to event handler
*
* slot_event_mask - the set of events for which the event handler
* gets invoked
*
* slot_bus - pointer to bus node for the slot
*
* slot_hpc_dip - devinfo node pointer to the HPC driver instance
* that controls this slot
*
* slot_{prev,next} - point to {previous,next} node in the list
*/
struct hpc_slot_entry {
hpc_slot_t slot_handle;
hpc_slot_info_t slot_info; /* should be static & copied */
hpc_slot_ops_t slot_ops;
caddr_t slot_ops_arg;
int (* slot_event_handler)(caddr_t, uint_t);
caddr_t slot_event_handler_arg;
uint_t slot_event_mask;
hpc_bus_entry_t *slot_bus;
dev_info_t *slot_hpc_dip;
hpc_slot_entry_t *slot_next, *slot_prev;
};
#define hpc_alloc_slot_entry() \
(hpc_slot_entry_t *)kmem_zalloc(sizeof (hpc_slot_entry_t), KM_SLEEP)
#define hpc_free_slot_entry(a) \
kmem_free((a), sizeof (hpc_slot_entry_t))
/*
* Definitions for slot registration callback table.
*/
typedef struct hpc_callback_entry hpc_callback_entry_t;
struct hpc_callback_entry {
int (* callback)(dev_info_t *dip, hpc_slot_t hdl,
hpc_slot_info_t *slot_info, int slot_state);
dev_info_t *dip;
hpc_slot_t hdl;
hpc_slot_info_t *slot_info;
int slot_state;
hpc_callback_entry_t *next;
};
#define hpc_alloc_callback_entry() \
(hpc_callback_entry_t *) \
kmem_zalloc(sizeof (hpc_callback_entry_t), KM_SLEEP)
#define hpc_free_callback_entry(a) \
kmem_free((a), sizeof (hpc_callback_entry_t))
/*
* Mutex lock for bus registration table and table head.
*/
static kmutex_t hpc_bus_mutex;
static hpc_bus_entry_t *hpc_bus_list_head;
/*
* Forward function declarations.
*/
static hpc_bus_entry_t *hpc_find_bus_by_name(char *name);
static void hpc_slot_event_dispatcher(hpc_bus_entry_t *busp);
/*
* loadable module definitions:
*/
extern struct mod_ops mod_miscops;
static struct modlmisc modlmisc = {
&mod_miscops, /* Type of module */
"hot-plug controller services"
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
int
_init(void)
{
int e;
mutex_init(&hpc_bus_mutex, NULL, MUTEX_DRIVER, NULL);
/*
* Install the module.
*/
e = mod_install(&modlinkage);
if (e != 0) {
mutex_destroy(&hpc_bus_mutex);
}
return (e);
}
int
_fini(void)
{
int e;
e = mod_remove(&modlinkage);
if (e == 0) {
mutex_destroy(&hpc_bus_mutex);
}
return (e);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
hpc_slot_ops_t *
hpc_alloc_slot_ops(int flag)
{
hpc_slot_ops_t *ops;
ops = (hpc_slot_ops_t *)kmem_zalloc(sizeof (hpc_slot_ops_t), flag);
return (ops);
}
void
hpc_free_slot_ops(hpc_slot_ops_t *ops)
{
kmem_free((void *)ops, sizeof (hpc_slot_ops_t));
}
/*ARGSUSED2*/
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)
{
hpc_bus_entry_t *busp;
hpc_slot_entry_t *slotp;
char bus_path[MAXPATHLEN + 1];
DEBUG2("hpc_nexus_register_bus: %s%d",
ddi_node_name(dip), ddi_get_instance(dip));
mutex_enter(&hpc_bus_mutex);
(void) ddi_pathname(dip, bus_path);
busp = hpc_find_bus_by_name(bus_path);
if (busp == NULL) {
/*
* Initialize the new bus node and link it at the head
* of the bus list.
*/
DEBUG0("hpc_nexus_register_bus: not in bus list");
busp = hpc_alloc_bus_entry();
busp->bus_dip = dip;
busp->bus_registered = B_TRUE;
(void) strcpy(busp->bus_name, bus_path);
mutex_init(&busp->bus_mutex, NULL, MUTEX_DRIVER, NULL);
busp->bus_callback = callback;
busp->bus_slot_list = NULL;
busp->bus_next = hpc_bus_list_head;
hpc_bus_list_head = busp;
} else {
/*
* The bus is in the bus list but isn't registered yet.
* Mark it as registered, and run the registration callbacks
* for it slots.
*/
DEBUG0("hpc_nexus_register_bus: in list, but not registered");
mutex_enter(&busp->bus_mutex);
if (busp->bus_registered == B_TRUE) {
mutex_exit(&busp->bus_mutex);
mutex_exit(&hpc_bus_mutex);
return (HPC_ERR_BUS_DUPLICATE);
}
busp->bus_dip = dip;
busp->bus_callback = callback;
busp->bus_registered = B_TRUE;
mutex_exit(&busp->bus_mutex);
mutex_exit(&hpc_bus_mutex);
if (callback) {
DEBUG0("hpc_nexus_register_bus: running callbacks");
for (slotp = busp->bus_slot_list; slotp;
slotp = slotp->slot_next) {
(void) callback(dip, slotp, &slotp->slot_info,
HPC_SLOT_ONLINE);
}
}
return (HPC_SUCCESS);
}
mutex_exit(&hpc_bus_mutex);
return (HPC_SUCCESS);
}
int
hpc_nexus_unregister_bus(dev_info_t *dip)
{
hpc_bus_entry_t *busp, *busp_prev;
hpc_slot_entry_t *slotp;
/*
* Search the list for the bus node and remove it.
*/
DEBUG2("hpc_nexus_unregister_bus: %s%d",
ddi_node_name(dip), ddi_get_instance(dip));
mutex_enter(&hpc_bus_mutex);
for (busp = hpc_bus_list_head; busp != NULL; busp_prev = busp,
busp = busp->bus_next) {
if (busp->bus_dip == dip)
break;
}
if (busp == NULL) {
mutex_exit(&hpc_bus_mutex);
return (HPC_ERR_BUS_NOTREGISTERED);
}
/*
* If the bus has slots, mark the bus as unregistered, otherwise
* remove the bus entry from the list.
*/
mutex_enter(&busp->bus_mutex);
if (busp->bus_slot_list == NULL) {
if (busp == hpc_bus_list_head)
hpc_bus_list_head = busp->bus_next;
else
busp_prev->bus_next = busp->bus_next;
mutex_exit(&busp->bus_mutex);
mutex_destroy(&busp->bus_mutex);
hpc_free_bus_entry(busp);
mutex_exit(&hpc_bus_mutex);
return (HPC_SUCCESS);
}
/*
* unregister event handlers for all the slots on this bus.
*/
for (slotp = busp->bus_slot_list; slotp != NULL;
slotp = slotp->slot_next) {
slotp->slot_event_handler = NULL;
slotp->slot_event_handler_arg = NULL;
}
busp->bus_registered = B_FALSE;
mutex_exit(&busp->bus_mutex);
mutex_exit(&hpc_bus_mutex);
return (HPC_SUCCESS);
}
/*ARGSUSED5*/
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)
{
hpc_bus_entry_t *busp;
hpc_slot_entry_t *slotp, *slot_list_head;
boolean_t run_callback = B_FALSE;
int (* callback)(dev_info_t *dip, hpc_slot_t hdl,
hpc_slot_info_t *slot_info, int slot_state);
dev_info_t *dip;
kthread_t *t;
/*
* Validate the arguments.
*/
DEBUG1("hpc_slot_register: %s", bus);
if (handlep == NULL || infop == NULL || opsp == NULL || hpc_dip == NULL)
return (HPC_ERR_INVALID);
/*
* The bus for the slot may or may not be in the bus list. If it's
* not, we create a node for the bus in the bus list and mark it as
* not registered.
*/
mutex_enter(&hpc_bus_mutex);
busp = hpc_find_bus_by_name(bus);
if (busp == NULL) {
/*
* Initialize the new bus node and link it at the
* head of the bus list.
*/
DEBUG1("hpc_slot_register: %s not in bus list", bus);
busp = hpc_alloc_bus_entry();
busp->bus_registered = B_FALSE;
(void) strcpy(busp->bus_name, bus);
mutex_init(&busp->bus_mutex, NULL, MUTEX_DRIVER, NULL);
busp->bus_slot_list = NULL;
busp->bus_next = hpc_bus_list_head;
hpc_bus_list_head = busp;
} else {
if (busp->bus_registered == B_TRUE) {
run_callback = B_TRUE;
callback = busp->bus_callback;
dip = busp->bus_dip;
}
}
mutex_enter(&busp->bus_mutex);
slot_list_head = busp->bus_slot_list;
if (slot_list_head == NULL) {
/*
* The slot list was empty, so this is the first slot
* registered for the bus. Create a per-bus thread
* for running the slot event handlers.
*/
DEBUG0("hpc_slot_register: creating event callback thread");
cv_init(&busp->bus_thread_cv, NULL, CV_DRIVER, NULL);
busp->bus_thread_exit = B_FALSE;
t = thread_create(NULL, 0, hpc_slot_event_dispatcher,
(caddr_t)busp, 0, &p0, TS_RUN, minclsyspri);
busp->bus_thread = t;
}
/*
* Create and initialize a new entry in the slot list for the bus.
*/
slotp = hpc_alloc_slot_entry();
slotp->slot_handle = (hpc_slot_t)slotp;
slotp->slot_info = *infop;
slotp->slot_ops = *opsp;
slotp->slot_ops_arg = ops_arg;
slotp->slot_bus = busp;
slotp->slot_hpc_dip = hpc_dip;
slotp->slot_prev = NULL;
busp->bus_slot_list = slotp;
slotp->slot_next = slot_list_head;
if (slot_list_head != NULL)
slot_list_head->slot_prev = slotp;
mutex_exit(&busp->bus_mutex);
mutex_exit(&hpc_bus_mutex);
/*
* Return the handle to the caller prior to return to avoid recursion.
*/
*handlep = (hpc_slot_t)slotp;
/*
* If the bus was registered, we run the callback registered by
* the bus node.
*/
if (run_callback) {
DEBUG0("hpc_slot_register: running callback");
(void) callback(dip, slotp, infop, HPC_SLOT_ONLINE);
}
/*
* Keep hpc driver in memory
*/
(void) ndi_hold_driver(hpc_dip);
return (HPC_SUCCESS);
}
int
hpc_slot_unregister(hpc_slot_t *handlep)
{
hpc_slot_entry_t *slotp;
hpc_bus_entry_t *busp, *busp_prev;
boolean_t run_callback;
int (* callback)(dev_info_t *dip, hpc_slot_t hdl,
hpc_slot_info_t *slot_info, int slot_state);
int r;
dev_info_t *dip;
char *bus_name;
DEBUG0("hpc_slot_unregister:");
ASSERT(handlep != NULL);
/* validate the handle */
slotp = (hpc_slot_entry_t *)*handlep;
if ((slotp == NULL) || slotp->slot_handle != *handlep)
return (HPC_ERR_INVALID);
/*
* Get the bus list entry from the slot to grap the mutex for
* the slot list of the bus.
*/
mutex_enter(&hpc_bus_mutex);
busp = slotp->slot_bus;
DEBUG2("hpc_slot_unregister: handlep=%x, slotp=%x", handlep, slotp);
if (busp == NULL) {
mutex_exit(&hpc_bus_mutex);
return (HPC_ERR_SLOT_NOTREGISTERED);
}
/*
* Determine if we need to run the slot offline callback and
* save the data necessary to do so.
*/
callback = busp->bus_callback;
run_callback = (busp->bus_registered == B_TRUE) && (callback != NULL);
dip = busp->bus_dip;
bus_name = busp->bus_name;
/*
* Run the slot offline callback if necessary.
*/
if (run_callback) {
mutex_exit(&hpc_bus_mutex);
DEBUG0("hpc_slot_unregister: running callback");
r = callback(dip, (hpc_slot_t)slotp, &slotp->slot_info,
HPC_SLOT_OFFLINE);
DEBUG1("hpc_slot_unregister: callback returned %x", r);
if (r != HPC_SUCCESS)
return (HPC_ERR_FAILED);
mutex_enter(&hpc_bus_mutex);
}
/*
* Remove the slot from list and free the memory associated with it.
*/
mutex_enter(&busp->bus_mutex);
DEBUG1("hpc_slot_unregister: freeing slot, bus_slot_list=%x",
busp->bus_slot_list);
if (slotp->slot_prev != NULL)
slotp->slot_prev->slot_next = slotp->slot_next;
if (slotp->slot_next != NULL)
slotp->slot_next->slot_prev = slotp->slot_prev;
if (slotp == busp->bus_slot_list)
busp->bus_slot_list = slotp->slot_next;
/*
* Release hold from slot registration
*/
ndi_rele_driver(slotp->slot_hpc_dip);
/* Free the memory associated with the slot entry structure */
hpc_free_slot_entry(slotp);
/*
* If the slot list is empty then stop the event handler thread.
*/
if (busp->bus_slot_list == NULL) {
DEBUG0("hpc_slot_unregister: stopping thread");
busp->bus_thread_exit = B_TRUE;
cv_signal(&busp->bus_thread_cv);
DEBUG0("hpc_slot_unregister: waiting for thread to exit");
cv_wait(&busp->bus_thread_cv, &busp->bus_mutex);
DEBUG0("hpc_slot_unregister: thread exit");
cv_destroy(&busp->bus_thread_cv);
}
/*
* If the bus is unregistered and this is the last slot for this bus
* then remove the entry from the bus list.
*/
if (busp->bus_registered == B_FALSE && busp->bus_slot_list == NULL) {
/* locate the previous entry in the bus list */
for (busp = hpc_bus_list_head; busp != NULL; busp_prev = busp,
busp = busp->bus_next)
if (strcmp(bus_name, busp->bus_name) == 0)
break;
if (busp == hpc_bus_list_head)
hpc_bus_list_head = busp->bus_next;
else
busp_prev->bus_next = busp->bus_next;
mutex_exit(&busp->bus_mutex);
mutex_destroy(&busp->bus_mutex);
hpc_free_bus_entry(busp);
} else
mutex_exit(&busp->bus_mutex);
mutex_exit(&hpc_bus_mutex);
/*
* reset the slot handle.
*/
*handlep = NULL;
return (HPC_SUCCESS);
}
int
hpc_install_event_handler(hpc_slot_t handle, uint_t event_mask,
int (*event_handler)(caddr_t, uint_t), caddr_t arg)
{
hpc_slot_entry_t *slotp;
hpc_bus_entry_t *busp;
DEBUG3("hpc_install_event_handler: handle=%x, mask=%x, arg=%x",
handle, event_mask, arg);
ASSERT((handle != NULL) && (event_handler != NULL));
slotp = (hpc_slot_entry_t *)handle;
busp = slotp->slot_bus;
ASSERT(slotp == slotp->slot_handle);
mutex_enter(&busp->bus_mutex);
slotp->slot_event_mask = event_mask;
slotp->slot_event_handler = event_handler;
slotp->slot_event_handler_arg = arg;
mutex_exit(&busp->bus_mutex);
return (HPC_SUCCESS);
}
int
hpc_remove_event_handler(hpc_slot_t handle)
{
hpc_slot_entry_t *slotp;
hpc_bus_entry_t *busp;
DEBUG1("hpc_remove_event_handler: handle=%x", handle);
ASSERT(handle != NULL);
slotp = (hpc_slot_entry_t *)handle;
ASSERT(slotp == slotp->slot_handle);
busp = slotp->slot_bus;
mutex_enter(&busp->bus_mutex);
slotp->slot_event_mask = 0;
slotp->slot_event_handler = NULL;
slotp->slot_event_handler_arg = NULL;
mutex_exit(&busp->bus_mutex);
return (HPC_SUCCESS);
}
/*ARGSUSED2*/
int
hpc_slot_event_notify(hpc_slot_t handle, uint_t event, uint_t flags)
{
hpc_slot_entry_t *slotp;
hpc_bus_entry_t *busp;
hpc_event_entry_t *eventp;
DEBUG2("hpc_slot_event_notify: handle=%x event=%x", handle, event);
ASSERT(handle != NULL);
slotp = (hpc_slot_entry_t *)handle;
ASSERT(slotp == slotp->slot_handle);
if (slotp->slot_event_handler == NULL)
return (HPC_EVENT_UNCLAIMED);
/*
* If the request is to handle the event synchronously, then call
* the event handler without queuing the event.
*/
if (flags == HPC_EVENT_SYNCHRONOUS) {
caddr_t arg;
int (* func)(caddr_t, uint_t);
func = slotp->slot_event_handler;
arg = slotp->slot_event_handler_arg;
return (func(arg, event));
}
/*
* Insert the event into the bus slot event handler list and
* signal the bus slot event handler dispatch thread.
*/
busp = slotp->slot_bus;
mutex_enter(&busp->bus_mutex);
if (busp->bus_slot_event_list_head == NULL) {
eventp = busp->bus_slot_event_list_head =
hpc_alloc_event_entry();
} else {
for (eventp = busp->bus_slot_event_list_head;
eventp->next != NULL; eventp = eventp->next)
;
eventp->next = hpc_alloc_event_entry();
eventp = eventp->next;
}
eventp->slotp = slotp;
eventp->event = event;
eventp->next = NULL;
DEBUG2("hpc_slot_event_notify: busp=%x event=%x", busp, event);
cv_signal(&busp->bus_thread_cv);
mutex_exit(&busp->bus_mutex);
return (HPC_EVENT_CLAIMED);
}
int
hpc_nexus_connect(hpc_slot_t handle, void *data, uint_t flags)
{
hpc_slot_entry_t *slotp;
ASSERT(handle != NULL);
slotp = (hpc_slot_entry_t *)handle;
if (slotp->slot_ops.hpc_op_connect)
return (slotp->slot_ops.hpc_op_connect(slotp->slot_ops_arg,
handle, data, flags));
return (HPC_ERR_FAILED);
}
int
hpc_nexus_disconnect(hpc_slot_t handle, void *data, uint_t flags)
{
hpc_slot_entry_t *slotp;
ASSERT(handle != NULL);
slotp = (hpc_slot_entry_t *)handle;
if (slotp->slot_ops.hpc_op_disconnect)
return (slotp->slot_ops.hpc_op_disconnect(slotp->slot_ops_arg,
handle, data, flags));
return (HPC_ERR_FAILED);
}
int
hpc_nexus_insert(hpc_slot_t handle, void *data, uint_t flags)
{
hpc_slot_entry_t *slotp;
ASSERT(handle != NULL);
slotp = (hpc_slot_entry_t *)handle;
if (slotp->slot_ops.hpc_op_insert)
return (slotp->slot_ops.hpc_op_insert(slotp->slot_ops_arg,
handle, data, flags));
return (HPC_ERR_FAILED);
}
int
hpc_nexus_remove(hpc_slot_t handle, void *data, uint_t flags)
{
hpc_slot_entry_t *slotp;
ASSERT(handle != NULL);
slotp = (hpc_slot_entry_t *)handle;
if (slotp->slot_ops.hpc_op_remove)
return (slotp->slot_ops.hpc_op_remove(slotp->slot_ops_arg,
handle, data, flags));
return (HPC_ERR_FAILED);
}
int
hpc_nexus_control(hpc_slot_t handle, int request, caddr_t arg)
{
hpc_slot_entry_t *slotp;
ASSERT(handle != NULL);
slotp = (hpc_slot_entry_t *)handle;
if (slotp->slot_ops.hpc_op_control)
return (slotp->slot_ops.hpc_op_control(slotp->slot_ops_arg,
handle, request, arg));
return (HPC_ERR_FAILED);
}
/*
* The following function is run from the bus entries slot event handling
* thread.
*/
static void
hpc_slot_event_dispatcher(hpc_bus_entry_t *busp)
{
hpc_event_entry_t *eventp;
hpc_slot_entry_t *slotp;
int event;
caddr_t arg;
int (* func)(caddr_t, uint_t);
callb_cpr_t cprinfo;
/*
* The creator of this thread is waiting to be signaled that
* the thread has been started.
*/
DEBUG1("hpc_slot_event_dispatcher: busp=%x", busp);
CALLB_CPR_INIT(&cprinfo, &busp->bus_mutex, callb_generic_cpr,
"hpc_slot_event_dispatcher");
mutex_enter(&busp->bus_mutex);
/*
* Wait for events to queue and then process them.
*/
for (;;) {
/*
* Note we only hold the mutex while determining
* the number of entries that have been added to
* the event list, while updating the event list
* after processing the event list entries.
*/
if (busp->bus_slot_event_list_head == NULL) {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
cv_wait(&busp->bus_thread_cv, &busp->bus_mutex);
CALLB_CPR_SAFE_END(&cprinfo, &busp->bus_mutex);
if (busp->bus_thread_exit)
break;
continue;
}
/*
* We have an event handler instance in the list to
* process. Remove the head of the list, saving the
* information required to run the event handler.
* Then run the event handler while the bus mutex
* is released.
*/
eventp = busp->bus_slot_event_list_head;
slotp = eventp->slotp;
event = eventp->event;
func = slotp->slot_event_handler;
arg = slotp->slot_event_handler_arg;
busp->bus_slot_event_list_head = eventp->next;
hpc_free_event_entry(eventp);
mutex_exit(&busp->bus_mutex);
func(arg, event);
mutex_enter(&busp->bus_mutex);
if (busp->bus_thread_exit)
break;
}
DEBUG0("hpc_slot_event_dispatcher: thread_exit");
cv_signal(&busp->bus_thread_cv);
CALLB_CPR_EXIT(&cprinfo);
thread_exit();
}
static hpc_bus_entry_t *
hpc_find_bus_by_name(char *path)
{
hpc_bus_entry_t *busp;
for (busp = hpc_bus_list_head; busp != NULL; busp = busp->bus_next) {
if (strcmp(path, busp->bus_name) == 0)
break;
}
return (busp);
}
boolean_t
hpc_bus_registered(hpc_slot_t slot_hdl)
{
hpc_slot_entry_t *slotp;
hpc_bus_entry_t *busp;
slotp = (hpc_slot_entry_t *)slot_hdl;
busp = slotp->slot_bus;
return (busp->bus_registered);
}
#ifdef DEBUG
extern void prom_printf(const char *, ...);
static void
debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
uintptr_t a4, uintptr_t a5)
{
if (hpcsvc_debug != 0) {
cmn_err(CE_CONT, "hpcsvc: ");
cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
cmn_err(CE_CONT, "\n");
}
}
#endif