/*
* 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
* 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
*/
/*
* debug macros:
*/
#if defined(DEBUG)
int hpcsvc_debug = 0;
#else
#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.
*
* 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
*/
struct hpc_event_entry {
int event;
};
#define hpc_alloc_event_entry() \
#define hpc_free_event_entry(a) \
kmem_free((a), sizeof (hpc_event_entry_t))
struct hpc_bus_entry {
};
#define hpc_alloc_bus_entry() \
#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.
*
* 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 {
};
#define hpc_alloc_slot_entry() \
#define hpc_free_slot_entry(a) \
kmem_free((a), sizeof (hpc_slot_entry_t))
/*
* Definitions for slot registration callback table.
*/
struct hpc_callback_entry {
int slot_state;
};
#define hpc_alloc_callback_entry() \
(hpc_callback_entry_t *) \
#define hpc_free_callback_entry(a) \
kmem_free((a), sizeof (hpc_callback_entry_t))
/*
* Mutex lock for bus registration table and table head.
*/
/*
* Forward function declarations.
*/
/*
* loadable module definitions:
*/
extern struct mod_ops mod_miscops;
&mod_miscops, /* Type of module */
"hot-plug controller services"
};
};
int
_init(void)
{
int e;
/*
* Install the module.
*/
e = mod_install(&modlinkage);
if (e != 0) {
}
return (e);
}
int
_fini(void)
{
int e;
e = mod_remove(&modlinkage);
if (e == 0) {
}
return (e);
}
int
{
}
{
return (ops);
}
void
{
}
/*ARGSUSED2*/
int
{
DEBUG2("hpc_nexus_register_bus: %s%d",
/*
* 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();
} 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");
return (HPC_ERR_BUS_DUPLICATE);
}
if (callback) {
DEBUG0("hpc_nexus_register_bus: running callbacks");
}
}
return (HPC_SUCCESS);
}
return (HPC_SUCCESS);
}
int
{
/*
* Search the list for the bus node and remove it.
*/
DEBUG2("hpc_nexus_unregister_bus: %s%d",
break;
}
return (HPC_ERR_BUS_NOTREGISTERED);
}
/*
* If the bus has slots, mark the bus as unregistered, otherwise
* remove the bus entry from the list.
*/
if (busp == hpc_bus_list_head)
else
return (HPC_SUCCESS);
}
/*
* unregister event handlers for all the slots on this bus.
*/
}
return (HPC_SUCCESS);
}
/*ARGSUSED5*/
int
{
kthread_t *t;
/*
* Validate the arguments.
*/
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.
*/
/*
* Initialize the new bus node and link it at the
* head of the bus list.
*/
busp = hpc_alloc_bus_entry();
} else {
}
}
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");
busp->bus_thread = t;
}
/*
* Create and initialize a new entry in the slot list for the bus.
*/
if (slot_list_head != NULL)
/*
* Return the handle to the caller prior to return to avoid recursion.
*/
/*
* If the bus was registered, we run the callback registered by
* the bus node.
*/
if (run_callback) {
DEBUG0("hpc_slot_register: running callback");
}
/*
* Keep hpc driver in memory
*/
(void) ndi_hold_driver(hpc_dip);
return (HPC_SUCCESS);
}
int
{
int r;
char *bus_name;
DEBUG0("hpc_slot_unregister:");
/* validate the handle */
return (HPC_ERR_INVALID);
/*
* Get the bus list entry from the slot to grap the mutex for
* the slot list of the bus.
*/
return (HPC_ERR_SLOT_NOTREGISTERED);
}
/*
* Determine if we need to run the slot offline callback and
* save the data necessary to do so.
*/
/*
* Run the slot offline callback if necessary.
*/
if (run_callback) {
DEBUG0("hpc_slot_unregister: running callback");
DEBUG1("hpc_slot_unregister: callback returned %x", r);
if (r != HPC_SUCCESS)
return (HPC_ERR_FAILED);
}
/*
* Remove the slot from list and free the memory associated with it.
*/
DEBUG1("hpc_slot_unregister: freeing slot, bus_slot_list=%x",
/*
* Release hold from slot registration
*/
/* Free the memory associated with the slot entry structure */
/*
* If the slot list is empty then stop the event handler thread.
*/
DEBUG0("hpc_slot_unregister: stopping thread");
DEBUG0("hpc_slot_unregister: waiting for thread to exit");
DEBUG0("hpc_slot_unregister: thread exit");
}
/*
* If the bus is unregistered and this is the last slot for this bus
* then remove the entry from the bus list.
*/
/* locate the previous entry in the bus list */
break;
if (busp == hpc_bus_list_head)
else
} else
/*
* reset the slot handle.
*/
return (HPC_SUCCESS);
}
int
{
DEBUG3("hpc_install_event_handler: handle=%x, mask=%x, arg=%x",
return (HPC_SUCCESS);
}
int
{
slotp->slot_event_mask = 0;
return (HPC_SUCCESS);
}
/*ARGSUSED2*/
int
{
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) {
}
/*
* Insert the event into the bus slot event handler list and
* signal the bus slot event handler dispatch thread.
*/
} else {
;
}
return (HPC_EVENT_CLAIMED);
}
int
{
return (HPC_ERR_FAILED);
}
int
{
return (HPC_ERR_FAILED);
}
int
{
return (HPC_ERR_FAILED);
}
int
{
return (HPC_ERR_FAILED);
}
int
{
return (HPC_ERR_FAILED);
}
/*
* The following function is run from the bus entries slot event handling
* thread.
*/
static void
{
int event;
/*
* The creator of this thread is waiting to be signaled that
* the thread has been started.
*/
"hpc_slot_event_dispatcher");
/*
* 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_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.
*/
if (busp->bus_thread_exit)
break;
}
DEBUG0("hpc_slot_event_dispatcher: thread_exit");
thread_exit();
}
static hpc_bus_entry_t *
{
break;
}
return (busp);
}
{
return (busp->bus_registered);
}
#ifdef DEBUG
extern void prom_printf(const char *, ...);
static void
{
if (hpcsvc_debug != 0) {
}
}
#endif