diff --git a/Makefile b/Makefile
index 5951655ea65fa43749a2c9188f32ea4021833a97..cf7b049fc4480dd9ed30cd0566f54b72676ea9b5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,18 +1,20 @@
 LIBRARIES=libmoberg.so
-CCFLAGS+=-Wall -Werror -I. -g
-LDFLAGS+=-Lbuild/ -lmoberg
+CCFLAGS+=-Wall -Werror -I$(shell pwd) -g
+LDFLAGS+=-L$(shell pwd)/build/ -lmoberg
 MODULES:=$(wildcard modules/*)
-
+export CCFLAGS LDFLAGS
 LDFLAGS_parse_config=-ldl -export-dynamic
 
-all: $(LIBRARIES:%=build/%) $(MODULES) test/test_c parse_config
+all: $(LIBRARIES:%=build/%) $(MODULES) parse_config
 	echo $(MODULES)
+	echo $(CCFLAGS)
 
 build/libmoberg.so:	moberg.c Makefile | build
-	$(CC) -o $@ $(CCFLAGS) -lxdg-basedir -shared -fPIC -I. $<
+	$(CC) -o $@ $(CCFLAGS) -shared -fPIC -I. $< \
+		$(filter %.o,$^) -lxdg-basedir -ldl
 
-build:
-	mkdir $@
+build/lib build:
+	mkdir -p $@
 
 %:	build/%.o Makefile
 	$(CC) $(LDFLAGS) $(LDFLAGS_$(*)) -o $@  $(filter %.o,$^)
@@ -20,6 +22,9 @@ build:
 build/%.o:	%.c Makefile
 	$(CC) $(CCFLAGS) -c -o $@ $<
 
+build/lib/%.o:	%.c Makefile | build/lib
+	$(CC) $(CCFLAGS) -c -fPIC -o $@ $<
+
 
 .PHONY: $(MODULES)
 $(MODULES): 
@@ -28,18 +33,19 @@ $(MODULES):
 
 .PHONY: test
 test: all
+	$(MAKE) -C test test
 	LD_LIBRARY_PATH=build \
 		valgrind ./parse_config test/*/*.conf
-	LD_LIBRARY_PATH=build HOME=$$(pwd)/test \
-		valgrind -q --error-exitcode=1 test/test_c
+#	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/*
 
 
+build/libmoberg.so: build/lib/moberg_driver.o
+build/libmoberg.so: build/lib/moberg_config_parser.o
 build/parse_config.o: moberg_config_parser.h
 parse_config: build/moberg_driver.o
 parse_config: build/parse_config.o
diff --git a/moberg.c b/moberg.c
index 57fbdaaf2db4ab376f92b54303fd314d2272ebec..8df1bab409bbadb90400d05716680062e759421f 100644
--- a/moberg.c
+++ b/moberg.c
@@ -10,35 +10,52 @@
 #include <stdio.h>
 #include <dirent.h>
 #include <string.h>
-#include "moberg.h"
+#include <moberg.h>
+#include <moberg_config.h>
+#include <moberg_config_parser.h>
 
 struct moberg_digital_in_t {
   int index;
   char *driver;
 };
 
-struct moberg_t {
+struct moberg {
   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)
+static void parse_config_at(
+  struct moberg *moberg,
+  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);
+      struct stat statbuf;
+      if (fstat(fd, &statbuf) == 0) {
+        char *buf = malloc(statbuf.st_size + 1);
+        if (buf) {
+          if (read(fd, buf, statbuf.st_size) == statbuf.st_size) {
+            buf[statbuf.st_size] = 0;
+          }
+          printf("Parsing... %s %d %d\n", pathname, dirfd, fd);
+          struct moberg_config *config;
+          config = moberg_config_parse(buf);
+          if (config) {
+          }
+          free(buf);
+        }
+      }
       close(fd);
     }
   }
-  
 }
 
