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