17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23*360e6f5eSmathue  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * This file contains the environmental PICL plug-in module.
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate /*
337c478bd9Sstevel@tonic-gate  * Excalibur system contains up to two CPU and two PCI MAX1617 temperature
347c478bd9Sstevel@tonic-gate  * devices, each consisting of two sensors: die and ambient. Each sensor is
357c478bd9Sstevel@tonic-gate  * represented as a different minor device and the current temperature is read
367c478bd9Sstevel@tonic-gate  * via an I2C_GET_TEMPERATURE ioctl call to the max1617 driver. Additionally,
377c478bd9Sstevel@tonic-gate  * the MAX1617 device supports both a low and high temperature limit, which
387c478bd9Sstevel@tonic-gate  * can trigger an alert condition, causing power supply to turn off.
397c478bd9Sstevel@tonic-gate  *
407c478bd9Sstevel@tonic-gate  * The environmental monitor defines the following thresholds per sensor:
417c478bd9Sstevel@tonic-gate  *
427c478bd9Sstevel@tonic-gate  *	high_power_off		high hard shutdown
437c478bd9Sstevel@tonic-gate  *	high_shutdown		high soft shutdown limit
447c478bd9Sstevel@tonic-gate  *	high_warning		high warning limit
457c478bd9Sstevel@tonic-gate  *	low_warning		low warning limit
467c478bd9Sstevel@tonic-gate  *	low_shutdown		low soft shutdown limit
477c478bd9Sstevel@tonic-gate  *	low_power_off		low hard shutdown limit
487c478bd9Sstevel@tonic-gate  *
497c478bd9Sstevel@tonic-gate  * Above mentioned threshold values can be changed via "piclenvd.conf"
507c478bd9Sstevel@tonic-gate  * configuration file.
517c478bd9Sstevel@tonic-gate  *
527c478bd9Sstevel@tonic-gate  * Environmental monitoring is done by the "envthr" thread. It periodically
537c478bd9Sstevel@tonic-gate  * monitors both CPU die and CPU ambient temperatures and takes appropriate
547c478bd9Sstevel@tonic-gate  * action depending upon the current temperature and threshold values for
557c478bd9Sstevel@tonic-gate  * that sensor. If the temperature reaches the high_shutdown limit or the
567c478bd9Sstevel@tonic-gate  * low_shutdown limit, and remains there for over shutdown_interval seconds,
577c478bd9Sstevel@tonic-gate  * it forces a graceful system shutdown via tuneable shutdown_cmd string
587c478bd9Sstevel@tonic-gate  * variable. Otherwise, if the temperature reaches the high_warning limit
597c478bd9Sstevel@tonic-gate  * or the low_warning limit, it logs and prints a message on the console.
607c478bd9Sstevel@tonic-gate  * This message will be printed at most at "warning_interval" seconds
617c478bd9Sstevel@tonic-gate  * interval, which is also a tuneable variable.
627c478bd9Sstevel@tonic-gate  *
637c478bd9Sstevel@tonic-gate  * Excalibur system contains three fans: cpu, system and power supply. The
647c478bd9Sstevel@tonic-gate  * cpu and system fans are under software control and their speed can be
657c478bd9Sstevel@tonic-gate  * set to a value in the range 0 through 63. However, the software has no
667c478bd9Sstevel@tonic-gate  * control over the power supply fan's speed (it's automatically controlled
677c478bd9Sstevel@tonic-gate  * by the hardware), but it can turn it ON or OFF. When in EStar mode (i.e.
687c478bd9Sstevel@tonic-gate  * the lowest power state), the environmental monitor turns off the power
697c478bd9Sstevel@tonic-gate  * supply fan.
707c478bd9Sstevel@tonic-gate  *
717c478bd9Sstevel@tonic-gate  * Each fan is represented as a different minor device and the fan speed
727c478bd9Sstevel@tonic-gate  * can be controlled by writing to the TDA8444 device driver. Note that
737c478bd9Sstevel@tonic-gate  * these devices are read only and the driver caches the last speed set
747c478bd9Sstevel@tonic-gate  * for each fan, thus allowing an interface to read the current fan speed
757c478bd9Sstevel@tonic-gate  * also.
767c478bd9Sstevel@tonic-gate  *
777c478bd9Sstevel@tonic-gate  * The policy to control fan speed depends upon the sensor. For CPU die
787c478bd9Sstevel@tonic-gate  * sensor, different policy is used depending upon whether the temperature
797c478bd9Sstevel@tonic-gate  * is rising, falling or steady state. In case of CPU ambient sensor, only
807c478bd9Sstevel@tonic-gate  * one policy (speed proportional to the current temperature) is used.
817c478bd9Sstevel@tonic-gate  *
827c478bd9Sstevel@tonic-gate  * The power state monitoring is done by the "pmthr" thread. It uses the
837c478bd9Sstevel@tonic-gate  * PM_GET_STATE_CHANGE and PM_GET_STATE_CHANGE_WAIT ioctl commands to pick
847c478bd9Sstevel@tonic-gate  * up any power state change events. It processes all queued power state
857c478bd9Sstevel@tonic-gate  * change events and determines the curret lowest power state and saves it
867c478bd9Sstevel@tonic-gate  * in cur_lpstate variable.
877c478bd9Sstevel@tonic-gate  *
887c478bd9Sstevel@tonic-gate  * Once the "envthr" and "pmthr" threads have been started, they are never
897c478bd9Sstevel@tonic-gate  * killed. This is desirable so that we can do environmental monitoring
907c478bd9Sstevel@tonic-gate  * during reinit process.  The "envd_rwlock" reader/writer lock is used
917c478bd9Sstevel@tonic-gate  * to protect initialization of global state during reinit process against
927c478bd9Sstevel@tonic-gate  * the "envthr" and "pmthr" trying to reference that state.
937c478bd9Sstevel@tonic-gate  */
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate #include <stdio.h>
967c478bd9Sstevel@tonic-gate #include <stdlib.h>
977c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
987c478bd9Sstevel@tonic-gate #include <limits.h>
997c478bd9Sstevel@tonic-gate #include <string.h>
1007c478bd9Sstevel@tonic-gate #include <stdarg.h>
1017c478bd9Sstevel@tonic-gate #include <alloca.h>
1027c478bd9Sstevel@tonic-gate #include <unistd.h>
1037c478bd9Sstevel@tonic-gate #include <sys/processor.h>
1047c478bd9Sstevel@tonic-gate #include <syslog.h>
1057c478bd9Sstevel@tonic-gate #include <errno.h>
1067c478bd9Sstevel@tonic-gate #include <fcntl.h>
1077c478bd9Sstevel@tonic-gate #include <picl.h>
1087c478bd9Sstevel@tonic-gate #include <picltree.h>
1097c478bd9Sstevel@tonic-gate #include <picldefs.h>
1107c478bd9Sstevel@tonic-gate #include <pthread.h>
1117c478bd9Sstevel@tonic-gate #include <signal.h>
1127c478bd9Sstevel@tonic-gate #include <libdevinfo.h>
1137c478bd9Sstevel@tonic-gate #include <sys/pm.h>
1147c478bd9Sstevel@tonic-gate #include <sys/open.h>
1157c478bd9Sstevel@tonic-gate #include <sys/time.h>
1167c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
1177c478bd9Sstevel@tonic-gate #include <sys/systeminfo.h>
1187c478bd9Sstevel@tonic-gate #include <sys/i2c/clients/max1617.h>
1197c478bd9Sstevel@tonic-gate #include <sys/i2c/clients/i2c_client.h>
1207c478bd9Sstevel@tonic-gate #include <sys/xcalwd.h>
1217c478bd9Sstevel@tonic-gate #include "envd.h"
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate static pthread_rwlock_t	envd_rwlock = PTHREAD_RWLOCK_INITIALIZER;
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate /*
1267c478bd9Sstevel@tonic-gate  * PICL plguin
1277c478bd9Sstevel@tonic-gate  */
1287c478bd9Sstevel@tonic-gate static void piclenvd_register(void);
1297c478bd9Sstevel@tonic-gate static void piclenvd_init(void);
1307c478bd9Sstevel@tonic-gate static void piclenvd_fini(void);
1317c478bd9Sstevel@tonic-gate extern void env_picl_setup(void);
1327c478bd9Sstevel@tonic-gate extern void env_picl_destroy(void);
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate #pragma	init(piclenvd_register)
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate static picld_plugin_reg_t my_reg_info = {
1377c478bd9Sstevel@tonic-gate 	PICLD_PLUGIN_VERSION_1,
1387c478bd9Sstevel@tonic-gate 	PICLD_PLUGIN_CRITICAL,
1397c478bd9Sstevel@tonic-gate 	"SUNW_piclenvd",
1407c478bd9Sstevel@tonic-gate 	piclenvd_init,
1417c478bd9Sstevel@tonic-gate 	piclenvd_fini,
1427c478bd9Sstevel@tonic-gate };
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate /*
1467c478bd9Sstevel@tonic-gate  * Default threshold values for CPU junction/die and ambient sensors
1477c478bd9Sstevel@tonic-gate  */
1487c478bd9Sstevel@tonic-gate static sensor_thresh_t cpu_die_thresh_default = {
1497c478bd9Sstevel@tonic-gate 	CPU_DIE_LOW_POWER_OFF, CPU_DIE_HIGH_POWER_OFF,
1507c478bd9Sstevel@tonic-gate 	CPU_DIE_LOW_SHUTDOWN, CPU_DIE_HIGH_SHUTDOWN,
1517c478bd9Sstevel@tonic-gate 	CPU_DIE_LOW_WARNING, CPU_DIE_HIGH_WARNING,
1527c478bd9Sstevel@tonic-gate 	MAX1617_MIN_TEMP, MAX1617_MAX_TEMP,
1537c478bd9Sstevel@tonic-gate 	POLICY_TARGET_TEMP, 2,
1547c478bd9Sstevel@tonic-gate 	CPU_DIE_NORMAL_TARGET, CPU_DIE_OTHER_TARGET,
1557c478bd9Sstevel@tonic-gate 	0, 0, 0, 0
1567c478bd9Sstevel@tonic-gate };
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate static sensor_thresh_t cpu_amb_thresh_default = {
1597c478bd9Sstevel@tonic-gate 	CPU_AMB_LOW_POWER_OFF, CPU_AMB_HIGH_POWER_OFF,
1607c478bd9Sstevel@tonic-gate 	CPU_AMB_LOW_SHUTDOWN, CPU_AMB_HIGH_SHUTDOWN,
1617c478bd9Sstevel@tonic-gate 	CPU_AMB_LOW_WARNING, CPU_AMB_HIGH_WARNING,
1627c478bd9Sstevel@tonic-gate 	MAX1617_MIN_TEMP, MAX1617_MAX_TEMP,
1637c478bd9Sstevel@tonic-gate 	POLICY_LINEAR, 2,
1647c478bd9Sstevel@tonic-gate 	CPU_AMB_LOW_NOMINAL, CPU_AMB_HIGH_NOMINAL,
1657c478bd9Sstevel@tonic-gate 	0, 0, 0, 0
1667c478bd9Sstevel@tonic-gate };
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate /*
1707c478bd9Sstevel@tonic-gate  * Dummy sensor threshold data structure for processing threshold tuneables
1717c478bd9Sstevel@tonic-gate  */
1727c478bd9Sstevel@tonic-gate static sensor_thresh_t	dummy_thresh;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate /*
1757c478bd9Sstevel@tonic-gate  * Temperature related constants for fan speed adjustment
1767c478bd9Sstevel@tonic-gate  */
1777c478bd9Sstevel@tonic-gate #define	AVG_TEMP_HYSTERESIS	0.25
1787c478bd9Sstevel@tonic-gate #define	RISING_TEMP_MARGIN	6
1797c478bd9Sstevel@tonic-gate #define	FALLING_TEMP_MARGIN	3
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate /*
1827c478bd9Sstevel@tonic-gate  * tuneable variables
1837c478bd9Sstevel@tonic-gate  */
1847c478bd9Sstevel@tonic-gate #define	FAN_SLOW_ADJUSTMENT	20		/* in percentage */
1857c478bd9Sstevel@tonic-gate #define	FAN_INCREMENT_LIMIT	6		/* absolute value */
1867c478bd9Sstevel@tonic-gate #define	FAN_DECREMENT_LIMIT	1		/* absolute value */
1877c478bd9Sstevel@tonic-gate #define	DEVFSADM_CMD 		"/usr/sbin/devfsadm -i max1617"
1887c478bd9Sstevel@tonic-gate #define	FRU_DEVFSADM_CMD 	"/usr/sbin/devfsadm -i seeprom"
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate int		env_debug;
1917c478bd9Sstevel@tonic-gate static int	sensor_poll_interval;
1927c478bd9Sstevel@tonic-gate static int	warning_interval;
1937c478bd9Sstevel@tonic-gate static int	warning_duration;
1947c478bd9Sstevel@tonic-gate static int	shutdown_interval;
1957c478bd9Sstevel@tonic-gate static int	fan_slow_adjustment;
1967c478bd9Sstevel@tonic-gate static int	fan_incr_limit;
1977c478bd9Sstevel@tonic-gate static int	fan_decr_limit;
1987c478bd9Sstevel@tonic-gate static int	disable_piclenvd;
1997c478bd9Sstevel@tonic-gate static int	disable_warning;
2007c478bd9Sstevel@tonic-gate static int	disable_power_off;
2017c478bd9Sstevel@tonic-gate static int	disable_shutdown;
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate static char	shutdown_cmd[128];
2047c478bd9Sstevel@tonic-gate static char	devfsadm_cmd[128];
2057c478bd9Sstevel@tonic-gate static char	fru_devfsadm_cmd[128];
2067c478bd9Sstevel@tonic-gate static sensor_thresh_t cpu0_die_thresh, cpu0_amb_thresh;
2077c478bd9Sstevel@tonic-gate static sensor_thresh_t cpu1_die_thresh, cpu1_amb_thresh;
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate /*
2107c478bd9Sstevel@tonic-gate  * Temperature sensors
2117c478bd9Sstevel@tonic-gate  */
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate static env_sensor_t envd_sensors[] = {
2147c478bd9Sstevel@tonic-gate 	{ SENSOR_CPU0_DIE, CPU0_DIE_SENSOR_DEVFS, &cpu0_die_thresh,
2157c478bd9Sstevel@tonic-gate 	    CPU0_FRU_DEVFS, CPU_FRU_DIE_SENSOR,
2167c478bd9Sstevel@tonic-gate 	    SFLAG_TARGET_TEMP | SFLAG_CPU_DIE_SENSOR, -1},
2177c478bd9Sstevel@tonic-gate 	{ SENSOR_CPU0_AMB, CPU0_AMB_SENSOR_DEVFS, &cpu0_amb_thresh,
2187c478bd9Sstevel@tonic-gate 	    CPU0_FRU_DEVFS, CPU_FRU_AMB_SENSOR, SFLAG_CPU_AMB_SENSOR, -1},
2197c478bd9Sstevel@tonic-gate 	{ SENSOR_CPU1_DIE, CPU1_DIE_SENSOR_DEVFS, &cpu1_die_thresh,
2207c478bd9Sstevel@tonic-gate 	    CPU1_FRU_DEVFS, CPU_FRU_DIE_SENSOR,
2217c478bd9Sstevel@tonic-gate 	    SFLAG_TARGET_TEMP | SFLAG_CPU_DIE_SENSOR, -1},
2227c478bd9Sstevel@tonic-gate 	{ SENSOR_CPU1_AMB, CPU1_AMB_SENSOR_DEVFS, &cpu1_amb_thresh,
2237c478bd9Sstevel@tonic-gate 	    CPU1_FRU_DEVFS, CPU_FRU_AMB_SENSOR, SFLAG_CPU_AMB_SENSOR, -1},
2247c478bd9Sstevel@tonic-gate 	{ NULL, NULL, NULL, NULL, 0, 0, -1}
2257c478bd9Sstevel@tonic-gate };
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate /*
2297c478bd9Sstevel@tonic-gate  * Fan devices
2307c478bd9Sstevel@tonic-gate  */
2317c478bd9Sstevel@tonic-gate static env_fan_t envd_system_fan = {
2327c478bd9Sstevel@tonic-gate 	ENV_SYSTEM_FAN, ENV_SYSTEM_FAN_DEVFS,
2337c478bd9Sstevel@tonic-gate 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
2347c478bd9Sstevel@tonic-gate };
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate static env_fan_t envd_cpu_fan = {
2377c478bd9Sstevel@tonic-gate 	ENV_CPU_FAN, ENV_CPU_FAN_DEVFS,
2387c478bd9Sstevel@tonic-gate 	CPU_FAN_SPEED_MIN, CPU_FAN_SPEED_MAX, -1, -1,
2397c478bd9Sstevel@tonic-gate };
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate static env_fan_t envd_psupply_fan = {
2427c478bd9Sstevel@tonic-gate 	ENV_PSUPPLY_FAN, ENV_PSUPPLY_FAN_DEVFS,
2437c478bd9Sstevel@tonic-gate 	PSUPPLY_FAN_SPEED_MIN, PSUPPLY_FAN_SPEED_MAX, -1, -1,
2447c478bd9Sstevel@tonic-gate };
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate static env_fan_t *envd_fans[] = {
2477c478bd9Sstevel@tonic-gate 	&envd_system_fan,
2487c478bd9Sstevel@tonic-gate 	&envd_cpu_fan,
2497c478bd9Sstevel@tonic-gate 	&envd_psupply_fan,
2507c478bd9Sstevel@tonic-gate 	NULL
2517c478bd9Sstevel@tonic-gate };
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate /*
2547c478bd9Sstevel@tonic-gate  * Linked list of devices advertising lpm-ranges
2557c478bd9Sstevel@tonic-gate  */
2567c478bd9Sstevel@tonic-gate static lpm_dev_t	*lpm_devices = NULL;
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate /*
2597c478bd9Sstevel@tonic-gate  * Excalibur lpm to system-fan speed
2607c478bd9Sstevel@tonic-gate  * lpm values must be monotonically increasing (avoid divide-by-zero)
2617c478bd9Sstevel@tonic-gate  */
2627c478bd9Sstevel@tonic-gate static point_t	excal_lpm_system_fan_tbl[] = {
2637c478bd9Sstevel@tonic-gate 	/* {lpm, fspeed} */
2647c478bd9Sstevel@tonic-gate 	{18, 12},
2657c478bd9Sstevel@tonic-gate 	{25, 20},
2667c478bd9Sstevel@tonic-gate 	{33, 26},
2677c478bd9Sstevel@tonic-gate 	{44, 32},
2687c478bd9Sstevel@tonic-gate 	{51, 39},
2697c478bd9Sstevel@tonic-gate 	{63, 52},
2707c478bd9Sstevel@tonic-gate 	{64, 63}
2717c478bd9Sstevel@tonic-gate };
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate static table_t	lpm_fspeed = {
2747c478bd9Sstevel@tonic-gate 	sizeof (excal_lpm_system_fan_tbl)/ sizeof (point_t),
2757c478bd9Sstevel@tonic-gate 	excal_lpm_system_fan_tbl
2767c478bd9Sstevel@tonic-gate };
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate /*
2797c478bd9Sstevel@tonic-gate  * Sensor to fan map
2807c478bd9Sstevel@tonic-gate  */
2817c478bd9Sstevel@tonic-gate typedef struct {
2827c478bd9Sstevel@tonic-gate 	char	*sensor_name;
2837c478bd9Sstevel@tonic-gate 	char	*fan_name;
2847c478bd9Sstevel@tonic-gate } sensor_fan_map_t;
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate static sensor_fan_map_t sensor_fan_map[] = {
2877c478bd9Sstevel@tonic-gate 	{SENSOR_CPU0_DIE, ENV_CPU_FAN},
2887c478bd9Sstevel@tonic-gate 	{SENSOR_CPU1_DIE, ENV_CPU_FAN},
2897c478bd9Sstevel@tonic-gate 	{SENSOR_CPU0_AMB, ENV_SYSTEM_FAN},
2907c478bd9Sstevel@tonic-gate 	{SENSOR_CPU1_AMB, ENV_SYSTEM_FAN},
2917c478bd9Sstevel@tonic-gate 	{NULL, NULL}
2927c478bd9Sstevel@tonic-gate };
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate /*
2957c478bd9Sstevel@tonic-gate  * Sensor to PM device map
2967c478bd9Sstevel@tonic-gate  */
2977c478bd9Sstevel@tonic-gate struct sensor_pmdev {
2987c478bd9Sstevel@tonic-gate 	int		sensor_id;
2997c478bd9Sstevel@tonic-gate 	char		*sensor_name;
3007c478bd9Sstevel@tonic-gate 	char		*pmdev_name;
3017c478bd9Sstevel@tonic-gate 	char		*speed_comp_name;
3027c478bd9Sstevel@tonic-gate 	int		speed_comp;
3037c478bd9Sstevel@tonic-gate 	int		full_power;
3047c478bd9Sstevel@tonic-gate 	int		cur_power;
3057c478bd9Sstevel@tonic-gate 	env_sensor_t	*sensorp;
3067c478bd9Sstevel@tonic-gate 	sensor_pmdev_t	*next;
3077c478bd9Sstevel@tonic-gate };
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate #define	SPEED_COMPONENT_NAME	"CPU Speed"
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate static sensor_pmdev_t sensor_pmdevs[] = {
3127c478bd9Sstevel@tonic-gate 	{SENSOR_CPU0_ID, SENSOR_CPU0_DIE, NULL, SPEED_COMPONENT_NAME},
3137c478bd9Sstevel@tonic-gate 	{SENSOR_CPU1_ID, SENSOR_CPU1_DIE, NULL, SPEED_COMPONENT_NAME},
3147c478bd9Sstevel@tonic-gate 	{-1, NULL, NULL, NULL}
3157c478bd9Sstevel@tonic-gate };
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate /*
3187c478bd9Sstevel@tonic-gate  * Environmental thread variables
3197c478bd9Sstevel@tonic-gate  */
3207c478bd9Sstevel@tonic-gate static boolean_t	system_shutdown_started = B_FALSE;
3217c478bd9Sstevel@tonic-gate static boolean_t	envthr_created = B_FALSE;	/* envthr created */
3227c478bd9Sstevel@tonic-gate static pthread_t	envthr_tid;		/* envthr thread ID */
3237c478bd9Sstevel@tonic-gate static pthread_attr_t	thr_attr;
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate /*
3267c478bd9Sstevel@tonic-gate  * Power management thread (pmthr) variables
3277c478bd9Sstevel@tonic-gate  */
3287c478bd9Sstevel@tonic-gate static boolean_t	pmdev_names_init = B_FALSE;
3297c478bd9Sstevel@tonic-gate static pthread_t	pmthr_tid;		/* pmthr thread ID */
3307c478bd9Sstevel@tonic-gate static int		pmthr_exists = B_FALSE;	/* pmthr exists */
3317c478bd9Sstevel@tonic-gate static int		pm_fd = -1;		/* PM device file descriptor */
3327c478bd9Sstevel@tonic-gate static int		cur_lpstate;		/* cur low power state */
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate /*
3357c478bd9Sstevel@tonic-gate  * Miscellaneous variables and declarations
3367c478bd9Sstevel@tonic-gate  */
3377c478bd9Sstevel@tonic-gate static int	fru_devfsadm_invoked = 0;
3387c478bd9Sstevel@tonic-gate static int	devfsadm_invoked = 0;
3397c478bd9Sstevel@tonic-gate static char	tokdel[] = " \t\n\r";
3407c478bd9Sstevel@tonic-gate static uint_t	envd_sleep(uint_t);
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate /*
3437c478bd9Sstevel@tonic-gate  * Tuneable data structure/array and processing functions
3447c478bd9Sstevel@tonic-gate  */
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate typedef struct {
3477c478bd9Sstevel@tonic-gate 	char		*name;		/* keyword */
3487c478bd9Sstevel@tonic-gate 	int		(*func)(char *, char *, void *, int, char *, int);
3497c478bd9Sstevel@tonic-gate 					/* tuneable processing function */
3507c478bd9Sstevel@tonic-gate 	void		*arg1;		/* tuneable arg1 (memory address) */
3517c478bd9Sstevel@tonic-gate 	int		arg2;		/* tuneable arg2 (size or flags) */
3527c478bd9Sstevel@tonic-gate } env_tuneable_t;
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate static int process_int_tuneable(char *keyword, char *buf, void *addr,
3557c478bd9Sstevel@tonic-gate     int size, char *fname, int line);
3567c478bd9Sstevel@tonic-gate static int process_string_tuneable(char *keyword, char *buf, void *addr,
3577c478bd9Sstevel@tonic-gate     int size, char *fname, int line);
3587c478bd9Sstevel@tonic-gate static int process_threshold_tuneable(char *keyword, char *buf, void *addr,
3597c478bd9Sstevel@tonic-gate     int flags, char *fname, int line);
3607c478bd9Sstevel@tonic-gate static void process_env_conf_file(void);
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate static env_tuneable_t env_tuneables[] = {
3637c478bd9Sstevel@tonic-gate 	{"low_power_off", process_threshold_tuneable,
3647c478bd9Sstevel@tonic-gate 	    &dummy_thresh.low_power_off, 0},
3657c478bd9Sstevel@tonic-gate 	{"low_shutdown", process_threshold_tuneable,
3667c478bd9Sstevel@tonic-gate 	    &dummy_thresh.low_shutdown, 0},
3677c478bd9Sstevel@tonic-gate 	{"low_warning", process_threshold_tuneable,
3687c478bd9Sstevel@tonic-gate 	    &dummy_thresh.low_warning, 0},
3697c478bd9Sstevel@tonic-gate 	{"high_power_off", process_threshold_tuneable,
3707c478bd9Sstevel@tonic-gate 	    &dummy_thresh.high_power_off, 0},
3717c478bd9Sstevel@tonic-gate 	{"high_shutdown", process_threshold_tuneable,
3727c478bd9Sstevel@tonic-gate 	    &dummy_thresh.high_shutdown, 0},
3737c478bd9Sstevel@tonic-gate 	{"high_warning", process_threshold_tuneable,
3747c478bd9Sstevel@tonic-gate 	    &dummy_thresh.high_warning, 0},
3757c478bd9Sstevel@tonic-gate 	{"force_cpu_fan", process_int_tuneable, &envd_cpu_fan.forced_speed,
3767c478bd9Sstevel@tonic-gate 	    sizeof (envd_cpu_fan.forced_speed)},
3777c478bd9Sstevel@tonic-gate 	{"force_system_fan", process_int_tuneable,
3787c478bd9Sstevel@tonic-gate 	    &envd_system_fan.forced_speed,
3797c478bd9Sstevel@tonic-gate 	    sizeof (envd_system_fan.forced_speed)},
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	{"cpu_amb_low_power_off", process_threshold_tuneable,
3827c478bd9Sstevel@tonic-gate 	    &dummy_thresh.low_power_off, SFLAG_CPU_AMB_SENSOR},
3837c478bd9Sstevel@tonic-gate 	{"cpu_amb_low_shutdown", process_threshold_tuneable,
3847c478bd9Sstevel@tonic-gate 	    &dummy_thresh.low_shutdown, SFLAG_CPU_AMB_SENSOR},
3857c478bd9Sstevel@tonic-gate 	{"cpu_amb_low_warning", process_threshold_tuneable,
3867c478bd9Sstevel@tonic-gate 	    &dummy_thresh.low_warning, SFLAG_CPU_AMB_SENSOR},
3877c478bd9Sstevel@tonic-gate 	{"cpu_amb_low_nominal", process_threshold_tuneable,
3887c478bd9Sstevel@tonic-gate 	    &dummy_thresh.policy_data[LOW_NOMINAL_LOC], SFLAG_CPU_AMB_SENSOR},
3897c478bd9Sstevel@tonic-gate 	{"cpu_amb_high_power_off", process_threshold_tuneable,
3907c478bd9Sstevel@tonic-gate 	    &dummy_thresh.high_power_off, SFLAG_CPU_AMB_SENSOR},
3917c478bd9Sstevel@tonic-gate 	{"cpu_amb_high_shutdown", process_threshold_tuneable,
3927c478bd9Sstevel@tonic-gate 	    &dummy_thresh.high_shutdown, SFLAG_CPU_AMB_SENSOR},
3937c478bd9Sstevel@tonic-gate 	{"cpu_amb_high_warning", process_threshold_tuneable,
3947c478bd9Sstevel@tonic-gate 	    &dummy_thresh.high_warning, SFLAG_CPU_AMB_SENSOR},
3957c478bd9Sstevel@tonic-gate 	{"cpu_amb_high_nominal", process_threshold_tuneable,
3967c478bd9Sstevel@tonic-gate 	    &dummy_thresh.policy_data[HIGH_NOMINAL_LOC], SFLAG_CPU_AMB_SENSOR},
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate 	{"cpu_die_low_power_off", process_threshold_tuneable,
3997c478bd9Sstevel@tonic-gate 	    &dummy_thresh.low_power_off, SFLAG_CPU_DIE_SENSOR},
4007c478bd9Sstevel@tonic-gate 	{"cpu_die_low_shutdown", process_threshold_tuneable,
4017c478bd9Sstevel@tonic-gate 	    &dummy_thresh.low_shutdown, SFLAG_CPU_DIE_SENSOR},
4027c478bd9Sstevel@tonic-gate 	{"cpu_die_low_warning", process_threshold_tuneable,
4037c478bd9Sstevel@tonic-gate 	    &dummy_thresh.low_warning, SFLAG_CPU_DIE_SENSOR},
4047c478bd9Sstevel@tonic-gate 	{"cpu_die_normal_target", process_threshold_tuneable,
4057c478bd9Sstevel@tonic-gate 	    &dummy_thresh.policy_data[0], SFLAG_CPU_DIE_SENSOR},
4067c478bd9Sstevel@tonic-gate 	{"cpu_die_high_power_off", process_threshold_tuneable,
4077c478bd9Sstevel@tonic-gate 	    &dummy_thresh.high_power_off, SFLAG_CPU_DIE_SENSOR},
4087c478bd9Sstevel@tonic-gate 	{"cpu_die_high_shutdown", process_threshold_tuneable,
4097c478bd9Sstevel@tonic-gate 	    &dummy_thresh.high_shutdown, SFLAG_CPU_DIE_SENSOR},
4107c478bd9Sstevel@tonic-gate 	{"cpu_die_high_warning", process_threshold_tuneable,
4117c478bd9Sstevel@tonic-gate 	    &dummy_thresh.high_warning, SFLAG_CPU_DIE_SENSOR},
4127c478bd9Sstevel@tonic-gate 	{"cpu_die_other_target", process_threshold_tuneable,
4137c478bd9Sstevel@tonic-gate 	    &dummy_thresh.policy_data[1], SFLAG_CPU_DIE_SENSOR},
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 	{"sensor_poll_interval", process_int_tuneable, &sensor_poll_interval,
4167c478bd9Sstevel@tonic-gate 	    sizeof (sensor_poll_interval)},
4177c478bd9Sstevel@tonic-gate 	{"warning_interval", process_int_tuneable, &warning_interval,
4187c478bd9Sstevel@tonic-gate 	    sizeof (warning_interval)},
4197c478bd9Sstevel@tonic-gate 	{"warning_duration", process_int_tuneable, &warning_duration,
4207c478bd9Sstevel@tonic-gate 	    sizeof (warning_duration)},
4217c478bd9Sstevel@tonic-gate 	{"disable_piclenvd", process_int_tuneable, &disable_piclenvd,
4227c478bd9Sstevel@tonic-gate 	    sizeof (disable_piclenvd)},
4237c478bd9Sstevel@tonic-gate 	{"disable_power_off", process_int_tuneable, &disable_power_off,
4247c478bd9Sstevel@tonic-gate 	    sizeof (disable_power_off)},
4257c478bd9Sstevel@tonic-gate 	{"disable_warning", process_int_tuneable, &disable_warning,
4267c478bd9Sstevel@tonic-gate 	    sizeof (disable_warning)},
4277c478bd9Sstevel@tonic-gate 	{"disable_shutdown", process_int_tuneable, &disable_shutdown,
4287c478bd9Sstevel@tonic-gate 	    sizeof (disable_shutdown)},
4297c478bd9Sstevel@tonic-gate 	{"shutdown_interval", process_int_tuneable, &shutdown_interval,
4307c478bd9Sstevel@tonic-gate 	    sizeof (shutdown_interval)},
4317c478bd9Sstevel@tonic-gate 	{"shutdown_cmd", process_string_tuneable, &shutdown_cmd[0],
4327c478bd9Sstevel@tonic-gate 	    sizeof (shutdown_cmd)},
4337c478bd9Sstevel@tonic-gate 	{"devfsadm_cmd", process_string_tuneable, &devfsadm_cmd[0],
4347c478bd9Sstevel@tonic-gate 	    sizeof (devfsadm_cmd)},
4357c478bd9Sstevel@tonic-gate 	{"fru_devfsadm_cmd", process_string_tuneable, &fru_devfsadm_cmd[0],
4367c478bd9Sstevel@tonic-gate 	    sizeof (fru_devfsadm_cmd)},
4377c478bd9Sstevel@tonic-gate 	{"fan_slow_adjustment", process_int_tuneable, &fan_slow_adjustment,
4387c478bd9Sstevel@tonic-gate 	    sizeof (fan_slow_adjustment)},
4397c478bd9Sstevel@tonic-gate 	{"fan_incr_limit", process_int_tuneable, &fan_incr_limit,
4407c478bd9Sstevel@tonic-gate 	    sizeof (fan_incr_limit)},
4417c478bd9Sstevel@tonic-gate 	{"fan_decr_limit", process_int_tuneable, &fan_decr_limit,
4427c478bd9Sstevel@tonic-gate 	    sizeof (fan_decr_limit)},
4437c478bd9Sstevel@tonic-gate 	{"env_debug", process_int_tuneable, &env_debug, sizeof (env_debug)},
4447c478bd9Sstevel@tonic-gate 	{ NULL, NULL, NULL, 0}
4457c478bd9Sstevel@tonic-gate };
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate static void
fini_table(table_t * tblp)4487c478bd9Sstevel@tonic-gate fini_table(table_t *tblp)
4497c478bd9Sstevel@tonic-gate {
4507c478bd9Sstevel@tonic-gate 	if (tblp == NULL)
4517c478bd9Sstevel@tonic-gate 		return;
4527c478bd9Sstevel@tonic-gate 	free(tblp->xymap);
4537c478bd9Sstevel@tonic-gate 	free(tblp);
4547c478bd9Sstevel@tonic-gate }
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate static table_t *
init_table(int npoints)4577c478bd9Sstevel@tonic-gate init_table(int npoints)
4587c478bd9Sstevel@tonic-gate {
4597c478bd9Sstevel@tonic-gate 	table_t		*tblp;
4607c478bd9Sstevel@tonic-gate 	point_t		*xy;
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate 	if (npoints == 0)
4637c478bd9Sstevel@tonic-gate 		return (NULL);
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	if ((tblp = malloc(sizeof (*tblp))) == NULL)
4667c478bd9Sstevel@tonic-gate 		return (NULL);
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	if ((xy = malloc(sizeof (*xy) * npoints)) == NULL) {
4697c478bd9Sstevel@tonic-gate 		free(tblp);
4707c478bd9Sstevel@tonic-gate 		return (NULL);
4717c478bd9Sstevel@tonic-gate 	}
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	tblp->nentries = npoints;
4747c478bd9Sstevel@tonic-gate 	tblp->xymap = xy;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	return (tblp);
4777c478bd9Sstevel@tonic-gate }
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate /*
4807c478bd9Sstevel@tonic-gate  * Temp-LPM Table format:
4817c478bd9Sstevel@tonic-gate  * temp, lpm, temp, lpm, ...
4827c478bd9Sstevel@tonic-gate  */
4837c478bd9Sstevel@tonic-gate static table_t *
parse_lpm_ranges(uint32_t * bufp,size_t nbytes)4847c478bd9Sstevel@tonic-gate parse_lpm_ranges(uint32_t *bufp, size_t nbytes)
4857c478bd9Sstevel@tonic-gate {
4867c478bd9Sstevel@tonic-gate 	int	nentries;
4877c478bd9Sstevel@tonic-gate 	table_t	*tblp = NULL;
4887c478bd9Sstevel@tonic-gate 	int	i;
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate 	if (bufp == NULL)
4917c478bd9Sstevel@tonic-gate 		return (NULL);
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	/*
4947c478bd9Sstevel@tonic-gate 	 * Table should have at least 2 points
4957c478bd9Sstevel@tonic-gate 	 * and all points should have x and y values
4967c478bd9Sstevel@tonic-gate 	 */
4977c478bd9Sstevel@tonic-gate 	if ((nbytes < (2 * sizeof (point_t))) ||
4987c478bd9Sstevel@tonic-gate 	    (nbytes & (sizeof (point_t) - 1))) {
4997c478bd9Sstevel@tonic-gate 		if (env_debug)
5007c478bd9Sstevel@tonic-gate 			envd_log(LOG_ERR, ENV_INVALID_PROPERTY_FORMAT,
5017c478bd9Sstevel@tonic-gate 			    LPM_RANGES_PROPERTY);
5027c478bd9Sstevel@tonic-gate 		return (NULL);
5037c478bd9Sstevel@tonic-gate 	}
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	/* number of entries in the temp-lpm table */
5067c478bd9Sstevel@tonic-gate 	nentries = nbytes/sizeof (point_t);
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	tblp = init_table(nentries);
5097c478bd9Sstevel@tonic-gate 	if (tblp == NULL)
5107c478bd9Sstevel@tonic-gate 		return (tblp);
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	/* copy the tuples */
5137c478bd9Sstevel@tonic-gate 	tblp->xymap[0].x = (int)*bufp++;
5147c478bd9Sstevel@tonic-gate 	tblp->xymap[0].y = (int)*bufp++;
5157c478bd9Sstevel@tonic-gate 	for (i = 1; i < nentries; ++i) {
5167c478bd9Sstevel@tonic-gate 		tblp->xymap[i].x = (int)*bufp++;
5177c478bd9Sstevel@tonic-gate 		tblp->xymap[i].y = (int)*bufp++;
5187c478bd9Sstevel@tonic-gate 		if (tblp->xymap[i].x <= tblp->xymap[i - 1].x) {
5197c478bd9Sstevel@tonic-gate 			fini_table(tblp);
5207c478bd9Sstevel@tonic-gate 			if (env_debug)
5217c478bd9Sstevel@tonic-gate 				envd_log(LOG_ERR, ENV_INVALID_PROPERTY_FORMAT,
5227c478bd9Sstevel@tonic-gate 				    LPM_RANGES_PROPERTY);
5237c478bd9Sstevel@tonic-gate 			return (NULL);
5247c478bd9Sstevel@tonic-gate 		}
5257c478bd9Sstevel@tonic-gate 	}
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	return (tblp);
5287c478bd9Sstevel@tonic-gate }
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate /*
5317c478bd9Sstevel@tonic-gate  * function: calculates y for a given x based on a table of points
5327c478bd9Sstevel@tonic-gate  * for monotonically increasing x values.
5337c478bd9Sstevel@tonic-gate  * 'tbl' specifies the table to use, 'val' specifies the 'x', returns 'y'
5347c478bd9Sstevel@tonic-gate  */
5357c478bd9Sstevel@tonic-gate static int
y_of_x(table_t * tbl,int xval)5367c478bd9Sstevel@tonic-gate y_of_x(table_t *tbl, int xval)
5377c478bd9Sstevel@tonic-gate {
5387c478bd9Sstevel@tonic-gate 	int		i;
5397c478bd9Sstevel@tonic-gate 	int		entries;
5407c478bd9Sstevel@tonic-gate 	point_t		*xymap;
5417c478bd9Sstevel@tonic-gate 	float		newval;
5427c478bd9Sstevel@tonic-gate 	float		dy, dx, slope;
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	entries = tbl->nentries;
5457c478bd9Sstevel@tonic-gate 	xymap = tbl->xymap;
5467c478bd9Sstevel@tonic-gate 	if (xval <= xymap[0].x)
5477c478bd9Sstevel@tonic-gate 		return (xymap[0].y);
5487c478bd9Sstevel@tonic-gate 	else if (xval >= xymap[entries - 1].x)
5497c478bd9Sstevel@tonic-gate 		return (xymap[entries - 1].y);
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	for (i = 1; i < entries - 1; i++) {
5527c478bd9Sstevel@tonic-gate 		if (xval == xymap[i].x)
5537c478bd9Sstevel@tonic-gate 			return (xymap[i].y);
5547c478bd9Sstevel@tonic-gate 		if (xval < xymap[i].x)
5557c478bd9Sstevel@tonic-gate 			break;
5567c478bd9Sstevel@tonic-gate 	}
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 	/*
5597c478bd9Sstevel@tonic-gate 	 * Use linear interpolation
5607c478bd9Sstevel@tonic-gate 	 */
5617c478bd9Sstevel@tonic-gate 	dy = (float)(xymap[i].y - xymap[i-1].y);
5627c478bd9Sstevel@tonic-gate 	dx = (float)(xymap[i].x - xymap[i-1].x);
5637c478bd9Sstevel@tonic-gate 	slope = dy/dx;
5647c478bd9Sstevel@tonic-gate 	newval = xymap[i - 1].y + slope * (xval - xymap[i - 1].x);
5657c478bd9Sstevel@tonic-gate 	return ((int)(newval + (newval >= 0 ? 0.5 : -0.5)));
5667c478bd9Sstevel@tonic-gate }
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate static int
get_lpm_speed(lpm_dev_t * lpmdevs,int temp)5697c478bd9Sstevel@tonic-gate get_lpm_speed(lpm_dev_t *lpmdevs, int temp)
5707c478bd9Sstevel@tonic-gate {
5717c478bd9Sstevel@tonic-gate 	lpm_dev_t	*devp;
5727c478bd9Sstevel@tonic-gate 	int		lpm;
5737c478bd9Sstevel@tonic-gate 	int		speed;
5747c478bd9Sstevel@tonic-gate 	int		maxspeed;
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 	if (lpmdevs == NULL)
5777c478bd9Sstevel@tonic-gate 		return (0);
5787c478bd9Sstevel@tonic-gate 	maxspeed = 0;
5797c478bd9Sstevel@tonic-gate 	for (devp = lpmdevs; devp != NULL; devp = devp->next) {
5807c478bd9Sstevel@tonic-gate 		if (devp->temp_lpm_tbl == NULL)
5817c478bd9Sstevel@tonic-gate 			continue;
5827c478bd9Sstevel@tonic-gate 		lpm = y_of_x(devp->temp_lpm_tbl, temp);
5837c478bd9Sstevel@tonic-gate 		if (env_debug)
5847c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO, "ambient %d lpm %d\n", temp, lpm);
5857c478bd9Sstevel@tonic-gate 		speed = y_of_x(&lpm_fspeed, lpm);
5867c478bd9Sstevel@tonic-gate 		maxspeed = maxspeed > speed ? maxspeed : speed;
5877c478bd9Sstevel@tonic-gate 		if (env_debug)
5887c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO, "lpm %d fanspeed %d\n", lpm, speed);
5897c478bd9Sstevel@tonic-gate 	}
5907c478bd9Sstevel@tonic-gate 	return (maxspeed);
5917c478bd9Sstevel@tonic-gate }
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate /*
5947c478bd9Sstevel@tonic-gate  * Callback function used by ptree_walk_tree_by_class
5957c478bd9Sstevel@tonic-gate  */
5967c478bd9Sstevel@tonic-gate static int
cb_lpm(picl_nodehdl_t nodeh,void * args)5977c478bd9Sstevel@tonic-gate cb_lpm(picl_nodehdl_t nodeh, void *args)
5987c478bd9Sstevel@tonic-gate {
5997c478bd9Sstevel@tonic-gate 	lpm_dev_t	**retp = (lpm_dev_t **)args;
6007c478bd9Sstevel@tonic-gate 	int		err;
6017c478bd9Sstevel@tonic-gate 	ptree_propinfo_t	pinfo;
6027c478bd9Sstevel@tonic-gate 	picl_prophdl_t		proph;
6037c478bd9Sstevel@tonic-gate 	size_t			psize;
6047c478bd9Sstevel@tonic-gate 	void			*bufp;
6057c478bd9Sstevel@tonic-gate 	table_t			*temp_lpm_tbl;
6067c478bd9Sstevel@tonic-gate 	lpm_dev_t		*newdev;
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 	err = ptree_get_prop_by_name(nodeh, LPM_RANGES_PROPERTY, &proph);
6097c478bd9Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
6107c478bd9Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	err = ptree_get_propinfo(proph, &pinfo);
6137c478bd9Sstevel@tonic-gate 	if ((err != PICL_SUCCESS) ||
6147c478bd9Sstevel@tonic-gate 	    (pinfo.piclinfo.type != PICL_PTYPE_BYTEARRAY))
6157c478bd9Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
6167c478bd9Sstevel@tonic-gate 	psize = pinfo.piclinfo.size;
6177c478bd9Sstevel@tonic-gate 	bufp = alloca(psize);
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	err = ptree_get_propval(proph, bufp, psize);
6207c478bd9Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
6217c478bd9Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	temp_lpm_tbl = parse_lpm_ranges(bufp, psize);
6247c478bd9Sstevel@tonic-gate 	if (temp_lpm_tbl == NULL) {
6257c478bd9Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
6267c478bd9Sstevel@tonic-gate 	}
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 	newdev = malloc(sizeof (*newdev));
6297c478bd9Sstevel@tonic-gate 	if (newdev == NULL) {
6307c478bd9Sstevel@tonic-gate 		fini_table(temp_lpm_tbl);
6317c478bd9Sstevel@tonic-gate 		return (PICL_WALK_TERMINATE);
6327c478bd9Sstevel@tonic-gate 	}
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 	memset(newdev, 0, sizeof (*newdev));
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	newdev->nodeh = nodeh;
6377c478bd9Sstevel@tonic-gate 	newdev->temp_lpm_tbl = temp_lpm_tbl;
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 	/* add newdev to the list */
6407c478bd9Sstevel@tonic-gate 	newdev->next = *retp;
6417c478bd9Sstevel@tonic-gate 	*retp = newdev;
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 	return (PICL_WALK_CONTINUE);
6447c478bd9Sstevel@tonic-gate }
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate /*
6477c478bd9Sstevel@tonic-gate  * Find all devices advertising "lpm-ranges" property, parse and store
6487c478bd9Sstevel@tonic-gate  * the lpm tables for each device
6497c478bd9Sstevel@tonic-gate  */
6507c478bd9Sstevel@tonic-gate static int
setup_lpm_devices(lpm_dev_t ** devpp)6517c478bd9Sstevel@tonic-gate setup_lpm_devices(lpm_dev_t **devpp)
6527c478bd9Sstevel@tonic-gate {
6537c478bd9Sstevel@tonic-gate 	picl_nodehdl_t	plath;
6547c478bd9Sstevel@tonic-gate 	int		err;
6557c478bd9Sstevel@tonic-gate 	lpm_dev_t	*lpmp;
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	err = ptree_get_node_by_path("/platform", &plath);
6587c478bd9Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
6597c478bd9Sstevel@tonic-gate 		return (err);
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	lpmp = NULL;
6627c478bd9Sstevel@tonic-gate 	err = ptree_walk_tree_by_class(plath, NULL, (void *)&lpmp, cb_lpm);
6637c478bd9Sstevel@tonic-gate 	if (err == PICL_SUCCESS)
6647c478bd9Sstevel@tonic-gate 		*devpp = lpmp;
6657c478bd9Sstevel@tonic-gate 	return (err);
6667c478bd9Sstevel@tonic-gate }
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate /*
6697c478bd9Sstevel@tonic-gate  * Remove all lpm_devices and their tables.
6707c478bd9Sstevel@tonic-gate  */
6717c478bd9Sstevel@tonic-gate static void
delete_lpm_devices(void)6727c478bd9Sstevel@tonic-gate delete_lpm_devices(void)
6737c478bd9Sstevel@tonic-gate {
6747c478bd9Sstevel@tonic-gate 	lpm_dev_t	*devp, *next;
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&envd_rwlock);
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	if (lpm_devices == NULL) {
6797c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&envd_rwlock);
6807c478bd9Sstevel@tonic-gate 		return;
6817c478bd9Sstevel@tonic-gate 	}
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 	devp = lpm_devices;
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate 	while (devp != NULL) {
6867c478bd9Sstevel@tonic-gate 		fini_table(devp->temp_lpm_tbl);
6877c478bd9Sstevel@tonic-gate 		next = devp->next;
6887c478bd9Sstevel@tonic-gate 		free(devp);
6897c478bd9Sstevel@tonic-gate 		devp = next;
6907c478bd9Sstevel@tonic-gate 	}
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate 	lpm_devices = NULL;
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&envd_rwlock);
6957c478bd9Sstevel@tonic-gate }
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate /*
6987c478bd9Sstevel@tonic-gate  * Translate observed (measured) temperature into expected (correct)
6997c478bd9Sstevel@tonic-gate  * temperature
7007c478bd9Sstevel@tonic-gate  */
7017c478bd9Sstevel@tonic-gate static int
xlate_obs2exp(env_sensor_t * sensorp,tempr_t temp)7027c478bd9Sstevel@tonic-gate xlate_obs2exp(env_sensor_t *sensorp, tempr_t temp)
7037c478bd9Sstevel@tonic-gate {
7047c478bd9Sstevel@tonic-gate 	int		i, entries, new_temp, denominator;
7057c478bd9Sstevel@tonic-gate 	tempr_map_t	*map;
7067c478bd9Sstevel@tonic-gate 	float		ftemp;
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 	entries = sensorp->obs2exp_cnt;
7097c478bd9Sstevel@tonic-gate 	map = sensorp->obs2exp_map;
7107c478bd9Sstevel@tonic-gate 	if (entries < 2 || map == NULL)  {
7117c478bd9Sstevel@tonic-gate 		/* no map or can't map it */
7127c478bd9Sstevel@tonic-gate 		new_temp = temp;
7137c478bd9Sstevel@tonic-gate 	} else {
7147c478bd9Sstevel@tonic-gate 		/*
7157c478bd9Sstevel@tonic-gate 		 * Any point beyond the range specified by the map is
7167c478bd9Sstevel@tonic-gate 		 * extrapolated using either the first two or the last
7177c478bd9Sstevel@tonic-gate 		 * two entries in the map.
7187c478bd9Sstevel@tonic-gate 		 */
7197c478bd9Sstevel@tonic-gate 		for (i = 1; i < entries-1; i++)
7207c478bd9Sstevel@tonic-gate 			if (temp < map[i].observed)
7217c478bd9Sstevel@tonic-gate 				break;
7227c478bd9Sstevel@tonic-gate 		/*
7237c478bd9Sstevel@tonic-gate 		 * Interpolate/extrapolate the temperature using linear
7247c478bd9Sstevel@tonic-gate 		 * equation with map[i-1] and map[i] being the two ends
7257c478bd9Sstevel@tonic-gate 		 * of the line segment.
7267c478bd9Sstevel@tonic-gate 		 */
7277c478bd9Sstevel@tonic-gate 		denominator = map[i].observed - map[i-1].observed;
7287c478bd9Sstevel@tonic-gate 		if (denominator == 0) {
7297c478bd9Sstevel@tonic-gate 			/*
7307c478bd9Sstevel@tonic-gate 			 * Infinite slope. Since the temperature reading
7317c478bd9Sstevel@tonic-gate 			 * resolution is 1C, force denominator to 1 to
7327c478bd9Sstevel@tonic-gate 			 * avoid divide by zero.
7337c478bd9Sstevel@tonic-gate 			 */
7347c478bd9Sstevel@tonic-gate 			denominator = 1;
7357c478bd9Sstevel@tonic-gate 		}
7367c478bd9Sstevel@tonic-gate 		ftemp = map[i-1].expected +  (temp - map[i-1].observed) *
7377c478bd9Sstevel@tonic-gate 		    (float)(map[i].expected - map[i-1].expected)/denominator;
7387c478bd9Sstevel@tonic-gate 		new_temp = (int)(ftemp + (ftemp >= 0 ? 0.5 : -0.5));
7397c478bd9Sstevel@tonic-gate 	}
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 	return (new_temp);
7427c478bd9Sstevel@tonic-gate }
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate /*
7467c478bd9Sstevel@tonic-gate  * Translate expected (correct) temperature into observed (measured)
7477c478bd9Sstevel@tonic-gate  * temperature
7487c478bd9Sstevel@tonic-gate  */
7497c478bd9Sstevel@tonic-gate static int
xlate_exp2obs(env_sensor_t * sensorp,tempr_t temp)7507c478bd9Sstevel@tonic-gate xlate_exp2obs(env_sensor_t *sensorp, tempr_t temp)
7517c478bd9Sstevel@tonic-gate {
7527c478bd9Sstevel@tonic-gate 	int		i, entries, new_temp, denominator;
7537c478bd9Sstevel@tonic-gate 	tempr_map_t	*map;
7547c478bd9Sstevel@tonic-gate 	float		ftemp;
7557c478bd9Sstevel@tonic-gate 	sensor_thresh_t	*threshp = sensorp->temp_thresh;
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate 	entries = sensorp->obs2exp_cnt;
7587c478bd9Sstevel@tonic-gate 	map = sensorp->obs2exp_map;
7597c478bd9Sstevel@tonic-gate 	if (entries < 2 || map == NULL)
7607c478bd9Sstevel@tonic-gate 		/* no map or can't map it */
7617c478bd9Sstevel@tonic-gate 		new_temp = temp;
7627c478bd9Sstevel@tonic-gate 	else {
7637c478bd9Sstevel@tonic-gate 		/*
7647c478bd9Sstevel@tonic-gate 		 * Any point beyond the range specified by the map is
7657c478bd9Sstevel@tonic-gate 		 * extrapolated using either the first two or the last
7667c478bd9Sstevel@tonic-gate 		 * two entries in the map.
7677c478bd9Sstevel@tonic-gate 		 */
7687c478bd9Sstevel@tonic-gate 		for (i = 1; i < entries-1; i++)
7697c478bd9Sstevel@tonic-gate 			if (temp < map[i].expected)
7707c478bd9Sstevel@tonic-gate 				break;
7717c478bd9Sstevel@tonic-gate 
7727c478bd9Sstevel@tonic-gate 		/*
7737c478bd9Sstevel@tonic-gate 		 * Interpolate/extrapolate the temperature using linear
7747c478bd9Sstevel@tonic-gate 		 * equation with map[i-1] and map[i] being the two ends
7757c478bd9Sstevel@tonic-gate 		 * of the line segment.
7767c478bd9Sstevel@tonic-gate 		 */
7777c478bd9Sstevel@tonic-gate 		denominator = map[i].expected - map[i-1].expected;
7787c478bd9Sstevel@tonic-gate 		if (denominator == 0) {
7797c478bd9Sstevel@tonic-gate 			/*
7807c478bd9Sstevel@tonic-gate 			 * Infinite slope. Since the temperature reading
7817c478bd9Sstevel@tonic-gate 			 * resolution is 1C, force denominator to 1 to
7827c478bd9Sstevel@tonic-gate 			 * avoid divide by zero.
7837c478bd9Sstevel@tonic-gate 			 */
7847c478bd9Sstevel@tonic-gate 			denominator = 1;
7857c478bd9Sstevel@tonic-gate 		}
7867c478bd9Sstevel@tonic-gate 		ftemp = map[i-1].observed + (temp - map[i-1].expected) *
7877c478bd9Sstevel@tonic-gate 		    (float)(map[i].observed - map[i-1].observed)/denominator;
7887c478bd9Sstevel@tonic-gate 		new_temp = (int)(ftemp + (ftemp >= 0 ? 0.5 : -0.5));
7897c478bd9Sstevel@tonic-gate 	}
7907c478bd9Sstevel@tonic-gate 
7917c478bd9Sstevel@tonic-gate 	if (threshp) {
7927c478bd9Sstevel@tonic-gate 		if (new_temp > threshp->max_limit)
7937c478bd9Sstevel@tonic-gate 			new_temp = threshp->max_limit;
7947c478bd9Sstevel@tonic-gate 		else if (new_temp < threshp->min_limit)
7957c478bd9Sstevel@tonic-gate 			new_temp = threshp->min_limit;
7967c478bd9Sstevel@tonic-gate 	}
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 	return (new_temp);
7997c478bd9Sstevel@tonic-gate }
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate /*
8037c478bd9Sstevel@tonic-gate  * Check if the specified FRU is present.
8047c478bd9Sstevel@tonic-gate  * Returns 1 if present; 0 otherwise.
8057c478bd9Sstevel@tonic-gate  */
8067c478bd9Sstevel@tonic-gate static int
fru_present(char * path)8077c478bd9Sstevel@tonic-gate fru_present(char *path)
8087c478bd9Sstevel@tonic-gate {
8097c478bd9Sstevel@tonic-gate 	char		*p, physpath[PATH_MAX];
8107c478bd9Sstevel@tonic-gate 	di_node_t	root_node;
8117c478bd9Sstevel@tonic-gate 	int		fru_present = 0;
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate 	/*
8147c478bd9Sstevel@tonic-gate 	 * Construct FRU device path by stripping minor
8157c478bd9Sstevel@tonic-gate 	 * node name from the path and use di_init() to
8167c478bd9Sstevel@tonic-gate 	 * see if the node exists.
8177c478bd9Sstevel@tonic-gate 	 */
8187c478bd9Sstevel@tonic-gate 	(void) strlcpy(physpath, path, sizeof (physpath));
8197c478bd9Sstevel@tonic-gate 	p = strrchr(physpath, ':');
8207c478bd9Sstevel@tonic-gate 	if (p != NULL)
8217c478bd9Sstevel@tonic-gate 		*p = '\0';
8227c478bd9Sstevel@tonic-gate 	if ((root_node = di_init(physpath, DINFOMINOR)) != DI_NODE_NIL) {
8237c478bd9Sstevel@tonic-gate 		di_fini(root_node);
8247c478bd9Sstevel@tonic-gate 		fru_present = 1;
8257c478bd9Sstevel@tonic-gate 	}
8267c478bd9Sstevel@tonic-gate 	return (fru_present);
8277c478bd9Sstevel@tonic-gate }
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate /*
8317c478bd9Sstevel@tonic-gate  * Get environmental segment from the specified FRU SEEPROM
8327c478bd9Sstevel@tonic-gate  */
8337c478bd9Sstevel@tonic-gate static int
get_envseg(int fd,void ** envsegp,int * envseglenp)8347c478bd9Sstevel@tonic-gate get_envseg(int fd, void **envsegp, int *envseglenp)
8357c478bd9Sstevel@tonic-gate {
8367c478bd9Sstevel@tonic-gate 	int			i, segcnt, envseglen;
8377c478bd9Sstevel@tonic-gate 	section_layout_t	section;
8387c478bd9Sstevel@tonic-gate 	segment_layout_t	segment;
8397c478bd9Sstevel@tonic-gate 	uint8_t			*envseg;
8407c478bd9Sstevel@tonic-gate 
8417c478bd9Sstevel@tonic-gate 	if (lseek(fd, (long)SECTION_HDR_OFFSET, 0) == -1L ||
8427c478bd9Sstevel@tonic-gate 	    read(fd, &section, sizeof (section)) != sizeof (section)) {
8437c478bd9Sstevel@tonic-gate 		return (EINVAL);
8447c478bd9Sstevel@tonic-gate 	}
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 	/*
8477c478bd9Sstevel@tonic-gate 	 * Verify we have the correct section and contents are valid
8487c478bd9Sstevel@tonic-gate 	 * For now, we don't verify the CRC.
8497c478bd9Sstevel@tonic-gate 	 */
8507c478bd9Sstevel@tonic-gate 	if (section.header_tag != SECTION_HDR_TAG ||
8517c478bd9Sstevel@tonic-gate 	    GET_UNALIGN16(&section.header_version[0]) != SECTION_HDR_VER) {
8527c478bd9Sstevel@tonic-gate 		if (env_debug)
8537c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
8547c478bd9Sstevel@tonic-gate 			    "Invalid section header tag:%x  version:%x\n",
8557c478bd9Sstevel@tonic-gate 			    section.header_tag,
8567c478bd9Sstevel@tonic-gate 			    GET_UNALIGN16(&section.header_version));
8577c478bd9Sstevel@tonic-gate 		return (EINVAL);
8587c478bd9Sstevel@tonic-gate 	}
8597c478bd9Sstevel@tonic-gate 
8607c478bd9Sstevel@tonic-gate 	/*
8617c478bd9Sstevel@tonic-gate 	 * Locate our environmental segment
8627c478bd9Sstevel@tonic-gate 	 */
8637c478bd9Sstevel@tonic-gate 	segcnt = section.segment_count;
8647c478bd9Sstevel@tonic-gate 	for (i = 0; i < segcnt; i++) {
8657c478bd9Sstevel@tonic-gate 		if (read(fd, &segment, sizeof (segment)) != sizeof (segment)) {
8667c478bd9Sstevel@tonic-gate 			return (errno);
8677c478bd9Sstevel@tonic-gate 		}
8687c478bd9Sstevel@tonic-gate 		if (env_debug > 1)
8697c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
8707c478bd9Sstevel@tonic-gate 			    "Seg name: %x  desc:%x off:%x  len:%x\n",
8717c478bd9Sstevel@tonic-gate 			    GET_UNALIGN16(&segment.name),
8727c478bd9Sstevel@tonic-gate 			    GET_UNALIGN32(&segment.descriptor[0]),
8737c478bd9Sstevel@tonic-gate 			    GET_UNALIGN16(&segment.offset),
8747c478bd9Sstevel@tonic-gate 			    GET_UNALIGN16(&segment.length));
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 		if (GET_UNALIGN16(&segment.name) == ENVSEG_NAME)
8777c478bd9Sstevel@tonic-gate 			break;
8787c478bd9Sstevel@tonic-gate 	}
8797c478bd9Sstevel@tonic-gate 
8807c478bd9Sstevel@tonic-gate 	if (i >= segcnt) {
8817c478bd9Sstevel@tonic-gate 		return (ENOENT);
8827c478bd9Sstevel@tonic-gate 	}
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 	/*
8857c478bd9Sstevel@tonic-gate 	 * Allocate memory to hold the environmental segment data.
8867c478bd9Sstevel@tonic-gate 	 */
8877c478bd9Sstevel@tonic-gate 	envseglen = GET_UNALIGN16(&segment.length);
8887c478bd9Sstevel@tonic-gate 	if ((envseg = malloc(envseglen)) == NULL) {
8897c478bd9Sstevel@tonic-gate 		return (ENOMEM);
8907c478bd9Sstevel@tonic-gate 	}
8917c478bd9Sstevel@tonic-gate 
8927c478bd9Sstevel@tonic-gate 	if (lseek(fd, (long)GET_UNALIGN16(&segment.offset), 0) == -1L ||
8937c478bd9Sstevel@tonic-gate 	    read(fd, envseg, envseglen) != envseglen) {
8947c478bd9Sstevel@tonic-gate 		(void) free(envseg);
8957c478bd9Sstevel@tonic-gate 		return (EIO);
8967c478bd9Sstevel@tonic-gate 	}
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 	*envsegp = envseg;
8997c478bd9Sstevel@tonic-gate 	*envseglenp = envseglen;
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 	if (env_debug > 1) {
9027c478bd9Sstevel@tonic-gate 		char	msgbuf[256];
9037c478bd9Sstevel@tonic-gate 		for (i = 0; i < envseglen; i++) {
9047c478bd9Sstevel@tonic-gate 			(void) sprintf(&msgbuf[3*(i&0xf)], "%2x ", envseg[i]);
9057c478bd9Sstevel@tonic-gate 			if ((i & 0xf) == 0xf || i == (envseglen-1))
9067c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO, "envseg[%2x]: %s\n",
9077c478bd9Sstevel@tonic-gate 				    (i & ~0xf), msgbuf);
9087c478bd9Sstevel@tonic-gate 		}
9097c478bd9Sstevel@tonic-gate 	}
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 	return (0);
9127c478bd9Sstevel@tonic-gate }
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate /*
9167c478bd9Sstevel@tonic-gate  * Get all environmental segments
9177c478bd9Sstevel@tonic-gate  */
9187c478bd9Sstevel@tonic-gate static fruenvseg_t *
get_fru_envsegs(void)9197c478bd9Sstevel@tonic-gate get_fru_envsegs(void)
9207c478bd9Sstevel@tonic-gate {
9217c478bd9Sstevel@tonic-gate 	env_sensor_t		*sensorp;
9227c478bd9Sstevel@tonic-gate 	fruenvseg_t		*frup, *fruenvsegs;
9237c478bd9Sstevel@tonic-gate 	envseg_layout_t		*envsegp;
9247c478bd9Sstevel@tonic-gate 	void			*envsegbufp;
9257c478bd9Sstevel@tonic-gate 	int			fd, envseglen, hdrlen;
9267c478bd9Sstevel@tonic-gate 	char			path[PATH_MAX];
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 	fruenvsegs = NULL;
9297c478bd9Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
9307c478bd9Sstevel@tonic-gate 		if (sensorp->fru == NULL)
9317c478bd9Sstevel@tonic-gate 			continue;
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate 		for (frup = fruenvsegs; frup != NULL; frup = frup->next)
9347c478bd9Sstevel@tonic-gate 			if (strcmp(frup->fru, sensorp->fru) == 0)
9357c478bd9Sstevel@tonic-gate 				break;
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 		if (frup != NULL)
9387c478bd9Sstevel@tonic-gate 			continue;
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 		frup = (fruenvseg_t *)malloc(sizeof (fruenvseg_t));
9417c478bd9Sstevel@tonic-gate 		if (frup == NULL)
9427c478bd9Sstevel@tonic-gate 			continue;
9437c478bd9Sstevel@tonic-gate 
9447c478bd9Sstevel@tonic-gate 		/* add this FRU to our list */
9457c478bd9Sstevel@tonic-gate 		frup->fru = sensorp->fru;
9467c478bd9Sstevel@tonic-gate 		frup->envsegbufp = NULL;
9477c478bd9Sstevel@tonic-gate 		frup->envseglen = 0;
9487c478bd9Sstevel@tonic-gate 		frup->next = fruenvsegs;
9497c478bd9Sstevel@tonic-gate 		fruenvsegs = frup;
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate 		/*
9527c478bd9Sstevel@tonic-gate 		 * Now get the environmental segment from this FRU
9537c478bd9Sstevel@tonic-gate 		 */
9547c478bd9Sstevel@tonic-gate 		(void) strcpy(path, "/devices");
9557c478bd9Sstevel@tonic-gate 		(void) strlcat(path, sensorp->fru, sizeof (path));
9567c478bd9Sstevel@tonic-gate 	retry:
9577c478bd9Sstevel@tonic-gate 		errno = 0;
9587c478bd9Sstevel@tonic-gate 		fd = open(path, O_RDONLY);
9597c478bd9Sstevel@tonic-gate 		if (env_debug > 1)
9607c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
9617c478bd9Sstevel@tonic-gate 			    "fru SEEPROM: %s fd: %d  errno:%d\n",
9627c478bd9Sstevel@tonic-gate 			    path, fd, errno);
9637c478bd9Sstevel@tonic-gate 		if (fd == -1 && errno == ENOENT && fru_present(frup->fru)) {
9647c478bd9Sstevel@tonic-gate 			if (fru_devfsadm_invoked ||
9657c478bd9Sstevel@tonic-gate 			    fru_devfsadm_cmd[0] == '\0') {
9667c478bd9Sstevel@tonic-gate 				envd_log(LOG_CRIT, ENV_FRU_OPEN_FAIL,
9677c478bd9Sstevel@tonic-gate 				    sensorp->fru, errno, strerror(errno));
9687c478bd9Sstevel@tonic-gate 				continue;
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 			}
9717c478bd9Sstevel@tonic-gate 			/*
9727c478bd9Sstevel@tonic-gate 			 * FRU is present but no path exists as
9737c478bd9Sstevel@tonic-gate 			 * someone rebooted the system without
9747c478bd9Sstevel@tonic-gate 			 * "-r" option. Let's invoke "devfsadm"
9757c478bd9Sstevel@tonic-gate 			 * once to create seeprom nodes and try
9767c478bd9Sstevel@tonic-gate 			 * again so that we can monitor all
9777c478bd9Sstevel@tonic-gate 			 * accessible sensors properly and prevent
9787c478bd9Sstevel@tonic-gate 			 * any CPU overheating.
9797c478bd9Sstevel@tonic-gate 			 */
9807c478bd9Sstevel@tonic-gate 			if (env_debug)
9817c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO,
9827c478bd9Sstevel@tonic-gate 				    "Invoking '%s' to create FRU nodes\n",
9837c478bd9Sstevel@tonic-gate 				    fru_devfsadm_cmd);
9847c478bd9Sstevel@tonic-gate 			fru_devfsadm_invoked = 1;
9857c478bd9Sstevel@tonic-gate 			(void) system(fru_devfsadm_cmd);
9867c478bd9Sstevel@tonic-gate 			goto retry;
9877c478bd9Sstevel@tonic-gate 		}
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 		/*
9907c478bd9Sstevel@tonic-gate 		 * Read environmental segment from this FRU SEEPROM
9917c478bd9Sstevel@tonic-gate 		 */
9927c478bd9Sstevel@tonic-gate 		if (get_envseg(fd, &envsegbufp, &envseglen) == 0) {
9937c478bd9Sstevel@tonic-gate 			/*
9947c478bd9Sstevel@tonic-gate 			 * Validate envseg version number and header length
9957c478bd9Sstevel@tonic-gate 			 */
9967c478bd9Sstevel@tonic-gate 			envsegp = (envseg_layout_t *)envsegbufp;
9977c478bd9Sstevel@tonic-gate 			hdrlen = sizeof (envseg_layout_t) -
9987c478bd9Sstevel@tonic-gate 			    sizeof (envseg_sensor_t) +
9997c478bd9Sstevel@tonic-gate 			    (envsegp->sensor_count) * sizeof (envseg_sensor_t);
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 			if (envsegp->version != ENVSEG_VERSION ||
10027c478bd9Sstevel@tonic-gate 			    envseglen < hdrlen) {
10037c478bd9Sstevel@tonic-gate 				/*
10047c478bd9Sstevel@tonic-gate 				 * version mismatch or header not big enough
10057c478bd9Sstevel@tonic-gate 				 */
10067c478bd9Sstevel@tonic-gate 				envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG,
10077c478bd9Sstevel@tonic-gate 				    sensorp->fru, errno, strerror(errno));
10087c478bd9Sstevel@tonic-gate 				if (envsegbufp != NULL)
10097c478bd9Sstevel@tonic-gate 					(void) free(envsegbufp);
10107c478bd9Sstevel@tonic-gate 			} else {
10117c478bd9Sstevel@tonic-gate 				frup->envseglen = envseglen;
10127c478bd9Sstevel@tonic-gate 				frup->envsegbufp = envsegbufp;
10137c478bd9Sstevel@tonic-gate 			}
10147c478bd9Sstevel@tonic-gate 		}
10157c478bd9Sstevel@tonic-gate 		(void) close(fd);
10167c478bd9Sstevel@tonic-gate 	}
10177c478bd9Sstevel@tonic-gate 	return (fruenvsegs);
10187c478bd9Sstevel@tonic-gate }
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate /*
10217c478bd9Sstevel@tonic-gate  * Process environmental segment for all FRUs.
10227c478bd9Sstevel@tonic-gate  */
10237c478bd9Sstevel@tonic-gate static void
process_fru_envseg()10247c478bd9Sstevel@tonic-gate process_fru_envseg()
10257c478bd9Sstevel@tonic-gate {
10267c478bd9Sstevel@tonic-gate 	env_sensor_t		*sensorp;
10277c478bd9Sstevel@tonic-gate 	sensor_thresh_t		*threshp;
10287c478bd9Sstevel@tonic-gate 	envseg_layout_t		*envsegp;
10297c478bd9Sstevel@tonic-gate 	envseg_sensor_data_t	*datap;
10307c478bd9Sstevel@tonic-gate 	fruenvseg_t		*frup, *fruenvsegs;
10317c478bd9Sstevel@tonic-gate 	int			i, envseglen, sensorcnt;
10327c478bd9Sstevel@tonic-gate 	uint_t			offset, length, mapentries;
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 	/*
10357c478bd9Sstevel@tonic-gate 	 * Lookup/read environmental segments from FRU SEEPROMs and
10367c478bd9Sstevel@tonic-gate 	 * process it. Note that we read each SEEPROM once as it's
10377c478bd9Sstevel@tonic-gate 	 * a slow device.
10387c478bd9Sstevel@tonic-gate 	 */
10397c478bd9Sstevel@tonic-gate 	fruenvsegs = get_fru_envsegs();
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
10427c478bd9Sstevel@tonic-gate 		if (sensorp->fru == NULL)
10437c478bd9Sstevel@tonic-gate 			continue;
10447c478bd9Sstevel@tonic-gate 
10457c478bd9Sstevel@tonic-gate 		/*
10467c478bd9Sstevel@tonic-gate 		 * Locate our FRU environmental segment
10477c478bd9Sstevel@tonic-gate 		 */
10487c478bd9Sstevel@tonic-gate 		for (frup = fruenvsegs; frup != NULL; frup = frup->next)
10497c478bd9Sstevel@tonic-gate 			if (strcmp(frup->fru, sensorp->fru) == 0)
10507c478bd9Sstevel@tonic-gate 				break;
10517c478bd9Sstevel@tonic-gate 		if (frup == NULL || frup->envsegbufp == NULL)
10527c478bd9Sstevel@tonic-gate 			continue;
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate 		envsegp = (envseg_layout_t *)frup->envsegbufp;
10557c478bd9Sstevel@tonic-gate 		envseglen = frup->envseglen;
10567c478bd9Sstevel@tonic-gate 		sensorcnt = envsegp->sensor_count;
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 		/*
10597c478bd9Sstevel@tonic-gate 		 * Locate our sensor data record entry
10607c478bd9Sstevel@tonic-gate 		 */
10617c478bd9Sstevel@tonic-gate 		for (i = 0; i < sensorcnt; i++) {
10627c478bd9Sstevel@tonic-gate 			uint32_t	id;
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 			id = GET_UNALIGN32(&envsegp->sensors[i].sensor_id[0]);
10657c478bd9Sstevel@tonic-gate 			if (env_debug > 1)
10667c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO, " sensor[%d]: id:%x\n",
10677c478bd9Sstevel@tonic-gate 				    i, id);
10687c478bd9Sstevel@tonic-gate 			if (id == sensorp->fru_sensor)
10697c478bd9Sstevel@tonic-gate 				break;
10707c478bd9Sstevel@tonic-gate 		}
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 		if (i >= sensorcnt)
10737c478bd9Sstevel@tonic-gate 			continue;
10747c478bd9Sstevel@tonic-gate 
10757c478bd9Sstevel@tonic-gate 		/*
10767c478bd9Sstevel@tonic-gate 		 * Validate offset/length of our sensor data record
10777c478bd9Sstevel@tonic-gate 		 */
10787c478bd9Sstevel@tonic-gate 		offset = (uint_t)GET_UNALIGN16(&envsegp->sensors[i].offset);
10797c478bd9Sstevel@tonic-gate 		datap =  (envseg_sensor_data_t *)((intptr_t)frup->envsegbufp +
10807c478bd9Sstevel@tonic-gate 		    offset);
10817c478bd9Sstevel@tonic-gate 		mapentries =  GET_UNALIGN16(&datap->obs2exp_cnt);
10827c478bd9Sstevel@tonic-gate 		length = sizeof (envseg_sensor_data_t) - sizeof (envseg_map_t) +
10837c478bd9Sstevel@tonic-gate 		    mapentries * sizeof (envseg_map_t);
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate 		if (env_debug > 1)
10867c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO, "Found sensor_id:%x idx:%x "
10877c478bd9Sstevel@tonic-gate 			"off:%x #maps:%x expected length:%x\n",
10887c478bd9Sstevel@tonic-gate 				sensorp->fru_sensor, i, offset,
10897c478bd9Sstevel@tonic-gate 				mapentries, length);
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 		if (offset >= envseglen || (offset+length) > envseglen) {
10927c478bd9Sstevel@tonic-gate 			/* corrupted sensor record */
10937c478bd9Sstevel@tonic-gate 			envd_log(LOG_CRIT, ENV_FRU_BAD_SENSOR_ENTRY,
10947c478bd9Sstevel@tonic-gate 			    sensorp->fru_sensor, sensorp->name, sensorp->fru);
10957c478bd9Sstevel@tonic-gate 			continue;
10967c478bd9Sstevel@tonic-gate 		}
10977c478bd9Sstevel@tonic-gate 
10987c478bd9Sstevel@tonic-gate 		if (env_debug > 1) {
10997c478bd9Sstevel@tonic-gate 			/* print threshold values */
11007c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
11017c478bd9Sstevel@tonic-gate 			    "Thresholds: HPwrOff %d  HShutDn %d  HWarn %d\n",
11027c478bd9Sstevel@tonic-gate 			    datap->high_power_off, datap->high_shutdown,
11037c478bd9Sstevel@tonic-gate 			    datap->high_warning);
11047c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
11057c478bd9Sstevel@tonic-gate 			    "Thresholds: LWarn %d  LShutDn %d  LPwrOff %d\n",
11067c478bd9Sstevel@tonic-gate 			    datap->low_warning, datap->low_shutdown,
11077c478bd9Sstevel@tonic-gate 			    datap->low_power_off);
11087c478bd9Sstevel@tonic-gate 
11097c478bd9Sstevel@tonic-gate 			/* print policy data */
11107c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
11117c478bd9Sstevel@tonic-gate 			    " Policy type: %d #%d data: %x %x %x %x %x %x\n",
11127c478bd9Sstevel@tonic-gate 			    datap->policy_type, datap->policy_entries,
11137c478bd9Sstevel@tonic-gate 			    datap->policy_data[0], datap->policy_data[1],
11147c478bd9Sstevel@tonic-gate 			    datap->policy_data[2], datap->policy_data[3],
11157c478bd9Sstevel@tonic-gate 			    datap->policy_data[4], datap->policy_data[5]);
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 			/* print map table */
11187c478bd9Sstevel@tonic-gate 			for (i = 0; i < mapentries; i++) {
11197c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO, " Map pair# %d: %d %d\n",
11207c478bd9Sstevel@tonic-gate 				    i, datap->obs2exp_map[i].observed,
11217c478bd9Sstevel@tonic-gate 				    datap->obs2exp_map[i].expected);
11227c478bd9Sstevel@tonic-gate 			}
11237c478bd9Sstevel@tonic-gate 		}
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate 		/*
11277c478bd9Sstevel@tonic-gate 		 * Copy threshold values
11287c478bd9Sstevel@tonic-gate 		 */
11297c478bd9Sstevel@tonic-gate 		threshp = sensorp->temp_thresh;
11307c478bd9Sstevel@tonic-gate 		threshp->high_power_off = datap->high_power_off;
11317c478bd9Sstevel@tonic-gate 		threshp->high_shutdown = datap->high_shutdown;
11327c478bd9Sstevel@tonic-gate 		threshp->high_warning = datap->high_warning;
11337c478bd9Sstevel@tonic-gate 		threshp->low_warning = datap->low_warning;
11347c478bd9Sstevel@tonic-gate 		threshp->low_shutdown = datap->low_shutdown;
11357c478bd9Sstevel@tonic-gate 		threshp->low_power_off = datap->low_power_off;
11367c478bd9Sstevel@tonic-gate 
11377c478bd9Sstevel@tonic-gate 		/*
11387c478bd9Sstevel@tonic-gate 		 * Copy policy data
11397c478bd9Sstevel@tonic-gate 		 */
11407c478bd9Sstevel@tonic-gate 		threshp->policy_type = datap->policy_type;
11417c478bd9Sstevel@tonic-gate 		threshp->policy_entries = datap->policy_entries;
11427c478bd9Sstevel@tonic-gate 		for (i = 0; i < MAX_POLICY_ENTRIES; i++)
11437c478bd9Sstevel@tonic-gate 			threshp->policy_data[i] =
11447c478bd9Sstevel@tonic-gate 			    (tempr_t)datap->policy_data[i];
11457c478bd9Sstevel@tonic-gate 
11467c478bd9Sstevel@tonic-gate 		/*
11477c478bd9Sstevel@tonic-gate 		 * Copy temperature mapping info (discard duplicate entries)
11487c478bd9Sstevel@tonic-gate 		 */
11497c478bd9Sstevel@tonic-gate 		if (sensorp->obs2exp_map) {
11507c478bd9Sstevel@tonic-gate 			(void) free(sensorp->obs2exp_map);
11517c478bd9Sstevel@tonic-gate 			sensorp->obs2exp_map = NULL;
11527c478bd9Sstevel@tonic-gate 			sensorp->obs2exp_cnt = 0;
11537c478bd9Sstevel@tonic-gate 		}
11547c478bd9Sstevel@tonic-gate 		if (mapentries > 0) {
11557c478bd9Sstevel@tonic-gate 			tempr_map_t	*map;
11567c478bd9Sstevel@tonic-gate 			int		cnt;
11577c478bd9Sstevel@tonic-gate 			tempr_t		observed, expected;
11587c478bd9Sstevel@tonic-gate 
11597c478bd9Sstevel@tonic-gate 			map = (tempr_map_t *)malloc(mapentries *
11607c478bd9Sstevel@tonic-gate 			    sizeof (tempr_map_t));
11617c478bd9Sstevel@tonic-gate 
11627c478bd9Sstevel@tonic-gate 			if (map == NULL) {
11637c478bd9Sstevel@tonic-gate 				envd_log(LOG_CRIT, ENV_FRU_SENSOR_MAP_NOMEM,
11647c478bd9Sstevel@tonic-gate 				    sensorp->fru_sensor, sensorp->name,
11657c478bd9Sstevel@tonic-gate 				    sensorp->fru);
11667c478bd9Sstevel@tonic-gate 				continue;
11677c478bd9Sstevel@tonic-gate 			}
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate 			for (i = 0, cnt = 0; i < mapentries; i++) {
11707c478bd9Sstevel@tonic-gate 
11717c478bd9Sstevel@tonic-gate 				observed = (tempr_t)
11727c478bd9Sstevel@tonic-gate 				    datap->obs2exp_map[i].observed;
1173*360e6f5eSmathue 				expected = (tempr_t)
11747c478bd9Sstevel@tonic-gate 				    datap->obs2exp_map[i].expected;
11757c478bd9Sstevel@tonic-gate 
11767c478bd9Sstevel@tonic-gate 				/* ignore if duplicate entry */
11777c478bd9Sstevel@tonic-gate 				if (cnt > 0 &&
11787c478bd9Sstevel@tonic-gate 				    observed == map[cnt-1].observed &&
11797c478bd9Sstevel@tonic-gate 				    expected == map[cnt-1].expected) {
11807c478bd9Sstevel@tonic-gate 					continue;
11817c478bd9Sstevel@tonic-gate 				}
11827c478bd9Sstevel@tonic-gate 				map[cnt].observed = observed;
11837c478bd9Sstevel@tonic-gate 				map[cnt].expected = expected;
11847c478bd9Sstevel@tonic-gate 				cnt++;
11857c478bd9Sstevel@tonic-gate 			}
11867c478bd9Sstevel@tonic-gate 			sensorp->obs2exp_cnt = cnt;
11877c478bd9Sstevel@tonic-gate 			sensorp->obs2exp_map = map;
11887c478bd9Sstevel@tonic-gate 		}
11897c478bd9Sstevel@tonic-gate 
11907c478bd9Sstevel@tonic-gate 		if (env_debug > 2 && sensorp->obs2exp_cnt > 1) {
11917c478bd9Sstevel@tonic-gate 			char	msgbuf[256];
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
11947c478bd9Sstevel@tonic-gate 			    "Measured --> Correct temperature table "
11957c478bd9Sstevel@tonic-gate 			    "for sensor: %s\n", sensorp->name);
11967c478bd9Sstevel@tonic-gate 			for (i = -128; i < 128; i++) {
11977c478bd9Sstevel@tonic-gate 				(void) sprintf(&msgbuf[6*(i&0x7)], "%6d",
11987c478bd9Sstevel@tonic-gate 				    xlate_obs2exp(sensorp, i));
11997c478bd9Sstevel@tonic-gate 				if ((i &0x7) == 0x7)
12007c478bd9Sstevel@tonic-gate 					envd_log(LOG_INFO,
12017c478bd9Sstevel@tonic-gate 					    "%8d: %s\n", (i & ~0x7), msgbuf);
12027c478bd9Sstevel@tonic-gate 			}
12037c478bd9Sstevel@tonic-gate 			if ((i & 0x7) != 0)
12047c478bd9Sstevel@tonic-gate 				(void) printf("%8d: %s\n", (i & ~0x7), msgbuf);
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
12077c478bd9Sstevel@tonic-gate 			    "Correct --> Measured temperature table "
12087c478bd9Sstevel@tonic-gate 			    "for sensor: %s\n", sensorp->name);
12097c478bd9Sstevel@tonic-gate 			for (i = -128; i < 128; i++) {
12107c478bd9Sstevel@tonic-gate 				(void) sprintf(&msgbuf[6*(i&0x7)], "%6d",
12117c478bd9Sstevel@tonic-gate 				    xlate_exp2obs(sensorp, i));
12127c478bd9Sstevel@tonic-gate 				if ((i &0x7) == 0x7)
12137c478bd9Sstevel@tonic-gate 					envd_log(LOG_INFO,
12147c478bd9Sstevel@tonic-gate 					    "%8d: %s\n", (i & ~0x7), msgbuf);
12157c478bd9Sstevel@tonic-gate 			}
12167c478bd9Sstevel@tonic-gate 			if ((i & 0x7) != 0)
12177c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO,
12187c478bd9Sstevel@tonic-gate 				    "%8d: %s\n", (i & ~0x7), msgbuf);
12197c478bd9Sstevel@tonic-gate 		}
12207c478bd9Sstevel@tonic-gate 	}
12217c478bd9Sstevel@tonic-gate 
12227c478bd9Sstevel@tonic-gate 	/*
12237c478bd9Sstevel@tonic-gate 	 * Deallocate environmental segment list
12247c478bd9Sstevel@tonic-gate 	 */
12257c478bd9Sstevel@tonic-gate 	while (fruenvsegs) {
12267c478bd9Sstevel@tonic-gate 		frup = fruenvsegs;
12277c478bd9Sstevel@tonic-gate 		fruenvsegs = frup->next;
12287c478bd9Sstevel@tonic-gate 		if (frup->envsegbufp != NULL)
12297c478bd9Sstevel@tonic-gate 			(void) free(frup->envsegbufp);
12307c478bd9Sstevel@tonic-gate 		(void) free(frup);
12317c478bd9Sstevel@tonic-gate 	}
12327c478bd9Sstevel@tonic-gate }
12337c478bd9Sstevel@tonic-gate 
12347c478bd9Sstevel@tonic-gate /*
12357c478bd9Sstevel@tonic-gate  * Lookup fan and return a pointer to env_fan_t data structure.
12367c478bd9Sstevel@tonic-gate  */
12377c478bd9Sstevel@tonic-gate env_fan_t *
fan_lookup(char * name)12387c478bd9Sstevel@tonic-gate fan_lookup(char *name)
12397c478bd9Sstevel@tonic-gate {
12407c478bd9Sstevel@tonic-gate 	int		i;
12417c478bd9Sstevel@tonic-gate 	env_fan_t	*fanp;
12427c478bd9Sstevel@tonic-gate 
12437c478bd9Sstevel@tonic-gate 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
12447c478bd9Sstevel@tonic-gate 		if (strcmp(fanp->name, name) == 0)
12457c478bd9Sstevel@tonic-gate 			return (fanp);
12467c478bd9Sstevel@tonic-gate 	}
12477c478bd9Sstevel@tonic-gate 	return (NULL);
12487c478bd9Sstevel@tonic-gate }
12497c478bd9Sstevel@tonic-gate 
12507c478bd9Sstevel@tonic-gate /*
12517c478bd9Sstevel@tonic-gate  * Lookup sensor and return a pointer to env_sensor_t data structure.
12527c478bd9Sstevel@tonic-gate  */
12537c478bd9Sstevel@tonic-gate env_sensor_t *
sensor_lookup(char * name)12547c478bd9Sstevel@tonic-gate sensor_lookup(char *name)
12557c478bd9Sstevel@tonic-gate {
12567c478bd9Sstevel@tonic-gate 	env_sensor_t	*sensorp;
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
12597c478bd9Sstevel@tonic-gate 		if (strcmp(sensorp->name, name) == 0)
12607c478bd9Sstevel@tonic-gate 			return (sensorp);
12617c478bd9Sstevel@tonic-gate 	}
12627c478bd9Sstevel@tonic-gate 	return (NULL);
12637c478bd9Sstevel@tonic-gate }
12647c478bd9Sstevel@tonic-gate 
12657c478bd9Sstevel@tonic-gate /*
12667c478bd9Sstevel@tonic-gate  * Get current temperature
12677c478bd9Sstevel@tonic-gate  * Returns -1 on error, 0 if successful
12687c478bd9Sstevel@tonic-gate  */
12697c478bd9Sstevel@tonic-gate int
get_temperature(env_sensor_t * sensorp,tempr_t * temp)12707c478bd9Sstevel@tonic-gate get_temperature(env_sensor_t *sensorp, tempr_t *temp)
12717c478bd9Sstevel@tonic-gate {
12727c478bd9Sstevel@tonic-gate 	int	fd = sensorp->fd;
12737c478bd9Sstevel@tonic-gate 	int	retval = 0;
12747c478bd9Sstevel@tonic-gate 	int	expected_temp;
12757c478bd9Sstevel@tonic-gate 
12767c478bd9Sstevel@tonic-gate 	if (fd == -1)
12777c478bd9Sstevel@tonic-gate 		retval = -1;
12787c478bd9Sstevel@tonic-gate 	else if (ioctl(fd, I2C_GET_TEMPERATURE, temp) == -1) {
12797c478bd9Sstevel@tonic-gate 		retval = -1;
12807c478bd9Sstevel@tonic-gate 		if (sensorp->error == 0) {
12817c478bd9Sstevel@tonic-gate 			sensorp->error = 1;
12827c478bd9Sstevel@tonic-gate 			envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_FAIL,
12837c478bd9Sstevel@tonic-gate 			    sensorp->name, errno, strerror(errno));
12847c478bd9Sstevel@tonic-gate 		}
12857c478bd9Sstevel@tonic-gate 	} else if (sensorp->error != 0) {
12867c478bd9Sstevel@tonic-gate 		sensorp->error = 0;
12877c478bd9Sstevel@tonic-gate 		envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_OK, sensorp->name);
12887c478bd9Sstevel@tonic-gate 	} else if (sensorp->obs2exp_map != NULL) {
12897c478bd9Sstevel@tonic-gate 		expected_temp = xlate_obs2exp(sensorp, (tempr_t)*temp);
12907c478bd9Sstevel@tonic-gate 		if (env_debug > 1)
12917c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
12927c478bd9Sstevel@tonic-gate 			    "sensor: %-13s temp:%d  CORRECED to %d\n",
12937c478bd9Sstevel@tonic-gate 			    sensorp->name, *temp, (tempr_t)expected_temp);
12947c478bd9Sstevel@tonic-gate 		*temp = (tempr_t)expected_temp;
12957c478bd9Sstevel@tonic-gate 	}
12967c478bd9Sstevel@tonic-gate 
12977c478bd9Sstevel@tonic-gate 	return (retval);
12987c478bd9Sstevel@tonic-gate }
12997c478bd9Sstevel@tonic-gate 
13007c478bd9Sstevel@tonic-gate /*
13017c478bd9Sstevel@tonic-gate  * Get current fan speed
13027c478bd9Sstevel@tonic-gate  * Returns -1 on error, 0 if successful
13037c478bd9Sstevel@tonic-gate  */
13047c478bd9Sstevel@tonic-gate int
get_fan_speed(env_fan_t * fanp,fanspeed_t * fanspeedp)13057c478bd9Sstevel@tonic-gate get_fan_speed(env_fan_t *fanp, fanspeed_t *fanspeedp)
13067c478bd9Sstevel@tonic-gate {
13077c478bd9Sstevel@tonic-gate 	int	fan_fd;
13087c478bd9Sstevel@tonic-gate 	int	retval = 0;
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate 	fan_fd = fanp->fd;
13117c478bd9Sstevel@tonic-gate 	if (fan_fd == -1 || read(fan_fd, fanspeedp, sizeof (fanspeed_t)) !=
13127c478bd9Sstevel@tonic-gate 	    sizeof (fanspeed_t))
13137c478bd9Sstevel@tonic-gate 		retval = -1;
13147c478bd9Sstevel@tonic-gate 	return (retval);
13157c478bd9Sstevel@tonic-gate }
13167c478bd9Sstevel@tonic-gate 
13177c478bd9Sstevel@tonic-gate /*
13187c478bd9Sstevel@tonic-gate  * Set fan speed
13197c478bd9Sstevel@tonic-gate  * Returns -1 on error, 0 if successful
13207c478bd9Sstevel@tonic-gate  */
13217c478bd9Sstevel@tonic-gate static int
set_fan_speed(env_fan_t * fanp,fanspeed_t fanspeed)13227c478bd9Sstevel@tonic-gate set_fan_speed(env_fan_t *fanp, fanspeed_t fanspeed)
13237c478bd9Sstevel@tonic-gate {
13247c478bd9Sstevel@tonic-gate 	int	fan_fd;
13257c478bd9Sstevel@tonic-gate 	int	retval = 0;
13267c478bd9Sstevel@tonic-gate 
13277c478bd9Sstevel@tonic-gate 	fan_fd = fanp->fd;
13287c478bd9Sstevel@tonic-gate 	if (fan_fd == -1 || write(fan_fd, &fanspeed, sizeof (fanspeed)) !=
13297c478bd9Sstevel@tonic-gate 	    sizeof (fanspeed_t))
13307c478bd9Sstevel@tonic-gate 		retval = -1;
13317c478bd9Sstevel@tonic-gate 	return (retval);
13327c478bd9Sstevel@tonic-gate }
13337c478bd9Sstevel@tonic-gate 
13347c478bd9Sstevel@tonic-gate 
13357c478bd9Sstevel@tonic-gate /*
13367c478bd9Sstevel@tonic-gate  * close all fan devices
13377c478bd9Sstevel@tonic-gate  */
13387c478bd9Sstevel@tonic-gate static void
envd_close_fans(void)13397c478bd9Sstevel@tonic-gate envd_close_fans(void)
13407c478bd9Sstevel@tonic-gate {
13417c478bd9Sstevel@tonic-gate 	int		i;
13427c478bd9Sstevel@tonic-gate 	env_fan_t	*fanp;
13437c478bd9Sstevel@tonic-gate 
13447c478bd9Sstevel@tonic-gate 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
13457c478bd9Sstevel@tonic-gate 		if (fanp->fd != -1) {
13467c478bd9Sstevel@tonic-gate 			(void) close(fanp->fd);
13477c478bd9Sstevel@tonic-gate 			fanp->fd = -1;
13487c478bd9Sstevel@tonic-gate 		}
13497c478bd9Sstevel@tonic-gate 	}
13507c478bd9Sstevel@tonic-gate }
13517c478bd9Sstevel@tonic-gate 
13527c478bd9Sstevel@tonic-gate /*
13537c478bd9Sstevel@tonic-gate  * Close sensor devices
13547c478bd9Sstevel@tonic-gate  */
13557c478bd9Sstevel@tonic-gate static void
envd_close_sensors(void)13567c478bd9Sstevel@tonic-gate envd_close_sensors(void)
13577c478bd9Sstevel@tonic-gate {
13587c478bd9Sstevel@tonic-gate 	env_sensor_t	*sensorp;
13597c478bd9Sstevel@tonic-gate 
13607c478bd9Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
13617c478bd9Sstevel@tonic-gate 		if (sensorp->fd != -1) {
13627c478bd9Sstevel@tonic-gate 			(void) close(sensorp->fd);
13637c478bd9Sstevel@tonic-gate 			sensorp->fd = -1;
13647c478bd9Sstevel@tonic-gate 		}
13657c478bd9Sstevel@tonic-gate 	}
13667c478bd9Sstevel@tonic-gate }
13677c478bd9Sstevel@tonic-gate 
13687c478bd9Sstevel@tonic-gate /*
13697c478bd9Sstevel@tonic-gate  * Open PM device
13707c478bd9Sstevel@tonic-gate  */
13717c478bd9Sstevel@tonic-gate static void
envd_open_pm(void)13727c478bd9Sstevel@tonic-gate envd_open_pm(void)
13737c478bd9Sstevel@tonic-gate {
13747c478bd9Sstevel@tonic-gate 	pm_fd = open(PM_DEVICE, O_RDONLY);
13757c478bd9Sstevel@tonic-gate 	if (pm_fd != -1)
13767c478bd9Sstevel@tonic-gate 		(void) fcntl(pm_fd, F_SETFD, FD_CLOEXEC);
13777c478bd9Sstevel@tonic-gate }
13787c478bd9Sstevel@tonic-gate 
13797c478bd9Sstevel@tonic-gate /*
13807c478bd9Sstevel@tonic-gate  * Close PM device
13817c478bd9Sstevel@tonic-gate  */
13827c478bd9Sstevel@tonic-gate static void
envd_close_pm(void)13837c478bd9Sstevel@tonic-gate envd_close_pm(void)
13847c478bd9Sstevel@tonic-gate {
13857c478bd9Sstevel@tonic-gate 	if (pm_fd != -1) {
13867c478bd9Sstevel@tonic-gate 		(void) close(pm_fd);
13877c478bd9Sstevel@tonic-gate 		pm_fd = -1;
13887c478bd9Sstevel@tonic-gate 	}
13897c478bd9Sstevel@tonic-gate }
13907c478bd9Sstevel@tonic-gate 
13917c478bd9Sstevel@tonic-gate /*
13927c478bd9Sstevel@tonic-gate  * Open fan devices and initialize per fan data structure.
13937c478bd9Sstevel@tonic-gate  * Returns #fans found.
13947c478bd9Sstevel@tonic-gate  */
13957c478bd9Sstevel@tonic-gate static int
envd_setup_fans(void)13967c478bd9Sstevel@tonic-gate envd_setup_fans(void)
13977c478bd9Sstevel@tonic-gate {
13987c478bd9Sstevel@tonic-gate 	int		i, fd;
13997c478bd9Sstevel@tonic-gate 	fanspeed_t	speed;
14007c478bd9Sstevel@tonic-gate 	env_fan_t	*fanp;
14017c478bd9Sstevel@tonic-gate 	char		path[PATH_MAX];
14027c478bd9Sstevel@tonic-gate 	int		fancnt = 0;
14037c478bd9Sstevel@tonic-gate 	char		*fan_name;
14047c478bd9Sstevel@tonic-gate 	sensor_fan_map_t *sfmap;
14057c478bd9Sstevel@tonic-gate 	env_sensor_t	*sensorp;
14067c478bd9Sstevel@tonic-gate 	int		sensor_cnt;
14077c478bd9Sstevel@tonic-gate 
14087c478bd9Sstevel@tonic-gate 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
14097c478bd9Sstevel@tonic-gate 		if (fanp->fd == -1) {
14107c478bd9Sstevel@tonic-gate 			fanp->sensor_cnt = 0;
14117c478bd9Sstevel@tonic-gate 			fanp->cur_speed = 0;
14127c478bd9Sstevel@tonic-gate 			fanp->prev_speed = 0;
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate 			(void) strcpy(path, "/devices");
14157c478bd9Sstevel@tonic-gate 			(void) strlcat(path, fanp->devfs_path, sizeof (path));
14167c478bd9Sstevel@tonic-gate 			fd = open(path, O_RDWR);
14177c478bd9Sstevel@tonic-gate 			if (fd == -1) {
14187c478bd9Sstevel@tonic-gate 				envd_log(LOG_CRIT,
14197c478bd9Sstevel@tonic-gate 				    ENV_FAN_OPEN_FAIL, fanp->name,
14207c478bd9Sstevel@tonic-gate 				    fanp->devfs_path, errno, strerror(errno));
14217c478bd9Sstevel@tonic-gate 				fanp->present = B_FALSE;
14227c478bd9Sstevel@tonic-gate 				continue;
14237c478bd9Sstevel@tonic-gate 			}
14247c478bd9Sstevel@tonic-gate 			(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
14257c478bd9Sstevel@tonic-gate 			fanp->fd = fd;
14267c478bd9Sstevel@tonic-gate 			fanp->present = B_TRUE;
14277c478bd9Sstevel@tonic-gate 		}
14287c478bd9Sstevel@tonic-gate 		fancnt++;
14297c478bd9Sstevel@tonic-gate 
14307c478bd9Sstevel@tonic-gate 		/*
14317c478bd9Sstevel@tonic-gate 		 * Set initial speed and update cur_speed/prev_speed
14327c478bd9Sstevel@tonic-gate 		 */
14337c478bd9Sstevel@tonic-gate 		if (fanp->forced_speed >= 0) {
14347c478bd9Sstevel@tonic-gate 			speed = (fanspeed_t)fanp->forced_speed;
14357c478bd9Sstevel@tonic-gate 			if (speed > fanp->speed_max)
14367c478bd9Sstevel@tonic-gate 				speed = fanp->speed_max;
14377c478bd9Sstevel@tonic-gate 			if (!disable_piclenvd)
14387c478bd9Sstevel@tonic-gate 				(void) set_fan_speed(fanp, speed);
14397c478bd9Sstevel@tonic-gate 		} else if (get_fan_speed(fanp, &speed) == -1) {
14407c478bd9Sstevel@tonic-gate 			/*
14417c478bd9Sstevel@tonic-gate 			 * The Fan driver does not know the current fan speed.
14427c478bd9Sstevel@tonic-gate 			 * Initialize all ON/OFF fans to ON state and all
14437c478bd9Sstevel@tonic-gate 			 * variable speed fans under software control to 50%
14447c478bd9Sstevel@tonic-gate 			 * of the max speed and reread the fan to get the
14457c478bd9Sstevel@tonic-gate 			 * current speed.
14467c478bd9Sstevel@tonic-gate 			 */
14477c478bd9Sstevel@tonic-gate 			speed = (fanp == &envd_psupply_fan) ?
14487c478bd9Sstevel@tonic-gate 				fanp->speed_max : fanp->speed_max/2;
14497c478bd9Sstevel@tonic-gate 			if (!disable_piclenvd) {
14507c478bd9Sstevel@tonic-gate 				(void) set_fan_speed(fanp, speed);
14517c478bd9Sstevel@tonic-gate 				if (get_fan_speed(fanp, &speed) == -1)
14527c478bd9Sstevel@tonic-gate 					continue;
14537c478bd9Sstevel@tonic-gate 			}
14547c478bd9Sstevel@tonic-gate 		}
14557c478bd9Sstevel@tonic-gate 		fanp->cur_speed = speed;
14567c478bd9Sstevel@tonic-gate 		fanp->prev_speed = speed;
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 		/*
14597c478bd9Sstevel@tonic-gate 		 * Process sensor_fan_map[] table and initialize sensors[]
14607c478bd9Sstevel@tonic-gate 		 * array for this fan.
14617c478bd9Sstevel@tonic-gate 		 */
14627c478bd9Sstevel@tonic-gate 		fan_name = fanp->name;
14637c478bd9Sstevel@tonic-gate 		for (sensor_cnt = 0, sfmap = &sensor_fan_map[0];
14647c478bd9Sstevel@tonic-gate 		    sfmap->sensor_name != NULL; sfmap++) {
14657c478bd9Sstevel@tonic-gate 			if (strcmp(sfmap->fan_name, fan_name) != 0)
14667c478bd9Sstevel@tonic-gate 				continue;
14677c478bd9Sstevel@tonic-gate 			sensorp = sensor_lookup(sfmap->sensor_name);
14687c478bd9Sstevel@tonic-gate 			if (sensorp != NULL && sensor_cnt < SENSORS_PER_FAN) {
14697c478bd9Sstevel@tonic-gate 				fanp->sensors[sensor_cnt] = sensorp;
14707c478bd9Sstevel@tonic-gate 				sensor_cnt++;
14717c478bd9Sstevel@tonic-gate 			}
14727c478bd9Sstevel@tonic-gate 		}
14737c478bd9Sstevel@tonic-gate 		fanp->sensor_cnt = sensor_cnt;
14747c478bd9Sstevel@tonic-gate 	}
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate 	return (fancnt);
14777c478bd9Sstevel@tonic-gate }
14787c478bd9Sstevel@tonic-gate 
14797c478bd9Sstevel@tonic-gate 
14807c478bd9Sstevel@tonic-gate /*
14817c478bd9Sstevel@tonic-gate  * Adjust specified sensor target temperature and fan adjustment rate
14827c478bd9Sstevel@tonic-gate  */
14837c478bd9Sstevel@tonic-gate 
14847c478bd9Sstevel@tonic-gate static void
adjust_sensor_target(env_sensor_t * sensorp)14857c478bd9Sstevel@tonic-gate adjust_sensor_target(env_sensor_t *sensorp)
14867c478bd9Sstevel@tonic-gate {
14877c478bd9Sstevel@tonic-gate 	int		target, index;
14887c478bd9Sstevel@tonic-gate 	sensor_pmdev_t	*pmdevp;
14897c478bd9Sstevel@tonic-gate 	sensor_thresh_t	*threshp;
14907c478bd9Sstevel@tonic-gate 	float		rate;
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate 	/*
14937c478bd9Sstevel@tonic-gate 	 * Look at current power state of all power managed devices
14947c478bd9Sstevel@tonic-gate 	 * associated with this sensor and look up the desired target
14957c478bd9Sstevel@tonic-gate 	 * temperature and pick the lowest one of those values. Also,
14967c478bd9Sstevel@tonic-gate 	 * calculate the rate of change based upon whether one or more
14977c478bd9Sstevel@tonic-gate 	 * of the associated power managed devices are not running at
14987c478bd9Sstevel@tonic-gate 	 * full power mode.
14997c478bd9Sstevel@tonic-gate 	 */
15007c478bd9Sstevel@tonic-gate 
15017c478bd9Sstevel@tonic-gate 	if (sensorp == NULL || (threshp = sensorp->temp_thresh) == NULL ||
15027c478bd9Sstevel@tonic-gate 	    threshp->policy_type != POLICY_TARGET_TEMP)
15037c478bd9Sstevel@tonic-gate 		return;
15047c478bd9Sstevel@tonic-gate 
15057c478bd9Sstevel@tonic-gate 	target = threshp->policy_data[0];
15067c478bd9Sstevel@tonic-gate 	rate = 1.0;
15077c478bd9Sstevel@tonic-gate 	for (pmdevp = sensorp->pmdevp; pmdevp != NULL; pmdevp = pmdevp->next) {
15087c478bd9Sstevel@tonic-gate 		index = pmdevp->full_power - pmdevp->cur_power;
15097c478bd9Sstevel@tonic-gate 		if (index <= 0)
15107c478bd9Sstevel@tonic-gate 			continue;
15117c478bd9Sstevel@tonic-gate 
15127c478bd9Sstevel@tonic-gate 		/* not running at full power */
15137c478bd9Sstevel@tonic-gate 		if (index >= threshp->policy_entries)
15147c478bd9Sstevel@tonic-gate 			index = threshp->policy_entries - 1;
15157c478bd9Sstevel@tonic-gate 		if (target > threshp->policy_data[index])
15167c478bd9Sstevel@tonic-gate 			target = threshp->policy_data[index];
15177c478bd9Sstevel@tonic-gate 		if (rate > (float)fan_slow_adjustment/100)
15187c478bd9Sstevel@tonic-gate 			rate = (float)fan_slow_adjustment/100;
15197c478bd9Sstevel@tonic-gate 		if (env_debug > 1)
15207c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
15217c478bd9Sstevel@tonic-gate 			    "pmdev: %-13s new_target:%d  cur:%d power:%d/%d\n",
15227c478bd9Sstevel@tonic-gate 			    pmdevp->pmdev_name, target, sensorp->target_temp,
15237c478bd9Sstevel@tonic-gate 			    pmdevp->cur_power, pmdevp->full_power);
15247c478bd9Sstevel@tonic-gate 	}
15257c478bd9Sstevel@tonic-gate 
15267c478bd9Sstevel@tonic-gate 	if (env_debug)
15277c478bd9Sstevel@tonic-gate 		envd_log(LOG_INFO,
15287c478bd9Sstevel@tonic-gate 		    "sensor: %-13s new_target:%d  cur:%d power:%d/%d\n",
15297c478bd9Sstevel@tonic-gate 		    sensorp->name, target, sensorp->target_temp,
15307c478bd9Sstevel@tonic-gate 		    ((sensorp->pmdevp) ? sensorp->pmdevp->cur_power : -1),
15317c478bd9Sstevel@tonic-gate 		    ((sensorp->pmdevp) ? sensorp->pmdevp->full_power : -1));
15327c478bd9Sstevel@tonic-gate 
15337c478bd9Sstevel@tonic-gate 	sensorp->fan_adjustment_rate = rate;
15347c478bd9Sstevel@tonic-gate 	sensorp->target_temp = target;
15357c478bd9Sstevel@tonic-gate }
15367c478bd9Sstevel@tonic-gate 
15377c478bd9Sstevel@tonic-gate /*
15387c478bd9Sstevel@tonic-gate  * Update current power level of all PM devices we are tracking and adjust
15397c478bd9Sstevel@tonic-gate  * the target temperature associated with the corresponding sensor.
15407c478bd9Sstevel@tonic-gate  *
15417c478bd9Sstevel@tonic-gate  * Returns 1 if one or more pmdev power level was adjusted; 0 otherwise.
15427c478bd9Sstevel@tonic-gate  */
15437c478bd9Sstevel@tonic-gate static int
update_pmdev_power()15447c478bd9Sstevel@tonic-gate update_pmdev_power()
15457c478bd9Sstevel@tonic-gate {
15467c478bd9Sstevel@tonic-gate 	sensor_pmdev_t	*pmdevp;
15477c478bd9Sstevel@tonic-gate 	pm_req_t	pmreq;
15487c478bd9Sstevel@tonic-gate 	int		cur_power;
15497c478bd9Sstevel@tonic-gate 	int		updated = 0;
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate 	for (pmdevp = sensor_pmdevs; pmdevp->pmdev_name != NULL; pmdevp++) {
15527c478bd9Sstevel@tonic-gate 		pmreq.physpath = pmdevp->pmdev_name;
15537c478bd9Sstevel@tonic-gate 		pmreq.data = NULL;
15547c478bd9Sstevel@tonic-gate 		pmreq.datasize = 0;
15557c478bd9Sstevel@tonic-gate 		pmreq.component = pmdevp->speed_comp;
15567c478bd9Sstevel@tonic-gate 		cur_power = ioctl(pm_fd, PM_GET_CURRENT_POWER, &pmreq);
15577c478bd9Sstevel@tonic-gate 		if (pmdevp->cur_power != cur_power) {
15587c478bd9Sstevel@tonic-gate 			pmdevp->cur_power = cur_power;
15597c478bd9Sstevel@tonic-gate 			if (pmdevp->sensorp) {
15607c478bd9Sstevel@tonic-gate 				adjust_sensor_target(pmdevp->sensorp);
15617c478bd9Sstevel@tonic-gate 				updated = 1;
15627c478bd9Sstevel@tonic-gate 			}
15637c478bd9Sstevel@tonic-gate 		}
15647c478bd9Sstevel@tonic-gate 	}
15657c478bd9Sstevel@tonic-gate 	return (updated);
15667c478bd9Sstevel@tonic-gate }
15677c478bd9Sstevel@tonic-gate 
15687c478bd9Sstevel@tonic-gate /*
15697c478bd9Sstevel@tonic-gate  * Check if the specified sensor is present.
15707c478bd9Sstevel@tonic-gate  * Returns 1 if present; 0 otherwise.
15717c478bd9Sstevel@tonic-gate  *
15727c478bd9Sstevel@tonic-gate  * Note that we don't use ptree_get_node_by_path() here to detect
15737c478bd9Sstevel@tonic-gate  * if a temperature device is present as we don't want to make
15747c478bd9Sstevel@tonic-gate  * "devtree" a critical plugin.
15757c478bd9Sstevel@tonic-gate  */
15767c478bd9Sstevel@tonic-gate static int
envd_sensor_present(env_sensor_t * sensorp)15777c478bd9Sstevel@tonic-gate envd_sensor_present(env_sensor_t *sensorp)
15787c478bd9Sstevel@tonic-gate {
15797c478bd9Sstevel@tonic-gate 	char		*p, physpath[PATH_MAX];
15807c478bd9Sstevel@tonic-gate 	di_node_t	root_node;
15817c478bd9Sstevel@tonic-gate 	int		sensor_present = 0;
15827c478bd9Sstevel@tonic-gate 
15837c478bd9Sstevel@tonic-gate 	/*
15847c478bd9Sstevel@tonic-gate 	 * Construct temperature device path by stripping minor
15857c478bd9Sstevel@tonic-gate 	 * node name from the devfs_path and use di_init() to
15867c478bd9Sstevel@tonic-gate 	 * see if the node exists.
15877c478bd9Sstevel@tonic-gate 	 */
15887c478bd9Sstevel@tonic-gate 	(void) strcpy(physpath, sensorp->devfs_path);
15897c478bd9Sstevel@tonic-gate 	p = strrchr(physpath, ':');
15907c478bd9Sstevel@tonic-gate 	if (p != NULL)
15917c478bd9Sstevel@tonic-gate 		*p = '\0';
15927c478bd9Sstevel@tonic-gate 	if ((root_node = di_init(physpath, DINFOMINOR)) != DI_NODE_NIL) {
15937c478bd9Sstevel@tonic-gate 		di_fini(root_node);
15947c478bd9Sstevel@tonic-gate 		sensor_present = 1;
15957c478bd9Sstevel@tonic-gate 	}
15967c478bd9Sstevel@tonic-gate 	return (sensor_present);
15977c478bd9Sstevel@tonic-gate }
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate /*
16007c478bd9Sstevel@tonic-gate  * Open temperature sensor devices and initialize per sensor data structure.
16017c478bd9Sstevel@tonic-gate  * Returns #sensors found.
16027c478bd9Sstevel@tonic-gate  */
16037c478bd9Sstevel@tonic-gate static int
envd_setup_sensors(void)16047c478bd9Sstevel@tonic-gate envd_setup_sensors(void)
16057c478bd9Sstevel@tonic-gate {
16067c478bd9Sstevel@tonic-gate 	tempr_t		temp;
16077c478bd9Sstevel@tonic-gate 	env_sensor_t	*sensorp;
16087c478bd9Sstevel@tonic-gate 	char		path[PATH_MAX];
16097c478bd9Sstevel@tonic-gate 	int		sensorcnt = 0;
16107c478bd9Sstevel@tonic-gate 	int		sensor_present;
16117c478bd9Sstevel@tonic-gate 	sensor_thresh_t	*threshp;
16127c478bd9Sstevel@tonic-gate 	sensor_pmdev_t	*pmdevp;
16137c478bd9Sstevel@tonic-gate 
16147c478bd9Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
16157c478bd9Sstevel@tonic-gate 		if (sensorp->fd != -1) {
16167c478bd9Sstevel@tonic-gate 			/* Don't reinitialize opened sensor */
16177c478bd9Sstevel@tonic-gate 			threshp = sensorp->temp_thresh;
16187c478bd9Sstevel@tonic-gate 			sensorp->pmdevp = NULL;
16197c478bd9Sstevel@tonic-gate 		} else {
16207c478bd9Sstevel@tonic-gate 			/* Initialize sensor's initial state */
16217c478bd9Sstevel@tonic-gate 			sensorp->shutdown_initiated = B_FALSE;
16227c478bd9Sstevel@tonic-gate 			sensorp->warning_tstamp = 0;
16237c478bd9Sstevel@tonic-gate 			sensorp->warning_start = 0;
16247c478bd9Sstevel@tonic-gate 			sensorp->shutdown_tstamp = 0;
16257c478bd9Sstevel@tonic-gate 			sensorp->pmdevp = NULL;
16267c478bd9Sstevel@tonic-gate 			sensorp->fan_adjustment_rate = 1.0;
16277c478bd9Sstevel@tonic-gate 
16287c478bd9Sstevel@tonic-gate 			threshp = sensorp->temp_thresh;
16297c478bd9Sstevel@tonic-gate 			temp = (threshp && threshp->policy_entries > 0) ?
16307c478bd9Sstevel@tonic-gate 			    threshp->policy_data[0] : 0;
16317c478bd9Sstevel@tonic-gate 			sensorp->target_temp = temp;
16327c478bd9Sstevel@tonic-gate 			sensorp->cur_temp = temp;
16337c478bd9Sstevel@tonic-gate 			sensorp->avg_temp = temp;
16347c478bd9Sstevel@tonic-gate 			sensorp->prev_avg_temp = temp;
16357c478bd9Sstevel@tonic-gate 			sensorp->error = 0;
16367c478bd9Sstevel@tonic-gate 
16377c478bd9Sstevel@tonic-gate 			(void) strcpy(path, "/devices");
16387c478bd9Sstevel@tonic-gate 			(void) strlcat(path, sensorp->devfs_path,
16397c478bd9Sstevel@tonic-gate 			    sizeof (path));
16407c478bd9Sstevel@tonic-gate 		retry:
16417c478bd9Sstevel@tonic-gate 			sensorp->fd = open(path, O_RDWR);
16427c478bd9Sstevel@tonic-gate 			if (sensorp->fd == -1) {
16437c478bd9Sstevel@tonic-gate 				sensor_present = envd_sensor_present(sensorp);
16447c478bd9Sstevel@tonic-gate 				if (sensor_present && !devfsadm_invoked &&
16457c478bd9Sstevel@tonic-gate 				    devfsadm_cmd[0] != '\0') {
16467c478bd9Sstevel@tonic-gate 					/*
16477c478bd9Sstevel@tonic-gate 					 * Sensor is present but no path
16487c478bd9Sstevel@tonic-gate 					 * exists as someone rebooted the
16497c478bd9Sstevel@tonic-gate 					 * system without "-r" option. Let's
16507c478bd9Sstevel@tonic-gate 					 * invoke "devfsadm" once to create
16517c478bd9Sstevel@tonic-gate 					 * max1617 sensors paths in /devices
16527c478bd9Sstevel@tonic-gate 					 * subtree and try again so that we
16537c478bd9Sstevel@tonic-gate 					 * can monitor all accessible sensors
16547c478bd9Sstevel@tonic-gate 					 * and prevent any CPU overheating.
16557c478bd9Sstevel@tonic-gate 					 *
16567c478bd9Sstevel@tonic-gate 					 * Note that this routine is always
16577c478bd9Sstevel@tonic-gate 					 * called in main thread context and
16587c478bd9Sstevel@tonic-gate 					 * serialized with respect to other
16597c478bd9Sstevel@tonic-gate 					 * plugins' initialization. Hence, it's
16607c478bd9Sstevel@tonic-gate 					 * safe to use system(3C) call here.
16617c478bd9Sstevel@tonic-gate 					 */
16627c478bd9Sstevel@tonic-gate 					devfsadm_invoked = 1;
16637c478bd9Sstevel@tonic-gate 					(void) system(devfsadm_cmd);
16647c478bd9Sstevel@tonic-gate 					goto retry;
16657c478bd9Sstevel@tonic-gate 				}
16667c478bd9Sstevel@tonic-gate 				if (sensor_present)
16677c478bd9Sstevel@tonic-gate 					envd_log(LOG_CRIT,
16687c478bd9Sstevel@tonic-gate 					    ENV_SENSOR_OPEN_FAIL,
16697c478bd9Sstevel@tonic-gate 					    sensorp->name,
16707c478bd9Sstevel@tonic-gate 					    sensorp->devfs_path, errno,
16717c478bd9Sstevel@tonic-gate 					    strerror(errno));
16727c478bd9Sstevel@tonic-gate 				sensorp->present = B_FALSE;
16737c478bd9Sstevel@tonic-gate 				continue;
16747c478bd9Sstevel@tonic-gate 			}
16757c478bd9Sstevel@tonic-gate 			(void) fcntl(sensorp->fd, F_SETFD, FD_CLOEXEC);
16767c478bd9Sstevel@tonic-gate 			sensorp->present = B_TRUE;
16777c478bd9Sstevel@tonic-gate 
16787c478bd9Sstevel@tonic-gate 			/*
16797c478bd9Sstevel@tonic-gate 			 * Set cur_temp field to the current temperature value
16807c478bd9Sstevel@tonic-gate 			 */
16817c478bd9Sstevel@tonic-gate 			if (get_temperature(sensorp, &temp) == 0) {
16827c478bd9Sstevel@tonic-gate 				sensorp->cur_temp = temp;
16837c478bd9Sstevel@tonic-gate 				sensorp->avg_temp = temp;
16847c478bd9Sstevel@tonic-gate 			}
16857c478bd9Sstevel@tonic-gate 		}
16867c478bd9Sstevel@tonic-gate 		sensorcnt++;
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 		/*
16897c478bd9Sstevel@tonic-gate 		 * Set low_power_off and high_power_off limits
16907c478bd9Sstevel@tonic-gate 		 */
16917c478bd9Sstevel@tonic-gate 		if (threshp && !disable_power_off) {
16927c478bd9Sstevel@tonic-gate 			temp = xlate_exp2obs(sensorp, threshp->low_power_off);
16937c478bd9Sstevel@tonic-gate 			if (env_debug > 1)
16947c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO, "sensor: %-13s low_power_"
16957c478bd9Sstevel@tonic-gate 				"off set to %d (real %d)\n", sensorp->name,
16967c478bd9Sstevel@tonic-gate 				    (int)temp, threshp->low_power_off);
16977c478bd9Sstevel@tonic-gate 			(void) ioctl(sensorp->fd, MAX1617_SET_LOW_LIMIT, &temp);
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 			temp = xlate_exp2obs(sensorp, threshp->high_power_off);
17007c478bd9Sstevel@tonic-gate 			if (env_debug > 1)
17017c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO, "sensor: %-13s high_power_"
17027c478bd9Sstevel@tonic-gate 				"off set to %d (real %d)\n", sensorp->name,
17037c478bd9Sstevel@tonic-gate 				    (int)temp, threshp->high_power_off);
17047c478bd9Sstevel@tonic-gate 			(void) ioctl(sensorp->fd, MAX1617_SET_HIGH_LIMIT,
17057c478bd9Sstevel@tonic-gate 			    &temp);
17067c478bd9Sstevel@tonic-gate 		}
17077c478bd9Sstevel@tonic-gate 	}
17087c478bd9Sstevel@tonic-gate 
17097c478bd9Sstevel@tonic-gate 	/*
17107c478bd9Sstevel@tonic-gate 	 * Locate "CPU Speed" component for any PM devices associated with
17117c478bd9Sstevel@tonic-gate 	 * the sensors.
17127c478bd9Sstevel@tonic-gate 	 */
17137c478bd9Sstevel@tonic-gate 	for (pmdevp = sensor_pmdevs; pmdevp->sensor_name; pmdevp++) {
17147c478bd9Sstevel@tonic-gate 		int		i, ncomp;
17157c478bd9Sstevel@tonic-gate 		char		physpath[PATH_MAX];
17167c478bd9Sstevel@tonic-gate 		pm_req_t	pmreq;
17177c478bd9Sstevel@tonic-gate 
17187c478bd9Sstevel@tonic-gate 		pmdevp->speed_comp = -1;
17197c478bd9Sstevel@tonic-gate 		pmdevp->full_power = -1;
17207c478bd9Sstevel@tonic-gate 		pmdevp->cur_power = -1;
17217c478bd9Sstevel@tonic-gate 		pmdevp->next = NULL;
17227c478bd9Sstevel@tonic-gate 		pmdevp->sensorp = sensorp = sensor_lookup(pmdevp->sensor_name);
17237c478bd9Sstevel@tonic-gate 
17247c478bd9Sstevel@tonic-gate 		/*
17257c478bd9Sstevel@tonic-gate 		 * Lookup speed component and get full and current power
17267c478bd9Sstevel@tonic-gate 		 * level for that component.
17277c478bd9Sstevel@tonic-gate 		 */
17287c478bd9Sstevel@tonic-gate 		pmreq.physpath = pmdevp->pmdev_name;
17297c478bd9Sstevel@tonic-gate 		pmreq.data = physpath;
17307c478bd9Sstevel@tonic-gate 		pmreq.datasize = sizeof (physpath);
17317c478bd9Sstevel@tonic-gate 
17327c478bd9Sstevel@tonic-gate 		ncomp = ioctl(pm_fd, PM_GET_NUM_COMPONENTS, &pmreq);
17337c478bd9Sstevel@tonic-gate 		for (i = 0; i < ncomp; i++) {
17347c478bd9Sstevel@tonic-gate 			pmreq.component = i;
17357c478bd9Sstevel@tonic-gate 			physpath[0] = '\0';
17367c478bd9Sstevel@tonic-gate 			if (ioctl(pm_fd, PM_GET_COMPONENT_NAME, &pmreq) <= 0)
17377c478bd9Sstevel@tonic-gate 				continue;
17387c478bd9Sstevel@tonic-gate 			if (strcasecmp(pmreq.data, pmdevp->speed_comp_name))
17397c478bd9Sstevel@tonic-gate 				continue;
17407c478bd9Sstevel@tonic-gate 			pmdevp->speed_comp = i;
17417c478bd9Sstevel@tonic-gate 
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate 			/*
17447c478bd9Sstevel@tonic-gate 			 * Get full power and current power level
17457c478bd9Sstevel@tonic-gate 			 */
17467c478bd9Sstevel@tonic-gate 			pmdevp->full_power = ioctl(pm_fd, PM_GET_FULL_POWER,
17477c478bd9Sstevel@tonic-gate 			    &pmreq);
17487c478bd9Sstevel@tonic-gate 
17497c478bd9Sstevel@tonic-gate 			pmdevp->cur_power = ioctl(pm_fd, PM_GET_CURRENT_POWER,
17507c478bd9Sstevel@tonic-gate 			    &pmreq);
17517c478bd9Sstevel@tonic-gate 
17527c478bd9Sstevel@tonic-gate 			if (sensorp) {
17537c478bd9Sstevel@tonic-gate 				pmdevp->next = sensorp->pmdevp;
17547c478bd9Sstevel@tonic-gate 				sensorp->pmdevp = pmdevp;
17557c478bd9Sstevel@tonic-gate 				adjust_sensor_target(sensorp);
17567c478bd9Sstevel@tonic-gate 			}
17577c478bd9Sstevel@tonic-gate 			break;
17587c478bd9Sstevel@tonic-gate 		}
17597c478bd9Sstevel@tonic-gate 		if (env_debug > 1)
17607c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
17617c478bd9Sstevel@tonic-gate 			    "sensor:%s %p pmdev:%s comp:%s %d power:%d/%d\n",
17627c478bd9Sstevel@tonic-gate 			    pmdevp->sensor_name, pmdevp->sensorp,
17637c478bd9Sstevel@tonic-gate 			    pmdevp->pmdev_name, pmdevp->speed_comp_name,
17647c478bd9Sstevel@tonic-gate 			    pmdevp->speed_comp, pmdevp->cur_power,
17657c478bd9Sstevel@tonic-gate 			    pmdevp->full_power);
17667c478bd9Sstevel@tonic-gate 	}
17677c478bd9Sstevel@tonic-gate 	return (sensorcnt);
17687c478bd9Sstevel@tonic-gate }
17697c478bd9Sstevel@tonic-gate 
17707c478bd9Sstevel@tonic-gate /*
17717c478bd9Sstevel@tonic-gate  * Read all temperature sensors and take appropriate action based
17727c478bd9Sstevel@tonic-gate  * upon temperature threshols associated with each sensor. Possible
17737c478bd9Sstevel@tonic-gate  * actions are:
17747c478bd9Sstevel@tonic-gate  *
17757c478bd9Sstevel@tonic-gate  *	temperature > high_shutdown
17767c478bd9Sstevel@tonic-gate  *	temperature < low_shutdown
17777c478bd9Sstevel@tonic-gate  *		Gracefully shutdown the system and log/print a message
17787c478bd9Sstevel@tonic-gate  *		on the system console provided the temperature has been
17797c478bd9Sstevel@tonic-gate  *		in shutdown range for "shutdown_interval" seconds.
17807c478bd9Sstevel@tonic-gate  *
17817c478bd9Sstevel@tonic-gate  *	high_warning < temperature <= high_shutdown
17827c478bd9Sstevel@tonic-gate  *	low_warning  > temperature >= low_shutdown
17837c478bd9Sstevel@tonic-gate  *		Log/print a warning message on the system console at most
17847c478bd9Sstevel@tonic-gate  *		once every "warning_interval" seconds.
17857c478bd9Sstevel@tonic-gate  *
17867c478bd9Sstevel@tonic-gate  * Note that the current temperature is recorded in the "cur_temp" field
17877c478bd9Sstevel@tonic-gate  * within each env_sensor_t structure.
17887c478bd9Sstevel@tonic-gate  */
17897c478bd9Sstevel@tonic-gate static void
monitor_sensors(void)17907c478bd9Sstevel@tonic-gate monitor_sensors(void)
17917c478bd9Sstevel@tonic-gate {
17927c478bd9Sstevel@tonic-gate 	tempr_t 	temp;
17937c478bd9Sstevel@tonic-gate 	env_sensor_t	*sensorp;
17947c478bd9Sstevel@tonic-gate 	sensor_thresh_t	*threshp;
17957c478bd9Sstevel@tonic-gate 	time_t		ct;
17967c478bd9Sstevel@tonic-gate 	char		msgbuf[BUFSIZ];
17977c478bd9Sstevel@tonic-gate 	char		syscmd[BUFSIZ];
17987c478bd9Sstevel@tonic-gate 
17997c478bd9Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
18007c478bd9Sstevel@tonic-gate 		if (get_temperature(sensorp, &temp) < 0)
18017c478bd9Sstevel@tonic-gate 			continue;
18027c478bd9Sstevel@tonic-gate 
18037c478bd9Sstevel@tonic-gate 		sensorp->prev_avg_temp = sensorp->avg_temp;
18047c478bd9Sstevel@tonic-gate 		sensorp->cur_temp = temp;
18057c478bd9Sstevel@tonic-gate 		sensorp->avg_temp = (sensorp->avg_temp + temp)/2;
18067c478bd9Sstevel@tonic-gate 		threshp = sensorp->temp_thresh;
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 		if (env_debug)
18097c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO,
18107c478bd9Sstevel@tonic-gate 			"sensor: %-13s temp  prev_avg:%6.2f  "
18117c478bd9Sstevel@tonic-gate 			"cur:%d avg_temp:%6.2f power:%d/%d target:%d\n",
18127c478bd9Sstevel@tonic-gate 			    sensorp->name, sensorp->prev_avg_temp,
18137c478bd9Sstevel@tonic-gate 			    temp, sensorp->avg_temp, ((sensorp->pmdevp) ?
18147c478bd9Sstevel@tonic-gate 			    sensorp->pmdevp->cur_power : -1),
18157c478bd9Sstevel@tonic-gate 			    ((sensorp->pmdevp) ? sensorp->pmdevp->full_power :
18167c478bd9Sstevel@tonic-gate 			    -1), sensorp->target_temp);
18177c478bd9Sstevel@tonic-gate 
18187c478bd9Sstevel@tonic-gate 
18197c478bd9Sstevel@tonic-gate 		/*
18207c478bd9Sstevel@tonic-gate 		 * If this sensor already triggered system shutdown, don't
18217c478bd9Sstevel@tonic-gate 		 * log any more shutdown/warning messages for it.
18227c478bd9Sstevel@tonic-gate 		 */
18237c478bd9Sstevel@tonic-gate 		if (sensorp->shutdown_initiated || threshp == NULL)
18247c478bd9Sstevel@tonic-gate 			continue;
18257c478bd9Sstevel@tonic-gate 
18267c478bd9Sstevel@tonic-gate 		/*
18277c478bd9Sstevel@tonic-gate 		 * Check for the temperature in warning and shutdown range
18287c478bd9Sstevel@tonic-gate 		 * and take appropriate action.
18297c478bd9Sstevel@tonic-gate 		 */
18307c478bd9Sstevel@tonic-gate 		if (TEMP_IN_WARNING_RANGE(temp, threshp) && !disable_warning) {
18317c478bd9Sstevel@tonic-gate 			/*
18327c478bd9Sstevel@tonic-gate 			 * Check if the temperature has been in warning
18337c478bd9Sstevel@tonic-gate 			 * range during last warning_duration interval.
18347c478bd9Sstevel@tonic-gate 			 * If so, the temperature is truly in warning
18357c478bd9Sstevel@tonic-gate 			 * range and we need to log a warning message,
18367c478bd9Sstevel@tonic-gate 			 * but no more than once every warning_interval
18377c478bd9Sstevel@tonic-gate 			 * seconds.
18387c478bd9Sstevel@tonic-gate 			 */
18397c478bd9Sstevel@tonic-gate 			time_t	wtstamp = sensorp->warning_tstamp;
18407c478bd9Sstevel@tonic-gate 
18417c478bd9Sstevel@tonic-gate 			ct = (time_t)(gethrtime() / NANOSEC);
18427c478bd9Sstevel@tonic-gate 			if (sensorp->warning_start == 0)
18437c478bd9Sstevel@tonic-gate 				sensorp->warning_start = ct;
18447c478bd9Sstevel@tonic-gate 			if (((ct - sensorp->warning_start) >=
18457c478bd9Sstevel@tonic-gate 			    warning_duration) && (wtstamp == 0 ||
18467c478bd9Sstevel@tonic-gate 			    (ct - wtstamp) >= warning_interval)) {
18477c478bd9Sstevel@tonic-gate 				envd_log(LOG_CRIT, ENV_WARNING_MSG,
18487c478bd9Sstevel@tonic-gate 				    sensorp->name, temp,
18497c478bd9Sstevel@tonic-gate 				    threshp->low_warning,
18507c478bd9Sstevel@tonic-gate 				    threshp->high_warning);
18517c478bd9Sstevel@tonic-gate 				sensorp->warning_tstamp = ct;
18527c478bd9Sstevel@tonic-gate 			}
18537c478bd9Sstevel@tonic-gate 		} else if (sensorp->warning_start != 0)
18547c478bd9Sstevel@tonic-gate 			sensorp->warning_start = 0;
18557c478bd9Sstevel@tonic-gate 
18567c478bd9Sstevel@tonic-gate 		if (TEMP_IN_SHUTDOWN_RANGE(temp, threshp) &&
18577c478bd9Sstevel@tonic-gate 		    !disable_shutdown) {
18587c478bd9Sstevel@tonic-gate 			ct = (time_t)(gethrtime() / NANOSEC);
18597c478bd9Sstevel@tonic-gate 			if (sensorp->shutdown_tstamp == 0)
18607c478bd9Sstevel@tonic-gate 				sensorp->shutdown_tstamp = ct;
18617c478bd9Sstevel@tonic-gate 
18627c478bd9Sstevel@tonic-gate 			/*
18637c478bd9Sstevel@tonic-gate 			 * Shutdown the system if the temperature remains
18647c478bd9Sstevel@tonic-gate 			 * in the shutdown range for over shutdown_interval
18657c478bd9Sstevel@tonic-gate 			 * seconds.
18667c478bd9Sstevel@tonic-gate 			 */
18677c478bd9Sstevel@tonic-gate 			if ((ct - sensorp->shutdown_tstamp) >=
18687c478bd9Sstevel@tonic-gate 			    shutdown_interval) {
18697c478bd9Sstevel@tonic-gate 				/* log error */
18707c478bd9Sstevel@tonic-gate 				sensorp->shutdown_initiated = B_TRUE;
18717c478bd9Sstevel@tonic-gate 				(void) snprintf(msgbuf, sizeof (msgbuf),
18727c478bd9Sstevel@tonic-gate 				    ENV_SHUTDOWN_MSG, sensorp->name,
18737c478bd9Sstevel@tonic-gate 				    temp, threshp->low_shutdown,
18747c478bd9Sstevel@tonic-gate 				    threshp->high_shutdown);
18757c478bd9Sstevel@tonic-gate 				envd_log(LOG_ALERT, msgbuf);
18767c478bd9Sstevel@tonic-gate 
18777c478bd9Sstevel@tonic-gate 				/* shutdown the system (only once) */
18787c478bd9Sstevel@tonic-gate 				if (system_shutdown_started == B_FALSE) {
18797c478bd9Sstevel@tonic-gate 					(void) snprintf(syscmd, sizeof (syscmd),
18807c478bd9Sstevel@tonic-gate 					    "%s \"%s\"", shutdown_cmd, msgbuf);
18817c478bd9Sstevel@tonic-gate 					envd_log(LOG_ALERT, syscmd);
18827c478bd9Sstevel@tonic-gate 					system_shutdown_started = B_TRUE;
18837c478bd9Sstevel@tonic-gate 					(void) system(syscmd);
18847c478bd9Sstevel@tonic-gate 				}
18857c478bd9Sstevel@tonic-gate 			}
18867c478bd9Sstevel@tonic-gate 		} else if (sensorp->shutdown_tstamp != 0)
18877c478bd9Sstevel@tonic-gate 			sensorp->shutdown_tstamp = 0;
18887c478bd9Sstevel@tonic-gate 	}
18897c478bd9Sstevel@tonic-gate }
18907c478bd9Sstevel@tonic-gate 
18917c478bd9Sstevel@tonic-gate 
18927c478bd9Sstevel@tonic-gate /*
18937c478bd9Sstevel@tonic-gate  * Adjust fan speed based upon the current temperature value of various
18947c478bd9Sstevel@tonic-gate  * sensors affected by the specified fan.
18957c478bd9Sstevel@tonic-gate  */
18967c478bd9Sstevel@tonic-gate static int
adjust_fan_speed(env_fan_t * fanp,lpm_dev_t * devp)18977c478bd9Sstevel@tonic-gate adjust_fan_speed(env_fan_t *fanp, lpm_dev_t *devp)
18987c478bd9Sstevel@tonic-gate {
18997c478bd9Sstevel@tonic-gate 	int		i;
19007c478bd9Sstevel@tonic-gate 	fanspeed_t	fanspeed;
19017c478bd9Sstevel@tonic-gate 	float		speed, cur_speed, new_speed, max_speed, min_speed;
19027c478bd9Sstevel@tonic-gate 	env_sensor_t	*sensorp;
19037c478bd9Sstevel@tonic-gate 	sensor_thresh_t	*threshp;
19047c478bd9Sstevel@tonic-gate 	tempr_t		temp;
19057c478bd9Sstevel@tonic-gate 	float		avg_temp, tempdiff, targetdiff;
19067c478bd9Sstevel@tonic-gate 	int		av_ambient;
19077c478bd9Sstevel@tonic-gate 	int		amb_cnt;
19087c478bd9Sstevel@tonic-gate 
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate 	/*
19117c478bd9Sstevel@tonic-gate 	 * Get current fan speed
19127c478bd9Sstevel@tonic-gate 	 */
19137c478bd9Sstevel@tonic-gate 	if (get_fan_speed(fanp, &fanspeed) < 0)
19147c478bd9Sstevel@tonic-gate 		return (-1);
19157c478bd9Sstevel@tonic-gate 	cur_speed = fanp->cur_speed;
19167c478bd9Sstevel@tonic-gate 	if (fanspeed != (int)cur_speed)
19177c478bd9Sstevel@tonic-gate 		cur_speed = (float)fanspeed;
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate 	/*
19207c478bd9Sstevel@tonic-gate 	 * Calculate new fan speed for each sensor and pick the largest one.
19217c478bd9Sstevel@tonic-gate 	 */
19227c478bd9Sstevel@tonic-gate 	min_speed = fanp->speed_min;
19237c478bd9Sstevel@tonic-gate 	max_speed = fanp->speed_max;
19247c478bd9Sstevel@tonic-gate 	speed = 0;
19257c478bd9Sstevel@tonic-gate 	av_ambient = 0;
19267c478bd9Sstevel@tonic-gate 	amb_cnt = 0;
19277c478bd9Sstevel@tonic-gate 
19287c478bd9Sstevel@tonic-gate 	for (i = 0; i < fanp->sensor_cnt; i++) {
19297c478bd9Sstevel@tonic-gate 		sensorp = fanp->sensors[i];
19307c478bd9Sstevel@tonic-gate 		if (sensorp == NULL || sensorp->fd == -1 ||
19317c478bd9Sstevel@tonic-gate 		    sensorp->temp_thresh == NULL)
19327c478bd9Sstevel@tonic-gate 			continue;
19337c478bd9Sstevel@tonic-gate 
19347c478bd9Sstevel@tonic-gate 		temp = sensorp->cur_temp;
19357c478bd9Sstevel@tonic-gate 		avg_temp = sensorp->avg_temp;
19367c478bd9Sstevel@tonic-gate 		threshp = sensorp->temp_thresh;
19377c478bd9Sstevel@tonic-gate 
19387c478bd9Sstevel@tonic-gate 		/*
19397c478bd9Sstevel@tonic-gate 		 * Note ambient temperatures to determine lpm for system fan
19407c478bd9Sstevel@tonic-gate 		 */
19417c478bd9Sstevel@tonic-gate 		if ((devp != NULL) &&
19427c478bd9Sstevel@tonic-gate 		    (sensorp->flags & SFLAG_CPU_AMB_SENSOR)) {
19437c478bd9Sstevel@tonic-gate 			av_ambient += temp;
19447c478bd9Sstevel@tonic-gate 			amb_cnt++;
19457c478bd9Sstevel@tonic-gate 		}
19467c478bd9Sstevel@tonic-gate 
19477c478bd9Sstevel@tonic-gate 		/*
19487c478bd9Sstevel@tonic-gate 		 * If the current temperature is above the warning
19497c478bd9Sstevel@tonic-gate 		 * threshold, use max fan speed.
19507c478bd9Sstevel@tonic-gate 		 */
19517c478bd9Sstevel@tonic-gate 		if (temp >= threshp->high_warning) {
19527c478bd9Sstevel@tonic-gate 			speed = max_speed;
19537c478bd9Sstevel@tonic-gate 			break;
19547c478bd9Sstevel@tonic-gate 		} else if (temp <= threshp->low_warning) {
19557c478bd9Sstevel@tonic-gate 			speed = min_speed;
19567c478bd9Sstevel@tonic-gate 			break;
19577c478bd9Sstevel@tonic-gate 		}
19587c478bd9Sstevel@tonic-gate 
19597c478bd9Sstevel@tonic-gate 		if (threshp->policy_type == POLICY_TARGET_TEMP) {
19607c478bd9Sstevel@tonic-gate 			/*
19617c478bd9Sstevel@tonic-gate 			 * Try to achieve the desired target temperature.
19627c478bd9Sstevel@tonic-gate 			 * Calculate new fan speed based upon whether the
19637c478bd9Sstevel@tonic-gate 			 * temperature is rising, falling or steady state.
19647c478bd9Sstevel@tonic-gate 			 * Also take into consideration the current fan
19657c478bd9Sstevel@tonic-gate 			 * speed as well as the desired target temperature.
19667c478bd9Sstevel@tonic-gate 			 */
19677c478bd9Sstevel@tonic-gate 			float	delta, speed_change;
19687c478bd9Sstevel@tonic-gate 			float	multiplier;
19697c478bd9Sstevel@tonic-gate 
19707c478bd9Sstevel@tonic-gate 			targetdiff = avg_temp - sensorp->target_temp;
19717c478bd9Sstevel@tonic-gate 			tempdiff = avg_temp - sensorp->prev_avg_temp;
19727c478bd9Sstevel@tonic-gate 
19737c478bd9Sstevel@tonic-gate 			if (tempdiff > AVG_TEMP_HYSTERESIS) {
19747c478bd9Sstevel@tonic-gate 				/*
19757c478bd9Sstevel@tonic-gate 				 * Temperature is rising. Increase fan
19767c478bd9Sstevel@tonic-gate 				 * speed 0.5% for every 1C above the
19777c478bd9Sstevel@tonic-gate 				 * (target - RISING_TEMP_MARGIN) limit.
19787c478bd9Sstevel@tonic-gate 				 * Also take into consideration temperature
19797c478bd9Sstevel@tonic-gate 				 * rising rate and the current fan speed.
19807c478bd9Sstevel@tonic-gate 				 */
19817c478bd9Sstevel@tonic-gate 				delta = max_speed * .005 *
19827c478bd9Sstevel@tonic-gate 				    (RISING_TEMP_MARGIN + targetdiff);
19837c478bd9Sstevel@tonic-gate 				if (delta <= 0)
19847c478bd9Sstevel@tonic-gate 					multiplier = 0;
19857c478bd9Sstevel@tonic-gate 				else
19867c478bd9Sstevel@tonic-gate 					multiplier = tempdiff/4 +
19877c478bd9Sstevel@tonic-gate 					    ((cur_speed < max_speed/2) ?
19887c478bd9Sstevel@tonic-gate 					    2 : 1);
19897c478bd9Sstevel@tonic-gate 			} else if (tempdiff < -AVG_TEMP_HYSTERESIS) {
19907c478bd9Sstevel@tonic-gate 				/*
19917c478bd9Sstevel@tonic-gate 				 * Temperature is falling. Decrease fan
19927c478bd9Sstevel@tonic-gate 				 * speed 0.5% for every 1C below the
19937c478bd9Sstevel@tonic-gate 				 * (target + FALLING_TEMP_MARGIN) limit.
19947c478bd9Sstevel@tonic-gate 				 * Also take into consideration temperature
19957c478bd9Sstevel@tonic-gate 				 * falling rate and the current fan speed.
19967c478bd9Sstevel@tonic-gate 				 */
19977c478bd9Sstevel@tonic-gate 				delta = -max_speed * .005 *
19987c478bd9Sstevel@tonic-gate 				    (FALLING_TEMP_MARGIN - targetdiff);
19997c478bd9Sstevel@tonic-gate 				if (delta >= 0)
20007c478bd9Sstevel@tonic-gate 					multiplier = 0;
20017c478bd9Sstevel@tonic-gate 				else
20027c478bd9Sstevel@tonic-gate 					multiplier = -tempdiff/4 +
20037c478bd9Sstevel@tonic-gate 					    ((cur_speed > max_speed/2) ?
20047c478bd9Sstevel@tonic-gate 					    2 : 1);
20057c478bd9Sstevel@tonic-gate 			} else {
20067c478bd9Sstevel@tonic-gate 				/*
20077c478bd9Sstevel@tonic-gate 				 * Temperature is changing very slowly.
20087c478bd9Sstevel@tonic-gate 				 * Adjust fan speed by 0.4% for every 1C
20097c478bd9Sstevel@tonic-gate 				 * below/above the target temperature.
20107c478bd9Sstevel@tonic-gate 				 */
20117c478bd9Sstevel@tonic-gate 				delta = max_speed * .004 * targetdiff;
20127c478bd9Sstevel@tonic-gate 				multiplier = 1.0;
20137c478bd9Sstevel@tonic-gate 			}
20147c478bd9Sstevel@tonic-gate 
20157c478bd9Sstevel@tonic-gate 
20167c478bd9Sstevel@tonic-gate 			/*
20177c478bd9Sstevel@tonic-gate 			 * Enforece some bounds on multiplier and the
20187c478bd9Sstevel@tonic-gate 			 * speed change.
20197c478bd9Sstevel@tonic-gate 			 */
20207c478bd9Sstevel@tonic-gate 			multiplier = MIN(multiplier, 3.0);
20217c478bd9Sstevel@tonic-gate 			speed_change = delta * multiplier *
20227c478bd9Sstevel@tonic-gate 			    sensorp->fan_adjustment_rate;
20237c478bd9Sstevel@tonic-gate 			speed_change = MIN(speed_change, fan_incr_limit);
20247c478bd9Sstevel@tonic-gate 			speed_change = MAX(speed_change, -fan_decr_limit);
20257c478bd9Sstevel@tonic-gate 			new_speed = cur_speed + speed_change;
20267c478bd9Sstevel@tonic-gate 
20277c478bd9Sstevel@tonic-gate 			if (env_debug > 1)
20287c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO,
20297c478bd9Sstevel@tonic-gate 				"sensor: %-8s temp/diff:%d/%3.1f  "
20307c478bd9Sstevel@tonic-gate 				"target/diff:%d/%3.1f  change:%4.2f x "
20317c478bd9Sstevel@tonic-gate 				"%4.2f x %4.2f speed %5.2f -> %5.2f\n",
20327c478bd9Sstevel@tonic-gate 				    sensorp->name, temp, tempdiff,
20337c478bd9Sstevel@tonic-gate 				    sensorp->target_temp, targetdiff, delta,
20347c478bd9Sstevel@tonic-gate 				    multiplier, sensorp->fan_adjustment_rate,
20357c478bd9Sstevel@tonic-gate 				    cur_speed, new_speed);
20367c478bd9Sstevel@tonic-gate 		} else if (threshp->policy_type == POLICY_LINEAR) {
20377c478bd9Sstevel@tonic-gate 			/*
20387c478bd9Sstevel@tonic-gate 			 * Set fan speed linearly within the operating
20397c478bd9Sstevel@tonic-gate 			 * range specified by the policy_data[LOW_NOMINAL_LOC]
20407c478bd9Sstevel@tonic-gate 			 * and policy_data[HIGH_NOMINAL_LOC] threshold values.
20417c478bd9Sstevel@tonic-gate 			 * Fan speed is set to minimum value at LOW_NOMINAL
20427c478bd9Sstevel@tonic-gate 			 * and to maximum value at HIGH_NOMINAL value.
20437c478bd9Sstevel@tonic-gate 			 */
20447c478bd9Sstevel@tonic-gate 			new_speed = min_speed + (max_speed - min_speed) *
20457c478bd9Sstevel@tonic-gate 			    (avg_temp - threshp->policy_data[LOW_NOMINAL_LOC])/
20467c478bd9Sstevel@tonic-gate 			    (threshp->policy_data[HIGH_NOMINAL_LOC] -
20477c478bd9Sstevel@tonic-gate 			    threshp->policy_data[LOW_NOMINAL_LOC]);
20487c478bd9Sstevel@tonic-gate 			if (env_debug > 1)
20497c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO,
20507c478bd9Sstevel@tonic-gate 				"sensor: %-8s policy: linear, cur_speed %5.2f"\
20517c478bd9Sstevel@tonic-gate 				" new_speed: %5.2f\n", sensorp->name, cur_speed,
20527c478bd9Sstevel@tonic-gate 				    new_speed);
20537c478bd9Sstevel@tonic-gate 		} else {
20547c478bd9Sstevel@tonic-gate 			new_speed = cur_speed;
20557c478bd9Sstevel@tonic-gate 		}
20567c478bd9Sstevel@tonic-gate 		speed = MAX(speed, new_speed);
20577c478bd9Sstevel@tonic-gate 	}
20587c478bd9Sstevel@tonic-gate 
20597c478bd9Sstevel@tonic-gate 	/*
20607c478bd9Sstevel@tonic-gate 	 * Adjust speed using lpm tables
20617c478bd9Sstevel@tonic-gate 	 */
20627c478bd9Sstevel@tonic-gate 	if (amb_cnt > 0) {
20637c478bd9Sstevel@tonic-gate 		av_ambient = (av_ambient >= 0 ?
20647c478bd9Sstevel@tonic-gate 			(int)(0.5 + (float)av_ambient/(float)amb_cnt):
20657c478bd9Sstevel@tonic-gate 			(int)(-0.5 + (float)av_ambient/(float)amb_cnt));
20667c478bd9Sstevel@tonic-gate 		speed = MAX(speed, (fanspeed_t)get_lpm_speed(devp, av_ambient));
20677c478bd9Sstevel@tonic-gate 	}
20687c478bd9Sstevel@tonic-gate 
20697c478bd9Sstevel@tonic-gate 	speed = MIN(speed, max_speed);
20707c478bd9Sstevel@tonic-gate 	speed = MAX(speed, min_speed);
20717c478bd9Sstevel@tonic-gate 
20727c478bd9Sstevel@tonic-gate 	/*
20737c478bd9Sstevel@tonic-gate 	 * Record and update fan speed, if different.
20747c478bd9Sstevel@tonic-gate 	 */
20757c478bd9Sstevel@tonic-gate 	fanp->prev_speed = fanp->cur_speed;
20767c478bd9Sstevel@tonic-gate 	fanp->cur_speed = speed;
20777c478bd9Sstevel@tonic-gate 	if ((fanspeed_t)speed != fanspeed) {
20787c478bd9Sstevel@tonic-gate 		fanspeed = (fanspeed_t)speed;
20797c478bd9Sstevel@tonic-gate 		(void) set_fan_speed(fanp, fanspeed);
20807c478bd9Sstevel@tonic-gate 	}
20817c478bd9Sstevel@tonic-gate 	if (env_debug)
20827c478bd9Sstevel@tonic-gate 		envd_log(LOG_INFO,
20837c478bd9Sstevel@tonic-gate 		    "fan: %-16s speed cur:%6.2f  new:%6.2f\n",
20847c478bd9Sstevel@tonic-gate 		    fanp->name, fanp->prev_speed, fanp->cur_speed);
20857c478bd9Sstevel@tonic-gate 
20867c478bd9Sstevel@tonic-gate 	return (0);
20877c478bd9Sstevel@tonic-gate }
20887c478bd9Sstevel@tonic-gate /*
20897c478bd9Sstevel@tonic-gate  * This is the environment thread, which monitors the current temperature
20907c478bd9Sstevel@tonic-gate  * and power managed state and controls system fan speed.  Temperature is
20917c478bd9Sstevel@tonic-gate  * polled every sensor-poll_interval seconds duration.
20927c478bd9Sstevel@tonic-gate  */
20937c478bd9Sstevel@tonic-gate /*ARGSUSED*/
20947c478bd9Sstevel@tonic-gate static void *
envthr(void * args)20957c478bd9Sstevel@tonic-gate envthr(void *args)
20967c478bd9Sstevel@tonic-gate {
20977c478bd9Sstevel@tonic-gate 	env_sensor_t	*sensorp;
20987c478bd9Sstevel@tonic-gate 	fanspeed_t 	fan_speed;
20997c478bd9Sstevel@tonic-gate 	env_fan_t	*pmfanp = &envd_psupply_fan;
21007c478bd9Sstevel@tonic-gate 	int		to;
21017c478bd9Sstevel@tonic-gate 	int		xwd = -1;
21027c478bd9Sstevel@tonic-gate 
21037c478bd9Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL;
21047c478bd9Sstevel@tonic-gate 	    sensorp++) {
21057c478bd9Sstevel@tonic-gate 		if (sensorp->obs2exp_map)
21067c478bd9Sstevel@tonic-gate 			(void) free(sensorp->obs2exp_map);
21077c478bd9Sstevel@tonic-gate 		sensorp->obs2exp_map = NULL;
21087c478bd9Sstevel@tonic-gate 		sensorp->obs2exp_cnt = 0;
21097c478bd9Sstevel@tonic-gate 	}
21107c478bd9Sstevel@tonic-gate 
21117c478bd9Sstevel@tonic-gate 	/*
21127c478bd9Sstevel@tonic-gate 	 * Process environmental segment data, if present,
21137c478bd9Sstevel@tonic-gate 	 * in the FRU SEEPROM.
21147c478bd9Sstevel@tonic-gate 	 */
21157c478bd9Sstevel@tonic-gate 	process_fru_envseg();
21167c478bd9Sstevel@tonic-gate 
21177c478bd9Sstevel@tonic-gate 	/*
21187c478bd9Sstevel@tonic-gate 	 * Process tuneable parameters
21197c478bd9Sstevel@tonic-gate 	 */
21207c478bd9Sstevel@tonic-gate 	process_env_conf_file();
21217c478bd9Sstevel@tonic-gate 
21227c478bd9Sstevel@tonic-gate 	/*
21237c478bd9Sstevel@tonic-gate 	 * Setup temperature sensors and fail if we can't open
21247c478bd9Sstevel@tonic-gate 	 * at least one sensor.
21257c478bd9Sstevel@tonic-gate 	 */
21267c478bd9Sstevel@tonic-gate 	if (envd_setup_sensors() <= 0) {
21277c478bd9Sstevel@tonic-gate 		envd_close_pm();
21287c478bd9Sstevel@tonic-gate 		return (NULL);
21297c478bd9Sstevel@tonic-gate 	}
21307c478bd9Sstevel@tonic-gate 
21317c478bd9Sstevel@tonic-gate 	to = 3 * sensor_poll_interval + 1;
21327c478bd9Sstevel@tonic-gate 	xwd = open(XCALWD_DEVFS, O_RDONLY);
21337c478bd9Sstevel@tonic-gate 	if (xwd < 0) {
21347c478bd9Sstevel@tonic-gate 		envd_log(LOG_CRIT, ENV_WATCHDOG_INIT_FAIL, errno,
21357c478bd9Sstevel@tonic-gate 		    strerror(errno));
21367c478bd9Sstevel@tonic-gate 	} else if (ioctl(xwd, XCALWD_STOPWATCHDOG) < 0 ||
21377c478bd9Sstevel@tonic-gate 	    ioctl(xwd, XCALWD_STARTWATCHDOG, &to) < 0) {
21387c478bd9Sstevel@tonic-gate 		envd_log(LOG_CRIT, ENV_WATCHDOG_INIT_FAIL, errno,
21397c478bd9Sstevel@tonic-gate 		    strerror(errno));
21407c478bd9Sstevel@tonic-gate 		(void) close(xwd);
21417c478bd9Sstevel@tonic-gate 		xwd = -1;
21427c478bd9Sstevel@tonic-gate 	}
21437c478bd9Sstevel@tonic-gate 
21447c478bd9Sstevel@tonic-gate 	/*
21457c478bd9Sstevel@tonic-gate 	 * Setup fan device (don't fail even if we can't access
21467c478bd9Sstevel@tonic-gate 	 * the fan as we can still monitor temeperature.
21477c478bd9Sstevel@tonic-gate 	 */
21487c478bd9Sstevel@tonic-gate 	(void) envd_setup_fans();
21497c478bd9Sstevel@tonic-gate 
21507c478bd9Sstevel@tonic-gate 	for (;;) {
21517c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_rdlock(&envd_rwlock);
21527c478bd9Sstevel@tonic-gate 
21537c478bd9Sstevel@tonic-gate 		/*
21547c478bd9Sstevel@tonic-gate 		 * If no "pmthr" thread, then we need to update the
21557c478bd9Sstevel@tonic-gate 		 * current power level for all power managed deviecs
21567c478bd9Sstevel@tonic-gate 		 * so that we can determine correct target temperature.
21577c478bd9Sstevel@tonic-gate 		 */
21587c478bd9Sstevel@tonic-gate 		if (pmthr_exists == B_FALSE)
21597c478bd9Sstevel@tonic-gate 			(void) update_pmdev_power();
21607c478bd9Sstevel@tonic-gate 
21617c478bd9Sstevel@tonic-gate 		if (xwd >= 0)
21627c478bd9Sstevel@tonic-gate 			(void) ioctl(xwd, XCALWD_KEEPALIVE);
21637c478bd9Sstevel@tonic-gate 
21647c478bd9Sstevel@tonic-gate 		if (!disable_piclenvd) {
21657c478bd9Sstevel@tonic-gate 			/*
21667c478bd9Sstevel@tonic-gate 			 * Monitor current temperature for all sensors
21677c478bd9Sstevel@tonic-gate 			 * (current temperature is recorded in the "cur_temp"
21687c478bd9Sstevel@tonic-gate 			 * field within each sensor data structure)
21697c478bd9Sstevel@tonic-gate 			 */
21707c478bd9Sstevel@tonic-gate 			monitor_sensors();
21717c478bd9Sstevel@tonic-gate 
21727c478bd9Sstevel@tonic-gate 			/*
21737c478bd9Sstevel@tonic-gate 			 * Adjust CPU and system fan speed
21747c478bd9Sstevel@tonic-gate 			 */
21757c478bd9Sstevel@tonic-gate 			if (envd_cpu_fan.forced_speed < 0)
21767c478bd9Sstevel@tonic-gate 				(void) adjust_fan_speed(&envd_cpu_fan, NULL);
21777c478bd9Sstevel@tonic-gate 			if (envd_system_fan.forced_speed < 0)
21787c478bd9Sstevel@tonic-gate 				(void) adjust_fan_speed(&envd_system_fan,
21797c478bd9Sstevel@tonic-gate 					lpm_devices);
21807c478bd9Sstevel@tonic-gate 
21817c478bd9Sstevel@tonic-gate 			/*
21827c478bd9Sstevel@tonic-gate 			 * Turn off power supply fan if in lowest power state.
21837c478bd9Sstevel@tonic-gate 			 */
21847c478bd9Sstevel@tonic-gate 			fan_speed = (cur_lpstate) ? pmfanp->speed_min :
21857c478bd9Sstevel@tonic-gate 			    pmfanp->speed_max;
21867c478bd9Sstevel@tonic-gate 
21877c478bd9Sstevel@tonic-gate 			if (env_debug)
21887c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO,
21897c478bd9Sstevel@tonic-gate 				"fan: %-16s speed cur:%6.2f  new:%6.2f "
21907c478bd9Sstevel@tonic-gate 				"low-power:%d\n", pmfanp->name,
21917c478bd9Sstevel@tonic-gate 				    (float)pmfanp->cur_speed,
21927c478bd9Sstevel@tonic-gate 				    (float)fan_speed, cur_lpstate);
21937c478bd9Sstevel@tonic-gate 
21947c478bd9Sstevel@tonic-gate 			if (fan_speed != (fanspeed_t)pmfanp->cur_speed &&
21957c478bd9Sstevel@tonic-gate 			    set_fan_speed(pmfanp, fan_speed) == 0)
21967c478bd9Sstevel@tonic-gate 				pmfanp->cur_speed = fan_speed;
21977c478bd9Sstevel@tonic-gate 		}
21987c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&envd_rwlock);
21997c478bd9Sstevel@tonic-gate 
22007c478bd9Sstevel@tonic-gate 		/*
22017c478bd9Sstevel@tonic-gate 		 * Wait for sensor_poll_interval seconds before polling
22027c478bd9Sstevel@tonic-gate 		 * again. Note that we use our own envd_sleep() routine
22037c478bd9Sstevel@tonic-gate 		 * as sleep() in POSIX thread library gets affected by
22047c478bd9Sstevel@tonic-gate 		 * the wall clock time being set back.
22057c478bd9Sstevel@tonic-gate 		 */
22067c478bd9Sstevel@tonic-gate 		(void) envd_sleep(sensor_poll_interval);
22077c478bd9Sstevel@tonic-gate 	}
22087c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
22097c478bd9Sstevel@tonic-gate 	return (NULL);
22107c478bd9Sstevel@tonic-gate }
22117c478bd9Sstevel@tonic-gate 
22127c478bd9Sstevel@tonic-gate /*
22137c478bd9Sstevel@tonic-gate  * This is the power management thread, which monitors all power state
22147c478bd9Sstevel@tonic-gate  * change events and wakes up the "envthr" thread when the system enters
22157c478bd9Sstevel@tonic-gate  * or exits the lowest power state.
22167c478bd9Sstevel@tonic-gate  */
22177c478bd9Sstevel@tonic-gate /*ARGSUSED*/
22187c478bd9Sstevel@tonic-gate static void *
pmthr(void * args)22197c478bd9Sstevel@tonic-gate pmthr(void *args)
22207c478bd9Sstevel@tonic-gate {
22217c478bd9Sstevel@tonic-gate 	pm_state_change_t	pmstate;
22227c478bd9Sstevel@tonic-gate 	char			physpath[PATH_MAX];
22237c478bd9Sstevel@tonic-gate 
22247c478bd9Sstevel@tonic-gate 	pmstate.physpath = physpath;
22257c478bd9Sstevel@tonic-gate 	pmstate.size = sizeof (physpath);
22267c478bd9Sstevel@tonic-gate 	cur_lpstate = 0;
22277c478bd9Sstevel@tonic-gate 
22287c478bd9Sstevel@tonic-gate 	for (;;) {
22297c478bd9Sstevel@tonic-gate 		/*
22307c478bd9Sstevel@tonic-gate 		 * Get PM state change events to check if the system
22317c478bd9Sstevel@tonic-gate 		 * is in lowest power state and wake up the "envthr"
22327c478bd9Sstevel@tonic-gate 		 * thread when the power state changes.
22337c478bd9Sstevel@tonic-gate 		 *
22347c478bd9Sstevel@tonic-gate 		 * To minimize polling, we use the blocking interface
22357c478bd9Sstevel@tonic-gate 		 * to get the power state change event here.
22367c478bd9Sstevel@tonic-gate 		 */
22377c478bd9Sstevel@tonic-gate 		if (ioctl(pm_fd, PM_GET_STATE_CHANGE_WAIT, &pmstate) != 0) {
22387c478bd9Sstevel@tonic-gate 			if (errno != EINTR)
22397c478bd9Sstevel@tonic-gate 				break;
22407c478bd9Sstevel@tonic-gate 			continue;
22417c478bd9Sstevel@tonic-gate 		}
22427c478bd9Sstevel@tonic-gate 
22437c478bd9Sstevel@tonic-gate 		/*
22447c478bd9Sstevel@tonic-gate 		 * Extract the lowest power state from the last queued
22457c478bd9Sstevel@tonic-gate 		 * state change events. We pick up queued state change
22467c478bd9Sstevel@tonic-gate 		 * events using the non-blocking interface and wake up
22477c478bd9Sstevel@tonic-gate 		 * the "envthr" thread only after consuming all the
22487c478bd9Sstevel@tonic-gate 		 * state change events queued at that time.
22497c478bd9Sstevel@tonic-gate 		 */
22507c478bd9Sstevel@tonic-gate 		do {
22517c478bd9Sstevel@tonic-gate 			if (env_debug > 1)  {
22527c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO,
22537c478bd9Sstevel@tonic-gate 				"pmstate event:0x%x flags:%x comp:%d "
22547c478bd9Sstevel@tonic-gate 				"oldval:%d newval:%d path:%s\n",
22557c478bd9Sstevel@tonic-gate 				    pmstate.event, pmstate.flags,
22567c478bd9Sstevel@tonic-gate 				    pmstate.component, pmstate.old_level,
22577c478bd9Sstevel@tonic-gate 				    pmstate.new_level, pmstate.physpath);
22587c478bd9Sstevel@tonic-gate 			}
22597c478bd9Sstevel@tonic-gate 			cur_lpstate =
22607c478bd9Sstevel@tonic-gate 			    (pmstate.flags & PSC_ALL_LOWEST) ? 1 : 0;
22617c478bd9Sstevel@tonic-gate 		} while (ioctl(pm_fd, PM_GET_STATE_CHANGE, &pmstate) == 0);
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate 		/*
22647c478bd9Sstevel@tonic-gate 		 * Update current PM state for the components we are
22657c478bd9Sstevel@tonic-gate 		 * tracking. In case of CPU devices, PM state change
22667c478bd9Sstevel@tonic-gate 		 * event can be generated even before the state change
22677c478bd9Sstevel@tonic-gate 		 * takes effect, hence we need to get the current state
22687c478bd9Sstevel@tonic-gate 		 * for all CPU devices every time and recalculate the
22697c478bd9Sstevel@tonic-gate 		 * target temperature. We do this once after consuming
22707c478bd9Sstevel@tonic-gate 		 * all the queued events.
22717c478bd9Sstevel@tonic-gate 		 */
22727c478bd9Sstevel@tonic-gate 
22737c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_rdlock(&envd_rwlock);
22747c478bd9Sstevel@tonic-gate 		(void) update_pmdev_power();
22757c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&envd_rwlock);
22767c478bd9Sstevel@tonic-gate 	}
22777c478bd9Sstevel@tonic-gate 
22787c478bd9Sstevel@tonic-gate 	/*
22797c478bd9Sstevel@tonic-gate 	 * We won't be able to monitor lowest power state any longer,
22807c478bd9Sstevel@tonic-gate 	 * hence reset it.
22817c478bd9Sstevel@tonic-gate 	 */
22827c478bd9Sstevel@tonic-gate 	cur_lpstate = 0;
22837c478bd9Sstevel@tonic-gate 	envd_log(LOG_ERR, PM_THREAD_EXITING, errno, strerror(errno));
22847c478bd9Sstevel@tonic-gate 	pmthr_exists = B_FALSE;
22857c478bd9Sstevel@tonic-gate 	return (NULL);
22867c478bd9Sstevel@tonic-gate }
22877c478bd9Sstevel@tonic-gate 
22887c478bd9Sstevel@tonic-gate 
22897c478bd9Sstevel@tonic-gate /*
22907c478bd9Sstevel@tonic-gate  * Process sensor threshold related tuneables
22917c478bd9Sstevel@tonic-gate  */
22927c478bd9Sstevel@tonic-gate static int
process_threshold_tuneable(char * keyword,char * buf,void * dummy_thresh_addr,int flags,char * fname,int line)22937c478bd9Sstevel@tonic-gate process_threshold_tuneable(char *keyword, char *buf, void *dummy_thresh_addr,
22947c478bd9Sstevel@tonic-gate     int flags, char *fname, int line)
22957c478bd9Sstevel@tonic-gate {
22967c478bd9Sstevel@tonic-gate 	int		retval = 0;
22977c478bd9Sstevel@tonic-gate 	long		val;
22987c478bd9Sstevel@tonic-gate 	void		*addr;
22997c478bd9Sstevel@tonic-gate 	char		*endp, *sname;
23007c478bd9Sstevel@tonic-gate 	env_sensor_t	*sensorp;
23017c478bd9Sstevel@tonic-gate 
23027c478bd9Sstevel@tonic-gate 	/*
23037c478bd9Sstevel@tonic-gate 	 * Tuneable entry can be in one of the following formats:
23047c478bd9Sstevel@tonic-gate 	 *
23057c478bd9Sstevel@tonic-gate 	 *	threshold-keyword <int-value>
23067c478bd9Sstevel@tonic-gate 	 *	threshold-keyword <int-value> <sensor-name> ...
23077c478bd9Sstevel@tonic-gate 	 *
23087c478bd9Sstevel@tonic-gate 	 * Convert threshold value into integer value and check for
23097c478bd9Sstevel@tonic-gate 	 * optional sensor name. If no sensor name is specified, then
23107c478bd9Sstevel@tonic-gate 	 * the tuneable applies to all sensors specified by the "flags".
23117c478bd9Sstevel@tonic-gate 	 * Otherwise, it is applicable to the specified sensors.
23127c478bd9Sstevel@tonic-gate 	 *
23137c478bd9Sstevel@tonic-gate 	 * Note that the dummy_thresh_addr is the address of the threshold
23147c478bd9Sstevel@tonic-gate 	 * to be changed and is converted into offset by subtracting the
23157c478bd9Sstevel@tonic-gate 	 * base dummy_thresh address. This offset is added to the base
23167c478bd9Sstevel@tonic-gate 	 * address of the threshold structure to be update to determine
23177c478bd9Sstevel@tonic-gate 	 * the final memory address to be modified.
23187c478bd9Sstevel@tonic-gate 	 */
23197c478bd9Sstevel@tonic-gate 
23207c478bd9Sstevel@tonic-gate 	errno = 0;
23217c478bd9Sstevel@tonic-gate 	val = strtol(buf, &endp, 0);
23227c478bd9Sstevel@tonic-gate 	sname = strtok(endp, tokdel);
23237c478bd9Sstevel@tonic-gate 
23247c478bd9Sstevel@tonic-gate 	if (errno != 0 || val != (tempr_t)val) {
23257c478bd9Sstevel@tonic-gate 		retval = -1;
23267c478bd9Sstevel@tonic-gate 		envd_log(LOG_INFO, ENV_CONF_INT_EXPECTED, fname, line, keyword);
23277c478bd9Sstevel@tonic-gate 	} else if (flags == 0 && sname == NULL) {
23287c478bd9Sstevel@tonic-gate 		envd_log(LOG_INFO, "SUNW_piclenvd: file:%s line:%d SKIPPED"
23297c478bd9Sstevel@tonic-gate 		    " as no sensor specified.\n", fname, line, keyword);
23307c478bd9Sstevel@tonic-gate 		retval = -1;
23317c478bd9Sstevel@tonic-gate 	} else if (sname == NULL) {
23327c478bd9Sstevel@tonic-gate 		int	cnt = 0;
23337c478bd9Sstevel@tonic-gate 
23347c478bd9Sstevel@tonic-gate 		for (sensorp = &envd_sensors[0]; sensorp->name; sensorp++) {
23357c478bd9Sstevel@tonic-gate 			if (sensorp->temp_thresh == NULL ||
23367c478bd9Sstevel@tonic-gate 			    (sensorp->flags & flags) == 0)
23377c478bd9Sstevel@tonic-gate 				continue;
23387c478bd9Sstevel@tonic-gate 
23397c478bd9Sstevel@tonic-gate 			/*
23407c478bd9Sstevel@tonic-gate 			 * Convert dummy_thresh_addr into memory address
23417c478bd9Sstevel@tonic-gate 			 * for this sensor threshold values.
23427c478bd9Sstevel@tonic-gate 			 */
23437c478bd9Sstevel@tonic-gate 			addr = (char *)sensorp->temp_thresh +
23447c478bd9Sstevel@tonic-gate 			    (int)((char *)dummy_thresh_addr -
23457c478bd9Sstevel@tonic-gate 			    (char *)&dummy_thresh);
23467c478bd9Sstevel@tonic-gate 
23477c478bd9Sstevel@tonic-gate 			*(tempr_t *)addr = (tempr_t)val;
23487c478bd9Sstevel@tonic-gate 			cnt++;
23497c478bd9Sstevel@tonic-gate 			if (env_debug)
23507c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO, "SUNW_piclenvd: file:%s "
23517c478bd9Sstevel@tonic-gate 				"line:%d %s = %d for sensor: '%s'\n",
23527c478bd9Sstevel@tonic-gate 				    fname, line, keyword, val, sensorp->name);
23537c478bd9Sstevel@tonic-gate 		}
23547c478bd9Sstevel@tonic-gate 		if (cnt == 0)
23557c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO, "SUNW_piclenvd: file:%s line:%d "
23567c478bd9Sstevel@tonic-gate 			"%s SKIPPED as no matching sensor found.\n",
23577c478bd9Sstevel@tonic-gate 			    fname, line, keyword);
23587c478bd9Sstevel@tonic-gate 	} else {
23597c478bd9Sstevel@tonic-gate 		/* apply threshold value to the specified sensors */
23607c478bd9Sstevel@tonic-gate 		do {
23617c478bd9Sstevel@tonic-gate 			sensorp = sensor_lookup(sname);
23627c478bd9Sstevel@tonic-gate 			if (sensorp == NULL || sensorp->temp_thresh == NULL ||
23637c478bd9Sstevel@tonic-gate 			    (flags && (sensorp->flags & flags) == 0)) {
23647c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO,
23657c478bd9Sstevel@tonic-gate 				"SUNW_piclenvd: file:%s line:%d %s SKIPPED"
23667c478bd9Sstevel@tonic-gate 				" for '%s' as not a valid sensor.\n",
23677c478bd9Sstevel@tonic-gate 				    fname, line, keyword, sname);
23687c478bd9Sstevel@tonic-gate 				continue;
23697c478bd9Sstevel@tonic-gate 			}
23707c478bd9Sstevel@tonic-gate 			/*
23717c478bd9Sstevel@tonic-gate 			 * Convert dummy_thresh_addr into memory address
23727c478bd9Sstevel@tonic-gate 			 * for this sensor threshold values.
23737c478bd9Sstevel@tonic-gate 			 */
23747c478bd9Sstevel@tonic-gate 			addr = (char *)sensorp->temp_thresh +
23757c478bd9Sstevel@tonic-gate 			    (int)((char *)dummy_thresh_addr -
23767c478bd9Sstevel@tonic-gate 			    (char *)&dummy_thresh);
23777c478bd9Sstevel@tonic-gate 
23787c478bd9Sstevel@tonic-gate 			*(tempr_t *)addr = (tempr_t)val;
23797c478bd9Sstevel@tonic-gate 			if (env_debug)
23807c478bd9Sstevel@tonic-gate 				envd_log(LOG_INFO, "SUNW_piclenvd: file:%s "
23817c478bd9Sstevel@tonic-gate 				"line:%d %s = %d for sensor: '%s'\n",
23827c478bd9Sstevel@tonic-gate 				    fname, line, keyword, val, sensorp->name);
23837c478bd9Sstevel@tonic-gate 		} while ((sname = strtok(NULL, tokdel)) != NULL);
23847c478bd9Sstevel@tonic-gate 	}
23857c478bd9Sstevel@tonic-gate 	return (retval);
23867c478bd9Sstevel@tonic-gate }
23877c478bd9Sstevel@tonic-gate 
23887c478bd9Sstevel@tonic-gate 
23897c478bd9Sstevel@tonic-gate /*
23907c478bd9Sstevel@tonic-gate  * Process integer tuneables
23917c478bd9Sstevel@tonic-gate  */
23927c478bd9Sstevel@tonic-gate static int
process_int_tuneable(char * keyword,char * buf,void * addr,int size,char * fname,int line)23937c478bd9Sstevel@tonic-gate process_int_tuneable(char *keyword, char *buf, void *addr, int size,
23947c478bd9Sstevel@tonic-gate     char *fname, int line)
23957c478bd9Sstevel@tonic-gate {
23967c478bd9Sstevel@tonic-gate 	int	retval = 0;
23977c478bd9Sstevel@tonic-gate 	char	*endp;
23987c478bd9Sstevel@tonic-gate 	long	val;
23997c478bd9Sstevel@tonic-gate 
24007c478bd9Sstevel@tonic-gate 	/*
24017c478bd9Sstevel@tonic-gate 	 * Convert input into integer value and ensure that there is
24027c478bd9Sstevel@tonic-gate 	 * no other token in the buffer.
24037c478bd9Sstevel@tonic-gate 	 */
24047c478bd9Sstevel@tonic-gate 	errno = 0;
24057c478bd9Sstevel@tonic-gate 	val = strtol(buf, &endp, 0);
24067c478bd9Sstevel@tonic-gate 	if (errno != 0 || strtok(endp, tokdel) != NULL)
24077c478bd9Sstevel@tonic-gate 		retval = -1;
24087c478bd9Sstevel@tonic-gate 	else {
24097c478bd9Sstevel@tonic-gate 		switch (size) {
24107c478bd9Sstevel@tonic-gate 		case 1:
24117c478bd9Sstevel@tonic-gate 			if (val != (int8_t)val)
24127c478bd9Sstevel@tonic-gate 				retval = -1;
24137c478bd9Sstevel@tonic-gate 			else
24147c478bd9Sstevel@tonic-gate 				*(int8_t *)addr = (int8_t)val;
24157c478bd9Sstevel@tonic-gate 			break;
24167c478bd9Sstevel@tonic-gate 		case 2:
24177c478bd9Sstevel@tonic-gate 			if (val != (short)val)
24187c478bd9Sstevel@tonic-gate 				retval = -1;
24197c478bd9Sstevel@tonic-gate 			else
24207c478bd9Sstevel@tonic-gate 				*(short *)addr = (short)val;
24217c478bd9Sstevel@tonic-gate 			break;
24227c478bd9Sstevel@tonic-gate 		case 4:
24237c478bd9Sstevel@tonic-gate 			*(int *)addr = (int)val;
24247c478bd9Sstevel@tonic-gate 			break;
24257c478bd9Sstevel@tonic-gate 		default:
24267c478bd9Sstevel@tonic-gate 			retval = -1;
24277c478bd9Sstevel@tonic-gate 		}
24287c478bd9Sstevel@tonic-gate 	}
24297c478bd9Sstevel@tonic-gate 
24307c478bd9Sstevel@tonic-gate 	if (retval == -1)
24317c478bd9Sstevel@tonic-gate 		envd_log(LOG_INFO, ENV_CONF_INT_EXPECTED,
24327c478bd9Sstevel@tonic-gate 		    fname, line, keyword);
24337c478bd9Sstevel@tonic-gate 	else if (env_debug)
24347c478bd9Sstevel@tonic-gate 		envd_log(LOG_INFO, "SUNW_piclenvd: file:%s line:%d %s = %d\n",
24357c478bd9Sstevel@tonic-gate 		    fname, line, keyword, val);
24367c478bd9Sstevel@tonic-gate 
24377c478bd9Sstevel@tonic-gate 	return (retval);
24387c478bd9Sstevel@tonic-gate }
24397c478bd9Sstevel@tonic-gate 
24407c478bd9Sstevel@tonic-gate 
24417c478bd9Sstevel@tonic-gate /*
24427c478bd9Sstevel@tonic-gate  * Process string tuneables
24437c478bd9Sstevel@tonic-gate  *
24447c478bd9Sstevel@tonic-gate  * String value must be within double quotes.  Skip over initial white
24457c478bd9Sstevel@tonic-gate  * spaces before looking for string value.
24467c478bd9Sstevel@tonic-gate  */
24477c478bd9Sstevel@tonic-gate static int
process_string_tuneable(char * keyword,char * buf,void * addr,int size,char * fname,int line)24487c478bd9Sstevel@tonic-gate process_string_tuneable(char *keyword, char *buf, void *addr, int size,
24497c478bd9Sstevel@tonic-gate     char *fname, int line)
24507c478bd9Sstevel@tonic-gate {
24517c478bd9Sstevel@tonic-gate 	int	retval = 0;
24527c478bd9Sstevel@tonic-gate 	char	c, *p, *strend;
24537c478bd9Sstevel@tonic-gate 
24547c478bd9Sstevel@tonic-gate 	/* Skip over white spaces */
24557c478bd9Sstevel@tonic-gate 	buf += strspn(buf, tokdel);
24567c478bd9Sstevel@tonic-gate 
24577c478bd9Sstevel@tonic-gate 	/*
24587c478bd9Sstevel@tonic-gate 	 * Parse srting and locate string end (handling escaped double quotes
24597c478bd9Sstevel@tonic-gate 	 * and other characters)
24607c478bd9Sstevel@tonic-gate 	 */
24617c478bd9Sstevel@tonic-gate 	if (buf[0] != '"')
24627c478bd9Sstevel@tonic-gate 		strend = NULL;
24637c478bd9Sstevel@tonic-gate 	else {
24647c478bd9Sstevel@tonic-gate 		for (p = buf+1; (c = *p) != '\0'; p++)
24657c478bd9Sstevel@tonic-gate 			if (c == '"' || (c == '\\' && *++p == '\0'))
24667c478bd9Sstevel@tonic-gate 				break;
24677c478bd9Sstevel@tonic-gate 		strend = (*p == '"') ? p : NULL;
24687c478bd9Sstevel@tonic-gate 	}
24697c478bd9Sstevel@tonic-gate 
24707c478bd9Sstevel@tonic-gate 	if (strend == NULL || (strend-buf) > size ||
24717c478bd9Sstevel@tonic-gate 	    strtok(strend+1, tokdel) != NULL) {
24727c478bd9Sstevel@tonic-gate 		envd_log(LOG_WARNING, ENV_CONF_STRING_EXPECTED,
24737c478bd9Sstevel@tonic-gate 		    fname, line, keyword, size);
24747c478bd9Sstevel@tonic-gate 		retval = -1;
24757c478bd9Sstevel@tonic-gate 	} else {
24767c478bd9Sstevel@tonic-gate 		*strend = '\0';
24777c478bd9Sstevel@tonic-gate 		(void) strcpy(addr, (caddr_t)buf+1);
24787c478bd9Sstevel@tonic-gate 		if (env_debug)
24797c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO, "SUNW_piclenvd: file:%s line:%d "
24807c478bd9Sstevel@tonic-gate 			    "%s = \"%s\"\n", fname, line, keyword, buf+1);
24817c478bd9Sstevel@tonic-gate 	}
24827c478bd9Sstevel@tonic-gate 
24837c478bd9Sstevel@tonic-gate 	return (retval);
24847c478bd9Sstevel@tonic-gate }
24857c478bd9Sstevel@tonic-gate 
24867c478bd9Sstevel@tonic-gate 
24877c478bd9Sstevel@tonic-gate /*
24887c478bd9Sstevel@tonic-gate  * Process configuration file
24897c478bd9Sstevel@tonic-gate  */
24907c478bd9Sstevel@tonic-gate static void
process_env_conf_file(void)24917c478bd9Sstevel@tonic-gate process_env_conf_file(void)
24927c478bd9Sstevel@tonic-gate {
24937c478bd9Sstevel@tonic-gate 	int		line, len, toklen;
24947c478bd9Sstevel@tonic-gate 	char		buf[BUFSIZ];
24957c478bd9Sstevel@tonic-gate 	FILE		*fp;
24967c478bd9Sstevel@tonic-gate 	env_tuneable_t	*tunep;
24977c478bd9Sstevel@tonic-gate 	char		nmbuf[SYS_NMLN];
24987c478bd9Sstevel@tonic-gate 	char		fname[PATH_MAX];
24997c478bd9Sstevel@tonic-gate 	char		*tok, *valuep;
25007c478bd9Sstevel@tonic-gate 	int		skip_line = 0;
25017c478bd9Sstevel@tonic-gate 
25027c478bd9Sstevel@tonic-gate 	if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) == -1)
25037c478bd9Sstevel@tonic-gate 		return;
25047c478bd9Sstevel@tonic-gate 
25057c478bd9Sstevel@tonic-gate 	(void) snprintf(fname, sizeof (fname), PICLD_PLAT_PLUGIN_DIRF, nmbuf);
25067c478bd9Sstevel@tonic-gate 	(void) strlcat(fname, ENV_CONF_FILE, sizeof (fname));
25077c478bd9Sstevel@tonic-gate 	fp = fopen(fname, "r");
25087c478bd9Sstevel@tonic-gate 	if (fp == NULL)
25097c478bd9Sstevel@tonic-gate 		return;
25107c478bd9Sstevel@tonic-gate 
25117c478bd9Sstevel@tonic-gate 	/*
25127c478bd9Sstevel@tonic-gate 	 * Blank lines or lines starting with "#" or "*" in the first
25137c478bd9Sstevel@tonic-gate 	 * column are ignored. All other lines are assumed to contain
25147c478bd9Sstevel@tonic-gate 	 * input in the following format:
25157c478bd9Sstevel@tonic-gate 	 *
25167c478bd9Sstevel@tonic-gate 	 *	keyword value
25177c478bd9Sstevel@tonic-gate 	 *
25187c478bd9Sstevel@tonic-gate 	 * where the "value" can be a signed integer or string (in
25197c478bd9Sstevel@tonic-gate 	 * double quotes) depending upon the keyword.
25207c478bd9Sstevel@tonic-gate 	 */
25217c478bd9Sstevel@tonic-gate 
25227c478bd9Sstevel@tonic-gate 	for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) {
25237c478bd9Sstevel@tonic-gate 		len = strlen(buf);
25247c478bd9Sstevel@tonic-gate 		if (len <= 0)
25257c478bd9Sstevel@tonic-gate 			continue;
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate 		/* skip long lines */
25287c478bd9Sstevel@tonic-gate 		if (buf[len-1] != '\n') {
25297c478bd9Sstevel@tonic-gate 			skip_line = 1;
25307c478bd9Sstevel@tonic-gate 			continue;
25317c478bd9Sstevel@tonic-gate 		} else if (skip_line) {
25327c478bd9Sstevel@tonic-gate 			skip_line = 0;
25337c478bd9Sstevel@tonic-gate 			continue;
25347c478bd9Sstevel@tonic-gate 		} else
25357c478bd9Sstevel@tonic-gate 			buf[len-1] = '\0';
25367c478bd9Sstevel@tonic-gate 
25377c478bd9Sstevel@tonic-gate 		/* skip comments */
25387c478bd9Sstevel@tonic-gate 		if (buf[0] == '*' || buf[0] == '#')
25397c478bd9Sstevel@tonic-gate 			continue;
25407c478bd9Sstevel@tonic-gate 
25417c478bd9Sstevel@tonic-gate 		/*
25427c478bd9Sstevel@tonic-gate 		 * Skip over white space to get the keyword
25437c478bd9Sstevel@tonic-gate 		 */
25447c478bd9Sstevel@tonic-gate 		tok = buf + strspn(buf, tokdel);
25457c478bd9Sstevel@tonic-gate 		if (*tok == '\0')
25467c478bd9Sstevel@tonic-gate 			continue;			/* blank line */
25477c478bd9Sstevel@tonic-gate 
25487c478bd9Sstevel@tonic-gate 		toklen = strcspn(tok, tokdel);
25497c478bd9Sstevel@tonic-gate 		tok[toklen] = '\0';
25507c478bd9Sstevel@tonic-gate 
25517c478bd9Sstevel@tonic-gate 		/* Get possible location for value (within current line) */
25527c478bd9Sstevel@tonic-gate 		valuep = tok + toklen + 1;
25537c478bd9Sstevel@tonic-gate 		if (valuep > buf+len)
25547c478bd9Sstevel@tonic-gate 			valuep = buf + len;
25557c478bd9Sstevel@tonic-gate 
25567c478bd9Sstevel@tonic-gate 		/*
25577c478bd9Sstevel@tonic-gate 		 * Lookup the keyword and process value accordingly
25587c478bd9Sstevel@tonic-gate 		 */
25597c478bd9Sstevel@tonic-gate 		for (tunep = &env_tuneables[0]; tunep->name != NULL; tunep++) {
25607c478bd9Sstevel@tonic-gate 			if (strcasecmp(tunep->name, tok) == 0) {
25617c478bd9Sstevel@tonic-gate 				(void) (*tunep->func)(tok, valuep,
25627c478bd9Sstevel@tonic-gate 				    tunep->arg1, tunep->arg2, fname, line);
25637c478bd9Sstevel@tonic-gate 				break;
25647c478bd9Sstevel@tonic-gate 			}
25657c478bd9Sstevel@tonic-gate 		}
25667c478bd9Sstevel@tonic-gate 
25677c478bd9Sstevel@tonic-gate 		if (tunep->name == NULL)
25687c478bd9Sstevel@tonic-gate 			envd_log(LOG_INFO, ENV_CONF_UNSUPPORTED_KEYWORD,
25697c478bd9Sstevel@tonic-gate 			    fname, line, tok);
25707c478bd9Sstevel@tonic-gate 	}
25717c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
25727c478bd9Sstevel@tonic-gate }
25737c478bd9Sstevel@tonic-gate 
25747c478bd9Sstevel@tonic-gate /*
25757c478bd9Sstevel@tonic-gate  * Setup envrionmental monitor state and start threads to monitor
25767c478bd9Sstevel@tonic-gate  * temperature and power management state.
25777c478bd9Sstevel@tonic-gate  * Returns -1 on error, 0 if successful.
25787c478bd9Sstevel@tonic-gate  */
25797c478bd9Sstevel@tonic-gate 
25807c478bd9Sstevel@tonic-gate static int
envd_setup(void)25817c478bd9Sstevel@tonic-gate envd_setup(void)
25827c478bd9Sstevel@tonic-gate {
25837c478bd9Sstevel@tonic-gate 	char		*valp, *endp;
25847c478bd9Sstevel@tonic-gate 	int		val;
25857c478bd9Sstevel@tonic-gate 	int		err;
25867c478bd9Sstevel@tonic-gate 
25877c478bd9Sstevel@tonic-gate 	if (pthread_attr_init(&thr_attr) != 0 ||
25887c478bd9Sstevel@tonic-gate 	    pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM) != 0)
25897c478bd9Sstevel@tonic-gate 		return (-1);
25907c478bd9Sstevel@tonic-gate 
25917c478bd9Sstevel@tonic-gate 	if (pm_fd == -1)
25927c478bd9Sstevel@tonic-gate 		envd_open_pm();
25937c478bd9Sstevel@tonic-gate 
25947c478bd9Sstevel@tonic-gate 	/*
25957c478bd9Sstevel@tonic-gate 	 * Setup lpm devices
25967c478bd9Sstevel@tonic-gate 	 */
25977c478bd9Sstevel@tonic-gate 	lpm_devices = NULL;
25987c478bd9Sstevel@tonic-gate 	if ((err = setup_lpm_devices(&lpm_devices)) != PICL_SUCCESS) {
25997c478bd9Sstevel@tonic-gate 		if (env_debug)
26007c478bd9Sstevel@tonic-gate 			envd_log(LOG_ERR, "setup_lpm_devices failed err = %d\n",
26017c478bd9Sstevel@tonic-gate 				err);
26027c478bd9Sstevel@tonic-gate 	}
26037c478bd9Sstevel@tonic-gate 
26047c478bd9Sstevel@tonic-gate 	/*
26057c478bd9Sstevel@tonic-gate 	 * Initialize global state to initial startup values
26067c478bd9Sstevel@tonic-gate 	 */
26077c478bd9Sstevel@tonic-gate 	sensor_poll_interval = SENSOR_POLL_INTERVAL;
26087c478bd9Sstevel@tonic-gate 	fan_slow_adjustment = FAN_SLOW_ADJUSTMENT;
26097c478bd9Sstevel@tonic-gate 	fan_incr_limit = FAN_INCREMENT_LIMIT;
26107c478bd9Sstevel@tonic-gate 	fan_decr_limit = FAN_DECREMENT_LIMIT;
26117c478bd9Sstevel@tonic-gate 	warning_interval = WARNING_INTERVAL;
26127c478bd9Sstevel@tonic-gate 	warning_duration = WARNING_DURATION;
26137c478bd9Sstevel@tonic-gate 	shutdown_interval = SHUTDOWN_INTERVAL;
26147c478bd9Sstevel@tonic-gate 	disable_piclenvd = 0;
26157c478bd9Sstevel@tonic-gate 	disable_power_off = 0;
26167c478bd9Sstevel@tonic-gate 	disable_shutdown = 0;
26177c478bd9Sstevel@tonic-gate 	disable_warning = 0;
26187c478bd9Sstevel@tonic-gate 
26197c478bd9Sstevel@tonic-gate 	(void) strlcpy(shutdown_cmd, SHUTDOWN_CMD, sizeof (shutdown_cmd));
26207c478bd9Sstevel@tonic-gate 	(void) strlcpy(devfsadm_cmd, DEVFSADM_CMD, sizeof (devfsadm_cmd));
26217c478bd9Sstevel@tonic-gate 	(void) strlcpy(fru_devfsadm_cmd, FRU_DEVFSADM_CMD,
26227c478bd9Sstevel@tonic-gate 	    sizeof (fru_devfsadm_cmd));
26237c478bd9Sstevel@tonic-gate 	envd_cpu_fan.forced_speed = -1;
26247c478bd9Sstevel@tonic-gate 	envd_system_fan.forced_speed = -1;
26257c478bd9Sstevel@tonic-gate 
26267c478bd9Sstevel@tonic-gate 	(void) memcpy(&cpu0_die_thresh, &cpu_die_thresh_default,
26277c478bd9Sstevel@tonic-gate 	    sizeof (cpu_die_thresh_default));
26287c478bd9Sstevel@tonic-gate 	(void) memcpy(&cpu0_amb_thresh, &cpu_amb_thresh_default,
26297c478bd9Sstevel@tonic-gate 	    sizeof (cpu_amb_thresh_default));
26307c478bd9Sstevel@tonic-gate 	(void) memcpy(&cpu1_die_thresh, &cpu_die_thresh_default,
26317c478bd9Sstevel@tonic-gate 	    sizeof (cpu_die_thresh_default));
26327c478bd9Sstevel@tonic-gate 	(void) memcpy(&cpu1_amb_thresh, &cpu_amb_thresh_default,
26337c478bd9Sstevel@tonic-gate 	    sizeof (cpu_amb_thresh_default));
26347c478bd9Sstevel@tonic-gate 
26357c478bd9Sstevel@tonic-gate 	if ((valp = getenv("SUNW_piclenvd_debug")) != NULL) {
26367c478bd9Sstevel@tonic-gate 		val = strtol(valp, &endp, 0);
26377c478bd9Sstevel@tonic-gate 		if (strtok(endp, tokdel) == NULL)
26387c478bd9Sstevel@tonic-gate 			env_debug = val;
26397c478bd9Sstevel@tonic-gate 	}
26407c478bd9Sstevel@tonic-gate 
26417c478bd9Sstevel@tonic-gate 	/*
26427c478bd9Sstevel@tonic-gate 	 * Create a thread to monitor temperature and control fan
26437c478bd9Sstevel@tonic-gate 	 * speed.
26447c478bd9Sstevel@tonic-gate 	 */
26457c478bd9Sstevel@tonic-gate 	if (envthr_created == B_FALSE && pthread_create(&envthr_tid,
26467c478bd9Sstevel@tonic-gate 	    &thr_attr, envthr, (void *)NULL) != 0) {
26477c478bd9Sstevel@tonic-gate 		envd_close_fans();
26487c478bd9Sstevel@tonic-gate 		envd_close_sensors();
26497c478bd9Sstevel@tonic-gate 		envd_close_pm();
26507c478bd9Sstevel@tonic-gate 		envd_log(LOG_CRIT, ENV_THREAD_CREATE_FAILED);
26517c478bd9Sstevel@tonic-gate 		return (-1);
26527c478bd9Sstevel@tonic-gate 	}
26537c478bd9Sstevel@tonic-gate 	envthr_created = B_TRUE;
26547c478bd9Sstevel@tonic-gate 
26557c478bd9Sstevel@tonic-gate 	/*
26567c478bd9Sstevel@tonic-gate 	 * Create a thread to monitor PM state
26577c478bd9Sstevel@tonic-gate 	 */
26587c478bd9Sstevel@tonic-gate 	if (pmthr_exists == B_FALSE) {
26597c478bd9Sstevel@tonic-gate 		if (pm_fd == -1 || pthread_create(&pmthr_tid, &thr_attr,
26607c478bd9Sstevel@tonic-gate 		    pmthr, (void *)NULL) != 0) {
26617c478bd9Sstevel@tonic-gate 			envd_log(LOG_CRIT, PM_THREAD_CREATE_FAILED);
26627c478bd9Sstevel@tonic-gate 		} else
26637c478bd9Sstevel@tonic-gate 			pmthr_exists = B_TRUE;
26647c478bd9Sstevel@tonic-gate 	}
26657c478bd9Sstevel@tonic-gate 	return (0);
26667c478bd9Sstevel@tonic-gate }
26677c478bd9Sstevel@tonic-gate 
26687c478bd9Sstevel@tonic-gate /*
26697c478bd9Sstevel@tonic-gate  * Callback function used by ptree_walk_tree_by_class for the cpu class
26707c478bd9Sstevel@tonic-gate  */
26717c478bd9Sstevel@tonic-gate static int
cb_cpu(picl_nodehdl_t nodeh,void * args)26727c478bd9Sstevel@tonic-gate cb_cpu(picl_nodehdl_t nodeh, void *args)
26737c478bd9Sstevel@tonic-gate {
26747c478bd9Sstevel@tonic-gate 	sensor_pmdev_t		*pmdevp;
26757c478bd9Sstevel@tonic-gate 	int			err;
26767c478bd9Sstevel@tonic-gate 	ptree_propinfo_t	pinfo;
26777c478bd9Sstevel@tonic-gate 	picl_prophdl_t		proph;
26787c478bd9Sstevel@tonic-gate 	size_t			psize;
26797c478bd9Sstevel@tonic-gate 	int			id;
26807c478bd9Sstevel@tonic-gate 
26817c478bd9Sstevel@tonic-gate 	/* Get CPU's ID, it is an int */
26827c478bd9Sstevel@tonic-gate 	err = ptree_get_propval_by_name(nodeh, PICL_PROP_ID, &id, sizeof (int));
26837c478bd9Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
26847c478bd9Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
26857c478bd9Sstevel@tonic-gate 
26867c478bd9Sstevel@tonic-gate 	/* Get the pmdevp for the CPU */
26877c478bd9Sstevel@tonic-gate 	pmdevp = sensor_pmdevs;
26887c478bd9Sstevel@tonic-gate 	while (pmdevp->sensor_id != -1) {
26897c478bd9Sstevel@tonic-gate 		if (id == pmdevp->sensor_id)
26907c478bd9Sstevel@tonic-gate 			break;
26917c478bd9Sstevel@tonic-gate 		pmdevp++;
26927c478bd9Sstevel@tonic-gate 	}
26937c478bd9Sstevel@tonic-gate 
26947c478bd9Sstevel@tonic-gate 	/* Return if didn't find the pmdevp for the cpu id */
26957c478bd9Sstevel@tonic-gate 	if (pmdevp->sensor_id == -1)
26967c478bd9Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
26977c478bd9Sstevel@tonic-gate 
26987c478bd9Sstevel@tonic-gate 	/* Get the devfs-path property */
26997c478bd9Sstevel@tonic-gate 	err = ptree_get_prop_by_name(nodeh, PICL_PROP_DEVFS_PATH, &proph);
27007c478bd9Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
27017c478bd9Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
27027c478bd9Sstevel@tonic-gate 
27037c478bd9Sstevel@tonic-gate 	err = ptree_get_propinfo(proph, &pinfo);
27047c478bd9Sstevel@tonic-gate 	if ((err != PICL_SUCCESS) ||
27057c478bd9Sstevel@tonic-gate 	    (pinfo.piclinfo.type != PICL_PTYPE_CHARSTRING))
27067c478bd9Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
27077c478bd9Sstevel@tonic-gate 
27087c478bd9Sstevel@tonic-gate 	psize = pinfo.piclinfo.size;
27097c478bd9Sstevel@tonic-gate 	pmdevp->pmdev_name = malloc(psize);
27107c478bd9Sstevel@tonic-gate 	if (pmdevp->pmdev_name == NULL)
27117c478bd9Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
27127c478bd9Sstevel@tonic-gate 
27137c478bd9Sstevel@tonic-gate 	err = ptree_get_propval(proph, pmdevp->pmdev_name, psize);
27147c478bd9Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
27157c478bd9Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
27167c478bd9Sstevel@tonic-gate 
27177c478bd9Sstevel@tonic-gate 	return (PICL_WALK_CONTINUE);
27187c478bd9Sstevel@tonic-gate }
27197c478bd9Sstevel@tonic-gate 
27207c478bd9Sstevel@tonic-gate /*
27217c478bd9Sstevel@tonic-gate  * Find the CPU's in the picl tree, set the devfs-path for pmdev_name
27227c478bd9Sstevel@tonic-gate  */
27237c478bd9Sstevel@tonic-gate static void
setup_pmdev_names()27247c478bd9Sstevel@tonic-gate setup_pmdev_names()
27257c478bd9Sstevel@tonic-gate {
27267c478bd9Sstevel@tonic-gate 	picl_nodehdl_t	plath;
27277c478bd9Sstevel@tonic-gate 	int		err;
27287c478bd9Sstevel@tonic-gate 
27297c478bd9Sstevel@tonic-gate 	err = ptree_get_node_by_path(PLATFORM_PATH, &plath);
27307c478bd9Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
27317c478bd9Sstevel@tonic-gate 		return;
27327c478bd9Sstevel@tonic-gate 
27337c478bd9Sstevel@tonic-gate 	err = ptree_walk_tree_by_class(plath, PICL_CLASS_CPU, NULL, cb_cpu);
27347c478bd9Sstevel@tonic-gate }
27357c478bd9Sstevel@tonic-gate 
27367c478bd9Sstevel@tonic-gate 
27377c478bd9Sstevel@tonic-gate static void
piclenvd_register(void)27387c478bd9Sstevel@tonic-gate piclenvd_register(void)
27397c478bd9Sstevel@tonic-gate {
27407c478bd9Sstevel@tonic-gate 	picld_plugin_register(&my_reg_info);
27417c478bd9Sstevel@tonic-gate }
27427c478bd9Sstevel@tonic-gate 
27437c478bd9Sstevel@tonic-gate static void
piclenvd_init(void)27447c478bd9Sstevel@tonic-gate piclenvd_init(void)
27457c478bd9Sstevel@tonic-gate {
27467c478bd9Sstevel@tonic-gate 	/*
27477c478bd9Sstevel@tonic-gate 	 * Setup the names for the pm sensors, we do it just the first time
27487c478bd9Sstevel@tonic-gate 	 */
27497c478bd9Sstevel@tonic-gate 	if (pmdev_names_init == B_FALSE) {
27507c478bd9Sstevel@tonic-gate 		(void) setup_pmdev_names();
27517c478bd9Sstevel@tonic-gate 		pmdev_names_init = B_TRUE;
27527c478bd9Sstevel@tonic-gate 	}
27537c478bd9Sstevel@tonic-gate 
27547c478bd9Sstevel@tonic-gate 	/*
27557c478bd9Sstevel@tonic-gate 	 * Start environmental monitor/threads
27567c478bd9Sstevel@tonic-gate 	 */
27577c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&envd_rwlock);
27587c478bd9Sstevel@tonic-gate 	if (envd_setup() != 0) {
27597c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&envd_rwlock);
27607c478bd9Sstevel@tonic-gate 		envd_log(LOG_CRIT, ENVD_PLUGIN_INIT_FAILED);
27617c478bd9Sstevel@tonic-gate 		return;
27627c478bd9Sstevel@tonic-gate 	}
27637c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&envd_rwlock);
27647c478bd9Sstevel@tonic-gate 
27657c478bd9Sstevel@tonic-gate 	/*
27667c478bd9Sstevel@tonic-gate 	 * Now setup/populate PICL tree
27677c478bd9Sstevel@tonic-gate 	 */
27687c478bd9Sstevel@tonic-gate 	env_picl_setup();
27697c478bd9Sstevel@tonic-gate }
27707c478bd9Sstevel@tonic-gate 
27717c478bd9Sstevel@tonic-gate static void
piclenvd_fini(void)27727c478bd9Sstevel@tonic-gate piclenvd_fini(void)
27737c478bd9Sstevel@tonic-gate {
27747c478bd9Sstevel@tonic-gate 	/*
27757c478bd9Sstevel@tonic-gate 	 * Delete the lpm device list. After this the lpm information
27767c478bd9Sstevel@tonic-gate 	 * will not be used in determining the fan speed, till the lpm
27777c478bd9Sstevel@tonic-gate 	 * device information is initialized by setup_lpm_devices called
27787c478bd9Sstevel@tonic-gate 	 * by envd_setup.
27797c478bd9Sstevel@tonic-gate 	 */
27807c478bd9Sstevel@tonic-gate 	delete_lpm_devices();
27817c478bd9Sstevel@tonic-gate 
27827c478bd9Sstevel@tonic-gate 	/*
27837c478bd9Sstevel@tonic-gate 	 * Invoke env_picl_destroy() to remove any PICL nodes/properties
27847c478bd9Sstevel@tonic-gate 	 * (including volatile properties) we created. Once this call
27857c478bd9Sstevel@tonic-gate 	 * returns, there can't be any more calls from the PICL framework
27867c478bd9Sstevel@tonic-gate 	 * to get current temperature or fan speed.
27877c478bd9Sstevel@tonic-gate 	 */
27887c478bd9Sstevel@tonic-gate 	env_picl_destroy();
27897c478bd9Sstevel@tonic-gate 
27907c478bd9Sstevel@tonic-gate 	/*
27917c478bd9Sstevel@tonic-gate 	 * Since this is a critical plug-in, we know that it won't be
27927c478bd9Sstevel@tonic-gate 	 * unloaded and will be reinited again unless picld process is
27937c478bd9Sstevel@tonic-gate 	 * going away. Therefore, it's okay to let "envthr" and "pmthr"
27947c478bd9Sstevel@tonic-gate 	 * continue so that we can monitor the environment during SIGHUP
27957c478bd9Sstevel@tonic-gate 	 * handling also.
27967c478bd9Sstevel@tonic-gate 	 */
27977c478bd9Sstevel@tonic-gate }
27987c478bd9Sstevel@tonic-gate 
27997c478bd9Sstevel@tonic-gate /*VARARGS2*/
28007c478bd9Sstevel@tonic-gate void
envd_log(int pri,const char * fmt,...)28017c478bd9Sstevel@tonic-gate envd_log(int pri, const char *fmt, ...)
28027c478bd9Sstevel@tonic-gate {
28037c478bd9Sstevel@tonic-gate 	va_list	ap;
28047c478bd9Sstevel@tonic-gate 
28057c478bd9Sstevel@tonic-gate 	va_start(ap, fmt);
28067c478bd9Sstevel@tonic-gate 	vsyslog(pri, fmt, ap);
28077c478bd9Sstevel@tonic-gate 	va_end(ap);
28087c478bd9Sstevel@tonic-gate }
28097c478bd9Sstevel@tonic-gate 
28107c478bd9Sstevel@tonic-gate #ifdef __lint
28117c478bd9Sstevel@tonic-gate /*
28127c478bd9Sstevel@tonic-gate  * Redefine sigwait to posix style external declaration so that LINT
28137c478bd9Sstevel@tonic-gate  * does not check against libc version of sigwait() and complain as
28147c478bd9Sstevel@tonic-gate  * it uses different number of arguments.
28157c478bd9Sstevel@tonic-gate  */
28167c478bd9Sstevel@tonic-gate #define	sigwait	my_posix_sigwait
28177c478bd9Sstevel@tonic-gate extern int my_posix_sigwait(const sigset_t *set, int *sig);
28187c478bd9Sstevel@tonic-gate #endif
28197c478bd9Sstevel@tonic-gate 
28207c478bd9Sstevel@tonic-gate /*
28217c478bd9Sstevel@tonic-gate  * sleep() in libpthread gets affected by time being set back, hence
28227c478bd9Sstevel@tonic-gate  * can cause the "envthr" not to wakeup for extended duration. For
28237c478bd9Sstevel@tonic-gate  * now, we implement our own sleep() routine below using alarm().
28247c478bd9Sstevel@tonic-gate  * This will work only if SIGALRM is masked off in all other threads.
28257c478bd9Sstevel@tonic-gate  * Note that SIGALRM signal is masked off in the main thread, hence
28267c478bd9Sstevel@tonic-gate  * in all threads, including the envthr, the one calling this routine.
28277c478bd9Sstevel@tonic-gate  *
28287c478bd9Sstevel@tonic-gate  * Note that SIGALRM and alarm() can't be used by any other thread
28297c478bd9Sstevel@tonic-gate  * in this manner.
28307c478bd9Sstevel@tonic-gate  */
28317c478bd9Sstevel@tonic-gate 
28327c478bd9Sstevel@tonic-gate static unsigned int
envd_sleep(unsigned int sleep_tm)28337c478bd9Sstevel@tonic-gate envd_sleep(unsigned int sleep_tm)
28347c478bd9Sstevel@tonic-gate {
28357c478bd9Sstevel@tonic-gate 	int  		sig;
28367c478bd9Sstevel@tonic-gate 	unsigned int	unslept;
28377c478bd9Sstevel@tonic-gate 	sigset_t	alrm_mask;
28387c478bd9Sstevel@tonic-gate 
28397c478bd9Sstevel@tonic-gate 	if (sleep_tm == 0)
28407c478bd9Sstevel@tonic-gate 		return (0);
28417c478bd9Sstevel@tonic-gate 
28427c478bd9Sstevel@tonic-gate 	(void) sigemptyset(&alrm_mask);
28437c478bd9Sstevel@tonic-gate 	(void) sigaddset(&alrm_mask, SIGALRM);
28447c478bd9Sstevel@tonic-gate 
28457c478bd9Sstevel@tonic-gate 	(void) alarm(sleep_tm);
28467c478bd9Sstevel@tonic-gate 	(void) sigwait(&alrm_mask, &sig);
28477c478bd9Sstevel@tonic-gate 
28487c478bd9Sstevel@tonic-gate 	unslept = alarm(0);
28497c478bd9Sstevel@tonic-gate 	return (unslept);
28507c478bd9Sstevel@tonic-gate }
2851