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