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