-static int conf_filter(const struct dirent *entry)
+static int conf_filter(
+  const struct dirent *entry)
 {
   char *dot = strrchr(entry->d_name, '.');
   if (dot != NULL && strcmp(dot, ".conf") == 0) {
@@ -48,44 +65,71 @@ static int conf_filter(const struct dirent *entry)
   }
 }
 
-static void parse_config_dir_at(struct moberg_t *config,
-                                int dirfd)
+static void parse_config_dir_at(
+  struct moberg *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[i]);
     }
     free(entry);
   }
   
 }
 
-const struct moberg_t *moberg_init()
+struct moberg *moberg_new(
+  struct moberg_config *config)
 {
-  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);
+  struct moberg *result = malloc(sizeof(*result));
+  if (result) {
+    if (! config) {
+      /* Parse default configuration(s) */
+      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((char*)*path);
       }
-      close(dirfd1);
+      free((const char **)config_paths);
+
+      /* Read environment default */
+      /* Parse environment overrides */
     }
   }
-  free((const char **)config_paths);
-
-  /* Read local default */
-  /* Read environment default */
-  /* Parse environment overrides */
-  
   
   return result;
 }
+
+void moberg_free(struct moberg *moberg)
+{
+  free(moberg);
+}
+
+enum moberg_status moberg_start(
+  struct moberg *moberg,
+  FILE *f)
+{
+  return moberg_OK;
+}
+
+
+enum moberg_status moberg_stop(
+  struct moberg *moberg,
+  FILE *f)
+{
+  return moberg_OK;
+}
+
diff --git a/moberg.h b/moberg.h
index c3dd7d212dc2fd83b8ba8fe31ce1c533d7874cf3..d2cb9d69b0d8a75b72ecfd6a4779769b6799cf08 100644
--- a/moberg.h
+++ b/moberg.h
@@ -1,15 +1,50 @@
 #ifndef __MOBERG_H__
 #define __MOBERG_H__
 
-struct moberg_t;
-struct moberg_digital_in_t;
+#include <stdio.h>
+#include <moberg_config.h>
 
+struct moberg;
+enum moberg_status { moberg_OK };
 
-const struct moberg_t *moberg_init();
+struct moberg *moberg_new(struct moberg_config *config);
+void moberg_free(struct moberg *moberg);
 
