1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/param.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <dirent.h>
34 #include "cfga_sata.h"
35 
36 /*
37  * This file contains the entry points to the plug-in as defined in the
38  * config_admin(3X) man page.
39  */
40 
41 /*
42  * Set the version number for the cfgadm library's use.
43  */
44 int cfga_version = CFGA_HSL_V2;
45 
46 enum {
47 	HELP_HEADER = 1,
48 	HELP_CONFIG,
49 	HELP_RESET_PORT,
50 	HELP_RESET_DEVICE,
51 	HELP_RESET_ALL,
52 	HELP_PORT_DEACTIVATE,
53 	HELP_PORT_ACTIVATE,
54 	HELP_PORT_SELF_TEST,
55 	HELP_CNTRL_SELF_TEST,
56 	HELP_UNKNOWN
57 };
58 
59 /* SATA specific help messages */
60 static char *sata_help[] = {
61 	NULL,
62 	"SATA specific commands:\n",
63 	" cfgadm -c [configure|unconfigure|disconnect|connect] ap_id "
64 	"[ap_id...]\n",
65 	" cfgadm -x sata_reset_port ap_id  [ap_id...]\n",
66 	" cfgadm -x sata_reset_device ap_id [ap_id...]\n",
67 	" cfgadm -x sata_reset_all ap_id\n",
68 	" cfgadm -x sata_port_deactivate ap_id [ap_id...]\n",
69 	" cfgadm -x sata_port_activate ap_id [ap_id...]\n",
70 	" cfgadm -x sata_port_self_test ap_id [ap_id...]\n",
71 	" cfgadm -t ap_id\n",
72 	"\tunknown command or option:\n",
73 	NULL
74 };	/* End help messages */
75 
76 
77 /*
78  * Messages.
79  */
80 static msgcvt_t sata_msgs[] = {
81 	/* CFGA_SATA_OK */
82 	{ CVT, CFGA_OK, "" },
83 
84 	/* CFGA_SATA_NACK */
85 	{ CVT, CFGA_NACK, "" },
86 
87 	/* CFGA_SATA_DEVICE_UNCONFIGURED */
88 	{ CVT, CFGA_OK, "Device unconfigured prior to disconnect" },
89 
90 	/* CFGA_SATA_UNKNOWN / CFGA_LIB_ERROR -> "Library error" */
91 	{ CVT, CFGA_LIB_ERROR, "Unknown message; internal error" },
92 
93 	/* CFGA_SATA_INTERNAL_ERROR / CFGA_LIB_ERROR -> "Library error" */
94 	{ CVT, CFGA_LIB_ERROR, "Internal error" },
95 
96 	/* CFGA_SATA_DATA_ERROR / CFGA_DATA_ERROR -> "Data error" */
97 	{ CVT, CFGA_DATA_ERROR, "cfgadm data error" },
98 
99 	/* CFGA_SATA_OPTIONS / CFGA_ERROR -> "Hardware specific failure" */
100 	{ CVT, CFGA_ERROR, "Hardware specific option not supported" },
101 
102 	/* CFGA_SATA_HWOPNOTSUPP / CFGA_ERROR -> "Hardware specific failure" */
103 	{ CVT, CFGA_ERROR, "Hardware specific operation not supported" },
104 
105 	/*
106 	 * CFGA_SATA_DYNAMIC_AP /
107 	 * CFGA_LIB_ERROR -> "Configuration operation invalid"
108 	 */
109 	{ CVT, CFGA_INVAL, "Cannot identify attached device" },
110 
111 	/* CFGA_SATA_AP / CFGA_APID_NOEXIST -> "Attachment point not found" */
112 	{ CVT, CFGA_APID_NOEXIST, "" },
113 
114 	/* CFGA_SATA_PORT / CFGA_LIB_ERROR -> "Library error" */
115 	{ CVT, CFGA_LIB_ERROR, "Cannot determine sata port number for " },
116 
117 	/* CFGA_SATA_DEVCTL / CFGA_LIB_ERROR -> "Library error" */
118 	{ CVT, CFGA_LIB_ERROR, "Internal error: "
119 	    "Cannot allocate devctl handle " },
120 
121 	/*
122 	 * CFGA_SATA_DEV_CONFIGURE /
123 	 * CFGA_ERROR -> "Hardware specific failure"
124 	 */
125 	{ CVT, CFGA_ERROR, "Failed to config device at " },
126 
127 	/*
128 	 * CFGA_SATA_DEV_UNCONFIGURE /
129 	 * CFGA_ERROR -> "Hardware specific failure"
130 	 */
131 	{ CVT, CFGA_ERROR, "Failed to unconfig device at " },
132 
133 	/*
134 	 * CFGA_SATA_DISCONNECTED
135 	 * CFGA_INVAL -> "Configuration operation invalid"
136 	 */
137 	{ CVT, CFGA_INVAL, "Port already disconnected " },
138 
139 	/*
140 	 * CFGA_SATA_NOT_CONNECTED
141 	 * CFGA_INVAL -> "Configuration operation invalid"
142 	 */
143 	{ CVT, CFGA_INVAL, "No device connected to " },
144 
145 	/*
146 	 * CFGA_SATA_NOT_CONFIGURED /
147 	 * CFGA_INVAL -> "Configuration operation invalid"
148 	 */
149 	{ CVT, CFGA_INVAL, "No device configured at " },
150 
151 	/*
152 	 * CFGA_SATA_ALREADY_CONNECTED /
153 	 * CFGA_INVAL -> "Configuration operation invalid"
154 	 */
155 	{ CVT, CFGA_INVAL, "Device already connected to " },
156 
157 	/*
158 	 * CFGA_SATA_ALREADY_CONFIGURED /
159 	 * CFGA_INVAL -> "Configuration operation invalid"
160 	 */
161 	{ CVT, CFGA_INVAL, "Device already configured at " },
162 
163 	/*
164 	 * CFGA_SATA_INVALID_DEVNAME /
165 	 * CFGA_INVAL -> "Configuration operation invalid"
166 	 */
167 	{ CVT, CFGA_INVAL, "Cannot specify device name" },
168 
169 	/* CFGA_SATA_OPEN / CFGA_LIB_ERROR -> "Library error" */
170 	{ CVT, CFGA_LIB_ERROR, "Cannot open " },
171 
172 	/* CFGA_SATA_IOCTL / CFGA_ERROR -> "Hardware specific failure"  */
173 	{ CVT, CFGA_ERROR, "Driver ioctl failed " },
174 
175 	/*
176 	 * CFGA_SATA_BUSY /
177 	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
178 	 */
179 	{ CVT, CFGA_SYSTEM_BUSY, "" },
180 
181 	/* CFGA_SATA_ALLOC_FAIL / CFGA_LIB_ERROR -> "Library error" */
182 	{ CVT, CFGA_LIB_ERROR, "Memory allocation failure" },
183 
184 	/*
185 	 * CFGA_SATA_OPNOTSUPP /
186 	 * CFGA_OPNOTSUPP -> "Configuration operation not supported"
187 	 */
188 	{ CVT, CFGA_OPNOTSUPP, "Operation not supported" },
189 
190 	/* CFGA_SATA_DEVLINK / CFGA_LIB_ERROR -> "Library error" */
191 	{ CVT, CFGA_LIB_ERROR, "Could not find /dev/cfg link for " },
192 
193 	/* CFGA_SATA_STATE / CFGA_LIB_ERROR -> "Library error" */
194 	{ CVT, CFGA_LIB_ERROR, "Internal error: Unrecognized ap state" },
195 
196 	/* CFGA_SATA_PRIV / CFGA_PRIV -> "Insufficient privileges" */
197 	{ CVT, CFGA_PRIV, "" },
198 
199 	/* CFGA_SATA_NVLIST / CFGA_ERROR -> "Hardware specific failure" */
200 	{ CVT, CFGA_ERROR, "Internal error (nvlist)" },
201 
202 	/* CFGA_SATA_ZEROLEN / CFGA_ERROR -> "Hardware specific failure" */
203 	{ CVT, CFGA_ERROR, "Internal error (zerolength string)" },
204 
205 	/* CFGA_SATA_RCM_HANDLE / CFGA_ERROR -> "Hardware specific failure" */
206 	{ CVT, CFGA_ERROR, "cannot get RCM handle"},
207 
208 	/*
209 	 * CFGA_SATA_RCM_ONLINE /
210 	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
211 	 */
212 	{ CVT, CFGA_SYSTEM_BUSY, "failed to online: "},
213 
214 	/*
215 	 * CFGA_SATA_RCM_OFFLINE /
216 	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
217 	 */
218 	{ CVT, CFGA_SYSTEM_BUSY, "failed to offline: "},
219 
220 	/* CFGA_SATA_RCM_INFO / CFGA_ERROR -> "Hardware specific failure" */
221 	{ CVT, CFGA_ERROR, "failed to query: "}
222 
223 };	/* End error messages */
224 
225 static cfga_sata_ret_t
226 verify_params(const char *ap_id, const char *options, char **errstring);
227 
228 
229 static cfga_sata_ret_t
230 setup_for_devctl_cmd(const char *ap_id, devctl_hdl_t *devctl_hdl,
231 	nvlist_t **user_nvlistp, uint_t oflag);
232 
233 static cfga_sata_ret_t
234 port_state(devctl_hdl_t hdl, nvlist_t *list,
235 	ap_rstate_t *rstate, ap_ostate_t *ostate);
236 
237 static cfga_sata_ret_t
238 do_control_ioctl(const char *ap_id, sata_cfga_apctl_t subcommand, uint_t arg,
239 	void **descrp, size_t *sizep);
240 
241 static void
242 cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl, nvlist_t *user_nvlist);
243 
244 static char *
245 sata_get_devicepath(const char *ap_id);
246 
247 static int
248 sata_confirm(struct cfga_confirm *confp, char *msg);
249 
250 
251 /* Utilities */
252 
253 static cfga_sata_ret_t
254 physpath_to_devlink(const char *basedir, const char *node_path,
255     char **logpp, int *l_errnop)
256 {
257 	char *linkpath;
258 	char *buf;
259 	char *real_path;
260 	DIR *dp;
261 	struct dirent *dep, *newdep;
262 	int deplen;
263 	boolean_t found = B_FALSE;
264 	int err = 0;
265 	struct stat sb;
266 	char *p;
267 	cfga_sata_ret_t rv = CFGA_SATA_INTERNAL_ERROR;
268 
269 	/*
270 	 * Using libdevinfo for this is overkill and kills performance
271 	 * when multiple consumers of libcfgadm are executing
272 	 * concurrently.
273 	 */
274 	if ((dp = opendir(basedir)) == NULL) {
275 		*l_errnop = errno;
276 		return (CFGA_SATA_INTERNAL_ERROR);
277 	}
278 
279 	linkpath = malloc(PATH_MAX);
280 	buf = malloc(PATH_MAX);
281 	real_path = malloc(PATH_MAX);
282 
283 	deplen = pathconf(basedir, _PC_NAME_MAX) +
284 	    sizeof (struct dirent);
285 	dep = (struct dirent *)malloc(deplen);
286 
287 	if (dep == NULL || linkpath == NULL || buf == NULL ||
288 	    real_path == NULL) {
289 		*l_errnop = ENOMEM;
290 		rv = CFGA_SATA_ALLOC_FAIL;
291 		goto pp_cleanup;
292 	}
293 
294 	*logpp = NULL;
295 
296 	while (!found && (err = readdir_r(dp, dep, &newdep)) == 0 &&
297 	    newdep != NULL) {
298 
299 		assert(newdep == dep);
300 
301 		if (strcmp(dep->d_name, ".") == 0 ||
302 		    strcmp(dep->d_name, "..") == 0)
303 			continue;
304 
305 		(void) snprintf(linkpath, MAXPATHLEN,
306 		    "%s/%s", basedir, dep->d_name);
307 
308 		if (lstat(linkpath, &sb) < 0)
309 			continue;
310 
311 		if (S_ISDIR(sb.st_mode)) {
312 
313 			if ((rv = physpath_to_devlink(linkpath, node_path,
314 			    logpp, l_errnop)) != CFGA_SATA_OK) {
315 
316 				goto pp_cleanup;
317 			}
318 
319 			if (*logpp != NULL)
320 				found = B_TRUE;
321 
322 		} else if (S_ISLNK(sb.st_mode)) {
323 
324 			bzero(buf, PATH_MAX);
325 			if (readlink(linkpath, buf, PATH_MAX) < 0)
326 				continue;
327 
328 
329 			/*
330 			 * realpath() is too darn slow, so fake
331 			 * it, by using what we know about /dev
332 			 * links: they are always of the form:
333 			 * <"../">+/devices/<path>
334 			 */
335 			p = buf;
336 			while (strncmp(p, "../", 3) == 0)
337 				p += 3;
338 
339 			if (p != buf)
340 				p--;	/* back up to get a slash */
341 
342 			assert (*p == '/');
343 
344 			if (strcmp(p, node_path) == 0) {
345 				*logpp = strdup(linkpath);
346 				if (*logpp == NULL) {
347 
348 					rv = CFGA_SATA_ALLOC_FAIL;
349 					goto pp_cleanup;
350 				}
351 
352 				found = B_TRUE;
353 			}
354 		}
355 	}
356 
357 	free(linkpath);
358 	free(buf);
359 	free(real_path);
360 	free(dep);
361 	(void) closedir(dp);
362 
363 	if (err != 0) {
364 		*l_errnop = err;
365 		return (CFGA_SATA_INTERNAL_ERROR);
366 	}
367 
368 	return (CFGA_SATA_OK);
369 
370 pp_cleanup:
371 
372 	if (dp)
373 		(void) closedir(dp);
374 	if (dep)
375 		free(dep);
376 	if (linkpath)
377 		free(linkpath);
378 	if (buf)
379 		free(buf);
380 	if (real_path)
381 		free(real_path);
382 	if (*logpp) {
383 		free(*logpp);
384 		*logpp = NULL;
385 	}
386 	return (rv);
387 }
388 
389 
390 /*
391  * Given the index into a table (msgcvt_t) of messages, get the message
392  * string, converting it to the proper locale if necessary.
393  * NOTE: Indexes are defined in cfga_sata.h
394  */
395 static const char *
396 get_msg(uint_t msg_index, msgcvt_t *msg_tbl, uint_t tbl_size)
397 {
398 	if (msg_index >= tbl_size) {
399 		msg_index = CFGA_SATA_UNKNOWN;
400 	}
401 
402 	return ((msg_tbl[msg_index].intl) ?
403 		dgettext(TEXT_DOMAIN, msg_tbl[msg_index].msgstr) :
404 		msg_tbl[msg_index].msgstr);
405 }
406 
407 /*
408  * Allocates and creates a message string (in *ret_str),
409  * by concatenating all the (char *) args together, in order.
410  * Last arg MUST be NULL.
411  */
412 static void
413 set_msg(char **ret_str, ...)
414 {
415 	char    *str;
416 	size_t  total_len;
417 	va_list valist;
418 
419 	va_start(valist, ret_str);
420 
421 	total_len = (*ret_str == NULL) ? 0 : strlen(*ret_str);
422 
423 	while ((str = va_arg(valist, char *)) != NULL) {
424 		size_t  len = strlen(str);
425 		char    *old_str = *ret_str;
426 
427 		*ret_str = (char *)realloc(*ret_str, total_len + len + 1);
428 		if (*ret_str == NULL) {
429 			/* We're screwed */
430 			free(old_str);
431 			va_end(valist);
432 			return;
433 		}
434 
435 		(void) strcpy(*ret_str + total_len, str);
436 		total_len += len;
437 	}
438 
439 	va_end(valist);
440 }
441 
442 /*
443  * Error message handling.
444  * For the rv passed in, looks up the corresponding error message string(s),
445  * internationalized if necessary, and concatenates it into a new
446  * memory buffer, and points *errstring to it.
447  * Note not all rvs will result in an error message return, as not all
448  * error conditions warrant a SATA-specific error message - for those
449  * conditions the cfgadm generic messages are sufficient.
450  *
451  * Some messages may display ap_id or errno, which is why they are passed
452  * in.
453  */
454 
455 cfga_err_t
456 sata_err_msg(
457 	char **errstring,
458 	cfga_sata_ret_t rv,
459 	const char *ap_id,
460 	int l_errno)
461 {
462 	if (errstring == NULL) {
463 		return (sata_msgs[rv].cfga_err);
464 	}
465 
466 	/*
467 	 * Generate the appropriate SATA-specific error message(s) (if any).
468 	 */
469 	switch (rv) {
470 	case CFGA_SATA_OK:
471 	case CFGA_NACK:
472 		/* Special case - do nothing.  */
473 		break;
474 
475 	case CFGA_SATA_UNKNOWN:
476 	case CFGA_SATA_DYNAMIC_AP:
477 	case CFGA_SATA_INTERNAL_ERROR:
478 	case CFGA_SATA_OPTIONS:
479 	case CFGA_SATA_ALLOC_FAIL:
480 	case CFGA_SATA_STATE:
481 	case CFGA_SATA_PRIV:
482 	case CFGA_SATA_OPNOTSUPP:
483 	case CFGA_SATA_DATA_ERROR:
484 		/* These messages require no additional strings passed. */
485 		set_msg(errstring, ERR_STR(rv), NULL);
486 		break;
487 
488 	case CFGA_SATA_HWOPNOTSUPP:
489 		/* hardware-specific help needed */
490 		set_msg(errstring, ERR_STR(rv), NULL);
491 		set_msg(errstring, "\n",
492 			dgettext(TEXT_DOMAIN, sata_help[HELP_HEADER]), NULL);
493 		set_msg(errstring, sata_help[HELP_RESET_PORT], NULL);
494 		set_msg(errstring, sata_help[HELP_RESET_DEVICE], NULL);
495 		set_msg(errstring, sata_help[HELP_RESET_ALL],  NULL);
496 		set_msg(errstring, sata_help[HELP_PORT_ACTIVATE], NULL);
497 		set_msg(errstring, sata_help[HELP_PORT_DEACTIVATE], NULL);
498 		set_msg(errstring, sata_help[HELP_PORT_SELF_TEST], NULL);
499 		set_msg(errstring, sata_help[HELP_CNTRL_SELF_TEST], NULL);
500 		break;
501 
502 	case CFGA_SATA_AP:
503 	case CFGA_SATA_PORT:
504 	case CFGA_SATA_NOT_CONNECTED:
505 	case CFGA_SATA_NOT_CONFIGURED:
506 	case CFGA_SATA_ALREADY_CONNECTED:
507 	case CFGA_SATA_ALREADY_CONFIGURED:
508 	case CFGA_SATA_BUSY:
509 	case CFGA_SATA_DEVLINK:
510 	case CFGA_SATA_RCM_HANDLE:
511 	case CFGA_SATA_RCM_ONLINE:
512 	case CFGA_SATA_RCM_OFFLINE:
513 	case CFGA_SATA_RCM_INFO:
514 	case CFGA_SATA_DEV_CONFIGURE:
515 	case CFGA_SATA_DEV_UNCONFIGURE:
516 	case CFGA_SATA_DISCONNECTED:
517 		/* These messages also print ap_id.  */
518 		set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "", NULL);
519 		break;
520 
521 
522 	case CFGA_SATA_IOCTL:
523 	case CFGA_SATA_NVLIST:
524 		/* These messages also print errno.  */
525 		{
526 			char *errno_str = l_errno ? strerror(l_errno) : "";
527 
528 			set_msg(errstring, ERR_STR(rv), errno_str,
529 			    l_errno ? "\n" : "", NULL);
530 			break;
531 		}
532 
533 	case CFGA_SATA_OPEN:
534 		/* These messages also apid and errno.  */
535 		{
536 			char *errno_str = l_errno ? strerror(l_errno) : "";
537 
538 			set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "\n",
539 			    errno_str, l_errno ? "\n" : "", NULL);
540 			break;
541 		}
542 
543 	default:
544 		set_msg(errstring, ERR_STR(CFGA_SATA_INTERNAL_ERROR), NULL);
545 
546 	} /* end switch */
547 
548 
549 	/*
550 	 * Determine the proper error code to send back to the cfgadm library.
551 	 */
552 	return (sata_msgs[rv].cfga_err);
553 }
554 
555 
556 /*
557  * Entry points
558  */
559 /* cfgadm entry point */
560 /*ARGSUSED*/
561 cfga_err_t
562 cfga_change_state(
563 	cfga_cmd_t state_change_cmd,
564 	const char *ap_id,
565 	const char *options,
566 	struct cfga_confirm *confp,
567 	struct cfga_msg *msgp,
568 	char **errstring,
569 	cfga_flags_t flags)
570 {
571 	int		ret;
572 	int 		len;
573 	char		*msg;
574 	char		*devpath;
575 	nvlist_t	*nvl = NULL;
576 	ap_rstate_t	rstate;
577 	ap_ostate_t	ostate;
578 	devctl_hdl_t	hdl = NULL;
579 	cfga_sata_ret_t	rv = CFGA_SATA_OK;
580 	char		*pdyn;
581 
582 	/*
583 	 * All sub-commands which can change state of device require
584 	 * root privileges.
585 	 */
586 	if (geteuid() != 0) {
587 		rv = CFGA_SATA_PRIV;
588 		goto bailout;
589 	}
590 
591 	if ((rv = verify_params(ap_id, options, errstring)) != CFGA_SATA_OK) {
592 		(void) cfga_help(msgp, options, flags);
593 		goto bailout;
594 	}
595 
596 	if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &nvl,
597 		DC_RDONLY)) != CFGA_SATA_OK) {
598 		goto bailout;
599 	}
600 
601 	switch (state_change_cmd) {
602 	case CFGA_CMD_CONFIGURE:
603 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
604 		    CFGA_SATA_OK)
605 			goto bailout;
606 
607 		if (ostate == AP_OSTATE_CONFIGURED) {
608 			rv = CFGA_SATA_ALREADY_CONFIGURED;
609 			goto bailout;
610 		}
611 		/* Disallow dynamic AP name component */
612 		if (GET_DYN(ap_id) != NULL) {
613 			rv = CFGA_SATA_INVALID_DEVNAME;
614 			goto bailout;
615 		}
616 
617 		if (rstate == AP_RSTATE_EMPTY) {
618 			rv = CFGA_SATA_NOT_CONNECTED;
619 			goto bailout;
620 		}
621 		rv = CFGA_SATA_OK;
622 
623 		if (devctl_ap_configure(hdl, nvl) != 0) {
624 			rv = CFGA_SATA_DEV_CONFIGURE;
625 			goto bailout;
626 		}
627 
628 		devpath = sata_get_devicepath(ap_id);
629 		if (devpath == NULL) {
630 			int i;
631 			/*
632 			 * Try for some time as SATA hotplug thread
633 			 * takes a while to create the path then
634 			 * eventually give up.
635 			 */
636 			for (i = 0; i < 12 && (devpath == NULL); i++) {
637 				(void) sleep(6);
638 				devpath = sata_get_devicepath(ap_id);
639 			}
640 
641 			if (devpath == NULL) {
642 				rv = CFGA_SATA_DEV_CONFIGURE;
643 				break;
644 			}
645 		}
646 
647 		S_FREE(devpath);
648 		break;
649 
650 	case CFGA_CMD_UNCONFIGURE:
651 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
652 		    CFGA_SATA_OK)
653 			goto bailout;
654 
655 		if (rstate != AP_RSTATE_CONNECTED) {
656 			rv = CFGA_SATA_NOT_CONNECTED;
657 			goto bailout;
658 		}
659 
660 		if (ostate != AP_OSTATE_CONFIGURED) {
661 			rv = CFGA_SATA_NOT_CONFIGURED;
662 			goto bailout;
663 		}
664 		/* Strip off AP name dynamic component, if present */
665 		if ((pdyn = GET_DYN(ap_id)) != NULL) {
666 			*pdyn = '\0';
667 		}
668 
669 		rv = CFGA_SATA_OK;
670 
671 		len = strlen(SATA_CONFIRM_DEVICE) +
672 			strlen(SATA_CONFIRM_DEVICE_SUSPEND) +
673 			strlen("Unconfigure") + strlen(ap_id);
674 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
675 			(void) snprintf(msg, len + 3, "Unconfigure"
676 				" %s%s\n%s",
677 				SATA_CONFIRM_DEVICE, ap_id,
678 				SATA_CONFIRM_DEVICE_SUSPEND);
679 		}
680 
681 		if (!sata_confirm(confp, msg)) {
682 			free(msg);
683 			rv = CFGA_SATA_NACK;
684 			break;
685 		}
686 		free(msg);
687 
688 		devpath = sata_get_devicepath(ap_id);
689 		if (devpath == NULL) {
690 			(void) printf(
691 				"cfga_change_state: get device path failed\n");
692 			rv = CFGA_SATA_DEV_UNCONFIGURE;
693 			break;
694 		}
695 
696 		if ((rv = sata_rcm_offline(ap_id, errstring, devpath, flags))
697 		    != CFGA_SATA_OK) {
698 			break;
699 		}
700 
701 		ret = devctl_ap_unconfigure(hdl, nvl);
702 
703 		if (ret != 0) {
704 			rv = CFGA_SATA_DEV_UNCONFIGURE;
705 			if (errno == EBUSY) {
706 				rv = CFGA_SATA_BUSY;
707 			}
708 			(void) sata_rcm_online(ap_id, errstring, devpath,
709 				flags);
710 		} else {
711 			(void) sata_rcm_remove(ap_id, errstring, devpath,
712 				flags);
713 
714 		}
715 		S_FREE(devpath);
716 
717 		break;
718 
719 	case CFGA_CMD_DISCONNECT:
720 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
721 		    CFGA_SATA_OK)
722 			goto bailout;
723 
724 		if (rstate == AP_RSTATE_DISCONNECTED) {
725 			rv = CFGA_SATA_DISCONNECTED;
726 			goto bailout;
727 		}
728 
729 		/* Strip off AP name dynamic component, if present */
730 		if ((pdyn = GET_DYN(ap_id)) != NULL) {
731 			*pdyn = '\0';
732 		}
733 
734 
735 		rv = CFGA_SATA_OK; /* other statuses don't matter */
736 
737 
738 		/*
739 		 * If the port originally with device attached and was
740 		 * unconfigured already, the devicepath for the sd will be
741 		 * removed. sata_get_devicepath in this case is not necessary.
742 		 */
743 
744 		/* only call rcm_offline if the state was CONFIGURED */
745 		if (ostate == AP_OSTATE_CONFIGURED) {
746 			devpath = sata_get_devicepath(ap_id);
747 			if (devpath == NULL) {
748 				(void) printf(
749 				    "cfga_change_state: get path failed\n");
750 				rv = CFGA_SATA_DEV_UNCONFIGURE;
751 				break;
752 			}
753 
754 			len = strlen(SATA_CONFIRM_DEVICE) +
755 				strlen(SATA_CONFIRM_DEVICE_SUSPEND) +
756 				strlen("Disconnect") + strlen(ap_id);
757 			if ((msg = (char *)calloc(len +3, 1)) != NULL) {
758 				(void) snprintf(msg, len + 3,
759 					"Disconnect"
760 					" %s%s\n%s",
761 					SATA_CONFIRM_DEVICE, ap_id,
762 					SATA_CONFIRM_DEVICE_SUSPEND);
763 			}
764 			if (!sata_confirm(confp, msg)) {
765 				free(msg);
766 				rv = CFGA_SATA_NACK;
767 				break;
768 			}
769 			free(msg);
770 
771 			if ((rv = sata_rcm_offline(ap_id, errstring,
772 			    devpath, flags)) != CFGA_SATA_OK) {
773 				break;
774 			}
775 
776 			ret = devctl_ap_unconfigure(hdl, nvl);
777 			if (ret != 0) {
778 				(void) printf(
779 				    "devctl_ap_unconfigure failed\n");
780 				rv = CFGA_SATA_DEV_UNCONFIGURE;
781 				if (errno == EBUSY)
782 					rv = CFGA_SATA_BUSY;
783 				(void) sata_rcm_online(ap_id, errstring,
784 					devpath, flags);
785 				S_FREE(devpath);
786 
787 				/*
788 				 * The current policy is that if unconfigure
789 				 * failed, do not continue with disconnect.
790 				 * If the port needs to be forced into the
791 				 * disconnect (shutdown) state,
792 				 * the -x sata_port_poweroff command should be
793 				 * used instead of -c disconnect
794 				 */
795 				break;
796 			} else {
797 				(void) printf("%s\n",
798 				    ERR_STR(CFGA_SATA_DEVICE_UNCONFIGURED));
799 				(void) sata_rcm_remove(ap_id, errstring,
800 					devpath, flags);
801 			}
802 			S_FREE(devpath);
803 		} else if (rstate == AP_RSTATE_CONNECTED ||
804 		    rstate == AP_RSTATE_EMPTY) {
805 			len = strlen(SATA_CONFIRM_PORT) +
806 				strlen(SATA_CONFIRM_PORT_DISABLE) +
807 				strlen("Deactivate Port") + strlen(ap_id);
808 			if ((msg = (char *)calloc(len +3, 1)) != NULL) {
809 				(void) snprintf(msg, len +3,
810 					"Disconnect"
811 					" %s%s\n%s",
812 					SATA_CONFIRM_PORT, ap_id,
813 					SATA_CONFIRM_PORT_DISABLE);
814 			}
815 			if (!sata_confirm(confp, msg)) {
816 				free(msg);
817 				rv = CFGA_SATA_NACK;
818 				break;
819 			}
820 		}
821 		ret = devctl_ap_disconnect(hdl, nvl);
822 		if (ret != 0) {
823 			rv = CFGA_SATA_IOCTL;
824 			if (errno == EBUSY) {
825 				rv = CFGA_SATA_BUSY;
826 			}
827 		}
828 		break;
829 
830 	case CFGA_CMD_CONNECT:
831 		if ((rv = port_state(hdl, nvl, &rstate, &ostate)) !=
832 		    CFGA_SATA_OK)
833 			goto bailout;
834 
835 		if (rstate == AP_RSTATE_CONNECTED) {
836 			rv = CFGA_SATA_ALREADY_CONNECTED;
837 			goto bailout;
838 		}
839 
840 		len = strlen(SATA_CONFIRM_PORT) +
841 			strlen(SATA_CONFIRM_PORT_ENABLE) +
842 			strlen("Activate Port") + strlen(ap_id);
843 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
844 			(void) snprintf(msg, len +3, "Activate"
845 				" %s%s\n%s",
846 				SATA_CONFIRM_PORT, ap_id,
847 				SATA_CONFIRM_PORT_ENABLE);
848 		}
849 		if (!sata_confirm(confp, msg)) {
850 			rv = CFGA_SATA_NACK;
851 			break;
852 		}
853 
854 		/* Disallow dynamic AP name component */
855 		if (GET_DYN(ap_id) != NULL) {
856 			rv = CFGA_SATA_INVALID_DEVNAME;
857 			goto bailout;
858 		}
859 
860 		ret = devctl_ap_connect(hdl, nvl);
861 		if (ret != 0) {
862 			rv = CFGA_SATA_IOCTL;
863 		} else {
864 			rv = CFGA_SATA_OK;
865 		}
866 
867 		break;
868 
869 	case CFGA_CMD_LOAD:
870 	case CFGA_CMD_UNLOAD:
871 		(void) cfga_help(msgp, options, flags);
872 		rv = CFGA_SATA_OPNOTSUPP;
873 		break;
874 
875 	case CFGA_CMD_NONE:
876 	default:
877 		(void) cfga_help(msgp, options, flags);
878 		rv = CFGA_SATA_INTERNAL_ERROR;
879 	}
880 
881 bailout:
882 	cleanup_after_devctl_cmd(hdl, nvl);
883 
884 	return (sata_err_msg(errstring, rv, ap_id, errno));
885 }
886 
887 /* cfgadm entry point */
888 cfga_err_t
889 cfga_private_func(
890 	const char *func,
891 	const char *ap_id,
892 	const char *options,
893 	struct cfga_confirm *confp,
894 	struct cfga_msg *msgp,
895 	char **errstring,
896 	cfga_flags_t flags)
897 {
898 	int			len;
899 	char 			*msg;
900 	nvlist_t		*list = NULL;
901 	ap_ostate_t		ostate;
902 	ap_rstate_t		rstate;
903 	devctl_hdl_t		hdl = NULL;
904 	cfga_sata_ret_t		rv;
905 	char			*str_p;
906 	size_t			size;
907 
908 	if ((rv = verify_params(ap_id, NULL, errstring)) != CFGA_SATA_OK) {
909 		(void) cfga_help(msgp, options, flags);
910 		return (sata_err_msg(errstring, rv, ap_id, errno));
911 	}
912 
913 	/*
914 	 * All subcommands which can change state of device require
915 	 * root privileges.
916 	 */
917 	if (geteuid() != 0) {
918 		rv = CFGA_SATA_PRIV;
919 		goto bailout;
920 	}
921 
922 	if (func == NULL) {
923 		(void) printf("No valid option specified\n");
924 		rv = CFGA_SATA_OPTIONS;
925 		goto bailout;
926 	}
927 
928 	if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &list, 0)) !=
929 	    CFGA_SATA_OK) {
930 		goto bailout;
931 	}
932 
933 	/* We do not care here about dynamic AP name component */
934 	if ((str_p = GET_DYN(ap_id)) != NULL) {
935 		*str_p = '\0';
936 	}
937 
938 	rv = CFGA_SATA_OK;
939 
940 	if (strcmp(func, SATA_RESET_PORT) == 0) {
941 		len = strlen(SATA_CONFIRM_PORT) +
942 		    strlen(SATA_CONFIRM_DEVICE_ABORT) +
943 		    strlen("Reset Port") + strlen(ap_id);
944 
945 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
946 			(void) snprintf(msg, len +3, "Reset"
947 				" %s%s\n%s",
948 				SATA_CONFIRM_PORT, ap_id,
949 				SATA_CONFIRM_DEVICE_ABORT);
950 		} else {
951 			rv = CFGA_SATA_NACK;
952 			goto bailout;
953 		}
954 
955 		if (!sata_confirm(confp, msg)) {
956 			rv = CFGA_SATA_NACK;
957 			goto bailout;
958 		}
959 
960 		rv = do_control_ioctl(ap_id, SATA_CFGA_RESET_PORT, NULL,
961 			(void **)&str_p, &size);
962 
963 	} else if (strcmp(func, SATA_RESET_DEVICE) == 0) {
964 		if ((rv = port_state(hdl, list, &rstate, &ostate)) !=
965 		    CFGA_SATA_OK)
966 			goto bailout;
967 		/*
968 		 * Reset device function requires device to be connected
969 		 */
970 		if (rstate != AP_RSTATE_CONNECTED) {
971 			rv = CFGA_SATA_NOT_CONNECTED;
972 			goto bailout;
973 		}
974 
975 		len = strlen(SATA_CONFIRM_DEVICE) +
976 		    strlen(SATA_CONFIRM_DEVICE_ABORT) +
977 		    strlen("Reset Device") + strlen(ap_id);
978 
979 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
980 			(void) snprintf(msg, len +3, "Reset"
981 				" %s%s\n%s",
982 				SATA_CONFIRM_DEVICE, ap_id,
983 				SATA_CONFIRM_DEVICE_ABORT);
984 		} else {
985 			rv = CFGA_SATA_NACK;
986 			goto bailout;
987 		}
988 
989 		if (!sata_confirm(confp, msg)) {
990 			rv = CFGA_SATA_NACK;
991 			goto bailout;
992 		}
993 
994 		rv = do_control_ioctl(ap_id, SATA_CFGA_RESET_DEVICE, NULL,
995 		    (void **)&str_p, &size);
996 
997 	} else if (strcmp(func, SATA_RESET_ALL) == 0) {
998 		len = strlen(SATA_CONFIRM_CONTROLLER) +
999 		    strlen(SATA_CONFIRM_CONTROLLER_ABORT) +
1000 		    strlen("Reset All") + strlen(ap_id);
1001 
1002 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
1003 			(void) snprintf(msg, len +3, "Reset"
1004 				" %s%s\n%s",
1005 				SATA_CONFIRM_CONTROLLER, ap_id,
1006 				SATA_CONFIRM_CONTROLLER_ABORT);
1007 		} else {
1008 			rv = CFGA_SATA_NACK;
1009 			goto bailout;
1010 		}
1011 
1012 		if (!sata_confirm(confp, msg)) {
1013 			rv = CFGA_SATA_NACK;
1014 			goto bailout;
1015 		}
1016 		rv = do_control_ioctl(ap_id, SATA_CFGA_RESET_ALL, NULL,
1017 			(void **)&str_p, &size);
1018 
1019 	} else if (strcmp(func, SATA_PORT_DEACTIVATE) == 0) {
1020 		len = strlen(SATA_CONFIRM_PORT) +
1021 		    strlen(SATA_CONFIRM_PORT_DISABLE) +
1022 		    strlen("Deactivate Port") + strlen(ap_id);
1023 
1024 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
1025 			(void) snprintf(msg, len +3, "Deactivate"
1026 				" %s%s\n%s",
1027 				SATA_CONFIRM_PORT, ap_id,
1028 				SATA_CONFIRM_PORT_DISABLE);
1029 		} else {
1030 			rv = CFGA_SATA_NACK;
1031 			goto bailout;
1032 		}
1033 		if (!sata_confirm(confp, msg)) {
1034 			rv = CFGA_SATA_NACK;
1035 			goto bailout;
1036 		}
1037 
1038 		rv = do_control_ioctl(ap_id, SATA_CFGA_PORT_DEACTIVATE, NULL,
1039 			(void **)&str_p, &size);
1040 
1041 	} else if (strcmp(func, SATA_PORT_ACTIVATE) == 0) {
1042 		len = strlen(SATA_CONFIRM_PORT) +
1043 		    strlen(SATA_CONFIRM_PORT_ENABLE) +
1044 		    strlen("Activate Port") + strlen(ap_id);
1045 
1046 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
1047 			(void) snprintf(msg, len +3, "Activate"
1048 				" %s%s\n%s",
1049 				SATA_CONFIRM_PORT, ap_id,
1050 				SATA_CONFIRM_PORT_ENABLE);
1051 		} else {
1052 			rv = CFGA_SATA_NACK;
1053 			goto bailout;
1054 		}
1055 		if (!sata_confirm(confp, msg)) {
1056 			rv = CFGA_SATA_NACK;
1057 			goto bailout;
1058 		}
1059 
1060 		rv = do_control_ioctl(ap_id, SATA_CFGA_PORT_ACTIVATE,
1061 		    NULL, (void **)&str_p, &size);
1062 			goto bailout;
1063 
1064 	} else if (strcmp(func, SATA_PORT_SELF_TEST) == 0) {
1065 		len = strlen(SATA_CONFIRM_PORT) +
1066 		    strlen(SATA_CONFIRM_DEVICE_SUSPEND) +
1067 		    strlen("Self Test Port") + strlen(ap_id);
1068 
1069 		if ((msg = (char *)calloc(len +3, 1)) != NULL) {
1070 			(void) snprintf(msg, len +3, "Self Test"
1071 				" %s%s\n%s",
1072 				SATA_CONFIRM_PORT, ap_id,
1073 				SATA_CONFIRM_DEVICE_SUSPEND);
1074 		} else {
1075 			rv = CFGA_SATA_NACK;
1076 			goto bailout;
1077 		}
1078 		if (!sata_confirm(confp, msg)) {
1079 			rv = CFGA_SATA_NACK;
1080 			goto bailout;
1081 		}
1082 
1083 		rv = do_control_ioctl(ap_id, SATA_CFGA_PORT_SELF_TEST,
1084 			NULL, (void **)&str_p, &size);
1085 	} else {
1086 		/* Unrecognized operation request */
1087 		rv = CFGA_SATA_HWOPNOTSUPP;
1088 	}
1089 
1090 bailout:
1091 	cleanup_after_devctl_cmd(hdl, list);
1092 
1093 	return (sata_err_msg(errstring, rv, ap_id, errno));
1094 
1095 }
1096 
1097 /* cfgadm entry point */
1098 /*ARGSUSED*/
1099 cfga_err_t
1100 cfga_test(
1101 	const char *ap_id,
1102 	const char *options,
1103 	struct cfga_msg *msgp,
1104 	char **errstring,
1105 	cfga_flags_t flags)
1106 {
1107 	/* Should call ioctl for self test - phase 2 */
1108 	return (CFGA_OPNOTSUPP);
1109 }
1110 
1111 
1112 int
1113 sata_check_target_node(di_node_t node, void *arg)
1114 {
1115 	char *minorpath;
1116 	char *cp;
1117 
1118 	minorpath = di_devfs_minor_path(di_minor_next(node, DI_MINOR_NIL));
1119 	if (minorpath != NULL) {
1120 		if (strstr(minorpath, arg) != NULL) {
1121 			cp = strrchr(minorpath, (int)*MINOR_SEP);
1122 			if (cp != NULL) {
1123 				(void) strcpy(arg, cp);
1124 			}
1125 			free(minorpath);
1126 			return (DI_WALK_TERMINATE);
1127 		}
1128 		free(minorpath);
1129 	}
1130 	return (DI_WALK_CONTINUE);
1131 }
1132 
1133 
1134 /*
1135  * The dynamic component buffer returned by this function has to be freed!
1136  */
1137 int
1138 sata_make_dyncomp(const char *ap_id, char **dyncomp)
1139 {
1140 	char	*devpath = NULL;
1141 	char	*cp = NULL;
1142 	int	l_errno;
1143 	char	minor_path[MAXPATHLEN];
1144 	char	name_part[MAXNAMELEN];
1145 	char	*devlink = NULL;
1146 	char	*minor_portion = NULL;
1147 	int	deplen;
1148 	int	err;
1149 	DIR	*dp = NULL;
1150 	struct stat sb;
1151 	struct dirent *dep = NULL;
1152 	struct dirent *newdep = NULL;
1153 	char	*p;
1154 
1155 	assert(dyncomp != NULL);
1156 
1157 	/*
1158 	 * Get target node path
1159 	 */
1160 	devpath = sata_get_devicepath(ap_id);
1161 	if (devpath == NULL) {
1162 
1163 		(void) printf("cfga_list_ext: cannot locate target device\n");
1164 		return (CFGA_SATA_DYNAMIC_AP);
1165 
1166 	} else {
1167 
1168 		cp = strrchr(devpath, *PATH_SEP);
1169 		assert(cp != NULL);
1170 		*cp = 0;	/* terminate path for opendir() */
1171 
1172 		(void) strncpy(name_part, cp + 1, MAXNAMELEN);
1173 
1174 		/*
1175 		 * Using libdevinfo for this is overkill and kills
1176 		 * performance when many consumers are using libcfgadm
1177 		 * concurrently.
1178 		 */
1179 		if ((dp = opendir(devpath)) == NULL) {
1180 			goto bailout;
1181 		}
1182 
1183 		/*
1184 		 * deplen is large enough to fit the largest path-
1185 		 * struct dirent includes one byte (the terminator)
1186 		 * so we don't add 1 to the calculation here.
1187 		 */
1188 		deplen = pathconf(devpath, _PC_NAME_MAX) +
1189 		    sizeof (struct dirent);
1190 		dep = (struct dirent *)malloc(deplen);
1191 		if (dep == NULL)
1192 			goto bailout;
1193 
1194 		while ((err = readdir_r(dp, dep, &newdep)) == 0 &&
1195 		    newdep != NULL) {
1196 
1197 			assert(newdep == dep);
1198 
1199 			if (strcmp(dep->d_name, ".") == 0 ||
1200 			    strcmp(dep->d_name, "..") == 0 ||
1201 			    (minor_portion = strchr(dep->d_name,
1202 			    *MINOR_SEP)) == NULL)
1203 				continue;
1204 
1205 			*minor_portion = 0;
1206 			if (strcmp(dep->d_name, name_part) != 0)
1207 				continue;
1208 			*minor_portion = *MINOR_SEP;
1209 
1210 			(void) snprintf(minor_path, MAXPATHLEN,
1211 			    "%s/%s", devpath, dep->d_name);
1212 
1213 			if (stat(minor_path, &sb) < 0)
1214 				continue;
1215 
1216 			if (S_ISBLK(sb.st_mode))
1217 				break;
1218 		}
1219 
1220 		(void) closedir(dp);
1221 		free(dep);
1222 		free(devpath);
1223 
1224 		dp = NULL;
1225 		dep = NULL;
1226 		devpath = NULL;
1227 
1228 		/*
1229 		 * If there was an error, or we didn't exit the loop
1230 		 * by finding a block or character device, bail out.
1231 		 */
1232 		if (err != 0 || newdep == NULL)
1233 			goto bailout;
1234 
1235 		/*
1236 		 * Look for links to the physical path in /dev/dsk,
1237 		 * since we ONLY looked for BLOCK devices above.
1238 		 */
1239 		(void) physpath_to_devlink("/dev/dsk",
1240 		    minor_path, &devlink, &l_errno);
1241 
1242 		/* postprocess and copy logical name here */
1243 		if (devlink != NULL) {
1244 			/*
1245 			 * For disks, remove partition/slice info
1246 			 */
1247 			if ((cp = strstr(devlink, "dsk/")) != NULL) {
1248 				/* cXtYdZ[(s[0..15])|(p[0..X])] */
1249 				if ((p = strchr(cp + 4, 'd')) != NULL) {
1250 					p++;	/* Skip the 'd' */
1251 					while (*p != 0 && isdigit(*p))
1252 						p++;
1253 					*p = 0;
1254 				}
1255 				*dyncomp = strdup(cp);
1256 			}
1257 
1258 			free(devlink);
1259 		}
1260 
1261 		return (SATA_CFGA_OK);
1262 	}
1263 
1264 bailout:
1265 	if (dp)
1266 		(void) closedir(dp);
1267 	if (devpath)
1268 		free(devpath);
1269 	if (dep)
1270 		free(dep);
1271 	return (CFGA_SATA_DYNAMIC_AP);
1272 }
1273 
1274 /* cfgadm entry point */
1275 /*ARGSUSED*/
1276 cfga_err_t
1277 cfga_list_ext(
1278 	const char *ap_id,
1279 	cfga_list_data_t **ap_id_list,
1280 	int *nlistp,
1281 	const char *options,
1282 	const char *listopts,
1283 	char **errstring,
1284 	cfga_flags_t flags)
1285 {
1286 	int			l_errno;
1287 	char			*ap_id_log = NULL;
1288 	size_t			size;
1289 	nvlist_t		*user_nvlist = NULL;
1290 	devctl_hdl_t		devctl_hdl = NULL;
1291 	cfga_sata_ret_t		rv = CFGA_SATA_OK;
1292 	devctl_ap_state_t	devctl_ap_state;
1293 	char			*pdyn;
1294 
1295 
1296 	if ((rv = verify_params(ap_id, options, errstring)) != CFGA_SATA_OK) {
1297 		(void) cfga_help(NULL, options, flags);
1298 		goto bailout;
1299 	}
1300 	/* We do not care here about dynamic AP name component */
1301 	if ((pdyn = GET_DYN(ap_id)) != NULL) {
1302 		*pdyn = '\0';
1303 	}
1304 
1305 	if (ap_id_list == NULL || nlistp == NULL) {
1306 		rv = CFGA_SATA_DATA_ERROR;
1307 		(void) cfga_help(NULL, options, flags);
1308 		goto bailout;
1309 	}
1310 
1311 	/* Get ap status */
1312 	if ((rv = setup_for_devctl_cmd(ap_id, &devctl_hdl, &user_nvlist,
1313 	    DC_RDONLY)) != CFGA_SATA_OK) {
1314 		goto bailout;
1315 	}
1316 
1317 	/* will call dc_cmd to send IOCTL to kernel */
1318 	if (devctl_ap_getstate(devctl_hdl, user_nvlist,
1319 	    &devctl_ap_state) == -1) {
1320 		cleanup_after_devctl_cmd(devctl_hdl, user_nvlist);
1321 		rv = CFGA_SATA_IOCTL;
1322 		goto bailout;
1323 	}
1324 
1325 	cleanup_after_devctl_cmd(devctl_hdl, user_nvlist);
1326 
1327 	/*
1328 	 * Create cfga_list_data_t struct.
1329 	 */
1330 	if ((*ap_id_list =
1331 	    (cfga_list_data_t *)malloc(sizeof (**ap_id_list))) == NULL) {
1332 		rv = CFGA_SATA_ALLOC_FAIL;
1333 		goto bailout;
1334 	}
1335 	*nlistp = 1;
1336 
1337 	/*
1338 	 * Rest of the code fills in the cfga_list_data_t struct.
1339 	 */
1340 
1341 	/* Get /dev/cfg path to corresponding to the physical ap_id */
1342 	/* Remember ap_id_log must be freed */
1343 	rv = physpath_to_devlink(CFGA_DEV_DIR, (char *)ap_id,
1344 	    &ap_id_log, &l_errno);
1345 
1346 	if (rv != 0) {
1347 		rv = CFGA_SATA_DEVLINK;
1348 		goto bailout;
1349 	}
1350 
1351 	/* Get logical ap_id corresponding to the physical */
1352 	if (ap_id_log == NULL || strstr(ap_id_log, CFGA_DEV_DIR) == NULL) {
1353 		rv = CFGA_SATA_DEVLINK;
1354 		goto bailout;
1355 	}
1356 
1357 	(void) strlcpy((*ap_id_list)->ap_log_id,
1358 	    /* Strip off /dev/cfg/ */ ap_id_log + strlen(CFGA_DEV_DIR)+ 1,
1359 	    sizeof ((*ap_id_list)->ap_log_id));
1360 
1361 	free(ap_id_log);
1362 	ap_id_log = NULL;
1363 
1364 	(void) strlcpy((*ap_id_list)->ap_phys_id, ap_id,
1365 	    sizeof ((*ap_id_list)->ap_phys_id));
1366 
1367 	switch (devctl_ap_state.ap_rstate) {
1368 		case AP_RSTATE_EMPTY:
1369 			(*ap_id_list)->ap_r_state = CFGA_STAT_EMPTY;
1370 			break;
1371 
1372 		case AP_RSTATE_DISCONNECTED:
1373 			(*ap_id_list)->ap_r_state = CFGA_STAT_DISCONNECTED;
1374 			break;
1375 
1376 		case AP_RSTATE_CONNECTED:
1377 			(*ap_id_list)->ap_r_state = CFGA_STAT_CONNECTED;
1378 			break;
1379 
1380 		default:
1381 			rv = CFGA_SATA_STATE;
1382 			goto bailout;
1383 	}
1384 
1385 	switch (devctl_ap_state.ap_ostate) {
1386 		case AP_OSTATE_CONFIGURED:
1387 			(*ap_id_list)->ap_o_state = CFGA_STAT_CONFIGURED;
1388 			break;
1389 
1390 		case AP_OSTATE_UNCONFIGURED:
1391 			(*ap_id_list)->ap_o_state = CFGA_STAT_UNCONFIGURED;
1392 			break;
1393 
1394 		default:
1395 			rv = CFGA_SATA_STATE;
1396 			goto bailout;
1397 	}
1398 
1399 	switch (devctl_ap_state.ap_condition) {
1400 		case AP_COND_OK:
1401 			(*ap_id_list)->ap_cond = CFGA_COND_OK;
1402 			break;
1403 
1404 		case AP_COND_FAILING:
1405 			(*ap_id_list)->ap_cond = CFGA_COND_FAILING;
1406 			break;
1407 
1408 		case AP_COND_FAILED:
1409 			(*ap_id_list)->ap_cond = CFGA_COND_FAILED;
1410 			break;
1411 
1412 		case AP_COND_UNUSABLE:
1413 			(*ap_id_list)->ap_cond = CFGA_COND_UNUSABLE;
1414 			break;
1415 
1416 		case AP_COND_UNKNOWN:
1417 			(*ap_id_list)->ap_cond = CFGA_COND_UNKNOWN;
1418 			break;
1419 
1420 		default:
1421 			rv = CFGA_SATA_STATE;
1422 			goto bailout;
1423 	}
1424 
1425 	(*ap_id_list)->ap_class[0] = '\0';	/* Filled by libcfgadm */
1426 	(*ap_id_list)->ap_busy = devctl_ap_state.ap_in_transition;
1427 	(*ap_id_list)->ap_status_time = devctl_ap_state.ap_last_change;
1428 	(*ap_id_list)->ap_info[0] = NULL;
1429 
1430 	if ((*ap_id_list)->ap_r_state == CFGA_STAT_CONNECTED) {
1431 		char *str_p;
1432 		int skip, i;
1433 
1434 		/*
1435 		 * Fill in the 'Information' field for the -v option
1436 		 * Model (MOD:)
1437 		 */
1438 		if ((rv = do_control_ioctl(ap_id, SATA_CFGA_GET_MODEL_INFO,
1439 		    NULL, (void **)&str_p, &size)) != CFGA_SATA_OK) {
1440 			(void) printf(
1441 				"SATA_CFGA_GET_MODULE_INFO ioctl failed\n");
1442 			goto bailout;
1443 		}
1444 		/* drop leading and trailing spaces */
1445 		skip = strspn(str_p, " ");
1446 		for (i = size - 1; i >= 0; i--) {
1447 			if (str_p[i] == '\040')
1448 				str_p[i] = '\0';
1449 			else if (str_p[i] != '\0')
1450 				break;
1451 		}
1452 
1453 		(void) strlcpy((*ap_id_list)->ap_info, "Mod: ",
1454 			sizeof ((*ap_id_list)->ap_info));
1455 		(void) strlcat((*ap_id_list)->ap_info, str_p + skip,
1456 			sizeof ((*ap_id_list)->ap_info));
1457 
1458 		free(str_p);
1459 
1460 		/*
1461 		 * Fill in the 'Information' field for the -v option
1462 		 * Firmware revision (FREV:)
1463 		 */
1464 		if ((rv = do_control_ioctl(ap_id,
1465 		    SATA_CFGA_GET_REVFIRMWARE_INFO,
1466 		    NULL, (void **)&str_p, &size)) != CFGA_SATA_OK) {
1467 			(void) printf(
1468 			    "SATA_CFGA_GET_REVFIRMWARE_INFO ioctl failed\n");
1469 			goto bailout;
1470 		}
1471 		/* drop leading and trailing spaces */
1472 		skip = strspn(str_p, " ");
1473 		for (i = size - 1; i >= 0; i--) {
1474 			if (str_p[i] == '\040')
1475 				str_p[i] = '\0';
1476 			else if (str_p[i] != '\0')
1477 				break;
1478 		}
1479 		(void) strlcat((*ap_id_list)->ap_info, " FRev: ",
1480 			sizeof ((*ap_id_list)->ap_info));
1481 		(void) strlcat((*ap_id_list)->ap_info, str_p + skip,
1482 			sizeof ((*ap_id_list)->ap_info));
1483 
1484 		free(str_p);
1485 
1486 
1487 		/*
1488 		 * Fill in the 'Information' field for the -v option
1489 		 * Serial Number (SN:)
1490 		 */
1491 		if ((rv = do_control_ioctl(ap_id,
1492 		    SATA_CFGA_GET_SERIALNUMBER_INFO,
1493 		    NULL, (void **)&str_p, &size)) != CFGA_SATA_OK) {
1494 			(void) printf(
1495 			    "SATA_CFGA_GET_SERIALNUMBER_INFO ioctl failed\n");
1496 			goto bailout;
1497 		}
1498 		/* drop leading and trailing spaces */
1499 		skip = strspn(str_p, " ");
1500 		for (i = size - 1; i >= 0; i--) {
1501 			if (str_p[i] == '\040')
1502 				str_p[i] = '\0';
1503 			else if (str_p[i] != '\0')
1504 				break;
1505 		}
1506 		(void) strlcat((*ap_id_list)->ap_info, " SN: ",
1507 				sizeof ((*ap_id_list)->ap_info));
1508 		(void) strlcat((*ap_id_list)->ap_info, str_p + skip,
1509 				sizeof ((*ap_id_list)->ap_info));
1510 
1511 		free(str_p);
1512 
1513 
1514 
1515 		/* Fill in ap_type which is collected from HBA driver */
1516 		/* call do_control_ioctl TBD */
1517 		if ((rv = do_control_ioctl(ap_id, SATA_CFGA_GET_AP_TYPE, NULL,
1518 		    (void **)&str_p, &size)) != CFGA_SATA_OK) {
1519 			(void) printf(
1520 				"SATA_CFGA_GET_AP_TYPE ioctl failed\n");
1521 			goto bailout;
1522 		}
1523 
1524 		(void) strlcpy((*ap_id_list)->ap_type, str_p,
1525 			sizeof ((*ap_id_list)->ap_type));
1526 
1527 		free(str_p);
1528 
1529 		if ((*ap_id_list)->ap_o_state == CFGA_STAT_CONFIGURED) {
1530 
1531 			char *dyncomp = NULL;
1532 
1533 			/*
1534 			 * This is the case where we need to generate
1535 			 * a dynamic component of the ap_id, i.e. device.
1536 			 */
1537 			rv = sata_make_dyncomp(ap_id, &dyncomp);
1538 			if (rv != CFGA_SATA_OK)
1539 				goto bailout;
1540 			if (dyncomp != NULL) {
1541 				(void) strcat((*ap_id_list)->ap_log_id,
1542 					DYN_SEP);
1543 				(void) strlcat((*ap_id_list)->ap_log_id,
1544 					dyncomp,
1545 					sizeof ((*ap_id_list)->ap_log_id));
1546 				free(dyncomp);
1547 			}
1548 		}
1549 
1550 	} else {
1551 		/* Change it when port multiplier is supported */
1552 		(void) strlcpy((*ap_id_list)->ap_type, "sata-port",
1553 			sizeof ((*ap_id_list)->ap_type));
1554 	}
1555 
1556 	return (sata_err_msg(errstring, rv, ap_id, errno));
1557 
1558 bailout:
1559 	if (*ap_id_list != NULL) {
1560 		free(*ap_id_list);
1561 	}
1562 	if (ap_id_log != NULL) {
1563 		free(ap_id_log);
1564 	}
1565 
1566 	return (sata_err_msg(errstring, rv, ap_id, errno));
1567 }
1568 /*
1569  * This routine accepts a string adn prints it using
1570  * the message print routine argument.
1571  */
1572 static void
1573 cfga_msg(struct cfga_msg *msgp, const char *str)
1574 {
1575 	int len;
1576 	char *q;
1577 
1578 	if (msgp == NULL || msgp->message_routine == NULL) {
1579 		(void) printf("cfga_msg: NULL msgp\n");
1580 		return;
1581 	}
1582 
1583 	if ((len = strlen(str)) == 0) {
1584 		(void) printf("cfga_msg: null str\n");
1585 		return;
1586 	}
1587 
1588 	if ((q = (char *)calloc(len + 1, 1)) == NULL) {
1589 		perror("cfga_msg"
1590 );
1591 		return;
1592 	}
1593 
1594 	(void) strcpy(q, str);
1595 	(*msgp->message_routine)(msgp->appdata_ptr, q);
1596 
1597 	free(q);
1598 }
1599 
1600 /* cfgadm entry point */
1601 /* ARGSUSED */
1602 cfga_err_t
1603 cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
1604 {
1605 	if (options != NULL) {
1606 		cfga_msg(msgp, dgettext(TEXT_DOMAIN, sata_help[HELP_UNKNOWN]));
1607 		cfga_msg(msgp, options);
1608 	}
1609 	cfga_msg(msgp, dgettext(TEXT_DOMAIN, sata_help[HELP_HEADER]));
1610 	cfga_msg(msgp, sata_help[HELP_CONFIG]);
1611 	cfga_msg(msgp, sata_help[HELP_RESET_PORT]);
1612 	cfga_msg(msgp, sata_help[HELP_RESET_DEVICE]);
1613 	cfga_msg(msgp, sata_help[HELP_RESET_ALL]);
1614 	cfga_msg(msgp, sata_help[HELP_PORT_ACTIVATE]);
1615 	cfga_msg(msgp, sata_help[HELP_PORT_DEACTIVATE]);
1616 	cfga_msg(msgp, sata_help[HELP_PORT_SELF_TEST]);
1617 	cfga_msg(msgp, sata_help[HELP_CNTRL_SELF_TEST]);
1618 
1619 	return (CFGA_OK);
1620 }
1621 
1622 
1623 /*
1624  * Ensure the ap_id passed is in the correct (physical ap_id) form:
1625  *     path/device:xx[.xx]
1626  * where xx is a one or two-digit number.
1627  *
1628  * Note the library always calls the plugin with a physical ap_id.
1629  */
1630 static int
1631 verify_valid_apid(const char *ap_id)
1632 {
1633 	char	*l_ap_id;
1634 
1635 	if (ap_id == NULL)
1636 		return (-1);
1637 
1638 	l_ap_id = strrchr(ap_id, (int)*MINOR_SEP);
1639 	l_ap_id++;
1640 
1641 	if (strspn(l_ap_id, "0123456789.") != strlen(l_ap_id)) {
1642 		/* Bad characters in the ap_id */
1643 		return (-1);
1644 	}
1645 
1646 	if (strstr(l_ap_id, "..") != NULL) {
1647 		/* ap_id has 1..2 or more than 2 dots */
1648 		return (-1);
1649 	}
1650 
1651 	return (0);
1652 }
1653 
1654 
1655 
1656 /*
1657  * Verify the params passed in are valid.
1658  */
1659 static cfga_sata_ret_t
1660 verify_params(
1661 	const char *ap_id,
1662 	const char *options,
1663 	char **errstring)
1664 {
1665 	char *pdyn, *lap_id;
1666 	int rv;
1667 
1668 	if (errstring != NULL) {
1669 		*errstring = NULL;
1670 	}
1671 
1672 	if (options != NULL) {
1673 		return (CFGA_SATA_OPTIONS);
1674 	}
1675 
1676 	/* Strip dynamic AP name component if it is present. */
1677 	lap_id = strdup(ap_id);
1678 	if (lap_id == NULL) {
1679 		return (CFGA_SATA_ALLOC_FAIL);
1680 	}
1681 	if ((pdyn = GET_DYN(lap_id)) != NULL) {
1682 		*pdyn = '\0';
1683 	}
1684 
1685 	if (verify_valid_apid(lap_id) != 0) {
1686 		rv = CFGA_SATA_AP;
1687 	} else {
1688 		rv = CFGA_SATA_OK;
1689 	}
1690 	free(lap_id);
1691 
1692 	return (rv);
1693 }
1694 
1695 /*
1696  * Takes a validated ap_id and extracts the port number.
1697  * For now, we do not support port multiplier port .
1698  */
1699 static cfga_sata_ret_t
1700 get_port_num(const char *ap_id, uint32_t *port)
1701 {
1702 	char *port_nbr_str;
1703 	char *temp;
1704 	uint32_t cport;
1705 	uint32_t pmport = 0;	/* port multiplier not supported yet */
1706 	int pmport_qual = 0;	/* port multiplier not supported yet */
1707 
1708 	port_nbr_str = strrchr(ap_id, (int)*MINOR_SEP) + strlen(MINOR_SEP);
1709 	if ((temp = strrchr(ap_id, (int)*PORT_SEPARATOR)) != 0) {
1710 		port_nbr_str = temp + strlen(PORT_SEPARATOR);
1711 	}
1712 
1713 	errno = 0;
1714 	cport = strtol(port_nbr_str, NULL, 10);
1715 	if ((cport & ~SATA_CFGA_CPORT_MASK) != 0 || errno != 0)
1716 		return (CFGA_SATA_PORT);
1717 
1718 	*port = cport + (pmport << SATA_CFGA_PMPORT_SHIFT) + pmport_qual;
1719 
1720 	return (CFGA_SATA_OK);
1721 }
1722 
1723 /*
1724  * Pair of routines to set up for/clean up after a devctl_ap_* lib call.
1725  */
1726 static void
1727 cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl, nvlist_t *user_nvlist)
1728 {
1729 	if (user_nvlist != NULL) {
1730 		nvlist_free(user_nvlist);
1731 	}
1732 	if (devctl_hdl != NULL) {
1733 		devctl_release(devctl_hdl);
1734 	}
1735 }
1736 
1737 static cfga_sata_ret_t
1738 setup_for_devctl_cmd(
1739 	const char *ap_id,
1740 	devctl_hdl_t *devctl_hdl,
1741 	nvlist_t **user_nvlistp,
1742 	uint_t oflag)
1743 {
1744 
1745 	uint_t	port;
1746 	cfga_sata_ret_t	rv = CFGA_SATA_OK;
1747 	char *lap_id, *pdyn;
1748 
1749 	lap_id = strdup(ap_id);
1750 	if (lap_id == NULL)
1751 		return (CFGA_SATA_ALLOC_FAIL);
1752 	if ((pdyn = GET_DYN(lap_id)) != NULL) {
1753 		*pdyn = '\0';
1754 	}
1755 
1756 	/* Get a devctl handle to pass to the devctl_ap_XXX functions */
1757 	if ((*devctl_hdl = devctl_ap_acquire((char *)lap_id, oflag)) == NULL) {
1758 		(void) fprintf(stderr, "[libcfgadm:sata] "
1759 		    "setup_for_devctl_cmd: devctl_ap_acquire failed: %s\n",
1760 		    strerror(errno));
1761 		rv = CFGA_SATA_DEVCTL;
1762 		goto bailout;
1763 	}
1764 
1765 	/* Set up nvlist to pass the port number down to the driver */
1766 	if (nvlist_alloc(user_nvlistp, NV_UNIQUE_NAME_TYPE, NULL) != 0) {
1767 		*user_nvlistp = NULL;
1768 		rv = CFGA_SATA_NVLIST;
1769 		(void) printf("nvlist_alloc failed\n");
1770 		goto bailout;
1771 	}
1772 
1773 	/*
1774 	 * Get port id, for Port Multiplier port, things could be a little bit
1775 	 * complicated because of "port.port" format in ap_id, thus for
1776 	 * port multiplier port, port number should be coded as 32bit int
1777 	 * with the sig 16 bit as sata channel number, least 16 bit as
1778 	 * the port number of sata port multiplier port.
1779 	 */
1780 	if ((rv = get_port_num(lap_id, &port)) != CFGA_SATA_OK) {
1781 		(void) printf(
1782 			"setup_for_devctl_cmd: get_port_num, errno: %d\n",
1783 			errno);
1784 		goto bailout;
1785 	}
1786 
1787 	/* Creates an int32_t entry */
1788 	if (nvlist_add_int32(*user_nvlistp, PORT, port) == -1) {
1789 		(void) printf("nvlist_add_int32 failed\n");
1790 		rv = CFGA_SATA_NVLIST;
1791 		goto bailout;
1792 	}
1793 
1794 	free(lap_id);
1795 	return (rv);
1796 
1797 bailout:
1798 	free(lap_id);
1799 	(void) cleanup_after_devctl_cmd(*devctl_hdl, *user_nvlistp);
1800 
1801 	return (rv);
1802 }
1803 
1804 
1805 static cfga_sata_ret_t
1806 port_state(devctl_hdl_t hdl, nvlist_t *list,
1807 	ap_rstate_t *rstate, ap_ostate_t *ostate)
1808 {
1809 	devctl_ap_state_t	devctl_ap_state;
1810 
1811 	if (devctl_ap_getstate(hdl, list, &devctl_ap_state) == -1) {
1812 		(void) printf("devctl_ap_getstate failed, errno: %d\n", errno);
1813 		return (CFGA_SATA_IOCTL);
1814 	}
1815 	*rstate = devctl_ap_state.ap_rstate;
1816 	*ostate =  devctl_ap_state.ap_ostate;
1817 	return (CFGA_SATA_OK);
1818 }
1819 
1820 
1821 /*
1822  * Given a subcommand to the DEVCTL_AP_CONTROL ioctl, rquest the size of
1823  * the data to be returned, allocate a buffer, then get the data.
1824  * Returns *descrp (which must be freed) and size.
1825  *
1826  * Note SATA_DESCR_TYPE_STRING returns an ASCII NULL-terminated string,
1827  * not a string descr.
1828  */
1829 cfga_sata_ret_t
1830 do_control_ioctl(const char *ap_id, sata_cfga_apctl_t subcommand, uint_t arg,
1831 		void **descrp, size_t *sizep)
1832 {
1833 	int			fd = -1;
1834 	uint_t			port;
1835 	uint32_t		local_size;
1836 	cfga_sata_ret_t		rv = CFGA_SATA_OK;
1837 	struct sata_ioctl_data	ioctl_data;
1838 
1839 	assert(descrp != NULL);
1840 	*descrp = NULL;
1841 	assert(sizep != NULL);
1842 
1843 	if ((rv = get_port_num(ap_id, &port)) != CFGA_SATA_OK) {
1844 		goto bailout;
1845 	}
1846 
1847 	if ((fd = open(ap_id, O_RDONLY)) == -1) {
1848 		(void) printf("do_control_ioctl: open failed: errno:%d\n",
1849 			errno);
1850 		rv = CFGA_SATA_OPEN;
1851 		if (errno == EBUSY) {
1852 			rv = CFGA_SATA_BUSY;
1853 		}
1854 		goto bailout;
1855 	}
1856 
1857 	ioctl_data.cmd = subcommand;
1858 	ioctl_data.port = port;
1859 	ioctl_data.misc_arg = (uint_t)arg;
1860 
1861 	/*
1862 	 * Find out how large a buf we need to get the data.
1863 	 * Note the ioctls only accept/return a 32-bit int for a get_size
1864 	 * to avoid 32/64 and BE/LE issues.
1865 	 */
1866 	if ((subcommand == SATA_CFGA_GET_AP_TYPE) ||
1867 	    (subcommand == SATA_CFGA_GET_DEVICE_PATH) ||
1868 	    (subcommand == SATA_CFGA_GET_MODEL_INFO) ||
1869 	    (subcommand == SATA_CFGA_GET_REVFIRMWARE_INFO) ||
1870 	    (subcommand == SATA_CFGA_GET_SERIALNUMBER_INFO)) {
1871 		ioctl_data.get_size = B_TRUE;
1872 		ioctl_data.buf = (caddr_t)&local_size;
1873 		ioctl_data.bufsiz = sizeof (local_size);
1874 
1875 		if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
1876 			perror("ioctl failed (size)");
1877 			rv = CFGA_SATA_IOCTL;
1878 			goto bailout;
1879 		}
1880 		*sizep = local_size;
1881 
1882 		if (local_size == 0) {
1883 			(void) printf("zero length data\n");
1884 			rv = CFGA_SATA_ZEROLEN;
1885 			goto bailout;
1886 		}
1887 		if ((*descrp = malloc(*sizep)) == NULL) {
1888 			(void) printf("do_control_ioctl: malloc failed\n");
1889 			rv = CFGA_SATA_ALLOC_FAIL;
1890 			goto bailout;
1891 		}
1892 	} else {
1893 		*sizep = 0;
1894 	}
1895 	ioctl_data.get_size = B_FALSE;
1896 	ioctl_data.buf = *descrp;
1897 	ioctl_data.bufsiz = *sizep;
1898 
1899 	/* Execute IOCTL */
1900 
1901 	if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
1902 		rv = CFGA_SATA_IOCTL;
1903 		goto bailout;
1904 	}
1905 
1906 	(void) close(fd);
1907 
1908 	return (rv);
1909 
1910 bailout:
1911 	if (fd != -1) {
1912 		(void) close(fd);
1913 	}
1914 	if (*descrp != NULL) {
1915 		free(*descrp);
1916 		*descrp = NULL;
1917 	}
1918 
1919 	if (rv == CFGA_SATA_IOCTL && errno == EBUSY) {
1920 		rv = CFGA_SATA_BUSY;
1921 	}
1922 
1923 	return (rv);
1924 }
1925 
1926 
1927 static int
1928 sata_confirm(struct cfga_confirm *confp, char *msg)
1929 {
1930 	int rval;
1931 
1932 	if (confp == NULL || confp->confirm == NULL) {
1933 		return (0);
1934 	}
1935 	rval = (*confp->confirm)(confp->appdata_ptr, msg);
1936 
1937 	return (rval);
1938 }
1939 
1940 
1941 static char *
1942 sata_get_devicepath(const char *ap_id)
1943 {
1944 	char		*devpath = NULL;
1945 	size_t		size;
1946 	cfga_sata_ret_t	rv;
1947 
1948 	rv = do_control_ioctl(ap_id, SATA_CFGA_GET_DEVICE_PATH, NULL,
1949 	    (void **)&devpath, &size);
1950 
1951 	if (rv == CFGA_SATA_OK) {
1952 		return (devpath);
1953 	} else {
1954 		return ((char *)NULL);
1955 	}
1956 
1957 }
1958