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