-struct moberg_digital_in_t *moberg_open_digital_in(
-  const struct moberg_t *handle,
+/* Input/output functions */
+
+enum moberg_status moberg_analog_in(
+  double *value,
+  struct moberg *moberg,
+  int channel);
+
+enum moberg_status moberg_analog_out(
+  double value,
+  struct moberg *moberg,
+  int channel);
+
+enum moberg_status moberg_digital_in(
+  int *value,
+  struct moberg *moberg,
+  int channel);
+
+enum moberg_status moberg_digital_out(
+  int value,
+  struct moberg *moberg,
+  int channel);
+
+enum moberg_status moberg_encoder_in(
+  long *value,
+  struct moberg *moberg,
   int channel);
-  
+
+/* Install functionality */
+
+enum moberg_status moberg_start(
+  struct moberg *moberg,
+  FILE *f);
+
+enum moberg_status moberg_stop(
+  struct moberg *moberg,
+  FILE *f);
 
 #endif
diff --git a/moberg_config.h b/moberg_config.h
new file mode 100644
index 0000000000000000000000000000000000000000..832b10a5ac92f3bc6b7a448685836bca0f91fc9e
--- /dev/null
+++ b/moberg_config.h
@@ -0,0 +1,6 @@
+#ifndef __MOBERG_CONFIG_H__
+#define __MOBERG_CONFIG_H__
+
+struct moberg_config;
+
+#endif
diff --git a/moberg_config_parser.c b/moberg_config_parser.c
new file mode 100644
index 0000000000000000000000000000000000000000..27b61c63677326dee15c0730dcc43dbbe08fe6e9
--- /dev/null
+++ b/moberg_config_parser.c
@@ -0,0 +1,408 @@
+#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_config_parser_module.h>
+#include <moberg_driver.h>
+#include <moberg_device.h>
+
+typedef enum moberg_config_parser_token_kind kind_t;
+typedef struct moberg_config_parser_token token_t;
+typedef struct moberg_config_parser_ident ident_t;
+
+#define MAX_EXPECTED 10
+
+typedef struct moberg_config_parser_context {
+  const char *buf; /* Pointer to data to be parsed */
+  const char *p;   /* current parse location */
+  token_t token;
+  struct {
+    int n;
+    const char *what[MAX_EXPECTED];
+  } expected;
+} context_t;
+
+
+static inline int acceptsym(context_t *c,
+			   kind_t kind,
+			   token_t *token)
+{
+  return moberg_config_parser_acceptsym(c, kind, token);
+}
+  
+static inline int acceptkeyword(context_t *c,
+				const char *keyword)
+{
+  return moberg_config_parser_acceptkeyword(c, keyword);
+}
+ 
+static const void nextsym_ident(context_t *c)
+{
+  c->token.kind = tok_IDENT;
+  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:
+        return;
+    }
+  }
+}
+
+static const void nextsym_integer(context_t *c)
+{
+  c->token.kind = tok_INTEGER;
+  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++;
+  }
+}
+
+static const void nextsym_string(context_t *c)
+{
+  c->token.kind = tok_STRING;
+  c->p++;
+  c->token.u.string.value = c->p;
+  c->token.u.string.length = 0;
+  while (*c->p && *c->p != '"') {
+    if (*c->p == '\\') {
+      c->token.u.string.length++;
+      c->p++;
+    }
+    if (*c->p) {
+      c->token.u.string.length++;
+      c->p++;
+    }
+  }
+  c->p++;
+}
+
+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] == '*') {
+      /* 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_LPAREN;
+        c->p++;
+        break;
+      case ')':
+        c->token.kind = tok_RPAREN;
+        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_COMMA;
+        c->p++;
+        break;
+      case ':':
+        c->token.kind = tok_COLON;
+        c->p++;
+        break;
+      case ';':
+        c->token.kind = tok_SEMICOLON;
+        c->p++;
+        break;
+      case '"':
+        nextsym_string(c);
+        break;
+      case 'a'...'z':
+      case 'A'...'Z':
+      case '_':
+        nextsym_ident(c);
+        break;
+      case '0'...'9':
+        nextsym_integer(c);
+        break;
+      default:
+        printf("UNEXPECTED %c\n\n", *c->p);
+        c->p++;
+        break;
+    }
+  }
+  if (c->token.kind != tok_none) {
+    return 1;
+  } else {
+    c->token.kind = tok_EOF;
+    return 0;
+  }
+}
+
+static int peeksym(context_t *c,
+		   kind_t kind,
+		   token_t *token)
+{
+  if (c->token.kind == kind) {
+    if (token) {
+      *token = c->token;
+    }
+    return 1;
+  }
+  return 0;
+}
+
+int moberg_config_parser_acceptsym(context_t *c,
+                                   kind_t kind,
+                                   token_t *token)
+{
+  if (c->token.kind == kind) {
+    if (token) {
+      *token = c->token;
+    }
+    nextsym(c);
+    c->expected.n = 0;
+    return 1;
+  }
+  if (c->expected.n < MAX_EXPECTED) {
+    const char *what = NULL;
+    switch (kind) {
+    case tok_none: break;
+      case tok_EOF: what = "<EOF>"; break;
+    case tok_LPAREN: what = "("; break;
+    case tok_RPAREN: what = ")"; break;
+    case tok_LBRACE: what = "{"; break;
+    case tok_RBRACE: what = "}"; break;
+    case tok_LBRACKET: what = "["; break;
+    case tok_RBRACKET: what = "]"; break;
+    case tok_EQUAL: what = "="; break;
+    case tok_COMMA: what = ","; break;
+    case tok_COLON: what = ":"; break;
+    case tok_SEMICOLON: what = ";"; break;
+    case tok_INTEGER: what = "<INTEGER>"; break;
+    case tok_IDENT: what = "<IDENT>"; break;
+    case tok_STRING: what = "<STRING>"; break;
+    }
+    if (what) {
+      c->expected.what[c->expected.n] = what;
+      c->expected.n++;
+    }
+  }
+  return 0;
+}
+
+int moberg_config_parser_acceptkeyword(context_t *c,
+				       const char *keyword)
+{
+  token_t t;
+  if (peeksym(c, tok_IDENT, &t) &&
+      strncmp(keyword, t.u.ident.value, t.u.ident.length) == 0) {
+    nextsym(c);
+    c->expected.n = 0;
+    return 1;
+  }
+  if (c->expected.n < MAX_EXPECTED) {
+    c->expected.what[c->expected.n] = keyword;
+    c->expected.n++;
+  }
+  return 0;
+}
+
+void moberg_config_parser_failed(
+  struct moberg_config_parser_context *c,
+  FILE *f)
+{
+  fprintf(f, "EXPECTED ");
+  for (int i = 0 ; i < c->expected.n; i++) {
+    if (i > 0) {
+      fprintf(f, " | ");
+    }
+    fprintf(f, "'%s'", c->expected.what[i]);
+  }
+  fprintf(f, "\nGOT: ");
+  switch (c->token.kind) {
+    case tok_none: break;
+    case tok_EOF: fprintf(f, "<EOF>"); break;
+    case tok_LPAREN: fprintf(f, "("); break;
+    case tok_RPAREN: fprintf(f, ")"); break;
+    case tok_LBRACE: fprintf(f, "{"); break;
+    case tok_RBRACE: fprintf(f, "}"); break;
+    case tok_LBRACKET: fprintf(f, "["); break;
+    case tok_RBRACKET: fprintf(f, "]"); break;
+    case tok_EQUAL: fprintf(f, "="); break;
+    case tok_COMMA: fprintf(f, ","); break;
+    case tok_COLON: fprintf(f, ":"); break;
+    case tok_SEMICOLON: fprintf(f, ";"); break;
+    case tok_INTEGER:
+      fprintf(f, "%d (<INTEGER>)", c->token.u.integer.value);
+      break;
+    case tok_IDENT:
+      fprintf(f, "%.*s (<IDENT>)",
+              c->token.u.ident.length, c->token.u.ident.value);
+      break;
+    case tok_STRING:
+      fprintf(f, "\"%.*s'\" (<STRING>)",
+              c->token.u.string.length, c->token.u.string.value);
+      break;
+  }
+  fprintf(f, "\n%s\n", c->p);
+}
+
+
+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:
+  moberg_config_parser_failed(c, stderr);
+  exit(1);
+  return 0;
+}
+
+static int parse_map(context_t *c,
+                     struct moberg_driver *driver)
+{
+  if (acceptkeyword(c, "analog_in") ||
+      acceptkeyword(c, "analog_out") ||
+      acceptkeyword(c, "digital_in") ||
+      acceptkeyword(c, "digital_out") ||
+      acceptkeyword(c, "encoder_in")) {
+    if (! parse_map_range(c)) { goto err; }
+    if (! acceptsym(c, tok_EQUAL, NULL)) { goto err; }
+    driver->module.parse_map(c, 0);
+    if (! acceptsym(c, tok_SEMICOLON, NULL)) { goto err; }
+  } else {
+    goto err;
+  }
+  return 1;
+err:    
+  moberg_config_parser_failed(c, stderr);
+  exit(1);
+  return 0;
+}
+
+struct moberg_device *parse_device(context_t *c,
+                                   struct moberg_driver *driver)
+{
+  struct moberg_device *result = moberg_device_new(driver);
+  if (! result) {
+    goto err;
+  }
+    
+  if (! acceptsym(c, tok_LBRACE, NULL)) { goto syntax_err; }
+  for (;;) {
+    if (acceptkeyword(c, "config")) {
+      void *config = driver->module.parse_config(c);
+      result->add_config(config);
+      free(config);
+    } else if (acceptkeyword(c, "map")) {
+      result->add_map(parse_map(c, driver));
+    } else if (acceptsym(c, tok_RBRACE, NULL)) {
+      break;
+    } else {
+      goto syntax_err;
+    }
+  }
+  return result;
+syntax_err:
+  moberg_config_parser_failed(c, stderr);
+err:
+  moberg_device_free(result);
+  return NULL;
+}
+
+static int parse(context_t *c)
+{
+  for (;;) {
+    if (acceptsym(c, tok_EOF, NULL)) {
+      break;
+    } else {
+      token_t t;
+      struct moberg_driver *driver;
+      
+      if (! acceptkeyword(c, "driver")) { goto syntax_err; }
+      if (! acceptsym(c, tok_LPAREN, NULL)) { goto syntax_err; }
+      if (! acceptsym(c, tok_IDENT, &t)) { goto syntax_err; }
+      if (! acceptsym(c, tok_RPAREN, NULL)) { goto syntax_err; }
+      
+      if (! (driver = moberg_driver_open(t.u.ident))) {
+        fprintf(stderr, "Could not open driver '%.*s'\n",
+                t.u.ident.length, t.u.ident.value);
+        goto err;
+      }
+      struct moberg_device *device = parse_device(c, driver);
+      moberg_driver_close(driver);
+      if (! device) {
+        goto err;
+      }
+      fprintf(stderr, "SAVE device\n");
+    }
+  }
+  return 1;
+syntax_err:  
+  moberg_config_parser_failed(c, stderr);
+  goto err;
+err:
+  return 0;
+}
+
+struct moberg_config *moberg_config_parse(const char *buf)
+{
+  context_t context;
+
+  context.expected.n = 0;
+  context.buf = buf;
+  context.p = context.buf;
+  nextsym(&context);
+  parse(&context);
+
+  return NULL;
+}
+  
+
diff --git a/moberg_config_parser.h b/moberg_config_parser.h
index d968b507c8bb6a32ca443a8736d71aaa91b51ed0..5990c0fa34c1b2b697f51f60b54050a59b7c02d0 100644
--- a/moberg_config_parser.h
+++ b/moberg_config_parser.h
@@ -1,56 +1,8 @@
 #ifndef __MOBERG_CONFIG_PARSER_H__
 #define __MOBERG_CONFIG_PARSER_H__
 
-#include <stdio.h>
+#include <moberg_config.h>
 
-struct moberg_config_parser_context;
-struct moberg_config_parser_token;
-
-enum moberg_config_parser_token_kind {
-  tok_none,
-  tok_EOF,
-  tok_LPAREN,
-  tok_RPAREN,
-  tok_LBRACE,
-  tok_RBRACE,
-  tok_LBRACKET,
-  tok_RBRACKET,
-  tok_EQUAL,
-  tok_COMMA,
-  tok_COLON,
-  tok_SEMICOLON,
-  tok_INTEGER,
-  tok_IDENT,
-  tok_STRING,
-};
-
-struct moberg_config_parser_ident {
-  int length;
-  const char *value;
-};
-
-struct moberg_config_parser_token {
-  enum moberg_config_parser_token_kind kind;
-  union {
-    struct moberg_config_parser_ident ident;
-    struct moberg_config_parser_ident string;
-    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);
-
-int moberg_config_parser_acceptkeyword(
-  struct moberg_config_parser_context *c,
-  const char *keyword);
-
-void moberg_config_parser_failed(
-  struct moberg_config_parser_context *c,
-  FILE *f);
+struct moberg_config *moberg_config_parse(const char *buf);
 
 #endif
diff --git a/moberg_config_parser_module.h b/moberg_config_parser_module.h
new file mode 100644
index 0000000000000000000000000000000000000000..bcfd2c87e397f83b975a6fbf823dd837eaa8ac71
--- /dev/null
+++ b/moberg_config_parser_module.h
@@ -0,0 +1,56 @@
+#ifndef __MOBERG_CONFIG_PARSER_MODULE_H__
+#define __MOBERG_CONFIG_PARSER_MODULE_H__
+
+#include <stdio.h>
+
+struct moberg_config_parser_context;
+struct moberg_config_parser_token;
+
+enum moberg_config_parser_token_kind {
+  tok_none,
+  tok_EOF,
+  tok_LPAREN,
+  tok_RPAREN,
+  tok_LBRACE,
+  tok_RBRACE,
+  tok_LBRACKET,
+  tok_RBRACKET,
+  tok_EQUAL,
+  tok_COMMA,
+  tok_COLON,
+  tok_SEMICOLON,
+  tok_INTEGER,
+  tok_IDENT,
+  tok_STRING,
+};
+
+struct moberg_config_parser_ident {
+  int length;
+  const char *value;
+};
+
+struct moberg_config_parser_token {
+  enum moberg_config_parser_token_kind kind;
+  union {
+    struct moberg_config_parser_ident ident;
+    struct moberg_config_parser_ident string;
+    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);
+
+int moberg_config_parser_acceptkeyword(
+  struct moberg_config_parser_context *c,
+  const char *keyword);
+
+void moberg_config_parser_failed(
+  struct moberg_config_parser_context *c,
+  FILE *f);
+
+#endif
diff --git a/moberg_device.h b/moberg_device.h
new file mode 100644
index 0000000000000000000000000000000000000000..b792a758d30274288f243bda7c619a050296ff6c
--- /dev/null
+++ b/moberg_device.h
@@ -0,0 +1,13 @@
+#ifndef __MOBERG_DEVICE_H__
+#define __MOBERG_DEVICE_H__
+
+struct moberg_device {
+  int (*add_config)(void *config);
+  int (*add_map)(int config);
+};
+
+struct moberg_device *moberg_device_new(struct moberg_driver *driver);
+
+void moberg_device_free(struct moberg_device *device);
+
+#endif
diff --git a/moberg_driver.h b/moberg_driver.h
index a774da5ac0c0cadfa917eab559d6a859f10e9f72..ca1b3051e12346b7749633120c1a878d5159e1ae 100644
--- a/moberg_driver.h
+++ b/moberg_driver.h
@@ -1,7 +1,7 @@
 #ifndef __MOBERG_DRIVER_H__
 #define __MOBERG_DRIVER_H__
 
-#include <moberg_config_parser.h>
+#include <moberg_config_parser_module.h>
 
 struct moberg_driver_device;
 
diff --git a/modules/comedi/Makefile b/modules/comedi/Makefile
index fc2fefaa1cd23d363ba28f3689385fcae3a736cb..4923fc69f0e63868378cb240e2b6e1f99fd1fea3 100644
--- a/modules/comedi/Makefile
+++ b/modules/comedi/Makefile
@@ -5,7 +5,7 @@ MODULES=$(wildcard modules/*)
 
 all:	$(LIBRARIES:%=../../build/%) 
 
-../../build/libmoberg_comedi.so: comedi.c 
+../../build/libmoberg_comedi.so: comedi.c Makefile
 	$(CC) -o $@ $(CCFLAGS) -shared -fPIC $<
 
-../../build/libmoberg_comedi.so: ../../moberg_config_parser.h
+../../build/libmoberg_comedi.so: ../../moberg_config_parser_module.h
diff --git a/modules/comedi/comedi.c b/modules/comedi/comedi.c
index f5ae143e86348d1e79ddc5cdac6084e0327254d7..59977600872a1325503f8ca35385ccf138040b01 100644
--- a/modules/comedi/comedi.c
+++ b/modules/comedi/comedi.c
@@ -1,4 +1,4 @@
-#include <moberg_config_parser.h>
+#include <moberg_config_parser_module.h>
 #include <moberg_driver.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/modules/serial2002/Makefile b/modules/serial2002/Makefile
index c107b53804891310cdcdf1812e6585d86a1f54c4..9f66f2d087173ae0b696046dd2076d7bcb5d2de6 100644
--- a/modules/serial2002/Makefile
+++ b/modules/serial2002/Makefile
@@ -5,7 +5,7 @@ MODULES=$(wildcard modules/*)
 
 all:	$(LIBRARIES:%=../../build/%) 
 
-../../build/libmoberg_serial2002.so: serial2002.c
+../../build/libmoberg_serial2002.so: serial2002.c Makefile
 	$(CC) -o $@ $(CCFLAGS) -shared -fPIC $<
 
-../../build/libmoberg_serial2002.so: ../../moberg_config_parser.h
+../../build/libmoberg_serial2002.so: ../../moberg_config_parser_module.h
diff --git a/modules/serial2002/serial2002.c b/modules/serial2002/serial2002.c
index af26b0c411504d36cb4db67d76bff68f5131e32b..35a7d344afc18b26c580970da2739733241900e8 100644
--- a/modules/serial2002/serial2002.c
+++ b/modules/serial2002/serial2002.c
@@ -1,4 +1,4 @@
-#include <moberg_config_parser.h>
+#include <moberg_config_parser_module.h>
 #include <moberg_driver.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/parse_config.c b/parse_config.c
index 53bde81659456164707e48d2976e0bea8b8b4a54..2c2b398c0ce35f8cf63c48cceca7b4777953589e 100644
--- a/parse_config.c
+++ b/parse_config.c
@@ -6,6 +6,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <moberg_config_parser.h>
+#include <moberg_config_parser_module.h>
 #include <moberg_driver.h>
 
 typedef enum moberg_config_parser_token_kind kind_t;
diff --git a/test/.config/moberg.d/moberg.conf b/test/.config/moberg.d/moberg.conf
new file mode 100644
index 0000000000000000000000000000000000000000..9523ce1625d4cfb32c01da6c34920855acf2dbd8
--- /dev/null
+++ b/test/.config/moberg.d/moberg.conf
@@ -0,0 +1,25 @@
+driver(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 analog_in[0:7] = { subdevice[0][0:7] };
+    map analog_out[0:1] = { subdevice[1][0:1] };
+    map digital_in[0:1] = { subdevice[7][15] ,
+                            subdevice[7][2] };
+    map digital_out[0:1] = { subdevice[7] route 16 [0:1] };
+}
+driver(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/Makefile b/test/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..c23d259f1988f25c578a1cda772f11bf852d4407
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,15 @@
+TEST = test_start_stop
+
+ENV_test_start_stop = LD_LIBRARY_PATH=../build HOME=. 
+all: 
+
+.PHONY: test
+test: $(TEST:%=run_%)
+	echo Testing
+	exit 1
+
+run_%:	%
+	$(ENV_$*) valgrind --leak-check=full ./$*
+
+%:	%.c
+	$(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $<
diff --git a/test/test_start_stop.c b/test/test_start_stop.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5fd56671db61aab842f20992880dd6f50d9f09b
--- /dev/null
+++ b/test/test_start_stop.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <moberg.h>
+
+int main(int argc, char *argv[])
+{
+  struct moberg *moberg = moberg_new(NULL);
+  printf("START:\n");
+  moberg_start(moberg, stdout);
+  printf("STOP:\n");
+  moberg_stop(moberg, stdout);
+  moberg_free(moberg);
+}