xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4u/cherrystone/psvcpolicy/psvcpolicy.c (revision 628e3cbed6489fa1db545d8524a06cd6535af456)
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 2007 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  * Cherrystone platform specific environment monitoring policies
31  */
32 
33 #include	<syslog.h>
34 #include	<unistd.h>
35 #include	<stdio.h>
36 #include 	<libintl.h>
37 #include	<string.h>
38 #include	<stdlib.h>
39 #include	<errno.h>
40 #include	<fcntl.h>
41 #include	<sys/types.h>
42 #include	<sys/time.h>
43 #include	<sys/time_impl.h>
44 #include	<sys/signal.h>
45 #include	<sys/devctl.h>
46 #include	<libdevinfo.h>
47 #include	<libdevice.h>
48 #include	<picl.h>
49 #include	<picltree.h>
50 #include	<sys/i2c/clients/i2c_client.h>
51 #include	<hbaapi.h>
52 #include	<limits.h>
53 #include	<sys/systeminfo.h>
54 
55 #include	<psvc_objects.h>
56 
57 /* Device paths for power supply hotplug handling */
58 #define	SEG5_ADDR		0x30
59 #define	EBUS_DEV_NAME		"/devices/pci@9,700000/ebus@1/"
60 #define	SEG5_DEV_NAME		EBUS_DEV_NAME "i2c@1,30/"
61 #define	SEG5_ADDR_DEV_FMT	EBUS_DEV_NAME "i2c@1,%x:devctl"
62 
63 #define	QLC_NODE		 "/pci@9,600000/SUNW,qlc@2"
64 
65 #define	DISK_DRV  "ssd"
66 #define	MAX_DISKS 2
67 #define	WWN_SIZE 8
68 #define	ONBOARD_CONTR	"../../devices/pci@9,600000/SUNW,qlc@2/fp@0,0:fc"
69 
70 /* Bit masks so we don't "wedge" the inputs */
71 #define	PCF8574_BIT_WRITE_VALUE(byte, bit, value)\
72 				((value << bit) | (byte & (~(0x01 << bit))))
73 
74 #define	PDB_MUST_BE_1		0xBF
75 #define	PSU_MUST_BE_1		0x7F
76 #define	DISKBP_MUST_BE_1	0x0F
77 
78 /*LINTLIBRARY*/
79 
80 #define	PSVC_MAX_STR_LEN	32
81 
82 #define	PS_MAX_FAULT_SENSORS 3
83 
84 /*
85  * Keep track of the power supply's fail status for reporting if/when
86  * they go good.
87  * ID's:
88  * O	PSx_FAULT_SENSOR
89  * 1	Doesn't matter	-- only need 0 to be PSx_FAULT_SENSOR
90  * 2	Doesn't matter
91  */
92 static char	*ps_prev_id[2][3] =
93 		{{NULL, NULL, NULL}, {NULL, NULL, NULL}};
94 static int	ps_prev_failed[2][3] = {{0, 0, 0}, {0, 0, 0}};
95 
96 /*
97  * Keep track of the power supply's previous presence
98  * because PSVC doesn't do that for us.
99  */
100 static boolean_t ps_prev_present[2];
101 static boolean_t ps_present[2];
102 
103 /* Local Routines for the environmental policies */
104 static int ac_unplugged(psvc_opaque_t, char *);
105 static int ac_power_check(psvc_opaque_t, char *, char *);
106 
107 /*
108  * The I2C bus is noisy, and the state may be incorrectly reported as
109  * having changed.  When the state changes, we attempt to confirm by
110  * retrying.  If any retries indicate that the state has not changed, we
111  * assume the state change(s) were incorrect and the state has not changed.
112  * The following variables are used to store the tuneable values read in
113  * from the optional i2cparam.conf file for this shared object library.
114  */
115 static int n_retry_fan = PSVC_NUM_OF_RETRIES;
116 static int retry_sleep_fan = 1;
117 static int n_retry_ps_status = PSVC_NUM_OF_RETRIES;
118 static int retry_sleep_ps_status = 1;
119 static int n_retry_pshp = PSVC_NUM_OF_RETRIES;
120 static int retry_sleep_pshp = 1;
121 static int n_retry_diskhp = PSVC_NUM_OF_RETRIES;
122 static int retry_sleep_diskhp = 1;
123 static int n_retry_temp_shutdown = PSVC_NUM_OF_RETRIES;
124 static int retry_sleep_temp_shutdown = 1;
125 static int n_retry_fsp_fault = PSVC_NUM_OF_RETRIES;
126 static int retry_sleep_fsp_fault = 1;
127 
128 typedef struct {
129 	int *pvar;
130 	char *texttag;
131 } i2c_noise_param_t;
132 
133 static i2c_noise_param_t i2cparams[] = {
134 	&n_retry_fan, "n_retry_fan",
135 	&retry_sleep_fan, "retry_sleep_fan",
136 	&n_retry_ps_status, "n_retry_ps_status",
137 	&retry_sleep_ps_status, "retry_sleep_ps_status",
138 	&n_retry_pshp, "n_retry_pshp",
139 	&retry_sleep_pshp, "retry_sleep_pshp",
140 	&n_retry_diskhp, "n_retry_diskhp",
141 	&retry_sleep_diskhp, "retry_sleep_diskhp",
142 	&n_retry_temp_shutdown, "n_retry_temp_shutdown",
143 	&retry_sleep_temp_shutdown, "retry_sleep_temp_shutdown",
144 	&n_retry_fsp_fault, "n_retry_fsp_fault",
145 	&retry_sleep_fsp_fault, "retry_sleep_fsp_fault",
146 	NULL, NULL
147 };
148 
149 #pragma init(i2cparams_load)
150 
151 static void
152 i2cparams_debug(i2c_noise_param_t *pi2cparams, char *platform,
153 	int usingDefaults)
154 {
155 	char s[128];
156 	i2c_noise_param_t *p;
157 
158 	if (!usingDefaults) {
159 		(void) snprintf(s, sizeof (s),
160 		    "# Values from /usr/platform/%s/lib/i2cparam.conf\n",
161 		    platform);
162 		syslog(LOG_WARNING, "%s", s);
163 	} else {
164 		/* no file - we're using the defaults */
165 		(void) snprintf(s, sizeof (s),
166 "# No /usr/platform/%s/lib/i2cparam.conf file, using defaults\n",
167 		    platform);
168 	}
169 	(void) fputs(s, stdout);
170 	p = pi2cparams;
171 	while (p->pvar != NULL) {
172 		(void) snprintf(s, sizeof (s), "%s %d\n", p->texttag,
173 		    *(p->pvar));
174 		if (!usingDefaults)
175 			syslog(LOG_WARNING, "%s", s);
176 		(void) fputs(s, stdout);
177 		p++;
178 	}
179 }
180 
181 static void
182 i2cparams_load(void)
183 {
184 	FILE *fp;
185 	char filename[PATH_MAX];
186 	char platform[64];
187 	char s[128];
188 	char var[128];
189 	int val;
190 	i2c_noise_param_t *p;
191 
192 	if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) == -1) {
193 		syslog(LOG_ERR, "sysinfo error %s\n", strerror(errno));
194 		return;
195 	}
196 	(void) snprintf(filename, sizeof (filename),
197 	    "/usr/platform/%s/lib/i2cparam.conf", platform);
198 	/* read thru the i2cparam.conf file and set variables */
199 	if ((fp = fopen(filename, "r")) != NULL) {
200 		while (fgets(s, sizeof (s), fp) != NULL) {
201 			if (s[0] == '#') /* skip comment lines */
202 				continue;
203 			/* try to find a string match and get the value */
204 			if (sscanf(s, "%127s %d", var, &val) != 2)
205 				continue;
206 			if (val < 1)
207 				val = 1;  /* clamp min value */
208 			p = &(i2cparams[0]);
209 			while (p->pvar != NULL) {
210 				if (strncmp(p->texttag, var, sizeof (var)) ==
211 				    0) {
212 					*(p->pvar) = val;
213 					break;
214 				}
215 				p++;
216 			}
217 		}
218 		(void) fclose(fp);
219 	}
220 	/* output the values of the parameters */
221 	i2cparams_debug(&(i2cparams[0]), platform, ((fp == NULL)? 1 : 0));
222 }
223 
224 /*
225  * Create an I2C device node.
226  */
227 static int
228 create_i2c_node(char *nd_name, char *nd_compat, int nd_nexi, int *nd_reg)
229 {
230 	devctl_ddef_t	ddef_hdl = NULL;
231 	devctl_hdl_t	bus_hdl = NULL;
232 	devctl_hdl_t	dev_hdl = NULL;
233 	char		buf[MAXPATHLEN];
234 	char		dev_path[MAXPATHLEN];
235 	int		rv = PSVC_FAILURE;
236 
237 	(void) snprintf(buf, sizeof (buf), SEG5_ADDR_DEV_FMT, nd_nexi);
238 	bus_hdl = devctl_bus_acquire(buf, 0);
239 	if (bus_hdl == NULL)
240 		goto bad;
241 
242 	/* device definition properties */
243 	ddef_hdl = devctl_ddef_alloc(nd_name, 0);
244 	(void) devctl_ddef_string(ddef_hdl, "compatible", nd_compat);
245 	(void) devctl_ddef_int_array(ddef_hdl, "reg", 2, nd_reg);
246 
247 	/* create the device node */
248 	if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl))
249 		goto bad;
250 
251 	if (devctl_get_pathname(dev_hdl, dev_path, MAXPATHLEN) == NULL)
252 		goto bad;
253 
254 #ifdef DEBUG
255 	syslog(LOG_ERR, "PSVC: create_i2c_node: Device node created: (%s)",
256 		dev_path);
257 #endif
258 	rv = PSVC_SUCCESS;
259 bad:
260 	if (dev_hdl)  devctl_release(dev_hdl);
261 	if (ddef_hdl) devctl_ddef_free(ddef_hdl);
262 	if (bus_hdl)  devctl_release(bus_hdl);
263 	return (rv);
264 }
265 
266 /*
267  * Delete an I2C device node given the device path.
268  */
269 static void
270 delete_i2c_node(char *nd)
271 {
272 	int		rv;
273 	devctl_hdl_t	dev_hdl;
274 
275 	dev_hdl = devctl_device_acquire(nd, 0);
276 	if (dev_hdl == NULL) {
277 		return;
278 	}
279 
280 	rv = devctl_device_remove(dev_hdl);
281 	if (rv != DDI_SUCCESS)
282 		perror(nd);
283 #ifdef DEBUG
284 	else
285 		syslog(LOG_ERR, "Device node deleted: (%s)", nd);
286 #endif
287 	devctl_release(dev_hdl);
288 }
289 
290 
291 /* PCF8574 Reset Function */
292 static int
293 send_pcf8574_reset(psvc_opaque_t hdlp, char *reset_dev)
294 {
295 	int	err;
296 	uint8_t reset_bits[2] = {0x7F, 0xFF};
297 	int	i;
298 	for (i = 0; i < 2; i++) {
299 		err = psvc_set_attr(hdlp, reset_dev, PSVC_GPIO_VALUE_ATTR,
300 			&reset_bits[i]);
301 		if (err != PSVC_SUCCESS) {
302 #ifdef DEBUG
303 			syslog(LOG_ERR,
304 				gettext("Reset to %s with 0x%x failed"),
305 				reset_dev, reset_bits[i]);
306 #endif
307 			return (err);
308 		}
309 	}
310 	/* Need to give u-code a chance to update */
311 	sleep(3);
312 	return (err);
313 }
314 
315 static int
316 pcf8574_write_bit(psvc_opaque_t hdlp, char *id, uint8_t bit_num,
317 	uint8_t bit_val, uint8_t write_must_be_1)
318 {
319 	int	rv = PSVC_FAILURE;
320 	uint8_t	byte;
321 
322 	rv = psvc_get_attr(hdlp, id, PSVC_GPIO_VALUE_ATTR, &byte);
323 	if (rv != PSVC_SUCCESS)
324 		return (rv);
325 
326 	byte = PCF8574_BIT_WRITE_VALUE(byte, bit_num, bit_val);
327 	byte |= write_must_be_1;
328 	rv = psvc_set_attr(hdlp, id, PSVC_GPIO_VALUE_ATTR, &byte);
329 	return (rv);
330 }
331 
332 /*
333  * To enable the i2c bus, we must toggle bit 6 on the PDB's
334  * PCF8574 (0x4C) high->low->high
335  */
336 static int
337 pdb_enable_i2c(psvc_opaque_t hdlp)
338 {
339 	int		rv = PSVC_SUCCESS, i;
340 	int		bit_vals[3] = {1, 0, 1};
341 	int		bit_num = 6;
342 
343 	for (i = 0; i < 3; i++) {
344 		rv = pcf8574_write_bit(hdlp, "PDB_PORT", bit_num, bit_vals[i],
345 			PDB_MUST_BE_1);
346 		if (rv != PSVC_SUCCESS) {
347 			goto bad;
348 		}
349 	}
350 	return (rv);
351 bad:
352 #ifdef DEBUG
353 	syslog(LOG_ERR, gettext("PDB I2C Bus Enabling Failed"));
354 #endif
355 	return (rv);
356 }
357 
358 int32_t
359 psvc_init_disk_bp_policy_0(psvc_opaque_t hdlp, char *id)
360 {
361 	uint8_t	reset = 0xFF;
362 	return (psvc_set_attr(hdlp, id, PSVC_GPIO_VALUE_ATTR,
363 		&reset));
364 }
365 
366 int32_t
367 pcf8574_init_policy_0(psvc_opaque_t hdlp, char *id)
368 {
369 	return (send_pcf8574_reset(hdlp, id));
370 }
371 
372 static int32_t
373 check_fan(psvc_opaque_t hdlp, char *tray_id, char *fan_id, boolean_t *fault_on)
374 {
375 	int		status;
376 	int		speed;
377 	int		low_thresh;
378 	boolean_t	have_fault = 0;
379 	char		*tach_id;
380 	char		state[PSVC_MAX_STR_LEN];
381 	char		prev_state[PSVC_MAX_STR_LEN];
382 	char		fault_state[PSVC_MAX_STR_LEN];
383 	int		retry;
384 
385 	/* Get this fan object's corresponding fan tach */
386 	status = psvc_get_attr(hdlp, fan_id, PSVC_ASSOC_ID_ATTR,
387 		&tach_id, PSVC_FAN_SPEED_TACHOMETER, 0);
388 	if (status != PSVC_SUCCESS)
389 		return (status);
390 
391 	/* Get the low fan speed threshold */
392 	status = psvc_get_attr(hdlp, tach_id, PSVC_LO_WARN_ATTR, &low_thresh);
393 	if (status != PSVC_SUCCESS)
394 		return (status);
395 
396 	retry = 0;
397 	do {
398 		if (retry)
399 			(void) sleep(retry_sleep_fan);
400 		/* Get the fan speed */
401 		status = psvc_get_attr(hdlp, tach_id, PSVC_SENSOR_VALUE_ATTR,
402 		    &speed);
403 		if (status != PSVC_SUCCESS)
404 		return (status);
405 
406 		if (speed <= low_thresh) { /* We see a fault */
407 			strlcpy(fault_state, "DEVICE_FAIL",
408 			    sizeof (fault_state));
409 			strlcpy(state, PSVC_ERROR, sizeof (state));
410 			have_fault = 1;
411 		} else { /* Fault gone? */
412 			strlcpy(fault_state, PSVC_NO_FAULT,
413 			    sizeof (fault_state));
414 			strlcpy(state, PSVC_OK, sizeof (state));
415 			have_fault = 0;
416 		}
417 		retry++;
418 	} while ((retry < n_retry_fan) && (speed <= low_thresh));
419 
420 	/* Assign new states to the fan object */
421 	status = psvc_set_attr(hdlp, fan_id, PSVC_FAULTID_ATTR, fault_state);
422 	if (status != PSVC_SUCCESS)
423 		return (status);
424 	status = psvc_set_attr(hdlp, fan_id, PSVC_STATE_ATTR, state);
425 	if (status != PSVC_SUCCESS)
426 		return (status);
427 
428 	/* Get state and previous state */
429 	status = psvc_get_attr(hdlp, fan_id, PSVC_STATE_ATTR, state);
430 	if (status != PSVC_SUCCESS)
431 		return (status);
432 	status = psvc_get_attr(hdlp, fan_id, PSVC_PREV_STATE_ATTR, prev_state);
433 	if (status != PSVC_SUCCESS)
434 		return (status);
435 
436 	/* Display notices */
437 	if (strcmp(state, PSVC_OK) != 0) {
438 		syslog(LOG_ERR,	gettext("WARNING: %s (%s) failure detected"),
439 			tray_id, fan_id);
440 	} else {
441 		if (strcmp(state, prev_state) != 0) {
442 		syslog(LOG_ERR,	gettext("NOTICE: Device %s (%s) OK"),
443 			tray_id, fan_id);
444 		}
445 	}
446 
447 	*fault_on |= have_fault;
448 	return (PSVC_SUCCESS);
449 }
450 
451 /*
452  * This policy acts on fan trays.  It looks at each of its fans
453  * and checks the speeds.  If the fan speed is less than the threshold,
454  * then indicate:  console, log, LED.
455  */
456 int32_t
457 psvc_fan_fault_check_policy_0(psvc_opaque_t hdlp, char *id)
458 {
459 	int		fan_count;
460 	int		led_count;
461 	int		err, i;
462 	char		*led_id;
463 	char		*fan_id;
464 	char		led_state[PSVC_MAX_STR_LEN];
465 	char		state[PSVC_MAX_STR_LEN];
466 	char		prev_state[PSVC_MAX_STR_LEN];
467 	boolean_t	fault_on = 0;
468 
469 	/* Get the number of fans associated with this fan tray. */
470 	err = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &fan_count,
471 		PSVC_FAN_TRAY_FANS);
472 	if (err != PSVC_SUCCESS)
473 		return (err);
474 
475 	for (i = 0; i < fan_count; i++) {
476 		err = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
477 			&fan_id, PSVC_FAN_TRAY_FANS, i);
478 		if (err != PSVC_SUCCESS)
479 			return (err);
480 
481 		err = check_fan(hdlp, id, fan_id, &fault_on);
482 		if (err != PSVC_SUCCESS)
483 			return (err);
484 	}
485 
486 	if (fault_on) {
487 		strlcpy(led_state, PSVC_LED_ON, sizeof (led_state));
488 		err = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, PSVC_ERROR);
489 		if (err != PSVC_SUCCESS)
490 			return (err);
491 
492 	} else {
493 		strlcpy(led_state, PSVC_LED_OFF, sizeof (led_state));
494 		err = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, PSVC_OK);
495 		if (err != PSVC_SUCCESS)
496 			return (err);
497 	}
498 
499 	err = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state);
500 	if (err != PSVC_SUCCESS)
501 		return (err);
502 	err = psvc_get_attr(hdlp, id, PSVC_PREV_STATE_ATTR, prev_state);
503 	if (err != PSVC_SUCCESS)
504 		return (err);
505 
506 	/*
507 	 * Set leds according to the fan tray's states.
508 	 * (we only do this if there is a change of state in order
509 	 *  to reduce i2c traffic)
510 	 */
511 	if (strcmp(state, prev_state) != 0) {
512 		err = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR,
513 			&led_count, PSVC_DEV_FAULT_LED);
514 		if (err != PSVC_SUCCESS)
515 			return (err);
516 		for (i = 0; i < led_count; i++) {
517 			err = psvc_get_attr(hdlp, id,
518 				PSVC_ASSOC_ID_ATTR, &led_id,
519 				PSVC_DEV_FAULT_LED, i);
520 			if (err != PSVC_SUCCESS)
521 				return (err);
522 			err = psvc_set_attr(hdlp, led_id,
523 				PSVC_LED_STATE_ATTR, led_state);
524 			if (err != PSVC_SUCCESS)
525 				return (err);
526 			err = psvc_get_attr(hdlp, led_id,
527 				PSVC_LED_STATE_ATTR, led_state);
528 			if (err != PSVC_SUCCESS)
529 				return (err);
530 		}
531 	}
532 	return (err);
533 }
534 
535 static int32_t
536 check_cpu_temp_fault(psvc_opaque_t hdlp, char *cpu, int32_t cpu_count)
537 {
538 	char *sensorid;
539 	int32_t sensor_count;
540 	int32_t status = PSVC_SUCCESS;
541 	int32_t i;
542 	char fault[PSVC_MAX_STR_LEN];
543 	int		retry;
544 
545 	psvc_get_attr(hdlp, cpu, PSVC_ASSOC_MATCHES_ATTR, &sensor_count,
546 		PSVC_DEV_TEMP_SENSOR);
547 	for (i = 0; i < sensor_count; ++i) {
548 		status = psvc_get_attr(hdlp, cpu, PSVC_ASSOC_ID_ATTR,
549 			&sensorid, PSVC_DEV_TEMP_SENSOR, i);
550 		if (status == PSVC_FAILURE)
551 			return (status);
552 
553 		retry = 0;
554 		do {
555 			if (retry)
556 				(void) sleep(retry_sleep_temp_shutdown);
557 			status = psvc_get_attr(hdlp, sensorid,
558 			    PSVC_FAULTID_ATTR, fault);
559 			if (status == PSVC_FAILURE)
560 				return (status);
561 			retry++;
562 		} while (((strcmp(fault, PSVC_TEMP_LO_SHUT) == 0) ||
563 		    (strcmp(fault, PSVC_TEMP_HI_SHUT) == 0)) &&
564 		    (retry < n_retry_temp_shutdown));
565 
566 		if ((strcmp(fault, PSVC_TEMP_HI_SHUT) == 0) ||
567 			(strcmp(fault, PSVC_TEMP_LO_SHUT) == 0)) {
568 			system("shutdown -y -g 60 -i 5 \"OVERTEMP condition\"");
569 		}
570 	}
571 
572 	return (status);
573 }
574 
575 int32_t
576 psvc_shutdown_policy_0(psvc_opaque_t hdlp, char *id)
577 {
578 	int32_t cpu_count;
579 	char *cpuid;
580 	int32_t i;
581 	boolean_t present;
582 	int32_t status = PSVC_SUCCESS;
583 
584 	psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &cpu_count,
585 		PSVC_CPU);
586 	for (i = 0; i < cpu_count; ++i) {
587 
588 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &cpuid,
589 			PSVC_CPU, i);
590 		if (status == PSVC_FAILURE)
591 			return (status);
592 
593 		status = psvc_get_attr(hdlp, cpuid,
594 			PSVC_PRESENCE_ATTR, &present);
595 		if (status == PSVC_FAILURE && present == PSVC_PRESENT)
596 			return (status);
597 		if (present == PSVC_PRESENT) {
598 			status = check_cpu_temp_fault(hdlp, cpuid, cpu_count);
599 			if (status == PSVC_FAILURE && errno != ENODEV)
600 				return (status);
601 		}
602 	}
603 
604 	return (PSVC_SUCCESS);
605 }
606 
607 /*
608  * Checks device specified by the PSVC_DEV_FAULT_SENSOR association
609  * for errors, and if there is, then report and turn on the FSP Fault
610  * Led.
611  */
612 int32_t
613 psvc_fsp_device_fault_check_policy_0(psvc_opaque_t hdlp, char *id)
614 {
615 	int32_t	status;
616 	int32_t	i;
617 	int32_t	device_count = 0;
618 	char	device_state[PSVC_MAX_STR_LEN];
619 	char	*device_id;
620 	int32_t	failed_count = 0;
621 	static int32_t led_on = 0;
622 	int		retry;
623 
624 	status = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR,
625 		&device_count, PSVC_DEV_FAULT_SENSOR);
626 	if (status != PSVC_SUCCESS)
627 		return (status);
628 
629 	for (i = 0; i < device_count; i++) {
630 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
631 			&device_id, PSVC_DEV_FAULT_SENSOR, i);
632 		if (status != PSVC_SUCCESS)
633 			return (status);
634 
635 		retry = 0;
636 		do {
637 			if (retry)
638 				(void) sleep(retry_sleep_fsp_fault);
639 			status = psvc_get_attr(hdlp, device_id, PSVC_STATE_ATTR,
640 			    device_state);
641 			if (status != PSVC_SUCCESS)
642 				return (status);
643 
644 			if (strcmp(device_state, PSVC_OK) != 0 &&
645 			    strcmp(device_state, PSVC_HOTPLUGGED) != 0 &&
646 			    strcmp(device_state, "NO AC POWER") != 0 &&
647 			    strlen(device_state) != 0) {
648 			    failed_count++;
649 			}
650 			retry++;
651 		} while ((retry < n_retry_fsp_fault) && (failed_count));
652 	}
653 	if (failed_count == 0 && led_on) {
654 		syslog(LOG_ERR, gettext("%s has turned OFF"), id);
655 		status = psvc_set_attr(hdlp, id, PSVC_LED_STATE_ATTR,
656 			PSVC_LED_OFF);
657 		led_on = 0;
658 	}
659 
660 	if (failed_count > 0 && ! led_on) {
661 		syslog(LOG_ERR,
662 			gettext("%s has turned ON"), id);
663 		status = psvc_set_attr(hdlp, id, PSVC_LED_STATE_ATTR,
664 			PSVC_LED_ON);
665 		led_on = 1;
666 	}
667 
668 	return (PSVC_SUCCESS);
669 }
670 
671 /* Power Supply Policy Helper and Worker Functions */
672 static void
673 ps_reset_prev_failed(int index)
674 {
675 	int	i;
676 	/* Reset the power supply's failure information */
677 	for (i = 0; i < 3; i++) {
678 		ps_prev_id[index][i] = NULL;
679 		ps_prev_failed[index][i] = 0;
680 	}
681 
682 }
683 static int
684 check_i2c_access(psvc_opaque_t hdlp, char *id)
685 {
686 	int		rv;
687 	char		state[PSVC_MAX_STR_LEN];
688 	char		ps_fault_sensor[PSVC_MAX_STR_LEN];
689 
690 	snprintf(ps_fault_sensor, sizeof (ps_fault_sensor),
691 		"%s_FAULT_SENSOR", id);
692 
693 	rv = psvc_get_attr(hdlp, ps_fault_sensor, PSVC_SWITCH_STATE_ATTR,
694 		&state);
695 	return (rv);
696 }
697 
698 /*
699  * This routine takes in the PSVC handle pointer, the PS name, and the
700  * instance number (0 or 1). It simply make a psvc_get call to get the
701  * presence of each of the children under the PS. This call will set the
702  * presence state of the child device if it was not there when the system
703  * was booted.
704  */
705 static int
706 handle_ps_hotplug_children_presence(psvc_opaque_t hdlp, char *id)
707 {
708 	char *child_add_on[4] = {"_RESET", "_LOGICAL_STATE", "_AC_IN_SENSOR",
709 				"_FAULT_SENSOR"};
710 	int add_ons = 4;
711 	char addon_id[PICL_PROPNAMELEN_MAX];
712 	char *sensor_id;
713 	int32_t	status = PSVC_SUCCESS;
714 	boolean_t presence;
715 	int j;
716 
717 	/* Go through the add on list and set presence */
718 	for (j = 0; j < add_ons; j++) {
719 		snprintf(addon_id, sizeof (addon_id), "%s%s", id,
720 		    child_add_on[j]);
721 		status = psvc_get_attr(hdlp, addon_id, PSVC_PRESENCE_ATTR,
722 		    &presence);
723 		if (status != PSVC_SUCCESS)
724 			return (status);
725 	}
726 
727 	/* Go through each PS's fault sensors */
728 	for (j = 0; j < PS_MAX_FAULT_SENSORS; j++) {
729 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
730 		    &(sensor_id), PSVC_DEV_FAULT_SENSOR, j);
731 		if (status != PSVC_SUCCESS)
732 			return (status);
733 		status = psvc_get_attr(hdlp, sensor_id, PSVC_PRESENCE_ATTR,
734 		    &presence);
735 		if (status != PSVC_SUCCESS)
736 			return (status);
737 	}
738 
739 	/* Go through each PS's onboard i2c hardware */
740 	for (j = 0; j < 2; j++) {
741 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
742 		    &(sensor_id), PSVC_PHYSICAL_DEVICE, j);
743 		if (status != PSVC_SUCCESS)
744 			return (status);
745 		status = psvc_get_attr(hdlp, sensor_id, PSVC_PRESENCE_ATTR,
746 		    &presence);
747 		if (status != PSVC_SUCCESS)
748 			return (status);
749 	}
750 
751 	return (status);
752 }
753 
754 static int
755 handle_ps_hotplug(psvc_opaque_t hdlp, char *id, boolean_t present)
756 {
757 	int32_t		status = PSVC_SUCCESS;
758 	int32_t		instance;
759 	picl_nodehdl_t	parent_node;
760 	picl_nodehdl_t	child_node;
761 	char		info[PSVC_MAX_STR_LEN];
762 	char		ps_logical_state[PICL_PROPNAMELEN_MAX];
763 	char		parent_path[PICL_PROPNAMELEN_MAX];
764 	char		ps_path[PICL_PROPNAMELEN_MAX];
765 	static int	fruprom_addr[2][2] = { {0, 0xa2}, {0, 0xa0} };
766 	static int	pcf8574_addr[2][2] = { {0, 0x72}, {0, 0x70} };
767 	char		dev_path[MAXPATHLEN];
768 
769 	/* Convert name to node and parent path */
770 	psvcplugin_lookup(id, parent_path, &child_node);
771 
772 	/*
773 	 * Get the power supply's instance.
774 	 * Used to index the xxx_addr arrays
775 	 */
776 	status = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance);
777 	if (status != PSVC_SUCCESS)
778 		return (status);
779 
780 	if (present == PSVC_PRESENT && !ps_prev_present[instance]) {
781 		/* Service Power Supply Insertion */
782 		syslog(LOG_ERR, gettext("Device %s inserted"), id);
783 
784 		/* PICL Tree Maintenance */
785 		ptree_get_node_by_path(parent_path, &parent_node);
786 		ptree_add_node(parent_node, child_node);
787 		snprintf(ps_path, sizeof (ps_path), "%s/%s", parent_path, id);
788 		psvcplugin_add_children(ps_path);
789 
790 		/*
791 		 * This code to update the presences of power supply
792 		 * child devices in the event that picld was started
793 		 * without a power supply present.  This call makes
794 		 * the devices available after that initial insertion.
795 		 */
796 		status = handle_ps_hotplug_children_presence(hdlp, id);
797 
798 		/*
799 		 * Device Tree Maintenance
800 		 * Add the devinfo tree node entry for the pcf8574 and seeprom
801 		 * and attach their drivers.
802 		 */
803 		status |= create_i2c_node("ioexp", "i2c-pcf8574", SEG5_ADDR,
804 			pcf8574_addr[instance]);
805 		status |= create_i2c_node("fru", "i2c-at24c64", SEG5_ADDR,
806 			fruprom_addr[instance]);
807 	} else {
808 		/* Service Power Supply Removal */
809 		syslog(LOG_ERR, gettext("Device %s removed"), id);
810 
811 		/* Reset the power supply's failure information */
812 		ps_reset_prev_failed(instance);
813 
814 		/* PICL Tree Maintenance */
815 		if (ptree_delete_node(child_node) != PICL_SUCCESS)
816 			syslog(LOG_ERR, "ptree_delete_node failed!");
817 
818 		/*
819 		 * The hardcoded subscript in pcf8574_add[instance][1]
820 		 * refers to the address.  We are appending the address to
821 		 * device path.  Both elements are used when creating
822 		 * the i2c node (above).
823 		 */
824 		snprintf(dev_path, sizeof (dev_path),
825 			SEG5_DEV_NAME"ioexp@0,%x:pcf8574",
826 			pcf8574_addr[instance][1]);
827 		delete_i2c_node(dev_path);
828 
829 		snprintf(dev_path, sizeof (dev_path),
830 			SEG5_DEV_NAME"fru@0,%x:fru", fruprom_addr[instance][1]);
831 			delete_i2c_node(dev_path);
832 	}
833 
834 	snprintf(ps_logical_state, sizeof (ps_logical_state),
835 		"%s_LOGICAL_STATE", id);
836 
837 	strlcpy(info, PSVC_OK, sizeof (info));
838 	status |= psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, info);
839 	status |= psvc_set_attr(hdlp, ps_logical_state,	PSVC_STATE_ATTR, info);
840 
841 	strlcpy(info, PSVC_NO_FAULT, sizeof (info));
842 	status |= psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, info);
843 
844 	/* Enable the i2c connection to the power supply */
845 	status |= pdb_enable_i2c(hdlp);
846 	return (status);
847 }
848 
849 /*
850  * check_ps_state() Checks for:
851  *
852  * - Failure bits:
853  *	Power Supply Fan Failure
854  *	Power Supply Temperature Failure
855  *	Power Supply Generic Fault
856  *	Power Supply AC Cord Plugged In
857  *
858  * - If we see a "bad" state we will report an error.
859  *
860  * - "Bad" states:
861  *	Fault bit shows fault.
862  *	Temperature fault shows fault.
863  *	Fan fault shows fault.
864  *	AC power NOT okay to supply.
865  *
866  * - If we see that the AC Cord is not plugged in, then the the other
867  *   failure bits are invalid.
868  *
869  * - Send pcf8574_reset at the end of the policy if we see
870  *   any "bad" states.
871  */
872 static int32_t
873 check_ps_state(psvc_opaque_t hdlp, char *id)
874 {
875 	int32_t		sensor_count;
876 	int32_t		status = PSVC_SUCCESS;
877 	int32_t		i;
878 	int32_t		fault_on = 0;
879 	char		*sensor_id;
880 	char		ps_ok_sensor[PICL_PROPNAMELEN_MAX];
881 	char		ps_logical_state[PICL_PROPNAMELEN_MAX];
882 	char		ps_reset[PICL_PROPNAMELEN_MAX];
883 	char		previous_state[PSVC_MAX_STR_LEN];
884 	char		state[PSVC_MAX_STR_LEN];
885 	char		fault[PSVC_MAX_STR_LEN];
886 	int		ps_okay = 1;	/* Keep track of the PDB PS OK Bit */
887 	int		instance;
888 	int		retry;
889 
890 	/* Logical state id */
891 	snprintf(ps_logical_state, sizeof (ps_logical_state),
892 		"%s_LOGICAL_STATE", id);
893 
894 	/*
895 	 * ac_power_check updates the Power Supply state with "NO AC POWER" if
896 	 * the power cord is out OR PSVC_OK if the power cord is in.
897 	 */
898 	status = ac_power_check(hdlp, id, ps_logical_state);
899 	if (status == PSVC_FAILURE)
900 		return (status);
901 
902 	/*
903 	 * After running ac_power_check we now need to get the current state
904 	 * of the PS.  If the power supply state is "NO AC POWER" then we do
905 	 * not need to check for failures and we return.
906 	 */
907 	status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state);
908 	if (status != PSVC_SUCCESS)
909 		return (status);
910 
911 	if (strcmp(state, "NO AC POWER") == 0)
912 		return (status);
913 
914 	status = psvc_get_attr(hdlp, id, PSVC_PREV_STATE_ATTR, previous_state);
915 	if (status != PSVC_SUCCESS)
916 		return (status);
917 
918 	snprintf(ps_ok_sensor, sizeof (ps_ok_sensor), "%s_OK_SENSOR", id);
919 	retry = 0;
920 	do {
921 		if (retry)
922 			(void) sleep(retry_sleep_ps_status);
923 		/* Handle the PDB P/S OK Bit */
924 		status = psvc_get_attr(hdlp, ps_ok_sensor,
925 		    PSVC_SWITCH_STATE_ATTR, state);
926 		if (status != PSVC_SUCCESS)
927 			return (status);
928 		retry++;
929 	} while ((retry < n_retry_ps_status) &&
930 	    (strcmp(previous_state, state)));
931 
932 
933 	/*
934 	 * If there is a change of state (current state differs from
935 	 * previous state, then assign the error values.
936 	 */
937 	if (strcmp(previous_state, state) != 0) {
938 		if (strcmp(state, PSVC_SWITCH_OFF) == 0) {
939 			strlcpy(state, PSVC_ERROR, sizeof (state));
940 			strlcpy(fault, "DEVICE_FAIL", sizeof (fault));
941 			fault_on = 1;
942 			syslog(LOG_ERR,	gettext(
943 				"Device %s: Failure Detected -- %s "
944 				"shutdown!"), id, id);
945 			ps_okay = 0;
946 		} else {
947 			strlcpy(state, PSVC_OK, sizeof (state));
948 			strlcpy(fault, PSVC_NO_FAULT, sizeof (fault));
949 		}
950 
951 		status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, state);
952 		if (status != PSVC_SUCCESS)
953 			return (status);
954 
955 		status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, fault);
956 		if (status != PSVC_SUCCESS)
957 			return (status);
958 	}
959 
960 	status = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance);
961 	if (status != PSVC_SUCCESS)
962 		return (status);
963 
964 	status = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &sensor_count,
965 		PSVC_DEV_FAULT_SENSOR);
966 	if (status != PSVC_SUCCESS) {
967 		return (status);
968 	}
969 
970 	/* Handle the power supply fail bits. */
971 	for (i = 0; i < sensor_count; i++) {
972 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
973 			&sensor_id, PSVC_DEV_FAULT_SENSOR, i);
974 		if (status != PSVC_SUCCESS)
975 			return (status);
976 
977 		retry = 0;
978 		do {
979 			if (retry)
980 				(void) sleep(retry_sleep_ps_status);
981 			status = psvc_get_attr(hdlp, sensor_id,
982 			    PSVC_SWITCH_STATE_ATTR, state);
983 			if (status != PSVC_SUCCESS)
984 				return (status);
985 			retry++;
986 		} while ((retry < n_retry_ps_status) &&
987 		    (strcmp(state, PSVC_SWITCH_ON) == 0));
988 
989 		if (strcmp(state, PSVC_SWITCH_ON) == 0) {
990 			if (ps_prev_id[instance][i] == NULL)
991 				ps_prev_id[instance][i] = sensor_id;
992 
993 			if (ps_prev_failed[instance][i] != 1)
994 				ps_prev_failed[instance][i] = 1;
995 			fault_on = 1;
996 			/*
997 			 * The first sensor in the list is:
998 			 * PSx_DEV_FAULT_SENSOR.  If this is on, we do not
999 			 * want to merely report that it's on, but rather
1000 			 * report that there was a fault detected, thus
1001 			 * improving diagnosability.
1002 			 */
1003 			if (i == 0) {
1004 				/*
1005 				 * Don't notify if the PDB PS OKAY Bit is
1006 				 * "0"
1007 				 */
1008 				if (ps_okay)
1009 					syslog(LOG_ERR, gettext(
1010 						"Device %s: Fault Detected"),
1011 							id);
1012 			} else {
1013 				syslog(LOG_ERR, gettext("Warning %s: %s is ON"),
1014 					id, sensor_id);
1015 			}
1016 		}
1017 	}
1018 
1019 	status = psvc_get_attr(hdlp, ps_logical_state,
1020 		PSVC_STATE_ATTR, state);
1021 	if (status != PSVC_SUCCESS)
1022 		return (status);
1023 
1024 	status = psvc_get_attr(hdlp, ps_logical_state,
1025 		PSVC_PREV_STATE_ATTR, previous_state);
1026 	if (status != PSVC_SUCCESS)
1027 		return (status);
1028 
1029 	/*
1030 	 * If we encountered a fault of any kind (something before
1031 	 * has set 'fault_on' to '1') then we want to send the reset
1032 	 * signal to the power supply's PCF8574 and also set
1033 	 * 'ps_logical_state' to "ERROR" so that the FSP General Fault
1034 	 * LED will light.
1035 	 */
1036 	if (fault_on) {
1037 		if (ps_okay) {
1038 			status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR,
1039 				PSVC_GEN_FAULT);
1040 			if (status != PSVC_SUCCESS)
1041 				return (status);
1042 		}
1043 		status = psvc_set_attr(hdlp, ps_logical_state,
1044 			PSVC_STATE_ATTR, PSVC_ERROR);
1045 		if (status != PSVC_SUCCESS)
1046 			return (status);
1047 		/*
1048 		 * "id" is in the form of "PSx", We need to make it
1049 		 * PSx_RESET.
1050 		 */
1051 		snprintf(ps_reset, sizeof (ps_reset), "%s_RESET", id);
1052 		status = send_pcf8574_reset(hdlp, ps_reset);
1053 		return (status);
1054 	}
1055 
1056 	/*
1057 	 * There was no fault encountered so we want to
1058 	 * set 'ps_logical_state' to "OK"
1059 	 */
1060 	if (strcmp(state, PSVC_OK) != 0) {
1061 		for (i = 0; i < 3; i++) {
1062 			char	*sensor = ps_prev_id[instance][i];
1063 			int	*prev_failed = &ps_prev_failed[instance][i];
1064 			if (sensor == NULL)
1065 				continue;
1066 			if (*prev_failed == 0)
1067 				continue;
1068 			*prev_failed = 0;
1069 			if (i == 0) {
1070 				/*
1071 				 * Don't notifiy if we have a power supply
1072 				 * failure (PDB PS OKAY == 0
1073 				 */
1074 				if (ps_okay)
1075 					syslog(LOG_ERR, gettext(
1076 						"Notice %s: Fault Cleared"),
1077 							id);
1078 			} else {
1079 				syslog(LOG_ERR, gettext("Notice %s: %s is OFF"),
1080 					id, sensor);
1081 			}
1082 		}
1083 
1084 		status = psvc_set_attr(hdlp, ps_logical_state,
1085 			PSVC_STATE_ATTR, PSVC_OK);
1086 		if (status != PSVC_SUCCESS)
1087 			return (status);
1088 		syslog(LOG_ERR, gettext("Device %s Okay"), id);
1089 	}
1090 
1091 	return (PSVC_SUCCESS);
1092 }
1093 
1094 /*
1095  * This routine takes in a handle pointer and a Power Supply id. It then gets
1096  * the switch state for the PSx_AC_IN_SENSOR. If the switch is OFF the cord is
1097  * unplugged and we return a true (1). If the switch is ON then the cord is
1098  * plugged in and we return a false (0). If the get_attr call fails we return
1099  * PSVC_FAILURE (-1).
1100  */
1101 static int
1102 ac_unplugged(psvc_opaque_t hdlp, char *id)
1103 {
1104 	int32_t		status = PSVC_SUCCESS;
1105 	char		ac_sensor_id[PICL_PROPNAMELEN_MAX];
1106 	char		ac_switch_state[PSVC_MAX_STR_LEN];
1107 
1108 	snprintf(ac_sensor_id, sizeof (ac_sensor_id), "%s_AC_IN_SENSOR", id);
1109 
1110 	status = psvc_get_attr(hdlp, ac_sensor_id, PSVC_SWITCH_STATE_ATTR,
1111 		ac_switch_state);
1112 	if (status == PSVC_FAILURE) {
1113 		return (status);
1114 	}
1115 
1116 	if (strcmp(ac_switch_state, PSVC_SWITCH_OFF) == 0) {
1117 		return (1);
1118 	} else {
1119 		return (0);
1120 	}
1121 }
1122 
1123 /*
1124  * This routine expects a handle pointer, a Power Supply ID, and a PS logical
1125  * state switch ID.  It check to see if the power cord has been removed from or
1126  * inserted to the power supply. It then updates the PS state accordingly.
1127  */
1128 static int
1129 ac_power_check(psvc_opaque_t hdlp, char *id, char *ps_logical_state)
1130 {
1131 	int32_t		status = PSVC_SUCCESS;
1132 	int32_t		sensor_count;
1133 	char		*sensor_id;
1134 	char		state[PSVC_MAX_STR_LEN];
1135 	int		unplugged, i;
1136 
1137 	status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state);
1138 	if (status != PSVC_SUCCESS)
1139 		return (status);
1140 
1141 	/*
1142 	 * Check for AC Power Cord. ac_unplugged will return true if the PS is
1143 	 * unplugged, a false is the PS is plugged in, and PSVC_FAILURE if the
1144 	 * call to get the state fails.
1145 	 */
1146 	unplugged = ac_unplugged(hdlp, id);
1147 	if (status == PSVC_FAILURE) {
1148 		return (status);
1149 	}
1150 
1151 	/*
1152 	 * If power cord is not in, then we set the fault and error
1153 	 * states to "".
1154 	 * If power cord is in, then we check the devices.
1155 	 */
1156 	status = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &sensor_count,
1157 		PSVC_DEV_FAULT_SENSOR);
1158 	if (status != PSVC_SUCCESS) {
1159 		return (status);
1160 	}
1161 
1162 	if ((unplugged) && (strcmp(state, "NO AC POWER") != 0)) {
1163 		/* set id's state to "NO AC POWER" */
1164 		status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR,
1165 		    "NO AC POWER");
1166 		if (status != PSVC_SUCCESS)
1167 			return (status);
1168 		/*
1169 		 * Set this state so that the FSP Fault LED lights
1170 		 * when there is no AC Power to the power supply.
1171 		 */
1172 		status = psvc_set_attr(hdlp, ps_logical_state, PSVC_STATE_ATTR,
1173 		    PSVC_ERROR);
1174 		if (status != PSVC_SUCCESS)
1175 			return (status);
1176 
1177 		status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR,
1178 		    "NO AC POWER");
1179 		if (status != PSVC_SUCCESS)
1180 			return (status);
1181 
1182 		syslog(LOG_ERR, gettext("Device %s AC UNAVAILABLE"), id);
1183 
1184 		/* Set fault sensor states to "" */
1185 		for (i = 0; i < sensor_count; ++i) {
1186 			status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
1187 				&sensor_id, PSVC_DEV_FAULT_SENSOR, i);
1188 			if (status != PSVC_SUCCESS)
1189 				return (status);
1190 
1191 			status = psvc_set_attr(hdlp, sensor_id,
1192 				PSVC_FAULTID_ATTR, "");
1193 			if (status != PSVC_SUCCESS)
1194 				return (status);
1195 		}
1196 	}
1197 
1198 	/* Power cord is plugged in */
1199 	if ((!unplugged) && (strcmp(state, "NO AC POWER") == 0)) {
1200 		/* Default the state to "OK" */
1201 		status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR,
1202 			PSVC_OK);
1203 		if (status != PSVC_SUCCESS)
1204 			return (status);
1205 		/* Default the PS_LOGICAL_STATE to "OK" */
1206 		status = psvc_set_attr(hdlp, ps_logical_state, PSVC_STATE_ATTR,
1207 			PSVC_OK);
1208 		if (status != PSVC_SUCCESS)
1209 			return (status);
1210 		/* Display message */
1211 		syslog(LOG_ERR, gettext("Device %s AC AVAILABLE"), id);
1212 	}
1213 
1214 	return (status);
1215 }
1216 
1217 int32_t
1218 psvc_init_ps_presence(psvc_opaque_t hdlp, char *id)
1219 {
1220 	int		err;
1221 	int		instance;
1222 	boolean_t	presence;
1223 
1224 	err = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance);
1225 	err |= psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &presence);
1226 	ps_prev_present[instance] = ps_present[instance] = presence;
1227 	return (err);
1228 }
1229 
1230 int32_t
1231 psvc_ps_monitor_policy_0(psvc_opaque_t hdlp, char *id)
1232 {
1233 	int		err;
1234 	int		instance;
1235 	static	int	failed_last_time[2] = {0, 0};
1236 	int	retry;
1237 
1238 	err = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance);
1239 	if (err != PSVC_SUCCESS)
1240 		return (err);
1241 
1242 	/* copy current presence to previous presence */
1243 	ps_prev_present[instance] = ps_present[instance];
1244 
1245 	retry = 0;
1246 	do {
1247 		if (retry)
1248 			(void) sleep(retry_sleep_pshp);
1249 		/* Get new presence */
1250 		err = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR,
1251 		    &ps_present[instance]);
1252 		if (err != PSVC_SUCCESS)
1253 			goto out;
1254 		retry++;
1255 	} while ((retry < n_retry_pshp) &&
1256 	    (ps_present[instance] != ps_prev_present[instance]));
1257 
1258 	/* Sustained Hotplug detected */
1259 	if (ps_present[instance] != ps_prev_present[instance]) {
1260 		err = handle_ps_hotplug(hdlp, id, ps_present[instance]);
1261 		return (err);
1262 	}
1263 
1264 	/* If our power supply is not present, we're done */
1265 	if (!ps_present[instance])
1266 		return (PSVC_SUCCESS);
1267 
1268 	err = check_i2c_access(hdlp, id);
1269 	if (err != PSVC_SUCCESS) {
1270 		/* Quickie hotplug */
1271 		if (ps_present[instance] == PSVC_PRESENT &&
1272 		    ps_prev_present[instance] == PSVC_PRESENT) {
1273 			syslog(LOG_ERR, "Device %s removed", id);
1274 			/* Reset prev_failed information */
1275 			ps_reset_prev_failed(instance);
1276 			ps_prev_present[instance] = 0;
1277 			handle_ps_hotplug(hdlp, id, ps_present[instance]);
1278 			/* We ignore the error on a quickie hotplug */
1279 			return (PSVC_SUCCESS);
1280 		}
1281 		/* There was an actual i2c access error */
1282 		goto out;
1283 	}
1284 
1285 	err = check_ps_state(hdlp, id);
1286 	if (err != PSVC_SUCCESS)
1287 		goto out;
1288 
1289 	failed_last_time[instance] = 0;
1290 	return (err);
1291 
1292 out:
1293 	if (! failed_last_time[instance]) {
1294 		/*
1295 		 * We ignore the error condition the first time thru
1296 		 * because the PS could have been removed after (or
1297 		 * during) our call to check_ps_hotplug().
1298 		 *
1299 		 * If the problem is still there the next time, then
1300 		 * we'll raise a flag.
1301 		 *
1302 		 * The instance determines which power supply the policy
1303 		 * errored on.  For instance PS0 might have failed and then
1304 		 * PS1 might have failed, but we'll display a warning
1305 		 * even though there might not be anything actually wrong.
1306 		 * The instance keeps track of which failure occurred so
1307 		 * we warn on the corresponding occurrence of errors.
1308 		 */
1309 		failed_last_time[instance] = 1;
1310 		return (PSVC_SUCCESS);
1311 	}
1312 	return (err);
1313 }
1314 
1315 static int
1316 light_disk_fault_leds(psvc_opaque_t hdlp, char *id, boolean_t disk_presence)
1317 {
1318 	int		err;
1319 	int		bit_nums[MAX_DISKS] = {6, 7};
1320 	uint8_t		led_masks[MAX_DISKS] = {0x40, 0x80};
1321 	int		instance;
1322 	int		bit_value;
1323 	char		state[PSVC_MAX_STR_LEN];
1324 	uint8_t		byte;
1325 
1326 	if (disk_presence != PSVC_PRESENT)
1327 		return (PSVC_SUCCESS);
1328 
1329 	err = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance);
1330 	if (err != PSVC_SUCCESS)
1331 		return (err);
1332 
1333 	err = psvc_get_attr(hdlp, "DISK_PORT", PSVC_GPIO_VALUE_ATTR,
1334 		&byte);
1335 	if (err != PSVC_SUCCESS)
1336 		return (err);
1337 
1338 	err = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state);
1339 	if (err != PSVC_SUCCESS)
1340 		return (err);
1341 	if (strcmp(state, PSVC_OK) == 0 || strcmp(state, "") == 0) { /* OK */
1342 		if (byte & led_masks[instance]) { /* Led is OFF */
1343 			return (err); /* Done. */
1344 		} else { /* Led is ON, Turn if OFF */
1345 			bit_value = 1;	/* Active Low */
1346 			err = pcf8574_write_bit(hdlp, "DISK_PORT",
1347 				bit_nums[instance], bit_value,
1348 				DISKBP_MUST_BE_1);
1349 			if (err != PSVC_SUCCESS)
1350 				return (err);
1351 		}
1352 	} else { /* Disk is NOT OK */
1353 		if (byte & led_masks[instance]) { /* Led is OFF, Turn it ON */
1354 			bit_value = 0;	/* Active Low */
1355 			err = pcf8574_write_bit(hdlp, "DISK_PORT",
1356 				bit_nums[instance], bit_value,
1357 				DISKBP_MUST_BE_1);
1358 			if (err != PSVC_SUCCESS)
1359 				return (err);
1360 		} else {
1361 			return (err); /* Done. */
1362 		}
1363 	}
1364 	return (err);
1365 }
1366 
1367 int
1368 verify_disk_wwn(char *wwn)
1369 {
1370 	HBA_PORTATTRIBUTES	hbaPortAttrs, discPortAttrs;
1371 	HBA_HANDLE	handle;
1372 	HBA_STATUS	status;
1373 	HBA_ADAPTERATTRIBUTES	hbaAttrs;
1374 	HBA_UINT32	numberOfAdapters, hbaCount, hbaPort, discPort;
1375 	char	adaptername[256];
1376 	char	vwwn[WWN_SIZE * 2];
1377 	char	OSDeviceName[PATH_MAX + 1];
1378 	int	count, linksize;
1379 
1380 	/* Load common lib */
1381 	status = HBA_LoadLibrary();
1382 	if (status != HBA_STATUS_OK) {
1383 		(void) HBA_FreeLibrary();
1384 		return (HBA_STATUS_ERROR);
1385 	}
1386 
1387 	/*
1388 	 * Since devfs can store multiple instances
1389 	 * of a target the validity of the WWN of a disk is
1390 	 * verified with an actual probe of internal disks
1391 	 */
1392 
1393 	/* Cycle through FC-AL Adapters and search for WWN */
1394 	numberOfAdapters = HBA_GetNumberOfAdapters();
1395 	for (hbaCount = 0; hbaCount < numberOfAdapters; hbaCount++) {
1396 		if ((status = HBA_GetAdapterName(hbaCount, adaptername)) !=
1397 		    HBA_STATUS_OK)
1398 			continue;
1399 
1400 		handle = HBA_OpenAdapter(adaptername);
1401 		if (handle == 0)
1402 			continue;
1403 
1404 		/* Get Adapter Attributes */
1405 		if ((status = HBA_GetAdapterAttributes(handle,
1406 		    &hbaAttrs)) != HBA_STATUS_OK) {
1407 			HBA_CloseAdapter(handle);
1408 			continue;
1409 		}
1410 
1411 		/* Get Adapter's Port Attributes */
1412 		for (hbaPort = 0;
1413 		    hbaPort < hbaAttrs.NumberOfPorts; hbaPort++) {
1414 			if ((status = HBA_GetAdapterPortAttributes(handle,
1415 			    hbaPort, &hbaPortAttrs)) != HBA_STATUS_OK)
1416 				continue;
1417 
1418 			/*
1419 			 * Verify whether this is onboard controller.
1420 			 * HBAAPI provides path of symbol link to
1421 			 * to the qlc node therefore readlink() is
1422 			 * needed to obtain hard link
1423 			 */
1424 			linksize = readlink(hbaPortAttrs.OSDeviceName,
1425 			    OSDeviceName, PATH_MAX);
1426 
1427 			/*
1428 			 * If readlink does not return size of onboard
1429 			 * controller than don't bother checking device
1430 			 */
1431 			if ((linksize + 1) != sizeof (ONBOARD_CONTR))
1432 				continue;
1433 
1434 			OSDeviceName[linksize] = '\0';
1435 			if (strcmp(OSDeviceName, ONBOARD_CONTR) != 0)
1436 				continue;
1437 
1438 			/* Get Discovered Port Attributes */
1439 			for (discPort = 0;
1440 			    discPort < hbaPortAttrs.NumberofDiscoveredPorts;
1441 			    discPort++) {
1442 				status = HBA_GetDiscoveredPortAttributes(
1443 					handle, hbaPort, discPort,
1444 						&discPortAttrs);
1445 				if (status != HBA_STATUS_OK)
1446 					continue;
1447 
1448 				/* Get target info */
1449 				for (count = 0; count < WWN_SIZE; count++)
1450 					(void) sprintf(&vwwn[count * 2],
1451 					    "%2.2x",
1452 					    discPortAttrs.NodeWWN.wwn[count]);
1453 
1454 				if (strcmp(wwn, vwwn) == 0) {
1455 					HBA_CloseAdapter(handle);
1456 					(void) HBA_FreeLibrary();
1457 					return (HBA_STATUS_OK);
1458 				}
1459 
1460 			}
1461 		}
1462 		HBA_CloseAdapter(handle);
1463 	}
1464 	(void) HBA_FreeLibrary();
1465 	return (HBA_STATUS_ERROR_ILLEGAL_WWN);
1466 }
1467 
1468 static int
1469 light_disk_ok2remove_leds(psvc_opaque_t hdlp, boolean_t *disk_present)
1470 {
1471 	di_node_t	node;
1472 	di_node_t	root_node;
1473 	di_minor_t	min_node;
1474 	int		*prop;
1475 	int		n;
1476 	int		target;
1477 	int		rv;
1478 	int		disk_online = 0;
1479 	static int	prev_online[MAX_DISKS] = {-1, -1};
1480 	int		bit_nums[MAX_DISKS] = {4, 5};
1481 	int		bit_val;
1482 	int		count;
1483 	char		*dev_path;
1484 	char		wwn[WWN_SIZE * 2];
1485 	uchar_t		*prop_wwn;
1486 
1487 	root_node = di_init("/", DINFOCPYALL);
1488 	if (root_node == DI_NODE_NIL)
1489 		return (PSVC_FAILURE);
1490 
1491 	for (node = di_drv_first_node(DISK_DRV, root_node);
1492 		node != DI_NODE_NIL;
1493 		node = di_drv_next_node(node)) {
1494 		n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "target", &prop);
1495 		if (n == -1)
1496 			continue;
1497 		target = *prop;
1498 		if (target < 0 || target > 1)
1499 			continue;
1500 
1501 		if (! disk_present[target])
1502 			continue;
1503 
1504 		dev_path = di_devfs_path(node);
1505 		if (memcmp(dev_path, QLC_NODE, (sizeof (QLC_NODE) - 1)) != 0) {
1506 			/*
1507 			 * This isn't our FC-AL controller, so this
1508 			 * must be an external disk on Loop B.  Skip it.
1509 			 */
1510 			di_devfs_path_free(dev_path);
1511 			continue;
1512 		}
1513 		di_devfs_path_free(dev_path);
1514 
1515 		/*
1516 		 * Verify if disk is valid by checking WWN
1517 		 * because devfs retains stale data.
1518 		 */
1519 		n = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
1520 		    "node-wwn", &prop_wwn);
1521 		if (n == -1)
1522 			continue;
1523 
1524 		for (count = 0; count < WWN_SIZE; count++)
1525 			(void) sprintf(&wwn[count * 2], "%2.2x",
1526 			    prop_wwn[count]);
1527 
1528 		n = verify_disk_wwn(wwn);
1529 		if (n == HBA_STATUS_ERROR_ILLEGAL_WWN)
1530 			continue;
1531 
1532 		min_node = di_minor_next(node, DI_MINOR_NIL);
1533 		disk_online = (min_node != DI_MINOR_NIL);
1534 		if ((disk_online == 0) && (prev_online[target] == 1)) {
1535 			/* Light Led */
1536 			bit_val = 0;
1537 			rv = pcf8574_write_bit(hdlp, "DISK_PORT",
1538 				bit_nums[target], bit_val, DISKBP_MUST_BE_1);
1539 			if (rv != PSVC_SUCCESS)
1540 				goto done;
1541 		} else if ((prev_online[target] == 0) && (disk_online == 1)) {
1542 			/* Unlight Led */
1543 			bit_val = 1;
1544 			rv = pcf8574_write_bit(hdlp, "DISK_PORT",
1545 				bit_nums[target], bit_val, DISKBP_MUST_BE_1);
1546 			if (rv != PSVC_SUCCESS)
1547 				goto done;
1548 		}
1549 		if (disk_online != prev_online[target])
1550 			prev_online[target] = disk_online;
1551 	}
1552 done:
1553 	di_fini(root_node);
1554 	return (rv);
1555 }
1556 
1557 static int
1558 check_disk_fault(psvc_opaque_t hdlp, char *id, boolean_t disk_presence)
1559 {
1560 	int32_t		status = PSVC_SUCCESS;
1561 	int32_t		fault_on = 0;
1562 	char		*sensor_id;
1563 	char		disk_state[PSVC_MAX_STR_LEN];
1564 	char		state[PSVC_MAX_STR_LEN];
1565 	char		fault[PSVC_MAX_STR_LEN];
1566 	boolean_t	change_of_state = 0;
1567 
1568 	if (disk_presence != PSVC_PRESENT)
1569 		return (PSVC_SUCCESS);
1570 
1571 	status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, disk_state);
1572 	if (status != PSVC_SUCCESS)
1573 		return (status);
1574 
1575 	status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
1576 		&sensor_id, PSVC_DEV_FAULT_SENSOR, 0);
1577 	if (status != PSVC_SUCCESS)
1578 		return (status);
1579 
1580 	status = psvc_get_attr(hdlp, sensor_id, PSVC_SWITCH_STATE_ATTR, state);
1581 	if (status != PSVC_SUCCESS)
1582 		return (status);
1583 
1584 	/* Fault detected */
1585 	if (strcmp(state, PSVC_SWITCH_ON) == 0) {
1586 		strlcpy(state, PSVC_ERROR, sizeof (state));
1587 		strlcpy(fault, PSVC_GEN_FAULT, sizeof (fault));
1588 		fault_on = 1;
1589 	} else { /* No fault detected */
1590 		if (strcmp(disk_state, PSVC_OK) != 0)
1591 			change_of_state = 1;
1592 		strlcpy(state, PSVC_OK, sizeof (state));
1593 		strlcpy(fault, PSVC_NO_FAULT, sizeof (fault));
1594 	}
1595 
1596 	status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, state);
1597 	if (status != PSVC_SUCCESS)
1598 		return (status);
1599 
1600 	status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, fault);
1601 	if (status != PSVC_SUCCESS)
1602 		return (status);
1603 
1604 	if (fault_on) {
1605 		syslog(LOG_ERR, gettext("Fault detected: %s"), id);
1606 
1607 	} else {
1608 		if (change_of_state)
1609 			syslog(LOG_ERR, gettext("Notice: %s okay"), id);
1610 	}
1611 	return (PSVC_SUCCESS);
1612 }
1613 
1614 static int
1615 check_disk_hotplug(psvc_opaque_t hdlp, char *id, boolean_t *disk_presence,
1616 	int disk_instance)
1617 {
1618 	boolean_t	presence;
1619 	boolean_t	previous_presence;
1620 	int32_t		status = PSVC_SUCCESS;
1621 	char		label[PSVC_MAX_STR_LEN];
1622 	uint8_t		disk_leds[MAX_DISKS][2] = {{4, 6}, {5, 7}};
1623 	int	retry;
1624 
1625 	status = psvc_get_attr(hdlp, id, PSVC_PREV_PRESENCE_ATTR,
1626 		&previous_presence);
1627 	if (status != PSVC_SUCCESS)
1628 		return (status);
1629 
1630 	retry = 0;
1631 	do {
1632 		if (retry)
1633 			(void) sleep(retry_sleep_diskhp);
1634 		status = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR,
1635 		    &presence);
1636 		if (status != PSVC_SUCCESS)
1637 			return (status);
1638 		retry++;
1639 	} while ((retry < n_retry_diskhp) &&
1640 	    (presence != previous_presence));
1641 
1642 	*disk_presence = presence;
1643 
1644 	if (presence != previous_presence) {
1645 		char		parent_path[PICL_PROPNAMELEN_MAX];
1646 		picl_nodehdl_t	child_node;
1647 
1648 		status = psvc_get_attr(hdlp, id, PSVC_LABEL_ATTR, label);
1649 		if (status != PSVC_SUCCESS)
1650 			return (status);
1651 
1652 		/* return parent path and node for an object */
1653 		psvcplugin_lookup(id, parent_path, &child_node);
1654 
1655 		if (presence == PSVC_PRESENT) {
1656 			picl_nodehdl_t	parent_node;
1657 			char		state[PSVC_MAX_STR_LEN];
1658 			char		fault[PSVC_MAX_STR_LEN];
1659 
1660 			syslog(LOG_ERR, gettext("Device %s inserted"), label);
1661 			strlcpy(state, PSVC_OK, sizeof (state));
1662 			status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR,
1663 				state);
1664 			if (status != PSVC_SUCCESS)
1665 				return (status);
1666 
1667 			strlcpy(fault, PSVC_NO_FAULT, sizeof (fault));
1668 			status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR,
1669 				fault);
1670 			if (status != PSVC_SUCCESS) {
1671 				return (status);
1672 			}
1673 
1674 			status = ptree_get_node_by_path(parent_path,
1675 				&parent_node);
1676 			if (status != PICL_SUCCESS)
1677 				return (PSVC_FAILURE);
1678 			status = ptree_add_node(parent_node, child_node);
1679 			if (status != PICL_SUCCESS)
1680 				return (PSVC_FAILURE);
1681 		} else {
1682 			/*
1683 			 * Disk Removed so we need to turn off these LEDs:
1684 			 * DISKx_FLT_LED
1685 			 * DISKx_REMOVE_LED
1686 			 */
1687 			int i;
1688 			int bit_val = 1;  /* Active Low */
1689 			for (i = 0; i < 2; i++) {
1690 				status = pcf8574_write_bit(hdlp, "DISK_PORT",
1691 					disk_leds[disk_instance][i], bit_val,
1692 					DISKBP_MUST_BE_1);
1693 				if (status != PSVC_SUCCESS)
1694 					syslog(LOG_ERR, "Failed in turning off"
1695 						" %d's LEDs", id);
1696 			}
1697 			syslog(LOG_ERR, gettext("Device %s removed"), label);
1698 			ptree_delete_node(child_node);
1699 		}
1700 	}
1701 
1702 	status = psvc_set_attr(hdlp, id, PSVC_PREV_PRESENCE_ATTR, &presence);
1703 	if (status != PSVC_SUCCESS)
1704 		return (status);
1705 
1706 	return (status);
1707 }
1708 
1709 int32_t
1710 psvc_disk_monitor_policy_0(psvc_opaque_t hdlp, char *id)
1711 {
1712 	int		rv, err, i;
1713 	char		*disks[MAX_DISKS] = {"DISK0", "DISK1"};
1714 	int		saved_errno = 0;
1715 	boolean_t	disk_present[MAX_DISKS] = {0, 0};
1716 
1717 	for (i = 0; i < MAX_DISKS; i++) {
1718 		err = check_disk_hotplug(hdlp, disks[i], &disk_present[i], i);
1719 		if (err) saved_errno = errno;
1720 		rv = err;
1721 
1722 		err = check_disk_fault(hdlp, disks[i], disk_present[i]);
1723 		if (err) saved_errno = errno;
1724 		rv |= err;
1725 
1726 		err |= light_disk_fault_leds(hdlp, disks[i], disk_present[i]);
1727 		if (err) saved_errno = errno;
1728 		rv |= err;
1729 	}
1730 
1731 	err = light_disk_ok2remove_leds(hdlp, disk_present);
1732 	if (err) saved_errno = errno;
1733 	rv |= err;
1734 
1735 	errno = saved_errno;
1736 	return (rv);
1737 }
1738 
1739 /*
1740  * Read in temperature thresholds from FRU Prom and update the
1741  * default values.
1742  */
1743 
1744 #define	START_OFFSET		0x1800	/* Last 2K of SEEPROM */
1745 #define	NUM_SEG_OFFSET		0x1805	/* Number of segments */
1746 #define	SEG_TABLE_OFFSET	0x1806	/* Segment description tables */
1747 
1748 static int32_t
1749 read_sc_segment(psvc_opaque_t hdlp, char *id, char *fru_id, int offset)
1750 {
1751 	static int thresh_names[] = {
1752 		PSVC_HW_LO_SHUT_ATTR,
1753 		PSVC_LO_SHUT_ATTR,
1754 		PSVC_LO_WARN_ATTR,
1755 		PSVC_NOT_USED,			/* LOW MODE  */
1756 		PSVC_OPTIMAL_TEMP_ATTR,
1757 		PSVC_HI_WARN_ATTR,
1758 		PSVC_HI_SHUT_ATTR,
1759 		PSVC_HW_HI_SHUT_ATTR
1760 	};
1761 	int8_t		amb_temp_array[8];
1762 	int		i;
1763 	fru_info_t	fru_info;
1764 	int		err;
1765 
1766 	fru_info.buf_start = offset + 8;
1767 	fru_info.buf = amb_temp_array;
1768 	fru_info.read_size = 8;
1769 
1770 	err = psvc_get_attr(hdlp, fru_id, PSVC_FRU_INFO_ATTR, &fru_info);
1771 	if (err != PSVC_SUCCESS)
1772 		return (err);
1773 
1774 	for (i = 0; i < 8; i++) {
1775 		int32_t temp = amb_temp_array[i];
1776 		if (thresh_names[i] == PSVC_NOT_USED)
1777 			continue;
1778 		err = psvc_set_attr(hdlp, id, thresh_names[i], &temp);
1779 		if (err != PSVC_SUCCESS)
1780 			return (err);
1781 	}
1782 	return (PSVC_SUCCESS);
1783 }
1784 
1785 int32_t
1786 update_disk_bp_temp_thresholds(psvc_opaque_t hdlp, char *id)
1787 {
1788 
1789 	char		*fru;
1790 	fru_info_t	fru_info;
1791 	int16_t		seg_offset;
1792 	int8_t		byte;
1793 	int8_t		seg_count;
1794 	char		seg_name[2];
1795 	int		current_offset, i, err;
1796 
1797 	err = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &fru, PSVC_FRU, 0);
1798 	if (err != PSVC_SUCCESS)
1799 		return (err);
1800 
1801 	/* Sanity Check */
1802 	fru_info.buf_start = START_OFFSET;
1803 	fru_info.buf = &byte;
1804 	fru_info.read_size = 1;
1805 
1806 	err = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR, &fru_info);
1807 	if (err != PSVC_SUCCESS)
1808 		return (err);
1809 	if (*fru_info.buf != 8) {
1810 		syslog(LOG_ERR, "Notice: FRU Prom %s not programmed", fru);
1811 	}
1812 	/* Should do CRC Check on fru */
1813 
1814 	/* Get Segment Count */
1815 	fru_info.buf_start = NUM_SEG_OFFSET;
1816 	fru_info.buf = &seg_count;
1817 	fru_info.read_size = 1;
1818 
1819 	err = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR, &fru_info);
1820 	if (err != PSVC_SUCCESS)
1821 		return (err);
1822 
1823 	current_offset = SEG_TABLE_OFFSET;
1824 	for (i = 0; i < seg_count; i++) {
1825 		fru_info.buf_start = current_offset;
1826 		fru_info.buf = seg_name;
1827 		fru_info.read_size = 2;
1828 		err = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR, &fru_info);
1829 		if (err != PSVC_SUCCESS)
1830 			return (err);
1831 
1832 		if (memcmp(seg_name, "SC", 2) == 0) {
1833 			current_offset += 6;	/* Skip over description */
1834 			fru_info.buf_start = current_offset;
1835 			fru_info.buf = (char *)&seg_offset;
1836 			fru_info.read_size = 2;
1837 			psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR,
1838 				&fru_info);
1839 			return (read_sc_segment(hdlp, id, fru, seg_offset));
1840 		}
1841 		current_offset += 10;
1842 	}
1843 
1844 	return (PSVC_SUCCESS);
1845 }
1846