/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2000 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains the environmental daemon module.
*/
/*
* Grover system contains one temperature device, MAX1617, which consists
* of two sensors: CPU die and CPU ambient. Each sensor is represented
* as a different minor device and the current temperature is read via an
* I2C_GET_TEMPERATURE ioctl call to the max1617 driver. Additionally, the
* MAX1617 device supports both a low and high temperature limit, which
* can trigger an alert condition, causing power supply to turn off.
*
* The environmental daemon defines the following thresholds per sensor:
*
* high_power_off high hard shutdown
* high_shutdown high soft shutdown limit
* high_warning high warning limit
* low_warning low warning limit
* low_shutdown low soft shutdown limit
* low_power_off low hard shutdown limit
*
* Except for the low_power_off and high_power_off limits, all other threshold
* values can be changed via "piclenvd.conf" configuration file.
*
* Environmental monitoring is done by the "envthr" thread. It periodically
* monitors both CPU die and CPU ambient temperatures and takes appropriate
* action depending upon the current temperature and threshold values for
* that sensor. If the temperature reaches the high_shutdown limit or the
* low_shutdown limit, and remains there for over shutdown_interval seconds,
* it forces a graceful system shutdown via tuneable shutdown_cmd string
* variable. Otherwise, if the temperature reaches the high_warning limit
* or the low_warning limit, it logs and prints a message on the console.
* This message will be printed at most at "warning_interval" seconds
* interval, which is also a tuneable variable.
*
* Grover system also contains a fan, known as system fan, which can be turned
* ON or OFF under software control. However, its speed is automatically
* controlled by the hardware based upon the ambient temperature. When in EStar
* mode (i.e. lowest power state), the environmental daemon will turn off this
* fan provided the CPU die and ambient temperature is below the high warning
* limits.
*
* The power state monitoring is done by the "pmthr" thread. It uses the
* PM_GET_STATE_CHANGE and PM_GET_STATE_CHANGE_WAIT ioctl commands to pick
* up any power state change events. It processes all queued power state
* change events and determines the curret lowest power state and saves it
* in cur_lpstate variable. Whenever this state changes from the previous
* lowest power state (saved in prev_lpstate), it wakes up the "envtrh"
* thread.
*
* The "lpstate_lock" mutex and "lpstate_cond" condition variables are used
* to communicate power state change events from the "pmthr" to the "envthr"
* thread. The "envthr" thread uses the pthread_cond_timedwait() interface
* to wait for any power state change notifications. The "pmthr" uses the
* pthread_signal() interface to wake up the "envthr" thread.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <limits.h>
#include <syslog.h>
#include <errno.h>
#include <fcntl.h>
#include <picl.h>
#include <picltree.h>
#include <pthread.h>
#include <sys/systeminfo.h>
#include "envd.h"
/*
* PICL plguin
*/
static void piclenvd_register(void);
static void piclenvd_init(void);
static void piclenvd_fini(void);
extern void env_picl_setup();
#pragma init(piclenvd_register)
"SUNW_piclenvd",
};
/*
* tuneable variables
*/
int env_debug;
static int monitor_temperature = 0;
};
};
/*
* Temperature sensors
*/
};
/*
* Fan devices
*/
};
};
/*
* Environmental thread variables
*/
/*
* Power management thread (pmthr) variables
*/
/*
*/
typedef struct {
/* keyword types */
sizeof (tempr_t)},
sizeof (tempr_t)},
sizeof (tempr_t)},
sizeof (tempr_t)},
sizeof (tempr_t)},
sizeof (tempr_t)},
sizeof (tempr_t)},
sizeof (tempr_t)},
sizeof (tempr_t)},
sizeof (tempr_t)},
sizeof (sensor_poll_interval)},
sizeof (monitor_temperature)},
sizeof (warning_interval)},
sizeof (shutdown_interval)},
};
/*
* Lookup fan and return a pointer to env_fan_t data structure.
*/
{
int i;
return (fanp);
}
return (NULL);
}
/*
* Lookup sensor and return a pointer to env_sensor_t data structure.
*/
{
int i;
return (sensorp);
}
return (NULL);
}
/*
* Get current temperature
* Returns -1 on error, 0 if successful
*/
int
{
int retval = 0;
if (fd == -1)
retval = -1;
retval = -1;
}
}
return (retval);
}
/*
* Get current fan speed
* Returns -1 on error, 0 if successful
*/
int
{
int fan_fd;
int retval = 0;
sizeof (fanspeed_t))
retval = -1;
return (retval);
}
/*
* Set fan speed
* Returns -1 on error, 0 if successful
*/
static int
{
int fan_fd;
int retval = 0;
sizeof (fanspeed_t))
retval = -1;
return (retval);
}
/*
* close all fan devices
*/
static void
envd_close_fans(void)
{
int i;
}
}
}
/*
* Close sensor devices
*/
static void
envd_close_sensors(void)
{
int i;
}
}
}
/*
* Close PM device
*/
static void
envd_close_pm(void)
{
if (pm_fd != -1) {
pm_fd = -1;
}
}
/*
* Open fan devices and initialize per fan data structure.
* Returns #fans found.
*/
static int
envd_setup_fans(void)
{
int i, fd;
int fancnt = 0;
fanp->prev_speed = 0;
if (fd == -1) {
continue;
}
fancnt++;
/*
* Set cur_speed/prev_speed to current fan speed
*/
/*
* The Fan driver does not know the current fan speed.
* Initialize it to 50% of the max speed and reread
* to get the current speed.
*/
continue;
}
}
return (fancnt);
}
/*
* Open temperature sensor devices and initialize per sensor data structure.
* Returns #sensors found.
*/
static int
envd_setup_sensors(void)
{
int i;
int sensorcnt = 0;
sensorp->warning_tstamp = 0;
sensorp->shutdown_tstamp = 0;
continue;
}
sensorcnt++;
if (monitor_temperature) {
/*
* Set low_power_off and high_power_off limits
*/
&threshp->low_power_off);
}
/*
* Set cur_temp field to the current temperature value
*/
}
}
return (sensorcnt);
}
/*
* Read all temperature sensors and take appropriate action based
* upon temperature threshols associated with each sensor. Possible
* actions are:
*
* temperature > high_shutdown
* temperature < low_shutdown
* on the system console provided the temperature has been
* in shutdown range for "shutdown_interval" seconds.
*
* high_warning < temperature <= high_shutdown
* low_warning > temperature >= low_shutdown
* once every "warning_interval" seconds.
*
* Note that the current temperature is recorded in the "cur_temp" field
* within each env_sensor_t structure.
*/
static void
monitor_sensors(void)
{
int i;
continue;
if (env_debug)
"sensor: %-13s temp cur:%3d target:%3d\n",
if (!monitor_temperature)
continue;
/*
* If this sensor already triggered system shutdown, don't
*/
if (sensorp->shutdown_initiated)
continue;
/*
* Check for the temperature in warning and shutdown range
* and take appropriate action.
*/
/*
* Log warning message at most once every
* warning_interval seconds.
*/
}
}
if (sensorp->shutdown_tstamp == 0)
/*
* Shutdown the system if the temperature remains
* in the shutdown range for over shutdown_interval
* seconds.
*/
/* log error */
/* shutdown the system (only once) */
if (system_shutdown_started == B_FALSE) {
}
}
} else if (sensorp->shutdown_tstamp != 0)
sensorp->shutdown_tstamp = 0;
}
}
/*
* This is the environment thread, which monitors the current temperature
* and power managed state and controls system fan speed. Temperature is
* polled every sensor-poll_interval seconds duration.
*/
static void *
{
int err;
for (;;) {
/*
* Monitor current temperature for all sensors
* (current temperature is recorded in the "cur_temp"
* field within each sensor data structure)
*/
/*
* Process any PM state change events while waiting until
* time to poll sensors again (i.e. sensor_poll_interval
* seconds from the last time).
*/
for (;;) {
/*
* Turn off system fan if in lowest power state
* and both CPU die and ambient temperatures are
* below corresponding high warning temperatures.
*/
if (env_debug)
"fan: %-16s speed cur:%3d new:%3d "
/* wait for power state change or time to poll */
&lpstate_lock, &to);
break;
}
}
/*NOTREACHED*/
return (NULL);
}
/*
* This is the power management thread, which monitors all power state
* change events and wakes up the "envthr" thread when the system enters
* or exits the lowest power state.
*/
static void *
{
int prev_lpstate;
cur_lpstate = 0;
prev_lpstate = 0;
for (;;) {
/*
* Get PM state change events to check if the system
* is in lowest power state and wake up the "envthr"
* thread when the power state changes.
*
* To minimize polling, we use the blocking interface
* to get the power state change event here.
*/
break;
continue;
}
/*
* Extract the lowest power state from the last queued
* state change events. We pick up queued state change
* events using the non-blocking interface and wake up
* the "envthr" thread only after consuming all the
* state change events queued at that time.
*/
do {
if (env_debug > 1) {
"pmstate event:0x%x flags:%x comp:%d "
"oldval:%d newval:%d path:%s\n",
}
if (cur_lpstate != prev_lpstate) {
}
}
/*
* We won't be able to monitor lowest power state any longer,
* hence reset it and wakeup the "envthr".
*/
if (cur_lpstate != 0) {
cur_lpstate = 0;
}
return (NULL);
}
/*
* Parse string value (handling escaped double quotes and other characters)
* and return string end pointer.
*/
static char *
{
char *p, c;
if (buf[0] != '"')
return (NULL);
if (c == '"' || (c == '\\' && *++p == '\0'))
break;
return ((*p == '"') ? p : NULL);
}
/*
* Process configuration file
*/
static void
process_env_conf_file(void)
{
int skip_line = 0;
return;
return;
/*
* Blank lines or lines starting with "#" or "*" in the first
* column are ignored. All other lines are assumed to contain
* input in the following format:
*
* keyword value
*
* where the "value" can be a signed integer or string (in
* double quotes) depending upon the keyword.
*/
if (len <= 0)
continue;
/* skip long lines */
skip_line = 1;
continue;
} else if (skip_line) {
skip_line = 0;
continue;
} else
/* skip comments */
continue;
/*
* Skip over white space to get the keyword
*/
if (*tok == '\0')
continue; /* blank line */
/* Get possible location for value (within current line) */
/*
* Lookup the keyword and process value accordingly
*/
continue;
case KTYPE_INT:
errno = 0;
/* Check for invalid value or extra tokens */
break;
}
/* Update only if value within range */
else {
break;
}
if (env_debug)
"file:%s line:%d %s = %d\n",
break;
case KTYPE_STRING:
/*
* String value must be within double quotes.
* Skip over initial white spaces before
* looking for value.
*/
break;
}
*strend = '\0';
if (env_debug)
" line:%d %s = \"%s\"\n",
break;
default:
}
break;
}
}
}
/*
* Setup envrionmental daemon state and start threads to monitor
* temperature and power management state.
* Returns -1 on error, 0 if successful.
*/
static int
envd_setup(void)
{
if (envd_inited == B_FALSE) {
/*
* Initialize global state
*/
if (pthread_attr_init(&thr_attr) != 0 ||
return (-1);
return (-1);
/*
* Process tuneable parameters
*/
/*
* Setup temperature sensors and fail if we can't open
* at least one sensor.
*/
if (envd_setup_sensors() <= 0)
return (-1);
/*
* Setup fan device (don't fail even if we can't access
* the fan as we can still monitor temeperature.
*/
(void) envd_setup_fans();
/*
* Create a thread to monitor temperature and control fan
* speed.
*/
return (-1);
}
}
/*
* Create a thread to monitor PM state
*/
if (pmthr_created == B_FALSE) {
} else
}
return (0);
}
static void
piclenvd_register(void)
{
}
static void
piclenvd_init(void)
{
/*
*/
if (envd_setup() != 0) {
return;
}
/*
*/
}
static void
piclenvd_fini(void)
{
void *exitval;
/*
* Kill both "envthr" and "pmthr" threads.
*/
if (envthr_created) {
(void) pthread_cancel(envthr_tid);
}
if (pmthr_created) {
(void) pthread_cancel(pmthr_tid);
}
/*
* close all sensors, fans and the power management device
*/
}
/*VARARGS2*/
void
{
}