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);
+}