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