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