cfga_sata.c revision 724365f7556fc4201fdb11766ebc6bd918523130
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include "cfga_sata.h"
/*
* This file contains the entry points to the plug-in as defined in the
* config_admin(3X) man page.
*/
/*
* Set the version number for the cfgadm library's use.
*/
int cfga_version = CFGA_HSL_V2;
enum {
HELP_HEADER = 1,
};
/* SATA specific help messages */
static char *sata_help[] = {
NULL,
"SATA specific commands:\n",
" cfgadm -c [configure|unconfigure|disconnect|connect] ap_id "
"[ap_id...]\n",
" cfgadm -x sata_reset_port ap_id [ap_id...]\n",
" cfgadm -x sata_reset_device ap_id [ap_id...]\n",
" cfgadm -x sata_reset_all ap_id\n",
" cfgadm -x sata_port_deactivate ap_id [ap_id...]\n",
" cfgadm -x sata_port_activate ap_id [ap_id...]\n",
" cfgadm -x sata_port_self_test ap_id [ap_id...]\n",
" cfgadm -t ap_id\n",
"\tunknown command or option:\n",
}; /* End help messages */
/*
* Messages.
*/
/* CFGA_SATA_OK */
/* CFGA_SATA_NACK */
/* CFGA_SATA_DEVICE_UNCONFIGURED */
/* CFGA_SATA_UNKNOWN / CFGA_LIB_ERROR -> "Library error" */
/* CFGA_SATA_INTERNAL_ERROR / CFGA_LIB_ERROR -> "Library error" */
/* CFGA_SATA_DATA_ERROR / CFGA_DATA_ERROR -> "Data error" */
/* CFGA_SATA_OPTIONS / CFGA_ERROR -> "Hardware specific failure" */
/* CFGA_SATA_HWOPNOTSUPP / CFGA_ERROR -> "Hardware specific failure" */
/*
* CFGA_SATA_DYNAMIC_AP /
* CFGA_LIB_ERROR -> "Configuration operation invalid"
*/
/* CFGA_SATA_AP / CFGA_APID_NOEXIST -> "Attachment point not found" */
/* CFGA_SATA_PORT / CFGA_LIB_ERROR -> "Library error" */
/* CFGA_SATA_DEVCTL / CFGA_LIB_ERROR -> "Library error" */
"Cannot allocate devctl handle " },
/*
* CFGA_SATA_DEV_CONFIGURE /
* CFGA_ERROR -> "Hardware specific failure"
*/
/*
* CFGA_SATA_DEV_UNCONFIGURE /
* CFGA_ERROR -> "Hardware specific failure"
*/
/*
* CFGA_SATA_DISCONNECTED
* CFGA_INVAL -> "Configuration operation invalid"
*/
/*
* CFGA_SATA_NOT_CONNECTED
* CFGA_INVAL -> "Configuration operation invalid"
*/
/*
* CFGA_SATA_NOT_CONFIGURED /
* CFGA_INVAL -> "Configuration operation invalid"
*/
/*
* CFGA_SATA_ALREADY_CONNECTED /
* CFGA_INVAL -> "Configuration operation invalid"
*/
/*
* CFGA_SATA_ALREADY_CONFIGURED /
* CFGA_INVAL -> "Configuration operation invalid"
*/
/*
* CFGA_SATA_INVALID_DEVNAME /
* CFGA_INVAL -> "Configuration operation invalid"
*/
/* CFGA_SATA_OPEN / CFGA_LIB_ERROR -> "Library error" */
/* CFGA_SATA_IOCTL / CFGA_ERROR -> "Hardware specific failure" */
/*
* CFGA_SATA_BUSY /
* CFGA_SYSTEM_BUSY -> "System is busy, try again"
*/
/* CFGA_SATA_ALLOC_FAIL / CFGA_LIB_ERROR -> "Library error" */
/*
* CFGA_SATA_OPNOTSUPP /
* CFGA_OPNOTSUPP -> "Configuration operation not supported"
*/
/* CFGA_SATA_DEVLINK / CFGA_LIB_ERROR -> "Library error" */
/* CFGA_SATA_STATE / CFGA_LIB_ERROR -> "Library error" */
/* CFGA_SATA_PRIV / CFGA_PRIV -> "Insufficient privileges" */
/* CFGA_SATA_NVLIST / CFGA_ERROR -> "Hardware specific failure" */
/* CFGA_SATA_ZEROLEN / CFGA_ERROR -> "Hardware specific failure" */
/* CFGA_SATA_RCM_HANDLE / CFGA_ERROR -> "Hardware specific failure" */
/*
* CFGA_SATA_RCM_ONLINE /
* CFGA_SYSTEM_BUSY -> "System is busy, try again"
*/
/*
* CFGA_SATA_RCM_OFFLINE /
* CFGA_SYSTEM_BUSY -> "System is busy, try again"
*/
/* CFGA_SATA_RCM_INFO / CFGA_ERROR -> "Hardware specific failure" */
}; /* End error messages */
static cfga_sata_ret_t
static cfga_sata_ret_t
static cfga_sata_ret_t
static cfga_sata_ret_t
static void
static char *
sata_get_devicepath(const char *ap_id);
static int
/* Utilities */
static cfga_sata_ret_t
{
char *linkpath;
char *buf;
char *real_path;
int deplen;
int err = 0;
char *p;
/*
* Using libdevinfo for this is overkill and kills performance
* when multiple consumers of libcfgadm are executing
* concurrently.
*/
return (CFGA_SATA_INTERNAL_ERROR);
}
sizeof (struct dirent);
goto pp_cleanup;
}
continue;
continue;
goto pp_cleanup;
}
continue;
/*
* realpath() is too darn slow, so fake
* it, by using what we know about /dev
* links: they are always of the form:
* <"../">+/devices/<path>
*/
p = buf;
p += 3;
if (p != buf)
p--; /* back up to get a slash */
assert (*p == '/');
goto pp_cleanup;
}
}
}
}
if (err != 0) {
return (CFGA_SATA_INTERNAL_ERROR);
}
return (CFGA_SATA_OK);
if (dp)
if (dep)
if (linkpath)
if (buf)
if (real_path)
if (*logpp) {
}
return (rv);
}
/*
* Given the index into a table (msgcvt_t) of messages, get the message
* string, converting it to the proper locale if necessary.
* NOTE: Indexes are defined in cfga_sata.h
*/
static const char *
{
}
}
/*
* Allocates and creates a message string (in *ret_str),
* by concatenating all the (char *) args together, in order.
* Last arg MUST be NULL.
*/
static void
{
char *str;
/* We're screwed */
return;
}
}
}
/*
* Error message handling.
* For the rv passed in, looks up the corresponding error message string(s),
* internationalized if necessary, and concatenates it into a new
* memory buffer, and points *errstring to it.
* Note not all rvs will result in an error message return, as not all
* error conditions warrant a SATA-specific error message - for those
* conditions the cfgadm generic messages are sufficient.
*
* Some messages may display ap_id or errno, which is why they are passed
* in.
*/
char **errstring,
const char *ap_id,
int l_errno)
{
}
/*
* Generate the appropriate SATA-specific error message(s) (if any).
*/
switch (rv) {
case CFGA_SATA_OK:
case CFGA_NACK:
/* Special case - do nothing. */
break;
case CFGA_SATA_UNKNOWN:
case CFGA_SATA_DYNAMIC_AP:
case CFGA_SATA_INTERNAL_ERROR:
case CFGA_SATA_OPTIONS:
case CFGA_SATA_ALLOC_FAIL:
case CFGA_SATA_STATE:
case CFGA_SATA_PRIV:
case CFGA_SATA_OPNOTSUPP:
case CFGA_SATA_DATA_ERROR:
/* These messages require no additional strings passed. */
break;
case CFGA_SATA_HWOPNOTSUPP:
/* hardware-specific help needed */
break;
case CFGA_SATA_AP:
case CFGA_SATA_PORT:
case CFGA_SATA_NOT_CONNECTED:
case CFGA_SATA_NOT_CONFIGURED:
case CFGA_SATA_BUSY:
case CFGA_SATA_DEVLINK:
case CFGA_SATA_RCM_HANDLE:
case CFGA_SATA_RCM_ONLINE:
case CFGA_SATA_RCM_OFFLINE:
case CFGA_SATA_RCM_INFO:
case CFGA_SATA_DEV_CONFIGURE:
case CFGA_SATA_DISCONNECTED:
/* These messages also print ap_id. */
break;
case CFGA_SATA_IOCTL:
case CFGA_SATA_NVLIST:
/* These messages also print errno. */
{
break;
}
case CFGA_SATA_OPEN:
/* These messages also apid and errno. */
{
break;
}
default:
} /* end switch */
/*
* Determine the proper error code to send back to the cfgadm library.
*/
}
/*
* Entry points
*/
/* cfgadm entry point */
/*ARGSUSED*/
const char *ap_id,
const char *options,
struct cfga_confirm *confp,
char **errstring,
{
int ret;
int len;
char *msg;
char *devpath;
char *pdyn;
/*
* All sub-commands which can change state of device require
* root privileges.
*/
if (geteuid() != 0) {
rv = CFGA_SATA_PRIV;
goto bailout;
}
goto bailout;
}
DC_RDONLY)) != CFGA_SATA_OK) {
goto bailout;
}
switch (state_change_cmd) {
case CFGA_CMD_CONFIGURE:
goto bailout;
if (ostate == AP_OSTATE_CONFIGURED) {
goto bailout;
}
/* Disallow dynamic AP name component */
goto bailout;
}
if (rstate == AP_RSTATE_EMPTY) {
goto bailout;
}
rv = CFGA_SATA_OK;
goto bailout;
}
int i;
/*
* Try for some time as SATA hotplug thread
* takes a while to create the path then
* eventually give up.
*/
(void) sleep(6);
}
break;
}
}
break;
case CFGA_CMD_UNCONFIGURE:
goto bailout;
if (rstate != AP_RSTATE_CONNECTED) {
goto bailout;
}
if (ostate != AP_OSTATE_CONFIGURED) {
goto bailout;
}
/* Strip off AP name dynamic component, if present */
*pdyn = '\0';
}
rv = CFGA_SATA_OK;
" %s%s\n%s",
}
rv = CFGA_SATA_NACK;
break;
}
(void) printf(
"cfga_change_state: get device path failed\n");
break;
}
!= CFGA_SATA_OK) {
break;
}
if (ret != 0) {
rv = CFGA_SATA_BUSY;
}
flags);
} else {
flags);
}
break;
case CFGA_CMD_DISCONNECT:
goto bailout;
if (rstate == AP_RSTATE_DISCONNECTED) {
goto bailout;
}
/* Strip off AP name dynamic component, if present */
*pdyn = '\0';
}
/*
* If the port originally with device attached and was
* unconfigured already, the devicepath for the sd will be
* removed. sata_get_devicepath in this case is not necessary.
*/
/* only call rcm_offline if the state was CONFIGURED */
if (ostate == AP_OSTATE_CONFIGURED) {
(void) printf(
"cfga_change_state: get path failed\n");
break;
}
"Disconnect"
" %s%s\n%s",
}
rv = CFGA_SATA_NACK;
break;
}
break;
}
if (ret != 0) {
(void) printf(
"devctl_ap_unconfigure failed\n");
rv = CFGA_SATA_BUSY;
/*
* The current policy is that if unconfigure
* failed, do not continue with disconnect.
* If the port needs to be forced into the
* disconnect (shutdown) state,
* the -x sata_port_poweroff command should be
* used instead of -c disconnect
*/
break;
} else {
(void) printf("%s\n",
}
} else if (rstate == AP_RSTATE_CONNECTED ||
rstate == AP_RSTATE_EMPTY) {
"Disconnect"
" %s%s\n%s",
}
rv = CFGA_SATA_NACK;
break;
}
}
if (ret != 0) {
rv = CFGA_SATA_BUSY;
}
}
break;
case CFGA_CMD_CONNECT:
goto bailout;
if (rstate == AP_RSTATE_CONNECTED) {
goto bailout;
}
" %s%s\n%s",
}
rv = CFGA_SATA_NACK;
break;
}
/* Disallow dynamic AP name component */
goto bailout;
}
if (ret != 0) {
} else {
rv = CFGA_SATA_OK;
}
break;
case CFGA_CMD_LOAD:
case CFGA_CMD_UNLOAD:
break;
case CFGA_CMD_NONE:
default:
}
}
/* cfgadm entry point */
const char *func,
const char *ap_id,
const char *options,
struct cfga_confirm *confp,
char **errstring,
{
int len;
char *msg;
char *str_p;
}
/*
* All subcommands which can change state of device require
* root privileges.
*/
if (geteuid() != 0) {
rv = CFGA_SATA_PRIV;
goto bailout;
}
(void) printf("No valid option specified\n");
goto bailout;
}
CFGA_SATA_OK) {
goto bailout;
}
/* We do not care here about dynamic AP name component */
*str_p = '\0';
}
rv = CFGA_SATA_OK;
" %s%s\n%s",
} else {
rv = CFGA_SATA_NACK;
goto bailout;
}
rv = CFGA_SATA_NACK;
goto bailout;
}
goto bailout;
/*
* Reset device function requires device to be connected
*/
if (rstate != AP_RSTATE_CONNECTED) {
goto bailout;
}
" %s%s\n%s",
} else {
rv = CFGA_SATA_NACK;
goto bailout;
}
rv = CFGA_SATA_NACK;
goto bailout;
}
" %s%s\n%s",
} else {
rv = CFGA_SATA_NACK;
goto bailout;
}
rv = CFGA_SATA_NACK;
goto bailout;
}
" %s%s\n%s",
} else {
rv = CFGA_SATA_NACK;
goto bailout;
}
rv = CFGA_SATA_NACK;
goto bailout;
}
" %s%s\n%s",
} else {
rv = CFGA_SATA_NACK;
goto bailout;
}
rv = CFGA_SATA_NACK;
goto bailout;
}
goto bailout;
" %s%s\n%s",
} else {
rv = CFGA_SATA_NACK;
goto bailout;
}
rv = CFGA_SATA_NACK;
goto bailout;
}
} else {
/* Unrecognized operation request */
}
}
/* cfgadm entry point */
/*ARGSUSED*/
const char *ap_id,
const char *options,
char **errstring,
{
/* Should call ioctl for self test - phase 2 */
return (CFGA_OPNOTSUPP);
}
int
{
char *minorpath;
char *cp;
}
return (DI_WALK_TERMINATE);
}
}
return (DI_WALK_CONTINUE);
}
/*
* The dynamic component buffer returned by this function has to be freed!
*/
int
{
int l_errno;
char minor_path[MAXPATHLEN];
char name_part[MAXNAMELEN];
char *minor_portion = NULL;
int deplen;
int err;
/*
* Get target node path
*/
(void) printf("cfga_list_ext: cannot locate target device\n");
return (CFGA_SATA_DYNAMIC_AP);
} else {
*cp = 0; /* terminate path for opendir() */
/*
* Using libdevinfo for this is overkill and kills
* performance when many consumers are using libcfgadm
* concurrently.
*/
goto bailout;
}
/*
* deplen is large enough to fit the largest path-
* struct dirent includes one byte (the terminator)
* so we don't add 1 to the calculation here.
*/
sizeof (struct dirent);
goto bailout;
continue;
*minor_portion = 0;
continue;
*minor_portion = *MINOR_SEP;
continue;
break;
}
/*
* If there was an error, or we didn't exit the loop
* by finding a block or character device, bail out.
*/
goto bailout;
/*
* since we ONLY looked for BLOCK devices above.
*/
(void) physpath_to_devlink("/dev/dsk",
/* postprocess and copy logical name here */
/*
* For disks, remove partition info
*/
}
}
return (SATA_CFGA_OK);
}
if (dp)
if (devpath)
if (dep)
return (CFGA_SATA_DYNAMIC_AP);
}
/* cfgadm entry point */
/*ARGSUSED*/
const char *ap_id,
int *nlistp,
const char *options,
const char *listopts,
char **errstring,
{
int l_errno;
char *pdyn;
goto bailout;
}
/* We do not care here about dynamic AP name component */
*pdyn = '\0';
}
goto bailout;
}
/* Get ap status */
DC_RDONLY)) != CFGA_SATA_OK) {
goto bailout;
}
/* will call dc_cmd to send IOCTL to kernel */
&devctl_ap_state) == -1) {
goto bailout;
}
/*
* Create cfga_list_data_t struct.
*/
if ((*ap_id_list =
goto bailout;
}
*nlistp = 1;
/*
* Rest of the code fills in the cfga_list_data_t struct.
*/
/* Remember ap_id_log must be freed */
if (rv != 0) {
goto bailout;
}
/* Get logical ap_id corresponding to the physical */
goto bailout;
}
sizeof ((*ap_id_list)->ap_log_id));
sizeof ((*ap_id_list)->ap_phys_id));
switch (devctl_ap_state.ap_rstate) {
case AP_RSTATE_EMPTY:
break;
case AP_RSTATE_DISCONNECTED:
break;
case AP_RSTATE_CONNECTED:
break;
default:
goto bailout;
}
switch (devctl_ap_state.ap_ostate) {
case AP_OSTATE_CONFIGURED:
break;
case AP_OSTATE_UNCONFIGURED:
break;
default:
goto bailout;
}
switch (devctl_ap_state.ap_condition) {
case AP_COND_OK:
break;
case AP_COND_FAILING:
break;
case AP_COND_FAILED:
break;
case AP_COND_UNUSABLE:
break;
case AP_COND_UNKNOWN:
break;
default:
goto bailout;
}
char *str_p;
int skip, i;
/*
* Fill in the 'Information' field for the -v option
* Model (MOD:)
*/
(void) printf(
"SATA_CFGA_GET_MODULE_INFO ioctl failed\n");
goto bailout;
}
/* drop leading and trailing spaces */
for (i = size - 1; i >= 0; i--) {
if (str_p[i] == '\040')
str_p[i] = '\0';
else if (str_p[i] != '\0')
break;
}
sizeof ((*ap_id_list)->ap_info));
sizeof ((*ap_id_list)->ap_info));
/*
* Fill in the 'Information' field for the -v option
* Firmware revision (FREV:)
*/
(void) printf(
"SATA_CFGA_GET_REVFIRMWARE_INFO ioctl failed\n");
goto bailout;
}
/* drop leading and trailing spaces */
for (i = size - 1; i >= 0; i--) {
if (str_p[i] == '\040')
str_p[i] = '\0';
else if (str_p[i] != '\0')
break;
}
sizeof ((*ap_id_list)->ap_info));
sizeof ((*ap_id_list)->ap_info));
/*
* Fill in the 'Information' field for the -v option
* Serial Number (SN:)
*/
(void) printf(
"SATA_CFGA_GET_SERIALNUMBER_INFO ioctl failed\n");
goto bailout;
}
/* drop leading and trailing spaces */
for (i = size - 1; i >= 0; i--) {
if (str_p[i] == '\040')
str_p[i] = '\0';
else if (str_p[i] != '\0')
break;
}
sizeof ((*ap_id_list)->ap_info));
sizeof ((*ap_id_list)->ap_info));
/* Fill in ap_type which is collected from HBA driver */
/* call do_control_ioctl TBD */
(void) printf(
"SATA_CFGA_GET_AP_TYPE ioctl failed\n");
goto bailout;
}
sizeof ((*ap_id_list)->ap_type));
/*
* This is the case where we need to generate
* a dynamic component of the ap_id, i.e. device.
*/
if (rv != CFGA_SATA_OK)
goto bailout;
DYN_SEP);
sizeof ((*ap_id_list)->ap_log_id));
}
}
} else {
/* Change it when port multiplier is supported */
sizeof ((*ap_id_list)->ap_type));
}
if (*ap_id_list != NULL) {
free(*ap_id_list);
}
}
}
/*
* This routine accepts a string adn prints it using
* the message print routine argument.
*/
static void
{
int len;
char *q;
(void) printf("cfga_msg: NULL msgp\n");
return;
}
(void) printf("cfga_msg: null str\n");
return;
}
perror("cfga_msg"
);
return;
}
free(q);
}
/* cfgadm entry point */
/* ARGSUSED */
{
}
return (CFGA_OK);
}
/*
* Ensure the ap_id passed is in the correct (physical ap_id) form:
* where xx is a one or two-digit number.
*
* Note the library always calls the plugin with a physical ap_id.
*/
static int
verify_valid_apid(const char *ap_id)
{
char *l_ap_id;
return (-1);
l_ap_id++;
/* Bad characters in the ap_id */
return (-1);
}
/* ap_id has 1..2 or more than 2 dots */
return (-1);
}
return (0);
}
/*
* Verify the params passed in are valid.
*/
static cfga_sata_ret_t
const char *ap_id,
const char *options,
char **errstring)
{
int rv;
}
return (CFGA_SATA_OPTIONS);
}
/* Strip dynamic AP name component if it is present. */
return (CFGA_SATA_ALLOC_FAIL);
}
*pdyn = '\0';
}
if (verify_valid_apid(lap_id) != 0) {
rv = CFGA_SATA_AP;
} else {
rv = CFGA_SATA_OK;
}
return (rv);
}
/*
* Takes a validated ap_id and extracts the port number.
* For now, we do not support port multiplier port .
*/
static cfga_sata_ret_t
{
char *port_nbr_str;
char *temp;
int pmport_qual = 0; /* port multiplier not supported yet */
}
errno = 0;
return (CFGA_SATA_PORT);
return (CFGA_SATA_OK);
}
/*
*/
static void
{
if (user_nvlist != NULL) {
}
if (devctl_hdl != NULL) {
}
}
static cfga_sata_ret_t
const char *ap_id,
{
return (CFGA_SATA_ALLOC_FAIL);
*pdyn = '\0';
}
/* Get a devctl handle to pass to the devctl_ap_XXX functions */
"setup_for_devctl_cmd: devctl_ap_acquire failed: %s\n",
goto bailout;
}
/* Set up nvlist to pass the port number down to the driver */
*user_nvlistp = NULL;
(void) printf("nvlist_alloc failed\n");
goto bailout;
}
/*
* Get port id, for Port Multiplier port, things could be a little bit
* complicated because of "port.port" format in ap_id, thus for
* port multiplier port, port number should be coded as 32bit int
* with the sig 16 bit as sata channel number, least 16 bit as
* the port number of sata port multiplier port.
*/
(void) printf(
"setup_for_devctl_cmd: get_port_num, errno: %d\n",
errno);
goto bailout;
}
/* Creates an int32_t entry */
(void) printf("nvlist_add_int32 failed\n");
goto bailout;
}
return (rv);
return (rv);
}
static cfga_sata_ret_t
{
return (CFGA_SATA_IOCTL);
}
return (CFGA_SATA_OK);
}
/*
* Given a subcommand to the DEVCTL_AP_CONTROL ioctl, rquest the size of
* the data to be returned, allocate a buffer, then get the data.
* Returns *descrp (which must be freed) and size.
*
* Note SATA_DESCR_TYPE_STRING returns an ASCII NULL-terminated string,
* not a string descr.
*/
{
int fd = -1;
struct sata_ioctl_data ioctl_data;
goto bailout;
}
(void) printf("do_control_ioctl: open failed: errno:%d\n",
errno);
rv = CFGA_SATA_OPEN;
rv = CFGA_SATA_BUSY;
}
goto bailout;
}
/*
* Find out how large a buf we need to get the data.
*/
if ((subcommand == SATA_CFGA_GET_AP_TYPE) ||
(subcommand == SATA_CFGA_GET_DEVICE_PATH) ||
(subcommand == SATA_CFGA_GET_MODEL_INFO) ||
perror("ioctl failed (size)");
goto bailout;
}
*sizep = local_size;
if (local_size == 0) {
(void) printf("zero length data\n");
goto bailout;
}
(void) printf("do_control_ioctl: malloc failed\n");
goto bailout;
}
} else {
*sizep = 0;
}
/* Execute IOCTL */
goto bailout;
}
return (rv);
if (fd != -1) {
}
}
rv = CFGA_SATA_BUSY;
}
return (rv);
}
static int
{
int rval;
return (0);
}
return (rval);
}
static char *
sata_get_devicepath(const char *ap_id)
{
if (rv == CFGA_SATA_OK) {
return (devpath);
} else {
return ((char *)NULL);
}
}