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