/*
* 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
* 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 2007 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 <syslog.h>
#include <unistd.h>
#include <stdio.h>
#include <libintl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/time_impl.h>
#include <libdevinfo.h>
#include <libdevice.h>
#include <picl.h>
#include <picltree.h>
#include <hbaapi.h>
#include <limits.h>
#include <sys/systeminfo.h>
#include <psvc_objects.h>
/* Device paths for power supply hotplug handling */
/* Bit masks so we don't "wedge" the inputs */
/*LINTLIBRARY*/
/*
* 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
*/
/*
* Keep track of the power supply's previous presence
* because PSVC doesn't do that for us.
*/
/* Local Routines for the environmental policies */
static int ac_unplugged(psvc_opaque_t, char *);
static int ac_power_check(psvc_opaque_t, char *, char *);
/*
* The I2C bus is noisy, and the state may be incorrectly reported as
* having changed. When the state changes, we attempt to confirm by
* retrying. If any retries indicate that the state has not changed, we
* assume the state change(s) were incorrect and the state has not changed.
* The following variables are used to store the tuneable values read in
* from the optional i2cparam.conf file for this shared object library.
*/
typedef struct {
int *pvar;
char *texttag;
&n_retry_fan, "n_retry_fan",
&retry_sleep_fan, "retry_sleep_fan",
&n_retry_ps_status, "n_retry_ps_status",
&retry_sleep_ps_status, "retry_sleep_ps_status",
&n_retry_pshp, "n_retry_pshp",
&retry_sleep_pshp, "retry_sleep_pshp",
&n_retry_diskhp, "n_retry_diskhp",
&retry_sleep_diskhp, "retry_sleep_diskhp",
&n_retry_temp_shutdown, "n_retry_temp_shutdown",
&retry_sleep_temp_shutdown, "retry_sleep_temp_shutdown",
&n_retry_fsp_fault, "n_retry_fsp_fault",
&retry_sleep_fsp_fault, "retry_sleep_fsp_fault",
};
#pragma init(i2cparams_load)
static void
int usingDefaults)
{
char s[128];
if (!usingDefaults) {
(void) snprintf(s, sizeof (s),
"# Values from /usr/platform/%s/lib/i2cparam.conf\n",
platform);
} else {
/* no file - we're using the defaults */
(void) snprintf(s, sizeof (s),
"# No /usr/platform/%s/lib/i2cparam.conf file, using defaults\n",
platform);
}
p = pi2cparams;
*(p->pvar));
if (!usingDefaults)
p++;
}
}
static void
i2cparams_load(void)
{
char s[128];
int val;
return;
}
/* read thru the i2cparam.conf file and set variables */
if (s[0] == '#') /* skip comment lines */
continue;
/* try to find a string match and get the value */
continue;
if (val < 1)
p = &(i2cparams[0]);
0) {
break;
}
p++;
}
}
}
/* output the values of the parameters */
}
/*
* Create an I2C device node.
*/
static int
{
goto bad;
/* device definition properties */
/* create the device node */
goto bad;
goto bad;
#ifdef DEBUG
dev_path);
#endif
rv = PSVC_SUCCESS;
bad:
return (rv);
}
/*
* Delete an I2C device node given the device path.
*/
static void
{
int rv;
return;
}
if (rv != DDI_SUCCESS)
#ifdef DEBUG
else
#endif
}
/* PCF8574 Reset Function */
static int
{
int err;
int i;
for (i = 0; i < 2; i++) {
&reset_bits[i]);
if (err != PSVC_SUCCESS) {
#ifdef DEBUG
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
{
if (rv != PSVC_SUCCESS)
return (rv);
byte |= write_must_be_1;
return (rv);
}
/*
* To enable the i2c bus, we must toggle bit 6 on the PDB's
* PCF8574 (0x4C) high->low->high
*/
static int
{
for (i = 0; i < 3; i++) {
if (rv != PSVC_SUCCESS) {
goto bad;
}
}
return (rv);
bad:
#ifdef DEBUG
#endif
return (rv);
}
{
&reset));
}
{
}
static int32_t
{
int status;
int speed;
int low_thresh;
char *tach_id;
int retry;
/* Get this fan object's corresponding fan tach */
&tach_id, PSVC_FAN_SPEED_TACHOMETER, 0);
if (status != PSVC_SUCCESS)
return (status);
/* Get the low fan speed threshold */
if (status != PSVC_SUCCESS)
return (status);
retry = 0;
do {
if (retry)
(void) sleep(retry_sleep_fan);
/* Get the fan speed */
&speed);
if (status != PSVC_SUCCESS)
return (status);
sizeof (fault_state));
have_fault = 1;
} else { /* Fault gone? */
sizeof (fault_state));
have_fault = 0;
}
retry++;
/* Assign new states to the fan object */
if (status != PSVC_SUCCESS)
return (status);
if (status != PSVC_SUCCESS)
return (status);
/* Get state and previous state */
if (status != PSVC_SUCCESS)
return (status);
if (status != PSVC_SUCCESS)
return (status);
/* Display notices */
} else {
}
}
*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.
*/
{
int fan_count;
int led_count;
int err, i;
char *led_id;
char *fan_id;
/* Get the number of fans associated with this fan tray. */
if (err != PSVC_SUCCESS)
return (err);
for (i = 0; i < fan_count; i++) {
&fan_id, PSVC_FAN_TRAY_FANS, i);
if (err != PSVC_SUCCESS)
return (err);
if (err != PSVC_SUCCESS)
return (err);
}
if (fault_on) {
if (err != PSVC_SUCCESS)
return (err);
} else {
if (err != PSVC_SUCCESS)
return (err);
}
if (err != PSVC_SUCCESS)
return (err);
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 (err != PSVC_SUCCESS)
return (err);
for (i = 0; i < led_count; i++) {
PSVC_DEV_FAULT_LED, i);
if (err != PSVC_SUCCESS)
return (err);
if (err != PSVC_SUCCESS)
return (err);
if (err != PSVC_SUCCESS)
return (err);
}
}
return (err);
}
static int32_t
{
char *sensorid;
int32_t i;
int retry;
for (i = 0; i < sensor_count; ++i) {
&sensorid, PSVC_DEV_TEMP_SENSOR, i);
if (status == PSVC_FAILURE)
return (status);
retry = 0;
do {
if (retry)
(void) sleep(retry_sleep_temp_shutdown);
if (status == PSVC_FAILURE)
return (status);
retry++;
(retry < n_retry_temp_shutdown));
system("shutdown -y -g 60 -i 5 \"OVERTEMP condition\"");
}
}
return (status);
}
{
char *cpuid;
int32_t i;
PSVC_CPU);
for (i = 0; i < cpu_count; ++i) {
PSVC_CPU, i);
if (status == PSVC_FAILURE)
return (status);
return (status);
if (present == PSVC_PRESENT) {
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 i;
char *device_id;
int retry;
if (status != PSVC_SUCCESS)
return (status);
for (i = 0; i < device_count; i++) {
&device_id, PSVC_DEV_FAULT_SENSOR, i);
if (status != PSVC_SUCCESS)
return (status);
retry = 0;
do {
if (retry)
(void) sleep(retry_sleep_fsp_fault);
if (status != PSVC_SUCCESS)
return (status);
strlen(device_state) != 0) {
failed_count++;
}
retry++;
}
if (failed_count == 0 && led_on) {
led_on = 0;
}
if (failed_count > 0 && ! led_on) {
led_on = 1;
}
return (PSVC_SUCCESS);
}
/* Power Supply Policy Helper and Worker Functions */
static void
{
int i;
/* Reset the power supply's failure information */
for (i = 0; i < 3; i++) {
ps_prev_failed[index][i] = 0;
}
}
static int
{
int rv;
"%s_FAULT_SENSOR", id);
&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
{
"_FAULT_SENSOR"};
char *sensor_id;
int j;
/* Go through the add on list and set presence */
for (j = 0; j < add_ons; j++) {
child_add_on[j]);
&presence);
if (status != PSVC_SUCCESS)
return (status);
}
/* Go through each PS's fault sensors */
for (j = 0; j < PS_MAX_FAULT_SENSORS; j++) {
&(sensor_id), PSVC_DEV_FAULT_SENSOR, j);
if (status != PSVC_SUCCESS)
return (status);
&presence);
if (status != PSVC_SUCCESS)
return (status);
}
/* Go through each PS's onboard i2c hardware */
for (j = 0; j < 2; j++) {
&(sensor_id), PSVC_PHYSICAL_DEVICE, j);
if (status != PSVC_SUCCESS)
return (status);
&presence);
if (status != PSVC_SUCCESS)
return (status);
}
return (status);
}
static int
{
/* Convert name to node and parent path */
/*
* Get the power supply's instance.
* Used to index the xxx_addr arrays
*/
if (status != PSVC_SUCCESS)
return (status);
/* Service Power Supply Insertion */
/* PICL Tree Maintenance */
/*
* 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.
*/
/*
* Device Tree Maintenance
* Add the devinfo tree node entry for the pcf8574 and seeprom
* and attach their drivers.
*/
} else {
/* Service Power Supply Removal */
/* Reset the power supply's failure information */
/* PICL Tree Maintenance */
/*
* 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).
*/
SEG5_DEV_NAME"ioexp@0,%x:pcf8574",
}
"%s_LOGICAL_STATE", id);
/* Enable the i2c connection to the power supply */
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
{
int32_t i;
char *sensor_id;
int instance;
int retry;
/* Logical state id */
"%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.
*/
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.
*/
if (status != PSVC_SUCCESS)
return (status);
return (status);
if (status != PSVC_SUCCESS)
return (status);
retry = 0;
do {
if (retry)
(void) sleep(retry_sleep_ps_status);
/* Handle the PDB P/S OK Bit */
if (status != PSVC_SUCCESS)
return (status);
retry++;
} while ((retry < n_retry_ps_status) &&
/*
* If there is a change of state (current state differs from
* previous state, then assign the error values.
*/
fault_on = 1;
"Device %s: Failure Detected -- %s "
ps_okay = 0;
} else {
}
if (status != PSVC_SUCCESS)
return (status);
if (status != PSVC_SUCCESS)
return (status);
}
if (status != PSVC_SUCCESS)
return (status);
if (status != PSVC_SUCCESS) {
return (status);
}
/* Handle the power supply fail bits. */
for (i = 0; i < sensor_count; i++) {
&sensor_id, PSVC_DEV_FAULT_SENSOR, i);
if (status != PSVC_SUCCESS)
return (status);
retry = 0;
do {
if (retry)
(void) sleep(retry_sleep_ps_status);
if (status != PSVC_SUCCESS)
return (status);
retry++;
} while ((retry < n_retry_ps_status) &&
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)
"Device %s: Fault Detected"),
id);
} else {
}
}
}
if (status != PSVC_SUCCESS)
return (status);
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) {
if (status != PSVC_SUCCESS)
return (status);
}
if (status != PSVC_SUCCESS)
return (status);
/*
* "id" is in the form of "PSx", We need to make it
* PSx_RESET.
*/
return (status);
}
/*
* There was no fault encountered so we want to
* set 'ps_logical_state' to "OK"
*/
for (i = 0; i < 3; i++) {
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)
"Notice %s: Fault Cleared"),
id);
} else {
}
}
if (status != PSVC_SUCCESS)
return (status);
}
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
{
if (status == PSVC_FAILURE) {
return (status);
}
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
{
char *sensor_id;
int unplugged, i;
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.
*/
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.
*/
if (status != PSVC_SUCCESS) {
return (status);
}
/* set id's state to "NO AC POWER" */
"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.
*/
if (status != PSVC_SUCCESS)
return (status);
"NO AC POWER");
if (status != PSVC_SUCCESS)
return (status);
/* Set fault sensor states to "" */
for (i = 0; i < sensor_count; ++i) {
&sensor_id, PSVC_DEV_FAULT_SENSOR, i);
if (status != PSVC_SUCCESS)
return (status);
PSVC_FAULTID_ATTR, "");
if (status != PSVC_SUCCESS)
return (status);
}
}
/* Power cord is plugged in */
/* Default the state to "OK" */
PSVC_OK);
if (status != PSVC_SUCCESS)
return (status);
/* Default the PS_LOGICAL_STATE to "OK" */
PSVC_OK);
if (status != PSVC_SUCCESS)
return (status);
/* Display message */
}
return (status);
}
{
int err;
int instance;
return (err);
}
{
int err;
int instance;
int retry;
if (err != PSVC_SUCCESS)
return (err);
/* copy current presence to previous presence */
retry = 0;
do {
if (retry)
(void) sleep(retry_sleep_pshp);
/* Get new presence */
&ps_present[instance]);
if (err != PSVC_SUCCESS)
goto out;
retry++;
} while ((retry < n_retry_pshp) &&
/* Sustained Hotplug detected */
return (err);
}
/* If our power supply is not present, we're done */
if (!ps_present[instance])
return (PSVC_SUCCESS);
if (err != PSVC_SUCCESS) {
/* Quickie hotplug */
/* Reset prev_failed information */
ps_prev_present[instance] = 0;
/* We ignore the error on a quickie hotplug */
return (PSVC_SUCCESS);
}
/* There was an actual i2c access error */
goto out;
}
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.
*/
return (PSVC_SUCCESS);
}
return (err);
}
static int
{
int err;
int instance;
int bit_value;
if (disk_presence != PSVC_PRESENT)
return (PSVC_SUCCESS);
if (err != PSVC_SUCCESS)
return (err);
&byte);
if (err != PSVC_SUCCESS)
return (err);
if (err != PSVC_SUCCESS)
return (err);
return (err); /* Done. */
} else { /* Led is ON, Turn if OFF */
if (err != PSVC_SUCCESS)
return (err);
}
} else { /* Disk is NOT OK */
bit_value = 0; /* Active Low */
if (err != PSVC_SUCCESS)
return (err);
} else {
return (err); /* Done. */
}
}
return (err);
}
int
{
/* 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 */
continue;
if (handle == 0)
continue;
/* Get Adapter Attributes */
&hbaAttrs)) != HBA_STATUS_OK) {
continue;
}
/* Get Adapter's Port Attributes */
for (hbaPort = 0;
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
*/
/*
* If readlink does not return size of onboard
* controller than don't bother checking device
*/
continue;
continue;
/* Get Discovered Port Attributes */
for (discPort = 0;
discPort++) {
if (status != HBA_STATUS_OK)
continue;
/* Get target info */
"%2.2x",
(void) HBA_FreeLibrary();
return (HBA_STATUS_OK);
}
}
}
}
(void) HBA_FreeLibrary();
return (HBA_STATUS_ERROR_ILLEGAL_WWN);
}
static int
{
int *prop;
int n;
int target;
int rv;
int disk_online = 0;
int bit_val;
int count;
char *dev_path;
if (root_node == DI_NODE_NIL)
return (PSVC_FAILURE);
node != DI_NODE_NIL;
if (n == -1)
continue;
continue;
if (! disk_present[target])
continue;
/*
* This isn't our FC-AL controller, so this
* must be an external disk on Loop B. Skip it.
*/
continue;
}
/*
* Verify if disk is valid by checking WWN
* because devfs retains stale data.
*/
"node-wwn", &prop_wwn);
if (n == -1)
continue;
n = verify_disk_wwn(wwn);
if (n == HBA_STATUS_ERROR_ILLEGAL_WWN)
continue;
/* Light Led */
bit_val = 0;
if (rv != PSVC_SUCCESS)
goto done;
/* Unlight Led */
bit_val = 1;
if (rv != PSVC_SUCCESS)
goto done;
}
}
done:
return (rv);
}
static int
{
char *sensor_id;
if (disk_presence != PSVC_PRESENT)
return (PSVC_SUCCESS);
if (status != PSVC_SUCCESS)
return (status);
&sensor_id, PSVC_DEV_FAULT_SENSOR, 0);
if (status != PSVC_SUCCESS)
return (status);
if (status != PSVC_SUCCESS)
return (status);
/* Fault detected */
fault_on = 1;
} else { /* No fault detected */
change_of_state = 1;
}
if (status != PSVC_SUCCESS)
return (status);
if (status != PSVC_SUCCESS)
return (status);
if (fault_on) {
} else {
if (change_of_state)
}
return (PSVC_SUCCESS);
}
static int
int disk_instance)
{
int retry;
if (status != PSVC_SUCCESS)
return (status);
retry = 0;
do {
if (retry)
(void) sleep(retry_sleep_diskhp);
&presence);
if (status != PSVC_SUCCESS)
return (status);
retry++;
} while ((retry < n_retry_diskhp) &&
(presence != previous_presence));
if (presence != previous_presence) {
if (status != PSVC_SUCCESS)
return (status);
/* return parent path and node for an object */
if (presence == PSVC_PRESENT) {
state);
if (status != PSVC_SUCCESS)
return (status);
fault);
if (status != PSVC_SUCCESS) {
return (status);
}
&parent_node);
if (status != PICL_SUCCESS)
return (PSVC_FAILURE);
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;
for (i = 0; i < 2; i++) {
if (status != PSVC_SUCCESS)
" %d's LEDs", id);
}
}
}
if (status != PSVC_SUCCESS)
return (status);
return (status);
}
{
int saved_errno = 0;
for (i = 0; i < MAX_DISKS; i++) {
}
errno = saved_errno;
return (rv);
}
/*
* Read in temperature thresholds from FRU Prom and update the
* default values.
*/
static int32_t
{
static int thresh_names[] = {
PSVC_NOT_USED, /* LOW MODE */
};
int i;
int err;
if (err != PSVC_SUCCESS)
return (err);
for (i = 0; i < 8; i++) {
if (thresh_names[i] == PSVC_NOT_USED)
continue;
if (err != PSVC_SUCCESS)
return (err);
}
return (PSVC_SUCCESS);
}
{
char *fru;
if (err != PSVC_SUCCESS)
return (err);
/* Sanity Check */
if (err != PSVC_SUCCESS)
return (err);
}
/* Should do CRC Check on fru */
/* Get Segment Count */
if (err != PSVC_SUCCESS)
return (err);
for (i = 0; i < seg_count; i++) {
if (err != PSVC_SUCCESS)
return (err);
&fru_info);
}
current_offset += 10;
}
return (PSVC_SUCCESS);
}