Files
peterj.nikhef.nl 58186dc995 Reverse merged revision(s) 1196-1195 from CLBv2/trunk/sw/embedded/WRPC-v2.1:
re-commit (effectively revert sw/embedded/WRPC-v2.1 back to version 1194)
2015-02-04 13:32:29 +00:00

348 lines
7.7 KiB
C

/*
* Copyright (C) 2013 CERN (www.cern.ch)
* Authors: Aurelio Colosimo, Alessandro Rubini
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
#include <ppsi/ppsi.h>
/* This file is built in hosted environments, so following headers are Ok */
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
/* config lines are global or ppi-specific. Keep track of globals and ppi */
static struct pp_globals *current_ppg;
static struct pp_instance *current_ppi;
/* A "port" (or "link", for compatibility) line creates or uses a pp instance */
static int f_port(int lineno, int iarg, char *sarg)
{
int i;
/* First look for an existing port with the same name */
for (i = 0; i < current_ppg->nlinks; i++) {
current_ppi = current_ppg->pp_instances + i;
if (!strcmp(sarg, current_ppi->cfg.port_name))
return 0;
}
/* Allocate a new ppi */
if (current_ppg->nlinks >= current_ppg->max_links) {
pp_printf("config line %i: out of available ports\n",
lineno);
return -1;
}
current_ppi = current_ppg->pp_instances + current_ppg->nlinks;
/* FIXME: strncpy (it is missing in bare archs by now) */
strcpy(current_ppi->cfg.port_name, sarg);
current_ppg->nlinks++;
return 0;
}
#define CHECK_PPI(need) /* Quick hack to factorize errors later */ \
({if (need && !current_ppi) { \
pp_printf("config line %i: no port for this config\n", lineno);\
return -1; \
} \
if (!need && current_ppi) { \
pp_printf("config line %i: global config under \"port\"\n", \
lineno); \
return -1; \
}})
static int f_if(int lineno, int iarg, char *sarg)
{
CHECK_PPI(1);
strcpy(current_ppi->cfg.iface_name, sarg);
return 0;
}
/* The following ones are so similar. Bah... set a pointer somewhere? */
static int f_proto(int lineno, int iarg, char *sarg)
{
CHECK_PPI(1);
current_ppi->cfg.proto = iarg;
return 0;
}
static int f_role(int lineno, int iarg, char *sarg)
{
CHECK_PPI(1);
current_ppi->cfg.role = iarg;
return 0;
}
static int f_ext(int lineno, int iarg, char *sarg)
{
CHECK_PPI(1);
current_ppi->cfg.ext = iarg;
return 0;
}
/* The following two are identical as well. I really need a pointer... */
static int f_class(int lineno, int iarg, char *sarg)
{
CHECK_PPI(0);
GOPTS(current_ppg)->clock_quality.clockClass = iarg;
return 0;
}
static int f_accuracy(int lineno, int iarg, char *sarg)
{
CHECK_PPI(0);
GOPTS(current_ppg)->clock_quality.clockAccuracy = iarg;
return 0;
}
/* Diagnostics can be per-port or global */
static int f_diag(int lineno, int iarg, char *sarg)
{
unsigned long level = pp_diag_parse(sarg);
if (current_ppi)
current_ppi->flags = level;
else
pp_global_flags = level;
return 0;
}
/* These are the tables for the parser */
static struct pp_argname arg_proto[] = {
{"raw", PPSI_PROTO_RAW},
{"udp", PPSI_PROTO_UDP},
{},
};
static struct pp_argname arg_role[] = {
{"auto", PPSI_ROLE_AUTO},
{"master",PPSI_ROLE_MASTER},
{"slave", PPSI_ROLE_SLAVE},
{},
};
static struct pp_argname arg_ext[] = {
{"none", PPSI_EXT_NONE},
{"whiterabbit", PPSI_EXT_WR},
{},
};
static struct pp_argline pp_global_arglines[] = {
{ f_port, "port", ARG_STR},
{ f_port, "link", ARG_STR}, /* old name for "port" */
{ f_if, "iface", ARG_STR},
{ f_proto, "proto", ARG_NAMES, arg_proto},
{ f_role, "role", ARG_NAMES, arg_role},
{ f_ext, "extension", ARG_NAMES, arg_ext},
{ f_diag, "diagnostics", ARG_STR},
{ f_class, "clock-class", ARG_INT},
{ f_accuracy, "clock-accuracy", ARG_INT},
{}
};
/* Provide default empty argument lines for architecture and extension */
struct pp_argline pp_arch_arglines[] __attribute__((weak)) = {
{}
};
struct pp_argline pp_ext_arglines[] __attribute__((weak)) = {
{}
};
/* local implementation of isblank() for bare-metal users */
static int blank(int c)
{
return c == ' ' || c == '\t' || c == '\n';
}
static char *first_word(char *line, char **rest)
{
char *ret;
int l = strlen(line) -1;
/* remove trailing blanks */
while (l >= 0 && blank(line[l]))
line [l--] = '\0';
/* skip leading blanks to find first word */
while (*line && blank(*line))
line++;
ret = line;
/* find next blank and thim there*/
while (*line && !blank(*line))
line++;
if (*line) {
*line = '\0';
line++;
}
*rest = line;
return ret;
}
static int pp_config_line(struct pp_globals *ppg, char *line, int lineno)
{
struct pp_argline *l;
struct pp_argname *n;
char *word;
int i;
current_ppg = ppg;
pp_diag(NULL, config, 2, "parsing line %i: \"%s\"\n", lineno, line);
word = first_word(line, &line);
/* now line points to the next word, with no leading blanks */
if (word[0] == '#')
return 0;
if (!*word) {
/* empty or blank-only */
current_ppi = NULL;
return 0;
}
/* Look for the configuration keyword in global, arch, ext */
for (l = pp_global_arglines; l->f; l++)
if (!strcmp(word, l->keyword))
break;
if (!l->f)
for (l = pp_arch_arglines; l->f; l++)
if (!strcmp(word, l->keyword))
break;
if (!l->f)
for (l = pp_ext_arglines; l->f; l++)
if (!strcmp(word, l->keyword))
break;
if (!l->f) {
pp_diag(NULL, config, 1, "line %i: no such keyword \"%s\"\n",
lineno, word);
return -1;
}
switch(l->t) {
case ARG_NONE:
break;
case ARG_INT:
if (sscanf(line, "%i", &i) != 1) {
pp_diag(NULL, config, 1, "line %i: \"%s\": not int\n",
lineno, word);
return -1;
}
if (l->f(lineno, i, NULL))
return -1;
break;
case ARG_STR:
while (*line && *line == ' ' && *line == '\t')
line++;
if (l->f(lineno, 0, line))
return -1;
break;
case ARG_NAMES:
for (n = l->args; n->name; n++)
if (!strcmp(line, n->name))
break;
if (!n->name) {
pp_diag(NULL, config, 1, "line %i: wrong arg \"%s\""
" for \"%s\"\n", lineno, line, word);
return -1;
}
if (l->f(lineno, n->value, NULL))
return -1;
break;
}
return 0;
}
/* Parse a whole string by splitting lines at '\n' or ';' */
static int pp_parse_conf(struct pp_globals *ppg, char *conf, int len)
{
int errcount = 0;
char *line, *rest, term;
int lineno = 1;
line = conf;
do {
for (rest = line;
*rest && *rest != '\n' && *rest != ';'; rest++)
;
term = *rest;
*rest = '\0';
if (*line)
errcount += pp_config_line(ppg, line, lineno) < 0;
line = rest + 1;
if (term == '\n')
lineno++;
} while (term); /* if terminator was already 0, we are done */
pp_config_line(ppg, "", 0); /* done: clear current_ppi */
return errcount ? -1 : 0;
}
/* Open a file, warn if not found */
static int pp_open_conf_file(char *name)
{
int fd;
if (!name)
return -1;
fd = open(name, O_RDONLY);
if (fd >= 0) {
pp_printf("Using config file %s\n", name);
return fd;
}
pp_printf("Warning: %s: %s\n", name, strerror(errno));
return -1;
}
/*
* This is one of the public entry points, opening a file
*
* "force" means that the called wants to open *this file.
* In this case we count it even if it fails, so the default
* config file is not used
*/
int pp_config_file(struct pp_globals *ppg, int force, char *fname)
{
int conf_fd, i, conf_len = 0;
int r = 0, next_r;
struct stat conf_fs;
char *conf_buf;
conf_fd = pp_open_conf_file(fname);
if (conf_fd < 0) {
if (force)
ppg->cfg_items++;
return -1;
}
ppg->cfg_items++;
/* read the whole file, it is split up later on */
fstat(conf_fd, &conf_fs);
conf_buf = calloc(1, conf_fs.st_size + 2);
do {
next_r = conf_fs.st_size - conf_len;
r = read(conf_fd, &conf_buf[conf_len], next_r);
if (r <= 0)
break;
conf_len = strlen(conf_buf);
} while (conf_len < conf_fs.st_size);
close(conf_fd);
i = pp_parse_conf(ppg, conf_buf, conf_len);
free(conf_buf);
return i;
}
/*
* This is the other public entry points, opening a file
*/
int pp_config_string(struct pp_globals *ppg, char *s)
{
return pp_parse_conf(ppg, s, strlen(s));
}