xref: /onnv-gate/usr/src/cmd/picl/plugins/sun4u/excalibur/envd/piclenvd.c (revision 796:e9b20e3531f8)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
23*796Smathue  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * This file contains the environmental PICL plug-in module.
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate 
340Sstevel@tonic-gate /*
350Sstevel@tonic-gate  * Excalibur system contains up to two CPU and two PCI MAX1617 temperature
360Sstevel@tonic-gate  * devices, each consisting of two sensors: die and ambient. Each sensor is
370Sstevel@tonic-gate  * represented as a different minor device and the current temperature is read
380Sstevel@tonic-gate  * via an I2C_GET_TEMPERATURE ioctl call to the max1617 driver. Additionally,
390Sstevel@tonic-gate  * the MAX1617 device supports both a low and high temperature limit, which
400Sstevel@tonic-gate  * can trigger an alert condition, causing power supply to turn off.
410Sstevel@tonic-gate  *
420Sstevel@tonic-gate  * The environmental monitor defines the following thresholds per sensor:
430Sstevel@tonic-gate  *
440Sstevel@tonic-gate  *	high_power_off		high hard shutdown
450Sstevel@tonic-gate  *	high_shutdown		high soft shutdown limit
460Sstevel@tonic-gate  *	high_warning		high warning limit
470Sstevel@tonic-gate  *	low_warning		low warning limit
480Sstevel@tonic-gate  *	low_shutdown		low soft shutdown limit
490Sstevel@tonic-gate  *	low_power_off		low hard shutdown limit
500Sstevel@tonic-gate  *
510Sstevel@tonic-gate  * Above mentioned threshold values can be changed via "piclenvd.conf"
520Sstevel@tonic-gate  * configuration file.
530Sstevel@tonic-gate  *
540Sstevel@tonic-gate  * Environmental monitoring is done by the "envthr" thread. It periodically
550Sstevel@tonic-gate  * monitors both CPU die and CPU ambient temperatures and takes appropriate
560Sstevel@tonic-gate  * action depending upon the current temperature and threshold values for
570Sstevel@tonic-gate  * that sensor. If the temperature reaches the high_shutdown limit or the
580Sstevel@tonic-gate  * low_shutdown limit, and remains there for over shutdown_interval seconds,
590Sstevel@tonic-gate  * it forces a graceful system shutdown via tuneable shutdown_cmd string
600Sstevel@tonic-gate  * variable. Otherwise, if the temperature reaches the high_warning limit
610Sstevel@tonic-gate  * or the low_warning limit, it logs and prints a message on the console.
620Sstevel@tonic-gate  * This message will be printed at most at "warning_interval" seconds
630Sstevel@tonic-gate  * interval, which is also a tuneable variable.
640Sstevel@tonic-gate  *
650Sstevel@tonic-gate  * Excalibur system contains three fans: cpu, system and power supply. The
660Sstevel@tonic-gate  * cpu and system fans are under software control and their speed can be
670Sstevel@tonic-gate  * set to a value in the range 0 through 63. However, the software has no
680Sstevel@tonic-gate  * control over the power supply fan's speed (it's automatically controlled
690Sstevel@tonic-gate  * by the hardware), but it can turn it ON or OFF. When in EStar mode (i.e.
700Sstevel@tonic-gate  * the lowest power state), the environmental monitor turns off the power
710Sstevel@tonic-gate  * supply fan.
720Sstevel@tonic-gate  *
730Sstevel@tonic-gate  * Each fan is represented as a different minor device and the fan speed
740Sstevel@tonic-gate  * can be controlled by writing to the TDA8444 device driver. Note that
750Sstevel@tonic-gate  * these devices are read only and the driver caches the last speed set
760Sstevel@tonic-gate  * for each fan, thus allowing an interface to read the current fan speed
770Sstevel@tonic-gate  * also.
780Sstevel@tonic-gate  *
790Sstevel@tonic-gate  * The policy to control fan speed depends upon the sensor. For CPU die
800Sstevel@tonic-gate  * sensor, different policy is used depending upon whether the temperature
810Sstevel@tonic-gate  * is rising, falling or steady state. In case of CPU ambient sensor, only
820Sstevel@tonic-gate  * one policy (speed proportional to the current temperature) is used.
830Sstevel@tonic-gate  *
840Sstevel@tonic-gate  * The power state monitoring is done by the "pmthr" thread. It uses the
850Sstevel@tonic-gate  * PM_GET_STATE_CHANGE and PM_GET_STATE_CHANGE_WAIT ioctl commands to pick
860Sstevel@tonic-gate  * up any power state change events. It processes all queued power state
870Sstevel@tonic-gate  * change events and determines the curret lowest power state and saves it
880Sstevel@tonic-gate  * in cur_lpstate variable.
890Sstevel@tonic-gate  *
900Sstevel@tonic-gate  * Once the "envthr" and "pmthr" threads have been started, they are never
910Sstevel@tonic-gate  * killed. This is desirable so that we can do environmental monitoring
920Sstevel@tonic-gate  * during reinit process.  The "envd_rwlock" reader/writer lock is used
930Sstevel@tonic-gate  * to protect initialization of global state during reinit process against
940Sstevel@tonic-gate  * the "envthr" and "pmthr" trying to reference that state.
950Sstevel@tonic-gate  */
960Sstevel@tonic-gate 
970Sstevel@tonic-gate #include <stdio.h>
980Sstevel@tonic-gate #include <stdlib.h>
990Sstevel@tonic-gate #include <sys/sysmacros.h>
1000Sstevel@tonic-gate #include <limits.h>
1010Sstevel@tonic-gate #include <string.h>
1020Sstevel@tonic-gate #include <stdarg.h>
1030Sstevel@tonic-gate #include <alloca.h>
1040Sstevel@tonic-gate #include <unistd.h>
1050Sstevel@tonic-gate #include <sys/processor.h>
1060Sstevel@tonic-gate #include <syslog.h>
1070Sstevel@tonic-gate #include <errno.h>
1080Sstevel@tonic-gate #include <fcntl.h>
1090Sstevel@tonic-gate #include <picl.h>
1100Sstevel@tonic-gate #include <picltree.h>
1110Sstevel@tonic-gate #include <picldefs.h>
1120Sstevel@tonic-gate #include <pthread.h>
1130Sstevel@tonic-gate #include <signal.h>
1140Sstevel@tonic-gate #include <libdevinfo.h>
1150Sstevel@tonic-gate #include <sys/pm.h>
1160Sstevel@tonic-gate #include <sys/open.h>
1170Sstevel@tonic-gate #include <sys/time.h>
1180Sstevel@tonic-gate #include <sys/utsname.h>
1190Sstevel@tonic-gate #include <sys/systeminfo.h>
1200Sstevel@tonic-gate #include <sys/i2c/clients/max1617.h>
1210Sstevel@tonic-gate #include <sys/i2c/clients/i2c_client.h>
1220Sstevel@tonic-gate #include <sys/xcalwd.h>
1230Sstevel@tonic-gate #include "envd.h"
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate static pthread_rwlock_t	envd_rwlock = PTHREAD_RWLOCK_INITIALIZER;
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate /*
1280Sstevel@tonic-gate  * PICL plguin
1290Sstevel@tonic-gate  */
1300Sstevel@tonic-gate static void piclenvd_register(void);
1310Sstevel@tonic-gate static void piclenvd_init(void);
1320Sstevel@tonic-gate static void piclenvd_fini(void);
1330Sstevel@tonic-gate extern void env_picl_setup(void);
1340Sstevel@tonic-gate extern void env_picl_destroy(void);
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate #pragma	init(piclenvd_register)
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate static picld_plugin_reg_t my_reg_info = {
1390Sstevel@tonic-gate 	PICLD_PLUGIN_VERSION_1,
1400Sstevel@tonic-gate 	PICLD_PLUGIN_CRITICAL,
1410Sstevel@tonic-gate 	"SUNW_piclenvd",
1420Sstevel@tonic-gate 	piclenvd_init,
1430Sstevel@tonic-gate 	piclenvd_fini,
1440Sstevel@tonic-gate };
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate /*
1480Sstevel@tonic-gate  * Default threshold values for CPU junction/die and ambient sensors
1490Sstevel@tonic-gate  */
1500Sstevel@tonic-gate static sensor_thresh_t cpu_die_thresh_default = {
1510Sstevel@tonic-gate 	CPU_DIE_LOW_POWER_OFF, CPU_DIE_HIGH_POWER_OFF,
1520Sstevel@tonic-gate 	CPU_DIE_LOW_SHUTDOWN, CPU_DIE_HIGH_SHUTDOWN,
1530Sstevel@tonic-gate 	CPU_DIE_LOW_WARNING, CPU_DIE_HIGH_WARNING,
1540Sstevel@tonic-gate 	MAX1617_MIN_TEMP, MAX1617_MAX_TEMP,
1550Sstevel@tonic-gate 	POLICY_TARGET_TEMP, 2,
1560Sstevel@tonic-gate 	CPU_DIE_NORMAL_TARGET, CPU_DIE_OTHER_TARGET,
1570Sstevel@tonic-gate 	0, 0, 0, 0
1580Sstevel@tonic-gate };
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate static sensor_thresh_t cpu_amb_thresh_default = {
1610Sstevel@tonic-gate 	CPU_AMB_LOW_POWER_OFF, CPU_AMB_HIGH_POWER_OFF,
1620Sstevel@tonic-gate 	CPU_AMB_LOW_SHUTDOWN, CPU_AMB_HIGH_SHUTDOWN,
1630Sstevel@tonic-gate 	CPU_AMB_LOW_WARNING, CPU_AMB_HIGH_WARNING,
1640Sstevel@tonic-gate 	MAX1617_MIN_TEMP, MAX1617_MAX_TEMP,
1650Sstevel@tonic-gate 	POLICY_LINEAR, 2,
1660Sstevel@tonic-gate 	CPU_AMB_LOW_NOMINAL, CPU_AMB_HIGH_NOMINAL,
1670Sstevel@tonic-gate 	0, 0, 0, 0
1680Sstevel@tonic-gate };
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate /*
1720Sstevel@tonic-gate  * Dummy sensor threshold data structure for processing threshold tuneables
1730Sstevel@tonic-gate  */
1740Sstevel@tonic-gate static sensor_thresh_t	dummy_thresh;
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate /*
1770Sstevel@tonic-gate  * Temperature related constants for fan speed adjustment
1780Sstevel@tonic-gate  */
1790Sstevel@tonic-gate #define	AVG_TEMP_HYSTERESIS	0.25
1800Sstevel@tonic-gate #define	RISING_TEMP_MARGIN	6
1810Sstevel@tonic-gate #define	FALLING_TEMP_MARGIN	3
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate /*
1840Sstevel@tonic-gate  * tuneable variables
1850Sstevel@tonic-gate  */
1860Sstevel@tonic-gate #define	FAN_SLOW_ADJUSTMENT	20		/* in percentage */
1870Sstevel@tonic-gate #define	FAN_INCREMENT_LIMIT	6		/* absolute value */
1880Sstevel@tonic-gate #define	FAN_DECREMENT_LIMIT	1		/* absolute value */
1890Sstevel@tonic-gate #define	DEVFSADM_CMD 		"/usr/sbin/devfsadm -i max1617"
1900Sstevel@tonic-gate #define	FRU_DEVFSADM_CMD 	"/usr/sbin/devfsadm -i seeprom"
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate int		env_debug;
1930Sstevel@tonic-gate static int	sensor_poll_interval;
1940Sstevel@tonic-gate static int	warning_interval;
1950Sstevel@tonic-gate static int	warning_duration;
1960Sstevel@tonic-gate static int	shutdown_interval;
1970Sstevel@tonic-gate static int	fan_slow_adjustment;
1980Sstevel@tonic-gate static int	fan_incr_limit;
1990Sstevel@tonic-gate static int	fan_decr_limit;
2000Sstevel@tonic-gate static int	disable_piclenvd;
2010Sstevel@tonic-gate static int	disable_warning;
2020Sstevel@tonic-gate static int	disable_power_off;
2030Sstevel@tonic-gate static int	disable_shutdown;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate static char	shutdown_cmd[128];
2060Sstevel@tonic-gate static char	devfsadm_cmd[128];
2070Sstevel@tonic-gate static char	fru_devfsadm_cmd[128];
2080Sstevel@tonic-gate static sensor_thresh_t cpu0_die_thresh, cpu0_amb_thresh;
2090Sstevel@tonic-gate static sensor_thresh_t cpu1_die_thresh, cpu1_amb_thresh;
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate /*
2120Sstevel@tonic-gate  * Temperature sensors
2130Sstevel@tonic-gate  */
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate static env_sensor_t envd_sensors[] = {
2160Sstevel@tonic-gate 	{ SENSOR_CPU0_DIE, CPU0_DIE_SENSOR_DEVFS, &cpu0_die_thresh,
2170Sstevel@tonic-gate 	    CPU0_FRU_DEVFS, CPU_FRU_DIE_SENSOR,
2180Sstevel@tonic-gate 	    SFLAG_TARGET_TEMP | SFLAG_CPU_DIE_SENSOR, -1},
2190Sstevel@tonic-gate 	{ SENSOR_CPU0_AMB, CPU0_AMB_SENSOR_DEVFS, &cpu0_amb_thresh,
2200Sstevel@tonic-gate 	    CPU0_FRU_DEVFS, CPU_FRU_AMB_SENSOR, SFLAG_CPU_AMB_SENSOR, -1},
2210Sstevel@tonic-gate 	{ SENSOR_CPU1_DIE, CPU1_DIE_SENSOR_DEVFS, &cpu1_die_thresh,
2220Sstevel@tonic-gate 	    CPU1_FRU_DEVFS, CPU_FRU_DIE_SENSOR,
2230Sstevel@tonic-gate 	    SFLAG_TARGET_TEMP | SFLAG_CPU_DIE_SENSOR, -1},
2240Sstevel@tonic-gate 	{ SENSOR_CPU1_AMB, CPU1_AMB_SENSOR_DEVFS, &cpu1_amb_thresh,
2250Sstevel@tonic-gate 	    CPU1_FRU_DEVFS, CPU_FRU_AMB_SENSOR, SFLAG_CPU_AMB_SENSOR, -1},
2260Sstevel@tonic-gate 	{ NULL, NULL, NULL, NULL, 0, 0, -1}
2270Sstevel@tonic-gate };
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate /*
2310Sstevel@tonic-gate  * Fan devices
2320Sstevel@tonic-gate  */
2330Sstevel@tonic-gate static env_fan_t envd_system_fan = {
2340Sstevel@tonic-gate 	ENV_SYSTEM_FAN, ENV_SYSTEM_FAN_DEVFS,
2350Sstevel@tonic-gate 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
2360Sstevel@tonic-gate };
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate static env_fan_t envd_cpu_fan = {
2390Sstevel@tonic-gate 	ENV_CPU_FAN, ENV_CPU_FAN_DEVFS,
2400Sstevel@tonic-gate 	CPU_FAN_SPEED_MIN, CPU_FAN_SPEED_MAX, -1, -1,
2410Sstevel@tonic-gate };
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate static env_fan_t envd_psupply_fan = {
2440Sstevel@tonic-gate 	ENV_PSUPPLY_FAN, ENV_PSUPPLY_FAN_DEVFS,
2450Sstevel@tonic-gate 	PSUPPLY_FAN_SPEED_MIN, PSUPPLY_FAN_SPEED_MAX, -1, -1,
2460Sstevel@tonic-gate };
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate static env_fan_t *envd_fans[] = {
2490Sstevel@tonic-gate 	&envd_system_fan,
2500Sstevel@tonic-gate 	&envd_cpu_fan,
2510Sstevel@tonic-gate 	&envd_psupply_fan,
2520Sstevel@tonic-gate 	NULL
2530Sstevel@tonic-gate };
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate /*
2560Sstevel@tonic-gate  * Linked list of devices advertising lpm-ranges
2570Sstevel@tonic-gate  */
2580Sstevel@tonic-gate static lpm_dev_t	*lpm_devices = NULL;
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate /*
2610Sstevel@tonic-gate  * Excalibur lpm to system-fan speed
2620Sstevel@tonic-gate  * lpm values must be monotonically increasing (avoid divide-by-zero)
2630Sstevel@tonic-gate  */
2640Sstevel@tonic-gate static point_t	excal_lpm_system_fan_tbl[] = {
2650Sstevel@tonic-gate 	/* {lpm, fspeed} */
2660Sstevel@tonic-gate 	{18, 12},
2670Sstevel@tonic-gate 	{25, 20},
2680Sstevel@tonic-gate 	{33, 26},
2690Sstevel@tonic-gate 	{44, 32},
2700Sstevel@tonic-gate 	{51, 39},
2710Sstevel@tonic-gate 	{63, 52},
2720Sstevel@tonic-gate 	{64, 63}
2730Sstevel@tonic-gate };
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate static table_t	lpm_fspeed = {
2760Sstevel@tonic-gate 	sizeof (excal_lpm_system_fan_tbl)/ sizeof (point_t),
2770Sstevel@tonic-gate 	excal_lpm_system_fan_tbl
2780Sstevel@tonic-gate };
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate /*
2810Sstevel@tonic-gate  * Sensor to fan map
2820Sstevel@tonic-gate  */
2830Sstevel@tonic-gate typedef struct {
2840Sstevel@tonic-gate 	char	*sensor_name;
2850Sstevel@tonic-gate 	char	*fan_name;
2860Sstevel@tonic-gate } sensor_fan_map_t;
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate static sensor_fan_map_t sensor_fan_map[] = {
2890Sstevel@tonic-gate 	{SENSOR_CPU0_DIE, ENV_CPU_FAN},
2900Sstevel@tonic-gate 	{SENSOR_CPU1_DIE, ENV_CPU_FAN},
2910Sstevel@tonic-gate 	{SENSOR_CPU0_AMB, ENV_SYSTEM_FAN},
2920Sstevel@tonic-gate 	{SENSOR_CPU1_AMB, ENV_SYSTEM_FAN},
2930Sstevel@tonic-gate 	{NULL, NULL}
2940Sstevel@tonic-gate };
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate /*
2970Sstevel@tonic-gate  * Sensor to PM device map
2980Sstevel@tonic-gate  */
2990Sstevel@tonic-gate struct sensor_pmdev {
3000Sstevel@tonic-gate 	int		sensor_id;
3010Sstevel@tonic-gate 	char		*sensor_name;
3020Sstevel@tonic-gate 	char		*pmdev_name;
3030Sstevel@tonic-gate 	char		*speed_comp_name;
3040Sstevel@tonic-gate 	int		speed_comp;
3050Sstevel@tonic-gate 	int		full_power;
3060Sstevel@tonic-gate 	int		cur_power;
3070Sstevel@tonic-gate 	env_sensor_t	*sensorp;
3080Sstevel@tonic-gate 	sensor_pmdev_t	*next;
3090Sstevel@tonic-gate };
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate #define	SPEED_COMPONENT_NAME	"CPU Speed"
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate static sensor_pmdev_t sensor_pmdevs[] = {
3140Sstevel@tonic-gate 	{SENSOR_CPU0_ID, SENSOR_CPU0_DIE, NULL, SPEED_COMPONENT_NAME},
3150Sstevel@tonic-gate 	{SENSOR_CPU1_ID, SENSOR_CPU1_DIE, NULL, SPEED_COMPONENT_NAME},
3160Sstevel@tonic-gate 	{-1, NULL, NULL, NULL}
3170Sstevel@tonic-gate };
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate /*
3200Sstevel@tonic-gate  * Environmental thread variables
3210Sstevel@tonic-gate  */
3220Sstevel@tonic-gate static boolean_t	system_shutdown_started = B_FALSE;
3230Sstevel@tonic-gate static boolean_t	envthr_created = B_FALSE;	/* envthr created */
3240Sstevel@tonic-gate static pthread_t	envthr_tid;		/* envthr thread ID */
3250Sstevel@tonic-gate static pthread_attr_t	thr_attr;
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate /*
3280Sstevel@tonic-gate  * Power management thread (pmthr) variables
3290Sstevel@tonic-gate  */
3300Sstevel@tonic-gate static boolean_t	pmdev_names_init = B_FALSE;
3310Sstevel@tonic-gate static pthread_t	pmthr_tid;		/* pmthr thread ID */
3320Sstevel@tonic-gate static int		pmthr_exists = B_FALSE;	/* pmthr exists */
3330Sstevel@tonic-gate static int		pm_fd = -1;		/* PM device file descriptor */
3340Sstevel@tonic-gate static int		cur_lpstate;		/* cur low power state */
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate /*
3370Sstevel@tonic-gate  * Miscellaneous variables and declarations
3380Sstevel@tonic-gate  */
3390Sstevel@tonic-gate static int	fru_devfsadm_invoked = 0;
3400Sstevel@tonic-gate static int	devfsadm_invoked = 0;
3410Sstevel@tonic-gate static char	tokdel[] = " \t\n\r";
3420Sstevel@tonic-gate static uint_t	envd_sleep(uint_t);
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate /*
3450Sstevel@tonic-gate  * Tuneable data structure/array and processing functions
3460Sstevel@tonic-gate  */
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate typedef struct {
3490Sstevel@tonic-gate 	char		*name;		/* keyword */
3500Sstevel@tonic-gate 	int		(*func)(char *, char *, void *, int, char *, int);
3510Sstevel@tonic-gate 					/* tuneable processing function */
3520Sstevel@tonic-gate 	void		*arg1;		/* tuneable arg1 (memory address) */
3530Sstevel@tonic-gate 	int		arg2;		/* tuneable arg2 (size or flags) */
3540Sstevel@tonic-gate } env_tuneable_t;
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate static int process_int_tuneable(char *keyword, char *buf, void *addr,
3570Sstevel@tonic-gate     int size, char *fname, int line);
3580Sstevel@tonic-gate static int process_string_tuneable(char *keyword, char *buf, void *addr,
3590Sstevel@tonic-gate     int size, char *fname, int line);
3600Sstevel@tonic-gate static int process_threshold_tuneable(char *keyword, char *buf, void *addr,
3610Sstevel@tonic-gate     int flags, char *fname, int line);
3620Sstevel@tonic-gate static void process_env_conf_file(void);
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate static env_tuneable_t env_tuneables[] = {
3650Sstevel@tonic-gate 	{"low_power_off", process_threshold_tuneable,
3660Sstevel@tonic-gate 	    &dummy_thresh.low_power_off, 0},
3670Sstevel@tonic-gate 	{"low_shutdown", process_threshold_tuneable,
3680Sstevel@tonic-gate 	    &dummy_thresh.low_shutdown, 0},
3690Sstevel@tonic-gate 	{"low_warning", process_threshold_tuneable,
3700Sstevel@tonic-gate 	    &dummy_thresh.low_warning, 0},
3710Sstevel@tonic-gate 	{"high_power_off", process_threshold_tuneable,
3720Sstevel@tonic-gate 	    &dummy_thresh.high_power_off, 0},
3730Sstevel@tonic-gate 	{"high_shutdown", process_threshold_tuneable,
3740Sstevel@tonic-gate 	    &dummy_thresh.high_shutdown, 0},
3750Sstevel@tonic-gate 	{"high_warning", process_threshold_tuneable,
3760Sstevel@tonic-gate 	    &dummy_thresh.high_warning, 0},
3770Sstevel@tonic-gate 	{"force_cpu_fan", process_int_tuneable, &envd_cpu_fan.forced_speed,
3780Sstevel@tonic-gate 	    sizeof (envd_cpu_fan.forced_speed)},
3790Sstevel@tonic-gate 	{"force_system_fan", process_int_tuneable,
3800Sstevel@tonic-gate 	    &envd_system_fan.forced_speed,
3810Sstevel@tonic-gate 	    sizeof (envd_system_fan.forced_speed)},
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 	{"cpu_amb_low_power_off", process_threshold_tuneable,
3840Sstevel@tonic-gate 	    &dummy_thresh.low_power_off, SFLAG_CPU_AMB_SENSOR},
3850Sstevel@tonic-gate 	{"cpu_amb_low_shutdown", process_threshold_tuneable,
3860Sstevel@tonic-gate 	    &dummy_thresh.low_shutdown, SFLAG_CPU_AMB_SENSOR},
3870Sstevel@tonic-gate 	{"cpu_amb_low_warning", process_threshold_tuneable,
3880Sstevel@tonic-gate 	    &dummy_thresh.low_warning, SFLAG_CPU_AMB_SENSOR},
3890Sstevel@tonic-gate 	{"cpu_amb_low_nominal", process_threshold_tuneable,
3900Sstevel@tonic-gate 	    &dummy_thresh.policy_data[LOW_NOMINAL_LOC], SFLAG_CPU_AMB_SENSOR},
3910Sstevel@tonic-gate 	{"cpu_amb_high_power_off", process_threshold_tuneable,
3920Sstevel@tonic-gate 	    &dummy_thresh.high_power_off, SFLAG_CPU_AMB_SENSOR},
3930Sstevel@tonic-gate 	{"cpu_amb_high_shutdown", process_threshold_tuneable,
3940Sstevel@tonic-gate 	    &dummy_thresh.high_shutdown, SFLAG_CPU_AMB_SENSOR},
3950Sstevel@tonic-gate 	{"cpu_amb_high_warning", process_threshold_tuneable,
3960Sstevel@tonic-gate 	    &dummy_thresh.high_warning, SFLAG_CPU_AMB_SENSOR},
3970Sstevel@tonic-gate 	{"cpu_amb_high_nominal", process_threshold_tuneable,
3980Sstevel@tonic-gate 	    &dummy_thresh.policy_data[HIGH_NOMINAL_LOC], SFLAG_CPU_AMB_SENSOR},
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 	{"cpu_die_low_power_off", process_threshold_tuneable,
4010Sstevel@tonic-gate 	    &dummy_thresh.low_power_off, SFLAG_CPU_DIE_SENSOR},
4020Sstevel@tonic-gate 	{"cpu_die_low_shutdown", process_threshold_tuneable,
4030Sstevel@tonic-gate 	    &dummy_thresh.low_shutdown, SFLAG_CPU_DIE_SENSOR},
4040Sstevel@tonic-gate 	{"cpu_die_low_warning", process_threshold_tuneable,
4050Sstevel@tonic-gate 	    &dummy_thresh.low_warning, SFLAG_CPU_DIE_SENSOR},
4060Sstevel@tonic-gate 	{"cpu_die_normal_target", process_threshold_tuneable,
4070Sstevel@tonic-gate 	    &dummy_thresh.policy_data[0], SFLAG_CPU_DIE_SENSOR},
4080Sstevel@tonic-gate 	{"cpu_die_high_power_off", process_threshold_tuneable,
4090Sstevel@tonic-gate 	    &dummy_thresh.high_power_off, SFLAG_CPU_DIE_SENSOR},
4100Sstevel@tonic-gate 	{"cpu_die_high_shutdown", process_threshold_tuneable,
4110Sstevel@tonic-gate 	    &dummy_thresh.high_shutdown, SFLAG_CPU_DIE_SENSOR},
4120Sstevel@tonic-gate 	{"cpu_die_high_warning", process_threshold_tuneable,
4130Sstevel@tonic-gate 	    &dummy_thresh.high_warning, SFLAG_CPU_DIE_SENSOR},
4140Sstevel@tonic-gate 	{"cpu_die_other_target", process_threshold_tuneable,
4150Sstevel@tonic-gate 	    &dummy_thresh.policy_data[1], SFLAG_CPU_DIE_SENSOR},
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 	{"sensor_poll_interval", process_int_tuneable, &sensor_poll_interval,
4180Sstevel@tonic-gate 	    sizeof (sensor_poll_interval)},
4190Sstevel@tonic-gate 	{"warning_interval", process_int_tuneable, &warning_interval,
4200Sstevel@tonic-gate 	    sizeof (warning_interval)},
4210Sstevel@tonic-gate 	{"warning_duration", process_int_tuneable, &warning_duration,
4220Sstevel@tonic-gate 	    sizeof (warning_duration)},
4230Sstevel@tonic-gate 	{"disable_piclenvd", process_int_tuneable, &disable_piclenvd,
4240Sstevel@tonic-gate 	    sizeof (disable_piclenvd)},
4250Sstevel@tonic-gate 	{"disable_power_off", process_int_tuneable, &disable_power_off,
4260Sstevel@tonic-gate 	    sizeof (disable_power_off)},
4270Sstevel@tonic-gate 	{"disable_warning", process_int_tuneable, &disable_warning,
4280Sstevel@tonic-gate 	    sizeof (disable_warning)},
4290Sstevel@tonic-gate 	{"disable_shutdown", process_int_tuneable, &disable_shutdown,
4300Sstevel@tonic-gate 	    sizeof (disable_shutdown)},
4310Sstevel@tonic-gate 	{"shutdown_interval", process_int_tuneable, &shutdown_interval,
4320Sstevel@tonic-gate 	    sizeof (shutdown_interval)},
4330Sstevel@tonic-gate 	{"shutdown_cmd", process_string_tuneable, &shutdown_cmd[0],
4340Sstevel@tonic-gate 	    sizeof (shutdown_cmd)},
4350Sstevel@tonic-gate 	{"devfsadm_cmd", process_string_tuneable, &devfsadm_cmd[0],
4360Sstevel@tonic-gate 	    sizeof (devfsadm_cmd)},
4370Sstevel@tonic-gate 	{"fru_devfsadm_cmd", process_string_tuneable, &fru_devfsadm_cmd[0],
4380Sstevel@tonic-gate 	    sizeof (fru_devfsadm_cmd)},
4390Sstevel@tonic-gate 	{"fan_slow_adjustment", process_int_tuneable, &fan_slow_adjustment,
4400Sstevel@tonic-gate 	    sizeof (fan_slow_adjustment)},
4410Sstevel@tonic-gate 	{"fan_incr_limit", process_int_tuneable, &fan_incr_limit,
4420Sstevel@tonic-gate 	    sizeof (fan_incr_limit)},
4430Sstevel@tonic-gate 	{"fan_decr_limit", process_int_tuneable, &fan_decr_limit,
4440Sstevel@tonic-gate 	    sizeof (fan_decr_limit)},
4450Sstevel@tonic-gate 	{"env_debug", process_int_tuneable, &env_debug, sizeof (env_debug)},
4460Sstevel@tonic-gate 	{ NULL, NULL, NULL, 0}
4470Sstevel@tonic-gate };
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate static void
fini_table(table_t * tblp)4500Sstevel@tonic-gate fini_table(table_t *tblp)
4510Sstevel@tonic-gate {
4520Sstevel@tonic-gate 	if (tblp == NULL)
4530Sstevel@tonic-gate 		return;
4540Sstevel@tonic-gate 	free(tblp->xymap);
4550Sstevel@tonic-gate 	free(tblp);
4560Sstevel@tonic-gate }
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate static table_t *
init_table(int npoints)4590Sstevel@tonic-gate init_table(int npoints)
4600Sstevel@tonic-gate {
4610Sstevel@tonic-gate 	table_t		*tblp;
4620Sstevel@tonic-gate 	point_t		*xy;
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	if (npoints == 0)
4650Sstevel@tonic-gate 		return (NULL);
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 	if ((tblp = malloc(sizeof (*tblp))) == NULL)
4680Sstevel@tonic-gate 		return (NULL);
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	if ((xy = malloc(sizeof (*xy) * npoints)) == NULL) {
4710Sstevel@tonic-gate 		free(tblp);
4720Sstevel@tonic-gate 		return (NULL);
4730Sstevel@tonic-gate 	}
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	tblp->nentries = npoints;
4760Sstevel@tonic-gate 	tblp->xymap = xy;
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 	return (tblp);
4790Sstevel@tonic-gate }
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate /*
4820Sstevel@tonic-gate  * Temp-LPM Table format:
4830Sstevel@tonic-gate  * temp, lpm, temp, lpm, ...
4840Sstevel@tonic-gate  */
4850Sstevel@tonic-gate static table_t *
parse_lpm_ranges(uint32_t * bufp,size_t nbytes)4860Sstevel@tonic-gate parse_lpm_ranges(uint32_t *bufp, size_t nbytes)
4870Sstevel@tonic-gate {
4880Sstevel@tonic-gate 	int	nentries;
4890Sstevel@tonic-gate 	table_t	*tblp = NULL;
4900Sstevel@tonic-gate 	int	i;
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 	if (bufp == NULL)
4930Sstevel@tonic-gate 		return (NULL);
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 	/*
4960Sstevel@tonic-gate 	 * Table should have at least 2 points
4970Sstevel@tonic-gate 	 * and all points should have x and y values
4980Sstevel@tonic-gate 	 */
4990Sstevel@tonic-gate 	if ((nbytes < (2 * sizeof (point_t))) ||
5000Sstevel@tonic-gate 	    (nbytes & (sizeof (point_t) - 1))) {
5010Sstevel@tonic-gate 		if (env_debug)
5020Sstevel@tonic-gate 			envd_log(LOG_ERR, ENV_INVALID_PROPERTY_FORMAT,
5030Sstevel@tonic-gate 			    LPM_RANGES_PROPERTY);
5040Sstevel@tonic-gate 		return (NULL);
5050Sstevel@tonic-gate 	}
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 	/* number of entries in the temp-lpm table */
5080Sstevel@tonic-gate 	nentries = nbytes/sizeof (point_t);
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 	tblp = init_table(nentries);
5110Sstevel@tonic-gate 	if (tblp == NULL)
5120Sstevel@tonic-gate 		return (tblp);
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 	/* copy the tuples */
5150Sstevel@tonic-gate 	tblp->xymap[0].x = (int)*bufp++;
5160Sstevel@tonic-gate 	tblp->xymap[0].y = (int)*bufp++;
5170Sstevel@tonic-gate 	for (i = 1; i < nentries; ++i) {
5180Sstevel@tonic-gate 		tblp->xymap[i].x = (int)*bufp++;
5190Sstevel@tonic-gate 		tblp->xymap[i].y = (int)*bufp++;
5200Sstevel@tonic-gate 		if (tblp->xymap[i].x <= tblp->xymap[i - 1].x) {
5210Sstevel@tonic-gate 			fini_table(tblp);
5220Sstevel@tonic-gate 			if (env_debug)
5230Sstevel@tonic-gate 				envd_log(LOG_ERR, ENV_INVALID_PROPERTY_FORMAT,
5240Sstevel@tonic-gate 				    LPM_RANGES_PROPERTY);
5250Sstevel@tonic-gate 			return (NULL);
5260Sstevel@tonic-gate 		}
5270Sstevel@tonic-gate 	}
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 	return (tblp);
5300Sstevel@tonic-gate }
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate /*
5330Sstevel@tonic-gate  * function: calculates y for a given x based on a table of points
5340Sstevel@tonic-gate  * for monotonically increasing x values.
5350Sstevel@tonic-gate  * 'tbl' specifies the table to use, 'val' specifies the 'x', returns 'y'
5360Sstevel@tonic-gate  */
5370Sstevel@tonic-gate static int
y_of_x(table_t * tbl,int xval)5380Sstevel@tonic-gate y_of_x(table_t *tbl, int xval)
5390Sstevel@tonic-gate {
5400Sstevel@tonic-gate 	int		i;
5410Sstevel@tonic-gate 	int		entries;
5420Sstevel@tonic-gate 	point_t		*xymap;
5430Sstevel@tonic-gate 	float		newval;
5440Sstevel@tonic-gate 	float		dy, dx, slope;
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 	entries = tbl->nentries;
5470Sstevel@tonic-gate 	xymap = tbl->xymap;
5480Sstevel@tonic-gate 	if (xval <= xymap[0].x)
5490Sstevel@tonic-gate 		return (xymap[0].y);
5500Sstevel@tonic-gate 	else if (xval >= xymap[entries - 1].x)
5510Sstevel@tonic-gate 		return (xymap[entries - 1].y);
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 	for (i = 1; i < entries - 1; i++) {
5540Sstevel@tonic-gate 		if (xval == xymap[i].x)
5550Sstevel@tonic-gate 			return (xymap[i].y);
5560Sstevel@tonic-gate 		if (xval < xymap[i].x)
5570Sstevel@tonic-gate 			break;
5580Sstevel@tonic-gate 	}
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	/*
5610Sstevel@tonic-gate 	 * Use linear interpolation
5620Sstevel@tonic-gate 	 */
5630Sstevel@tonic-gate 	dy = (float)(xymap[i].y - xymap[i-1].y);
5640Sstevel@tonic-gate 	dx = (float)(xymap[i].x - xymap[i-1].x);
5650Sstevel@tonic-gate 	slope = dy/dx;
5660Sstevel@tonic-gate 	newval = xymap[i - 1].y + slope * (xval - xymap[i - 1].x);
5670Sstevel@tonic-gate 	return ((int)(newval + (newval >= 0 ? 0.5 : -0.5)));
5680Sstevel@tonic-gate }
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate static int
get_lpm_speed(lpm_dev_t * lpmdevs,int temp)5710Sstevel@tonic-gate get_lpm_speed(lpm_dev_t *lpmdevs, int temp)
5720Sstevel@tonic-gate {
5730Sstevel@tonic-gate 	lpm_dev_t	*devp;
5740Sstevel@tonic-gate 	int		lpm;
5750Sstevel@tonic-gate 	int		speed;
5760Sstevel@tonic-gate 	int		maxspeed;
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 	if (lpmdevs == NULL)
5790Sstevel@tonic-gate 		return (0);
5800Sstevel@tonic-gate 	maxspeed = 0;
5810Sstevel@tonic-gate 	for (devp = lpmdevs; devp != NULL; devp = devp->next) {
5820Sstevel@tonic-gate 		if (devp->temp_lpm_tbl == NULL)
5830Sstevel@tonic-gate 			continue;
5840Sstevel@tonic-gate 		lpm = y_of_x(devp->temp_lpm_tbl, temp);
5850Sstevel@tonic-gate 		if (env_debug)
5860Sstevel@tonic-gate 			envd_log(LOG_INFO, "ambient %d lpm %d\n", temp, lpm);
5870Sstevel@tonic-gate 		speed = y_of_x(&lpm_fspeed, lpm);
5880Sstevel@tonic-gate 		maxspeed = maxspeed > speed ? maxspeed : speed;
5890Sstevel@tonic-gate 		if (env_debug)
5900Sstevel@tonic-gate 			envd_log(LOG_INFO, "lpm %d fanspeed %d\n", lpm, speed);
5910Sstevel@tonic-gate 	}
5920Sstevel@tonic-gate 	return (maxspeed);
5930Sstevel@tonic-gate }
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate /*
5960Sstevel@tonic-gate  * Callback function used by ptree_walk_tree_by_class
5970Sstevel@tonic-gate  */
5980Sstevel@tonic-gate static int
cb_lpm(picl_nodehdl_t nodeh,void * args)5990Sstevel@tonic-gate cb_lpm(picl_nodehdl_t nodeh, void *args)
6000Sstevel@tonic-gate {
6010Sstevel@tonic-gate 	lpm_dev_t	**retp = (lpm_dev_t **)args;
6020Sstevel@tonic-gate 	int		err;
6030Sstevel@tonic-gate 	ptree_propinfo_t	pinfo;
6040Sstevel@tonic-gate 	picl_prophdl_t		proph;
6050Sstevel@tonic-gate 	size_t			psize;
6060Sstevel@tonic-gate 	void			*bufp;
6070Sstevel@tonic-gate 	table_t			*temp_lpm_tbl;
6080Sstevel@tonic-gate 	lpm_dev_t		*newdev;
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 	err = ptree_get_prop_by_name(nodeh, LPM_RANGES_PROPERTY, &proph);
6110Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
6120Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	err = ptree_get_propinfo(proph, &pinfo);
6150Sstevel@tonic-gate 	if ((err != PICL_SUCCESS) ||
6160Sstevel@tonic-gate 	    (pinfo.piclinfo.type != PICL_PTYPE_BYTEARRAY))
6170Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
6180Sstevel@tonic-gate 	psize = pinfo.piclinfo.size;
6190Sstevel@tonic-gate 	bufp = alloca(psize);
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 	err = ptree_get_propval(proph, bufp, psize);
6220Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
6230Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 	temp_lpm_tbl = parse_lpm_ranges(bufp, psize);
6260Sstevel@tonic-gate 	if (temp_lpm_tbl == NULL) {
6270Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
6280Sstevel@tonic-gate 	}
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 	newdev = malloc(sizeof (*newdev));
6310Sstevel@tonic-gate 	if (newdev == NULL) {
6320Sstevel@tonic-gate 		fini_table(temp_lpm_tbl);
6330Sstevel@tonic-gate 		return (PICL_WALK_TERMINATE);
6340Sstevel@tonic-gate 	}
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 	memset(newdev, 0, sizeof (*newdev));
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	newdev->nodeh = nodeh;
6390Sstevel@tonic-gate 	newdev->temp_lpm_tbl = temp_lpm_tbl;
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	/* add newdev to the list */
6420Sstevel@tonic-gate 	newdev->next = *retp;
6430Sstevel@tonic-gate 	*retp = newdev;
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate 	return (PICL_WALK_CONTINUE);
6460Sstevel@tonic-gate }
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate /*
6490Sstevel@tonic-gate  * Find all devices advertising "lpm-ranges" property, parse and store
6500Sstevel@tonic-gate  * the lpm tables for each device
6510Sstevel@tonic-gate  */
6520Sstevel@tonic-gate static int
setup_lpm_devices(lpm_dev_t ** devpp)6530Sstevel@tonic-gate setup_lpm_devices(lpm_dev_t **devpp)
6540Sstevel@tonic-gate {
6550Sstevel@tonic-gate 	picl_nodehdl_t	plath;
6560Sstevel@tonic-gate 	int		err;
6570Sstevel@tonic-gate 	lpm_dev_t	*lpmp;
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	err = ptree_get_node_by_path("/platform", &plath);
6600Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
6610Sstevel@tonic-gate 		return (err);
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	lpmp = NULL;
6640Sstevel@tonic-gate 	err = ptree_walk_tree_by_class(plath, NULL, (void *)&lpmp, cb_lpm);
6650Sstevel@tonic-gate 	if (err == PICL_SUCCESS)
6660Sstevel@tonic-gate 		*devpp = lpmp;
6670Sstevel@tonic-gate 	return (err);
6680Sstevel@tonic-gate }
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate /*
6710Sstevel@tonic-gate  * Remove all lpm_devices and their tables.
6720Sstevel@tonic-gate  */
6730Sstevel@tonic-gate static void
delete_lpm_devices(void)6740Sstevel@tonic-gate delete_lpm_devices(void)
6750Sstevel@tonic-gate {
6760Sstevel@tonic-gate 	lpm_dev_t	*devp, *next;
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&envd_rwlock);
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 	if (lpm_devices == NULL) {
6810Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&envd_rwlock);
6820Sstevel@tonic-gate 		return;
6830Sstevel@tonic-gate 	}
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 	devp = lpm_devices;
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 	while (devp != NULL) {
6880Sstevel@tonic-gate 		fini_table(devp->temp_lpm_tbl);
6890Sstevel@tonic-gate 		next = devp->next;
6900Sstevel@tonic-gate 		free(devp);
6910Sstevel@tonic-gate 		devp = next;
6920Sstevel@tonic-gate 	}
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 	lpm_devices = NULL;
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&envd_rwlock);
6970Sstevel@tonic-gate }
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate /*
7000Sstevel@tonic-gate  * Translate observed (measured) temperature into expected (correct)
7010Sstevel@tonic-gate  * temperature
7020Sstevel@tonic-gate  */
7030Sstevel@tonic-gate static int
xlate_obs2exp(env_sensor_t * sensorp,tempr_t temp)7040Sstevel@tonic-gate xlate_obs2exp(env_sensor_t *sensorp, tempr_t temp)
7050Sstevel@tonic-gate {
7060Sstevel@tonic-gate 	int		i, entries, new_temp, denominator;
7070Sstevel@tonic-gate 	tempr_map_t	*map;
7080Sstevel@tonic-gate 	float		ftemp;
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 	entries = sensorp->obs2exp_cnt;
7110Sstevel@tonic-gate 	map = sensorp->obs2exp_map;
7120Sstevel@tonic-gate 	if (entries < 2 || map == NULL)  {
7130Sstevel@tonic-gate 		/* no map or can't map it */
7140Sstevel@tonic-gate 		new_temp = temp;
7150Sstevel@tonic-gate 	} else {
7160Sstevel@tonic-gate 		/*
7170Sstevel@tonic-gate 		 * Any point beyond the range specified by the map is
7180Sstevel@tonic-gate 		 * extrapolated using either the first two or the last
7190Sstevel@tonic-gate 		 * two entries in the map.
7200Sstevel@tonic-gate 		 */
7210Sstevel@tonic-gate 		for (i = 1; i < entries-1; i++)
7220Sstevel@tonic-gate 			if (temp < map[i].observed)
7230Sstevel@tonic-gate 				break;
7240Sstevel@tonic-gate 		/*
7250Sstevel@tonic-gate 		 * Interpolate/extrapolate the temperature using linear
7260Sstevel@tonic-gate 		 * equation with map[i-1] and map[i] being the two ends
7270Sstevel@tonic-gate 		 * of the line segment.
7280Sstevel@tonic-gate 		 */
7290Sstevel@tonic-gate 		denominator = map[i].observed - map[i-1].observed;
7300Sstevel@tonic-gate 		if (denominator == 0) {
7310Sstevel@tonic-gate 			/*
7320Sstevel@tonic-gate 			 * Infinite slope. Since the temperature reading
7330Sstevel@tonic-gate 			 * resolution is 1C, force denominator to 1 to
7340Sstevel@tonic-gate 			 * avoid divide by zero.
7350Sstevel@tonic-gate 			 */
7360Sstevel@tonic-gate 			denominator = 1;
7370Sstevel@tonic-gate 		}
7380Sstevel@tonic-gate 		ftemp = map[i-1].expected +  (temp - map[i-1].observed) *
7390Sstevel@tonic-gate 		    (float)(map[i].expected - map[i-1].expected)/denominator;
7400Sstevel@tonic-gate 		new_temp = (int)(ftemp + (ftemp >= 0 ? 0.5 : -0.5));
7410Sstevel@tonic-gate 	}
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 	return (new_temp);
7440Sstevel@tonic-gate }
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate /*
7480Sstevel@tonic-gate  * Translate expected (correct) temperature into observed (measured)
7490Sstevel@tonic-gate  * temperature
7500Sstevel@tonic-gate  */
7510Sstevel@tonic-gate static int
xlate_exp2obs(env_sensor_t * sensorp,tempr_t temp)7520Sstevel@tonic-gate xlate_exp2obs(env_sensor_t *sensorp, tempr_t temp)
7530Sstevel@tonic-gate {
7540Sstevel@tonic-gate 	int		i, entries, new_temp, denominator;
7550Sstevel@tonic-gate 	tempr_map_t	*map;
7560Sstevel@tonic-gate 	float		ftemp;
7570Sstevel@tonic-gate 	sensor_thresh_t	*threshp = sensorp->temp_thresh;
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 	entries = sensorp->obs2exp_cnt;
7600Sstevel@tonic-gate 	map = sensorp->obs2exp_map;
7610Sstevel@tonic-gate 	if (entries < 2 || map == NULL)
7620Sstevel@tonic-gate 		/* no map or can't map it */
7630Sstevel@tonic-gate 		new_temp = temp;
7640Sstevel@tonic-gate 	else {
7650Sstevel@tonic-gate 		/*
7660Sstevel@tonic-gate 		 * Any point beyond the range specified by the map is
7670Sstevel@tonic-gate 		 * extrapolated using either the first two or the last
7680Sstevel@tonic-gate 		 * two entries in the map.
7690Sstevel@tonic-gate 		 */
7700Sstevel@tonic-gate 		for (i = 1; i < entries-1; i++)
7710Sstevel@tonic-gate 			if (temp < map[i].expected)
7720Sstevel@tonic-gate 				break;
7730Sstevel@tonic-gate 
7740Sstevel@tonic-gate 		/*
7750Sstevel@tonic-gate 		 * Interpolate/extrapolate the temperature using linear
7760Sstevel@tonic-gate 		 * equation with map[i-1] and map[i] being the two ends
7770Sstevel@tonic-gate 		 * of the line segment.
7780Sstevel@tonic-gate 		 */
7790Sstevel@tonic-gate 		denominator = map[i].expected - map[i-1].expected;
7800Sstevel@tonic-gate 		if (denominator == 0) {
7810Sstevel@tonic-gate 			/*
7820Sstevel@tonic-gate 			 * Infinite slope. Since the temperature reading
7830Sstevel@tonic-gate 			 * resolution is 1C, force denominator to 1 to
7840Sstevel@tonic-gate 			 * avoid divide by zero.
7850Sstevel@tonic-gate 			 */
7860Sstevel@tonic-gate 			denominator = 1;
7870Sstevel@tonic-gate 		}
7880Sstevel@tonic-gate 		ftemp = map[i-1].observed + (temp - map[i-1].expected) *
7890Sstevel@tonic-gate 		    (float)(map[i].observed - map[i-1].observed)/denominator;
7900Sstevel@tonic-gate 		new_temp = (int)(ftemp + (ftemp >= 0 ? 0.5 : -0.5));
7910Sstevel@tonic-gate 	}
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 	if (threshp) {
7940Sstevel@tonic-gate 		if (new_temp > threshp->max_limit)
7950Sstevel@tonic-gate 			new_temp = threshp->max_limit;
7960Sstevel@tonic-gate 		else if (new_temp < threshp->min_limit)
7970Sstevel@tonic-gate 			new_temp = threshp->min_limit;
7980Sstevel@tonic-gate 	}
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	return (new_temp);
8010Sstevel@tonic-gate }
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate /*
8050Sstevel@tonic-gate  * Check if the specified FRU is present.
8060Sstevel@tonic-gate  * Returns 1 if present; 0 otherwise.
8070Sstevel@tonic-gate  */
8080Sstevel@tonic-gate static int
fru_present(char * path)8090Sstevel@tonic-gate fru_present(char *path)
8100Sstevel@tonic-gate {
8110Sstevel@tonic-gate 	char		*p, physpath[PATH_MAX];
8120Sstevel@tonic-gate 	di_node_t	root_node;
8130Sstevel@tonic-gate 	int		fru_present = 0;
8140Sstevel@tonic-gate 
8150Sstevel@tonic-gate 	/*
8160Sstevel@tonic-gate 	 * Construct FRU device path by stripping minor
8170Sstevel@tonic-gate 	 * node name from the path and use di_init() to
8180Sstevel@tonic-gate 	 * see if the node exists.
8190Sstevel@tonic-gate 	 */
8200Sstevel@tonic-gate 	(void) strlcpy(physpath, path, sizeof (physpath));
8210Sstevel@tonic-gate 	p = strrchr(physpath, ':');
8220Sstevel@tonic-gate 	if (p != NULL)
8230Sstevel@tonic-gate 		*p = '\0';
8240Sstevel@tonic-gate 	if ((root_node = di_init(physpath, DINFOMINOR)) != DI_NODE_NIL) {
8250Sstevel@tonic-gate 		di_fini(root_node);
8260Sstevel@tonic-gate 		fru_present = 1;
8270Sstevel@tonic-gate 	}
8280Sstevel@tonic-gate 	return (fru_present);
8290Sstevel@tonic-gate }
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate /*
8330Sstevel@tonic-gate  * Get environmental segment from the specified FRU SEEPROM
8340Sstevel@tonic-gate  */
8350Sstevel@tonic-gate static int
get_envseg(int fd,void ** envsegp,int * envseglenp)8360Sstevel@tonic-gate get_envseg(int fd, void **envsegp, int *envseglenp)
8370Sstevel@tonic-gate {
8380Sstevel@tonic-gate 	int			i, segcnt, envseglen;
8390Sstevel@tonic-gate 	section_layout_t	section;
8400Sstevel@tonic-gate 	segment_layout_t	segment;
8410Sstevel@tonic-gate 	uint8_t			*envseg;
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 	if (lseek(fd, (long)SECTION_HDR_OFFSET, 0) == -1L ||
8440Sstevel@tonic-gate 	    read(fd, &section, sizeof (section)) != sizeof (section)) {
8450Sstevel@tonic-gate 		return (EINVAL);
8460Sstevel@tonic-gate 	}
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	/*
8490Sstevel@tonic-gate 	 * Verify we have the correct section and contents are valid
8500Sstevel@tonic-gate 	 * For now, we don't verify the CRC.
8510Sstevel@tonic-gate 	 */
8520Sstevel@tonic-gate 	if (section.header_tag != SECTION_HDR_TAG ||
8530Sstevel@tonic-gate 	    GET_UNALIGN16(&section.header_version[0]) != SECTION_HDR_VER) {
8540Sstevel@tonic-gate 		if (env_debug)
8550Sstevel@tonic-gate 			envd_log(LOG_INFO,
8560Sstevel@tonic-gate 			    "Invalid section header tag:%x  version:%x\n",
8570Sstevel@tonic-gate 			    section.header_tag,
8580Sstevel@tonic-gate 			    GET_UNALIGN16(&section.header_version));
8590Sstevel@tonic-gate 		return (EINVAL);
8600Sstevel@tonic-gate 	}
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 	/*
8630Sstevel@tonic-gate 	 * Locate our environmental segment
8640Sstevel@tonic-gate 	 */
8650Sstevel@tonic-gate 	segcnt = section.segment_count;
8660Sstevel@tonic-gate 	for (i = 0; i < segcnt; i++) {
8670Sstevel@tonic-gate 		if (read(fd, &segment, sizeof (segment)) != sizeof (segment)) {
8680Sstevel@tonic-gate 			return (errno);
8690Sstevel@tonic-gate 		}
8700Sstevel@tonic-gate 		if (env_debug > 1)
8710Sstevel@tonic-gate 			envd_log(LOG_INFO,
8720Sstevel@tonic-gate 			    "Seg name: %x  desc:%x off:%x  len:%x\n",
8730Sstevel@tonic-gate 			    GET_UNALIGN16(&segment.name),
8740Sstevel@tonic-gate 			    GET_UNALIGN32(&segment.descriptor[0]),
8750Sstevel@tonic-gate 			    GET_UNALIGN16(&segment.offset),
8760Sstevel@tonic-gate 			    GET_UNALIGN16(&segment.length));
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 		if (GET_UNALIGN16(&segment.name) == ENVSEG_NAME)
8790Sstevel@tonic-gate 			break;
8800Sstevel@tonic-gate 	}
8810Sstevel@tonic-gate 
8820Sstevel@tonic-gate 	if (i >= segcnt) {
8830Sstevel@tonic-gate 		return (ENOENT);
8840Sstevel@tonic-gate 	}
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 	/*
8870Sstevel@tonic-gate 	 * Allocate memory to hold the environmental segment data.
8880Sstevel@tonic-gate 	 */
8890Sstevel@tonic-gate 	envseglen = GET_UNALIGN16(&segment.length);
8900Sstevel@tonic-gate 	if ((envseg = malloc(envseglen)) == NULL) {
8910Sstevel@tonic-gate 		return (ENOMEM);
8920Sstevel@tonic-gate 	}
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 	if (lseek(fd, (long)GET_UNALIGN16(&segment.offset), 0) == -1L ||
8950Sstevel@tonic-gate 	    read(fd, envseg, envseglen) != envseglen) {
8960Sstevel@tonic-gate 		(void) free(envseg);
8970Sstevel@tonic-gate 		return (EIO);
8980Sstevel@tonic-gate 	}
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate 	*envsegp = envseg;
9010Sstevel@tonic-gate 	*envseglenp = envseglen;
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 	if (env_debug > 1) {
9040Sstevel@tonic-gate 		char	msgbuf[256];
9050Sstevel@tonic-gate 		for (i = 0; i < envseglen; i++) {
9060Sstevel@tonic-gate 			(void) sprintf(&msgbuf[3*(i&0xf)], "%2x ", envseg[i]);
9070Sstevel@tonic-gate 			if ((i & 0xf) == 0xf || i == (envseglen-1))
9080Sstevel@tonic-gate 				envd_log(LOG_INFO, "envseg[%2x]: %s\n",
9090Sstevel@tonic-gate 				    (i & ~0xf), msgbuf);
9100Sstevel@tonic-gate 		}
9110Sstevel@tonic-gate 	}
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 	return (0);
9140Sstevel@tonic-gate }
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate /*
9180Sstevel@tonic-gate  * Get all environmental segments
9190Sstevel@tonic-gate  */
9200Sstevel@tonic-gate static fruenvseg_t *
get_fru_envsegs(void)9210Sstevel@tonic-gate get_fru_envsegs(void)
9220Sstevel@tonic-gate {
9230Sstevel@tonic-gate 	env_sensor_t		*sensorp;
9240Sstevel@tonic-gate 	fruenvseg_t		*frup, *fruenvsegs;
9250Sstevel@tonic-gate 	envseg_layout_t		*envsegp;
9260Sstevel@tonic-gate 	void			*envsegbufp;
9270Sstevel@tonic-gate 	int			fd, envseglen, hdrlen;
9280Sstevel@tonic-gate 	char			path[PATH_MAX];
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate 	fruenvsegs = NULL;
9310Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
9320Sstevel@tonic-gate 		if (sensorp->fru == NULL)
9330Sstevel@tonic-gate 			continue;
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 		for (frup = fruenvsegs; frup != NULL; frup = frup->next)
9360Sstevel@tonic-gate 			if (strcmp(frup->fru, sensorp->fru) == 0)
9370Sstevel@tonic-gate 				break;
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate 		if (frup != NULL)
9400Sstevel@tonic-gate 			continue;
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate 		frup = (fruenvseg_t *)malloc(sizeof (fruenvseg_t));
9430Sstevel@tonic-gate 		if (frup == NULL)
9440Sstevel@tonic-gate 			continue;
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate 		/* add this FRU to our list */
9470Sstevel@tonic-gate 		frup->fru = sensorp->fru;
9480Sstevel@tonic-gate 		frup->envsegbufp = NULL;
9490Sstevel@tonic-gate 		frup->envseglen = 0;
9500Sstevel@tonic-gate 		frup->next = fruenvsegs;
9510Sstevel@tonic-gate 		fruenvsegs = frup;
9520Sstevel@tonic-gate 
9530Sstevel@tonic-gate 		/*
9540Sstevel@tonic-gate 		 * Now get the environmental segment from this FRU
9550Sstevel@tonic-gate 		 */
9560Sstevel@tonic-gate 		(void) strcpy(path, "/devices");
9570Sstevel@tonic-gate 		(void) strlcat(path, sensorp->fru, sizeof (path));
9580Sstevel@tonic-gate 	retry:
9590Sstevel@tonic-gate 		errno = 0;
9600Sstevel@tonic-gate 		fd = open(path, O_RDONLY);
9610Sstevel@tonic-gate 		if (env_debug > 1)
9620Sstevel@tonic-gate 			envd_log(LOG_INFO,
9630Sstevel@tonic-gate 			    "fru SEEPROM: %s fd: %d  errno:%d\n",
9640Sstevel@tonic-gate 			    path, fd, errno);
9650Sstevel@tonic-gate 		if (fd == -1 && errno == ENOENT && fru_present(frup->fru)) {
9660Sstevel@tonic-gate 			if (fru_devfsadm_invoked ||
9670Sstevel@tonic-gate 			    fru_devfsadm_cmd[0] == '\0') {
9680Sstevel@tonic-gate 				envd_log(LOG_CRIT, ENV_FRU_OPEN_FAIL,
9690Sstevel@tonic-gate 				    sensorp->fru, errno, strerror(errno));
9700Sstevel@tonic-gate 				continue;
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 			}
9730Sstevel@tonic-gate 			/*
9740Sstevel@tonic-gate 			 * FRU is present but no path exists as
9750Sstevel@tonic-gate 			 * someone rebooted the system without
9760Sstevel@tonic-gate 			 * "-r" option. Let's invoke "devfsadm"
9770Sstevel@tonic-gate 			 * once to create seeprom nodes and try
9780Sstevel@tonic-gate 			 * again so that we can monitor all
9790Sstevel@tonic-gate 			 * accessible sensors properly and prevent
9800Sstevel@tonic-gate 			 * any CPU overheating.
9810Sstevel@tonic-gate 			 */
9820Sstevel@tonic-gate 			if (env_debug)
9830Sstevel@tonic-gate 				envd_log(LOG_INFO,
9840Sstevel@tonic-gate 				    "Invoking '%s' to create FRU nodes\n",
9850Sstevel@tonic-gate 				    fru_devfsadm_cmd);
9860Sstevel@tonic-gate 			fru_devfsadm_invoked = 1;
9870Sstevel@tonic-gate 			(void) system(fru_devfsadm_cmd);
9880Sstevel@tonic-gate 			goto retry;
9890Sstevel@tonic-gate 		}
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 		/*
9920Sstevel@tonic-gate 		 * Read environmental segment from this FRU SEEPROM
9930Sstevel@tonic-gate 		 */
9940Sstevel@tonic-gate 		if (get_envseg(fd, &envsegbufp, &envseglen) == 0) {
9950Sstevel@tonic-gate 			/*
9960Sstevel@tonic-gate 			 * Validate envseg version number and header length
9970Sstevel@tonic-gate 			 */
9980Sstevel@tonic-gate 			envsegp = (envseg_layout_t *)envsegbufp;
9990Sstevel@tonic-gate 			hdrlen = sizeof (envseg_layout_t) -
10000Sstevel@tonic-gate 			    sizeof (envseg_sensor_t) +
10010Sstevel@tonic-gate 			    (envsegp->sensor_count) * sizeof (envseg_sensor_t);
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 			if (envsegp->version != ENVSEG_VERSION ||
10040Sstevel@tonic-gate 			    envseglen < hdrlen) {
10050Sstevel@tonic-gate 				/*
10060Sstevel@tonic-gate 				 * version mismatch or header not big enough
10070Sstevel@tonic-gate 				 */
10080Sstevel@tonic-gate 				envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG,
10090Sstevel@tonic-gate 				    sensorp->fru, errno, strerror(errno));
10100Sstevel@tonic-gate 				if (envsegbufp != NULL)
10110Sstevel@tonic-gate 					(void) free(envsegbufp);
10120Sstevel@tonic-gate 			} else {
10130Sstevel@tonic-gate 				frup->envseglen = envseglen;
10140Sstevel@tonic-gate 				frup->envsegbufp = envsegbufp;
10150Sstevel@tonic-gate 			}
10160Sstevel@tonic-gate 		}
10170Sstevel@tonic-gate 		(void) close(fd);
10180Sstevel@tonic-gate 	}
10190Sstevel@tonic-gate 	return (fruenvsegs);
10200Sstevel@tonic-gate }
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate /*
10230Sstevel@tonic-gate  * Process environmental segment for all FRUs.
10240Sstevel@tonic-gate  */
10250Sstevel@tonic-gate static void
process_fru_envseg()10260Sstevel@tonic-gate process_fru_envseg()
10270Sstevel@tonic-gate {
10280Sstevel@tonic-gate 	env_sensor_t		*sensorp;
10290Sstevel@tonic-gate 	sensor_thresh_t		*threshp;
10300Sstevel@tonic-gate 	envseg_layout_t		*envsegp;
10310Sstevel@tonic-gate 	envseg_sensor_data_t	*datap;
10320Sstevel@tonic-gate 	fruenvseg_t		*frup, *fruenvsegs;
10330Sstevel@tonic-gate 	int			i, envseglen, sensorcnt;
10340Sstevel@tonic-gate 	uint_t			offset, length, mapentries;
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	/*
10370Sstevel@tonic-gate 	 * Lookup/read environmental segments from FRU SEEPROMs and
10380Sstevel@tonic-gate 	 * process it. Note that we read each SEEPROM once as it's
10390Sstevel@tonic-gate 	 * a slow device.
10400Sstevel@tonic-gate 	 */
10410Sstevel@tonic-gate 	fruenvsegs = get_fru_envsegs();
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
10440Sstevel@tonic-gate 		if (sensorp->fru == NULL)
10450Sstevel@tonic-gate 			continue;
10460Sstevel@tonic-gate 
10470Sstevel@tonic-gate 		/*
10480Sstevel@tonic-gate 		 * Locate our FRU environmental segment
10490Sstevel@tonic-gate 		 */
10500Sstevel@tonic-gate 		for (frup = fruenvsegs; frup != NULL; frup = frup->next)
10510Sstevel@tonic-gate 			if (strcmp(frup->fru, sensorp->fru) == 0)
10520Sstevel@tonic-gate 				break;
10530Sstevel@tonic-gate 		if (frup == NULL || frup->envsegbufp == NULL)
10540Sstevel@tonic-gate 			continue;
10550Sstevel@tonic-gate 
10560Sstevel@tonic-gate 		envsegp = (envseg_layout_t *)frup->envsegbufp;
10570Sstevel@tonic-gate 		envseglen = frup->envseglen;
10580Sstevel@tonic-gate 		sensorcnt = envsegp->sensor_count;
10590Sstevel@tonic-gate 
10600Sstevel@tonic-gate 		/*
10610Sstevel@tonic-gate 		 * Locate our sensor data record entry
10620Sstevel@tonic-gate 		 */
10630Sstevel@tonic-gate 		for (i = 0; i < sensorcnt; i++) {
10640Sstevel@tonic-gate 			uint32_t	id;
10650Sstevel@tonic-gate 
10660Sstevel@tonic-gate 			id = GET_UNALIGN32(&envsegp->sensors[i].sensor_id[0]);
10670Sstevel@tonic-gate 			if (env_debug > 1)
10680Sstevel@tonic-gate 				envd_log(LOG_INFO, " sensor[%d]: id:%x\n",
10690Sstevel@tonic-gate 				    i, id);
10700Sstevel@tonic-gate 			if (id == sensorp->fru_sensor)
10710Sstevel@tonic-gate 				break;
10720Sstevel@tonic-gate 		}
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate 		if (i >= sensorcnt)
10750Sstevel@tonic-gate 			continue;
10760Sstevel@tonic-gate 
10770Sstevel@tonic-gate 		/*
10780Sstevel@tonic-gate 		 * Validate offset/length of our sensor data record
10790Sstevel@tonic-gate 		 */
10800Sstevel@tonic-gate 		offset = (uint_t)GET_UNALIGN16(&envsegp->sensors[i].offset);
10810Sstevel@tonic-gate 		datap =  (envseg_sensor_data_t *)((intptr_t)frup->envsegbufp +
10820Sstevel@tonic-gate 		    offset);
10830Sstevel@tonic-gate 		mapentries =  GET_UNALIGN16(&datap->obs2exp_cnt);
10840Sstevel@tonic-gate 		length = sizeof (envseg_sensor_data_t) - sizeof (envseg_map_t) +
10850Sstevel@tonic-gate 		    mapentries * sizeof (envseg_map_t);
10860Sstevel@tonic-gate 
10870Sstevel@tonic-gate 		if (env_debug > 1)
10880Sstevel@tonic-gate 			envd_log(LOG_INFO, "Found sensor_id:%x idx:%x "
10890Sstevel@tonic-gate 			"off:%x #maps:%x expected length:%x\n",
10900Sstevel@tonic-gate 				sensorp->fru_sensor, i, offset,
10910Sstevel@tonic-gate 				mapentries, length);
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 		if (offset >= envseglen || (offset+length) > envseglen) {
10940Sstevel@tonic-gate 			/* corrupted sensor record */
10950Sstevel@tonic-gate 			envd_log(LOG_CRIT, ENV_FRU_BAD_SENSOR_ENTRY,
10960Sstevel@tonic-gate 			    sensorp->fru_sensor, sensorp->name, sensorp->fru);
10970Sstevel@tonic-gate 			continue;
10980Sstevel@tonic-gate 		}
10990Sstevel@tonic-gate 
11000Sstevel@tonic-gate 		if (env_debug > 1) {
11010Sstevel@tonic-gate 			/* print threshold values */
11020Sstevel@tonic-gate 			envd_log(LOG_INFO,
11030Sstevel@tonic-gate 			    "Thresholds: HPwrOff %d  HShutDn %d  HWarn %d\n",
11040Sstevel@tonic-gate 			    datap->high_power_off, datap->high_shutdown,
11050Sstevel@tonic-gate 			    datap->high_warning);
11060Sstevel@tonic-gate 			envd_log(LOG_INFO,
11070Sstevel@tonic-gate 			    "Thresholds: LWarn %d  LShutDn %d  LPwrOff %d\n",
11080Sstevel@tonic-gate 			    datap->low_warning, datap->low_shutdown,
11090Sstevel@tonic-gate 			    datap->low_power_off);
11100Sstevel@tonic-gate 
11110Sstevel@tonic-gate 			/* print policy data */
11120Sstevel@tonic-gate 			envd_log(LOG_INFO,
11130Sstevel@tonic-gate 			    " Policy type: %d #%d data: %x %x %x %x %x %x\n",
11140Sstevel@tonic-gate 			    datap->policy_type, datap->policy_entries,
11150Sstevel@tonic-gate 			    datap->policy_data[0], datap->policy_data[1],
11160Sstevel@tonic-gate 			    datap->policy_data[2], datap->policy_data[3],
11170Sstevel@tonic-gate 			    datap->policy_data[4], datap->policy_data[5]);
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate 			/* print map table */
11200Sstevel@tonic-gate 			for (i = 0; i < mapentries; i++) {
11210Sstevel@tonic-gate 				envd_log(LOG_INFO, " Map pair# %d: %d %d\n",
11220Sstevel@tonic-gate 				    i, datap->obs2exp_map[i].observed,
11230Sstevel@tonic-gate 				    datap->obs2exp_map[i].expected);
11240Sstevel@tonic-gate 			}
11250Sstevel@tonic-gate 		}
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate 		/*
11290Sstevel@tonic-gate 		 * Copy threshold values
11300Sstevel@tonic-gate 		 */
11310Sstevel@tonic-gate 		threshp = sensorp->temp_thresh;
11320Sstevel@tonic-gate 		threshp->high_power_off = datap->high_power_off;
11330Sstevel@tonic-gate 		threshp->high_shutdown = datap->high_shutdown;
11340Sstevel@tonic-gate 		threshp->high_warning = datap->high_warning;
11350Sstevel@tonic-gate 		threshp->low_warning = datap->low_warning;
11360Sstevel@tonic-gate 		threshp->low_shutdown = datap->low_shutdown;
11370Sstevel@tonic-gate 		threshp->low_power_off = datap->low_power_off;
11380Sstevel@tonic-gate 
11390Sstevel@tonic-gate 		/*
11400Sstevel@tonic-gate 		 * Copy policy data
11410Sstevel@tonic-gate 		 */
11420Sstevel@tonic-gate 		threshp->policy_type = datap->policy_type;
11430Sstevel@tonic-gate 		threshp->policy_entries = datap->policy_entries;
11440Sstevel@tonic-gate 		for (i = 0; i < MAX_POLICY_ENTRIES; i++)
11450Sstevel@tonic-gate 			threshp->policy_data[i] =
11460Sstevel@tonic-gate 			    (tempr_t)datap->policy_data[i];
11470Sstevel@tonic-gate 
11480Sstevel@tonic-gate 		/*
11490Sstevel@tonic-gate 		 * Copy temperature mapping info (discard duplicate entries)
11500Sstevel@tonic-gate 		 */
11510Sstevel@tonic-gate 		if (sensorp->obs2exp_map) {
11520Sstevel@tonic-gate 			(void) free(sensorp->obs2exp_map);
11530Sstevel@tonic-gate 			sensorp->obs2exp_map = NULL;
11540Sstevel@tonic-gate 			sensorp->obs2exp_cnt = 0;
11550Sstevel@tonic-gate 		}
11560Sstevel@tonic-gate 		if (mapentries > 0) {
11570Sstevel@tonic-gate 			tempr_map_t	*map;
11580Sstevel@tonic-gate 			int		cnt;
11590Sstevel@tonic-gate 			tempr_t		observed, expected;
11600Sstevel@tonic-gate 
11610Sstevel@tonic-gate 			map = (tempr_map_t *)malloc(mapentries *
11620Sstevel@tonic-gate 			    sizeof (tempr_map_t));
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 			if (map == NULL) {
11650Sstevel@tonic-gate 				envd_log(LOG_CRIT, ENV_FRU_SENSOR_MAP_NOMEM,
11660Sstevel@tonic-gate 				    sensorp->fru_sensor, sensorp->name,
11670Sstevel@tonic-gate 				    sensorp->fru);
11680Sstevel@tonic-gate 				continue;
11690Sstevel@tonic-gate 			}
11700Sstevel@tonic-gate 
11710Sstevel@tonic-gate 			for (i = 0, cnt = 0; i < mapentries; i++) {
11720Sstevel@tonic-gate 
11730Sstevel@tonic-gate 				observed = (tempr_t)
11740Sstevel@tonic-gate 				    datap->obs2exp_map[i].observed;
1175*796Smathue 				expected = (tempr_t)
11760Sstevel@tonic-gate 				    datap->obs2exp_map[i].expected;
11770Sstevel@tonic-gate 
11780Sstevel@tonic-gate 				/* ignore if duplicate entry */
11790Sstevel@tonic-gate 				if (cnt > 0 &&
11800Sstevel@tonic-gate 				    observed == map[cnt-1].observed &&
11810Sstevel@tonic-gate 				    expected == map[cnt-1].expected) {
11820Sstevel@tonic-gate 					continue;
11830Sstevel@tonic-gate 				}
11840Sstevel@tonic-gate 				map[cnt].observed = observed;
11850Sstevel@tonic-gate 				map[cnt].expected = expected;
11860Sstevel@tonic-gate 				cnt++;
11870Sstevel@tonic-gate 			}
11880Sstevel@tonic-gate 			sensorp->obs2exp_cnt = cnt;
11890Sstevel@tonic-gate 			sensorp->obs2exp_map = map;
11900Sstevel@tonic-gate 		}
11910Sstevel@tonic-gate 
11920Sstevel@tonic-gate 		if (env_debug > 2 && sensorp->obs2exp_cnt > 1) {
11930Sstevel@tonic-gate 			char	msgbuf[256];
11940Sstevel@tonic-gate 
11950Sstevel@tonic-gate 			envd_log(LOG_INFO,
11960Sstevel@tonic-gate 			    "Measured --> Correct temperature table "
11970Sstevel@tonic-gate 			    "for sensor: %s\n", sensorp->name);
11980Sstevel@tonic-gate 			for (i = -128; i < 128; i++) {
11990Sstevel@tonic-gate 				(void) sprintf(&msgbuf[6*(i&0x7)], "%6d",
12000Sstevel@tonic-gate 				    xlate_obs2exp(sensorp, i));
12010Sstevel@tonic-gate 				if ((i &0x7) == 0x7)
12020Sstevel@tonic-gate 					envd_log(LOG_INFO,
12030Sstevel@tonic-gate 					    "%8d: %s\n", (i & ~0x7), msgbuf);
12040Sstevel@tonic-gate 			}
12050Sstevel@tonic-gate 			if ((i & 0x7) != 0)
12060Sstevel@tonic-gate 				(void) printf("%8d: %s\n", (i & ~0x7), msgbuf);
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate 			envd_log(LOG_INFO,
12090Sstevel@tonic-gate 			    "Correct --> Measured temperature table "
12100Sstevel@tonic-gate 			    "for sensor: %s\n", sensorp->name);
12110Sstevel@tonic-gate 			for (i = -128; i < 128; i++) {
12120Sstevel@tonic-gate 				(void) sprintf(&msgbuf[6*(i&0x7)], "%6d",
12130Sstevel@tonic-gate 				    xlate_exp2obs(sensorp, i));
12140Sstevel@tonic-gate 				if ((i &0x7) == 0x7)
12150Sstevel@tonic-gate 					envd_log(LOG_INFO,
12160Sstevel@tonic-gate 					    "%8d: %s\n", (i & ~0x7), msgbuf);
12170Sstevel@tonic-gate 			}
12180Sstevel@tonic-gate 			if ((i & 0x7) != 0)
12190Sstevel@tonic-gate 				envd_log(LOG_INFO,
12200Sstevel@tonic-gate 				    "%8d: %s\n", (i & ~0x7), msgbuf);
12210Sstevel@tonic-gate 		}
12220Sstevel@tonic-gate 	}
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate 	/*
12250Sstevel@tonic-gate 	 * Deallocate environmental segment list
12260Sstevel@tonic-gate 	 */
12270Sstevel@tonic-gate 	while (fruenvsegs) {
12280Sstevel@tonic-gate 		frup = fruenvsegs;
12290Sstevel@tonic-gate 		fruenvsegs = frup->next;
12300Sstevel@tonic-gate 		if (frup->envsegbufp != NULL)
12310Sstevel@tonic-gate 			(void) free(frup->envsegbufp);
12320Sstevel@tonic-gate 		(void) free(frup);
12330Sstevel@tonic-gate 	}
12340Sstevel@tonic-gate }
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate /*
12370Sstevel@tonic-gate  * Lookup fan and return a pointer to env_fan_t data structure.
12380Sstevel@tonic-gate  */
12390Sstevel@tonic-gate env_fan_t *
fan_lookup(char * name)12400Sstevel@tonic-gate fan_lookup(char *name)
12410Sstevel@tonic-gate {
12420Sstevel@tonic-gate 	int		i;
12430Sstevel@tonic-gate 	env_fan_t	*fanp;
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
12460Sstevel@tonic-gate 		if (strcmp(fanp->name, name) == 0)
12470Sstevel@tonic-gate 			return (fanp);
12480Sstevel@tonic-gate 	}
12490Sstevel@tonic-gate 	return (NULL);
12500Sstevel@tonic-gate }
12510Sstevel@tonic-gate 
12520Sstevel@tonic-gate /*
12530Sstevel@tonic-gate  * Lookup sensor and return a pointer to env_sensor_t data structure.
12540Sstevel@tonic-gate  */
12550Sstevel@tonic-gate env_sensor_t *
sensor_lookup(char * name)12560Sstevel@tonic-gate sensor_lookup(char *name)
12570Sstevel@tonic-gate {
12580Sstevel@tonic-gate 	env_sensor_t	*sensorp;
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
12610Sstevel@tonic-gate 		if (strcmp(sensorp->name, name) == 0)
12620Sstevel@tonic-gate 			return (sensorp);
12630Sstevel@tonic-gate 	}
12640Sstevel@tonic-gate 	return (NULL);
12650Sstevel@tonic-gate }
12660Sstevel@tonic-gate 
12670Sstevel@tonic-gate /*
12680Sstevel@tonic-gate  * Get current temperature
12690Sstevel@tonic-gate  * Returns -1 on error, 0 if successful
12700Sstevel@tonic-gate  */
12710Sstevel@tonic-gate int
get_temperature(env_sensor_t * sensorp,tempr_t * temp)12720Sstevel@tonic-gate get_temperature(env_sensor_t *sensorp, tempr_t *temp)
12730Sstevel@tonic-gate {
12740Sstevel@tonic-gate 	int	fd = sensorp->fd;
12750Sstevel@tonic-gate 	int	retval = 0;
12760Sstevel@tonic-gate 	int	expected_temp;
12770Sstevel@tonic-gate 
12780Sstevel@tonic-gate 	if (fd == -1)
12790Sstevel@tonic-gate 		retval = -1;
12800Sstevel@tonic-gate 	else if (ioctl(fd, I2C_GET_TEMPERATURE, temp) == -1) {
12810Sstevel@tonic-gate 		retval = -1;
12820Sstevel@tonic-gate 		if (sensorp->error == 0) {
12830Sstevel@tonic-gate 			sensorp->error = 1;
12840Sstevel@tonic-gate 			envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_FAIL,
12850Sstevel@tonic-gate 			    sensorp->name, errno, strerror(errno));
12860Sstevel@tonic-gate 		}
12870Sstevel@tonic-gate 	} else if (sensorp->error != 0) {
12880Sstevel@tonic-gate 		sensorp->error = 0;
12890Sstevel@tonic-gate 		envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_OK, sensorp->name);
12900Sstevel@tonic-gate 	} else if (sensorp->obs2exp_map != NULL) {
12910Sstevel@tonic-gate 		expected_temp = xlate_obs2exp(sensorp, (tempr_t)*temp);
12920Sstevel@tonic-gate 		if (env_debug > 1)
12930Sstevel@tonic-gate 			envd_log(LOG_INFO,
12940Sstevel@tonic-gate 			    "sensor: %-13s temp:%d  CORRECED to %d\n",
12950Sstevel@tonic-gate 			    sensorp->name, *temp, (tempr_t)expected_temp);
12960Sstevel@tonic-gate 		*temp = (tempr_t)expected_temp;
12970Sstevel@tonic-gate 	}
12980Sstevel@tonic-gate 
12990Sstevel@tonic-gate 	return (retval);
13000Sstevel@tonic-gate }
13010Sstevel@tonic-gate 
13020Sstevel@tonic-gate /*
13030Sstevel@tonic-gate  * Get current fan speed
13040Sstevel@tonic-gate  * Returns -1 on error, 0 if successful
13050Sstevel@tonic-gate  */
13060Sstevel@tonic-gate int
get_fan_speed(env_fan_t * fanp,fanspeed_t * fanspeedp)13070Sstevel@tonic-gate get_fan_speed(env_fan_t *fanp, fanspeed_t *fanspeedp)
13080Sstevel@tonic-gate {
13090Sstevel@tonic-gate 	int	fan_fd;
13100Sstevel@tonic-gate 	int	retval = 0;
13110Sstevel@tonic-gate 
13120Sstevel@tonic-gate 	fan_fd = fanp->fd;
13130Sstevel@tonic-gate 	if (fan_fd == -1 || read(fan_fd, fanspeedp, sizeof (fanspeed_t)) !=
13140Sstevel@tonic-gate 	    sizeof (fanspeed_t))
13150Sstevel@tonic-gate 		retval = -1;
13160Sstevel@tonic-gate 	return (retval);
13170Sstevel@tonic-gate }
13180Sstevel@tonic-gate 
13190Sstevel@tonic-gate /*
13200Sstevel@tonic-gate  * Set fan speed
13210Sstevel@tonic-gate  * Returns -1 on error, 0 if successful
13220Sstevel@tonic-gate  */
13230Sstevel@tonic-gate static int
set_fan_speed(env_fan_t * fanp,fanspeed_t fanspeed)13240Sstevel@tonic-gate set_fan_speed(env_fan_t *fanp, fanspeed_t fanspeed)
13250Sstevel@tonic-gate {
13260Sstevel@tonic-gate 	int	fan_fd;
13270Sstevel@tonic-gate 	int	retval = 0;
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate 	fan_fd = fanp->fd;
13300Sstevel@tonic-gate 	if (fan_fd == -1 || write(fan_fd, &fanspeed, sizeof (fanspeed)) !=
13310Sstevel@tonic-gate 	    sizeof (fanspeed_t))
13320Sstevel@tonic-gate 		retval = -1;
13330Sstevel@tonic-gate 	return (retval);
13340Sstevel@tonic-gate }
13350Sstevel@tonic-gate 
13360Sstevel@tonic-gate 
13370Sstevel@tonic-gate /*
13380Sstevel@tonic-gate  * close all fan devices
13390Sstevel@tonic-gate  */
13400Sstevel@tonic-gate static void
envd_close_fans(void)13410Sstevel@tonic-gate envd_close_fans(void)
13420Sstevel@tonic-gate {
13430Sstevel@tonic-gate 	int		i;
13440Sstevel@tonic-gate 	env_fan_t	*fanp;
13450Sstevel@tonic-gate 
13460Sstevel@tonic-gate 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
13470Sstevel@tonic-gate 		if (fanp->fd != -1) {
13480Sstevel@tonic-gate 			(void) close(fanp->fd);
13490Sstevel@tonic-gate 			fanp->fd = -1;
13500Sstevel@tonic-gate 		}
13510Sstevel@tonic-gate 	}
13520Sstevel@tonic-gate }
13530Sstevel@tonic-gate 
13540Sstevel@tonic-gate /*
13550Sstevel@tonic-gate  * Close sensor devices
13560Sstevel@tonic-gate  */
13570Sstevel@tonic-gate static void
envd_close_sensors(void)13580Sstevel@tonic-gate envd_close_sensors(void)
13590Sstevel@tonic-gate {
13600Sstevel@tonic-gate 	env_sensor_t	*sensorp;
13610Sstevel@tonic-gate 
13620Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
13630Sstevel@tonic-gate 		if (sensorp->fd != -1) {
13640Sstevel@tonic-gate 			(void) close(sensorp->fd);
13650Sstevel@tonic-gate 			sensorp->fd = -1;
13660Sstevel@tonic-gate 		}
13670Sstevel@tonic-gate 	}
13680Sstevel@tonic-gate }
13690Sstevel@tonic-gate 
13700Sstevel@tonic-gate /*
13710Sstevel@tonic-gate  * Open PM device
13720Sstevel@tonic-gate  */
13730Sstevel@tonic-gate static void
envd_open_pm(void)13740Sstevel@tonic-gate envd_open_pm(void)
13750Sstevel@tonic-gate {
13760Sstevel@tonic-gate 	pm_fd = open(PM_DEVICE, O_RDONLY);
13770Sstevel@tonic-gate 	if (pm_fd != -1)
13780Sstevel@tonic-gate 		(void) fcntl(pm_fd, F_SETFD, FD_CLOEXEC);
13790Sstevel@tonic-gate }
13800Sstevel@tonic-gate 
13810Sstevel@tonic-gate /*
13820Sstevel@tonic-gate  * Close PM device
13830Sstevel@tonic-gate  */
13840Sstevel@tonic-gate static void
envd_close_pm(void)13850Sstevel@tonic-gate envd_close_pm(void)
13860Sstevel@tonic-gate {
13870Sstevel@tonic-gate 	if (pm_fd != -1) {
13880Sstevel@tonic-gate 		(void) close(pm_fd);
13890Sstevel@tonic-gate 		pm_fd = -1;
13900Sstevel@tonic-gate 	}
13910Sstevel@tonic-gate }
13920Sstevel@tonic-gate 
13930Sstevel@tonic-gate /*
13940Sstevel@tonic-gate  * Open fan devices and initialize per fan data structure.
13950Sstevel@tonic-gate  * Returns #fans found.
13960Sstevel@tonic-gate  */
13970Sstevel@tonic-gate static int
envd_setup_fans(void)13980Sstevel@tonic-gate envd_setup_fans(void)
13990Sstevel@tonic-gate {
14000Sstevel@tonic-gate 	int		i, fd;
14010Sstevel@tonic-gate 	fanspeed_t	speed;
14020Sstevel@tonic-gate 	env_fan_t	*fanp;
14030Sstevel@tonic-gate 	char		path[PATH_MAX];
14040Sstevel@tonic-gate 	int		fancnt = 0;
14050Sstevel@tonic-gate 	char		*fan_name;
14060Sstevel@tonic-gate 	sensor_fan_map_t *sfmap;
14070Sstevel@tonic-gate 	env_sensor_t	*sensorp;
14080Sstevel@tonic-gate 	int		sensor_cnt;
14090Sstevel@tonic-gate 
14100Sstevel@tonic-gate 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
14110Sstevel@tonic-gate 		if (fanp->fd == -1) {
14120Sstevel@tonic-gate 			fanp->sensor_cnt = 0;
14130Sstevel@tonic-gate 			fanp->cur_speed = 0;
14140Sstevel@tonic-gate 			fanp->prev_speed = 0;
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate 			(void) strcpy(path, "/devices");
14170Sstevel@tonic-gate 			(void) strlcat(path, fanp->devfs_path, sizeof (path));
14180Sstevel@tonic-gate 			fd = open(path, O_RDWR);
14190Sstevel@tonic-gate 			if (fd == -1) {
14200Sstevel@tonic-gate 				envd_log(LOG_CRIT,
14210Sstevel@tonic-gate 				    ENV_FAN_OPEN_FAIL, fanp->name,
14220Sstevel@tonic-gate 				    fanp->devfs_path, errno, strerror(errno));
14230Sstevel@tonic-gate 				fanp->present = B_FALSE;
14240Sstevel@tonic-gate 				continue;
14250Sstevel@tonic-gate 			}
14260Sstevel@tonic-gate 			(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
14270Sstevel@tonic-gate 			fanp->fd = fd;
14280Sstevel@tonic-gate 			fanp->present = B_TRUE;
14290Sstevel@tonic-gate 		}
14300Sstevel@tonic-gate 		fancnt++;
14310Sstevel@tonic-gate 
14320Sstevel@tonic-gate 		/*
14330Sstevel@tonic-gate 		 * Set initial speed and update cur_speed/prev_speed
14340Sstevel@tonic-gate 		 */
14350Sstevel@tonic-gate 		if (fanp->forced_speed >= 0) {
14360Sstevel@tonic-gate 			speed = (fanspeed_t)fanp->forced_speed;
14370Sstevel@tonic-gate 			if (speed > fanp->speed_max)
14380Sstevel@tonic-gate 				speed = fanp->speed_max;
14390Sstevel@tonic-gate 			if (!disable_piclenvd)
14400Sstevel@tonic-gate 				(void) set_fan_speed(fanp, speed);
14410Sstevel@tonic-gate 		} else if (get_fan_speed(fanp, &speed) == -1) {
14420Sstevel@tonic-gate 			/*
14430Sstevel@tonic-gate 			 * The Fan driver does not know the current fan speed.
14440Sstevel@tonic-gate 			 * Initialize all ON/OFF fans to ON state and all
14450Sstevel@tonic-gate 			 * variable speed fans under software control to 50%
14460Sstevel@tonic-gate 			 * of the max speed and reread the fan to get the
14470Sstevel@tonic-gate 			 * current speed.
14480Sstevel@tonic-gate 			 */
14490Sstevel@tonic-gate 			speed = (fanp == &envd_psupply_fan) ?
14500Sstevel@tonic-gate 				fanp->speed_max : fanp->speed_max/2;
14510Sstevel@tonic-gate 			if (!disable_piclenvd) {
14520Sstevel@tonic-gate 				(void) set_fan_speed(fanp, speed);
14530Sstevel@tonic-gate 				if (get_fan_speed(fanp, &speed) == -1)
14540Sstevel@tonic-gate 					continue;
14550Sstevel@tonic-gate 			}
14560Sstevel@tonic-gate 		}
14570Sstevel@tonic-gate 		fanp->cur_speed = speed;
14580Sstevel@tonic-gate 		fanp->prev_speed = speed;
14590Sstevel@tonic-gate 
14600Sstevel@tonic-gate 		/*
14610Sstevel@tonic-gate 		 * Process sensor_fan_map[] table and initialize sensors[]
14620Sstevel@tonic-gate 		 * array for this fan.
14630Sstevel@tonic-gate 		 */
14640Sstevel@tonic-gate 		fan_name = fanp->name;
14650Sstevel@tonic-gate 		for (sensor_cnt = 0, sfmap = &sensor_fan_map[0];
14660Sstevel@tonic-gate 		    sfmap->sensor_name != NULL; sfmap++) {
14670Sstevel@tonic-gate 			if (strcmp(sfmap->fan_name, fan_name) != 0)
14680Sstevel@tonic-gate 				continue;
14690Sstevel@tonic-gate 			sensorp = sensor_lookup(sfmap->sensor_name);
14700Sstevel@tonic-gate 			if (sensorp != NULL && sensor_cnt < SENSORS_PER_FAN) {
14710Sstevel@tonic-gate 				fanp->sensors[sensor_cnt] = sensorp;
14720Sstevel@tonic-gate 				sensor_cnt++;
14730Sstevel@tonic-gate 			}
14740Sstevel@tonic-gate 		}
14750Sstevel@tonic-gate 		fanp->sensor_cnt = sensor_cnt;
14760Sstevel@tonic-gate 	}
14770Sstevel@tonic-gate 
14780Sstevel@tonic-gate 	return (fancnt);
14790Sstevel@tonic-gate }
14800Sstevel@tonic-gate 
14810Sstevel@tonic-gate 
14820Sstevel@tonic-gate /*
14830Sstevel@tonic-gate  * Adjust specified sensor target temperature and fan adjustment rate
14840Sstevel@tonic-gate  */
14850Sstevel@tonic-gate 
14860Sstevel@tonic-gate static void
adjust_sensor_target(env_sensor_t * sensorp)14870Sstevel@tonic-gate adjust_sensor_target(env_sensor_t *sensorp)
14880Sstevel@tonic-gate {
14890Sstevel@tonic-gate 	int		target, index;
14900Sstevel@tonic-gate 	sensor_pmdev_t	*pmdevp;
14910Sstevel@tonic-gate 	sensor_thresh_t	*threshp;
14920Sstevel@tonic-gate 	float		rate;
14930Sstevel@tonic-gate 
14940Sstevel@tonic-gate 	/*
14950Sstevel@tonic-gate 	 * Look at current power state of all power managed devices
14960Sstevel@tonic-gate 	 * associated with this sensor and look up the desired target
14970Sstevel@tonic-gate 	 * temperature and pick the lowest one of those values. Also,
14980Sstevel@tonic-gate 	 * calculate the rate of change based upon whether one or more
14990Sstevel@tonic-gate 	 * of the associated power managed devices are not running at
15000Sstevel@tonic-gate 	 * full power mode.
15010Sstevel@tonic-gate 	 */
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate 	if (sensorp == NULL || (threshp = sensorp->temp_thresh) == NULL ||
15040Sstevel@tonic-gate 	    threshp->policy_type != POLICY_TARGET_TEMP)
15050Sstevel@tonic-gate 		return;
15060Sstevel@tonic-gate 
15070Sstevel@tonic-gate 	target = threshp->policy_data[0];
15080Sstevel@tonic-gate 	rate = 1.0;
15090Sstevel@tonic-gate 	for (pmdevp = sensorp->pmdevp; pmdevp != NULL; pmdevp = pmdevp->next) {
15100Sstevel@tonic-gate 		index = pmdevp->full_power - pmdevp->cur_power;
15110Sstevel@tonic-gate 		if (index <= 0)
15120Sstevel@tonic-gate 			continue;
15130Sstevel@tonic-gate 
15140Sstevel@tonic-gate 		/* not running at full power */
15150Sstevel@tonic-gate 		if (index >= threshp->policy_entries)
15160Sstevel@tonic-gate 			index = threshp->policy_entries - 1;
15170Sstevel@tonic-gate 		if (target > threshp->policy_data[index])
15180Sstevel@tonic-gate 			target = threshp->policy_data[index];
15190Sstevel@tonic-gate 		if (rate > (float)fan_slow_adjustment/100)
15200Sstevel@tonic-gate 			rate = (float)fan_slow_adjustment/100;
15210Sstevel@tonic-gate 		if (env_debug > 1)
15220Sstevel@tonic-gate 			envd_log(LOG_INFO,
15230Sstevel@tonic-gate 			    "pmdev: %-13s new_target:%d  cur:%d power:%d/%d\n",
15240Sstevel@tonic-gate 			    pmdevp->pmdev_name, target, sensorp->target_temp,
15250Sstevel@tonic-gate 			    pmdevp->cur_power, pmdevp->full_power);
15260Sstevel@tonic-gate 	}
15270Sstevel@tonic-gate 
15280Sstevel@tonic-gate 	if (env_debug)
15290Sstevel@tonic-gate 		envd_log(LOG_INFO,
15300Sstevel@tonic-gate 		    "sensor: %-13s new_target:%d  cur:%d power:%d/%d\n",
15310Sstevel@tonic-gate 		    sensorp->name, target, sensorp->target_temp,
15320Sstevel@tonic-gate 		    ((sensorp->pmdevp) ? sensorp->pmdevp->cur_power : -1),
15330Sstevel@tonic-gate 		    ((sensorp->pmdevp) ? sensorp->pmdevp->full_power : -1));
15340Sstevel@tonic-gate 
15350Sstevel@tonic-gate 	sensorp->fan_adjustment_rate = rate;
15360Sstevel@tonic-gate 	sensorp->target_temp = target;
15370Sstevel@tonic-gate }
15380Sstevel@tonic-gate 
15390Sstevel@tonic-gate /*
15400Sstevel@tonic-gate  * Update current power level of all PM devices we are tracking and adjust
15410Sstevel@tonic-gate  * the target temperature associated with the corresponding sensor.
15420Sstevel@tonic-gate  *
15430Sstevel@tonic-gate  * Returns 1 if one or more pmdev power level was adjusted; 0 otherwise.
15440Sstevel@tonic-gate  */
15450Sstevel@tonic-gate static int
update_pmdev_power()15460Sstevel@tonic-gate update_pmdev_power()
15470Sstevel@tonic-gate {
15480Sstevel@tonic-gate 	sensor_pmdev_t	*pmdevp;
15490Sstevel@tonic-gate 	pm_req_t	pmreq;
15500Sstevel@tonic-gate 	int		cur_power;
15510Sstevel@tonic-gate 	int		updated = 0;
15520Sstevel@tonic-gate 
15530Sstevel@tonic-gate 	for (pmdevp = sensor_pmdevs; pmdevp->pmdev_name != NULL; pmdevp++) {
15540Sstevel@tonic-gate 		pmreq.physpath = pmdevp->pmdev_name;
15550Sstevel@tonic-gate 		pmreq.data = NULL;
15560Sstevel@tonic-gate 		pmreq.datasize = 0;
15570Sstevel@tonic-gate 		pmreq.component = pmdevp->speed_comp;
15580Sstevel@tonic-gate 		cur_power = ioctl(pm_fd, PM_GET_CURRENT_POWER, &pmreq);
15590Sstevel@tonic-gate 		if (pmdevp->cur_power != cur_power) {
15600Sstevel@tonic-gate 			pmdevp->cur_power = cur_power;
15610Sstevel@tonic-gate 			if (pmdevp->sensorp) {
15620Sstevel@tonic-gate 				adjust_sensor_target(pmdevp->sensorp);
15630Sstevel@tonic-gate 				updated = 1;
15640Sstevel@tonic-gate 			}
15650Sstevel@tonic-gate 		}
15660Sstevel@tonic-gate 	}
15670Sstevel@tonic-gate 	return (updated);
15680Sstevel@tonic-gate }
15690Sstevel@tonic-gate 
15700Sstevel@tonic-gate /*
15710Sstevel@tonic-gate  * Check if the specified sensor is present.
15720Sstevel@tonic-gate  * Returns 1 if present; 0 otherwise.
15730Sstevel@tonic-gate  *
15740Sstevel@tonic-gate  * Note that we don't use ptree_get_node_by_path() here to detect
15750Sstevel@tonic-gate  * if a temperature device is present as we don't want to make
15760Sstevel@tonic-gate  * "devtree" a critical plugin.
15770Sstevel@tonic-gate  */
15780Sstevel@tonic-gate static int
envd_sensor_present(env_sensor_t * sensorp)15790Sstevel@tonic-gate envd_sensor_present(env_sensor_t *sensorp)
15800Sstevel@tonic-gate {
15810Sstevel@tonic-gate 	char		*p, physpath[PATH_MAX];
15820Sstevel@tonic-gate 	di_node_t	root_node;
15830Sstevel@tonic-gate 	int		sensor_present = 0;
15840Sstevel@tonic-gate 
15850Sstevel@tonic-gate 	/*
15860Sstevel@tonic-gate 	 * Construct temperature device path by stripping minor
15870Sstevel@tonic-gate 	 * node name from the devfs_path and use di_init() to
15880Sstevel@tonic-gate 	 * see if the node exists.
15890Sstevel@tonic-gate 	 */
15900Sstevel@tonic-gate 	(void) strcpy(physpath, sensorp->devfs_path);
15910Sstevel@tonic-gate 	p = strrchr(physpath, ':');
15920Sstevel@tonic-gate 	if (p != NULL)
15930Sstevel@tonic-gate 		*p = '\0';
15940Sstevel@tonic-gate 	if ((root_node = di_init(physpath, DINFOMINOR)) != DI_NODE_NIL) {
15950Sstevel@tonic-gate 		di_fini(root_node);
15960Sstevel@tonic-gate 		sensor_present = 1;
15970Sstevel@tonic-gate 	}
15980Sstevel@tonic-gate 	return (sensor_present);
15990Sstevel@tonic-gate }
16000Sstevel@tonic-gate 
16010Sstevel@tonic-gate /*
16020Sstevel@tonic-gate  * Open temperature sensor devices and initialize per sensor data structure.
16030Sstevel@tonic-gate  * Returns #sensors found.
16040Sstevel@tonic-gate  */
16050Sstevel@tonic-gate static int
envd_setup_sensors(void)16060Sstevel@tonic-gate envd_setup_sensors(void)
16070Sstevel@tonic-gate {
16080Sstevel@tonic-gate 	tempr_t		temp;
16090Sstevel@tonic-gate 	env_sensor_t	*sensorp;
16100Sstevel@tonic-gate 	char		path[PATH_MAX];
16110Sstevel@tonic-gate 	int		sensorcnt = 0;
16120Sstevel@tonic-gate 	int		sensor_present;
16130Sstevel@tonic-gate 	sensor_thresh_t	*threshp;
16140Sstevel@tonic-gate 	sensor_pmdev_t	*pmdevp;
16150Sstevel@tonic-gate 
16160Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
16170Sstevel@tonic-gate 		if (sensorp->fd != -1) {
16180Sstevel@tonic-gate 			/* Don't reinitialize opened sensor */
16190Sstevel@tonic-gate 			threshp = sensorp->temp_thresh;
16200Sstevel@tonic-gate 			sensorp->pmdevp = NULL;
16210Sstevel@tonic-gate 		} else {
16220Sstevel@tonic-gate 			/* Initialize sensor's initial state */
16230Sstevel@tonic-gate 			sensorp->shutdown_initiated = B_FALSE;
16240Sstevel@tonic-gate 			sensorp->warning_tstamp = 0;
16250Sstevel@tonic-gate 			sensorp->warning_start = 0;
16260Sstevel@tonic-gate 			sensorp->shutdown_tstamp = 0;
16270Sstevel@tonic-gate 			sensorp->pmdevp = NULL;
16280Sstevel@tonic-gate 			sensorp->fan_adjustment_rate = 1.0;
16290Sstevel@tonic-gate 
16300Sstevel@tonic-gate 			threshp = sensorp->temp_thresh;
16310Sstevel@tonic-gate 			temp = (threshp && threshp->policy_entries > 0) ?
16320Sstevel@tonic-gate 			    threshp->policy_data[0] : 0;
16330Sstevel@tonic-gate 			sensorp->target_temp = temp;
16340Sstevel@tonic-gate 			sensorp->cur_temp = temp;
16350Sstevel@tonic-gate 			sensorp->avg_temp = temp;
16360Sstevel@tonic-gate 			sensorp->prev_avg_temp = temp;
16370Sstevel@tonic-gate 			sensorp->error = 0;
16380Sstevel@tonic-gate 
16390Sstevel@tonic-gate 			(void) strcpy(path, "/devices");
16400Sstevel@tonic-gate 			(void) strlcat(path, sensorp->devfs_path,
16410Sstevel@tonic-gate 			    sizeof (path));
16420Sstevel@tonic-gate 		retry:
16430Sstevel@tonic-gate 			sensorp->fd = open(path, O_RDWR);
16440Sstevel@tonic-gate 			if (sensorp->fd == -1) {
16450Sstevel@tonic-gate 				sensor_present = envd_sensor_present(sensorp);
16460Sstevel@tonic-gate 				if (sensor_present && !devfsadm_invoked &&
16470Sstevel@tonic-gate 				    devfsadm_cmd[0] != '\0') {
16480Sstevel@tonic-gate 					/*
16490Sstevel@tonic-gate 					 * Sensor is present but no path
16500Sstevel@tonic-gate 					 * exists as someone rebooted the
16510Sstevel@tonic-gate 					 * system without "-r" option. Let's
16520Sstevel@tonic-gate 					 * invoke "devfsadm" once to create
16530Sstevel@tonic-gate 					 * max1617 sensors paths in /devices
16540Sstevel@tonic-gate 					 * subtree and try again so that we
16550Sstevel@tonic-gate 					 * can monitor all accessible sensors
16560Sstevel@tonic-gate 					 * and prevent any CPU overheating.
16570Sstevel@tonic-gate 					 *
16580Sstevel@tonic-gate 					 * Note that this routine is always
16590Sstevel@tonic-gate 					 * called in main thread context and
16600Sstevel@tonic-gate 					 * serialized with respect to other
16610Sstevel@tonic-gate 					 * plugins' initialization. Hence, it's
16620Sstevel@tonic-gate 					 * safe to use system(3C) call here.
16630Sstevel@tonic-gate 					 */
16640Sstevel@tonic-gate 					devfsadm_invoked = 1;
16650Sstevel@tonic-gate 					(void) system(devfsadm_cmd);
16660Sstevel@tonic-gate 					goto retry;
16670Sstevel@tonic-gate 				}
16680Sstevel@tonic-gate 				if (sensor_present)
16690Sstevel@tonic-gate 					envd_log(LOG_CRIT,
16700Sstevel@tonic-gate 					    ENV_SENSOR_OPEN_FAIL,
16710Sstevel@tonic-gate 					    sensorp->name,
16720Sstevel@tonic-gate 					    sensorp->devfs_path, errno,
16730Sstevel@tonic-gate 					    strerror(errno));
16740Sstevel@tonic-gate 				sensorp->present = B_FALSE;
16750Sstevel@tonic-gate 				continue;
16760Sstevel@tonic-gate 			}
16770Sstevel@tonic-gate 			(void) fcntl(sensorp->fd, F_SETFD, FD_CLOEXEC);
16780Sstevel@tonic-gate 			sensorp->present = B_TRUE;
16790Sstevel@tonic-gate 
16800Sstevel@tonic-gate 			/*
16810Sstevel@tonic-gate 			 * Set cur_temp field to the current temperature value
16820Sstevel@tonic-gate 			 */
16830Sstevel@tonic-gate 			if (get_temperature(sensorp, &temp) == 0) {
16840Sstevel@tonic-gate 				sensorp->cur_temp = temp;
16850Sstevel@tonic-gate 				sensorp->avg_temp = temp;
16860Sstevel@tonic-gate 			}
16870Sstevel@tonic-gate 		}
16880Sstevel@tonic-gate 		sensorcnt++;
16890Sstevel@tonic-gate 
16900Sstevel@tonic-gate 		/*
16910Sstevel@tonic-gate 		 * Set low_power_off and high_power_off limits
16920Sstevel@tonic-gate 		 */
16930Sstevel@tonic-gate 		if (threshp && !disable_power_off) {
16940Sstevel@tonic-gate 			temp = xlate_exp2obs(sensorp, threshp->low_power_off);
16950Sstevel@tonic-gate 			if (env_debug > 1)
16960Sstevel@tonic-gate 				envd_log(LOG_INFO, "sensor: %-13s low_power_"
16970Sstevel@tonic-gate 				"off set to %d (real %d)\n", sensorp->name,
16980Sstevel@tonic-gate 				    (int)temp, threshp->low_power_off);
16990Sstevel@tonic-gate 			(void) ioctl(sensorp->fd, MAX1617_SET_LOW_LIMIT, &temp);
17000Sstevel@tonic-gate 
17010Sstevel@tonic-gate 			temp = xlate_exp2obs(sensorp, threshp->high_power_off);
17020Sstevel@tonic-gate 			if (env_debug > 1)
17030Sstevel@tonic-gate 				envd_log(LOG_INFO, "sensor: %-13s high_power_"
17040Sstevel@tonic-gate 				"off set to %d (real %d)\n", sensorp->name,
17050Sstevel@tonic-gate 				    (int)temp, threshp->high_power_off);
17060Sstevel@tonic-gate 			(void) ioctl(sensorp->fd, MAX1617_SET_HIGH_LIMIT,
17070Sstevel@tonic-gate 			    &temp);
17080Sstevel@tonic-gate 		}
17090Sstevel@tonic-gate 	}
17100Sstevel@tonic-gate 
17110Sstevel@tonic-gate 	/*
17120Sstevel@tonic-gate 	 * Locate "CPU Speed" component for any PM devices associated with
17130Sstevel@tonic-gate 	 * the sensors.
17140Sstevel@tonic-gate 	 */
17150Sstevel@tonic-gate 	for (pmdevp = sensor_pmdevs; pmdevp->sensor_name; pmdevp++) {
17160Sstevel@tonic-gate 		int		i, ncomp;
17170Sstevel@tonic-gate 		char		physpath[PATH_MAX];
17180Sstevel@tonic-gate 		pm_req_t	pmreq;
17190Sstevel@tonic-gate 
17200Sstevel@tonic-gate 		pmdevp->speed_comp = -1;
17210Sstevel@tonic-gate 		pmdevp->full_power = -1;
17220Sstevel@tonic-gate 		pmdevp->cur_power = -1;
17230Sstevel@tonic-gate 		pmdevp->next = NULL;
17240Sstevel@tonic-gate 		pmdevp->sensorp = sensorp = sensor_lookup(pmdevp->sensor_name);
17250Sstevel@tonic-gate 
17260Sstevel@tonic-gate 		/*
17270Sstevel@tonic-gate 		 * Lookup speed component and get full and current power
17280Sstevel@tonic-gate 		 * level for that component.
17290Sstevel@tonic-gate 		 */
17300Sstevel@tonic-gate 		pmreq.physpath = pmdevp->pmdev_name;
17310Sstevel@tonic-gate 		pmreq.data = physpath;
17320Sstevel@tonic-gate 		pmreq.datasize = sizeof (physpath);
17330Sstevel@tonic-gate 
17340Sstevel@tonic-gate 		ncomp = ioctl(pm_fd, PM_GET_NUM_COMPONENTS, &pmreq);
17350Sstevel@tonic-gate 		for (i = 0; i < ncomp; i++) {
17360Sstevel@tonic-gate 			pmreq.component = i;
17370Sstevel@tonic-gate 			physpath[0] = '\0';
17380Sstevel@tonic-gate 			if (ioctl(pm_fd, PM_GET_COMPONENT_NAME, &pmreq) <= 0)
17390Sstevel@tonic-gate 				continue;
17400Sstevel@tonic-gate 			if (strcasecmp(pmreq.data, pmdevp->speed_comp_name))
17410Sstevel@tonic-gate 				continue;
17420Sstevel@tonic-gate 			pmdevp->speed_comp = i;
17430Sstevel@tonic-gate 
17440Sstevel@tonic-gate 
17450Sstevel@tonic-gate 			/*
17460Sstevel@tonic-gate 			 * Get full power and current power level
17470Sstevel@tonic-gate 			 */
17480Sstevel@tonic-gate 			pmdevp->full_power = ioctl(pm_fd, PM_GET_FULL_POWER,
17490Sstevel@tonic-gate 			    &pmreq);
17500Sstevel@tonic-gate 
17510Sstevel@tonic-gate 			pmdevp->cur_power = ioctl(pm_fd, PM_GET_CURRENT_POWER,
17520Sstevel@tonic-gate 			    &pmreq);
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate 			if (sensorp) {
17550Sstevel@tonic-gate 				pmdevp->next = sensorp->pmdevp;
17560Sstevel@tonic-gate 				sensorp->pmdevp = pmdevp;
17570Sstevel@tonic-gate 				adjust_sensor_target(sensorp);
17580Sstevel@tonic-gate 			}
17590Sstevel@tonic-gate 			break;
17600Sstevel@tonic-gate 		}
17610Sstevel@tonic-gate 		if (env_debug > 1)
17620Sstevel@tonic-gate 			envd_log(LOG_INFO,
17630Sstevel@tonic-gate 			    "sensor:%s %p pmdev:%s comp:%s %d power:%d/%d\n",
17640Sstevel@tonic-gate 			    pmdevp->sensor_name, pmdevp->sensorp,
17650Sstevel@tonic-gate 			    pmdevp->pmdev_name, pmdevp->speed_comp_name,
17660Sstevel@tonic-gate 			    pmdevp->speed_comp, pmdevp->cur_power,
17670Sstevel@tonic-gate 			    pmdevp->full_power);
17680Sstevel@tonic-gate 	}
17690Sstevel@tonic-gate 	return (sensorcnt);
17700Sstevel@tonic-gate }
17710Sstevel@tonic-gate 
17720Sstevel@tonic-gate /*
17730Sstevel@tonic-gate  * Read all temperature sensors and take appropriate action based
17740Sstevel@tonic-gate  * upon temperature threshols associated with each sensor. Possible
17750Sstevel@tonic-gate  * actions are:
17760Sstevel@tonic-gate  *
17770Sstevel@tonic-gate  *	temperature > high_shutdown
17780Sstevel@tonic-gate  *	temperature < low_shutdown
17790Sstevel@tonic-gate  *		Gracefully shutdown the system and log/print a message
17800Sstevel@tonic-gate  *		on the system console provided the temperature has been
17810Sstevel@tonic-gate  *		in shutdown range for "shutdown_interval" seconds.
17820Sstevel@tonic-gate  *
17830Sstevel@tonic-gate  *	high_warning < temperature <= high_shutdown
17840Sstevel@tonic-gate  *	low_warning  > temperature >= low_shutdown
17850Sstevel@tonic-gate  *		Log/print a warning message on the system console at most
17860Sstevel@tonic-gate  *		once every "warning_interval" seconds.
17870Sstevel@tonic-gate  *
17880Sstevel@tonic-gate  * Note that the current temperature is recorded in the "cur_temp" field
17890Sstevel@tonic-gate  * within each env_sensor_t structure.
17900Sstevel@tonic-gate  */
17910Sstevel@tonic-gate static void
monitor_sensors(void)17920Sstevel@tonic-gate monitor_sensors(void)
17930Sstevel@tonic-gate {
17940Sstevel@tonic-gate 	tempr_t 	temp;
17950Sstevel@tonic-gate 	env_sensor_t	*sensorp;
17960Sstevel@tonic-gate 	sensor_thresh_t	*threshp;
17970Sstevel@tonic-gate 	time_t		ct;
17980Sstevel@tonic-gate 	char		msgbuf[BUFSIZ];
17990Sstevel@tonic-gate 	char		syscmd[BUFSIZ];
18000Sstevel@tonic-gate 
18010Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) {
18020Sstevel@tonic-gate 		if (get_temperature(sensorp, &temp) < 0)
18030Sstevel@tonic-gate 			continue;
18040Sstevel@tonic-gate 
18050Sstevel@tonic-gate 		sensorp->prev_avg_temp = sensorp->avg_temp;
18060Sstevel@tonic-gate 		sensorp->cur_temp = temp;
18070Sstevel@tonic-gate 		sensorp->avg_temp = (sensorp->avg_temp + temp)/2;
18080Sstevel@tonic-gate 		threshp = sensorp->temp_thresh;
18090Sstevel@tonic-gate 
18100Sstevel@tonic-gate 		if (env_debug)
18110Sstevel@tonic-gate 			envd_log(LOG_INFO,
18120Sstevel@tonic-gate 			"sensor: %-13s temp  prev_avg:%6.2f  "
18130Sstevel@tonic-gate 			"cur:%d avg_temp:%6.2f power:%d/%d target:%d\n",
18140Sstevel@tonic-gate 			    sensorp->name, sensorp->prev_avg_temp,
18150Sstevel@tonic-gate 			    temp, sensorp->avg_temp, ((sensorp->pmdevp) ?
18160Sstevel@tonic-gate 			    sensorp->pmdevp->cur_power : -1),
18170Sstevel@tonic-gate 			    ((sensorp->pmdevp) ? sensorp->pmdevp->full_power :
18180Sstevel@tonic-gate 			    -1), sensorp->target_temp);
18190Sstevel@tonic-gate 
18200Sstevel@tonic-gate 
18210Sstevel@tonic-gate 		/*
18220Sstevel@tonic-gate 		 * If this sensor already triggered system shutdown, don't
18230Sstevel@tonic-gate 		 * log any more shutdown/warning messages for it.
18240Sstevel@tonic-gate 		 */
18250Sstevel@tonic-gate 		if (sensorp->shutdown_initiated || threshp == NULL)
18260Sstevel@tonic-gate 			continue;
18270Sstevel@tonic-gate 
18280Sstevel@tonic-gate 		/*
18290Sstevel@tonic-gate 		 * Check for the temperature in warning and shutdown range
18300Sstevel@tonic-gate 		 * and take appropriate action.
18310Sstevel@tonic-gate 		 */
18320Sstevel@tonic-gate 		if (TEMP_IN_WARNING_RANGE(temp, threshp) && !disable_warning) {
18330Sstevel@tonic-gate 			/*
18340Sstevel@tonic-gate 			 * Check if the temperature has been in warning
18350Sstevel@tonic-gate 			 * range during last warning_duration interval.
18360Sstevel@tonic-gate 			 * If so, the temperature is truly in warning
18370Sstevel@tonic-gate 			 * range and we need to log a warning message,
18380Sstevel@tonic-gate 			 * but no more than once every warning_interval
18390Sstevel@tonic-gate 			 * seconds.
18400Sstevel@tonic-gate 			 */
18410Sstevel@tonic-gate 			time_t	wtstamp = sensorp->warning_tstamp;
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate 			ct = (time_t)(gethrtime() / NANOSEC);
18440Sstevel@tonic-gate 			if (sensorp->warning_start == 0)
18450Sstevel@tonic-gate 				sensorp->warning_start = ct;
18460Sstevel@tonic-gate 			if (((ct - sensorp->warning_start) >=
18470Sstevel@tonic-gate 			    warning_duration) && (wtstamp == 0 ||
18480Sstevel@tonic-gate 			    (ct - wtstamp) >= warning_interval)) {
18490Sstevel@tonic-gate 				envd_log(LOG_CRIT, ENV_WARNING_MSG,
18500Sstevel@tonic-gate 				    sensorp->name, temp,
18510Sstevel@tonic-gate 				    threshp->low_warning,
18520Sstevel@tonic-gate 				    threshp->high_warning);
18530Sstevel@tonic-gate 				sensorp->warning_tstamp = ct;
18540Sstevel@tonic-gate 			}
18550Sstevel@tonic-gate 		} else if (sensorp->warning_start != 0)
18560Sstevel@tonic-gate 			sensorp->warning_start = 0;
18570Sstevel@tonic-gate 
18580Sstevel@tonic-gate 		if (TEMP_IN_SHUTDOWN_RANGE(temp, threshp) &&
18590Sstevel@tonic-gate 		    !disable_shutdown) {
18600Sstevel@tonic-gate 			ct = (time_t)(gethrtime() / NANOSEC);
18610Sstevel@tonic-gate 			if (sensorp->shutdown_tstamp == 0)
18620Sstevel@tonic-gate 				sensorp->shutdown_tstamp = ct;
18630Sstevel@tonic-gate 
18640Sstevel@tonic-gate 			/*
18650Sstevel@tonic-gate 			 * Shutdown the system if the temperature remains
18660Sstevel@tonic-gate 			 * in the shutdown range for over shutdown_interval
18670Sstevel@tonic-gate 			 * seconds.
18680Sstevel@tonic-gate 			 */
18690Sstevel@tonic-gate 			if ((ct - sensorp->shutdown_tstamp) >=
18700Sstevel@tonic-gate 			    shutdown_interval) {
18710Sstevel@tonic-gate 				/* log error */
18720Sstevel@tonic-gate 				sensorp->shutdown_initiated = B_TRUE;
18730Sstevel@tonic-gate 				(void) snprintf(msgbuf, sizeof (msgbuf),
18740Sstevel@tonic-gate 				    ENV_SHUTDOWN_MSG, sensorp->name,
18750Sstevel@tonic-gate 				    temp, threshp->low_shutdown,
18760Sstevel@tonic-gate 				    threshp->high_shutdown);
18770Sstevel@tonic-gate 				envd_log(LOG_ALERT, msgbuf);
18780Sstevel@tonic-gate 
18790Sstevel@tonic-gate 				/* shutdown the system (only once) */
18800Sstevel@tonic-gate 				if (system_shutdown_started == B_FALSE) {
18810Sstevel@tonic-gate 					(void) snprintf(syscmd, sizeof (syscmd),
18820Sstevel@tonic-gate 					    "%s \"%s\"", shutdown_cmd, msgbuf);
18830Sstevel@tonic-gate 					envd_log(LOG_ALERT, syscmd);
18840Sstevel@tonic-gate 					system_shutdown_started = B_TRUE;
18850Sstevel@tonic-gate 					(void) system(syscmd);
18860Sstevel@tonic-gate 				}
18870Sstevel@tonic-gate 			}
18880Sstevel@tonic-gate 		} else if (sensorp->shutdown_tstamp != 0)
18890Sstevel@tonic-gate 			sensorp->shutdown_tstamp = 0;
18900Sstevel@tonic-gate 	}
18910Sstevel@tonic-gate }
18920Sstevel@tonic-gate 
18930Sstevel@tonic-gate 
18940Sstevel@tonic-gate /*
18950Sstevel@tonic-gate  * Adjust fan speed based upon the current temperature value of various
18960Sstevel@tonic-gate  * sensors affected by the specified fan.
18970Sstevel@tonic-gate  */
18980Sstevel@tonic-gate static int
adjust_fan_speed(env_fan_t * fanp,lpm_dev_t * devp)18990Sstevel@tonic-gate adjust_fan_speed(env_fan_t *fanp, lpm_dev_t *devp)
19000Sstevel@tonic-gate {
19010Sstevel@tonic-gate 	int		i;
19020Sstevel@tonic-gate 	fanspeed_t	fanspeed;
19030Sstevel@tonic-gate 	float		speed, cur_speed, new_speed, max_speed, min_speed;
19040Sstevel@tonic-gate 	env_sensor_t	*sensorp;
19050Sstevel@tonic-gate 	sensor_thresh_t	*threshp;
19060Sstevel@tonic-gate 	tempr_t		temp;
19070Sstevel@tonic-gate 	float		avg_temp, tempdiff, targetdiff;
19080Sstevel@tonic-gate 	int		av_ambient;
19090Sstevel@tonic-gate 	int		amb_cnt;
19100Sstevel@tonic-gate 
19110Sstevel@tonic-gate 
19120Sstevel@tonic-gate 	/*
19130Sstevel@tonic-gate 	 * Get current fan speed
19140Sstevel@tonic-gate 	 */
19150Sstevel@tonic-gate 	if (get_fan_speed(fanp, &fanspeed) < 0)
19160Sstevel@tonic-gate 		return (-1);
19170Sstevel@tonic-gate 	cur_speed = fanp->cur_speed;
19180Sstevel@tonic-gate 	if (fanspeed != (int)cur_speed)
19190Sstevel@tonic-gate 		cur_speed = (float)fanspeed;
19200Sstevel@tonic-gate 
19210Sstevel@tonic-gate 	/*
19220Sstevel@tonic-gate 	 * Calculate new fan speed for each sensor and pick the largest one.
19230Sstevel@tonic-gate 	 */
19240Sstevel@tonic-gate 	min_speed = fanp->speed_min;
19250Sstevel@tonic-gate 	max_speed = fanp->speed_max;
19260Sstevel@tonic-gate 	speed = 0;
19270Sstevel@tonic-gate 	av_ambient = 0;
19280Sstevel@tonic-gate 	amb_cnt = 0;
19290Sstevel@tonic-gate 
19300Sstevel@tonic-gate 	for (i = 0; i < fanp->sensor_cnt; i++) {
19310Sstevel@tonic-gate 		sensorp = fanp->sensors[i];
19320Sstevel@tonic-gate 		if (sensorp == NULL || sensorp->fd == -1 ||
19330Sstevel@tonic-gate 		    sensorp->temp_thresh == NULL)
19340Sstevel@tonic-gate 			continue;
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 		temp = sensorp->cur_temp;
19370Sstevel@tonic-gate 		avg_temp = sensorp->avg_temp;
19380Sstevel@tonic-gate 		threshp = sensorp->temp_thresh;
19390Sstevel@tonic-gate 
19400Sstevel@tonic-gate 		/*
19410Sstevel@tonic-gate 		 * Note ambient temperatures to determine lpm for system fan
19420Sstevel@tonic-gate 		 */
19430Sstevel@tonic-gate 		if ((devp != NULL) &&
19440Sstevel@tonic-gate 		    (sensorp->flags & SFLAG_CPU_AMB_SENSOR)) {
19450Sstevel@tonic-gate 			av_ambient += temp;
19460Sstevel@tonic-gate 			amb_cnt++;
19470Sstevel@tonic-gate 		}
19480Sstevel@tonic-gate 
19490Sstevel@tonic-gate 		/*
19500Sstevel@tonic-gate 		 * If the current temperature is above the warning
19510Sstevel@tonic-gate 		 * threshold, use max fan speed.
19520Sstevel@tonic-gate 		 */
19530Sstevel@tonic-gate 		if (temp >= threshp->high_warning) {
19540Sstevel@tonic-gate 			speed = max_speed;
19550Sstevel@tonic-gate 			break;
19560Sstevel@tonic-gate 		} else if (temp <= threshp->low_warning) {
19570Sstevel@tonic-gate 			speed = min_speed;
19580Sstevel@tonic-gate 			break;
19590Sstevel@tonic-gate 		}
19600Sstevel@tonic-gate 
19610Sstevel@tonic-gate 		if (threshp->policy_type == POLICY_TARGET_TEMP) {
19620Sstevel@tonic-gate 			/*
19630Sstevel@tonic-gate 			 * Try to achieve the desired target temperature.
19640Sstevel@tonic-gate 			 * Calculate new fan speed based upon whether the
19650Sstevel@tonic-gate 			 * temperature is rising, falling or steady state.
19660Sstevel@tonic-gate 			 * Also take into consideration the current fan
19670Sstevel@tonic-gate 			 * speed as well as the desired target temperature.
19680Sstevel@tonic-gate 			 */
19690Sstevel@tonic-gate 			float	delta, speed_change;
19700Sstevel@tonic-gate 			float	multiplier;
19710Sstevel@tonic-gate 
19720Sstevel@tonic-gate 			targetdiff = avg_temp - sensorp->target_temp;
19730Sstevel@tonic-gate 			tempdiff = avg_temp - sensorp->prev_avg_temp;
19740Sstevel@tonic-gate 
19750Sstevel@tonic-gate 			if (tempdiff > AVG_TEMP_HYSTERESIS) {
19760Sstevel@tonic-gate 				/*
19770Sstevel@tonic-gate 				 * Temperature is rising. Increase fan
19780Sstevel@tonic-gate 				 * speed 0.5% for every 1C above the
19790Sstevel@tonic-gate 				 * (target - RISING_TEMP_MARGIN) limit.
19800Sstevel@tonic-gate 				 * Also take into consideration temperature
19810Sstevel@tonic-gate 				 * rising rate and the current fan speed.
19820Sstevel@tonic-gate 				 */
19830Sstevel@tonic-gate 				delta = max_speed * .005 *
19840Sstevel@tonic-gate 				    (RISING_TEMP_MARGIN + targetdiff);
19850Sstevel@tonic-gate 				if (delta <= 0)
19860Sstevel@tonic-gate 					multiplier = 0;
19870Sstevel@tonic-gate 				else
19880Sstevel@tonic-gate 					multiplier = tempdiff/4 +
19890Sstevel@tonic-gate 					    ((cur_speed < max_speed/2) ?
19900Sstevel@tonic-gate 					    2 : 1);
19910Sstevel@tonic-gate 			} else if (tempdiff < -AVG_TEMP_HYSTERESIS) {
19920Sstevel@tonic-gate 				/*
19930Sstevel@tonic-gate 				 * Temperature is falling. Decrease fan
19940Sstevel@tonic-gate 				 * speed 0.5% for every 1C below the
19950Sstevel@tonic-gate 				 * (target + FALLING_TEMP_MARGIN) limit.
19960Sstevel@tonic-gate 				 * Also take into consideration temperature
19970Sstevel@tonic-gate 				 * falling rate and the current fan speed.
19980Sstevel@tonic-gate 				 */
19990Sstevel@tonic-gate 				delta = -max_speed * .005 *
20000Sstevel@tonic-gate 				    (FALLING_TEMP_MARGIN - targetdiff);
20010Sstevel@tonic-gate 				if (delta >= 0)
20020Sstevel@tonic-gate 					multiplier = 0;
20030Sstevel@tonic-gate 				else
20040Sstevel@tonic-gate 					multiplier = -tempdiff/4 +
20050Sstevel@tonic-gate 					    ((cur_speed > max_speed/2) ?
20060Sstevel@tonic-gate 					    2 : 1);
20070Sstevel@tonic-gate 			} else {
20080Sstevel@tonic-gate 				/*
20090Sstevel@tonic-gate 				 * Temperature is changing very slowly.
20100Sstevel@tonic-gate 				 * Adjust fan speed by 0.4% for every 1C
20110Sstevel@tonic-gate 				 * below/above the target temperature.
20120Sstevel@tonic-gate 				 */
20130Sstevel@tonic-gate 				delta = max_speed * .004 * targetdiff;
20140Sstevel@tonic-gate 				multiplier = 1.0;
20150Sstevel@tonic-gate 			}
20160Sstevel@tonic-gate 
20170Sstevel@tonic-gate 
20180Sstevel@tonic-gate 			/*
20190Sstevel@tonic-gate 			 * Enforece some bounds on multiplier and the
20200Sstevel@tonic-gate 			 * speed change.
20210Sstevel@tonic-gate 			 */
20220Sstevel@tonic-gate 			multiplier = MIN(multiplier, 3.0);
20230Sstevel@tonic-gate 			speed_change = delta * multiplier *
20240Sstevel@tonic-gate 			    sensorp->fan_adjustment_rate;
20250Sstevel@tonic-gate 			speed_change = MIN(speed_change, fan_incr_limit);
20260Sstevel@tonic-gate 			speed_change = MAX(speed_change, -fan_decr_limit);
20270Sstevel@tonic-gate 			new_speed = cur_speed + speed_change;
20280Sstevel@tonic-gate 
20290Sstevel@tonic-gate 			if (env_debug > 1)
20300Sstevel@tonic-gate 				envd_log(LOG_INFO,
20310Sstevel@tonic-gate 				"sensor: %-8s temp/diff:%d/%3.1f  "
20320Sstevel@tonic-gate 				"target/diff:%d/%3.1f  change:%4.2f x "
20330Sstevel@tonic-gate 				"%4.2f x %4.2f speed %5.2f -> %5.2f\n",
20340Sstevel@tonic-gate 				    sensorp->name, temp, tempdiff,
20350Sstevel@tonic-gate 				    sensorp->target_temp, targetdiff, delta,
20360Sstevel@tonic-gate 				    multiplier, sensorp->fan_adjustment_rate,
20370Sstevel@tonic-gate 				    cur_speed, new_speed);
20380Sstevel@tonic-gate 		} else if (threshp->policy_type == POLICY_LINEAR) {
20390Sstevel@tonic-gate 			/*
20400Sstevel@tonic-gate 			 * Set fan speed linearly within the operating
20410Sstevel@tonic-gate 			 * range specified by the policy_data[LOW_NOMINAL_LOC]
20420Sstevel@tonic-gate 			 * and policy_data[HIGH_NOMINAL_LOC] threshold values.
20430Sstevel@tonic-gate 			 * Fan speed is set to minimum value at LOW_NOMINAL
20440Sstevel@tonic-gate 			 * and to maximum value at HIGH_NOMINAL value.
20450Sstevel@tonic-gate 			 */
20460Sstevel@tonic-gate 			new_speed = min_speed + (max_speed - min_speed) *
20470Sstevel@tonic-gate 			    (avg_temp - threshp->policy_data[LOW_NOMINAL_LOC])/
20480Sstevel@tonic-gate 			    (threshp->policy_data[HIGH_NOMINAL_LOC] -
20490Sstevel@tonic-gate 			    threshp->policy_data[LOW_NOMINAL_LOC]);
20500Sstevel@tonic-gate 			if (env_debug > 1)
20510Sstevel@tonic-gate 				envd_log(LOG_INFO,
20520Sstevel@tonic-gate 				"sensor: %-8s policy: linear, cur_speed %5.2f"\
20530Sstevel@tonic-gate 				" new_speed: %5.2f\n", sensorp->name, cur_speed,
20540Sstevel@tonic-gate 				    new_speed);
20550Sstevel@tonic-gate 		} else {
20560Sstevel@tonic-gate 			new_speed = cur_speed;
20570Sstevel@tonic-gate 		}
20580Sstevel@tonic-gate 		speed = MAX(speed, new_speed);
20590Sstevel@tonic-gate 	}
20600Sstevel@tonic-gate 
20610Sstevel@tonic-gate 	/*
20620Sstevel@tonic-gate 	 * Adjust speed using lpm tables
20630Sstevel@tonic-gate 	 */
20640Sstevel@tonic-gate 	if (amb_cnt > 0) {
20650Sstevel@tonic-gate 		av_ambient = (av_ambient >= 0 ?
20660Sstevel@tonic-gate 			(int)(0.5 + (float)av_ambient/(float)amb_cnt):
20670Sstevel@tonic-gate 			(int)(-0.5 + (float)av_ambient/(float)amb_cnt));
20680Sstevel@tonic-gate 		speed = MAX(speed, (fanspeed_t)get_lpm_speed(devp, av_ambient));
20690Sstevel@tonic-gate 	}
20700Sstevel@tonic-gate 
20710Sstevel@tonic-gate 	speed = MIN(speed, max_speed);
20720Sstevel@tonic-gate 	speed = MAX(speed, min_speed);
20730Sstevel@tonic-gate 
20740Sstevel@tonic-gate 	/*
20750Sstevel@tonic-gate 	 * Record and update fan speed, if different.
20760Sstevel@tonic-gate 	 */
20770Sstevel@tonic-gate 	fanp->prev_speed = fanp->cur_speed;
20780Sstevel@tonic-gate 	fanp->cur_speed = speed;
20790Sstevel@tonic-gate 	if ((fanspeed_t)speed != fanspeed) {
20800Sstevel@tonic-gate 		fanspeed = (fanspeed_t)speed;
20810Sstevel@tonic-gate 		(void) set_fan_speed(fanp, fanspeed);
20820Sstevel@tonic-gate 	}
20830Sstevel@tonic-gate 	if (env_debug)
20840Sstevel@tonic-gate 		envd_log(LOG_INFO,
20850Sstevel@tonic-gate 		    "fan: %-16s speed cur:%6.2f  new:%6.2f\n",
20860Sstevel@tonic-gate 		    fanp->name, fanp->prev_speed, fanp->cur_speed);
20870Sstevel@tonic-gate 
20880Sstevel@tonic-gate 	return (0);
20890Sstevel@tonic-gate }
20900Sstevel@tonic-gate /*
20910Sstevel@tonic-gate  * This is the environment thread, which monitors the current temperature
20920Sstevel@tonic-gate  * and power managed state and controls system fan speed.  Temperature is
20930Sstevel@tonic-gate  * polled every sensor-poll_interval seconds duration.
20940Sstevel@tonic-gate  */
20950Sstevel@tonic-gate /*ARGSUSED*/
20960Sstevel@tonic-gate static void *
envthr(void * args)20970Sstevel@tonic-gate envthr(void *args)
20980Sstevel@tonic-gate {
20990Sstevel@tonic-gate 	env_sensor_t	*sensorp;
21000Sstevel@tonic-gate 	fanspeed_t 	fan_speed;
21010Sstevel@tonic-gate 	env_fan_t	*pmfanp = &envd_psupply_fan;
21020Sstevel@tonic-gate 	int		to;
21030Sstevel@tonic-gate 	int		xwd = -1;
21040Sstevel@tonic-gate 
21050Sstevel@tonic-gate 	for (sensorp = &envd_sensors[0]; sensorp->name != NULL;
21060Sstevel@tonic-gate 	    sensorp++) {
21070Sstevel@tonic-gate 		if (sensorp->obs2exp_map)
21080Sstevel@tonic-gate 			(void) free(sensorp->obs2exp_map);
21090Sstevel@tonic-gate 		sensorp->obs2exp_map = NULL;
21100Sstevel@tonic-gate 		sensorp->obs2exp_cnt = 0;
21110Sstevel@tonic-gate 	}
21120Sstevel@tonic-gate 
21130Sstevel@tonic-gate 	/*
21140Sstevel@tonic-gate 	 * Process environmental segment data, if present,
21150Sstevel@tonic-gate 	 * in the FRU SEEPROM.
21160Sstevel@tonic-gate 	 */
21170Sstevel@tonic-gate 	process_fru_envseg();
21180Sstevel@tonic-gate 
21190Sstevel@tonic-gate 	/*
21200Sstevel@tonic-gate 	 * Process tuneable parameters
21210Sstevel@tonic-gate 	 */
21220Sstevel@tonic-gate 	process_env_conf_file();
21230Sstevel@tonic-gate 
21240Sstevel@tonic-gate 	/*
21250Sstevel@tonic-gate 	 * Setup temperature sensors and fail if we can't open
21260Sstevel@tonic-gate 	 * at least one sensor.
21270Sstevel@tonic-gate 	 */
21280Sstevel@tonic-gate 	if (envd_setup_sensors() <= 0) {
21290Sstevel@tonic-gate 		envd_close_pm();
21300Sstevel@tonic-gate 		return (NULL);
21310Sstevel@tonic-gate 	}
21320Sstevel@tonic-gate 
21330Sstevel@tonic-gate 	to = 3 * sensor_poll_interval + 1;
21340Sstevel@tonic-gate 	xwd = open(XCALWD_DEVFS, O_RDONLY);
21350Sstevel@tonic-gate 	if (xwd < 0) {
21360Sstevel@tonic-gate 		envd_log(LOG_CRIT, ENV_WATCHDOG_INIT_FAIL, errno,
21370Sstevel@tonic-gate 		    strerror(errno));
21380Sstevel@tonic-gate 	} else if (ioctl(xwd, XCALWD_STOPWATCHDOG) < 0 ||
21390Sstevel@tonic-gate 	    ioctl(xwd, XCALWD_STARTWATCHDOG, &to) < 0) {
21400Sstevel@tonic-gate 		envd_log(LOG_CRIT, ENV_WATCHDOG_INIT_FAIL, errno,
21410Sstevel@tonic-gate 		    strerror(errno));
21420Sstevel@tonic-gate 		(void) close(xwd);
21430Sstevel@tonic-gate 		xwd = -1;
21440Sstevel@tonic-gate 	}
21450Sstevel@tonic-gate 
21460Sstevel@tonic-gate 	/*
21470Sstevel@tonic-gate 	 * Setup fan device (don't fail even if we can't access
21480Sstevel@tonic-gate 	 * the fan as we can still monitor temeperature.
21490Sstevel@tonic-gate 	 */
21500Sstevel@tonic-gate 	(void) envd_setup_fans();
21510Sstevel@tonic-gate 
21520Sstevel@tonic-gate 	for (;;) {
21530Sstevel@tonic-gate 		(void) pthread_rwlock_rdlock(&envd_rwlock);
21540Sstevel@tonic-gate 
21550Sstevel@tonic-gate 		/*
21560Sstevel@tonic-gate 		 * If no "pmthr" thread, then we need to update the
21570Sstevel@tonic-gate 		 * current power level for all power managed deviecs
21580Sstevel@tonic-gate 		 * so that we can determine correct target temperature.
21590Sstevel@tonic-gate 		 */
21600Sstevel@tonic-gate 		if (pmthr_exists == B_FALSE)
21610Sstevel@tonic-gate 			(void) update_pmdev_power();
21620Sstevel@tonic-gate 
21630Sstevel@tonic-gate 		if (xwd >= 0)
21640Sstevel@tonic-gate 			(void) ioctl(xwd, XCALWD_KEEPALIVE);
21650Sstevel@tonic-gate 
21660Sstevel@tonic-gate 		if (!disable_piclenvd) {
21670Sstevel@tonic-gate 			/*
21680Sstevel@tonic-gate 			 * Monitor current temperature for all sensors
21690Sstevel@tonic-gate 			 * (current temperature is recorded in the "cur_temp"
21700Sstevel@tonic-gate 			 * field within each sensor data structure)
21710Sstevel@tonic-gate 			 */
21720Sstevel@tonic-gate 			monitor_sensors();
21730Sstevel@tonic-gate 
21740Sstevel@tonic-gate 			/*
21750Sstevel@tonic-gate 			 * Adjust CPU and system fan speed
21760Sstevel@tonic-gate 			 */
21770Sstevel@tonic-gate 			if (envd_cpu_fan.forced_speed < 0)
21780Sstevel@tonic-gate 				(void) adjust_fan_speed(&envd_cpu_fan, NULL);
21790Sstevel@tonic-gate 			if (envd_system_fan.forced_speed < 0)
21800Sstevel@tonic-gate 				(void) adjust_fan_speed(&envd_system_fan,
21810Sstevel@tonic-gate 					lpm_devices);
21820Sstevel@tonic-gate 
21830Sstevel@tonic-gate 			/*
21840Sstevel@tonic-gate 			 * Turn off power supply fan if in lowest power state.
21850Sstevel@tonic-gate 			 */
21860Sstevel@tonic-gate 			fan_speed = (cur_lpstate) ? pmfanp->speed_min :
21870Sstevel@tonic-gate 			    pmfanp->speed_max;
21880Sstevel@tonic-gate 
21890Sstevel@tonic-gate 			if (env_debug)
21900Sstevel@tonic-gate 				envd_log(LOG_INFO,
21910Sstevel@tonic-gate 				"fan: %-16s speed cur:%6.2f  new:%6.2f "
21920Sstevel@tonic-gate 				"low-power:%d\n", pmfanp->name,
21930Sstevel@tonic-gate 				    (float)pmfanp->cur_speed,
21940Sstevel@tonic-gate 				    (float)fan_speed, cur_lpstate);
21950Sstevel@tonic-gate 
21960Sstevel@tonic-gate 			if (fan_speed != (fanspeed_t)pmfanp->cur_speed &&
21970Sstevel@tonic-gate 			    set_fan_speed(pmfanp, fan_speed) == 0)
21980Sstevel@tonic-gate 				pmfanp->cur_speed = fan_speed;
21990Sstevel@tonic-gate 		}
22000Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&envd_rwlock);
22010Sstevel@tonic-gate 
22020Sstevel@tonic-gate 		/*
22030Sstevel@tonic-gate 		 * Wait for sensor_poll_interval seconds before polling
22040Sstevel@tonic-gate 		 * again. Note that we use our own envd_sleep() routine
22050Sstevel@tonic-gate 		 * as sleep() in POSIX thread library gets affected by
22060Sstevel@tonic-gate 		 * the wall clock time being set back.
22070Sstevel@tonic-gate 		 */
22080Sstevel@tonic-gate 		(void) envd_sleep(sensor_poll_interval);
22090Sstevel@tonic-gate 	}
22100Sstevel@tonic-gate 	/*NOTREACHED*/
22110Sstevel@tonic-gate 	return (NULL);
22120Sstevel@tonic-gate }
22130Sstevel@tonic-gate 
22140Sstevel@tonic-gate /*
22150Sstevel@tonic-gate  * This is the power management thread, which monitors all power state
22160Sstevel@tonic-gate  * change events and wakes up the "envthr" thread when the system enters
22170Sstevel@tonic-gate  * or exits the lowest power state.
22180Sstevel@tonic-gate  */
22190Sstevel@tonic-gate /*ARGSUSED*/
22200Sstevel@tonic-gate static void *
pmthr(void * args)22210Sstevel@tonic-gate pmthr(void *args)
22220Sstevel@tonic-gate {
22230Sstevel@tonic-gate 	pm_state_change_t	pmstate;
22240Sstevel@tonic-gate 	char			physpath[PATH_MAX];
22250Sstevel@tonic-gate 
22260Sstevel@tonic-gate 	pmstate.physpath = physpath;
22270Sstevel@tonic-gate 	pmstate.size = sizeof (physpath);
22280Sstevel@tonic-gate 	cur_lpstate = 0;
22290Sstevel@tonic-gate 
22300Sstevel@tonic-gate 	for (;;) {
22310Sstevel@tonic-gate 		/*
22320Sstevel@tonic-gate 		 * Get PM state change events to check if the system
22330Sstevel@tonic-gate 		 * is in lowest power state and wake up the "envthr"
22340Sstevel@tonic-gate 		 * thread when the power state changes.
22350Sstevel@tonic-gate 		 *
22360Sstevel@tonic-gate 		 * To minimize polling, we use the blocking interface
22370Sstevel@tonic-gate 		 * to get the power state change event here.
22380Sstevel@tonic-gate 		 */
22390Sstevel@tonic-gate 		if (ioctl(pm_fd, PM_GET_STATE_CHANGE_WAIT, &pmstate) != 0) {
22400Sstevel@tonic-gate 			if (errno != EINTR)
22410Sstevel@tonic-gate 				break;
22420Sstevel@tonic-gate 			continue;
22430Sstevel@tonic-gate 		}
22440Sstevel@tonic-gate 
22450Sstevel@tonic-gate 		/*
22460Sstevel@tonic-gate 		 * Extract the lowest power state from the last queued
22470Sstevel@tonic-gate 		 * state change events. We pick up queued state change
22480Sstevel@tonic-gate 		 * events using the non-blocking interface and wake up
22490Sstevel@tonic-gate 		 * the "envthr" thread only after consuming all the
22500Sstevel@tonic-gate 		 * state change events queued at that time.
22510Sstevel@tonic-gate 		 */
22520Sstevel@tonic-gate 		do {
22530Sstevel@tonic-gate 			if (env_debug > 1)  {
22540Sstevel@tonic-gate 				envd_log(LOG_INFO,
22550Sstevel@tonic-gate 				"pmstate event:0x%x flags:%x comp:%d "
22560Sstevel@tonic-gate 				"oldval:%d newval:%d path:%s\n",
22570Sstevel@tonic-gate 				    pmstate.event, pmstate.flags,
22580Sstevel@tonic-gate 				    pmstate.component, pmstate.old_level,
22590Sstevel@tonic-gate 				    pmstate.new_level, pmstate.physpath);
22600Sstevel@tonic-gate 			}
22610Sstevel@tonic-gate 			cur_lpstate =
22620Sstevel@tonic-gate 			    (pmstate.flags & PSC_ALL_LOWEST) ? 1 : 0;
22630Sstevel@tonic-gate 		} while (ioctl(pm_fd, PM_GET_STATE_CHANGE, &pmstate) == 0);
22640Sstevel@tonic-gate 
22650Sstevel@tonic-gate 		/*
22660Sstevel@tonic-gate 		 * Update current PM state for the components we are
22670Sstevel@tonic-gate 		 * tracking. In case of CPU devices, PM state change
22680Sstevel@tonic-gate 		 * event can be generated even before the state change
22690Sstevel@tonic-gate 		 * takes effect, hence we need to get the current state
22700Sstevel@tonic-gate 		 * for all CPU devices every time and recalculate the
22710Sstevel@tonic-gate 		 * target temperature. We do this once after consuming
22720Sstevel@tonic-gate 		 * all the queued events.
22730Sstevel@tonic-gate 		 */
22740Sstevel@tonic-gate 
22750Sstevel@tonic-gate 		(void) pthread_rwlock_rdlock(&envd_rwlock);
22760Sstevel@tonic-gate 		(void) update_pmdev_power();
22770Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&envd_rwlock);
22780Sstevel@tonic-gate 	}
22790Sstevel@tonic-gate 
22800Sstevel@tonic-gate 	/*
22810Sstevel@tonic-gate 	 * We won't be able to monitor lowest power state any longer,
22820Sstevel@tonic-gate 	 * hence reset it.
22830Sstevel@tonic-gate 	 */
22840Sstevel@tonic-gate 	cur_lpstate = 0;
22850Sstevel@tonic-gate 	envd_log(LOG_ERR, PM_THREAD_EXITING, errno, strerror(errno));
22860Sstevel@tonic-gate 	pmthr_exists = B_FALSE;
22870Sstevel@tonic-gate 	return (NULL);
22880Sstevel@tonic-gate }
22890Sstevel@tonic-gate 
22900Sstevel@tonic-gate 
22910Sstevel@tonic-gate /*
22920Sstevel@tonic-gate  * Process sensor threshold related tuneables
22930Sstevel@tonic-gate  */
22940Sstevel@tonic-gate static int
process_threshold_tuneable(char * keyword,char * buf,void * dummy_thresh_addr,int flags,char * fname,int line)22950Sstevel@tonic-gate process_threshold_tuneable(char *keyword, char *buf, void *dummy_thresh_addr,
22960Sstevel@tonic-gate     int flags, char *fname, int line)
22970Sstevel@tonic-gate {
22980Sstevel@tonic-gate 	int		retval = 0;
22990Sstevel@tonic-gate 	long		val;
23000Sstevel@tonic-gate 	void		*addr;
23010Sstevel@tonic-gate 	char		*endp, *sname;
23020Sstevel@tonic-gate 	env_sensor_t	*sensorp;
23030Sstevel@tonic-gate 
23040Sstevel@tonic-gate 	/*
23050Sstevel@tonic-gate 	 * Tuneable entry can be in one of the following formats:
23060Sstevel@tonic-gate 	 *
23070Sstevel@tonic-gate 	 *	threshold-keyword <int-value>
23080Sstevel@tonic-gate 	 *	threshold-keyword <int-value> <sensor-name> ...
23090Sstevel@tonic-gate 	 *
23100Sstevel@tonic-gate 	 * Convert threshold value into integer value and check for
23110Sstevel@tonic-gate 	 * optional sensor name. If no sensor name is specified, then
23120Sstevel@tonic-gate 	 * the tuneable applies to all sensors specified by the "flags".
23130Sstevel@tonic-gate 	 * Otherwise, it is applicable to the specified sensors.
23140Sstevel@tonic-gate 	 *
23150Sstevel@tonic-gate 	 * Note that the dummy_thresh_addr is the address of the threshold
23160Sstevel@tonic-gate 	 * to be changed and is converted into offset by subtracting the
23170Sstevel@tonic-gate 	 * base dummy_thresh address. This offset is added to the base
23180Sstevel@tonic-gate 	 * address of the threshold structure to be update to determine
23190Sstevel@tonic-gate 	 * the final memory address to be modified.
23200Sstevel@tonic-gate 	 */
23210Sstevel@tonic-gate 
23220Sstevel@tonic-gate 	errno = 0;
23230Sstevel@tonic-gate 	val = strtol(buf, &endp, 0);
23240Sstevel@tonic-gate 	sname = strtok(endp, tokdel);
23250Sstevel@tonic-gate 
23260Sstevel@tonic-gate 	if (errno != 0 || val != (tempr_t)val) {
23270Sstevel@tonic-gate 		retval = -1;
23280Sstevel@tonic-gate 		envd_log(LOG_INFO, ENV_CONF_INT_EXPECTED, fname, line, keyword);
23290Sstevel@tonic-gate 	} else if (flags == 0 && sname == NULL) {
23300Sstevel@tonic-gate 		envd_log(LOG_INFO, "SUNW_piclenvd: file:%s line:%d SKIPPED"
23310Sstevel@tonic-gate 		    " as no sensor specified.\n", fname, line, keyword);
23320Sstevel@tonic-gate 		retval = -1;
23330Sstevel@tonic-gate 	} else if (sname == NULL) {
23340Sstevel@tonic-gate 		int	cnt = 0;
23350Sstevel@tonic-gate 
23360Sstevel@tonic-gate 		for (sensorp = &envd_sensors[0]; sensorp->name; sensorp++) {
23370Sstevel@tonic-gate 			if (sensorp->temp_thresh == NULL ||
23380Sstevel@tonic-gate 			    (sensorp->flags & flags) == 0)
23390Sstevel@tonic-gate 				continue;
23400Sstevel@tonic-gate 
23410Sstevel@tonic-gate 			/*
23420Sstevel@tonic-gate 			 * Convert dummy_thresh_addr into memory address
23430Sstevel@tonic-gate 			 * for this sensor threshold values.
23440Sstevel@tonic-gate 			 */
23450Sstevel@tonic-gate 			addr = (char *)sensorp->temp_thresh +
23460Sstevel@tonic-gate 			    (int)((char *)dummy_thresh_addr -
23470Sstevel@tonic-gate 			    (char *)&dummy_thresh);
23480Sstevel@tonic-gate 
23490Sstevel@tonic-gate 			*(tempr_t *)addr = (tempr_t)val;
23500Sstevel@tonic-gate 			cnt++;
23510Sstevel@tonic-gate 			if (env_debug)
23520Sstevel@tonic-gate 				envd_log(LOG_INFO, "SUNW_piclenvd: file:%s "
23530Sstevel@tonic-gate 				"line:%d %s = %d for sensor: '%s'\n",
23540Sstevel@tonic-gate 				    fname, line, keyword, val, sensorp->name);
23550Sstevel@tonic-gate 		}
23560Sstevel@tonic-gate 		if (cnt == 0)
23570Sstevel@tonic-gate 			envd_log(LOG_INFO, "SUNW_piclenvd: file:%s line:%d "
23580Sstevel@tonic-gate 			"%s SKIPPED as no matching sensor found.\n",
23590Sstevel@tonic-gate 			    fname, line, keyword);
23600Sstevel@tonic-gate 	} else {
23610Sstevel@tonic-gate 		/* apply threshold value to the specified sensors */
23620Sstevel@tonic-gate 		do {
23630Sstevel@tonic-gate 			sensorp = sensor_lookup(sname);
23640Sstevel@tonic-gate 			if (sensorp == NULL || sensorp->temp_thresh == NULL ||
23650Sstevel@tonic-gate 			    (flags && (sensorp->flags & flags) == 0)) {
23660Sstevel@tonic-gate 				envd_log(LOG_INFO,
23670Sstevel@tonic-gate 				"SUNW_piclenvd: file:%s line:%d %s SKIPPED"
23680Sstevel@tonic-gate 				" for '%s' as not a valid sensor.\n",
23690Sstevel@tonic-gate 				    fname, line, keyword, sname);
23700Sstevel@tonic-gate 				continue;
23710Sstevel@tonic-gate 			}
23720Sstevel@tonic-gate 			/*
23730Sstevel@tonic-gate 			 * Convert dummy_thresh_addr into memory address
23740Sstevel@tonic-gate 			 * for this sensor threshold values.
23750Sstevel@tonic-gate 			 */
23760Sstevel@tonic-gate 			addr = (char *)sensorp->temp_thresh +
23770Sstevel@tonic-gate 			    (int)((char *)dummy_thresh_addr -
23780Sstevel@tonic-gate 			    (char *)&dummy_thresh);
23790Sstevel@tonic-gate 
23800Sstevel@tonic-gate 			*(tempr_t *)addr = (tempr_t)val;
23810Sstevel@tonic-gate 			if (env_debug)
23820Sstevel@tonic-gate 				envd_log(LOG_INFO, "SUNW_piclenvd: file:%s "
23830Sstevel@tonic-gate 				"line:%d %s = %d for sensor: '%s'\n",
23840Sstevel@tonic-gate 				    fname, line, keyword, val, sensorp->name);
23850Sstevel@tonic-gate 		} while ((sname = strtok(NULL, tokdel)) != NULL);
23860Sstevel@tonic-gate 	}
23870Sstevel@tonic-gate 	return (retval);
23880Sstevel@tonic-gate }
23890Sstevel@tonic-gate 
23900Sstevel@tonic-gate 
23910Sstevel@tonic-gate /*
23920Sstevel@tonic-gate  * Process integer tuneables
23930Sstevel@tonic-gate  */
23940Sstevel@tonic-gate static int
process_int_tuneable(char * keyword,char * buf,void * addr,int size,char * fname,int line)23950Sstevel@tonic-gate process_int_tuneable(char *keyword, char *buf, void *addr, int size,
23960Sstevel@tonic-gate     char *fname, int line)
23970Sstevel@tonic-gate {
23980Sstevel@tonic-gate 	int	retval = 0;
23990Sstevel@tonic-gate 	char	*endp;
24000Sstevel@tonic-gate 	long	val;
24010Sstevel@tonic-gate 
24020Sstevel@tonic-gate 	/*
24030Sstevel@tonic-gate 	 * Convert input into integer value and ensure that there is
24040Sstevel@tonic-gate 	 * no other token in the buffer.
24050Sstevel@tonic-gate 	 */
24060Sstevel@tonic-gate 	errno = 0;
24070Sstevel@tonic-gate 	val = strtol(buf, &endp, 0);
24080Sstevel@tonic-gate 	if (errno != 0 || strtok(endp, tokdel) != NULL)
24090Sstevel@tonic-gate 		retval = -1;
24100Sstevel@tonic-gate 	else {
24110Sstevel@tonic-gate 		switch (size) {
24120Sstevel@tonic-gate 		case 1:
24130Sstevel@tonic-gate 			if (val != (int8_t)val)
24140Sstevel@tonic-gate 				retval = -1;
24150Sstevel@tonic-gate 			else
24160Sstevel@tonic-gate 				*(int8_t *)addr = (int8_t)val;
24170Sstevel@tonic-gate 			break;
24180Sstevel@tonic-gate 		case 2:
24190Sstevel@tonic-gate 			if (val != (short)val)
24200Sstevel@tonic-gate 				retval = -1;
24210Sstevel@tonic-gate 			else
24220Sstevel@tonic-gate 				*(short *)addr = (short)val;
24230Sstevel@tonic-gate 			break;
24240Sstevel@tonic-gate 		case 4:
24250Sstevel@tonic-gate 			*(int *)addr = (int)val;
24260Sstevel@tonic-gate 			break;
24270Sstevel@tonic-gate 		default:
24280Sstevel@tonic-gate 			retval = -1;
24290Sstevel@tonic-gate 		}
24300Sstevel@tonic-gate 	}
24310Sstevel@tonic-gate 
24320Sstevel@tonic-gate 	if (retval == -1)
24330Sstevel@tonic-gate 		envd_log(LOG_INFO, ENV_CONF_INT_EXPECTED,
24340Sstevel@tonic-gate 		    fname, line, keyword);
24350Sstevel@tonic-gate 	else if (env_debug)
24360Sstevel@tonic-gate 		envd_log(LOG_INFO, "SUNW_piclenvd: file:%s line:%d %s = %d\n",
24370Sstevel@tonic-gate 		    fname, line, keyword, val);
24380Sstevel@tonic-gate 
24390Sstevel@tonic-gate 	return (retval);
24400Sstevel@tonic-gate }
24410Sstevel@tonic-gate 
24420Sstevel@tonic-gate 
24430Sstevel@tonic-gate /*
24440Sstevel@tonic-gate  * Process string tuneables
24450Sstevel@tonic-gate  *
24460Sstevel@tonic-gate  * String value must be within double quotes.  Skip over initial white
24470Sstevel@tonic-gate  * spaces before looking for string value.
24480Sstevel@tonic-gate  */
24490Sstevel@tonic-gate static int
process_string_tuneable(char * keyword,char * buf,void * addr,int size,char * fname,int line)24500Sstevel@tonic-gate process_string_tuneable(char *keyword, char *buf, void *addr, int size,
24510Sstevel@tonic-gate     char *fname, int line)
24520Sstevel@tonic-gate {
24530Sstevel@tonic-gate 	int	retval = 0;
24540Sstevel@tonic-gate 	char	c, *p, *strend;
24550Sstevel@tonic-gate 
24560Sstevel@tonic-gate 	/* Skip over white spaces */
24570Sstevel@tonic-gate 	buf += strspn(buf, tokdel);
24580Sstevel@tonic-gate 
24590Sstevel@tonic-gate 	/*
24600Sstevel@tonic-gate 	 * Parse srting and locate string end (handling escaped double quotes
24610Sstevel@tonic-gate 	 * and other characters)
24620Sstevel@tonic-gate 	 */
24630Sstevel@tonic-gate 	if (buf[0] != '"')
24640Sstevel@tonic-gate 		strend = NULL;
24650Sstevel@tonic-gate 	else {
24660Sstevel@tonic-gate 		for (p = buf+1; (c = *p) != '\0'; p++)
24670Sstevel@tonic-gate 			if (c == '"' || (c == '\\' && *++p == '\0'))
24680Sstevel@tonic-gate 				break;
24690Sstevel@tonic-gate 		strend = (*p == '"') ? p : NULL;
24700Sstevel@tonic-gate 	}
24710Sstevel@tonic-gate 
24720Sstevel@tonic-gate 	if (strend == NULL || (strend-buf) > size ||
24730Sstevel@tonic-gate 	    strtok(strend+1, tokdel) != NULL) {
24740Sstevel@tonic-gate 		envd_log(LOG_WARNING, ENV_CONF_STRING_EXPECTED,
24750Sstevel@tonic-gate 		    fname, line, keyword, size);
24760Sstevel@tonic-gate 		retval = -1;
24770Sstevel@tonic-gate 	} else {
24780Sstevel@tonic-gate 		*strend = '\0';
24790Sstevel@tonic-gate 		(void) strcpy(addr, (caddr_t)buf+1);
24800Sstevel@tonic-gate 		if (env_debug)
24810Sstevel@tonic-gate 			envd_log(LOG_INFO, "SUNW_piclenvd: file:%s line:%d "
24820Sstevel@tonic-gate 			    "%s = \"%s\"\n", fname, line, keyword, buf+1);
24830Sstevel@tonic-gate 	}
24840Sstevel@tonic-gate 
24850Sstevel@tonic-gate 	return (retval);
24860Sstevel@tonic-gate }
24870Sstevel@tonic-gate 
24880Sstevel@tonic-gate 
24890Sstevel@tonic-gate /*
24900Sstevel@tonic-gate  * Process configuration file
24910Sstevel@tonic-gate  */
24920Sstevel@tonic-gate static void
process_env_conf_file(void)24930Sstevel@tonic-gate process_env_conf_file(void)
24940Sstevel@tonic-gate {
24950Sstevel@tonic-gate 	int		line, len, toklen;
24960Sstevel@tonic-gate 	char		buf[BUFSIZ];
24970Sstevel@tonic-gate 	FILE		*fp;
24980Sstevel@tonic-gate 	env_tuneable_t	*tunep;
24990Sstevel@tonic-gate 	char		nmbuf[SYS_NMLN];
25000Sstevel@tonic-gate 	char		fname[PATH_MAX];
25010Sstevel@tonic-gate 	char		*tok, *valuep;
25020Sstevel@tonic-gate 	int		skip_line = 0;
25030Sstevel@tonic-gate 
25040Sstevel@tonic-gate 	if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) == -1)
25050Sstevel@tonic-gate 		return;
25060Sstevel@tonic-gate 
25070Sstevel@tonic-gate 	(void) snprintf(fname, sizeof (fname), PICLD_PLAT_PLUGIN_DIRF, nmbuf);
25080Sstevel@tonic-gate 	(void) strlcat(fname, ENV_CONF_FILE, sizeof (fname));
25090Sstevel@tonic-gate 	fp = fopen(fname, "r");
25100Sstevel@tonic-gate 	if (fp == NULL)
25110Sstevel@tonic-gate 		return;
25120Sstevel@tonic-gate 
25130Sstevel@tonic-gate 	/*
25140Sstevel@tonic-gate 	 * Blank lines or lines starting with "#" or "*" in the first
25150Sstevel@tonic-gate 	 * column are ignored. All other lines are assumed to contain
25160Sstevel@tonic-gate 	 * input in the following format:
25170Sstevel@tonic-gate 	 *
25180Sstevel@tonic-gate 	 *	keyword value
25190Sstevel@tonic-gate 	 *
25200Sstevel@tonic-gate 	 * where the "value" can be a signed integer or string (in
25210Sstevel@tonic-gate 	 * double quotes) depending upon the keyword.
25220Sstevel@tonic-gate 	 */
25230Sstevel@tonic-gate 
25240Sstevel@tonic-gate 	for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) {
25250Sstevel@tonic-gate 		len = strlen(buf);
25260Sstevel@tonic-gate 		if (len <= 0)
25270Sstevel@tonic-gate 			continue;
25280Sstevel@tonic-gate 
25290Sstevel@tonic-gate 		/* skip long lines */
25300Sstevel@tonic-gate 		if (buf[len-1] != '\n') {
25310Sstevel@tonic-gate 			skip_line = 1;
25320Sstevel@tonic-gate 			continue;
25330Sstevel@tonic-gate 		} else if (skip_line) {
25340Sstevel@tonic-gate 			skip_line = 0;
25350Sstevel@tonic-gate 			continue;
25360Sstevel@tonic-gate 		} else
25370Sstevel@tonic-gate 			buf[len-1] = '\0';
25380Sstevel@tonic-gate 
25390Sstevel@tonic-gate 		/* skip comments */
25400Sstevel@tonic-gate 		if (buf[0] == '*' || buf[0] == '#')
25410Sstevel@tonic-gate 			continue;
25420Sstevel@tonic-gate 
25430Sstevel@tonic-gate 		/*
25440Sstevel@tonic-gate 		 * Skip over white space to get the keyword
25450Sstevel@tonic-gate 		 */
25460Sstevel@tonic-gate 		tok = buf + strspn(buf, tokdel);
25470Sstevel@tonic-gate 		if (*tok == '\0')
25480Sstevel@tonic-gate 			continue;			/* blank line */
25490Sstevel@tonic-gate 
25500Sstevel@tonic-gate 		toklen = strcspn(tok, tokdel);
25510Sstevel@tonic-gate 		tok[toklen] = '\0';
25520Sstevel@tonic-gate 
25530Sstevel@tonic-gate 		/* Get possible location for value (within current line) */
25540Sstevel@tonic-gate 		valuep = tok + toklen + 1;
25550Sstevel@tonic-gate 		if (valuep > buf+len)
25560Sstevel@tonic-gate 			valuep = buf + len;
25570Sstevel@tonic-gate 
25580Sstevel@tonic-gate 		/*
25590Sstevel@tonic-gate 		 * Lookup the keyword and process value accordingly
25600Sstevel@tonic-gate 		 */
25610Sstevel@tonic-gate 		for (tunep = &env_tuneables[0]; tunep->name != NULL; tunep++) {
25620Sstevel@tonic-gate 			if (strcasecmp(tunep->name, tok) == 0) {
25630Sstevel@tonic-gate 				(void) (*tunep->func)(tok, valuep,
25640Sstevel@tonic-gate 				    tunep->arg1, tunep->arg2, fname, line);
25650Sstevel@tonic-gate 				break;
25660Sstevel@tonic-gate 			}
25670Sstevel@tonic-gate 		}
25680Sstevel@tonic-gate 
25690Sstevel@tonic-gate 		if (tunep->name == NULL)
25700Sstevel@tonic-gate 			envd_log(LOG_INFO, ENV_CONF_UNSUPPORTED_KEYWORD,
25710Sstevel@tonic-gate 			    fname, line, tok);
25720Sstevel@tonic-gate 	}
25730Sstevel@tonic-gate 	(void) fclose(fp);
25740Sstevel@tonic-gate }
25750Sstevel@tonic-gate 
25760Sstevel@tonic-gate /*
25770Sstevel@tonic-gate  * Setup envrionmental monitor state and start threads to monitor
25780Sstevel@tonic-gate  * temperature and power management state.
25790Sstevel@tonic-gate  * Returns -1 on error, 0 if successful.
25800Sstevel@tonic-gate  */
25810Sstevel@tonic-gate 
25820Sstevel@tonic-gate static int
envd_setup(void)25830Sstevel@tonic-gate envd_setup(void)
25840Sstevel@tonic-gate {
25850Sstevel@tonic-gate 	char		*valp, *endp;
25860Sstevel@tonic-gate 	int		val;
25870Sstevel@tonic-gate 	int		err;
25880Sstevel@tonic-gate 
25890Sstevel@tonic-gate 	if (pthread_attr_init(&thr_attr) != 0 ||
25900Sstevel@tonic-gate 	    pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM) != 0)
25910Sstevel@tonic-gate 		return (-1);
25920Sstevel@tonic-gate 
25930Sstevel@tonic-gate 	if (pm_fd == -1)
25940Sstevel@tonic-gate 		envd_open_pm();
25950Sstevel@tonic-gate 
25960Sstevel@tonic-gate 	/*
25970Sstevel@tonic-gate 	 * Setup lpm devices
25980Sstevel@tonic-gate 	 */
25990Sstevel@tonic-gate 	lpm_devices = NULL;
26000Sstevel@tonic-gate 	if ((err = setup_lpm_devices(&lpm_devices)) != PICL_SUCCESS) {
26010Sstevel@tonic-gate 		if (env_debug)
26020Sstevel@tonic-gate 			envd_log(LOG_ERR, "setup_lpm_devices failed err = %d\n",
26030Sstevel@tonic-gate 				err);
26040Sstevel@tonic-gate 	}
26050Sstevel@tonic-gate 
26060Sstevel@tonic-gate 	/*
26070Sstevel@tonic-gate 	 * Initialize global state to initial startup values
26080Sstevel@tonic-gate 	 */
26090Sstevel@tonic-gate 	sensor_poll_interval = SENSOR_POLL_INTERVAL;
26100Sstevel@tonic-gate 	fan_slow_adjustment = FAN_SLOW_ADJUSTMENT;
26110Sstevel@tonic-gate 	fan_incr_limit = FAN_INCREMENT_LIMIT;
26120Sstevel@tonic-gate 	fan_decr_limit = FAN_DECREMENT_LIMIT;
26130Sstevel@tonic-gate 	warning_interval = WARNING_INTERVAL;
26140Sstevel@tonic-gate 	warning_duration = WARNING_DURATION;
26150Sstevel@tonic-gate 	shutdown_interval = SHUTDOWN_INTERVAL;
26160Sstevel@tonic-gate 	disable_piclenvd = 0;
26170Sstevel@tonic-gate 	disable_power_off = 0;
26180Sstevel@tonic-gate 	disable_shutdown = 0;
26190Sstevel@tonic-gate 	disable_warning = 0;
26200Sstevel@tonic-gate 
26210Sstevel@tonic-gate 	(void) strlcpy(shutdown_cmd, SHUTDOWN_CMD, sizeof (shutdown_cmd));
26220Sstevel@tonic-gate 	(void) strlcpy(devfsadm_cmd, DEVFSADM_CMD, sizeof (devfsadm_cmd));
26230Sstevel@tonic-gate 	(void) strlcpy(fru_devfsadm_cmd, FRU_DEVFSADM_CMD,
26240Sstevel@tonic-gate 	    sizeof (fru_devfsadm_cmd));
26250Sstevel@tonic-gate 	envd_cpu_fan.forced_speed = -1;
26260Sstevel@tonic-gate 	envd_system_fan.forced_speed = -1;
26270Sstevel@tonic-gate 
26280Sstevel@tonic-gate 	(void) memcpy(&cpu0_die_thresh, &cpu_die_thresh_default,
26290Sstevel@tonic-gate 	    sizeof (cpu_die_thresh_default));
26300Sstevel@tonic-gate 	(void) memcpy(&cpu0_amb_thresh, &cpu_amb_thresh_default,
26310Sstevel@tonic-gate 	    sizeof (cpu_amb_thresh_default));
26320Sstevel@tonic-gate 	(void) memcpy(&cpu1_die_thresh, &cpu_die_thresh_default,
26330Sstevel@tonic-gate 	    sizeof (cpu_die_thresh_default));
26340Sstevel@tonic-gate 	(void) memcpy(&cpu1_amb_thresh, &cpu_amb_thresh_default,
26350Sstevel@tonic-gate 	    sizeof (cpu_amb_thresh_default));
26360Sstevel@tonic-gate 
26370Sstevel@tonic-gate 	if ((valp = getenv("SUNW_piclenvd_debug")) != NULL) {
26380Sstevel@tonic-gate 		val = strtol(valp, &endp, 0);
26390Sstevel@tonic-gate 		if (strtok(endp, tokdel) == NULL)
26400Sstevel@tonic-gate 			env_debug = val;
26410Sstevel@tonic-gate 	}
26420Sstevel@tonic-gate 
26430Sstevel@tonic-gate 	/*
26440Sstevel@tonic-gate 	 * Create a thread to monitor temperature and control fan
26450Sstevel@tonic-gate 	 * speed.
26460Sstevel@tonic-gate 	 */
26470Sstevel@tonic-gate 	if (envthr_created == B_FALSE && pthread_create(&envthr_tid,
26480Sstevel@tonic-gate 	    &thr_attr, envthr, (void *)NULL) != 0) {
26490Sstevel@tonic-gate 		envd_close_fans();
26500Sstevel@tonic-gate 		envd_close_sensors();
26510Sstevel@tonic-gate 		envd_close_pm();
26520Sstevel@tonic-gate 		envd_log(LOG_CRIT, ENV_THREAD_CREATE_FAILED);
26530Sstevel@tonic-gate 		return (-1);
26540Sstevel@tonic-gate 	}
26550Sstevel@tonic-gate 	envthr_created = B_TRUE;
26560Sstevel@tonic-gate 
26570Sstevel@tonic-gate 	/*
26580Sstevel@tonic-gate 	 * Create a thread to monitor PM state
26590Sstevel@tonic-gate 	 */
26600Sstevel@tonic-gate 	if (pmthr_exists == B_FALSE) {
26610Sstevel@tonic-gate 		if (pm_fd == -1 || pthread_create(&pmthr_tid, &thr_attr,
26620Sstevel@tonic-gate 		    pmthr, (void *)NULL) != 0) {
26630Sstevel@tonic-gate 			envd_log(LOG_CRIT, PM_THREAD_CREATE_FAILED);
26640Sstevel@tonic-gate 		} else
26650Sstevel@tonic-gate 			pmthr_exists = B_TRUE;
26660Sstevel@tonic-gate 	}
26670Sstevel@tonic-gate 	return (0);
26680Sstevel@tonic-gate }
26690Sstevel@tonic-gate 
26700Sstevel@tonic-gate /*
26710Sstevel@tonic-gate  * Callback function used by ptree_walk_tree_by_class for the cpu class
26720Sstevel@tonic-gate  */
26730Sstevel@tonic-gate static int
cb_cpu(picl_nodehdl_t nodeh,void * args)26740Sstevel@tonic-gate cb_cpu(picl_nodehdl_t nodeh, void *args)
26750Sstevel@tonic-gate {
26760Sstevel@tonic-gate 	sensor_pmdev_t		*pmdevp;
26770Sstevel@tonic-gate 	int			err;
26780Sstevel@tonic-gate 	ptree_propinfo_t	pinfo;
26790Sstevel@tonic-gate 	picl_prophdl_t		proph;
26800Sstevel@tonic-gate 	size_t			psize;
26810Sstevel@tonic-gate 	int			id;
26820Sstevel@tonic-gate 
26830Sstevel@tonic-gate 	/* Get CPU's ID, it is an int */
26840Sstevel@tonic-gate 	err = ptree_get_propval_by_name(nodeh, PICL_PROP_ID, &id, sizeof (int));
26850Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
26860Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
26870Sstevel@tonic-gate 
26880Sstevel@tonic-gate 	/* Get the pmdevp for the CPU */
26890Sstevel@tonic-gate 	pmdevp = sensor_pmdevs;
26900Sstevel@tonic-gate 	while (pmdevp->sensor_id != -1) {
26910Sstevel@tonic-gate 		if (id == pmdevp->sensor_id)
26920Sstevel@tonic-gate 			break;
26930Sstevel@tonic-gate 		pmdevp++;
26940Sstevel@tonic-gate 	}
26950Sstevel@tonic-gate 
26960Sstevel@tonic-gate 	/* Return if didn't find the pmdevp for the cpu id */
26970Sstevel@tonic-gate 	if (pmdevp->sensor_id == -1)
26980Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
26990Sstevel@tonic-gate 
27000Sstevel@tonic-gate 	/* Get the devfs-path property */
27010Sstevel@tonic-gate 	err = ptree_get_prop_by_name(nodeh, PICL_PROP_DEVFS_PATH, &proph);
27020Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
27030Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
27040Sstevel@tonic-gate 
27050Sstevel@tonic-gate 	err = ptree_get_propinfo(proph, &pinfo);
27060Sstevel@tonic-gate 	if ((err != PICL_SUCCESS) ||
27070Sstevel@tonic-gate 	    (pinfo.piclinfo.type != PICL_PTYPE_CHARSTRING))
27080Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
27090Sstevel@tonic-gate 
27100Sstevel@tonic-gate 	psize = pinfo.piclinfo.size;
27110Sstevel@tonic-gate 	pmdevp->pmdev_name = malloc(psize);
27120Sstevel@tonic-gate 	if (pmdevp->pmdev_name == NULL)
27130Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
27140Sstevel@tonic-gate 
27150Sstevel@tonic-gate 	err = ptree_get_propval(proph, pmdevp->pmdev_name, psize);
27160Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
27170Sstevel@tonic-gate 		return (PICL_WALK_CONTINUE);
27180Sstevel@tonic-gate 
27190Sstevel@tonic-gate 	return (PICL_WALK_CONTINUE);
27200Sstevel@tonic-gate }
27210Sstevel@tonic-gate 
27220Sstevel@tonic-gate /*
27230Sstevel@tonic-gate  * Find the CPU's in the picl tree, set the devfs-path for pmdev_name
27240Sstevel@tonic-gate  */
27250Sstevel@tonic-gate static void
setup_pmdev_names()27260Sstevel@tonic-gate setup_pmdev_names()
27270Sstevel@tonic-gate {
27280Sstevel@tonic-gate 	picl_nodehdl_t	plath;
27290Sstevel@tonic-gate 	int		err;
27300Sstevel@tonic-gate 
27310Sstevel@tonic-gate 	err = ptree_get_node_by_path(PLATFORM_PATH, &plath);
27320Sstevel@tonic-gate 	if (err != PICL_SUCCESS)
27330Sstevel@tonic-gate 		return;
27340Sstevel@tonic-gate 
27350Sstevel@tonic-gate 	err = ptree_walk_tree_by_class(plath, PICL_CLASS_CPU, NULL, cb_cpu);
27360Sstevel@tonic-gate }
27370Sstevel@tonic-gate 
27380Sstevel@tonic-gate 
27390Sstevel@tonic-gate static void
piclenvd_register(void)27400Sstevel@tonic-gate piclenvd_register(void)
27410Sstevel@tonic-gate {
27420Sstevel@tonic-gate 	picld_plugin_register(&my_reg_info);
27430Sstevel@tonic-gate }
27440Sstevel@tonic-gate 
27450Sstevel@tonic-gate static void
piclenvd_init(void)27460Sstevel@tonic-gate piclenvd_init(void)
27470Sstevel@tonic-gate {
27480Sstevel@tonic-gate 	/*
27490Sstevel@tonic-gate 	 * Setup the names for the pm sensors, we do it just the first time
27500Sstevel@tonic-gate 	 */
27510Sstevel@tonic-gate 	if (pmdev_names_init == B_FALSE) {
27520Sstevel@tonic-gate 		(void) setup_pmdev_names();
27530Sstevel@tonic-gate 		pmdev_names_init = B_TRUE;
27540Sstevel@tonic-gate 	}
27550Sstevel@tonic-gate 
27560Sstevel@tonic-gate 	/*
27570Sstevel@tonic-gate 	 * Start environmental monitor/threads
27580Sstevel@tonic-gate 	 */
27590Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&envd_rwlock);
27600Sstevel@tonic-gate 	if (envd_setup() != 0) {
27610Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&envd_rwlock);
27620Sstevel@tonic-gate 		envd_log(LOG_CRIT, ENVD_PLUGIN_INIT_FAILED);
27630Sstevel@tonic-gate 		return;
27640Sstevel@tonic-gate 	}
27650Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&envd_rwlock);
27660Sstevel@tonic-gate 
27670Sstevel@tonic-gate 	/*
27680Sstevel@tonic-gate 	 * Now setup/populate PICL tree
27690Sstevel@tonic-gate 	 */
27700Sstevel@tonic-gate 	env_picl_setup();
27710Sstevel@tonic-gate }
27720Sstevel@tonic-gate 
27730Sstevel@tonic-gate static void
piclenvd_fini(void)27740Sstevel@tonic-gate piclenvd_fini(void)
27750Sstevel@tonic-gate {
27760Sstevel@tonic-gate 	/*
27770Sstevel@tonic-gate 	 * Delete the lpm device list. After this the lpm information
27780Sstevel@tonic-gate 	 * will not be used in determining the fan speed, till the lpm
27790Sstevel@tonic-gate 	 * device information is initialized by setup_lpm_devices called
27800Sstevel@tonic-gate 	 * by envd_setup.
27810Sstevel@tonic-gate 	 */
27820Sstevel@tonic-gate 	delete_lpm_devices();
27830Sstevel@tonic-gate 
27840Sstevel@tonic-gate 	/*
27850Sstevel@tonic-gate 	 * Invoke env_picl_destroy() to remove any PICL nodes/properties
27860Sstevel@tonic-gate 	 * (including volatile properties) we created. Once this call
27870Sstevel@tonic-gate 	 * returns, there can't be any more calls from the PICL framework
27880Sstevel@tonic-gate 	 * to get current temperature or fan speed.
27890Sstevel@tonic-gate 	 */
27900Sstevel@tonic-gate 	env_picl_destroy();
27910Sstevel@tonic-gate 
27920Sstevel@tonic-gate 	/*
27930Sstevel@tonic-gate 	 * Since this is a critical plug-in, we know that it won't be
27940Sstevel@tonic-gate 	 * unloaded and will be reinited again unless picld process is
27950Sstevel@tonic-gate 	 * going away. Therefore, it's okay to let "envthr" and "pmthr"
27960Sstevel@tonic-gate 	 * continue so that we can monitor the environment during SIGHUP
27970Sstevel@tonic-gate 	 * handling also.
27980Sstevel@tonic-gate 	 */
27990Sstevel@tonic-gate }
28000Sstevel@tonic-gate 
28010Sstevel@tonic-gate /*VARARGS2*/
28020Sstevel@tonic-gate void
envd_log(int pri,const char * fmt,...)28030Sstevel@tonic-gate envd_log(int pri, const char *fmt, ...)
28040Sstevel@tonic-gate {
28050Sstevel@tonic-gate 	va_list	ap;
28060Sstevel@tonic-gate 
28070Sstevel@tonic-gate 	va_start(ap, fmt);
28080Sstevel@tonic-gate 	vsyslog(pri, fmt, ap);
28090Sstevel@tonic-gate 	va_end(ap);
28100Sstevel@tonic-gate }
28110Sstevel@tonic-gate 
28120Sstevel@tonic-gate #ifdef __lint
28130Sstevel@tonic-gate /*
28140Sstevel@tonic-gate  * Redefine sigwait to posix style external declaration so that LINT
28150Sstevel@tonic-gate  * does not check against libc version of sigwait() and complain as
28160Sstevel@tonic-gate  * it uses different number of arguments.
28170Sstevel@tonic-gate  */
28180Sstevel@tonic-gate #define	sigwait	my_posix_sigwait
28190Sstevel@tonic-gate extern int my_posix_sigwait(const sigset_t *set, int *sig);
28200Sstevel@tonic-gate #endif
28210Sstevel@tonic-gate 
28220Sstevel@tonic-gate /*
28230Sstevel@tonic-gate  * sleep() in libpthread gets affected by time being set back, hence
28240Sstevel@tonic-gate  * can cause the "envthr" not to wakeup for extended duration. For
28250Sstevel@tonic-gate  * now, we implement our own sleep() routine below using alarm().
28260Sstevel@tonic-gate  * This will work only if SIGALRM is masked off in all other threads.
28270Sstevel@tonic-gate  * Note that SIGALRM signal is masked off in the main thread, hence
28280Sstevel@tonic-gate  * in all threads, including the envthr, the one calling this routine.
28290Sstevel@tonic-gate  *
28300Sstevel@tonic-gate  * Note that SIGALRM and alarm() can't be used by any other thread
28310Sstevel@tonic-gate  * in this manner.
28320Sstevel@tonic-gate  */
28330Sstevel@tonic-gate 
28340Sstevel@tonic-gate static unsigned int
envd_sleep(unsigned int sleep_tm)28350Sstevel@tonic-gate envd_sleep(unsigned int sleep_tm)
28360Sstevel@tonic-gate {
28370Sstevel@tonic-gate 	int  		sig;
28380Sstevel@tonic-gate 	unsigned int	unslept;
28390Sstevel@tonic-gate 	sigset_t	alrm_mask;
28400Sstevel@tonic-gate 
28410Sstevel@tonic-gate 	if (sleep_tm == 0)
28420Sstevel@tonic-gate 		return (0);
28430Sstevel@tonic-gate 
28440Sstevel@tonic-gate 	(void) sigemptyset(&alrm_mask);
28450Sstevel@tonic-gate 	(void) sigaddset(&alrm_mask, SIGALRM);
28460Sstevel@tonic-gate 
28470Sstevel@tonic-gate 	(void) alarm(sleep_tm);
28480Sstevel@tonic-gate 	(void) sigwait(&alrm_mask, &sig);
28490Sstevel@tonic-gate 
28500Sstevel@tonic-gate 	unslept = alarm(0);
28510Sstevel@tonic-gate 	return (unslept);
28520Sstevel@tonic-gate }
2853