8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / cfgadm_plugins / sbd / common / ap.c
blob502d192014f1202ef59c0282a7f31f31dcc67a3f
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <assert.h>
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <macros.h>
33 #include <dirent.h>
34 #include <libgen.h>
35 #include <libdevinfo.h>
36 #define CFGA_PLUGIN_LIB
37 #include <config_admin.h>
38 #include "ap.h"
40 /*ARGSUSED0*/
41 int
42 ap_symid(apd_t *a, char *apid, char *symid, size_t bufsize)
44 int n;
45 int rc;
46 char path[MAXPATHLEN];
47 char *p;
48 DIR *dirp;
49 struct dirent *dp;
51 *symid = '\0';
52 n = sprintf(path, "/dev/cfg/");
53 rc = -1;
55 if ((dirp = opendir(path)) == NULL)
56 return (rc);
58 p = path + n;
60 while ((dp = readdir(dirp)) != NULL) {
61 char buf[MAXPATHLEN];
62 char *cp;
63 size_t len;
65 *p = '\0';
66 (void) strcat(path, dp->d_name);
67 if ((len = readlink(path, buf, sizeof (buf))) == (size_t)-1)
68 continue;
69 buf[len] = '\0';
71 len = strlen("../");
72 cp = buf;
73 while (strncmp(cp, "../", len) == 0)
74 cp += len;
75 if (cp != buf)
76 cp--; /* Get the '/' */
78 if (strcmp(cp, apid) == 0) {
79 (void) snprintf(symid, bufsize, "%s", dp->d_name);
80 rc = 0;
81 break;
85 (void) closedir(dirp);
86 return (rc);
89 char *
90 ap_logid(apd_t *a, char *apid)
92 int n;
93 char *buf;
95 if ((buf = calloc(1, MAXPATHLEN)) == NULL)
96 return (NULL);
99 * Look for a symlink. On any error, fallback to
100 * driver and instance based logical ap_ids.
102 if (ap_symid(a, apid, buf, MAXPATHLEN) == 0)
103 n = strlen(buf);
104 else
105 n = snprintf(buf, MAXPATHLEN, "%s%d:%s",
106 a->drv, a->inst, a->minor);
108 * Append the dynamic portion, if any.
110 if (a->cid != NULL)
111 (void) snprintf(&buf[n], MAXPATHLEN - n, "::%s", a->cid);
113 return (buf);
117 ap_parse(apd_t *a, const char *ap_id)
119 int i;
120 int rc;
121 int phys;
122 char c;
123 char *s;
124 char *p;
125 char *q;
126 char *base;
127 int len;
128 char *t;
130 if (a == NULL)
131 return (-1);
133 a->cnum = -1;
134 a->bnum = -1;
135 a->inst = -1;
136 a->apid = ap_id;
137 rc = ERR_NONE;
139 if (!str_valid(ap_id)) {
140 rc = ERR_AP_INVAL;
141 goto done;
144 if ((a->path = strdup(ap_id)) == NULL) {
145 rc = ERR_NOMEM;
146 goto done;
150 * For a physical ap_id, look only at the base part.
151 * For a logical/symbolic one, use the entire ap_id.
153 if (strncmp(a->path, DEVDIR, strlen(DEVDIR)) == 0) {
154 phys = 1;
155 base = strrchr((const char *)a->path, '/') + 1;
156 } else {
157 phys = 0;
158 base = a->path;
159 if ((a->target = strdup(a->path)) == NULL) {
160 rc = ERR_NOMEM;
161 goto done;
165 if ((s = strchr(base, ':')) == NULL || s[1] == ':') {
167 * No ':' found, or got a '::'. If this is a physical
168 * ap_id, it must have a minor separtor ':' which must
169 * appear before the dynamic part (starting with '::').
170 * For a symbolic ap_id, skip looking for driver/minor
171 * names.
173 if (phys) {
174 rc = ERR_AP_INVAL;
175 goto done;
176 } else
177 s = base;
178 } else {
180 * Look for driver name/instance only up to the first ':',
181 * i.e. up to the minor node name.
183 *s = '\0';
185 if ((p = strchr(base, '@')) != NULL) {
187 * Get the driver name/instance.
189 *p = '\0';
190 if ((a->drv = strdup(base)) == NULL) {
191 rc = ERR_NOMEM;
192 goto done;
194 *p++ = '@';
196 i = strtol(p, &q, 10);
197 if (q > p)
198 a->inst = i;
201 *s++ = ':';
202 a->minor = s;
206 * Need to go to the end of the string before the :: if any
207 * If the string is null then we are done
209 t = strstr(s, "::");
210 if (t != NULL)
211 len = strlen(t);
212 else
213 len = 0;
215 s += (strlen(s) - len);
217 p = s;
219 if (*p == '\0')
220 a->tgt = AP_BOARD;
221 else if (strncmp(p, "::", 2) != 0) {
222 rc = ERR_AP_INVAL;
223 goto done;
224 } else {
226 * Save the component id.
228 *p++ = '\0';
229 *p++ = '\0';
230 a->cid = p;
234 * Get the operation target, e.g. slot0, slot0::cpu0.
235 * At this point, a->path points to the /devices path
236 * minus the dynamic part, for a physical ap_id. In
237 * the case of a logical ap_id, the target is already
238 * initialized above.
240 if (phys != 0 && (a->target = ap_logid(a, a->path)) == NULL) {
241 rc = ERR_NOMEM;
242 goto done;
245 if (a->tgt == AP_BOARD)
246 goto done;
248 while ((*p != '\0') && !isdigit(*p))
249 p++;
252 * Get the component unit number, if present.
254 i = strtol(p, &s, 10);
256 * There must be no characters after the unit number.
258 if (*s != '\0') {
259 rc = ERR_CM_INVAL;
260 goto done;
262 if (s > p) {
264 * Disallow leading zeroes, e.g. cpu00, cpu01, cpu001.
265 * If there are 2 or more digits and the first is a zero,
266 * we fail.
268 if ((s-p) >= 2 && *p == '0') {
269 rc = ERR_CM_INVAL;
270 goto done;
272 a->cnum = i;
275 c = *p;
276 *p = '\0';
277 if ((a->cname = strdup(a->cid)) == NULL)
278 rc = ERR_NOMEM;
279 *p = c;
280 done:
281 switch (rc) {
282 case ERR_NONE:
283 break;
284 case ERR_CM_INVAL:
285 ap_err(a, ERR_CM_INVAL, a->cid);
286 break;
287 default:
288 ap_err(a, rc);
289 break;
292 DBG("path=<%s> ", a->path ? a->path : "");
293 DBG("drv=<%s> inst=%d minor=<%s> ",
294 a->drv ? a->drv : "", a->inst, a->minor ? a->minor : "");
295 DBG("target=<%s>\n", a->target ? a->target : "");
296 DBG("cid=<%s> ", a->cid ? a->cid : "");
297 DBG("cname=<%s> ", a->cname ? a->cname : "");
298 DBG("cnum=%d\n", a->cnum);
299 DBG("tgt=%d opts=%x\n", a->tgt, a->opts.flags);
301 return (rc == ERR_NONE? 0 : -1);
305 * Command table.
307 * The first set of commands in the table are in sequencing order,
308 * for example, the first group starts with assign and ends with
309 * configure. command sequencer relies on this ordering.
311 static char *
312 ap_cmd_names[] = {
313 "assign",
314 "poweron",
315 "test",
316 "connect",
317 "configure",
318 "notify online",
319 "notify add capacity",
320 "suspend check",
321 "request suspend",
322 "request delete capacity",
323 "request offline",
324 "unconfigure",
325 "notify remove",
326 "notify capacity change",
327 "disconnect",
328 "poweroff",
329 "unassign",
330 "notify resume",
331 "status",
332 "getncm",
333 "passthru",
334 "help",
335 "errtest",
336 NULL
339 char *
340 ap_cmd_name(int i)
342 return (ap_cmd_names[min(i, CMD_NONE)]);
345 static char *
346 ap_opt_names[] = {
347 "unassign",
348 "skip",
349 "parsable",
350 "nopoweroff",
351 "code",
352 "mid",
353 "err",
354 "platform",
355 "sim",
356 NULL
359 char *
360 ap_opt_name(int i)
362 return (ap_opt_names[i]);
366 * Command descriptor.
368 * Each command has a (command) mask specifying the AP target classes
369 * it operates on, e.g. the assign command applies only to boards.
370 * In addition each AP target class has a separate option mask specifying
371 * which command options are valid for that target class.
372 * A global value mask specifies which options require values.
374 typedef struct {
375 int cmd;
376 uint_t cmask;
377 uint_t omask[AP_NCLASS];
378 } ap_cmd_t;
381 * Command option definitions.
383 #define SHFT(i) ((uint_t)1 << (i))
384 #define NULOPT 0
385 #define ALLOPT 0xffffffff
386 #define CMNOPT (SHFT(OPT_VERBOSE)|SHFT(OPT_PLATFORM)|SHFT(OPT_SIM))
387 #define CMFOPT (CMNOPT|SHFT(OPT_FORCE))
388 #define STSOPT (CMNOPT|SHFT(OPT_PARSABLE))
389 #define BRDDCN (CMNOPT|SHFT(OPT_UNASSIGN)|SHFT(OPT_NOPOWEROFF))
391 #define BRD SHFT(AP_BOARD)
392 #define BIO SHFT(AP_BOARD)|SHFT(AP_IO)
393 #define ALL (BRD|SHFT(AP_CPU)|SHFT(AP_MEM)|SHFT(AP_IO)|SHFT(AP_CMP))
395 static ap_cmd_t
396 ap_cmds[] = {
398 * cmd cmd board cpu mem io cmp
399 * cmask omask omask omask omask omask
401 {CMD_ASSIGN, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT},
402 {CMD_UNASSIGN, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT},
403 {CMD_POWERON, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT},
404 {CMD_POWEROFF, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT},
405 {CMD_CONNECT, BRD, 0, CMFOPT, NULOPT, NULOPT, NULOPT, NULOPT},
406 {CMD_DISCONNECT, BRD, 0, BRDDCN, NULOPT, NULOPT, NULOPT, NULOPT},
407 {CMD_CONFIGURE, ALL, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
408 {CMD_UNCONFIGURE, ALL, 0, CMFOPT, CMFOPT, CMFOPT, CMFOPT, CMNOPT},
409 {CMD_RCM_OFFLINE, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
410 {CMD_RCM_ONLINE, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
411 {CMD_RCM_SUSPEND, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
412 {CMD_RCM_RESUME, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
413 {CMD_RCM_CAP_ADD, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
414 {CMD_RCM_CAP_DEL, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
415 {CMD_RCM_CAP_NOTIFY, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
416 {CMD_RCM_REMOVE, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
417 {CMD_TEST, BRD, 0, CMFOPT, NULOPT, NULOPT, NULOPT, NULOPT},
418 {CMD_STATUS, ALL, 0, STSOPT, STSOPT, STSOPT, STSOPT, STSOPT},
419 {CMD_GETNCM, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT},
420 {CMD_PASSTHRU, ALL, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
421 {CMD_HELP, ALL, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
422 {CMD_ERRTEST, ALL, 0, ALLOPT, ALLOPT, ALLOPT, ALLOPT, ALLOPT},
423 {CMD_NONE, 0, 0, 0, 0, 0, 0, 0 }
427 * Global mask for options that require values.
429 #define AP_VMASK (\
430 SHFT(OPT_CODE)|SHFT(OPT_MID)|SHFT(OPT_ERR)| \
431 SHFT(OPT_PLATFORM)|SHFT(OPT_SKIP))
433 #if SBD_DEBUG
434 void
435 ap_cmds_dump()
437 int i;
438 ap_cmd_t *acp;
440 dbg("vmask=0x%x\n", AP_VMASK);
441 dbg("%23s%5s%5s%9s%9s%9s%9s%9s\n",
442 "cmd", "msk", "none", "brd", "cpu", "mem", "io", "cmp");
444 for (acp = ap_cmds; acp->cmd != CMD_NONE; acp++) {
445 dbg("%23s%5x%5x", ap_cmd_name(acp->cmd), acp->cmask,
446 acp->omask[AP_NONE]);
447 for (i = AP_BOARD; i < AP_NCLASS; i++) {
448 dbg("%9x", acp->omask[i]);
450 dbg("\n");
453 #endif
456 ap_state_cmd(cfga_cmd_t i, int *cmd)
458 int c;
459 int rc;
461 rc = CFGA_OK;
463 switch (i) {
464 case CFGA_CMD_CONNECT:
465 c = CMD_CONNECT;
466 break;
467 case CFGA_CMD_DISCONNECT:
468 c = CMD_DISCONNECT;
469 break;
470 case CFGA_CMD_CONFIGURE:
471 c = CMD_CONFIGURE;
472 break;
473 case CFGA_CMD_UNCONFIGURE:
474 c = CMD_UNCONFIGURE;
475 break;
476 case CFGA_CMD_LOAD:
477 case CFGA_CMD_UNLOAD:
478 rc = CFGA_OPNOTSUPP;
479 c = CMD_NONE;
480 break;
481 default:
482 rc = CFGA_INVAL;
483 c = CMD_NONE;
484 break;
487 *cmd = c;
489 return (rc);
492 static int
493 ap_cmd(char *name)
495 int i;
496 char **p;
498 if (name == NULL)
499 return (CMD_NONE);
501 for (i = 0, p = ap_cmd_names; *p != NULL; p++, i++)
502 if (strcmp(*p, name) == 0)
503 break;
504 if (*p == NULL)
505 i = CMD_NONE;
507 return (i);
510 static int
511 ap_opt_parse(apd_t *a, ap_cmd_t *acp, const char *options)
513 char *optstr;
514 ap_opts_t *opts;
517 * Set default values.
519 opts = &a->opts;
520 opts->mid = (char *)a->class;
521 opts->err = ERR_CMD_FAIL;
523 if (options == NULL)
524 return (0);
526 if ((optstr = strdup(options)) == NULL) {
527 ap_err(a, ERR_NOMEM);
528 return (-1);
531 a->options = optstr;
533 if (acp->cmd == CMD_PASSTHRU)
534 return (0);
536 while (*optstr != '\0') {
537 int i;
538 int opt;
539 int omask;
540 char *p;
541 char *value;
542 char *optname;
544 value = NULL;
545 opt = getsubopt(&optstr, ap_opt_names, &value);
547 DBG("opt=%d\n", opt);
549 if (opt == -1) {
550 ap_err(a, ERR_OPT_INVAL, value);
551 return (-1);
554 optname = ap_opt_names[opt];
555 omask = acp->omask[a->tgt];
557 i = mask(opt) & omask;
559 DBG("tgt=%d opt=%x omask=%x\n", a->tgt, mask(opt), omask);
561 if (i == 0) {
562 ap_err(a, ERR_OPT_INVAL, optname);
563 return (-1);
567 * Check whether the option requires a value.
569 i = mask(opt) & AP_VMASK;
570 if (i != 0 && value == NULL) {
571 ap_err(a, ERR_OPT_NOVAL, optname);
572 return (-1);
573 } else if (i == 0 && value != NULL) {
574 ap_err(a, ERR_OPT_VAL, optname);
575 return (-1);
578 if (value == NULL)
579 assert(opt != OPT_CODE); /* XXX prefix */
582 * Set the options's value.
584 switch (opt) {
585 case OPT_SIM:
586 case OPT_PARSABLE:
587 case OPT_UNASSIGN:
588 break;
589 case OPT_CODE:
590 i = strtol(value, &p, 10);
591 if (p > value)
592 opts->code = i;
593 break;
594 case OPT_MID:
595 opts->mid = value;
596 break;
597 case OPT_ERR:
598 i = strtol(value, &p, 10);
599 if (p > value)
600 opts->err = i;
601 break;
602 case OPT_NOPOWEROFF:
603 i = ap_cmd("poweroff");
604 opts->skip |= mask(i);
605 break;
606 case OPT_SKIP: /* for debugging */
608 * The skip value may be a ':' separated
609 * list of steps (commands) to be skipped
610 * during sequencing.
612 for (p = strtok(value, ":"); p != NULL;
613 p = strtok(NULL, ":")) {
614 if ((i = ap_cmd(p)) == CMD_NONE) {
615 ap_err(a, ERR_CMD_INVAL, p);
616 return (-1);
618 opts->skip |= mask(i);
620 break;
621 case OPT_PLATFORM:
622 opts->platform = value;
623 break;
624 default:
625 ap_err(a, ERR_OPT_INVAL, optname);
626 return (-1);
629 ap_setopt(a, opt);
632 return (0);
635 static ap_cmd_t *
636 ap_cmdp(int cmd)
638 ap_cmd_t *acp;
640 for (acp = ap_cmds; acp->cmd != CMD_NONE; acp++)
641 if (acp->cmd == cmd)
642 break;
644 if (acp->cmd == CMD_NONE)
645 return (NULL);
647 return (acp);
650 cfga_err_t
651 ap_cmd_parse(apd_t *a, const char *f, const char *options, int *cmd)
653 int c;
654 int all;
655 int tgt;
656 int target;
657 ap_cmd_t *acp;
658 cfga_err_t rc;
660 #ifdef _SBD_DEBUG
661 ap_cmds_dump();
662 #endif
664 rc = CFGA_INVAL;
666 if ((c = ap_cmd((char *)f)) == CMD_NONE ||
667 (acp = ap_cmdp(c)) == NULL) {
668 ap_err(a, ERR_CMD_INVAL, f);
669 return (rc);
673 * Change a->statonly to 1, if the case is CMD_STATUS. We are only
674 * wanting to read the devices and no more
677 * Get the status for all components if either the list all
678 * option being specified or if we are configuring/unconfiguring
679 * the board. The latter is needed for the RCM interface.
681 switch (c) {
682 case CMD_STATUS:
683 all = ap_getopt(a, OPT_LIST_ALL);
684 a->statonly = 1;
685 break;
686 case CMD_CONFIGURE:
687 case CMD_UNCONFIGURE:
688 case CMD_CONNECT:
689 case CMD_DISCONNECT:
690 all = (a->tgt == AP_BOARD);
691 a->statonly = 0;
692 break;
693 default:
694 all = 0;
695 a->statonly = 0;
696 break;
699 if ((rc = apd_init(a, all)) != CFGA_OK)
700 return (rc);
702 rc = CFGA_INVAL;
705 * Get the target here in case it is a component in which
706 * case its type is known after the initialization.
708 tgt = a->tgt;
709 target = mask(tgt);
711 DBG("cmd=%s(%d) tmask=0x%x cmask=0x%x omask=0x%x\n",
712 ap_cmd_name(c), c, target, acp->cmask, acp->omask[tgt]);
714 if ((acp->cmask & target) == 0)
715 ap_err(a, ERR_CMD_NOTSUPP, c);
716 else if (options != NULL && acp->omask[tgt] == 0)
717 ap_err(a, ERR_OPT_INVAL, options);
718 else if (ap_opt_parse(a, acp, options) != -1) {
719 if (c == CMD_STATUS)
720 rc = ap_platopts_check(a, c, c);
721 else
722 rc = CFGA_OK;
725 if (cmd)
726 *cmd = c;
728 return (rc);
732 ap_cnt(apd_t *a)
734 int cnt;
736 if ((a->tgt == AP_BOARD) && ap_getopt(a, OPT_LIST_ALL))
737 cnt = a->ncm + 1;
738 else
739 cnt = 1;
741 return (cnt);