103831d35Sstevel /*
203831d35Sstevel * CDDL HEADER START
303831d35Sstevel *
403831d35Sstevel * The contents of this file are subject to the terms of the
503831d35Sstevel * Common Development and Distribution License (the "License").
603831d35Sstevel * You may not use this file except in compliance with the License.
703831d35Sstevel *
803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel * See the License for the specific language governing permissions
1103831d35Sstevel * and limitations under the License.
1203831d35Sstevel *
1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel *
1903831d35Sstevel * CDDL HEADER END
2003831d35Sstevel */
2103831d35Sstevel
2203831d35Sstevel /*
23*07d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2403831d35Sstevel * Use is subject to license terms.
2503831d35Sstevel */
2603831d35Sstevel
2703831d35Sstevel
2803831d35Sstevel #include <sys/time.h>
2903831d35Sstevel #include <sys/errno.h>
3003831d35Sstevel #include <sys/kmem.h>
3103831d35Sstevel #include <sys/stat.h>
3203831d35Sstevel #include <sys/cmn_err.h>
3303831d35Sstevel
3403831d35Sstevel #include <sys/conf.h>
3503831d35Sstevel #include <sys/modctl.h>
3603831d35Sstevel #include <sys/devops.h>
3703831d35Sstevel #include <sys/ddi.h>
3803831d35Sstevel #include <sys/sunddi.h>
3903831d35Sstevel #include <sys/callb.h>
4003831d35Sstevel #include <sys/disp.h>
4103831d35Sstevel #include <sys/strlog.h>
4203831d35Sstevel
4303831d35Sstevel #include <sys/sgevents.h>
4403831d35Sstevel #include <sys/serengeti.h>
4503831d35Sstevel #include <sys/sgsbbc.h>
4603831d35Sstevel #include <sys/sgsbbc_iosram.h>
4703831d35Sstevel #include <sys/sgsbbc_mailbox.h>
4803831d35Sstevel #include <sys/uadmin.h>
4903831d35Sstevel #include <sys/machsystm.h>
5003831d35Sstevel #include <sys/sysevent.h>
5103831d35Sstevel #include <sys/sysevent/dr.h>
5203831d35Sstevel #include <sys/sysevent/eventdefs.h>
5303831d35Sstevel #include <sys/file.h>
5403831d35Sstevel #include <sys/lw8.h>
5503831d35Sstevel #include <sys/lw8_impl.h>
5603831d35Sstevel #include <sys/plat_ecc_unum.h>
5703831d35Sstevel
5803831d35Sstevel /*
5903831d35Sstevel * Global Variables - can be patched from Solaris
6003831d35Sstevel * ==============================================
6103831d35Sstevel */
6203831d35Sstevel
6303831d35Sstevel /*
6403831d35Sstevel * Module Variables
6503831d35Sstevel * ================
6603831d35Sstevel */
6703831d35Sstevel
6803831d35Sstevel /*
6903831d35Sstevel * functions local to this driver.
7003831d35Sstevel */
7103831d35Sstevel static int lw8_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
7203831d35Sstevel static int lw8_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
7303831d35Sstevel static int lw8_add_intr_handlers(void);
7403831d35Sstevel static int lw8_remove_intr_handlers(void);
7503831d35Sstevel static void lw8_wakeup_sleepers(void);
7603831d35Sstevel static uint_t lw8_fast_shutdown(char *arg);
7703831d35Sstevel static uint_t lw8_slow_shutdown(char *arg);
7803831d35Sstevel static uint_t lw8_event_data_handler(char *);
7903831d35Sstevel static uint_t lw8_dr_data_handler(char *);
8003831d35Sstevel static uint_t lw8_env_data_handler(char *);
8103831d35Sstevel static uint_t lw8_cap_ecc_msg_handler(char *);
8203831d35Sstevel static int lw8_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
8303831d35Sstevel static int lw8_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
8403831d35Sstevel static int lw8_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
8503831d35Sstevel cred_t *cred_p, int *rval_p);
8603831d35Sstevel static void lw8_logger_start(void);
8703831d35Sstevel static void lw8_logger_destroy(void);
8803831d35Sstevel static void lw8_logger_wakeup(void);
8903831d35Sstevel
9003831d35Sstevel /*
9103831d35Sstevel * Driver entry points
9203831d35Sstevel */
9303831d35Sstevel static struct cb_ops lw8_cb_ops = {
9403831d35Sstevel lw8_open, /* open */
9503831d35Sstevel lw8_close, /* close */
9603831d35Sstevel nodev, /* strategy() */
9703831d35Sstevel nodev, /* print() */
9803831d35Sstevel nodev, /* dump() */
9903831d35Sstevel nodev, /* read() */
10003831d35Sstevel nodev, /* write() */
10103831d35Sstevel lw8_ioctl, /* ioctl() */
10203831d35Sstevel nodev, /* devmap() */
10303831d35Sstevel nodev, /* mmap() */
10403831d35Sstevel ddi_segmap, /* segmap() */
10503831d35Sstevel nochpoll, /* poll() */
10603831d35Sstevel ddi_prop_op, /* prop_op() */
10703831d35Sstevel NULL, /* cb_str */
10803831d35Sstevel D_NEW | D_MP /* cb_flag */
10903831d35Sstevel };
11003831d35Sstevel
11103831d35Sstevel
11203831d35Sstevel static struct dev_ops lw8_ops = {
11303831d35Sstevel DEVO_REV,
11403831d35Sstevel 0, /* ref count */
11503831d35Sstevel ddi_getinfo_1to1, /* getinfo() */
11603831d35Sstevel nulldev, /* identify() */
11703831d35Sstevel nulldev, /* probe() */
11803831d35Sstevel lw8_attach, /* attach() */
11903831d35Sstevel lw8_detach, /* detach */
12003831d35Sstevel nodev, /* reset */
12103831d35Sstevel &lw8_cb_ops, /* pointer to cb_ops structure */
12203831d35Sstevel (struct bus_ops *)NULL,
12319397407SSherry Moore nulldev, /* power() */
12419397407SSherry Moore ddi_quiesce_not_needed, /* quiesce() */
12503831d35Sstevel };
12603831d35Sstevel
12703831d35Sstevel /*
12803831d35Sstevel * Loadable module support.
12903831d35Sstevel */
13003831d35Sstevel extern struct mod_ops mod_driverops;
13103831d35Sstevel
13203831d35Sstevel static struct modldrv modldrv = {
13303831d35Sstevel &mod_driverops, /* Type of module. This is a driver */
13419397407SSherry Moore "Netra-T12 control driver", /* Name of the module */
13503831d35Sstevel &lw8_ops /* pointer to the dev_ops structure */
13603831d35Sstevel };
13703831d35Sstevel
13803831d35Sstevel static struct modlinkage modlinkage = {
13903831d35Sstevel MODREV_1,
14003831d35Sstevel &modldrv,
14103831d35Sstevel NULL
14203831d35Sstevel };
14303831d35Sstevel
14403831d35Sstevel /*
14503831d35Sstevel * messages
14603831d35Sstevel */
14703831d35Sstevel #define SHUTDOWN_EVENT_MSG "lw8: system shutdown due to " \
14803831d35Sstevel "SC request.\n"
14903831d35Sstevel #define VOLTAGE_EVENT_MSG "lw8: system shutdown due to " \
15003831d35Sstevel "voltage out of range.\n"
15103831d35Sstevel #define TEMPERATURE_EVENT_MSG "lw8: system shutdown due to " \
15203831d35Sstevel "temperature exceeding limits.\n"
15303831d35Sstevel #define FANFAIL_EVENT_MSG "lw8: system shutdown due to " \
15403831d35Sstevel "too many fan failures.\n"
15503831d35Sstevel #define NO_SCC_EVENT_MSG "lw8: system shutdown due to " \
15603831d35Sstevel "no system configuration card.\n"
15703831d35Sstevel
15803831d35Sstevel /*
15903831d35Sstevel * led table - the following provides a cache of the led state - needed
16003831d35Sstevel * to avoid the overhead of readoing from the SC each time
16103831d35Sstevel */
16203831d35Sstevel
16303831d35Sstevel struct led_info {
16403831d35Sstevel char id[MAX_ID_LEN];
16503831d35Sstevel int position;
16603831d35Sstevel int status;
16703831d35Sstevel char color[MAX_COLOR_LEN];
16803831d35Sstevel };
16903831d35Sstevel
17003831d35Sstevel static struct fru_led_info {
17103831d35Sstevel char location[MAX_LOCATION_LEN];
17203831d35Sstevel struct led_info led_info[MAX_LEDS_PER_FRU];
17303831d35Sstevel } fru_led_table[MAX_FRUS] = {
17403831d35Sstevel "SB0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
17503831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
17603831d35Sstevel "ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
17703831d35Sstevel "PS0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
17803831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
17903831d35Sstevel "predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
18003831d35Sstevel "SB2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
18103831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
18203831d35Sstevel "ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
18303831d35Sstevel "PS1", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
18403831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
18503831d35Sstevel "predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
18603831d35Sstevel "SB4", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
18703831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
18803831d35Sstevel "ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
18903831d35Sstevel "PS2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
19003831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
19103831d35Sstevel "predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
19203831d35Sstevel "IB6", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
19303831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
19403831d35Sstevel "ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
19503831d35Sstevel "PS3", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
19603831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
19703831d35Sstevel "predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
19803831d35Sstevel "FT0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
19903831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
20003831d35Sstevel "ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
20103831d35Sstevel "FAN0", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20203831d35Sstevel "FAN1", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20303831d35Sstevel "FAN2", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20403831d35Sstevel "FAN3", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20503831d35Sstevel "FAN4", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20603831d35Sstevel "FAN5", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20703831d35Sstevel "FAN6", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20803831d35Sstevel "FAN7", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20903831d35Sstevel "FAN8", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
21003831d35Sstevel "FAN9", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
21103831d35Sstevel "DISK0", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber",
21203831d35Sstevel "power", LOM_LED_POSITION_LOCATION, 0, "green",
21303831d35Sstevel "ok_to_remove", LOM_LED_POSITION_LOCATION, 0, "blue"},
21403831d35Sstevel "DISK1", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber",
21503831d35Sstevel "power", LOM_LED_POSITION_LOCATION, 0, "green",
21603831d35Sstevel "ok_to_remove", LOM_LED_POSITION_LOCATION, 0, "blue"},
21703831d35Sstevel "RP0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
21803831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
21903831d35Sstevel "ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
22003831d35Sstevel "RP2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
22103831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
22203831d35Sstevel "ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
22303831d35Sstevel "chassis", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
22403831d35Sstevel "power", LOM_LED_POSITION_FRU, 0, "green",
22503831d35Sstevel "locator", LOM_LED_POSITION_FRU, 0, "white",
22603831d35Sstevel "top_access", LOM_LED_POSITION_FRU, 0, "amber",
22703831d35Sstevel "alarm1", LOM_LED_POSITION_FRU, 0, "amber",
22803831d35Sstevel "alarm2", LOM_LED_POSITION_FRU, 0, "amber",
22903831d35Sstevel "system", LOM_LED_POSITION_FRU, 0, "green",
23003831d35Sstevel "supplyA", LOM_LED_POSITION_FRU, 0, "green",
23103831d35Sstevel "supplyB", LOM_LED_POSITION_FRU, 0, "green"},
23203831d35Sstevel };
23303831d35Sstevel
23403831d35Sstevel char *fru_locn[MAX_LOCATION_LEN] = {
23503831d35Sstevel "SB0",
23603831d35Sstevel "PS0",
23703831d35Sstevel "SB2",
23803831d35Sstevel "PS1",
23903831d35Sstevel "SB4",
24003831d35Sstevel "PS2",
24103831d35Sstevel "IB6",
24203831d35Sstevel "PS3",
24303831d35Sstevel "SCC",
24403831d35Sstevel "SSC1",
24503831d35Sstevel };
24603831d35Sstevel
24703831d35Sstevel /*
24803831d35Sstevel * mutexes which protect the interrupt handlers.
24903831d35Sstevel */
25003831d35Sstevel static kmutex_t lw8_shutdown_hdlr_lock;
25103831d35Sstevel static kmutex_t lw8_dr_hdlr_lock;
25203831d35Sstevel static kmutex_t lw8_env_hdlr_lock;
25303831d35Sstevel static kmutex_t lw8_event_mutex;
25403831d35Sstevel static kmutex_t lw8_logger_lock;
25503831d35Sstevel static kmutex_t lw8_cap_msg_hdlr_lock;
25603831d35Sstevel static kcondvar_t lw8_event_cv;
25703831d35Sstevel static kcondvar_t lw8_logger_sig_cv;
25803831d35Sstevel
25903831d35Sstevel /*
26003831d35Sstevel * state booleans
26103831d35Sstevel */
26203831d35Sstevel static boolean_t lw8_event_pending = B_FALSE;
26303831d35Sstevel static boolean_t led_state_cached = B_FALSE;
26403831d35Sstevel
26503831d35Sstevel /*
26603831d35Sstevel * Payloads of the event handlers.
26703831d35Sstevel */
26803831d35Sstevel static lw8_event_t lw8_shutdown_payload;
26903831d35Sstevel static sbbc_msg_t lw8_shutdown_payload_msg;
27003831d35Sstevel static sg_system_fru_descriptor_t lw8_dr_payload;
27103831d35Sstevel static sbbc_msg_t lw8_dr_payload_msg;
27203831d35Sstevel static sg_event_fan_status_t lw8_env_payload;
27303831d35Sstevel static sbbc_msg_t lw8_env_payload_msg;
27403831d35Sstevel static plat_capability_data_t lw8_cap_payload;
27503831d35Sstevel static sbbc_msg_t lw8_cap_payload_msg;
27603831d35Sstevel
27703831d35Sstevel /*
27803831d35Sstevel * The IDs of the soft interrupts
27903831d35Sstevel */
28003831d35Sstevel static ddi_softintr_t lw8_slow_shutdown_softint_id;
28103831d35Sstevel static ddi_softintr_t lw8_fast_shutdown_softint_id;
28203831d35Sstevel
28303831d35Sstevel /*
28403831d35Sstevel * Logger commands..
28503831d35Sstevel */
28603831d35Sstevel #define LW8_LOGGER_EXITNOW -1
28703831d35Sstevel #define LW8_LOGGER_WAIT 0
28803831d35Sstevel #define LW8_LOGGER_PROCESSNOW 1
28903831d35Sstevel
29003831d35Sstevel /*
29103831d35Sstevel * Logger thread state
29203831d35Sstevel */
29303831d35Sstevel static int lw8_logger_sig = LW8_LOGGER_WAIT;
29403831d35Sstevel static kt_did_t lw8_logger_tid = 0;
29503831d35Sstevel
29603831d35Sstevel extern pri_t maxclsyspri;
29703831d35Sstevel
29803831d35Sstevel int
_init(void)29903831d35Sstevel _init(void)
30003831d35Sstevel {
30103831d35Sstevel int error = 0;
30203831d35Sstevel
30303831d35Sstevel mutex_init(&lw8_shutdown_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
30403831d35Sstevel mutex_init(&lw8_dr_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
30503831d35Sstevel mutex_init(&lw8_env_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
30603831d35Sstevel mutex_init(&lw8_cap_msg_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
30703831d35Sstevel mutex_init(&lw8_event_mutex, NULL, MUTEX_DRIVER, NULL);
30803831d35Sstevel mutex_init(&lw8_logger_lock, NULL, MUTEX_DRIVER, NULL);
30903831d35Sstevel cv_init(&lw8_event_cv, NULL, CV_DRIVER, NULL);
31003831d35Sstevel cv_init(&lw8_logger_sig_cv, NULL, CV_DRIVER, NULL);
31103831d35Sstevel
31203831d35Sstevel error = mod_install(&modlinkage);
31303831d35Sstevel if (error) {
31403831d35Sstevel cv_destroy(&lw8_logger_sig_cv);
31503831d35Sstevel cv_destroy(&lw8_event_cv);
31603831d35Sstevel mutex_destroy(&lw8_logger_lock);
31703831d35Sstevel mutex_destroy(&lw8_event_mutex);
31803831d35Sstevel mutex_destroy(&lw8_env_hdlr_lock);
31903831d35Sstevel mutex_destroy(&lw8_cap_msg_hdlr_lock);
32003831d35Sstevel mutex_destroy(&lw8_dr_hdlr_lock);
32103831d35Sstevel mutex_destroy(&lw8_shutdown_hdlr_lock);
32203831d35Sstevel }
32303831d35Sstevel return (error);
32403831d35Sstevel }
32503831d35Sstevel
32603831d35Sstevel
32703831d35Sstevel int
_info(struct modinfo * modinfop)32803831d35Sstevel _info(struct modinfo *modinfop)
32903831d35Sstevel {
33003831d35Sstevel return (mod_info(&modlinkage, modinfop));
33103831d35Sstevel }
33203831d35Sstevel
33303831d35Sstevel
33403831d35Sstevel int
_fini(void)33503831d35Sstevel _fini(void)
33603831d35Sstevel {
33703831d35Sstevel int error = 0;
33803831d35Sstevel
33903831d35Sstevel error = mod_remove(&modlinkage);
34003831d35Sstevel if (error)
34103831d35Sstevel return (error);
34203831d35Sstevel cv_destroy(&lw8_logger_sig_cv);
34303831d35Sstevel cv_destroy(&lw8_event_cv);
34403831d35Sstevel mutex_destroy(&lw8_logger_lock);
34503831d35Sstevel mutex_destroy(&lw8_event_mutex);
34603831d35Sstevel mutex_destroy(&lw8_env_hdlr_lock);
34703831d35Sstevel mutex_destroy(&lw8_cap_msg_hdlr_lock);
34803831d35Sstevel mutex_destroy(&lw8_dr_hdlr_lock);
34903831d35Sstevel mutex_destroy(&lw8_shutdown_hdlr_lock);
35003831d35Sstevel return (error);
35103831d35Sstevel }
35203831d35Sstevel
35303831d35Sstevel
35403831d35Sstevel static int
lw8_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)35503831d35Sstevel lw8_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
35603831d35Sstevel {
35703831d35Sstevel int instance;
35803831d35Sstevel int err;
35903831d35Sstevel
36003831d35Sstevel switch (cmd) {
36103831d35Sstevel case DDI_ATTACH:
36203831d35Sstevel /*
36303831d35Sstevel * only allow one instance
36403831d35Sstevel */
36503831d35Sstevel instance = ddi_get_instance(dip);
36603831d35Sstevel if (instance != 0)
36703831d35Sstevel return (DDI_FAILURE);
36803831d35Sstevel
36903831d35Sstevel err = ddi_create_minor_node(dip, "lw8", S_IFCHR,
37003831d35Sstevel instance, DDI_PSEUDO, NULL);
37103831d35Sstevel if (err != DDI_SUCCESS)
37203831d35Sstevel return (DDI_FAILURE);
37303831d35Sstevel
37403831d35Sstevel err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
37503831d35Sstevel &lw8_slow_shutdown_softint_id, NULL, NULL,
37603831d35Sstevel lw8_slow_shutdown, NULL);
37703831d35Sstevel if (err != 0) {
37803831d35Sstevel cmn_err(CE_WARN, "Failed to add polling softint"
37903831d35Sstevel "handler for lw8. Err=%d", err);
38003831d35Sstevel ddi_remove_minor_node(dip, NULL);
38103831d35Sstevel return (DDI_FAILURE);
38203831d35Sstevel }
38303831d35Sstevel
38403831d35Sstevel err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
38503831d35Sstevel &lw8_fast_shutdown_softint_id, NULL, NULL,
38603831d35Sstevel lw8_fast_shutdown, NULL);
38703831d35Sstevel if (err != 0) {
38803831d35Sstevel cmn_err(CE_WARN, "Failed to add polling softint"
38903831d35Sstevel "handler for lw8. Err=%d", err);
39003831d35Sstevel ddi_remove_softintr(lw8_slow_shutdown_softint_id);
39103831d35Sstevel ddi_remove_minor_node(dip, NULL);
39203831d35Sstevel return (DDI_FAILURE);
39303831d35Sstevel }
39403831d35Sstevel
39503831d35Sstevel lw8_logger_start();
39603831d35Sstevel
39703831d35Sstevel /*
39803831d35Sstevel * Add the handlers which watch for unsolicited messages
39903831d35Sstevel * and post event to Sysevent Framework.
40003831d35Sstevel */
40103831d35Sstevel err = lw8_add_intr_handlers();
40203831d35Sstevel if (err != DDI_SUCCESS) {
40303831d35Sstevel cmn_err(CE_WARN, "Failed to add event handlers");
40403831d35Sstevel lw8_logger_destroy();
40503831d35Sstevel ddi_remove_softintr(lw8_fast_shutdown_softint_id);
40603831d35Sstevel ddi_remove_softintr(lw8_slow_shutdown_softint_id);
40703831d35Sstevel ddi_remove_minor_node(dip, NULL);
40803831d35Sstevel return (DDI_FAILURE);
40903831d35Sstevel }
41003831d35Sstevel
41103831d35Sstevel ddi_report_dev(dip);
41203831d35Sstevel return (DDI_SUCCESS);
41303831d35Sstevel case DDI_RESUME:
41403831d35Sstevel return (DDI_SUCCESS);
41503831d35Sstevel default:
41603831d35Sstevel return (DDI_FAILURE);
41703831d35Sstevel }
41803831d35Sstevel }
41903831d35Sstevel
42003831d35Sstevel
42103831d35Sstevel static int
lw8_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)42203831d35Sstevel lw8_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
42303831d35Sstevel {
42403831d35Sstevel int instance;
42503831d35Sstevel int err;
42603831d35Sstevel
42703831d35Sstevel switch (cmd) {
42803831d35Sstevel case DDI_DETACH:
42903831d35Sstevel instance = ddi_get_instance(dip);
43003831d35Sstevel if (instance != 0)
43103831d35Sstevel return (DDI_FAILURE);
43203831d35Sstevel
43303831d35Sstevel /*
43403831d35Sstevel * Remove the handlers which watch for unsolicited messages
43503831d35Sstevel * and post event to Sysevent Framework.
43603831d35Sstevel */
43703831d35Sstevel err = lw8_remove_intr_handlers();
43803831d35Sstevel if (err != DDI_SUCCESS) {
43903831d35Sstevel cmn_err(CE_WARN, "Failed to remove event handlers");
44003831d35Sstevel return (DDI_FAILURE);
44103831d35Sstevel }
44203831d35Sstevel lw8_logger_destroy();
44303831d35Sstevel ddi_remove_softintr(lw8_slow_shutdown_softint_id);
44403831d35Sstevel ddi_remove_softintr(lw8_fast_shutdown_softint_id);
44503831d35Sstevel ddi_remove_minor_node(dip, NULL);
44603831d35Sstevel return (DDI_SUCCESS);
44703831d35Sstevel case DDI_SUSPEND:
44803831d35Sstevel return (DDI_SUCCESS);
44903831d35Sstevel default:
45003831d35Sstevel return (DDI_FAILURE);
45103831d35Sstevel }
45203831d35Sstevel }
45303831d35Sstevel
45403831d35Sstevel static int
lw8_add_intr_handlers()45503831d35Sstevel lw8_add_intr_handlers()
45603831d35Sstevel {
45703831d35Sstevel int err;
45803831d35Sstevel
45903831d35Sstevel lw8_shutdown_payload_msg.msg_buf = (caddr_t)&lw8_shutdown_payload;
46003831d35Sstevel lw8_shutdown_payload_msg.msg_len = sizeof (lw8_shutdown_payload);
46103831d35Sstevel err = sbbc_mbox_reg_intr(MBOX_EVENT_LW8, lw8_event_data_handler,
46203831d35Sstevel &lw8_shutdown_payload_msg, NULL, &lw8_shutdown_hdlr_lock);
46303831d35Sstevel if (err != 0) {
46403831d35Sstevel cmn_err(CE_WARN, "Failed to register MBOX_EVENT_LW8 "
46503831d35Sstevel " handler. Err=%d", err);
46603831d35Sstevel return (DDI_FAILURE);
46703831d35Sstevel }
46803831d35Sstevel
46903831d35Sstevel lw8_dr_payload_msg.msg_buf = (caddr_t)&lw8_dr_payload;
47003831d35Sstevel lw8_dr_payload_msg.msg_len = sizeof (lw8_dr_payload);
47103831d35Sstevel err = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler,
47203831d35Sstevel &lw8_dr_payload_msg, NULL, &lw8_dr_hdlr_lock);
47303831d35Sstevel if (err != 0) {
47403831d35Sstevel cmn_err(CE_WARN, "Failed to register MBOX_EVENT_GENERIC "
47503831d35Sstevel " handler. Err=%d", err);
476*07d06da5SSurya Prakki (void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
477*07d06da5SSurya Prakki lw8_event_data_handler);
47803831d35Sstevel return (DDI_FAILURE);
47903831d35Sstevel }
48003831d35Sstevel
48103831d35Sstevel lw8_env_payload_msg.msg_buf = (caddr_t)&lw8_env_payload;
48203831d35Sstevel lw8_env_payload_msg.msg_len = sizeof (lw8_env_payload);
48303831d35Sstevel err = sbbc_mbox_reg_intr(MBOX_EVENT_ENV, lw8_env_data_handler,
48403831d35Sstevel &lw8_env_payload_msg, NULL, &lw8_env_hdlr_lock);
48503831d35Sstevel if (err != 0) {
48603831d35Sstevel cmn_err(CE_WARN, "Failed to register MBOX_EVENT_ENV "
48703831d35Sstevel " handler. Err=%d", err);
488*07d06da5SSurya Prakki (void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC,
489*07d06da5SSurya Prakki lw8_dr_data_handler);
490*07d06da5SSurya Prakki (void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
491*07d06da5SSurya Prakki lw8_event_data_handler);
49203831d35Sstevel return (DDI_FAILURE);
49303831d35Sstevel }
49403831d35Sstevel
49503831d35Sstevel lw8_cap_payload_msg.msg_buf = (caddr_t)&lw8_cap_payload;
49603831d35Sstevel lw8_cap_payload_msg.msg_len = sizeof (lw8_cap_payload);
49703831d35Sstevel err = sbbc_mbox_reg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler,
49803831d35Sstevel &lw8_cap_payload_msg, NULL, &lw8_cap_msg_hdlr_lock);
49903831d35Sstevel if (err != 0) {
50003831d35Sstevel cmn_err(CE_WARN, "Failed to register INFO_MBOX "
50103831d35Sstevel " handler. Err=%d", err);
502*07d06da5SSurya Prakki (void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC,
503*07d06da5SSurya Prakki lw8_dr_data_handler);
504*07d06da5SSurya Prakki (void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
505*07d06da5SSurya Prakki lw8_event_data_handler);
506*07d06da5SSurya Prakki (void) sbbc_mbox_unreg_intr(INFO_MBOX,
507*07d06da5SSurya Prakki lw8_cap_ecc_msg_handler);
50803831d35Sstevel return (DDI_FAILURE);
50903831d35Sstevel }
51003831d35Sstevel
51103831d35Sstevel return (DDI_SUCCESS);
51203831d35Sstevel }
51303831d35Sstevel
51403831d35Sstevel static int
lw8_remove_intr_handlers(void)51503831d35Sstevel lw8_remove_intr_handlers(void)
51603831d35Sstevel {
51703831d35Sstevel int rv = DDI_SUCCESS;
51803831d35Sstevel int err;
51903831d35Sstevel
52003831d35Sstevel err = sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
52103831d35Sstevel if (err != 0) {
52203831d35Sstevel cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_LW8 "
52303831d35Sstevel "handler. Err=%d", err);
52403831d35Sstevel rv = DDI_FAILURE;
52503831d35Sstevel }
52603831d35Sstevel err = sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler);
52703831d35Sstevel if (err != 0) {
52803831d35Sstevel cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_GENERIC "
52903831d35Sstevel "handler. Err=%d", err);
53003831d35Sstevel rv = DDI_FAILURE;
53103831d35Sstevel }
53203831d35Sstevel err = sbbc_mbox_unreg_intr(MBOX_EVENT_ENV, lw8_env_data_handler);
53303831d35Sstevel if (err != 0) {
53403831d35Sstevel cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_ENV "
53503831d35Sstevel "handler. Err=%d", err);
53603831d35Sstevel rv = DDI_FAILURE;
53703831d35Sstevel }
53803831d35Sstevel err = sbbc_mbox_unreg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler);
53903831d35Sstevel if (err != 0) {
54003831d35Sstevel cmn_err(CE_WARN, "Failed to unregister INFO_MBOX "
54103831d35Sstevel "handler. Err=%d", err);
54203831d35Sstevel rv = DDI_FAILURE;
54303831d35Sstevel }
54403831d35Sstevel return (rv);
54503831d35Sstevel }
54603831d35Sstevel
54703831d35Sstevel static uint_t
lw8_dr_data_handler(char * arg)54803831d35Sstevel lw8_dr_data_handler(char *arg)
54903831d35Sstevel {
55003831d35Sstevel sg_system_fru_descriptor_t *payload;
55103831d35Sstevel sbbc_msg_t *msg;
55203831d35Sstevel int hint;
55303831d35Sstevel sysevent_t *ev;
55403831d35Sstevel sysevent_id_t eid;
55503831d35Sstevel int rv = 0;
55603831d35Sstevel sysevent_value_t evnt_val;
55703831d35Sstevel sysevent_attr_list_t *evnt_attr_list = NULL;
55803831d35Sstevel char attach_pnt[MAXPATHLEN];
55903831d35Sstevel
56003831d35Sstevel msg = (sbbc_msg_t *)arg;
56103831d35Sstevel if (msg == NULL) {
56203831d35Sstevel return (DDI_INTR_CLAIMED);
56303831d35Sstevel }
56403831d35Sstevel payload = (sg_system_fru_descriptor_t *)msg->msg_buf;
56503831d35Sstevel if (payload == NULL) {
56603831d35Sstevel return (DDI_INTR_CLAIMED);
56703831d35Sstevel }
56803831d35Sstevel if (payload->slot < 0 || payload->slot >= sizeof (fru_locn) /
56903831d35Sstevel sizeof (char *)) {
57003831d35Sstevel return (DDI_INTR_CLAIMED);
57103831d35Sstevel }
57203831d35Sstevel
57303831d35Sstevel /*
57403831d35Sstevel * if not SB send sysevent (SBs send sysevent from ssm driver)
57503831d35Sstevel */
57603831d35Sstevel if (strncmp(fru_locn[payload->slot], "SB", 2) != 0) {
57703831d35Sstevel switch (payload->event_details) {
57803831d35Sstevel case SG_EVT_BOARD_ABSENT:
57903831d35Sstevel hint = SE_HINT_REMOVE;
58003831d35Sstevel break;
58103831d35Sstevel case SG_EVT_BOARD_PRESENT:
58203831d35Sstevel hint = SE_HINT_INSERT;
58303831d35Sstevel break;
58403831d35Sstevel default:
58503831d35Sstevel hint = SE_NO_HINT;
58603831d35Sstevel break;
58703831d35Sstevel }
58803831d35Sstevel (void) snprintf(attach_pnt, sizeof (attach_pnt), "ssm0:N0.%s",
58903831d35Sstevel fru_locn[payload->slot]);
59003831d35Sstevel ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
59103831d35Sstevel KM_NOSLEEP);
59203831d35Sstevel if (ev == NULL) {
59303831d35Sstevel cmn_err(CE_WARN, "Failed to allocate %s event", EC_DR);
59403831d35Sstevel return (DDI_INTR_CLAIMED);
59503831d35Sstevel }
59603831d35Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING;
59703831d35Sstevel evnt_val.value.sv_string = attach_pnt;
59803831d35Sstevel rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
59903831d35Sstevel KM_NOSLEEP);
60003831d35Sstevel if (rv != 0) {
60103831d35Sstevel cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
60203831d35Sstevel DR_AP_ID, EC_DR);
60303831d35Sstevel sysevent_free(ev);
60403831d35Sstevel return (DDI_INTR_CLAIMED);
60503831d35Sstevel }
60603831d35Sstevel
60703831d35Sstevel /*
60803831d35Sstevel * Add the hint
60903831d35Sstevel */
61003831d35Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING;
61103831d35Sstevel evnt_val.value.sv_string = SE_HINT2STR(hint);
61203831d35Sstevel rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
61303831d35Sstevel KM_NOSLEEP);
61403831d35Sstevel if (rv != 0) {
61503831d35Sstevel cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
61603831d35Sstevel DR_HINT, EC_DR);
61703831d35Sstevel sysevent_free_attr(evnt_attr_list);
61803831d35Sstevel sysevent_free(ev);
61903831d35Sstevel return (DDI_INTR_CLAIMED);
62003831d35Sstevel }
62103831d35Sstevel if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
62203831d35Sstevel cmn_err(CE_WARN, "Failed to attach attr list for %s "
62303831d35Sstevel "event", EC_DR);
62403831d35Sstevel sysevent_free_attr(evnt_attr_list);
62503831d35Sstevel sysevent_free(ev);
62603831d35Sstevel return (DDI_INTR_CLAIMED);
62703831d35Sstevel }
62803831d35Sstevel rv = log_sysevent(ev, KM_NOSLEEP, &eid);
62903831d35Sstevel if (rv != 0) {
63003831d35Sstevel cmn_err(CE_WARN,
63103831d35Sstevel "lw8_dr_event_handler: failed to log event");
63203831d35Sstevel }
63303831d35Sstevel sysevent_free(ev);
63403831d35Sstevel }
63503831d35Sstevel lw8_wakeup_sleepers();
63603831d35Sstevel return (DDI_INTR_CLAIMED);
63703831d35Sstevel }
63803831d35Sstevel
63903831d35Sstevel static uint_t
lw8_cap_ecc_msg_handler(char * addr)64003831d35Sstevel lw8_cap_ecc_msg_handler(char *addr)
64103831d35Sstevel {
64203831d35Sstevel sbbc_msg_t *msg = NULL;
64303831d35Sstevel plat_capability_data_t *cap = NULL;
64403831d35Sstevel
64503831d35Sstevel msg = (sbbc_msg_t *)addr;
64603831d35Sstevel if (msg == NULL || msg->msg_buf == NULL)
64703831d35Sstevel return (DDI_INTR_CLAIMED);
64803831d35Sstevel
64903831d35Sstevel cap = (plat_capability_data_t *)msg->msg_buf;
65003831d35Sstevel switch (cap->capd_msg_type) {
65103831d35Sstevel case PLAT_ECC_CAPABILITY_MESSAGE:
65203831d35Sstevel plat_ecc_capability_sc_set(cap->capd_capability);
65303831d35Sstevel break;
65403831d35Sstevel default:
65503831d35Sstevel break;
65603831d35Sstevel }
65703831d35Sstevel
65803831d35Sstevel return (DDI_INTR_CLAIMED);
65903831d35Sstevel }
66003831d35Sstevel
66103831d35Sstevel /*ARGSUSED*/
66203831d35Sstevel static uint_t
lw8_env_data_handler(char * arg)66303831d35Sstevel lw8_env_data_handler(char *arg)
66403831d35Sstevel {
66503831d35Sstevel lw8_wakeup_sleepers();
66603831d35Sstevel return (DDI_INTR_CLAIMED);
66703831d35Sstevel }
66803831d35Sstevel
66903831d35Sstevel /*
67003831d35Sstevel * wakeup sleepers + mark led cache for this fru as invalid
67103831d35Sstevel */
67203831d35Sstevel static void
lw8_wakeup_sleepers()67303831d35Sstevel lw8_wakeup_sleepers()
67403831d35Sstevel {
67503831d35Sstevel mutex_enter(&lw8_event_mutex);
67603831d35Sstevel lw8_event_pending = B_TRUE;
67703831d35Sstevel cv_broadcast(&lw8_event_cv);
67803831d35Sstevel led_state_cached = B_FALSE;
67903831d35Sstevel mutex_exit(&lw8_event_mutex);
68003831d35Sstevel }
68103831d35Sstevel
68203831d35Sstevel /*
68303831d35Sstevel * This function is triggered by a soft interrupt and it's purpose is to call
68403831d35Sstevel * to kadmin() to shutdown the system.
68503831d35Sstevel */
68603831d35Sstevel /*ARGSUSED*/
68703831d35Sstevel static uint_t
lw8_fast_shutdown(char * arg)68803831d35Sstevel lw8_fast_shutdown(char *arg)
68903831d35Sstevel {
69003831d35Sstevel (void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
69103831d35Sstevel
69203831d35Sstevel /*
69303831d35Sstevel * If kadmin fails for some reason then we bring the system down
69403831d35Sstevel * via power_down(), or failing that using halt().
69503831d35Sstevel */
69603831d35Sstevel power_down("kadmin() failed, trying power_down()");
69703831d35Sstevel
69803831d35Sstevel halt("power_down() failed, trying halt()");
69903831d35Sstevel
70003831d35Sstevel /*
70103831d35Sstevel * We should never make it this far, so something must have gone
70203831d35Sstevel * horribly, horribly wrong.
70303831d35Sstevel */
70403831d35Sstevel /*NOTREACHED*/
705055d7c80Scarlsonj return (DDI_INTR_UNCLAIMED);
70603831d35Sstevel }
70703831d35Sstevel
70803831d35Sstevel /*
70903831d35Sstevel * This function is triggered by a soft interrupt and it's purpose is to call
71003831d35Sstevel * to do_shutdown() to shutdown the system.
71103831d35Sstevel */
71203831d35Sstevel /*ARGSUSED*/
71303831d35Sstevel static uint_t
lw8_slow_shutdown(char * arg)71403831d35Sstevel lw8_slow_shutdown(char *arg)
71503831d35Sstevel {
71603831d35Sstevel do_shutdown();
71703831d35Sstevel return (DDI_SUCCESS);
71803831d35Sstevel }
71903831d35Sstevel
72003831d35Sstevel static uint_t
lw8_event_data_handler(char * arg)72103831d35Sstevel lw8_event_data_handler(char *arg)
72203831d35Sstevel {
72303831d35Sstevel lw8_event_t *payload;
72403831d35Sstevel sbbc_msg_t *msg;
72503831d35Sstevel
72603831d35Sstevel if (arg == NULL) {
72703831d35Sstevel return (DDI_INTR_CLAIMED);
72803831d35Sstevel }
72903831d35Sstevel
73003831d35Sstevel msg = (sbbc_msg_t *)arg;
73103831d35Sstevel if (msg->msg_buf == NULL) {
73203831d35Sstevel return (DDI_INTR_CLAIMED);
73303831d35Sstevel }
73403831d35Sstevel
73503831d35Sstevel payload = (lw8_event_t *)msg->msg_buf;
73603831d35Sstevel switch (payload->event_type) {
73703831d35Sstevel case LW8_EVENT_REQUESTED_SHUTDOWN:
73803831d35Sstevel
73903831d35Sstevel /*
74003831d35Sstevel * Let the user know why the domain is going down.
74103831d35Sstevel */
74203831d35Sstevel cmn_err(CE_WARN, "%s", SHUTDOWN_EVENT_MSG);
74303831d35Sstevel ddi_trigger_softintr(lw8_slow_shutdown_softint_id);
74403831d35Sstevel
74503831d35Sstevel /*NOTREACHED*/
74603831d35Sstevel break;
74703831d35Sstevel
74803831d35Sstevel case LW8_EVENT_VOLTAGE_SHUTDOWN:
74903831d35Sstevel
75003831d35Sstevel /*
75103831d35Sstevel * Let the user know why the domain is going down.
75203831d35Sstevel */
75303831d35Sstevel cmn_err(CE_WARN, "%s", VOLTAGE_EVENT_MSG);
75403831d35Sstevel ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
75503831d35Sstevel
75603831d35Sstevel /*NOTREACHED*/
75703831d35Sstevel break;
75803831d35Sstevel
75903831d35Sstevel case LW8_EVENT_TEMPERATURE_SHUTDOWN:
76003831d35Sstevel
76103831d35Sstevel /*
76203831d35Sstevel * Let the user know why the domain is going down.
76303831d35Sstevel */
76403831d35Sstevel cmn_err(CE_WARN, "%s", TEMPERATURE_EVENT_MSG);
76503831d35Sstevel ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
76603831d35Sstevel
76703831d35Sstevel /*NOTREACHED*/
76803831d35Sstevel break;
76903831d35Sstevel
77003831d35Sstevel case LW8_EVENT_FANFAIL_SHUTDOWN:
77103831d35Sstevel
77203831d35Sstevel /*
77303831d35Sstevel * Let the user know why the domain is going down.
77403831d35Sstevel */
77503831d35Sstevel cmn_err(CE_WARN, "%s", FANFAIL_EVENT_MSG);
77603831d35Sstevel ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
77703831d35Sstevel
77803831d35Sstevel /*NOTREACHED*/
77903831d35Sstevel break;
78003831d35Sstevel
78103831d35Sstevel case LW8_EVENT_NO_SCC_SHUTDOWN:
78203831d35Sstevel
78303831d35Sstevel /*
78403831d35Sstevel * Let the user know why the domain is going down.
78503831d35Sstevel */
78603831d35Sstevel cmn_err(CE_WARN, "%s", NO_SCC_EVENT_MSG);
78703831d35Sstevel ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
78803831d35Sstevel
78903831d35Sstevel /*NOTREACHED*/
79003831d35Sstevel break;
79103831d35Sstevel
79203831d35Sstevel case LW8_EVENT_NEW_LOG_MSG:
79303831d35Sstevel
79403831d35Sstevel /*
79503831d35Sstevel * Wake up the log retrieval thread.
79603831d35Sstevel */
79703831d35Sstevel lw8_logger_wakeup();
79803831d35Sstevel
79903831d35Sstevel break;
80003831d35Sstevel
80103831d35Sstevel default:
80203831d35Sstevel return (DDI_INTR_CLAIMED);
80303831d35Sstevel }
80403831d35Sstevel
80503831d35Sstevel return (DDI_INTR_CLAIMED);
80603831d35Sstevel }
80703831d35Sstevel
80803831d35Sstevel /*ARGSUSED*/
80903831d35Sstevel static int
lw8_open(dev_t * dev_p,int flag,int otyp,cred_t * cred_p)81003831d35Sstevel lw8_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
81103831d35Sstevel {
81203831d35Sstevel int error = 0;
81303831d35Sstevel int instance = getminor(*dev_p);
81403831d35Sstevel static fn_t f = "lw8_open";
81503831d35Sstevel
81603831d35Sstevel if (instance != 0)
81703831d35Sstevel return (ENXIO);
81803831d35Sstevel
81903831d35Sstevel if ((error = drv_priv(cred_p)) != 0) {
82003831d35Sstevel cmn_err(CE_WARN, "lw8:%s: inst %d drv_priv failed",
82103831d35Sstevel f, instance);
82203831d35Sstevel return (error);
82303831d35Sstevel }
82403831d35Sstevel return (error);
82503831d35Sstevel }
82603831d35Sstevel
82703831d35Sstevel /*ARGSUSED*/
82803831d35Sstevel static int
lw8_close(dev_t dev,int flag,int otyp,cred_t * cred_p)82903831d35Sstevel lw8_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
83003831d35Sstevel {
83103831d35Sstevel return (DDI_SUCCESS);
83203831d35Sstevel }
83303831d35Sstevel
83403831d35Sstevel static int
lw8_lomcmd(int cmd,intptr_t arg)83503831d35Sstevel lw8_lomcmd(int cmd, intptr_t arg)
83603831d35Sstevel {
83703831d35Sstevel sbbc_msg_t request, *reqp = &request;
83803831d35Sstevel sbbc_msg_t response, *resp = &response;
83903831d35Sstevel int rv = 0;
84003831d35Sstevel lom_eventreq_t *eventreqp;
84103831d35Sstevel
84203831d35Sstevel bzero((caddr_t)&request, sizeof (request));
84303831d35Sstevel reqp->msg_type.type = LW8_MBOX;
84403831d35Sstevel reqp->msg_type.sub_type = cmd;
84503831d35Sstevel bzero((caddr_t)&response, sizeof (response));
84603831d35Sstevel resp->msg_type.type = LW8_MBOX;
84703831d35Sstevel resp->msg_type.sub_type = cmd;
84803831d35Sstevel
84903831d35Sstevel switch (cmd) {
85003831d35Sstevel case LW8_MBOX_GET_INFO:
85103831d35Sstevel reqp->msg_len = 0;
85203831d35Sstevel reqp->msg_buf = (caddr_t)NULL;
85303831d35Sstevel resp->msg_len = sizeof (lom2_info_t);
85403831d35Sstevel resp->msg_buf = (caddr_t)arg;
85503831d35Sstevel break;
85603831d35Sstevel case LW8_MBOX_SET_CTL:
85703831d35Sstevel reqp->msg_len = sizeof (lom_ctl2_t);
85803831d35Sstevel reqp->msg_buf = (caddr_t)arg;
85903831d35Sstevel resp->msg_len = 0;
86003831d35Sstevel resp->msg_buf = (caddr_t)NULL;
86103831d35Sstevel break;
86203831d35Sstevel case LW8_MBOX_UPDATE_FW:
86303831d35Sstevel reqp->msg_len = sizeof (lom_prog_t);
86403831d35Sstevel reqp->msg_buf = (caddr_t)arg;
86503831d35Sstevel resp->msg_len = 0;
86603831d35Sstevel resp->msg_buf = (caddr_t)NULL;
86703831d35Sstevel break;
86803831d35Sstevel case LW8_MBOX_GET_LED:
86903831d35Sstevel reqp->msg_len = sizeof (lw8_get_led_payload_t);
87003831d35Sstevel reqp->msg_buf = (caddr_t)arg;
87103831d35Sstevel resp->msg_len = sizeof (lw8_get_led_payload_t);
87203831d35Sstevel resp->msg_buf = (caddr_t)arg;
87303831d35Sstevel break;
87403831d35Sstevel case LW8_MBOX_SET_LED:
87503831d35Sstevel reqp->msg_len = sizeof (lw8_set_led_payload_t);
87603831d35Sstevel reqp->msg_buf = (caddr_t)arg;
87703831d35Sstevel resp->msg_len = 0;
87803831d35Sstevel resp->msg_buf = (caddr_t)NULL;
87903831d35Sstevel break;
88003831d35Sstevel case LW8_MBOX_GET_EVENTS:
88103831d35Sstevel /*
88203831d35Sstevel * cast as lom_eventreq_t to minimise data traffic
88303831d35Sstevel */
88403831d35Sstevel eventreqp = (lom_eventreq_t *)arg;
88503831d35Sstevel reqp->msg_len = sizeof (lom_eventreq_t);
88603831d35Sstevel reqp->msg_buf = (caddr_t)arg;
88703831d35Sstevel resp->msg_len = sizeof (lom_eventreq_t) +
88803831d35Sstevel (eventreqp->num * MAX_EVENT_STR);
88903831d35Sstevel resp->msg_buf = (caddr_t)arg;
89003831d35Sstevel break;
89103831d35Sstevel case LW8_MBOX_GET_NEXT_MSG:
89203831d35Sstevel reqp->msg_len = 0;
89303831d35Sstevel reqp->msg_buf = (caddr_t)NULL;
89403831d35Sstevel resp->msg_len = sizeof (lw8_logmsg_t);
89503831d35Sstevel resp->msg_buf = (caddr_t)arg;
89603831d35Sstevel break;
89703831d35Sstevel default:
89803831d35Sstevel return (EINVAL);
89903831d35Sstevel }
90003831d35Sstevel
90103831d35Sstevel rv = sbbc_mbox_request_response(reqp, resp,
90203831d35Sstevel LW8_DEFAULT_MAX_MBOX_WAIT_TIME);
90303831d35Sstevel
90403831d35Sstevel if ((rv) || (resp->msg_status != SG_MBOX_STATUS_SUCCESS)) {
90503831d35Sstevel
90603831d35Sstevel /* errors from sgsbbc */
90703831d35Sstevel if (resp->msg_status > 0) {
90803831d35Sstevel return (resp->msg_status);
90903831d35Sstevel }
91003831d35Sstevel
91103831d35Sstevel /* errors from SCAPP */
91203831d35Sstevel switch (resp->msg_status) {
91303831d35Sstevel
91403831d35Sstevel case SG_MBOX_STATUS_COMMAND_FAILURE:
91503831d35Sstevel /* internal SCAPP error */
91603831d35Sstevel return (EINTR);
91703831d35Sstevel
91803831d35Sstevel case SG_MBOX_STATUS_HARDWARE_FAILURE:
91903831d35Sstevel /* seprom read/write errors */
92003831d35Sstevel return (EIO);
92103831d35Sstevel
92203831d35Sstevel case SG_MBOX_STATUS_ILLEGAL_PARAMETER:
92303831d35Sstevel /* illegal ioctl parameter */
92403831d35Sstevel return (EINVAL);
92503831d35Sstevel
92603831d35Sstevel case SG_MBOX_STATUS_BOARD_ACCESS_DENIED:
92703831d35Sstevel /* board access denied */
92803831d35Sstevel return (EACCES);
92903831d35Sstevel
93003831d35Sstevel case SG_MBOX_STATUS_STALE_CONTENTS:
93103831d35Sstevel /* stale contents */
93203831d35Sstevel return (ESTALE);
93303831d35Sstevel
93403831d35Sstevel case SG_MBOX_STATUS_STALE_OBJECT:
93503831d35Sstevel /* stale handle */
93603831d35Sstevel return (ENOENT);
93703831d35Sstevel
93803831d35Sstevel case SG_MBOX_STATUS_NO_SEPROM_SPACE:
93903831d35Sstevel /* seprom lacks space */
94003831d35Sstevel return (ENOSPC);
94103831d35Sstevel
94203831d35Sstevel case SG_MBOX_STATUS_NO_MEMORY:
94303831d35Sstevel /* user prog. lacks space */
94403831d35Sstevel return (ENOMEM);
94503831d35Sstevel
94603831d35Sstevel case SG_MBOX_STATUS_NOT_SUPPORTED:
94703831d35Sstevel /* unsupported operation */
94803831d35Sstevel return (ENOTSUP);
94903831d35Sstevel
95003831d35Sstevel default:
95103831d35Sstevel return (EIO);
95203831d35Sstevel }
95303831d35Sstevel }
95403831d35Sstevel return (0);
95503831d35Sstevel }
95603831d35Sstevel
95703831d35Sstevel /*
95803831d35Sstevel * set the requested led, and mark cache as empty
95903831d35Sstevel */
96003831d35Sstevel static int
lw8_setled(lom_set_led_t * set_ledp)96103831d35Sstevel lw8_setled(lom_set_led_t *set_ledp)
96203831d35Sstevel {
96303831d35Sstevel int retval;
96403831d35Sstevel int i, j;
96503831d35Sstevel struct led_info *lip;
96603831d35Sstevel lw8_set_led_payload_t lw8_set_led;
96703831d35Sstevel
96803831d35Sstevel for (i = 0; i < MAX_FRUS; i++) {
96903831d35Sstevel if (strncmp(set_ledp->location, fru_led_table[i].location,
97003831d35Sstevel MAX_LOCATION_LEN) != 0)
97103831d35Sstevel continue;
97203831d35Sstevel for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
97303831d35Sstevel lip = &fru_led_table[i].led_info[j];
97403831d35Sstevel if (lip->id == NULL)
97503831d35Sstevel continue;
97603831d35Sstevel if (strncmp(set_ledp->id, lip->id, MAX_ID_LEN) != 0)
97703831d35Sstevel continue;
97803831d35Sstevel lw8_set_led.value = set_ledp->status;
97903831d35Sstevel
98003831d35Sstevel /*
98103831d35Sstevel * to minimise data transfer, the SC maintains
98203831d35Sstevel * just 3 values per fru - except for
98303831d35Sstevel * the chassis itself at the end which has
98403831d35Sstevel * MAX_LEDS_PER_FRU
98503831d35Sstevel */
98603831d35Sstevel lw8_set_led.offset = (i * 3) + j;
98703831d35Sstevel retval = lw8_lomcmd(LW8_MBOX_SET_LED,
98803831d35Sstevel (intptr_t)&lw8_set_led);
98903831d35Sstevel if (retval != 0)
99003831d35Sstevel return (retval);
99103831d35Sstevel mutex_enter(&lw8_event_mutex);
99203831d35Sstevel led_state_cached = B_FALSE;
99303831d35Sstevel mutex_exit(&lw8_event_mutex);
99403831d35Sstevel return (0);
99503831d35Sstevel }
99603831d35Sstevel }
99703831d35Sstevel return (EINVAL);
99803831d35Sstevel }
99903831d35Sstevel
100003831d35Sstevel /*
100103831d35Sstevel * read led value from cache if possible, otherwise read from sc and
100203831d35Sstevel * update the cache
100303831d35Sstevel */
100403831d35Sstevel static int
lw8_getled(lom_get_led_t * get_ledp)100503831d35Sstevel lw8_getled(lom_get_led_t *get_ledp)
100603831d35Sstevel {
100703831d35Sstevel int retval;
100803831d35Sstevel int i, j, k;
100903831d35Sstevel struct led_info *lip;
101003831d35Sstevel lw8_get_led_payload_t lw8_get_led;
101103831d35Sstevel
101203831d35Sstevel for (i = 0; i < MAX_FRUS; i++) {
101303831d35Sstevel if (strncmp(get_ledp->location, fru_led_table[i].location,
101403831d35Sstevel MAX_LOCATION_LEN) != 0)
101503831d35Sstevel continue;
101603831d35Sstevel if (get_ledp->id[0] == '\0') {
101703831d35Sstevel (void) strncpy(get_ledp->next_id,
101803831d35Sstevel fru_led_table[i].led_info[0].id, MAX_ID_LEN);
101903831d35Sstevel return (0);
102003831d35Sstevel }
102103831d35Sstevel for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
102203831d35Sstevel lip = &fru_led_table[i].led_info[j];
102303831d35Sstevel if (lip->id == NULL)
102403831d35Sstevel continue;
102503831d35Sstevel if (strncmp(get_ledp->id, lip->id, MAX_ID_LEN) != 0)
102603831d35Sstevel continue;
102703831d35Sstevel mutex_enter(&lw8_event_mutex);
102803831d35Sstevel if (!led_state_cached) {
102903831d35Sstevel mutex_exit(&lw8_event_mutex);
103003831d35Sstevel retval = lw8_lomcmd(LW8_MBOX_GET_LED,
103103831d35Sstevel (intptr_t)&lw8_get_led);
103203831d35Sstevel if (retval != 0)
103303831d35Sstevel return (retval);
103403831d35Sstevel mutex_enter(&lw8_event_mutex);
103503831d35Sstevel
103603831d35Sstevel /*
103703831d35Sstevel * to minimise data transfer, the
103803831d35Sstevel * lw8_get_led_payload_t structure just has 3
103903831d35Sstevel * values per fru - except for the chassis
104003831d35Sstevel * itself at the end which has MAX_LEDS_PER_FRU
104103831d35Sstevel */
104203831d35Sstevel for (k = 0; k < (MAX_FRUS - 1) * 3; k++) {
104303831d35Sstevel fru_led_table[k / 3].led_info[k % 3].
104403831d35Sstevel status = lw8_get_led.value[k];
104503831d35Sstevel }
104603831d35Sstevel for (k = 0; k < MAX_LEDS_PER_FRU; k++) {
104703831d35Sstevel fru_led_table[MAX_FRUS - 1].led_info[k].
104803831d35Sstevel status = lw8_get_led.value[k +
104903831d35Sstevel ((MAX_FRUS - 1) * 3)];
105003831d35Sstevel }
105103831d35Sstevel led_state_cached = B_TRUE;
105203831d35Sstevel }
105303831d35Sstevel get_ledp->status = lip->status;
105403831d35Sstevel mutex_exit(&lw8_event_mutex);
105503831d35Sstevel get_ledp->position = lip->position;
105603831d35Sstevel (void) strncpy(get_ledp->color, lip->color,
105703831d35Sstevel MAX_COLOR_LEN);
105803831d35Sstevel if (j == MAX_LEDS_PER_FRU - 1) {
105903831d35Sstevel get_ledp->next_id[0] = '\0';
106003831d35Sstevel return (0);
106103831d35Sstevel }
106203831d35Sstevel (void) strncpy(get_ledp->next_id,
106303831d35Sstevel fru_led_table[i].led_info[j + 1].id, MAX_ID_LEN);
106403831d35Sstevel return (0);
106503831d35Sstevel }
106603831d35Sstevel }
106703831d35Sstevel if (get_ledp->id[0] == '\0') {
106803831d35Sstevel get_ledp->next_id[0] = '\0';
106903831d35Sstevel return (0);
107003831d35Sstevel }
107103831d35Sstevel return (EINVAL);
107203831d35Sstevel }
107303831d35Sstevel
107403831d35Sstevel /*ARGSUSED*/
107503831d35Sstevel static int
lw8_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)107603831d35Sstevel lw8_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
107703831d35Sstevel int *rval_p)
107803831d35Sstevel {
107903831d35Sstevel int instance = getminor(dev);
108003831d35Sstevel lom2_info_t lw8_info2;
108103831d35Sstevel lom_ctl_t lw8_ctl;
108203831d35Sstevel lom_ctl2_t lw8_ctl2;
108303831d35Sstevel lom_mprog_t lw8_mprog;
108403831d35Sstevel lom_fled_info_t lw8_fled_info;
108503831d35Sstevel lom_info_t lw8_info;
108603831d35Sstevel lom_aldata_t lw8_aldata;
108703831d35Sstevel lom_get_led_t lw8_get_led;
108803831d35Sstevel lom_set_led_t lw8_set_led;
108903831d35Sstevel lom_prog_t *lw8_progp;
109003831d35Sstevel lom_eventlog2_t *lw8_eventlogp;
109103831d35Sstevel lom_eventresp_t *lw8_eventresp;
109203831d35Sstevel int retval = 0;
109303831d35Sstevel int i, j;
109403831d35Sstevel
109503831d35Sstevel if (instance != 0)
109603831d35Sstevel return (ENXIO);
109703831d35Sstevel
109803831d35Sstevel switch (cmd) {
109903831d35Sstevel case LOMIOCWTMON:
110003831d35Sstevel mutex_enter(&lw8_event_mutex);
110103831d35Sstevel if (!lw8_event_pending) {
110203831d35Sstevel if (cv_wait_sig(&lw8_event_cv, &lw8_event_mutex) == 0) {
110303831d35Sstevel mutex_exit(&lw8_event_mutex);
110403831d35Sstevel retval = EINTR;
110503831d35Sstevel break;
110603831d35Sstevel }
110703831d35Sstevel }
110803831d35Sstevel lw8_event_pending = B_FALSE;
110903831d35Sstevel mutex_exit(&lw8_event_mutex);
111003831d35Sstevel break;
111103831d35Sstevel case LOMIOCMREAD:
111203831d35Sstevel bzero((caddr_t)&lw8_mprog, sizeof (lw8_mprog));
111303831d35Sstevel lw8_mprog.config = 4;
111403831d35Sstevel if (ddi_copyout((caddr_t)&lw8_mprog, (caddr_t)arg,
111503831d35Sstevel sizeof (lw8_mprog), mode) != 0) {
111603831d35Sstevel retval = EFAULT;
111703831d35Sstevel }
111803831d35Sstevel break;
111903831d35Sstevel case LOMIOCCTL2:
112003831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl2,
112103831d35Sstevel sizeof (lw8_ctl2), mode) != 0) {
112203831d35Sstevel retval = EFAULT;
112303831d35Sstevel break;
112403831d35Sstevel }
112503831d35Sstevel retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
112603831d35Sstevel break;
112703831d35Sstevel case LOMIOCPROG:
112803831d35Sstevel lw8_progp = kmem_alloc(sizeof (*lw8_progp), KM_SLEEP);
112903831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_progp,
113003831d35Sstevel sizeof (*lw8_progp), mode) != 0) {
113103831d35Sstevel kmem_free(lw8_progp, sizeof (*lw8_progp));
113203831d35Sstevel retval = EFAULT;
113303831d35Sstevel break;
113403831d35Sstevel }
113503831d35Sstevel retval = lw8_lomcmd(LW8_MBOX_UPDATE_FW, (intptr_t)lw8_progp);
113603831d35Sstevel kmem_free(lw8_progp, sizeof (*lw8_progp));
113703831d35Sstevel break;
113803831d35Sstevel case LOMIOCINFO2:
113903831d35Sstevel bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
114003831d35Sstevel retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
114103831d35Sstevel if (retval != 0)
114203831d35Sstevel break;
114303831d35Sstevel if (ddi_copyout((caddr_t)&lw8_info2, (caddr_t)arg,
114403831d35Sstevel sizeof (lw8_info2), mode) != 0) {
114503831d35Sstevel retval = EFAULT;
114603831d35Sstevel }
114703831d35Sstevel break;
114803831d35Sstevel case LOMIOCINFO:
114903831d35Sstevel bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
115003831d35Sstevel retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
115103831d35Sstevel if (retval != 0)
115203831d35Sstevel break;
115303831d35Sstevel bzero((caddr_t)&lw8_info, sizeof (lw8_info));
115403831d35Sstevel lw8_info.ser_char = lw8_info2.escape_chars[0];
115503831d35Sstevel lw8_info.fver = lw8_info2.fver;
115603831d35Sstevel lw8_info.fchksum = lw8_info2.fchksum;
115703831d35Sstevel lw8_info.prod_rev = lw8_info2.prod_rev;
1158*07d06da5SSurya Prakki (void) strncpy(lw8_info.prod_id, lw8_info2.prod_id, MAX_ID_LEN);
115903831d35Sstevel if (ddi_copyout((caddr_t)&lw8_info, (caddr_t)arg,
116003831d35Sstevel sizeof (lw8_info), mode) != 0) {
116103831d35Sstevel retval = EFAULT;
116203831d35Sstevel }
116303831d35Sstevel break;
116403831d35Sstevel case LOMIOCFLEDSTATE:
116503831d35Sstevel bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
116603831d35Sstevel (void) strncpy(lw8_get_led.location, "chassis",
116703831d35Sstevel MAX_LOCATION_LEN);
116803831d35Sstevel (void) strncpy(lw8_get_led.id, "fault", MAX_ID_LEN);
116903831d35Sstevel retval = lw8_getled(&lw8_get_led);
117003831d35Sstevel if (retval != 0)
117103831d35Sstevel break;
117203831d35Sstevel lw8_fled_info.on = lw8_get_led.status;
117303831d35Sstevel if (ddi_copyout((caddr_t)&lw8_fled_info, (caddr_t)arg,
117403831d35Sstevel sizeof (lw8_fled_info), mode) != 0) {
117503831d35Sstevel retval = EFAULT;
117603831d35Sstevel }
117703831d35Sstevel break;
117803831d35Sstevel case LOMIOCALSTATE:
117903831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
118003831d35Sstevel sizeof (lw8_aldata), mode) != 0) {
118103831d35Sstevel retval = EFAULT;
118203831d35Sstevel break;
118303831d35Sstevel }
118403831d35Sstevel bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
118503831d35Sstevel (void) strncpy(lw8_get_led.location, "chassis",
118603831d35Sstevel MAX_LOCATION_LEN);
118703831d35Sstevel if (lw8_aldata.alarm_no == 3)
118803831d35Sstevel (void) snprintf(lw8_get_led.id, MAX_ID_LEN, "system");
118903831d35Sstevel else
119003831d35Sstevel (void) snprintf(lw8_get_led.id, MAX_ID_LEN, "alarm%d",
119103831d35Sstevel lw8_aldata.alarm_no);
119203831d35Sstevel retval = lw8_getled(&lw8_get_led);
119303831d35Sstevel if (retval != 0)
119403831d35Sstevel break;
119503831d35Sstevel lw8_aldata.state = lw8_get_led.status;
119603831d35Sstevel if (ddi_copyout((caddr_t)&lw8_aldata, (caddr_t)arg,
119703831d35Sstevel sizeof (lw8_aldata), mode) != 0) {
119803831d35Sstevel retval = EFAULT;
119903831d35Sstevel }
120003831d35Sstevel break;
120103831d35Sstevel case LOMIOCGETLED:
120203831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_get_led,
120303831d35Sstevel sizeof (lw8_get_led), mode) != 0) {
120403831d35Sstevel retval = EFAULT;
120503831d35Sstevel break;
120603831d35Sstevel }
120703831d35Sstevel retval = lw8_getled(&lw8_get_led);
120803831d35Sstevel if (retval != 0)
120903831d35Sstevel break;
121003831d35Sstevel if (ddi_copyout((caddr_t)&lw8_get_led, (caddr_t)arg,
121103831d35Sstevel sizeof (lw8_get_led), mode) != 0) {
121203831d35Sstevel retval = EFAULT;
121303831d35Sstevel }
121403831d35Sstevel break;
121503831d35Sstevel case LOMIOCEVENTLOG2:
121603831d35Sstevel lw8_eventlogp = kmem_alloc(sizeof (*lw8_eventlogp), KM_SLEEP);
121703831d35Sstevel lw8_eventresp = kmem_zalloc(sizeof (*lw8_eventresp), KM_SLEEP);
121803831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_eventlogp,
121903831d35Sstevel sizeof (*lw8_eventlogp), mode) != 0) {
122003831d35Sstevel kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
122103831d35Sstevel kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
122203831d35Sstevel retval = EFAULT;
122303831d35Sstevel break;
122403831d35Sstevel }
122503831d35Sstevel lw8_eventresp->num = lw8_eventlogp->num;
122603831d35Sstevel lw8_eventresp->level = lw8_eventlogp->level;
122703831d35Sstevel retval = lw8_lomcmd(LW8_MBOX_GET_EVENTS,
122803831d35Sstevel (intptr_t)lw8_eventresp);
122903831d35Sstevel if (retval == 0) {
123003831d35Sstevel lw8_eventlogp->num = lw8_eventresp->num;
123103831d35Sstevel for (i = 0; i < lw8_eventresp->num; i++) {
123203831d35Sstevel for (j = 0; j < MAX_EVENT_STR; j++) {
123303831d35Sstevel lw8_eventlogp->string[i][j] =
123403831d35Sstevel lw8_eventresp->string[i][j];
123503831d35Sstevel }
123603831d35Sstevel }
123703831d35Sstevel if (ddi_copyout((caddr_t)lw8_eventlogp, (caddr_t)arg,
123803831d35Sstevel sizeof (*lw8_eventlogp), mode) != 0) {
123903831d35Sstevel retval = EFAULT;
124003831d35Sstevel }
124103831d35Sstevel }
124203831d35Sstevel kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
124303831d35Sstevel kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
124403831d35Sstevel break;
124503831d35Sstevel case LOMIOCALCTL:
124603831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
124703831d35Sstevel sizeof (lw8_aldata), mode) != 0) {
124803831d35Sstevel retval = EFAULT;
124903831d35Sstevel break;
125003831d35Sstevel }
125103831d35Sstevel bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
125203831d35Sstevel (void) strncpy(lw8_set_led.location, "chassis",
125303831d35Sstevel MAX_LOCATION_LEN);
125403831d35Sstevel if (lw8_aldata.alarm_no == 3)
125503831d35Sstevel (void) snprintf(lw8_set_led.id, MAX_ID_LEN, "system");
125603831d35Sstevel else
125703831d35Sstevel (void) snprintf(lw8_set_led.id, MAX_ID_LEN, "alarm%d",
125803831d35Sstevel lw8_aldata.alarm_no);
125903831d35Sstevel lw8_set_led.status = lw8_aldata.state;
126003831d35Sstevel retval = lw8_setled(&lw8_set_led);
126103831d35Sstevel break;
126203831d35Sstevel case LOMIOCSETLED:
126303831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_set_led,
126403831d35Sstevel sizeof (lw8_set_led), mode) != 0) {
126503831d35Sstevel retval = EFAULT;
126603831d35Sstevel break;
126703831d35Sstevel }
126803831d35Sstevel retval = lw8_setled(&lw8_set_led);
126903831d35Sstevel break;
127003831d35Sstevel case LOMIOCCTL:
127103831d35Sstevel /*
127203831d35Sstevel * for this ioctl, as well as setting the fault led in the
127303831d35Sstevel * LOMIOCCTL case in lw8_lomcmd(), we also need to set the
127403831d35Sstevel * escape character. To do this we must use LW8_MBOX_SET_CTL,
127503831d35Sstevel * but this also needs the serial_event value which we have
127603831d35Sstevel * to get via LW8_MBOX_GET_INFO
127703831d35Sstevel */
127803831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl,
127903831d35Sstevel sizeof (lw8_ctl), mode) != 0) {
128003831d35Sstevel retval = EFAULT;
128103831d35Sstevel break;
128203831d35Sstevel }
128303831d35Sstevel bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
128403831d35Sstevel retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
128503831d35Sstevel if (retval != 0)
128603831d35Sstevel break;
128703831d35Sstevel bzero((caddr_t)&lw8_ctl2, sizeof (lw8_ctl2));
128803831d35Sstevel lw8_ctl2.escape_chars[0] = lw8_ctl.ser_char;
128903831d35Sstevel lw8_ctl2.serial_events = lw8_info2.serial_events;
129003831d35Sstevel retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
129103831d35Sstevel if (retval != 0)
129203831d35Sstevel break;
129303831d35Sstevel
129403831d35Sstevel /*
129503831d35Sstevel * if fault_led != 0, then set the led
129603831d35Sstevel */
129703831d35Sstevel if (lw8_ctl.fault_led == 0)
129803831d35Sstevel break;
129903831d35Sstevel bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
130003831d35Sstevel (void) strncpy(lw8_set_led.location, "chassis",
130103831d35Sstevel MAX_LOCATION_LEN);
130203831d35Sstevel (void) strncpy(lw8_set_led.id, "fault", MAX_ID_LEN);
130303831d35Sstevel lw8_set_led.status = lw8_ctl.fault_led - 1;
130403831d35Sstevel retval = lw8_setled(&lw8_set_led);
130503831d35Sstevel break;
130603831d35Sstevel default:
130703831d35Sstevel retval = ENOTSUP;
130803831d35Sstevel break;
130903831d35Sstevel }
131003831d35Sstevel return (retval);
131103831d35Sstevel }
131203831d35Sstevel
131303831d35Sstevel /* ARGSUSED */
131403831d35Sstevel static void
lw8_logger(caddr_t arg)131503831d35Sstevel lw8_logger(caddr_t arg)
131603831d35Sstevel {
131703831d35Sstevel callb_cpr_t cprinfo;
131803831d35Sstevel lw8_logmsg_t *lw8_logmsgp;
131903831d35Sstevel boolean_t more_waiting;
132003831d35Sstevel char level;
132103831d35Sstevel int retval;
132203831d35Sstevel
132303831d35Sstevel CALLB_CPR_INIT(&cprinfo, &lw8_logger_lock, callb_generic_cpr,
132403831d35Sstevel "lw8_logger");
132503831d35Sstevel
132603831d35Sstevel lw8_logmsgp = kmem_zalloc(sizeof (*lw8_logmsgp), KM_SLEEP);
132703831d35Sstevel mutex_enter(&lw8_logger_lock);
132803831d35Sstevel for (;;) {
132903831d35Sstevel
133003831d35Sstevel /*
133103831d35Sstevel * Wait for someone to tell me to continue.
133203831d35Sstevel */
133303831d35Sstevel while (lw8_logger_sig == LW8_LOGGER_WAIT) {
133403831d35Sstevel CALLB_CPR_SAFE_BEGIN(&cprinfo);
133503831d35Sstevel cv_wait(&lw8_logger_sig_cv, &lw8_logger_lock);
133603831d35Sstevel CALLB_CPR_SAFE_END(&cprinfo, &lw8_logger_lock);
133703831d35Sstevel }
133803831d35Sstevel
133903831d35Sstevel /* LW8_LOGGER_EXITNOW implies signal by _detach(). */
134003831d35Sstevel if (lw8_logger_sig == LW8_LOGGER_EXITNOW) {
134103831d35Sstevel lw8_logger_sig = LW8_LOGGER_WAIT;
134203831d35Sstevel
134303831d35Sstevel kmem_free(lw8_logmsgp, sizeof (*lw8_logmsgp));
134403831d35Sstevel
134503831d35Sstevel /* lw8_logger_lock is held at this point! */
134603831d35Sstevel CALLB_CPR_EXIT(&cprinfo);
134703831d35Sstevel
134803831d35Sstevel thread_exit();
134903831d35Sstevel /* NOTREACHED */
135003831d35Sstevel }
135103831d35Sstevel
135203831d35Sstevel ASSERT(lw8_logger_sig == LW8_LOGGER_PROCESSNOW);
135303831d35Sstevel lw8_logger_sig = LW8_LOGGER_WAIT;
135403831d35Sstevel
135503831d35Sstevel mutex_exit(&lw8_logger_lock);
135603831d35Sstevel
135703831d35Sstevel /* Do lw8_event logging */
135803831d35Sstevel
135903831d35Sstevel /*
136003831d35Sstevel * Get one message per iteration. We do not sleep if
136103831d35Sstevel * there are more to process. This makes exit from the
136203831d35Sstevel * routine much more reliable.
136303831d35Sstevel */
136403831d35Sstevel more_waiting = B_FALSE;
136503831d35Sstevel
136603831d35Sstevel retval = lw8_lomcmd(LW8_MBOX_GET_NEXT_MSG,
136703831d35Sstevel (intptr_t)lw8_logmsgp);
136803831d35Sstevel if (retval == 0) {
136903831d35Sstevel if (lw8_logmsgp->msg_valid) {
137003831d35Sstevel
137103831d35Sstevel switch (lw8_logmsgp->level) {
137203831d35Sstevel case 0: /* LOG_EMERG */
137303831d35Sstevel level = SL_FATAL;
137403831d35Sstevel break;
137503831d35Sstevel case 1: /* LOG_ALERT */
137603831d35Sstevel level = SL_FATAL;
137703831d35Sstevel break;
137803831d35Sstevel case 2: /* LOG_CRIT */
137903831d35Sstevel level = SL_FATAL;
138003831d35Sstevel break;
138103831d35Sstevel case 3: /* LOG_ERR */
138203831d35Sstevel level = SL_ERROR;
138303831d35Sstevel break;
138403831d35Sstevel case 4: /* LOG_WARNING */
138503831d35Sstevel level = SL_WARN;
138603831d35Sstevel break;
138703831d35Sstevel case 5: /* LOG_NOTICE */
138803831d35Sstevel level = SL_NOTE;
138903831d35Sstevel break;
139003831d35Sstevel case 6: /* LOG_INFO */
139103831d35Sstevel level = SL_NOTE;
139203831d35Sstevel break;
139303831d35Sstevel case 7: /* LOG_DEBUG */
139403831d35Sstevel level = SL_TRACE;
139503831d35Sstevel break;
139603831d35Sstevel default: /* unknown */
139703831d35Sstevel level = SL_NOTE;
139803831d35Sstevel break;
139903831d35Sstevel }
140003831d35Sstevel
140103831d35Sstevel /* Ensure NUL termination */
140203831d35Sstevel lw8_logmsgp->msg[
140303831d35Sstevel sizeof (lw8_logmsgp->msg) - 1] = '\0';
1404*07d06da5SSurya Prakki (void) strlog(0, 0, 0, SL_CONSOLE | level,
140503831d35Sstevel lw8_logmsgp->msg);
140603831d35Sstevel }
140703831d35Sstevel
140803831d35Sstevel if (lw8_logmsgp->num_remaining > 0)
140903831d35Sstevel more_waiting = B_TRUE;
141003831d35Sstevel }
141103831d35Sstevel
141203831d35Sstevel /*
141303831d35Sstevel * Re-enter the lock to prepare for another iteration.
141403831d35Sstevel * We must have the lock here to protect lw8_logger_sig.
141503831d35Sstevel */
141603831d35Sstevel mutex_enter(&lw8_logger_lock);
141703831d35Sstevel if ((lw8_logger_sig == LW8_LOGGER_WAIT) && more_waiting)
141803831d35Sstevel /* We need to get more events */
141903831d35Sstevel lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
142003831d35Sstevel }
142103831d35Sstevel }
142203831d35Sstevel
142303831d35Sstevel static void
lw8_logger_start(void)142403831d35Sstevel lw8_logger_start(void)
142503831d35Sstevel {
142603831d35Sstevel kthread_t *tp;
142703831d35Sstevel
142803831d35Sstevel mutex_enter(&lw8_logger_lock);
142903831d35Sstevel
143003831d35Sstevel if (lw8_logger_tid == 0) {
143103831d35Sstevel /* Force retrieval of any pending messages */
143203831d35Sstevel lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
143303831d35Sstevel
143403831d35Sstevel tp = thread_create(NULL, 0, lw8_logger, NULL, 0,
143503831d35Sstevel &p0, TS_RUN, maxclsyspri);
143603831d35Sstevel lw8_logger_tid = tp->t_did;
143703831d35Sstevel }
143803831d35Sstevel
143903831d35Sstevel mutex_exit(&lw8_logger_lock);
144003831d35Sstevel }
144103831d35Sstevel
144203831d35Sstevel static void
lw8_logger_destroy(void)144303831d35Sstevel lw8_logger_destroy(void)
144403831d35Sstevel {
144503831d35Sstevel kt_did_t tid;
144603831d35Sstevel
144703831d35Sstevel mutex_enter(&lw8_logger_lock);
144803831d35Sstevel tid = lw8_logger_tid;
144903831d35Sstevel if (tid != 0) {
145003831d35Sstevel lw8_logger_sig = LW8_LOGGER_EXITNOW;
145103831d35Sstevel cv_signal(&lw8_logger_sig_cv);
145203831d35Sstevel lw8_logger_tid = 0;
145303831d35Sstevel }
145403831d35Sstevel mutex_exit(&lw8_logger_lock);
145503831d35Sstevel
145603831d35Sstevel /*
145703831d35Sstevel * Wait for lw8_logger() to finish.
145803831d35Sstevel */
145903831d35Sstevel if (tid != 0)
146003831d35Sstevel thread_join(tid);
146103831d35Sstevel }
146203831d35Sstevel
146303831d35Sstevel static void
lw8_logger_wakeup(void)146403831d35Sstevel lw8_logger_wakeup(void)
146503831d35Sstevel {
146603831d35Sstevel mutex_enter(&lw8_logger_lock);
146703831d35Sstevel
146803831d35Sstevel if (lw8_logger_sig != LW8_LOGGER_EXITNOW)
146903831d35Sstevel lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
147003831d35Sstevel cv_signal(&lw8_logger_sig_cv);
147103831d35Sstevel
147203831d35Sstevel mutex_exit(&lw8_logger_lock);
147303831d35Sstevel }
1474