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