/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Cherrystone platform specific environment monitoring policies */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Device paths for power supply hotplug handling */ #define SEG5_ADDR 0x30 #define EBUS_DEV_NAME "/devices/pci@9,700000/ebus@1/" #define SEG5_DEV_NAME EBUS_DEV_NAME "i2c@1,30/" #define SEG5_ADDR_DEV_FMT EBUS_DEV_NAME "i2c@1,%x:devctl" #define QLC_NODE "/pci@9,600000/SUNW,qlc@2" #define DISK_DRV "ssd" #define MAX_DISKS 2 #define WWN_SIZE 8 #define ONBOARD_CONTR "../../devices/pci@9,600000/SUNW,qlc@2/fp@0,0:fc" /* Bit masks so we don't "wedge" the inputs */ #define PCF8574_BIT_WRITE_VALUE(byte, bit, value)\ ((value << bit) | (byte & (~(0x01 << bit)))) #define PDB_MUST_BE_1 0xBF #define PSU_MUST_BE_1 0x7F #define DISKBP_MUST_BE_1 0x0F /*LINTLIBRARY*/ #define PSVC_MAX_STR_LEN 32 #define PS_MAX_FAULT_SENSORS 3 /* * Keep track of the power supply's fail status for reporting if/when * they go good. * ID's: * O PSx_FAULT_SENSOR * 1 Doesn't matter -- only need 0 to be PSx_FAULT_SENSOR * 2 Doesn't matter */ static char *ps_prev_id[2][3] = {{NULL, NULL, NULL}, {NULL, NULL, NULL}}; static int ps_prev_failed[2][3] = {{0, 0, 0}, {0, 0, 0}}; /* * Keep track of the power supply's previous presence * because PSVC doesn't do that for us. */ static boolean_t ps_prev_present[2]; static boolean_t ps_present[2]; /* Local Routines for the environmental policies */ static int ac_unplugged(psvc_opaque_t, char *); static int ac_power_check(psvc_opaque_t, char *, char *); /* * Create an I2C device node. */ static int create_i2c_node(char *nd_name, char *nd_compat, int nd_nexi, int *nd_reg) { devctl_ddef_t ddef_hdl = NULL; devctl_hdl_t bus_hdl = NULL; devctl_hdl_t dev_hdl = NULL; char buf[MAXPATHLEN]; char dev_path[MAXPATHLEN]; int rv = PSVC_FAILURE; (void) snprintf(buf, sizeof (buf), SEG5_ADDR_DEV_FMT, nd_nexi); bus_hdl = devctl_bus_acquire(buf, 0); if (bus_hdl == NULL) goto bad; /* device definition properties */ ddef_hdl = devctl_ddef_alloc(nd_name, 0); (void) devctl_ddef_string(ddef_hdl, "compatible", nd_compat); (void) devctl_ddef_int_array(ddef_hdl, "reg", 2, nd_reg); /* create the device node */ if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl)) goto bad; if (devctl_get_pathname(dev_hdl, dev_path, MAXPATHLEN) == NULL) goto bad; #ifdef DEBUG syslog(LOG_ERR, "PSVC: create_i2c_node: Device node created: (%s)", dev_path); #endif rv = PSVC_SUCCESS; bad: if (dev_hdl) devctl_release(dev_hdl); if (ddef_hdl) devctl_ddef_free(ddef_hdl); if (bus_hdl) devctl_release(bus_hdl); return (rv); } /* * Delete an I2C device node given the device path. */ static void delete_i2c_node(char *nd) { int rv; devctl_hdl_t dev_hdl; dev_hdl = devctl_device_acquire(nd, 0); if (dev_hdl == NULL) { return; } rv = devctl_device_remove(dev_hdl); if (rv != DDI_SUCCESS) perror(nd); #ifdef DEBUG else syslog(LOG_ERR, "Device node deleted: (%s)", nd); #endif devctl_release(dev_hdl); } /* PCF8574 Reset Function */ static int send_pcf8574_reset(psvc_opaque_t hdlp, char *reset_dev) { int err; uint8_t reset_bits[2] = {0x7F, 0xFF}; int i; for (i = 0; i < 2; i++) { err = psvc_set_attr(hdlp, reset_dev, PSVC_GPIO_VALUE_ATTR, &reset_bits[i]); if (err != PSVC_SUCCESS) { #ifdef DEBUG syslog(LOG_ERR, gettext("Reset to %s with 0x%x failed"), reset_dev, reset_bits[i]); #endif return (err); } } /* Need to give u-code a chance to update */ sleep(3); return (err); } static int pcf8574_write_bit(psvc_opaque_t hdlp, char *id, uint8_t bit_num, uint8_t bit_val, uint8_t write_must_be_1) { int rv = PSVC_FAILURE; uint8_t byte; rv = psvc_get_attr(hdlp, id, PSVC_GPIO_VALUE_ATTR, &byte); if (rv != PSVC_SUCCESS) return (rv); byte = PCF8574_BIT_WRITE_VALUE(byte, bit_num, bit_val); byte |= write_must_be_1; rv = psvc_set_attr(hdlp, id, PSVC_GPIO_VALUE_ATTR, &byte); return (rv); } /* * To enable the i2c bus, we must toggle bit 6 on the PDB's * PCF8574 (0x4C) high->low->high */ static int pdb_enable_i2c(psvc_opaque_t hdlp) { int rv = PSVC_SUCCESS, i; int bit_vals[3] = {1, 0, 1}; int bit_num = 6; for (i = 0; i < 3; i++) { rv = pcf8574_write_bit(hdlp, "PDB_PORT", bit_num, bit_vals[i], PDB_MUST_BE_1); if (rv != PSVC_SUCCESS) { goto bad; } } return (rv); bad: #ifdef DEBUG syslog(LOG_ERR, gettext("PDB I2C Bus Enabling Failed")); #endif return (rv); } int32_t psvc_init_disk_bp_policy_0(psvc_opaque_t hdlp, char *id) { uint8_t reset = 0xFF; return (psvc_set_attr(hdlp, id, PSVC_GPIO_VALUE_ATTR, &reset)); } int32_t pcf8574_init_policy_0(psvc_opaque_t hdlp, char *id) { return (send_pcf8574_reset(hdlp, id)); } static int32_t check_fan(psvc_opaque_t hdlp, char *tray_id, char *fan_id, boolean_t *fault_on) { int status; int speed; int low_thresh; boolean_t have_fault = 0; char *tach_id; char state[PSVC_MAX_STR_LEN]; char prev_state[PSVC_MAX_STR_LEN]; char fault_state[PSVC_MAX_STR_LEN]; /* Get this fan object's corresponding fan tach */ status = psvc_get_attr(hdlp, fan_id, PSVC_ASSOC_ID_ATTR, &tach_id, PSVC_FAN_SPEED_TACHOMETER, 0); if (status != PSVC_SUCCESS) return (status); /* Get the low fan speed threshold */ status = psvc_get_attr(hdlp, tach_id, PSVC_LO_WARN_ATTR, &low_thresh); if (status != PSVC_SUCCESS) return (status); /* Get the fan speed */ status = psvc_get_attr(hdlp, tach_id, PSVC_SENSOR_VALUE_ATTR, &speed); if (status != PSVC_SUCCESS) return (status); if (speed <= low_thresh) { /* We see a fault */ strlcpy(fault_state, "DEVICE_FAIL", sizeof (fault_state)); strlcpy(state, PSVC_ERROR, sizeof (state)); have_fault = 1; } else { /* Fault gone? */ strlcpy(fault_state, PSVC_NO_FAULT, sizeof (fault_state)); strlcpy(state, PSVC_OK, sizeof (state)); /* have_fault is already 0 */ } /* Assign new states to the fan object */ status = psvc_set_attr(hdlp, fan_id, PSVC_FAULTID_ATTR, fault_state); if (status != PSVC_SUCCESS) return (status); status = psvc_set_attr(hdlp, fan_id, PSVC_STATE_ATTR, state); if (status != PSVC_SUCCESS) return (status); /* Get state and previous state */ status = psvc_get_attr(hdlp, fan_id, PSVC_STATE_ATTR, state); if (status != PSVC_SUCCESS) return (status); status = psvc_get_attr(hdlp, fan_id, PSVC_PREV_STATE_ATTR, prev_state); if (status != PSVC_SUCCESS) return (status); /* Display notices */ if (strcmp(state, PSVC_OK) != 0) { syslog(LOG_ERR, gettext("WARNING: %s (%s) failure detected"), tray_id, fan_id); } else { if (strcmp(state, prev_state) != 0) { syslog(LOG_ERR, gettext("NOTICE: Device %s (%s) OK"), tray_id, fan_id); } } *fault_on |= have_fault; return (PSVC_SUCCESS); } /* * This policy acts on fan trays. It looks at each of its fans * and checks the speeds. If the fan speed is less than the threshold, * then indicate: console, log, LED. */ int32_t psvc_fan_fault_check_policy_0(psvc_opaque_t hdlp, char *id) { int fan_count; int led_count; int err, i; char *led_id; char *fan_id; char led_state[PSVC_MAX_STR_LEN]; char state[PSVC_MAX_STR_LEN]; char prev_state[PSVC_MAX_STR_LEN]; boolean_t fault_on = 0; /* Get the number of fans associated with this fan tray. */ err = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &fan_count, PSVC_FAN_TRAY_FANS); if (err != PSVC_SUCCESS) return (err); for (i = 0; i < fan_count; i++) { err = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &fan_id, PSVC_FAN_TRAY_FANS, i); if (err != PSVC_SUCCESS) return (err); err = check_fan(hdlp, id, fan_id, &fault_on); if (err != PSVC_SUCCESS) return (err); } if (fault_on) { strlcpy(led_state, PSVC_LED_ON, sizeof (led_state)); err = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, PSVC_ERROR); if (err != PSVC_SUCCESS) return (err); } else { strlcpy(led_state, PSVC_LED_OFF, sizeof (led_state)); err = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, PSVC_OK); if (err != PSVC_SUCCESS) return (err); } err = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state); if (err != PSVC_SUCCESS) return (err); err = psvc_get_attr(hdlp, id, PSVC_PREV_STATE_ATTR, prev_state); if (err != PSVC_SUCCESS) return (err); /* * Set leds according to the fan tray's states. * (we only do this if there is a change of state in order * to reduce i2c traffic) */ if (strcmp(state, prev_state) != 0) { err = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &led_count, PSVC_DEV_FAULT_LED); if (err != PSVC_SUCCESS) return (err); for (i = 0; i < led_count; i++) { err = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &led_id, PSVC_DEV_FAULT_LED, i); if (err != PSVC_SUCCESS) return (err); err = psvc_set_attr(hdlp, led_id, PSVC_LED_STATE_ATTR, led_state); if (err != PSVC_SUCCESS) return (err); err = psvc_get_attr(hdlp, led_id, PSVC_LED_STATE_ATTR, led_state); if (err != PSVC_SUCCESS) return (err); } } return (err); } static int32_t check_cpu_temp_fault(psvc_opaque_t hdlp, char *cpu, int32_t cpu_count) { char *sensorid; int32_t sensor_count; int32_t status = PSVC_SUCCESS; int32_t i; char fault[PSVC_MAX_STR_LEN]; psvc_get_attr(hdlp, cpu, PSVC_ASSOC_MATCHES_ATTR, &sensor_count, PSVC_DEV_TEMP_SENSOR); for (i = 0; i < sensor_count; ++i) { status = psvc_get_attr(hdlp, cpu, PSVC_ASSOC_ID_ATTR, &sensorid, PSVC_DEV_TEMP_SENSOR, i); if (status == PSVC_FAILURE) return (status); status = psvc_get_attr(hdlp, sensorid, PSVC_FAULTID_ATTR, fault); if (status == PSVC_FAILURE) return (status); if ((strcmp(fault, PSVC_TEMP_HI_SHUT) == 0) || (strcmp(fault, PSVC_TEMP_LO_SHUT) == 0)) { system("shutdown -y -g 60 -i 5 \"OVERTEMP condition\""); } } return (status); } int32_t psvc_shutdown_policy_0(psvc_opaque_t hdlp, char *id) { int32_t cpu_count; char *cpuid; int32_t i; boolean_t present; int32_t status = PSVC_SUCCESS; psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &cpu_count, PSVC_CPU); for (i = 0; i < cpu_count; ++i) { status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &cpuid, PSVC_CPU, i); if (status == PSVC_FAILURE) return (status); status = psvc_get_attr(hdlp, cpuid, PSVC_PRESENCE_ATTR, &present); if (status == PSVC_FAILURE && present == PSVC_PRESENT) return (status); if (present == PSVC_PRESENT) { status = check_cpu_temp_fault(hdlp, cpuid, cpu_count); if (status == PSVC_FAILURE && errno != ENODEV) return (status); } } return (PSVC_SUCCESS); } /* * Checks device specified by the PSVC_DEV_FAULT_SENSOR association * for errors, and if there is, then report and turn on the FSP Fault * Led. */ int32_t psvc_fsp_device_fault_check_policy_0(psvc_opaque_t hdlp, char *id) { int32_t status; int32_t i; int32_t device_count = 0; char device_state[PSVC_MAX_STR_LEN]; char *device_id; int32_t failed_count = 0; static int32_t led_on = 0; status = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &device_count, PSVC_DEV_FAULT_SENSOR); if (status != PSVC_SUCCESS) return (status); for (i = 0; i < device_count; i++) { status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &device_id, PSVC_DEV_FAULT_SENSOR, i); if (status != PSVC_SUCCESS) return (status); status = psvc_get_attr(hdlp, device_id, PSVC_STATE_ATTR, device_state); if (status != PSVC_SUCCESS) return (status); if (strcmp(device_state, PSVC_OK) != 0 && strcmp(device_state, PSVC_HOTPLUGGED) != 0 && strcmp(device_state, "NO AC POWER") != 0 && strlen(device_state) != 0) { failed_count++; } } if (failed_count == 0 && led_on) { syslog(LOG_ERR, gettext("%s has turned OFF"), id); status = psvc_set_attr(hdlp, id, PSVC_LED_STATE_ATTR, PSVC_LED_OFF); led_on = 0; } if (failed_count > 0 && ! led_on) { syslog(LOG_ERR, gettext("%s has turned ON"), id); status = psvc_set_attr(hdlp, id, PSVC_LED_STATE_ATTR, PSVC_LED_ON); led_on = 1; } return (PSVC_SUCCESS); } /* Power Supply Policy Helper and Worker Functions */ static void ps_reset_prev_failed(int index) { int i; /* Reset the power supply's failure information */ for (i = 0; i < 3; i++) { ps_prev_id[index][i] = NULL; ps_prev_failed[index][i] = 0; } } static int check_i2c_access(psvc_opaque_t hdlp, char *id) { int rv; char state[PSVC_MAX_STR_LEN]; char ps_fault_sensor[PSVC_MAX_STR_LEN]; snprintf(ps_fault_sensor, sizeof (ps_fault_sensor), "%s_FAULT_SENSOR", id); rv = psvc_get_attr(hdlp, ps_fault_sensor, PSVC_SWITCH_STATE_ATTR, &state); return (rv); } /* * This routine takes in the PSVC handle pointer, the PS name, and the * instance number (0 or 1). It simply make a psvc_get call to get the * presence of each of the children under the PS. This call will set the * presence state of the child device if it was not there when the system * was booted. */ static int handle_ps_hotplug_children_presence(psvc_opaque_t hdlp, char *id) { char *child_add_on[4] = {"_RESET", "_LOGICAL_STATE", "_AC_IN_SENSOR", "_FAULT_SENSOR"}; int add_ons = 4; char addon_id[PICL_PROPNAMELEN_MAX]; char *sensor_id; int32_t status = PSVC_SUCCESS; boolean_t presence; int j; /* Go through the add on list and set presence */ for (j = 0; j < add_ons; j++) { snprintf(addon_id, sizeof (addon_id), "%s%s", id, child_add_on[j]); status = psvc_get_attr(hdlp, addon_id, PSVC_PRESENCE_ATTR, &presence); if (status != PSVC_SUCCESS) return (status); } /* Go through each PS's fault sensors */ for (j = 0; j < PS_MAX_FAULT_SENSORS; j++) { status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &(sensor_id), PSVC_DEV_FAULT_SENSOR, j); if (status != PSVC_SUCCESS) return (status); status = psvc_get_attr(hdlp, sensor_id, PSVC_PRESENCE_ATTR, &presence); if (status != PSVC_SUCCESS) return (status); } /* Go through each PS's onboard i2c hardware */ for (j = 0; j < 2; j++) { status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &(sensor_id), PSVC_PHYSICAL_DEVICE, j); if (status != PSVC_SUCCESS) return (status); status = psvc_get_attr(hdlp, sensor_id, PSVC_PRESENCE_ATTR, &presence); if (status != PSVC_SUCCESS) return (status); } return (status); } static int handle_ps_hotplug(psvc_opaque_t hdlp, char *id, boolean_t present) { int32_t status = PSVC_SUCCESS; int32_t instance; picl_nodehdl_t parent_node; picl_nodehdl_t child_node; char info[PSVC_MAX_STR_LEN]; char ps_logical_state[PICL_PROPNAMELEN_MAX]; char parent_path[PICL_PROPNAMELEN_MAX]; char ps_path[PICL_PROPNAMELEN_MAX]; static int fruprom_addr[2][2] = { {0, 0xa2}, {0, 0xa0} }; static int pcf8574_addr[2][2] = { {0, 0x72}, {0, 0x70} }; char dev_path[MAXPATHLEN]; /* Convert name to node and parent path */ psvcplugin_lookup(id, parent_path, &child_node); /* * Get the power supply's instance. * Used to index the xxx_addr arrays */ status = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance); if (status != PSVC_SUCCESS) return (status); if (present == PSVC_PRESENT && !ps_prev_present[instance]) { /* Service Power Supply Insertion */ syslog(LOG_ERR, gettext("Device %s inserted"), id); /* PICL Tree Maintenance */ ptree_get_node_by_path(parent_path, &parent_node); ptree_add_node(parent_node, child_node); snprintf(ps_path, sizeof (ps_path), "%s/%s", parent_path, id); psvcplugin_add_children(ps_path); /* * This code to update the presences of power supply * child devices in the event that picld was started * without a power supply present. This call makes * the devices available after that initial insertion. */ status = handle_ps_hotplug_children_presence(hdlp, id); /* * Device Tree Maintenance * Add the devinfo tree node entry for the pcf8574 and seeprom * and attach their drivers. */ status |= create_i2c_node("ioexp", "i2c-pcf8574", SEG5_ADDR, pcf8574_addr[instance]); status |= create_i2c_node("fru", "i2c-at24c64", SEG5_ADDR, fruprom_addr[instance]); } else { /* Service Power Supply Removal */ syslog(LOG_ERR, gettext("Device %s removed"), id); /* Reset the power supply's failure information */ ps_reset_prev_failed(instance); /* PICL Tree Maintenance */ if (ptree_delete_node(child_node) != PICL_SUCCESS) syslog(LOG_ERR, "ptree_delete_node failed!"); /* * The hardcoded subscript in pcf8574_add[instance][1] * refers to the address. We are appending the address to * device path. Both elements are used when creating * the i2c node (above). */ snprintf(dev_path, sizeof (dev_path), SEG5_DEV_NAME"ioexp@0,%x:pcf8574", pcf8574_addr[instance][1]); delete_i2c_node(dev_path); snprintf(dev_path, sizeof (dev_path), SEG5_DEV_NAME"fru@0,%x:fru", fruprom_addr[instance][1]); delete_i2c_node(dev_path); } snprintf(ps_logical_state, sizeof (ps_logical_state), "%s_LOGICAL_STATE", id); strlcpy(info, PSVC_OK, sizeof (info)); status |= psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, info); status |= psvc_set_attr(hdlp, ps_logical_state, PSVC_STATE_ATTR, info); strlcpy(info, PSVC_NO_FAULT, sizeof (info)); status |= psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, info); /* Enable the i2c connection to the power supply */ status |= pdb_enable_i2c(hdlp); return (status); } /* * check_ps_state() Checks for: * * - Failure bits: * Power Supply Fan Failure * Power Supply Temperature Failure * Power Supply Generic Fault * Power Supply AC Cord Plugged In * * - If we see a "bad" state we will report an error. * * - "Bad" states: * Fault bit shows fault. * Temperature fault shows fault. * Fan fault shows fault. * AC power NOT okay to supply. * * - If we see that the AC Cord is not plugged in, then the the other * failure bits are invalid. * * - Send pcf8574_reset at the end of the policy if we see * any "bad" states. */ static int32_t check_ps_state(psvc_opaque_t hdlp, char *id) { int32_t sensor_count; int32_t status = PSVC_SUCCESS; int32_t i; int32_t fault_on = 0; char *sensor_id; char ps_ok_sensor[PICL_PROPNAMELEN_MAX]; char ps_logical_state[PICL_PROPNAMELEN_MAX]; char ps_reset[PICL_PROPNAMELEN_MAX]; char previous_state[PSVC_MAX_STR_LEN]; char state[PSVC_MAX_STR_LEN]; char fault[PSVC_MAX_STR_LEN]; int ps_okay = 1; /* Keep track of the PDB PS OK Bit */ int instance; /* Logical state id */ snprintf(ps_logical_state, sizeof (ps_logical_state), "%s_LOGICAL_STATE", id); /* * ac_power_check updates the Power Supply state with "NO AC POWER" if * the power cord is out OR PSVC_OK if the power cord is in. */ status = ac_power_check(hdlp, id, ps_logical_state); if (status == PSVC_FAILURE) return (status); /* * After running ac_power_check we now need to get the current state * of the PS. If the power supply state is "NO AC POWER" then we do * not need to check for failures and we return. */ status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state); if (status != PSVC_SUCCESS) return (status); if (strcmp(state, "NO AC POWER") == 0) return (status); /* Handle the PDB P/S OK Bit */ snprintf(ps_ok_sensor, sizeof (ps_ok_sensor), "%s_OK_SENSOR", id); status = psvc_get_attr(hdlp, ps_ok_sensor, PSVC_SWITCH_STATE_ATTR, state); if (status != PSVC_SUCCESS) return (status); status = psvc_get_attr(hdlp, id, PSVC_PREV_STATE_ATTR, previous_state); if (status != PSVC_SUCCESS) return (status); /* * If there is a change of state (current state differs from * previous state, then assign the error values. */ if (strcmp(previous_state, state) != 0) { if (strcmp(state, PSVC_SWITCH_OFF) == 0) { strlcpy(state, PSVC_ERROR, sizeof (state)); strlcpy(fault, "DEVICE_FAIL", sizeof (fault)); fault_on = 1; syslog(LOG_ERR, gettext( "Device %s: Failure Detected -- %s " "shutdown!"), id, id); ps_okay = 0; } else { strlcpy(state, PSVC_OK, sizeof (state)); strlcpy(fault, PSVC_NO_FAULT, sizeof (fault)); } status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, state); if (status != PSVC_SUCCESS) return (status); status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, fault); if (status != PSVC_SUCCESS) return (status); } status = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance); if (status != PSVC_SUCCESS) return (status); status = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &sensor_count, PSVC_DEV_FAULT_SENSOR); if (status != PSVC_SUCCESS) { return (status); } /* Handle the power supply fail bits. */ for (i = 0; i < sensor_count; i++) { status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &sensor_id, PSVC_DEV_FAULT_SENSOR, i); if (status != PSVC_SUCCESS) return (status); status = psvc_get_attr(hdlp, sensor_id, PSVC_SWITCH_STATE_ATTR, state); if (status != PSVC_SUCCESS) return (status); if (strcmp(state, PSVC_SWITCH_ON) == 0) { if (ps_prev_id[instance][i] == NULL) ps_prev_id[instance][i] = sensor_id; if (ps_prev_failed[instance][i] != 1) ps_prev_failed[instance][i] = 1; fault_on = 1; /* * The first sensor in the list is: * PSx_DEV_FAULT_SENSOR. If this is on, we do not * want to merely report that it's on, but rather * report that there was a fault detected, thus * improving diagnosability. */ if (i == 0) { /* * Don't notify if the PDB PS OKAY Bit is * "0" */ if (ps_okay) syslog(LOG_ERR, gettext( "Device %s: Fault Detected"), id); } else { syslog(LOG_ERR, gettext("Warning %s: %s is ON"), id, sensor_id); } } } status = psvc_get_attr(hdlp, ps_logical_state, PSVC_STATE_ATTR, state); if (status != PSVC_SUCCESS) return (status); status = psvc_get_attr(hdlp, ps_logical_state, PSVC_PREV_STATE_ATTR, previous_state); if (status != PSVC_SUCCESS) return (status); /* * If we encountered a fault of any kind (something before * has set 'fault_on' to '1') then we want to send the reset * signal to the power supply's PCF8574 and also set * 'ps_logical_state' to "ERROR" so that the FSP General Fault * LED will light. */ if (fault_on) { if (ps_okay) { status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, PSVC_GEN_FAULT); if (status != PSVC_SUCCESS) return (status); } status = psvc_set_attr(hdlp, ps_logical_state, PSVC_STATE_ATTR, PSVC_ERROR); if (status != PSVC_SUCCESS) return (status); /* * "id" is in the form of "PSx", We need to make it * PSx_RESET. */ snprintf(ps_reset, sizeof (ps_reset), "%s_RESET", id); status = send_pcf8574_reset(hdlp, ps_reset); return (status); } /* * There was no fault encountered so we want to * set 'ps_logical_state' to "OK" */ if (strcmp(state, PSVC_OK) != 0) { for (i = 0; i < 3; i++) { char *sensor = ps_prev_id[instance][i]; int *prev_failed = &ps_prev_failed[instance][i]; if (sensor == NULL) continue; if (*prev_failed == 0) continue; *prev_failed = 0; if (i == 0) { /* * Don't notifiy if we have a power supply * failure (PDB PS OKAY == 0 */ if (ps_okay) syslog(LOG_ERR, gettext( "Notice %s: Fault Cleared"), id); } else { syslog(LOG_ERR, gettext("Notice %s: %s is OFF"), id, sensor); } } status = psvc_set_attr(hdlp, ps_logical_state, PSVC_STATE_ATTR, PSVC_OK); if (status != PSVC_SUCCESS) return (status); syslog(LOG_ERR, gettext("Device %s Okay"), id); } return (PSVC_SUCCESS); } /* * This routine takes in a handle pointer and a Power Supply id. It then gets * the switch state for the PSx_AC_IN_SENSOR. If the switch is OFF the cord is * unplugged and we return a true (1). If the switch is ON then the cord is * plugged in and we return a false (0). If the get_attr call fails we return * PSVC_FAILURE (-1). */ static int ac_unplugged(psvc_opaque_t hdlp, char *id) { int32_t status = PSVC_SUCCESS; char ac_sensor_id[PICL_PROPNAMELEN_MAX]; char ac_switch_state[PSVC_MAX_STR_LEN]; snprintf(ac_sensor_id, sizeof (ac_sensor_id), "%s_AC_IN_SENSOR", id); status = psvc_get_attr(hdlp, ac_sensor_id, PSVC_SWITCH_STATE_ATTR, ac_switch_state); if (status == PSVC_FAILURE) { return (status); } if (strcmp(ac_switch_state, PSVC_SWITCH_OFF) == 0) { return (1); } else { return (0); } } /* * This routine expects a handle pointer, a Power Supply ID, and a PS logical * state switch ID. It check to see if the power cord has been removed from or * inserted to the power supply. It then updates the PS state accordingly. */ static int ac_power_check(psvc_opaque_t hdlp, char *id, char *ps_logical_state) { int32_t status = PSVC_SUCCESS; int32_t sensor_count; char *sensor_id; char state[PSVC_MAX_STR_LEN]; int unplugged, i; status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state); if (status != PSVC_SUCCESS) return (status); /* * Check for AC Power Cord. ac_unplugged will return true if the PS is * unplugged, a false is the PS is plugged in, and PSVC_FAILURE if the * call to get the state fails. */ unplugged = ac_unplugged(hdlp, id); if (status == PSVC_FAILURE) { return (status); } /* * If power cord is not in, then we set the fault and error * states to "". * If power cord is in, then we check the devices. */ status = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &sensor_count, PSVC_DEV_FAULT_SENSOR); if (status != PSVC_SUCCESS) { return (status); } if ((unplugged) && (strcmp(state, "NO AC POWER") != 0)) { /* set id's state to "NO AC POWER" */ status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, "NO AC POWER"); if (status != PSVC_SUCCESS) return (status); /* * Set this state so that the FSP Fault LED lights * when there is no AC Power to the power supply. */ status = psvc_set_attr(hdlp, ps_logical_state, PSVC_STATE_ATTR, PSVC_ERROR); if (status != PSVC_SUCCESS) return (status); status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, "NO AC POWER"); if (status != PSVC_SUCCESS) return (status); syslog(LOG_ERR, gettext("Device %s AC UNAVAILABLE"), id); /* Set fault sensor states to "" */ for (i = 0; i < sensor_count; ++i) { status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &sensor_id, PSVC_DEV_FAULT_SENSOR, i); if (status != PSVC_SUCCESS) return (status); status = psvc_set_attr(hdlp, sensor_id, PSVC_FAULTID_ATTR, ""); if (status != PSVC_SUCCESS) return (status); } } /* Power cord is plugged in */ if ((!unplugged) && (strcmp(state, "NO AC POWER") == 0)) { /* Default the state to "OK" */ status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, PSVC_OK); if (status != PSVC_SUCCESS) return (status); /* Default the PS_LOGICAL_STATE to "OK" */ status = psvc_set_attr(hdlp, ps_logical_state, PSVC_STATE_ATTR, PSVC_OK); if (status != PSVC_SUCCESS) return (status); /* Display message */ syslog(LOG_ERR, gettext("Device %s AC AVAILABLE"), id); } return (status); } int32_t psvc_init_ps_presence(psvc_opaque_t hdlp, char *id) { int err; int instance; boolean_t presence; err = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance); err |= psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &presence); ps_prev_present[instance] = ps_present[instance] = presence; return (err); } int32_t psvc_ps_monitor_policy_0(psvc_opaque_t hdlp, char *id) { int err; int instance; static int failed_last_time[2] = {0, 0}; err = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance); if (err != PSVC_SUCCESS) return (err); /* copy current presence to previous presence */ ps_prev_present[instance] = ps_present[instance]; /* Get new presence */ err = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &ps_present[instance]); if (err != PSVC_SUCCESS) goto out; /* Sustained Hotplug detected */ if (ps_present[instance] != ps_prev_present[instance]) { err = handle_ps_hotplug(hdlp, id, ps_present[instance]); return (err); } /* If our power supply is not present, we're done */ if (!ps_present[instance]) return (PSVC_SUCCESS); err = check_i2c_access(hdlp, id); if (err != PSVC_SUCCESS) { /* Quickie hotplug */ if (ps_present[instance] == PSVC_PRESENT && ps_prev_present[instance] == PSVC_PRESENT) { syslog(LOG_ERR, "Device %s removed", id); /* Reset prev_failed information */ ps_reset_prev_failed(instance); ps_prev_present[instance] = 0; handle_ps_hotplug(hdlp, id, ps_present[instance]); /* We ignore the error on a quickie hotplug */ return (PSVC_SUCCESS); } /* There was an actual i2c access error */ goto out; } err = check_ps_state(hdlp, id); if (err != PSVC_SUCCESS) goto out; failed_last_time[instance] = 0; return (err); out: if (! failed_last_time[instance]) { /* * We ignore the error condition the first time thru * because the PS could have been removed after (or * during) our call to check_ps_hotplug(). * * If the problem is still there the next time, then * we'll raise a flag. * * The instance determines which power supply the policy * errored on. For instance PS0 might have failed and then * PS1 might have failed, but we'll display a warning * even though there might not be anything actually wrong. * The instance keeps track of which failure occurred so * we warn on the corresponding occurrence of errors. */ failed_last_time[instance] = 1; return (PSVC_SUCCESS); } return (err); } static int light_disk_fault_leds(psvc_opaque_t hdlp, char *id, boolean_t disk_presence) { int err; int bit_nums[MAX_DISKS] = {6, 7}; uint8_t led_masks[MAX_DISKS] = {0x40, 0x80}; int instance; int bit_value; char state[PSVC_MAX_STR_LEN]; uint8_t byte; if (disk_presence != PSVC_PRESENT) return (PSVC_SUCCESS); err = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance); if (err != PSVC_SUCCESS) return (err); err = psvc_get_attr(hdlp, "DISK_PORT", PSVC_GPIO_VALUE_ATTR, &byte); if (err != PSVC_SUCCESS) return (err); err = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state); if (err != PSVC_SUCCESS) return (err); if (strcmp(state, PSVC_OK) == 0 || strcmp(state, "") == 0) { /* OK */ if (byte & led_masks[instance]) { /* Led is OFF */ return (err); /* Done. */ } else { /* Led is ON, Turn if OFF */ bit_value = 1; /* Active Low */ err = pcf8574_write_bit(hdlp, "DISK_PORT", bit_nums[instance], bit_value, DISKBP_MUST_BE_1); if (err != PSVC_SUCCESS) return (err); } } else { /* Disk is NOT OK */ if (byte & led_masks[instance]) { /* Led is OFF, Turn it ON */ bit_value = 0; /* Active Low */ err = pcf8574_write_bit(hdlp, "DISK_PORT", bit_nums[instance], bit_value, DISKBP_MUST_BE_1); if (err != PSVC_SUCCESS) return (err); } else { return (err); /* Done. */ } } return (err); } int verify_disk_wwn(char *wwn) { HBA_PORTATTRIBUTES hbaPortAttrs, discPortAttrs; HBA_HANDLE handle; HBA_STATUS status; HBA_ADAPTERATTRIBUTES hbaAttrs; HBA_UINT32 numberOfAdapters, hbaCount, hbaPort, discPort; char adaptername[256]; char vwwn[WWN_SIZE * 2]; char OSDeviceName[PATH_MAX + 1]; int count, linksize; /* Load common lib */ status = HBA_LoadLibrary(); if (status != HBA_STATUS_OK) { (void) HBA_FreeLibrary(); return (HBA_STATUS_ERROR); } /* * Since devfs can store multiple instances * of a target the validity of the WWN of a disk is * verified with an actual probe of internal disks */ /* Cycle through FC-AL Adapters and search for WWN */ numberOfAdapters = HBA_GetNumberOfAdapters(); for (hbaCount = 0; hbaCount < numberOfAdapters; hbaCount++) { if ((status = HBA_GetAdapterName(hbaCount, adaptername)) != HBA_STATUS_OK) continue; handle = HBA_OpenAdapter(adaptername); if (handle == 0) continue; /* Get Adapter Attributes */ if ((status = HBA_GetAdapterAttributes(handle, &hbaAttrs)) != HBA_STATUS_OK) { HBA_CloseAdapter(handle); continue; } /* Get Adapter's Port Attributes */ for (hbaPort = 0; hbaPort < hbaAttrs.NumberOfPorts; hbaPort++) { if ((status = HBA_GetAdapterPortAttributes(handle, hbaPort, &hbaPortAttrs)) != HBA_STATUS_OK) continue; /* * Verify whether this is onboard controller. * HBAAPI provides path of symbol link to * to the qlc node therefore readlink() is * needed to obtain hard link */ linksize = readlink(hbaPortAttrs.OSDeviceName, OSDeviceName, PATH_MAX); /* * If readlink does not return size of onboard * controller than don't bother checking device */ if ((linksize + 1) != sizeof (ONBOARD_CONTR)) continue; OSDeviceName[linksize] = '\0'; if (strcmp(OSDeviceName, ONBOARD_CONTR) != 0) continue; /* Get Discovered Port Attributes */ for (discPort = 0; discPort < hbaPortAttrs.NumberofDiscoveredPorts; discPort++) { status = HBA_GetDiscoveredPortAttributes( handle, hbaPort, discPort, &discPortAttrs); if (status != HBA_STATUS_OK) continue; /* Get target info */ for (count = 0; count < WWN_SIZE; count++) (void) sprintf(&vwwn[count * 2], "%2.2x", discPortAttrs.NodeWWN.wwn[count]); if (strcmp(wwn, vwwn) == 0) { HBA_CloseAdapter(handle); (void) HBA_FreeLibrary(); return (HBA_STATUS_OK); } } } HBA_CloseAdapter(handle); } (void) HBA_FreeLibrary(); return (HBA_STATUS_ERROR_ILLEGAL_WWN); } static int light_disk_ok2remove_leds(psvc_opaque_t hdlp, boolean_t *disk_present) { di_node_t node; di_node_t root_node; di_minor_t min_node; int *prop; int n; int target; int rv; int disk_online = 0; static int prev_online[MAX_DISKS] = {-1, -1}; int bit_nums[MAX_DISKS] = {4, 5}; int bit_val; int count; char *dev_path; char wwn[WWN_SIZE * 2]; uchar_t *prop_wwn; root_node = di_init("/", DINFOCPYALL); if (root_node == DI_NODE_NIL) return (PSVC_FAILURE); for (node = di_drv_first_node(DISK_DRV, root_node); node != DI_NODE_NIL; node = di_drv_next_node(node)) { n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "target", &prop); if (n == -1) continue; target = *prop; if (target < 0 || target > 1) continue; if (! disk_present[target]) continue; dev_path = di_devfs_path(node); if (memcmp(dev_path, QLC_NODE, (sizeof (QLC_NODE) - 1)) != 0) { /* * This isn't our FC-AL controller, so this * must be an external disk on Loop B. Skip it. */ di_devfs_path_free(dev_path); continue; } di_devfs_path_free(dev_path); /* * Verify if disk is valid by checking WWN * because devfs retains stale data. */ n = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, "node-wwn", &prop_wwn); if (n == -1) continue; for (count = 0; count < WWN_SIZE; count++) (void) sprintf(&wwn[count * 2], "%2.2x", prop_wwn[count]); n = verify_disk_wwn(wwn); if (n == HBA_STATUS_ERROR_ILLEGAL_WWN) continue; min_node = di_minor_next(node, DI_MINOR_NIL); disk_online = (min_node != DI_MINOR_NIL); if (! disk_online && prev_online[target]) { /* Light Led */ bit_val = 0; rv = pcf8574_write_bit(hdlp, "DISK_PORT", bit_nums[target], bit_val, DISKBP_MUST_BE_1); if (rv != PSVC_SUCCESS) goto done; } else if (!prev_online[target] && disk_online) { /* Unlight Led */ bit_val = 1; rv = pcf8574_write_bit(hdlp, "DISK_PORT", bit_nums[target], bit_val, DISKBP_MUST_BE_1); if (rv != PSVC_SUCCESS) goto done; } if (disk_online != prev_online[target]) prev_online[target] = disk_online; } done: di_fini(root_node); return (rv); } static int check_disk_fault(psvc_opaque_t hdlp, char *id, boolean_t disk_presence) { int32_t status = PSVC_SUCCESS; int32_t fault_on = 0; char *sensor_id; char disk_state[PSVC_MAX_STR_LEN]; char state[PSVC_MAX_STR_LEN]; char fault[PSVC_MAX_STR_LEN]; boolean_t change_of_state = 0; if (disk_presence != PSVC_PRESENT) return (PSVC_SUCCESS); status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, disk_state); if (status != PSVC_SUCCESS) return (status); status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &sensor_id, PSVC_DEV_FAULT_SENSOR, 0); if (status != PSVC_SUCCESS) return (status); status = psvc_get_attr(hdlp, sensor_id, PSVC_SWITCH_STATE_ATTR, state); if (status != PSVC_SUCCESS) return (status); /* Fault detected */ if (strcmp(state, PSVC_SWITCH_ON) == 0) { strlcpy(state, PSVC_ERROR, sizeof (state)); strlcpy(fault, PSVC_GEN_FAULT, sizeof (fault)); fault_on = 1; } else { /* No fault detected */ if (strcmp(disk_state, PSVC_OK) != 0) change_of_state = 1; strlcpy(state, PSVC_OK, sizeof (state)); strlcpy(fault, PSVC_NO_FAULT, sizeof (fault)); } status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, state); if (status != PSVC_SUCCESS) return (status); status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, fault); if (status != PSVC_SUCCESS) return (status); if (fault_on) { syslog(LOG_ERR, gettext("Fault detected: %s"), id); } else { if (change_of_state) syslog(LOG_ERR, gettext("Notice: %s okay"), id); } return (PSVC_SUCCESS); } static int check_disk_hotplug(psvc_opaque_t hdlp, char *id, boolean_t *disk_presence, int disk_instance) { boolean_t presence; boolean_t previous_presence; int32_t status = PSVC_SUCCESS; char label[PSVC_MAX_STR_LEN]; uint8_t disk_leds[MAX_DISKS][2] = {{4, 6}, {5, 7}}; status = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &presence); if (status != PSVC_SUCCESS) return (status); status = psvc_get_attr(hdlp, id, PSVC_PREV_PRESENCE_ATTR, &previous_presence); if (status != PSVC_SUCCESS) return (status); *disk_presence = presence; if (presence != previous_presence) { char parent_path[PICL_PROPNAMELEN_MAX]; picl_nodehdl_t child_node; status = psvc_get_attr(hdlp, id, PSVC_LABEL_ATTR, label); if (status != PSVC_SUCCESS) return (status); /* return parent path and node for an object */ psvcplugin_lookup(id, parent_path, &child_node); if (presence == PSVC_PRESENT) { picl_nodehdl_t parent_node; char state[PSVC_MAX_STR_LEN]; char fault[PSVC_MAX_STR_LEN]; syslog(LOG_ERR, gettext("Device %s inserted"), label); strlcpy(state, PSVC_OK, sizeof (state)); status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, state); if (status != PSVC_SUCCESS) return (status); strlcpy(fault, PSVC_NO_FAULT, sizeof (fault)); status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, fault); if (status != PSVC_SUCCESS) { return (status); } status = ptree_get_node_by_path(parent_path, &parent_node); if (status != PICL_SUCCESS) return (PSVC_FAILURE); status = ptree_add_node(parent_node, child_node); if (status != PICL_SUCCESS) return (PSVC_FAILURE); } else { /* * Disk Removed so we need to turn off these LEDs: * DISKx_FLT_LED * DISKx_REMOVE_LED */ int i; int bit_val = 1; /* Active Low */ for (i = 0; i < 2; i++) { status = pcf8574_write_bit(hdlp, "DISK_PORT", disk_leds[disk_instance][i], bit_val, DISKBP_MUST_BE_1); if (status != PSVC_SUCCESS) syslog(LOG_ERR, "Failed in turning off" " %d's LEDs", id); } syslog(LOG_ERR, gettext("Device %s removed"), label); ptree_delete_node(child_node); } } status = psvc_set_attr(hdlp, id, PSVC_PREV_PRESENCE_ATTR, &presence); if (status != PSVC_SUCCESS) return (status); return (status); } int32_t psvc_disk_monitor_policy_0(psvc_opaque_t hdlp, char *id) { int rv, err, i; char *disks[MAX_DISKS] = {"DISK0", "DISK1"}; int saved_errno = 0; boolean_t disk_present[MAX_DISKS] = {0, 0}; for (i = 0; i < MAX_DISKS; i++) { err = check_disk_hotplug(hdlp, disks[i], &disk_present[i], i); if (err) saved_errno = errno; rv = err; err = check_disk_fault(hdlp, disks[i], disk_present[i]); if (err) saved_errno = errno; rv |= err; err |= light_disk_fault_leds(hdlp, disks[i], disk_present[i]); if (err) saved_errno = errno; rv |= err; } err = light_disk_ok2remove_leds(hdlp, disk_present); if (err) saved_errno = errno; rv |= err; errno = saved_errno; return (rv); } /* * Read in temperature thresholds from FRU Prom and update the * default values. */ #define START_OFFSET 0x1800 /* Last 2K of SEEPROM */ #define NUM_SEG_OFFSET 0x1805 /* Number of segments */ #define SEG_TABLE_OFFSET 0x1806 /* Segment description tables */ static int32_t read_sc_segment(psvc_opaque_t hdlp, char *id, char *fru_id, int offset) { static int thresh_names[] = { PSVC_HW_LO_SHUT_ATTR, PSVC_LO_SHUT_ATTR, PSVC_LO_WARN_ATTR, PSVC_NOT_USED, /* LOW MODE */ PSVC_OPTIMAL_TEMP_ATTR, PSVC_HI_WARN_ATTR, PSVC_HI_SHUT_ATTR, PSVC_HW_HI_SHUT_ATTR }; int8_t amb_temp_array[8]; int i; fru_info_t fru_info; int err; fru_info.buf_start = offset + 8; fru_info.buf = amb_temp_array; fru_info.read_size = 8; err = psvc_get_attr(hdlp, fru_id, PSVC_FRU_INFO_ATTR, &fru_info); if (err != PSVC_SUCCESS) return (err); for (i = 0; i < 8; i++) { int32_t temp = amb_temp_array[i]; if (thresh_names[i] == PSVC_NOT_USED) continue; err = psvc_set_attr(hdlp, id, thresh_names[i], &temp); if (err != PSVC_SUCCESS) return (err); } return (PSVC_SUCCESS); } int32_t update_disk_bp_temp_thresholds(psvc_opaque_t hdlp, char *id) { char *fru; fru_info_t fru_info; int16_t seg_offset; int8_t byte; int8_t seg_count; char seg_name[2]; int current_offset, i, err; err = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &fru, PSVC_FRU, 0); if (err != PSVC_SUCCESS) return (err); /* Sanity Check */ fru_info.buf_start = START_OFFSET; fru_info.buf = &byte; fru_info.read_size = 1; err = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR, &fru_info); if (err != PSVC_SUCCESS) return (err); if (*fru_info.buf != 8) { syslog(LOG_ERR, "Notice: FRU Prom %s not programmed", fru); } /* Should do CRC Check on fru */ /* Get Segment Count */ fru_info.buf_start = NUM_SEG_OFFSET; fru_info.buf = &seg_count; fru_info.read_size = 1; err = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR, &fru_info); if (err != PSVC_SUCCESS) return (err); current_offset = SEG_TABLE_OFFSET; for (i = 0; i < seg_count; i++) { fru_info.buf_start = current_offset; fru_info.buf = seg_name; fru_info.read_size = 2; err = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR, &fru_info); if (err != PSVC_SUCCESS) return (err); if (memcmp(seg_name, "SC", 2) == 0) { current_offset += 6; /* Skip over description */ fru_info.buf_start = current_offset; fru_info.buf = (char *)&seg_offset; fru_info.read_size = 2; psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR, &fru_info); return (read_sc_segment(hdlp, id, fru, seg_offset)); } current_offset += 10; } return (PSVC_SUCCESS); }