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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  *      Plugin library for PCI Express and PCI (SHPC) hotplug controller
  28  */
  29 
  30 #include <stddef.h>
  31 #include <locale.h>
  32 #include <ctype.h>
  33 #include <stdio.h>
  34 #include <stdlib.h>
  35 #include <string.h>
  36 #include <fcntl.h>
  37 #include <unistd.h>
  38 #include <errno.h>
  39 #include <locale.h>
  40 #include <langinfo.h>
  41 #include <time.h>
  42 #include <sys/param.h>
  43 #include <stdarg.h>
  44 #include <libdevinfo.h>
  45 #include <libdevice.h>
  46 
  47 #define CFGA_PLUGIN_LIB
  48 
  49 #include <config_admin.h>
  50 
  51 #include <assert.h>
  52 #include <sys/types.h>
  53 #include <sys/stat.h>
  54 #include <sys/dditypes.h>
  55 #include <sys/pci.h>
  56 #include <libintl.h>
  57 
  58 #include <dirent.h>
  59 #include <limits.h>
  60 #include <sys/mkdev.h>
  61 #include "../../../../uts/common/sys/hotplug/pci/pcie_hp.h"
  62 #include "../../../../common/pci/pci_strings.h"
  63 #include <libhotplug.h>
  64 
  65 extern const struct pci_class_strings_s class_pci[];
  66 extern int class_pci_items;
  67 
  68 #define MSG_HOTPLUG_DISABLED \
  69         "Error: hotplug service is probably not running, " \
  70         "please use 'svcadm enable hotplug' to enable the service. " \
  71         "See cfgadm_shp(1M) for more details."
  72 
  73 #define DEVICES_DIR             "/devices"
  74 #define SLASH                   "/"
  75 #define GET_DYN(a)      (strstr((a), CFGA_DYN_SEP))
  76 
  77 /*
  78  * Set the version number
  79  */
  80 int cfga_version = CFGA_HSL_V2;
  81 
  82 #ifdef  DEBUG
  83 #define SHP_DBG 1
  84 #endif
  85 
  86 #if !defined(TEXT_DOMAIN)
  87 #define TEXT_DOMAIN     "SYS_TEST"
  88 #endif
  89 
  90 /*
  91  *      DEBUGING LEVEL
  92  *
  93  *      External routines:  1 - 2
  94  *      Internal routines:  3 - 4
  95  */
  96 #ifdef  SHP_DBG
  97 int     shp_debug = 1;
  98 #define DBG(level, args) \
  99         { if (shp_debug >= (level)) printf args; }
 100 #define DBG_F(level, args) \
 101         { if (shp_debug >= (level)) fprintf args; }
 102 #else
 103 #define DBG(level, args)        /* nothing */
 104 #define DBG_F(level, args)      /* nothing */
 105 #endif
 106 
 107 #define CMD_ACQUIRE             0
 108 #define CMD_GETSTAT             1
 109 #define CMD_LIST                2
 110 #define CMD_SLOT_CONNECT        3
 111 #define CMD_SLOT_DISCONNECT     4
 112 #define CMD_SLOT_CONFIGURE      5
 113 #define CMD_SLOT_UNCONFIGURE    6
 114 #define CMD_SLOT_INSERT         7
 115 #define CMD_SLOT_REMOVE         8
 116 #define CMD_OPEN                9
 117 #define CMD_FSTAT               10
 118 #define ERR_CMD_INVAL           11
 119 #define ERR_AP_INVAL            12
 120 #define ERR_AP_ERR              13
 121 #define ERR_OPT_INVAL           14
 122 
 123 static char *
 124 cfga_errstrs[] = {
 125         /* n */ "acquire ",
 126         /* n */ "get-status ",
 127         /* n */ "list ",
 128         /* n */ "connect ",
 129         /* n */ "disconnect ",
 130         /* n */ "configure ",
 131         /* n */ "unconfigure ",
 132         /* n */ "insert ",
 133         /* n */ "remove ",
 134         /* n */ "open ",
 135         /* n */ "fstat ",
 136         /* y */ "invalid command ",
 137         /* y */ "invalid attachment point ",
 138         /* y */ "invalid transition ",
 139         /* y */ "invalid option ",
 140                 NULL
 141 };
 142 
 143 #define HELP_HEADER             1
 144 #define HELP_CONFIG             2
 145 #define HELP_ENABLE_SLOT        3
 146 #define HELP_DISABLE_SLOT       4
 147 #define HELP_ENABLE_AUTOCONF    5
 148 #define HELP_DISABLE_AUTOCONF   6
 149 #define HELP_LED_CNTRL          7
 150 #define HELP_UNKNOWN            8
 151 #define SUCCESS                 9
 152 #define FAILED                  10
 153 #define UNKNOWN                 11
 154 
 155 #define MAXLINE                 256
 156 
 157 extern int errno;
 158 
 159 static void cfga_err(char **errstring, ...);
 160 static cfga_err_t fix_ap_name(char *ap_log_id, const char *ap_id,
 161     char *slot_name, char **errstring);
 162 static cfga_err_t check_options(const char *options);
 163 static void cfga_msg(struct cfga_msg *msgp, const char *str);
 164 static char *findlink(char *ap_phys_id);
 165 
 166 static char *
 167 cfga_strs[] = {
 168 NULL,
 169 "\nPCI hotplug specific commands:",
 170 "\t-c [connect|disconnect|configure|unconfigure|insert|remove] "
 171 "ap_id [ap_id...]",
 172 "\t-x enable_slot  ap_id [ap_id...]",
 173 "\t-x disable_slot ap_id [ap_id...]",
 174 "\t-x enable_autoconfig  ap_id [ap_id...]",
 175 "\t-x disable_autoconfig ap_id [ap_id...]",
 176 "\t-x led[=[fault|power|active|attn],mode=[on|off|blink]] ap_id [ap_id...]",
 177 "\tunknown command or option: ",
 178 "success   ",
 179 "failed   ",
 180 "unknown",
 181 NULL
 182 };
 183 
 184 #define MAX_FORMAT 80
 185 
 186 #define ENABLE_SLOT     0
 187 #define DISABLE_SLOT    1
 188 #define ENABLE_AUTOCNF  2
 189 #define DISABLE_AUTOCNF 3
 190 #define LED             4
 191 #define MODE            5
 192 
 193 typedef enum { PCIEHPC_FAULT_LED, PCIEHPC_POWER_LED, PCIEHPC_ATTN_LED,
 194         PCIEHPC_ACTIVE_LED} pciehpc_led_t;
 195 
 196 typedef enum { PCIEHPC_BOARD_UNKNOWN, PCIEHPC_BOARD_PCI_HOTPLUG }
 197         pciehpc_board_type_t;
 198 
 199 /*
 200  * Board Type
 201  */
 202 static char *
 203 board_strs[] = {
 204         /* n */ "???",  /* PCIEHPC_BOARD_UNKNOWN */
 205         /* n */ "hp",   /* PCIEHPC_BOARD_PCI_HOTPLUG */
 206         /* n */ NULL
 207 };
 208 
 209 /*
 210  * HW functions
 211  */
 212 static char *
 213 func_strs[] = {
 214         /* n */ "enable_slot",
 215         /* n */ "disable_slot",
 216         /* n */ "enable_autoconfig",
 217         /* n */ "disable_autoconfig",
 218         /* n */ "led",
 219         /* n */ "mode",
 220         /* n */ NULL
 221 };
 222 
 223 /*
 224  * LED strings
 225  */
 226 static char *
 227 led_strs[] = {
 228         /* n */ "fault",        /* PCIEHPC_FAULT_LED */
 229         /* n */ "power",        /* PCIEHPC_POWER_LED */
 230         /* n */ "attn",         /* PCIEHPC_ATTN_LED */
 231         /* n */ "active",       /* PCIEHPC_ACTIVE_LED */
 232         /* n */ NULL
 233 };
 234 
 235 static char *
 236 led_strs2[] = {
 237         /* n */ PCIEHPC_PROP_LED_FAULT,         /* PCIEHPC_FAULT_LED */
 238         /* n */ PCIEHPC_PROP_LED_POWER,         /* PCIEHPC_POWER_LED */
 239         /* n */ PCIEHPC_PROP_LED_ATTN,          /* PCIEHPC_ATTN_LED */
 240         /* n */ PCIEHPC_PROP_LED_ACTIVE,        /* PCIEHPC_ACTIVE_LED */
 241         /* n */ NULL
 242 };
 243 
 244 #define FAULT   0
 245 #define POWER   1
 246 #define ATTN    2
 247 #define ACTIVE  3
 248 
 249 static char *
 250 mode_strs[] = {
 251         /* n */ "off",          /* OFF */
 252         /* n */ "on",           /* ON */
 253         /* n */ "blink",        /* BLINK */
 254         /* n */ NULL
 255 };
 256 
 257 #define OFF     0
 258 #define ON      1
 259 #define BLINK   2
 260 
 261 #define cfga_errstrs(i)         cfga_errstrs[(i)]
 262 
 263 #define cfga_eid(a, b)          (((a) << 8) + (b))
 264 #define MAXDEVS                 32
 265 
 266 typedef enum {
 267         SOLARIS_SLT_NAME,
 268         PROM_SLT_NAME
 269 } slt_name_src_t;
 270 
 271 struct searcharg {
 272         char    *devpath;
 273         char    slotnames[MAXDEVS][MAXNAMELEN];
 274         int     minor;
 275         di_prom_handle_t        promp;
 276         slt_name_src_t  slt_name_src;
 277 };
 278 
 279 static void *private_check;
 280 
 281 /*
 282  * Return the corresponding hp node for a given ap_id, it is the caller's
 283  * responsibility to call hp_fini() to free the snapshot.
 284  */
 285 static cfga_err_t
 286 physpath2node(const char *physpath, char **errstring, hp_node_t *nodep)
 287 {
 288         char *rpath;
 289         char *cp;
 290         hp_node_t node;
 291         size_t len;
 292         char *errmsg;
 293 
 294         if (getuid() != 0 && geteuid() != 0)
 295                 return (CFGA_ERROR);
 296 
 297         if ((rpath = malloc(strlen(physpath) + 1)) == NULL)
 298                 return (CFGA_ERROR);
 299 
 300         (void) strcpy(rpath, physpath);
 301 
 302         /* Remove devices prefix (if any) */
 303         len = strlen(DEVICES_DIR);
 304         if (strncmp(rpath, DEVICES_DIR SLASH, len + strlen(SLASH)) == 0) {
 305                 (void) memmove(rpath, rpath + len,
 306                     strlen(rpath + len) + 1);
 307         }
 308 
 309         /* Remove dynamic component if any */
 310         if ((cp = GET_DYN(rpath)) != NULL) {
 311                 *cp = '\0';
 312         }
 313 
 314         /* Remove minor name (if any) */
 315         if ((cp = strrchr(rpath, ':')) == NULL) {
 316                 free(rpath);
 317                 return (CFGA_INVAL);
 318         }
 319 
 320         *cp = '\0';
 321         cp++;
 322 
 323         DBG(1, ("rpath=%s,cp=%s\n", rpath, cp));
 324         if ((node = hp_init(rpath, cp, 0)) == NULL) {
 325                 if (errno == EBADF) {
 326                         /* No reponse to operations on the door file. */
 327                         assert(errstring != NULL);
 328                         *errstring = strdup(MSG_HOTPLUG_DISABLED);
 329                         free(rpath);
 330                         return (CFGA_NOTSUPP);
 331                 }
 332                 free(rpath);
 333                 return (CFGA_ERROR);
 334         }
 335 
 336         free(rpath);
 337 
 338         *nodep = node;
 339         return (CFGA_OK);
 340 }
 341 
 342 typedef struct error_size_cb_arg {
 343         size_t  rsrc_width;
 344         size_t  info_width;
 345         int     cnt;
 346 } error_size_cb_arg_t;
 347 
 348 /*
 349  * Callback function for hp_traverse(), to sum up the
 350  * maximum length for error message display.
 351  */
 352 static int
 353 error_sizeup_cb(hp_node_t node, void *arg)
 354 {
 355         error_size_cb_arg_t     *sizearg = (error_size_cb_arg_t *)arg;
 356         size_t                  len;
 357 
 358         /* Only process USAGE nodes */
 359         if (hp_type(node) != HP_NODE_USAGE)
 360                 return (HP_WALK_CONTINUE);
 361 
 362         sizearg->cnt++;
 363 
 364         /* size up resource name */
 365         len = strlen(hp_name(node));
 366         if (sizearg->rsrc_width < len)
 367                 sizearg->rsrc_width = len;
 368 
 369         /* size up usage description */
 370         len = strlen(hp_usage(node));
 371         if (sizearg->info_width < len)
 372                 sizearg->info_width = len;
 373 
 374         return (HP_WALK_CONTINUE);
 375 }
 376 
 377 typedef struct error_sum_cb_arg {
 378         char **table;
 379         char *format;
 380 } error_sum_cb_arg_t;
 381 
 382 /*
 383  * Callback function for hp_traverse(), to add the error
 384  * message to he table.
 385  */
 386 static int
 387 error_sumup_cb(hp_node_t node, void *arg)
 388 {
 389         error_sum_cb_arg_t *sumarg = (error_sum_cb_arg_t *)arg;
 390         char **table = sumarg->table;
 391         char *format = sumarg->format;
 392 
 393         /* Only process USAGE nodes */
 394         if (hp_type(node) != HP_NODE_USAGE)
 395                 return (HP_WALK_CONTINUE);
 396 
 397         (void) strcat(*table, "\n");
 398         (void) sprintf(&((*table)[strlen(*table)]),
 399             format, hp_name(node), hp_usage(node));
 400 
 401         return (HP_WALK_CONTINUE);
 402 }
 403 
 404 /*
 405  * Takes an opaque rcm_info_t pointer and a character pointer, and appends
 406  * the rcm_info_t data in the form of a table to the given character pointer.
 407  */
 408 static void
 409 pci_rcm_info_table(hp_node_t node, char **table)
 410 {
 411         int i;
 412         size_t w;
 413         size_t width = 0;
 414         size_t w_rsrc = 0;
 415         size_t w_info = 0;
 416         size_t table_size = 0;
 417         uint_t tuples = 0;
 418         char *rsrc;
 419         char *info;
 420         char *newtable;
 421         static char format[MAX_FORMAT];
 422         const char *infostr;
 423         error_size_cb_arg_t sizearg;
 424         error_sum_cb_arg_t sumarg;
 425 
 426         /* Protect against invalid arguments */
 427         if (table == NULL)
 428                 return;
 429 
 430         /* Set localized table header strings */
 431         rsrc = dgettext(TEXT_DOMAIN, "Resource");
 432         info = dgettext(TEXT_DOMAIN, "Information");
 433 
 434         /* A first pass, to size up the RCM information */
 435         sizearg.rsrc_width = strlen(rsrc);
 436         sizearg.info_width = strlen(info);
 437         sizearg.cnt = 0;
 438         (void) hp_traverse(node, &sizearg, error_sizeup_cb);
 439 
 440         /* If nothing was sized up above, stop early */
 441         if (sizearg.cnt == 0)
 442                 return;
 443 
 444         w_rsrc = sizearg.rsrc_width;
 445         w_info = sizearg.info_width;
 446         tuples = sizearg.cnt;
 447 
 448         /* Adjust column widths for column headings */
 449         if ((w = strlen(rsrc)) > w_rsrc)
 450                 w_rsrc = w;
 451         else if ((w_rsrc - w) % 2)
 452                 w_rsrc++;
 453         if ((w = strlen(info)) > w_info)
 454                 w_info = w;
 455         else if ((w_info - w) % 2)
 456                 w_info++;
 457 
 458         /*
 459          * Compute the total line width of each line,
 460          * accounting for intercolumn spacing.
 461          */
 462         width = w_info + w_rsrc + 4;
 463 
 464         /* Allocate space for the table */
 465         table_size = (2 + tuples) * (width + 1) + 2;
 466         if (*table == NULL) {
 467                 /* zero fill for the strcat() call below */
 468                 *table = calloc(table_size, sizeof (char));
 469                 if (*table == NULL)
 470                         return;
 471         } else {
 472                 newtable = realloc(*table, strlen(*table) + table_size);
 473                 if (newtable == NULL)
 474                         return;
 475                 else
 476                         *table = newtable;
 477         }
 478 
 479         /* Place a table header into the string */
 480 
 481         /* The resource header */
 482         (void) strcat(*table, "\n");
 483         w = strlen(rsrc);
 484         for (i = 0; i < ((w_rsrc - w) / 2); i++)
 485                 (void) strcat(*table, " ");
 486         (void) strcat(*table, rsrc);
 487         for (i = 0; i < ((w_rsrc - w) / 2); i++)
 488                 (void) strcat(*table, " ");
 489 
 490         /* The information header */
 491         (void) strcat(*table, "  ");
 492         w = strlen(info);
 493         for (i = 0; i < ((w_info - w) / 2); i++)
 494                 (void) strcat(*table, " ");
 495         (void) strcat(*table, info);
 496         for (i = 0; i < ((w_info - w) / 2); i++)
 497                 (void) strcat(*table, " ");
 498         /* Underline the headers */
 499         (void) strcat(*table, "\n");
 500         for (i = 0; i < w_rsrc; i++)
 501                 (void) strcat(*table, "-");
 502         (void) strcat(*table, "  ");
 503         for (i = 0; i < w_info; i++)
 504                 (void) strcat(*table, "-");
 505 
 506         /* Construct the format string */
 507         (void) snprintf(format, MAX_FORMAT, "%%-%ds  %%-%ds",
 508             (int)w_rsrc, (int)w_info);
 509 
 510         /* Add the tuples to the table string */
 511         sumarg.table = table;
 512         sumarg.format = format;
 513         (void) hp_traverse(node, &sumarg, error_sumup_cb);
 514 }
 515 
 516 /*
 517  * Figure out the target kernel state for a given cfgadm
 518  * change-state operation.
 519  */
 520 static cfga_err_t
 521 cfga_target_state(cfga_cmd_t state_change_cmd, int *state)
 522 {
 523         switch (state_change_cmd) {
 524         case CFGA_CMD_CONNECT:
 525                 *state = DDI_HP_CN_STATE_POWERED;
 526                 break;
 527         case CFGA_CMD_DISCONNECT:
 528                 *state = DDI_HP_CN_STATE_PRESENT;
 529                 break;
 530         case CFGA_CMD_CONFIGURE:
 531                 *state = DDI_HP_CN_STATE_ENABLED;
 532                 break;
 533         case CFGA_CMD_UNCONFIGURE:
 534                 *state = DDI_HP_CN_STATE_POWERED;
 535                 break;
 536         default:
 537                 return (CFGA_ERROR);
 538         }
 539 
 540         return (CFGA_OK);
 541 }
 542 
 543 /*
 544  * Translate kernel state to cfgadm receptacle state and occupant state.
 545  */
 546 static cfga_err_t
 547 cfga_get_state(hp_node_t connector, ap_rstate_t *rs, ap_ostate_t *os)
 548 {
 549         int state;
 550         hp_node_t port;
 551 
 552         state = hp_state(connector);
 553 
 554         /* Receptacle state */
 555         switch (state) {
 556         case DDI_HP_CN_STATE_EMPTY:
 557                 *rs = AP_RSTATE_EMPTY;
 558                 break;
 559         case DDI_HP_CN_STATE_PRESENT:
 560                 *rs = AP_RSTATE_DISCONNECTED;
 561                 break;
 562         case DDI_HP_CN_STATE_POWERED:
 563         case DDI_HP_CN_STATE_ENABLED:
 564                 *rs = AP_RSTATE_CONNECTED;
 565                 break;
 566                 /*
 567                  * Connector state can only be one of
 568                  * Empty, Present, Powered, Enabled.
 569                  */
 570         default:
 571                 return (CFGA_ERROR);
 572         }
 573 
 574         /*
 575          * Occupant state
 576          */
 577         port = hp_child(connector);
 578         while (port != NULL) {
 579                 DBG(1, ("cfga_get_state:(%x)\n", hp_state(port)));
 580 
 581                 /*
 582                  * Mark occupant state as "configured" if at least one of the
 583                  * associated ports is at state "offline" or above. Driver
 584                  * attach ("online" state) is not necessary here.
 585                  */
 586                 if (hp_state(port) >= DDI_HP_CN_STATE_OFFLINE)
 587                         break;
 588 
 589                 port = hp_sibling(port);
 590         }
 591 
 592         if (port != NULL)
 593                 *os = AP_OSTATE_CONFIGURED;
 594         else
 595                 *os = AP_OSTATE_UNCONFIGURED;
 596 
 597         return (CFGA_OK);
 598 }
 599 
 600 /*
 601  * Transitional Diagram:
 602  *
 603  *  empty               unconfigure
 604  * (remove)     ^|  (physically insert card)
 605  *                      |V
 606  * disconnect   configure
 607  * "-c DISCONNECT"      ^|      "-c CONNECT"
 608  *                              |V      "-c CONFIGURE"
 609  * connect      unconfigure     ->   connect    configure
 610  *                                              <-
 611  *                                      "-c UNCONFIGURE"
 612  *
 613  */
 614 /*ARGSUSED*/
 615 cfga_err_t
 616 cfga_change_state(cfga_cmd_t state_change_cmd, const char *ap_id,
 617     const char *options, struct cfga_confirm *confp,
 618     struct cfga_msg *msgp, char **errstring, cfga_flags_t flags)
 619 {
 620         int             rv, state, new_state;
 621         uint_t          hpflags = 0;
 622         hp_node_t       node;
 623         hp_node_t       results = NULL;
 624 
 625         if ((rv = check_options(options)) != CFGA_OK) {
 626                 return (rv);
 627         }
 628 
 629         if (errstring != NULL)
 630                 *errstring = NULL;
 631 
 632         rv = CFGA_OK;
 633         DBG(1, ("cfga_change_state:(%s)\n", ap_id));
 634 
 635         rv = physpath2node(ap_id, errstring, &node);
 636         if (rv != CFGA_OK)
 637                 return (rv);
 638 
 639         /*
 640          * Check for the FORCE flag.  It is only used
 641          * for DISCONNECT or UNCONFIGURE state changes.
 642          */
 643         if (flags & CFGA_FLAG_FORCE)
 644                 hpflags |= HPFORCE;
 645 
 646         state = hp_state(node);
 647 
 648         /*
 649          * Which state should we drive to ?
 650          */
 651         if ((state_change_cmd != CFGA_CMD_LOAD) &&
 652             (state_change_cmd != CFGA_CMD_UNLOAD)) {
 653                 if (cfga_target_state(state_change_cmd,
 654                     &new_state) != CFGA_OK) {
 655                         hp_fini(node);
 656                         return (CFGA_ERROR);
 657                 }
 658         }
 659 
 660         DBG(1, ("cfga_change_state: state is %d\n", state));
 661         switch (state_change_cmd) {
 662         case CFGA_CMD_CONNECT:
 663                 DBG(1, ("connect\n"));
 664                 if (state == DDI_HP_CN_STATE_EMPTY) {
 665                         cfga_err(errstring, ERR_AP_ERR, 0);
 666                         rv = CFGA_INVAL;
 667                 } else if (state == DDI_HP_CN_STATE_PRESENT) {
 668                         /* Connect the slot */
 669                         if (hp_set_state(node, 0, new_state, &results) != 0) {
 670                                 rv = CFGA_ERROR;
 671                                 cfga_err(errstring, CMD_SLOT_CONNECT, 0);
 672                         }
 673                 }
 674                 break;
 675 
 676         case CFGA_CMD_DISCONNECT:
 677                 DBG(1, ("disconnect\n"));
 678                 if (state == DDI_HP_CN_STATE_EMPTY) {
 679                         cfga_err(errstring, ERR_AP_ERR, 0);
 680                         rv = CFGA_INVAL;
 681                 } else if (state > DDI_HP_CN_STATE_PRESENT) {
 682                         /* Disconnect the slot */
 683                         rv = hp_set_state(node, hpflags, new_state, &results);
 684                         if (rv != 0) {
 685                                 if (rv == EBUSY)
 686                                         rv = CFGA_BUSY;
 687                                 else
 688                                         rv = CFGA_ERROR;
 689 
 690                                 if (results) {
 691                                         pci_rcm_info_table(results, errstring);
 692                                         hp_fini(results);
 693                                 } else {
 694                                         cfga_err(errstring,
 695                                             CMD_SLOT_DISCONNECT, 0);
 696                                 }
 697                         }
 698                 }
 699                 break;
 700 
 701         case CFGA_CMD_CONFIGURE:
 702                 /*
 703                  * for multi-func device we allow multiple
 704                  * configure on the same slot because one
 705                  * func can be configured and other one won't
 706                  */
 707                 DBG(1, ("configure\n"));
 708                 if (state == DDI_HP_CN_STATE_EMPTY) {
 709                         cfga_err(errstring, ERR_AP_ERR, 0);
 710                         rv = CFGA_INVAL;
 711                 } else if (hp_set_state(node, 0, new_state, &results) != 0) {
 712                         rv = CFGA_ERROR;
 713                         cfga_err(errstring, CMD_SLOT_CONFIGURE, 0);
 714                 }
 715                 break;
 716 
 717         case CFGA_CMD_UNCONFIGURE:
 718                 DBG(1, ("unconfigure\n"));
 719                 if (state == DDI_HP_CN_STATE_EMPTY) {
 720                         cfga_err(errstring, ERR_AP_ERR, 0);
 721                         rv = CFGA_INVAL;
 722                 } else if (state >= DDI_HP_CN_STATE_ENABLED) {
 723                         rv = hp_set_state(node, hpflags, new_state, &results);
 724                         if (rv != 0) {
 725                                 if (rv == EBUSY)
 726                                         rv = CFGA_BUSY;
 727                                 else
 728                                         rv = CFGA_ERROR;
 729 
 730                                 if (results) {
 731                                         pci_rcm_info_table(results, errstring);
 732                                         hp_fini(results);
 733                                 } else {
 734                                         cfga_err(errstring,
 735                                             CMD_SLOT_UNCONFIGURE, 0);
 736                                 }
 737                         }
 738                 }
 739                 DBG(1, ("unconfigure rv:(%i)\n", rv));
 740                 break;
 741 
 742         case CFGA_CMD_LOAD:
 743                 /* do nothing, just produce error msg as is */
 744                 if (state < DDI_HP_CN_STATE_POWERED) {
 745                         rv = CFGA_ERROR;
 746                         cfga_err(errstring, CMD_SLOT_INSERT, 0);
 747                 } else {
 748                         cfga_err(errstring, ERR_AP_ERR, 0);
 749                         rv = CFGA_INVAL;
 750                 }
 751                 break;
 752 
 753         case CFGA_CMD_UNLOAD:
 754                 /* do nothing, just produce error msg as is */
 755                 if (state < DDI_HP_CN_STATE_POWERED) {
 756                         rv = CFGA_ERROR;
 757                         cfga_err(errstring, CMD_SLOT_REMOVE, 0);
 758                 } else {
 759                         cfga_err(errstring, ERR_AP_ERR, 0);
 760                         rv = CFGA_INVAL;
 761                 }
 762                 break;
 763 
 764         default:
 765                 rv = CFGA_OPNOTSUPP;
 766                 break;
 767         }
 768 
 769         hp_fini(node);
 770         return (rv);
 771 }
 772 
 773 char *
 774 get_val_from_result(char *result)
 775 {
 776         char *tmp;
 777 
 778         tmp = strchr(result, '=');
 779         if (tmp == NULL)
 780                 return (NULL);
 781 
 782         tmp++;
 783         return (tmp);
 784 }
 785 
 786 static cfga_err_t
 787 prt_led_mode(const char *ap_id, int repeat, char **errstring,
 788     struct cfga_msg *msgp)
 789 {
 790         pciehpc_led_t led;
 791         hp_node_t node;
 792         char *buff;
 793         char *buf;
 794         char *cp, line[MAXLINE];
 795         char *tmp;
 796         char *format;
 797         char *result;
 798         int i, n, rv;
 799         int len = MAXLINE;
 800 
 801         pciehpc_led_t states[] = {
 802                 PCIEHPC_POWER_LED,
 803                 PCIEHPC_FAULT_LED,
 804                 PCIEHPC_ATTN_LED,
 805                 PCIEHPC_ACTIVE_LED
 806         };
 807 
 808         DBG(1, ("prt_led_mod function\n"));
 809         if (!repeat)
 810                 cfga_msg(msgp, "Ap_Id\t\t\tLed");
 811 
 812         rv = physpath2node(ap_id, errstring, &node);
 813         if (rv != CFGA_OK)
 814                 return (rv);
 815 
 816         if ((buff = malloc(MAXPATHLEN)) == NULL) {
 817                 hp_fini(node);
 818                 cfga_err(errstring, "malloc ", 0);
 819                 return (CFGA_ERROR);
 820         }
 821 
 822         (void) memset(buff, 0, MAXPATHLEN);
 823 
 824         if (fix_ap_name(buff, ap_id, hp_name(node),
 825             errstring) != CFGA_OK) {
 826                 hp_fini(node);
 827                 free(buff);
 828                 return (CFGA_ERROR);
 829         }
 830 
 831         cp = line;
 832         (void) snprintf(cp, len, "%s\t\t", buff);
 833         len -= strlen(cp);
 834         cp += strlen(cp);
 835 
 836         free(buff);
 837 
 838         n = sizeof (states)/sizeof (pciehpc_led_t);
 839         for (i = 0; i < n; i++) {
 840                 led = states[i];
 841 
 842                 format = (i == n - 1) ? "%s=%s" : "%s=%s,";
 843                 if (hp_get_private(node, led_strs2[led], &result) != 0) {
 844                         (void) snprintf(cp, len, format,
 845                             led_strs[led], cfga_strs[UNKNOWN]);
 846                         len -= strlen(cp);
 847                         cp += strlen(cp);
 848                         DBG(1, ("%s:%s\n", led_strs[led], cfga_strs[UNKNOWN]));
 849                 } else {
 850                         /*
 851                          * hp_get_private() will return back things like
 852                          * "led_fault=off", transform it to cfgadm desired
 853                          * format.
 854                          */
 855                         tmp = get_val_from_result(result);
 856                         if (tmp == NULL) {
 857                                 free(result);
 858                                 hp_fini(node);
 859                                 return (CFGA_ERROR);
 860                         }
 861 
 862                         (void) snprintf(cp, len, format,
 863                             led_strs[led], tmp);
 864                         len -= strlen(cp);
 865                         cp += strlen(cp);
 866                         DBG(1, ("%s:%s\n", led_strs[led], tmp));
 867                         free(result);
 868                 }
 869         }
 870 
 871         cfga_msg(msgp, line);   /* print the message */
 872 
 873         hp_fini(node);
 874 
 875         return (CFGA_OK);
 876 }
 877 
 878 /*ARGSUSED*/
 879 cfga_err_t
 880 cfga_private_func(const char *function, const char *ap_id,
 881     const char *options, struct cfga_confirm *confp,
 882     struct cfga_msg *msgp, char **errstring, cfga_flags_t flags)
 883 {
 884         char *str;
 885         int   len, fd, i = 0, repeat = 0;
 886         char buf[MAXNAMELEN];
 887         char ptr;
 888         cfga_err_t rv;
 889         char *led, *mode;
 890         hp_node_t node;
 891         char *result;
 892 
 893         DBG(1, ("cfgadm_private_func: ap_id:%s\n", ap_id));
 894         DBG(2, ("  options: %s\n", (options == NULL)?"null":options));
 895         DBG(2, ("  confp: %x\n", confp));
 896         DBG(2, ("  cfga_msg: %x\n", cfga_msg));
 897         DBG(2, ("  flag: %d\n", flags));
 898 
 899         if ((rv = check_options(options)) != CFGA_OK) {
 900                 return (rv);
 901         }
 902 
 903         if (private_check == confp)
 904                 repeat = 1;
 905         else
 906                 private_check = (void*)confp;
 907 
 908         for (i = 0, str = func_strs[i], len = strlen(str);
 909             func_strs[i] != NULL; i++) {
 910                 str = func_strs[i];
 911                 len = strlen(str);
 912                 if (strncmp(function, str, len) == 0)
 913                         break;
 914         }
 915 
 916         switch (i) {
 917                 case ENABLE_SLOT:
 918                 case DISABLE_SLOT:
 919                         /* pass through */
 920                 case ENABLE_AUTOCNF:
 921                 case DISABLE_AUTOCNF:
 922                         /* no action needed */
 923                         return (CFGA_OK);
 924                         break;
 925                 case LED:
 926                         /* set mode */
 927                         ptr = function[len++];
 928                         if (ptr == '=') {
 929                                 str = (char *)function;
 930                                 for (str = (str+len++), i = 0; *str != ',';
 931                                     i++, str++) {
 932                                         if (i == (MAXNAMELEN - 1))
 933                                                 break;
 934 
 935                                         buf[i] = *str;
 936                                         DBG_F(2, (stdout, "%c\n", buf[i]));
 937                                 }
 938                                 buf[i] = '\0'; str++;
 939                                 DBG(2, ("buf = %s\n", buf));
 940 
 941                                 /* ACTIVE=3,ATTN=2,POWER=1,FAULT=0 */
 942                                 if (strcmp(buf, led_strs[POWER]) == 0)
 943                                         led = PCIEHPC_PROP_LED_POWER;
 944                                 else if (strcmp(buf, led_strs[FAULT]) == 0)
 945                                         led = PCIEHPC_PROP_LED_FAULT;
 946                                 else if (strcmp(buf, led_strs[ATTN]) == 0)
 947                                         led = PCIEHPC_PROP_LED_ATTN;
 948                                 else if (strcmp(buf, led_strs[ACTIVE]) == 0)
 949                                         led = PCIEHPC_PROP_LED_ACTIVE;
 950                                 else return (CFGA_INVAL);
 951 
 952                                 len = strlen(func_strs[MODE]);
 953                                 if ((strncmp(str, func_strs[MODE], len) == 0) &&
 954                                     (*(str+(len)) == '=')) {
 955                                         for (str = (str+(++len)), i = 0;
 956                                             *str != NULL; i++, str++) {
 957                                                 buf[i] = *str;
 958                                         }
 959                                 }
 960                                 buf[i] = '\0';
 961                                 DBG(2, ("buf_mode= %s\n", buf));
 962 
 963                                 /* ON = 1, OFF = 0 */
 964                                 if (strcmp(buf, mode_strs[ON]) == 0)
 965                                         mode = PCIEHPC_PROP_VALUE_ON;
 966                                 else if (strcmp(buf, mode_strs[OFF]) == 0)
 967                                         mode = PCIEHPC_PROP_VALUE_OFF;
 968                                 else if (strcmp(buf, mode_strs[BLINK]) == 0)
 969                                         mode = PCIEHPC_PROP_VALUE_BLINK;
 970                                 else return (CFGA_INVAL);
 971 
 972                                 /* sendin  */
 973                                 memset(buf, 0, sizeof (buf));
 974                                 snprintf(buf, sizeof (buf), "%s=%s",
 975                                     led, mode);
 976                                 buf[MAXNAMELEN - 1] = '\0';
 977 
 978                                 break;
 979                         } else if (ptr == '\0') {
 980                                 /* print mode */
 981                                 DBG(1, ("Print mode\n"));
 982                                 return (prt_led_mode(ap_id, repeat, errstring,
 983                                     msgp));
 984                         }
 985                 default:
 986                         DBG(1, ("default\n"));
 987                         errno = EINVAL;
 988                         return (CFGA_INVAL);
 989         }
 990 
 991         rv = physpath2node(ap_id, errstring, &node);
 992         if (rv != CFGA_OK)
 993                 return (rv);
 994 
 995         if (hp_set_private(node, buf, &result) != 0) {
 996                 hp_fini(node);
 997                 return (CFGA_ERROR);
 998         }
 999 
1000         hp_fini(node);
1001         return (CFGA_OK);
1002 }
1003 
1004 /*ARGSUSED*/
1005 cfga_err_t cfga_test(const char *ap_id, const char *options,
1006     struct cfga_msg *msgp, char **errstring, cfga_flags_t flags)
1007 {
1008         cfga_err_t rv;
1009         if (errstring != NULL)
1010                 *errstring = NULL;
1011 
1012         if ((rv = check_options(options)) != CFGA_OK) {
1013                 return (rv);
1014         }
1015 
1016         DBG(1, ("cfga_test:(%s)\n", ap_id));
1017         /* will need to implement pci CTRL command */
1018         return (CFGA_NOTSUPP);
1019 }
1020 
1021 /*
1022  * The slot-names property describes the external labeling of add-in slots.
1023  * This property is an encoded array, an integer followed by a list of
1024  * strings. The return value from di_prop_lookup_ints for slot-names is -1.
1025  * The expected return value should be the number of elements.
1026  * Di_prop_decode_common does not decode encoded data from software,
1027  * such as the solaris device tree, unlike from the prom.
1028  * Di_prop_decode_common takes the size of the encoded data and mods
1029  * it with the size of int. The size of the encoded data for slot-names is 9
1030  * and the size of int is 4, yielding a non zero result. A value of -1 is used
1031  * to indicate that the number of elements can not be determined.
1032  * Di_prop_decode_common can be modified to decode encoded data from the solaris
1033  * device tree.
1034  */
1035 static int
1036 fixup_slotname(int rval, int *intp, struct searcharg *slotarg)
1037 {
1038         if ((slotarg->slt_name_src == PROM_SLT_NAME) && (rval == -1)) {
1039                 return (DI_WALK_TERMINATE);
1040         } else {
1041                 int i;
1042                 char *tmptr = (char *)(intp+1);
1043                 DBG(1, ("slot-bitmask: %x \n", *intp));
1044 
1045                 rval = (rval -1) * 4;
1046 
1047                 for (i = 0; i <= slotarg->minor; i++) {
1048                         DBG(2, ("curr slot-name: %s \n", tmptr));
1049 
1050                         if (i >= MAXDEVS)
1051                                 return (DI_WALK_TERMINATE);
1052 
1053                         if ((*intp >> i) & 1) {
1054                                 /* assign tmptr */
1055                                 DBG(2, ("slot-name: %s \n", tmptr));
1056                                 if (i == slotarg->minor)
1057                                         (void) strcpy(slotarg->slotnames[i],
1058                                             tmptr);
1059                                 /* wind tmptr to next \0 */
1060                                 while (*tmptr != '\0') {
1061                                         tmptr++;
1062                                 }
1063                                 tmptr++;
1064                         } else {
1065                                 /* point at unknown string */
1066                                 if (i == slotarg->minor)
1067                                         (void) strcpy(slotarg->slotnames[i],
1068                                             "unknown");
1069                         }
1070                 }
1071         }
1072         return (DI_WALK_TERMINATE);
1073 }
1074 
1075 static int
1076 find_slotname(di_node_t din, di_minor_t dim, void *arg)
1077 {
1078         struct searcharg *slotarg = (struct searcharg *)arg;
1079         di_prom_handle_t ph = (di_prom_handle_t)slotarg->promp;
1080         di_prom_prop_t  prom_prop;
1081         di_prop_t       solaris_prop;
1082         int *intp, rval;
1083         char *devname;
1084         char fulldevname[MAXNAMELEN];
1085 
1086         slotarg->minor = dim->dev_minor % 256;
1087 
1088         DBG(2, ("minor number:(%i)\n", slotarg->minor));
1089         DBG(2, ("hot plug slots found so far:(%i)\n", 0));
1090 
1091         if ((devname = di_devfs_path(din)) != NULL) {
1092                 (void) snprintf(fulldevname, MAXNAMELEN,
1093                     "/devices%s:%s", devname, di_minor_name(dim));
1094                 di_devfs_path_free(devname);
1095         }
1096 
1097         if (strcmp(fulldevname, slotarg->devpath) == 0) {
1098 
1099                 /*
1100                  * Check the Solaris device tree first
1101                  * in the case of a DR operation
1102                  */
1103                 solaris_prop = di_prop_hw_next(din, DI_PROP_NIL);
1104                 while (solaris_prop != DI_PROP_NIL) {
1105                         if (strcmp("slot-names", di_prop_name(solaris_prop))
1106                             == 0) {
1107                                 rval = di_prop_lookup_ints(DDI_DEV_T_ANY,
1108                                     din, di_prop_name(solaris_prop), &intp);
1109                                 slotarg->slt_name_src = SOLARIS_SLT_NAME;
1110 
1111                                 return (fixup_slotname(rval, intp, slotarg));
1112                         }
1113                         solaris_prop = di_prop_hw_next(din, solaris_prop);
1114                 }
1115 
1116                 /*
1117                  * Check the prom device tree which is populated at boot.
1118                  * If this fails, give up and set the slot name to null.
1119                  */
1120                 prom_prop = di_prom_prop_next(ph, din, DI_PROM_PROP_NIL);
1121                 while (prom_prop != DI_PROM_PROP_NIL) {
1122                         if (strcmp("slot-names", di_prom_prop_name(prom_prop))
1123                             == 0) {
1124                                 rval = di_prom_prop_lookup_ints(ph,
1125                                     din, di_prom_prop_name(prom_prop), &intp);
1126                                 slotarg->slt_name_src = PROM_SLT_NAME;
1127 
1128                                 return (fixup_slotname(rval, intp, slotarg));
1129                         }
1130                         prom_prop = di_prom_prop_next(ph, din, prom_prop);
1131                 }
1132                 *slotarg->slotnames[slotarg->minor] = '\0';
1133                 return (DI_WALK_TERMINATE);
1134         } else
1135                 return (DI_WALK_CONTINUE);
1136 }
1137 
1138 static int
1139 find_physical_slot_names(const char *devcomp, struct searcharg *slotarg)
1140 {
1141         di_node_t root_node;
1142 
1143         DBG(1, ("find_physical_slot_names\n"));
1144 
1145         if ((root_node = di_init("/", DINFOCPYALL|DINFOPATH))
1146             == DI_NODE_NIL) {
1147                 DBG(1, ("di_init() failed\n"));
1148                 return (-1);
1149         }
1150 
1151         slotarg->devpath = (char *)devcomp;
1152 
1153         if ((slotarg->promp = di_prom_init()) == DI_PROM_HANDLE_NIL) {
1154                 DBG(1, ("di_prom_init() failed\n"));
1155                 di_fini(root_node);
1156                 return (-1);
1157         }
1158 
1159         (void) di_walk_minor(root_node, "ddi_ctl:attachment_point:pci",
1160             0, (void *)slotarg, find_slotname);
1161 
1162         di_prom_fini(slotarg->promp);
1163         di_fini(root_node);
1164         if (slotarg->slotnames[0] != NULL)
1165                 return (0);
1166         else
1167                 return (-1);
1168 }
1169 
1170 static void
1171 get_type(const char *boardtype, const char *cardtype, char *buf)
1172 {
1173 /* for type string assembly in get_type() */
1174 #define TPCT(s) (void) strlcat(buf, (s), CFGA_TYPE_LEN)
1175 
1176         int i;
1177 
1178         if (strcmp(cardtype, "unknown") == 0) {
1179                 TPCT("unknown");
1180                 return;
1181         }
1182 
1183         TPCT(cardtype);
1184         TPCT("/");
1185 
1186         if (strcmp(boardtype, PCIEHPC_PROP_VALUE_PCIHOTPLUG) == 0)
1187                 TPCT(board_strs[PCIEHPC_BOARD_PCI_HOTPLUG]);
1188         else
1189                 TPCT(board_strs[PCIEHPC_BOARD_UNKNOWN]);
1190 }
1191 
1192 /*
1193  * call-back function for di_devlink_walk
1194  * if the link lives in /dev/cfg copy its name
1195  */
1196 static int
1197 found_devlink(di_devlink_t link, void *ap_log_id)
1198 {
1199         if (strncmp("/dev/cfg/", di_devlink_path(link), 9) == 0) {
1200                 /* copy everything but /dev/cfg/ */
1201                 (void) strcpy((char *)ap_log_id, di_devlink_path(link) + 9);
1202                 DBG(1, ("found_devlink: %s\n", (char *)ap_log_id));
1203                 return (DI_WALK_TERMINATE);
1204         }
1205         return (DI_WALK_CONTINUE);
1206 }
1207 
1208 /*
1209  * Walk throught the cached /dev link tree looking for links to the ap
1210  * if none are found return an error
1211  */
1212 static cfga_err_t
1213 check_devlinks(char *ap_log_id, const char *ap_id)
1214 {
1215         di_devlink_handle_t hdl;
1216 
1217         DBG(1, ("check_devlinks: %s\n", ap_id));
1218 
1219         hdl = di_devlink_init(NULL, 0);
1220 
1221         if (strncmp("/devices/", ap_id, 9) == 0) {
1222                 /* ap_id is a valid minor_path with /devices prepended */
1223                 (void) di_devlink_walk(hdl, NULL, ap_id + 8, DI_PRIMARY_LINK,
1224                     (void *)ap_log_id, found_devlink);
1225         } else {
1226                 DBG(1, ("check_devlinks: invalid ap_id: %s\n", ap_id));
1227                 return (CFGA_ERROR);
1228         }
1229 
1230         (void) di_devlink_fini(&hdl);
1231 
1232         if (ap_log_id[0] != '\0')
1233                 return (CFGA_OK);
1234         else
1235                 return (CFGA_ERROR);
1236 }
1237 
1238 /*
1239  * most of this is needed to compensate for
1240  * differences between various platforms
1241  */
1242 static cfga_err_t
1243 fix_ap_name(char *ap_log_id, const char *ap_id, char *slot_name,
1244     char **errstring)
1245 {
1246         char *buf;
1247         char *tmp;
1248         char *ptr;
1249 
1250         di_node_t ap_node;
1251 
1252         ap_log_id[0] = '\0';
1253 
1254         if (check_devlinks(ap_log_id, ap_id) == CFGA_OK)
1255                 return (CFGA_OK);
1256 
1257         DBG(1, ("fix_ap_name: %s\n", ap_id));
1258 
1259         if ((buf = malloc(strlen(ap_id) + 1)) == NULL) {
1260                 DBG(1, ("malloc failed\n"));
1261                 return (CFGA_ERROR);
1262         }
1263         (void) strcpy(buf, ap_id);
1264         tmp = buf + sizeof ("/devices") - 1;
1265 
1266         ptr = strchr(tmp, ':');
1267         ptr[0] = '\0';
1268 
1269         DBG(1, ("fix_ap_name: %s\n", tmp));
1270 
1271         ap_node = di_init(tmp, DINFOMINOR);
1272         if (ap_node == DI_NODE_NIL) {
1273                 cfga_err(errstring, "di_init ", 0);
1274                 DBG(1, ("fix_ap_name: failed to snapshot node\n"));
1275                 return (CFGA_ERROR);
1276         }
1277 
1278         (void) snprintf(ap_log_id, strlen(ap_id) + 1, "%s%i:%s",
1279             di_driver_name(ap_node), di_instance(ap_node), slot_name);
1280 
1281         DBG(1, ("fix_ap_name: %s\n", ap_log_id));
1282 
1283         di_fini(ap_node);
1284 
1285         free(buf);
1286         return (CFGA_OK);
1287 }
1288 
1289 
1290 static int
1291 findlink_cb(di_devlink_t devlink, void *arg)
1292 {
1293         (*(char **)arg) = strdup(di_devlink_path(devlink));
1294 
1295         return (DI_WALK_TERMINATE);
1296 }
1297 
1298 /*
1299  * returns an allocated string containing the full path to the devlink for
1300  * <ap_phys_id> in the devlink database; we expect only one devlink per
1301  * <ap_phys_id> so we return the first encountered
1302  */
1303 static char *
1304 findlink(char *ap_phys_id)
1305 {
1306         di_devlink_handle_t hdl;
1307         char *path = NULL;
1308 
1309         hdl = di_devlink_init(NULL, 0);
1310 
1311         if (strncmp("/devices/", ap_phys_id, 9) == 0)
1312                 ap_phys_id += 8;
1313 
1314         (void) di_devlink_walk(hdl, "^cfg/.+$", ap_phys_id, DI_PRIMARY_LINK,
1315             (void *)&path, findlink_cb);
1316 
1317         (void) di_devlink_fini(&hdl);
1318         return (path);
1319 }
1320 
1321 
1322 /*
1323  * returns CFGA_OK if it can succesfully retrieve the devlink info associated
1324  * with devlink for <ap_phys_id> which will be returned through <ap_info>
1325  */
1326 cfga_err_t
1327 get_dli(char *dlpath, char *ap_info, int ap_info_sz)
1328 {
1329         int fd;
1330 
1331         fd = di_dli_openr(dlpath);
1332         if (fd < 0)
1333                 return (CFGA_ERROR);
1334 
1335         (void) read(fd, ap_info, ap_info_sz);
1336         ap_info[ap_info_sz - 1] = '\0';
1337 
1338         di_dli_close(fd);
1339         return (CFGA_OK);
1340 }
1341 
1342 static cfga_err_t
1343 cfga_get_condition(hp_node_t node, ap_condition_t *cond)
1344 {
1345         char *condition;
1346 
1347         /* "condition" bus specific commands */
1348         if (hp_get_private(node, PCIEHPC_PROP_SLOT_CONDITION,
1349             &condition) != 0) {
1350                 *cond = AP_COND_UNKNOWN;
1351                 return (CFGA_ERROR);
1352         }
1353 
1354         condition = get_val_from_result(condition);
1355 
1356         if (strcmp(condition, PCIEHPC_PROP_COND_OK) == 0)
1357                 *cond = AP_COND_OK;
1358         else if (strcmp(condition, PCIEHPC_PROP_COND_FAILING) == 0)
1359                 *cond = AP_COND_FAILING;
1360         else if (strcmp(condition, PCIEHPC_PROP_COND_FAILED) == 0)
1361                 *cond = AP_COND_FAILED;
1362         else if (strcmp(condition, PCIEHPC_PROP_COND_UNUSABLE) == 0)
1363                 *cond = AP_COND_UNUSABLE;
1364         else if (strcmp(condition, PCIEHPC_PROP_COND_UNKNOWN) == 0)
1365                 *cond = AP_COND_UNKNOWN;
1366         else
1367                 return (CFGA_ERROR);
1368 
1369         return (CFGA_OK);
1370 }
1371 
1372 /*ARGSUSED*/
1373 cfga_err_t
1374 cfga_list_ext(const char *ap_id, cfga_list_data_t **cs,
1375     int *nlist, const char *options, const char *listopts, char **errstring,
1376     cfga_flags_t flags)
1377 {
1378         char                    *boardtype;
1379         char                    *cardtype;
1380         struct  searcharg       slotname_arg;
1381         int                     fd;
1382         int                     rv = CFGA_OK;
1383         char                    *dlpath = NULL;
1384         hp_node_t               node;
1385         ap_rstate_t             rs;
1386         ap_ostate_t             os;
1387         ap_condition_t          cond;
1388 
1389         if ((rv = check_options(options)) != CFGA_OK) {
1390                 return (rv);
1391         }
1392 
1393         if (errstring != NULL)
1394                 *errstring = NULL;
1395 
1396         DBG(1, ("cfga_list_ext:(%s)\n", ap_id));
1397 
1398         if (cs == NULL || nlist == NULL) {
1399                 rv = CFGA_ERROR;
1400                 return (rv);
1401         }
1402 
1403         *nlist = 1;
1404 
1405         if ((*cs = malloc(sizeof (cfga_list_data_t))) == NULL) {
1406                 cfga_err(errstring, "malloc ", 0);
1407                 DBG(1, ("malloc failed\n"));
1408                 rv = CFGA_ERROR;
1409                 return (rv);
1410         }
1411         (void) memset(*cs, 0, sizeof (cfga_list_data_t));
1412 
1413         rv = physpath2node(ap_id, errstring, &node);
1414         if (rv != CFGA_OK) {
1415                 DBG(1, ("physpath2node failed\n"));
1416                 return (rv);
1417         }
1418 
1419         if (cfga_get_state(node, &rs, &os) != CFGA_OK) {
1420                 DBG(1, ("cfga_get_state failed\n"));
1421                 hp_fini(node);
1422                 return (CFGA_ERROR);
1423         }
1424 
1425         switch (rs) {
1426                 case AP_RSTATE_EMPTY:
1427                         (*cs)->ap_r_state = CFGA_STAT_EMPTY;
1428                         DBG(2, ("ap_rstate = CFGA_STAT_EMPTY\n"));
1429                         break;
1430                 case AP_RSTATE_DISCONNECTED:
1431                         (*cs)->ap_r_state = CFGA_STAT_DISCONNECTED;
1432                         DBG(2, ("ap_rstate = CFGA_STAT_DISCONNECTED\n"));
1433                         break;
1434                 case AP_RSTATE_CONNECTED:
1435                         (*cs)->ap_r_state = CFGA_STAT_CONNECTED;
1436                         DBG(2, ("ap_rstate = CFGA_STAT_CONNECTED\n"));
1437                         break;
1438         default:
1439                 cfga_err(errstring, CMD_GETSTAT, ap_id, 0);
1440                 rv = CFGA_ERROR;
1441                 hp_fini(node);
1442                 return (rv);
1443         }
1444 
1445         switch (os) {
1446                 case AP_OSTATE_CONFIGURED:
1447                         (*cs)->ap_o_state = CFGA_STAT_CONFIGURED;
1448                         DBG(2, ("ap_ostate = CFGA_STAT_CONFIGURED\n"));
1449                         break;
1450                 case AP_OSTATE_UNCONFIGURED:
1451                         (*cs)->ap_o_state = CFGA_STAT_UNCONFIGURED;
1452                         DBG(2, ("ap_ostate = CFGA_STAT_UNCONFIGURED\n"));
1453                         break;
1454         default:
1455                 cfga_err(errstring, CMD_GETSTAT, ap_id, 0);
1456                 rv = CFGA_ERROR;
1457                 hp_fini(node);
1458                 return (rv);
1459         }
1460 
1461         (void) cfga_get_condition(node, &cond);
1462 
1463         switch (cond) {
1464                 case AP_COND_OK:
1465                         (*cs)->ap_cond = CFGA_COND_OK;
1466                         DBG(2, ("ap_cond = CFGA_COND_OK\n"));
1467                         break;
1468                 case AP_COND_FAILING:
1469                         (*cs)->ap_cond = CFGA_COND_FAILING;
1470                         DBG(2, ("ap_cond = CFGA_COND_FAILING\n"));
1471                         break;
1472                 case AP_COND_FAILED:
1473                         (*cs)->ap_cond = CFGA_COND_FAILED;
1474                         DBG(2, ("ap_cond = CFGA_COND_FAILED\n"));
1475                         break;
1476                 case AP_COND_UNUSABLE:
1477                         (*cs)->ap_cond = CFGA_COND_UNUSABLE;
1478                         DBG(2, ("ap_cond = CFGA_COND_UNUSABLE\n"));
1479                         break;
1480                 case AP_COND_UNKNOWN:
1481                         (*cs)->ap_cond = CFGA_COND_UNKNOWN;
1482                         DBG(2, ("ap_cond = CFGA_COND_UNKNOW\n"));
1483                         break;
1484         default:
1485                 cfga_err(errstring, CMD_GETSTAT, ap_id, 0);
1486                 rv = CFGA_ERROR;
1487                 hp_fini(node);
1488                 return (rv);
1489         }
1490         /*
1491          * We're not busy since the entrance into the kernel has been
1492          * sync'ed via libhotplug.
1493          */
1494         (*cs)->ap_busy = 0;
1495 
1496         /* last change */
1497         (*cs)->ap_status_time = hp_last_change(node);
1498 
1499         /* board type */
1500         if (hp_get_private(node, PCIEHPC_PROP_BOARD_TYPE, &boardtype) != 0)
1501                 boardtype = PCIEHPC_PROP_VALUE_UNKNOWN;
1502         else
1503                 boardtype = get_val_from_result(boardtype);
1504 
1505         /* card type */
1506         if (hp_get_private(node, PCIEHPC_PROP_CARD_TYPE, &cardtype) != 0)
1507                 cardtype = PCIEHPC_PROP_VALUE_UNKNOWN;
1508         else
1509                 cardtype = get_val_from_result(cardtype);
1510 
1511         /* logical ap_id */
1512         rv = fix_ap_name((*cs)->ap_log_id, ap_id,
1513             hp_name(node), errstring);
1514         DBG(1, ("logical id: %s\n", (*cs)->ap_log_id));
1515         /* physical ap_id */
1516         (void) strcpy((*cs)->ap_phys_id, ap_id);    /* physical path of AP */
1517 
1518         /* information */
1519         dlpath = findlink((*cs)->ap_phys_id);
1520         if (dlpath != NULL) {
1521                 if (get_dli(dlpath, (*cs)->ap_info,
1522                     sizeof ((*cs)->ap_info)) != CFGA_OK)
1523                         (*cs)->ap_info[0] = '\0';
1524                 free(dlpath);
1525         }
1526 
1527         if ((*cs)->ap_log_id[0] == '\0')
1528                 (void) strcpy((*cs)->ap_log_id, hp_name(node));
1529 
1530         if ((*cs)->ap_info[0] == '\0') {
1531                 /* slot_names of bus node  */
1532                 if (find_physical_slot_names(ap_id, &slotname_arg) != -1)
1533                         (void) strcpy((*cs)->ap_info,
1534                             slotname_arg.slotnames[slotname_arg.minor]);
1535         }
1536 
1537         /* class_code/subclass/boardtype */
1538         get_type(boardtype, cardtype, (*cs)->ap_type);
1539 
1540         DBG(1, ("cfga_list_ext return success\n"));
1541         rv = CFGA_OK;
1542 
1543         hp_fini(node);
1544         return (rv);
1545 }
1546 
1547 /*
1548  * This routine prints a single line of help message
1549  */
1550 static void
1551 cfga_msg(struct cfga_msg *msgp, const char *str)
1552 {
1553         DBG(2, ("<%s>", str));
1554 
1555         if (msgp == NULL || msgp->message_routine == NULL)
1556                 return;
1557 
1558         (*msgp->message_routine)(msgp->appdata_ptr, str);
1559         (*msgp->message_routine)(msgp->appdata_ptr, "\n");
1560 }
1561 
1562 static cfga_err_t
1563 check_options(const char *options)
1564 {
1565         struct cfga_msg *msgp = NULL;
1566 
1567         if (options) {
1568                 cfga_msg(msgp, dgettext(TEXT_DOMAIN, cfga_strs[HELP_UNKNOWN]));
1569                 cfga_msg(msgp, options);
1570                 return (CFGA_INVAL);
1571         }
1572         return (CFGA_OK);
1573 }
1574 
1575 /*ARGSUSED*/
1576 cfga_err_t
1577 cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
1578 {
1579         if (options) {
1580                 cfga_msg(msgp, dgettext(TEXT_DOMAIN, cfga_strs[HELP_UNKNOWN]));
1581                 cfga_msg(msgp, options);
1582         }
1583         DBG(1, ("cfga_help\n"));
1584 
1585         cfga_msg(msgp, dgettext(TEXT_DOMAIN, cfga_strs[HELP_HEADER]));
1586         cfga_msg(msgp, cfga_strs[HELP_CONFIG]);
1587         cfga_msg(msgp, cfga_strs[HELP_ENABLE_SLOT]);
1588         cfga_msg(msgp, cfga_strs[HELP_DISABLE_SLOT]);
1589         cfga_msg(msgp, cfga_strs[HELP_ENABLE_AUTOCONF]);
1590         cfga_msg(msgp, cfga_strs[HELP_DISABLE_AUTOCONF]);
1591         cfga_msg(msgp, cfga_strs[HELP_LED_CNTRL]);
1592         return (CFGA_OK);
1593 }
1594 
1595 /*
1596  * cfga_err() accepts a variable number of message IDs and constructs
1597  * a corresponding error string which is returned via the errstring argument.
1598  * cfga_err() calls gettext() to internationalize proper messages.
1599  */
1600 static void
1601 cfga_err(char **errstring, ...)
1602 {
1603         int a;
1604         int i;
1605         int n;
1606         int len;
1607         int flen;
1608         char *p;
1609         char *q;
1610         char *s[32];
1611         char *failed;
1612         va_list ap;
1613 
1614         /*
1615          * If errstring is null it means user is not interested in getting
1616          * error status. So we don't do all the work
1617          */
1618         if (errstring == NULL) {
1619                 return;
1620         }
1621         va_start(ap, errstring);
1622 
1623         failed = dgettext(TEXT_DOMAIN, cfga_strs[FAILED]);
1624         flen = strlen(failed);
1625 
1626         for (n = len = 0; (a = va_arg(ap, int)) != 0; n++) {
1627                 switch (a) {
1628                 case CMD_GETSTAT:
1629                 case CMD_LIST:
1630                 case CMD_SLOT_CONNECT:
1631                 case CMD_SLOT_DISCONNECT:
1632                 case CMD_SLOT_CONFIGURE:
1633                 case CMD_SLOT_UNCONFIGURE:
1634                         p =  cfga_errstrs(a);
1635                         len += (strlen(p) + flen);
1636                         s[n] = p;
1637                         s[++n] = cfga_strs[FAILED];
1638 
1639                         DBG(2, ("<%s>", p));
1640                         DBG(2, (cfga_strs[FAILED]));
1641                         break;
1642 
1643                 case ERR_CMD_INVAL:
1644                 case ERR_AP_INVAL:
1645                 case ERR_OPT_INVAL:
1646                 case ERR_AP_ERR:
1647                         switch (a) {
1648                         case ERR_CMD_INVAL:
1649                                 p = dgettext(TEXT_DOMAIN,
1650                                     cfga_errstrs[ERR_CMD_INVAL]);
1651                                 break;
1652                         case ERR_AP_INVAL:
1653                                 p = dgettext(TEXT_DOMAIN,
1654                                     cfga_errstrs[ERR_AP_INVAL]);
1655                                 break;
1656                         case ERR_OPT_INVAL:
1657                                 p = dgettext(TEXT_DOMAIN,
1658                                     cfga_errstrs[ERR_OPT_INVAL]);
1659                                 break;
1660                         case ERR_AP_ERR:
1661                                 p = dgettext(TEXT_DOMAIN,
1662                                     cfga_errstrs[ERR_AP_ERR]);
1663                                 break;
1664                         }
1665 
1666                         if ((q = va_arg(ap, char *)) != NULL) {
1667                                 len += (strlen(p) + strlen(q));
1668                                 s[n] = p;
1669                                 s[++n] = q;
1670                                 DBG(2, ("<%s>", p));
1671                                 DBG(2, ("<%s>", q));
1672                                 break;
1673                         } else {
1674                                 len += strlen(p);
1675                                 s[n] = p;
1676 
1677                         }
1678                         DBG(2, ("<%s>", p));
1679                         break;
1680 
1681                 default:
1682                         n--;
1683                         break;
1684                 }
1685         }
1686 
1687         DBG(2, ("\n"));
1688         va_end(ap);
1689 
1690         if ((p = calloc(len + 1, 1)) == NULL)
1691                 return;
1692 
1693         for (i = 0; i < n; i++) {
1694                 (void) strlcat(p, s[i], len + 1);
1695                 DBG(2, ("i:%d, %s\n", i, s[i]));
1696         }
1697 
1698         *errstring = p;
1699         DBG(2, ("%s\n", *errstring));
1700 }
1701 
1702 /*
1703  * cfga_ap_id_cmp -- use default_ap_id_cmp() in libcfgadm
1704  */