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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 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#include <stddef.h>
30#include <locale.h>
31#include <ctype.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <fcntl.h>
36#include <unistd.h>
37#include <errno.h>
38#include <locale.h>
39#include <langinfo.h>
40#include <time.h>
41#include <stdarg.h>
42#include <sys/types.h>
43#include <sys/ioctl.h>
44#include <sys/dditypes.h>
45#include <sys/modctl.h>
46#include <sys/obpdefs.h>
47#include <sys/fhc.h>
48#include <sys/sysctrl.h>
49#include <sys/openpromio.h>
50#ifdef	SIM
51#include <sys/stat.h>
52#endif
53#define	CFGA_PLUGIN_LIB
54#include <config_admin.h>
55
56#ifdef	DEBUG
57#define	DBG	printf
58#define	DBG1	printf
59#define	DBG3	printf
60#define	DBG4	printf
61#else
62#define	DBG(a, b)
63#define	DBG1(a)
64#define	DBG3(a, b, c)
65#define	DBG4(a, b, c, d)
66#endif
67
68#define	BD_CPU			1
69#define	BD_MEM			2
70#define	BD_IO_2SBUS		3
71#define	BD_IO_SBUS_FFB		4
72#define	BD_IO_PCI		5
73#define	BD_DISK			6
74#define	BD_IO_2SBUS_SOCPLUS	7
75#define	BD_IO_SBUS_FFB_SOCPLUS	8
76#define	BD_UNKNOWN		9
77#define	CMD_GETSTAT		10
78#define	CMD_LIST		11
79#define	CMD_CONNECT		12
80#define	CMD_DISCONNECT		13
81#define	CMD_CONFIGURE		14
82#define	CMD_UNCONFIGURE		15
83#define	CMD_QUIESCE		16
84#define	CMD_INSERT		17
85#define	CMD_REMOVE		18
86#define	CMD_SET_COND		19
87#define	OPT_ENABLE		20
88#define	OPT_DISABLE		21
89#define	ERR_PROM_OPEN		22
90#define	ERR_PROM_GETPROP	23
91#define	ERR_PROM_SETPROP	24
92#define	ERR_TRANS		25
93#define	ERR_CMD_INVAL		26
94#define	ERR_OPT_INVAL		27
95#define	ERR_AP_INVAL		28
96#define	ERR_DISABLED		29
97#define	DIAG_FORCE		30
98#define	DIAG_TRANS_OK		31
99#define	DIAG_FAILED		32
100#define	DIAG_WAS_ENABLED	33
101#define	DIAG_WAS_DISABLED	34
102#define	DIAG_WILL_ENABLE	35
103#define	DIAG_WILL_DISABLE	36
104#define	HELP_HEADER		37
105#define	HELP_QUIESCE		38
106#define	HELP_INSERT		39
107#define	HELP_REMOVE		40
108#define	HELP_SET_COND		41
109#define	HELP_ENABLE		42
110#define	HELP_DISABLE		43
111#define	HELP_UNKNOWN		44
112#define	ASK_CONNECT		45
113#define	STR_BD			46
114#define	STR_COL			47
115#define	COND_UNKNOWN		48
116#define	COND_OK			49
117#define	COND_FAILING		50
118#define	COND_FAILED		51
119#define	COND_UNUSABLE		52
120#define	SYSC_COOLING		53
121#define	SYSC_POWER		54
122#define	SYSC_PRECHARGE		55
123#define	SYSC_INTRANS		56
124#define	SYSC_UTHREAD		57
125#define	SYSC_KTHREAD		58
126#define	SYSC_DEV_ATTACH		59
127#define	SYSC_DEV_DETACH		60
128#define	SYSC_NDI_ATTACH		61
129#define	SYSC_NDI_DETACH		62
130#define	SYSC_CORE_RESOURCE	63
131#define	SYSC_OSTATE		64
132#define	SYSC_RSTATE		65
133#define	SYSC_COND		66
134#define	SYSC_PROM		67
135#define	SYSC_NOMEM		68
136#define	SYSC_HOTPLUG		69
137#define	SYSC_HW_COMPAT		70
138#define	SYSC_NON_DR_PROM	71
139#define	SYSC_SUSPEND		72
140#define	SYSC_RESUME		73
141#define	SYSC_UNKNOWN		74
142#define	SYSC_DEVSTR		75
143
144/*
145 * The string table contains all the strings used by the platform
146 * library.  The comment next to each string specifies whether the
147 * string should be internationalized (y) or not (n).
148 * Note that there are calls to dgettext() with strings other than
149 * the ones below, they are marked by the li18 symbol.
150 */
151static char *
152cfga_strs[] = {
153	/*   */ NULL,
154	/* n */ "cpu/mem   ",
155	/* n */ "mem       ",
156	/* n */ "dual-sbus ",
157	/* n */ "sbus-upa  ",
158	/* n */ "dual-pci  ",
159	/* n */ "disk      ",
160	/* n */ "soc+sbus  ",
161	/* n */ "soc+upa   ",
162	/* n */ "unknown   ",
163	/* n */ "get-status",
164	/* n */ "list",
165	/* n */ "connect",
166	/* n */ "disconnect",
167	/* n */ "configure",
168	/* n */ "unconfigure",
169	/* n */ "quiesce-test",
170	/* n */ "insert-test",
171	/* n */ "remove-test",
172	/* n */ "set-condition-test",
173	/* n */ "enable-at-boot",
174	/* n */ "disable-at-boot",
175	/* n */ "prom open",
176	/* n */ "prom getprop",
177	/* n */ "prom setprop",
178	/* y */ "invalid transition",
179	/* y */ "invalid command: ",
180	/* y */ "invalid option: ",
181	/* y */ "invalid attachment point: ",
182	/* y */ "board is disabled: must override with ",
183	/* n */ "[-f][-o enable-at-boot]",
184	/* y */ "transition succeeded but ",
185	/* y */ " failed: ",
186	/* y */ "was already enabled at boot time",
187	/* y */ "was already disabled at boot time",
188	/* y */ "will be enabled at boot time",
189	/* y */ "will be disabled at boot time",
190	/* y */ "\nSysctrl specific commands/options:",
191	/* n */ "\t-x quiesce-test ap_id [ap_id...]",
192	/* n */ "\t-x insert-test  ap_id [ap_id...]",
193	/* n */ "\t-x remove-test  ap_id [ap_id...]",
194	/* n */ "\t-x set-condition-test=<condition>",
195	/* n */ "\t-o enable-at-boot",
196	/* n */ "\t-o disable-at-boot",
197	/* y */ "\tunknown command or option: ",
198	/* y */
199	"system will be temporarily suspended to connect a board: proceed",
200	/* y */ "board ",
201	/* y */ ": ",
202	/* n */ "unknown",
203	/* n */ "ok",
204	/* n */ "failing",
205	/* n */ "failed",
206	/* n */ "unusable",
207	/* y */ "not enough cooling for a new board",
208	/* y */ "not enough power for a new board",
209	/* y */ "not enough precharge power for a new board",
210	/* y */ "configuration operation already in progress on this board",
211	/* y */ "could not suspend user process: ",
212	/* y */ "could not suspend system processes",
213	/* y */ "device did not attach",
214	/* y */ "device did not detach",
215	/* y */ "nexus error during attach",
216	/* y */ "nexus error during detach",
217	/* y */ "attempt to remove core system resource",
218	/* y */ "invalid occupant state",
219	/* y */ "invalid receptacle state",
220	/* y */ "insufficient condition",
221	/* y */ "firmware operation error",
222	/* y */ "not enough memory",
223	/* y */ "hotplug feature unavailable on this machine",
224	/* y */ "board does not support dynamic reconfiguration",
225	/* y */ "firmware does not support dynamic reconfiguration",
226	/* y */ "system suspend error",
227	/* y */ "system resume error",
228	/* y */ "unknown system error",
229	/*   */ NULL
230};
231
232#define	cfga_str(i)		cfga_strs[(i)]
233
234#define	cfga_eid(a, b)		(((a) << 8) + (b))
235
236/*
237 *
238 *	Translation table for mapping from an <errno,sysc_err>
239 *	pair to an error string.
240 *
241 *
242 *	SYSC_COOLING,		EAGAIN,  SYSC_ERR_COOLING
243 *	SYSC_POWER,		EAGAIN,  SYSC_ERR_POWER
244 *	SYSC_PRECHARGE,		EAGAIN,  SYSC_ERR_PRECHARGE
245 *	SYSC_INTRANS,		EBUSY,   SYSC_ERR_INTRANS
246 *	SYSC_KTHREAD,		EBUSY,   SYSC_ERR_KTHREAD
247 *	SYSC_DEV_ATTACH,	EBUSY,   SYSC_ERR_NDI_ATTACH
248 *	SYSC_DEV_DETACH,	EBUSY,   SYSC_ERR_NDI_DETACH
249 *	SYSC_NDI_ATTACH,	EFAULT,  SYSC_ERR_NDI_ATTACH
250 *	SYSC_NDI_DETACH,	EFAULT,  SYSC_ERR_NDI_DETACH
251 *	SYSC_CORE_RESOURCE,	EINVAL,  SYSC_ERR_CORE_RESOURCE
252 *	SYSC_OSTATE,		EINVAL,  SYSC_ERR_OSTATE
253 *	SYSC_RSTATE,		EINVAL,  SYSC_ERR_RSTATE
254 *	SYSC_COND,		EINVAL,  SYSC_ERR_COND
255 *	SYSC_PROM,		EIO,     SYSC_ERR_PROM
256 *	SYSC_NOMEM,		ENOMEM,  SYSC_ERR_DR_INIT
257 *	SYSC_NOMEM,		ENOMEM,  SYSC_ERR_NDI_ATTACH
258 *	SYSC_NOMEM,		ENOMEM,  SYSC_ERR_NDI_DETACH
259 *	SYSC_HOTPLUG,		ENOTSUP, SYSC_ERR_HOTPLUG
260 *	SYSC_HW_COMPAT,		ENOTSUP, SYSC_ERR_HW_COMPAT
261 *	SYSC_NON_DR_PROM,	ENOTSUP, SYSC_ERR_NON_DR_PROM
262 *	SYSC_SUSPEND,		ENXIO,   SYSC_ERR_SUSPEND
263 *	SYSC_RESUME,		ENXIO,   SYSC_ERR_RESUME
264 *	SYSC_UTHREAD,		ESRCH,   SYSC_ERR_UTHREAD
265 */
266static int
267cfga_sid(int err, int scerr)
268{
269	if (scerr == SYSC_ERR_DEFAULT)
270		return (SYSC_UNKNOWN);
271
272	switch (cfga_eid(err, scerr)) {
273	case cfga_eid(EAGAIN, SYSC_ERR_COOLING):
274		return (SYSC_COOLING);
275	case cfga_eid(EAGAIN, SYSC_ERR_POWER):
276		return (SYSC_POWER);
277	case cfga_eid(EAGAIN, SYSC_ERR_PRECHARGE):
278		return (SYSC_PRECHARGE);
279	case cfga_eid(EBUSY, SYSC_ERR_INTRANS):
280		return (SYSC_INTRANS);
281	case cfga_eid(EBUSY, SYSC_ERR_KTHREAD):
282		return (SYSC_KTHREAD);
283	case cfga_eid(EBUSY, SYSC_ERR_NDI_ATTACH):
284		return (SYSC_DEV_ATTACH);
285	case cfga_eid(EBUSY, SYSC_ERR_NDI_DETACH):
286		return (SYSC_DEV_DETACH);
287	case cfga_eid(EFAULT, SYSC_ERR_NDI_ATTACH):
288		return (SYSC_NDI_ATTACH);
289	case cfga_eid(EFAULT, SYSC_ERR_NDI_DETACH):
290		return (SYSC_NDI_DETACH);
291	case cfga_eid(EINVAL, SYSC_ERR_CORE_RESOURCE):
292		return (SYSC_CORE_RESOURCE);
293	case cfga_eid(EINVAL, SYSC_ERR_OSTATE):
294		return (SYSC_OSTATE);
295	case cfga_eid(EINVAL, SYSC_ERR_RSTATE):
296		return (SYSC_RSTATE);
297	case cfga_eid(EINVAL, SYSC_ERR_COND):
298		return (SYSC_COND);
299	case cfga_eid(EIO, SYSC_ERR_PROM):
300		return (SYSC_PROM);
301	case cfga_eid(ENOMEM, SYSC_ERR_DR_INIT):
302		return (SYSC_NOMEM);
303	case cfga_eid(ENOMEM, SYSC_ERR_NDI_ATTACH):
304		return (SYSC_NOMEM);
305	case cfga_eid(ENOMEM, SYSC_ERR_NDI_DETACH):
306		return (SYSC_NOMEM);
307	case cfga_eid(ENOTSUP, SYSC_ERR_HOTPLUG):
308		return (SYSC_HOTPLUG);
309	case cfga_eid(ENOTSUP, SYSC_ERR_HW_COMPAT):
310		return (SYSC_HW_COMPAT);
311	case cfga_eid(ENOTSUP, SYSC_ERR_NON_DR_PROM):
312		return (SYSC_NON_DR_PROM);
313	case cfga_eid(ENXIO, SYSC_ERR_SUSPEND):
314		return (SYSC_SUSPEND);
315	case cfga_eid(ENXIO, SYSC_ERR_RESUME):
316		return (SYSC_RESUME);
317	case cfga_eid(ESRCH, SYSC_ERR_UTHREAD):
318		return (SYSC_UTHREAD);
319	default:
320		break;
321	}
322
323	return (SYSC_UNKNOWN);
324}
325
326static void
327sysc_cmd_init(sysc_cfga_cmd_t *sc, char *outputstr, int force)
328{
329	sc->force = force;
330	sc->outputstr = outputstr;
331	sc->errtype = SYSC_ERR_DEFAULT;
332
333	(void) memset((void *)outputstr, 0, sizeof (outputstr));
334
335	cfga_str(SYSC_DEVSTR) = outputstr;
336}
337
338/*
339 * cfga_err() accepts a variable number of message IDs and constructs
340 * a corresponding error string which is returned via the errstring argument.
341 * cfga_err() calls dgettext() to internationalize proper messages.
342 */
343static void
344cfga_err(sysc_cfga_cmd_t *sc, char **errstring, ...)
345{
346	int a;
347	int i;
348	int n;
349	int len;
350	int flen;
351	char *p;
352	char *q;
353	char *s[32];
354	char *failed;
355	va_list ap;
356	char syserr_num[20];
357
358	/*
359	 * If errstring is null it means user in not interested in getting
360	 * error status. So we don't do all the work
361	 */
362	if (errstring == NULL) {
363		return;
364	}
365	va_start(ap, errstring);
366
367	failed = dgettext(TEXT_DOMAIN, cfga_str(DIAG_FAILED));
368	flen = strlen(failed);
369
370	for (n = len = 0; (a = va_arg(ap, int)) != 0; n++) {
371
372		switch (a) {
373		case ERR_PROM_OPEN:
374		case ERR_PROM_GETPROP:
375		case ERR_PROM_SETPROP:
376		case CMD_GETSTAT:
377		case CMD_LIST:
378		case CMD_CONNECT:
379		case CMD_DISCONNECT:
380		case CMD_CONFIGURE:
381		case CMD_UNCONFIGURE:
382		case CMD_QUIESCE:
383		case CMD_INSERT:
384		case CMD_REMOVE:
385		case CMD_SET_COND:
386			p =  cfga_str(a);
387			len += (strlen(p) + flen);
388			s[n] = p;
389			s[++n] = failed;
390
391			DBG("<%s>", p);
392			DBG("<%s>", failed);
393			break;
394
395		case OPT_ENABLE:
396		case OPT_DISABLE:
397			p = dgettext(TEXT_DOMAIN, cfga_str(DIAG_TRANS_OK));
398			q = cfga_str(a);
399			len += (strlen(p) + strlen(q) + flen);
400			s[n] = p;
401			s[++n] = q;
402			s[++n] = failed;
403
404			DBG("<%s>", p);
405			DBG("<%s>", q);
406			DBG("<%s>", failed);
407			break;
408
409		case ERR_CMD_INVAL:
410		case ERR_AP_INVAL:
411		case ERR_OPT_INVAL:
412			p =  dgettext(TEXT_DOMAIN, cfga_str(a));
413			q = va_arg(ap, char *);
414			len += (strlen(p) + strlen(q));
415			s[n] = p;
416			s[++n] = q;
417
418			DBG("<%s>", p);
419			DBG("<%s>", q);
420			break;
421
422		case ERR_TRANS:
423		case ERR_DISABLED:
424			p =  dgettext(TEXT_DOMAIN, cfga_str(a));
425			len += strlen(p);
426			s[n] = p;
427
428			DBG("<%s>", p);
429			break;
430
431		case DIAG_FORCE:
432		default:
433			p =  cfga_str(a);
434			len += strlen(p);
435			s[n] = p;
436
437			DBG("<%s>", p);
438			break;
439		}
440	}
441
442	DBG1("\n");
443	va_end(ap);
444
445	if (errno) {
446		if (sc)
447			i = cfga_sid(errno, (int)sc->errtype);
448		else
449			i = SYSC_UNKNOWN;
450
451		DBG4("cfga_sid(%d,%d)=%d\n", errno, sc->errtype, i);
452
453		if (i == SYSC_UNKNOWN) {
454			p = strerror(errno);
455			if (p == NULL) {
456				(void) sprintf(syserr_num, "errno=%d", errno);
457				p = syserr_num;
458			}
459		} else
460			p = dgettext(TEXT_DOMAIN, cfga_str(i));
461
462		len += strlen(p);
463		s[n++] = p;
464		p = cfga_str(SYSC_DEVSTR);
465		if (p && p[0]) {
466			q = cfga_str(STR_COL);
467
468			len += strlen(q);
469			s[n++] = q;
470			len += strlen(p);
471			s[n++] = p;
472		}
473	}
474
475	if ((p = (char *)calloc(len, 1)) == NULL)
476		return;
477
478	for (i = 0; i < n; i++)
479		(void) strcat(p, s[i]);
480
481	*errstring = p;
482#ifdef	SIM_MSG
483	printf("%s\n", *errstring);
484#endif
485}
486
487/*
488 * This routine accepts a variable number of message IDs and constructs
489 * a corresponding error string which is printed via the message print routine
490 * argument.  The HELP_UNKNOWN message ID has an argument string (the unknown
491 * help topic) that follows.
492 */
493static void
494cfga_msg(struct cfga_msg *msgp, ...)
495{
496	int a;
497	int i;
498	int n;
499	int len;
500	char *p;
501	char *s[32];
502	va_list ap;
503
504	va_start(ap, msgp);
505
506	for (n = len = 0; (a = va_arg(ap, int)) != 0; n++) {
507		DBG("<%d>", a);
508		p =  dgettext(TEXT_DOMAIN, cfga_str(a));
509		len += strlen(p);
510		s[n] = p;
511		if (a == HELP_UNKNOWN) {
512			p = va_arg(ap, char *);
513			len += strlen(p);
514			s[++n] = p;
515		}
516	}
517
518	va_end(ap);
519
520	if ((p = (char *)calloc(len + 1, 1)) == NULL)
521		return;
522
523	for (i = 0; i < n; i++)
524		(void) strcat(p, s[i]);
525	(void) strcat(p, "\n");
526
527#ifdef	SIM_MSG
528	printf("%s", p);
529#else
530	(*msgp->message_routine)(msgp->appdata_ptr, p);
531#endif
532	free(p);
533}
534
535static sysc_cfga_stat_t *
536sysc_stat(const char *ap_id, int *fdp)
537{
538	int fd;
539	static sysc_cfga_stat_t sc_list[MAX_BOARDS];
540
541
542	if ((fd = open(ap_id, O_RDWR, 0)) == -1)
543		return (NULL);
544	else if (ioctl(fd, SYSC_CFGA_CMD_GETSTATUS, sc_list) == -1) {
545		(void) close(fd);
546		return (NULL);
547	} else if (fdp)
548		*fdp = fd;
549	else
550		(void) close(fd);
551
552	return (sc_list);
553}
554
555/*
556 * This code implementes the simulation of the ioctls that transition state.
557 * The GETSTAT ioctl is not simulated.  In this way a snapshot of the system
558 * state is read and manipulated by the simulation routines.  It is basically
559 * a useful debugging tool.
560 */
561#ifdef	SIM
562static int sim_idx;
563static int sim_fd = -1;
564static int sim_size = MAX_BOARDS * sizeof (sysc_cfga_stat_t);
565static sysc_cfga_stat_t sim_sc_list[MAX_BOARDS];
566
567static sysc_cfga_stat_t *
568sim_sysc_stat(const char *ap_id, int *fdp)
569{
570	int fd;
571	struct stat buf;
572
573	if (sim_fd != -1)
574		return (sim_sc_list);
575
576	if ((sim_fd = open("/tmp/cfga_simdata", O_RDWR|O_CREAT)) == -1) {
577		perror("sim_open");
578		exit(1);
579	} else if (fstat(sim_fd, &buf) == -1) {
580		perror("sim_stat");
581		exit(1);
582	}
583
584	if (buf.st_size) {
585		if (buf.st_size != sim_size) {
586			perror("sim_size");
587			exit(1);
588		} else if (read(sim_fd, sim_sc_list, sim_size) == -1) {
589			perror("sim_read");
590			exit(1);
591		}
592	} else if ((fd = open(ap_id, O_RDWR, 0)) == -1)
593		return (NULL);
594	else if (ioctl(fd, SYSC_CFGA_CMD_GETSTATUS, sim_sc_list) == -1) {
595		(void) close(fd);
596		return (NULL);
597	} else if (fdp)
598		*fdp = fd;
599
600	return (sim_sc_list);
601}
602
603static int
604sim_open(char *a, int b, int c)
605{
606	printf("sim_open(%s)\n", a);
607
608	if (strcmp(a, "/dev/openprom") == 0)
609		return (open(a, b, c));
610	return (0);
611}
612
613static int
614sim_close(int a) { return (0); }
615
616static int
617sim_ioctl(int fd, int cmd, void *a)
618{
619	printf("sim_ioctl(%d)\n", sim_idx);
620
621	switch (cmd) {
622	case SYSC_CFGA_CMD_CONNECT:
623		sim_sc_list[sim_idx].rstate = SYSC_CFGA_RSTATE_CONNECTED;
624		break;
625	case SYSC_CFGA_CMD_CONFIGURE:
626		sim_sc_list[sim_idx].ostate = SYSC_CFGA_OSTATE_CONFIGURED;
627		break;
628	case SYSC_CFGA_CMD_UNCONFIGURE:
629		sim_sc_list[sim_idx].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
630		break;
631	case SYSC_CFGA_CMD_DISCONNECT:
632		sim_sc_list[sim_idx].rstate = SYSC_CFGA_RSTATE_DISCONNECTED;
633		break;
634	case SYSC_CFGA_CMD_QUIESCE_TEST:
635	case SYSC_CFGA_CMD_TEST:
636		return (0);
637	case OPROMGETOPT:
638		return (ioctl(fd, OPROMGETOPT, a));
639	case OPROMSETOPT:
640		return (ioctl(fd, OPROMSETOPT, a));
641	}
642
643	if (lseek(sim_fd, SEEK_SET, 0) == -1) {
644		perror("sim_seek");
645		exit(1);
646	}
647	if (write(sim_fd, sim_sc_list, sim_size) == -1) {
648		perror("sim_write");
649		exit(1);
650	}
651
652	return (0);
653}
654
655#define	open(a, b, c)	sim_open((char *)(a), (int)(b), (int)(c))
656#define	close(a)	sim_close(a)
657#define	ioctl(a, b, c)	sim_ioctl((int)(a), (int)(b), (void *)(c))
658#define	sysc_stat(a, b)	sim_sysc_stat(a, b)
659#endif	/* SIM */
660
661static char *promdev = "/dev/openprom";
662static char *dlprop = "disabled-board-list";
663
664#define	BUFSIZE		128
665
666typedef union {
667	char buf[BUFSIZE];
668	struct openpromio opp;
669} oppbuf_t;
670
671static int
672prom_get_prop(int prom_fd, char *var, char **val)
673{
674	static oppbuf_t oppbuf;
675	struct openpromio *opp = &(oppbuf.opp);
676
677	(void) strncpy(opp->oprom_array, var, OBP_MAXPROPNAME);
678	opp->oprom_array[OBP_MAXPROPNAME + 1] = '\0';
679	opp->oprom_size = BUFSIZE;
680
681	DBG3("getprop(%s, %d)\n", opp->oprom_array, opp->oprom_size);
682
683	if (ioctl(prom_fd, OPROMGETOPT, opp) < 0)
684		return (ERR_PROM_GETPROP);
685	else if (opp->oprom_size > 0)
686		*val = opp->oprom_array;
687	else
688		*val = NULL;
689
690	return (0);
691}
692
693static cfga_err_t
694prom_set_prop(int prom_fd, char *var, char *val)
695{
696	oppbuf_t oppbuf;
697	struct openpromio *opp = &(oppbuf.opp);
698	int varlen = strlen(var) + 1;
699	int vallen = strlen(val);
700
701	DBG("prom_set_prop(%s)\n", val);
702
703	(void) strcpy(opp->oprom_array, var);
704	(void) strcpy(opp->oprom_array + varlen, val);
705	opp->oprom_size = varlen + vallen;
706
707	if (ioctl(prom_fd, OPROMSETOPT, opp) < 0)
708		return (ERR_PROM_SETPROP);
709
710	return (0);
711}
712
713static int
714dlist_find(int board, char **dlist, int *disabled)
715{
716	int i;
717	int err;
718	int prom_fd;
719	char *p;
720	char *dl;
721	char b[2];
722
723	if ((prom_fd = open(promdev, O_RDWR, 0)) < 0)
724		return (ERR_PROM_OPEN);
725	else if (err = prom_get_prop(prom_fd, dlprop, dlist)) {
726		(void) close(prom_fd);
727		return (err);
728	} else
729		(void) close(prom_fd);
730
731	b[1] = 0;
732	*disabled = 0;
733
734	if ((dl = *dlist) != NULL) {
735		int len = strlen(dl);
736
737		for (i = 0; i < len; i++) {
738			int bd;
739
740			b[0] = dl[i];
741			bd = strtol(b, &p, 16);
742
743			if (p != b && bd == board)
744				(*disabled)++;
745		}
746	}
747
748	return (0);
749}
750
751static int
752dlist_update(int board, int disable, char *dlist, struct cfga_msg *msgp,
753	int verbose)
754{
755	int i, j, n;
756	int err;
757	int found;
758	int update;
759	int prom_fd;
760	char *p;
761	char b[2];
762	char ndlist[64];
763
764	b[1] = 0;
765	ndlist[0] = 0;
766	j = 0;
767	found = 0;
768	update = 0;
769
770	if ((prom_fd = open(promdev, O_RDWR, 0)) < 0)
771		return (ERR_PROM_OPEN);
772
773	if (dlist) {
774		int len = strlen(dlist);
775
776		for (i = 0; i < len; i++) {
777			int bd;
778
779			b[0] = dlist[i];
780			bd = strtol(b, &p, 16);
781
782			if (p != b && bd == board) {
783
784				found++;
785				if (disable) {
786					if (verbose)
787						cfga_msg(msgp, STR_BD,
788						    DIAG_WAS_DISABLED, 0);
789				} else {
790					if (verbose)
791						cfga_msg(msgp, STR_BD,
792						    DIAG_WILL_ENABLE, 0);
793					update++;
794					continue;
795				}
796			}
797			ndlist[j++] = dlist[i];
798		}
799		ndlist[j] = 0;
800	}
801
802	if (!found)
803		if (disable) {
804			if (verbose)
805				cfga_msg(msgp, STR_BD, DIAG_WILL_DISABLE, 0);
806			p = &ndlist[j];
807			n = sprintf(p, "%x", board);
808			p[n] = 0;
809			update++;
810		} else {
811			if (verbose)
812				cfga_msg(msgp, STR_BD, DIAG_WAS_ENABLED, 0);
813		}
814
815	if (update)
816		err = prom_set_prop(prom_fd, dlprop, ndlist);
817	else
818		err = 0;
819
820	(void) close(prom_fd);
821
822	return (err);
823}
824
825static int
826ap_idx(const char *ap_id)
827{
828	int id;
829	char *s;
830	static char *slot = "slot";
831
832	DBG("ap_idx(%s)\n", ap_id);
833
834	if ((s = strstr(ap_id, slot)) == NULL)
835		return (-1);
836	else {
837		int n;
838
839		s += strlen(slot);
840		n = strlen(s);
841
842		DBG3("ap_idx: s=%s, n=%d\n", s, n);
843
844		switch (n) {
845		case 2:
846			if (!isdigit(s[1]))
847				return (-1);
848		/* FALLTHROUGH */
849		case 1:
850			if (!isdigit(s[0]))
851				return (-1);
852			break;
853		default:
854			return (-1);
855		}
856	}
857
858	if ((id = atoi(s)) > MAX_BOARDS)
859		return (-1);
860
861	DBG3("ap_idx(%s)=%d\n", s, id);
862
863	return (id);
864}
865
866/*ARGSUSED*/
867cfga_err_t
868cfga_change_state(
869	cfga_cmd_t state_change_cmd,
870	const char *ap_id,
871	const char *options,
872	struct cfga_confirm *confp,
873	struct cfga_msg *msgp,
874	char **errstring,
875	cfga_flags_t flags)
876{
877	int fd;
878	int idx;
879	int err;
880	int force;
881	int verbose;
882	int opterr;
883	int disable;
884	int disabled;
885	cfga_err_t rc;
886	sysc_cfga_stat_t *ss;
887	sysc_cfga_cmd_t *sc, sysc_cmd;
888	sysc_cfga_rstate_t rs;
889	sysc_cfga_ostate_t os;
890	char *dlist;
891	char outputstr[SYSC_OUTPUT_LEN];
892
893	if (errstring != NULL)
894		*errstring = NULL;
895
896	rc = CFGA_ERROR;
897
898	if (options) {
899		disable = 0;
900		if (strcmp(options, cfga_str(OPT_DISABLE)) == 0)
901			disable++;
902		else if (strcmp(options, cfga_str(OPT_ENABLE))) {
903			cfga_err(NULL, errstring, ERR_OPT_INVAL, options, 0);
904			return (rc);
905		}
906	}
907
908	if ((idx = ap_idx(ap_id)) == -1) {
909		cfga_err(NULL, errstring, ERR_AP_INVAL, ap_id, 0);
910		return (rc);
911	} else if ((ss = sysc_stat(ap_id, &fd)) == NULL) {
912		cfga_err(NULL, errstring, CMD_GETSTAT, 0);
913		return (rc);
914	}
915#ifdef	SIM
916	sim_idx = idx;
917#endif
918	/*
919	 * We disallow connecting on the disabled list unless
920	 * either the FORCE flag or the enable-at-boot option
921	 * is set. The check is made further below
922	 */
923	if (opterr = dlist_find(idx, &dlist, &disabled)) {
924		err = disable ? OPT_DISABLE : OPT_ENABLE;
925		cfga_err(NULL, errstring, err, opterr, 0);
926		(void) close(fd);
927		return (rc);
928	} else
929		force = flags & CFGA_FLAG_FORCE;
930
931	rs = ss[idx].rstate;
932	os = ss[idx].ostate;
933
934	sc = &sysc_cmd;
935	sysc_cmd_init(sc, outputstr, force);
936	verbose = flags & CFGA_FLAG_VERBOSE;
937
938	switch (state_change_cmd) {
939	case CFGA_CMD_CONNECT:
940		if (rs != SYSC_CFGA_RSTATE_DISCONNECTED)
941			cfga_err(NULL, errstring, ERR_TRANS, 0);
942		else if (disabled && !(force || (options && !disable)))
943			cfga_err(NULL, errstring, CMD_CONNECT,
944				ERR_DISABLED, DIAG_FORCE, 0);
945		else if (!(*confp->confirm)(confp->appdata_ptr,
946		    cfga_str(ASK_CONNECT))) {
947			(void) close(fd);
948			return (CFGA_NACK);
949		} else if (ioctl(fd, SYSC_CFGA_CMD_CONNECT, sc) == -1)
950			cfga_err(sc, errstring, CMD_CONNECT, 0);
951		else if (options && (opterr = dlist_update(idx, disable,
952			dlist, msgp, verbose))) {
953			err = disable ? OPT_DISABLE : OPT_ENABLE;
954			cfga_err(NULL, errstring, err, opterr, 0);
955		} else
956			rc = CFGA_OK;
957		break;
958
959	case CFGA_CMD_DISCONNECT:
960		if ((os == SYSC_CFGA_OSTATE_CONFIGURED) &&
961		    (ioctl(fd, SYSC_CFGA_CMD_UNCONFIGURE, sc) == -1)) {
962			cfga_err(sc, errstring, CMD_UNCONFIGURE, 0);
963			(void) close(fd);
964			return (CFGA_ERROR);
965		} else
966			sysc_cmd_init(sc, outputstr, force);
967
968		if (rs == SYSC_CFGA_RSTATE_CONNECTED) {
969			if (ioctl(fd, SYSC_CFGA_CMD_DISCONNECT, sc) == -1)
970				cfga_err(sc, errstring, CMD_DISCONNECT, 0);
971			else if (options && (opterr = dlist_update(idx, disable,
972			    dlist, msgp, verbose))) {
973				err = disable ? OPT_DISABLE : OPT_ENABLE;
974				cfga_err(NULL, errstring, err, opterr, 0);
975			} else
976				rc = CFGA_OK;
977		} else
978			cfga_err(NULL, errstring, ERR_TRANS, 0);
979		break;
980
981	case CFGA_CMD_CONFIGURE:
982		if (rs == SYSC_CFGA_RSTATE_DISCONNECTED)
983			if (disabled && !(force || (options && !disable))) {
984				cfga_err(NULL, errstring, CMD_CONFIGURE,
985					ERR_DISABLED, DIAG_FORCE, 0);
986				(void) close(fd);
987				return (CFGA_ERROR);
988			} else if (!(*confp->confirm)(confp->appdata_ptr,
989			    cfga_str(ASK_CONNECT))) {
990				(void) close(fd);
991				return (CFGA_NACK);
992			} else if (ioctl(fd, SYSC_CFGA_CMD_CONNECT, sc) == -1) {
993				cfga_err(sc, errstring, CMD_CONNECT, 0);
994				(void) close(fd);
995				return (CFGA_ERROR);
996			} else
997				sysc_cmd_init(sc, outputstr, force);
998
999		if (os == SYSC_CFGA_OSTATE_UNCONFIGURED) {
1000			if (ioctl(fd, SYSC_CFGA_CMD_CONFIGURE, sc) == -1)
1001				cfga_err(sc, errstring, CMD_CONFIGURE, 0);
1002			else if (options && (opterr = dlist_update(idx,
1003				disable, dlist, msgp, verbose))) {
1004				err = disable ? OPT_DISABLE : OPT_ENABLE;
1005				cfga_err(NULL, errstring, err, opterr, 0);
1006			} else
1007				rc = CFGA_OK;
1008		} else
1009			cfga_err(NULL, errstring, ERR_TRANS, 0);
1010		break;
1011
1012	case CFGA_CMD_UNCONFIGURE:
1013		if (os != SYSC_CFGA_OSTATE_CONFIGURED)
1014			cfga_err(NULL, errstring, ERR_TRANS, 0);
1015		else if (ioctl(fd, SYSC_CFGA_CMD_UNCONFIGURE, sc) == -1)
1016			cfga_err(sc, errstring, CMD_UNCONFIGURE, 0);
1017		else if (options && (opterr = dlist_update(idx, disable,
1018			dlist, msgp, verbose))) {
1019			err = disable ? OPT_DISABLE : OPT_ENABLE;
1020			cfga_err(NULL, errstring, err, opterr, 0);
1021		} else
1022			rc = CFGA_OK;
1023		break;
1024
1025	default:
1026		rc = CFGA_OPNOTSUPP;
1027		break;
1028	}
1029
1030	(void) close(fd);
1031	return (rc);
1032}
1033
1034static int
1035str2cond(const char *cond)
1036{
1037	int c;
1038
1039	if (strcmp(cond, cfga_str(COND_UNKNOWN)) == 0)
1040		c =  SYSC_CFGA_COND_UNKNOWN;
1041	else if (strcmp(cond, cfga_str(COND_OK)) == 0)
1042		c =  SYSC_CFGA_COND_OK;
1043	else if (strcmp(cond, cfga_str(COND_FAILING)) == 0)
1044		c =  SYSC_CFGA_COND_FAILING;
1045	else if (strcmp(cond, cfga_str(COND_FAILED)) == 0)
1046		c =  SYSC_CFGA_COND_FAILED;
1047	else if (strcmp(cond, cfga_str(COND_UNUSABLE)) == 0)
1048		c =  SYSC_CFGA_COND_UNUSABLE;
1049	else
1050		c = -1;
1051
1052	return (c);
1053}
1054
1055/*ARGSUSED*/
1056cfga_err_t
1057cfga_private_func(
1058	const char *function,
1059	const char *ap_id,
1060	const char *options,
1061	struct cfga_confirm *confp,
1062	struct cfga_msg *msgp,
1063	char **errstring,
1064	cfga_flags_t flags)
1065{
1066	int fd;
1067	int idx;
1068	int len;
1069	int cmd;
1070	int cond;
1071	int err;
1072	int opterr;
1073	int verbose;
1074	int disable;
1075	int disabled;
1076	cfga_err_t rc;
1077	char *str;
1078	char *dlist;
1079	char outputstr[SYSC_OUTPUT_LEN];
1080	sysc_cfga_cmd_t *sc, sysc_cmd;
1081
1082	if (errstring != NULL)
1083		*errstring = NULL;
1084
1085	verbose = flags & CFGA_FLAG_VERBOSE;
1086
1087	rc = CFGA_ERROR;
1088
1089	if (options) {
1090		disable = 0;
1091		if (strcmp(options, cfga_str(OPT_DISABLE)) == 0)
1092			disable++;
1093		else if (strcmp(options, cfga_str(OPT_ENABLE))) {
1094			cfga_err(NULL, errstring, ERR_OPT_INVAL, options, 0);
1095			return (rc);
1096		}
1097	}
1098
1099	sc = &sysc_cmd;
1100	str = cfga_str(CMD_SET_COND);
1101	len = strlen(str);
1102
1103	if ((strncmp(function, str, len) == 0) && (function[len++] == '=') &&
1104	    ((cond = (str2cond(&function[len]))) != -1)) {
1105		cmd = SYSC_CFGA_CMD_TEST_SET_COND;
1106		err = CMD_SET_COND;
1107		sc->arg = cond;
1108	} else if (strcmp(function, cfga_str(CMD_QUIESCE)) == 0) {
1109		cmd = SYSC_CFGA_CMD_QUIESCE_TEST;
1110		err = CMD_QUIESCE;
1111	} else if (strcmp(function, cfga_str(CMD_INSERT)) == 0) {
1112		cmd = SYSC_CFGA_CMD_TEST;
1113		err = CMD_INSERT;
1114	} else if (strcmp(function, cfga_str(CMD_REMOVE)) == 0) {
1115		cmd = SYSC_CFGA_CMD_TEST;
1116		err = CMD_REMOVE;
1117	} else {
1118		cfga_err(NULL, errstring, ERR_CMD_INVAL, (char *)function, 0);
1119		return (rc);
1120	}
1121
1122	sysc_cmd_init(sc, outputstr, 0);
1123
1124	if ((idx = ap_idx(ap_id)) == -1)
1125		cfga_err(NULL, errstring, ERR_AP_INVAL, ap_id, 0);
1126	else if (((fd = open(ap_id, O_RDWR, 0)) == -1) ||
1127		(ioctl(fd, cmd, sc) == -1))
1128		cfga_err(NULL, errstring, err, 0);
1129	else
1130		rc = CFGA_OK;
1131
1132	if (options) {
1133		opterr = (dlist_find(idx, &dlist, &disabled) ||
1134			dlist_update(idx, disable, dlist, msgp, verbose));
1135		if (opterr) {
1136			err = disable ? OPT_DISABLE : OPT_ENABLE;
1137			if (verbose)
1138				cfga_msg(msgp, err, opterr, 0);
1139		}
1140	}
1141
1142	(void) close(fd);
1143	return (rc);
1144}
1145
1146
1147/*ARGSUSED*/
1148cfga_err_t
1149cfga_test(
1150	const char *ap_id,
1151	const char *options,
1152	struct cfga_msg *msgp,
1153	char **errstring,
1154	cfga_flags_t flags)
1155{
1156	if (errstring != NULL)
1157		*errstring = NULL;
1158
1159	return (CFGA_OPNOTSUPP);
1160}
1161
1162static cfga_stat_t
1163rstate_cvt(sysc_cfga_rstate_t rs)
1164{
1165	cfga_stat_t cs;
1166
1167	switch (rs) {
1168	case SYSC_CFGA_RSTATE_EMPTY:
1169		cs = CFGA_STAT_EMPTY;
1170		break;
1171	case SYSC_CFGA_RSTATE_DISCONNECTED:
1172		cs = CFGA_STAT_DISCONNECTED;
1173		break;
1174	case SYSC_CFGA_RSTATE_CONNECTED:
1175		cs = CFGA_STAT_CONNECTED;
1176		break;
1177	default:
1178		cs = CFGA_STAT_NONE;
1179		break;
1180	}
1181
1182	return (cs);
1183}
1184
1185static cfga_stat_t
1186ostate_cvt(sysc_cfga_ostate_t os)
1187{
1188	cfga_stat_t cs;
1189
1190	switch (os) {
1191	case SYSC_CFGA_OSTATE_UNCONFIGURED:
1192		cs = CFGA_STAT_UNCONFIGURED;
1193		break;
1194	case SYSC_CFGA_OSTATE_CONFIGURED:
1195		cs = CFGA_STAT_CONFIGURED;
1196		break;
1197	default:
1198		cs = CFGA_STAT_NONE;
1199		break;
1200	}
1201
1202	return (cs);
1203}
1204
1205static cfga_cond_t
1206cond_cvt(sysc_cfga_cond_t sc)
1207{
1208	cfga_cond_t cc;
1209
1210	switch (sc) {
1211	case SYSC_CFGA_COND_OK:
1212		cc = CFGA_COND_OK;
1213		break;
1214	case SYSC_CFGA_COND_FAILING:
1215		cc = CFGA_COND_FAILING;
1216		break;
1217	case SYSC_CFGA_COND_FAILED:
1218		cc = CFGA_COND_FAILED;
1219		break;
1220	case SYSC_CFGA_COND_UNUSABLE:
1221		cc = CFGA_COND_UNUSABLE;
1222		break;
1223	case SYSC_CFGA_COND_UNKNOWN:
1224	default:
1225		cc = CFGA_COND_UNKNOWN;
1226		break;
1227	}
1228
1229	return (cc);
1230}
1231
1232static char *
1233type_str(enum board_type type)
1234{
1235	char *type_str;
1236
1237	switch (type) {
1238	case MEM_BOARD:
1239		type_str = cfga_str(BD_MEM);
1240		break;
1241	case CPU_BOARD:
1242		type_str = cfga_str(BD_CPU);
1243		break;
1244	case IO_2SBUS_BOARD:
1245		type_str = cfga_str(BD_IO_2SBUS);
1246		break;
1247	case IO_SBUS_FFB_BOARD:
1248		type_str = cfga_str(BD_IO_SBUS_FFB);
1249		break;
1250	case IO_PCI_BOARD:
1251		type_str = cfga_str(BD_IO_PCI);
1252		break;
1253	case DISK_BOARD:
1254		type_str = cfga_str(BD_DISK);
1255		break;
1256	case IO_2SBUS_SOCPLUS_BOARD:
1257		type_str = cfga_str(BD_IO_2SBUS_SOCPLUS);
1258		break;
1259	case IO_SBUS_FFB_SOCPLUS_BOARD:
1260		type_str = cfga_str(BD_IO_SBUS_FFB_SOCPLUS);
1261		break;
1262	case UNKNOWN_BOARD:
1263	default:
1264		type_str = cfga_str(BD_UNKNOWN);
1265		break;
1266	}
1267	return (type_str);
1268}
1269
1270static void
1271info_set(sysc_cfga_stat_t *sc, cfga_info_t info, int disabled)
1272{
1273	int i;
1274	struct cpu_info *cpu;
1275	union bd_un *bd = &sc->bd;
1276
1277	*info = NULL;
1278
1279	switch (sc->type) {
1280	case CPU_BOARD:
1281		for (i = 0, cpu = bd->cpu; i < 2; i++, cpu++) {
1282			if (cpu->cpu_speed > 1) {
1283				info += sprintf(info, "cpu %d: ", i);
1284				info += sprintf(info, "%3d MHz ",
1285						cpu->cpu_speed);
1286				if (cpu->cache_size)
1287					info += sprintf(info, "%0.1fM ",
1288						(float)cpu->cache_size /
1289						(float)(1024 * 1024));
1290			}
1291		}
1292		break;
1293	case IO_SBUS_FFB_BOARD:
1294		switch (bd->io2.ffb_size) {
1295		case FFB_SINGLE:
1296			info += sprintf(info, "single buffered ffb   ");
1297			break;
1298		case FFB_DOUBLE:
1299			info += sprintf(info, "double buffered ffb   ");
1300			break;
1301		case FFB_NOT_FOUND:
1302#ifdef FFB_DR_SUPPORT
1303			info += sprintf(info, "no ffb installed   ");
1304#endif
1305			break;
1306		default:
1307			info += sprintf(info, "illegal ffb size   ");
1308			break;
1309		}
1310		break;
1311	case DISK_BOARD:
1312		for (i = 0; i < 2; i++)
1313			if (bd->dsk.disk_pres[i])
1314				info += sprintf(info, "target: %2d ",
1315						bd->dsk.disk_id[i]);
1316			else
1317				info += sprintf(info, "no disk   ");
1318		break;
1319	}
1320
1321	if (disabled)
1322		info += sprintf(info, "disabled at boot   ");
1323
1324	if (sc->no_detach)
1325		info += sprintf(info, "non-detachable   ");
1326
1327	if (sc->plus_board)
1328		info += sprintf(info, "100 MHz capable   ");
1329}
1330
1331static void
1332sysc_cvt(sysc_cfga_stat_t *sc, cfga_stat_data_t *cs, int disabled)
1333{
1334	(void) strcpy(cs->ap_type, type_str(sc->type));
1335	cs->ap_r_state = rstate_cvt(sc->rstate);
1336	cs->ap_o_state = ostate_cvt(sc->ostate);
1337	cs->ap_cond = cond_cvt(sc->condition);
1338	cs->ap_busy = (cfga_busy_t)sc->in_transition;
1339	cs->ap_status_time = sc->last_change;
1340	info_set(sc, cs->ap_info, disabled);
1341	cs->ap_log_id[0] = NULL;
1342	cs->ap_phys_id[0] = NULL;
1343}
1344
1345/*ARGSUSED*/
1346cfga_err_t
1347cfga_list(
1348	const char *ap_id,
1349	cfga_stat_data_t **ap_list,
1350	int *nlist,
1351	const char *options,
1352	char **errstring)
1353{
1354	int i;
1355	cfga_err_t rc;
1356	sysc_cfga_stat_t *sc;
1357	cfga_stat_data_t *cs;
1358
1359	if (errstring != NULL)
1360		*errstring = NULL;
1361
1362	rc = CFGA_ERROR;
1363
1364	if (ap_idx(ap_id) == -1)
1365		cfga_err(NULL, errstring, ERR_AP_INVAL, ap_id, 0);
1366	else if ((sc = sysc_stat(ap_id, NULL)) == NULL)
1367		cfga_err(NULL, errstring, CMD_LIST, 0);
1368	else if (!(cs = (cfga_stat_data_t *)malloc(MAX_BOARDS * sizeof (*cs))))
1369		cfga_err(NULL, errstring, CMD_LIST, 0);
1370	else {
1371		*ap_list = cs;
1372
1373		for (*nlist = 0, i = 0; i < MAX_BOARDS; i++, sc++) {
1374			if (sc->board == -1)
1375				continue;
1376			sysc_cvt(sc, cs++, 0); /* XXX - disable */
1377			(*nlist)++;
1378		}
1379
1380		rc = CFGA_OK;
1381	}
1382
1383	return (rc);
1384}
1385
1386/*ARGSUSED*/
1387cfga_err_t
1388cfga_stat(
1389	const char *ap_id,
1390	struct cfga_stat_data *cs,
1391	const char *options,
1392	char **errstring)
1393{
1394	cfga_err_t rc;
1395	int idx;
1396	int err;
1397	int opterr;
1398	int disable;
1399	int disabled;
1400	char *dlist;
1401	sysc_cfga_stat_t *sc;
1402
1403	if (errstring != NULL)
1404		*errstring = NULL;
1405
1406	rc = CFGA_ERROR;
1407
1408	if (options && options[0]) {
1409		disable = 0;
1410		if (strcmp(options, cfga_str(OPT_DISABLE)) == 0)
1411			disable++;
1412		else if (strcmp(options, cfga_str(OPT_ENABLE))) {
1413			cfga_err(NULL, errstring, ERR_OPT_INVAL, options, 0);
1414			return (rc);
1415		}
1416	}
1417
1418	if ((idx = ap_idx(ap_id)) == -1)
1419		cfga_err(NULL, errstring, ERR_AP_INVAL, ap_id, 0);
1420	else if ((sc = sysc_stat(ap_id, NULL)) == NULL)
1421		cfga_err(NULL, errstring, CMD_GETSTAT, 0);
1422	else {
1423		opterr = dlist_find(idx, &dlist, &disabled);
1424		sysc_cvt(sc + idx, cs, disabled);
1425
1426		rc = CFGA_OK;
1427
1428		if (options && options[0] && ((opterr != 0) ||
1429			((opterr = dlist_update(idx, disable, dlist, NULL, 0))
1430			!= 0))) {
1431				err = disable ? OPT_DISABLE : OPT_ENABLE;
1432				cfga_err(NULL, errstring, err, opterr, 0);
1433		}
1434	}
1435
1436	return (rc);
1437}
1438
1439/*ARGSUSED*/
1440cfga_err_t
1441cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
1442{
1443	int help = 0;
1444
1445	if (options) {
1446		if (strcmp(options, cfga_str(OPT_DISABLE)) == 0)
1447			help = HELP_DISABLE;
1448		else if (strcmp(options, cfga_str(OPT_ENABLE)) == 0)
1449			help = HELP_ENABLE;
1450		else if (strcmp(options, cfga_str(CMD_INSERT)) == 0)
1451			help = HELP_INSERT;
1452		else if (strcmp(options, cfga_str(CMD_REMOVE)) == 0)
1453			help = HELP_REMOVE;
1454		else if (strcmp(options, cfga_str(CMD_QUIESCE)) == 0)
1455			help = HELP_QUIESCE;
1456		else
1457			help = HELP_UNKNOWN;
1458	}
1459
1460	if (help)  {
1461		if (help == HELP_UNKNOWN)
1462			cfga_msg(msgp, help, options, 0);
1463		else
1464			cfga_msg(msgp, help, 0);
1465	} else {
1466		cfga_msg(msgp, HELP_HEADER, 0);
1467		cfga_msg(msgp, HELP_DISABLE, 0);
1468		cfga_msg(msgp, HELP_ENABLE, 0);
1469		cfga_msg(msgp, HELP_INSERT, 0);
1470		cfga_msg(msgp, HELP_REMOVE, 0);
1471		cfga_msg(msgp, HELP_QUIESCE, 0);
1472		cfga_msg(msgp, HELP_SET_COND, 0);
1473	}
1474
1475	return (CFGA_OK);
1476}
1477
1478/*
1479 * cfga_ap_id_cmp -- use default_ap_id_cmp() in libcfgadm
1480 */
1481