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