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