xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4u/psvc/psvcpolicy/psvcpolicy.c (revision c65ebfc7045424bd04a6c7719a27b0ad3399ad54)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  *
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains routines to support the Platform Services Plugin
31  * These routines implement the platform independent environment monitoring
32  * and control policies that may be invoked by a daemon thread within
33  * the plugin
34  */
35 
36 #include <syslog.h>
37 #include <unistd.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <libintl.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <strings.h>
44 #include <libintl.h>
45 #include <sys/types.h>
46 #include <string.h>
47 #include <limits.h>
48 #include <picl.h>
49 #include <picltree.h>
50 #include <sys/types.h>
51 #include <string.h>
52 #include <psvc_objects.h>
53 
54 #define	LOWTEMP_CRITICAL_MSG		\
55 	gettext("CRITICAL : LOW TEMPERATURE DETECTED %d, %s")
56 #define	LOWTEMP_WARNING_MSG		\
57 	gettext("WARNING : LOW TEMPERATURE DETECTED %d, %s")
58 #define	HIGHTEMP_CRITICAL_MSG		\
59 	gettext("CRITICAL : HIGH TEMPERATURE DETECTED %d, %s")
60 #define	HIGHTEMP_WARNING_MSG		\
61 	gettext("WARNING : HIGH TEMPERATURE DETECTED %d, %s")
62 #define	DEVICE_INSERTED_MSG	gettext("Device %s inserted")
63 #define	DEVICE_REMOVED_MSG	gettext("Device %s removed")
64 #define	DEVICE_FAILURE_MSG		\
65 	gettext("CRITICAL: Device %s failure detected by sensor %s\n")
66 #define	DEVICE_OK_MSG	gettext("Device %s OK")
67 #define	SECONDARY_FAN_FAIL_MSG	gettext("Secondary fan failure, device %s")
68 #define	KEYSWITCH_POS_READ_FAILED_MSG	\
69 	gettext("Keyswitch position could not be determined")
70 #define	KEYSWITCH_POS_CHANGED_MSG gettext("Keyswitch position changed to %s")
71 #define	GET_PRESENCE_FAILED_MSG		\
72 	gettext("Failed to get presence attribute, id = %s, errno = %d\n")
73 #define	GET_SENSOR_FAILED_MSG		\
74 	gettext("Failed to get sensor value, id = %s, errno = %d\n")
75 #define	PS_OVER_CURRENT_MSG		\
76 	gettext("WARNING: Power Supply overcurrent detected for %s\n")
77 #define	SET_LED_FAILED_MSG		\
78 	gettext("Failed to set LED state, id = %s, errno = %d\n")
79 #define	SET_FANSPEED_FAILED_MSG		\
80 	gettext("Failed to set fan speed, id = %s, errno = %d\n")
81 #define	FAN_MISSING_MSG			\
82 	gettext("WARNING: Fan missing, id = %s\n")
83 #define	TEMP_SENSOR_FAULT		\
84 	gettext("WARNING: Temperature Sensor %s returning faulty temp\n")
85 #define	TEMP_OFFSET	17
86 
87 static char *shutdown_string = "shutdown -y -g 60 -i 5 \"OVERTEMP condition\"";
88 
89 static int cpus_online = 0;
90 
91 typedef struct seg_desc {
92 	int32_t segdesc;
93 	int16_t segoffset;
94 	int16_t seglength;
95 } seg_desc_t;
96 
97 static int32_t threshold_names[] = {
98 	PSVC_HW_LO_SHUT_ATTR,
99 	PSVC_LO_SHUT_ATTR,
100 	PSVC_LO_WARN_ATTR,
101 	PSVC_NOT_USED,			/* LOW MODE which is not used */
102 	PSVC_OPTIMAL_TEMP_ATTR,
103 	PSVC_HI_WARN_ATTR,
104 	PSVC_HI_SHUT_ATTR,
105 	PSVC_HW_HI_SHUT_ATTR
106 };
107 
108 /*
109  * The I2C bus is noisy, and the state may be incorrectly reported as
110  * having changed.  When the state changes, we attempt to confirm by
111  * retrying.  If any retries indicate that the state has not changed, we
112  * assume the state change(s) were incorrect and the state has not changed.
113  * The following variables are used to store the tuneable values read in
114  * from the optional i2cparam.conf file for this shared object library.
115  */
116 static int n_read_temp = PSVC_THRESHOLD_COUNTER;
117 static int n_retry_keyswitch = PSVC_NUM_OF_RETRIES;
118 static int retry_sleep_keyswitch = 1;
119 static int n_retry_hotplug = PSVC_NUM_OF_RETRIES;
120 static int retry_sleep_hotplug = 1;
121 static int n_retry_fan_hotplug = PSVC_NUM_OF_RETRIES;
122 static int retry_sleep_fan_hotplug = 1;
123 static int n_retry_fan_present = PSVC_NUM_OF_RETRIES;
124 static int retry_sleep_fan_present = 1;
125 
126 typedef struct {
127 	int *pvar;
128 	char *texttag;
129 } i2c_noise_param_t;
130 
131 static i2c_noise_param_t i2cparams_sun4u[] = {
132 	&n_read_temp, "n_read_temp",
133 	&n_retry_keyswitch, "n_retry_keyswitch",
134 	&retry_sleep_keyswitch, "retry_sleep_keyswitch",
135 	&n_retry_hotplug, "n_retry_hotplug",
136 	&retry_sleep_hotplug, "retry_sleep_hotplug",
137 	&n_retry_fan_hotplug, "n_retry_fan_hotplug",
138 	&retry_sleep_fan_hotplug, "retry_sleep_fan_hotplug",
139 	&n_retry_fan_present, "n_retry_fan_present",
140 	&retry_sleep_fan_present, "retry_sleep_fan_present",
141 	NULL, NULL
142 };
143 
144 #pragma init(i2cparams_sun4u_load)
145 
146 static void
147 i2cparams_sun4u_debug(i2c_noise_param_t *pi2cparams, int usingDefaults)
148 {
149 	char s[128];
150 	i2c_noise_param_t *p;
151 
152 	if (!usingDefaults) {
153 		(void) strncpy(s,
154 		    "# Values from /usr/platform/sun4u/lib/i2cparam.conf\n",
155 			sizeof (s) - 1);
156 		syslog(LOG_WARNING, "%s", s);
157 	} else {
158 		/* no file - we're using the defaults */
159 		(void) strncpy(s,
160 "# No /usr/platform/sun4u/lib/i2cparam.conf file, using defaults\n",
161 			sizeof (s) - 1);
162 	}
163 	(void) fputs(s, stdout);
164 	p = pi2cparams;
165 	while (p->pvar != NULL) {
166 		(void) snprintf(s, sizeof (s), "%s %d\n", p->texttag,
167 		    *(p->pvar));
168 		if (!usingDefaults)
169 			syslog(LOG_WARNING, "%s", s);
170 		(void) fputs(s, stdout);
171 		p++;
172 	}
173 }
174 
175 static void
176 i2cparams_sun4u_load(void)
177 {
178 	FILE *fp;
179 	char *filename = "/usr/platform/sun4u/lib/i2cparam.conf";
180 	char s[128];
181 	char var[128];
182 	int val;
183 	i2c_noise_param_t *p;
184 
185 	/* read thru the i2cparam.conf file and set variables */
186 	if ((fp = fopen(filename, "r")) != NULL) {
187 		while (fgets(s, sizeof (s), fp) != NULL) {
188 			if (s[0] == '#') /* skip comment lines */
189 				continue;
190 			/* try to find a string match and get the value */
191 			if (sscanf(s, "%127s %d", var, &val) != 2)
192 				continue;
193 			if (val < 1)
194 				val = 1;  /* clamp min value */
195 			p = &(i2cparams_sun4u[0]);
196 			while (p->pvar != NULL) {
197 				if (strncmp(p->texttag, var, sizeof (var)) ==
198 				    0) {
199 					*(p->pvar) = val;
200 					break;
201 				}
202 				p++;
203 			}
204 		}
205 		(void) fclose(fp);
206 	}
207 	/* output the values of the parameters */
208 	i2cparams_sun4u_debug(&(i2cparams_sun4u[0]), ((fp == NULL)? 1 : 0));
209 }
210 
211 
212 int32_t
213 psvc_update_thresholds_0(psvc_opaque_t hdlp, char *id)
214 {
215 	int32_t status = PSVC_SUCCESS;
216 	fru_info_t fru_data;
217 	char *fru, seg_name[2];
218 	int8_t seg_count, temp_array[8];
219 	int32_t match_count, i, j, seg_desc_start = 0x1806, temp_address;
220 	int32_t seg_found, temp;
221 	boolean_t present;
222 	seg_desc_t segment;
223 
224 	status = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &present);
225 	if ((status != PSVC_SUCCESS) || (present != PSVC_PRESENT))
226 		return (status);
227 
228 	status = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &match_count,
229 	    PSVC_FRU);
230 	if (status == PSVC_FAILURE)
231 		return (status);
232 
233 	for (i = 0; i < match_count; i++) {
234 		seg_found = 0;
235 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
236 		    &fru, PSVC_FRU, i);
237 		if (status != PSVC_SUCCESS)
238 			return (status);
239 
240 		fru_data.buf_start = 0x1805;
241 		fru_data.buf = (char *)&seg_count;
242 		fru_data.read_size = 1;
243 
244 		status = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR,
245 		    &fru_data);
246 		if (status != PSVC_SUCCESS) {
247 			return (status);
248 		}
249 		for (j = 0; (j < seg_count) && (!seg_found); j++) {
250 			fru_data.buf_start = seg_desc_start;
251 			fru_data.buf = seg_name;
252 			fru_data.read_size = 2;
253 
254 			status = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR,
255 			    &fru_data);
256 
257 			seg_desc_start = seg_desc_start + 2;
258 			fru_data.buf_start = seg_desc_start;
259 			fru_data.buf = (char *)&segment;
260 			fru_data.read_size = sizeof (seg_desc_t);
261 
262 			status = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR,
263 			    &fru_data);
264 			if (status != PSVC_SUCCESS) {
265 				syslog(LOG_ERR,
266 				    "Failed psvc_get_attr for FRU info\n");
267 				return (status);
268 			}
269 			seg_desc_start = seg_desc_start + sizeof (seg_desc_t);
270 			if (memcmp(seg_name, "SC", 2) == 0)
271 				seg_found = 1;
272 		}
273 		if (seg_found) {
274 			temp_address = segment.segoffset + TEMP_OFFSET;
275 			fru_data.buf_start = temp_address;
276 			fru_data.buf = (char *)&temp_array;
277 			fru_data.read_size = sizeof (temp_array);
278 			status = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR,
279 			    &fru_data);
280 			if (status != PSVC_SUCCESS) {
281 				syslog(LOG_ERR,
282 				    "Failed psvc_get_attr for FRU info\n");
283 				return (status);
284 			} else {
285 				for (j = 0; j < sizeof (temp_array); j++) {
286 					if (threshold_names[j] == PSVC_NOT_USED)
287 						continue;
288 					temp = temp_array[j];
289 					status = psvc_set_attr(hdlp, id,
290 					    threshold_names[j], &temp);
291 					if (status != PSVC_SUCCESS) {
292 						return (status);
293 					}
294 				}
295 			}
296 		} else {
297 			syslog(LOG_ERR, "No FRU Information for %s"
298 			    " using default temperatures\n", id);
299 		}
300 	}
301 	return (status);
302 }
303 
304 #define	MAX_TEMP_SENSORS	256
305 
306 static int32_t
307 check_temp(psvc_opaque_t hdlp, char *id, int32_t silent)
308 {
309 	int32_t		lo_warn, hi_warn, lo_shut, hi_shut;
310 	uint64_t	features;
311 	int32_t		temp;
312 	char		previous_state[32];
313 	char		led_state[32];
314 	char		state[32];
315 	char		fault[32];
316 	char		label[32];
317 	boolean_t	pr;
318 	int32_t		status = PSVC_SUCCESS;
319 	int8_t		fail = 0;
320 	static int	threshold_low_shut[MAX_TEMP_SENSORS] = {0};
321 	static int	threshold_high_shut[MAX_TEMP_SENSORS] = {0};
322 	static int	threshold_low_warn[MAX_TEMP_SENSORS] = {0};
323 	static int	threshold_high_warn[MAX_TEMP_SENSORS] = {0};
324 	int32_t		instance;
325 
326 	status = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance);
327 	if (status != PSVC_SUCCESS)
328 		return (status);
329 
330 	status = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &pr);
331 	if ((status != PSVC_SUCCESS) || (pr != PSVC_PRESENT)) {
332 		return (status);
333 	}
334 
335 	status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state);
336 	if (status == PSVC_FAILURE)
337 		return (status);
338 
339 	if ((strcmp(state, PSVC_HOTPLUGGED) == 0)) {
340 		return (PSVC_SUCCESS);
341 	}
342 
343 	status = psvc_get_attr(hdlp, id, PSVC_FEATURES_ATTR, &features);
344 	if (status != PSVC_SUCCESS)
345 		return (status);
346 
347 	status = psvc_get_attr(hdlp, id, PSVC_LO_WARN_ATTR, &lo_warn);
348 	if (status != PSVC_SUCCESS)
349 		return (status);
350 
351 	status = psvc_get_attr(hdlp, id, PSVC_LO_SHUT_ATTR, &lo_shut);
352 	if (status != PSVC_SUCCESS)
353 		return (status);
354 
355 	status = psvc_get_attr(hdlp, id, PSVC_HI_WARN_ATTR, &hi_warn);
356 	if (status != PSVC_SUCCESS)
357 		return (status);
358 
359 	status = psvc_get_attr(hdlp, id, PSVC_HI_SHUT_ATTR, &hi_shut);
360 	if (status != PSVC_SUCCESS)
361 		return (status);
362 
363 	status = psvc_get_attr(hdlp, id, PSVC_SENSOR_VALUE_ATTR, &temp);
364 	if (status != PSVC_SUCCESS) {
365 		return (status);
366 	}
367 
368 	/*
369 	 * The following code is to check to see if the temp sensor is
370 	 * returning a faulty reading due to it either being bad or the
371 	 * CPU being powered off for some reason. Is so we will alert the user
372 	 * and just label the sensor bad but not the WHOLE CPU module.
373 	 */
374 	if ((temp == 127) && (strcmp(state, PSVC_ERROR) != 0)) {
375 		status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, PSVC_ERROR);
376 		if (status != PSVC_SUCCESS)
377 			return (status);
378 		status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR,
379 		    PSVC_GEN_FAULT);
380 		if (status != PSVC_SUCCESS)
381 			return (status);
382 		syslog(LOG_ERR, TEMP_SENSOR_FAULT, id);
383 		return (status);
384 	}
385 
386 	status = psvc_get_attr(hdlp, id, PSVC_LABEL_ATTR, label);
387 	if (status != PSVC_SUCCESS)
388 		return (status);
389 
390 	/*
391 	 * if any of the four temperature states (lo_shut, lo_warn,
392 	 * hi_shut, hi_warn) is detected we will not take an action
393 	 * until the number of similar back-to-back readings equals
394 	 * 'n_read_temp' (default is PSVC_THRESHOLD_COUNTER).
395 	 */
396 	if ((features & PSVC_LOW_SHUT) && temp < lo_shut) {
397 		/*
398 		 * once we are in one state, clear all the
399 		 * counters for the other three states since
400 		 * back-to-back readings of these other three
401 		 * states could not happen anymore.
402 		 */
403 		threshold_low_warn[instance] = 0;
404 		threshold_high_shut[instance] = 0;
405 		threshold_high_warn[instance] = 0;
406 		threshold_low_shut[instance]++;
407 		if (threshold_low_shut[instance] == n_read_temp) {
408 			threshold_low_shut[instance] = 0;
409 			fail = 1;
410 			strcpy(state, PSVC_ERROR);
411 			strcpy(fault, PSVC_TEMP_LO_SHUT);
412 			strcpy(led_state, PSVC_LED_ON);
413 			if (silent == 0)
414 				syslog(LOG_ERR, LOWTEMP_CRITICAL_MSG,
415 				    temp, label);
416 		} else { /* Threshold for showing error not reached */
417 			return (PSVC_SUCCESS);
418 		}
419 	} else if ((features & PSVC_LOW_WARN) && temp < lo_warn) {
420 		threshold_low_shut[instance] = 0;
421 		threshold_high_shut[instance] = 0;
422 		threshold_high_warn[instance] = 0;
423 		threshold_low_warn[instance]++;
424 		if (threshold_low_warn[instance] == n_read_temp) {
425 			threshold_low_warn[instance] = 0;
426 			fail = 1;
427 			strcpy(state, PSVC_ERROR);
428 			strcpy(fault, PSVC_TEMP_LO_WARN);
429 			strcpy(led_state, PSVC_LED_ON);
430 			if (silent == 0)
431 				syslog(LOG_ERR, LOWTEMP_WARNING_MSG,
432 				    temp, label);
433 		} else { /* Threshold for showing error not reached */
434 			return (PSVC_SUCCESS);
435 		}
436 	} else if ((features & PSVC_HIGH_SHUT) && temp > hi_shut) {
437 		threshold_low_warn[instance] = 0;
438 		threshold_low_shut[instance] = 0;
439 		threshold_high_warn[instance] = 0;
440 		threshold_high_shut[instance]++;
441 		if (threshold_high_shut[instance] == n_read_temp) {
442 			threshold_high_shut[instance] = 0;
443 			fail = 1;
444 			strcpy(state, PSVC_ERROR);
445 			strcpy(fault, PSVC_TEMP_HI_SHUT);
446 			strcpy(led_state, PSVC_LED_ON);
447 			if (silent == 0)
448 				syslog(LOG_ERR, HIGHTEMP_CRITICAL_MSG,
449 				    temp, label);
450 		} else { /* Threshold for showing error not reached */
451 			return (PSVC_SUCCESS);
452 		}
453 	} else if ((features & PSVC_HIGH_WARN) && temp > hi_warn) {
454 		threshold_low_warn[instance] = 0;
455 		threshold_low_shut[instance] = 0;
456 		threshold_high_shut[instance] = 0;
457 		threshold_high_warn[instance]++;
458 		if (threshold_high_warn[instance] == n_read_temp) {
459 			threshold_high_warn[instance] = 0;
460 			fail = 1;
461 			strcpy(state, PSVC_ERROR);
462 			strcpy(fault, PSVC_TEMP_HI_WARN);
463 			strcpy(led_state, PSVC_LED_ON);
464 			if (silent == 0)
465 				syslog(LOG_ERR, HIGHTEMP_WARNING_MSG,
466 				    temp, label);
467 		} else { /* Threshold for showing error not reached */
468 			return (PSVC_SUCCESS);
469 		}
470 	}
471 
472 	/*
473 	 * If we reached this point then that means that we are either
474 	 * okay, or we have showed error n_read_temp times.
475 	 */
476 	if (fail != 1) {
477 		/* within limits */
478 		strcpy(state, PSVC_OK);
479 		strcpy(fault, PSVC_NO_FAULT);
480 		strcpy(led_state, PSVC_LED_OFF);
481 	}
482 
483 	status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, state);
484 	if (status != PSVC_SUCCESS)
485 		return (status);
486 	status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, fault);
487 	if (status != PSVC_SUCCESS)
488 		return (status);
489 	status = psvc_get_attr(hdlp, id, PSVC_PREV_STATE_ATTR,
490 		previous_state);
491 	if (status != PSVC_SUCCESS)
492 		return (status);
493 
494 	if (strcmp(previous_state, state) != 0) {
495 		char *led_id;
496 		int32_t led_count;
497 		int32_t i;
498 
499 		/* change state of fault LEDs */
500 		psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &led_count,
501 			PSVC_TS_OVERTEMP_LED);
502 		for (i = 0; i < led_count; ++i) {
503 			status = psvc_get_attr(hdlp, id,
504 				PSVC_ASSOC_ID_ATTR, &led_id,
505 				PSVC_TS_OVERTEMP_LED, i);
506 			if (status == PSVC_FAILURE)
507 				return (status);
508 			status = psvc_set_attr(hdlp, led_id,
509 				PSVC_LED_STATE_ATTR, led_state);
510 			if (status == PSVC_FAILURE)
511 				return (status);
512 		}
513 	}
514 
515 	return (PSVC_SUCCESS);
516 }
517 
518 int32_t
519 psvc_check_temperature_policy_0(psvc_opaque_t hdlp, char *id)
520 {
521 	return (check_temp(hdlp, id, 0));
522 }
523 
524 int32_t
525 psvc_check_temperature_silent_policy_0(psvc_opaque_t hdlp, char *id)
526 {
527 	return (check_temp(hdlp, id, 1));
528 }
529 
530 int32_t
531 psvc_fan_enable_disable_policy_0(psvc_opaque_t hdlp, char *id)
532 {
533 	char state[32], previous_state[32];
534 	char *backup_fan;
535 	int32_t status = PSVC_SUCCESS;
536 	uint64_t features;
537 	char label[32];
538 	boolean_t presence;
539 	boolean_t enable;
540 	int retry;
541 
542 	status = psvc_get_attr(hdlp, id, PSVC_FEATURES_ATTR, &features);
543 	if (status != PSVC_SUCCESS)
544 		return (status);
545 
546 	retry = 0;
547 	do {
548 		if (retry)
549 			(void) sleep(retry_sleep_fan_present);
550 
551 		status = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &presence);
552 		if (status != PSVC_SUCCESS)
553 			return (status);
554 		retry++;
555 	} while ((retry < n_retry_fan_present) && (presence == PSVC_ABSENT));
556 
557 	if (presence == PSVC_ABSENT) {
558 		status = psvc_get_attr(hdlp, id, PSVC_LABEL_ATTR, label);
559 		if (status != PSVC_SUCCESS)
560 			return (status);
561 
562 		status = psvc_get_attr(hdlp, id, PSVC_ENABLE_ATTR, &enable);
563 		if (status != PSVC_SUCCESS)
564 			return (status);
565 
566 		if (features & PSVC_DEV_PRIMARY) {
567 			status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
568 				&backup_fan, PSVC_ALTERNATE, 0);
569 			if (status != PSVC_SUCCESS)
570 				return (status);
571 
572 			enable = PSVC_DISABLED;
573 			status = psvc_set_attr(hdlp, id, PSVC_ENABLE_ATTR,
574 				&enable);
575 			if (status != PSVC_SUCCESS)
576 				return (status);
577 
578 			enable = PSVC_ENABLED;
579 			status = psvc_set_attr(hdlp, backup_fan,
580 				PSVC_ENABLE_ATTR, &enable);
581 			if (status != PSVC_SUCCESS)
582 				return (status);
583 		} else {
584 			enable = PSVC_DISABLED;
585 			status = psvc_set_attr(hdlp, id, PSVC_ENABLE_ATTR,
586 				&enable);
587 			if (status != PSVC_SUCCESS)
588 				return (status);
589 		}
590 		return (PSVC_SUCCESS);
591 	}
592 
593 	/* device was present */
594 	status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state);
595 	if (status != PSVC_SUCCESS)
596 		return (status);
597 
598 	status = psvc_get_attr(hdlp, id, PSVC_PREV_STATE_ATTR, previous_state);
599 	if (status != PSVC_SUCCESS)
600 		return (status);
601 
602 	if (features & PSVC_DEV_PRIMARY) {
603 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
604 			&backup_fan, PSVC_ALTERNATE, 0);
605 		if (status != PSVC_SUCCESS)
606 			return (status);
607 
608 		if (strcmp(state, PSVC_OK) == 0) {
609 			enable = PSVC_ENABLED;
610 			status = psvc_set_attr(hdlp, id, PSVC_ENABLE_ATTR,
611 				&enable);
612 			if (status != PSVC_SUCCESS)
613 				return (status);
614 
615 			enable = PSVC_DISABLED;
616 			status = psvc_set_attr(hdlp, backup_fan,
617 				PSVC_ENABLE_ATTR, &enable);
618 			if (status != PSVC_SUCCESS)
619 				return (status);
620 		}
621 		if ((strcmp(state, PSVC_ERROR) == 0) &&
622 			(strcmp(previous_state, PSVC_ERROR) != 0)) {
623 			enable = PSVC_DISABLED;
624 			status = psvc_set_attr(hdlp, id, PSVC_ENABLE_ATTR,
625 				&enable);
626 			if (status != PSVC_SUCCESS)
627 				return (status);
628 
629 			enable = PSVC_ENABLED;
630 			status = psvc_set_attr(hdlp, backup_fan,
631 				PSVC_ENABLE_ATTR, &enable);
632 			if (status != PSVC_SUCCESS)
633 				return (status);
634 		}
635 	} else {
636 		if ((strcmp(state, PSVC_ERROR) == 0) &&
637 			(strcmp(previous_state, PSVC_ERROR) != 0)) {
638 			status = psvc_get_attr(hdlp, id, PSVC_LABEL_ATTR,
639 				label);
640 			if (status != PSVC_SUCCESS)
641 				return (status);
642 			syslog(LOG_ERR, SECONDARY_FAN_FAIL_MSG, label);
643 		}
644 	}
645 	return (status);
646 }
647 
648 /*
649  * psvc_switch_fan_onoff_policy_0
650  * Turn a fan on if it is enabled, turn it off if it is disabled.
651  */
652 int32_t
653 psvc_switch_fan_onoff_policy_0(psvc_opaque_t hdlp, char *id)
654 {
655 	boolean_t enable;
656 	char *switchid;
657 	char state[32];
658 	int32_t status = PSVC_SUCCESS;
659 
660 	status = psvc_get_attr(hdlp, id, PSVC_ENABLE_ATTR, &enable);
661 	if (status != PSVC_SUCCESS)
662 		return (status);
663 
664 	status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &switchid,
665 		PSVC_FAN_ONOFF_SENSOR, 0);
666 	if (status != PSVC_SUCCESS)
667 		return (status);
668 
669 	if (enable == PSVC_DISABLED) {
670 		strcpy(state, PSVC_SWITCH_OFF);
671 	} else {
672 		strcpy(state, PSVC_SWITCH_ON);
673 	}
674 
675 	status = psvc_set_attr(hdlp, switchid, PSVC_SWITCH_STATE_ATTR, state);
676 	return (status);
677 }
678 
679 static int32_t
680 check_cpu_temp_fault(psvc_opaque_t hdlp, char *cpu, int32_t cpu_count)
681 {
682 	char *sensorid;
683 	int32_t sensor_count;
684 	int32_t status = PSVC_SUCCESS;
685 	int32_t i;
686 	uint64_t features;
687 	char fault[32];
688 
689 	status = psvc_get_attr(hdlp, cpu, PSVC_FEATURES_ATTR, &features);
690 	if (status == PSVC_FAILURE)
691 		return (status);
692 
693 	psvc_get_attr(hdlp, cpu, PSVC_ASSOC_MATCHES_ATTR, &sensor_count,
694 		PSVC_DEV_TEMP_SENSOR);
695 	for (i = 0; i < sensor_count; ++i) {
696 		status = psvc_get_attr(hdlp, cpu, PSVC_ASSOC_ID_ATTR,
697 			&sensorid, PSVC_DEV_TEMP_SENSOR, i);
698 		if (status == PSVC_FAILURE)
699 			return (status);
700 
701 		status = psvc_get_attr(hdlp, sensorid, PSVC_FAULTID_ATTR,
702 			fault);
703 		if (status == PSVC_FAILURE)
704 			return (status);
705 
706 		if ((strcmp(fault, PSVC_TEMP_HI_SHUT) == 0) ||
707 			(strcmp(fault, PSVC_TEMP_LO_SHUT) == 0)) {
708 			if (cpu_count == 1 || cpus_online == 1 ||
709 			    !(features & PSVC_DEV_HOTPLUG)) {
710 				system(shutdown_string);
711 			} else {
712 				/* FIX offline cpu */
713 				--cpus_online;
714 			}
715 		}
716 	}
717 
718 	return (status);
719 }
720 
721 int32_t
722 psvc_shutdown_policy_0(psvc_opaque_t hdlp, char *id)
723 {
724 	int32_t cpu_count;
725 	char *cpuid;
726 	int32_t i;
727 	boolean_t present;
728 	int32_t status = PSVC_SUCCESS;
729 
730 	if (cpus_online == 0) {
731 		/* obviously, zero isn't correct, count present cpu's */
732 		psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &cpu_count,
733 			PSVC_CPU);
734 		for (i = 0; i < cpu_count; ++i) {
735 			status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
736 				&cpuid, PSVC_CPU, i);
737 			if (status == PSVC_FAILURE)
738 				return (status);
739 
740 			status = psvc_get_attr(hdlp, cpuid,
741 				PSVC_PRESENCE_ATTR, &present);
742 			if (status == PSVC_FAILURE && present == PSVC_PRESENT)
743 				return (status);
744 			if (present == PSVC_PRESENT)
745 				++cpus_online;
746 		}
747 	}
748 	psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &cpu_count,
749 		PSVC_CPU);
750 	for (i = 0; i < cpu_count; ++i) {
751 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &cpuid,
752 			PSVC_CPU, i);
753 		if (status == PSVC_FAILURE)
754 			return (status);
755 		status = check_cpu_temp_fault(hdlp, cpuid, cpu_count);
756 		if (status == PSVC_FAILURE && errno != ENODEV)
757 			return (status);
758 	}
759 
760 	return (PSVC_SUCCESS);
761 }
762 
763 /*
764  * psvc_keyswitch_position_policy_0
765  * Checks the state of the keyswitch sensors.
766  * If a keyswitch position sensor's state is on, the position
767  * of the key is written to syslog.  If none of the sensors
768  * are on (keyswitch is not at one of the detents), a message is sent
769  * to syslog stating that the position is unknown.
770  */
771 int32_t
772 psvc_keyswitch_position_policy_0(psvc_opaque_t hdlp, char *id)
773 {
774 	char position[32];
775 	int32_t status = PSVC_SUCCESS;
776 	static int error_reported = 0;
777 	static char local_previous_position[32];
778 	static int32_t first_time = 1;
779 	int retry;
780 
781 	if (first_time) {
782 		first_time = 0;
783 		status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR,
784 		    local_previous_position);
785 		if (status != PSVC_SUCCESS)
786 			return (status);
787 	}
788 
789 	retry = 0;
790 	do {
791 		if (retry)
792 			(void) sleep(retry_sleep_keyswitch);
793 
794 		status = psvc_get_attr(hdlp, id, PSVC_SWITCH_STATE_ATTR,
795 		    position);
796 		if (status != PSVC_SUCCESS)
797 			return (status);
798 
799 		if (strcmp(position, PSVC_ERROR) == 0) {
800 			if ((errno == EINVAL) && (!(error_reported))) {
801 				syslog(LOG_ERR,
802 				    KEYSWITCH_POS_READ_FAILED_MSG);
803 				error_reported = 1;
804 				return (PSVC_SUCCESS);
805 			}
806 		}
807 		retry++;
808 	} while ((retry < n_retry_keyswitch) &&
809 	    (strcmp(position, local_previous_position) != 0));
810 
811 	status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, position);
812 	if (status != PSVC_SUCCESS)
813 		return (status);
814 
815 	if (strcmp(position, local_previous_position) != 0) {
816 		error_reported = 0;
817 		strcpy(local_previous_position, position);
818 		syslog(LOG_ERR, KEYSWITCH_POS_CHANGED_MSG, position);
819 	}
820 
821 	return (status);
822 }
823 
824 int32_t
825 psvc_hotplug_notifier_policy_0(psvc_opaque_t hdlp, char *id)
826 {
827 	boolean_t presence, previous_presence;
828 	int32_t status = PSVC_SUCCESS;
829 	char label[32];
830 	int retry;
831 
832 	status = psvc_get_attr(hdlp, id, PSVC_PREV_PRESENCE_ATTR,
833 		&previous_presence);
834 	if (status != PSVC_SUCCESS)
835 		return (status);
836 
837 	retry = 0;
838 	do {
839 		if (retry)
840 			(void) sleep(retry_sleep_hotplug);
841 		status = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &presence);
842 		if (status != PSVC_SUCCESS)
843 			return (status);
844 		retry++;
845 	} while ((retry < n_retry_hotplug) &&
846 	    (presence != previous_presence));
847 
848 
849 	if (presence != previous_presence) {
850 		char parent_path[256];
851 		picl_nodehdl_t child_node;
852 
853 		status = psvc_get_attr(hdlp, id, PSVC_LABEL_ATTR, label);
854 		if (status != PSVC_SUCCESS)
855 			return (status);
856 
857 		/* return parent path and node for an object */
858 		psvcplugin_lookup(id, parent_path, &child_node);
859 
860 		if (presence == PSVC_PRESENT) {
861 			char state[32], fault[32];
862 			picl_nodehdl_t parent_node;
863 
864 			syslog(LOG_ERR, DEVICE_INSERTED_MSG, label);
865 			strcpy(state, PSVC_OK);
866 			status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR,
867 				state);
868 			if (status != PSVC_SUCCESS)
869 				return (status);
870 			strcpy(fault, PSVC_NO_FAULT);
871 			status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR,
872 				fault);
873 			if (status != PSVC_SUCCESS) {
874 				return (status);
875 			}
876 
877 			status = ptree_get_node_by_path(parent_path,
878 				&parent_node);
879 			if (status != 0)
880 				return (PSVC_FAILURE);
881 			status = ptree_add_node(parent_node, child_node);
882 			if (status != 0)
883 				return (PSVC_FAILURE);
884 		} else {
885 			syslog(LOG_ERR, DEVICE_REMOVED_MSG, label);
886 
887 			ptree_delete_node(child_node);
888 
889 		}
890 	}
891 
892 	status = psvc_set_attr(hdlp, id, PSVC_PREV_PRESENCE_ATTR, &presence);
893 	if (status != PSVC_SUCCESS)
894 		return (status);
895 
896 	return (status);
897 }
898 
899 int32_t
900 psvc_fan_hotplug_policy_0(psvc_opaque_t hdlp, char *id)
901 {
902 	boolean_t presence, previous_presence;
903 	int32_t status = PSVC_SUCCESS;
904 	char label[32];
905 	int retry;
906 
907 	status = psvc_get_attr(hdlp, id, PSVC_PREV_PRESENCE_ATTR,
908 		&previous_presence);
909 	if (status != PSVC_SUCCESS)
910 		return (status);
911 
912 	retry = 0;
913 	do {
914 		if (retry)
915 			(void) sleep(retry_sleep_fan_hotplug);
916 
917 		status = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &presence);
918 		if (status != PSVC_SUCCESS)
919 			return (status);
920 		retry++;
921 	} while ((retry < n_retry_fan_hotplug) &&
922 	    (presence != previous_presence));
923 
924 
925 	if (presence != previous_presence) {
926 		char parent_path[256];
927 		picl_nodehdl_t child_node;
928 
929 		status = psvc_get_attr(hdlp, id, PSVC_LABEL_ATTR, label);
930 		if (status != PSVC_SUCCESS)
931 			return (status);
932 
933 		/* return parent path and node for an object */
934 		psvcplugin_lookup(id, parent_path, &child_node);
935 
936 		if (presence == PSVC_PRESENT) {
937 			char state[32], fault[32];
938 			char *slot_id;
939 			char *led_id;
940 			int32_t i, led_count;
941 			char led_state[32];
942 			picl_nodehdl_t parent_node;
943 
944 			syslog(LOG_ERR, DEVICE_INSERTED_MSG, label);
945 
946 			strcpy(state, PSVC_OK);
947 			status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR,
948 				state);
949 			if (status != PSVC_SUCCESS)
950 				return (status);
951 			strcpy(fault, PSVC_NO_FAULT);
952 			status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR,
953 				fault);
954 			if (status != PSVC_SUCCESS)
955 				return (status);
956 
957 			/* turn off fault LEDs */
958 			psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR,
959 				&led_count, PSVC_DEV_FAULT_LED);
960 			strcpy(led_state, PSVC_LED_OFF);
961 			for (i = 0; i < led_count; ++i) {
962 				status = psvc_get_attr(hdlp, id,
963 					PSVC_ASSOC_ID_ATTR, &led_id,
964 					PSVC_DEV_FAULT_LED, i);
965 				if (status == PSVC_FAILURE)
966 					return (status);
967 				status = psvc_set_attr(hdlp, led_id,
968 					PSVC_LED_STATE_ATTR, led_state);
969 				if (status == PSVC_FAILURE)
970 					return (status);
971 			}
972 
973 			/* turn off OK to remove LEDs */
974 			status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
975 				&slot_id, PSVC_PARENT, 0);
976 			if (status != PSVC_SUCCESS)
977 				return (status);
978 
979 			psvc_get_attr(hdlp, slot_id, PSVC_ASSOC_MATCHES_ATTR,
980 				&led_count, PSVC_SLOT_REMOVE_LED);
981 			strcpy(led_state, PSVC_LED_OFF);
982 			for (i = 0; i < led_count; ++i) {
983 				status = psvc_get_attr(hdlp, slot_id,
984 					PSVC_ASSOC_ID_ATTR, &led_id,
985 					PSVC_SLOT_REMOVE_LED, i);
986 				if (status == PSVC_FAILURE)
987 					return (status);
988 
989 				status = psvc_set_attr(hdlp, led_id,
990 					PSVC_LED_STATE_ATTR, led_state);
991 				if (status == PSVC_FAILURE)
992 					return (status);
993 			}
994 
995 			ptree_get_node_by_path(parent_path, &parent_node);
996 			ptree_add_node(parent_node, child_node);
997 		} else {
998 			syslog(LOG_ERR, DEVICE_REMOVED_MSG, label);
999 			ptree_delete_node(child_node);
1000 		}
1001 	}
1002 
1003 	status = psvc_set_attr(hdlp, id, PSVC_PREV_PRESENCE_ATTR, &presence);
1004 	if (status != PSVC_SUCCESS)
1005 		return (status);
1006 
1007 	return (status);
1008 }
1009 
1010 int32_t
1011 psvc_init_led_policy_0(psvc_opaque_t hdlp, char *id)
1012 {
1013 	int32_t status;
1014 
1015 	status = psvc_set_attr(hdlp, id, PSVC_LED_STATE_ATTR, PSVC_LED_OFF);
1016 	return (status);
1017 }
1018 
1019 int32_t
1020 psvc_init_state_policy_0(psvc_opaque_t hdlp, char *id)
1021 {
1022 	int32_t status;
1023 
1024 	status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, PSVC_OK);
1025 	status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, PSVC_NO_FAULT);
1026 	return (status);
1027 }
1028 
1029 int32_t
1030 psvc_ps_overcurrent_check_policy_0(psvc_opaque_t hdlp, char *power_supply_id)
1031 {
1032 	int32_t status = PSVC_SUCCESS;
1033 	boolean_t present;
1034 	char *sensor_id;
1035 	int32_t sensor_count;
1036 	int32_t i;
1037 	int32_t amps, hi_warn;
1038 
1039 	status = psvc_get_attr(hdlp, power_supply_id, PSVC_PRESENCE_ATTR,
1040 		&present);
1041 	if (status == PSVC_FAILURE) {
1042 		syslog(LOG_ERR, GET_PRESENCE_FAILED_MSG, power_supply_id,
1043 			errno);
1044 		return (status);
1045 	}
1046 
1047 	if (present == PSVC_ABSENT) {
1048 		errno = ENODEV;
1049 		return (PSVC_FAILURE);
1050 	}
1051 
1052 	psvc_get_attr(hdlp, power_supply_id, PSVC_ASSOC_MATCHES_ATTR,
1053 		&sensor_count, PSVC_PS_I_SENSOR);
1054 	for (i = 0; i < sensor_count; ++i) {
1055 		status = psvc_get_attr(hdlp, power_supply_id,
1056 			PSVC_ASSOC_ID_ATTR, &sensor_id, PSVC_PS_I_SENSOR, i);
1057 		if (status != PSVC_SUCCESS)
1058 			return (status);
1059 
1060 		status = psvc_get_attr(hdlp, sensor_id, PSVC_HI_WARN_ATTR,
1061 			&hi_warn);
1062 		if (status != PSVC_SUCCESS)
1063 			return (status);
1064 
1065 		status = psvc_get_attr(hdlp, sensor_id,
1066 			PSVC_SENSOR_VALUE_ATTR, &amps);
1067 		if (status != PSVC_SUCCESS) {
1068 			syslog(LOG_ERR, GET_SENSOR_FAILED_MSG, sensor_id,
1069 				errno);
1070 			return (status);
1071 		}
1072 
1073 		if (amps >= hi_warn) {
1074 			char label[32];
1075 
1076 			status = psvc_get_attr(hdlp, power_supply_id,
1077 				PSVC_LABEL_ATTR, &label);
1078 			if (status != PSVC_SUCCESS)
1079 				return (status);
1080 
1081 			syslog(LOG_ERR, PS_OVER_CURRENT_MSG, label);
1082 		}
1083 	}
1084 
1085 	return (PSVC_SUCCESS);
1086 
1087 }
1088 
1089 int32_t
1090 psvc_device_fail_notifier_policy_0(psvc_opaque_t hdlp, char *id)
1091 {
1092 	int32_t led_count, sensor_count;
1093 	char *led_id, *sensor_id;
1094 	int i;
1095 	char state[32], fault[32], previous_state[32];
1096 	char led_state[32];
1097 	int32_t status = PSVC_SUCCESS;
1098 	boolean_t present;
1099 
1100 	status = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &present);
1101 	if (status == PSVC_FAILURE)
1102 		return (status);
1103 
1104 	if (present == PSVC_ABSENT) {
1105 		errno = ENODEV;
1106 		return (PSVC_FAILURE);
1107 	}
1108 
1109 	psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &sensor_count,
1110 		PSVC_DEV_FAULT_SENSOR);
1111 	for (i = 0; i < sensor_count; ++i) {
1112 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
1113 			&sensor_id, PSVC_DEV_FAULT_SENSOR, i);
1114 		if (status != PSVC_SUCCESS)
1115 			return (status);
1116 
1117 		status = psvc_get_attr(hdlp, sensor_id,
1118 			PSVC_SWITCH_STATE_ATTR, state);
1119 		if (status != PSVC_SUCCESS)
1120 			return (status);
1121 
1122 		if (strcmp(state, PSVC_SWITCH_ON) == 0) {
1123 			strcpy(state, PSVC_ERROR);
1124 			strcpy(fault, PSVC_GEN_FAULT);
1125 		} else {
1126 			strcpy(state, PSVC_OK);
1127 			strcpy(fault, PSVC_NO_FAULT);
1128 		}
1129 
1130 		status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, state);
1131 		if (status != PSVC_SUCCESS)
1132 			return (status);
1133 		status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, fault);
1134 		if (status != PSVC_SUCCESS)
1135 			return (status);
1136 		status = psvc_get_attr(hdlp, id, PSVC_PREV_STATE_ATTR,
1137 			previous_state);
1138 		if (status != PSVC_SUCCESS)
1139 			return (status);
1140 
1141 		if (strcmp(state, previous_state) != 0) {
1142 			char sensor_label[32];
1143 			char dev_label[32];
1144 			int32_t j;
1145 
1146 			psvc_get_attr(hdlp, id, PSVC_LABEL_ATTR, dev_label);
1147 			psvc_get_attr(hdlp, sensor_id, PSVC_LABEL_ATTR,
1148 				sensor_label);
1149 
1150 			if (strcmp(state, PSVC_ERROR) == 0) {
1151 				syslog(LOG_ERR, DEVICE_FAILURE_MSG, dev_label,
1152 					sensor_label);
1153 				strcpy(led_state, PSVC_LED_ON);
1154 			} else {
1155 				syslog(LOG_ERR, DEVICE_OK_MSG, dev_label);
1156 				strcpy(led_state, PSVC_LED_OFF);
1157 			}
1158 
1159 			psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR,
1160 				&led_count, PSVC_DEV_FAULT_LED);
1161 			for (j = 0; j < led_count; j++) {
1162 				status = psvc_get_attr(hdlp, id,
1163 					PSVC_ASSOC_ID_ATTR, &led_id,
1164 					PSVC_DEV_FAULT_LED, j);
1165 				if (status != PSVC_SUCCESS)
1166 					return (status);
1167 				status = psvc_set_attr(hdlp, led_id,
1168 					PSVC_LED_STATE_ATTR, led_state);
1169 				if (status != PSVC_SUCCESS) {
1170 					syslog(LOG_ERR, SET_LED_FAILED_MSG,
1171 						led_id, errno);
1172 					return (status);
1173 				}
1174 			}
1175 		}
1176 	}
1177 
1178 	return (PSVC_SUCCESS);
1179 }
1180 
1181 static float
1182 get_filtered_error(float *last_errors, int current_error)
1183 {
1184 	float error;
1185 	float adder;
1186 	int i = 0;
1187 
1188 	adder = last_errors[0];
1189 	for (i = 1; i < PSVC_MAXERRORS; i++) {
1190 		adder = adder + last_errors[i];
1191 	}
1192 	adder = adder + current_error;
1193 	error = adder/(PSVC_MAXERRORS+1);
1194 
1195 	return (error);
1196 }
1197 
1198 static int32_t
1199 change_cpu_fans(psvc_opaque_t hdlp, char *fan_id, int32_t fan_speed)
1200 {
1201 	int err = PSVC_SUCCESS;
1202 	int i;
1203 	int32_t control_count;
1204 	char *control_id;
1205 	int32_t old_fan_speed;
1206 
1207 	psvc_get_attr(hdlp, fan_id, PSVC_ASSOC_MATCHES_ATTR, &control_count,
1208 		PSVC_FAN_DRIVE_CONTROL);
1209 	if (control_count == 0)
1210 		return (PSVC_SUCCESS);
1211 
1212 	err = psvc_get_attr(hdlp, fan_id, PSVC_ASSOC_ID_ATTR, &control_id,
1213 		PSVC_FAN_DRIVE_CONTROL, 0);
1214 	if (err != PSVC_SUCCESS)
1215 		return (err);
1216 
1217 	/*
1218 	 * this call will return PSVC_FAILURE on the first pass,
1219 	 * because no value has been set.
1220 	 */
1221 	err = psvc_get_attr(hdlp, control_id, PSVC_CONTROL_VALUE_ATTR,
1222 		&old_fan_speed);
1223 	if (err == PSVC_SUCCESS && old_fan_speed == fan_speed)
1224 		return (PSVC_SUCCESS);
1225 
1226 	for (i = 0; i < control_count; i++) {
1227 		err = psvc_get_attr(hdlp, fan_id, PSVC_ASSOC_ID_ATTR,
1228 			&control_id, PSVC_FAN_DRIVE_CONTROL, i);
1229 		if (err != PSVC_SUCCESS)
1230 			return (err);
1231 
1232 		err = psvc_set_attr(hdlp, control_id, PSVC_CONTROL_VALUE_ATTR,
1233 			&fan_speed);
1234 		if (err == PSVC_FAILURE) {
1235 			syslog(LOG_ERR, SET_FANSPEED_FAILED_MSG, control_id,
1236 				errno);
1237 			return (err);
1238 		}
1239 	}
1240 	return (err);
1241 }
1242 
1243 static int32_t
1244 device_temp_check(psvc_opaque_t hdlp, char *fan_id, int32_t *hot_device)
1245 {
1246 	int i;
1247 	int32_t err = PSVC_SUCCESS;
1248 	char *sensor_id;
1249 	int32_t sensor_count;
1250 	int32_t temp;
1251 
1252 	*hot_device = 0;
1253 
1254 	psvc_get_attr(hdlp, fan_id, PSVC_ASSOC_MATCHES_ATTR, &sensor_count,
1255 		PSVC_DEV_TEMP_SENSOR);
1256 	for (i = 0; i < sensor_count; i++) {
1257 		err = psvc_get_attr(hdlp, fan_id, PSVC_ASSOC_ID_ATTR,
1258 			&sensor_id, PSVC_DEV_TEMP_SENSOR, i);
1259 		if (err == PSVC_FAILURE)
1260 			return (err);
1261 		err = psvc_get_attr(hdlp, sensor_id, PSVC_SENSOR_VALUE_ATTR,
1262 			&temp);
1263 		if (err == PSVC_FAILURE) {
1264 			if (errno == ENODEV) {
1265 				temp = 0;
1266 			} else {
1267 				syslog(LOG_ERR, GET_SENSOR_FAILED_MSG,
1268 				    sensor_id, errno);
1269 				return (err);
1270 			}
1271 		}
1272 
1273 		if (*hot_device < temp)
1274 			*hot_device = temp;
1275 	}
1276 	return (PSVC_SUCCESS);
1277 }
1278 
1279 int32_t
1280 psvc_fan_control_policy_0(psvc_opaque_t hdlp, char *fan_id)
1281 {
1282 	boolean_t is_enabled;
1283 	int32_t err = PSVC_SUCCESS;
1284 	int16_t setpoint, hysteresis, loopgain, loopbias;
1285 	int current_error;		/* Holds current error */
1286 					/* Signal before signaling */
1287 	float filtered_error;		/* Holds the filtered error signal */
1288 	int ampout;			/* output of loop amplifier */
1289 	int hot_device;
1290 
1291 	int16_t error_number;
1292 	float last_errors[PSVC_MAXERRORS];	/* Holds the filtered error */
1293 						/* from the last n iterations */
1294 
1295 	psvc_get_attr(hdlp, fan_id, PSVC_ENABLE_ATTR, &is_enabled);
1296 	if (is_enabled == PSVC_DISABLED)
1297 		return (PSVC_SUCCESS);
1298 
1299 	err = psvc_get_attr(hdlp, fan_id, PSVC_SETPOINT_ATTR, &setpoint);
1300 	if (err != PSVC_SUCCESS)
1301 		return (err);
1302 
1303 	err = psvc_get_attr(hdlp, fan_id, PSVC_HYSTERESIS_ATTR,
1304 		&hysteresis);
1305 	if (err != PSVC_SUCCESS)
1306 		return (err);
1307 
1308 	err = psvc_get_attr(hdlp, fan_id, PSVC_LOOPGAIN_ATTR, &loopgain);
1309 	if (err != PSVC_SUCCESS)
1310 		return (err);
1311 
1312 	err = psvc_get_attr(hdlp, fan_id, PSVC_LOOPBIAS_ATTR, &loopbias);
1313 	if (err != PSVC_SUCCESS)
1314 		return (err);
1315 
1316 	err = psvc_get_attr(hdlp, fan_id, PSVC_TEMP_DIFFERENTIAL_ATTR,
1317 		last_errors);
1318 	if (err != PSVC_SUCCESS)
1319 		return (err);
1320 
1321 	err = psvc_get_attr(hdlp, fan_id, PSVC_TEMP_DIFFERENTIAL_INDEX_ATTR,
1322 		&error_number);
1323 	if (err != PSVC_SUCCESS)
1324 		return (err);
1325 
1326 	err = device_temp_check(hdlp, fan_id, &hot_device);
1327 	if (err != PSVC_SUCCESS) {
1328 		printf("psvc_fan_control failure in device_temp_check\n");
1329 		return (err);
1330 	}
1331 	current_error = setpoint - hot_device;
1332 	filtered_error = get_filtered_error(last_errors, current_error);
1333 	if (filtered_error <= 0 || filtered_error > hysteresis) {
1334 		ampout = (int)((filtered_error * loopgain) + loopbias);
1335 		if (ampout < 0)
1336 			ampout = 0;
1337 		if (ampout > 1023)
1338 			ampout = 1023;
1339 		err = change_cpu_fans(hdlp, fan_id, ampout);
1340 		if (err != PSVC_SUCCESS)
1341 			return (err);
1342 	}
1343 	last_errors[error_number++] = current_error;
1344 	if (error_number == PSVC_MAXERRORS)
1345 		error_number = 0;
1346 
1347 	err = psvc_set_attr(hdlp, fan_id, PSVC_TEMP_DIFFERENTIAL_ATTR,
1348 		last_errors);
1349 	if (err != PSVC_SUCCESS)
1350 		return (err);
1351 
1352 	err = psvc_set_attr(hdlp, fan_id, PSVC_TEMP_DIFFERENTIAL_INDEX_ATTR,
1353 		&error_number);
1354 	if (err != PSVC_SUCCESS)
1355 		return (err);
1356 
1357 	return (PSVC_SUCCESS);
1358 }
1359 
1360 int32_t
1361 psvc_fan_present_policy_0(psvc_opaque_t hdlp, char *id)
1362 {
1363 	int32_t		status = PSVC_SUCCESS;
1364 	boolean_t	presence;
1365 	int fd;
1366 	FILE *fp;
1367 	int retry;
1368 
1369 	retry = 0;
1370 	do {
1371 		if (retry)
1372 			(void) sleep(retry_sleep_fan_present);
1373 
1374 		status = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &presence);
1375 		if (status != PSVC_SUCCESS)
1376 			return (status);
1377 		retry++;
1378 	} while ((retry < n_retry_fan_present) && (presence == PSVC_ABSENT));
1379 
1380 	if (presence == PSVC_ABSENT) {
1381 		/*
1382 		 * We make this open, write, close, call because picld
1383 		 * starts in rcS.d while print services does not start
1384 		 * until later (either rc2.d or rc3.d)
1385 		 */
1386 		fd = open("/dev/console", O_WRONLY | O_NOCTTY);
1387 		if (fd != -1) {
1388 			fp = fdopen(fd, "w+");
1389 			if (fp != NULL) {
1390 				fprintf(fp, FAN_MISSING_MSG, id);
1391 				fclose(fp);
1392 			}
1393 			close(fd);
1394 		}
1395 		syslog(LOG_ERR, FAN_MISSING_MSG, id);
1396 	}
1397 	return (status);
1398 }
1399