diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b9512a7378e5196216a7621dd427fc91e852e145 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*~ +*.o +build/ \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..77e25af99451d05814b50dd5bd3bb1bd45b95d3f --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +LIBRARIES=libmoberg.so +CCFLAGS+=-Wall -Werror -I. -g +LDFLAGS+=-Lbuild/ -lmoberg +MODULES:=$(wildcard modules/*) + +LDFLAGS_parse_config=-ldl moberg_driver.o + +all: $(LIBRARIES:%=build/%) $(MODULES) test/test_c parse_config + echo $(MODULES) + +build/libmoberg.so: moberg.c Makefile | build + $(CC) -o $@ $(CCFLAGS) -lxdg-basedir -shared -fPIC -I. $< + +build: + mkdir $@ + +%: %.o + $(CC) $(LDFLAGS) $(LDFLAGS_$(*)) -o $@ $< + +%.o: %.c + $(CC) $(CCFLAGS) -c $< + + +.PHONY: $(MODULES) +$(MODULES): + $(MAKE) -C $@ + + +.PHONY: test +test: all + LD_LIBRARY_PATH=build \ + ./parse_config test/*/*.conf + LD_LIBRARY_PATH=build HOME=$$(pwd)/test \ + valgrind -q --error-exitcode=1 test/test_c + +test/test_c: test/test_c.c Makefile + $(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $< + +clean: + rm build/* + + +parse_config: moberg_config_parser.h +parse_config: moberg_driver.o diff --git a/README.md b/README.md index fdc73927980ac34005dd6c51f1e42bf1a1b466ed..f922bf4f0eb3c3eb16a5e4f21ad1d7a322923616 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,25 @@ struct moberg_digital_in_t *moberg_open_digital_in( Config files are formatted as ``` -comedi = { - device = /dev/comedi0 - modprobe = [ comedi 8255 comedi_fc mite ni_tio ni_tiocmd ni_pcimio ] - config = [ ni_pcimio ] - # Moberg mapping[indices] = driver specific[indices] - map digital_in[0:7] = subdevice[4][0:7] +comedi { + config { + /* Parsed by parse_config in libmoberg_comedi.so */ + device = /dev/comedi0 ; + modprobe = [ comedi 8255 comedi_fc mite ni_tio ni_tiocmd ni_pcimio ] ; + config = [ ni_pcimio ] ; + } + /* Moberg mapping[indices] = {driver specific}[indices] + {driver specific} is parsed by parse_map in libmoberg_comedi.so */ + map digital_in[0:7] = subdevice[4][0:7] ; } -serial2002 = { - device = /dev/ttyS0 - baud = 115200 - # Moberg mapping[indices] = driver specific[indices] - map digital_in[30:37] = digital_in[0:7] +serial2002 { + config { + /* Parsed by parse_config in libmoberg_serial2002.so */ + device = /dev/ttyS0 ; + baud = 115200 ; + } + /* Moberg mapping[indices] = {driver specific}[indices] + {driver specific} is parsed by parse_map in libmoberg_serial2002.so */ + map digital_in[30:37] = digital_in[0:7] ; } ``` \ No newline at end of file diff --git a/moberg.c b/moberg.c new file mode 100644 index 0000000000000000000000000000000000000000..57fbdaaf2db4ab376f92b54303fd314d2272ebec --- /dev/null +++ b/moberg.c @@ -0,0 +1,91 @@ +#define _POSIX_C_SOURCE 200809L +#define _GNU_SOURCE /* scandirat */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <basedir.h> +#include <stdio.h> +#include <dirent.h> +#include <string.h> +#include "moberg.h" + +struct moberg_digital_in_t { + int index; + char *driver; +}; + +struct moberg_t { + struct { + int count; + struct moberg_digital_in_t *value; + } digital_in; +}; + +static void parse_config_at(struct moberg_t *config, + int dirfd, + const char *pathname) +{ + if (dirfd >= 0) { + int fd = openat(dirfd, pathname, O_RDONLY); + if (fd >= 0) { + printf("Parsing... %s %d %d\n", pathname, dirfd, fd); + close(fd); + } + } + +} + +static int conf_filter(const struct dirent *entry) +{ + char *dot = strrchr(entry->d_name, '.'); + if (dot != NULL && strcmp(dot, ".conf") == 0) { + return 1; + } else { + return 0; + } +} + +static void parse_config_dir_at(struct moberg_t *config, + int dirfd) +{ + if (dirfd >= 0) { + struct dirent **entry = NULL; + int n = scandirat(dirfd, ".", &entry, conf_filter, alphasort); + for (int i = 0 ; i < n ; i++) { + parse_config_at(config, dirfd, entry[i]->d_name); + } + free(entry); + } + +} + +const struct moberg_t *moberg_init() +{ + struct moberg_t *result = malloc(sizeof(struct moberg_t)); + + const char * const *config_paths = xdgSearchableConfigDirectories(NULL); + const char * const *path; + for (path = config_paths ; *path ; path++) { + int dirfd1 = open(*path, O_DIRECTORY); + if (dirfd >= 0) { + parse_config_at(result, dirfd1, "moberg.conf"); + int dirfd2 = openat(dirfd1, "moberg.d", O_DIRECTORY); + if (dirfd2 >= 0) { + parse_config_dir_at(result, dirfd2); + close(dirfd2); + } + close(dirfd1); + } + } + free((const char **)config_paths); + + /* Read local default */ + /* Read environment default */ + /* Parse environment overrides */ + + + return result; +} diff --git a/moberg.h b/moberg.h new file mode 100644 index 0000000000000000000000000000000000000000..c3dd7d212dc2fd83b8ba8fe31ce1c533d7874cf3 --- /dev/null +++ b/moberg.h @@ -0,0 +1,15 @@ +#ifndef __MOBERG_H__ +#define __MOBERG_H__ + +struct moberg_t; +struct moberg_digital_in_t; + + +const struct moberg_t *moberg_init(); + +struct moberg_digital_in_t *moberg_open_digital_in( + const struct moberg_t *handle, + int channel); + + +#endif diff --git a/moberg_config_parser.h b/moberg_config_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..806af0de6a7919501873271c5d80c2b9413ef5b1 --- /dev/null +++ b/moberg_config_parser.h @@ -0,0 +1,45 @@ +#ifndef __MOBERG_CONFIG_PARSER_H__ +#define __MOBERG_CONFIG_PARSER_H__ + +struct moberg_config_parser_context; +struct moberg_config_parser_token; + +enum moberg_config_parser_token_kind { + tok_none, + tok_LBRACE = '{', + tok_RBRACE, + tok_LBRACKET, + tok_RBRACKET, + tok_EQUAL, + tok_COLON, + tok_SEMICOLON, + tok_INTEGER, + tok_CONFIG, + tok_MAP, + tok_ANALOGIN, + tok_ANALOGOUT, + tok_DIGITALIN, + tok_DIGITALOUT, + tok_ENCODERIN, + tok_IDENT, +}; + +struct moberg_config_parser_token { + enum moberg_config_parser_token_kind kind; + union { + struct moberg_config_parser_ident { + int length; + const char *value; + } ident; + struct moberg_config_parser_integer { + int value; + } integer; + } u; +}; + +int moberg_config_parser_acceptsym( + struct moberg_config_parser_context *c, + enum moberg_config_parser_token_kind kind, + struct moberg_config_parser_token *token); + +#endif diff --git a/moberg_driver.c b/moberg_driver.c new file mode 100644 index 0000000000000000000000000000000000000000..a73234525b927243a7163a123ec5214e4173ebb8 --- /dev/null +++ b/moberg_driver.c @@ -0,0 +1,39 @@ +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <moberg_driver.h> +#include <dlfcn.h> + +struct moberg_driver *moberg_open_driver(struct moberg_config_parser_ident id) +{ + struct moberg_driver *result = NULL; + + char *driver_name = malloc(sizeof("libmoberg_.so") + id.length + 1); + if (!driver_name) { goto out; } + sprintf(driver_name, "libmoberg_%.*s.so", id.length, id.value); + printf("%s", driver_name); + void *handle = dlopen(driver_name, RTLD_LAZY || RTLD_DEEPBIND); + if (! handle) { goto free_driver_name; } + struct moberg_driver_module *module = + (struct moberg_driver_module *) dlsym(handle, "moberg_module"); + if (! module) { goto dlclose_driver; } + result = malloc(sizeof(*result)); + if (! result) { goto dlclose_driver; } + result->handle = handle; + result->module = *module; + goto free_driver_name; + +dlclose_driver: + dlclose(handle); +free_driver_name: + free(driver_name); +out: + return result; +} + +void moberg_close_driver(struct moberg_driver *driver) +{ + dlclose(driver->handle); + free(driver); +} + diff --git a/moberg_driver.h b/moberg_driver.h new file mode 100644 index 0000000000000000000000000000000000000000..e7986f8dc15ebb80b643df22cbbca80eb4af4162 --- /dev/null +++ b/moberg_driver.h @@ -0,0 +1,32 @@ +#ifndef __MOBERG_DRIVER_H__ +#define __MOBERG_DRIVER_H__ + +#include <moberg_config_parser.h> + +struct moberg_driver_config { +}; + +struct moberg_driver_map { +}; + +typedef struct moberg_driver_config (*moberg_driver_parse_config_t)( + struct moberg_config_parser_context *context); + +typedef struct moberg_driver_map (*moberg_driver_parse_map_t)( + struct moberg_config_parser_context *context, + enum moberg_config_parser_token_kind kind); + +struct moberg_driver { + void *handle; + struct moberg_driver_module { + moberg_driver_parse_config_t parse_config; + moberg_driver_parse_map_t parse_map; + } module; +}; + +struct moberg_driver *moberg_open_driver(struct moberg_config_parser_ident id); + +void moberg_close_driver(struct moberg_driver *driver); + + +#endif diff --git a/modules/comedi/Makefile b/modules/comedi/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..fc2fefaa1cd23d363ba28f3689385fcae3a736cb --- /dev/null +++ b/modules/comedi/Makefile @@ -0,0 +1,11 @@ +LIBRARIES=libmoberg_comedi.so +CCFLAGS+=-Wall -Werror -I../.. -g +LDFLAGS+=-Lbuild/ -lmoberg +MODULES=$(wildcard modules/*) + +all: $(LIBRARIES:%=../../build/%) + +../../build/libmoberg_comedi.so: comedi.c + $(CC) -o $@ $(CCFLAGS) -shared -fPIC $< + +../../build/libmoberg_comedi.so: ../../moberg_config_parser.h diff --git a/modules/comedi/comedi.c b/modules/comedi/comedi.c new file mode 100644 index 0000000000000000000000000000000000000000..a34e526b05e21e971d9f648bceb3e6a454178b4a --- /dev/null +++ b/modules/comedi/comedi.c @@ -0,0 +1,41 @@ +#include <moberg_config_parser.h> +#include <moberg_driver.h> +#include <stdio.h> + +static struct moberg_driver_config parse_config( + struct moberg_config_parser_context *context) +{ + struct moberg_driver_config result; + + printf("PARSE_CONFIG %s\n", __FILE__); +/* + const char *buf = context->buf; + while (*buf && *buf != '}') { + buf++; + } + context->buf = buf + 1; +*/ + return result; +} + +static struct moberg_driver_map parse_map( + struct moberg_config_parser_context *context, + enum moberg_config_parser_token_kind kind) +{ + struct moberg_driver_map result; + + printf("PARSE_MAP %s\n", __FILE__); +/* + const char *buf = context->buf; + while (*buf && *buf != '}') { + buf++; + } + context->buf = buf + 1; +*/ + return result; +} + +struct moberg_driver_module moberg_module = { + .parse_config = parse_config, + .parse_map = parse_map, +}; diff --git a/modules/serial2002/Makefile b/modules/serial2002/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c107b53804891310cdcdf1812e6585d86a1f54c4 --- /dev/null +++ b/modules/serial2002/Makefile @@ -0,0 +1,11 @@ +LIBRARIES=libmoberg_serial2002.so +CCFLAGS+=-Wall -Werror -I../.. -g +LDFLAGS+=-Lbuild/ -lmoberg +MODULES=$(wildcard modules/*) + +all: $(LIBRARIES:%=../../build/%) + +../../build/libmoberg_serial2002.so: serial2002.c + $(CC) -o $@ $(CCFLAGS) -shared -fPIC $< + +../../build/libmoberg_serial2002.so: ../../moberg_config_parser.h diff --git a/modules/serial2002/serial2002.c b/modules/serial2002/serial2002.c new file mode 100644 index 0000000000000000000000000000000000000000..a34e526b05e21e971d9f648bceb3e6a454178b4a --- /dev/null +++ b/modules/serial2002/serial2002.c @@ -0,0 +1,41 @@ +#include <moberg_config_parser.h> +#include <moberg_driver.h> +#include <stdio.h> + +static struct moberg_driver_config parse_config( + struct moberg_config_parser_context *context) +{ + struct moberg_driver_config result; + + printf("PARSE_CONFIG %s\n", __FILE__); +/* + const char *buf = context->buf; + while (*buf && *buf != '}') { + buf++; + } + context->buf = buf + 1; +*/ + return result; +} + +static struct moberg_driver_map parse_map( + struct moberg_config_parser_context *context, + enum moberg_config_parser_token_kind kind) +{ + struct moberg_driver_map result; + + printf("PARSE_MAP %s\n", __FILE__); +/* + const char *buf = context->buf; + while (*buf && *buf != '}') { + buf++; + } + context->buf = buf + 1; +*/ + return result; +} + +struct moberg_driver_module moberg_module = { + .parse_config = parse_config, + .parse_map = parse_map, +}; diff --git a/parse_config.c b/parse_config.c new file mode 100644 index 0000000000000000000000000000000000000000..45b6f654567875fec2025c6c19a964ac7fa860e1 --- /dev/null +++ b/parse_config.c @@ -0,0 +1,285 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <moberg_config_parser.h> +#include <moberg_driver.h> + +#define token moberg_config_parser_token +#define token_kind moberg_config_parser_token_kind +#define acceptsym moberg_config_parser_acceptsym + +typedef struct moberg_config_parser_token token_t; +typedef struct moberg_config_parser_ident ident_t; + +typedef struct moberg_config_parser_context { + char *buf; /* Pointer to data to be parsed */ + const char *p; /* current parse location */ + token_t token; +} context_t; + +static const void nextsym_ident_or_keyword(context_t *c) +{ + c->token.u.ident.length = 1; + c->token.u.ident.value = c->p; + c->p++; + while (*c->p) { + switch (*c->p) { + case 'a'...'z': + case 'A'...'Z': + case '0'...'9': + case '_': + c->p++; + c->token.u.ident.length++; + break; + default: + goto out; + break; + } + } +out: ; + const char *v = c->token.u.ident.value; + int l = c->token.u.ident.length; + if (strncmp("config", v, l) == 0) { + c->token.kind = tok_CONFIG; + } else if (strncmp("map", v, l) == 0) { + c->token.kind = tok_MAP; + } else if (strncmp("analogin", v, l) == 0) { + c->token.kind = tok_ANALOGIN; + } else if (strncmp("analogout", v, l) == 0) { + c->token.kind = tok_ANALOGOUT; + } else if (strncmp("digitalin", v, l) == 0) { + c->token.kind = tok_DIGITALIN; + } else if (strncmp("digitalout", v, l) == 0) { + c->token.kind = tok_DIGITALOUT; + } else if (strncmp("encoderin", v, l) == 0) { + c->token.kind = tok_ENCODERIN; + } else { + c->token.kind = tok_IDENT; + } + printf("IDENT: %.*s\n", l, v); +} + +static const void nextsym_integer(context_t *c) +{ + c->token.u.integer.value = 0; + while (*c->p && '0' <= *c->p && *c->p <= '9') { + c->token.u.integer.value *= 10; + c->token.u.integer.value += *c->p - '0'; + c->p++; + } + printf("INTEGER: %d\n", c->token.u.integer.value); +} + +static int nextsym(context_t *c) +{ + c->token.kind = tok_none; + while (c->p && *c->p && c->token.kind == tok_none) { + if (c->p[0] == '/' && c->p[1] == '*') { + printf("Skipping COMMENT\n"); + /* Skip comment */ + c->p += 2; + while (*c->p && (c->p[0] != '*' || c->p[1] != '/')) { + c->p++; + } + c->p += 2; + continue; + } + switch (*c->p) { + case ' ': + case '\t': + case '\n': + case '\r': + /* Skip whitespace */ + c->p++; + break; + case '{': + c->token.kind = tok_LBRACE; + c->p++; + break; + case '}': + c->token.kind = tok_RBRACE; + c->p++; + break; + case '[': + c->token.kind = tok_LBRACKET; + c->p++; + break; + case ']': + c->token.kind = tok_RBRACKET; + c->p++; + break; + case '=': + c->token.kind = tok_EQUAL; + c->p++; + break; + case ':': + c->token.kind = tok_COLON; + c->p++; + break; + case ';': + c->token.kind = tok_SEMICOLON; + c->p++; + break; + case 'a'...'z': + case 'A'...'Z': + case '_': + nextsym_ident_or_keyword(c); + break; + case '0'...'9': + nextsym_integer(c); + break; + default: + printf("UNEXPECTED %c\n\n", *c->p); + c->p++; + break; + } + } + printf("TOKEN %d\n\n", c->token.kind); + if (c->token.kind != tok_none) { + return 1; + } else { + return 0; + } +} + +int moberg_config_parser_acceptsym(context_t *c, + enum token_kind kind, + token_t *token) +{ + if (c->token.kind == kind) { + if (token) { + *token = c->token; + } + nextsym(c); + return 1; + } + return 0; +} + +static int parse_map_range(context_t *c) +{ + token_t low, high; + if (! acceptsym(c, tok_LBRACKET, NULL)) { goto err; } + if (! acceptsym(c, tok_INTEGER, &low)) { goto err; } + if (acceptsym(c, tok_COLON, NULL)) { + if (! acceptsym(c, tok_INTEGER, &high)) { goto err; } + } else { + high = low; + } + if (! acceptsym(c, tok_RBRACKET, NULL)) { goto err; } + return 1; +err: + printf("OOPS"); + return 0; +} + +static int parse_map(context_t *c, + struct moberg_driver *driver) +{ + struct token t; + if (acceptsym(c, tok_ANALOGIN, &t) || + acceptsym(c, tok_ANALOGOUT, &t) || + acceptsym(c, tok_DIGITALIN, &t) || + acceptsym(c, tok_DIGITALOUT, &t) || + acceptsym(c, tok_ENCODERIN, &t)) { + if (! parse_map_range(c)) { goto err; } + if (! acceptsym(c, tok_EQUAL, NULL)) { goto err; } + driver->module.parse_map(c, t.kind); + if (! parse_map_range(c)) { goto err; } + if (! acceptsym(c, tok_SEMICOLON, NULL)) { goto err; } + switch (t.kind) { + case tok_ANALOGIN: + + case tok_ANALOGOUT: + + case tok_DIGITALIN: + + case tok_DIGITALOUT: + + case tok_ENCODERIN: + break; + default: + goto err; + } + } else { + goto err; + } + return 1; +err: + return 0; +} + +static int parse_device(context_t *c, + struct moberg_driver *driver) +{ + if (! acceptsym(c, tok_LBRACE, NULL)) { goto err; } + for (;;) { + if (acceptsym(c, tok_CONFIG, NULL)) { + driver->module.parse_config(c); + } else if (acceptsym(c, tok_MAP, NULL)) { + parse_map(c, driver); + } else { + goto err; + } + } + return 1; +err: + return 0; +} + +static int parse_driver(context_t *c, + ident_t name) +{ + struct moberg_driver *driver = moberg_open_driver(name); + if (! driver) { + printf("Driver not found\n"); + goto err; + } else { + int OK = parse_device(c, driver); + moberg_close_driver(driver); + if (! OK) { goto err; } + } + return 1; +err: + return 0; +} + +static int parse_config(context_t *c) +{ + for (;;) { + struct token t; + if (acceptsym(c, tok_IDENT, &t)) { + printf("DRIVER=%.*s\n", t.u.ident.length, t.u.ident.value); + if (! parse_driver(c, t.u.ident)) { goto err; } + } + } + return 1; +err: + printf("Failed!!"); + return 0; +} + +static char *read_fd(int fd) +{ + char *result = malloc(10000); /* HACK */ + int pos = read(fd, result, 10000); + result[pos] = 0; + return result; +} + +int main(int argc, char *argv[]) +{ + context_t context; + int fd = open(argv[1], O_RDONLY); + context.buf = read_fd(fd); + context.p = context.buf; + close(fd); + printf("%s\n", context.buf); + nextsym(&context); + parse_config(&context); + free(context.buf); +} diff --git a/test/a/moberg.conf b/test/a/moberg.conf new file mode 100644 index 0000000000000000000000000000000000000000..39fedd1d115916c074957f2bf754e93adf03a623 --- /dev/null +++ b/test/a/moberg.conf @@ -0,0 +1,21 @@ +comedi { + config { + /* Parsed by parse_config in libmoberg_comedi.so */ + device = /dev/comedi0 ; + modprobe = [ comedi 8255 comedi_fc mite ni_tio ni_tiocmd ni_pcimio ] ; + config = [ ni_pcimio ] ; + } + /* Moberg mapping[indices] = {driver specific}[indices] + {driver specific} is parsed by parse_map in libmoberg_comedi.so */ + map digital_in[0:7] = {subdevice[4]}[0:7] ; +} +serial2002 { + config { + /* Parsed by parse_config in libmoberg_serial2002.so */ + device = /dev/ttyS0 ; + baud = 115200 ; + } + /* Moberg mapping[indices] = {driver specific}[indices] + {driver specific} is parsed by parse_map in libmoberg_serial2002.so */ + map digital_in[30:37] = {digital_in}[0:7] ; +} diff --git a/test/b/moberg.conf b/test/b/moberg.conf new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/c/moberg.conf b/test/c/moberg.conf new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/test_c b/test/test_c new file mode 100755 index 0000000000000000000000000000000000000000..c438e6bf1ca8839f32a19270463fce4a7e5704a8 Binary files /dev/null and b/test/test_c differ diff --git a/test/test_c.c b/test/test_c.c new file mode 100644 index 0000000000000000000000000000000000000000..0233e502855035b07e86a3e9654b797fa918c870 --- /dev/null +++ b/test/test_c.c @@ -0,0 +1,9 @@ +#include <stdio.h> +#include <stdlib.h> +#include <moberg.h> + +int main(int argc, char *argv[]) +{ + const struct moberg_t *moberg = moberg_init(); + free((struct moberg_t*) moberg); +}