cmd: remove sparc-only virtinfo
[unleashed.git] / usr / src / cmd / picl / plugins / sun4u / grover / envd / piclenvd.c
blob2dc18ba8f63a0a78f2e4a915b798b5a16a0355bc
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright (c) 2000 by Sun Microsystems, Inc.
24 * All rights reserved.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * This file contains the environmental daemon module.
35 * Grover system contains one temperature device, MAX1617, which consists
36 * of two sensors: CPU die and CPU ambient. Each sensor is represented
37 * as a different minor device and the current temperature is read via an
38 * I2C_GET_TEMPERATURE ioctl call to the max1617 driver. Additionally, the
39 * MAX1617 device supports both a low and high temperature limit, which
40 * can trigger an alert condition, causing power supply to turn off.
42 * The environmental daemon defines the following thresholds per sensor:
44 * high_power_off high hard shutdown
45 * high_shutdown high soft shutdown limit
46 * high_warning high warning limit
47 * low_warning low warning limit
48 * low_shutdown low soft shutdown limit
49 * low_power_off low hard shutdown limit
51 * Except for the low_power_off and high_power_off limits, all other threshold
52 * values can be changed via "piclenvd.conf" configuration file.
54 * Environmental monitoring is done by the "envthr" thread. It periodically
55 * monitors both CPU die and CPU ambient temperatures and takes appropriate
56 * action depending upon the current temperature and threshold values for
57 * that sensor. If the temperature reaches the high_shutdown limit or the
58 * low_shutdown limit, and remains there for over shutdown_interval seconds,
59 * it forces a graceful system shutdown via tuneable shutdown_cmd string
60 * variable. Otherwise, if the temperature reaches the high_warning limit
61 * or the low_warning limit, it logs and prints a message on the console.
62 * This message will be printed at most at "warning_interval" seconds
63 * interval, which is also a tuneable variable.
65 * Grover system also contains a fan, known as system fan, which can be turned
66 * ON or OFF under software control. However, its speed is automatically
67 * controlled by the hardware based upon the ambient temperature. When in EStar
68 * mode (i.e. lowest power state), the environmental daemon will turn off this
69 * fan provided the CPU die and ambient temperature is below the high warning
70 * limits.
72 * The power state monitoring is done by the "pmthr" thread. It uses the
73 * PM_GET_STATE_CHANGE and PM_GET_STATE_CHANGE_WAIT ioctl commands to pick
74 * up any power state change events. It processes all queued power state
75 * change events and determines the curret lowest power state and saves it
76 * in cur_lpstate variable. Whenever this state changes from the previous
77 * lowest power state (saved in prev_lpstate), it wakes up the "envtrh"
78 * thread.
80 * The "lpstate_lock" mutex and "lpstate_cond" condition variables are used
81 * to communicate power state change events from the "pmthr" to the "envthr"
82 * thread. The "envthr" thread uses the pthread_cond_timedwait() interface
83 * to wait for any power state change notifications. The "pmthr" uses the
84 * pthread_signal() interface to wake up the "envthr" thread.
87 #include <stdio.h>
88 #include <stdlib.h>
89 #include <string.h>
90 #include <stdarg.h>
91 #include <unistd.h>
92 #include <limits.h>
93 #include <syslog.h>
94 #include <errno.h>
95 #include <fcntl.h>
96 #include <picl.h>
97 #include <picltree.h>
98 #include <pthread.h>
99 #include <sys/pm.h>
100 #include <sys/open.h>
101 #include <sys/time.h>
102 #include <sys/utsname.h>
103 #include <sys/systeminfo.h>
104 #include <sys/i2c/clients/max1617.h>
105 #include <sys/i2c/clients/i2c_client.h>
106 #include "envd.h"
110 * PICL plguin
112 static void piclenvd_register(void);
113 static void piclenvd_init(void);
114 static void piclenvd_fini(void);
115 extern void env_picl_setup();
117 #pragma init(piclenvd_register)
119 static picld_plugin_reg_t my_reg_info = {
120 PICLD_PLUGIN_VERSION_1,
121 PICLD_PLUGIN_CRITICAL,
122 "SUNW_piclenvd",
123 piclenvd_init,
124 piclenvd_fini,
128 * tuneable variables
130 int env_debug;
131 static int sensor_poll_interval = SENSOR_POLL_INTERVAL;
132 static int warning_interval = WARNING_INTERVAL;
133 static int shutdown_interval = SHUTDOWN_INTERVAL;
134 static char shutdown_cmd[128] = SHUTDOWN_CMD;
135 static int monitor_temperature = 0;
137 static sensor_thresh_t cpu_die_thresh = {
138 CPU_DIE_LOW_POWER_OFF, CPU_DIE_HIGH_POWER_OFF,
139 CPU_DIE_LOW_SHUTDOWN, CPU_DIE_HIGH_SHUTDOWN,
140 CPU_DIE_LOW_WARNING, CPU_DIE_HIGH_WARNING,
141 CPU_DIE_TARGET_TEMP
144 static sensor_thresh_t cpu_amb_thresh = {
145 CPU_AMB_LOW_POWER_OFF, CPU_AMB_HIGH_POWER_OFF,
146 CPU_AMB_LOW_SHUTDOWN, CPU_AMB_HIGH_SHUTDOWN,
147 CPU_AMB_LOW_WARNING, CPU_AMB_HIGH_WARNING,
148 CPU_AMB_TARGET_TEMP
152 * Temperature sensors
155 static env_sensor_t cpu_die_sensor =
156 { SENSOR_CPU_DIE, CPU_DIE_SENSOR_DEVFS, &cpu_die_thresh};
158 static env_sensor_t cpu_amb_sensor =
159 { SENSOR_CPU_AMB, CPU_AMB_SENSOR_DEVFS, &cpu_amb_thresh};
162 static env_sensor_t *envd_sensors[] = {
163 &cpu_die_sensor,
164 &cpu_amb_sensor,
165 NULL
169 * Fan devices
171 static env_fan_t envd_system_fan = {
172 ENV_SYSTEM_FAN, ENV_SYSTEM_FAN_DEVFS,
173 SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX,
176 static env_fan_t *envd_fans[] = {
177 &envd_system_fan,
178 NULL
183 * Environmental thread variables
185 static boolean_t envd_inited = B_FALSE;
186 static boolean_t system_shutdown_started;
187 static boolean_t envthr_created; /* envthr created */
188 static pthread_t envthr_tid; /* envthr thread ID */
189 static pthread_attr_t thr_attr;
192 * Power management thread (pmthr) variables
194 static pthread_t pmthr_tid; /* pmthr thread ID */
195 static int pmthr_created; /* pmthr created */
196 static int pm_fd; /* PM device file descriptor */
197 static int cur_lpstate; /* cur low power state */
199 static pthread_mutex_t lpstate_lock; /* low power state lock */
200 static pthread_cond_t lpstate_cond; /* low power state condvar */
204 * Tuneable variables data structure/array
207 typedef struct {
208 char *name; /* keyword */
209 void *addr; /* memory (variable) address */
210 int type; /* keyword type */
211 int size; /* variable size */
212 } env_tuneable_t;
214 /* keyword types */
215 #define KTYPE_INT 1 /* signed int */
216 #define KTYPE_STRING 2 /* string in double quotes */
218 static env_tuneable_t env_tuneables[] = {
219 {"cpu_amb_low_shutdown", &cpu_amb_thresh.low_shutdown, KTYPE_INT,
220 sizeof (tempr_t)},
221 {"cpu_amb_low_warning", &cpu_amb_thresh.low_warning, KTYPE_INT,
222 sizeof (tempr_t)},
223 {"cpu_amb_target_temp", &cpu_amb_thresh.target_temp, KTYPE_INT,
224 sizeof (tempr_t)},
225 {"cpu_amb_high_shutdown", &cpu_amb_thresh.high_shutdown, KTYPE_INT,
226 sizeof (tempr_t)},
227 {"cpu_amb_high_warning", &cpu_amb_thresh.high_warning, KTYPE_INT,
228 sizeof (tempr_t)},
229 {"cpu_die_low_shutdown", &cpu_die_thresh.low_shutdown, KTYPE_INT,
230 sizeof (tempr_t)},
231 {"cpu_die_low_warning", &cpu_die_thresh.low_warning, KTYPE_INT,
232 sizeof (tempr_t)},
233 {"cpu_die_target_temp", &cpu_die_thresh.target_temp, KTYPE_INT,
234 sizeof (tempr_t)},
235 {"cpu_die_high_shutdown", &cpu_die_thresh.high_shutdown, KTYPE_INT,
236 sizeof (tempr_t)},
237 {"cpu_die_high_warning", &cpu_die_thresh.high_warning, KTYPE_INT,
238 sizeof (tempr_t)},
239 {"sensor_poll_interval", &sensor_poll_interval, KTYPE_INT,
240 sizeof (sensor_poll_interval)},
241 {"monitor_temperature", &monitor_temperature, KTYPE_INT,
242 sizeof (monitor_temperature)},
243 {"warning_interval", &warning_interval, KTYPE_INT,
244 sizeof (warning_interval)},
245 {"shutdown_interval", &shutdown_interval, KTYPE_INT,
246 sizeof (shutdown_interval)},
247 {"shutdown_cmd", &shutdown_cmd[0], KTYPE_STRING, sizeof (shutdown_cmd)},
248 {"env_debug", &env_debug, KTYPE_INT, sizeof (env_debug)},
249 { NULL, NULL, 0, 0}
253 * Lookup fan and return a pointer to env_fan_t data structure.
255 env_fan_t *
256 fan_lookup(char *name)
258 int i;
259 env_fan_t *fanp;
261 for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
262 if (strcmp(fanp->name, name) == 0)
263 return (fanp);
265 return (NULL);
269 * Lookup sensor and return a pointer to env_sensor_t data structure.
271 env_sensor_t *
272 sensor_lookup(char *name)
274 int i;
275 env_sensor_t *sensorp;
277 for (i = 0; (sensorp = envd_sensors[i]) != NULL; i++) {
278 if (strcmp(sensorp->name, name) == 0)
279 return (sensorp);
281 return (NULL);
285 * Get current temperature
286 * Returns -1 on error, 0 if successful
289 get_temperature(env_sensor_t *sensorp, tempr_t *temp)
291 int fd = sensorp->fd;
292 int retval = 0;
294 if (fd == -1)
295 retval = -1;
296 else if (ioctl(fd, I2C_GET_TEMPERATURE, temp) == -1) {
297 retval = -1;
298 if (sensorp->error == 0) {
299 sensorp->error = 1;
300 envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_FAIL,
301 sensorp->name, errno, strerror(errno));
303 } else if (sensorp->error != 0) {
304 sensorp->error = 0;
305 envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_OK, sensorp->name);
308 return (retval);
312 * Get current fan speed
313 * Returns -1 on error, 0 if successful
316 get_fan_speed(env_fan_t *fanp, fanspeed_t *fanspeedp)
318 int fan_fd;
319 int retval = 0;
321 fan_fd = fanp->fd;
322 if (fan_fd == -1 || read(fan_fd, fanspeedp, sizeof (fanspeed_t)) !=
323 sizeof (fanspeed_t))
324 retval = -1;
325 return (retval);
329 * Set fan speed
330 * Returns -1 on error, 0 if successful
332 static int
333 set_fan_speed(env_fan_t *fanp, fanspeed_t fanspeed)
335 int fan_fd;
336 int retval = 0;
338 fan_fd = fanp->fd;
339 if (fan_fd == -1 || write(fan_fd, &fanspeed, sizeof (fanspeed)) !=
340 sizeof (fanspeed_t))
341 retval = -1;
342 return (retval);
347 * close all fan devices
349 static void
350 envd_close_fans(void)
352 int i;
353 env_fan_t *fanp;
355 for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
356 if (fanp->fd != -1) {
357 (void) close(fanp->fd);
358 fanp->fd = -1;
364 * Close sensor devices
366 static void
367 envd_close_sensors(void)
369 int i;
370 env_sensor_t *sensorp;
372 for (i = 0; (sensorp = envd_sensors[i]) != NULL; i++) {
373 if (sensorp->fd != -1) {
374 (void) close(sensorp->fd);
375 sensorp->fd = -1;
381 * Close PM device
383 static void
384 envd_close_pm(void)
386 if (pm_fd != -1) {
387 (void) close(pm_fd);
388 pm_fd = -1;
393 * Open fan devices and initialize per fan data structure.
394 * Returns #fans found.
396 static int
397 envd_setup_fans(void)
399 int i, fd;
400 fanspeed_t speed;
401 env_fan_t *fanp;
402 char path[FILENAME_MAX];
403 int fancnt = 0;
405 for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
406 fanp->fd = -1;
407 fanp->cur_speed = 0;
408 fanp->prev_speed = 0;
410 (void) strcpy(path, "/devices");
411 (void) strlcat(path, fanp->devfs_path, sizeof (path));
412 fd = open(path, O_RDWR);
413 if (fd == -1) {
414 envd_log(LOG_WARNING, ENV_FAN_OPEN_FAIL, fanp->name,
415 fanp->devfs_path, errno, strerror(errno));
416 fanp->present = B_FALSE;
417 continue;
419 fanp->fd = fd;
420 fanp->present = B_TRUE;
421 fancnt++;
424 * Set cur_speed/prev_speed to current fan speed
426 if (get_fan_speed(fanp, &speed) == -1) {
428 * The Fan driver does not know the current fan speed.
429 * Initialize it to 50% of the max speed and reread
430 * to get the current speed.
432 speed = fanp->speed_max/2;
433 (void) set_fan_speed(fanp, speed);
434 if (get_fan_speed(fanp, &speed) == -1)
435 continue;
437 fanp->cur_speed = speed;
438 fanp->prev_speed = speed;
440 return (fancnt);
444 * Open temperature sensor devices and initialize per sensor data structure.
445 * Returns #sensors found.
447 static int
448 envd_setup_sensors(void)
450 int i;
451 tempr_t temp;
452 env_sensor_t *sensorp;
453 char path[FILENAME_MAX];
454 int sensorcnt = 0;
455 sensor_thresh_t *threshp;
457 for (i = 0; (sensorp = envd_sensors[i]) != NULL; i++) {
458 sensorp->fd = -1;
459 sensorp->shutdown_initiated = B_FALSE;
460 sensorp->warning_tstamp = 0;
461 sensorp->shutdown_tstamp = 0;
462 threshp = sensorp->temp_thresh;
463 sensorp->cur_temp = threshp->target_temp;
464 sensorp->error = 0;
466 (void) strcpy(path, "/devices");
467 (void) strlcat(path, sensorp->devfs_path, sizeof (path));
468 sensorp->fd = open(path, O_RDWR);
469 if (sensorp->fd == -1) {
470 envd_log(LOG_WARNING, ENV_SENSOR_OPEN_FAIL,
471 sensorp->name, sensorp->devfs_path, errno,
472 strerror(errno));
473 sensorp->present = B_FALSE;
474 continue;
476 sensorp->present = B_TRUE;
477 sensorcnt++;
479 if (monitor_temperature) {
481 * Set low_power_off and high_power_off limits
483 (void) ioctl(sensorp->fd, MAX1617_SET_LOW_LIMIT,
484 &threshp->low_power_off);
485 (void) ioctl(sensorp->fd, MAX1617_SET_HIGH_LIMIT,
486 &threshp->high_power_off);
490 * Set cur_temp field to the current temperature value
492 if (get_temperature(sensorp, &temp) == 0) {
493 sensorp->cur_temp = temp;
496 return (sensorcnt);
500 * Read all temperature sensors and take appropriate action based
501 * upon temperature threshols associated with each sensor. Possible
502 * actions are:
504 * temperature > high_shutdown
505 * temperature < low_shutdown
506 * Gracefully shutdown the system and log/print a message
507 * on the system console provided the temperature has been
508 * in shutdown range for "shutdown_interval" seconds.
510 * high_warning < temperature <= high_shutdown
511 * low_warning > temperature >= low_shutdown
512 * Log/print a warning message on the system console at most
513 * once every "warning_interval" seconds.
515 * Note that the current temperature is recorded in the "cur_temp" field
516 * within each env_sensor_t structure.
518 static void
519 monitor_sensors(void)
521 tempr_t temp;
522 int i;
523 env_sensor_t *sensorp;
524 sensor_thresh_t *threshp;
525 struct timeval ct;
526 char msgbuf[BUFSIZ];
527 char syscmd[BUFSIZ];
529 for (i = 0; (sensorp = envd_sensors[i]) != NULL; i++) {
530 if (get_temperature(sensorp, &temp) < 0)
531 continue;
533 sensorp->cur_temp = temp;
535 if (env_debug)
536 envd_log(LOG_INFO,
537 "sensor: %-13s temp cur:%3d target:%3d\n",
538 sensorp->name, temp,
539 sensorp->temp_thresh->target_temp);
541 if (!monitor_temperature)
542 continue;
545 * If this sensor already triggered system shutdown, don't
546 * log any more shutdown/warning messages for it.
548 if (sensorp->shutdown_initiated)
549 continue;
552 * Check for the temperature in warning and shutdown range
553 * and take appropriate action.
555 threshp = sensorp->temp_thresh;
556 if (TEMP_IN_WARNING_RANGE(temp, threshp)) {
558 * Log warning message at most once every
559 * warning_interval seconds.
561 (void) gettimeofday(&ct, NULL);
562 if ((ct.tv_sec - sensorp->warning_tstamp) >=
563 warning_interval) {
564 envd_log(LOG_WARNING, ENV_WARNING_MSG,
565 sensorp->name, temp,
566 threshp->low_warning,
567 threshp->high_warning);
568 sensorp->warning_tstamp = ct.tv_sec;
572 if (TEMP_IN_SHUTDOWN_RANGE(temp, threshp)) {
573 (void) gettimeofday(&ct, NULL);
574 if (sensorp->shutdown_tstamp == 0)
575 sensorp->shutdown_tstamp = ct.tv_sec;
578 * Shutdown the system if the temperature remains
579 * in the shutdown range for over shutdown_interval
580 * seconds.
582 if ((ct.tv_sec - sensorp->shutdown_tstamp) >=
583 shutdown_interval) {
584 /* log error */
585 sensorp->shutdown_initiated = B_TRUE;
586 (void) snprintf(msgbuf, sizeof (msgbuf),
587 ENV_SHUTDOWN_MSG, sensorp->name,
588 temp, threshp->low_shutdown,
589 threshp->high_shutdown);
590 envd_log(LOG_CRIT, msgbuf);
592 /* shutdown the system (only once) */
593 if (system_shutdown_started == B_FALSE) {
594 (void) snprintf(syscmd, sizeof (syscmd),
595 "%s \"%s\"", shutdown_cmd, msgbuf);
596 envd_log(LOG_CRIT, syscmd);
597 system_shutdown_started = B_TRUE;
598 (void) system(syscmd);
601 } else if (sensorp->shutdown_tstamp != 0)
602 sensorp->shutdown_tstamp = 0;
608 * This is the environment thread, which monitors the current temperature
609 * and power managed state and controls system fan speed. Temperature is
610 * polled every sensor-poll_interval seconds duration.
612 static void *
613 envthr(void *args)
615 int err;
616 fanspeed_t fan_speed;
617 struct timeval ct;
618 struct timespec to;
619 env_fan_t *pmfanp = &envd_system_fan;
620 tempr_t cpu_amb_temp, cpu_die_temp;
621 tempr_t cpu_amb_warning, cpu_die_warning;
623 (void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
624 (void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
626 cpu_amb_warning = cpu_amb_sensor.temp_thresh->high_warning;
627 cpu_die_warning = cpu_die_sensor.temp_thresh->high_warning;
629 for (;;) {
630 (void) gettimeofday(&ct, NULL);
633 * Monitor current temperature for all sensors
634 * (current temperature is recorded in the "cur_temp"
635 * field within each sensor data structure)
637 monitor_sensors();
639 cpu_amb_temp = cpu_amb_sensor.cur_temp;
640 cpu_die_temp = cpu_die_sensor.cur_temp;
643 * Process any PM state change events while waiting until
644 * time to poll sensors again (i.e. sensor_poll_interval
645 * seconds from the last time).
647 to.tv_sec = ct.tv_sec + sensor_poll_interval;
648 to.tv_nsec = 0;
649 for (;;) {
651 * Turn off system fan if in lowest power state
652 * and both CPU die and ambient temperatures are
653 * below corresponding high warning temperatures.
655 fan_speed = pmfanp->speed_max;
656 if (cur_lpstate && cpu_amb_temp < cpu_amb_warning &&
657 cpu_die_temp < cpu_die_warning)
658 fan_speed = pmfanp->speed_min;
660 if (env_debug)
661 envd_log(LOG_INFO,
662 "fan: %-16s speed cur:%3d new:%3d "
663 "low-power:%d\n", pmfanp->name,
664 (uint_t)pmfanp->cur_speed,
665 (uint_t)fan_speed, cur_lpstate);
667 if (fan_speed != pmfanp->cur_speed &&
668 set_fan_speed(pmfanp, fan_speed) == 0)
669 pmfanp->cur_speed = fan_speed;
671 /* wait for power state change or time to poll */
672 pthread_mutex_lock(&lpstate_lock);
673 err = pthread_cond_timedwait(&lpstate_cond,
674 &lpstate_lock, &to);
675 pthread_mutex_unlock(&lpstate_lock);
676 if (err == ETIMEDOUT)
677 break;
680 /*NOTREACHED*/
681 return (NULL);
685 * This is the power management thread, which monitors all power state
686 * change events and wakes up the "envthr" thread when the system enters
687 * or exits the lowest power state.
689 static void *
690 pmthr(void *args)
692 pm_state_change_t pmstate;
693 char physpath[PATH_MAX];
694 int prev_lpstate;
696 (void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
697 (void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
699 pmstate.physpath = physpath;
700 pmstate.size = sizeof (physpath);
701 cur_lpstate = 0;
702 prev_lpstate = 0;
704 for (;;) {
706 * Get PM state change events to check if the system
707 * is in lowest power state and wake up the "envthr"
708 * thread when the power state changes.
710 * To minimize polling, we use the blocking interface
711 * to get the power state change event here.
713 if (ioctl(pm_fd, PM_GET_STATE_CHANGE_WAIT, &pmstate) != 0) {
714 if (errno != EINTR)
715 break;
716 continue;
720 * Extract the lowest power state from the last queued
721 * state change events. We pick up queued state change
722 * events using the non-blocking interface and wake up
723 * the "envthr" thread only after consuming all the
724 * state change events queued at that time.
726 do {
727 if (env_debug > 1) {
728 envd_log(LOG_INFO,
729 "pmstate event:0x%x flags:%x comp:%d "
730 "oldval:%d newval:%d path:%s\n",
731 pmstate.event, pmstate.flags,
732 pmstate.component, pmstate.old_level,
733 pmstate.new_level, pmstate.physpath);
735 cur_lpstate =
736 (pmstate.flags & PSC_ALL_LOWEST) ? 1 : 0;
737 } while (ioctl(pm_fd, PM_GET_STATE_CHANGE, &pmstate) == 0);
739 if (cur_lpstate != prev_lpstate) {
740 prev_lpstate = cur_lpstate;
741 pthread_mutex_lock(&lpstate_lock);
742 pthread_cond_signal(&lpstate_cond);
743 pthread_mutex_unlock(&lpstate_lock);
748 * We won't be able to monitor lowest power state any longer,
749 * hence reset it and wakeup the "envthr".
751 if (cur_lpstate != 0) {
752 prev_lpstate = cur_lpstate;
753 cur_lpstate = 0;
754 pthread_mutex_lock(&lpstate_lock);
755 pthread_cond_signal(&lpstate_cond);
756 pthread_mutex_unlock(&lpstate_lock);
758 envd_log(LOG_ERR, PM_THREAD_EXITING, errno, strerror(errno));
759 return (NULL);
764 * Parse string value (handling escaped double quotes and other characters)
765 * and return string end pointer.
768 static char *
769 parse_string_val(char *buf)
771 char *p, c;
773 if (buf[0] != '"')
774 return (NULL);
776 for (p = buf+1; (c = *p) != '\0'; p++)
777 if (c == '"' || (c == '\\' && *++p == '\0'))
778 break;
780 return ((*p == '"') ? p : NULL);
785 * Process configuration file
787 static void
788 process_env_conf_file(void)
790 int line, len, val, toklen;
791 char buf[BUFSIZ];
792 FILE *fp;
793 env_tuneable_t *tunep;
794 char nmbuf[SYS_NMLN];
795 char fname[PATH_MAX];
796 char *tok, *valuep, *strend;
797 char tokdel[] = " \t\n\r";
798 int skip_line = 0;
800 if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) == -1)
801 return;
803 (void) snprintf(fname, sizeof (fname), PICLD_PLAT_PLUGIN_DIRF, nmbuf);
804 (void) strlcat(fname, ENV_CONF_FILE, sizeof (fname));
805 fp = fopen(fname, "r");
806 if (fp == NULL)
807 return;
810 * Blank lines or lines starting with "#" or "*" in the first
811 * column are ignored. All other lines are assumed to contain
812 * input in the following format:
814 * keyword value
816 * where the "value" can be a signed integer or string (in
817 * double quotes) depending upon the keyword.
820 for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) {
821 len = strlen(buf);
822 if (len <= 0)
823 continue;
825 /* skip long lines */
826 if (buf[len-1] != '\n') {
827 skip_line = 1;
828 continue;
829 } else if (skip_line) {
830 skip_line = 0;
831 continue;
832 } else
833 buf[len-1] = '\0';
835 /* skip comments */
836 if (buf[0] == '*' || buf[0] == '#')
837 continue;
840 * Skip over white space to get the keyword
842 tok = buf + strspn(buf, tokdel);
843 if (*tok == '\0')
844 continue; /* blank line */
846 toklen = strcspn(tok, tokdel);
847 tok[toklen] = '\0';
849 /* Get possible location for value (within current line) */
850 valuep = tok + toklen + 1;
851 if (valuep > buf+len)
852 valuep = buf + len;
855 * Lookup the keyword and process value accordingly
857 for (tunep = &env_tuneables[0]; tunep->name != NULL; tunep++) {
858 if (strcmp(tunep->name, tok) != 0)
859 continue;
861 switch (tunep->type) {
862 case KTYPE_INT:
863 errno = 0;
864 val = strtol(valuep, &valuep, 0);
866 /* Check for invalid value or extra tokens */
867 if (errno != 0 || strtok(valuep, tokdel)) {
868 envd_log(LOG_INFO,
869 ENV_CONF_INT_EXPECTED,
870 fname, line, tok);
871 break;
874 /* Update only if value within range */
875 if (tunep->size == sizeof (int8_t) &&
876 val == (int8_t)val)
877 *(int8_t *)tunep->addr = (int8_t)val;
878 else if (tunep->size == sizeof (short) &&
879 val == (short)val)
880 *(short *)tunep->addr = (short)val;
881 else if (tunep->size == sizeof (int))
882 *(int *)tunep->addr = (int)val;
883 else {
884 envd_log(LOG_INFO,
885 ENV_CONF_INT_EXPECTED,
886 fname, line, tok);
887 break;
889 if (env_debug)
890 envd_log(LOG_INFO, "SUNW_piclenvd: "
891 "file:%s line:%d %s = %d\n",
892 fname, line, tok, val);
893 break;
895 case KTYPE_STRING:
897 * String value must be within double quotes.
898 * Skip over initial white spaces before
899 * looking for value.
901 valuep += strspn(valuep, tokdel);
902 strend = parse_string_val(valuep);
904 if (strend == NULL || *valuep != '"' ||
905 strtok(strend+1, tokdel) != NULL ||
906 (strend-valuep) > tunep->size) {
907 envd_log(LOG_INFO,
908 ENV_CONF_STRING_EXPECTED,
909 fname, line, tok,
910 tunep->size);
911 break;
913 *strend = '\0';
914 if (env_debug)
915 envd_log(LOG_INFO, "piclenvd: file:%s"
916 " line:%d %s = \"%s\"\n",
917 fname, line, tok, valuep+1);
918 (void) strcpy(tunep->addr, (caddr_t)valuep+1);
919 break;
921 default:
922 envd_log(LOG_INFO,
923 ENV_CONF_UNSUPPORTED_TYPE,
924 fname, line,
925 tunep->type, tunep->name);
927 break;
930 if (tunep->name == NULL)
931 envd_log(LOG_INFO, ENV_CONF_UNSUPPORTED_KEYWORD,
932 fname, line, tok);
934 (void) fclose(fp);
938 * Setup envrionmental daemon state and start threads to monitor
939 * temperature and power management state.
940 * Returns -1 on error, 0 if successful.
943 static int
944 envd_setup(void)
946 if (envd_inited == B_FALSE) {
948 * Initialize global state
950 system_shutdown_started = B_FALSE;
951 envthr_created = B_FALSE;
952 pmthr_created = B_FALSE;
954 if (pthread_attr_init(&thr_attr) != 0 ||
955 pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM) != 0)
956 return (-1);
958 if (pthread_mutex_init(&lpstate_lock, NULL) != 0 ||
959 pthread_cond_init(&lpstate_cond, NULL) != 0)
960 return (-1);
963 * Process tuneable parameters
965 process_env_conf_file();
968 * Setup temperature sensors and fail if we can't open
969 * at least one sensor.
971 if (envd_setup_sensors() <= 0)
972 return (-1);
975 * Setup fan device (don't fail even if we can't access
976 * the fan as we can still monitor temeperature.
978 (void) envd_setup_fans();
981 * Create a thread to monitor temperature and control fan
982 * speed.
984 if (envthr_created == B_FALSE && pthread_create(&envthr_tid,
985 &thr_attr, envthr, NULL) != 0) {
986 envd_close_fans();
987 envd_close_sensors();
988 envd_log(LOG_CRIT, ENV_THREAD_CREATE_FAILED);
989 return (-1);
991 envthr_created = B_TRUE;
993 envd_inited = B_TRUE;
996 * Create a thread to monitor PM state
998 if (pmthr_created == B_FALSE) {
999 pm_fd = open(PM_DEVICE, O_RDONLY);
1000 if (pm_fd == -1 || pthread_create(&pmthr_tid, &thr_attr,
1001 pmthr, NULL) != 0) {
1002 envd_close_pm();
1003 envd_log(LOG_CRIT, PM_THREAD_CREATE_FAILED);
1004 } else
1005 pmthr_created = B_TRUE;
1007 return (0);
1011 static void
1012 piclenvd_register(void)
1014 picld_plugin_register(&my_reg_info);
1017 static void
1018 piclenvd_init(void)
1021 * Start environmental daemon/threads
1023 if (envd_setup() != 0) {
1024 envd_log(LOG_CRIT, ENVD_PLUGIN_INIT_FAILED);
1025 return;
1029 * Now setup/populate PICL tree
1031 env_picl_setup();
1034 static void
1035 piclenvd_fini(void)
1037 void *exitval;
1040 * Kill both "envthr" and "pmthr" threads.
1042 if (envthr_created) {
1043 (void) pthread_cancel(envthr_tid);
1044 (void) pthread_join(envthr_tid, &exitval);
1045 envthr_created = B_FALSE;
1048 if (pmthr_created) {
1049 (void) pthread_cancel(pmthr_tid);
1050 (void) pthread_join(pmthr_tid, &exitval);
1051 pmthr_created = B_FALSE;
1055 * close all sensors, fans and the power management device
1057 envd_close_pm();
1058 envd_close_fans();
1059 envd_close_sensors();
1060 envd_inited = B_FALSE;
1063 /*VARARGS2*/
1064 void
1065 envd_log(int pri, const char *fmt, ...)
1067 va_list ap;
1069 va_start(ap, fmt);
1070 vsyslog(pri, fmt, ap);
1071 va_end(ap);