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