xref: /onnv-gate/usr/src/lib/cfgadm_plugins/sata/common/cfga_sata.c (revision 10318:811db323512d)
11258Smlf /*
21258Smlf  * CDDL HEADER START
31258Smlf  *
41258Smlf  * The contents of this file are subject to the terms of the
51258Smlf  * Common Development and Distribution License (the "License").
61258Smlf  * You may not use this file except in compliance with the License.
71258Smlf  *
81258Smlf  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91258Smlf  * or http://www.opensolaris.org/os/licensing.
101258Smlf  * See the License for the specific language governing permissions
111258Smlf  * and limitations under the License.
121258Smlf  *
131258Smlf  * When distributing Covered Code, include this CDDL HEADER in each
141258Smlf  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151258Smlf  * If applicable, add the following below this CDDL HEADER, with the
161258Smlf  * fields enclosed by brackets "[]" replaced with your own identifying
171258Smlf  * information: Portions Copyright [yyyy] [name of copyright owner]
181258Smlf  *
191258Smlf  * CDDL HEADER END
201258Smlf  */
211258Smlf 
221258Smlf /*
23*10318SXiao-Yu.Zhang@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241258Smlf  * Use is subject to license terms.
251258Smlf  */
261258Smlf 
272027Ssethg #include <sys/param.h>
282027Ssethg #include <sys/stat.h>
292027Ssethg #include <errno.h>
302027Ssethg #include <string.h>
312027Ssethg #include <dirent.h>
321258Smlf #include "cfga_sata.h"
331258Smlf 
341258Smlf /*
351258Smlf  * This file contains the entry points to the plug-in as defined in the
361258Smlf  * config_admin(3X) man page.
371258Smlf  */
381258Smlf 
391258Smlf /*
401258Smlf  * Set the version number for the cfgadm library's use.
411258Smlf  */
421258Smlf int cfga_version = CFGA_HSL_V2;
431258Smlf 
441258Smlf enum {
451258Smlf 	HELP_HEADER = 1,
461258Smlf 	HELP_CONFIG,
471258Smlf 	HELP_RESET_PORT,
481258Smlf 	HELP_RESET_DEVICE,
491258Smlf 	HELP_RESET_ALL,
501258Smlf 	HELP_PORT_DEACTIVATE,
511258Smlf 	HELP_PORT_ACTIVATE,
521258Smlf 	HELP_PORT_SELF_TEST,
531258Smlf 	HELP_CNTRL_SELF_TEST,
541258Smlf 	HELP_UNKNOWN
551258Smlf };
561258Smlf 
571258Smlf /* SATA specific help messages */
581258Smlf static char *sata_help[] = {
591258Smlf 	NULL,
601258Smlf 	"SATA specific commands:\n",
611258Smlf 	" cfgadm -c [configure|unconfigure|disconnect|connect] ap_id "
62*10318SXiao-Yu.Zhang@Sun.COM 	    "[ap_id...]\n",
631258Smlf 	" cfgadm -x sata_reset_port ap_id  [ap_id...]\n",
641258Smlf 	" cfgadm -x sata_reset_device ap_id [ap_id...]\n",
651258Smlf 	" cfgadm -x sata_reset_all ap_id\n",
661258Smlf 	" cfgadm -x sata_port_deactivate ap_id [ap_id...]\n",
671258Smlf 	" cfgadm -x sata_port_activate ap_id [ap_id...]\n",
681258Smlf 	" cfgadm -x sata_port_self_test ap_id [ap_id...]\n",
691258Smlf 	" cfgadm -t ap_id\n",
701258Smlf 	"\tunknown command or option:\n",
711258Smlf 	NULL
721258Smlf };	/* End help messages */
731258Smlf 
741258Smlf 
751258Smlf /*
761258Smlf  * Messages.
771258Smlf  */
781258Smlf static msgcvt_t sata_msgs[] = {
791258Smlf 	/* CFGA_SATA_OK */
801258Smlf 	{ CVT, CFGA_OK, "" },
811258Smlf 
821258Smlf 	/* CFGA_SATA_NACK */
831258Smlf 	{ CVT, CFGA_NACK, "" },
841258Smlf 
851258Smlf 	/* CFGA_SATA_DEVICE_UNCONFIGURED */
861258Smlf 	{ CVT, CFGA_OK, "Device unconfigured prior to disconnect" },
871258Smlf 
881258Smlf 	/* CFGA_SATA_UNKNOWN / CFGA_LIB_ERROR -> "Library error" */
891258Smlf 	{ CVT, CFGA_LIB_ERROR, "Unknown message; internal error" },
901258Smlf 
911258Smlf 	/* CFGA_SATA_INTERNAL_ERROR / CFGA_LIB_ERROR -> "Library error" */
921258Smlf 	{ CVT, CFGA_LIB_ERROR, "Internal error" },
931258Smlf 
941258Smlf 	/* CFGA_SATA_DATA_ERROR / CFGA_DATA_ERROR -> "Data error" */
951258Smlf 	{ CVT, CFGA_DATA_ERROR, "cfgadm data error" },
961258Smlf 
971258Smlf 	/* CFGA_SATA_OPTIONS / CFGA_ERROR -> "Hardware specific failure" */
981258Smlf 	{ CVT, CFGA_ERROR, "Hardware specific option not supported" },
991258Smlf 
1001258Smlf 	/* CFGA_SATA_HWOPNOTSUPP / CFGA_ERROR -> "Hardware specific failure" */
1011258Smlf 	{ CVT, CFGA_ERROR, "Hardware specific operation not supported" },
1021258Smlf 
1031258Smlf 	/*
1041258Smlf 	 * CFGA_SATA_DYNAMIC_AP /
1051258Smlf 	 * CFGA_LIB_ERROR -> "Configuration operation invalid"
1061258Smlf 	 */
1071258Smlf 	{ CVT, CFGA_INVAL, "Cannot identify attached device" },
1081258Smlf 
1091258Smlf 	/* CFGA_SATA_AP / CFGA_APID_NOEXIST -> "Attachment point not found" */
1101258Smlf 	{ CVT, CFGA_APID_NOEXIST, "" },
1111258Smlf 
1121258Smlf 	/* CFGA_SATA_PORT / CFGA_LIB_ERROR -> "Library error" */
1131258Smlf 	{ CVT, CFGA_LIB_ERROR, "Cannot determine sata port number for " },
1141258Smlf 
1151258Smlf 	/* CFGA_SATA_DEVCTL / CFGA_LIB_ERROR -> "Library error" */
1161258Smlf 	{ CVT, CFGA_LIB_ERROR, "Internal error: "
117*10318SXiao-Yu.Zhang@Sun.COM 		"Cannot allocate devctl handle " },
1181258Smlf 
1191258Smlf 	/*
1201258Smlf 	 * CFGA_SATA_DEV_CONFIGURE /
1211258Smlf 	 * CFGA_ERROR -> "Hardware specific failure"
1221258Smlf 	 */
1231258Smlf 	{ CVT, CFGA_ERROR, "Failed to config device at " },
1241258Smlf 
1251258Smlf 	/*
1261258Smlf 	 * CFGA_SATA_DEV_UNCONFIGURE /
1271258Smlf 	 * CFGA_ERROR -> "Hardware specific failure"
1281258Smlf 	 */
1291258Smlf 	{ CVT, CFGA_ERROR, "Failed to unconfig device at " },
1301258Smlf 
1311258Smlf 	/*
1321258Smlf 	 * CFGA_SATA_DISCONNECTED
1331258Smlf 	 * CFGA_INVAL -> "Configuration operation invalid"
1341258Smlf 	 */
1351258Smlf 	{ CVT, CFGA_INVAL, "Port already disconnected " },
1361258Smlf 
1371258Smlf 	/*
1381258Smlf 	 * CFGA_SATA_NOT_CONNECTED
1391258Smlf 	 * CFGA_INVAL -> "Configuration operation invalid"
1401258Smlf 	 */
1411258Smlf 	{ CVT, CFGA_INVAL, "No device connected to " },
1421258Smlf 
1431258Smlf 	/*
1441258Smlf 	 * CFGA_SATA_NOT_CONFIGURED /
1451258Smlf 	 * CFGA_INVAL -> "Configuration operation invalid"
1461258Smlf 	 */
1471258Smlf 	{ CVT, CFGA_INVAL, "No device configured at " },
1481258Smlf 
1491258Smlf 	/*
1501258Smlf 	 * CFGA_SATA_ALREADY_CONNECTED /
1511258Smlf 	 * CFGA_INVAL -> "Configuration operation invalid"
1521258Smlf 	 */
1531258Smlf 	{ CVT, CFGA_INVAL, "Device already connected to " },
1541258Smlf 
1551258Smlf 	/*
1561258Smlf 	 * CFGA_SATA_ALREADY_CONFIGURED /
1571258Smlf 	 * CFGA_INVAL -> "Configuration operation invalid"
1581258Smlf 	 */
1591258Smlf 	{ CVT, CFGA_INVAL, "Device already configured at " },
1601258Smlf 
1611258Smlf 	/*
1621258Smlf 	 * CFGA_SATA_INVALID_DEVNAME /
1631258Smlf 	 * CFGA_INVAL -> "Configuration operation invalid"
1641258Smlf 	 */
1651258Smlf 	{ CVT, CFGA_INVAL, "Cannot specify device name" },
1661258Smlf 
1671258Smlf 	/* CFGA_SATA_OPEN / CFGA_LIB_ERROR -> "Library error" */
1681258Smlf 	{ CVT, CFGA_LIB_ERROR, "Cannot open " },
1691258Smlf 
1701258Smlf 	/* CFGA_SATA_IOCTL / CFGA_ERROR -> "Hardware specific failure"  */
1711258Smlf 	{ CVT, CFGA_ERROR, "Driver ioctl failed " },
1721258Smlf 
1731258Smlf 	/*
1741258Smlf 	 * CFGA_SATA_BUSY /
1751258Smlf 	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
1761258Smlf 	 */
1771258Smlf 	{ CVT, CFGA_SYSTEM_BUSY, "" },
1781258Smlf 
1791258Smlf 	/* CFGA_SATA_ALLOC_FAIL / CFGA_LIB_ERROR -> "Library error" */
1801258Smlf 	{ CVT, CFGA_LIB_ERROR, "Memory allocation failure" },
1811258Smlf 
1821258Smlf 	/*
1831258Smlf 	 * CFGA_SATA_OPNOTSUPP /
1841258Smlf 	 * CFGA_OPNOTSUPP -> "Configuration operation not supported"
1851258Smlf 	 */
1861258Smlf 	{ CVT, CFGA_OPNOTSUPP, "Operation not supported" },
1871258Smlf 
1881258Smlf 	/* CFGA_SATA_DEVLINK / CFGA_LIB_ERROR -> "Library error" */
1891258Smlf 	{ CVT, CFGA_LIB_ERROR, "Could not find /dev/cfg link for " },
1901258Smlf 
1911258Smlf 	/* CFGA_SATA_STATE / CFGA_LIB_ERROR -> "Library error" */
1921258Smlf 	{ CVT, CFGA_LIB_ERROR, "Internal error: Unrecognized ap state" },
1931258Smlf 
1941258Smlf 	/* CFGA_SATA_PRIV / CFGA_PRIV -> "Insufficient privileges" */
1951258Smlf 	{ CVT, CFGA_PRIV, "" },
1961258Smlf 
1971258Smlf 	/* CFGA_SATA_NVLIST / CFGA_ERROR -> "Hardware specific failure" */
1981258Smlf 	{ CVT, CFGA_ERROR, "Internal error (nvlist)" },
1991258Smlf 
2001258Smlf 	/* CFGA_SATA_ZEROLEN / CFGA_ERROR -> "Hardware specific failure" */
2011258Smlf 	{ CVT, CFGA_ERROR, "Internal error (zerolength string)" },
2021258Smlf 
2031258Smlf 	/* CFGA_SATA_RCM_HANDLE / CFGA_ERROR -> "Hardware specific failure" */
2041258Smlf 	{ CVT, CFGA_ERROR, "cannot get RCM handle"},
2051258Smlf 
2061258Smlf 	/*
2071258Smlf 	 * CFGA_SATA_RCM_ONLINE /
2081258Smlf 	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
2091258Smlf 	 */
2101258Smlf 	{ CVT, CFGA_SYSTEM_BUSY, "failed to online: "},
2111258Smlf 
2121258Smlf 	/*
2131258Smlf 	 * CFGA_SATA_RCM_OFFLINE /
2141258Smlf 	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
2151258Smlf 	 */
2161258Smlf 	{ CVT, CFGA_SYSTEM_BUSY, "failed to offline: "},
2171258Smlf 
2181258Smlf 	/* CFGA_SATA_RCM_INFO / CFGA_ERROR -> "Hardware specific failure" */
2191258Smlf 	{ CVT, CFGA_ERROR, "failed to query: "}
2201258Smlf 
2211258Smlf };	/* End error messages */
2221258Smlf 
2231258Smlf static cfga_sata_ret_t
2241258Smlf verify_params(const char *ap_id, const char *options, char **errstring);
2251258Smlf 
2261258Smlf 
2271258Smlf static cfga_sata_ret_t
2281258Smlf setup_for_devctl_cmd(const char *ap_id, devctl_hdl_t *devctl_hdl,
229*10318SXiao-Yu.Zhang@Sun.COM     nvlist_t **user_nvlistp, uint_t oflag);
2301258Smlf 
2311258Smlf static cfga_sata_ret_t
2321258Smlf port_state(devctl_hdl_t hdl, nvlist_t *list,
233*10318SXiao-Yu.Zhang@Sun.COM     ap_rstate_t *rstate, ap_ostate_t *ostate);
2341258Smlf 
2351258Smlf static cfga_sata_ret_t
2361258Smlf do_control_ioctl(const char *ap_id, sata_cfga_apctl_t subcommand, uint_t arg,
237*10318SXiao-Yu.Zhang@Sun.COM     void **descrp, size_t *sizep);
2381258Smlf 
2391258Smlf static void
2401258Smlf cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl, nvlist_t *user_nvlist);
2411258Smlf 
2421258Smlf static char *
2431258Smlf sata_get_devicepath(const char *ap_id);
2441258Smlf 
2451258Smlf static int
2461258Smlf sata_confirm(struct cfga_confirm *confp, char *msg);
2471258Smlf 
248*10318SXiao-Yu.Zhang@Sun.COM static cfga_sata_ret_t
249*10318SXiao-Yu.Zhang@Sun.COM get_port_num(const char *ap_id, uint32_t *port);
2501258Smlf 
2511258Smlf /* Utilities */
2521258Smlf 
2532027Ssethg static cfga_sata_ret_t
physpath_to_devlink(const char * basedir,const char * node_path,char ** logpp,int * l_errnop)2542027Ssethg physpath_to_devlink(const char *basedir, const char *node_path,
2552027Ssethg     char **logpp, int *l_errnop)
2561258Smlf {
2572027Ssethg 	char *linkpath;
2582027Ssethg 	char *buf;
2592027Ssethg 	char *real_path;
2602027Ssethg 	DIR *dp;
2612027Ssethg 	struct dirent *dep, *newdep;
2622027Ssethg 	int deplen;
2632027Ssethg 	boolean_t found = B_FALSE;
2642027Ssethg 	int err = 0;
2652027Ssethg 	struct stat sb;
2662027Ssethg 	char *p;
2672027Ssethg 	cfga_sata_ret_t rv = CFGA_SATA_INTERNAL_ERROR;
2681258Smlf 
2691258Smlf 	/*
2702027Ssethg 	 * Using libdevinfo for this is overkill and kills performance
2712027Ssethg 	 * when multiple consumers of libcfgadm are executing
2722027Ssethg 	 * concurrently.
2731258Smlf 	 */
2742027Ssethg 	if ((dp = opendir(basedir)) == NULL) {
2752027Ssethg 		*l_errnop = errno;
2762027Ssethg 		return (CFGA_SATA_INTERNAL_ERROR);
2772027Ssethg 	}
2782027Ssethg 
2792027Ssethg 	linkpath = malloc(PATH_MAX);
2802027Ssethg 	buf = malloc(PATH_MAX);
2812027Ssethg 	real_path = malloc(PATH_MAX);
2822027Ssethg 
2833019Ssethg 	deplen = pathconf(basedir, _PC_NAME_MAX);
2843019Ssethg 	deplen = (deplen <= 0 ? MAXNAMELEN : deplen) +
2852027Ssethg 	    sizeof (struct dirent);
2862027Ssethg 	dep = (struct dirent *)malloc(deplen);
2872027Ssethg 
2882027Ssethg 	if (dep == NULL || linkpath == NULL || buf == NULL ||
2892027Ssethg 	    real_path == NULL) {
2902027Ssethg 		*l_errnop = ENOMEM;
2912027Ssethg 		rv = CFGA_SATA_ALLOC_FAIL;
2922027Ssethg 		goto pp_cleanup;
2932027Ssethg 	}
2942027Ssethg 
2952027Ssethg 	*logpp = NULL;
2962027Ssethg 
2972027Ssethg 	while (!found && (err = readdir_r(dp, dep, &newdep)) == 0 &&
2982027Ssethg 	    newdep != NULL) {
2992027Ssethg 
3002027Ssethg 		assert(newdep == dep);
3012027Ssethg 
3022027Ssethg 		if (strcmp(dep->d_name, ".") == 0 ||
3032027Ssethg 		    strcmp(dep->d_name, "..") == 0)
3042027Ssethg 			continue;
3052027Ssethg 
3062027Ssethg 		(void) snprintf(linkpath, MAXPATHLEN,
3072027Ssethg 		    "%s/%s", basedir, dep->d_name);
3082027Ssethg 
3092027Ssethg 		if (lstat(linkpath, &sb) < 0)
3102027Ssethg 			continue;
3111258Smlf 
3122027Ssethg 		if (S_ISDIR(sb.st_mode)) {
3132027Ssethg 
3142027Ssethg 			if ((rv = physpath_to_devlink(linkpath, node_path,
3152027Ssethg 			    logpp, l_errnop)) != CFGA_SATA_OK) {
3162027Ssethg 
3172027Ssethg 				goto pp_cleanup;
3182027Ssethg 			}
3192027Ssethg 
3202027Ssethg 			if (*logpp != NULL)
3212027Ssethg 				found = B_TRUE;
3222027Ssethg 
3232027Ssethg 		} else if (S_ISLNK(sb.st_mode)) {
3242027Ssethg 
3252027Ssethg 			bzero(buf, PATH_MAX);
3262027Ssethg 			if (readlink(linkpath, buf, PATH_MAX) < 0)
3272027Ssethg 				continue;
3282027Ssethg 
3291258Smlf 
3302027Ssethg 			/*
3312027Ssethg 			 * realpath() is too darn slow, so fake
3322027Ssethg 			 * it, by using what we know about /dev
3332027Ssethg 			 * links: they are always of the form:
3342027Ssethg 			 * <"../">+/devices/<path>
3352027Ssethg 			 */
3362027Ssethg 			p = buf;
3372027Ssethg 			while (strncmp(p, "../", 3) == 0)
3382027Ssethg 				p += 3;
3392027Ssethg 
3402027Ssethg 			if (p != buf)
3412027Ssethg 				p--;	/* back up to get a slash */
3422027Ssethg 
3432027Ssethg 			assert (*p == '/');
3442027Ssethg 
3452027Ssethg 			if (strcmp(p, node_path) == 0) {
3462027Ssethg 				*logpp = strdup(linkpath);
3472027Ssethg 				if (*logpp == NULL) {
3482027Ssethg 
3492027Ssethg 					rv = CFGA_SATA_ALLOC_FAIL;
3502027Ssethg 					goto pp_cleanup;
3512027Ssethg 				}
3522027Ssethg 
3532027Ssethg 				found = B_TRUE;
3542027Ssethg 			}
3551258Smlf 		}
3561258Smlf 	}
3571258Smlf 
3582027Ssethg 	free(linkpath);
3592027Ssethg 	free(buf);
3602027Ssethg 	free(real_path);
3612027Ssethg 	free(dep);
3622027Ssethg 	(void) closedir(dp);
3631258Smlf 
3642027Ssethg 	if (err != 0) {
3652027Ssethg 		*l_errnop = err;
3662027Ssethg 		return (CFGA_SATA_INTERNAL_ERROR);
3671258Smlf 	}
3681258Smlf 
3692027Ssethg 	return (CFGA_SATA_OK);
3702027Ssethg 
3712027Ssethg pp_cleanup:
3722027Ssethg 
3732027Ssethg 	if (dp)
3742027Ssethg 		(void) closedir(dp);
3752027Ssethg 	if (dep)
3762027Ssethg 		free(dep);
3772027Ssethg 	if (linkpath)
3782027Ssethg 		free(linkpath);
3792027Ssethg 	if (buf)
3802027Ssethg 		free(buf);
3812027Ssethg 	if (real_path)
3822027Ssethg 		free(real_path);
3832027Ssethg 	if (*logpp) {
3842027Ssethg 		free(*logpp);
3852027Ssethg 		*logpp = NULL;
3861258Smlf 	}
3872027Ssethg 	return (rv);
3881258Smlf }
3891258Smlf 
3901258Smlf 
3911258Smlf /*
3921258Smlf  * Given the index into a table (msgcvt_t) of messages, get the message
3931258Smlf  * string, converting it to the proper locale if necessary.
3941258Smlf  * NOTE: Indexes are defined in cfga_sata.h
3951258Smlf  */
3961258Smlf static const char *
get_msg(uint_t msg_index,msgcvt_t * msg_tbl,uint_t tbl_size)3971258Smlf get_msg(uint_t msg_index, msgcvt_t *msg_tbl, uint_t tbl_size)
3981258Smlf {
3991258Smlf 	if (msg_index >= tbl_size) {
4001258Smlf 		msg_index = CFGA_SATA_UNKNOWN;
4011258Smlf 	}
4021258Smlf 
4031258Smlf 	return ((msg_tbl[msg_index].intl) ?
404*10318SXiao-Yu.Zhang@Sun.COM 	    dgettext(TEXT_DOMAIN, msg_tbl[msg_index].msgstr) :
405*10318SXiao-Yu.Zhang@Sun.COM 	    msg_tbl[msg_index].msgstr);
4061258Smlf }
4071258Smlf 
4081258Smlf /*
4091258Smlf  * Allocates and creates a message string (in *ret_str),
4101258Smlf  * by concatenating all the (char *) args together, in order.
4111258Smlf  * Last arg MUST be NULL.
4121258Smlf  */
4131258Smlf static void
set_msg(char ** ret_str,...)4141258Smlf set_msg(char **ret_str, ...)
4151258Smlf {
4161258Smlf 	char    *str;
4171258Smlf 	size_t  total_len;
4181258Smlf 	va_list valist;
4191258Smlf 
4201258Smlf 	va_start(valist, ret_str);
4211258Smlf 
4221258Smlf 	total_len = (*ret_str == NULL) ? 0 : strlen(*ret_str);
4231258Smlf 
4241258Smlf 	while ((str = va_arg(valist, char *)) != NULL) {
4251258Smlf 		size_t  len = strlen(str);
4261258Smlf 		char    *old_str = *ret_str;
4271258Smlf 
4281258Smlf 		*ret_str = (char *)realloc(*ret_str, total_len + len + 1);
4291258Smlf 		if (*ret_str == NULL) {
4301258Smlf 			/* We're screwed */
4311258Smlf 			free(old_str);
4321258Smlf 			va_end(valist);
4331258Smlf 			return;
4341258Smlf 		}
4351258Smlf 
4361258Smlf 		(void) strcpy(*ret_str + total_len, str);
4371258Smlf 		total_len += len;
4381258Smlf 	}
4391258Smlf 
4401258Smlf 	va_end(valist);
4411258Smlf }
4421258Smlf 
4431258Smlf /*
4441258Smlf  * Error message handling.
4451258Smlf  * For the rv passed in, looks up the corresponding error message string(s),
4461258Smlf  * internationalized if necessary, and concatenates it into a new
4471258Smlf  * memory buffer, and points *errstring to it.
4481258Smlf  * Note not all rvs will result in an error message return, as not all
4491258Smlf  * error conditions warrant a SATA-specific error message - for those
4501258Smlf  * conditions the cfgadm generic messages are sufficient.
4511258Smlf  *
4521258Smlf  * Some messages may display ap_id or errno, which is why they are passed
4531258Smlf  * in.
4541258Smlf  */
4551258Smlf 
4561258Smlf cfga_err_t
sata_err_msg(char ** errstring,cfga_sata_ret_t rv,const char * ap_id,int l_errno)4571258Smlf sata_err_msg(
458*10318SXiao-Yu.Zhang@Sun.COM     char **errstring,
459*10318SXiao-Yu.Zhang@Sun.COM     cfga_sata_ret_t rv,
460*10318SXiao-Yu.Zhang@Sun.COM     const char *ap_id,
461*10318SXiao-Yu.Zhang@Sun.COM     int l_errno)
4621258Smlf {
4631258Smlf 	if (errstring == NULL) {
4641258Smlf 		return (sata_msgs[rv].cfga_err);
4651258Smlf 	}
4661258Smlf 
4671258Smlf 	/*
4681258Smlf 	 * Generate the appropriate SATA-specific error message(s) (if any).
4691258Smlf 	 */
4701258Smlf 	switch (rv) {
4711258Smlf 	case CFGA_SATA_OK:
4721258Smlf 	case CFGA_NACK:
4731258Smlf 		/* Special case - do nothing.  */
4741258Smlf 		break;
4751258Smlf 
4761258Smlf 	case CFGA_SATA_UNKNOWN:
4771258Smlf 	case CFGA_SATA_DYNAMIC_AP:
4781258Smlf 	case CFGA_SATA_INTERNAL_ERROR:
4791258Smlf 	case CFGA_SATA_OPTIONS:
4801258Smlf 	case CFGA_SATA_ALLOC_FAIL:
4811258Smlf 	case CFGA_SATA_STATE:
4821258Smlf 	case CFGA_SATA_PRIV:
4831258Smlf 	case CFGA_SATA_OPNOTSUPP:
4841258Smlf 	case CFGA_SATA_DATA_ERROR:
4851258Smlf 		/* These messages require no additional strings passed. */
4861258Smlf 		set_msg(errstring, ERR_STR(rv), NULL);
4871258Smlf 		break;
4881258Smlf 
4891258Smlf 	case CFGA_SATA_HWOPNOTSUPP:
4901258Smlf 		/* hardware-specific help needed */
4911258Smlf 		set_msg(errstring, ERR_STR(rv), NULL);
4921258Smlf 		set_msg(errstring, "\n",
493*10318SXiao-Yu.Zhang@Sun.COM 		    dgettext(TEXT_DOMAIN, sata_help[HELP_HEADER]), NULL);
4941258Smlf 		set_msg(errstring, sata_help[HELP_RESET_PORT], NULL);
4951258Smlf 		set_msg(errstring, sata_help[HELP_RESET_DEVICE], NULL);
4961258Smlf 		set_msg(errstring, sata_help[HELP_RESET_ALL],  NULL);
4971258Smlf 		set_msg(errstring, sata_help[HELP_PORT_ACTIVATE], NULL);
4981258Smlf 		set_msg(errstring, sata_help[HELP_PORT_DEACTIVATE], NULL);
4991258Smlf 		set_msg(errstring, sata_help[HELP_PORT_SELF_TEST], NULL);
5001258Smlf 		set_msg(errstring, sata_help[HELP_CNTRL_SELF_TEST], NULL);
5011258Smlf 		break;
5021258Smlf 
5031258Smlf 	case CFGA_SATA_AP:
5041258Smlf 	case CFGA_SATA_PORT:
5051258Smlf 	case CFGA_SATA_NOT_CONNECTED:
5061258Smlf 	case CFGA_SATA_NOT_CONFIGURED:
5071258Smlf 	case CFGA_SATA_ALREADY_CONNECTED:
5081258Smlf 	case CFGA_SATA_ALREADY_CONFIGURED:
5091258Smlf 	case CFGA_SATA_BUSY:
5101258Smlf 	case CFGA_SATA_DEVLINK:
5111258Smlf 	case CFGA_SATA_RCM_HANDLE:
5121258Smlf 	case CFGA_SATA_RCM_ONLINE:
5131258Smlf 	case CFGA_SATA_RCM_OFFLINE:
5141258Smlf 	case CFGA_SATA_RCM_INFO:
5151258Smlf 	case CFGA_SATA_DEV_CONFIGURE:
5161258Smlf 	case CFGA_SATA_DEV_UNCONFIGURE:
5171258Smlf 	case CFGA_SATA_DISCONNECTED:
5181258Smlf 		/* These messages also print ap_id.  */
5191258Smlf 		set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "", NULL);
5201258Smlf 		break;
5211258Smlf 
5221258Smlf 
5231258Smlf 	case CFGA_SATA_IOCTL:
5241258Smlf 	case CFGA_SATA_NVLIST:
5251258Smlf 		/* These messages also print errno.  */
5261258Smlf 		{
5271258Smlf 			char *errno_str = l_errno ? strerror(l_errno) : "";
5281258Smlf 
5291258Smlf 			set_msg(errstring, ERR_STR(rv), errno_str,
5301258Smlf 			    l_errno ? "\n" : "", NULL);
5311258Smlf 			break;
5321258Smlf 		}
5331258Smlf 
5341258Smlf 	case CFGA_SATA_OPEN:
5351258Smlf 		/* These messages also apid and errno.  */
5361258Smlf 		{
5371258Smlf 			char *errno_str = l_errno ? strerror(l_errno) : "";
5381258Smlf 
5391258Smlf 			set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "\n",
5401258Smlf 			    errno_str, l_errno ? "\n" : "", NULL);
5411258Smlf 			break;
5421258Smlf 		}
5431258Smlf 
5441258Smlf 	default:
5451258Smlf 		set_msg(errstring, ERR_STR(CFGA_SATA_INTERNAL_ERROR), NULL);
5461258Smlf 
5471258Smlf 	} /* end switch */
5481258Smlf 
5491258Smlf 
5501258Smlf 	/*
5511258Smlf 	 * Determine the proper error code to send back to the cfgadm library.
5521258Smlf 	 */
5531258Smlf 	return (sata_msgs[rv].cfga_err);
5541258Smlf }
5551258Smlf 
5561258Smlf 
5571258Smlf /*
5581258Smlf  * Entry points
5591258Smlf  */
5601258Smlf /* cfgadm entry point */
5611258Smlf /*ARGSUSED*/
5621258Smlf cfga_err_t
cfga_change_state(cfga_cmd_t state_change_cmd,const char * ap_id,const char * options,struct cfga_confirm * confp,struct cfga_msg * msgp,char ** errstring,cfga_flags_t flags)5631258Smlf cfga_change_state(
564*10318SXiao-Yu.Zhang@Sun.COM     cfga_cmd_t state_change_cmd,
565*10318SXiao-Yu.Zhang@Sun.COM     const char *ap_id,
566*10318SXiao-Yu.Zhang@Sun.COM     const char *options,
567*10318SXiao-Yu.Zhang@Sun.COM     struct cfga_confirm *confp,
568*10318SXiao-Yu.Zhang@Sun.COM     struct cfga_msg *msgp,
569*10318SXiao-Yu.Zhang@Sun.COM     char **errstring,
570*10318SXiao-Yu.Zhang@Sun.COM     cfga_flags_t flags)
5711258Smlf {
5721258Smlf 	int		ret;
5731258Smlf 	int 		len;
5741258Smlf 	char		*msg;
5751258Smlf 	char		*devpath;
5761258Smlf 	nvlist_t	*nvl = NULL;
5771258Smlf 	ap_rstate_t	rstate;
5781258Smlf 	ap_ostate_t	ostate;
5791258Smlf 	devctl_hdl_t	hdl = NULL;
5801258Smlf 	cfga_sata_ret_t	rv = CFGA_SATA_OK;
5811258Smlf 	char		*pdyn;
582*10318SXiao-Yu.Zhang@Sun.COM 	char		*str_type;
583*10318SXiao-Yu.Zhang@Sun.COM 	size_t		size;
584*10318SXiao-Yu.Zhang@Sun.COM 	boolean_t	pmult = B_FALSE;
5851258Smlf 
5861258Smlf 	/*
5871258Smlf 	 * All sub-commands which can change state of device require
5881258Smlf 	 * root privileges.
5891258Smlf 	 */
5901258Smlf 	if (geteuid() != 0) {
5911258Smlf 		rv = CFGA_SATA_PRIV;
5921258Smlf 		goto bailout;
5931258Smlf 	}
5941258Smlf 
5951258Smlf 	if ((rv = verify_params(ap_id, options, errstring)) != CFGA_SATA_OK) {
5961258Smlf 		(void) cfga_help(msgp, options, flags);
5971258Smlf 		goto bailout;
5981258Smlf 	}
5991258Smlf 
6001258Smlf 	if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &nvl,
601*10318SXiao-Yu.Zhang@Sun.COM 	    DC_RDONLY)) != CFGA_SATA_OK) {
6021258Smlf 		goto bailout;
6031258Smlf 	}
6041258Smlf 
605*10318SXiao-Yu.Zhang@Sun.COM 	/*
606*10318SXiao-Yu.Zhang@Sun.COM 	 * Checking device type. A port multiplier is not configurable - it is
607*10318SXiao-Yu.Zhang@Sun.COM 	 * already configured as soon as it is connected.
608*10318SXiao-Yu.Zhang@Sun.COM 	 */
609*10318SXiao-Yu.Zhang@Sun.COM 	if ((rv = do_control_ioctl(ap_id, SATA_CFGA_GET_AP_TYPE, NULL,
610*10318SXiao-Yu.Zhang@Sun.COM 	    (void **)&str_type, &size)) != CFGA_SATA_OK) {
611*10318SXiao-Yu.Zhang@Sun.COM 		/* no such deivce */
612*10318SXiao-Yu.Zhang@Sun.COM 		goto bailout;
613*10318SXiao-Yu.Zhang@Sun.COM 	}
614*10318SXiao-Yu.Zhang@Sun.COM 	if (strncmp(str_type, "sata-pmult", sizeof ("sata-pmult")) == 0) {
615*10318SXiao-Yu.Zhang@Sun.COM 		pmult = B_TRUE;
616*10318SXiao-Yu.Zhang@Sun.COM 	}
617*10318SXiao-Yu.Zhang@Sun.COM 
6181258Smlf 	switch (state_change_cmd) {
6191258Smlf 	case CFGA_CMD_CONFIGURE:
620*10318SXiao-Yu.Zhang@Sun.COM 		if (pmult == B_TRUE) {
621*10318SXiao-Yu.Zhang@Sun.COM 			rv = CFGA_SATA_HWOPNOTSUPP;
622*10318SXiao-Yu.Zhang@Sun.COM 			goto bailout;
623*10318SXiao-Yu.Zhang@Sun.COM 		}
624*10318SXiao-Yu.Zhang@Sun.COM 
6251258Smlf 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
6261258Smlf 		    CFGA_SATA_OK)
6271258Smlf 			goto bailout;
6281258Smlf 
6291258Smlf 		if (ostate == AP_OSTATE_CONFIGURED) {
6301258Smlf 			rv = CFGA_SATA_ALREADY_CONFIGURED;
6311258Smlf 			goto bailout;
6321258Smlf 		}
6331258Smlf 		/* Disallow dynamic AP name component */
6341258Smlf 		if (GET_DYN(ap_id) != NULL) {
6351258Smlf 			rv = CFGA_SATA_INVALID_DEVNAME;
6361258Smlf 			goto bailout;
6371258Smlf 		}
6381258Smlf 
6391258Smlf 		if (rstate == AP_RSTATE_EMPTY) {
6401258Smlf 			rv = CFGA_SATA_NOT_CONNECTED;
6411258Smlf 			goto bailout;
6421258Smlf 		}
6431258Smlf 		rv = CFGA_SATA_OK;
6441258Smlf 
6451258Smlf 		if (devctl_ap_configure(hdl, nvl) != 0) {
6461258Smlf 			rv = CFGA_SATA_DEV_CONFIGURE;
6471258Smlf 			goto bailout;
6481258Smlf 		}
6491258Smlf 
6501258Smlf 		devpath = sata_get_devicepath(ap_id);
6511258Smlf 		if (devpath == NULL) {
6521258Smlf 			int i;
6531258Smlf 			/*
6541258Smlf 			 * Try for some time as SATA hotplug thread
6551258Smlf 			 * takes a while to create the path then
6561258Smlf 			 * eventually give up.
6571258Smlf 			 */
6581258Smlf 			for (i = 0; i < 12 && (devpath == NULL); i++) {
6591258Smlf 				(void) sleep(6);
6601258Smlf 				devpath = sata_get_devicepath(ap_id);
6611258Smlf 			}
6621258Smlf 
6631258Smlf 			if (devpath == NULL) {
6641258Smlf 				rv = CFGA_SATA_DEV_CONFIGURE;
6651258Smlf 				break;
6661258Smlf 			}
6671258Smlf 		}
6681258Smlf 
6691258Smlf 		S_FREE(devpath);
6701258Smlf 		break;
6711258Smlf 
6721258Smlf 	case CFGA_CMD_UNCONFIGURE:
673*10318SXiao-Yu.Zhang@Sun.COM 		if (pmult == B_TRUE) {
674*10318SXiao-Yu.Zhang@Sun.COM 			rv = CFGA_SATA_HWOPNOTSUPP;
675*10318SXiao-Yu.Zhang@Sun.COM 			goto bailout;
676*10318SXiao-Yu.Zhang@Sun.COM 		}
677*10318SXiao-Yu.Zhang@Sun.COM 
6781258Smlf 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
6791258Smlf 		    CFGA_SATA_OK)
6801258Smlf 			goto bailout;
6811258Smlf 
6821258Smlf 		if (rstate != AP_RSTATE_CONNECTED) {
6831258Smlf 			rv = CFGA_SATA_NOT_CONNECTED;
6841258Smlf 			goto bailout;
6851258Smlf 		}
6861258Smlf 
6871258Smlf 		if (ostate != AP_OSTATE_CONFIGURED) {
6881258Smlf 			rv = CFGA_SATA_NOT_CONFIGURED;
6891258Smlf 			goto bailout;
6901258Smlf 		}
6911258Smlf 		/* Strip off AP name dynamic component, if present */
6921258Smlf 		if ((pdyn = GET_DYN(ap_id)) != NULL) {
6931258Smlf 			*pdyn = '\0';
6941258Smlf 		}
6951258Smlf 
6961258Smlf 		rv = CFGA_SATA_OK;
6971258Smlf 
6981258Smlf 		len = strlen(SATA_CONFIRM_DEVICE) +
699*10318SXiao-Yu.Zhang@Sun.COM 		    strlen(SATA_CONFIRM_DEVICE_SUSPEND) +
700*10318SXiao-Yu.Zhang@Sun.COM 		    strlen("Unconfigure") + strlen(ap_id);
7011258Smlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
7021258Smlf 			(void) snprintf(msg, len + 3, "Unconfigure"
703*10318SXiao-Yu.Zhang@Sun.COM 			    " %s%s\n%s",
704*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_DEVICE, ap_id,
705*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_DEVICE_SUSPEND);
7061258Smlf 		}
7071258Smlf 
7081258Smlf 		if (!sata_confirm(confp, msg)) {
7091258Smlf 			free(msg);
7101258Smlf 			rv = CFGA_SATA_NACK;
7111258Smlf 			break;
7121258Smlf 		}
7131258Smlf 		free(msg);
7141258Smlf 
7151258Smlf 		devpath = sata_get_devicepath(ap_id);
7161258Smlf 		if (devpath == NULL) {
7171258Smlf 			(void) printf(
718*10318SXiao-Yu.Zhang@Sun.COM 			    "cfga_change_state: get device path failed\n");
7191258Smlf 			rv = CFGA_SATA_DEV_UNCONFIGURE;
7201258Smlf 			break;
7211258Smlf 		}
7221258Smlf 
7231258Smlf 		if ((rv = sata_rcm_offline(ap_id, errstring, devpath, flags))
7241258Smlf 		    != CFGA_SATA_OK) {
7251258Smlf 			break;
7261258Smlf 		}
7271258Smlf 
7281258Smlf 		ret = devctl_ap_unconfigure(hdl, nvl);
7291258Smlf 
7301258Smlf 		if (ret != 0) {
7311258Smlf 			rv = CFGA_SATA_DEV_UNCONFIGURE;
7321258Smlf 			if (errno == EBUSY) {
7331258Smlf 				rv = CFGA_SATA_BUSY;
7341258Smlf 			}
7351258Smlf 			(void) sata_rcm_online(ap_id, errstring, devpath,
736*10318SXiao-Yu.Zhang@Sun.COM 			    flags);
7371258Smlf 		} else {
7381258Smlf 			(void) sata_rcm_remove(ap_id, errstring, devpath,
739*10318SXiao-Yu.Zhang@Sun.COM 			    flags);
7401258Smlf 
7411258Smlf 		}
7421258Smlf 		S_FREE(devpath);
7431258Smlf 
7441258Smlf 		break;
7451258Smlf 
7461258Smlf 	case CFGA_CMD_DISCONNECT:
7471258Smlf 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
7481258Smlf 		    CFGA_SATA_OK)
7491258Smlf 			goto bailout;
7501258Smlf 
7511258Smlf 		if (rstate == AP_RSTATE_DISCONNECTED) {
7521258Smlf 			rv = CFGA_SATA_DISCONNECTED;
7531258Smlf 			goto bailout;
7541258Smlf 		}
7551258Smlf 
7561258Smlf 		/* Strip off AP name dynamic component, if present */
7571258Smlf 		if ((pdyn = GET_DYN(ap_id)) != NULL) {
7581258Smlf 			*pdyn = '\0';
7591258Smlf 		}
7601258Smlf 
7611258Smlf 
7621258Smlf 		rv = CFGA_SATA_OK; /* other statuses don't matter */
7631258Smlf 
7641258Smlf 		/*
7651258Smlf 		 * If the port originally with device attached and was
7661258Smlf 		 * unconfigured already, the devicepath for the sd will be
7671258Smlf 		 * removed. sata_get_devicepath in this case is not necessary.
7681258Smlf 		 */
7691258Smlf 		/* only call rcm_offline if the state was CONFIGURED */
770*10318SXiao-Yu.Zhang@Sun.COM 		if (ostate == AP_OSTATE_CONFIGURED &&
771*10318SXiao-Yu.Zhang@Sun.COM 		    pmult == B_FALSE) {
7721258Smlf 			devpath = sata_get_devicepath(ap_id);
7731258Smlf 			if (devpath == NULL) {
7741258Smlf 				(void) printf(
7751258Smlf 				    "cfga_change_state: get path failed\n");
7761258Smlf 				rv = CFGA_SATA_DEV_UNCONFIGURE;
7771258Smlf 				break;
7781258Smlf 			}
7791258Smlf 
7801258Smlf 			len = strlen(SATA_CONFIRM_DEVICE) +
781*10318SXiao-Yu.Zhang@Sun.COM 			    strlen(SATA_CONFIRM_DEVICE_SUSPEND) +
782*10318SXiao-Yu.Zhang@Sun.COM 			    strlen("Disconnect") + strlen(ap_id);
7831258Smlf 			if ((msg = (char *)calloc(len +3, 1)) != NULL) {
7841258Smlf 				(void) snprintf(msg, len + 3,
785*10318SXiao-Yu.Zhang@Sun.COM 				    "Disconnect"
786*10318SXiao-Yu.Zhang@Sun.COM 				    " %s%s\n%s",
787*10318SXiao-Yu.Zhang@Sun.COM 				    SATA_CONFIRM_DEVICE, ap_id,
788*10318SXiao-Yu.Zhang@Sun.COM 				    SATA_CONFIRM_DEVICE_SUSPEND);
7891258Smlf 			}
7901258Smlf 			if (!sata_confirm(confp, msg)) {
7911258Smlf 				free(msg);
7921258Smlf 				rv = CFGA_SATA_NACK;
7931258Smlf 				break;
7941258Smlf 			}
7951258Smlf 			free(msg);
7961258Smlf 
7971258Smlf 			if ((rv = sata_rcm_offline(ap_id, errstring,
7981258Smlf 			    devpath, flags)) != CFGA_SATA_OK) {
7991258Smlf 				break;
8001258Smlf 			}
8011258Smlf 
8021258Smlf 			ret = devctl_ap_unconfigure(hdl, nvl);
8031258Smlf 			if (ret != 0) {
8041258Smlf 				(void) printf(
8051258Smlf 				    "devctl_ap_unconfigure failed\n");
8061258Smlf 				rv = CFGA_SATA_DEV_UNCONFIGURE;
8071258Smlf 				if (errno == EBUSY)
8081258Smlf 					rv = CFGA_SATA_BUSY;
8091258Smlf 				(void) sata_rcm_online(ap_id, errstring,
810*10318SXiao-Yu.Zhang@Sun.COM 				    devpath, flags);
8111258Smlf 				S_FREE(devpath);
8121258Smlf 
8131258Smlf 				/*
8141258Smlf 				 * The current policy is that if unconfigure
8151258Smlf 				 * failed, do not continue with disconnect.
8161258Smlf 				 * If the port needs to be forced into the
8171258Smlf 				 * disconnect (shutdown) state,
8181258Smlf 				 * the -x sata_port_poweroff command should be
8191258Smlf 				 * used instead of -c disconnect
8201258Smlf 				 */
8211258Smlf 				break;
8221258Smlf 			} else {
8231258Smlf 				(void) printf("%s\n",
8241258Smlf 				    ERR_STR(CFGA_SATA_DEVICE_UNCONFIGURED));
8251258Smlf 				(void) sata_rcm_remove(ap_id, errstring,
826*10318SXiao-Yu.Zhang@Sun.COM 				    devpath, flags);
8271258Smlf 			}
8281258Smlf 			S_FREE(devpath);
8291258Smlf 		} else if (rstate == AP_RSTATE_CONNECTED ||
8301258Smlf 		    rstate == AP_RSTATE_EMPTY) {
8311258Smlf 			len = strlen(SATA_CONFIRM_PORT) +
832*10318SXiao-Yu.Zhang@Sun.COM 			    strlen(SATA_CONFIRM_PORT_DISABLE) +
833*10318SXiao-Yu.Zhang@Sun.COM 			    strlen("Deactivate Port") + strlen(ap_id);
8341258Smlf 			if ((msg = (char *)calloc(len +3, 1)) != NULL) {
8351258Smlf 				(void) snprintf(msg, len +3,
836*10318SXiao-Yu.Zhang@Sun.COM 				    "Disconnect"
837*10318SXiao-Yu.Zhang@Sun.COM 				    " %s%s\n%s",
838*10318SXiao-Yu.Zhang@Sun.COM 				    SATA_CONFIRM_PORT, ap_id,
839*10318SXiao-Yu.Zhang@Sun.COM 				    SATA_CONFIRM_PORT_DISABLE);
8401258Smlf 			}
8411258Smlf 			if (!sata_confirm(confp, msg)) {
8421258Smlf 				free(msg);
8431258Smlf 				rv = CFGA_SATA_NACK;
8441258Smlf 				break;
8451258Smlf 			}
8461258Smlf 		}
8471258Smlf 		ret = devctl_ap_disconnect(hdl, nvl);
8481258Smlf 		if (ret != 0) {
8491258Smlf 			rv = CFGA_SATA_IOCTL;
8501258Smlf 			if (errno == EBUSY) {
8511258Smlf 				rv = CFGA_SATA_BUSY;
8521258Smlf 			}
8531258Smlf 		}
8541258Smlf 		break;
8551258Smlf 
8561258Smlf 	case CFGA_CMD_CONNECT:
8571258Smlf 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
8581258Smlf 		    CFGA_SATA_OK)
8591258Smlf 			goto bailout;
8601258Smlf 
8611258Smlf 		if (rstate == AP_RSTATE_CONNECTED) {
8621258Smlf 			rv = CFGA_SATA_ALREADY_CONNECTED;
8631258Smlf 			goto bailout;
8641258Smlf 		}
8651258Smlf 
8661258Smlf 		len = strlen(SATA_CONFIRM_PORT) +
867*10318SXiao-Yu.Zhang@Sun.COM 		    strlen(SATA_CONFIRM_PORT_ENABLE) +
868*10318SXiao-Yu.Zhang@Sun.COM 		    strlen("Activate Port") + strlen(ap_id);
8691258Smlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
8701258Smlf 			(void) snprintf(msg, len +3, "Activate"
871*10318SXiao-Yu.Zhang@Sun.COM 			    " %s%s\n%s",
872*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_PORT, ap_id,
873*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_PORT_ENABLE);
8741258Smlf 		}
8751258Smlf 		if (!sata_confirm(confp, msg)) {
8761258Smlf 			rv = CFGA_SATA_NACK;
8771258Smlf 			break;
8781258Smlf 		}
8791258Smlf 
8801258Smlf 		/* Disallow dynamic AP name component */
8811258Smlf 		if (GET_DYN(ap_id) != NULL) {
8821258Smlf 			rv = CFGA_SATA_INVALID_DEVNAME;
8831258Smlf 			goto bailout;
8841258Smlf 		}
8851258Smlf 
8861258Smlf 		ret = devctl_ap_connect(hdl, nvl);
8871258Smlf 		if (ret != 0) {
8881258Smlf 			rv = CFGA_SATA_IOCTL;
8891258Smlf 		} else {
8901258Smlf 			rv = CFGA_SATA_OK;
8911258Smlf 		}
8921258Smlf 
8931258Smlf 		break;
8941258Smlf 
8951258Smlf 	case CFGA_CMD_LOAD:
8961258Smlf 	case CFGA_CMD_UNLOAD:
8971258Smlf 		(void) cfga_help(msgp, options, flags);
8981258Smlf 		rv = CFGA_SATA_OPNOTSUPP;
8991258Smlf 		break;
9001258Smlf 
9011258Smlf 	case CFGA_CMD_NONE:
9021258Smlf 	default:
9031258Smlf 		(void) cfga_help(msgp, options, flags);
9041258Smlf 		rv = CFGA_SATA_INTERNAL_ERROR;
9051258Smlf 	}
9061258Smlf 
9071258Smlf bailout:
9081258Smlf 	cleanup_after_devctl_cmd(hdl, nvl);
9091258Smlf 
9101258Smlf 	return (sata_err_msg(errstring, rv, ap_id, errno));
9111258Smlf }
9121258Smlf 
9131258Smlf /* cfgadm entry point */
9141258Smlf cfga_err_t
cfga_private_func(const char * func,const char * ap_id,const char * options,struct cfga_confirm * confp,struct cfga_msg * msgp,char ** errstring,cfga_flags_t flags)9151258Smlf cfga_private_func(
916*10318SXiao-Yu.Zhang@Sun.COM     const char *func,
917*10318SXiao-Yu.Zhang@Sun.COM     const char *ap_id,
918*10318SXiao-Yu.Zhang@Sun.COM     const char *options,
919*10318SXiao-Yu.Zhang@Sun.COM     struct cfga_confirm *confp,
920*10318SXiao-Yu.Zhang@Sun.COM     struct cfga_msg *msgp,
921*10318SXiao-Yu.Zhang@Sun.COM     char **errstring,
922*10318SXiao-Yu.Zhang@Sun.COM     cfga_flags_t flags)
9231258Smlf {
9241258Smlf 	int			len;
9251258Smlf 	char 			*msg;
9261258Smlf 	nvlist_t		*list = NULL;
9271258Smlf 	ap_ostate_t		ostate;
9281258Smlf 	ap_rstate_t		rstate;
9291258Smlf 	devctl_hdl_t		hdl = NULL;
9301258Smlf 	cfga_sata_ret_t		rv;
9311258Smlf 	char			*str_p;
9321258Smlf 	size_t			size;
9331258Smlf 
9341258Smlf 	if ((rv = verify_params(ap_id, NULL, errstring)) != CFGA_SATA_OK) {
9351258Smlf 		(void) cfga_help(msgp, options, flags);
9361258Smlf 		return (sata_err_msg(errstring, rv, ap_id, errno));
9371258Smlf 	}
9381258Smlf 
9391258Smlf 	/*
9401258Smlf 	 * All subcommands which can change state of device require
9411258Smlf 	 * root privileges.
9421258Smlf 	 */
9431258Smlf 	if (geteuid() != 0) {
9441258Smlf 		rv = CFGA_SATA_PRIV;
9451258Smlf 		goto bailout;
9461258Smlf 	}
9471258Smlf 
9481258Smlf 	if (func == NULL) {
9491258Smlf 		(void) printf("No valid option specified\n");
9501258Smlf 		rv = CFGA_SATA_OPTIONS;
9511258Smlf 		goto bailout;
9521258Smlf 	}
9531258Smlf 
9541258Smlf 	if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &list, 0)) !=
9551258Smlf 	    CFGA_SATA_OK) {
9561258Smlf 		goto bailout;
9571258Smlf 	}
9581258Smlf 
9591258Smlf 	/* We do not care here about dynamic AP name component */
9601258Smlf 	if ((str_p = GET_DYN(ap_id)) != NULL) {
9611258Smlf 		*str_p = '\0';
9621258Smlf 	}
9631258Smlf 
9641258Smlf 	rv = CFGA_SATA_OK;
9651258Smlf 
9661258Smlf 	if (strcmp(func, SATA_RESET_PORT) == 0) {
9671258Smlf 		len = strlen(SATA_CONFIRM_PORT) +
9681258Smlf 		    strlen(SATA_CONFIRM_DEVICE_ABORT) +
9691258Smlf 		    strlen("Reset Port") + strlen(ap_id);
9701258Smlf 
9711258Smlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
9721258Smlf 			(void) snprintf(msg, len +3, "Reset"
973*10318SXiao-Yu.Zhang@Sun.COM 			    " %s%s\n%s",
974*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_PORT, ap_id,
975*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_DEVICE_ABORT);
9761258Smlf 		} else {
9771258Smlf 			rv = CFGA_SATA_NACK;
9781258Smlf 			goto bailout;
9791258Smlf 		}
9801258Smlf 
9811258Smlf 		if (!sata_confirm(confp, msg)) {
9821258Smlf 			rv = CFGA_SATA_NACK;
9831258Smlf 			goto bailout;
9841258Smlf 		}
9851258Smlf 
9861258Smlf 		rv = do_control_ioctl(ap_id, SATA_CFGA_RESET_PORT, NULL,
987*10318SXiao-Yu.Zhang@Sun.COM 		    (void **)&str_p, &size);
9881258Smlf 
9891258Smlf 	} else if (strcmp(func, SATA_RESET_DEVICE) == 0) {
9901258Smlf 		if ((rv = port_state(hdl, list, &rstate, &ostate)) !=
9911258Smlf 		    CFGA_SATA_OK)
9921258Smlf 			goto bailout;
9931258Smlf 		/*
9941258Smlf 		 * Reset device function requires device to be connected
9951258Smlf 		 */
9961258Smlf 		if (rstate != AP_RSTATE_CONNECTED) {
9971258Smlf 			rv = CFGA_SATA_NOT_CONNECTED;
9981258Smlf 			goto bailout;
9991258Smlf 		}
10001258Smlf 
10011258Smlf 		len = strlen(SATA_CONFIRM_DEVICE) +
10021258Smlf 		    strlen(SATA_CONFIRM_DEVICE_ABORT) +
10031258Smlf 		    strlen("Reset Device") + strlen(ap_id);
10041258Smlf 
10051258Smlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
10061258Smlf 			(void) snprintf(msg, len +3, "Reset"
1007*10318SXiao-Yu.Zhang@Sun.COM 			    " %s%s\n%s",
1008*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_DEVICE, ap_id,
1009*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_DEVICE_ABORT);
10101258Smlf 		} else {
10111258Smlf 			rv = CFGA_SATA_NACK;
10121258Smlf 			goto bailout;
10131258Smlf 		}
10141258Smlf 
10151258Smlf 		if (!sata_confirm(confp, msg)) {
10161258Smlf 			rv = CFGA_SATA_NACK;
10171258Smlf 			goto bailout;
10181258Smlf 		}
10191258Smlf 
10201258Smlf 		rv = do_control_ioctl(ap_id, SATA_CFGA_RESET_DEVICE, NULL,
10211258Smlf 		    (void **)&str_p, &size);
10221258Smlf 
10231258Smlf 	} else if (strcmp(func, SATA_RESET_ALL) == 0) {
10241258Smlf 		len = strlen(SATA_CONFIRM_CONTROLLER) +
10251258Smlf 		    strlen(SATA_CONFIRM_CONTROLLER_ABORT) +
10261258Smlf 		    strlen("Reset All") + strlen(ap_id);
10271258Smlf 
10281258Smlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
10291258Smlf 			(void) snprintf(msg, len +3, "Reset"
1030*10318SXiao-Yu.Zhang@Sun.COM 			    " %s%s\n%s",
1031*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_CONTROLLER, ap_id,
1032*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_CONTROLLER_ABORT);
10331258Smlf 		} else {
10341258Smlf 			rv = CFGA_SATA_NACK;
10351258Smlf 			goto bailout;
10361258Smlf 		}
10371258Smlf 
10381258Smlf 		if (!sata_confirm(confp, msg)) {
10391258Smlf 			rv = CFGA_SATA_NACK;
10401258Smlf 			goto bailout;
10411258Smlf 		}
10421258Smlf 		rv = do_control_ioctl(ap_id, SATA_CFGA_RESET_ALL, NULL,
1043*10318SXiao-Yu.Zhang@Sun.COM 		    (void **)&str_p, &size);
10441258Smlf 
10451258Smlf 	} else if (strcmp(func, SATA_PORT_DEACTIVATE) == 0) {
10461258Smlf 		len = strlen(SATA_CONFIRM_PORT) +
10471258Smlf 		    strlen(SATA_CONFIRM_PORT_DISABLE) +
10481258Smlf 		    strlen("Deactivate Port") + strlen(ap_id);
10491258Smlf 
10501258Smlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
10511258Smlf 			(void) snprintf(msg, len +3, "Deactivate"
1052*10318SXiao-Yu.Zhang@Sun.COM 			    " %s%s\n%s",
1053*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_PORT, ap_id,
1054*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_PORT_DISABLE);
10551258Smlf 		} else {
10561258Smlf 			rv = CFGA_SATA_NACK;
10571258Smlf 			goto bailout;
10581258Smlf 		}
10591258Smlf 		if (!sata_confirm(confp, msg)) {
10601258Smlf 			rv = CFGA_SATA_NACK;
10611258Smlf 			goto bailout;
10621258Smlf 		}
10631258Smlf 
10641258Smlf 		rv = do_control_ioctl(ap_id, SATA_CFGA_PORT_DEACTIVATE, NULL,
1065*10318SXiao-Yu.Zhang@Sun.COM 		    (void **)&str_p, &size);
10661258Smlf 
10671258Smlf 	} else if (strcmp(func, SATA_PORT_ACTIVATE) == 0) {
10681258Smlf 		len = strlen(SATA_CONFIRM_PORT) +
10691258Smlf 		    strlen(SATA_CONFIRM_PORT_ENABLE) +
10701258Smlf 		    strlen("Activate Port") + strlen(ap_id);
10711258Smlf 
10721258Smlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
10731258Smlf 			(void) snprintf(msg, len +3, "Activate"
1074*10318SXiao-Yu.Zhang@Sun.COM 			    " %s%s\n%s",
1075*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_PORT, ap_id,
1076*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_PORT_ENABLE);
10771258Smlf 		} else {
10781258Smlf 			rv = CFGA_SATA_NACK;
10791258Smlf 			goto bailout;
10801258Smlf 		}
10811258Smlf 		if (!sata_confirm(confp, msg)) {
10821258Smlf 			rv = CFGA_SATA_NACK;
10831258Smlf 			goto bailout;
10841258Smlf 		}
10851258Smlf 
10861258Smlf 		rv = do_control_ioctl(ap_id, SATA_CFGA_PORT_ACTIVATE,
10871258Smlf 		    NULL, (void **)&str_p, &size);
1088*10318SXiao-Yu.Zhang@Sun.COM 		goto bailout;
10891258Smlf 
10901258Smlf 	} else if (strcmp(func, SATA_PORT_SELF_TEST) == 0) {
10911258Smlf 		len = strlen(SATA_CONFIRM_PORT) +
10921258Smlf 		    strlen(SATA_CONFIRM_DEVICE_SUSPEND) +
10931258Smlf 		    strlen("Self Test Port") + strlen(ap_id);
10941258Smlf 
10951258Smlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
10961258Smlf 			(void) snprintf(msg, len +3, "Self Test"
1097*10318SXiao-Yu.Zhang@Sun.COM 			    " %s%s\n%s",
1098*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_PORT, ap_id,
1099*10318SXiao-Yu.Zhang@Sun.COM 			    SATA_CONFIRM_DEVICE_SUSPEND);
11001258Smlf 		} else {
11011258Smlf 			rv = CFGA_SATA_NACK;
11021258Smlf 			goto bailout;
11031258Smlf 		}
11041258Smlf 		if (!sata_confirm(confp, msg)) {
11051258Smlf 			rv = CFGA_SATA_NACK;
11061258Smlf 			goto bailout;
11071258Smlf 		}
11081258Smlf 
11091258Smlf 		rv = do_control_ioctl(ap_id, SATA_CFGA_PORT_SELF_TEST,
1110*10318SXiao-Yu.Zhang@Sun.COM 		    NULL, (void **)&str_p, &size);
11111258Smlf 	} else {
11121258Smlf 		/* Unrecognized operation request */
11131258Smlf 		rv = CFGA_SATA_HWOPNOTSUPP;
11141258Smlf 	}
11151258Smlf 
11161258Smlf bailout:
11171258Smlf 	cleanup_after_devctl_cmd(hdl, list);
11181258Smlf 
11191258Smlf 	return (sata_err_msg(errstring, rv, ap_id, errno));
11201258Smlf 
11211258Smlf }
11221258Smlf 
11231258Smlf /* cfgadm entry point */
11241258Smlf /*ARGSUSED*/
11251258Smlf cfga_err_t
cfga_test(const char * ap_id,const char * options,struct cfga_msg * msgp,char ** errstring,cfga_flags_t flags)11261258Smlf cfga_test(
1127*10318SXiao-Yu.Zhang@Sun.COM     const char *ap_id,
1128*10318SXiao-Yu.Zhang@Sun.COM     const char *options,
1129*10318SXiao-Yu.Zhang@Sun.COM     struct cfga_msg *msgp,
1130*10318SXiao-Yu.Zhang@Sun.COM     char **errstring,
1131*10318SXiao-Yu.Zhang@Sun.COM     cfga_flags_t flags)
11321258Smlf {
11331258Smlf 	/* Should call ioctl for self test - phase 2 */
11341258Smlf 	return (CFGA_OPNOTSUPP);
11351258Smlf }
11361258Smlf 
11371258Smlf 
11381258Smlf int
sata_check_target_node(di_node_t node,void * arg)11391258Smlf sata_check_target_node(di_node_t node, void *arg)
11401258Smlf {
11411258Smlf 	char *minorpath;
11421258Smlf 	char *cp;
11431258Smlf 
11441258Smlf 	minorpath = di_devfs_minor_path(di_minor_next(node, DI_MINOR_NIL));
11451258Smlf 	if (minorpath != NULL) {
11461258Smlf 		if (strstr(minorpath, arg) != NULL) {
11471258Smlf 			cp = strrchr(minorpath, (int)*MINOR_SEP);
11481258Smlf 			if (cp != NULL) {
11491258Smlf 				(void) strcpy(arg, cp);
11501258Smlf 			}
11511258Smlf 			free(minorpath);
11521258Smlf 			return (DI_WALK_TERMINATE);
11531258Smlf 		}
11541258Smlf 		free(minorpath);
11551258Smlf 	}
11561258Smlf 	return (DI_WALK_CONTINUE);
11571258Smlf }
11581258Smlf 
11594707Svikram struct chk_dev {
11604707Svikram 	int c_isblk;
11614707Svikram 	char *c_minor;
11624707Svikram };
11634707Svikram 
11644707Svikram /*ARGSUSED*/
11654707Svikram static int
chk_dev_fcn(di_node_t node,di_minor_t minor,void * arg)11664707Svikram chk_dev_fcn(di_node_t node, di_minor_t minor, void *arg)
11674707Svikram {
11684707Svikram 	char	*mn;
11694707Svikram 	struct chk_dev *chkp = (struct chk_dev *)arg;
11704707Svikram 
11714707Svikram 	mn = di_minor_name(minor);
11724707Svikram 	if (mn == NULL)
11734707Svikram 		return (DI_WALK_CONTINUE);
11744707Svikram 
11754707Svikram 	if (strcmp(mn, chkp->c_minor) != 0)
11764707Svikram 		return (DI_WALK_CONTINUE);
11774707Svikram 
11784707Svikram 	chkp->c_isblk = di_minor_spectype(minor) == S_IFBLK ? 1 : 0;
11794707Svikram 
11804707Svikram 	return (DI_WALK_TERMINATE);
11814707Svikram }
11824707Svikram 
11834707Svikram /*
11844707Svikram  * Don't use devfs if stat() in /devices fails. Use libdevinfo instead.
11854707Svikram  * Retired devices don't show up in devfs.
11864707Svikram  *
11874707Svikram  *	Returns:
11884707Svikram  *		1 - minor exists and is of type BLK
11894707Svikram  *		0 - minor does not exist or is not of type BLK.
11904707Svikram  */
11914707Svikram static int
is_devinfo_blk(char * minor_path)11924707Svikram is_devinfo_blk(char *minor_path)
11934707Svikram {
11944707Svikram 	char	*minor_portion;
11954707Svikram 	struct chk_dev chk_dev;
11964707Svikram 	di_node_t node;
11974707Svikram 	int	rv;
11984707Svikram 
11994707Svikram 	/*
12004707Svikram 	 * prune minor path for di_init() - no /devices prefix and no minor name
12014707Svikram 	 */
12024707Svikram 	if (strncmp(minor_path, "/devices/", strlen("/devices/")) != 0)
12034707Svikram 		return (0);
12044707Svikram 
12054707Svikram 	minor_portion = strrchr(minor_path, *MINOR_SEP);
12064707Svikram 	if (minor_portion == NULL)
12074707Svikram 		return (0);
12084707Svikram 
12094707Svikram 	*minor_portion = 0;
12104707Svikram 
12114707Svikram 	node = di_init(minor_path + strlen("/devices"), DINFOMINOR);
12124707Svikram 
12134707Svikram 	*minor_portion = *MINOR_SEP;
12144707Svikram 
12154707Svikram 	if (node == DI_NODE_NIL)
12164707Svikram 		return (0);
12174707Svikram 
12184707Svikram 	chk_dev.c_isblk = 0;
12194707Svikram 	chk_dev.c_minor = minor_portion + 1;
12204707Svikram 
12214707Svikram 	rv = di_walk_minor(node, NULL, 0, &chk_dev, chk_dev_fcn);
12224707Svikram 
12234707Svikram 	di_fini(node);
12244707Svikram 
12254707Svikram 	if (rv == 0 && chk_dev.c_isblk)
12264707Svikram 		return (1);
12274707Svikram 	else
12284707Svikram 		return (0);
12294707Svikram }
12301258Smlf 
12311258Smlf /*
12321258Smlf  * The dynamic component buffer returned by this function has to be freed!
12331258Smlf  */
12341258Smlf int
sata_make_dyncomp(const char * ap_id,char ** dyncomp,const char * type)12358010SYing.Tian@Sun.COM sata_make_dyncomp(const char *ap_id, char **dyncomp, const char *type)
12361258Smlf {
12372027Ssethg 	char	*devpath = NULL;
12382027Ssethg 	char	*cp = NULL;
12392027Ssethg 	int	l_errno;
12402027Ssethg 	char	minor_path[MAXPATHLEN];
12412027Ssethg 	char	name_part[MAXNAMELEN];
12422027Ssethg 	char	*devlink = NULL;
12432027Ssethg 	char	*minor_portion = NULL;
12442027Ssethg 	int	deplen;
12452027Ssethg 	int	err;
12462027Ssethg 	DIR	*dp = NULL;
12472027Ssethg 	struct stat sb;
12482027Ssethg 	struct dirent *dep = NULL;
12492027Ssethg 	struct dirent *newdep = NULL;
12502247Ssethg 	char	*p;
12511258Smlf 
12521258Smlf 	assert(dyncomp != NULL);
12531258Smlf 
12541258Smlf 	/*
12551258Smlf 	 * Get target node path
12561258Smlf 	 */
12571258Smlf 	devpath = sata_get_devicepath(ap_id);
12581258Smlf 	if (devpath == NULL) {
12592027Ssethg 
12601258Smlf 		(void) printf("cfga_list_ext: cannot locate target device\n");
12611258Smlf 		return (CFGA_SATA_DYNAMIC_AP);
12622027Ssethg 
12631258Smlf 	} else {
12642027Ssethg 
12652027Ssethg 		cp = strrchr(devpath, *PATH_SEP);
12662027Ssethg 		assert(cp != NULL);
12672027Ssethg 		*cp = 0;	/* terminate path for opendir() */
12681258Smlf 
12692027Ssethg 		(void) strncpy(name_part, cp + 1, MAXNAMELEN);
12701258Smlf 
12712027Ssethg 		/*
12722027Ssethg 		 * Using libdevinfo for this is overkill and kills
12732027Ssethg 		 * performance when many consumers are using libcfgadm
12742027Ssethg 		 * concurrently.
12752027Ssethg 		 */
12762027Ssethg 		if ((dp = opendir(devpath)) == NULL) {
12771258Smlf 			goto bailout;
12781258Smlf 		}
12791258Smlf 
12801258Smlf 		/*
12812027Ssethg 		 * deplen is large enough to fit the largest path-
12822027Ssethg 		 * struct dirent includes one byte (the terminator)
12832027Ssethg 		 * so we don't add 1 to the calculation here.
12841258Smlf 		 */
12853019Ssethg 		deplen = pathconf(devpath, _PC_NAME_MAX);
12863019Ssethg 		deplen = ((deplen <= 0) ? MAXNAMELEN : deplen) +
12872027Ssethg 		    sizeof (struct dirent);
12882027Ssethg 		dep = (struct dirent *)malloc(deplen);
12892027Ssethg 		if (dep == NULL)
12902027Ssethg 			goto bailout;
12912027Ssethg 
12922027Ssethg 		while ((err = readdir_r(dp, dep, &newdep)) == 0 &&
12932027Ssethg 		    newdep != NULL) {
12942027Ssethg 
12952027Ssethg 			assert(newdep == dep);
12961258Smlf 
12972027Ssethg 			if (strcmp(dep->d_name, ".") == 0 ||
12982027Ssethg 			    strcmp(dep->d_name, "..") == 0 ||
12992027Ssethg 			    (minor_portion = strchr(dep->d_name,
13002027Ssethg 			    *MINOR_SEP)) == NULL)
13012027Ssethg 				continue;
13022027Ssethg 
13032027Ssethg 			*minor_portion = 0;
13042027Ssethg 			if (strcmp(dep->d_name, name_part) != 0)
13052027Ssethg 				continue;
13062027Ssethg 			*minor_portion = *MINOR_SEP;
13072027Ssethg 
13082027Ssethg 			(void) snprintf(minor_path, MAXPATHLEN,
13092027Ssethg 			    "%s/%s", devpath, dep->d_name);
13102027Ssethg 
13114707Svikram 			/*
13128010SYing.Tian@Sun.COM 			 * Break directly for tape device
13138010SYing.Tian@Sun.COM 			 */
13148010SYing.Tian@Sun.COM 			if (strcmp(type, "tape") == 0)
13158010SYing.Tian@Sun.COM 				break;
13168010SYing.Tian@Sun.COM 
13178010SYing.Tian@Sun.COM 			/*
13184707Svikram 			 * If stat() fails, the device *may* be retired.
13194707Svikram 			 * Check via libdevinfo if the device has a BLK minor.
13204707Svikram 			 * We don't use libdevinfo all the time, since taking
13214707Svikram 			 * a snapshot is slower than a stat().
13224707Svikram 			 */
13234707Svikram 			if (stat(minor_path, &sb) < 0) {
13244707Svikram 				if (is_devinfo_blk(minor_path)) {
13254707Svikram 					break;
13264707Svikram 				} else {
13274707Svikram 					continue;
13284707Svikram 				}
13294707Svikram 			}
13302027Ssethg 
13312027Ssethg 			if (S_ISBLK(sb.st_mode))
13322027Ssethg 				break;
13334707Svikram 
13341258Smlf 		}
13351258Smlf 
13362027Ssethg 		(void) closedir(dp);
13372027Ssethg 		free(dep);
13381258Smlf 		free(devpath);
13391258Smlf 
13402027Ssethg 		dp = NULL;
13412027Ssethg 		dep = NULL;
13422027Ssethg 		devpath = NULL;
13432027Ssethg 
13442027Ssethg 		/*
13452027Ssethg 		 * If there was an error, or we didn't exit the loop
13462027Ssethg 		 * by finding a block or character device, bail out.
13472027Ssethg 		 */
13482027Ssethg 		if (err != 0 || newdep == NULL)
13492027Ssethg 			goto bailout;
13502027Ssethg 
13512027Ssethg 		/*
13528010SYing.Tian@Sun.COM 		 * Look for links to the physical path in /dev/dsk
13538010SYing.Tian@Sun.COM 		 * and /dev/rmt. So far, sata modue supports disk,
13548010SYing.Tian@Sun.COM 		 * dvd and tape devices, so we will first look for
13558010SYing.Tian@Sun.COM 		 * BLOCK devices, and then look for tape devices.
13562027Ssethg 		 */
13572027Ssethg 		(void) physpath_to_devlink("/dev/dsk",
13582027Ssethg 		    minor_path, &devlink, &l_errno);
13591258Smlf 
13601258Smlf 		/* postprocess and copy logical name here */
13611258Smlf 		if (devlink != NULL) {
13621258Smlf 			/*
13632247Ssethg 			 * For disks, remove partition/slice info
13641258Smlf 			 */
13652027Ssethg 			if ((cp = strstr(devlink, "dsk/")) != NULL) {
13662247Ssethg 				/* cXtYdZ[(s[0..15])|(p[0..X])] */
13672247Ssethg 				if ((p = strchr(cp + 4, 'd')) != NULL) {
13682247Ssethg 					p++;	/* Skip the 'd' */
13692247Ssethg 					while (*p != 0 && isdigit(*p))
13702247Ssethg 						p++;
13712247Ssethg 					*p = 0;
13722247Ssethg 				}
13732027Ssethg 				*dyncomp = strdup(cp);
13741258Smlf 			}
13752027Ssethg 
13761258Smlf 			free(devlink);
13778010SYing.Tian@Sun.COM 		} else if (strcmp(type, "tape") == 0) {
13788010SYing.Tian@Sun.COM 
13798010SYing.Tian@Sun.COM 			/*
13808010SYing.Tian@Sun.COM 			 * For tape device, logical name looks like
13818010SYing.Tian@Sun.COM 			 * rmt/X
13828010SYing.Tian@Sun.COM 			 */
13838010SYing.Tian@Sun.COM 			(void) physpath_to_devlink("/dev/rmt",
13848010SYing.Tian@Sun.COM 			    minor_path, &devlink, &l_errno);
13858010SYing.Tian@Sun.COM 
13868010SYing.Tian@Sun.COM 			if (devlink != NULL) {
13878010SYing.Tian@Sun.COM 				if ((cp = strstr(devlink, "rmt/")) != NULL) {
13888010SYing.Tian@Sun.COM 					*dyncomp = strdup(cp);
13898010SYing.Tian@Sun.COM 				}
13908010SYing.Tian@Sun.COM 
13918010SYing.Tian@Sun.COM 				free(devlink);
13928010SYing.Tian@Sun.COM 			}
13931258Smlf 		}
13942027Ssethg 
13951258Smlf 		return (SATA_CFGA_OK);
13961258Smlf 	}
13972027Ssethg 
13981258Smlf bailout:
13992027Ssethg 	if (dp)
14002027Ssethg 		(void) closedir(dp);
14012027Ssethg 	if (devpath)
14021258Smlf 		free(devpath);
14032027Ssethg 	if (dep)
14042027Ssethg 		free(dep);
14051258Smlf 	return (CFGA_SATA_DYNAMIC_AP);
14061258Smlf }
14071258Smlf 
14081258Smlf /* cfgadm entry point */
14091258Smlf /*ARGSUSED*/
14101258Smlf cfga_err_t
cfga_list_ext(const char * ap_id,cfga_list_data_t ** ap_id_list,int * nlistp,const char * options,const char * listopts,char ** errstring,cfga_flags_t flags)14111258Smlf cfga_list_ext(
1412*10318SXiao-Yu.Zhang@Sun.COM     const char *ap_id,
1413*10318SXiao-Yu.Zhang@Sun.COM     cfga_list_data_t **ap_id_list,
1414*10318SXiao-Yu.Zhang@Sun.COM     int *nlistp,
1415*10318SXiao-Yu.Zhang@Sun.COM     const char *options,
1416*10318SXiao-Yu.Zhang@Sun.COM     const char *listopts,
1417*10318SXiao-Yu.Zhang@Sun.COM     char **errstring,
1418*10318SXiao-Yu.Zhang@Sun.COM     cfga_flags_t flags)
14191258Smlf {
14201258Smlf 	int			l_errno;
14211258Smlf 	char			*ap_id_log = NULL;
14221258Smlf 	size_t			size;
14231258Smlf 	nvlist_t		*user_nvlist = NULL;
14241258Smlf 	devctl_hdl_t		devctl_hdl = NULL;
14251258Smlf 	cfga_sata_ret_t		rv = CFGA_SATA_OK;
14261258Smlf 	devctl_ap_state_t	devctl_ap_state;
14271258Smlf 	char			*pdyn;
1428*10318SXiao-Yu.Zhang@Sun.COM 	boolean_t		pmult = B_FALSE;
1429*10318SXiao-Yu.Zhang@Sun.COM 	uint32_t		port;
14301258Smlf 
14311258Smlf 
14321258Smlf 	if ((rv = verify_params(ap_id, options, errstring)) != CFGA_SATA_OK) {
14331258Smlf 		(void) cfga_help(NULL, options, flags);
14341258Smlf 		goto bailout;
14351258Smlf 	}
14361258Smlf 	/* We do not care here about dynamic AP name component */
14371258Smlf 	if ((pdyn = GET_DYN(ap_id)) != NULL) {
14381258Smlf 		*pdyn = '\0';
14391258Smlf 	}
14401258Smlf 
14411258Smlf 	if (ap_id_list == NULL || nlistp == NULL) {
14421258Smlf 		rv = CFGA_SATA_DATA_ERROR;
14431258Smlf 		(void) cfga_help(NULL, options, flags);
14441258Smlf 		goto bailout;
14451258Smlf 	}
14461258Smlf 
14471258Smlf 	/* Get ap status */
14481258Smlf 	if ((rv = setup_for_devctl_cmd(ap_id, &devctl_hdl, &user_nvlist,
14491258Smlf 	    DC_RDONLY)) != CFGA_SATA_OK) {
14501258Smlf 		goto bailout;
14511258Smlf 	}
14521258Smlf 
14531258Smlf 	/* will call dc_cmd to send IOCTL to kernel */
14541258Smlf 	if (devctl_ap_getstate(devctl_hdl, user_nvlist,
14551258Smlf 	    &devctl_ap_state) == -1) {
14561258Smlf 		cleanup_after_devctl_cmd(devctl_hdl, user_nvlist);
14571258Smlf 		rv = CFGA_SATA_IOCTL;
14581258Smlf 		goto bailout;
14591258Smlf 	}
14601258Smlf 
14611258Smlf 	cleanup_after_devctl_cmd(devctl_hdl, user_nvlist);
14621258Smlf 
14631258Smlf 	/*
14641258Smlf 	 * Create cfga_list_data_t struct.
14651258Smlf 	 */
14661258Smlf 	if ((*ap_id_list =
14671258Smlf 	    (cfga_list_data_t *)malloc(sizeof (**ap_id_list))) == NULL) {
14681258Smlf 		rv = CFGA_SATA_ALLOC_FAIL;
14691258Smlf 		goto bailout;
14701258Smlf 	}
14711258Smlf 	*nlistp = 1;
14721258Smlf 
14731258Smlf 	/*
14741258Smlf 	 * Rest of the code fills in the cfga_list_data_t struct.
14751258Smlf 	 */
14761258Smlf 
14771258Smlf 	/* Get /dev/cfg path to corresponding to the physical ap_id */
14781258Smlf 	/* Remember ap_id_log must be freed */
14792027Ssethg 	rv = physpath_to_devlink(CFGA_DEV_DIR, (char *)ap_id,
14802027Ssethg 	    &ap_id_log, &l_errno);
14811258Smlf 
14821258Smlf 	if (rv != 0) {
14831258Smlf 		rv = CFGA_SATA_DEVLINK;
14841258Smlf 		goto bailout;
14851258Smlf 	}
14861258Smlf 
14871258Smlf 	/* Get logical ap_id corresponding to the physical */
14882033Ssethg 	if (ap_id_log == NULL || strstr(ap_id_log, CFGA_DEV_DIR) == NULL) {
14891258Smlf 		rv = CFGA_SATA_DEVLINK;
14901258Smlf 		goto bailout;
14911258Smlf 	}
14921258Smlf 
14931258Smlf 	(void) strlcpy((*ap_id_list)->ap_log_id,
14941258Smlf 	    /* Strip off /dev/cfg/ */ ap_id_log + strlen(CFGA_DEV_DIR)+ 1,
14951258Smlf 	    sizeof ((*ap_id_list)->ap_log_id));
14961258Smlf 
14971258Smlf 	free(ap_id_log);
14981258Smlf 	ap_id_log = NULL;
14991258Smlf 
15001258Smlf 	(void) strlcpy((*ap_id_list)->ap_phys_id, ap_id,
15011258Smlf 	    sizeof ((*ap_id_list)->ap_phys_id));
15021258Smlf 
15031258Smlf 	switch (devctl_ap_state.ap_rstate) {
1504*10318SXiao-Yu.Zhang@Sun.COM 	case AP_RSTATE_EMPTY:
1505*10318SXiao-Yu.Zhang@Sun.COM 		(*ap_id_list)->ap_r_state = CFGA_STAT_EMPTY;
1506*10318SXiao-Yu.Zhang@Sun.COM 		break;
15071258Smlf 
1508*10318SXiao-Yu.Zhang@Sun.COM 	case AP_RSTATE_DISCONNECTED:
1509*10318SXiao-Yu.Zhang@Sun.COM 		(*ap_id_list)->ap_r_state = CFGA_STAT_DISCONNECTED;
1510*10318SXiao-Yu.Zhang@Sun.COM 		break;
15111258Smlf 
1512*10318SXiao-Yu.Zhang@Sun.COM 	case AP_RSTATE_CONNECTED:
1513*10318SXiao-Yu.Zhang@Sun.COM 		(*ap_id_list)->ap_r_state = CFGA_STAT_CONNECTED;
1514*10318SXiao-Yu.Zhang@Sun.COM 		break;
15151258Smlf 
1516*10318SXiao-Yu.Zhang@Sun.COM 	default:
1517*10318SXiao-Yu.Zhang@Sun.COM 		rv = CFGA_SATA_STATE;
1518*10318SXiao-Yu.Zhang@Sun.COM 		goto bailout;
15191258Smlf 	}
15201258Smlf 
15211258Smlf 	switch (devctl_ap_state.ap_ostate) {
1522*10318SXiao-Yu.Zhang@Sun.COM 	case AP_OSTATE_CONFIGURED:
1523*10318SXiao-Yu.Zhang@Sun.COM 		(*ap_id_list)->ap_o_state = CFGA_STAT_CONFIGURED;
1524*10318SXiao-Yu.Zhang@Sun.COM 		break;
15251258Smlf 
1526*10318SXiao-Yu.Zhang@Sun.COM 	case AP_OSTATE_UNCONFIGURED:
1527*10318SXiao-Yu.Zhang@Sun.COM 		(*ap_id_list)->ap_o_state = CFGA_STAT_UNCONFIGURED;
1528*10318SXiao-Yu.Zhang@Sun.COM 		break;
15291258Smlf 
1530*10318SXiao-Yu.Zhang@Sun.COM 	default:
1531*10318SXiao-Yu.Zhang@Sun.COM 		rv = CFGA_SATA_STATE;
1532*10318SXiao-Yu.Zhang@Sun.COM 		goto bailout;
15331258Smlf 	}
15341258Smlf 
15351258Smlf 	switch (devctl_ap_state.ap_condition) {
1536*10318SXiao-Yu.Zhang@Sun.COM 	case AP_COND_OK:
1537*10318SXiao-Yu.Zhang@Sun.COM 		(*ap_id_list)->ap_cond = CFGA_COND_OK;
1538*10318SXiao-Yu.Zhang@Sun.COM 		break;
15391258Smlf 
1540*10318SXiao-Yu.Zhang@Sun.COM 	case AP_COND_FAILING:
1541*10318SXiao-Yu.Zhang@Sun.COM 		(*ap_id_list)->ap_cond = CFGA_COND_FAILING;
1542*10318SXiao-Yu.Zhang@Sun.COM 		break;
15431258Smlf 
1544*10318SXiao-Yu.Zhang@Sun.COM 	case AP_COND_FAILED:
1545*10318SXiao-Yu.Zhang@Sun.COM 		(*ap_id_list)->ap_cond = CFGA_COND_FAILED;
1546*10318SXiao-Yu.Zhang@Sun.COM 		break;
15471258Smlf 
1548*10318SXiao-Yu.Zhang@Sun.COM 	case AP_COND_UNUSABLE:
1549*10318SXiao-Yu.Zhang@Sun.COM 		(*ap_id_list)->ap_cond = CFGA_COND_UNUSABLE;
1550*10318SXiao-Yu.Zhang@Sun.COM 		break;
15511258Smlf 
1552*10318SXiao-Yu.Zhang@Sun.COM 	case AP_COND_UNKNOWN:
1553*10318SXiao-Yu.Zhang@Sun.COM 		(*ap_id_list)->ap_cond = CFGA_COND_UNKNOWN;
1554*10318SXiao-Yu.Zhang@Sun.COM 		break;
15551258Smlf 
1556*10318SXiao-Yu.Zhang@Sun.COM 	default:
1557*10318SXiao-Yu.Zhang@Sun.COM 		rv = CFGA_SATA_STATE;
1558*10318SXiao-Yu.Zhang@Sun.COM 		goto bailout;
15591258Smlf 	}
15601258Smlf 
15611258Smlf 	(*ap_id_list)->ap_class[0] = '\0';	/* Filled by libcfgadm */
15621258Smlf 	(*ap_id_list)->ap_busy = devctl_ap_state.ap_in_transition;
15631258Smlf 	(*ap_id_list)->ap_status_time = devctl_ap_state.ap_last_change;
15641258Smlf 	(*ap_id_list)->ap_info[0] = NULL;
15651258Smlf 
15661258Smlf 	if ((*ap_id_list)->ap_r_state == CFGA_STAT_CONNECTED) {
15671258Smlf 		char *str_p;
15681258Smlf 		int skip, i;
15691258Smlf 
15701258Smlf 		/*
15711258Smlf 		 * Fill in the 'Information' field for the -v option
15721258Smlf 		 * Model (MOD:)
15731258Smlf 		 */
15741258Smlf 		if ((rv = do_control_ioctl(ap_id, SATA_CFGA_GET_MODEL_INFO,
15751258Smlf 		    NULL, (void **)&str_p, &size)) != CFGA_SATA_OK) {
15761258Smlf 			(void) printf(
1577*10318SXiao-Yu.Zhang@Sun.COM 			    "SATA_CFGA_GET_MODULE_INFO ioctl failed\n");
15781258Smlf 			goto bailout;
15791258Smlf 		}
15801258Smlf 		/* drop leading and trailing spaces */
15811258Smlf 		skip = strspn(str_p, " ");
15821258Smlf 		for (i = size - 1; i >= 0; i--) {
15831258Smlf 			if (str_p[i] == '\040')
15841258Smlf 				str_p[i] = '\0';
15851258Smlf 			else if (str_p[i] != '\0')
15861258Smlf 				break;
15871258Smlf 		}
15881258Smlf 
15891258Smlf 		(void) strlcpy((*ap_id_list)->ap_info, "Mod: ",
1590*10318SXiao-Yu.Zhang@Sun.COM 		    sizeof ((*ap_id_list)->ap_info));
15911258Smlf 		(void) strlcat((*ap_id_list)->ap_info, str_p + skip,
1592*10318SXiao-Yu.Zhang@Sun.COM 		    sizeof ((*ap_id_list)->ap_info));
15931258Smlf 
15941258Smlf 		free(str_p);
15951258Smlf 
15961258Smlf 		/*
15971258Smlf 		 * Fill in the 'Information' field for the -v option
15981258Smlf 		 * Firmware revision (FREV:)
15991258Smlf 		 */
16001258Smlf 		if ((rv = do_control_ioctl(ap_id,
16011258Smlf 		    SATA_CFGA_GET_REVFIRMWARE_INFO,
16021258Smlf 		    NULL, (void **)&str_p, &size)) != CFGA_SATA_OK) {
16031258Smlf 			(void) printf(
16041258Smlf 			    "SATA_CFGA_GET_REVFIRMWARE_INFO ioctl failed\n");
16051258Smlf 			goto bailout;
16061258Smlf 		}
16071258Smlf 		/* drop leading and trailing spaces */
16081258Smlf 		skip = strspn(str_p, " ");
16091258Smlf 		for (i = size - 1; i >= 0; i--) {
16101258Smlf 			if (str_p[i] == '\040')
16111258Smlf 				str_p[i] = '\0';
16121258Smlf 			else if (str_p[i] != '\0')
16131258Smlf 				break;
16141258Smlf 		}
16151258Smlf 		(void) strlcat((*ap_id_list)->ap_info, " FRev: ",
1616*10318SXiao-Yu.Zhang@Sun.COM 		    sizeof ((*ap_id_list)->ap_info));
16171258Smlf 		(void) strlcat((*ap_id_list)->ap_info, str_p + skip,
1618*10318SXiao-Yu.Zhang@Sun.COM 		    sizeof ((*ap_id_list)->ap_info));
16191258Smlf 
16201258Smlf 		free(str_p);
16211258Smlf 
16221258Smlf 
16231258Smlf 		/*
16241258Smlf 		 * Fill in the 'Information' field for the -v option
16251258Smlf 		 * Serial Number (SN:)
16261258Smlf 		 */
16271258Smlf 		if ((rv = do_control_ioctl(ap_id,
16281258Smlf 		    SATA_CFGA_GET_SERIALNUMBER_INFO,
16291258Smlf 		    NULL, (void **)&str_p, &size)) != CFGA_SATA_OK) {
16301258Smlf 			(void) printf(
16311258Smlf 			    "SATA_CFGA_GET_SERIALNUMBER_INFO ioctl failed\n");
16321258Smlf 			goto bailout;
16331258Smlf 		}
16341258Smlf 		/* drop leading and trailing spaces */
16351258Smlf 		skip = strspn(str_p, " ");
16361258Smlf 		for (i = size - 1; i >= 0; i--) {
16371258Smlf 			if (str_p[i] == '\040')
16381258Smlf 				str_p[i] = '\0';
16391258Smlf 			else if (str_p[i] != '\0')
16401258Smlf 				break;
16411258Smlf 		}
16421258Smlf 		(void) strlcat((*ap_id_list)->ap_info, " SN: ",
1643*10318SXiao-Yu.Zhang@Sun.COM 		    sizeof ((*ap_id_list)->ap_info));
16441258Smlf 		(void) strlcat((*ap_id_list)->ap_info, str_p + skip,
1645*10318SXiao-Yu.Zhang@Sun.COM 		    sizeof ((*ap_id_list)->ap_info));
16461258Smlf 
16471258Smlf 		free(str_p);
16481258Smlf 
16491258Smlf 
16501258Smlf 
16511258Smlf 		/* Fill in ap_type which is collected from HBA driver */
16521258Smlf 		/* call do_control_ioctl TBD */
16531258Smlf 		if ((rv = do_control_ioctl(ap_id, SATA_CFGA_GET_AP_TYPE, NULL,
16541258Smlf 		    (void **)&str_p, &size)) != CFGA_SATA_OK) {
16551258Smlf 			(void) printf(
1656*10318SXiao-Yu.Zhang@Sun.COM 			    "SATA_CFGA_GET_AP_TYPE ioctl failed\n");
16571258Smlf 			goto bailout;
16581258Smlf 		}
16591258Smlf 
16601258Smlf 		(void) strlcpy((*ap_id_list)->ap_type, str_p,
1661*10318SXiao-Yu.Zhang@Sun.COM 		    sizeof ((*ap_id_list)->ap_type));
16621258Smlf 
16631258Smlf 		free(str_p);
16641258Smlf 
1665*10318SXiao-Yu.Zhang@Sun.COM 		/*
1666*10318SXiao-Yu.Zhang@Sun.COM 		 * Checking device type. Port multiplier has no dynamic
1667*10318SXiao-Yu.Zhang@Sun.COM 		 * suffix.
1668*10318SXiao-Yu.Zhang@Sun.COM 		 */
1669*10318SXiao-Yu.Zhang@Sun.COM 		if (strncmp((*ap_id_list)->ap_type, "sata-pmult",
1670*10318SXiao-Yu.Zhang@Sun.COM 		    sizeof ("sata-pmult")) == 0)
1671*10318SXiao-Yu.Zhang@Sun.COM 			pmult = B_TRUE;
1672*10318SXiao-Yu.Zhang@Sun.COM 
1673*10318SXiao-Yu.Zhang@Sun.COM 		if ((*ap_id_list)->ap_o_state == CFGA_STAT_CONFIGURED &&
1674*10318SXiao-Yu.Zhang@Sun.COM 		    pmult == B_FALSE) {
16751258Smlf 
16761258Smlf 			char *dyncomp = NULL;
16771258Smlf 
16781258Smlf 			/*
16791258Smlf 			 * This is the case where we need to generate
16801258Smlf 			 * a dynamic component of the ap_id, i.e. device.
16811258Smlf 			 */
16828010SYing.Tian@Sun.COM 			rv = sata_make_dyncomp(ap_id, &dyncomp,
16838010SYing.Tian@Sun.COM 			    (*ap_id_list)->ap_type);
16841258Smlf 			if (rv != CFGA_SATA_OK)
16851258Smlf 				goto bailout;
16861258Smlf 			if (dyncomp != NULL) {
16871258Smlf 				(void) strcat((*ap_id_list)->ap_log_id,
1688*10318SXiao-Yu.Zhang@Sun.COM 				    DYN_SEP);
16891258Smlf 				(void) strlcat((*ap_id_list)->ap_log_id,
1690*10318SXiao-Yu.Zhang@Sun.COM 				    dyncomp,
1691*10318SXiao-Yu.Zhang@Sun.COM 				    sizeof ((*ap_id_list)->ap_log_id));
16921258Smlf 				free(dyncomp);
16931258Smlf 			}
16941258Smlf 		}
16951258Smlf 
16961258Smlf 	} else {
1697*10318SXiao-Yu.Zhang@Sun.COM 		/* This is an empty port */
1698*10318SXiao-Yu.Zhang@Sun.COM 		if (get_port_num(ap_id, &port) != SATA_CFGA_OK) {
1699*10318SXiao-Yu.Zhang@Sun.COM 			goto bailout;
1700*10318SXiao-Yu.Zhang@Sun.COM 		}
1701*10318SXiao-Yu.Zhang@Sun.COM 
1702*10318SXiao-Yu.Zhang@Sun.COM 		if (port & SATA_CFGA_PMPORT_QUAL) {
1703*10318SXiao-Yu.Zhang@Sun.COM 			(void) strlcpy((*ap_id_list)->ap_type, "pmult-port",
1704*10318SXiao-Yu.Zhang@Sun.COM 			    sizeof ((*ap_id_list)->ap_type));
1705*10318SXiao-Yu.Zhang@Sun.COM 		} else {
1706*10318SXiao-Yu.Zhang@Sun.COM 			(void) strlcpy((*ap_id_list)->ap_type, "sata-port",
1707*10318SXiao-Yu.Zhang@Sun.COM 			    sizeof ((*ap_id_list)->ap_type));
1708*10318SXiao-Yu.Zhang@Sun.COM 		}
17091258Smlf 	}
17101258Smlf 
17111258Smlf 	return (sata_err_msg(errstring, rv, ap_id, errno));
17121258Smlf 
17131258Smlf bailout:
17141258Smlf 	if (*ap_id_list != NULL) {
17151258Smlf 		free(*ap_id_list);
17161258Smlf 	}
17171258Smlf 	if (ap_id_log != NULL) {
17181258Smlf 		free(ap_id_log);
17191258Smlf 	}
17201258Smlf 
17211258Smlf 	return (sata_err_msg(errstring, rv, ap_id, errno));
17221258Smlf }
17231258Smlf /*
17241258Smlf  * This routine accepts a string adn prints it using
17251258Smlf  * the message print routine argument.
17261258Smlf  */
17271258Smlf static void
cfga_msg(struct cfga_msg * msgp,const char * str)17281258Smlf cfga_msg(struct cfga_msg *msgp, const char *str)
17291258Smlf {
17301258Smlf 	int len;
17311258Smlf 	char *q;
17321258Smlf 
17331258Smlf 	if (msgp == NULL || msgp->message_routine == NULL) {
17341258Smlf 		(void) printf("cfga_msg: NULL msgp\n");
17351258Smlf 		return;
17361258Smlf 	}
17371258Smlf 
17381258Smlf 	if ((len = strlen(str)) == 0) {
17391258Smlf 		(void) printf("cfga_msg: null str\n");
17401258Smlf 		return;
17411258Smlf 	}
17421258Smlf 
17431258Smlf 	if ((q = (char *)calloc(len + 1, 1)) == NULL) {
1744*10318SXiao-Yu.Zhang@Sun.COM 		perror("cfga_msg");
17451258Smlf 		return;
17461258Smlf 	}
17471258Smlf 
17481258Smlf 	(void) strcpy(q, str);
17491258Smlf 	(*msgp->message_routine)(msgp->appdata_ptr, q);
17501258Smlf 
17511258Smlf 	free(q);
17521258Smlf }
17531258Smlf 
17541258Smlf /* cfgadm entry point */
17551258Smlf /* ARGSUSED */
17561258Smlf cfga_err_t
cfga_help(struct cfga_msg * msgp,const char * options,cfga_flags_t flags)17571258Smlf cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
17581258Smlf {
17591258Smlf 	if (options != NULL) {
17601258Smlf 		cfga_msg(msgp, dgettext(TEXT_DOMAIN, sata_help[HELP_UNKNOWN]));
17611258Smlf 		cfga_msg(msgp, options);
17621258Smlf 	}
17631258Smlf 	cfga_msg(msgp, dgettext(TEXT_DOMAIN, sata_help[HELP_HEADER]));
17641258Smlf 	cfga_msg(msgp, sata_help[HELP_CONFIG]);
17651258Smlf 	cfga_msg(msgp, sata_help[HELP_RESET_PORT]);
17661258Smlf 	cfga_msg(msgp, sata_help[HELP_RESET_DEVICE]);
17671258Smlf 	cfga_msg(msgp, sata_help[HELP_RESET_ALL]);
17681258Smlf 	cfga_msg(msgp, sata_help[HELP_PORT_ACTIVATE]);
17691258Smlf 	cfga_msg(msgp, sata_help[HELP_PORT_DEACTIVATE]);
17701258Smlf 	cfga_msg(msgp, sata_help[HELP_PORT_SELF_TEST]);
17711258Smlf 	cfga_msg(msgp, sata_help[HELP_CNTRL_SELF_TEST]);
17721258Smlf 
17731258Smlf 	return (CFGA_OK);
17741258Smlf }
17751258Smlf 
17761258Smlf 
17771258Smlf /*
17781258Smlf  * Ensure the ap_id passed is in the correct (physical ap_id) form:
17791258Smlf  *     path/device:xx[.xx]
17801258Smlf  * where xx is a one or two-digit number.
17811258Smlf  *
17821258Smlf  * Note the library always calls the plugin with a physical ap_id.
17831258Smlf  */
17841258Smlf static int
verify_valid_apid(const char * ap_id)17851258Smlf verify_valid_apid(const char *ap_id)
17861258Smlf {
17871258Smlf 	char	*l_ap_id;
17881258Smlf 
17891258Smlf 	if (ap_id == NULL)
17901258Smlf 		return (-1);
17911258Smlf 
17921258Smlf 	l_ap_id = strrchr(ap_id, (int)*MINOR_SEP);
17931258Smlf 	l_ap_id++;
17941258Smlf 
17951258Smlf 	if (strspn(l_ap_id, "0123456789.") != strlen(l_ap_id)) {
17961258Smlf 		/* Bad characters in the ap_id */
17971258Smlf 		return (-1);
17981258Smlf 	}
17991258Smlf 
18001258Smlf 	if (strstr(l_ap_id, "..") != NULL) {
18011258Smlf 		/* ap_id has 1..2 or more than 2 dots */
18021258Smlf 		return (-1);
18031258Smlf 	}
18041258Smlf 
18051258Smlf 	return (0);
18061258Smlf }
18071258Smlf 
18081258Smlf 
18091258Smlf 
18101258Smlf /*
18111258Smlf  * Verify the params passed in are valid.
18121258Smlf  */
18131258Smlf static cfga_sata_ret_t
verify_params(const char * ap_id,const char * options,char ** errstring)18141258Smlf verify_params(
1815*10318SXiao-Yu.Zhang@Sun.COM     const char *ap_id,
1816*10318SXiao-Yu.Zhang@Sun.COM     const char *options,
1817*10318SXiao-Yu.Zhang@Sun.COM     char **errstring)
18181258Smlf {
18191258Smlf 	char *pdyn, *lap_id;
18201258Smlf 	int rv;
18211258Smlf 
18221258Smlf 	if (errstring != NULL) {
18231258Smlf 		*errstring = NULL;
18241258Smlf 	}
18251258Smlf 
18261258Smlf 	if (options != NULL) {
18271258Smlf 		return (CFGA_SATA_OPTIONS);
18281258Smlf 	}
18291258Smlf 
18301258Smlf 	/* Strip dynamic AP name component if it is present. */
18311258Smlf 	lap_id = strdup(ap_id);
18321258Smlf 	if (lap_id == NULL) {
18331258Smlf 		return (CFGA_SATA_ALLOC_FAIL);
18341258Smlf 	}
18351258Smlf 	if ((pdyn = GET_DYN(lap_id)) != NULL) {
18361258Smlf 		*pdyn = '\0';
18371258Smlf 	}
18381258Smlf 
18391258Smlf 	if (verify_valid_apid(lap_id) != 0) {
18401258Smlf 		rv = CFGA_SATA_AP;
18411258Smlf 	} else {
18421258Smlf 		rv = CFGA_SATA_OK;
18431258Smlf 	}
18441258Smlf 	free(lap_id);
18451258Smlf 
18461258Smlf 	return (rv);
18471258Smlf }
18481258Smlf 
18491258Smlf /*
18501258Smlf  * Takes a validated ap_id and extracts the port number.
1851*10318SXiao-Yu.Zhang@Sun.COM  * Port multiplier is supported now.
18521258Smlf  */
18531258Smlf static cfga_sata_ret_t
get_port_num(const char * ap_id,uint32_t * port)18541258Smlf get_port_num(const char *ap_id, uint32_t *port)
18551258Smlf {
1856*10318SXiao-Yu.Zhang@Sun.COM 	uint32_t	cport, pmport = 0, qual = 0;
1857*10318SXiao-Yu.Zhang@Sun.COM 	char		*cport_str, *pmport_str;
18581258Smlf 
1859*10318SXiao-Yu.Zhang@Sun.COM 	/* Get the cport number */
1860*10318SXiao-Yu.Zhang@Sun.COM 	cport_str = strrchr(ap_id, (int)*MINOR_SEP) + strlen(MINOR_SEP);
1861*10318SXiao-Yu.Zhang@Sun.COM 
1862*10318SXiao-Yu.Zhang@Sun.COM 	errno = 0;
1863*10318SXiao-Yu.Zhang@Sun.COM 	cport = strtol(cport_str, NULL, 10);
1864*10318SXiao-Yu.Zhang@Sun.COM 	if ((cport & ~SATA_CFGA_CPORT_MASK) != 0 || errno != 0) {
1865*10318SXiao-Yu.Zhang@Sun.COM 		return (CFGA_SATA_PORT);
18661258Smlf 	}
18671258Smlf 
1868*10318SXiao-Yu.Zhang@Sun.COM 	/* Get pmport number if there is a PORT_SEPARATOR */
18691258Smlf 	errno = 0;
1870*10318SXiao-Yu.Zhang@Sun.COM 	if ((pmport_str = strrchr(ap_id, (int)*PORT_SEPARATOR)) != 0) {
1871*10318SXiao-Yu.Zhang@Sun.COM 		pmport_str += strlen(PORT_SEPARATOR);
1872*10318SXiao-Yu.Zhang@Sun.COM 		pmport = strtol(pmport_str, NULL, 10);
1873*10318SXiao-Yu.Zhang@Sun.COM 		qual = SATA_CFGA_PMPORT_QUAL;
1874*10318SXiao-Yu.Zhang@Sun.COM 		if ((pmport & ~SATA_CFGA_PMPORT_MASK) != 0 || errno != 0) {
1875*10318SXiao-Yu.Zhang@Sun.COM 			return (CFGA_SATA_PORT);
1876*10318SXiao-Yu.Zhang@Sun.COM 		}
1877*10318SXiao-Yu.Zhang@Sun.COM 	}
18781258Smlf 
1879*10318SXiao-Yu.Zhang@Sun.COM 	*port = cport | (pmport << SATA_CFGA_PMPORT_SHIFT) | qual;
18801258Smlf 	return (CFGA_SATA_OK);
18811258Smlf }
18821258Smlf 
18831258Smlf /*
18841258Smlf  * Pair of routines to set up for/clean up after a devctl_ap_* lib call.
18851258Smlf  */
18861258Smlf static void
cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl,nvlist_t * user_nvlist)18871258Smlf cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl, nvlist_t *user_nvlist)
18881258Smlf {
18891258Smlf 	if (user_nvlist != NULL) {
18901258Smlf 		nvlist_free(user_nvlist);
18911258Smlf 	}
18921258Smlf 	if (devctl_hdl != NULL) {
18931258Smlf 		devctl_release(devctl_hdl);
18941258Smlf 	}
18951258Smlf }
18961258Smlf 
18971258Smlf static cfga_sata_ret_t
setup_for_devctl_cmd(const char * ap_id,devctl_hdl_t * devctl_hdl,nvlist_t ** user_nvlistp,uint_t oflag)18981258Smlf setup_for_devctl_cmd(
1899*10318SXiao-Yu.Zhang@Sun.COM     const char *ap_id,
1900*10318SXiao-Yu.Zhang@Sun.COM     devctl_hdl_t *devctl_hdl,
1901*10318SXiao-Yu.Zhang@Sun.COM     nvlist_t **user_nvlistp,
1902*10318SXiao-Yu.Zhang@Sun.COM     uint_t oflag)
19031258Smlf {
19041258Smlf 
19051258Smlf 	uint_t	port;
19061258Smlf 	cfga_sata_ret_t	rv = CFGA_SATA_OK;
19071258Smlf 	char *lap_id, *pdyn;
19081258Smlf 
19091258Smlf 	lap_id = strdup(ap_id);
19101258Smlf 	if (lap_id == NULL)
19111258Smlf 		return (CFGA_SATA_ALLOC_FAIL);
19121258Smlf 	if ((pdyn = GET_DYN(lap_id)) != NULL) {
19131258Smlf 		*pdyn = '\0';
19141258Smlf 	}
19151258Smlf 
19161258Smlf 	/* Get a devctl handle to pass to the devctl_ap_XXX functions */
19171258Smlf 	if ((*devctl_hdl = devctl_ap_acquire((char *)lap_id, oflag)) == NULL) {
19182027Ssethg 		(void) fprintf(stderr, "[libcfgadm:sata] "
19192027Ssethg 		    "setup_for_devctl_cmd: devctl_ap_acquire failed: %s\n",
19202027Ssethg 		    strerror(errno));
19211258Smlf 		rv = CFGA_SATA_DEVCTL;
19221258Smlf 		goto bailout;
19231258Smlf 	}
19241258Smlf 
19251258Smlf 	/* Set up nvlist to pass the port number down to the driver */
19261258Smlf 	if (nvlist_alloc(user_nvlistp, NV_UNIQUE_NAME_TYPE, NULL) != 0) {
19271258Smlf 		*user_nvlistp = NULL;
19281258Smlf 		rv = CFGA_SATA_NVLIST;
19291258Smlf 		(void) printf("nvlist_alloc failed\n");
19301258Smlf 		goto bailout;
19311258Smlf 	}
19321258Smlf 
19331258Smlf 	/*
19341258Smlf 	 * Get port id, for Port Multiplier port, things could be a little bit
19351258Smlf 	 * complicated because of "port.port" format in ap_id, thus for
19361258Smlf 	 * port multiplier port, port number should be coded as 32bit int
19371258Smlf 	 * with the sig 16 bit as sata channel number, least 16 bit as
19381258Smlf 	 * the port number of sata port multiplier port.
19391258Smlf 	 */
19401258Smlf 	if ((rv = get_port_num(lap_id, &port)) != CFGA_SATA_OK) {
19411258Smlf 		(void) printf(
1942*10318SXiao-Yu.Zhang@Sun.COM 		    "setup_for_devctl_cmd: get_port_num, errno: %d\n",
1943*10318SXiao-Yu.Zhang@Sun.COM 		    errno);
19441258Smlf 		goto bailout;
19451258Smlf 	}
19461258Smlf 
19471258Smlf 	/* Creates an int32_t entry */
19481258Smlf 	if (nvlist_add_int32(*user_nvlistp, PORT, port) == -1) {
19491258Smlf 		(void) printf("nvlist_add_int32 failed\n");
19501258Smlf 		rv = CFGA_SATA_NVLIST;
19511258Smlf 		goto bailout;
19521258Smlf 	}
19531258Smlf 
19542027Ssethg 	free(lap_id);
19551258Smlf 	return (rv);
19561258Smlf 
19571258Smlf bailout:
19581258Smlf 	free(lap_id);
19591258Smlf 	(void) cleanup_after_devctl_cmd(*devctl_hdl, *user_nvlistp);
19601258Smlf 
19611258Smlf 	return (rv);
19621258Smlf }
19631258Smlf 
19641258Smlf 
19651258Smlf static cfga_sata_ret_t
port_state(devctl_hdl_t hdl,nvlist_t * list,ap_rstate_t * rstate,ap_ostate_t * ostate)19661258Smlf port_state(devctl_hdl_t hdl, nvlist_t *list,
1967*10318SXiao-Yu.Zhang@Sun.COM     ap_rstate_t *rstate, ap_ostate_t *ostate)
19681258Smlf {
19691258Smlf 	devctl_ap_state_t	devctl_ap_state;
19701258Smlf 
19711258Smlf 	if (devctl_ap_getstate(hdl, list, &devctl_ap_state) == -1) {
19721258Smlf 		(void) printf("devctl_ap_getstate failed, errno: %d\n", errno);
19731258Smlf 		return (CFGA_SATA_IOCTL);
19741258Smlf 	}
19751258Smlf 	*rstate = devctl_ap_state.ap_rstate;
19761258Smlf 	*ostate =  devctl_ap_state.ap_ostate;
19771258Smlf 	return (CFGA_SATA_OK);
19781258Smlf }
19791258Smlf 
19801258Smlf 
19811258Smlf /*
19821258Smlf  * Given a subcommand to the DEVCTL_AP_CONTROL ioctl, rquest the size of
19831258Smlf  * the data to be returned, allocate a buffer, then get the data.
19841258Smlf  * Returns *descrp (which must be freed) and size.
19851258Smlf  *
19861258Smlf  * Note SATA_DESCR_TYPE_STRING returns an ASCII NULL-terminated string,
19871258Smlf  * not a string descr.
19881258Smlf  */
19891258Smlf cfga_sata_ret_t
do_control_ioctl(const char * ap_id,sata_cfga_apctl_t subcommand,uint_t arg,void ** descrp,size_t * sizep)19901258Smlf do_control_ioctl(const char *ap_id, sata_cfga_apctl_t subcommand, uint_t arg,
1991*10318SXiao-Yu.Zhang@Sun.COM     void **descrp, size_t *sizep)
19921258Smlf {
19931258Smlf 	int			fd = -1;
19941258Smlf 	uint_t			port;
19951258Smlf 	uint32_t		local_size;
19961258Smlf 	cfga_sata_ret_t		rv = CFGA_SATA_OK;
19971258Smlf 	struct sata_ioctl_data	ioctl_data;
19981258Smlf 
19991258Smlf 	assert(descrp != NULL);
20001258Smlf 	*descrp = NULL;
20011258Smlf 	assert(sizep != NULL);
20021258Smlf 
20031258Smlf 	if ((rv = get_port_num(ap_id, &port)) != CFGA_SATA_OK) {
20041258Smlf 		goto bailout;
20051258Smlf 	}
20061258Smlf 
20071258Smlf 	if ((fd = open(ap_id, O_RDONLY)) == -1) {
20081258Smlf 		(void) printf("do_control_ioctl: open failed: errno:%d\n",
2009*10318SXiao-Yu.Zhang@Sun.COM 		    errno);
20101258Smlf 		rv = CFGA_SATA_OPEN;
20111258Smlf 		if (errno == EBUSY) {
20121258Smlf 			rv = CFGA_SATA_BUSY;
20131258Smlf 		}
20141258Smlf 		goto bailout;
20151258Smlf 	}
20161258Smlf 
20171258Smlf 	ioctl_data.cmd = subcommand;
20181258Smlf 	ioctl_data.port = port;
20191258Smlf 	ioctl_data.misc_arg = (uint_t)arg;
20201258Smlf 
20211258Smlf 	/*
20221258Smlf 	 * Find out how large a buf we need to get the data.
20231258Smlf 	 * Note the ioctls only accept/return a 32-bit int for a get_size
20241258Smlf 	 * to avoid 32/64 and BE/LE issues.
20251258Smlf 	 */
20261258Smlf 	if ((subcommand == SATA_CFGA_GET_AP_TYPE) ||
20271258Smlf 	    (subcommand == SATA_CFGA_GET_DEVICE_PATH) ||
20281258Smlf 	    (subcommand == SATA_CFGA_GET_MODEL_INFO) ||
20291258Smlf 	    (subcommand == SATA_CFGA_GET_REVFIRMWARE_INFO) ||
20301258Smlf 	    (subcommand == SATA_CFGA_GET_SERIALNUMBER_INFO)) {
20311258Smlf 		ioctl_data.get_size = B_TRUE;
20321258Smlf 		ioctl_data.buf = (caddr_t)&local_size;
20331258Smlf 		ioctl_data.bufsiz = sizeof (local_size);
20341258Smlf 
20351258Smlf 		if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
20361258Smlf 			perror("ioctl failed (size)");
20371258Smlf 			rv = CFGA_SATA_IOCTL;
20381258Smlf 			goto bailout;
20391258Smlf 		}
20401258Smlf 		*sizep = local_size;
20411258Smlf 
20421258Smlf 		if (local_size == 0) {
20431258Smlf 			(void) printf("zero length data\n");
20441258Smlf 			rv = CFGA_SATA_ZEROLEN;
20451258Smlf 			goto bailout;
20461258Smlf 		}
20471258Smlf 		if ((*descrp = malloc(*sizep)) == NULL) {
20481258Smlf 			(void) printf("do_control_ioctl: malloc failed\n");
20491258Smlf 			rv = CFGA_SATA_ALLOC_FAIL;
20501258Smlf 			goto bailout;
20511258Smlf 		}
20521258Smlf 	} else {
20531258Smlf 		*sizep = 0;
20541258Smlf 	}
20551258Smlf 	ioctl_data.get_size = B_FALSE;
20561258Smlf 	ioctl_data.buf = *descrp;
20571258Smlf 	ioctl_data.bufsiz = *sizep;
20581258Smlf 
20591258Smlf 	/* Execute IOCTL */
20601258Smlf 
20611258Smlf 	if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
20621258Smlf 		rv = CFGA_SATA_IOCTL;
20631258Smlf 		goto bailout;
20641258Smlf 	}
20651258Smlf 
20661258Smlf 	(void) close(fd);
20671258Smlf 
20681258Smlf 	return (rv);
20691258Smlf 
20701258Smlf bailout:
20711258Smlf 	if (fd != -1) {
20721258Smlf 		(void) close(fd);
20731258Smlf 	}
20741258Smlf 	if (*descrp != NULL) {
20751258Smlf 		free(*descrp);
20761258Smlf 		*descrp = NULL;
20771258Smlf 	}
20781258Smlf 
20791258Smlf 	if (rv == CFGA_SATA_IOCTL && errno == EBUSY) {
20801258Smlf 		rv = CFGA_SATA_BUSY;
20811258Smlf 	}
20821258Smlf 
20831258Smlf 	return (rv);
20841258Smlf }
20851258Smlf 
20861258Smlf 
20871258Smlf static int
sata_confirm(struct cfga_confirm * confp,char * msg)20881258Smlf sata_confirm(struct cfga_confirm *confp, char *msg)
20891258Smlf {
20901258Smlf 	int rval;
20911258Smlf 
20921258Smlf 	if (confp == NULL || confp->confirm == NULL) {
20931258Smlf 		return (0);
20941258Smlf 	}
20951258Smlf 	rval = (*confp->confirm)(confp->appdata_ptr, msg);
20961258Smlf 
20971258Smlf 	return (rval);
20981258Smlf }
20991258Smlf 
21001258Smlf 
21011258Smlf static char *
sata_get_devicepath(const char * ap_id)21021258Smlf sata_get_devicepath(const char *ap_id)
21031258Smlf {
21041258Smlf 	char		*devpath = NULL;
21051258Smlf 	size_t		size;
21061258Smlf 	cfga_sata_ret_t	rv;
21071258Smlf 
21081258Smlf 	rv = do_control_ioctl(ap_id, SATA_CFGA_GET_DEVICE_PATH, NULL,
21091258Smlf 	    (void **)&devpath, &size);
21101258Smlf 
21111258Smlf 	if (rv == CFGA_SATA_OK) {
21121258Smlf 		return (devpath);
21131258Smlf 	} else {
21141258Smlf 		return ((char *)NULL);
21151258Smlf 	}
21161258Smlf 
21171258Smlf }
2118