lw8.c revision 055d7c804dc8f1263f0b3166ba459c65996b8697
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/time.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/stat.h>
#include <sys/cmn_err.h>
#include <sys/conf.h>
#include <sys/modctl.h>
#include <sys/devops.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/callb.h>
#include <sys/disp.h>
#include <sys/strlog.h>
#include <sys/sgevents.h>
#include <sys/serengeti.h>
#include <sys/sgsbbc.h>
#include <sys/sgsbbc_iosram.h>
#include <sys/sgsbbc_mailbox.h>
#include <sys/uadmin.h>
#include <sys/machsystm.h>
#include <sys/sysevent.h>
#include <sys/sysevent/dr.h>
#include <sys/sysevent/eventdefs.h>
#include <sys/file.h>
#include <sys/lw8.h>
#include <sys/lw8_impl.h>
#include <sys/plat_ecc_unum.h>
/*
* Global Variables - can be patched from Solaris
* ==============================================
*/
/*
* Module Variables
* ================
*/
/*
* functions local to this driver.
*/
static int lw8_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int lw8_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static int lw8_add_intr_handlers(void);
static int lw8_remove_intr_handlers(void);
static void lw8_wakeup_sleepers(void);
static uint_t lw8_fast_shutdown(char *arg);
static uint_t lw8_slow_shutdown(char *arg);
static uint_t lw8_event_data_handler(char *);
static uint_t lw8_dr_data_handler(char *);
static uint_t lw8_env_data_handler(char *);
static uint_t lw8_cap_ecc_msg_handler(char *);
static int lw8_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
static int lw8_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
static int lw8_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *cred_p, int *rval_p);
static void lw8_logger_start(void);
static void lw8_logger_destroy(void);
static void lw8_logger_wakeup(void);
/*
* Driver entry points
*/
static struct cb_ops lw8_cb_ops = {
lw8_open, /* open */
lw8_close, /* close */
nodev, /* strategy() */
nodev, /* print() */
nodev, /* dump() */
nodev, /* read() */
nodev, /* write() */
lw8_ioctl, /* ioctl() */
nodev, /* devmap() */
nodev, /* mmap() */
ddi_segmap, /* segmap() */
nochpoll, /* poll() */
ddi_prop_op, /* prop_op() */
NULL, /* cb_str */
D_NEW | D_MP /* cb_flag */
};
static struct dev_ops lw8_ops = {
DEVO_REV,
0, /* ref count */
ddi_getinfo_1to1, /* getinfo() */
nulldev, /* identify() */
nulldev, /* probe() */
lw8_attach, /* attach() */
lw8_detach, /* detach */
nodev, /* reset */
&lw8_cb_ops, /* pointer to cb_ops structure */
(struct bus_ops *)NULL,
nulldev /* power() */
};
/*
* Loadable module support.
*/
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops, /* Type of module. This is a driver */
"Netra-T12 control driver v%I%", /* Name of the module */
&lw8_ops /* pointer to the dev_ops structure */
};
static struct modlinkage modlinkage = {
MODREV_1,
&modldrv,
NULL
};
/*
* messages
*/
#define SHUTDOWN_EVENT_MSG "lw8: system shutdown due to " \
"SC request.\n"
#define VOLTAGE_EVENT_MSG "lw8: system shutdown due to " \
"voltage out of range.\n"
#define TEMPERATURE_EVENT_MSG "lw8: system shutdown due to " \
"temperature exceeding limits.\n"
#define FANFAIL_EVENT_MSG "lw8: system shutdown due to " \
"too many fan failures.\n"
#define NO_SCC_EVENT_MSG "lw8: system shutdown due to " \
"no system configuration card.\n"
/*
* led table - the following provides a cache of the led state - needed
* to avoid the overhead of readoing from the SC each time
*/
struct led_info {
char id[MAX_ID_LEN];
int position;
int status;
char color[MAX_COLOR_LEN];
};
static struct fru_led_info {
char location[MAX_LOCATION_LEN];
struct led_info led_info[MAX_LEDS_PER_FRU];
} fru_led_table[MAX_FRUS] = {
"SB0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
"PS0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
"SB2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
"PS1", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
"SB4", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
"PS2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
"IB6", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
"PS3", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
"FT0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
"FAN0", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
"FAN1", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
"FAN2", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
"FAN3", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
"FAN4", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
"FAN5", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
"FAN6", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
"FAN7", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
"FAN8", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
"FAN9", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
"DISK0", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber",
"power", LOM_LED_POSITION_LOCATION, 0, "green",
"ok_to_remove", LOM_LED_POSITION_LOCATION, 0, "blue"},
"DISK1", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber",
"power", LOM_LED_POSITION_LOCATION, 0, "green",
"ok_to_remove", LOM_LED_POSITION_LOCATION, 0, "blue"},
"RP0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
"RP2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
"chassis", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
"power", LOM_LED_POSITION_FRU, 0, "green",
"locator", LOM_LED_POSITION_FRU, 0, "white",
"top_access", LOM_LED_POSITION_FRU, 0, "amber",
"alarm1", LOM_LED_POSITION_FRU, 0, "amber",
"alarm2", LOM_LED_POSITION_FRU, 0, "amber",
"system", LOM_LED_POSITION_FRU, 0, "green",
"supplyA", LOM_LED_POSITION_FRU, 0, "green",
"supplyB", LOM_LED_POSITION_FRU, 0, "green"},
};
char *fru_locn[MAX_LOCATION_LEN] = {
"SB0",
"PS0",
"SB2",
"PS1",
"SB4",
"PS2",
"IB6",
"PS3",
"SCC",
"SSC1",
};
/*
* mutexes which protect the interrupt handlers.
*/
static kmutex_t lw8_shutdown_hdlr_lock;
static kmutex_t lw8_dr_hdlr_lock;
static kmutex_t lw8_env_hdlr_lock;
static kmutex_t lw8_event_mutex;
static kmutex_t lw8_logger_lock;
static kmutex_t lw8_cap_msg_hdlr_lock;
static kcondvar_t lw8_event_cv;
static kcondvar_t lw8_logger_sig_cv;
/*
* state booleans
*/
static boolean_t lw8_event_pending = B_FALSE;
static boolean_t led_state_cached = B_FALSE;
/*
* Payloads of the event handlers.
*/
static lw8_event_t lw8_shutdown_payload;
static sbbc_msg_t lw8_shutdown_payload_msg;
static sg_system_fru_descriptor_t lw8_dr_payload;
static sbbc_msg_t lw8_dr_payload_msg;
static sg_event_fan_status_t lw8_env_payload;
static sbbc_msg_t lw8_env_payload_msg;
static plat_capability_data_t lw8_cap_payload;
static sbbc_msg_t lw8_cap_payload_msg;
/*
* The IDs of the soft interrupts
*/
static ddi_softintr_t lw8_slow_shutdown_softint_id;
static ddi_softintr_t lw8_fast_shutdown_softint_id;
/*
* Logger commands..
*/
#define LW8_LOGGER_EXITNOW -1
#define LW8_LOGGER_WAIT 0
#define LW8_LOGGER_PROCESSNOW 1
/*
* Logger thread state
*/
static int lw8_logger_sig = LW8_LOGGER_WAIT;
static kt_did_t lw8_logger_tid = 0;
extern pri_t maxclsyspri;
int
_init(void)
{
int error = 0;
mutex_init(&lw8_shutdown_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&lw8_dr_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&lw8_env_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&lw8_cap_msg_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&lw8_event_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&lw8_logger_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&lw8_event_cv, NULL, CV_DRIVER, NULL);
cv_init(&lw8_logger_sig_cv, NULL, CV_DRIVER, NULL);
error = mod_install(&modlinkage);
if (error) {
cv_destroy(&lw8_logger_sig_cv);
cv_destroy(&lw8_event_cv);
mutex_destroy(&lw8_logger_lock);
mutex_destroy(&lw8_event_mutex);
mutex_destroy(&lw8_env_hdlr_lock);
mutex_destroy(&lw8_cap_msg_hdlr_lock);
mutex_destroy(&lw8_dr_hdlr_lock);
mutex_destroy(&lw8_shutdown_hdlr_lock);
}
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
_fini(void)
{
int error = 0;
error = mod_remove(&modlinkage);
if (error)
return (error);
cv_destroy(&lw8_logger_sig_cv);
cv_destroy(&lw8_event_cv);
mutex_destroy(&lw8_logger_lock);
mutex_destroy(&lw8_event_mutex);
mutex_destroy(&lw8_env_hdlr_lock);
mutex_destroy(&lw8_cap_msg_hdlr_lock);
mutex_destroy(&lw8_dr_hdlr_lock);
mutex_destroy(&lw8_shutdown_hdlr_lock);
return (error);
}
static int
lw8_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance;
int err;
switch (cmd) {
case DDI_ATTACH:
/*
* only allow one instance
*/
instance = ddi_get_instance(dip);
if (instance != 0)
return (DDI_FAILURE);
err = ddi_create_minor_node(dip, "lw8", S_IFCHR,
instance, DDI_PSEUDO, NULL);
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
&lw8_slow_shutdown_softint_id, NULL, NULL,
lw8_slow_shutdown, NULL);
if (err != 0) {
cmn_err(CE_WARN, "Failed to add polling softint"
"handler for lw8. Err=%d", err);
ddi_remove_minor_node(dip, NULL);
return (DDI_FAILURE);
}
err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
&lw8_fast_shutdown_softint_id, NULL, NULL,
lw8_fast_shutdown, NULL);
if (err != 0) {
cmn_err(CE_WARN, "Failed to add polling softint"
"handler for lw8. Err=%d", err);
ddi_remove_softintr(lw8_slow_shutdown_softint_id);
ddi_remove_minor_node(dip, NULL);
return (DDI_FAILURE);
}
lw8_logger_start();
/*
* Add the handlers which watch for unsolicited messages
* and post event to Sysevent Framework.
*/
err = lw8_add_intr_handlers();
if (err != DDI_SUCCESS) {
cmn_err(CE_WARN, "Failed to add event handlers");
lw8_logger_destroy();
ddi_remove_softintr(lw8_fast_shutdown_softint_id);
ddi_remove_softintr(lw8_slow_shutdown_softint_id);
ddi_remove_minor_node(dip, NULL);
return (DDI_FAILURE);
}
ddi_report_dev(dip);
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
lw8_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int instance;
int err;
switch (cmd) {
case DDI_DETACH:
instance = ddi_get_instance(dip);
if (instance != 0)
return (DDI_FAILURE);
/*
* Remove the handlers which watch for unsolicited messages
* and post event to Sysevent Framework.
*/
err = lw8_remove_intr_handlers();
if (err != DDI_SUCCESS) {
cmn_err(CE_WARN, "Failed to remove event handlers");
return (DDI_FAILURE);
}
lw8_logger_destroy();
ddi_remove_softintr(lw8_slow_shutdown_softint_id);
ddi_remove_softintr(lw8_fast_shutdown_softint_id);
ddi_remove_minor_node(dip, NULL);
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
lw8_add_intr_handlers()
{
int err;
lw8_shutdown_payload_msg.msg_buf = (caddr_t)&lw8_shutdown_payload;
lw8_shutdown_payload_msg.msg_len = sizeof (lw8_shutdown_payload);
err = sbbc_mbox_reg_intr(MBOX_EVENT_LW8, lw8_event_data_handler,
&lw8_shutdown_payload_msg, NULL, &lw8_shutdown_hdlr_lock);
if (err != 0) {
cmn_err(CE_WARN, "Failed to register MBOX_EVENT_LW8 "
" handler. Err=%d", err);
return (DDI_FAILURE);
}
lw8_dr_payload_msg.msg_buf = (caddr_t)&lw8_dr_payload;
lw8_dr_payload_msg.msg_len = sizeof (lw8_dr_payload);
err = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler,
&lw8_dr_payload_msg, NULL, &lw8_dr_hdlr_lock);
if (err != 0) {
cmn_err(CE_WARN, "Failed to register MBOX_EVENT_GENERIC "
" handler. Err=%d", err);
sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
return (DDI_FAILURE);
}
lw8_env_payload_msg.msg_buf = (caddr_t)&lw8_env_payload;
lw8_env_payload_msg.msg_len = sizeof (lw8_env_payload);
err = sbbc_mbox_reg_intr(MBOX_EVENT_ENV, lw8_env_data_handler,
&lw8_env_payload_msg, NULL, &lw8_env_hdlr_lock);
if (err != 0) {
cmn_err(CE_WARN, "Failed to register MBOX_EVENT_ENV "
" handler. Err=%d", err);
sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler);
sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
return (DDI_FAILURE);
}
lw8_cap_payload_msg.msg_buf = (caddr_t)&lw8_cap_payload;
lw8_cap_payload_msg.msg_len = sizeof (lw8_cap_payload);
err = sbbc_mbox_reg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler,
&lw8_cap_payload_msg, NULL, &lw8_cap_msg_hdlr_lock);
if (err != 0) {
cmn_err(CE_WARN, "Failed to register INFO_MBOX "
" handler. Err=%d", err);
sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler);
sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
sbbc_mbox_unreg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
lw8_remove_intr_handlers(void)
{
int rv = DDI_SUCCESS;
int err;
err = sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
if (err != 0) {
cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_LW8 "
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
err = sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler);
if (err != 0) {
cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_GENERIC "
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
err = sbbc_mbox_unreg_intr(MBOX_EVENT_ENV, lw8_env_data_handler);
if (err != 0) {
cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_ENV "
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
err = sbbc_mbox_unreg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler);
if (err != 0) {
cmn_err(CE_WARN, "Failed to unregister INFO_MBOX "
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
return (rv);
}
static uint_t
lw8_dr_data_handler(char *arg)
{
sg_system_fru_descriptor_t *payload;
sbbc_msg_t *msg;
int hint;
sysevent_t *ev;
sysevent_id_t eid;
int rv = 0;
sysevent_value_t evnt_val;
sysevent_attr_list_t *evnt_attr_list = NULL;
char attach_pnt[MAXPATHLEN];
msg = (sbbc_msg_t *)arg;
if (msg == NULL) {
return (DDI_INTR_CLAIMED);
}
payload = (sg_system_fru_descriptor_t *)msg->msg_buf;
if (payload == NULL) {
return (DDI_INTR_CLAIMED);
}
if (payload->slot < 0 || payload->slot >= sizeof (fru_locn) /
sizeof (char *)) {
return (DDI_INTR_CLAIMED);
}
/*
* if not SB send sysevent (SBs send sysevent from ssm driver)
*/
if (strncmp(fru_locn[payload->slot], "SB", 2) != 0) {
switch (payload->event_details) {
case SG_EVT_BOARD_ABSENT:
hint = SE_HINT_REMOVE;
break;
case SG_EVT_BOARD_PRESENT:
hint = SE_HINT_INSERT;
break;
default:
hint = SE_NO_HINT;
break;
}
(void) snprintf(attach_pnt, sizeof (attach_pnt), "ssm0:N0.%s",
fru_locn[payload->slot]);
ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
KM_NOSLEEP);
if (ev == NULL) {
cmn_err(CE_WARN, "Failed to allocate %s event", EC_DR);
return (DDI_INTR_CLAIMED);
}
evnt_val.value_type = SE_DATA_TYPE_STRING;
evnt_val.value.sv_string = attach_pnt;
rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
KM_NOSLEEP);
if (rv != 0) {
cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
DR_AP_ID, EC_DR);
sysevent_free(ev);
return (DDI_INTR_CLAIMED);
}
/*
* Add the hint
*/
evnt_val.value_type = SE_DATA_TYPE_STRING;
evnt_val.value.sv_string = SE_HINT2STR(hint);
rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
KM_NOSLEEP);
if (rv != 0) {
cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
DR_HINT, EC_DR);
sysevent_free_attr(evnt_attr_list);
sysevent_free(ev);
return (DDI_INTR_CLAIMED);
}
if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
cmn_err(CE_WARN, "Failed to attach attr list for %s "
"event", EC_DR);
sysevent_free_attr(evnt_attr_list);
sysevent_free(ev);
return (DDI_INTR_CLAIMED);
}
rv = log_sysevent(ev, KM_NOSLEEP, &eid);
if (rv != 0) {
cmn_err(CE_WARN,
"lw8_dr_event_handler: failed to log event");
}
sysevent_free(ev);
}
lw8_wakeup_sleepers();
return (DDI_INTR_CLAIMED);
}
static uint_t
lw8_cap_ecc_msg_handler(char *addr)
{
sbbc_msg_t *msg = NULL;
plat_capability_data_t *cap = NULL;
msg = (sbbc_msg_t *)addr;
if (msg == NULL || msg->msg_buf == NULL)
return (DDI_INTR_CLAIMED);
cap = (plat_capability_data_t *)msg->msg_buf;
switch (cap->capd_msg_type) {
case PLAT_ECC_CAPABILITY_MESSAGE:
plat_ecc_capability_sc_set(cap->capd_capability);
break;
default:
break;
}
return (DDI_INTR_CLAIMED);
}
/*ARGSUSED*/
static uint_t
lw8_env_data_handler(char *arg)
{
lw8_wakeup_sleepers();
return (DDI_INTR_CLAIMED);
}
/*
* wakeup sleepers + mark led cache for this fru as invalid
*/
static void
lw8_wakeup_sleepers()
{
mutex_enter(&lw8_event_mutex);
lw8_event_pending = B_TRUE;
cv_broadcast(&lw8_event_cv);
led_state_cached = B_FALSE;
mutex_exit(&lw8_event_mutex);
}
/*
* This function is triggered by a soft interrupt and it's purpose is to call
* to kadmin() to shutdown the system.
*/
/*ARGSUSED*/
static uint_t
lw8_fast_shutdown(char *arg)
{
(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
/*
* If kadmin fails for some reason then we bring the system down
* via power_down(), or failing that using halt().
*/
power_down("kadmin() failed, trying power_down()");
halt("power_down() failed, trying halt()");
/*
* We should never make it this far, so something must have gone
* horribly, horribly wrong.
*/
/*NOTREACHED*/
return (DDI_INTR_UNCLAIMED);
}
/*
* This function is triggered by a soft interrupt and it's purpose is to call
* to do_shutdown() to shutdown the system.
*/
/*ARGSUSED*/
static uint_t
lw8_slow_shutdown(char *arg)
{
do_shutdown();
return (DDI_SUCCESS);
}
static uint_t
lw8_event_data_handler(char *arg)
{
lw8_event_t *payload;
sbbc_msg_t *msg;
if (arg == NULL) {
return (DDI_INTR_CLAIMED);
}
msg = (sbbc_msg_t *)arg;
if (msg->msg_buf == NULL) {
return (DDI_INTR_CLAIMED);
}
payload = (lw8_event_t *)msg->msg_buf;
switch (payload->event_type) {
case LW8_EVENT_REQUESTED_SHUTDOWN:
/*
* Let the user know why the domain is going down.
*/
cmn_err(CE_WARN, "%s", SHUTDOWN_EVENT_MSG);
ddi_trigger_softintr(lw8_slow_shutdown_softint_id);
/*NOTREACHED*/
break;
case LW8_EVENT_VOLTAGE_SHUTDOWN:
/*
* Let the user know why the domain is going down.
*/
cmn_err(CE_WARN, "%s", VOLTAGE_EVENT_MSG);
ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
/*NOTREACHED*/
break;
case LW8_EVENT_TEMPERATURE_SHUTDOWN:
/*
* Let the user know why the domain is going down.
*/
cmn_err(CE_WARN, "%s", TEMPERATURE_EVENT_MSG);
ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
/*NOTREACHED*/
break;
case LW8_EVENT_FANFAIL_SHUTDOWN:
/*
* Let the user know why the domain is going down.
*/
cmn_err(CE_WARN, "%s", FANFAIL_EVENT_MSG);
ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
/*NOTREACHED*/
break;
case LW8_EVENT_NO_SCC_SHUTDOWN:
/*
* Let the user know why the domain is going down.
*/
cmn_err(CE_WARN, "%s", NO_SCC_EVENT_MSG);
ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
/*NOTREACHED*/
break;
case LW8_EVENT_NEW_LOG_MSG:
/*
* Wake up the log retrieval thread.
*/
lw8_logger_wakeup();
break;
default:
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_CLAIMED);
}
/*ARGSUSED*/
static int
lw8_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
{
int error = 0;
int instance = getminor(*dev_p);
static fn_t f = "lw8_open";
if (instance != 0)
return (ENXIO);
if ((error = drv_priv(cred_p)) != 0) {
cmn_err(CE_WARN, "lw8:%s: inst %d drv_priv failed",
f, instance);
return (error);
}
return (error);
}
/*ARGSUSED*/
static int
lw8_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
{
return (DDI_SUCCESS);
}
static int
lw8_lomcmd(int cmd, intptr_t arg)
{
sbbc_msg_t request, *reqp = &request;
sbbc_msg_t response, *resp = &response;
int rv = 0;
lom_eventreq_t *eventreqp;
bzero((caddr_t)&request, sizeof (request));
reqp->msg_type.type = LW8_MBOX;
reqp->msg_type.sub_type = cmd;
bzero((caddr_t)&response, sizeof (response));
resp->msg_type.type = LW8_MBOX;
resp->msg_type.sub_type = cmd;
switch (cmd) {
case LW8_MBOX_GET_INFO:
reqp->msg_len = 0;
reqp->msg_buf = (caddr_t)NULL;
resp->msg_len = sizeof (lom2_info_t);
resp->msg_buf = (caddr_t)arg;
break;
case LW8_MBOX_SET_CTL:
reqp->msg_len = sizeof (lom_ctl2_t);
reqp->msg_buf = (caddr_t)arg;
resp->msg_len = 0;
resp->msg_buf = (caddr_t)NULL;
break;
case LW8_MBOX_UPDATE_FW:
reqp->msg_len = sizeof (lom_prog_t);
reqp->msg_buf = (caddr_t)arg;
resp->msg_len = 0;
resp->msg_buf = (caddr_t)NULL;
break;
case LW8_MBOX_GET_LED:
reqp->msg_len = sizeof (lw8_get_led_payload_t);
reqp->msg_buf = (caddr_t)arg;
resp->msg_len = sizeof (lw8_get_led_payload_t);
resp->msg_buf = (caddr_t)arg;
break;
case LW8_MBOX_SET_LED:
reqp->msg_len = sizeof (lw8_set_led_payload_t);
reqp->msg_buf = (caddr_t)arg;
resp->msg_len = 0;
resp->msg_buf = (caddr_t)NULL;
break;
case LW8_MBOX_GET_EVENTS:
/*
* cast as lom_eventreq_t to minimise data traffic
*/
eventreqp = (lom_eventreq_t *)arg;
reqp->msg_len = sizeof (lom_eventreq_t);
reqp->msg_buf = (caddr_t)arg;
resp->msg_len = sizeof (lom_eventreq_t) +
(eventreqp->num * MAX_EVENT_STR);
resp->msg_buf = (caddr_t)arg;
break;
case LW8_MBOX_GET_NEXT_MSG:
reqp->msg_len = 0;
reqp->msg_buf = (caddr_t)NULL;
resp->msg_len = sizeof (lw8_logmsg_t);
resp->msg_buf = (caddr_t)arg;
break;
default:
return (EINVAL);
}
rv = sbbc_mbox_request_response(reqp, resp,
LW8_DEFAULT_MAX_MBOX_WAIT_TIME);
if ((rv) || (resp->msg_status != SG_MBOX_STATUS_SUCCESS)) {
/* errors from sgsbbc */
if (resp->msg_status > 0) {
return (resp->msg_status);
}
/* errors from SCAPP */
switch (resp->msg_status) {
case SG_MBOX_STATUS_COMMAND_FAILURE:
/* internal SCAPP error */
return (EINTR);
case SG_MBOX_STATUS_HARDWARE_FAILURE:
/* seprom read/write errors */
return (EIO);
case SG_MBOX_STATUS_ILLEGAL_PARAMETER:
/* illegal ioctl parameter */
return (EINVAL);
case SG_MBOX_STATUS_BOARD_ACCESS_DENIED:
/* board access denied */
return (EACCES);
case SG_MBOX_STATUS_STALE_CONTENTS:
/* stale contents */
return (ESTALE);
case SG_MBOX_STATUS_STALE_OBJECT:
/* stale handle */
return (ENOENT);
case SG_MBOX_STATUS_NO_SEPROM_SPACE:
/* seprom lacks space */
return (ENOSPC);
case SG_MBOX_STATUS_NO_MEMORY:
/* user prog. lacks space */
return (ENOMEM);
case SG_MBOX_STATUS_NOT_SUPPORTED:
/* unsupported operation */
return (ENOTSUP);
default:
return (EIO);
}
}
return (0);
}
/*
* set the requested led, and mark cache as empty
*/
static int
lw8_setled(lom_set_led_t *set_ledp)
{
int retval;
int i, j;
struct led_info *lip;
lw8_set_led_payload_t lw8_set_led;
for (i = 0; i < MAX_FRUS; i++) {
if (strncmp(set_ledp->location, fru_led_table[i].location,
MAX_LOCATION_LEN) != 0)
continue;
for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
lip = &fru_led_table[i].led_info[j];
if (lip->id == NULL)
continue;
if (strncmp(set_ledp->id, lip->id, MAX_ID_LEN) != 0)
continue;
lw8_set_led.value = set_ledp->status;
/*
* to minimise data transfer, the SC maintains
* just 3 values per fru - except for
* the chassis itself at the end which has
* MAX_LEDS_PER_FRU
*/
lw8_set_led.offset = (i * 3) + j;
retval = lw8_lomcmd(LW8_MBOX_SET_LED,
(intptr_t)&lw8_set_led);
if (retval != 0)
return (retval);
mutex_enter(&lw8_event_mutex);
led_state_cached = B_FALSE;
mutex_exit(&lw8_event_mutex);
return (0);
}
}
return (EINVAL);
}
/*
* read led value from cache if possible, otherwise read from sc and
* update the cache
*/
static int
lw8_getled(lom_get_led_t *get_ledp)
{
int retval;
int i, j, k;
struct led_info *lip;
lw8_get_led_payload_t lw8_get_led;
for (i = 0; i < MAX_FRUS; i++) {
if (strncmp(get_ledp->location, fru_led_table[i].location,
MAX_LOCATION_LEN) != 0)
continue;
if (get_ledp->id[0] == '\0') {
(void) strncpy(get_ledp->next_id,
fru_led_table[i].led_info[0].id, MAX_ID_LEN);
return (0);
}
for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
lip = &fru_led_table[i].led_info[j];
if (lip->id == NULL)
continue;
if (strncmp(get_ledp->id, lip->id, MAX_ID_LEN) != 0)
continue;
mutex_enter(&lw8_event_mutex);
if (!led_state_cached) {
mutex_exit(&lw8_event_mutex);
retval = lw8_lomcmd(LW8_MBOX_GET_LED,
(intptr_t)&lw8_get_led);
if (retval != 0)
return (retval);
mutex_enter(&lw8_event_mutex);
/*
* to minimise data transfer, the
* lw8_get_led_payload_t structure just has 3
* values per fru - except for the chassis
* itself at the end which has MAX_LEDS_PER_FRU
*/
for (k = 0; k < (MAX_FRUS - 1) * 3; k++) {
fru_led_table[k / 3].led_info[k % 3].
status = lw8_get_led.value[k];
}
for (k = 0; k < MAX_LEDS_PER_FRU; k++) {
fru_led_table[MAX_FRUS - 1].led_info[k].
status = lw8_get_led.value[k +
((MAX_FRUS - 1) * 3)];
}
led_state_cached = B_TRUE;
}
get_ledp->status = lip->status;
mutex_exit(&lw8_event_mutex);
get_ledp->position = lip->position;
(void) strncpy(get_ledp->color, lip->color,
MAX_COLOR_LEN);
if (j == MAX_LEDS_PER_FRU - 1) {
get_ledp->next_id[0] = '\0';
return (0);
}
(void) strncpy(get_ledp->next_id,
fru_led_table[i].led_info[j + 1].id, MAX_ID_LEN);
return (0);
}
}
if (get_ledp->id[0] == '\0') {
get_ledp->next_id[0] = '\0';
return (0);
}
return (EINVAL);
}
/*ARGSUSED*/
static int
lw8_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
int *rval_p)
{
int instance = getminor(dev);
lom2_info_t lw8_info2;
lom_ctl_t lw8_ctl;
lom_ctl2_t lw8_ctl2;
lom_mprog_t lw8_mprog;
lom_fled_info_t lw8_fled_info;
lom_info_t lw8_info;
lom_aldata_t lw8_aldata;
lom_get_led_t lw8_get_led;
lom_set_led_t lw8_set_led;
lom_prog_t *lw8_progp;
lom_eventlog2_t *lw8_eventlogp;
lom_eventresp_t *lw8_eventresp;
int retval = 0;
int i, j;
if (instance != 0)
return (ENXIO);
switch (cmd) {
case LOMIOCWTMON:
mutex_enter(&lw8_event_mutex);
if (!lw8_event_pending) {
if (cv_wait_sig(&lw8_event_cv, &lw8_event_mutex) == 0) {
mutex_exit(&lw8_event_mutex);
retval = EINTR;
break;
}
}
lw8_event_pending = B_FALSE;
mutex_exit(&lw8_event_mutex);
break;
case LOMIOCMREAD:
bzero((caddr_t)&lw8_mprog, sizeof (lw8_mprog));
lw8_mprog.config = 4;
if (ddi_copyout((caddr_t)&lw8_mprog, (caddr_t)arg,
sizeof (lw8_mprog), mode) != 0) {
retval = EFAULT;
}
break;
case LOMIOCCTL2:
if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl2,
sizeof (lw8_ctl2), mode) != 0) {
retval = EFAULT;
break;
}
retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
break;
case LOMIOCPROG:
lw8_progp = kmem_alloc(sizeof (*lw8_progp), KM_SLEEP);
if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_progp,
sizeof (*lw8_progp), mode) != 0) {
kmem_free(lw8_progp, sizeof (*lw8_progp));
retval = EFAULT;
break;
}
retval = lw8_lomcmd(LW8_MBOX_UPDATE_FW, (intptr_t)lw8_progp);
kmem_free(lw8_progp, sizeof (*lw8_progp));
break;
case LOMIOCINFO2:
bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
if (retval != 0)
break;
if (ddi_copyout((caddr_t)&lw8_info2, (caddr_t)arg,
sizeof (lw8_info2), mode) != 0) {
retval = EFAULT;
}
break;
case LOMIOCINFO:
bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
if (retval != 0)
break;
bzero((caddr_t)&lw8_info, sizeof (lw8_info));
lw8_info.ser_char = lw8_info2.escape_chars[0];
lw8_info.fver = lw8_info2.fver;
lw8_info.fchksum = lw8_info2.fchksum;
lw8_info.prod_rev = lw8_info2.prod_rev;
strncpy(lw8_info.prod_id, lw8_info2.prod_id, MAX_ID_LEN);
if (ddi_copyout((caddr_t)&lw8_info, (caddr_t)arg,
sizeof (lw8_info), mode) != 0) {
retval = EFAULT;
}
break;
case LOMIOCFLEDSTATE:
bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
(void) strncpy(lw8_get_led.location, "chassis",
MAX_LOCATION_LEN);
(void) strncpy(lw8_get_led.id, "fault", MAX_ID_LEN);
retval = lw8_getled(&lw8_get_led);
if (retval != 0)
break;
lw8_fled_info.on = lw8_get_led.status;
if (ddi_copyout((caddr_t)&lw8_fled_info, (caddr_t)arg,
sizeof (lw8_fled_info), mode) != 0) {
retval = EFAULT;
}
break;
case LOMIOCALSTATE:
if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
sizeof (lw8_aldata), mode) != 0) {
retval = EFAULT;
break;
}
bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
(void) strncpy(lw8_get_led.location, "chassis",
MAX_LOCATION_LEN);
if (lw8_aldata.alarm_no == 3)
(void) snprintf(lw8_get_led.id, MAX_ID_LEN, "system");
else
(void) snprintf(lw8_get_led.id, MAX_ID_LEN, "alarm%d",
lw8_aldata.alarm_no);
retval = lw8_getled(&lw8_get_led);
if (retval != 0)
break;
lw8_aldata.state = lw8_get_led.status;
if (ddi_copyout((caddr_t)&lw8_aldata, (caddr_t)arg,
sizeof (lw8_aldata), mode) != 0) {
retval = EFAULT;
}
break;
case LOMIOCGETLED:
if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_get_led,
sizeof (lw8_get_led), mode) != 0) {
retval = EFAULT;
break;
}
retval = lw8_getled(&lw8_get_led);
if (retval != 0)
break;
if (ddi_copyout((caddr_t)&lw8_get_led, (caddr_t)arg,
sizeof (lw8_get_led), mode) != 0) {
retval = EFAULT;
}
break;
case LOMIOCEVENTLOG2:
lw8_eventlogp = kmem_alloc(sizeof (*lw8_eventlogp), KM_SLEEP);
lw8_eventresp = kmem_zalloc(sizeof (*lw8_eventresp), KM_SLEEP);
if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_eventlogp,
sizeof (*lw8_eventlogp), mode) != 0) {
kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
retval = EFAULT;
break;
}
lw8_eventresp->num = lw8_eventlogp->num;
lw8_eventresp->level = lw8_eventlogp->level;
retval = lw8_lomcmd(LW8_MBOX_GET_EVENTS,
(intptr_t)lw8_eventresp);
if (retval == 0) {
lw8_eventlogp->num = lw8_eventresp->num;
for (i = 0; i < lw8_eventresp->num; i++) {
for (j = 0; j < MAX_EVENT_STR; j++) {
lw8_eventlogp->string[i][j] =
lw8_eventresp->string[i][j];
}
}
if (ddi_copyout((caddr_t)lw8_eventlogp, (caddr_t)arg,
sizeof (*lw8_eventlogp), mode) != 0) {
retval = EFAULT;
}
}
kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
break;
case LOMIOCALCTL:
if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
sizeof (lw8_aldata), mode) != 0) {
retval = EFAULT;
break;
}
bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
(void) strncpy(lw8_set_led.location, "chassis",
MAX_LOCATION_LEN);
if (lw8_aldata.alarm_no == 3)
(void) snprintf(lw8_set_led.id, MAX_ID_LEN, "system");
else
(void) snprintf(lw8_set_led.id, MAX_ID_LEN, "alarm%d",
lw8_aldata.alarm_no);
lw8_set_led.status = lw8_aldata.state;
retval = lw8_setled(&lw8_set_led);
break;
case LOMIOCSETLED:
if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_set_led,
sizeof (lw8_set_led), mode) != 0) {
retval = EFAULT;
break;
}
retval = lw8_setled(&lw8_set_led);
break;
case LOMIOCCTL:
/*
* for this ioctl, as well as setting the fault led in the
* LOMIOCCTL case in lw8_lomcmd(), we also need to set the
* escape character. To do this we must use LW8_MBOX_SET_CTL,
* but this also needs the serial_event value which we have
* to get via LW8_MBOX_GET_INFO
*/
if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl,
sizeof (lw8_ctl), mode) != 0) {
retval = EFAULT;
break;
}
bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
if (retval != 0)
break;
bzero((caddr_t)&lw8_ctl2, sizeof (lw8_ctl2));
lw8_ctl2.escape_chars[0] = lw8_ctl.ser_char;
lw8_ctl2.serial_events = lw8_info2.serial_events;
retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
if (retval != 0)
break;
/*
* if fault_led != 0, then set the led
*/
if (lw8_ctl.fault_led == 0)
break;
bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
(void) strncpy(lw8_set_led.location, "chassis",
MAX_LOCATION_LEN);
(void) strncpy(lw8_set_led.id, "fault", MAX_ID_LEN);
lw8_set_led.status = lw8_ctl.fault_led - 1;
retval = lw8_setled(&lw8_set_led);
break;
default:
retval = ENOTSUP;
break;
}
return (retval);
}
/* ARGSUSED */
static void
lw8_logger(caddr_t arg)
{
callb_cpr_t cprinfo;
lw8_logmsg_t *lw8_logmsgp;
boolean_t more_waiting;
char level;
int retval;
CALLB_CPR_INIT(&cprinfo, &lw8_logger_lock, callb_generic_cpr,
"lw8_logger");
lw8_logmsgp = kmem_zalloc(sizeof (*lw8_logmsgp), KM_SLEEP);
mutex_enter(&lw8_logger_lock);
for (;;) {
/*
* Wait for someone to tell me to continue.
*/
while (lw8_logger_sig == LW8_LOGGER_WAIT) {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
cv_wait(&lw8_logger_sig_cv, &lw8_logger_lock);
CALLB_CPR_SAFE_END(&cprinfo, &lw8_logger_lock);
}
/* LW8_LOGGER_EXITNOW implies signal by _detach(). */
if (lw8_logger_sig == LW8_LOGGER_EXITNOW) {
lw8_logger_sig = LW8_LOGGER_WAIT;
kmem_free(lw8_logmsgp, sizeof (*lw8_logmsgp));
/* lw8_logger_lock is held at this point! */
CALLB_CPR_EXIT(&cprinfo);
thread_exit();
/* NOTREACHED */
}
ASSERT(lw8_logger_sig == LW8_LOGGER_PROCESSNOW);
lw8_logger_sig = LW8_LOGGER_WAIT;
mutex_exit(&lw8_logger_lock);
/* Do lw8_event logging */
/*
* Get one message per iteration. We do not sleep if
* there are more to process. This makes exit from the
* routine much more reliable.
*/
more_waiting = B_FALSE;
retval = lw8_lomcmd(LW8_MBOX_GET_NEXT_MSG,
(intptr_t)lw8_logmsgp);
if (retval == 0) {
if (lw8_logmsgp->msg_valid) {
switch (lw8_logmsgp->level) {
case 0: /* LOG_EMERG */
level = SL_FATAL;
break;
case 1: /* LOG_ALERT */
level = SL_FATAL;
break;
case 2: /* LOG_CRIT */
level = SL_FATAL;
break;
case 3: /* LOG_ERR */
level = SL_ERROR;
break;
case 4: /* LOG_WARNING */
level = SL_WARN;
break;
case 5: /* LOG_NOTICE */
level = SL_NOTE;
break;
case 6: /* LOG_INFO */
level = SL_NOTE;
break;
case 7: /* LOG_DEBUG */
level = SL_TRACE;
break;
default: /* unknown */
level = SL_NOTE;
break;
}
/* Ensure NUL termination */
lw8_logmsgp->msg[
sizeof (lw8_logmsgp->msg) - 1] = '\0';
strlog(0, 0, 0, SL_CONSOLE | level,
lw8_logmsgp->msg);
}
if (lw8_logmsgp->num_remaining > 0)
more_waiting = B_TRUE;
}
/*
* Re-enter the lock to prepare for another iteration.
* We must have the lock here to protect lw8_logger_sig.
*/
mutex_enter(&lw8_logger_lock);
if ((lw8_logger_sig == LW8_LOGGER_WAIT) && more_waiting)
/* We need to get more events */
lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
}
}
static void
lw8_logger_start(void)
{
kthread_t *tp;
mutex_enter(&lw8_logger_lock);
if (lw8_logger_tid == 0) {
/* Force retrieval of any pending messages */
lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
tp = thread_create(NULL, 0, lw8_logger, NULL, 0,
&p0, TS_RUN, maxclsyspri);
lw8_logger_tid = tp->t_did;
}
mutex_exit(&lw8_logger_lock);
}
static void
lw8_logger_destroy(void)
{
kt_did_t tid;
mutex_enter(&lw8_logger_lock);
tid = lw8_logger_tid;
if (tid != 0) {
lw8_logger_sig = LW8_LOGGER_EXITNOW;
cv_signal(&lw8_logger_sig_cv);
lw8_logger_tid = 0;
}
mutex_exit(&lw8_logger_lock);
/*
* Wait for lw8_logger() to finish.
*/
if (tid != 0)
thread_join(tid);
}
static void
lw8_logger_wakeup(void)
{
mutex_enter(&lw8_logger_lock);
if (lw8_logger_sig != LW8_LOGGER_EXITNOW)
lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
cv_signal(&lw8_logger_sig_cv);
mutex_exit(&lw8_logger_lock);
}