xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4u/chicago/envd/piclenvd.c (revision cc6c5292fa8a241fe50604cf6a918edfbf7cd7d2)
1 /*
2  * CDDL HEADER START
3  *
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.
8  *
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.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains the environmental PICL plug-in module.
31  */
32 
33 /*
34  * This plugin sets up the PICLTREE for Chicago WS.
35  * It provides functionality to get/set temperatures and
36  * fan speeds.
37  *
38  * The environmental policy defaults to the auto mode
39  * as programmed by OBP at boot time.
40  */
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <sys/sysmacros.h>
45 #include <limits.h>
46 #include <string.h>
47 #include <strings.h>
48 #include <stdarg.h>
49 #include <alloca.h>
50 #include <unistd.h>
51 #include <sys/processor.h>
52 #include <syslog.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <picl.h>
56 #include <picltree.h>
57 #include <picldefs.h>
58 #include <pthread.h>
59 #include <signal.h>
60 #include <libdevinfo.h>
61 #include <sys/pm.h>
62 #include <sys/open.h>
63 #include <sys/time.h>
64 #include <sys/utsname.h>
65 #include <sys/systeminfo.h>
66 #include <note.h>
67 #include <sys/pic16f747.h>
68 #include "envd.h"
69 #include <sys/scsi/scsi.h>
70 #include <sys/scsi/generic/commands.h>
71 
72 int	debug_fd;
73 /*
74  * PICL plugin entry points
75  */
76 static void piclenvd_register(void);
77 static void piclenvd_init(void);
78 static void piclenvd_fini(void);
79 
80 /*
81  * Env setup routines
82  */
83 extern void env_picl_setup(void);
84 extern void env_picl_destroy(void);
85 extern int env_picl_setup_tuneables(void);
86 
87 static boolean_t has_fan_failed(env_fan_t *fanp);
88 
89 #pragma init(piclenvd_register)
90 
91 /*
92  * Plugin registration information
93  */
94 static picld_plugin_reg_t my_reg_info = {
95 	PICLD_PLUGIN_VERSION,
96 	PICLD_PLUGIN_CRITICAL,
97 	"SUNW_piclenvd",
98 	piclenvd_init,
99 	piclenvd_fini,
100 };
101 
102 #define	REGISTER_INFORMATION_STRING_LENGTH	16
103 static char fan_rpm_string[REGISTER_INFORMATION_STRING_LENGTH] = {0};
104 static char fan_status_string[REGISTER_INFORMATION_STRING_LENGTH] = {0};
105 
106 static int	scsi_log_sense(env_disk_t *diskp, uchar_t page_code,
107 			void *pagebuf, uint16_t pagelen, int page_control);
108 static int scsi_mode_select(env_disk_t *diskp, uchar_t page_code,
109 			uchar_t *pagebuf, uint16_t pagelen);
110 
111 static int	get_disk_temp(env_disk_t *);
112 
113 /*
114  * ES Segment stuff
115  */
116 static es_sensor_blk_t sensor_ctl[MAX_SENSORS];
117 
118 /*
119  * Default limits for sensors, in case ES segment is not present, or has
120  * inconsistent information
121  */
122 static es_sensor_blk_t sensor_default_ctl[MAX_SENSORS] = {
123 	{
124 	    CPU0_HIGH_POWER_OFF, CPU0_HIGH_SHUTDOWN, CPU0_HIGH_WARNING,
125 	    CPU0_LOW_WARNING, CPU0_LOW_SHUTDOWN, CPU0_LOW_POWER_OFF
126 	},
127 	{
128 	    CPU1_HIGH_POWER_OFF, CPU1_HIGH_SHUTDOWN, CPU1_HIGH_WARNING,
129 	    CPU1_LOW_WARNING, CPU1_LOW_SHUTDOWN, CPU1_LOW_POWER_OFF
130 	},
131 	{
132 	    ADT7462_HIGH_POWER_OFF, ADT7462_HIGH_SHUTDOWN, ADT7462_HIGH_WARNING,
133 	    ADT7462_LOW_WARNING, ADT7462_LOW_SHUTDOWN, ADT7462_LOW_POWER_OFF
134 	},
135 	{
136 	    MB_HIGH_POWER_OFF, MB_HIGH_SHUTDOWN, MB_HIGH_WARNING,
137 	    MB_LOW_WARNING, MB_LOW_SHUTDOWN, MB_LOW_POWER_OFF
138 	},
139 	{
140 	    LM95221_HIGH_POWER_OFF, LM95221_HIGH_SHUTDOWN, LM95221_HIGH_WARNING,
141 	    LM95221_LOW_WARNING, LM95221_LOW_SHUTDOWN, LM95221_LOW_POWER_OFF
142 	},
143 	{
144 	    FIRE_HIGH_POWER_OFF, FIRE_HIGH_SHUTDOWN, FIRE_HIGH_WARNING,
145 	    FIRE_LOW_WARNING, FIRE_LOW_SHUTDOWN, FIRE_LOW_POWER_OFF
146 	},
147 	{
148 	    LSI1064_HIGH_POWER_OFF, LSI1064_HIGH_SHUTDOWN, LSI1064_HIGH_WARNING,
149 	    LSI1064_LOW_WARNING, LSI1064_LOW_SHUTDOWN, LSI1064_LOW_POWER_OFF
150 	},
151 	{
152 	    FRONT_PANEL_HIGH_POWER_OFF, FRONT_PANEL_HIGH_SHUTDOWN,
153 	    FRONT_PANEL_HIGH_WARNING, FRONT_PANEL_LOW_WARNING,
154 	    FRONT_PANEL_LOW_SHUTDOWN, FRONT_PANEL_LOW_POWER_OFF
155 	}
156 };
157 
158 /*
159  * Env thread variables
160  */
161 static boolean_t  system_shutdown_started = B_FALSE;
162 static boolean_t  system_temp_thr_created = B_FALSE;
163 static pthread_t  system_temp_thr_id;
164 static pthread_attr_t thr_attr;
165 static boolean_t  disk_temp_thr_created = B_FALSE;
166 static pthread_t  disk_temp_thr_id;
167 static boolean_t  fan_thr_created = B_FALSE;
168 static pthread_t  fan_thr_id;
169 
170 /*
171  * PM thread related variables
172  */
173 static pthread_t	pmthr_tid;	/* pmthr thread ID */
174 static int		pm_fd = -1;	/* PM device file descriptor */
175 static boolean_t	pmthr_created = B_FALSE;
176 static int		cur_lpstate;	/* cur low power state */
177 
178 /*
179  * Envd plug-in verbose flag set by SUNW_PICLENVD_DEBUG environment var
180  * Setting the verbose tuneable also enables debugging for better
181  * control
182  */
183 int	env_debug = 0;
184 
185 /*
186  * These are debug variables for keeping track of the total number
187  * of Fan and Temp sensor retries over the lifetime of the plugin.
188  */
189 static int total_fan_retries = 0;
190 static int total_temp_retries = 0;
191 
192 /*
193  * Fan devices
194  */
195 static env_fan_t envd_system_fan0 = {
196 	ENV_SYSTEM_FAN0, ENV_SYSTEM_FAN0_DEVFS, SYSTEM_FAN0_ID,
197 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
198 };
199 static env_fan_t envd_system_fan1 = {
200 	ENV_SYSTEM_FAN1, ENV_SYSTEM_FAN1_DEVFS, SYSTEM_FAN1_ID,
201 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
202 };
203 static env_fan_t envd_system_fan2 = {
204 	ENV_SYSTEM_FAN2, ENV_SYSTEM_FAN2_DEVFS, SYSTEM_FAN2_ID,
205 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
206 };
207 static env_fan_t envd_system_fan3 = {
208 	ENV_SYSTEM_FAN3, ENV_SYSTEM_FAN3_DEVFS, SYSTEM_FAN3_ID,
209 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
210 };
211 static env_fan_t envd_system_fan4 = {
212 	ENV_SYSTEM_FAN4, ENV_SYSTEM_FAN4_DEVFS, SYSTEM_FAN4_ID,
213 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
214 };
215 
216 /*
217  * Disk devices
218  */
219 static env_disk_t envd_disk0 = {
220 	ENV_DISK0, ENV_DISK0_DEVFS, DISK0_PHYSPATH, DISK0_NODE_PATH,
221 	DISK0_ID, -1,
222 };
223 static env_disk_t envd_disk1 = {
224 	ENV_DISK1, ENV_DISK1_DEVFS, DISK1_PHYSPATH, DISK1_NODE_PATH,
225 	DISK1_ID, -1,
226 };
227 static env_disk_t envd_disk2 = {
228 	ENV_DISK2, ENV_DISK2_DEVFS, DISK2_PHYSPATH, DISK2_NODE_PATH,
229 	DISK2_ID, -1,
230 };
231 static env_disk_t envd_disk3 = {
232 	ENV_DISK3, ENV_DISK3_DEVFS, DISK3_PHYSPATH, DISK3_NODE_PATH,
233 	DISK3_ID, -1,
234 };
235 
236 /*
237  * Sensors
238  */
239 static env_sensor_t envd_sensor_cpu0 = {
240 	SENSOR_CPU0, SENSOR_CPU0_DEVFS, CPU0_SENSOR_ID, -1, NULL,
241 };
242 static env_sensor_t envd_sensor_cpu1 = {
243 	SENSOR_CPU1, SENSOR_CPU1_DEVFS, CPU1_SENSOR_ID, -1, NULL,
244 };
245 static env_sensor_t envd_sensor_adt7462 = {
246 	SENSOR_ADT7462, SENSOR_ADT7462_DEVFS, ADT7462_SENSOR_ID, -1, NULL,
247 };
248 static env_sensor_t envd_sensor_mb = {
249 	SENSOR_MB, SENSOR_MB_DEVFS, MB_SENSOR_ID, -1, NULL,
250 };
251 static env_sensor_t envd_sensor_lm95221 = {
252 	SENSOR_LM95221, SENSOR_LM95221_DEVFS, LM95221_SENSOR_ID, -1, NULL,
253 };
254 static env_sensor_t envd_sensor_fire = {
255 	SENSOR_FIRE, SENSOR_FIRE_DEVFS, FIRE_SENSOR_ID, -1, NULL,
256 };
257 static env_sensor_t envd_sensor_lsi1064 = {
258 	SENSOR_LSI1064, SENSOR_LSI1064_DEVFS, LSI1064_SENSOR_ID, -1, NULL,
259 };
260 static env_sensor_t envd_sensor_front_panel = {
261 	SENSOR_FRONT_PANEL, SENSOR_FRONT_PANEL_DEVFS, FRONT_PANEL_SENSOR_ID,
262 	-1, NULL,
263 };
264 
265 /*
266  * The vendor-id and device-id are the properties associated with
267  * the SCSI controller. This is used to identify a particular controller
268  * like LSI1064.
269  */
270 #define	VENDOR_ID	"vendor-id"
271 #define	DEVICE_ID	"device-id"
272 
273 /*
274  * The implementation for SCSI disk drives to supply info. about
275  * temperature is not mandatory. Hence we first determine if the
276  * temperature page is supported. To do this we need to scan the list
277  * of pages supported.
278  */
279 #define	SUPPORTED_LPAGES	0
280 #define	TEMPERATURE_PAGE	0x0D
281 #define	LOGPAGEHDRSIZE	4
282 
283 /*
284  * NULL terminated array of fans
285  */
286 static env_fan_t *envd_fans[] = {
287 	&envd_system_fan0,
288 	&envd_system_fan1,
289 	&envd_system_fan2,
290 	&envd_system_fan3,
291 	&envd_system_fan4,
292 	NULL
293 };
294 
295 /*
296  * NULL terminated array of disks
297  */
298 static env_disk_t *envd_disks[] = {
299 	&envd_disk0,
300 	&envd_disk1,
301 	&envd_disk2,
302 	&envd_disk3,
303 	NULL
304 };
305 
306 /*
307  * NULL terminated array of temperature sensors
308  */
309 #define	N_ENVD_SENSORS	8
310 static env_sensor_t *envd_sensors[] = {
311 	&envd_sensor_cpu0,
312 	&envd_sensor_cpu1,
313 	&envd_sensor_adt7462,
314 	&envd_sensor_mb,
315 	&envd_sensor_lm95221,
316 	&envd_sensor_fire,
317 	&envd_sensor_lsi1064,
318 	&envd_sensor_front_panel,
319 	NULL
320 };
321 
322 #define	NOT_AVAILABLE	"NA"
323 
324 /*
325  * Tuneables
326  */
327 #define	ENABLE	1
328 #define	DISABLE	0
329 
330 static	int	disk_high_warn_temperature	= DISK_HIGH_WARN_TEMPERATURE;
331 static	int	disk_low_warn_temperature	= DISK_LOW_WARN_TEMPERATURE;
332 static	int	disk_high_shutdown_temperature	=
333 						DISK_HIGH_SHUTDOWN_TEMPERATURE;
334 static	int	disk_low_shutdown_temperature	= DISK_LOW_SHUTDOWN_TEMPERATURE;
335 
336 static	int	disk_scan_interval		= DISK_SCAN_INTERVAL;
337 static	int	sensor_scan_interval		= SENSOR_SCAN_INTERVAL;
338 static	int	fan_scan_interval		= FAN_SCAN_INTERVAL;
339 
340 static int get_int_val(ptree_rarg_t *parg, void *buf);
341 static int set_int_val(ptree_warg_t *parg, const void *buf);
342 static int get_string_val(ptree_rarg_t *parg, void *buf);
343 static int set_string_val(ptree_warg_t *parg, const void *buf);
344 
345 static int 	shutdown_override	= 0;
346 static int	sensor_warning_interval	= SENSOR_WARNING_INTERVAL;
347 static int	sensor_warning_duration	= SENSOR_WARNING_DURATION;
348 static int	sensor_shutdown_interval = SENSOR_SHUTDOWN_INTERVAL;
349 static int	disk_warning_interval	= DISK_WARNING_INTERVAL;
350 static int	disk_warning_duration	= DISK_WARNING_DURATION;
351 static int 	disk_shutdown_interval	= DISK_SHUTDOWN_INTERVAL;
352 
353 static int	system_temp_monitor	= 1;	/* enabled */
354 static int	fan_monitor		= 1;	/* enabled */
355 static int	pm_monitor		= 1;	/* enabled */
356 int		disk_temp_monitor	= 1;	/* enabled */
357 
358 static char	shutdown_cmd[] = SHUTDOWN_CMD;
359 const char	*iofru_devname = I2C_DEVFS "/" IOFRU_DEV;
360 
361 env_tuneable_t tuneables[] = {
362 	{"system_temp-monitor", PICL_PTYPE_INT, &system_temp_monitor,
363 	    &get_int_val, &set_int_val, sizeof (int)},
364 
365 	{"fan-monitor", PICL_PTYPE_INT, &fan_monitor,
366 	    &get_int_val, &set_int_val, sizeof (int)},
367 
368 	{"pm-monitor", PICL_PTYPE_INT, &pm_monitor,
369 	    &get_int_val, &set_int_val, sizeof (int)},
370 
371 	{"shutdown-override", PICL_PTYPE_INT, &shutdown_override,
372 	    &get_int_val, &set_int_val, sizeof (int)},
373 
374 	{"sensor-warning-duration", PICL_PTYPE_INT,
375 	    &sensor_warning_duration,
376 	    &get_int_val, &set_int_val,
377 	    sizeof (int)},
378 
379 	{"disk-scan-interval", PICL_PTYPE_INT,
380 	    &disk_scan_interval,
381 	    &get_int_val, &set_int_val,
382 	    sizeof (int)},
383 
384 	{"fan-scan-interval", PICL_PTYPE_INT,
385 	    &fan_scan_interval,
386 	    &get_int_val, &set_int_val,
387 	    sizeof (int)},
388 
389 	{"sensor-scan-interval", PICL_PTYPE_INT,
390 	    &sensor_scan_interval,
391 	    &get_int_val, &set_int_val,
392 	    sizeof (int)},
393 
394 	{"sensor_warning-interval", PICL_PTYPE_INT, &sensor_warning_interval,
395 	    &get_int_val, &set_int_val,
396 	    sizeof (int)},
397 
398 	{"sensor_shutdown-interval", PICL_PTYPE_INT, &sensor_shutdown_interval,
399 	    &get_int_val, &set_int_val,
400 	    sizeof (int)},
401 
402 	{"disk_warning-interval", PICL_PTYPE_INT, &disk_warning_interval,
403 	    &get_int_val, &set_int_val,
404 	    sizeof (int)},
405 
406 	{"disk_warning-duration", PICL_PTYPE_INT, &disk_warning_duration,
407 	    &get_int_val, &set_int_val,
408 	    sizeof (int)},
409 
410 	{"disk_shutdown-interval", PICL_PTYPE_INT, &disk_shutdown_interval,
411 	    &get_int_val, &set_int_val,
412 	    sizeof (int)},
413 
414 	{"shutdown-command", PICL_PTYPE_CHARSTRING, shutdown_cmd,
415 	    &get_string_val, &set_string_val,
416 	    sizeof (shutdown_cmd)},
417 
418 	{"monitor-disk-temp", PICL_PTYPE_INT, &disk_temp_monitor,
419 	    &get_int_val, &set_int_val, sizeof (int)},
420 
421 	{"disk-high-warn-temperature", PICL_PTYPE_INT,
422 	    &disk_high_warn_temperature, &get_int_val,
423 	    &set_int_val, sizeof (int)},
424 
425 	{"disk-low-warn-temperature", PICL_PTYPE_INT,
426 	    &disk_low_warn_temperature, &get_int_val,
427 	    &set_int_val, sizeof (int)},
428 
429 	{"disk-high-shutdown-temperature", PICL_PTYPE_INT,
430 	    &disk_high_shutdown_temperature, &get_int_val,
431 	    &set_int_val, sizeof (int)},
432 
433 	{"disk-low-shutdown-temperature", PICL_PTYPE_INT,
434 	    &disk_low_shutdown_temperature, &get_int_val,
435 	    &set_int_val, sizeof (int)},
436 
437 	{"verbose", PICL_PTYPE_INT, &env_debug,
438 	    &get_int_val, &set_int_val, sizeof (int)}
439 };
440 
441 /*
442  * We use this to figure out how many tuneables there are
443  * This is variable because the publishing routine needs this info
444  * in piclenvsetup.c
445  */
446 int	ntuneables = (sizeof (tuneables)/sizeof (tuneables[0]));
447 
448 /*
449  * Lookup fan and return a pointer to env_fan_t data structure.
450  */
451 env_fan_t *
452 fan_lookup(char *name)
453 {
454 	int		i;
455 	env_fan_t	*fanp;
456 
457 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
458 		if (strcmp(fanp->name, name) == 0)
459 			return (fanp);
460 	}
461 	return (NULL);
462 }
463 
464 /*
465  * Lookup sensor and return a pointer to env_sensor_t data structure.
466  */
467 env_sensor_t *
468 sensor_lookup(char *name)
469 {
470 	env_sensor_t	*sensorp;
471 	int		i;
472 
473 	for (i = 0; i < N_ENVD_SENSORS; ++i) {
474 		sensorp = envd_sensors[i];
475 		if (strcmp(sensorp->name, name) == 0)
476 			return (sensorp);
477 	}
478 	return (NULL);
479 }
480 
481 /*
482  * Lookup disk and return a pointer to env_disk_t data structure.
483  */
484 env_disk_t *
485 disk_lookup(char *name)
486 {
487 	int		i;
488 	env_disk_t	*diskp;
489 
490 	for (i = 0; (diskp = envd_disks[i]) != NULL; i++) {
491 		if (strncmp(diskp->name, name, strlen(name)) == 0)
492 			return (diskp);
493 	}
494 	return (NULL);
495 }
496 
497 /*
498  * Get current temperature
499  * Returns -1 on error, 0 if successful
500  */
501 int
502 get_temperature(env_sensor_t *sensorp, tempr_t *temp)
503 {
504 	int	fd = sensorp->fd;
505 	int	retval = 0;
506 
507 	if (fd == -1)
508 		retval = -1;
509 	else if (ioctl(fd, PIC_GET_TEMPERATURE, temp) != 0) {
510 
511 		retval = -1;
512 
513 		sensorp->error++;
514 
515 		if (sensorp->error == MAX_SENSOR_RETRIES) {
516 			envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_FAIL,
517 			    sensorp->name, errno, strerror(errno));
518 		}
519 
520 		total_temp_retries++;
521 		(void) sleep(1);
522 
523 	} else if (sensorp->error != 0) {
524 		if (sensorp->error >= MAX_SENSOR_RETRIES) {
525 			envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_OK,
526 			    sensorp->name);
527 		}
528 
529 		sensorp->error = 0;
530 
531 		if (total_temp_retries && env_debug) {
532 			envd_log(LOG_WARNING,
533 			    "Total retries for sensors = %d",
534 			    total_temp_retries);
535 		}
536 	}
537 
538 	return (retval);
539 }
540 
541 /*
542  * Get current disk temperature
543  * Returns -1 on error, 0 if successful
544  */
545 int
546 disk_temperature(env_disk_t *diskp, tempr_t *temp)
547 {
548 	int	retval = 0;
549 
550 	if (diskp == NULL)
551 		retval = -1;
552 	else
553 		*temp = diskp->current_temp;
554 
555 	return (retval);
556 }
557 
558 /*
559  * Get current fan speed
560  * This function returns a RPM value for fanspeed
561  * in fanspeedp.
562  * Returns -1 on error, 0 if successful
563  */
564 int
565 get_fan_speed(env_fan_t *fanp, fanspeed_t *fanspeedp)
566 {
567 	uint8_t tach;
568 	int	real_tach;
569 	int	retries;
570 
571 	if (fanp->fd == -1)
572 		return (-1);
573 
574 	if (has_fan_failed(fanp)) {
575 		*fanspeedp = 0;
576 		return (0);
577 	}
578 
579 	/* try to read the fan information */
580 	for (retries = 0; retries <= MAX_FAN_RETRIES; retries++) {
581 		if (ioctl(fanp->fd, PIC_GET_FAN_SPEED, &tach) == 0)
582 			break;
583 		(void) sleep(1);
584 	}
585 
586 	total_fan_retries += retries;
587 	if (retries == MAX_FAN_RETRIES)
588 		return (-1);
589 
590 	if (total_fan_retries && env_debug) {
591 		envd_log(LOG_WARNING, "total retries for fan = %d",
592 		    total_fan_retries);
593 	}
594 
595 	real_tach = tach << 8;
596 	*fanspeedp = TACH_TO_RPM(real_tach);
597 	return (0);
598 }
599 
600 /*
601  * Set fan speed
602  * This function accepts a percentage of fan speed
603  * from 0-100 and programs the HW monitor fans to the corresponding
604  * fanspeed value.
605  * Returns -1 on error, -2 on invalid args passed, 0 if successful
606  */
607 int
608 set_fan_speed(env_fan_t *fanp, fanspeed_t fanspeed)
609 {
610 	uint8_t	speed;
611 
612 	if (fanp->fd == -1)
613 		return (-1);
614 
615 	if (fanspeed < 0 || fanspeed > 100)
616 		return (-2);
617 
618 	speed = fanspeed;
619 	if (ioctl(fanp->fd, PIC_SET_FAN_SPEED, &speed) != 0)
620 		return (-1);
621 
622 	return (0);
623 }
624 
625 /*
626  * close all fan devices
627  */
628 static void
629 envd_close_fans(void)
630 {
631 	int		i;
632 	env_fan_t	*fanp;
633 
634 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
635 		if (fanp->fd != -1) {
636 			(void) close(fanp->fd);
637 			fanp->fd = -1;
638 		}
639 	}
640 }
641 
642 /*
643  * Close sensor devices and freeup resources
644  */
645 static void
646 envd_close_sensors(void)
647 {
648 	env_sensor_t	*sensorp;
649 	int		i;
650 
651 	for (i = 0; i < N_ENVD_SENSORS; ++i) {
652 		sensorp = envd_sensors[i];
653 		if (sensorp->fd != -1) {
654 			(void) close(sensorp->fd);
655 			sensorp->fd = -1;
656 		}
657 	}
658 }
659 
660 /*
661  * Open fan devices and initialize per fan data structure.
662  */
663 static int
664 envd_setup_fans(void)
665 {
666 	int		i, fd;
667 	env_fan_t	*fanp;
668 	int		fancnt = 0;
669 	picl_nodehdl_t tnodeh;
670 
671 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
672 		fanp->last_status = FAN_OK;
673 
674 		/* Make sure cpu0/1 present for validating cpu fans */
675 		if (fanp->id == CPU0_FAN_ID) {
676 			if (ptree_get_node_by_path(CPU0_PATH, &tnodeh) !=
677 			    PICL_SUCCESS) {
678 					if (env_debug) {
679 						envd_log(LOG_ERR,
680 					"get node by path failed for %s\n",
681 						    CPU0_PATH);
682 					}
683 					fanp->present = B_FALSE;
684 					continue;
685 			}
686 		}
687 		if (fanp->id == CPU1_FAN_ID) {
688 			if (ptree_get_node_by_path(CPU1_PATH, &tnodeh) !=
689 			    PICL_SUCCESS) {
690 					if (env_debug) {
691 						envd_log(LOG_ERR,
692 				"get node by path failed for %s\n", CPU0_PATH);
693 					}
694 					fanp->present = B_FALSE;
695 					continue;
696 			}
697 		}
698 		if ((fd = open(fanp->devfs_path, O_RDWR)) == -1) {
699 			envd_log(LOG_CRIT,
700 			    ENV_FAN_OPEN_FAIL, fanp->name,
701 			    fanp->devfs_path, errno, strerror(errno));
702 			fanp->present = B_FALSE;
703 			continue;
704 		}
705 		fanp->fd = fd;
706 		fanp->present = B_TRUE;
707 		fancnt++;
708 	}
709 
710 	if (fancnt == 0)
711 		return (-1);
712 
713 	return (0);
714 }
715 
716 static int
717 envd_setup_disks(void)
718 {
719 	int	ret, i, page_index, page_len;
720 	picl_nodehdl_t tnodeh;
721 	env_disk_t	*diskp;
722 	uint_t	vendor_id;
723 	uint_t	device_id;
724 	uchar_t	log_page[256];
725 
726 	if (ptree_get_node_by_path(SCSI_CONTROLLER_NODE_PATH,
727 	    &tnodeh) != PICL_SUCCESS) {
728 		if (env_debug) {
729 			envd_log(LOG_ERR, "On-Board SCSI controller %s "
730 			    "not found in the system.\n",
731 			    SCSI_CONTROLLER_NODE_PATH);
732 		}
733 		return (-1);
734 	}
735 
736 	if ((ret = ptree_get_propval_by_name(tnodeh, VENDOR_ID,
737 	    &vendor_id, sizeof (vendor_id))) != 0) {
738 		if (env_debug) {
739 			envd_log(LOG_ERR, "Error in getting vendor-id "
740 			    "for SCSI controller. ret = %d errno = 0x%d\n",
741 			    ret, errno);
742 		}
743 		return (-1);
744 	}
745 	if ((ret = ptree_get_propval_by_name(tnodeh, DEVICE_ID,
746 	    &device_id, sizeof (device_id))) != 0) {
747 		if (env_debug) {
748 			envd_log(LOG_ERR, "Error in getting device-id "
749 			    "for SCSI controller. ret = %d errno = 0x%d\n",
750 			    ret, errno);
751 		}
752 		return (-1);
753 	}
754 
755 	/*
756 	 * We have found LSI1064 SCSi controller onboard.
757 	 */
758 	for (i = 0; (diskp = envd_disks[i]) != NULL; i++) {
759 		if (ptree_get_node_by_path(diskp->nodepath,
760 		    &tnodeh) != PICL_SUCCESS) {
761 			diskp->present = B_FALSE;
762 			if (env_debug) {
763 				envd_log(LOG_ERR,
764 				    "DISK %d: %s not found in the system.\n",
765 				    diskp->id, diskp->nodepath);
766 			}
767 			continue;
768 		}
769 		if ((diskp->fd = open(diskp->devfs_path, O_RDONLY)) == -1) {
770 			diskp->present = B_FALSE;
771 			if (env_debug) {
772 				envd_log(LOG_ERR,
773 				    "Error in opening %s errno = 0x%x\n",
774 				    diskp->devfs_path, errno);
775 			}
776 			continue;
777 		}
778 		diskp->present = B_TRUE;
779 		diskp->tpage_supported = B_FALSE;
780 		diskp->smart_supported = B_FALSE;
781 		diskp->warning_tstamp = 0;
782 		diskp->shutdown_tstamp = 0;
783 		diskp->high_warning = disk_high_warn_temperature;
784 		diskp->low_warning = disk_low_warn_temperature;
785 		diskp->high_shutdown = disk_high_shutdown_temperature;
786 		diskp->low_shutdown = disk_low_shutdown_temperature;
787 		/*
788 		 * Find out if the Temperature page is supported by the disk.
789 		 */
790 		if (scsi_log_sense(diskp, SUPPORTED_LPAGES, log_page,
791 		    sizeof (log_page), 1) == 0) {
792 
793 			page_len = ((log_page[2] << 8) & 0xFF00) | log_page[3];
794 
795 			for (page_index = LOGPAGEHDRSIZE;
796 			    page_index < page_len + LOGPAGEHDRSIZE;
797 			    page_index++) {
798 				if (log_page[page_index] != TEMPERATURE_PAGE)
799 					continue;
800 
801 				diskp->tpage_supported = B_TRUE;
802 				if (env_debug) {
803 					envd_log(LOG_ERR,
804 					    "tpage supported for %s\n",
805 					    diskp->nodepath);
806 				}
807 			}
808 		}
809 		/*
810 		 * If the temp log page failed, we can check if this is
811 		 * a SATA drive and attempt to read the temperature
812 		 * using the SMART interface.
813 		 */
814 		if (diskp->tpage_supported != B_TRUE) {
815 			uchar_t iec_page[IEC_PAGE_SIZE];
816 
817 			if (env_debug)
818 				envd_log(LOG_ERR, "Turning on SMART\n");
819 
820 			(void) memset(iec_page, 0, sizeof (iec_page));
821 			iec_page[0] = IEC_PAGE;	/* SMART PAGE */
822 			iec_page[1] = 0xa;	/* length */
823 			/* Notification, only when requested */
824 			iec_page[3] = REPORT_ON_REQUEST;
825 
826 			ret = scsi_mode_select(diskp, IEC_PAGE,
827 			    iec_page, sizeof (iec_page));
828 
829 			/*
830 			 * Since we know this is a SMART capable
831 			 * drive, we will try to set the page and
832 			 * determine if the drive is not capable
833 			 * of reading the TEMP page when we
834 			 * try to read the temperature and disable
835 			 * it then. We do not fail when reading
836 			 * or writing this page because we will
837 			 * determine the SMART capabilities
838 			 * when reading the temperature.
839 			 */
840 			if ((ret != 0) && (env_debug)) {
841 				envd_log(LOG_ERR,
842 				    "Failed to set mode page");
843 			}
844 
845 			diskp->smart_supported = B_TRUE;
846 			diskp->tpage_supported = B_TRUE;
847 		}
848 
849 		if (get_disk_temp(diskp) < 0) {
850 			envd_log(LOG_ERR, " error reading temperature of:%s\n",
851 			    diskp->name);
852 		} else if (env_debug) {
853 			envd_log(LOG_ERR, "%s: temperature = %d\n",
854 			    diskp->name, diskp->current_temp);
855 		}
856 
857 	}
858 
859 	return (0);
860 }
861 
862 static int
863 envd_es_setup(void)
864 {
865 	seeprom_scn_t	scn_hdr;
866 	seeprom_seg_t	seg_hdr;
867 	es_data_t	*envseg;
868 	es_sensor_t	*sensorp;
869 	int		i, fd, id;
870 	int		envseg_len, esd_len;
871 	char		*envsegp;
872 
873 	/*
874 	 * Open the front io fru
875 	 */
876 	if ((fd = open(iofru_devname, O_RDONLY)) == -1) {
877 		envd_log(LOG_ERR, ENV_FRU_OPEN_FAIL, iofru_devname, errno);
878 		return (-1);
879 	}
880 
881 	/*
882 	 * Read section header from the fru SEEPROM
883 	 */
884 	if (lseek(fd, SSCN_OFFSET, SEEK_SET) == (off_t)-1 ||
885 	    read(fd, &scn_hdr, sizeof (scn_hdr)) != sizeof (scn_hdr)) {
886 		envd_log(LOG_ERR, ENV_FRU_BAD_ENVSEG, iofru_devname);
887 		(void) close(fd);
888 		return (-1);
889 	}
890 	if ((scn_hdr.sscn_tag != SSCN_TAG) ||
891 	    (GET_UNALIGN16(&scn_hdr.sscn_ver) != SSCN_VER)) {
892 		envd_log(LOG_ERR, ENV_FRU_BAD_SCNHDR, scn_hdr.sscn_tag,
893 		    GET_UNALIGN16(&scn_hdr.sscn_ver));
894 		(void) close(fd);
895 		return (-1);
896 	}
897 
898 	/*
899 	 * Locate environmental segment
900 	 */
901 	for (i = 0; i < scn_hdr.sscn_nsegs; i++) {
902 		if (read(fd, &seg_hdr, sizeof (seg_hdr)) != sizeof (seg_hdr)) {
903 			envd_log(LOG_ERR, ENV_FRU_BAD_ENVSEG, iofru_devname);
904 			(void) close(fd);
905 			return (-1);
906 		}
907 
908 		if (env_debug) {
909 			envd_log(LOG_INFO,
910 			    "Seg name: %x off:%x len:%x\n",
911 			    GET_UNALIGN16(&seg_hdr.sseg_name),
912 			    GET_UNALIGN16(&seg_hdr.sseg_off),
913 			    GET_UNALIGN16(&seg_hdr.sseg_len));
914 		}
915 
916 		if (GET_UNALIGN16(&seg_hdr.sseg_name) == ENVSEG_NAME)
917 			break;
918 	}
919 	if (i == scn_hdr.sscn_nsegs) {
920 		envd_log(LOG_ERR, ENV_FRU_BAD_ENVSEG, iofru_devname);
921 		(void) close(fd);
922 		return (-1);
923 	}
924 
925 	/*
926 	 * Read environmental segment
927 	 */
928 	envseg_len = GET_UNALIGN16(&seg_hdr.sseg_len);
929 	if ((envseg = malloc(envseg_len)) == NULL) {
930 		envd_log(LOG_ERR, ENV_FRU_NOMEM_FOR_SEG, envseg_len);
931 		(void) close(fd);
932 		return (-1);
933 	}
934 
935 	if (lseek(fd, (off_t)GET_UNALIGN16(&seg_hdr.sseg_off),
936 	    SEEK_SET) == (off_t)-1 ||
937 	    read(fd, envseg, envseg_len) != envseg_len) {
938 		envd_log(LOG_ERR, ENV_FRU_BAD_ENVSEG, iofru_devname);
939 		free(envseg);
940 		(void) close(fd);
941 		return (-1);
942 	}
943 
944 	/*
945 	 * Check environmental segment data for consistency
946 	 */
947 	esd_len = sizeof (*envseg) +
948 	    (envseg->esd_nsensors - 1) * sizeof (envseg->esd_sensors[0]);
949 	if (envseg->esd_ver != ENVSEG_VERSION || envseg_len < esd_len) {
950 		envd_log(LOG_ERR, ENV_FRU_BAD_ENVSEG, iofru_devname);
951 		free(envseg);
952 		(void) close(fd);
953 		return (-1);
954 	}
955 
956 	/*
957 	 * Process environmental segment data
958 	 */
959 	if (envseg->esd_nsensors > MAX_SENSORS) {
960 		envd_log(LOG_ERR, ENV_FRU_BAD_ENVSEG, iofru_devname);
961 		free(envseg);
962 		(void) close(fd);
963 		return (-1);
964 	}
965 
966 	sensorp = &(envseg->esd_sensors[0]);
967 	envsegp = (char *)envseg;
968 	for (i = 0; i < envseg->esd_nsensors; i++) {
969 		uint32_t ess_id;
970 
971 		(void) memcpy(&ess_id,
972 			sensorp->ess_id, sizeof (sensorp->ess_id));
973 
974 		if (env_debug) {
975 			envd_log(LOG_INFO, "\n Sensor Id %x offset %x",
976 			    ess_id, sensorp->ess_off);
977 		}
978 		if (ess_id >= MAX_SENSORS) {
979 			envd_log(LOG_ERR, ENV_FRU_BAD_ENVSEG, iofru_devname);
980 			free(envseg);
981 			(void) close(fd);
982 			return (-1);
983 		}
984 		(void) memcpy(&sensor_ctl[ess_id], &envsegp[sensorp->ess_off],
985 		    sizeof (es_sensor_blk_t));
986 
987 		sensorp++;
988 	}
989 
990 	/*
991 	 * Match sensor/ES id and point to correct data based on IDs
992 	 */
993 	for (i = 0; i < N_ENVD_SENSORS; i++) {
994 		id = envd_sensors[i]->id;
995 		envd_sensors[i]->es = &sensor_ctl[id];
996 	}
997 
998 	/*
999 	 * Cleanup and return
1000 	 */
1001 	free(envseg);
1002 	(void) close(fd);
1003 
1004 	return (0);
1005 }
1006 
1007 static void
1008 envd_es_default_setup(void)
1009 {
1010 	int	i, id;
1011 
1012 	for (i = 0; i < N_ENVD_SENSORS; i++) {
1013 		id = envd_sensors[i]->id;
1014 		envd_sensors[i]->es = &sensor_default_ctl[id];
1015 	}
1016 }
1017 
1018 /*
1019  * Open temperature sensor devices and initialize per sensor data structure.
1020  */
1021 static int
1022 envd_setup_sensors(void)
1023 {
1024 	env_sensor_t	*sensorp;
1025 	int		sensorcnt = 0;
1026 	int		i;
1027 	picl_nodehdl_t	tnodeh;
1028 
1029 	for (i = 0; i < N_ENVD_SENSORS; i++) {
1030 		if (env_debug)
1031 			envd_log(LOG_ERR, "scanning sensor %d\n", i);
1032 
1033 		sensorp = envd_sensors[i];
1034 
1035 		/* Initialize sensor's initial state */
1036 		sensorp->shutdown_initiated = B_FALSE;
1037 		sensorp->warning_tstamp = 0;
1038 		sensorp->shutdown_tstamp = 0;
1039 		sensorp->error = 0;
1040 
1041 		/* Make sure cpu0/1 sensors are present */
1042 		if (sensorp->id == CPU0_SENSOR_ID) {
1043 			if (ptree_get_node_by_path(CPU0_PATH, &tnodeh) !=
1044 			    PICL_SUCCESS) {
1045 				if (env_debug) {
1046 					envd_log(LOG_ERR,
1047 					    "get node by path failed for %s\n",
1048 					    CPU0_PATH);
1049 				}
1050 				sensorp->present = B_FALSE;
1051 				continue;
1052 			}
1053 		}
1054 		if (sensorp->id == CPU1_SENSOR_ID) {
1055 			if (ptree_get_node_by_path(CPU1_PATH, &tnodeh) !=
1056 			    PICL_SUCCESS) {
1057 				if (env_debug) {
1058 					envd_log(LOG_ERR,
1059 					    "get node by path failed for %s\n",
1060 					    CPU1_PATH);
1061 				}
1062 				sensorp->present = B_FALSE;
1063 				continue;
1064 			}
1065 		}
1066 
1067 		sensorp->fd = open(sensorp->devfs_path, O_RDWR);
1068 		if (sensorp->fd == -1) {
1069 			if (env_debug) {
1070 				envd_log(LOG_ERR, ENV_SENSOR_OPEN_FAIL,
1071 				    sensorp->name, sensorp->devfs_path,
1072 				    errno, strerror(errno));
1073 			}
1074 			sensorp->present = B_FALSE;
1075 			continue;
1076 		}
1077 
1078 		/*
1079 		 * Determine if the front panel is attached, we want the
1080 		 * information if it exists, but should not shut down
1081 		 * the system if it is removed.
1082 		 */
1083 		if (sensorp->id == FRONT_PANEL_SENSOR_ID) {
1084 			tempr_t temp;
1085 			int	tries;
1086 
1087 			for (tries = 0; tries < MAX_SENSOR_RETRIES; tries++) {
1088 				if (ioctl(sensorp->fd, PIC_GET_TEMPERATURE,
1089 				    &temp) == 0) {
1090 					break;
1091 				}
1092 				(void) sleep(1);
1093 			}
1094 			if (tries == MAX_SENSOR_RETRIES)
1095 				sensorp->present = B_FALSE;
1096 		}
1097 
1098 		sensorp->present = B_TRUE;
1099 		sensorcnt++;
1100 	}
1101 
1102 	if (sensorcnt == 0)
1103 		return (-1);
1104 
1105 	return (0);
1106 }
1107 
1108 /* ARGSUSED */
1109 static void *
1110 pmthr(void *args)
1111 {
1112 	pm_state_change_t	pmstate;
1113 	char			physpath[PATH_MAX];
1114 	int			pre_lpstate;
1115 	uint8_t			estar_state;
1116 	int			env_monitor_fd;
1117 
1118 	pmstate.physpath = physpath;
1119 	pmstate.size = sizeof (physpath);
1120 	cur_lpstate = 0;
1121 	pre_lpstate = 1;
1122 
1123 	pm_fd = open(PM_DEVICE, O_RDWR);
1124 	if (pm_fd == -1) {
1125 		envd_log(LOG_ERR, PM_THREAD_EXITING, errno, strerror(errno));
1126 		return (NULL);
1127 	}
1128 	for (;;) {
1129 		/*
1130 		 * Get PM state change events to check if the system
1131 		 * is in lowest power state and inform PIC which controls
1132 		 * fan speeds.
1133 		 *
1134 		 * To minimize polling, we use the blocking interface
1135 		 * to get the power state change event here.
1136 		 */
1137 		if (ioctl(pm_fd, PM_GET_STATE_CHANGE_WAIT, &pmstate) != 0) {
1138 			if (errno != EINTR)
1139 				break;
1140 			continue;
1141 		}
1142 
1143 		do {
1144 			if (env_debug)  {
1145 				envd_log(LOG_INFO,
1146 				"pmstate event:0x%x flags:%x"
1147 				"comp:%d oldval:%d newval:%d path:%s\n",
1148 				    pmstate.event, pmstate.flags,
1149 				    pmstate.component,
1150 				    pmstate.old_level,
1151 				    pmstate.new_level,
1152 				    pmstate.physpath);
1153 			}
1154 			cur_lpstate =
1155 			    (pmstate.flags & PSC_ALL_LOWEST) ? 1 : 0;
1156 		} while (ioctl(pm_fd, PM_GET_STATE_CHANGE, &pmstate) == 0);
1157 
1158 		if (pre_lpstate != cur_lpstate) {
1159 			pre_lpstate = cur_lpstate;
1160 			estar_state = (cur_lpstate & 0x1);
1161 			if (env_debug)
1162 				envd_log(LOG_ERR,
1163 				    "setting PIC ESTAR SATE to %x\n",
1164 				    estar_state);
1165 
1166 			env_monitor_fd = open(ENV_MONITOR_DEVFS, O_RDWR);
1167 			if (env_monitor_fd != -1) {
1168 				if (ioctl(env_monitor_fd, PIC_SET_ESTAR_MODE,
1169 				    &estar_state) < 0) {
1170 					if (env_debug)
1171 						envd_log(LOG_ERR,
1172 					"unable to set ESTAR_MODE in PIC\n");
1173 				}
1174 				(void) close(env_monitor_fd);
1175 			} else {
1176 				if (env_debug)
1177 					envd_log(LOG_ERR,
1178 				"Failed to open %s\n",
1179 					    ENV_MONITOR_DEVFS);
1180 			}
1181 		}
1182 	}
1183 
1184 	/*NOTREACHED*/
1185 	return (NULL);
1186 }
1187 
1188 /*
1189  * This is env thread which monitors the current temperature when
1190  * warning threshold is exceeded. The job is to make sure it does
1191  * not execced/decrease shutdown threshold. If it does it will start
1192  * forced shutdown to avoid reaching hardware poweroff via THERM interrupt.
1193  */
1194 /*ARGSUSED*/
1195 static void *
1196 system_temp_thr(void *args)
1197 {
1198 	char syscmd[BUFSIZ];
1199 	char msgbuf[BUFSIZ];
1200 	timespec_t	to;
1201 	int	ret, i;
1202 	env_sensor_t	*sensorp;
1203 	pthread_mutex_t	env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
1204 	pthread_cond_t	env_monitor_cv = PTHREAD_COND_INITIALIZER;
1205 	time_t	ct;
1206 	tempr_t  temp;
1207 
1208 	for (;;) {
1209 		/*
1210 		 * Sleep for specified seconds before issuing IOCTL
1211 		 * again.
1212 		 */
1213 		(void) pthread_mutex_lock(&env_monitor_mutex);
1214 		ret = pthread_cond_reltimedwait_np(&env_monitor_cv,
1215 		    &env_monitor_mutex, &to);
1216 		to.tv_sec = sensor_scan_interval;
1217 		to.tv_nsec = 0;
1218 		if (ret != ETIMEDOUT) {
1219 			(void) pthread_mutex_unlock(&env_monitor_mutex);
1220 			continue;
1221 		}
1222 
1223 		(void) pthread_mutex_unlock(&env_monitor_mutex);
1224 		for (i = 0; i < N_ENVD_SENSORS; i++) {
1225 			sensorp = envd_sensors[i];
1226 			if (sensorp->present == B_FALSE)
1227 				continue;
1228 			if (get_temperature(sensorp, &temp) == -1)
1229 				continue;
1230 
1231 			sensorp->cur_temp = temp;
1232 			if (env_debug) {
1233 				envd_log(LOG_ERR,
1234 				"%s temp = %d",
1235 				    sensorp->name, sensorp->cur_temp);
1236 			}
1237 
1238 			/*
1239 			 * If this sensor already triggered system shutdown,
1240 			 * don't log any more shutdown/warning messages for it.
1241 			 */
1242 			if (sensorp->shutdown_initiated)
1243 				continue;
1244 
1245 			/*
1246 			 * Check for the temperature in warning and shutdown
1247 			 * range and take appropriate action.
1248 			 */
1249 			if (SENSOR_TEMP_IN_WARNING_RANGE(sensorp->cur_temp,
1250 			    sensorp)) {
1251 				/*
1252 				 * Check if the temperature has been in
1253 				 * warning range during last
1254 				 * sensor_warning_duration interval. If so,
1255 				 * the temperature is truly in warning range
1256 				 * and we need to log a warning message, but
1257 				 * no more than once every
1258 				 * sensor_warning_interval seconds.
1259 				 */
1260 				time_t	wtstamp = sensorp->warning_tstamp;
1261 
1262 				ct = (time_t)(gethrtime() / NANOSEC);
1263 				if (sensorp->warning_start == 0)
1264 					sensorp->warning_start = ct;
1265 				if (((ct - sensorp->warning_start) >=
1266 				    sensor_warning_duration) &&
1267 				    (wtstamp == 0 || (ct - wtstamp) >=
1268 				    sensor_warning_interval)) {
1269 					envd_log(LOG_CRIT, ENV_WARNING_MSG,
1270 					    sensorp->name, sensorp->cur_temp,
1271 					    sensorp->es->esb_low_warning,
1272 					    sensorp->es->esb_high_warning);
1273 					sensorp->warning_tstamp = ct;
1274 				}
1275 			} else if (sensorp->warning_start != 0)
1276 				sensorp->warning_start = 0;
1277 
1278 			if (!shutdown_override &&
1279 			    SENSOR_TEMP_IN_SHUTDOWN_RANGE(sensorp->cur_temp,
1280 			    sensorp)) {
1281 				ct = (time_t)(gethrtime() / NANOSEC);
1282 				if (sensorp->shutdown_tstamp == 0)
1283 					sensorp->shutdown_tstamp = ct;
1284 
1285 				/*
1286 				 * Shutdown the system if the temperature
1287 				 * remains in the shutdown range for over
1288 				 * sensor_shutdown_interval seconds.
1289 				 */
1290 				if ((ct - sensorp->shutdown_tstamp) >=
1291 				    sensor_shutdown_interval) {
1292 					/*
1293 					 * Log error
1294 					 */
1295 					sensorp->shutdown_initiated = B_TRUE;
1296 					(void) snprintf(msgbuf, sizeof (msgbuf),
1297 					    ENV_SHUTDOWN_MSG, sensorp->name,
1298 					    sensorp->cur_temp,
1299 					    sensorp->es->esb_low_shutdown,
1300 					    sensorp->es->esb_high_shutdown);
1301 					envd_log(LOG_ALERT, msgbuf);
1302 
1303 					/*
1304 					 * Shutdown the system (only once)
1305 					 */
1306 					if (system_shutdown_started ==
1307 					    B_FALSE) {
1308 						(void) snprintf(syscmd,
1309 						    sizeof (syscmd),
1310 						    "%s \"%s\"", shutdown_cmd,
1311 						    msgbuf);
1312 
1313 						envd_log(LOG_ALERT, syscmd);
1314 						system_shutdown_started =
1315 						    B_TRUE;
1316 
1317 						(void) system(syscmd);
1318 					}
1319 				}
1320 			} else if (sensorp->shutdown_tstamp != 0)
1321 				sensorp->shutdown_tstamp = 0;
1322 		}
1323 	}	/* end of forever loop */
1324 
1325 	/*NOTREACHED*/
1326 	return (NULL);
1327 }
1328 
1329 static int
1330 scsi_log_sense(env_disk_t *diskp, uchar_t page_code, void *pagebuf,
1331 		uint16_t pagelen, int page_control)
1332 {
1333 	struct uscsi_cmd	ucmd_buf;
1334 	uchar_t		cdb_buf[CDB_GROUP1];
1335 	struct	scsi_extended_sense	sense_buf;
1336 	int	ret_val;
1337 
1338 	bzero(&cdb_buf, sizeof (cdb_buf));
1339 	bzero(&ucmd_buf, sizeof (ucmd_buf));
1340 	bzero(&sense_buf, sizeof (sense_buf));
1341 
1342 	cdb_buf[0] = SCMD_LOG_SENSE_G1;
1343 
1344 	/*
1345 	 * For SATA we need to have the current threshold value set.
1346 	 * For SAS drives we can use the current cumulative value.
1347 	 * This is set for non-SMART drives, by passing a non-zero
1348 	 * page_control.
1349 	 */
1350 	if (page_control)
1351 		cdb_buf[2] = (0x01 << 6) | page_code;
1352 	else
1353 		cdb_buf[2] = page_code;
1354 
1355 	cdb_buf[7] = (uchar_t)((pagelen & 0xFF00) >> 8);
1356 	cdb_buf[8] = (uchar_t)(pagelen  & 0x00FF);
1357 
1358 	ucmd_buf.uscsi_cdb = (char *)cdb_buf;
1359 	ucmd_buf.uscsi_cdblen = sizeof (cdb_buf);
1360 	ucmd_buf.uscsi_bufaddr = (caddr_t)pagebuf;
1361 	ucmd_buf.uscsi_buflen = pagelen;
1362 	ucmd_buf.uscsi_rqbuf = (caddr_t)&sense_buf;
1363 	ucmd_buf.uscsi_rqlen = sizeof (struct scsi_extended_sense);
1364 	ucmd_buf.uscsi_flags = USCSI_RQENABLE | USCSI_READ | USCSI_SILENT;
1365 	ucmd_buf.uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
1366 
1367 	ret_val = ioctl(diskp->fd, USCSICMD, ucmd_buf);
1368 	if ((ret_val == 0) && (ucmd_buf.uscsi_status == 0)) {
1369 		if (env_debug)
1370 			envd_log(LOG_ERR,
1371 		"log sense command for page_code 0x%x succeeded\n", page_code);
1372 		return (ret_val);
1373 	}
1374 	if (env_debug)
1375 		envd_log(LOG_ERR, "log sense command for %s failed. "
1376 		    "page_code 0x%x ret_val = 0x%x "
1377 		    "status = 0x%x errno = 0x%x\n", diskp->name, page_code,
1378 		    ret_val, ucmd_buf.uscsi_status, errno);
1379 
1380 	return (1);
1381 }
1382 
1383 
1384 static int
1385 get_disk_temp(env_disk_t *diskp)
1386 {
1387 	int	ret;
1388 	uchar_t	tpage[256];
1389 
1390 	if (diskp->smart_supported == B_TRUE) {
1391 		smart_structure	smartpage;
1392 		smart_attribute	*temp_attrib = NULL;
1393 		uint8_t		checksum;
1394 		uint8_t		*index;
1395 		int		i;
1396 
1397 		bzero(&smartpage, sizeof (smartpage));
1398 
1399 		ret = scsi_log_sense(diskp, GET_SMART_INFO,
1400 		    &smartpage, sizeof (smartpage), 0);
1401 
1402 		if (ret != 0) {
1403 			diskp->current_temp = DISK_INVALID_TEMP;
1404 			diskp->ref_temp = DISK_INVALID_TEMP;
1405 			return (-1);
1406 		}
1407 
1408 		/*
1409 		 * verify the checksum of the data. A 2's compliment
1410 		 * of the result addition of the is stored in the
1411 		 * last byte. The sum of all the checksum should be
1412 		 * 0. If the checksum is bad, return an error for
1413 		 * this iteration.
1414 		 */
1415 		index = (uint8_t *)&smartpage;
1416 
1417 		for (i = checksum = 0; i < 512; i++)
1418 			checksum += index[i];
1419 
1420 		if ((checksum != 0) && env_debug) {
1421 			envd_log(LOG_ERR,
1422 			    "SMART checksum error! 0x%x\n", checksum);
1423 
1424 			/*
1425 			 * We got bad data back from the drive, fail this
1426 			 * time around and picl will retry again. If this
1427 			 * continues to fail picl will give this drive a
1428 			 * failed status.
1429 			 */
1430 			diskp->current_temp = DISK_INVALID_TEMP;
1431 			diskp->ref_temp = DISK_INVALID_TEMP;
1432 
1433 			return (-1);
1434 		}
1435 
1436 		/*
1437 		 * Scan through the various SMART data and look for
1438 		 * the complete drive temp.
1439 		 */
1440 
1441 		for (i = 0; (i < SMART_FIELDS) &&
1442 		    (smartpage.attribute[i].id != 0) &&
1443 		    (temp_attrib == NULL); i++) {
1444 
1445 			if (smartpage.attribute[i].id == HDA_TEMP) {
1446 				temp_attrib = &smartpage.attribute[i];
1447 			}
1448 		}
1449 
1450 		/*
1451 		 * If we dont find any temp SMART attributes, this drive
1452 		 * does not support this page, disable temp checking
1453 		 * for this drive.
1454 		 */
1455 		if (temp_attrib == NULL) {
1456 
1457 			/*
1458 			 * If the checksum is valid, the temp. attributes are
1459 			 * not supported, disable this drive from temp.
1460 			 * checking.
1461 			 */
1462 			if (env_debug)
1463 				envd_log(LOG_ERR,
1464 				    "Temp ATTRIBUTE not supported\n");
1465 			diskp->smart_supported = B_FALSE;
1466 			diskp->tpage_supported = B_FALSE;
1467 			diskp->current_temp = DISK_INVALID_TEMP;
1468 			diskp->ref_temp = DISK_INVALID_TEMP;
1469 
1470 			return (-1);
1471 		}
1472 
1473 		if (env_debug) {
1474 			envd_log(LOG_ERR, "flags = 0x%x%x,curr = 0x%x,"
1475 			"data = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
1476 			temp_attrib->flags[0], temp_attrib->flags[1],
1477 			temp_attrib->raw_data[0], temp_attrib->raw_data[1],
1478 			temp_attrib->raw_data[2], temp_attrib->raw_data[3],
1479 			temp_attrib->raw_data[4], temp_attrib->raw_data[5],
1480 			temp_attrib->raw_data[6], temp_attrib->raw_data[7]);
1481 		}
1482 		if (temp_attrib->raw_data[1] != 0xFF) {
1483 			diskp->current_temp = temp_attrib->raw_data[2];
1484 			diskp->ref_temp	= temp_attrib->raw_data[2];
1485 		} else {
1486 			diskp->ref_temp = DISK_INVALID_TEMP;
1487 			diskp->current_temp = DISK_INVALID_TEMP;
1488 
1489 			return (-1);
1490 		}
1491 
1492 	} else {
1493 		ret = scsi_log_sense(diskp, TEMPERATURE_PAGE, tpage,
1494 		    sizeof (tpage), 1);
1495 
1496 		if (ret != 0) {
1497 			diskp->current_temp = DISK_INVALID_TEMP;
1498 			diskp->ref_temp = DISK_INVALID_TEMP;
1499 			return (-1);
1500 		}
1501 		/*
1502 		 * For the current temperature verify that the parameter
1503 		 * length is 0x02 and the parameter code is 0x00
1504 		 * Temperature value of 255(0xFF) is considered INVALID.
1505 		 */
1506 		if ((tpage[7] == 0x02) && (tpage[4] == 0x00) &&
1507 		    (tpage[5] == 0x00)) {
1508 			if (tpage[9] == 0xFF) {
1509 				diskp->current_temp = DISK_INVALID_TEMP;
1510 				return (-1);
1511 			} else {
1512 				diskp->current_temp = tpage[9];
1513 			}
1514 		}
1515 
1516 		/*
1517 		 * For the reference temperature verify that the parameter
1518 		 * length is 0x02 and the parameter code is 0x01
1519 		 * Temperature value of 255(0xFF) is considered INVALID.
1520 		 */
1521 		if ((tpage[13] == 0x02) && (tpage[10] == 0x00) &&
1522 		    (tpage[11] == 0x01)) {
1523 			if (tpage[15] == 0xFF) {
1524 				diskp->ref_temp = DISK_INVALID_TEMP;
1525 			} else {
1526 				diskp->ref_temp = tpage[15];
1527 			}
1528 		}
1529 	}
1530 	return (0);
1531 }
1532 
1533 /* ARGSUSED */
1534 static void *
1535 disk_temp_thr(void *args)
1536 {
1537 	char syscmd[BUFSIZ];
1538 	char msgbuf[BUFSIZ];
1539 	timespec_t	to;
1540 	int	ret, i;
1541 	env_disk_t	*diskp;
1542 	pthread_mutex_t	env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
1543 	pthread_cond_t	env_monitor_cv = PTHREAD_COND_INITIALIZER;
1544 	pm_state_change_t	pmstate;
1545 	int	idle_time;
1546 	int	disk_pm_fd;
1547 	time_t	ct;
1548 
1549 	if ((disk_pm_fd = open(PM_DEVICE, O_RDWR)) == -1) {
1550 		envd_log(LOG_ERR, DISK_TEMP_THREAD_EXITING,
1551 		    errno, strerror(errno));
1552 		return (NULL);
1553 	}
1554 
1555 	for (;;) {
1556 		/*
1557 		 * Sleep for specified seconds before issuing IOCTL
1558 		 * again.
1559 		 */
1560 		(void) pthread_mutex_lock(&env_monitor_mutex);
1561 		ret = pthread_cond_reltimedwait_np(&env_monitor_cv,
1562 		    &env_monitor_mutex, &to);
1563 
1564 		to.tv_sec = disk_scan_interval;
1565 		to.tv_nsec = 0;
1566 
1567 		if (ret != ETIMEDOUT) {
1568 			(void) pthread_mutex_unlock(
1569 			    &env_monitor_mutex);
1570 			continue;
1571 		}
1572 		(void) pthread_mutex_unlock(&env_monitor_mutex);
1573 
1574 		for (i = 0; (diskp = envd_disks[i]) != NULL; i++) {
1575 			if (diskp->present == B_FALSE)
1576 				continue;
1577 			if (diskp->tpage_supported == B_FALSE)
1578 				continue;
1579 		/*
1580 		 * If the disk temperature is above the warning threshold
1581 		 * continue monitoring until the temperature drops below
1582 		 * warning threshold.
1583 		 * if the temperature is in the NORMAL range monitor only
1584 		 * when the disk is BUSY.
1585 		 * We do not want to read the disk temperature if the disk is
1586 		 * is idling. The reason for this is disk will never get into
1587 		 * lowest power mode if we scan the disk temperature
1588 		 * peridoically. To avoid this situation we first determine
1589 		 * the idle_time of the disk. If the disk has been IDLE since
1590 		 * we scanned the temperature last time we will not read the
1591 		 * temperature.
1592 		 */
1593 		if (!DISK_TEMP_IN_WARNING_RANGE(diskp->current_temp, diskp)) {
1594 			pmstate.physpath = diskp->physpath;
1595 			pmstate.size = strlen(diskp->physpath);
1596 			pmstate.component = 0;
1597 			if ((idle_time =
1598 			    ioctl(disk_pm_fd, PM_GET_TIME_IDLE,
1599 			    &pmstate)) == -1) {
1600 
1601 				if (errno != EINTR) {
1602 					if (env_debug)
1603 						envd_log(LOG_ERR,
1604 			"ioctl PM_GET_TIME_IDLE failed for DISK0. errno=0x%x\n",
1605 						    errno);
1606 					continue;
1607 				}
1608 				continue;
1609 			}
1610 			if (idle_time >= (disk_scan_interval/2)) {
1611 				if (env_debug) {
1612 					envd_log(LOG_ERR, "%s idle time = %d\n",
1613 					    diskp->name, idle_time);
1614 				}
1615 				continue;
1616 			}
1617 		}
1618 		ret = get_disk_temp(diskp);
1619 		if (ret != 0)
1620 			continue;
1621 		if (env_debug) {
1622 			envd_log(LOG_ERR, "%s temp = %d ref. temp = %d\n",
1623 			    diskp->name, diskp->current_temp, diskp->ref_temp);
1624 		}
1625 		/*
1626 		 * If this disk already triggered system shutdown, don't
1627 		 * log any more shutdown/warning messages for it.
1628 		 */
1629 		if (diskp->shutdown_initiated)
1630 			continue;
1631 
1632 		/*
1633 		 * Check for the temperature in warning and shutdown range
1634 		 * and take appropriate action.
1635 		 */
1636 		if (DISK_TEMP_IN_WARNING_RANGE(diskp->current_temp, diskp)) {
1637 			/*
1638 			 * Check if the temperature has been in warning
1639 			 * range during last disk_warning_duration interval.
1640 			 * If so, the temperature is truly in warning
1641 			 * range and we need to log a warning message,
1642 			 * but no more than once every disk_warning_interval
1643 			 * seconds.
1644 			 */
1645 			time_t	wtstamp = diskp->warning_tstamp;
1646 
1647 			ct = (time_t)(gethrtime() / NANOSEC);
1648 			if (diskp->warning_start == 0)
1649 				diskp->warning_start = ct;
1650 			if (((ct - diskp->warning_start) >=
1651 			    disk_warning_duration) && (wtstamp == 0 ||
1652 			    (ct - wtstamp) >= disk_warning_interval)) {
1653 				envd_log(LOG_CRIT, ENV_WARNING_MSG,
1654 				    diskp->name, diskp->current_temp,
1655 				    diskp->low_warning,
1656 				    diskp->high_warning);
1657 				diskp->warning_tstamp = ct;
1658 			}
1659 		} else if (diskp->warning_start != 0)
1660 			diskp->warning_start = 0;
1661 
1662 		if (!shutdown_override &&
1663 		    DISK_TEMP_IN_SHUTDOWN_RANGE(diskp->current_temp, diskp)) {
1664 			ct = (time_t)(gethrtime() / NANOSEC);
1665 			if (diskp->shutdown_tstamp == 0)
1666 				diskp->shutdown_tstamp = ct;
1667 
1668 			/*
1669 			 * Shutdown the system if the temperature remains
1670 			 * in the shutdown range for over disk_shutdown_interval
1671 			 * seconds.
1672 			 */
1673 			if ((ct - diskp->shutdown_tstamp) >=
1674 			    disk_shutdown_interval) {
1675 				/* log error */
1676 				diskp->shutdown_initiated = B_TRUE;
1677 				(void) snprintf(msgbuf, sizeof (msgbuf),
1678 				    ENV_SHUTDOWN_MSG, diskp->name,
1679 				    diskp->current_temp, diskp->low_shutdown,
1680 				    diskp->high_shutdown);
1681 				envd_log(LOG_ALERT, msgbuf);
1682 
1683 				/* shutdown the system (only once) */
1684 				if (system_shutdown_started == B_FALSE) {
1685 					(void) snprintf(syscmd, sizeof (syscmd),
1686 					    "%s \"%s\"", shutdown_cmd, msgbuf);
1687 					envd_log(LOG_ALERT, syscmd);
1688 					system_shutdown_started = B_TRUE;
1689 					(void) system(syscmd);
1690 				}
1691 			}
1692 		} else if (diskp->shutdown_tstamp != 0)
1693 			diskp->shutdown_tstamp = 0;
1694 		}
1695 	} /* end of forever loop */
1696 }
1697 
1698 static void *
1699 fan_thr(void *args)
1700 {
1701 	char msgbuf[BUFSIZ];
1702 	timespec_t	to;
1703 	int	ret, i;
1704 	pthread_mutex_t	env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
1705 	pthread_cond_t	env_monitor_cv = PTHREAD_COND_INITIALIZER;
1706 	env_fan_t	*fanp;
1707 
1708 #ifdef	__lint
1709 	args = args;
1710 #endif
1711 
1712 	for (;;) {
1713 		/*
1714 		 * Sleep for specified seconds before issuing IOCTL
1715 		 * again.
1716 		 */
1717 		(void) pthread_mutex_lock(&env_monitor_mutex);
1718 		ret = pthread_cond_reltimedwait_np(&env_monitor_cv,
1719 		    &env_monitor_mutex, &to);
1720 		to.tv_sec = fan_scan_interval;
1721 		to.tv_nsec = 0;
1722 		if (ret != ETIMEDOUT) {
1723 			(void) pthread_mutex_unlock(&env_monitor_mutex);
1724 			continue;
1725 		}
1726 		(void) pthread_mutex_unlock(&env_monitor_mutex);
1727 
1728 		for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
1729 			if (fanp->present == B_FALSE)
1730 				continue;
1731 			/*
1732 			 * We initiate shutdown if fan status indicates
1733 			 * failure. Also, don't warn repeatedly.
1734 			 */
1735 			if (has_fan_failed(fanp) == B_TRUE) {
1736 				if (fanp->last_status == FAN_FAILED)
1737 					continue;
1738 				fanp->last_status = FAN_FAILED;
1739 				(void) snprintf(msgbuf, sizeof (msgbuf),
1740 				    ENV_FAN_FAILURE_WARNING_MSG, fanp->name,
1741 				    fan_rpm_string, fan_status_string);
1742 				envd_log(LOG_ALERT, msgbuf);
1743 			} else {
1744 				if (fanp->last_status == FAN_OK)
1745 					continue;
1746 				fanp->last_status = FAN_OK;
1747 				(void) snprintf(msgbuf, sizeof (msgbuf),
1748 				    ENV_FAN_OK_MSG, fanp->name);
1749 				envd_log(LOG_ALERT, msgbuf);
1750 			}
1751 		}
1752 	}
1753 
1754 	/*NOTREACHED*/
1755 	return (NULL);
1756 }
1757 
1758 /*
1759  * Setup envrionmental monitor state and start threads to monitor
1760  * temperature, fan, disk and power management state.
1761  * Returns -1 on error, 0 if successful.
1762  */
1763 static int
1764 envd_setup(void)
1765 {
1766 
1767 	if (getenv("SUNW_piclenvd_debug") != NULL)
1768 		env_debug = 1;
1769 
1770 	if (pthread_attr_init(&thr_attr) != 0 ||
1771 	    pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM) != 0) {
1772 		return (-1);
1773 	}
1774 
1775 	/*
1776 	 * If ES segment is not present or has inconsistent information, we
1777 	 * use default values for sensor limits. For the sake of simplicity,
1778 	 * we still store these limits internally in the 'es' member in the
1779 	 * structure.
1780 	 */
1781 	if (envd_es_setup() < 0) {
1782 		envd_log(LOG_WARNING, ENV_DEFAULT_LIMITS);
1783 		envd_es_default_setup();
1784 	}
1785 
1786 	if (envd_setup_sensors() < 0) {
1787 		if (env_debug)
1788 			envd_log(LOG_ERR, "Failed to setup sensors\n");
1789 		system_temp_monitor = 0;
1790 	}
1791 
1792 	if (envd_setup_fans() < 0) {
1793 		if (env_debug)
1794 			envd_log(LOG_ERR, "Failed to setup fans\n");
1795 		fan_monitor = 0;
1796 		pm_monitor = 0;
1797 	}
1798 
1799 	if (envd_setup_disks() < 0) {
1800 		if (env_debug)
1801 			envd_log(LOG_ERR, "Failed to setup disks\n");
1802 		disk_temp_monitor = 0;
1803 	}
1804 
1805 	/*
1806 	 * Create a thread to monitor system temperatures
1807 	 */
1808 	if ((system_temp_monitor) && (system_temp_thr_created == B_FALSE)) {
1809 		if (pthread_create(&system_temp_thr_id, &thr_attr,
1810 		    system_temp_thr, NULL) != 0) {
1811 			envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED);
1812 		} else {
1813 			system_temp_thr_created = B_TRUE;
1814 			if (env_debug)
1815 				envd_log(LOG_ERR,
1816 			"Created thread to monitor system temperatures\n");
1817 		}
1818 	}
1819 
1820 	/*
1821 	 * Create a thread to monitor fans
1822 	 */
1823 	if ((fan_monitor) && (fan_thr_created == B_FALSE)) {
1824 		if (pthread_create(&fan_thr_id, &thr_attr, fan_thr, NULL) != 0)
1825 			envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED);
1826 		else {
1827 			fan_thr_created = B_TRUE;
1828 			if (env_debug) {
1829 				envd_log(LOG_ERR,
1830 				    "Created thread to monitor system fans\n");
1831 			}
1832 		}
1833 	}
1834 
1835 	/*
1836 	 * Create a thread to monitor PM state
1837 	 */
1838 	if ((pm_monitor) && (pmthr_created == B_FALSE)) {
1839 		if (pthread_create(&pmthr_tid, &thr_attr, pmthr, NULL) != 0)
1840 			envd_log(LOG_CRIT, PM_THREAD_CREATE_FAILED);
1841 		else {
1842 			pmthr_created = B_TRUE;
1843 			if (env_debug)
1844 				envd_log(LOG_ERR,
1845 			"Created thread to monitor system power state\n");
1846 		}
1847 	}
1848 
1849 	/*
1850 	 * Create a thread to monitor disk temperature
1851 	 */
1852 	if ((disk_temp_monitor) && (disk_temp_thr_created == B_FALSE)) {
1853 		if (pthread_create(&disk_temp_thr_id, &thr_attr,
1854 		    disk_temp_thr, NULL) != 0) {
1855 			envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED);
1856 		} else {
1857 			disk_temp_thr_created = B_TRUE;
1858 			if (env_debug)
1859 				envd_log(LOG_ERR,
1860 			"Created thread for disk temperatures\n");
1861 		}
1862 	}
1863 
1864 	return (0);
1865 }
1866 
1867 static void
1868 piclenvd_register(void)
1869 {
1870 	picld_plugin_register(&my_reg_info);
1871 }
1872 
1873 static void
1874 piclenvd_init(void)
1875 {
1876 
1877 	(void) env_picl_setup_tuneables();
1878 
1879 	/*
1880 	 * Setup the environmental data structures
1881 	 */
1882 	if (envd_setup() != 0) {
1883 		envd_log(LOG_CRIT, ENVD_PLUGIN_INIT_FAILED);
1884 		return;
1885 	}
1886 
1887 	/*
1888 	 * Now setup/populate PICL tree
1889 	 */
1890 	env_picl_setup();
1891 }
1892 
1893 static void
1894 piclenvd_fini(void)
1895 {
1896 
1897 	/*
1898 	 * Invoke env_picl_destroy() to remove any PICL nodes/properties
1899 	 * (including volatile properties) we created. Once this call
1900 	 * returns, there can't be any more calls from the PICL framework
1901 	 * to get current temperature or fan speed.
1902 	 */
1903 	env_picl_destroy();
1904 	envd_close_sensors();
1905 	envd_close_fans();
1906 }
1907 
1908 /*VARARGS2*/
1909 void
1910 envd_log(int pri, const char *fmt, ...)
1911 {
1912 	va_list	ap;
1913 
1914 	va_start(ap, fmt);
1915 	vsyslog(pri, fmt, ap);
1916 	va_end(ap);
1917 }
1918 
1919 /*
1920  * Tunables support functions
1921  */
1922 static env_tuneable_t *
1923 tuneable_lookup(picl_prophdl_t proph)
1924 {
1925 	int i;
1926 	env_tuneable_t	*tuneablep = NULL;
1927 
1928 	for (i = 0; i < ntuneables; i++) {
1929 		tuneablep = &tuneables[i];
1930 		if (tuneablep->proph == proph)
1931 			return (tuneablep);
1932 	}
1933 
1934 	return (NULL);
1935 }
1936 
1937 static int
1938 get_string_val(ptree_rarg_t *parg, void *buf)
1939 {
1940 	picl_prophdl_t	proph;
1941 	env_tuneable_t	*tuneablep;
1942 
1943 	proph = parg->proph;
1944 
1945 	tuneablep = tuneable_lookup(proph);
1946 
1947 	if (tuneablep == NULL)
1948 		return (PICL_FAILURE);
1949 
1950 	(void) memcpy(buf, tuneablep->value, tuneablep->nbytes);
1951 
1952 	return (PICL_SUCCESS);
1953 }
1954 
1955 static int
1956 set_string_val(ptree_warg_t *parg, const void *buf)
1957 {
1958 	picl_prophdl_t	proph;
1959 	env_tuneable_t	*tuneablep;
1960 
1961 	if (parg->cred.dc_euid != 0)
1962 		return (PICL_PERMDENIED);
1963 
1964 	proph = parg->proph;
1965 
1966 	tuneablep = tuneable_lookup(proph);
1967 
1968 	if (tuneablep == NULL)
1969 		return (PICL_FAILURE);
1970 
1971 	(void) memcpy(tuneables->value, buf, tuneables->nbytes);
1972 
1973 
1974 	return (PICL_SUCCESS);
1975 }
1976 
1977 static int
1978 get_int_val(ptree_rarg_t *parg, void *buf)
1979 {
1980 	picl_prophdl_t	proph;
1981 	env_tuneable_t	*tuneablep;
1982 
1983 	proph = parg->proph;
1984 
1985 	tuneablep = tuneable_lookup(proph);
1986 
1987 	if (tuneablep == NULL)
1988 		return (PICL_FAILURE);
1989 
1990 	(void) memcpy(buf, tuneablep->value, tuneablep->nbytes);
1991 
1992 	return (PICL_SUCCESS);
1993 }
1994 
1995 static int
1996 set_int_val(ptree_warg_t *parg, const void *buf)
1997 {
1998 	picl_prophdl_t	proph;
1999 	env_tuneable_t	*tuneablep;
2000 
2001 	if (parg->cred.dc_euid != 0)
2002 		return (PICL_PERMDENIED);
2003 
2004 	proph = parg->proph;
2005 
2006 	tuneablep = tuneable_lookup(proph);
2007 
2008 	if (tuneablep == NULL)
2009 		return (PICL_FAILURE);
2010 
2011 	(void) memcpy(tuneablep->value, buf, tuneablep->nbytes);
2012 
2013 	return (PICL_SUCCESS);
2014 }
2015 
2016 boolean_t
2017 has_fan_failed(env_fan_t *fanp)
2018 {
2019 	fanspeed_t	fan_speed;
2020 	uchar_t		status;
2021 	uint8_t		tach;
2022 	int		real_tach;
2023 	int		ret, ntries;
2024 
2025 	if (fanp->fd == -1)
2026 		return (B_TRUE);
2027 
2028 	/*
2029 	 * Read RF_FAN_STATUS bit of the fan fault register, retry if
2030 	 * the PIC is busy, with a 1 second delay to allow it to update.
2031 	 */
2032 	for (ntries = 0; ntries < MAX_RETRIES_FOR_FAN_FAULT; ntries++) {
2033 		ret = ioctl(fanp->fd, PIC_GET_FAN_STATUS, &status);
2034 		if ((ret == 0) && ((status & 0x1) == 0))
2035 			break;
2036 		(void) sleep(1);
2037 	}
2038 
2039 	if (ntries > 0) {
2040 		if (env_debug) {
2041 			envd_log(LOG_ERR,
2042 			    "%d retries attempted in reading fan status.\n",
2043 			    ntries);
2044 		}
2045 	}
2046 
2047 	if (ntries == MAX_RETRIES_FOR_FAN_FAULT) {
2048 		(void) strncpy(fan_status_string, NOT_AVAILABLE,
2049 		    sizeof (fan_status_string));
2050 		(void) strncpy(fan_rpm_string, NOT_AVAILABLE,
2051 		    sizeof (fan_rpm_string));
2052 		return (B_TRUE);
2053 	}
2054 
2055 	if (env_debug)
2056 		envd_log(LOG_ERR, "fan status = 0x%x\n", status);
2057 
2058 	/*
2059 	 * ST_FFAULT bit isn't implemented yet and we're reading only
2060 	 * individual fan status
2061 	 */
2062 	if (status & 0x1) {
2063 		(void) snprintf(fan_status_string, sizeof (fan_status_string),
2064 		    "0x%x", status);
2065 		if (ioctl(fanp->fd, PIC_GET_FAN_SPEED, &tach) != 0) {
2066 			(void) strncpy(fan_rpm_string, NOT_AVAILABLE,
2067 			    sizeof (fan_rpm_string));
2068 		} else {
2069 			real_tach = tach << 8;
2070 			fan_speed = TACH_TO_RPM(real_tach);
2071 			(void) snprintf(fan_rpm_string, sizeof (fan_rpm_string),
2072 			    "%d", fan_speed);
2073 		}
2074 		return (B_TRUE);
2075 	}
2076 
2077 	return (B_FALSE);
2078 }
2079 
2080 static int
2081 scsi_mode_select(env_disk_t *diskp, uchar_t page_code, uchar_t *pagebuf,
2082     uint16_t pagelen)
2083 {
2084 	struct uscsi_cmd		ucmd_buf;
2085 	uchar_t				cdb_buf[CDB_GROUP1];
2086 	struct scsi_extended_sense	sense_buf;
2087 	int				ret_val;
2088 
2089 	bzero(&cdb_buf, sizeof (cdb_buf));
2090 	bzero(&ucmd_buf, sizeof (ucmd_buf));
2091 	bzero(&sense_buf, sizeof (sense_buf));
2092 
2093 	cdb_buf[0] = SCMD_MODE_SELECT_G1;
2094 	cdb_buf[1] = 1<<PAGE_FMT;
2095 
2096 	cdb_buf[7] = (uchar_t)((pagelen & 0xFF00) >> 8);
2097 	cdb_buf[8] = (uchar_t)(pagelen  & 0x00FF);
2098 
2099 	ucmd_buf.uscsi_cdb = (char *)cdb_buf;
2100 	ucmd_buf.uscsi_cdblen = sizeof (cdb_buf);
2101 	ucmd_buf.uscsi_bufaddr = (caddr_t)pagebuf;
2102 	ucmd_buf.uscsi_buflen = pagelen;
2103 	ucmd_buf.uscsi_rqbuf = (caddr_t)&sense_buf;
2104 	ucmd_buf.uscsi_rqlen = sizeof (struct scsi_extended_sense);
2105 	ucmd_buf.uscsi_flags = USCSI_RQENABLE | USCSI_WRITE | USCSI_SILENT;
2106 	ucmd_buf.uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
2107 
2108 	ret_val = ioctl(diskp->fd, USCSICMD, ucmd_buf);
2109 
2110 	if (ret_val == 0 && ucmd_buf.uscsi_status == 0) {
2111 		return (ret_val);
2112 	}
2113 	if (env_debug)
2114 		envd_log(LOG_ERR, "mode select command for %s failed. "
2115 		    "page_code 0x%x ret_val = 0x%x "
2116 		    "status = 0x%x errno = 0x%x\n", diskp->name, page_code,
2117 		    ret_val, ucmd_buf.uscsi_status, errno);
2118 
2119 	return (1);
2120 
2121 }
2122