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