166f9d5cbSmlf /*
266f9d5cbSmlf  * CDDL HEADER START
366f9d5cbSmlf  *
466f9d5cbSmlf  * The contents of this file are subject to the terms of the
566f9d5cbSmlf  * Common Development and Distribution License (the "License").
666f9d5cbSmlf  * You may not use this file except in compliance with the License.
766f9d5cbSmlf  *
866f9d5cbSmlf  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
966f9d5cbSmlf  * or http://www.opensolaris.org/os/licensing.
1066f9d5cbSmlf  * See the License for the specific language governing permissions
1166f9d5cbSmlf  * and limitations under the License.
1266f9d5cbSmlf  *
1366f9d5cbSmlf  * When distributing Covered Code, include this CDDL HEADER in each
1466f9d5cbSmlf  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1566f9d5cbSmlf  * If applicable, add the following below this CDDL HEADER, with the
1666f9d5cbSmlf  * fields enclosed by brackets "[]" replaced with your own identifying
1766f9d5cbSmlf  * information: Portions Copyright [yyyy] [name of copyright owner]
1866f9d5cbSmlf  *
1966f9d5cbSmlf  * CDDL HEADER END
2066f9d5cbSmlf  */
2166f9d5cbSmlf 
2266f9d5cbSmlf /*
238aa6aadbSXiao-Yu Zhang  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2466f9d5cbSmlf  * Use is subject to license terms.
25cf252232SAndy Fiddaman  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
2661233e71SJohn Levon  * Copyright (c) 2018, Joyent, Inc.
2766f9d5cbSmlf  */
2866f9d5cbSmlf 
29724365f7Ssethg #include <sys/param.h>
30724365f7Ssethg #include <sys/stat.h>
31724365f7Ssethg #include <errno.h>
32724365f7Ssethg #include <string.h>
33724365f7Ssethg #include <dirent.h>
3466f9d5cbSmlf #include "cfga_sata.h"
3566f9d5cbSmlf 
3666f9d5cbSmlf /*
3766f9d5cbSmlf  * This file contains the entry points to the plug-in as defined in the
38*bbf21555SRichard Lowe  * config_admin(3CFGADM) man page.
3966f9d5cbSmlf  */
4066f9d5cbSmlf 
4166f9d5cbSmlf /*
4266f9d5cbSmlf  * Set the version number for the cfgadm library's use.
4366f9d5cbSmlf  */
4466f9d5cbSmlf int cfga_version = CFGA_HSL_V2;
4566f9d5cbSmlf 
4666f9d5cbSmlf enum {
4766f9d5cbSmlf 	HELP_HEADER = 1,
4866f9d5cbSmlf 	HELP_CONFIG,
4966f9d5cbSmlf 	HELP_RESET_PORT,
5066f9d5cbSmlf 	HELP_RESET_DEVICE,
5166f9d5cbSmlf 	HELP_RESET_ALL,
5266f9d5cbSmlf 	HELP_PORT_DEACTIVATE,
5366f9d5cbSmlf 	HELP_PORT_ACTIVATE,
5466f9d5cbSmlf 	HELP_PORT_SELF_TEST,
5566f9d5cbSmlf 	HELP_CNTRL_SELF_TEST,
5666f9d5cbSmlf 	HELP_UNKNOWN
5766f9d5cbSmlf };
5866f9d5cbSmlf 
5966f9d5cbSmlf /* SATA specific help messages */
6066f9d5cbSmlf static char *sata_help[] = {
6166f9d5cbSmlf 	NULL,
6266f9d5cbSmlf 	"SATA specific commands:\n",
6366f9d5cbSmlf 	" cfgadm -c [configure|unconfigure|disconnect|connect] ap_id "
648aa6aadbSXiao-Yu Zhang 	    "[ap_id...]\n",
6566f9d5cbSmlf 	" cfgadm -x sata_reset_port ap_id  [ap_id...]\n",
6666f9d5cbSmlf 	" cfgadm -x sata_reset_device ap_id [ap_id...]\n",
6766f9d5cbSmlf 	" cfgadm -x sata_reset_all ap_id\n",
6866f9d5cbSmlf 	" cfgadm -x sata_port_deactivate ap_id [ap_id...]\n",
6966f9d5cbSmlf 	" cfgadm -x sata_port_activate ap_id [ap_id...]\n",
7066f9d5cbSmlf 	" cfgadm -x sata_port_self_test ap_id [ap_id...]\n",
7166f9d5cbSmlf 	" cfgadm -t ap_id\n",
7266f9d5cbSmlf 	"\tunknown command or option:\n",
7366f9d5cbSmlf 	NULL
7466f9d5cbSmlf };	/* End help messages */
7566f9d5cbSmlf 
7666f9d5cbSmlf 
7766f9d5cbSmlf /*
7866f9d5cbSmlf  * Messages.
7966f9d5cbSmlf  */
8066f9d5cbSmlf static msgcvt_t sata_msgs[] = {
8166f9d5cbSmlf 	/* CFGA_SATA_OK */
8266f9d5cbSmlf 	{ CVT, CFGA_OK, "" },
8366f9d5cbSmlf 
8466f9d5cbSmlf 	/* CFGA_SATA_NACK */
8566f9d5cbSmlf 	{ CVT, CFGA_NACK, "" },
8666f9d5cbSmlf 
8766f9d5cbSmlf 	/* CFGA_SATA_DEVICE_UNCONFIGURED */
8866f9d5cbSmlf 	{ CVT, CFGA_OK, "Device unconfigured prior to disconnect" },
8966f9d5cbSmlf 
9066f9d5cbSmlf 	/* CFGA_SATA_UNKNOWN / CFGA_LIB_ERROR -> "Library error" */
9166f9d5cbSmlf 	{ CVT, CFGA_LIB_ERROR, "Unknown message; internal error" },
9266f9d5cbSmlf 
9366f9d5cbSmlf 	/* CFGA_SATA_INTERNAL_ERROR / CFGA_LIB_ERROR -> "Library error" */
9466f9d5cbSmlf 	{ CVT, CFGA_LIB_ERROR, "Internal error" },
9566f9d5cbSmlf 
9666f9d5cbSmlf 	/* CFGA_SATA_DATA_ERROR / CFGA_DATA_ERROR -> "Data error" */
9766f9d5cbSmlf 	{ CVT, CFGA_DATA_ERROR, "cfgadm data error" },
9866f9d5cbSmlf 
9966f9d5cbSmlf 	/* CFGA_SATA_OPTIONS / CFGA_ERROR -> "Hardware specific failure" */
10066f9d5cbSmlf 	{ CVT, CFGA_ERROR, "Hardware specific option not supported" },
10166f9d5cbSmlf 
10266f9d5cbSmlf 	/* CFGA_SATA_HWOPNOTSUPP / CFGA_ERROR -> "Hardware specific failure" */
10366f9d5cbSmlf 	{ CVT, CFGA_ERROR, "Hardware specific operation not supported" },
10466f9d5cbSmlf 
10566f9d5cbSmlf 	/*
10666f9d5cbSmlf 	 * CFGA_SATA_DYNAMIC_AP /
10766f9d5cbSmlf 	 * CFGA_LIB_ERROR -> "Configuration operation invalid"
10866f9d5cbSmlf 	 */
10966f9d5cbSmlf 	{ CVT, CFGA_INVAL, "Cannot identify attached device" },
11066f9d5cbSmlf 
11166f9d5cbSmlf 	/* CFGA_SATA_AP / CFGA_APID_NOEXIST -> "Attachment point not found" */
11266f9d5cbSmlf 	{ CVT, CFGA_APID_NOEXIST, "" },
11366f9d5cbSmlf 
11466f9d5cbSmlf 	/* CFGA_SATA_PORT / CFGA_LIB_ERROR -> "Library error" */
11566f9d5cbSmlf 	{ CVT, CFGA_LIB_ERROR, "Cannot determine sata port number for " },
11666f9d5cbSmlf 
11766f9d5cbSmlf 	/* CFGA_SATA_DEVCTL / CFGA_LIB_ERROR -> "Library error" */
11866f9d5cbSmlf 	{ CVT, CFGA_LIB_ERROR, "Internal error: "
1198aa6aadbSXiao-Yu Zhang 		"Cannot allocate devctl handle " },
12066f9d5cbSmlf 
12166f9d5cbSmlf 	/*
12266f9d5cbSmlf 	 * CFGA_SATA_DEV_CONFIGURE /
12366f9d5cbSmlf 	 * CFGA_ERROR -> "Hardware specific failure"
12466f9d5cbSmlf 	 */
12566f9d5cbSmlf 	{ CVT, CFGA_ERROR, "Failed to config device at " },
12666f9d5cbSmlf 
12766f9d5cbSmlf 	/*
12866f9d5cbSmlf 	 * CFGA_SATA_DEV_UNCONFIGURE /
12966f9d5cbSmlf 	 * CFGA_ERROR -> "Hardware specific failure"
13066f9d5cbSmlf 	 */
13166f9d5cbSmlf 	{ CVT, CFGA_ERROR, "Failed to unconfig device at " },
13266f9d5cbSmlf 
13366f9d5cbSmlf 	/*
13466f9d5cbSmlf 	 * CFGA_SATA_DISCONNECTED
13566f9d5cbSmlf 	 * CFGA_INVAL -> "Configuration operation invalid"
13666f9d5cbSmlf 	 */
13766f9d5cbSmlf 	{ CVT, CFGA_INVAL, "Port already disconnected " },
13866f9d5cbSmlf 
13966f9d5cbSmlf 	/*
14066f9d5cbSmlf 	 * CFGA_SATA_NOT_CONNECTED
14166f9d5cbSmlf 	 * CFGA_INVAL -> "Configuration operation invalid"
14266f9d5cbSmlf 	 */
14366f9d5cbSmlf 	{ CVT, CFGA_INVAL, "No device connected to " },
14466f9d5cbSmlf 
14566f9d5cbSmlf 	/*
14666f9d5cbSmlf 	 * CFGA_SATA_NOT_CONFIGURED /
14766f9d5cbSmlf 	 * CFGA_INVAL -> "Configuration operation invalid"
14866f9d5cbSmlf 	 */
14966f9d5cbSmlf 	{ CVT, CFGA_INVAL, "No device configured at " },
15066f9d5cbSmlf 
15166f9d5cbSmlf 	/*
15266f9d5cbSmlf 	 * CFGA_SATA_ALREADY_CONNECTED /
15366f9d5cbSmlf 	 * CFGA_INVAL -> "Configuration operation invalid"
15466f9d5cbSmlf 	 */
15566f9d5cbSmlf 	{ CVT, CFGA_INVAL, "Device already connected to " },
15666f9d5cbSmlf 
15766f9d5cbSmlf 	/*
15866f9d5cbSmlf 	 * CFGA_SATA_ALREADY_CONFIGURED /
15966f9d5cbSmlf 	 * CFGA_INVAL -> "Configuration operation invalid"
16066f9d5cbSmlf 	 */
16166f9d5cbSmlf 	{ CVT, CFGA_INVAL, "Device already configured at " },
16266f9d5cbSmlf 
16366f9d5cbSmlf 	/*
16466f9d5cbSmlf 	 * CFGA_SATA_INVALID_DEVNAME /
16566f9d5cbSmlf 	 * CFGA_INVAL -> "Configuration operation invalid"
16666f9d5cbSmlf 	 */
16766f9d5cbSmlf 	{ CVT, CFGA_INVAL, "Cannot specify device name" },
16866f9d5cbSmlf 
16966f9d5cbSmlf 	/* CFGA_SATA_OPEN / CFGA_LIB_ERROR -> "Library error" */
17066f9d5cbSmlf 	{ CVT, CFGA_LIB_ERROR, "Cannot open " },
17166f9d5cbSmlf 
17266f9d5cbSmlf 	/* CFGA_SATA_IOCTL / CFGA_ERROR -> "Hardware specific failure"  */
17366f9d5cbSmlf 	{ CVT, CFGA_ERROR, "Driver ioctl failed " },
17466f9d5cbSmlf 
17566f9d5cbSmlf 	/*
17666f9d5cbSmlf 	 * CFGA_SATA_BUSY /
17766f9d5cbSmlf 	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
17866f9d5cbSmlf 	 */
17966f9d5cbSmlf 	{ CVT, CFGA_SYSTEM_BUSY, "" },
18066f9d5cbSmlf 
18166f9d5cbSmlf 	/* CFGA_SATA_ALLOC_FAIL / CFGA_LIB_ERROR -> "Library error" */
18266f9d5cbSmlf 	{ CVT, CFGA_LIB_ERROR, "Memory allocation failure" },
18366f9d5cbSmlf 
18466f9d5cbSmlf 	/*
18566f9d5cbSmlf 	 * CFGA_SATA_OPNOTSUPP /
18666f9d5cbSmlf 	 * CFGA_OPNOTSUPP -> "Configuration operation not supported"
18766f9d5cbSmlf 	 */
18866f9d5cbSmlf 	{ CVT, CFGA_OPNOTSUPP, "Operation not supported" },
18966f9d5cbSmlf 
19066f9d5cbSmlf 	/* CFGA_SATA_DEVLINK / CFGA_LIB_ERROR -> "Library error" */
19166f9d5cbSmlf 	{ CVT, CFGA_LIB_ERROR, "Could not find /dev/cfg link for " },
19266f9d5cbSmlf 
19366f9d5cbSmlf 	/* CFGA_SATA_STATE / CFGA_LIB_ERROR -> "Library error" */
19466f9d5cbSmlf 	{ CVT, CFGA_LIB_ERROR, "Internal error: Unrecognized ap state" },
19566f9d5cbSmlf 
19666f9d5cbSmlf 	/* CFGA_SATA_PRIV / CFGA_PRIV -> "Insufficient privileges" */
19766f9d5cbSmlf 	{ CVT, CFGA_PRIV, "" },
19866f9d5cbSmlf 
19966f9d5cbSmlf 	/* CFGA_SATA_NVLIST / CFGA_ERROR -> "Hardware specific failure" */
20066f9d5cbSmlf 	{ CVT, CFGA_ERROR, "Internal error (nvlist)" },
20166f9d5cbSmlf 
20266f9d5cbSmlf 	/* CFGA_SATA_ZEROLEN / CFGA_ERROR -> "Hardware specific failure" */
20366f9d5cbSmlf 	{ CVT, CFGA_ERROR, "Internal error (zerolength string)" },
20466f9d5cbSmlf 
20566f9d5cbSmlf 	/* CFGA_SATA_RCM_HANDLE / CFGA_ERROR -> "Hardware specific failure" */
20666f9d5cbSmlf 	{ CVT, CFGA_ERROR, "cannot get RCM handle"},
20766f9d5cbSmlf 
20866f9d5cbSmlf 	/*
20966f9d5cbSmlf 	 * CFGA_SATA_RCM_ONLINE /
21066f9d5cbSmlf 	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
21166f9d5cbSmlf 	 */
21266f9d5cbSmlf 	{ CVT, CFGA_SYSTEM_BUSY, "failed to online: "},
21366f9d5cbSmlf 
21466f9d5cbSmlf 	/*
21566f9d5cbSmlf 	 * CFGA_SATA_RCM_OFFLINE /
21666f9d5cbSmlf 	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
21766f9d5cbSmlf 	 */
21866f9d5cbSmlf 	{ CVT, CFGA_SYSTEM_BUSY, "failed to offline: "},
21966f9d5cbSmlf 
22066f9d5cbSmlf 	/* CFGA_SATA_RCM_INFO / CFGA_ERROR -> "Hardware specific failure" */
22166f9d5cbSmlf 	{ CVT, CFGA_ERROR, "failed to query: "}
22266f9d5cbSmlf 
22366f9d5cbSmlf };	/* End error messages */
22466f9d5cbSmlf 
22566f9d5cbSmlf static cfga_sata_ret_t
22666f9d5cbSmlf verify_params(const char *ap_id, const char *options, char **errstring);
22766f9d5cbSmlf 
22866f9d5cbSmlf 
22966f9d5cbSmlf static cfga_sata_ret_t
23066f9d5cbSmlf setup_for_devctl_cmd(const char *ap_id, devctl_hdl_t *devctl_hdl,
2318aa6aadbSXiao-Yu Zhang     nvlist_t **user_nvlistp, uint_t oflag);
23266f9d5cbSmlf 
23366f9d5cbSmlf static cfga_sata_ret_t
23466f9d5cbSmlf port_state(devctl_hdl_t hdl, nvlist_t *list,
2358aa6aadbSXiao-Yu Zhang     ap_rstate_t *rstate, ap_ostate_t *ostate);
23666f9d5cbSmlf 
23766f9d5cbSmlf static cfga_sata_ret_t
23866f9d5cbSmlf do_control_ioctl(const char *ap_id, sata_cfga_apctl_t subcommand, uint_t arg,
2398aa6aadbSXiao-Yu Zhang     void **descrp, size_t *sizep);
24066f9d5cbSmlf 
24166f9d5cbSmlf static void
24266f9d5cbSmlf cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl, nvlist_t *user_nvlist);
24366f9d5cbSmlf 
24466f9d5cbSmlf static char *
24566f9d5cbSmlf sata_get_devicepath(const char *ap_id);
24666f9d5cbSmlf 
24766f9d5cbSmlf static int
24866f9d5cbSmlf sata_confirm(struct cfga_confirm *confp, char *msg);
24966f9d5cbSmlf 
2508aa6aadbSXiao-Yu Zhang static cfga_sata_ret_t
2518aa6aadbSXiao-Yu Zhang get_port_num(const char *ap_id, uint32_t *port);
25266f9d5cbSmlf 
25366f9d5cbSmlf /* Utilities */
25466f9d5cbSmlf 
255724365f7Ssethg static cfga_sata_ret_t
physpath_to_devlink(const char * basedir,const char * node_path,char ** logpp,int * l_errnop)256724365f7Ssethg physpath_to_devlink(const char *basedir, const char *node_path,
257724365f7Ssethg     char **logpp, int *l_errnop)
25866f9d5cbSmlf {
259724365f7Ssethg 	char *linkpath;
260724365f7Ssethg 	char *buf;
261724365f7Ssethg 	char *real_path;
262724365f7Ssethg 	DIR *dp;
263724365f7Ssethg 	struct dirent *dep, *newdep;
264724365f7Ssethg 	int deplen;
265724365f7Ssethg 	boolean_t found = B_FALSE;
266724365f7Ssethg 	int err = 0;
267724365f7Ssethg 	struct stat sb;
268724365f7Ssethg 	char *p;
269724365f7Ssethg 	cfga_sata_ret_t rv = CFGA_SATA_INTERNAL_ERROR;
27066f9d5cbSmlf 
27166f9d5cbSmlf 	/*
272724365f7Ssethg 	 * Using libdevinfo for this is overkill and kills performance
273724365f7Ssethg 	 * when multiple consumers of libcfgadm are executing
274724365f7Ssethg 	 * concurrently.
27566f9d5cbSmlf 	 */
276724365f7Ssethg 	if ((dp = opendir(basedir)) == NULL) {
277724365f7Ssethg 		*l_errnop = errno;
278724365f7Ssethg 		return (CFGA_SATA_INTERNAL_ERROR);
279724365f7Ssethg 	}
28066f9d5cbSmlf 
281724365f7Ssethg 	linkpath = malloc(PATH_MAX);
282724365f7Ssethg 	buf = malloc(PATH_MAX);
283724365f7Ssethg 	real_path = malloc(PATH_MAX);
28466f9d5cbSmlf 
285d3080269Ssethg 	deplen = pathconf(basedir, _PC_NAME_MAX);
286d3080269Ssethg 	deplen = (deplen <= 0 ? MAXNAMELEN : deplen) +
287724365f7Ssethg 	    sizeof (struct dirent);
288724365f7Ssethg 	dep = (struct dirent *)malloc(deplen);
289724365f7Ssethg 
290724365f7Ssethg 	if (dep == NULL || linkpath == NULL || buf == NULL ||
291724365f7Ssethg 	    real_path == NULL) {
292724365f7Ssethg 		*l_errnop = ENOMEM;
293724365f7Ssethg 		rv = CFGA_SATA_ALLOC_FAIL;
294724365f7Ssethg 		goto pp_cleanup;
29566f9d5cbSmlf 	}
29666f9d5cbSmlf 
297724365f7Ssethg 	*logpp = NULL;
29866f9d5cbSmlf 
299724365f7Ssethg 	while (!found && (err = readdir_r(dp, dep, &newdep)) == 0 &&
300724365f7Ssethg 	    newdep != NULL) {
30166f9d5cbSmlf 
302724365f7Ssethg 		assert(newdep == dep);
30366f9d5cbSmlf 
304724365f7Ssethg 		if (strcmp(dep->d_name, ".") == 0 ||
305724365f7Ssethg 		    strcmp(dep->d_name, "..") == 0)
306724365f7Ssethg 			continue;
30766f9d5cbSmlf 
308724365f7Ssethg 		(void) snprintf(linkpath, MAXPATHLEN,
309724365f7Ssethg 		    "%s/%s", basedir, dep->d_name);
31066f9d5cbSmlf 
311724365f7Ssethg 		if (lstat(linkpath, &sb) < 0)
312724365f7Ssethg 			continue;
313724365f7Ssethg 
314724365f7Ssethg 		if (S_ISDIR(sb.st_mode)) {
315724365f7Ssethg 
316724365f7Ssethg 			if ((rv = physpath_to_devlink(linkpath, node_path,
317724365f7Ssethg 			    logpp, l_errnop)) != CFGA_SATA_OK) {
318724365f7Ssethg 
319724365f7Ssethg 				goto pp_cleanup;
320724365f7Ssethg 			}
321724365f7Ssethg 
322724365f7Ssethg 			if (*logpp != NULL)
323724365f7Ssethg 				found = B_TRUE;
324724365f7Ssethg 
325724365f7Ssethg 		} else if (S_ISLNK(sb.st_mode)) {
326724365f7Ssethg 
327724365f7Ssethg 			bzero(buf, PATH_MAX);
328724365f7Ssethg 			if (readlink(linkpath, buf, PATH_MAX) < 0)
329724365f7Ssethg 				continue;
330724365f7Ssethg 
331724365f7Ssethg 
332724365f7Ssethg 			/*
333724365f7Ssethg 			 * realpath() is too darn slow, so fake
334724365f7Ssethg 			 * it, by using what we know about /dev
335724365f7Ssethg 			 * links: they are always of the form:
336724365f7Ssethg 			 * <"../">+/devices/<path>
337724365f7Ssethg 			 */
338724365f7Ssethg 			p = buf;
339724365f7Ssethg 			while (strncmp(p, "../", 3) == 0)
340724365f7Ssethg 				p += 3;
341724365f7Ssethg 
342724365f7Ssethg 			if (p != buf)
343724365f7Ssethg 				p--;	/* back up to get a slash */
344724365f7Ssethg 
345724365f7Ssethg 			assert (*p == '/');
346724365f7Ssethg 
347724365f7Ssethg 			if (strcmp(p, node_path) == 0) {
348724365f7Ssethg 				*logpp = strdup(linkpath);
349724365f7Ssethg 				if (*logpp == NULL) {
350724365f7Ssethg 
351724365f7Ssethg 					rv = CFGA_SATA_ALLOC_FAIL;
352724365f7Ssethg 					goto pp_cleanup;
353724365f7Ssethg 				}
354724365f7Ssethg 
355724365f7Ssethg 				found = B_TRUE;
356724365f7Ssethg 			}
357724365f7Ssethg 		}
35866f9d5cbSmlf 	}
35966f9d5cbSmlf 
360724365f7Ssethg 	free(linkpath);
361724365f7Ssethg 	free(buf);
362724365f7Ssethg 	free(real_path);
363724365f7Ssethg 	free(dep);
364724365f7Ssethg 	(void) closedir(dp);
36566f9d5cbSmlf 
366724365f7Ssethg 	if (err != 0) {
367724365f7Ssethg 		*l_errnop = err;
368724365f7Ssethg 		return (CFGA_SATA_INTERNAL_ERROR);
36966f9d5cbSmlf 	}
37066f9d5cbSmlf 
371724365f7Ssethg 	return (CFGA_SATA_OK);
372724365f7Ssethg 
373724365f7Ssethg pp_cleanup:
374724365f7Ssethg 
375724365f7Ssethg 	if (dp)
376724365f7Ssethg 		(void) closedir(dp);
377724365f7Ssethg 	if (dep)
378724365f7Ssethg 		free(dep);
379724365f7Ssethg 	if (linkpath)
380724365f7Ssethg 		free(linkpath);
381724365f7Ssethg 	if (buf)
382724365f7Ssethg 		free(buf);
383724365f7Ssethg 	if (real_path)
384724365f7Ssethg 		free(real_path);
385724365f7Ssethg 	if (*logpp) {
386724365f7Ssethg 		free(*logpp);
387724365f7Ssethg 		*logpp = NULL;
388724365f7Ssethg 	}
389724365f7Ssethg 	return (rv);
39066f9d5cbSmlf }
39166f9d5cbSmlf 
39266f9d5cbSmlf 
39366f9d5cbSmlf /*
39466f9d5cbSmlf  * Given the index into a table (msgcvt_t) of messages, get the message
39566f9d5cbSmlf  * string, converting it to the proper locale if necessary.
39666f9d5cbSmlf  * NOTE: Indexes are defined in cfga_sata.h
39766f9d5cbSmlf  */
39866f9d5cbSmlf static const char *
get_msg(uint_t msg_index,msgcvt_t * msg_tbl,uint_t tbl_size)39966f9d5cbSmlf get_msg(uint_t msg_index, msgcvt_t *msg_tbl, uint_t tbl_size)
40066f9d5cbSmlf {
40166f9d5cbSmlf 	if (msg_index >= tbl_size) {
40266f9d5cbSmlf 		msg_index = CFGA_SATA_UNKNOWN;
40366f9d5cbSmlf 	}
40466f9d5cbSmlf 
40566f9d5cbSmlf 	return ((msg_tbl[msg_index].intl) ?
4068aa6aadbSXiao-Yu Zhang 	    dgettext(TEXT_DOMAIN, msg_tbl[msg_index].msgstr) :
4078aa6aadbSXiao-Yu Zhang 	    msg_tbl[msg_index].msgstr);
40866f9d5cbSmlf }
40966f9d5cbSmlf 
41066f9d5cbSmlf /*
41166f9d5cbSmlf  * Allocates and creates a message string (in *ret_str),
41266f9d5cbSmlf  * by concatenating all the (char *) args together, in order.
41366f9d5cbSmlf  * Last arg MUST be NULL.
41466f9d5cbSmlf  */
41566f9d5cbSmlf static void
set_msg(char ** ret_str,...)41666f9d5cbSmlf set_msg(char **ret_str, ...)
41766f9d5cbSmlf {
41866f9d5cbSmlf 	char    *str;
41966f9d5cbSmlf 	size_t  total_len;
42066f9d5cbSmlf 	va_list valist;
42166f9d5cbSmlf 
42266f9d5cbSmlf 	va_start(valist, ret_str);
42366f9d5cbSmlf 
42466f9d5cbSmlf 	total_len = (*ret_str == NULL) ? 0 : strlen(*ret_str);
42566f9d5cbSmlf 
42666f9d5cbSmlf 	while ((str = va_arg(valist, char *)) != NULL) {
42766f9d5cbSmlf 		size_t  len = strlen(str);
42866f9d5cbSmlf 		char    *old_str = *ret_str;
42966f9d5cbSmlf 
43066f9d5cbSmlf 		*ret_str = (char *)realloc(*ret_str, total_len + len + 1);
43166f9d5cbSmlf 		if (*ret_str == NULL) {
43266f9d5cbSmlf 			/* We're screwed */
43366f9d5cbSmlf 			free(old_str);
43466f9d5cbSmlf 			va_end(valist);
43566f9d5cbSmlf 			return;
43666f9d5cbSmlf 		}
43766f9d5cbSmlf 
43866f9d5cbSmlf 		(void) strcpy(*ret_str + total_len, str);
43966f9d5cbSmlf 		total_len += len;
44066f9d5cbSmlf 	}
44166f9d5cbSmlf 
44266f9d5cbSmlf 	va_end(valist);
44366f9d5cbSmlf }
44466f9d5cbSmlf 
44566f9d5cbSmlf /*
44666f9d5cbSmlf  * Error message handling.
44766f9d5cbSmlf  * For the rv passed in, looks up the corresponding error message string(s),
44866f9d5cbSmlf  * internationalized if necessary, and concatenates it into a new
44966f9d5cbSmlf  * memory buffer, and points *errstring to it.
45066f9d5cbSmlf  * Note not all rvs will result in an error message return, as not all
45166f9d5cbSmlf  * error conditions warrant a SATA-specific error message - for those
45266f9d5cbSmlf  * conditions the cfgadm generic messages are sufficient.
45366f9d5cbSmlf  *
45466f9d5cbSmlf  * Some messages may display ap_id or errno, which is why they are passed
45566f9d5cbSmlf  * in.
45666f9d5cbSmlf  */
45766f9d5cbSmlf 
45866f9d5cbSmlf cfga_err_t
sata_err_msg(char ** errstring,cfga_sata_ret_t rv,const char * ap_id,int l_errno)45966f9d5cbSmlf sata_err_msg(
4608aa6aadbSXiao-Yu Zhang     char **errstring,
4618aa6aadbSXiao-Yu Zhang     cfga_sata_ret_t rv,
4628aa6aadbSXiao-Yu Zhang     const char *ap_id,
4638aa6aadbSXiao-Yu Zhang     int l_errno)
46466f9d5cbSmlf {
46566f9d5cbSmlf 	if (errstring == NULL) {
46666f9d5cbSmlf 		return (sata_msgs[rv].cfga_err);
46766f9d5cbSmlf 	}
46866f9d5cbSmlf 
46966f9d5cbSmlf 	/*
47066f9d5cbSmlf 	 * Generate the appropriate SATA-specific error message(s) (if any).
47166f9d5cbSmlf 	 */
47266f9d5cbSmlf 	switch (rv) {
47366f9d5cbSmlf 	case CFGA_SATA_OK:
47466f9d5cbSmlf 	case CFGA_NACK:
47566f9d5cbSmlf 		/* Special case - do nothing.  */
47666f9d5cbSmlf 		break;
47766f9d5cbSmlf 
47866f9d5cbSmlf 	case CFGA_SATA_UNKNOWN:
47966f9d5cbSmlf 	case CFGA_SATA_DYNAMIC_AP:
48066f9d5cbSmlf 	case CFGA_SATA_INTERNAL_ERROR:
48166f9d5cbSmlf 	case CFGA_SATA_OPTIONS:
48266f9d5cbSmlf 	case CFGA_SATA_ALLOC_FAIL:
48366f9d5cbSmlf 	case CFGA_SATA_STATE:
48466f9d5cbSmlf 	case CFGA_SATA_PRIV:
48566f9d5cbSmlf 	case CFGA_SATA_OPNOTSUPP:
48666f9d5cbSmlf 	case CFGA_SATA_DATA_ERROR:
48766f9d5cbSmlf 		/* These messages require no additional strings passed. */
48866f9d5cbSmlf 		set_msg(errstring, ERR_STR(rv), NULL);
48966f9d5cbSmlf 		break;
49066f9d5cbSmlf 
49166f9d5cbSmlf 	case CFGA_SATA_HWOPNOTSUPP:
49266f9d5cbSmlf 		/* hardware-specific help needed */
49366f9d5cbSmlf 		set_msg(errstring, ERR_STR(rv), NULL);
49466f9d5cbSmlf 		set_msg(errstring, "\n",
4958aa6aadbSXiao-Yu Zhang 		    dgettext(TEXT_DOMAIN, sata_help[HELP_HEADER]), NULL);
49666f9d5cbSmlf 		set_msg(errstring, sata_help[HELP_RESET_PORT], NULL);
49766f9d5cbSmlf 		set_msg(errstring, sata_help[HELP_RESET_DEVICE], NULL);
49866f9d5cbSmlf 		set_msg(errstring, sata_help[HELP_RESET_ALL],  NULL);
49966f9d5cbSmlf 		set_msg(errstring, sata_help[HELP_PORT_ACTIVATE], NULL);
50066f9d5cbSmlf 		set_msg(errstring, sata_help[HELP_PORT_DEACTIVATE], NULL);
50166f9d5cbSmlf 		set_msg(errstring, sata_help[HELP_PORT_SELF_TEST], NULL);
50266f9d5cbSmlf 		set_msg(errstring, sata_help[HELP_CNTRL_SELF_TEST], NULL);
50366f9d5cbSmlf 		break;
50466f9d5cbSmlf 
50566f9d5cbSmlf 	case CFGA_SATA_AP:
50666f9d5cbSmlf 	case CFGA_SATA_PORT:
50766f9d5cbSmlf 	case CFGA_SATA_NOT_CONNECTED:
50866f9d5cbSmlf 	case CFGA_SATA_NOT_CONFIGURED:
50966f9d5cbSmlf 	case CFGA_SATA_ALREADY_CONNECTED:
51066f9d5cbSmlf 	case CFGA_SATA_ALREADY_CONFIGURED:
51166f9d5cbSmlf 	case CFGA_SATA_BUSY:
51266f9d5cbSmlf 	case CFGA_SATA_DEVLINK:
51366f9d5cbSmlf 	case CFGA_SATA_RCM_HANDLE:
51466f9d5cbSmlf 	case CFGA_SATA_RCM_ONLINE:
51566f9d5cbSmlf 	case CFGA_SATA_RCM_OFFLINE:
51666f9d5cbSmlf 	case CFGA_SATA_RCM_INFO:
51766f9d5cbSmlf 	case CFGA_SATA_DEV_CONFIGURE:
51866f9d5cbSmlf 	case CFGA_SATA_DEV_UNCONFIGURE:
51966f9d5cbSmlf 	case CFGA_SATA_DISCONNECTED:
52066f9d5cbSmlf 		/* These messages also print ap_id.  */
52166f9d5cbSmlf 		set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "", NULL);
52266f9d5cbSmlf 		break;
52366f9d5cbSmlf 
52466f9d5cbSmlf 
52566f9d5cbSmlf 	case CFGA_SATA_IOCTL:
52666f9d5cbSmlf 	case CFGA_SATA_NVLIST:
52766f9d5cbSmlf 		/* These messages also print errno.  */
52866f9d5cbSmlf 		{
52966f9d5cbSmlf 			char *errno_str = l_errno ? strerror(l_errno) : "";
53066f9d5cbSmlf 
53166f9d5cbSmlf 			set_msg(errstring, ERR_STR(rv), errno_str,
53266f9d5cbSmlf 			    l_errno ? "\n" : "", NULL);
53366f9d5cbSmlf 			break;
53466f9d5cbSmlf 		}
53566f9d5cbSmlf 
53666f9d5cbSmlf 	case CFGA_SATA_OPEN:
53766f9d5cbSmlf 		/* These messages also apid and errno.  */
53866f9d5cbSmlf 		{
53966f9d5cbSmlf 			char *errno_str = l_errno ? strerror(l_errno) : "";
54066f9d5cbSmlf 
54166f9d5cbSmlf 			set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "\n",
54266f9d5cbSmlf 			    errno_str, l_errno ? "\n" : "", NULL);
54366f9d5cbSmlf 			break;
54466f9d5cbSmlf 		}
54566f9d5cbSmlf 
54666f9d5cbSmlf 	default:
54766f9d5cbSmlf 		set_msg(errstring, ERR_STR(CFGA_SATA_INTERNAL_ERROR), NULL);
54866f9d5cbSmlf 
54966f9d5cbSmlf 	} /* end switch */
55066f9d5cbSmlf 
55166f9d5cbSmlf 
55266f9d5cbSmlf 	/*
55366f9d5cbSmlf 	 * Determine the proper error code to send back to the cfgadm library.
55466f9d5cbSmlf 	 */
55566f9d5cbSmlf 	return (sata_msgs[rv].cfga_err);
55666f9d5cbSmlf }
55766f9d5cbSmlf 
55866f9d5cbSmlf 
55966f9d5cbSmlf /*
56066f9d5cbSmlf  * Entry points
56166f9d5cbSmlf  */
56266f9d5cbSmlf /* cfgadm entry point */
56366f9d5cbSmlf /*ARGSUSED*/
56466f9d5cbSmlf 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)56566f9d5cbSmlf cfga_change_state(
5668aa6aadbSXiao-Yu Zhang     cfga_cmd_t state_change_cmd,
5678aa6aadbSXiao-Yu Zhang     const char *ap_id,
5688aa6aadbSXiao-Yu Zhang     const char *options,
5698aa6aadbSXiao-Yu Zhang     struct cfga_confirm *confp,
5708aa6aadbSXiao-Yu Zhang     struct cfga_msg *msgp,
5718aa6aadbSXiao-Yu Zhang     char **errstring,
5728aa6aadbSXiao-Yu Zhang     cfga_flags_t flags)
57366f9d5cbSmlf {
57466f9d5cbSmlf 	int		ret;
575cf252232SAndy Fiddaman 	int		len;
57666f9d5cbSmlf 	char		*msg;
57766f9d5cbSmlf 	char		*devpath;
57866f9d5cbSmlf 	nvlist_t	*nvl = NULL;
57966f9d5cbSmlf 	ap_rstate_t	rstate;
58066f9d5cbSmlf 	ap_ostate_t	ostate;
58166f9d5cbSmlf 	devctl_hdl_t	hdl = NULL;
58266f9d5cbSmlf 	cfga_sata_ret_t	rv = CFGA_SATA_OK;
58366f9d5cbSmlf 	char		*pdyn;
5848aa6aadbSXiao-Yu Zhang 	char		*str_type;
5858aa6aadbSXiao-Yu Zhang 	size_t		size;
5868aa6aadbSXiao-Yu Zhang 	boolean_t	pmult = B_FALSE;
58766f9d5cbSmlf 
58866f9d5cbSmlf 	/*
58966f9d5cbSmlf 	 * All sub-commands which can change state of device require
59066f9d5cbSmlf 	 * root privileges.
59166f9d5cbSmlf 	 */
59266f9d5cbSmlf 	if (geteuid() != 0) {
59366f9d5cbSmlf 		rv = CFGA_SATA_PRIV;
59466f9d5cbSmlf 		goto bailout;
59566f9d5cbSmlf 	}
59666f9d5cbSmlf 
59766f9d5cbSmlf 	if ((rv = verify_params(ap_id, options, errstring)) != CFGA_SATA_OK) {
59866f9d5cbSmlf 		(void) cfga_help(msgp, options, flags);
59966f9d5cbSmlf 		goto bailout;
60066f9d5cbSmlf 	}
60166f9d5cbSmlf 
60266f9d5cbSmlf 	if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &nvl,
6038aa6aadbSXiao-Yu Zhang 	    DC_RDONLY)) != CFGA_SATA_OK) {
60466f9d5cbSmlf 		goto bailout;
60566f9d5cbSmlf 	}
60666f9d5cbSmlf 
6078aa6aadbSXiao-Yu Zhang 	/*
6088aa6aadbSXiao-Yu Zhang 	 * Checking device type. A port multiplier is not configurable - it is
6098aa6aadbSXiao-Yu Zhang 	 * already configured as soon as it is connected.
6108aa6aadbSXiao-Yu Zhang 	 */
61121f023dfSToomas Soome 	if ((rv = do_control_ioctl(ap_id, SATA_CFGA_GET_AP_TYPE, 0,
6128aa6aadbSXiao-Yu Zhang 	    (void **)&str_type, &size)) != CFGA_SATA_OK) {
6138aa6aadbSXiao-Yu Zhang 		/* no such deivce */
6148aa6aadbSXiao-Yu Zhang 		goto bailout;
6158aa6aadbSXiao-Yu Zhang 	}
6168aa6aadbSXiao-Yu Zhang 	if (strncmp(str_type, "sata-pmult", sizeof ("sata-pmult")) == 0) {
6178aa6aadbSXiao-Yu Zhang 		pmult = B_TRUE;
6188aa6aadbSXiao-Yu Zhang 	}
6198aa6aadbSXiao-Yu Zhang 
62066f9d5cbSmlf 	switch (state_change_cmd) {
62166f9d5cbSmlf 	case CFGA_CMD_CONFIGURE:
6228aa6aadbSXiao-Yu Zhang 		if (pmult == B_TRUE) {
6238aa6aadbSXiao-Yu Zhang 			rv = CFGA_SATA_HWOPNOTSUPP;
6248aa6aadbSXiao-Yu Zhang 			goto bailout;
6258aa6aadbSXiao-Yu Zhang 		}
6268aa6aadbSXiao-Yu Zhang 
62766f9d5cbSmlf 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
62866f9d5cbSmlf 		    CFGA_SATA_OK)
62966f9d5cbSmlf 			goto bailout;
63066f9d5cbSmlf 
63166f9d5cbSmlf 		if (ostate == AP_OSTATE_CONFIGURED) {
63266f9d5cbSmlf 			rv = CFGA_SATA_ALREADY_CONFIGURED;
63366f9d5cbSmlf 			goto bailout;
63466f9d5cbSmlf 		}
63566f9d5cbSmlf 		/* Disallow dynamic AP name component */
63666f9d5cbSmlf 		if (GET_DYN(ap_id) != NULL) {
63766f9d5cbSmlf 			rv = CFGA_SATA_INVALID_DEVNAME;
63866f9d5cbSmlf 			goto bailout;
63966f9d5cbSmlf 		}
64066f9d5cbSmlf 
64166f9d5cbSmlf 		if (rstate == AP_RSTATE_EMPTY) {
64266f9d5cbSmlf 			rv = CFGA_SATA_NOT_CONNECTED;
64366f9d5cbSmlf 			goto bailout;
64466f9d5cbSmlf 		}
64566f9d5cbSmlf 		rv = CFGA_SATA_OK;
64666f9d5cbSmlf 
64766f9d5cbSmlf 		if (devctl_ap_configure(hdl, nvl) != 0) {
64866f9d5cbSmlf 			rv = CFGA_SATA_DEV_CONFIGURE;
64966f9d5cbSmlf 			goto bailout;
65066f9d5cbSmlf 		}
65166f9d5cbSmlf 
65266f9d5cbSmlf 		devpath = sata_get_devicepath(ap_id);
65366f9d5cbSmlf 		if (devpath == NULL) {
65466f9d5cbSmlf 			int i;
65566f9d5cbSmlf 			/*
65666f9d5cbSmlf 			 * Try for some time as SATA hotplug thread
65766f9d5cbSmlf 			 * takes a while to create the path then
65866f9d5cbSmlf 			 * eventually give up.
65966f9d5cbSmlf 			 */
66066f9d5cbSmlf 			for (i = 0; i < 12 && (devpath == NULL); i++) {
66166f9d5cbSmlf 				(void) sleep(6);
66266f9d5cbSmlf 				devpath = sata_get_devicepath(ap_id);
66366f9d5cbSmlf 			}
66466f9d5cbSmlf 
66566f9d5cbSmlf 			if (devpath == NULL) {
66666f9d5cbSmlf 				rv = CFGA_SATA_DEV_CONFIGURE;
66766f9d5cbSmlf 				break;
66866f9d5cbSmlf 			}
66966f9d5cbSmlf 		}
67066f9d5cbSmlf 
67166f9d5cbSmlf 		S_FREE(devpath);
67266f9d5cbSmlf 		break;
67366f9d5cbSmlf 
67466f9d5cbSmlf 	case CFGA_CMD_UNCONFIGURE:
6758aa6aadbSXiao-Yu Zhang 		if (pmult == B_TRUE) {
6768aa6aadbSXiao-Yu Zhang 			rv = CFGA_SATA_HWOPNOTSUPP;
6778aa6aadbSXiao-Yu Zhang 			goto bailout;
6788aa6aadbSXiao-Yu Zhang 		}
6798aa6aadbSXiao-Yu Zhang 
68066f9d5cbSmlf 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
68166f9d5cbSmlf 		    CFGA_SATA_OK)
68266f9d5cbSmlf 			goto bailout;
68366f9d5cbSmlf 
68466f9d5cbSmlf 		if (rstate != AP_RSTATE_CONNECTED) {
68566f9d5cbSmlf 			rv = CFGA_SATA_NOT_CONNECTED;
68666f9d5cbSmlf 			goto bailout;
68766f9d5cbSmlf 		}
68866f9d5cbSmlf 
68966f9d5cbSmlf 		if (ostate != AP_OSTATE_CONFIGURED) {
69066f9d5cbSmlf 			rv = CFGA_SATA_NOT_CONFIGURED;
69166f9d5cbSmlf 			goto bailout;
69266f9d5cbSmlf 		}
69366f9d5cbSmlf 		/* Strip off AP name dynamic component, if present */
69466f9d5cbSmlf 		if ((pdyn = GET_DYN(ap_id)) != NULL) {
69566f9d5cbSmlf 			*pdyn = '\0';
69666f9d5cbSmlf 		}
69766f9d5cbSmlf 
69866f9d5cbSmlf 		rv = CFGA_SATA_OK;
69966f9d5cbSmlf 
70066f9d5cbSmlf 		len = strlen(SATA_CONFIRM_DEVICE) +
7018aa6aadbSXiao-Yu Zhang 		    strlen(SATA_CONFIRM_DEVICE_SUSPEND) +
7028aa6aadbSXiao-Yu Zhang 		    strlen("Unconfigure") + strlen(ap_id);
70366f9d5cbSmlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
70466f9d5cbSmlf 			(void) snprintf(msg, len + 3, "Unconfigure"
7058aa6aadbSXiao-Yu Zhang 			    " %s%s\n%s",
7068aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_DEVICE, ap_id,
7078aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_DEVICE_SUSPEND);
70866f9d5cbSmlf 		}
70966f9d5cbSmlf 
71066f9d5cbSmlf 		if (!sata_confirm(confp, msg)) {
71166f9d5cbSmlf 			free(msg);
71266f9d5cbSmlf 			rv = CFGA_SATA_NACK;
71366f9d5cbSmlf 			break;
71466f9d5cbSmlf 		}
71566f9d5cbSmlf 		free(msg);
71666f9d5cbSmlf 
71766f9d5cbSmlf 		devpath = sata_get_devicepath(ap_id);
71866f9d5cbSmlf 		if (devpath == NULL) {
71966f9d5cbSmlf 			(void) printf(
7208aa6aadbSXiao-Yu Zhang 			    "cfga_change_state: get device path failed\n");
72166f9d5cbSmlf 			rv = CFGA_SATA_DEV_UNCONFIGURE;
72266f9d5cbSmlf 			break;
72366f9d5cbSmlf 		}
72466f9d5cbSmlf 
72566f9d5cbSmlf 		if ((rv = sata_rcm_offline(ap_id, errstring, devpath, flags))
72666f9d5cbSmlf 		    != CFGA_SATA_OK) {
72766f9d5cbSmlf 			break;
72866f9d5cbSmlf 		}
72966f9d5cbSmlf 
73066f9d5cbSmlf 		ret = devctl_ap_unconfigure(hdl, nvl);
73166f9d5cbSmlf 
73266f9d5cbSmlf 		if (ret != 0) {
73366f9d5cbSmlf 			rv = CFGA_SATA_DEV_UNCONFIGURE;
73466f9d5cbSmlf 			if (errno == EBUSY) {
73566f9d5cbSmlf 				rv = CFGA_SATA_BUSY;
73666f9d5cbSmlf 			}
73766f9d5cbSmlf 			(void) sata_rcm_online(ap_id, errstring, devpath,
7388aa6aadbSXiao-Yu Zhang 			    flags);
73966f9d5cbSmlf 		} else {
74066f9d5cbSmlf 			(void) sata_rcm_remove(ap_id, errstring, devpath,
7418aa6aadbSXiao-Yu Zhang 			    flags);
74266f9d5cbSmlf 
74366f9d5cbSmlf 		}
74466f9d5cbSmlf 		S_FREE(devpath);
74566f9d5cbSmlf 
74666f9d5cbSmlf 		break;
74766f9d5cbSmlf 
74866f9d5cbSmlf 	case CFGA_CMD_DISCONNECT:
74966f9d5cbSmlf 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
75066f9d5cbSmlf 		    CFGA_SATA_OK)
75166f9d5cbSmlf 			goto bailout;
75266f9d5cbSmlf 
75366f9d5cbSmlf 		if (rstate == AP_RSTATE_DISCONNECTED) {
75466f9d5cbSmlf 			rv = CFGA_SATA_DISCONNECTED;
75566f9d5cbSmlf 			goto bailout;
75666f9d5cbSmlf 		}
75766f9d5cbSmlf 
75866f9d5cbSmlf 		/* Strip off AP name dynamic component, if present */
75966f9d5cbSmlf 		if ((pdyn = GET_DYN(ap_id)) != NULL) {
76066f9d5cbSmlf 			*pdyn = '\0';
76166f9d5cbSmlf 		}
76266f9d5cbSmlf 
76366f9d5cbSmlf 
76466f9d5cbSmlf 		rv = CFGA_SATA_OK; /* other statuses don't matter */
76566f9d5cbSmlf 
76666f9d5cbSmlf 		/*
76766f9d5cbSmlf 		 * If the port originally with device attached and was
76866f9d5cbSmlf 		 * unconfigured already, the devicepath for the sd will be
76966f9d5cbSmlf 		 * removed. sata_get_devicepath in this case is not necessary.
77066f9d5cbSmlf 		 */
77166f9d5cbSmlf 		/* only call rcm_offline if the state was CONFIGURED */
7728aa6aadbSXiao-Yu Zhang 		if (ostate == AP_OSTATE_CONFIGURED &&
7738aa6aadbSXiao-Yu Zhang 		    pmult == B_FALSE) {
77466f9d5cbSmlf 			devpath = sata_get_devicepath(ap_id);
77566f9d5cbSmlf 			if (devpath == NULL) {
77666f9d5cbSmlf 				(void) printf(
77766f9d5cbSmlf 				    "cfga_change_state: get path failed\n");
77866f9d5cbSmlf 				rv = CFGA_SATA_DEV_UNCONFIGURE;
77966f9d5cbSmlf 				break;
78066f9d5cbSmlf 			}
78166f9d5cbSmlf 
78266f9d5cbSmlf 			len = strlen(SATA_CONFIRM_DEVICE) +
7838aa6aadbSXiao-Yu Zhang 			    strlen(SATA_CONFIRM_DEVICE_SUSPEND) +
7848aa6aadbSXiao-Yu Zhang 			    strlen("Disconnect") + strlen(ap_id);
78566f9d5cbSmlf 			if ((msg = (char *)calloc(len +3, 1)) != NULL) {
78666f9d5cbSmlf 				(void) snprintf(msg, len + 3,
7878aa6aadbSXiao-Yu Zhang 				    "Disconnect"
7888aa6aadbSXiao-Yu Zhang 				    " %s%s\n%s",
7898aa6aadbSXiao-Yu Zhang 				    SATA_CONFIRM_DEVICE, ap_id,
7908aa6aadbSXiao-Yu Zhang 				    SATA_CONFIRM_DEVICE_SUSPEND);
79166f9d5cbSmlf 			}
79266f9d5cbSmlf 			if (!sata_confirm(confp, msg)) {
79366f9d5cbSmlf 				free(msg);
79466f9d5cbSmlf 				rv = CFGA_SATA_NACK;
79566f9d5cbSmlf 				break;
79666f9d5cbSmlf 			}
79766f9d5cbSmlf 			free(msg);
79866f9d5cbSmlf 
79966f9d5cbSmlf 			if ((rv = sata_rcm_offline(ap_id, errstring,
80066f9d5cbSmlf 			    devpath, flags)) != CFGA_SATA_OK) {
80166f9d5cbSmlf 				break;
80266f9d5cbSmlf 			}
80366f9d5cbSmlf 
80466f9d5cbSmlf 			ret = devctl_ap_unconfigure(hdl, nvl);
80566f9d5cbSmlf 			if (ret != 0) {
80666f9d5cbSmlf 				(void) printf(
80766f9d5cbSmlf 				    "devctl_ap_unconfigure failed\n");
80866f9d5cbSmlf 				rv = CFGA_SATA_DEV_UNCONFIGURE;
80966f9d5cbSmlf 				if (errno == EBUSY)
81066f9d5cbSmlf 					rv = CFGA_SATA_BUSY;
81166f9d5cbSmlf 				(void) sata_rcm_online(ap_id, errstring,
8128aa6aadbSXiao-Yu Zhang 				    devpath, flags);
81366f9d5cbSmlf 				S_FREE(devpath);
81466f9d5cbSmlf 
81566f9d5cbSmlf 				/*
81666f9d5cbSmlf 				 * The current policy is that if unconfigure
81766f9d5cbSmlf 				 * failed, do not continue with disconnect.
81866f9d5cbSmlf 				 * If the port needs to be forced into the
81966f9d5cbSmlf 				 * disconnect (shutdown) state,
82066f9d5cbSmlf 				 * the -x sata_port_poweroff command should be
82166f9d5cbSmlf 				 * used instead of -c disconnect
82266f9d5cbSmlf 				 */
82366f9d5cbSmlf 				break;
82466f9d5cbSmlf 			} else {
82566f9d5cbSmlf 				(void) printf("%s\n",
82666f9d5cbSmlf 				    ERR_STR(CFGA_SATA_DEVICE_UNCONFIGURED));
82766f9d5cbSmlf 				(void) sata_rcm_remove(ap_id, errstring,
8288aa6aadbSXiao-Yu Zhang 				    devpath, flags);
82966f9d5cbSmlf 			}
83066f9d5cbSmlf 			S_FREE(devpath);
83166f9d5cbSmlf 		} else if (rstate == AP_RSTATE_CONNECTED ||
83266f9d5cbSmlf 		    rstate == AP_RSTATE_EMPTY) {
83366f9d5cbSmlf 			len = strlen(SATA_CONFIRM_PORT) +
8348aa6aadbSXiao-Yu Zhang 			    strlen(SATA_CONFIRM_PORT_DISABLE) +
8358aa6aadbSXiao-Yu Zhang 			    strlen("Deactivate Port") + strlen(ap_id);
83666f9d5cbSmlf 			if ((msg = (char *)calloc(len +3, 1)) != NULL) {
83766f9d5cbSmlf 				(void) snprintf(msg, len +3,
8388aa6aadbSXiao-Yu Zhang 				    "Disconnect"
8398aa6aadbSXiao-Yu Zhang 				    " %s%s\n%s",
8408aa6aadbSXiao-Yu Zhang 				    SATA_CONFIRM_PORT, ap_id,
8418aa6aadbSXiao-Yu Zhang 				    SATA_CONFIRM_PORT_DISABLE);
84266f9d5cbSmlf 			}
84366f9d5cbSmlf 			if (!sata_confirm(confp, msg)) {
84466f9d5cbSmlf 				free(msg);
84566f9d5cbSmlf 				rv = CFGA_SATA_NACK;
84666f9d5cbSmlf 				break;
84766f9d5cbSmlf 			}
84866f9d5cbSmlf 		}
84966f9d5cbSmlf 		ret = devctl_ap_disconnect(hdl, nvl);
85066f9d5cbSmlf 		if (ret != 0) {
85166f9d5cbSmlf 			rv = CFGA_SATA_IOCTL;
85266f9d5cbSmlf 			if (errno == EBUSY) {
85366f9d5cbSmlf 				rv = CFGA_SATA_BUSY;
85466f9d5cbSmlf 			}
85566f9d5cbSmlf 		}
85666f9d5cbSmlf 		break;
85766f9d5cbSmlf 
85866f9d5cbSmlf 	case CFGA_CMD_CONNECT:
85966f9d5cbSmlf 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
86066f9d5cbSmlf 		    CFGA_SATA_OK)
86166f9d5cbSmlf 			goto bailout;
86266f9d5cbSmlf 
86366f9d5cbSmlf 		if (rstate == AP_RSTATE_CONNECTED) {
86466f9d5cbSmlf 			rv = CFGA_SATA_ALREADY_CONNECTED;
86566f9d5cbSmlf 			goto bailout;
86666f9d5cbSmlf 		}
86766f9d5cbSmlf 
86866f9d5cbSmlf 		len = strlen(SATA_CONFIRM_PORT) +
8698aa6aadbSXiao-Yu Zhang 		    strlen(SATA_CONFIRM_PORT_ENABLE) +
8708aa6aadbSXiao-Yu Zhang 		    strlen("Activate Port") + strlen(ap_id);
87166f9d5cbSmlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
87266f9d5cbSmlf 			(void) snprintf(msg, len +3, "Activate"
8738aa6aadbSXiao-Yu Zhang 			    " %s%s\n%s",
8748aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_PORT, ap_id,
8758aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_PORT_ENABLE);
87666f9d5cbSmlf 		}
87766f9d5cbSmlf 		if (!sata_confirm(confp, msg)) {
87866f9d5cbSmlf 			rv = CFGA_SATA_NACK;
87966f9d5cbSmlf 			break;
88066f9d5cbSmlf 		}
88166f9d5cbSmlf 
88266f9d5cbSmlf 		/* Disallow dynamic AP name component */
88366f9d5cbSmlf 		if (GET_DYN(ap_id) != NULL) {
88466f9d5cbSmlf 			rv = CFGA_SATA_INVALID_DEVNAME;
88566f9d5cbSmlf 			goto bailout;
88666f9d5cbSmlf 		}
88766f9d5cbSmlf 
88866f9d5cbSmlf 		ret = devctl_ap_connect(hdl, nvl);
88966f9d5cbSmlf 		if (ret != 0) {
89066f9d5cbSmlf 			rv = CFGA_SATA_IOCTL;
89166f9d5cbSmlf 		} else {
89266f9d5cbSmlf 			rv = CFGA_SATA_OK;
89366f9d5cbSmlf 		}
89466f9d5cbSmlf 
89566f9d5cbSmlf 		break;
89666f9d5cbSmlf 
89766f9d5cbSmlf 	case CFGA_CMD_LOAD:
89866f9d5cbSmlf 	case CFGA_CMD_UNLOAD:
89966f9d5cbSmlf 		(void) cfga_help(msgp, options, flags);
90066f9d5cbSmlf 		rv = CFGA_SATA_OPNOTSUPP;
90166f9d5cbSmlf 		break;
90266f9d5cbSmlf 
90366f9d5cbSmlf 	case CFGA_CMD_NONE:
90466f9d5cbSmlf 	default:
90566f9d5cbSmlf 		(void) cfga_help(msgp, options, flags);
90666f9d5cbSmlf 		rv = CFGA_SATA_INTERNAL_ERROR;
90766f9d5cbSmlf 	}
90866f9d5cbSmlf 
90966f9d5cbSmlf bailout:
91066f9d5cbSmlf 	cleanup_after_devctl_cmd(hdl, nvl);
91166f9d5cbSmlf 
91266f9d5cbSmlf 	return (sata_err_msg(errstring, rv, ap_id, errno));
91366f9d5cbSmlf }
91466f9d5cbSmlf 
91566f9d5cbSmlf /* cfgadm entry point */
91666f9d5cbSmlf 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)91766f9d5cbSmlf cfga_private_func(
9188aa6aadbSXiao-Yu Zhang     const char *func,
9198aa6aadbSXiao-Yu Zhang     const char *ap_id,
9208aa6aadbSXiao-Yu Zhang     const char *options,
9218aa6aadbSXiao-Yu Zhang     struct cfga_confirm *confp,
9228aa6aadbSXiao-Yu Zhang     struct cfga_msg *msgp,
9238aa6aadbSXiao-Yu Zhang     char **errstring,
9248aa6aadbSXiao-Yu Zhang     cfga_flags_t flags)
92566f9d5cbSmlf {
92666f9d5cbSmlf 	int			len;
927cf252232SAndy Fiddaman 	char			*msg;
92866f9d5cbSmlf 	nvlist_t		*list = NULL;
92966f9d5cbSmlf 	ap_ostate_t		ostate;
93066f9d5cbSmlf 	ap_rstate_t		rstate;
93166f9d5cbSmlf 	devctl_hdl_t		hdl = NULL;
93266f9d5cbSmlf 	cfga_sata_ret_t		rv;
93366f9d5cbSmlf 	char			*str_p;
93466f9d5cbSmlf 	size_t			size;
93566f9d5cbSmlf 
93666f9d5cbSmlf 	if ((rv = verify_params(ap_id, NULL, errstring)) != CFGA_SATA_OK) {
93766f9d5cbSmlf 		(void) cfga_help(msgp, options, flags);
93866f9d5cbSmlf 		return (sata_err_msg(errstring, rv, ap_id, errno));
93966f9d5cbSmlf 	}
94066f9d5cbSmlf 
94166f9d5cbSmlf 	/*
94266f9d5cbSmlf 	 * All subcommands which can change state of device require
94366f9d5cbSmlf 	 * root privileges.
94466f9d5cbSmlf 	 */
94566f9d5cbSmlf 	if (geteuid() != 0) {
94666f9d5cbSmlf 		rv = CFGA_SATA_PRIV;
94766f9d5cbSmlf 		goto bailout;
94866f9d5cbSmlf 	}
94966f9d5cbSmlf 
95066f9d5cbSmlf 	if (func == NULL) {
95166f9d5cbSmlf 		(void) printf("No valid option specified\n");
95266f9d5cbSmlf 		rv = CFGA_SATA_OPTIONS;
95366f9d5cbSmlf 		goto bailout;
95466f9d5cbSmlf 	}
95566f9d5cbSmlf 
95666f9d5cbSmlf 	if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &list, 0)) !=
95766f9d5cbSmlf 	    CFGA_SATA_OK) {
95866f9d5cbSmlf 		goto bailout;
95966f9d5cbSmlf 	}
96066f9d5cbSmlf 
96166f9d5cbSmlf 	/* We do not care here about dynamic AP name component */
96266f9d5cbSmlf 	if ((str_p = GET_DYN(ap_id)) != NULL) {
96366f9d5cbSmlf 		*str_p = '\0';
96466f9d5cbSmlf 	}
96566f9d5cbSmlf 
96666f9d5cbSmlf 	rv = CFGA_SATA_OK;
96766f9d5cbSmlf 
96866f9d5cbSmlf 	if (strcmp(func, SATA_RESET_PORT) == 0) {
96966f9d5cbSmlf 		len = strlen(SATA_CONFIRM_PORT) +
97066f9d5cbSmlf 		    strlen(SATA_CONFIRM_DEVICE_ABORT) +
97166f9d5cbSmlf 		    strlen("Reset Port") + strlen(ap_id);
97266f9d5cbSmlf 
97366f9d5cbSmlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
97466f9d5cbSmlf 			(void) snprintf(msg, len +3, "Reset"
9758aa6aadbSXiao-Yu Zhang 			    " %s%s\n%s",
9768aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_PORT, ap_id,
9778aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_DEVICE_ABORT);
97866f9d5cbSmlf 		} else {
97966f9d5cbSmlf 			rv = CFGA_SATA_NACK;
98066f9d5cbSmlf 			goto bailout;
98166f9d5cbSmlf 		}
98266f9d5cbSmlf 
98366f9d5cbSmlf 		if (!sata_confirm(confp, msg)) {
98466f9d5cbSmlf 			rv = CFGA_SATA_NACK;
98566f9d5cbSmlf 			goto bailout;
98666f9d5cbSmlf 		}
98766f9d5cbSmlf 
98821f023dfSToomas Soome 		rv = do_control_ioctl(ap_id, SATA_CFGA_RESET_PORT, 0,
9898aa6aadbSXiao-Yu Zhang 		    (void **)&str_p, &size);
99066f9d5cbSmlf 
99166f9d5cbSmlf 	} else if (strcmp(func, SATA_RESET_DEVICE) == 0) {
99266f9d5cbSmlf 		if ((rv = port_state(hdl, list, &rstate, &ostate)) !=
99366f9d5cbSmlf 		    CFGA_SATA_OK)
99466f9d5cbSmlf 			goto bailout;
99566f9d5cbSmlf 		/*
99666f9d5cbSmlf 		 * Reset device function requires device to be connected
99766f9d5cbSmlf 		 */
99866f9d5cbSmlf 		if (rstate != AP_RSTATE_CONNECTED) {
99966f9d5cbSmlf 			rv = CFGA_SATA_NOT_CONNECTED;
100066f9d5cbSmlf 			goto bailout;
100166f9d5cbSmlf 		}
100266f9d5cbSmlf 
100366f9d5cbSmlf 		len = strlen(SATA_CONFIRM_DEVICE) +
100466f9d5cbSmlf 		    strlen(SATA_CONFIRM_DEVICE_ABORT) +
100566f9d5cbSmlf 		    strlen("Reset Device") + strlen(ap_id);
100666f9d5cbSmlf 
100766f9d5cbSmlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
100866f9d5cbSmlf 			(void) snprintf(msg, len +3, "Reset"
10098aa6aadbSXiao-Yu Zhang 			    " %s%s\n%s",
10108aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_DEVICE, ap_id,
10118aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_DEVICE_ABORT);
101266f9d5cbSmlf 		} else {
101366f9d5cbSmlf 			rv = CFGA_SATA_NACK;
101466f9d5cbSmlf 			goto bailout;
101566f9d5cbSmlf 		}
101666f9d5cbSmlf 
101766f9d5cbSmlf 		if (!sata_confirm(confp, msg)) {
101866f9d5cbSmlf 			rv = CFGA_SATA_NACK;
101966f9d5cbSmlf 			goto bailout;
102066f9d5cbSmlf 		}
102166f9d5cbSmlf 
102221f023dfSToomas Soome 		rv = do_control_ioctl(ap_id, SATA_CFGA_RESET_DEVICE, 0,
102366f9d5cbSmlf 		    (void **)&str_p, &size);
102466f9d5cbSmlf 
102566f9d5cbSmlf 	} else if (strcmp(func, SATA_RESET_ALL) == 0) {
102666f9d5cbSmlf 		len = strlen(SATA_CONFIRM_CONTROLLER) +
102766f9d5cbSmlf 		    strlen(SATA_CONFIRM_CONTROLLER_ABORT) +
102866f9d5cbSmlf 		    strlen("Reset All") + strlen(ap_id);
102966f9d5cbSmlf 
103066f9d5cbSmlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
103166f9d5cbSmlf 			(void) snprintf(msg, len +3, "Reset"
10328aa6aadbSXiao-Yu Zhang 			    " %s%s\n%s",
10338aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_CONTROLLER, ap_id,
10348aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_CONTROLLER_ABORT);
103566f9d5cbSmlf 		} else {
103666f9d5cbSmlf 			rv = CFGA_SATA_NACK;
103766f9d5cbSmlf 			goto bailout;
103866f9d5cbSmlf 		}
103966f9d5cbSmlf 
104066f9d5cbSmlf 		if (!sata_confirm(confp, msg)) {
104166f9d5cbSmlf 			rv = CFGA_SATA_NACK;
104266f9d5cbSmlf 			goto bailout;
104366f9d5cbSmlf 		}
104421f023dfSToomas Soome 		rv = do_control_ioctl(ap_id, SATA_CFGA_RESET_ALL, 0,
10458aa6aadbSXiao-Yu Zhang 		    (void **)&str_p, &size);
104666f9d5cbSmlf 
104766f9d5cbSmlf 	} else if (strcmp(func, SATA_PORT_DEACTIVATE) == 0) {
104866f9d5cbSmlf 		len = strlen(SATA_CONFIRM_PORT) +
104966f9d5cbSmlf 		    strlen(SATA_CONFIRM_PORT_DISABLE) +
105066f9d5cbSmlf 		    strlen("Deactivate Port") + strlen(ap_id);
105166f9d5cbSmlf 
105266f9d5cbSmlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
105366f9d5cbSmlf 			(void) snprintf(msg, len +3, "Deactivate"
10548aa6aadbSXiao-Yu Zhang 			    " %s%s\n%s",
10558aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_PORT, ap_id,
10568aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_PORT_DISABLE);
105766f9d5cbSmlf 		} else {
105866f9d5cbSmlf 			rv = CFGA_SATA_NACK;
105966f9d5cbSmlf 			goto bailout;
106066f9d5cbSmlf 		}
106166f9d5cbSmlf 		if (!sata_confirm(confp, msg)) {
106266f9d5cbSmlf 			rv = CFGA_SATA_NACK;
106366f9d5cbSmlf 			goto bailout;
106466f9d5cbSmlf 		}
106566f9d5cbSmlf 
106621f023dfSToomas Soome 		rv = do_control_ioctl(ap_id, SATA_CFGA_PORT_DEACTIVATE, 0,
10678aa6aadbSXiao-Yu Zhang 		    (void **)&str_p, &size);
106866f9d5cbSmlf 
106966f9d5cbSmlf 	} else if (strcmp(func, SATA_PORT_ACTIVATE) == 0) {
107066f9d5cbSmlf 		len = strlen(SATA_CONFIRM_PORT) +
107166f9d5cbSmlf 		    strlen(SATA_CONFIRM_PORT_ENABLE) +
107266f9d5cbSmlf 		    strlen("Activate Port") + strlen(ap_id);
107366f9d5cbSmlf 
107466f9d5cbSmlf 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
107566f9d5cbSmlf 			(void) snprintf(msg, len +3, "Activate"
10768aa6aadbSXiao-Yu Zhang 			    " %s%s\n%s",
10778aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_PORT, ap_id,
10788aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_PORT_ENABLE);
107966f9d5cbSmlf 		} else {
108066f9d5cbSmlf 			rv = CFGA_SATA_NACK;
108166f9d5cbSmlf 			goto bailout;
108266f9d5cbSmlf 		}
108366f9d5cbSmlf 		if (!sata_confirm(confp, msg)) {
108466f9d5cbSmlf 			rv = CFGA_SATA_NACK;
108566f9d5cbSmlf 			goto bailout;
108666f9d5cbSmlf 		}
108766f9d5cbSmlf 
108866f9d5cbSmlf 		rv = do_control_ioctl(ap_id, SATA_CFGA_PORT_ACTIVATE,
108921f023dfSToomas Soome 		    0, (void **)&str_p, &size);
10908aa6aadbSXiao-Yu Zhang 		goto bailout;
109166f9d5cbSmlf 
109266f9d5cbSmlf 	} else if (strcmp(func, SATA_PORT_SELF_TEST) == 0) {
109366f9d5cbSmlf 		len = strlen(SATA_CONFIRM_PORT) +
109466f9d5cbSmlf 		    strlen(SATA_CONFIRM_DEVICE_SUSPEND) +
109566f9d5cbSmlf 		    strlen("Self Test Port") + strlen(ap_id);
109666f9d5cbSmlf 
109721f023dfSToomas Soome 		if ((msg = calloc(len +3, 1)) != NULL) {
109866f9d5cbSmlf 			(void) snprintf(msg, len +3, "Self Test"
10998aa6aadbSXiao-Yu Zhang 			    " %s%s\n%s",
11008aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_PORT, ap_id,
11018aa6aadbSXiao-Yu Zhang 			    SATA_CONFIRM_DEVICE_SUSPEND);
110266f9d5cbSmlf 		} else {
110366f9d5cbSmlf 			rv = CFGA_SATA_NACK;
110466f9d5cbSmlf 			goto bailout;
110566f9d5cbSmlf 		}
110666f9d5cbSmlf 		if (!sata_confirm(confp, msg)) {
110766f9d5cbSmlf 			rv = CFGA_SATA_NACK;
110866f9d5cbSmlf 			goto bailout;
110966f9d5cbSmlf 		}
111066f9d5cbSmlf 
111166f9d5cbSmlf 		rv = do_control_ioctl(ap_id, SATA_CFGA_PORT_SELF_TEST,
111221f023dfSToomas Soome 		    0, (void **)&str_p, &size);
111366f9d5cbSmlf 	} else {
111466f9d5cbSmlf 		/* Unrecognized operation request */
111566f9d5cbSmlf 		rv = CFGA_SATA_HWOPNOTSUPP;
111666f9d5cbSmlf 	}
111766f9d5cbSmlf 
111866f9d5cbSmlf bailout:
111966f9d5cbSmlf 	cleanup_after_devctl_cmd(hdl, list);
112066f9d5cbSmlf 
112166f9d5cbSmlf 	return (sata_err_msg(errstring, rv, ap_id, errno));
112266f9d5cbSmlf 
112366f9d5cbSmlf }
112466f9d5cbSmlf 
112566f9d5cbSmlf /* cfgadm entry point */
112666f9d5cbSmlf /*ARGSUSED*/
112766f9d5cbSmlf cfga_err_t
cfga_test(const char * ap_id,const char * options,struct cfga_msg * msgp,char ** errstring,cfga_flags_t flags)112866f9d5cbSmlf cfga_test(
11298aa6aadbSXiao-Yu Zhang     const char *ap_id,
11308aa6aadbSXiao-Yu Zhang     const char *options,
11318aa6aadbSXiao-Yu Zhang     struct cfga_msg *msgp,
11328aa6aadbSXiao-Yu Zhang     char **errstring,
11338aa6aadbSXiao-Yu Zhang     cfga_flags_t flags)
113466f9d5cbSmlf {
113566f9d5cbSmlf 	/* Should call ioctl for self test - phase 2 */
113666f9d5cbSmlf 	return (CFGA_OPNOTSUPP);
113766f9d5cbSmlf }
113866f9d5cbSmlf 
113966f9d5cbSmlf 
114066f9d5cbSmlf int
sata_check_target_node(di_node_t node,void * arg)114166f9d5cbSmlf sata_check_target_node(di_node_t node, void *arg)
114266f9d5cbSmlf {
114366f9d5cbSmlf 	char *minorpath;
114466f9d5cbSmlf 	char *cp;
114566f9d5cbSmlf 
114666f9d5cbSmlf 	minorpath = di_devfs_minor_path(di_minor_next(node, DI_MINOR_NIL));
114766f9d5cbSmlf 	if (minorpath != NULL) {
114866f9d5cbSmlf 		if (strstr(minorpath, arg) != NULL) {
114966f9d5cbSmlf 			cp = strrchr(minorpath, (int)*MINOR_SEP);
115066f9d5cbSmlf 			if (cp != NULL) {
115166f9d5cbSmlf 				(void) strcpy(arg, cp);
115266f9d5cbSmlf 			}
115366f9d5cbSmlf 			free(minorpath);
115466f9d5cbSmlf 			return (DI_WALK_TERMINATE);
115566f9d5cbSmlf 		}
115666f9d5cbSmlf 		free(minorpath);
115766f9d5cbSmlf 	}
115866f9d5cbSmlf 	return (DI_WALK_CONTINUE);
115966f9d5cbSmlf }
116066f9d5cbSmlf 
1161d7b0e458Svikram struct chk_dev {
1162d7b0e458Svikram 	int c_isblk;
1163d7b0e458Svikram 	char *c_minor;
1164d7b0e458Svikram };
1165d7b0e458Svikram 
1166d7b0e458Svikram /*ARGSUSED*/
1167d7b0e458Svikram static int
chk_dev_fcn(di_node_t node,di_minor_t minor,void * arg)1168d7b0e458Svikram chk_dev_fcn(di_node_t node, di_minor_t minor, void *arg)
1169d7b0e458Svikram {
1170d7b0e458Svikram 	char	*mn;
1171d7b0e458Svikram 	struct chk_dev *chkp = (struct chk_dev *)arg;
1172d7b0e458Svikram 
1173d7b0e458Svikram 	mn = di_minor_name(minor);
1174d7b0e458Svikram 	if (mn == NULL)
1175d7b0e458Svikram 		return (DI_WALK_CONTINUE);
1176d7b0e458Svikram 
1177d7b0e458Svikram 	if (strcmp(mn, chkp->c_minor) != 0)
1178d7b0e458Svikram 		return (DI_WALK_CONTINUE);
1179d7b0e458Svikram 
1180d7b0e458Svikram 	chkp->c_isblk = di_minor_spectype(minor) == S_IFBLK ? 1 : 0;
1181d7b0e458Svikram 
1182d7b0e458Svikram 	return (DI_WALK_TERMINATE);
1183d7b0e458Svikram }
1184d7b0e458Svikram 
1185d7b0e458Svikram /*
1186d7b0e458Svikram  * Don't use devfs if stat() in /devices fails. Use libdevinfo instead.
1187d7b0e458Svikram  * Retired devices don't show up in devfs.
1188d7b0e458Svikram  *
1189d7b0e458Svikram  *	Returns:
1190d7b0e458Svikram  *		1 - minor exists and is of type BLK
1191d7b0e458Svikram  *		0 - minor does not exist or is not of type BLK.
1192d7b0e458Svikram  */
1193d7b0e458Svikram static int
is_devinfo_blk(char * minor_path)1194d7b0e458Svikram is_devinfo_blk(char *minor_path)
1195d7b0e458Svikram {
1196d7b0e458Svikram 	char	*minor_portion;
1197d7b0e458Svikram 	struct chk_dev chk_dev;
1198d7b0e458Svikram 	di_node_t node;
1199d7b0e458Svikram 	int	rv;
1200d7b0e458Svikram 
1201d7b0e458Svikram 	/*
1202d7b0e458Svikram 	 * prune minor path for di_init() - no /devices prefix and no minor name
1203d7b0e458Svikram 	 */
1204d7b0e458Svikram 	if (strncmp(minor_path, "/devices/", strlen("/devices/")) != 0)
1205d7b0e458Svikram 		return (0);
1206d7b0e458Svikram 
1207d7b0e458Svikram 	minor_portion = strrchr(minor_path, *MINOR_SEP);
1208d7b0e458Svikram 	if (minor_portion == NULL)
1209d7b0e458Svikram 		return (0);
1210d7b0e458Svikram 
1211d7b0e458Svikram 	*minor_portion = 0;
1212d7b0e458Svikram 
1213d7b0e458Svikram 	node = di_init(minor_path + strlen("/devices"), DINFOMINOR);
1214d7b0e458Svikram 
1215d7b0e458Svikram 	*minor_portion = *MINOR_SEP;
1216d7b0e458Svikram 
1217d7b0e458Svikram 	if (node == DI_NODE_NIL)
1218d7b0e458Svikram 		return (0);
1219d7b0e458Svikram 
1220d7b0e458Svikram 	chk_dev.c_isblk = 0;
1221d7b0e458Svikram 	chk_dev.c_minor = minor_portion + 1;
1222d7b0e458Svikram 
1223d7b0e458Svikram 	rv = di_walk_minor(node, NULL, 0, &chk_dev, chk_dev_fcn);
1224d7b0e458Svikram 
1225d7b0e458Svikram 	di_fini(node);
1226d7b0e458Svikram 
1227d7b0e458Svikram 	if (rv == 0 && chk_dev.c_isblk)
1228d7b0e458Svikram 		return (1);
1229d7b0e458Svikram 	else
1230d7b0e458Svikram 		return (0);
1231d7b0e458Svikram }
123266f9d5cbSmlf 
123366f9d5cbSmlf /*
123466f9d5cbSmlf  * The dynamic component buffer returned by this function has to be freed!
123566f9d5cbSmlf  */
123666f9d5cbSmlf int
sata_make_dyncomp(const char * ap_id,char ** dyncomp,const char * type)123738547057Sying tian - Beijing China sata_make_dyncomp(const char *ap_id, char **dyncomp, const char *type)
123866f9d5cbSmlf {
1239724365f7Ssethg 	char	*devpath = NULL;
1240724365f7Ssethg 	char	*cp = NULL;
1241724365f7Ssethg 	int	l_errno;
1242724365f7Ssethg 	char	minor_path[MAXPATHLEN];
1243724365f7Ssethg 	char	name_part[MAXNAMELEN];
1244724365f7Ssethg 	char	*devlink = NULL;
1245724365f7Ssethg 	char	*minor_portion = NULL;
1246724365f7Ssethg 	int	deplen;
1247724365f7Ssethg 	int	err;
1248724365f7Ssethg 	DIR	*dp = NULL;
1249724365f7Ssethg 	struct stat sb;
1250724365f7Ssethg 	struct dirent *dep = NULL;
1251724365f7Ssethg 	struct dirent *newdep = NULL;
1252545e5dadSsethg 	char	*p;
125366f9d5cbSmlf 
125466f9d5cbSmlf 	assert(dyncomp != NULL);
125566f9d5cbSmlf 
125666f9d5cbSmlf 	/*
125766f9d5cbSmlf 	 * Get target node path
125866f9d5cbSmlf 	 */
125966f9d5cbSmlf 	devpath = sata_get_devicepath(ap_id);
126066f9d5cbSmlf 	if (devpath == NULL) {
1261724365f7Ssethg 
126266f9d5cbSmlf 		(void) printf("cfga_list_ext: cannot locate target device\n");
126366f9d5cbSmlf 		return (CFGA_SATA_DYNAMIC_AP);
1264724365f7Ssethg 
126566f9d5cbSmlf 	} else {
1266724365f7Ssethg 
1267724365f7Ssethg 		cp = strrchr(devpath, *PATH_SEP);
1268724365f7Ssethg 		assert(cp != NULL);
1269724365f7Ssethg 		*cp = 0;	/* terminate path for opendir() */
1270724365f7Ssethg 
1271724365f7Ssethg 		(void) strncpy(name_part, cp + 1, MAXNAMELEN);
127266f9d5cbSmlf 
127366f9d5cbSmlf 		/*
1274724365f7Ssethg 		 * Using libdevinfo for this is overkill and kills
1275724365f7Ssethg 		 * performance when many consumers are using libcfgadm
1276724365f7Ssethg 		 * concurrently.
127766f9d5cbSmlf 		 */
1278724365f7Ssethg 		if ((dp = opendir(devpath)) == NULL) {
127966f9d5cbSmlf 			goto bailout;
128066f9d5cbSmlf 		}
128166f9d5cbSmlf 
1282724365f7Ssethg 		/*
1283724365f7Ssethg 		 * deplen is large enough to fit the largest path-
1284724365f7Ssethg 		 * struct dirent includes one byte (the terminator)
1285724365f7Ssethg 		 * so we don't add 1 to the calculation here.
1286724365f7Ssethg 		 */
1287d3080269Ssethg 		deplen = pathconf(devpath, _PC_NAME_MAX);
1288d3080269Ssethg 		deplen = ((deplen <= 0) ? MAXNAMELEN : deplen) +
1289724365f7Ssethg 		    sizeof (struct dirent);
1290724365f7Ssethg 		dep = (struct dirent *)malloc(deplen);
1291724365f7Ssethg 		if (dep == NULL)
129266f9d5cbSmlf 			goto bailout;
1293724365f7Ssethg 
1294724365f7Ssethg 		while ((err = readdir_r(dp, dep, &newdep)) == 0 &&
1295724365f7Ssethg 		    newdep != NULL) {
1296724365f7Ssethg 
1297724365f7Ssethg 			assert(newdep == dep);
1298724365f7Ssethg 
1299724365f7Ssethg 			if (strcmp(dep->d_name, ".") == 0 ||
1300724365f7Ssethg 			    strcmp(dep->d_name, "..") == 0 ||
1301724365f7Ssethg 			    (minor_portion = strchr(dep->d_name,
1302724365f7Ssethg 			    *MINOR_SEP)) == NULL)
1303724365f7Ssethg 				continue;
1304724365f7Ssethg 
1305724365f7Ssethg 			*minor_portion = 0;
1306724365f7Ssethg 			if (strcmp(dep->d_name, name_part) != 0)
1307724365f7Ssethg 				continue;
1308724365f7Ssethg 			*minor_portion = *MINOR_SEP;
1309724365f7Ssethg 
1310724365f7Ssethg 			(void) snprintf(minor_path, MAXPATHLEN,
1311724365f7Ssethg 			    "%s/%s", devpath, dep->d_name);
1312724365f7Ssethg 
131338547057Sying tian - Beijing China 			/*
131438547057Sying tian - Beijing China 			 * Break directly for tape device
131538547057Sying tian - Beijing China 			 */
131638547057Sying tian - Beijing China 			if (strcmp(type, "tape") == 0)
131738547057Sying tian - Beijing China 				break;
131838547057Sying tian - Beijing China 
1319d7b0e458Svikram 			/*
1320d7b0e458Svikram 			 * If stat() fails, the device *may* be retired.
1321d7b0e458Svikram 			 * Check via libdevinfo if the device has a BLK minor.
1322d7b0e458Svikram 			 * We don't use libdevinfo all the time, since taking
1323d7b0e458Svikram 			 * a snapshot is slower than a stat().
1324d7b0e458Svikram 			 */
1325d7b0e458Svikram 			if (stat(minor_path, &sb) < 0) {
1326d7b0e458Svikram 				if (is_devinfo_blk(minor_path)) {
1327d7b0e458Svikram 					break;
1328d7b0e458Svikram 				} else {
1329d7b0e458Svikram 					continue;
1330d7b0e458Svikram 				}
1331d7b0e458Svikram 			}
1332724365f7Ssethg 
1333724365f7Ssethg 			if (S_ISBLK(sb.st_mode))
1334724365f7Ssethg 				break;
1335d7b0e458Svikram 
133666f9d5cbSmlf 		}
133766f9d5cbSmlf 
1338724365f7Ssethg 		(void) closedir(dp);
1339724365f7Ssethg 		free(dep);
134066f9d5cbSmlf 		free(devpath);
134166f9d5cbSmlf 
1342724365f7Ssethg 		dp = NULL;
1343724365f7Ssethg 		dep = NULL;
1344724365f7Ssethg 		devpath = NULL;
1345724365f7Ssethg 
1346724365f7Ssethg 		/*
1347724365f7Ssethg 		 * If there was an error, or we didn't exit the loop
1348724365f7Ssethg 		 * by finding a block or character device, bail out.
1349724365f7Ssethg 		 */
1350724365f7Ssethg 		if (err != 0 || newdep == NULL)
1351724365f7Ssethg 			goto bailout;
1352724365f7Ssethg 
1353724365f7Ssethg 		/*
135438547057Sying tian - Beijing China 		 * Look for links to the physical path in /dev/dsk
135538547057Sying tian - Beijing China 		 * and /dev/rmt. So far, sata modue supports disk,
135638547057Sying tian - Beijing China 		 * dvd and tape devices, so we will first look for
135738547057Sying tian - Beijing China 		 * BLOCK devices, and then look for tape devices.
1358724365f7Ssethg 		 */
1359724365f7Ssethg 		(void) physpath_to_devlink("/dev/dsk",
1360724365f7Ssethg 		    minor_path, &devlink, &l_errno);
136166f9d5cbSmlf 
136266f9d5cbSmlf 		/* postprocess and copy logical name here */
136366f9d5cbSmlf 		if (devlink != NULL) {
136466f9d5cbSmlf 			/*
1365545e5dadSsethg 			 * For disks, remove partition/slice info
136666f9d5cbSmlf 			 */
1367724365f7Ssethg 			if ((cp = strstr(devlink, "dsk/")) != NULL) {
1368545e5dadSsethg 				/* cXtYdZ[(s[0..15])|(p[0..X])] */
1369545e5dadSsethg 				if ((p = strchr(cp + 4, 'd')) != NULL) {
1370545e5dadSsethg 					p++;	/* Skip the 'd' */
1371545e5dadSsethg 					while (*p != 0 && isdigit(*p))
1372545e5dadSsethg 						p++;
1373545e5dadSsethg 					*p = 0;
1374545e5dadSsethg 				}
1375724365f7Ssethg 				*dyncomp = strdup(cp);
137666f9d5cbSmlf 			}
1377724365f7Ssethg 
137866f9d5cbSmlf 			free(devlink);
137938547057Sying tian - Beijing China 		} else if (strcmp(type, "tape") == 0) {
138038547057Sying tian - Beijing China 
138138547057Sying tian - Beijing China 			/*
138238547057Sying tian - Beijing China 			 * For tape device, logical name looks like
138338547057Sying tian - Beijing China 			 * rmt/X
138438547057Sying tian - Beijing China 			 */
138538547057Sying tian - Beijing China 			(void) physpath_to_devlink("/dev/rmt",
138638547057Sying tian - Beijing China 			    minor_path, &devlink, &l_errno);
138738547057Sying tian - Beijing China 
138838547057Sying tian - Beijing China 			if (devlink != NULL) {
138938547057Sying tian - Beijing China 				if ((cp = strstr(devlink, "rmt/")) != NULL) {
139038547057Sying tian - Beijing China 					*dyncomp = strdup(cp);
139138547057Sying tian - Beijing China 				}
139238547057Sying tian - Beijing China 
139338547057Sying tian - Beijing China 				free(devlink);
139438547057Sying tian - Beijing China 			}
139566f9d5cbSmlf 		}
1396724365f7Ssethg 
139766f9d5cbSmlf 		return (SATA_CFGA_OK);
139866f9d5cbSmlf 	}
1399724365f7Ssethg 
140066f9d5cbSmlf bailout:
1401724365f7Ssethg 	if (dp)
1402724365f7Ssethg 		(void) closedir(dp);
1403724365f7Ssethg 	if (devpath)
140466f9d5cbSmlf 		free(devpath);
1405724365f7Ssethg 	if (dep)
1406724365f7Ssethg 		free(dep);
140766f9d5cbSmlf 	return (CFGA_SATA_DYNAMIC_AP);
140866f9d5cbSmlf }
140966f9d5cbSmlf 
141066f9d5cbSmlf /* cfgadm entry point */
141166f9d5cbSmlf /*ARGSUSED*/
141266f9d5cbSmlf 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)141366f9d5cbSmlf cfga_list_ext(
14148aa6aadbSXiao-Yu Zhang     const char *ap_id,
14158aa6aadbSXiao-Yu Zhang     cfga_list_data_t **ap_id_list,
14168aa6aadbSXiao-Yu Zhang     int *nlistp,
14178aa6aadbSXiao-Yu Zhang     const char *options,
14188aa6aadbSXiao-Yu Zhang     const char *listopts,
14198aa6aadbSXiao-Yu Zhang     char **errstring,
14208aa6aadbSXiao-Yu Zhang     cfga_flags_t flags)
142166f9d5cbSmlf {
142266f9d5cbSmlf 	int			l_errno;
142366f9d5cbSmlf 	char			*ap_id_log = NULL;
142466f9d5cbSmlf 	size_t			size;
142566f9d5cbSmlf 	nvlist_t		*user_nvlist = NULL;
142666f9d5cbSmlf 	devctl_hdl_t		devctl_hdl = NULL;
142766f9d5cbSmlf 	cfga_sata_ret_t		rv = CFGA_SATA_OK;
142866f9d5cbSmlf 	devctl_ap_state_t	devctl_ap_state;
142966f9d5cbSmlf 	char			*pdyn;
14308aa6aadbSXiao-Yu Zhang 	boolean_t		pmult = B_FALSE;
14318aa6aadbSXiao-Yu Zhang 	uint32_t		port;
143266f9d5cbSmlf 
143366f9d5cbSmlf 
143466f9d5cbSmlf 	if ((rv = verify_params(ap_id, options, errstring)) != CFGA_SATA_OK) {
143566f9d5cbSmlf 		goto bailout;
143666f9d5cbSmlf 	}
143766f9d5cbSmlf 	/* We do not care here about dynamic AP name component */
143866f9d5cbSmlf 	if ((pdyn = GET_DYN(ap_id)) != NULL) {
143966f9d5cbSmlf 		*pdyn = '\0';
144066f9d5cbSmlf 	}
144166f9d5cbSmlf 
144266f9d5cbSmlf 	if (ap_id_list == NULL || nlistp == NULL) {
144366f9d5cbSmlf 		rv = CFGA_SATA_DATA_ERROR;
144466f9d5cbSmlf 		goto bailout;
144566f9d5cbSmlf 	}
144666f9d5cbSmlf 
144766f9d5cbSmlf 	/* Get ap status */
144866f9d5cbSmlf 	if ((rv = setup_for_devctl_cmd(ap_id, &devctl_hdl, &user_nvlist,
144966f9d5cbSmlf 	    DC_RDONLY)) != CFGA_SATA_OK) {
145066f9d5cbSmlf 		goto bailout;
145166f9d5cbSmlf 	}
145266f9d5cbSmlf 
145366f9d5cbSmlf 	/* will call dc_cmd to send IOCTL to kernel */
145466f9d5cbSmlf 	if (devctl_ap_getstate(devctl_hdl, user_nvlist,
145566f9d5cbSmlf 	    &devctl_ap_state) == -1) {
145666f9d5cbSmlf 		cleanup_after_devctl_cmd(devctl_hdl, user_nvlist);
145766f9d5cbSmlf 		rv = CFGA_SATA_IOCTL;
145866f9d5cbSmlf 		goto bailout;
145966f9d5cbSmlf 	}
146066f9d5cbSmlf 
146166f9d5cbSmlf 	cleanup_after_devctl_cmd(devctl_hdl, user_nvlist);
146266f9d5cbSmlf 
146366f9d5cbSmlf 	/*
146466f9d5cbSmlf 	 * Create cfga_list_data_t struct.
146566f9d5cbSmlf 	 */
146666f9d5cbSmlf 	if ((*ap_id_list =
146766f9d5cbSmlf 	    (cfga_list_data_t *)malloc(sizeof (**ap_id_list))) == NULL) {
146866f9d5cbSmlf 		rv = CFGA_SATA_ALLOC_FAIL;
146966f9d5cbSmlf 		goto bailout;
147066f9d5cbSmlf 	}
147166f9d5cbSmlf 	*nlistp = 1;
147266f9d5cbSmlf 
147366f9d5cbSmlf 	/*
147466f9d5cbSmlf 	 * Rest of the code fills in the cfga_list_data_t struct.
147566f9d5cbSmlf 	 */
147666f9d5cbSmlf 
147766f9d5cbSmlf 	/* Get /dev/cfg path to corresponding to the physical ap_id */
147866f9d5cbSmlf 	/* Remember ap_id_log must be freed */
1479724365f7Ssethg 	rv = physpath_to_devlink(CFGA_DEV_DIR, (char *)ap_id,
1480724365f7Ssethg 	    &ap_id_log, &l_errno);
148166f9d5cbSmlf 
148266f9d5cbSmlf 	if (rv != 0) {
148366f9d5cbSmlf 		rv = CFGA_SATA_DEVLINK;
148466f9d5cbSmlf 		goto bailout;
148566f9d5cbSmlf 	}
148666f9d5cbSmlf 
148766f9d5cbSmlf 	/* Get logical ap_id corresponding to the physical */
148820e7b521Ssethg 	if (ap_id_log == NULL || strstr(ap_id_log, CFGA_DEV_DIR) == NULL) {
148966f9d5cbSmlf 		rv = CFGA_SATA_DEVLINK;
149066f9d5cbSmlf 		goto bailout;
149166f9d5cbSmlf 	}
149266f9d5cbSmlf 
149366f9d5cbSmlf 	(void) strlcpy((*ap_id_list)->ap_log_id,
149466f9d5cbSmlf 	    /* Strip off /dev/cfg/ */ ap_id_log + strlen(CFGA_DEV_DIR)+ 1,
149566f9d5cbSmlf 	    sizeof ((*ap_id_list)->ap_log_id));
149666f9d5cbSmlf 
149766f9d5cbSmlf 	free(ap_id_log);
149866f9d5cbSmlf 	ap_id_log = NULL;
149966f9d5cbSmlf 
150066f9d5cbSmlf 	(void) strlcpy((*ap_id_list)->ap_phys_id, ap_id,
150166f9d5cbSmlf 	    sizeof ((*ap_id_list)->ap_phys_id));
150266f9d5cbSmlf 
150366f9d5cbSmlf 	switch (devctl_ap_state.ap_rstate) {
15048aa6aadbSXiao-Yu Zhang 	case AP_RSTATE_EMPTY:
15058aa6aadbSXiao-Yu Zhang 		(*ap_id_list)->ap_r_state = CFGA_STAT_EMPTY;
15068aa6aadbSXiao-Yu Zhang 		break;
150766f9d5cbSmlf 
15088aa6aadbSXiao-Yu Zhang 	case AP_RSTATE_DISCONNECTED:
15098aa6aadbSXiao-Yu Zhang 		(*ap_id_list)->ap_r_state = CFGA_STAT_DISCONNECTED;
15108aa6aadbSXiao-Yu Zhang 		break;
151166f9d5cbSmlf 
15128aa6aadbSXiao-Yu Zhang 	case AP_RSTATE_CONNECTED:
15138aa6aadbSXiao-Yu Zhang 		(*ap_id_list)->ap_r_state = CFGA_STAT_CONNECTED;
15148aa6aadbSXiao-Yu Zhang 		break;
151566f9d5cbSmlf 
15168aa6aadbSXiao-Yu Zhang 	default:
15178aa6aadbSXiao-Yu Zhang 		rv = CFGA_SATA_STATE;
15188aa6aadbSXiao-Yu Zhang 		goto bailout;
151966f9d5cbSmlf 	}
152066f9d5cbSmlf 
152166f9d5cbSmlf 	switch (devctl_ap_state.ap_ostate) {
15228aa6aadbSXiao-Yu Zhang 	case AP_OSTATE_CONFIGURED:
15238aa6aadbSXiao-Yu Zhang 		(*ap_id_list)->ap_o_state = CFGA_STAT_CONFIGURED;
15248aa6aadbSXiao-Yu Zhang 		break;
152566f9d5cbSmlf 
15268aa6aadbSXiao-Yu Zhang 	case AP_OSTATE_UNCONFIGURED:
15278aa6aadbSXiao-Yu Zhang 		(*ap_id_list)->ap_o_state = CFGA_STAT_UNCONFIGURED;
15288aa6aadbSXiao-Yu Zhang 		break;
152966f9d5cbSmlf 
15308aa6aadbSXiao-Yu Zhang 	default:
15318aa6aadbSXiao-Yu Zhang 		rv = CFGA_SATA_STATE;
15328aa6aadbSXiao-Yu Zhang 		goto bailout;
153366f9d5cbSmlf 	}
153466f9d5cbSmlf 
153566f9d5cbSmlf 	switch (devctl_ap_state.ap_condition) {
15368aa6aadbSXiao-Yu Zhang 	case AP_COND_OK:
15378aa6aadbSXiao-Yu Zhang 		(*ap_id_list)->ap_cond = CFGA_COND_OK;
15388aa6aadbSXiao-Yu Zhang 		break;
153966f9d5cbSmlf 
15408aa6aadbSXiao-Yu Zhang 	case AP_COND_FAILING:
15418aa6aadbSXiao-Yu Zhang 		(*ap_id_list)->ap_cond = CFGA_COND_FAILING;
15428aa6aadbSXiao-Yu Zhang 		break;
154366f9d5cbSmlf 
15448aa6aadbSXiao-Yu Zhang 	case AP_COND_FAILED:
15458aa6aadbSXiao-Yu Zhang 		(*ap_id_list)->ap_cond = CFGA_COND_FAILED;
15468aa6aadbSXiao-Yu Zhang 		break;
154766f9d5cbSmlf 
15488aa6aadbSXiao-Yu Zhang 	case AP_COND_UNUSABLE:
15498aa6aadbSXiao-Yu Zhang 		(*ap_id_list)->ap_cond = CFGA_COND_UNUSABLE;
15508aa6aadbSXiao-Yu Zhang 		break;
155166f9d5cbSmlf 
15528aa6aadbSXiao-Yu Zhang 	case AP_COND_UNKNOWN:
15538aa6aadbSXiao-Yu Zhang 		(*ap_id_list)->ap_cond = CFGA_COND_UNKNOWN;
15548aa6aadbSXiao-Yu Zhang 		break;
155566f9d5cbSmlf 
15568aa6aadbSXiao-Yu Zhang 	default:
15578aa6aadbSXiao-Yu Zhang 		rv = CFGA_SATA_STATE;
15588aa6aadbSXiao-Yu Zhang 		goto bailout;
155966f9d5cbSmlf 	}
156066f9d5cbSmlf 
156166f9d5cbSmlf 	(*ap_id_list)->ap_class[0] = '\0';	/* Filled by libcfgadm */
156266f9d5cbSmlf 	(*ap_id_list)->ap_busy = devctl_ap_state.ap_in_transition;
156366f9d5cbSmlf 	(*ap_id_list)->ap_status_time = devctl_ap_state.ap_last_change;
156421f023dfSToomas Soome 	(*ap_id_list)->ap_info[0] = '\0';
156566f9d5cbSmlf 
156666f9d5cbSmlf 	if ((*ap_id_list)->ap_r_state == CFGA_STAT_CONNECTED) {
156766f9d5cbSmlf 		char *str_p;
156866f9d5cbSmlf 		int skip, i;
156966f9d5cbSmlf 
157066f9d5cbSmlf 		/*
157166f9d5cbSmlf 		 * Fill in the 'Information' field for the -v option
157266f9d5cbSmlf 		 * Model (MOD:)
157366f9d5cbSmlf 		 */
157466f9d5cbSmlf 		if ((rv = do_control_ioctl(ap_id, SATA_CFGA_GET_MODEL_INFO,
157521f023dfSToomas Soome 		    0, (void **)&str_p, &size)) != CFGA_SATA_OK) {
157666f9d5cbSmlf 			(void) printf(
15778aa6aadbSXiao-Yu Zhang 			    "SATA_CFGA_GET_MODULE_INFO ioctl failed\n");
157866f9d5cbSmlf 			goto bailout;
157966f9d5cbSmlf 		}
158066f9d5cbSmlf 		/* drop leading and trailing spaces */
158166f9d5cbSmlf 		skip = strspn(str_p, " ");
158266f9d5cbSmlf 		for (i = size - 1; i >= 0; i--) {
158366f9d5cbSmlf 			if (str_p[i] == '\040')
158466f9d5cbSmlf 				str_p[i] = '\0';
158566f9d5cbSmlf 			else if (str_p[i] != '\0')
158666f9d5cbSmlf 				break;
158766f9d5cbSmlf 		}
158866f9d5cbSmlf 
158966f9d5cbSmlf 		(void) strlcpy((*ap_id_list)->ap_info, "Mod: ",
15908aa6aadbSXiao-Yu Zhang 		    sizeof ((*ap_id_list)->ap_info));
159166f9d5cbSmlf 		(void) strlcat((*ap_id_list)->ap_info, str_p + skip,
15928aa6aadbSXiao-Yu Zhang 		    sizeof ((*ap_id_list)->ap_info));
159366f9d5cbSmlf 
159466f9d5cbSmlf 		free(str_p);
159566f9d5cbSmlf 
159666f9d5cbSmlf 		/*
159766f9d5cbSmlf 		 * Fill in the 'Information' field for the -v option
159866f9d5cbSmlf 		 * Firmware revision (FREV:)
159966f9d5cbSmlf 		 */
160066f9d5cbSmlf 		if ((rv = do_control_ioctl(ap_id,
160166f9d5cbSmlf 		    SATA_CFGA_GET_REVFIRMWARE_INFO,
160221f023dfSToomas Soome 		    0, (void **)&str_p, &size)) != CFGA_SATA_OK) {
160366f9d5cbSmlf 			(void) printf(
160466f9d5cbSmlf 			    "SATA_CFGA_GET_REVFIRMWARE_INFO ioctl failed\n");
160566f9d5cbSmlf 			goto bailout;
160666f9d5cbSmlf 		}
160766f9d5cbSmlf 		/* drop leading and trailing spaces */
160866f9d5cbSmlf 		skip = strspn(str_p, " ");
160966f9d5cbSmlf 		for (i = size - 1; i >= 0; i--) {
161066f9d5cbSmlf 			if (str_p[i] == '\040')
161166f9d5cbSmlf 				str_p[i] = '\0';
161266f9d5cbSmlf 			else if (str_p[i] != '\0')
161366f9d5cbSmlf 				break;
161466f9d5cbSmlf 		}
161566f9d5cbSmlf 		(void) strlcat((*ap_id_list)->ap_info, " FRev: ",
16168aa6aadbSXiao-Yu Zhang 		    sizeof ((*ap_id_list)->ap_info));
161766f9d5cbSmlf 		(void) strlcat((*ap_id_list)->ap_info, str_p + skip,
16188aa6aadbSXiao-Yu Zhang 		    sizeof ((*ap_id_list)->ap_info));
161966f9d5cbSmlf 
162066f9d5cbSmlf 		free(str_p);
162166f9d5cbSmlf 
162266f9d5cbSmlf 
162366f9d5cbSmlf 		/*
162466f9d5cbSmlf 		 * Fill in the 'Information' field for the -v option
162566f9d5cbSmlf 		 * Serial Number (SN:)
162666f9d5cbSmlf 		 */
162766f9d5cbSmlf 		if ((rv = do_control_ioctl(ap_id,
162866f9d5cbSmlf 		    SATA_CFGA_GET_SERIALNUMBER_INFO,
162921f023dfSToomas Soome 		    0, (void **)&str_p, &size)) != CFGA_SATA_OK) {
163066f9d5cbSmlf 			(void) printf(
163166f9d5cbSmlf 			    "SATA_CFGA_GET_SERIALNUMBER_INFO ioctl failed\n");
163266f9d5cbSmlf 			goto bailout;
163366f9d5cbSmlf 		}
163466f9d5cbSmlf 		/* drop leading and trailing spaces */
163566f9d5cbSmlf 		skip = strspn(str_p, " ");
163666f9d5cbSmlf 		for (i = size - 1; i >= 0; i--) {
163766f9d5cbSmlf 			if (str_p[i] == '\040')
163866f9d5cbSmlf 				str_p[i] = '\0';
163966f9d5cbSmlf 			else if (str_p[i] != '\0')
164066f9d5cbSmlf 				break;
164166f9d5cbSmlf 		}
164266f9d5cbSmlf 		(void) strlcat((*ap_id_list)->ap_info, " SN: ",
16438aa6aadbSXiao-Yu Zhang 		    sizeof ((*ap_id_list)->ap_info));
164466f9d5cbSmlf 		(void) strlcat((*ap_id_list)->ap_info, str_p + skip,
16458aa6aadbSXiao-Yu Zhang 		    sizeof ((*ap_id_list)->ap_info));
164666f9d5cbSmlf 
164766f9d5cbSmlf 		free(str_p);
164866f9d5cbSmlf 
164966f9d5cbSmlf 
165066f9d5cbSmlf 
165166f9d5cbSmlf 		/* Fill in ap_type which is collected from HBA driver */
165266f9d5cbSmlf 		/* call do_control_ioctl TBD */
165321f023dfSToomas Soome 		if ((rv = do_control_ioctl(ap_id, SATA_CFGA_GET_AP_TYPE, 0,
165466f9d5cbSmlf 		    (void **)&str_p, &size)) != CFGA_SATA_OK) {
165566f9d5cbSmlf 			(void) printf(
16568aa6aadbSXiao-Yu Zhang 			    "SATA_CFGA_GET_AP_TYPE ioctl failed\n");
165766f9d5cbSmlf 			goto bailout;
165866f9d5cbSmlf 		}
165966f9d5cbSmlf 
166066f9d5cbSmlf 		(void) strlcpy((*ap_id_list)->ap_type, str_p,
16618aa6aadbSXiao-Yu Zhang 		    sizeof ((*ap_id_list)->ap_type));
166266f9d5cbSmlf 
166366f9d5cbSmlf 		free(str_p);
166466f9d5cbSmlf 
16658aa6aadbSXiao-Yu Zhang 		/*
16668aa6aadbSXiao-Yu Zhang 		 * Checking device type. Port multiplier has no dynamic
16678aa6aadbSXiao-Yu Zhang 		 * suffix.
16688aa6aadbSXiao-Yu Zhang 		 */
16698aa6aadbSXiao-Yu Zhang 		if (strncmp((*ap_id_list)->ap_type, "sata-pmult",
16708aa6aadbSXiao-Yu Zhang 		    sizeof ("sata-pmult")) == 0)
16718aa6aadbSXiao-Yu Zhang 			pmult = B_TRUE;
16728aa6aadbSXiao-Yu Zhang 
16738aa6aadbSXiao-Yu Zhang 		if ((*ap_id_list)->ap_o_state == CFGA_STAT_CONFIGURED &&
16748aa6aadbSXiao-Yu Zhang 		    pmult == B_FALSE) {
167566f9d5cbSmlf 
167666f9d5cbSmlf 			char *dyncomp = NULL;
167766f9d5cbSmlf 
167866f9d5cbSmlf 			/*
167966f9d5cbSmlf 			 * This is the case where we need to generate
168066f9d5cbSmlf 			 * a dynamic component of the ap_id, i.e. device.
168166f9d5cbSmlf 			 */
168238547057Sying tian - Beijing China 			rv = sata_make_dyncomp(ap_id, &dyncomp,
168338547057Sying tian - Beijing China 			    (*ap_id_list)->ap_type);
168466f9d5cbSmlf 			if (rv != CFGA_SATA_OK)
168566f9d5cbSmlf 				goto bailout;
168666f9d5cbSmlf 			if (dyncomp != NULL) {
168766f9d5cbSmlf 				(void) strcat((*ap_id_list)->ap_log_id,
16888aa6aadbSXiao-Yu Zhang 				    DYN_SEP);
168966f9d5cbSmlf 				(void) strlcat((*ap_id_list)->ap_log_id,
16908aa6aadbSXiao-Yu Zhang 				    dyncomp,
16918aa6aadbSXiao-Yu Zhang 				    sizeof ((*ap_id_list)->ap_log_id));
169266f9d5cbSmlf 				free(dyncomp);
169366f9d5cbSmlf 			}
169466f9d5cbSmlf 		}
169566f9d5cbSmlf 
169666f9d5cbSmlf 	} else {
16978aa6aadbSXiao-Yu Zhang 		/* This is an empty port */
16982c2d21e9SRichard Lowe 		if (get_port_num(ap_id, &port) != CFGA_SATA_OK) {
16998aa6aadbSXiao-Yu Zhang 			goto bailout;
17008aa6aadbSXiao-Yu Zhang 		}
17018aa6aadbSXiao-Yu Zhang 
17028aa6aadbSXiao-Yu Zhang 		if (port & SATA_CFGA_PMPORT_QUAL) {
17038aa6aadbSXiao-Yu Zhang 			(void) strlcpy((*ap_id_list)->ap_type, "pmult-port",
17048aa6aadbSXiao-Yu Zhang 			    sizeof ((*ap_id_list)->ap_type));
17058aa6aadbSXiao-Yu Zhang 		} else {
17068aa6aadbSXiao-Yu Zhang 			(void) strlcpy((*ap_id_list)->ap_type, "sata-port",
17078aa6aadbSXiao-Yu Zhang 			    sizeof ((*ap_id_list)->ap_type));
17088aa6aadbSXiao-Yu Zhang 		}
170966f9d5cbSmlf 	}
171066f9d5cbSmlf 
171166f9d5cbSmlf 	return (sata_err_msg(errstring, rv, ap_id, errno));
171266f9d5cbSmlf 
171366f9d5cbSmlf bailout:
171466f9d5cbSmlf 	if (*ap_id_list != NULL) {
171566f9d5cbSmlf 		free(*ap_id_list);
171666f9d5cbSmlf 	}
171766f9d5cbSmlf 	if (ap_id_log != NULL) {
171866f9d5cbSmlf 		free(ap_id_log);
171966f9d5cbSmlf 	}
172066f9d5cbSmlf 
172166f9d5cbSmlf 	return (sata_err_msg(errstring, rv, ap_id, errno));
172266f9d5cbSmlf }
172366f9d5cbSmlf /*
172466f9d5cbSmlf  * This routine accepts a string adn prints it using
172566f9d5cbSmlf  * the message print routine argument.
172666f9d5cbSmlf  */
172766f9d5cbSmlf static void
cfga_msg(struct cfga_msg * msgp,const char * str)172866f9d5cbSmlf cfga_msg(struct cfga_msg *msgp, const char *str)
172966f9d5cbSmlf {
173066f9d5cbSmlf 	int len;
173166f9d5cbSmlf 	char *q;
173266f9d5cbSmlf 
173366f9d5cbSmlf 	if (msgp == NULL || msgp->message_routine == NULL) {
173466f9d5cbSmlf 		(void) printf("cfga_msg: NULL msgp\n");
173566f9d5cbSmlf 		return;
173666f9d5cbSmlf 	}
173766f9d5cbSmlf 
173866f9d5cbSmlf 	if ((len = strlen(str)) == 0) {
173966f9d5cbSmlf 		(void) printf("cfga_msg: null str\n");
174066f9d5cbSmlf 		return;
174166f9d5cbSmlf 	}
174266f9d5cbSmlf 
174366f9d5cbSmlf 	if ((q = (char *)calloc(len + 1, 1)) == NULL) {
17448aa6aadbSXiao-Yu Zhang 		perror("cfga_msg");
174566f9d5cbSmlf 		return;
174666f9d5cbSmlf 	}
174766f9d5cbSmlf 
174866f9d5cbSmlf 	(void) strcpy(q, str);
174961233e71SJohn Levon 	(void) (*msgp->message_routine)(msgp->appdata_ptr, q);
175066f9d5cbSmlf 
175166f9d5cbSmlf 	free(q);
175266f9d5cbSmlf }
175366f9d5cbSmlf 
175466f9d5cbSmlf /* cfgadm entry point */
175566f9d5cbSmlf /* ARGSUSED */
175666f9d5cbSmlf cfga_err_t
cfga_help(struct cfga_msg * msgp,const char * options,cfga_flags_t flags)175766f9d5cbSmlf cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
175866f9d5cbSmlf {
175966f9d5cbSmlf 	if (options != NULL) {
176066f9d5cbSmlf 		cfga_msg(msgp, dgettext(TEXT_DOMAIN, sata_help[HELP_UNKNOWN]));
176166f9d5cbSmlf 		cfga_msg(msgp, options);
176266f9d5cbSmlf 	}
176366f9d5cbSmlf 	cfga_msg(msgp, dgettext(TEXT_DOMAIN, sata_help[HELP_HEADER]));
176466f9d5cbSmlf 	cfga_msg(msgp, sata_help[HELP_CONFIG]);
176566f9d5cbSmlf 	cfga_msg(msgp, sata_help[HELP_RESET_PORT]);
176666f9d5cbSmlf 	cfga_msg(msgp, sata_help[HELP_RESET_DEVICE]);
176766f9d5cbSmlf 	cfga_msg(msgp, sata_help[HELP_RESET_ALL]);
176866f9d5cbSmlf 	cfga_msg(msgp, sata_help[HELP_PORT_ACTIVATE]);
176966f9d5cbSmlf 	cfga_msg(msgp, sata_help[HELP_PORT_DEACTIVATE]);
177066f9d5cbSmlf 	cfga_msg(msgp, sata_help[HELP_PORT_SELF_TEST]);
177166f9d5cbSmlf 	cfga_msg(msgp, sata_help[HELP_CNTRL_SELF_TEST]);
177266f9d5cbSmlf 
177366f9d5cbSmlf 	return (CFGA_OK);
177466f9d5cbSmlf }
177566f9d5cbSmlf 
177666f9d5cbSmlf 
177766f9d5cbSmlf /*
177866f9d5cbSmlf  * Ensure the ap_id passed is in the correct (physical ap_id) form:
177966f9d5cbSmlf  *     path/device:xx[.xx]
178066f9d5cbSmlf  * where xx is a one or two-digit number.
178166f9d5cbSmlf  *
178266f9d5cbSmlf  * Note the library always calls the plugin with a physical ap_id.
178366f9d5cbSmlf  */
178466f9d5cbSmlf static int
verify_valid_apid(const char * ap_id)178566f9d5cbSmlf verify_valid_apid(const char *ap_id)
178666f9d5cbSmlf {
178766f9d5cbSmlf 	char	*l_ap_id;
178866f9d5cbSmlf 
178966f9d5cbSmlf 	if (ap_id == NULL)
179066f9d5cbSmlf 		return (-1);
179166f9d5cbSmlf 
179266f9d5cbSmlf 	l_ap_id = strrchr(ap_id, (int)*MINOR_SEP);
179366f9d5cbSmlf 	l_ap_id++;
179466f9d5cbSmlf 
179566f9d5cbSmlf 	if (strspn(l_ap_id, "0123456789.") != strlen(l_ap_id)) {
179666f9d5cbSmlf 		/* Bad characters in the ap_id */
179766f9d5cbSmlf 		return (-1);
179866f9d5cbSmlf 	}
179966f9d5cbSmlf 
180066f9d5cbSmlf 	if (strstr(l_ap_id, "..") != NULL) {
180166f9d5cbSmlf 		/* ap_id has 1..2 or more than 2 dots */
180266f9d5cbSmlf 		return (-1);
180366f9d5cbSmlf 	}
180466f9d5cbSmlf 
180566f9d5cbSmlf 	return (0);
180666f9d5cbSmlf }
180766f9d5cbSmlf 
180866f9d5cbSmlf 
180966f9d5cbSmlf 
181066f9d5cbSmlf /*
181166f9d5cbSmlf  * Verify the params passed in are valid.
181266f9d5cbSmlf  */
181366f9d5cbSmlf static cfga_sata_ret_t
verify_params(const char * ap_id,const char * options,char ** errstring)181466f9d5cbSmlf verify_params(
18158aa6aadbSXiao-Yu Zhang     const char *ap_id,
18168aa6aadbSXiao-Yu Zhang     const char *options,
18178aa6aadbSXiao-Yu Zhang     char **errstring)
181866f9d5cbSmlf {
181966f9d5cbSmlf 	char *pdyn, *lap_id;
182066f9d5cbSmlf 	int rv;
182166f9d5cbSmlf 
182266f9d5cbSmlf 	if (errstring != NULL) {
182366f9d5cbSmlf 		*errstring = NULL;
182466f9d5cbSmlf 	}
182566f9d5cbSmlf 
182666f9d5cbSmlf 	if (options != NULL) {
182766f9d5cbSmlf 		return (CFGA_SATA_OPTIONS);
182866f9d5cbSmlf 	}
182966f9d5cbSmlf 
183066f9d5cbSmlf 	/* Strip dynamic AP name component if it is present. */
183166f9d5cbSmlf 	lap_id = strdup(ap_id);
183266f9d5cbSmlf 	if (lap_id == NULL) {
183366f9d5cbSmlf 		return (CFGA_SATA_ALLOC_FAIL);
183466f9d5cbSmlf 	}
183566f9d5cbSmlf 	if ((pdyn = GET_DYN(lap_id)) != NULL) {
183666f9d5cbSmlf 		*pdyn = '\0';
183766f9d5cbSmlf 	}
183866f9d5cbSmlf 
183966f9d5cbSmlf 	if (verify_valid_apid(lap_id) != 0) {
184066f9d5cbSmlf 		rv = CFGA_SATA_AP;
184166f9d5cbSmlf 	} else {
184266f9d5cbSmlf 		rv = CFGA_SATA_OK;
184366f9d5cbSmlf 	}
184466f9d5cbSmlf 	free(lap_id);
184566f9d5cbSmlf 
184666f9d5cbSmlf 	return (rv);
184766f9d5cbSmlf }
184866f9d5cbSmlf 
184966f9d5cbSmlf /*
185066f9d5cbSmlf  * Takes a validated ap_id and extracts the port number.
18518aa6aadbSXiao-Yu Zhang  * Port multiplier is supported now.
185266f9d5cbSmlf  */
185366f9d5cbSmlf static cfga_sata_ret_t
get_port_num(const char * ap_id,uint32_t * port)185466f9d5cbSmlf get_port_num(const char *ap_id, uint32_t *port)
185566f9d5cbSmlf {
18568aa6aadbSXiao-Yu Zhang 	uint32_t	cport, pmport = 0, qual = 0;
18578aa6aadbSXiao-Yu Zhang 	char		*cport_str, *pmport_str;
18588aa6aadbSXiao-Yu Zhang 
18598aa6aadbSXiao-Yu Zhang 	/* Get the cport number */
18608aa6aadbSXiao-Yu Zhang 	cport_str = strrchr(ap_id, (int)*MINOR_SEP) + strlen(MINOR_SEP);
186166f9d5cbSmlf 
186266f9d5cbSmlf 	errno = 0;
18638aa6aadbSXiao-Yu Zhang 	cport = strtol(cport_str, NULL, 10);
18648aa6aadbSXiao-Yu Zhang 	if ((cport & ~SATA_CFGA_CPORT_MASK) != 0 || errno != 0) {
186566f9d5cbSmlf 		return (CFGA_SATA_PORT);
18668aa6aadbSXiao-Yu Zhang 	}
186766f9d5cbSmlf 
18688aa6aadbSXiao-Yu Zhang 	/* Get pmport number if there is a PORT_SEPARATOR */
18698aa6aadbSXiao-Yu Zhang 	errno = 0;
18708aa6aadbSXiao-Yu Zhang 	if ((pmport_str = strrchr(ap_id, (int)*PORT_SEPARATOR)) != 0) {
18718aa6aadbSXiao-Yu Zhang 		pmport_str += strlen(PORT_SEPARATOR);
18728aa6aadbSXiao-Yu Zhang 		pmport = strtol(pmport_str, NULL, 10);
18738aa6aadbSXiao-Yu Zhang 		qual = SATA_CFGA_PMPORT_QUAL;
18748aa6aadbSXiao-Yu Zhang 		if ((pmport & ~SATA_CFGA_PMPORT_MASK) != 0 || errno != 0) {
18758aa6aadbSXiao-Yu Zhang 			return (CFGA_SATA_PORT);
18768aa6aadbSXiao-Yu Zhang 		}
18778aa6aadbSXiao-Yu Zhang 	}
187866f9d5cbSmlf 
18798aa6aadbSXiao-Yu Zhang 	*port = cport | (pmport << SATA_CFGA_PMPORT_SHIFT) | qual;
188066f9d5cbSmlf 	return (CFGA_SATA_OK);
188166f9d5cbSmlf }
188266f9d5cbSmlf 
188366f9d5cbSmlf /*
188466f9d5cbSmlf  * Pair of routines to set up for/clean up after a devctl_ap_* lib call.
188566f9d5cbSmlf  */
188666f9d5cbSmlf static void
cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl,nvlist_t * user_nvlist)188766f9d5cbSmlf cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl, nvlist_t *user_nvlist)
188866f9d5cbSmlf {
188966f9d5cbSmlf 	if (user_nvlist != NULL) {
189066f9d5cbSmlf 		nvlist_free(user_nvlist);
189166f9d5cbSmlf 	}
189266f9d5cbSmlf 	if (devctl_hdl != NULL) {
189366f9d5cbSmlf 		devctl_release(devctl_hdl);
189466f9d5cbSmlf 	}
189566f9d5cbSmlf }
189666f9d5cbSmlf 
189766f9d5cbSmlf 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)189866f9d5cbSmlf setup_for_devctl_cmd(
18998aa6aadbSXiao-Yu Zhang     const char *ap_id,
19008aa6aadbSXiao-Yu Zhang     devctl_hdl_t *devctl_hdl,
19018aa6aadbSXiao-Yu Zhang     nvlist_t **user_nvlistp,
19028aa6aadbSXiao-Yu Zhang     uint_t oflag)
190366f9d5cbSmlf {
190466f9d5cbSmlf 
190566f9d5cbSmlf 	uint_t	port;
190666f9d5cbSmlf 	cfga_sata_ret_t	rv = CFGA_SATA_OK;
190766f9d5cbSmlf 	char *lap_id, *pdyn;
190866f9d5cbSmlf 
190966f9d5cbSmlf 	lap_id = strdup(ap_id);
191066f9d5cbSmlf 	if (lap_id == NULL)
191166f9d5cbSmlf 		return (CFGA_SATA_ALLOC_FAIL);
191266f9d5cbSmlf 	if ((pdyn = GET_DYN(lap_id)) != NULL) {
191366f9d5cbSmlf 		*pdyn = '\0';
191466f9d5cbSmlf 	}
191566f9d5cbSmlf 
191666f9d5cbSmlf 	/* Get a devctl handle to pass to the devctl_ap_XXX functions */
191766f9d5cbSmlf 	if ((*devctl_hdl = devctl_ap_acquire((char *)lap_id, oflag)) == NULL) {
1918724365f7Ssethg 		(void) fprintf(stderr, "[libcfgadm:sata] "
1919724365f7Ssethg 		    "setup_for_devctl_cmd: devctl_ap_acquire failed: %s\n",
1920724365f7Ssethg 		    strerror(errno));
192166f9d5cbSmlf 		rv = CFGA_SATA_DEVCTL;
192266f9d5cbSmlf 		goto bailout;
192366f9d5cbSmlf 	}
192466f9d5cbSmlf 
192566f9d5cbSmlf 	/* Set up nvlist to pass the port number down to the driver */
192621f023dfSToomas Soome 	if (nvlist_alloc(user_nvlistp, NV_UNIQUE_NAME_TYPE, 0) != 0) {
192766f9d5cbSmlf 		*user_nvlistp = NULL;
192866f9d5cbSmlf 		rv = CFGA_SATA_NVLIST;
192966f9d5cbSmlf 		(void) printf("nvlist_alloc failed\n");
193066f9d5cbSmlf 		goto bailout;
193166f9d5cbSmlf 	}
193266f9d5cbSmlf 
193366f9d5cbSmlf 	/*
193466f9d5cbSmlf 	 * Get port id, for Port Multiplier port, things could be a little bit
193566f9d5cbSmlf 	 * complicated because of "port.port" format in ap_id, thus for
193666f9d5cbSmlf 	 * port multiplier port, port number should be coded as 32bit int
193766f9d5cbSmlf 	 * with the sig 16 bit as sata channel number, least 16 bit as
193866f9d5cbSmlf 	 * the port number of sata port multiplier port.
193966f9d5cbSmlf 	 */
194066f9d5cbSmlf 	if ((rv = get_port_num(lap_id, &port)) != CFGA_SATA_OK) {
194166f9d5cbSmlf 		(void) printf(
19428aa6aadbSXiao-Yu Zhang 		    "setup_for_devctl_cmd: get_port_num, errno: %d\n",
19438aa6aadbSXiao-Yu Zhang 		    errno);
194466f9d5cbSmlf 		goto bailout;
194566f9d5cbSmlf 	}
194666f9d5cbSmlf 
194766f9d5cbSmlf 	/* Creates an int32_t entry */
194866f9d5cbSmlf 	if (nvlist_add_int32(*user_nvlistp, PORT, port) == -1) {
194966f9d5cbSmlf 		(void) printf("nvlist_add_int32 failed\n");
195066f9d5cbSmlf 		rv = CFGA_SATA_NVLIST;
195166f9d5cbSmlf 		goto bailout;
195266f9d5cbSmlf 	}
195366f9d5cbSmlf 
1954724365f7Ssethg 	free(lap_id);
195566f9d5cbSmlf 	return (rv);
195666f9d5cbSmlf 
195766f9d5cbSmlf bailout:
195866f9d5cbSmlf 	free(lap_id);
195966f9d5cbSmlf 	(void) cleanup_after_devctl_cmd(*devctl_hdl, *user_nvlistp);
196066f9d5cbSmlf 
196166f9d5cbSmlf 	return (rv);
196266f9d5cbSmlf }
196366f9d5cbSmlf 
196466f9d5cbSmlf 
196566f9d5cbSmlf static cfga_sata_ret_t
port_state(devctl_hdl_t hdl,nvlist_t * list,ap_rstate_t * rstate,ap_ostate_t * ostate)196666f9d5cbSmlf port_state(devctl_hdl_t hdl, nvlist_t *list,
19678aa6aadbSXiao-Yu Zhang     ap_rstate_t *rstate, ap_ostate_t *ostate)
196866f9d5cbSmlf {
196966f9d5cbSmlf 	devctl_ap_state_t	devctl_ap_state;
197066f9d5cbSmlf 
197166f9d5cbSmlf 	if (devctl_ap_getstate(hdl, list, &devctl_ap_state) == -1) {
197266f9d5cbSmlf 		(void) printf("devctl_ap_getstate failed, errno: %d\n", errno);
197366f9d5cbSmlf 		return (CFGA_SATA_IOCTL);
197466f9d5cbSmlf 	}
197566f9d5cbSmlf 	*rstate = devctl_ap_state.ap_rstate;
197666f9d5cbSmlf 	*ostate =  devctl_ap_state.ap_ostate;
197766f9d5cbSmlf 	return (CFGA_SATA_OK);
197866f9d5cbSmlf }
197966f9d5cbSmlf 
198066f9d5cbSmlf 
198166f9d5cbSmlf /*
198266f9d5cbSmlf  * Given a subcommand to the DEVCTL_AP_CONTROL ioctl, rquest the size of
198366f9d5cbSmlf  * the data to be returned, allocate a buffer, then get the data.
198466f9d5cbSmlf  * Returns *descrp (which must be freed) and size.
198566f9d5cbSmlf  *
198666f9d5cbSmlf  * Note SATA_DESCR_TYPE_STRING returns an ASCII NULL-terminated string,
198766f9d5cbSmlf  * not a string descr.
198866f9d5cbSmlf  */
198966f9d5cbSmlf cfga_sata_ret_t
do_control_ioctl(const char * ap_id,sata_cfga_apctl_t subcommand,uint_t arg,void ** descrp,size_t * sizep)199066f9d5cbSmlf do_control_ioctl(const char *ap_id, sata_cfga_apctl_t subcommand, uint_t arg,
19918aa6aadbSXiao-Yu Zhang     void **descrp, size_t *sizep)
199266f9d5cbSmlf {
199366f9d5cbSmlf 	int			fd = -1;
199466f9d5cbSmlf 	uint_t			port;
199566f9d5cbSmlf 	uint32_t		local_size;
199666f9d5cbSmlf 	cfga_sata_ret_t		rv = CFGA_SATA_OK;
199766f9d5cbSmlf 	struct sata_ioctl_data	ioctl_data;
199866f9d5cbSmlf 
199966f9d5cbSmlf 	assert(descrp != NULL);
200066f9d5cbSmlf 	*descrp = NULL;
200166f9d5cbSmlf 	assert(sizep != NULL);
200266f9d5cbSmlf 
200366f9d5cbSmlf 	if ((rv = get_port_num(ap_id, &port)) != CFGA_SATA_OK) {
200466f9d5cbSmlf 		goto bailout;
200566f9d5cbSmlf 	}
200666f9d5cbSmlf 
200766f9d5cbSmlf 	if ((fd = open(ap_id, O_RDONLY)) == -1) {
200866f9d5cbSmlf 		(void) printf("do_control_ioctl: open failed: errno:%d\n",
20098aa6aadbSXiao-Yu Zhang 		    errno);
201066f9d5cbSmlf 		rv = CFGA_SATA_OPEN;
201166f9d5cbSmlf 		if (errno == EBUSY) {
201266f9d5cbSmlf 			rv = CFGA_SATA_BUSY;
201366f9d5cbSmlf 		}
201466f9d5cbSmlf 		goto bailout;
201566f9d5cbSmlf 	}
201666f9d5cbSmlf 
201766f9d5cbSmlf 	ioctl_data.cmd = subcommand;
201866f9d5cbSmlf 	ioctl_data.port = port;
201966f9d5cbSmlf 	ioctl_data.misc_arg = (uint_t)arg;
202066f9d5cbSmlf 
202166f9d5cbSmlf 	/*
202266f9d5cbSmlf 	 * Find out how large a buf we need to get the data.
202366f9d5cbSmlf 	 * Note the ioctls only accept/return a 32-bit int for a get_size
202466f9d5cbSmlf 	 * to avoid 32/64 and BE/LE issues.
202566f9d5cbSmlf 	 */
202666f9d5cbSmlf 	if ((subcommand == SATA_CFGA_GET_AP_TYPE) ||
202766f9d5cbSmlf 	    (subcommand == SATA_CFGA_GET_DEVICE_PATH) ||
202866f9d5cbSmlf 	    (subcommand == SATA_CFGA_GET_MODEL_INFO) ||
202966f9d5cbSmlf 	    (subcommand == SATA_CFGA_GET_REVFIRMWARE_INFO) ||
203066f9d5cbSmlf 	    (subcommand == SATA_CFGA_GET_SERIALNUMBER_INFO)) {
203166f9d5cbSmlf 		ioctl_data.get_size = B_TRUE;
203266f9d5cbSmlf 		ioctl_data.buf = (caddr_t)&local_size;
203366f9d5cbSmlf 		ioctl_data.bufsiz = sizeof (local_size);
203466f9d5cbSmlf 
203566f9d5cbSmlf 		if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
203666f9d5cbSmlf 			perror("ioctl failed (size)");
203766f9d5cbSmlf 			rv = CFGA_SATA_IOCTL;
203866f9d5cbSmlf 			goto bailout;
203966f9d5cbSmlf 		}
204066f9d5cbSmlf 		*sizep = local_size;
204166f9d5cbSmlf 
204266f9d5cbSmlf 		if (local_size == 0) {
204366f9d5cbSmlf 			(void) printf("zero length data\n");
204466f9d5cbSmlf 			rv = CFGA_SATA_ZEROLEN;
204566f9d5cbSmlf 			goto bailout;
204666f9d5cbSmlf 		}
204766f9d5cbSmlf 		if ((*descrp = malloc(*sizep)) == NULL) {
204866f9d5cbSmlf 			(void) printf("do_control_ioctl: malloc failed\n");
204966f9d5cbSmlf 			rv = CFGA_SATA_ALLOC_FAIL;
205066f9d5cbSmlf 			goto bailout;
205166f9d5cbSmlf 		}
205266f9d5cbSmlf 	} else {
205366f9d5cbSmlf 		*sizep = 0;
205466f9d5cbSmlf 	}
205566f9d5cbSmlf 	ioctl_data.get_size = B_FALSE;
205666f9d5cbSmlf 	ioctl_data.buf = *descrp;
205766f9d5cbSmlf 	ioctl_data.bufsiz = *sizep;
205866f9d5cbSmlf 
205966f9d5cbSmlf 	/* Execute IOCTL */
206066f9d5cbSmlf 
206166f9d5cbSmlf 	if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
206266f9d5cbSmlf 		rv = CFGA_SATA_IOCTL;
206366f9d5cbSmlf 		goto bailout;
206466f9d5cbSmlf 	}
206566f9d5cbSmlf 
206666f9d5cbSmlf 	(void) close(fd);
206766f9d5cbSmlf 
206866f9d5cbSmlf 	return (rv);
206966f9d5cbSmlf 
207066f9d5cbSmlf bailout:
207166f9d5cbSmlf 	if (fd != -1) {
207266f9d5cbSmlf 		(void) close(fd);
207366f9d5cbSmlf 	}
207466f9d5cbSmlf 	if (*descrp != NULL) {
207566f9d5cbSmlf 		free(*descrp);
207666f9d5cbSmlf 		*descrp = NULL;
207766f9d5cbSmlf 	}
207866f9d5cbSmlf 
207966f9d5cbSmlf 	if (rv == CFGA_SATA_IOCTL && errno == EBUSY) {
208066f9d5cbSmlf 		rv = CFGA_SATA_BUSY;
208166f9d5cbSmlf 	}
208266f9d5cbSmlf 
208366f9d5cbSmlf 	return (rv);
208466f9d5cbSmlf }
208566f9d5cbSmlf 
208666f9d5cbSmlf 
208766f9d5cbSmlf static int
sata_confirm(struct cfga_confirm * confp,char * msg)208866f9d5cbSmlf sata_confirm(struct cfga_confirm *confp, char *msg)
208966f9d5cbSmlf {
209066f9d5cbSmlf 	int rval;
209166f9d5cbSmlf 
209266f9d5cbSmlf 	if (confp == NULL || confp->confirm == NULL) {
209366f9d5cbSmlf 		return (0);
209466f9d5cbSmlf 	}
209566f9d5cbSmlf 	rval = (*confp->confirm)(confp->appdata_ptr, msg);
209666f9d5cbSmlf 
209766f9d5cbSmlf 	return (rval);
209866f9d5cbSmlf }
209966f9d5cbSmlf 
210066f9d5cbSmlf 
210166f9d5cbSmlf static char *
sata_get_devicepath(const char * ap_id)210266f9d5cbSmlf sata_get_devicepath(const char *ap_id)
210366f9d5cbSmlf {
210466f9d5cbSmlf 	char		*devpath = NULL;
210566f9d5cbSmlf 	size_t		size;
210666f9d5cbSmlf 	cfga_sata_ret_t	rv;
210766f9d5cbSmlf 
210821f023dfSToomas Soome 	rv = do_control_ioctl(ap_id, SATA_CFGA_GET_DEVICE_PATH, 0,
210966f9d5cbSmlf 	    (void **)&devpath, &size);
211066f9d5cbSmlf 
211166f9d5cbSmlf 	if (rv == CFGA_SATA_OK) {
211266f9d5cbSmlf 		return (devpath);
211366f9d5cbSmlf 	} else {
211466f9d5cbSmlf 		return ((char *)NULL);
211566f9d5cbSmlf 	}
211666f9d5cbSmlf 
211766f9d5cbSmlf }
2118