piclfrudr.c revision 8326d453818c9fb78ac1670cf49d26fa4da15003
/*
* 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 <stdio.h>
#include <stddef.h>
#include <syslog.h>
#include <strings.h>
#include <unistd.h>
#include <libintl.h>
#include <stdlib.h>
#include <ctype.h>
#include <picl.h>
#include <picltree.h>
#include <picld_pluginutil.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/sysevent/dr.h>
#include <pthread.h>
#include <libdevinfo.h>
#include <limits.h>
#include <sys/systeminfo.h>
#include <sys/envmon.h>
#include <i2c_gpio.h>
#include "libdevice.h"
#include "picldefs.h"
#include <sys/raidioctl.h>
#include <sys/param.h>
#include <sys/epic.h>
/*
* Plugin registration entry points
*/
static void piclfrudr_register(void);
static void piclfrudr_init(void);
static void piclfrudr_fini(void);
static void rmc_state_event(void);
static void seattle_setleds(void);
static void boston_set_frontleds(const char *, int);
static void boston_set_rearleds(const char *, int);
#pragma init(piclfrudr_register)
static picld_plugin_reg_t my_reg_info = {
PICLD_PLUGIN_VERSION_1,
PICLD_PLUGIN_CRITICAL,
"SUNW_MPXU_frudr",
piclfrudr_init,
piclfrudr_fini,
};
/*
* Log message texts
*/
#define EM_THREAD_CREATE_FAILED gettext("piclfrudr: pthread_create failed: %s")
#define DELETE_PROP_FAIL gettext("ptree_delete_prop failed: %d")
#define EM_DI_INIT_FAIL gettext("piclfrudr: di_init failed: %s")
#define PROPINFO_FAIL gettext("ptree_init_propinfo %s failed: %d")
#define ADD_NODE_FAIL gettext("ptree_create_and_add_node %s failed: %d")
#define ADD_TBL_ENTRY_FAIL gettext("piclfrudr: cannot add entry to table")
#define EM_POLL_FAIL gettext("piclfrudr: poll() failed: %s")
#define ADD_PROP_FAIL gettext("ptree_create_and_add_prop %s failed: %d")
#define EM_MUTEX_FAIL gettext("piclfrudr: pthread_mutex_lock returned: %s")
#define EM_UNK_FRU gettext("piclfrudr: Fru removed event for unknown node")
#define PARSE_CONF_FAIL gettext("parse config file %s failed")
#define EM_NO_SC_DEV gettext("piclfrudr: failed to locate SC device node")
#define EM_NO_SYSINFO gettext("piclfrudr: failed to get SC sysinfo: %s")
/*
* PICL property values
*/
#define PICL_PROPVAL_ON "ON"
#define PICL_PROPVAL_OFF "OFF"
/*
* Local defines
*/
#define SEEPROM_DRIVER_NAME "seeprom"
#define FRUTREE_PATH "/frutree"
#define CHASSIS_LOC_PATH "/frutree/chassis/%s"
#define SYS_BOARD_PATH "/frutree/chassis/MB/system-board/%s"
#define SEATTLE1U_HDDBP_PATH \
"/frutree/chassis/MB/system-board/HDDBP/disk-backplane-1/%s"
#define SEATTLE2U_HDDBP_PATH \
"/frutree/chassis/MB/system-board/HDDBP/disk-backplane-3/%s"
#define BOSTON_HDDBP_PATH \
"/frutree/chassis/MB/system-board/HDDCNTRL/disk-controller/HDDBP" \
"/disk-backplane-8/%s"
#define CONFFILE_PREFIX "fru_"
#define CONFFILE_SUFFIX ".conf"
#define CONFFILE_FRUTREE "piclfrutree.conf"
#define PS_NAME "PS"
#define PS_NAME_LEN 2
#define PS_FRU_NAME "power-supply"
#define PS_PLATFORM_NAME "power-supply-fru-prom"
#define DISK_NAME "HDD"
#define DISK_NAME_LEN 3
#define DISK_FRU_NAME "disk"
#define SCC_NAME "SCC"
#define SCC_NAME_LEN 3
#define SCC_FRU_NAME "scc"
#define RMC_NAME "SC"
#define RMC_NAME_LEN 2
#define RMC_FRU_NAME "sc"
#define FT_NAME "FT"
#define FT_NAME_LEN 2
#define FT_FRU_NAME "fan-tray"
#define FT_ID_BUFSZ (FT_NAME_LEN + 2)
#define DEV_PREFIX "/devices"
#define ENXS_FRONT_SRVC_LED 0x20
#define ENXS_FRONT_ACT_LED 0x10
#define ENXS_REAR_SRVC_LED 0x20
#define ENXS_REAR_ACT_LED 0x10
#define ENTS_SRVC_LED 0x20
#define ENTS_ACT_LED 0x10
#define V440_SRVC_LED 0x2
#define V440_ACT_LED 0x1
#define BOSTON_FRONT_SRVC_LED 0x2
#define BOSTON_FRONT_ACT_LED 0x4
#define BOSTON_FRONT_CLEAR_DIR 0x0
#define BOSTON_FRONT_CLEAR_POL 0x0
#define BOSTON_FRONT_LED_MASK 0xffffffff
#define BOSTON_REAR_SRVC_LED 0x8000
#define BOSTON_REAR_ACT_LED 0x2000
#define BOSTON_REAR_CLEAR_POL 0x0000
#define BOSTON_REAR_LED_MASK 0xe000
/*
* PSU defines
*/
#define PSU_I2C_BUS_DEV "/devices/pci@1e,600000/isa@7/i2c@0,320:devctl"
#define PSU_DEV \
"/devices/pci@1e,600000/isa@7/i2c@0,320/power-supply-fru-prom@0,%x"
#define PSU_PLATFORM "/platform/pci@1e,600000/isa@7/i2c@0,320"
#define PS0_ADDR ((sys_platform == PLAT_CHALUPA19) ? 0xc0 : 0xb0)
#define PS1_ADDR ((sys_platform == PLAT_CHALUPA19) ? 0xc2 : 0xa4)
#define PS2_ADDR 0x70
#define PS3_ADDR 0x72
#define PS0_UNITADDR ((sys_platform == PLAT_CHALUPA19) ? "0,c0" : "0,b0")
#define PS1_UNITADDR ((sys_platform == PLAT_CHALUPA19) ? "0,c2" : "0,a4")
#define PS2_UNITADDR "0,70"
#define PS3_UNITADDR "0,72"
#define PS0_NAME "PS0"
#define PS1_NAME "PS1"
#define PS2_NAME "PS2"
#define PS3_NAME "PS3"
#define PSU0_NAME "PSU0"
#define PSU1_NAME "PSU1"
#define PSU2_NAME "PSU2"
#define PSU3_NAME "PSU3"
#define PS_DEVICE_NAME "power-supply-fru-prom"
#define PSU_COMPATIBLE "i2c-at24c64"
/*
* Seattle/Boston PSU defines
*/
#define SEATTLE_PSU_I2C_BUS_DEV "/devices/i2c@1f,530000:devctl"
#define SEATTLE_PSU_DEV \
"/devices/i2c@1f,530000/power-supply-fru-prom@0,%x"
#define SEATTLE_PSU_PLATFORM "/platform/i2c@1f,530000"
#define SEATTLE_PS0_ADDR 0x6c
#define SEATTLE_PS1_ADDR 0x6e
#define SEATTLE_PS0_UNITADDR "0,6c"
#define SEATTLE_PS1_UNITADDR "0,6e"
#define SEATTLE_PSU_COMPATIBLE "i2c-at34c02"
#define BOSTON_PSU_I2C_BUS_DEV "/devices/i2c@1f,520000:devctl"
#define BOSTON_PSU_DEV \
"/devices/i2c@1f,520000/power-supply-fru-prom@0,%x"
#define BOSTON_PSU_PLATFORM "/platform/i2c@1f,520000"
#define BOSTON_PS0_ADDR 0x24
#define BOSTON_PS1_ADDR 0x32
#define BOSTON_PS2_ADDR 0x52
#define BOSTON_PS3_ADDR 0x72
#define BOSTON_PS0_UNITADDR "0,24"
#define BOSTON_PS1_UNITADDR "0,32"
#define BOSTON_PS2_UNITADDR "0,52"
#define BOSTON_PS3_UNITADDR "0,72"
#define BOSTON_PSU_COMPATIBLE "i2c-at34c02"
/*
* Seattle fan-tray paths
*/
#define SEATTLE_FCB0_1U \
"/frutree/chassis/MB/system-board/FIOB/front-io-board-1" \
"/FCB0/fan-connector-board/%s"
#define SEATTLE_FCB1_1U \
"/frutree/chassis/MB/system-board/FIOB/front-io-board-1" \
"/FCB1/fan-connector-board/%s"
#define SEATTLE_PDB_1U \
"/frutree/chassis/PDB/power-distribution-board/%s"
#define SEATTLE_FCB0_2U \
"/frutree/chassis/MB/system-board/FIOB/front-io-board-2" \
"/FCB0/fan-connector-board/%s"
#define SEATTLE_FCB1_2U \
"/frutree/chassis/MB/system-board/FIOB/front-io-board-2" \
"/FCB1/fan-connector-board/%s"
#define SEATTLE_PDB_2U \
"/frutree/chassis/PDB/power-distribution-board" \
"/HDDFB/fan-connector-board/%s"
/*
* disk defines
*/
#define REMOK_LED "OK2RM"
#define FAULT_LED "SERVICE"
#define PLATFORMLEN 9
#define N_DISKS 8
#define N_CHALUPA_DISKS 4
#define N_ENTS_DISKS 8
#define N_MPXU_DISKS 4
#define N_EN19_DISKS 2
#define DISK_POLL_TIME 5000
/* For V440 RAID policy */
#define V440_DISK_DEVCTL "/devices/pci@1f,700000/scsi@2:devctl"
/*
* Seattle/Boston disk defines
*/
#define N_SEATTLE1U_DISKS 2
#define N_SEATTLE2U_DISKS 4
#define N_BOSTON_DISKS 8
#define SEATTLE_DISK_DEVCTL \
"/devices/pci@1e,600000/pci@0/pci@a/pci@0/pci@8/scsi@1:devctl"
#define BOSTON_DISK_DEVCTL_1068X \
"/devices/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1:devctl"
#define BOSTON_DISK_DEVCTL_1068E \
"/devices/pci@1e,600000/pci@0/pci@2/scsi@0:devctl"
/*
* led defines
*/
#define ENXS_LED_DIR "/devices/pci@1e,600000/isa@7/i2c@0,320/"
#define ENXS_FRONT_LEDS "gpio@0,70:"
#define ENXS_REAR_LEDS ENXS_LED_DIR "gpio@0,44:port_1"
#define ENTS_LED_DIR "/devices/pci@1e,600000/isa@7/i2c@0,320/"
#define ENTS_LEDS "gpio@0,70:"
#define V440_LED_DIR "/devices/pci@1e,600000/isa@7/i2c@0,320/"
#define V440_LED_PATH V440_LED_DIR "gpio@0,48:port_0"
/*
* Seattle/Boston led defines
*/
#define SEATTLE_LED_DEV "/devices/ebus@1f,464000/env-monitor@3,0:env-monitor0"
#define BOSTON_LED_DIR "/devices/i2c@1f,520000/"
#define BOSTON_FRONT_LED_PATH BOSTON_LED_DIR "gpio@0,3a:port_0"
#define BOSTON_REAR_LED_PATH BOSTON_LED_DIR "hardware-monitor@0,5c:adm1026"
/*
* Seattle/Boston USB defines
*/
#define MAX_USB_PORTS 4
#define USB_CONF_FILE_NAME "usb-a-"
typedef struct id_props {
envmon_handle_t envhandle;
picl_prophdl_t volprop;
} id_props_t;
typedef struct idp_lkup {
int maxnum; /* entries in array */
int num; /* entries in use */
id_props_t idp[1];
} idp_lkup_t;
/*
* table for mapping RMC handles to volatile property handles
*/
static idp_lkup_t *idprop = NULL;
/*
* path names to system-controller device and fault led gpio
*/
static char *sc_device_name = NULL;
static char *bezel_leds = NULL;
/*
* disk data
*/
static int disk_ready[N_DISKS];
static char *disk_name[N_DISKS] = { "HDD0", "HDD1", "HDD2", "HDD3",
"HDD4", "HDD5", "HDD6", "HDD7" };
static volatile boolean_t disk_leds_thread_ack = B_FALSE;
static volatile boolean_t disk_leds_thread_running = B_FALSE;
static pthread_t ledsthr_tid;
static pthread_attr_t ledsthr_attr;
static boolean_t ledsthr_created = B_FALSE;
static boolean_t g_mutex_init = B_FALSE;
static pthread_cond_t g_cv;
static pthread_cond_t g_cv_ack;
static pthread_mutex_t g_mutex;
static volatile boolean_t g_finish_now = B_FALSE;
/*
* Boston platform-specific flag which tells us if we are using
* a LSI 1068X disk controller (0) or a LSI 1068E (1).
*/
static int boston_1068e_flag = 0;
/*
* static strings
*/
static const char str_devfs_path[] = "devfs-path";
/*
* OperationalStatus property values
*/
static const char str_opst_present[] = "present";
static const char str_opst_ok[] = "okay";
static const char str_opst_faulty[] = "faulty";
static const char str_opst_download[] = "download";
static const char str_opst_unknown[] = "unknown";
static size_t max_opst_len = sizeof (str_opst_download);
/*
* forward reference
*/
static void opst_init(void);
static void add_op_status_by_name(const char *name, const char *child_name,
picl_prophdl_t *prophdl_p);
static void add_op_status_to_node(picl_nodehdl_t nodeh,
picl_prophdl_t *prophdl_p);
static int read_vol_data(ptree_rarg_t *r_arg, void *buf);
static int find_picl_handle(picl_prophdl_t proph);
static void disk_leds_init(void);
static void disk_leds_fini(void);
static void *disk_leds_thread(void *args);
static picl_nodehdl_t find_child_by_name(picl_nodehdl_t parh, char *name);
static void post_frudr_event(char *ename, picl_nodehdl_t parenth,
picl_nodehdl_t fruh);
static int add_prop_ref(picl_nodehdl_t nodeh, picl_nodehdl_t value, char *name);
static void remove_fru_parents(picl_nodehdl_t fruh);
static int get_node_by_class(picl_nodehdl_t nodeh, const char *classname,
picl_nodehdl_t *foundnodeh);
static int get_sys_controller_node(picl_nodehdl_t *nodeh);
static char *create_sys_controller_pathname(picl_nodehdl_t sysconh);
static char *create_bezel_leds_pathname(const char *dirpath,
const char *devname);
static void frudr_evhandler(const char *ename, const void *earg,
size_t size, void *cookie);
static void fru_add_handler(const char *ename, const void *earg,
size_t size, void *cookie);
static void frutree_evhandler(const char *ename, const void *earg,
size_t size, void *cookie);
static int create_table(picl_nodehdl_t fruhdl, picl_prophdl_t *tblhdlp,
char *tbl_name);
static int create_table_entry(picl_prophdl_t tblhdl,
picl_nodehdl_t refhdl, char *class);
static int create_i2c_node(char *ap_id);
static void delete_i2c_node(char *ap_id);
static int set_led(char *name, char *ptr, char *value);
static int ps_name_to_addr(char *name);
static char *ps_name_to_unitaddr(char *name);
static char *ps_apid_to_nodename(char *apid);
static void add_op_status(envmon_hpu_t *hpu, int *index);
static void get_fantray_path(char *ap_id, char *path, int bufsz);
#define sprintf_buf2(buf, a1, a2) (void) snprintf(buf, sizeof (buf), a1, a2)
/*
* Because this plugin is shared across different platforms, we need to
* distinguish for certain functionality
*/
#define PLAT_UNKNOWN (-1)
#define PLAT_ENXS 0
#define PLAT_ENTS 1
#define PLAT_CHALUPA 2
#define PLAT_EN19 3
#define PLAT_CHALUPA19 4
#define PLAT_SALSA19 5
#define PLAT_SEATTLE1U 6
#define PLAT_SEATTLE2U 7
#define PLAT_BOSTON 8
static int sys_platform;
static void
get_platform()
{
char platform[64];
(void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
if (strcmp(platform, "SUNW,Sun-Fire-V250") == 0)
sys_platform = PLAT_ENTS;
else if (strcmp(platform, "SUNW,Sun-Fire-V440") == 0)
sys_platform = PLAT_CHALUPA;
else if (strcmp(platform, "SUNW,Sun-Fire-V210") == 0)
sys_platform = PLAT_ENXS;
else if (strcmp(platform, "SUNW,Sun-Fire-V240") == 0)
sys_platform = PLAT_ENXS;
else if (strcmp(platform, "SUNW,Netra-240") == 0)
sys_platform = PLAT_EN19;
else if (strcmp(platform, "SUNW,Netra-210") == 0)
sys_platform = PLAT_SALSA19;
else if (strcmp(platform, "SUNW,Netra-440") == 0)
sys_platform = PLAT_CHALUPA19;
else if (strcmp(platform, "SUNW,Sun-Fire-V215") == 0)
sys_platform = PLAT_SEATTLE1U;
else if (strcmp(platform, "SUNW,Sun-Fire-V245") == 0)
sys_platform = PLAT_SEATTLE2U;
else if (strcmp(platform, "SUNW,Sun-Fire-V445") == 0)
sys_platform = PLAT_BOSTON;
else
sys_platform = PLAT_UNKNOWN;
}
/*
* This function is executed as part of .init when the plugin is
* dlopen()ed
*/
static void
piclfrudr_register(void)
{
(void) picld_plugin_register(&my_reg_info);
}
/*
* This function is the init entry point of the plugin.
* It initializes the /frutree tree
*/
static void
piclfrudr_init(void)
{
picl_nodehdl_t sc_nodeh;
picl_nodehdl_t locationh;
picl_nodehdl_t childh;
char namebuf[PATH_MAX];
get_platform();
if (sc_device_name != NULL) {
free(sc_device_name); /* must have reen restarted */
sc_device_name = NULL;
}
if ((get_sys_controller_node(&sc_nodeh) != PICL_SUCCESS) ||
((sc_device_name = create_sys_controller_pathname(sc_nodeh)) ==
NULL))
syslog(LOG_ERR, EM_NO_SC_DEV);
opst_init();
disk_leds_init();
(void) ptree_register_handler(PICLEVENT_DR_AP_STATE_CHANGE,
frudr_evhandler, NULL);
(void) ptree_register_handler(PICL_FRU_ADDED, fru_add_handler, NULL);
(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
frutree_evhandler, NULL);
/*
* There is a window of opportunity for the RMC to deliver an event
* indicating a newly operable state just before we are listening for
* it. In this case, envmon will have missed setting up /platform
* and won't get a signal from frudr. So send it a PICL_FRU_ADDED just
* in case.
*/
if ((sys_platform == PLAT_CHALUPA) ||
(sys_platform == PLAT_CHALUPA19)) {
sprintf_buf2(namebuf, CHASSIS_LOC_PATH, RMC_NAME);
} else {
sprintf_buf2(namebuf, SYS_BOARD_PATH, RMC_NAME);
}
if (ptree_get_node_by_path(namebuf, &locationh) != PICL_SUCCESS)
return;
if (ptree_get_propval_by_name(locationh, PICL_PROP_CHILD,
&childh, sizeof (picl_nodehdl_t)) != PICL_SUCCESS)
return;
post_frudr_event(PICL_FRU_ADDED, locationh, childh);
}
static void
add_op_status_by_name(const char *name, const char *child_name,
picl_prophdl_t *prophdl_p)
{
picl_nodehdl_t nodeh;
if (ptree_get_node_by_path(name, &nodeh) != PICL_SUCCESS) {
return;
}
if (ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD,
&nodeh, sizeof (picl_nodehdl_t)) != PICL_SUCCESS) {
if (child_name == NULL)
return;
/*
* create fru node of supplied name
*/
if (ptree_create_and_add_node(nodeh, child_name,
PICL_CLASS_FRU, &nodeh) != PICL_SUCCESS)
return;
}
add_op_status_to_node(nodeh, prophdl_p);
}
/*
* function to add a volatile property to a specified node
*/
static void
add_op_status_to_node(picl_nodehdl_t nodeh, picl_prophdl_t *prophdl_p)
{
int err;
ptree_propinfo_t info;
picl_prophdl_t proph;
err = ptree_init_propinfo(&info, PTREE_PROPINFO_VERSION,
PICL_PTYPE_CHARSTRING, PICL_VOLATILE | PICL_READ, max_opst_len,
PICL_PROP_OPERATIONAL_STATUS, read_vol_data, NULL);
if (err == PICL_SUCCESS) {
if (ptree_get_prop_by_name(nodeh, PICL_PROP_OPERATIONAL_STATUS,
&proph) == PICL_SUCCESS) {
if (ptree_delete_prop(proph) == PICL_SUCCESS)
err = ptree_destroy_prop(proph);
}
}
if ((err != PICL_SUCCESS) || ((err = ptree_create_and_add_prop(nodeh,
&info, NULL, prophdl_p)) != PICL_SUCCESS)) {
syslog(LOG_ERR, ADD_PROP_FAIL, PICL_PROP_OPERATIONAL_STATUS,
err);
return;
}
}
/*
* Deliver volatile property value.
* prtpicl gets very upset if we fail this command, so if the property
* cannot be retrieved, return a status of unknown.
*/
static int
read_vol_data(ptree_rarg_t *r_arg, void *buf)
{
picl_prophdl_t proph;
int index;
int envmon_fd;
int err;
envmon_hpu_t data;
proph = r_arg->proph;
index = find_picl_handle(proph);
if (index < 0) {
/*
* We drop memory of PSU op status handles in opst_init()
* when we get an RMC faulty event. We cannot access the
* status info in this circumstance, so returning "unknown"
* is appropriate.
*/
(void) strlcpy(buf, str_opst_unknown, max_opst_len);
return (PICL_SUCCESS);
}
envmon_fd = open(sc_device_name, O_RDONLY);
if (envmon_fd < 0) {
/*
* To get this far we must have succeeded with an earlier
* open, so this is an unlikely failure. It would be more
* helpful to indicate the nature of the failure, but we
* don't have the space to say much. Just return "unknown".
*/
(void) strlcpy(buf, str_opst_unknown, max_opst_len);
return (PICL_SUCCESS);
}
data.id = idprop->idp[index].envhandle;
err = ioctl(envmon_fd, ENVMONIOCHPU, &data);
if (err < 0) {
/*
* If we can't read the stats, "unknown" is a reasonable
* status to return. This one really shouldn't happen.
*/
(void) strlcpy(buf, str_opst_unknown, max_opst_len);
(void) close(envmon_fd);
return (PICL_SUCCESS);
}
(void) close(envmon_fd);
if (strncmp(data.id.name, DISK_NAME, DISK_NAME_LEN) == 0 &&
data.fru_status == ENVMON_FRU_PRESENT) {
(void) strlcpy(buf, str_opst_present, max_opst_len);
return (PICL_SUCCESS);
}
if (data.sensor_status != ENVMON_SENSOR_OK) {
(void) strlcpy(buf, str_opst_unknown, max_opst_len);
return (PICL_SUCCESS);
}
(void) strlcpy(buf,
data.fru_status == ENVMON_FRU_PRESENT ? str_opst_ok :
data.fru_status == ENVMON_FRU_DOWNLOAD ? str_opst_download :
data.fru_status == ENVMON_FRU_FAULT ? str_opst_faulty :
str_opst_unknown, max_opst_len);
return (PICL_SUCCESS);
}
/*
* Function for explicitly turning on system leds
* for a failed/degraded RMC (SC).
*/
static void
solaris_setleds(const char *led_path, int leds)
{
i2c_gpio_t gpio;
int fd = open(led_path, O_RDWR);
if (fd < 0)
return;
gpio.reg_val = (leds ^ 0xff);
gpio.reg_mask = 0xffffffff;
if (ioctl(fd, GPIO_SET_CONFIG, &gpio) == 0) {
gpio.reg_val = (leds ^ 0xff);
gpio.reg_mask = 0xffffffff;
(void) ioctl(fd, GPIO_SET_OUTPUT, &gpio);
}
(void) close(fd);
}
/*
* Function for explicitly turning on system leds
* for a failed/degraded RMC (SC) on Seattle
*/
static void
seattle_setleds(void)
{
int fd;
fd = open(SEATTLE_LED_DEV, O_RDWR);
if (fd < 0)
return;
ioctl(fd, EPIC_SET_POWER_LED, (char *)0);
ioctl(fd, EPIC_SET_ALERT_LED, (char *)0);
(void) close(fd);
}
/*
* Function for explicitly turning on the front system leds
* for a failed/degraded RMC (SC) on Boston
*/
static void
boston_set_frontleds(const char *led_path, int leds)
{
i2c_gpio_t gpio;
int fd = open(led_path, O_RDWR);
if (fd < 0) {
return;
}
/* first clear the polarity */
gpio.reg_val = BOSTON_FRONT_CLEAR_POL;
gpio.reg_mask = BOSTON_FRONT_LED_MASK;
if (ioctl(fd, GPIO_SET_POLARITY, &gpio) < 0) {
(void) close(fd);
return;
}
/* now clear the direction */
gpio.reg_val = BOSTON_FRONT_CLEAR_DIR;
gpio.reg_mask = BOSTON_FRONT_LED_MASK;
if (ioctl(fd, GPIO_SET_CONFIG, &gpio) < 0) {
(void) close(fd);
return;
}
/* and light the leds */
gpio.reg_val = leds;
gpio.reg_mask = BOSTON_FRONT_LED_MASK;
ioctl(fd, GPIO_SET_OUTPUT, &gpio);
(void) close(fd);
}
/*
* Function for explicitly turning on the rear system leds
* for a failed/degraded RMC (SC) on Boston
*/
static void
boston_set_rearleds(const char *led_path, int leds)
{
i2c_gpio_t gpio;
int fd = open(led_path, O_RDWR);
if (fd < 0) {
return;
}
/* first clear the polarity */
gpio.reg_val = BOSTON_REAR_CLEAR_POL;
gpio.reg_mask = BOSTON_REAR_LED_MASK;
if (ioctl(fd, GPIO_SET_POLARITY, &gpio) < 0) {
(void) close(fd);
return;
}
/* now set the direction */
gpio.reg_val = BOSTON_REAR_LED_MASK;
gpio.reg_mask = BOSTON_REAR_LED_MASK;
if (ioctl(fd, GPIO_SET_CONFIG, &gpio) < 0) {
(void) close(fd);
return;
}
/* and light the leds */
gpio.reg_val = leds;
gpio.reg_mask = BOSTON_REAR_LED_MASK;
ioctl(fd, GPIO_SET_OUTPUT, &gpio);
(void) close(fd);
}
static void
rmc_state_event(void)
{
envmon_hpu_t hpu;
int res;
int fd = open(sc_device_name, O_RDONLY);
if (fd < 0)
return;
(void) strlcpy(hpu.id.name, RMC_NAME, sizeof (hpu.id.name));
res = ioctl(fd, ENVMONIOCHPU, &hpu);
(void) close(fd);
if ((res == 0) && (hpu.sensor_status == ENVMON_SENSOR_OK) &&
((hpu.fru_status & ENVMON_FRU_FAULT) != 0)) {
/*
* SC failed event - light the service led
* note that as Solaris is still running,
* the Solaris active led should be lit too.
*/
switch (sys_platform) {
case PLAT_ENXS:
case PLAT_SALSA19:
case PLAT_EN19:
solaris_setleds(ENXS_REAR_LEDS,
ENXS_REAR_SRVC_LED | ENXS_REAR_ACT_LED);
/*
* the device name for the bezel leds GPIO device
* tends to vary from Unix to Unix. Search for it.
*/
if (bezel_leds == NULL) {
bezel_leds =
create_bezel_leds_pathname(ENXS_LED_DIR,
ENXS_FRONT_LEDS);
}
if (bezel_leds == NULL)
return;
solaris_setleds(bezel_leds,
ENXS_FRONT_SRVC_LED | ENXS_FRONT_ACT_LED);
break;
case PLAT_ENTS:
/*
* the device name for the system leds gpio can vary
* as there are several similar gpio devices. Search
* for one with the desired address.
*/
if (bezel_leds == NULL) {
bezel_leds =
create_bezel_leds_pathname(ENTS_LED_DIR,
ENTS_LEDS);
}
if (bezel_leds == NULL)
return;
solaris_setleds(bezel_leds,
ENTS_SRVC_LED | ENTS_ACT_LED);
break;
case PLAT_CHALUPA:
case PLAT_CHALUPA19:
solaris_setleds(V440_LED_PATH,
V440_SRVC_LED | V440_ACT_LED);
break;
case PLAT_BOSTON:
/* set front leds */
boston_set_frontleds(BOSTON_FRONT_LED_PATH,
BOSTON_FRONT_SRVC_LED | BOSTON_FRONT_ACT_LED);
/* and then the rear leds */
boston_set_rearleds(BOSTON_REAR_LED_PATH,
BOSTON_REAR_SRVC_LED | BOSTON_REAR_ACT_LED);
break;
case PLAT_SEATTLE1U:
case PLAT_SEATTLE2U:
seattle_setleds();
break;
default:
break;
}
}
}
static int
find_picl_handle(picl_prophdl_t proph)
{
int index;
if (idprop == NULL)
return (-1);
for (index = 0; index < idprop->num; index++) {
if (idprop->idp[index].volprop == proph)
return (index);
}
return (-1);
}
static int
find_vol_prop_by_name(const char *name)
{
int index;
if (idprop == NULL)
return (-1);
for (index = 0; index < idprop->num; index++) {
if (strcmp(idprop->idp[index].envhandle.name, name) == 0)
return (index);
}
return (-1);
}
/*
* This function is the fini entry point of the plugin.
*/
static void
piclfrudr_fini(void)
{
(void) ptree_unregister_handler(PICLEVENT_DR_AP_STATE_CHANGE,
frudr_evhandler, NULL);
(void) ptree_unregister_handler(PICL_FRU_ADDED, fru_add_handler,
NULL);
disk_leds_fini();
if (idprop != NULL) {
free(idprop);
idprop = NULL;
}
if (sc_device_name != NULL) {
free(sc_device_name);
sc_device_name = NULL;
}
}
/*
* subroutine for various functions. Finds immediate child of parh with
* requested name if present. Otherwise returns NULL.
*/
static picl_nodehdl_t
find_child_by_name(picl_nodehdl_t parh, char *name)
{
picl_nodehdl_t nodeh;
int err;
char nodename[PICL_PROPNAMELEN_MAX];
err = ptree_get_propval_by_name(parh, PICL_PROP_CHILD,
&nodeh, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS)
return (NULL);
for (;;) {
err = ptree_get_propval_by_name(nodeh, PICL_PROP_NAME, nodename,
sizeof (nodename));
if (err != PICL_SUCCESS)
return (NULL);
if (strcmp(name, nodename) == 0) {
return (nodeh);
}
err = ptree_get_propval_by_name(nodeh, PICL_PROP_PEER,
&nodeh, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS)
return (NULL);
}
}
/* Creates a reference property for a given PICL node */
static int
add_prop_ref(picl_nodehdl_t nodeh, picl_nodehdl_t value, char *name)
{
picl_prophdl_t proph;
ptree_propinfo_t propinfo;
int err;
err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
PICL_PTYPE_REFERENCE, PICL_READ, sizeof (picl_nodehdl_t), name,
NULL, NULL);
if (err != PICL_SUCCESS) {
syslog(LOG_ERR, PROPINFO_FAIL, name, err);
return (err);
}
err = ptree_create_and_add_prop(nodeh, &propinfo, &value, &proph);
if (err != PICL_SUCCESS) {
syslog(LOG_ERR, ADD_PROP_FAIL, name, err);
return (err);
}
return (PICL_SUCCESS);
}
/* create an entry in the specified table */
static int
create_table_entry(picl_prophdl_t tblhdl, picl_nodehdl_t refhdl, char *class)
{
int err;
ptree_propinfo_t prop;
picl_prophdl_t prophdl[2];
/* first column is class */
prop.version = PTREE_PROPINFO_VERSION;
prop.piclinfo.type = PICL_PTYPE_CHARSTRING;
prop.piclinfo.accessmode = PICL_READ;
prop.piclinfo.size = PICL_CLASSNAMELEN_MAX;
prop.read = NULL;
prop.write = NULL;
(void) strlcpy(prop.piclinfo.name, PICL_PROP_CLASS,
sizeof (prop.piclinfo.name));
err = ptree_create_prop(&prop, class, &prophdl[0]);
if (err != PICL_SUCCESS) {
syslog(LOG_ERR, ADD_TBL_ENTRY_FAIL, err);
return (err);
}
/* second column is reference property */
prop.version = PTREE_PROPINFO_VERSION;
prop.piclinfo.type = PICL_PTYPE_REFERENCE;
prop.piclinfo.accessmode = PICL_READ;
prop.piclinfo.size = sizeof (picl_nodehdl_t);
prop.read = NULL;
prop.write = NULL;
sprintf_buf2(prop.piclinfo.name, "_%s_", class);
err = ptree_create_prop(&prop, &refhdl, &prophdl[1]);
if (err != PICL_SUCCESS) {
syslog(LOG_ERR, ADD_TBL_ENTRY_FAIL, err);
return (err);
}
/* add row to table */
err = ptree_add_row_to_table(tblhdl, 2, prophdl);
if (err != PICL_SUCCESS)
syslog(LOG_ERR, ADD_TBL_ENTRY_FAIL, err);
return (err);
}
/* create an empty table property */
static int
create_table(picl_nodehdl_t fruhdl, picl_prophdl_t *tblhdlp, char *tbl_name)
{
int err;
ptree_propinfo_t prop;
picl_prophdl_t tblprophdl;
err = ptree_create_table(tblhdlp);
if (err != PICL_SUCCESS) {
syslog(LOG_ERR, ADD_PROP_FAIL, tbl_name, err);
return (err);
}
prop.version = PTREE_PROPINFO_VERSION;
prop.piclinfo.type = PICL_PTYPE_TABLE;
prop.piclinfo.accessmode = PICL_READ;
prop.piclinfo.size = sizeof (picl_prophdl_t);
prop.read = NULL;
prop.write = NULL;
(void) strlcpy(prop.piclinfo.name, tbl_name,
sizeof (prop.piclinfo.name));
err = ptree_create_and_add_prop(fruhdl, &prop, tblhdlp, &tblprophdl);
if (err != PICL_SUCCESS)
syslog(LOG_ERR, ADD_PROP_FAIL, tbl_name, err);
return (err);
}
/*
* The size of outfilename must be PATH_MAX
*/
static int
get_config_file(char *outfilename, char *fru)
{
char nmbuf[SYS_NMLN];
char pname[PATH_MAX];
int dir;
for (dir = 0; dir < 2; dir++) {
if (sysinfo(dir == 0 ? SI_PLATFORM : SI_MACHINE,
nmbuf, sizeof (nmbuf)) == -1) {
continue;
}
(void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf);
(void) strlcat(pname, CONFFILE_PREFIX, PATH_MAX);
(void) strlcat(pname, fru, PATH_MAX);
(void) strlcat(pname, CONFFILE_SUFFIX, PATH_MAX);
if (access(pname, R_OK) == 0) {
(void) strlcpy(outfilename, pname, PATH_MAX);
return (0);
}
}
(void) snprintf(pname, PATH_MAX, "%s/%s%s%s",
PICLD_COMMON_PLUGIN_DIR, CONFFILE_PREFIX, fru,
CONFFILE_SUFFIX);
if (access(pname, R_OK) == 0) {
(void) strlcpy(outfilename, pname, PATH_MAX);
return (0);
}
return (-1);
}
static void
remove_fru_parents(picl_nodehdl_t fruh)
{
char name[MAXPATHLEN];
int retval;
picl_nodehdl_t nodeh;
picl_prophdl_t tableh;
picl_prophdl_t tblh;
picl_prophdl_t fruph;
picl_nodehdl_t childh;
int seabos_fanfru = 0;
retval = ptree_get_propval_by_name(fruh, PICL_PROP_NAME, name,
sizeof (name));
if (retval != PICL_SUCCESS) {
syslog(LOG_ERR, EM_UNK_FRU);
return;
}
retval = ptree_get_prop_by_name(fruh, PICL_PROP_DEVICES, &tableh);
if (retval != PICL_SUCCESS) {
/*
* No Devices table. However on Seattle and Boston (which
* support fan fru hotplug), the Devices table will be
* found under the child node (Fn) of the fru (fan-tray).
* Therefore, check the first child of the fru for the
* Devices table on these platforms before returning.
*/
switch (sys_platform) {
case PLAT_SEATTLE1U:
case PLAT_SEATTLE2U:
case PLAT_BOSTON:
if (strcmp(name, FT_FRU_NAME) != 0)
return;
retval = ptree_get_propval_by_name(fruh,
PICL_PROP_CHILD, &childh, sizeof (picl_nodehdl_t));
if (retval != PICL_SUCCESS)
return;
retval = ptree_get_prop_by_name(childh,
PICL_PROP_DEVICES, &tableh);
if (retval != PICL_SUCCESS)
return;
seabos_fanfru = 1;
break;
default:
/* nothing to do */
return;
}
}
/*
* follow all reference properties in the second
* column of the table and delete any _fru_parent node
* at the referenced node.
*/
retval = ptree_get_propval(tableh, &tblh, sizeof (tblh));
if (retval != PICL_SUCCESS) {
/* can't get value of table property */
return;
}
/* get first col, first row */
retval = ptree_get_next_by_col(tblh, &tblh);
if (retval != PICL_SUCCESS) {
/* no rows? */
return;
}
/*
* starting at next col, get every entry in the column
*/
for (retval = ptree_get_next_by_row(tblh, &tblh);
retval == PICL_SUCCESS;
retval = ptree_get_next_by_col(tblh, &tblh)) {
/*
* should be a ref prop in our hands,
* get the target node handle
*/
retval = ptree_get_propval(tblh, &nodeh,
sizeof (nodeh));
if (retval != PICL_SUCCESS) {
continue;
}
/*
* got the referenced node, has it got a
* _fru_parent property?
*/
retval = ptree_get_prop_by_name(nodeh,
PICL_REFPROP_FRU_PARENT, &fruph);
if (retval != PICL_SUCCESS) {
/*
* on Boston and Seattle, we should actually be
* looking for the _location_parent property
* for fan frus
*/
if (seabos_fanfru) {
retval = ptree_get_prop_by_name(nodeh,
PICL_REFPROP_LOC_PARENT, &fruph);
}
if (retval != PICL_SUCCESS)
continue;
}
/*
* got a _fru_parent node reference delete it
*/
retval = ptree_delete_prop(fruph);
if (retval != PICL_SUCCESS) {
continue;
}
retval = ptree_destroy_prop(fruph);
if (retval != PICL_SUCCESS) {
continue;
}
}
}
static void
remove_tables(picl_nodehdl_t rootnd)
{
picl_nodehdl_t tableh;
int retval;
retval = ptree_get_prop_by_name(rootnd, PICL_PROP_DEVICES, &tableh);
if (retval == PICL_SUCCESS) {
/*
* found a Devices property, delete it
*/
if ((retval = ptree_delete_prop(tableh)) == PICL_SUCCESS) {
(void) ptree_destroy_prop(tableh);
}
}
/*
* is there a child node?
*/
retval = ptree_get_propval_by_name(rootnd, PICL_PROP_CHILD, &rootnd,
sizeof (rootnd));
while (retval == PICL_SUCCESS) {
remove_tables(rootnd);
/*
* any siblings?
*/
retval = ptree_get_propval_by_name(rootnd, PICL_PROP_PEER,
&rootnd, sizeof (rootnd));
}
}
/*
* Event completion handler for PICL_FRU_ADDED/PICL_FRU_REMOVED events
*/
static void
frudr_completion_handler(char *ename, void *earg, size_t size)
{
picl_nodehdl_t fruh;
picl_nodehdl_t parh;
char nodename[PICL_PROPNAMELEN_MAX];
int err;
if (strcmp(ename, PICL_FRU_REMOVED) == 0) {
/*
* now frudata has been notified that the node is to be
* removed, we can actually remove it
*/
fruh = NULL;
(void) nvlist_lookup_uint64(earg,
PICLEVENTARG_FRUHANDLE, &fruh);
if (fruh != NULL) {
/*
* first find name of the fru
*/
err = ptree_get_propval_by_name(fruh, PICL_PROP_PARENT,
&parh, sizeof (parh));
if (err == PICL_SUCCESS) {
err = ptree_get_propval_by_name(parh,
PICL_PROP_NAME, nodename,
sizeof (nodename));
}
if (err == PICL_SUCCESS) {
/*
* if it was a power supply, delete i2c node
*/
if (strncmp(nodename, PS_NAME,
PS_NAME_LEN) == 0) {
(void) delete_i2c_node(nodename);
}
/*
* is disk node, make thread re-evaluate led
* state
*/
if (strncmp(nodename, DISK_NAME,
DISK_NAME_LEN) == 0) {
disk_ready[nodename[DISK_NAME_LEN] -
'0'] = -1;
}
}
remove_fru_parents(fruh);
/*
* now we can delete the node
*/
err = ptree_delete_node(fruh);
if (err == PICL_SUCCESS) {
(void) ptree_destroy_node(fruh);
} else {
syslog(LOG_ERR, DELETE_PROP_FAIL, err);
}
}
}
free(ename);
nvlist_free(earg);
}
/*
* Post the PICL_FRU_ADDED/PICL_FRU_REMOVED event
*/
static void
post_frudr_event(char *ename, picl_nodehdl_t parenth, picl_nodehdl_t fruh)
{
nvlist_t *nvl;
char *ev_name;
ev_name = strdup(ename);
if (ev_name == NULL)
return;
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, NULL)) {
free(ev_name);
return;
}
if (parenth != 0L &&
nvlist_add_uint64(nvl, PICLEVENTARG_PARENTHANDLE, parenth)) {
free(ev_name);
nvlist_free(nvl);
return;
}
if (fruh != 0L &&
nvlist_add_uint64(nvl, PICLEVENTARG_FRUHANDLE, fruh)) {
free(ev_name);
nvlist_free(nvl);
return;
}
if (ptree_post_event(ev_name, nvl, sizeof (nvl),
frudr_completion_handler) != 0) {
free(ev_name);
nvlist_free(nvl);
}
}
static void
add_ps_to_platform(char *unit)
{
picl_nodehdl_t parent_hdl;
picl_nodehdl_t child_hdl;
ptree_propinfo_t info;
int unit_size = 1 + strlen(unit);
int res;
char unit_addr[PICL_UNITADDR_LEN_MAX];
switch (sys_platform) {
case PLAT_SEATTLE1U:
case PLAT_SEATTLE2U:
res = ptree_get_node_by_path(SEATTLE_PSU_PLATFORM, &parent_hdl);
break;
case PLAT_BOSTON:
res = ptree_get_node_by_path(BOSTON_PSU_PLATFORM, &parent_hdl);
break;
default:
res = ptree_get_node_by_path(PSU_PLATFORM, &parent_hdl);
break;
}
if (res != PICL_SUCCESS)
return;
/*
* seeprom nodes sit below this node,
* is there one with the supplied unit address?
*/
res = ptree_get_propval_by_name(parent_hdl, PICL_PROP_CHILD,
&child_hdl, sizeof (picl_nodehdl_t));
while (res == PICL_SUCCESS) {
res = ptree_get_propval_by_name(child_hdl, PICL_PROP_PEER,
&child_hdl, sizeof (picl_nodehdl_t));
if ((res == PICL_SUCCESS) &&
ptree_get_propval_by_name(child_hdl,
PICL_PROP_UNIT_ADDRESS, unit_addr,
sizeof (unit_addr)) == PICL_SUCCESS) {
unit_addr[sizeof (unit_addr) - 1] = '\0';
if (strcmp(unit_addr, unit) == 0)
return; /* unit address exists already */
}
}
/*
* found platform location for PS seeprom node, create it
*/
if (ptree_create_and_add_node(parent_hdl, PS_PLATFORM_NAME,
PICL_CLASS_SEEPROM, &child_hdl) != PICL_SUCCESS)
return;
if (ptree_init_propinfo(&info, PTREE_PROPINFO_VERSION,
PICL_PTYPE_CHARSTRING, PICL_READ, unit_size,
PICL_PROP_UNIT_ADDRESS, NULL, NULL) != PICL_SUCCESS)
return;
(void) ptree_create_and_add_prop(child_hdl, &info, unit, NULL);
}
static void
get_fantray_path(char *ap_id, char *path, int bufsz)
{
char ft_id[FT_ID_BUFSZ];
(void) strlcpy(ft_id, ap_id, FT_ID_BUFSZ);
switch (sys_platform) {
case PLAT_SEATTLE1U:
if ((strncmp(ap_id, "FT0", 3) == 0) ||
(strncmp(ap_id, "FT1", 3) == 0) ||
(strncmp(ap_id, "FT2", 3) == 0)) {
(void) snprintf(path, bufsz, SEATTLE_FCB0_1U, ft_id);
} else if ((strncmp(ap_id, "FT3", 3) == 0) ||
(strncmp(ap_id, "FT4", 3) == 0) ||
(strncmp(ap_id, "FT5", 3) == 0)) {
(void) snprintf(path, bufsz, SEATTLE_FCB1_1U, ft_id);
} else {
(void) snprintf(path, bufsz, SEATTLE_PDB_1U, ft_id);
}
break;
case PLAT_SEATTLE2U:
if ((strncmp(ap_id, "FT0", 3) == 0) ||
(strncmp(ap_id, "FT1", 3) == 0) ||
(strncmp(ap_id, "FT2", 3) == 0)) {
(void) snprintf(path, bufsz, SEATTLE_FCB0_2U, ft_id);
} else if ((strncmp(ap_id, "FT3", 3) == 0) ||
(strncmp(ap_id, "FT4", 3) == 0) ||
(strncmp(ap_id, "FT5", 3) == 0)) {
(void) snprintf(path, bufsz, SEATTLE_FCB1_2U, ft_id);
} else {
(void) snprintf(path, bufsz, SEATTLE_PDB_2U, ft_id);
}
break;
case PLAT_BOSTON:
(void) snprintf(path, bufsz, SYS_BOARD_PATH, ft_id);
break;
default:
(void) snprintf(path, bufsz, CHASSIS_LOC_PATH, ft_id);
break;
}
}
/*
* handle EC_DR picl events
*/
/*ARGSUSED*/
static void
frudr_evhandler(const char *ename, const void *earg, size_t size, void *cookie)
{
nvlist_t *nvlp;
char *dtype;
char *ap_id;
char *hint;
char path[MAXPATHLEN];
picl_nodehdl_t fruh;
picl_nodehdl_t locnodeh;
int err;
int index;
picl_nodehdl_t childh;
char *fru_name;
boolean_t rmc_flag = B_FALSE;
if (strcmp(ename, PICLEVENT_DR_AP_STATE_CHANGE) != 0) {
return;
}
if (nvlist_unpack((char *)earg, size, &nvlp, NULL)) {
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_DATA_TYPE, &dtype)) {
nvlist_free(nvlp);
return;
}
if (strcmp(dtype, PICLEVENTARG_PICLEVENT_DATA) != 0) {
nvlist_free(nvlp);
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_AP_ID, &ap_id)) {
nvlist_free(nvlp);
return;
}
/*
* check ap_id really is a hot-plug device
*/
if (strncmp(ap_id, PS_NAME, PS_NAME_LEN) == 0) {
fru_name = PS_FRU_NAME;
} else if (strncmp(ap_id, DISK_NAME, DISK_NAME_LEN) == 0) {
fru_name = DISK_FRU_NAME;
} else if (strncmp(ap_id, SCC_NAME, SCC_NAME_LEN) == 0) {
fru_name = SCC_FRU_NAME;
} else if (strncmp(ap_id, RMC_NAME, RMC_NAME_LEN) == 0) {
fru_name = RMC_FRU_NAME;
rmc_flag = B_TRUE;
} else if (strncmp(ap_id, FT_NAME, FT_NAME_LEN) == 0) {
fru_name = FT_FRU_NAME;
} else {
nvlist_free(nvlp);
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_HINT, &hint)) {
nvlist_free(nvlp);
return;
}
/*
* OK - so this is an EC_DR event - let's handle it.
*/
if (rmc_flag && (sys_platform != PLAT_CHALUPA) &&
(sys_platform != PLAT_CHALUPA19))
sprintf_buf2(path, SYS_BOARD_PATH, ap_id);
else {
if ((sys_platform == PLAT_CHALUPA19) &&
(strncmp(ap_id, PS_NAME, PS_NAME_LEN) == 0)) {
sprintf_buf2(path, CHASSIS_LOC_PATH,
ps_apid_to_nodename(ap_id));
} else if (strncmp(ap_id, DISK_NAME, DISK_NAME_LEN) == 0) {
switch (sys_platform) {
case PLAT_SEATTLE1U:
sprintf_buf2(path, SEATTLE1U_HDDBP_PATH, ap_id);
break;
case PLAT_SEATTLE2U:
sprintf_buf2(path, SEATTLE2U_HDDBP_PATH, ap_id);
break;
case PLAT_BOSTON:
sprintf_buf2(path, BOSTON_HDDBP_PATH, ap_id);
break;
default:
sprintf_buf2(path, CHASSIS_LOC_PATH, ap_id);
break;
}
} else if (strncmp(ap_id, FT_NAME, FT_NAME_LEN) == 0) {
get_fantray_path(ap_id, path, MAXPATHLEN);
} else {
sprintf_buf2(path, CHASSIS_LOC_PATH, ap_id);
}
}
if (ptree_get_node_by_path(path, &locnodeh) != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
/*
* now either add or delete the fru node as appropriate. If no
* hint, treat as insert and update the tree if necessary.
*/
if (strcmp(hint, DR_HINT_REMOVE) == 0) {
if (ptree_get_propval_by_name(locnodeh, PICL_PROP_CHILD,
&fruh, sizeof (picl_nodehdl_t)) == PICL_SUCCESS) {
/*
* fru was there - but has gone away
*/
post_frudr_event(PICL_FRU_REMOVED, NULL, fruh);
}
} else if (rmc_flag) {
/*
* An event on the RMC location, just pass it on
* it's not really a PICL_FRU_ADDED event, so offer
* the child handle as well (if it exists).
*/
if (ptree_get_propval_by_name(locnodeh, PICL_PROP_CHILD,
&fruh, sizeof (picl_nodehdl_t)) != PICL_SUCCESS) {
fruh = NULL;
}
post_frudr_event(PICL_FRU_ADDED, locnodeh, fruh);
} else {
/*
* fru has been inserted (or may need to update)
* if node already there, then just return
*/
childh = find_child_by_name(locnodeh, fru_name);
if (childh != NULL) {
nvlist_free(nvlp);
return;
}
/*
* create requested fru node
*/
err = ptree_create_and_add_node(locnodeh, fru_name,
PICL_CLASS_FRU, &childh);
if (err != PICL_SUCCESS) {
syslog(LOG_ERR, ADD_NODE_FAIL, ap_id, err);
nvlist_free(nvlp);
return;
}
/*
* power supplies have operational status and fruid -
* add OperationalStatus property and create i2c device node
* before posting fru_added event
*/
if (strncmp(ap_id, PS_NAME, PS_NAME_LEN) == 0) {
index = find_vol_prop_by_name(
ps_apid_to_nodename(ap_id));
if (index >= 0)
add_op_status_to_node(childh,
&idprop->idp[index].volprop);
(void) create_i2c_node(ap_id);
add_ps_to_platform(ps_name_to_unitaddr(ap_id));
}
/*
* now post event
*/
post_frudr_event(PICL_FRU_ADDED, locnodeh, NULL);
}
nvlist_free(nvlp);
}
/*
* Handle PICL_FRU_ADDED events.
* These events are posted by the frudr_evhandler of this plugin in response to
* PICLEVENT_DR_AP_STATE_CHANGE events. The sequence is as follows:
* 1) frudr_evhandler catches PICLEVENT_DR_AP_STATE_CHANGE and creates a
* child node below the relevant location.
* 2) frudr_evhandler posts a PICL_FRU_ADDED event.
* 3) envmon catches PICL_FRU_ADDED event, gropes the RMC configuration
* and creates platform tree nodes (primarily for PSUs). (If the event
* is for the RMC itself, envmon deletes existing platform nodes and
* rebuilds from scratch.)
* 4) this plugin catches PICL_FRU_ADDED event, looks for a related
* configuration file and parses it. This adds Fru data properties (etc.).
* 5) frudata catches the event and updates its FRUID data cache.
*/
/*ARGSUSED*/
static void
fru_add_handler(const char *ename, const void *earg, size_t size, void *cookie)
{
int retval;
picl_nodehdl_t locnodeh;
picl_nodehdl_t rooth;
char path[MAXPATHLEN];
char *fru_name;
if (strcmp(ename, PICL_FRU_ADDED) != 0)
return;
retval = nvlist_lookup_uint64((nvlist_t *)earg,
PICLEVENTARG_PARENTHANDLE, &locnodeh);
if (retval != PICL_SUCCESS)
return;
retval = ptree_get_propval_by_name(locnodeh, PICL_PROP_NAME,
path, sizeof (path));
if (retval != PICL_SUCCESS)
return;
fru_name = strdup(path);
if (fru_name == NULL)
return;
/*
* We're about to parse a fru-specific .conf file to populate
* picl nodes relating to the dynamically added component. In the
* case of the RMC, there is a problem: all of its /platform tree
* nodes have just been replaced by envmon. It is now necessary to
* repopulate Devices tables in /frutree.
* picld_pluginutil_parse_config_file doesn't handle repopulating
* existing tables, so as a work round, delete all tables found
* under /frutree. This works on Enchilada Server as the tables
* are all created from parsing a .conf file, and we're about to
* redo that action.
*/
if (strcmp(fru_name, RMC_NAME) == 0) {
rmc_state_event();
retval = ptree_get_node_by_path(FRUTREE_PATH, &rooth);
if (retval == PICL_SUCCESS) {
remove_tables(rooth);
}
}
/*
* Re-establish the HPU(FRU) volatile properties.
* This needs to be done before the .conf file is parsed because
* it has a side effect of re-creating any missing power-supply
* fru node. The .conf file can then hang properties beneath.
*/
opst_init();
/*
* see if there's a .conf file for this fru
*/
if (get_config_file(path, fru_name) == 0) {
if ((ptree_get_root(&rooth) != PICL_SUCCESS) ||
(picld_pluginutil_parse_config_file(rooth, path) !=
PICL_SUCCESS)) {
syslog(LOG_ERR, PARSE_CONF_FAIL, path);
}
}
free(fru_name);
}
/*
* Handle PICLEVENT_SYSEVENT_DEVICE_ADDED events.
*/
/*ARGSUSED*/
static void
frutree_evhandler(const char *ename, const void *earg, size_t size,
void *cookie)
{
nvlist_t *nvlp;
picl_nodehdl_t rooth;
char path[MAXPATHLEN];
char *fru_name;
char *dtype;
char *dpath;
char *ptr;
char *ptr2;
int done = B_FALSE;
int i;
if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) != 0)
return;
if (nvlist_unpack((char *)earg, size, &nvlp, NULL))
return;
if (nvlist_lookup_string(nvlp, PICLEVENTARG_DATA_TYPE, &dtype)) {
nvlist_free(nvlp);
return;
}
if (strcmp(dtype, PICLEVENTARG_PICLEVENT_DATA) != 0) {
nvlist_free(nvlp);
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_DEVFS_PATH, &dpath)) {
nvlist_free(nvlp);
return;
}
fru_name = strdup(dpath);
if (fru_name == NULL) {
nvlist_free(nvlp);
return;
}
nvlist_free(nvlp);
/*
* fru_name is of the form
* "/pci@1e,600000/usb@a/mouse@2"
* or
* "/pci@1e,600000/usb@a/device@2/mouse@0"
* reduce it to "usb-a-2"
*/
if ((sys_platform == PLAT_SEATTLE1U) ||
(sys_platform == PLAT_SEATTLE2U) ||
(sys_platform == PLAT_BOSTON)) {
for (i = 0; i < MAX_USB_PORTS; i++) {
sprintf(fru_name, "%s%d", USB_CONF_FILE_NAME, i+1);
if (get_config_file(path, fru_name) == 0) {
if ((ptree_get_root(&rooth) != PICL_SUCCESS) ||
(picld_pluginutil_parse_config_file(rooth,
path) != PICL_SUCCESS)) {
syslog(LOG_ERR, PARSE_CONF_FAIL, path);
}
}
}
} else {
ptr = fru_name;
if (*ptr == '/') {
ptr++;
ptr = strchr(ptr, '/');
if (ptr != NULL) {
ptr++;
(void) memmove(fru_name, ptr, strlen(ptr) + 1);
ptr = strchr(fru_name, '@');
if (ptr != NULL) {
*ptr = '-';
ptr++;
ptr = strchr(ptr, '/');
if (ptr != NULL) {
*ptr = '-';
ptr++;
ptr2 = ptr;
ptr = strchr(ptr, '@');
if (ptr != NULL) {
ptr++;
(void) memmove(ptr2,
ptr, strlen(ptr)+1);
ptr2 = strchr(ptr2,
'/');
if (ptr2 != NULL) {
*ptr2 = '\0';
}
done = B_TRUE;
}
}
}
}
}
if (done == B_FALSE) {
free(fru_name);
return;
}
/*
* see if there's a .conf file for this fru
*/
if (get_config_file(path, fru_name) == 0) {
if ((ptree_get_root(&rooth) != PICL_SUCCESS) ||
(picld_pluginutil_parse_config_file(rooth, path) !=
PICL_SUCCESS)) {
syslog(LOG_ERR, PARSE_CONF_FAIL, path);
}
}
}
free(fru_name);
}
static int
set_led(char *name, char *ptr, char *value)
{
char path[MAXPATHLEN];
picl_prophdl_t proph;
ptree_propinfo_t propinfo;
picl_prophdl_t tableh;
picl_nodehdl_t locnodeh;
picl_nodehdl_t nodeh;
picl_prophdl_t tblh;
int retval;
char *value_ptr;
char label[PICL_PROPNAMELEN_MAX];
char class[PICL_PROPNAMELEN_MAX];
/* find the location node */
switch (sys_platform) {
case PLAT_CHALUPA:
case PLAT_CHALUPA19:
sprintf_buf2(path, CHASSIS_LOC_PATH, name);
break;
case PLAT_SEATTLE1U:
sprintf_buf2(path, SEATTLE1U_HDDBP_PATH, name);
break;
case PLAT_SEATTLE2U:
sprintf_buf2(path, SEATTLE2U_HDDBP_PATH, name);
break;
case PLAT_BOSTON:
sprintf_buf2(path, BOSTON_HDDBP_PATH, name);
break;
default:
sprintf_buf2(path, CHASSIS_LOC_PATH, name);
break;
}
if (ptree_get_node_by_path(path, &locnodeh) != PICL_SUCCESS) {
return (PICL_FAILURE);
}
/*
* if no fru node, then turn led off
*/
if (find_child_by_name(locnodeh, DISK_FRU_NAME) != NULL)
value_ptr = value;
else
value_ptr = PICL_PROPVAL_OFF;
/* get its Devices table */
if (ptree_get_prop_by_name(locnodeh, PICL_PROP_DEVICES, &tableh) !=
PICL_SUCCESS)
return (PICL_FAILURE);
if (ptree_get_propval(tableh, &tblh, sizeof (tblh)) != PICL_SUCCESS)
return (PICL_FAILURE);
/* get first col, first row */
if (ptree_get_next_by_col(tblh, &tblh) != PICL_SUCCESS)
return (PICL_FAILURE);
/*
* starting at next col, get every entry in the column
*/
for (retval = ptree_get_next_by_row(tblh, &tblh);
retval == PICL_SUCCESS;
retval = ptree_get_next_by_col(tblh, &tblh)) {
/*
* get the target node handle
*/
if (ptree_get_propval(tblh, &nodeh, sizeof (nodeh))
!= PICL_SUCCESS)
continue;
/*
* check it's a led
*/
if (ptree_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
class, sizeof (class)) != PICL_SUCCESS)
continue;
if (strcmp(class, "led") != 0)
continue;
/*
* check its the right led
*/
if (ptree_get_propval_by_name(nodeh, PICL_PROP_LABEL,
label, sizeof (label)) != PICL_SUCCESS)
continue;
if (strcmp(label, ptr) == 0) {
/*
* set it
*/
if (ptree_get_prop_by_name(nodeh, PICL_PROP_STATE,
&proph) != PICL_SUCCESS)
continue;
if (ptree_get_propinfo(proph, &propinfo) !=
PICL_SUCCESS)
continue;
retval = ptree_update_propval_by_name(nodeh,
PICL_PROP_STATE, value_ptr, propinfo.piclinfo.size);
return (retval);
}
}
return (PICL_FAILURE);
}
/*
* function to find first node of specified class beneath supplied node
*/
static int
get_node_by_class(picl_nodehdl_t nodeh, const char *classname,
picl_nodehdl_t *foundnodeh)
{
int err;
char clname[PICL_CLASSNAMELEN_MAX+1];
picl_nodehdl_t childh;
/*
* go through the children
*/
err = ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD, &childh,
sizeof (picl_nodehdl_t));
while (err == PICL_SUCCESS) {
err = ptree_get_propval_by_name(childh, PICL_PROP_CLASSNAME,
clname, sizeof (clname));
if ((err == PICL_SUCCESS) && (strcmp(clname, classname) == 0)) {
*foundnodeh = childh;
return (PICL_SUCCESS);
}
err = get_node_by_class(childh, classname, foundnodeh);
if (err == PICL_SUCCESS)
return (PICL_SUCCESS);
err = ptree_get_propval_by_name(childh, PICL_PROP_PEER,
&childh, sizeof (picl_nodehdl_t));
}
return (PICL_NODENOTFOUND);
}
/*
* get system-controller node
*/
static int
get_sys_controller_node(picl_nodehdl_t *nodeh)
{
int err;
/* get platform node */
err = ptree_get_node_by_path(PICL_NODE_ROOT PICL_NODE_PLATFORM, nodeh);
if (err != PICL_SUCCESS)
return (err);
err = get_node_by_class(*nodeh, PICL_CLASS_SERVICE_PROCESSOR, nodeh);
return (err);
}
/*
* create pathname string for system-controller device
*/
static char *
create_sys_controller_pathname(picl_nodehdl_t sysconh)
{
char *ptr;
char namebuf[PATH_MAX];
size_t len;
DIR *dirp;
struct dirent *dp;
struct stat statbuf;
/*
* prefix devfs-path name with /devices
*/
(void) strlcpy(namebuf, DEV_PREFIX, PATH_MAX);
/*
* append devfs-path property
*/
len = strlen(namebuf);
if (ptree_get_propval_by_name(sysconh, str_devfs_path, namebuf + len,
sizeof (namebuf) - len) != PICL_SUCCESS) {
return (NULL);
}
/*
* locate final component of name
*/
ptr = strrchr(namebuf, '/');
if (ptr == NULL)
return (NULL);
*ptr = '\0'; /* terminate at end of directory path */
len = strlen(ptr + 1); /* length of terminal name */
dirp = opendir(namebuf);
if (dirp == NULL) {
return (NULL);
}
*ptr++ = '/'; /* restore '/' and advance to final name */
while ((dp = readdir(dirp)) != NULL) {
/*
* look for a name which starts with the string at *ptr
*/
if (strlen(dp->d_name) < len)
continue; /* skip short names */
if (strncmp(dp->d_name, ptr, len) == 0) {
/*
* Got a match, restore full pathname and stat the
* entry. Reject if not a char device
*/
(void) strlcpy(ptr, dp->d_name,
sizeof (namebuf) - (ptr - namebuf));
if (stat(namebuf, &statbuf) < 0)
continue; /* reject if can't stat it */
if (!S_ISCHR(statbuf.st_mode))
continue; /* not a character device */
/*
* go with this entry
*/
(void) closedir(dirp);
return (strdup(namebuf));
}
}
(void) closedir(dirp);
return (NULL);
}
/*
* create pathname string for bezel leds device
*/
static char *
create_bezel_leds_pathname(const char *dirpath, const char *devname)
{
char namebuf[PATH_MAX];
size_t lendirpath;
size_t len;
DIR *dirp;
struct dirent *dp;
struct stat statbuf;
/*
* start with directory name
*/
(void) strlcpy(namebuf, dirpath, PATH_MAX);
/*
* append devfs-path property
*/
lendirpath = strlen(namebuf);
dirp = opendir(namebuf);
if (dirp == NULL) {
return (NULL);
}
len = strlen(devname);
while ((dp = readdir(dirp)) != NULL) {
/*
* look for a name which starts with the gpio string
*/
if (strlen(dp->d_name) < len)
continue; /* skip short names */
if (strncmp(dp->d_name, devname, len) == 0) {
/*
* Got a match, restore full pathname and stat the
* entry. Reject if not a char device
*/
(void) strlcpy(namebuf + lendirpath, dp->d_name,
sizeof (namebuf) - lendirpath);
if (stat(namebuf, &statbuf) < 0)
continue; /* reject if can't stat it */
if (!S_ISCHR(statbuf.st_mode))
continue; /* not a character device */
/*
* go with this entry
*/
(void) closedir(dirp);
return (strdup(namebuf));
}
}
(void) closedir(dirp);
return (NULL);
}
/*
* initialise structure associated with nodes requiring OperationalStatus
*/
static void
opst_init(void)
{
int res;
int index = 0;
int fd;
int entries = 0;
int err = 0;
boolean_t rmc_flag;
boolean_t ps_flag;
boolean_t disk_flag;
size_t len;
envmon_sysinfo_t sysinfo;
envmon_hpu_t hpu;
if (idprop != NULL) {
/*
* This must be a restart, clean up earlier allocation
*/
free(idprop);
idprop = NULL;
}
if (sc_device_name == NULL)
err = 1;
else {
fd = open(sc_device_name, O_RDONLY);
if (fd < 0) {
syslog(LOG_ERR, EM_NO_SC_DEV);
err = 1;
}
}
if (err == 0) {
res = ioctl(fd, ENVMONIOCSYSINFO, &sysinfo);
if (res < 0) {
syslog(LOG_ERR, EM_NO_SYSINFO, strerror(errno));
(void) close(fd);
err = 1;
}
}
if (err == 0) {
entries = sysinfo.maxHPU;
len = offsetof(idp_lkup_t, idp) + entries * sizeof (id_props_t);
idprop = calloc(len, 1);
if (idprop == NULL) {
(void) close(fd);
err = 1;
}
}
if (err == 0) {
idprop->maxnum = entries;
hpu.id.name[0] = '\0'; /* request for first name */
res = ioctl(fd, ENVMONIOCHPU, &hpu);
/*
* The HPU node for the RMC is a special case. Its handle is
* generated by the rmclomv driver. Rather than building
* knowledge of its frutree hierarchic name into the driver, we
* put that knowledge here.
*/
while ((res == 0) && (index < entries) &&
(hpu.next_id.name[0] != '\0')) {
hpu.id = hpu.next_id;
res = ioctl(fd, ENVMONIOCHPU, &hpu);
if ((res == 0) &&
((hpu.sensor_status & ENVMON_NOT_PRESENT) == 0)) {
add_op_status(&hpu, &index);
}
}
idprop->num = index;
(void) close(fd);
}
}
static void
disk_leds_init(void)
{
int err = 0, i;
if (!g_mutex_init) {
if ((pthread_cond_init(&g_cv, NULL) == 0) &&
(pthread_cond_init(&g_cv_ack, NULL) == 0) &&
(pthread_mutex_init(&g_mutex, NULL) == 0)) {
g_mutex_init = B_TRUE;
} else {
return;
}
}
/*
* Initialise to -1 so the led thread will set correctly.
* Do this before creating the disk_leds thread,
* so there's no race.
*/
for (i = 0; i < N_DISKS; i++)
disk_ready[i] = -1;
if (ledsthr_created) {
/*
* this is a restart, wake up sleeping threads
*/
err = pthread_mutex_lock(&g_mutex);
if (err != 0) {
syslog(LOG_ERR, EM_MUTEX_FAIL, strerror(errno));
return;
}
g_finish_now = B_FALSE;
(void) pthread_cond_broadcast(&g_cv);
(void) pthread_mutex_unlock(&g_mutex);
} else {
if ((pthread_attr_init(&ledsthr_attr) != 0) ||
(pthread_attr_setscope(&ledsthr_attr,
PTHREAD_SCOPE_SYSTEM) != 0))
return;
if ((err = pthread_create(&ledsthr_tid, &ledsthr_attr,
disk_leds_thread, NULL)) != 0) {
syslog(LOG_ERR, EM_THREAD_CREATE_FAILED,
strerror(errno));
return;
}
ledsthr_created = B_TRUE;
}
}
static void
disk_leds_fini(void)
{
int err, i;
/*
* turn the leds off as we'll no longer be monitoring them
*/
for (i = 0; i < N_DISKS; i++)
(void) set_led(disk_name[i], REMOK_LED, PICL_PROPVAL_OFF);
/*
* disk_leds_thread() never started or an error occured so
* that it's not running
*/
if (!disk_leds_thread_running)
return;
/*
* tell led thread to pause
*/
if (!ledsthr_created)
return;
err = pthread_mutex_lock(&g_mutex);
if (err != 0) {
syslog(LOG_ERR, EM_MUTEX_FAIL, strerror(errno));
return;
}
g_finish_now = B_TRUE;
disk_leds_thread_ack = B_FALSE;
(void) pthread_cond_broadcast(&g_cv);
/*
* and wait for them to acknowledge
*/
while (!disk_leds_thread_ack) {
(void) pthread_cond_wait(&g_cv_ack, &g_mutex);
}
(void) pthread_mutex_unlock(&g_mutex);
}
static void
update_disk_node(char *fruname, char *devpath)
{
picl_nodehdl_t slotndh;
picl_nodehdl_t diskndh;
picl_nodehdl_t devhdl;
picl_prophdl_t tblhdl;
picl_prophdl_t tblhdl2;
picl_prophdl_t tblproph;
int err;
char path[MAXPATHLEN];
switch (sys_platform) {
case PLAT_CHALUPA:
case PLAT_CHALUPA19:
sprintf_buf2(path, CHASSIS_LOC_PATH, fruname);
break;
case PLAT_SEATTLE1U:
sprintf_buf2(path, SEATTLE1U_HDDBP_PATH, fruname);
break;
case PLAT_SEATTLE2U:
sprintf_buf2(path, SEATTLE2U_HDDBP_PATH, fruname);
break;
case PLAT_BOSTON:
sprintf_buf2(path, BOSTON_HDDBP_PATH, fruname);
break;
default:
sprintf_buf2(path, CHASSIS_LOC_PATH, fruname);
break;
}
if (ptree_get_node_by_path(path, &slotndh) != PICL_SUCCESS) {
return;
}
diskndh = find_child_by_name(slotndh, DISK_FRU_NAME);
if (diskndh == NULL) {
return;
}
err = ptree_get_node_by_path(devpath, &devhdl);
if (err == PICL_SUCCESS) {
err = ptree_get_propval_by_name(diskndh,
PICL_PROP_DEVICES, &tblhdl, sizeof (tblhdl));
if (err != PICL_SUCCESS)
return;
err = ptree_get_next_by_col(tblhdl, &tblhdl2);
if (err != PICL_SUCCESS) {
err = create_table_entry(tblhdl, devhdl,
PICL_CLASS_BLOCK);
if (err != PICL_SUCCESS)
return;
err = add_prop_ref(devhdl, diskndh,
PICL_REFPROP_FRU_PARENT);
if (err != PICL_SUCCESS)
return;
}
} else {
/*
* no mechanism for deleting row - so delete
* whole table and start again
*/
err = ptree_get_prop_by_name(diskndh, PICL_PROP_DEVICES,
&tblproph);
if (err != PICL_SUCCESS)
return;
err = ptree_delete_prop(tblproph);
if (err != PICL_SUCCESS)
return;
(void) ptree_destroy_prop(tblproph);
err = create_table(diskndh, &tblhdl, PICL_PROP_DEVICES);
if (err != PICL_SUCCESS)
return;
}
}
/*
* We will light the OK2REMOVE LED for disks configured
* into a raid if (and only if) the driver reports
* that the disk has failed.
*/
static int
raid_ok2rem_policy(raid_config_t config, int target)
{
int i;
for (i = 0; i < config.ndisks; i++) {
int d = config.disk[i];
int dstatus = config.diskstatus[i];
if (d == target) {
switch (dstatus) {
case RAID_DISKSTATUS_MISSING:
/* If LED is on, turn it off */
if (disk_ready[d] == B_FALSE) {
if (set_led(disk_name[d], REMOK_LED,
PICL_PROPVAL_OFF) == PICL_SUCCESS) {
disk_ready[d] = B_TRUE;
}
}
break;
case RAID_DISKSTATUS_GOOD:
if (disk_ready[d] != B_TRUE) {
if (set_led(disk_name[d], REMOK_LED,
PICL_PROPVAL_OFF) == PICL_SUCCESS) {
disk_ready[d] = B_TRUE;
}
}
break;
case RAID_DISKSTATUS_FAILED:
if (disk_ready[d] != B_FALSE) {
if (set_led(disk_name[d], REMOK_LED,
PICL_PROPVAL_ON) == PICL_SUCCESS) {
disk_ready[d] = B_FALSE;
}
}
break;
default:
break;
}
return (1);
}
}
return (0);
}
static int
check_raid(int target)
{
raid_config_t config;
int fd;
int numvols;
int i, j;
switch (sys_platform) {
case PLAT_CHALUPA:
case PLAT_CHALUPA19:
fd = open(V440_DISK_DEVCTL, O_RDONLY);
break;
case PLAT_SEATTLE1U:
case PLAT_SEATTLE2U:
fd = open(SEATTLE_DISK_DEVCTL, O_RDONLY);
break;
case PLAT_BOSTON:
if (boston_1068e_flag) {
fd = open(BOSTON_DISK_DEVCTL_1068E, O_RDONLY);
} else {
fd = open(BOSTON_DISK_DEVCTL_1068X, O_RDONLY);
}
break;
default:
fd = -1;
break;
}
if (fd == -1) {
return (0);
}
if (ioctl(fd, RAID_NUMVOLUMES, &numvols)) {
(void) close(fd);
return (0);
}
for (i = 0; i < numvols; i++) {
config.unitid = i;
if (ioctl(fd, RAID_GETCONFIG, &config)) {
(void) close(fd);
return (0);
}
if (raid_ok2rem_policy(config, target)) {
(void) close(fd);
return (1);
}
}
(void) close(fd);
return (0);
}
/*ARGSUSED*/
static void *
disk_leds_thread(void *args)
{
int c;
int i;
char **disk_dev;
int fd;
devctl_hdl_t dhdl;
int n_disks = 0,
do_raid = 0,
err = 0;
uint_t statep = 0;
static char *mpxu_devs[] = {
"/pci@1c,600000/scsi@2/sd@0,0",
"/pci@1c,600000/scsi@2/sd@1,0",
"/pci@1c,600000/scsi@2/sd@2,0",
"/pci@1c,600000/scsi@2/sd@3,0"
};
static char *ents_devs[] = {
"/pci@1d,700000/scsi@4/sd@0,0",
"/pci@1d,700000/scsi@4/sd@1,0",
"/pci@1d,700000/scsi@4/sd@2,0",
"/pci@1d,700000/scsi@4/sd@3,0",
"/pci@1d,700000/scsi@4/sd@8,0",
"/pci@1d,700000/scsi@4/sd@9,0",
"/pci@1d,700000/scsi@4/sd@a,0",
"/pci@1d,700000/scsi@4/sd@b,0"
};
static char *v440_devs[] = {
"/pci@1f,700000/scsi@2/sd@0,0",
"/pci@1f,700000/scsi@2/sd@1,0",
"/pci@1f,700000/scsi@2/sd@2,0",
"/pci@1f,700000/scsi@2/sd@3,0"
};
static char *n210_devs[] = {
"/pci@1c,600000/LSILogic,sas@1/sd@0,0",
"/pci@1c,600000/LSILogic,sas@1/sd@1,0"
};
static char *seattle_devs[] = {
"/pci@1e,600000/pci@0/pci@a/pci@0/pci@8/scsi@1/sd@0,0",
"/pci@1e,600000/pci@0/pci@a/pci@0/pci@8/scsi@1/sd@1,0",
"/pci@1e,600000/pci@0/pci@a/pci@0/pci@8/scsi@1/sd@2,0",
"/pci@1e,600000/pci@0/pci@a/pci@0/pci@8/scsi@1/sd@3,0"
};
static char *boston_devs_1068e[] = {
"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@0,0",
"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@1,0",
"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@2,0",
"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@3,0",
"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@4,0",
"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@5,0",
"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@6,0",
"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@7,0"
};
static char *boston_devs_1068x[] = {
"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@0,0",
"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@1,0",
"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@2,0",
"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@3,0",
"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@4,0",
"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@5,0",
"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@6,0",
"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@7,0"
};
char *ddev[N_DISKS]; /* "/devices" */
char *pdev[N_DISKS]; /* "/platform" */
switch (sys_platform) {
case PLAT_ENTS:
disk_dev = ents_devs;
n_disks = N_ENTS_DISKS;
break;
case PLAT_CHALUPA:
case PLAT_CHALUPA19:
do_raid = 1;
disk_dev = v440_devs;
n_disks = N_CHALUPA_DISKS;
break;
case PLAT_SALSA19:
disk_dev = n210_devs;
n_disks = N_EN19_DISKS;
break;
case PLAT_SEATTLE1U:
case PLAT_SEATTLE2U:
do_raid = 1;
disk_dev = seattle_devs;
n_disks = (sys_platform == PLAT_SEATTLE1U) ?
N_SEATTLE1U_DISKS : N_SEATTLE2U_DISKS;
break;
case PLAT_BOSTON:
/*
* If we can open the devctl path for the built-in 1068E
* controller then assume we're a 1068E-equipped Boston
* and make all the paths appropriate for that hardware.
* Otherwise assume we are a 1068X-equipped Boston and
* make all of the paths appropriate for a 1068X PCI-X
* controller in PCI slot 4.
*
* The flag is also used in the check_raid() function.
*/
if ((fd = open(BOSTON_DISK_DEVCTL_1068E, O_RDONLY)) != -1) {
boston_1068e_flag = 1;
disk_dev = boston_devs_1068e;
} else {
boston_1068e_flag = 0;
disk_dev = boston_devs_1068x;
}
(void) close(fd);
do_raid = 1;
n_disks = N_BOSTON_DISKS;
break;
default: /* PLAT_ENXS/PLAT_EN19 */
disk_dev = mpxu_devs;
n_disks = (sys_platform == PLAT_EN19) ?
N_EN19_DISKS : N_MPXU_DISKS;
}
/*
* make up disk names
*/
for (i = 0; i < n_disks; i++) {
char buffer[MAXPATHLEN];
sprintf(buffer, "/devices%s", disk_dev[i]);
ddev[i] = strdup(buffer);
sprintf(buffer, "/platform%s", disk_dev[i]);
pdev[i] = strdup(buffer);
}
disk_leds_thread_running = B_TRUE;
for (;;) {
for (i = 0; i < n_disks; i++) {
/*
* If it's one of the RAID disks, we have already
* applied the ok2remove policy.
*/
if (do_raid && check_raid(i)) {
continue;
}
dhdl = devctl_device_acquire(ddev[i], 0);
devctl_device_getstate(dhdl, &statep);
devctl_release(dhdl);
if (statep & DEVICE_OFFLINE) {
if (disk_ready[i] != B_FALSE) {
update_disk_node(disk_name[i], pdev[i]);
if (set_led(disk_name[i], REMOK_LED,
PICL_PROPVAL_ON) == PICL_SUCCESS)
disk_ready[i] = B_FALSE;
}
} else if (statep & DEVICE_ONLINE) {
if (disk_ready[i] != B_TRUE) {
update_disk_node(disk_name[i], pdev[i]);
if (set_led(disk_name[i], REMOK_LED,
PICL_PROPVAL_OFF) == PICL_SUCCESS)
disk_ready[i] = B_TRUE;
}
}
}
/*
* wait a bit until we check again
*/
(void) poll(NULL, 0, DISK_POLL_TIME);
/*
* are we to stop?
*/
(void) pthread_mutex_lock(&g_mutex);
while (g_finish_now) {
/*
* notify _fini routine that we've paused
*/
disk_leds_thread_ack = B_TRUE;
(void) pthread_cond_signal(&g_cv_ack);
/*
* and go to sleep in case we get restarted
*/
(void) pthread_cond_wait(&g_cv, &g_mutex);
}
(void) pthread_mutex_unlock(&g_mutex);
}
return ((void *)err);
}
/*
* Given the powersupply name, convert to addr
*/
static int
ps_name_to_addr(char *name)
{
int ps_addr = 0;
if ((strcmp(name, PS0_NAME) == 0) ||
(strcmp(name, PSU0_NAME) == 0)) {
switch (sys_platform) {
case PLAT_SEATTLE1U:
case PLAT_SEATTLE2U:
ps_addr = SEATTLE_PS0_ADDR;
break;
case PLAT_BOSTON:
ps_addr = BOSTON_PS0_ADDR;
break;
default:
ps_addr = PS0_ADDR;
break;
}
} else if ((strcmp(name, PS1_NAME) == 0) ||
(strcmp(name, PSU1_NAME) == 0)) {
switch (sys_platform) {
case PLAT_SEATTLE1U:
case PLAT_SEATTLE2U:
ps_addr = SEATTLE_PS1_ADDR;
break;
case PLAT_BOSTON:
ps_addr = BOSTON_PS1_ADDR;
break;
default:
ps_addr = PS1_ADDR;
break;
}
} else if ((strcmp(name, PS2_NAME) == 0) ||
(strcmp(name, PSU2_NAME) == 0)) {
switch (sys_platform) {
case PLAT_BOSTON:
ps_addr = BOSTON_PS2_ADDR;
break;
default:
ps_addr = PS2_ADDR;
break;
}
} else if ((strcmp(name, PS3_NAME) == 0) ||
(strcmp(name, PSU3_NAME) == 0)) {
switch (sys_platform) {
case PLAT_BOSTON:
ps_addr = BOSTON_PS3_ADDR;
break;
default:
ps_addr = PS3_ADDR;
break;
}
}
return (ps_addr);
}
/*
* Given powersupply name, convert to unit addr
*/
static char *
ps_name_to_unitaddr(char *name)
{
char *unitaddr;
if (strcmp(name, PS0_NAME) == 0) {
switch (sys_platform) {
case PLAT_SEATTLE1U:
case PLAT_SEATTLE2U:
unitaddr = SEATTLE_PS0_UNITADDR;
break;
case PLAT_BOSTON:
unitaddr = BOSTON_PS0_UNITADDR;
break;
default:
unitaddr = PS0_UNITADDR;
break;
}
} else if (strcmp(name, PS1_NAME) == 0) {
switch (sys_platform) {
case PLAT_SEATTLE1U:
case PLAT_SEATTLE2U:
unitaddr = SEATTLE_PS1_UNITADDR;
break;
case PLAT_BOSTON:
unitaddr = BOSTON_PS1_UNITADDR;
break;
default:
unitaddr = PS1_UNITADDR;
break;
}
} else if (strcmp(name, PS2_NAME) == 0) {
switch (sys_platform) {
case PLAT_BOSTON:
unitaddr = BOSTON_PS2_UNITADDR;
break;
default:
unitaddr = PS2_UNITADDR;
break;
}
} else if (strcmp(name, PS3_NAME) == 0) {
switch (sys_platform) {
case PLAT_BOSTON:
unitaddr = BOSTON_PS3_UNITADDR;
break;
default:
unitaddr = PS3_UNITADDR;
break;
}
}
else
unitaddr = NULL;
return (unitaddr);
}
/*
* converts apid to real FRU name in PICL tree. The
* name of powersupply devices on chalupa19 are
* PSU instead of PS
*/
static char *
ps_apid_to_nodename(char *apid)
{
char *nodename;
if (sys_platform != PLAT_CHALUPA19)
return (apid);
if (strcmp(apid, PS0_NAME) == 0)
nodename = PSU0_NAME;
else if (strcmp(apid, PS1_NAME) == 0)
nodename = PSU1_NAME;
else if (strcmp(apid, PS2_NAME) == 0)
nodename = PSU2_NAME;
else if (strcmp(apid, PS3_NAME) == 0)
nodename = PSU3_NAME;
else
nodename = apid;
return (nodename);
}
/*
* Create SEEPROM node at insertion time.
*/
static int
create_i2c_node(char *ap_id)
{
int nd_reg[2];
devctl_ddef_t ddef_hdl;
devctl_hdl_t bus_hdl;
devctl_hdl_t dev_hdl;
char dev_path[MAXPATHLEN];
char *compatible;
/* create seeprom node */
nd_reg[0] = 0;
nd_reg[1] = ps_name_to_addr(ap_id);
switch (sys_platform) {
case PLAT_SEATTLE1U:
case PLAT_SEATTLE2U:
bus_hdl = devctl_bus_acquire(SEATTLE_PSU_I2C_BUS_DEV, 0);
compatible = SEATTLE_PSU_COMPATIBLE;
break;
case PLAT_BOSTON:
bus_hdl = devctl_bus_acquire(BOSTON_PSU_I2C_BUS_DEV, 0);
compatible = BOSTON_PSU_COMPATIBLE;
break;
default:
bus_hdl = devctl_bus_acquire(PSU_I2C_BUS_DEV, 0);
compatible = PSU_COMPATIBLE;
break;
}
if (bus_hdl == NULL)
return (DDI_FAILURE);
/* device definition properties */
ddef_hdl = devctl_ddef_alloc(PS_DEVICE_NAME, 0);
(void) devctl_ddef_string(ddef_hdl, "compatible", compatible);
(void) devctl_ddef_string(ddef_hdl, "device_type", "seeprom");
(void) devctl_ddef_int_array(ddef_hdl, "reg", 2, nd_reg);
/* create the device node */
if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl))
return (DDI_FAILURE);
if (devctl_get_pathname(dev_hdl, dev_path, MAXPATHLEN) == NULL)
return (DDI_FAILURE);
devctl_release(dev_hdl);
devctl_ddef_free(ddef_hdl);
devctl_release(bus_hdl);
return (DDI_SUCCESS);
}
/*
* Delete SEEPROM node at insertion time.
*/
static void
delete_i2c_node(char *ap_id)
{
devctl_hdl_t dev_hdl;
char buf[MAXPATHLEN];
switch (sys_platform) {
case PLAT_SEATTLE1U:
case PLAT_SEATTLE2U:
sprintf_buf2(buf, SEATTLE_PSU_DEV, ps_name_to_addr(ap_id));
break;
case PLAT_BOSTON:
sprintf_buf2(buf, BOSTON_PSU_DEV, ps_name_to_addr(ap_id));
break;
default:
sprintf_buf2(buf, PSU_DEV, ps_name_to_addr(ap_id));
break;
}
dev_hdl = devctl_device_acquire(buf, 0);
if (dev_hdl == NULL) {
return;
}
/*
* If the seeprom driver is not loaded, calls to
* devctl_device_remove fails for seeprom devices
*/
if (devctl_device_remove(dev_hdl)) {
di_init_driver(SEEPROM_DRIVER_NAME, 0);
devctl_device_remove(dev_hdl);
}
devctl_release(dev_hdl);
}
static void
add_op_status(envmon_hpu_t *hpu, int *index)
{
boolean_t rmc_flag;
boolean_t ps_flag;
boolean_t disk_flag;
char node_name[MAXPATHLEN];
boolean_t flag;
rmc_flag = (strcmp(hpu->id.name, RMC_NAME) == 0);
ps_flag = (strncmp(hpu->id.name, PS_NAME,
PS_NAME_LEN) == 0);
disk_flag = (strncmp(hpu->id.name, DISK_NAME,
DISK_NAME_LEN) == 0);
if (rmc_flag || ps_flag) {
idprop->idp[*index].envhandle = hpu->id;
flag = rmc_flag && ((sys_platform != PLAT_CHALUPA) &&
(sys_platform != PLAT_CHALUPA19));
sprintf_buf2(node_name,
flag ? SYS_BOARD_PATH : CHASSIS_LOC_PATH, ps_flag ?
ps_apid_to_nodename(hpu->id.name) : hpu->id.name);
add_op_status_by_name(node_name, ps_flag ? PS_FRU_NAME : NULL,
&idprop->idp[(*index)++].volprop);
} else if (disk_flag) {
idprop->idp[*index].envhandle = hpu->id;
switch (sys_platform) {
case PLAT_CHALUPA:
case PLAT_CHALUPA19:
sprintf_buf2(node_name, CHASSIS_LOC_PATH, hpu->id.name);
break;
case PLAT_SEATTLE1U:
sprintf_buf2(node_name, SEATTLE1U_HDDBP_PATH, \
hpu->id.name);
break;
case PLAT_SEATTLE2U:
sprintf_buf2(node_name, SEATTLE2U_HDDBP_PATH, \
hpu->id.name);
break;
case PLAT_BOSTON:
sprintf_buf2(node_name, BOSTON_HDDBP_PATH, \
hpu->id.name);
break;
default:
sprintf_buf2(node_name, SYS_BOARD_PATH, hpu->id.name);
break;
}
add_op_status_by_name(node_name, DISK_FRU_NAME,
&idprop->idp[(*index)++].volprop);
}
}