diff --git a/Makefile b/Makefile
index cf7b049fc4480dd9ed30cd0566f54b72676ea9b5..18201813d5613e8ac5b1ec6ace02682487bd36f3 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ MODULES:=$(wildcard modules/*)
 export CCFLAGS LDFLAGS
 LDFLAGS_parse_config=-ldl -export-dynamic
 
-all: $(LIBRARIES:%=build/%) $(MODULES) parse_config
+all: $(LIBRARIES:%=build/%) $(MODULES) 
 	echo $(MODULES)
 	echo $(CCFLAGS)
 
@@ -34,18 +34,20 @@ $(MODULES):
 .PHONY: test
 test: all
 	$(MAKE) -C test test
-	LD_LIBRARY_PATH=build \
-		valgrind ./parse_config test/*/*.conf
+#	LD_LIBRARY_PATH=build \
+#		valgrind ./parse_config test/*/*.conf
 #	LD_LIBRARY_PATH=build HOME=$$(pwd)/test \
 #		valgrind -q --error-exitcode=1 test/test_c
 
 
 clean:
-	rm build/*
+	find build -type f -delete
+	rm *~
 
-
-build/libmoberg.so: build/lib/moberg_driver.o
+build/libmoberg.so: build/lib/moberg_config.o
+build/libmoberg.so: build/lib/moberg_device.o
 build/libmoberg.so: build/lib/moberg_config_parser.o
+build/lib/moberg_device.o: moberg_device.h
 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 8df1bab409bbadb90400d05716680062e759421f..f89e8b2f927fde0c5acf48f7973a4cf23c4f7738 100644
--- a/moberg.c
+++ b/moberg.c
@@ -20,6 +20,7 @@ struct moberg_digital_in_t {
 };
 
 struct moberg {
+  struct moberg_config *config;
   struct {
     int count;
     struct moberg_digital_in_t *value;
@@ -42,9 +43,15 @@ static void parse_config_at(
             buf[statbuf.st_size] = 0;
           }
           printf("Parsing... %s %d %d\n", pathname, dirfd, fd);
-          struct moberg_config *config;
-          config = moberg_config_parse(buf);
+          struct moberg_config *config = moberg_config_parse(buf);
+          printf("-> %p\n", config);
           if (config) {
+            if (! moberg->config) {
+              moberg->config = config;
+            } else {
+              moberg_config_join(moberg->config, config);
+              moberg_config_free(config);
+            }
           }
           free(buf);
         }
@@ -85,7 +92,10 @@ struct moberg *moberg_new(
   struct moberg_config *config)
 {
   struct moberg *result = malloc(sizeof(*result));
-  if (result) {
+  if (! result) {
+    fprintf(stderr, "Failed to allocate moberg struct\n");
+  } else {
+    result->config = NULL;
     if (! config) {
       /* Parse default configuration(s) */
       const char * const *config_paths = xdgSearchableConfigDirectories(NULL);
@@ -96,6 +106,7 @@ struct moberg *moberg_new(
           parse_config_at(result, dirfd1, "moberg.conf");
           int dirfd2 = openat(dirfd1, "moberg.d", O_DIRECTORY);
           if (dirfd2 >= 0) { 
+            parse_config_dir_at(result, dirfd2);
             parse_config_dir_at(result, dirfd2);
             close(dirfd2);
           }
@@ -115,7 +126,10 @@ struct moberg *moberg_new(
 
 void moberg_free(struct moberg *moberg)
 {
-  free(moberg);
+  if (moberg) {
+    moberg_config_free(moberg->config);
+    free(moberg);
+  }
 }
 
 enum moberg_status moberg_start(
diff --git a/moberg_config.c b/moberg_config.c
new file mode 100644
index 0000000000000000000000000000000000000000..b9f4e1ce51a7c951e2686231eb07eecaf590431d
--- /dev/null
+++ b/moberg_config.c
@@ -0,0 +1,99 @@
+#include <stdlib.h>
+#include <string.h>
+#include <moberg_config.h>
+
+#define LIST_COND_GROW(LIST, INDEX, ONERR)                                \
+  ({                                                                    \
+    if (LIST.capacity <= INDEX) {                                       \
+      int new_cap;                                                      \
+      for (new_cap = 2 ; new_cap <= INDEX ; new_cap *= 2);              \
+      void *new = realloc(LIST.value, new_cap * sizeof(*LIST.value));   \
+      if (! new) { ONERR; }                                             \
+      void *p = new + LIST.capacity * sizeof(*LIST.value);              \
+      memset(p, 0, (new_cap - LIST.capacity) * sizeof(*LIST.value));    \
+      LIST.value = new;                                                 \
+      LIST.capacity= new_cap;                                           \
+    }                                                                   \
+  })
+
+#define LIST_SET(LIST, INDEX, VALUE, ONERR)       \
+  ( LIST_COND_GROW(LIST, INDEX, ONERR),           \
+    LIST.value[INDEX] = VALUE )
+
+#define LIST_GET(LIST, INDEX, VALUE, ONERR)       \
+  ( LIST_COND_GROW(LIST, INDEX, ONERR),           \
+    LIST.value[INDEX] )
+
+struct moberg_config
+{
+  struct device_entry {
+    struct device_entry *next;
+    struct moberg_device *device;
+  } *device;
+  struct  {
+    int capacity;
+    struct analog_in_entry {
+      struct moberg_device* device;
+      struct moberg_device_analog_in *channel;
+    } *value;
+  } analog_in;
+};
+
+struct moberg_config *moberg_config_new()
+{
+  struct moberg_config *result = malloc(sizeof *result);
+
+  result->device = NULL;
+  
+  return result;
+}
+
+void moberg_config_free(struct moberg_config *config)
+{
+  struct device_entry *entry = config->device;
+  while (entry) {
+    struct device_entry *tmp = entry;
+    entry = entry->next;
+    moberg_device_free(tmp->device);
+    free(tmp);
+  }
+  free(config);
+}
+
+int moberg_config_join(struct moberg_config *dest,
+                       struct moberg_config *src)
+{
+  if (src && dest) {   
+    struct device_entry **tail;
+    for (tail = &dest->device ; *tail ; tail = &(*tail)->next) { }
+    *tail = src->device;
+    src->device = NULL;
+    return 1;
+  }
+  return 0;
+}
+
+int moberg_config_add_device(struct moberg_config *config,
+                             struct moberg_device *device)
+{
+  struct device_entry *entry = malloc(sizeof(*entry));
+
+  entry->next = config->device;
+  entry->device = device;
+  config->device = entry;
+
+  return 1;
+}
+
+int moberg_config_add_analog_in(struct moberg_config *config,
+                                int index,
+                                struct moberg_device* device,
+                                struct moberg_device_analog_in *channel)
+{
+  struct analog_in_entry e = { device, channel };
+  LIST_SET(config->analog_in, index, e, goto err);
+  return 1;
+err:
+  return 0;
+}
+                            
diff --git a/moberg_config.h b/moberg_config.h
index 832b10a5ac92f3bc6b7a448685836bca0f91fc9e..d8a9ef2a6879e247c9a6969f229b37f2135d5bff 100644
--- a/moberg_config.h
+++ b/moberg_config.h
@@ -1,6 +1,45 @@
 #ifndef __MOBERG_CONFIG_H__
 #define __MOBERG_CONFIG_H__
 
+#include <moberg_device.h>
+
 struct moberg_config;
 
+struct moberg_config *moberg_config_new();
+
+void moberg_config_free(struct moberg_config *config);
+
+int moberg_config_join(struct moberg_config *dest,
+                       struct moberg_config *src);
+
+int moberg_config_add_device(struct moberg_config *config,
+                             struct moberg_device *device);
+
+int moberg_config_add_analog_in(struct moberg_config *config,
+                                int index,
+                                struct moberg_device* device,
+                                struct moberg_device_analog_in *channel);
+                            
+int moberg_config_add_analog_out(struct moberg_config *config,
+                                 int index,
+                                 struct moberg_device* device,
+                                 struct moberg_device_analog_out *channel);
+                            
+int moberg_config_add_digital_in(struct moberg_config *config,
+                                 int index,
+                                 struct moberg_device* device,
+                                 struct moberg_device_digital_in *channel);
+                            
+int moberg_config_add_digital_out(struct moberg_config *config,
+                                  int index,
+                                  struct moberg_device* device,
+                                  struct moberg_device_digital_out *channel);
+                            
+int moberg_config_add_encoder_in(struct moberg_config *config,
+                                 int index,
+                                 struct moberg_device* device,
+                                 struct moberg_device_encoder_in *channel);
+
+
+
 #endif
diff --git a/moberg_config_parser.c b/moberg_config_parser.c
index 27b61c63677326dee15c0730dcc43dbbe08fe6e9..3ede13b07b6c73539ea98d9aeebb78192d9e087f 100644
--- a/moberg_config_parser.c
+++ b/moberg_config_parser.c
@@ -7,7 +7,6 @@
 #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;
@@ -17,6 +16,7 @@ typedef struct moberg_config_parser_ident ident_t;
 #define MAX_EXPECTED 10
 
 typedef struct moberg_config_parser_context {
+  struct moberg_config *config;
   const char *buf; /* Pointer to data to be parsed */
   const char *p;   /* current parse location */
   token_t token;
@@ -287,74 +287,78 @@ void moberg_config_parser_failed(
 }
 
 
-static int parse_map_range(context_t *c)
+static int parse_map_range(context_t *c,
+                           struct moberg_device_map_range *range)
 {
-  token_t low, high;
-  if (! acceptsym(c, tok_LBRACKET, NULL)) { goto err; }
-  if (! acceptsym(c, tok_INTEGER, &low)) { goto err; }
+  token_t min, max;
+  if (! acceptsym(c, tok_LBRACKET, NULL)) { goto syntax_err; }
+  if (! acceptsym(c, tok_INTEGER, &min)) { goto syntax_err; }
   if (acceptsym(c, tok_COLON, NULL)) {
-    if (! acceptsym(c, tok_INTEGER, &high)) { goto err; }
+    if (! acceptsym(c, tok_INTEGER, &max)) { goto syntax_err; }
   } else {
-    high = low;
+    max = min;
   }
-  if (! acceptsym(c, tok_RBRACKET, NULL)) { goto err; }
+  if (! acceptsym(c, tok_RBRACKET, NULL)) { goto syntax_err; }
+  if (! range) {
+    fprintf(stderr, "Range is NULL\n");
+    goto err;
+  }
+  range->min = min.u.integer.value;
+  range->max = max.u.integer.value;
   return 1;
-err:
+syntax_err:
   moberg_config_parser_failed(c, stderr);
-  exit(1);
+err:
   return 0;
 }
 
 static int parse_map(context_t *c,
-                     struct moberg_driver *driver)
+                     struct moberg_device *device)
 {
-  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;
-  }
+  struct moberg_device_map_range range;
+  
+  if (acceptkeyword(c, "analog_in")) { range.kind = map_analog_in; }
+  else if (acceptkeyword(c, "analog_out")) { range.kind = map_analog_out; }
+  else if (acceptkeyword(c, "digital_in")) { range.kind = map_digital_in; }
+  else if (acceptkeyword(c, "digital_out")) { range.kind = map_digital_out; }
+  else if (acceptkeyword(c, "encoder_in")) { range.kind = map_encoder_in; }
+  else { goto syntax_err; }
+  if (! parse_map_range(c, &range)) { goto syntax_err; }
+  if (! acceptsym(c, tok_EQUAL, NULL)) { goto syntax_err; }
+  if (! moberg_device_parse_map(device, c, range)) { goto err; }
+  if (! acceptsym(c, tok_SEMICOLON, NULL)) { goto syntax_err; }
   return 1;
-err:    
+syntax_err:
   moberg_config_parser_failed(c, stderr);
-  exit(1);
+err:    
   return 0;
 }
 
-struct moberg_device *parse_device(context_t *c,
-                                   struct moberg_driver *driver)
+static int parse_device(context_t *c,
+                        struct moberg_device *device)
 {
-  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")) {
+     moberg_device_parse_config(device, c);
+/*      
       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));
+      if (! parse_map(c, device)) { goto err; }
     } else if (acceptsym(c, tok_RBRACE, NULL)) {
       break;
     } else {
       goto syntax_err;
     }
   }
-  return result;
+  return 1;
 syntax_err:
   moberg_config_parser_failed(c, stderr);
 err:
-  moberg_device_free(result);
-  return NULL;
+  return 0;
 }
 
 static int parse(context_t *c)
@@ -364,24 +368,31 @@ static int parse(context_t *c)
       break;
     } else {
       token_t t;
-      struct moberg_driver *driver;
+      struct moberg_device *device;
       
       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",
+
+      char *name = strndup(t.u.ident.value, t.u.ident.length);
+      if (! name) {
+        fprintf(stderr, "Failed to allocate driver name '%.*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) {
+      device = moberg_device_new(name);
+      free(name);
+      if (! device) { goto err; }
+
+      if (! parse_device(c, device)) {
+        moberg_device_free(device);
+        goto err;
+      }
+      if (! moberg_config_add_device(c->config, device)) {
+        moberg_device_free(device);
         goto err;
       }
-      fprintf(stderr, "SAVE device\n");
     }
   }
   return 1;
@@ -396,13 +407,19 @@ 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);
+  context.config = moberg_config_new();
+  if (context.config) {
+    context.expected.n = 0;
+    context.buf = buf;
+    context.p = context.buf;
+    nextsym(&context);
+    if (! parse(&context)) {
+      moberg_config_free(context.config);
+      context.config = NULL;
+    }
+  }
 
-  return NULL;
+  return context.config;
 }
   
 
diff --git a/moberg_config_parser.h b/moberg_config_parser.h
index 5990c0fa34c1b2b697f51f60b54050a59b7c02d0..cc50adcf09589a1b526a56c13fde16b9da1520c6 100644
--- a/moberg_config_parser.h
+++ b/moberg_config_parser.h
@@ -3,6 +3,8 @@
 
 #include <moberg_config.h>
 
+struct moberg_config_parser_context;
+
 struct moberg_config *moberg_config_parse(const char *buf);
 
 #endif
diff --git a/moberg_config_parser_module.h b/moberg_config_parser_module.h
index bcfd2c87e397f83b975a6fbf823dd837eaa8ac71..e51fb6e21d5275c0c07c609a4359b91a186a08e9 100644
--- a/moberg_config_parser_module.h
+++ b/moberg_config_parser_module.h
@@ -2,8 +2,8 @@
 #define __MOBERG_CONFIG_PARSER_MODULE_H__
 
 #include <stdio.h>
+#include <moberg_config_parser.h>
 
-struct moberg_config_parser_context;
 struct moberg_config_parser_token;
 
 enum moberg_config_parser_token_kind {
diff --git a/moberg_device.c b/moberg_device.c
new file mode 100644
index 0000000000000000000000000000000000000000..a7e2fae3ab965914179311d53166d72412b343af
--- /dev/null
+++ b/moberg_device.c
@@ -0,0 +1,153 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <moberg_config.h>
+#include <moberg_device.h>
+
+struct moberg_device {
+  void *driver_handle;
+  struct moberg_device_driver driver;
+  struct moberg_device_config *config;
+  struct moberg_device_map_range *range;
+};
+
+struct moberg_device *moberg_device_new(const char *driver)
+{
+  struct moberg_device *result = NULL;
+
+  char *name = malloc(strlen("libmoberg_.so") + strlen(driver) + 1);
+  if (!name) { goto out; }
+  sprintf(name, "libmoberg_%s.so", driver);
+  void *handle = dlopen(name, RTLD_LAZY || RTLD_DEEPBIND);
+  if (! handle) {
+    fprintf(stderr, "Could not find driver %s\n", name);
+    goto free_name;
+  }
+  struct moberg_device_driver *device_driver =
+    (struct moberg_device_driver *) dlsym(handle, "moberg_device_driver");
+  if (! device_driver) {
+    fprintf(stderr, "No moberg_device_driver in driver %s\n", name);
+    goto dlclose_driver;
+  }
+  result = malloc(sizeof(*result));
+  if (! result) {
+    fprintf(stderr, "Could not allocate result for %s\n", name);
+    goto dlclose_driver;
+  }
+  result->driver_handle = handle;
+  result->driver = *device_driver;
+  result->config = NULL;
+  goto free_name;
+  
+dlclose_driver:
+  dlclose(handle);
+free_name:
+  free(name);
+out:
+  return result;
+}
+
+void moberg_device_free(struct moberg_device *device)
+{
+  device->driver.config_free(device->config);
+  free(device->config);
+  free(device);
+}
+
+int moberg_device_parse_config(struct moberg_device *device,
+                               struct moberg_config_parser_context *context)
+{
+  return device->driver.parse_config(device, context);
+}
+
+int moberg_device_set_config(struct moberg_device *device,
+                             struct moberg_device_config *config)
+{
+  if (device->config) {
+    device->driver.config_free(device->config);
+    free(device->config);
+  }
+  device->config = config;
+  return 1;
+}
+
+int moberg_device_parse_map(struct moberg_device* device,
+                            struct moberg_config_parser_context *context,
+                            struct moberg_device_map_range range)
+{
+  int result;
+  struct moberg_device_map_range r = range;
+  device->range = &r;
+  result = device->driver.parse_map(device, context, range.kind);
+  device->range = NULL;
+  printf("RRR %d %d\n", r.min, r.max);
+  return result;
+}
+
+int moberg_device_add_analog_in(struct moberg_device* device,
+                                struct moberg_device_analog_in *channel)
+{
+  if (device->range->kind == map_analog_in &&
+      device->range->min <= device->range->max) {
+    printf("Mapping %d\n", device->range->min);
+    // moberg_config_add_analog_in()
+    device->range->min++;
+    return 1;
+  } else {
+    return 0;
+  }
+}
+                            
+int moberg_device_add_analog_out(struct moberg_device* device,
+                                 struct moberg_device_analog_out *channel)
+{
+  if (device->range->kind == map_analog_out &&
+      device->range->min <= device->range->max) {
+    printf("Mapping %d\n", device->range->min);
+    device->range->min++;
+    return 1;
+  } else {
+    return 0;
+  }
+}
+                            
+int moberg_device_add_digital_in(struct moberg_device* device,
+                                 struct moberg_device_digital_in *channel)
+{
+  if (device->range->kind == map_digital_in &&
+      device->range->min <= device->range->max) {
+    printf("Mapping %d\n", device->range->min);
+    device->range->min++;
+    return 1;
+  } else {
+    return 0;
+  }
+}
+                            
+int moberg_device_add_digital_out(struct moberg_device* device,
+                                  struct moberg_device_digital_out *channel)
+{
+  if (device->range->kind == map_digital_out &&
+      device->range->min <= device->range->max) {
+    printf("Mapping %d\n", device->range->min);
+    device->range->min++;
+    return 1;
+  } else {
+    return 0;
+  }
+}
+                            
+int moberg_device_add_encoder_in(struct moberg_device* device,
+                                 struct moberg_device_encoder_in *channel)
+{
+  if (device->range->kind == map_encoder_in &&
+      device->range->min <= device->range->max) {
+    printf("Mapping %d\n", device->range->min);
+    device->range->min++;
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
diff --git a/moberg_device.h b/moberg_device.h
index b792a758d30274288f243bda7c619a050296ff6c..c379b3d877471b0b687ef2ae0920bdc8c3bef2e5 100644
--- a/moberg_device.h
+++ b/moberg_device.h
@@ -1,13 +1,101 @@
 #ifndef __MOBERG_DEVICE_H__
 #define __MOBERG_DEVICE_H__
 
-struct moberg_device {
-  int (*add_config)(void *config);
-  int (*add_map)(int config);
+#include <moberg_config.h>
+#include <moberg_config_parser.h>
+
+struct moberg_device_map_range {
+  enum moberg_device_map_kind {
+    map_analog_in,
+    map_analog_out,
+    map_digital_in,
+    map_digital_out,
+    map_encoder_in
+  } kind;
+  int min;
+  int max;
+};
+
+struct moberg_device_analog_in {
+  struct moberg_device_analog_in_context *context;
+  int (*open)(struct moberg_device_analog_in_context *);
+  int (*close)(struct moberg_device_analog_in_context *);
+  int (*read)(struct moberg_device_analog_in_context *, double *value);
+};
+  
+struct moberg_device_analog_out {
+  struct moberg_device_analog_out_context *context;
+  int (*open)(struct moberg_device_analog_out_context *);
+  int (*close)(struct moberg_device_analog_out_context *);
+  int (*write)(struct moberg_device_analog_out_context *, double value);
+};
+  
+struct moberg_device_digital_in {
+  struct moberg_device_digital_in_context *context;
+  int (*open)(struct moberg_device_digital_in_context *);
+  int (*close)(struct moberg_device_digital_in_context *);
+  int (*read)(struct moberg_device_digital_in_context *, int *value);
+};
+  
+struct moberg_device_digital_out {
+  struct moberg_device_digital_out_context *context;
+  int (*open)(struct moberg_device_digital_out_context *);
+  int (*close)(struct moberg_device_digital_out_context *);
+  int (*write)(struct moberg_device_digital_out_context *, int value);
+};
+  
+struct moberg_device_encoder_in {
+  struct moberg_device_encoder_in_context *context;
+  int (*open)(struct moberg_device_encoder_in_context *);
+  int (*close)(struct moberg_device_encoder_in_context *);
+  int (*read)(struct moberg_device_encoder_in_context *, long *value);
 };
+  
+struct moberg_config_parser_context;
+struct moberg_device;
+struct moberg_device_config;
 
-struct moberg_device *moberg_device_new(struct moberg_driver *driver);
+struct moberg_device_driver {
+  int (*parse_config)(
+    struct moberg_device* device,
+    struct moberg_config_parser_context *context);
+  int (*parse_map)(
+    struct moberg_device* device,
+    struct moberg_config_parser_context *context,
+    enum moberg_device_map_kind kind);
+  int (*config_free)(
+    struct moberg_device_config *config);
+};
+
+struct moberg_device;
+
+struct moberg_device *moberg_device_new(const char *driver);
 
 void moberg_device_free(struct moberg_device *device);
 
+int moberg_device_parse_config(struct moberg_device* device,
+                               struct moberg_config_parser_context *context);
+
+int moberg_device_set_config(struct moberg_device* device,
+                             struct moberg_device_config *config);
+
+int moberg_device_parse_map(struct moberg_device* device,
+                            struct moberg_config_parser_context *context,
+                            struct moberg_device_map_range range);
+
+int moberg_device_add_analog_in(struct moberg_device* device,
+                                struct moberg_device_analog_in *channel);
+                            
+int moberg_device_add_analog_out(struct moberg_device* device,
+                                 struct moberg_device_analog_out *channel);
+                            
+int moberg_device_add_digital_in(struct moberg_device* device,
+                                 struct moberg_device_digital_in *channel);
+                            
+int moberg_device_add_digital_out(struct moberg_device* device,
+                                  struct moberg_device_digital_out *channel);
+                            
+int moberg_device_add_encoder_in(struct moberg_device* device,
+                                 struct moberg_device_encoder_in *channel);
+
 #endif
diff --git a/moberg_driver.c b/moberg_driver.c
deleted file mode 100644
index 1780950ba5813a9930316001368aa8801805f675..0000000000000000000000000000000000000000
--- a/moberg_driver.c
+++ /dev/null
@@ -1,47 +0,0 @@
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <moberg_driver.h>
-#include <dlfcn.h>
-
-struct moberg_driver *moberg_driver_open(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);
-  void *handle = dlopen(driver_name, RTLD_LAZY || RTLD_DEEPBIND);
-  if (! handle) {
-    fprintf(stderr, "Could not find driver %s\n", driver_name);
-    goto free_driver_name;
-  }
-  struct moberg_driver_module *module =
-    (struct moberg_driver_module *) dlsym(handle, "moberg_module");
-  if (! module) {
-    fprintf(stderr, "No moberg_module in driver %s\n", driver_name);
-    goto dlclose_driver;
-  }
-  result = malloc(sizeof(*result));
-  if (! result) {
-    fprintf(stderr, "Could not allocate result for %s\n", driver_name);
-    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_driver_close(struct moberg_driver *driver)
-{
-  dlclose(driver->handle);
-  free(driver);
-}
-
diff --git a/moberg_driver.h b/moberg_driver.h
deleted file mode 100644
index ca1b3051e12346b7749633120c1a878d5159e1ae..0000000000000000000000000000000000000000
--- a/moberg_driver.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef __MOBERG_DRIVER_H__
-#define __MOBERG_DRIVER_H__
-
-#include <moberg_config_parser_module.h>
-
-struct moberg_driver_device;
-
-struct moberg_driver_map {
-};
-
-typedef struct moberg_driver_device *(*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_driver_open(struct moberg_config_parser_ident id);
-
-void moberg_driver_close(struct moberg_driver *driver);
-
-void moberg_driver_device_free(struct moberg_driver_device *device);
-
-
-#endif
diff --git a/modules/comedi/comedi.c b/modules/comedi/comedi.c
index 59977600872a1325503f8ca35385ccf138040b01..457c2a78c99ae43eb9bd13019ba9ac0c624b697d 100644
--- a/modules/comedi/comedi.c
+++ b/modules/comedi/comedi.c
@@ -1,5 +1,7 @@
+#include <moberg_config.h>
+#include <moberg_config_parser.h>
 #include <moberg_config_parser_module.h>
-#include <moberg_driver.h>
+#include <moberg_device.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -9,6 +11,11 @@ typedef struct moberg_config_parser_token token_t;
 typedef struct moberg_config_parser_ident ident_t;
 typedef struct moberg_config_parser_context context_t;
 
+struct moberg_device_map {};
+struct moberg_device_config {
+  char *device;
+};
+
 static inline int acceptsym(context_t *c,
 			   kind_t kind,
 			   token_t *token)
@@ -22,17 +29,13 @@ static inline int acceptkeyword(context_t *c,
   return moberg_config_parser_acceptkeyword(c, keyword);
 }
   
-struct moberg_driver_device {
-  const char *device;
-};
-
-
-static struct moberg_driver_device *parse_config(
+static int parse_config(
+  struct moberg_device *device,
   struct moberg_config_parser_context *c)
 {
-  struct moberg_driver_device *result = malloc(sizeof *result);
-  if (! result) {
-    fprintf(stderr, "Failed to allocate moberg device\n");
+  struct moberg_device_config *config = malloc(sizeof *config);
+  if (! config) {
+    fprintf(stderr, "Failed to allocate moberg device config\n");
     goto err;
   }
   if (! acceptsym(c, tok_LBRACE, NULL)) { goto syntax_err; }
@@ -44,7 +47,7 @@ static struct moberg_driver_device *parse_config(
       if (! acceptsym(c, tok_EQUAL, NULL)) { goto syntax_err; }
       if (! acceptsym(c, tok_STRING, &device)) { goto syntax_err; }
       if (! acceptsym(c, tok_SEMICOLON, NULL)) { goto syntax_err; }
-      result->device = strndup(device.u.string.value, device.u.string.length);
+      config->device = strndup(device.u.string.value, device.u.string.length);
     } else if (acceptkeyword(c, "config")) {
       if (! acceptsym(c, tok_EQUAL, NULL)) { goto syntax_err; }
       if (! acceptsym(c, tok_LBRACKET, NULL)) { goto syntax_err; }
@@ -75,18 +78,21 @@ static struct moberg_driver_device *parse_config(
       goto syntax_err;
     }
   }
-  return result;
+  moberg_device_set_config(device, config);
+  return 1;
 syntax_err:
   moberg_config_parser_failed(c, stderr);
+  free(config);
 err:
-  return NULL;
+  return 0;
 }
 
-static struct moberg_driver_map parse_map(
+static int parse_map(
+  struct moberg_device *device,
   struct moberg_config_parser_context *c,
-  enum moberg_config_parser_token_kind kind)
+  enum moberg_device_map_kind kind)
 {
-  struct moberg_driver_map result;
+  token_t min, max;
 
   if (! acceptsym(c, tok_LBRACE, NULL)) { goto err; }
   for (;;) {
@@ -100,21 +106,50 @@ static struct moberg_driver_map parse_map(
       if (! acceptsym(c, tok_INTEGER, &route)) { goto err; }
     }
     if (! acceptsym(c, tok_LBRACKET, NULL)) { goto err; }
-    if (! acceptsym(c, tok_INTEGER, NULL)) { goto err; }
+    if (! acceptsym(c, tok_INTEGER, &min)) { goto err; }
     if (acceptsym(c, tok_COLON, NULL)) { 
-      if (! acceptsym(c, tok_INTEGER, NULL)) { goto err; }
+      if (! acceptsym(c, tok_INTEGER, &max)) { goto err; }
+    } else {
+      max = min;
+    }
+    for (int i = min.u.integer.value ; i <= max.u.integer.value ; i++) {
+      switch (kind) {
+        case map_analog_in:
+          moberg_device_add_analog_in(device, NULL);
+          break;
+        case map_analog_out:
+          moberg_device_add_analog_out(device, NULL);
+          break;
+        case map_digital_in:
+          moberg_device_add_digital_in(device, NULL);
+          break;
+        case map_digital_out:
+          moberg_device_add_digital_out(device, NULL);
+          break;
+        case map_encoder_in:
+          moberg_device_add_encoder_in(device, NULL);
+          break;
+      }
     }
     if (! acceptsym(c, tok_RBRACKET, NULL)) { goto err; }
     if (! acceptsym(c, tok_COMMA, NULL)) { break; }
+    
   }
   if (! acceptsym(c, tok_RBRACE, NULL)) { goto err; }
-  return result;
+  return 1;
 err:
   moberg_config_parser_failed(c, stderr);
-  exit(1);
+  return 0;
+}
+
+static int config_free(struct moberg_device_config *config)
+{
+  free(config->device);
+  return 1;
 }
 
-struct moberg_driver_module moberg_module = {
+struct moberg_device_driver moberg_device_driver = {
   .parse_config = parse_config,
   .parse_map = parse_map,
+  .config_free = config_free
 };
diff --git a/modules/serial2002/serial2002.c b/modules/serial2002/serial2002.c
index 35a7d344afc18b26c580970da2739733241900e8..b1e2a8cb4d50b3ac035801b5692ae4d1d9073112 100644
--- a/modules/serial2002/serial2002.c
+++ b/modules/serial2002/serial2002.c
@@ -1,17 +1,18 @@
+#include <moberg_config.h>
+#include <moberg_config_parser.h>
 #include <moberg_config_parser_module.h>
-#include <moberg_driver.h>
+#include <moberg_device.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.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;
 typedef struct moberg_config_parser_context context_t;
 
 static inline int acceptsym(context_t *c,
-			   kind_t kind,
-			   token_t *token)
+                            kind_t kind,
+                            token_t *token)
 {
   return moberg_config_parser_acceptsym(c, kind, token);
 }
@@ -22,18 +23,18 @@ static inline int acceptkeyword(context_t *c,
   return moberg_config_parser_acceptkeyword(c, keyword);
 }
   
-struct moberg_driver_device {
-  const char *device;
+struct moberg_device_config {
+  char *device;
   int baud;
 };
 
-
-static struct moberg_driver_device *parse_config(
+static int parse_config(
+  struct moberg_device *device,
   struct moberg_config_parser_context *c)
 {
-  struct moberg_driver_device *result = malloc(sizeof *result);
-  if (! result) {
-    fprintf(stderr, "Failed to allocate moberg device\n");
+  struct moberg_device_config *config = malloc(sizeof *config);
+  if (! config) {
+    fprintf(stderr, "Failed to allocate moberg device config\n");
     goto err;
   }
   if (! acceptsym(c, tok_LBRACE, NULL)) { goto syntax_err; }
@@ -45,49 +46,82 @@ static struct moberg_driver_device *parse_config(
       if (! acceptsym(c, tok_EQUAL, NULL)) { goto syntax_err; }
       if (! acceptsym(c, tok_STRING, &device)) { goto syntax_err; }
       if (! acceptsym(c, tok_SEMICOLON, NULL)) { goto syntax_err; }
-      result->device = strndup(device.u.string.value, device.u.string.length);
+      config->device = strndup(device.u.string.value, device.u.string.length);
     } else if (acceptkeyword(c, "baud")) {
       token_t baud;
       if (! acceptsym(c, tok_EQUAL, NULL)) { goto syntax_err; }
       if (! acceptsym(c, tok_INTEGER, &baud)) { goto syntax_err; }
       if (! acceptsym(c, tok_SEMICOLON, NULL)) { goto syntax_err; }
-      result->baud = baud.u.integer.value;
+      config->baud = baud.u.integer.value;
     } else {
       goto syntax_err;
     }
   }
-  return result;
+  moberg_device_set_config(device, config);
+  return 1;
 syntax_err:
   moberg_config_parser_failed(c, stderr);
+  free(config);
 err:
-  return NULL;
+  return 0;
 }
 
-static struct moberg_driver_map parse_map(
+static int parse_map(
+  struct moberg_device *device,
   struct moberg_config_parser_context *c,
-  enum moberg_config_parser_token_kind kind)
+  enum moberg_device_map_kind ignore)
 {
-  struct moberg_driver_map result;
-
-  if (acceptkeyword(c, "analog_in") ||
-      acceptkeyword(c, "analog_out") ||
-      acceptkeyword(c, "digital_in") ||
-      acceptkeyword(c, "digital_out") ||
-      acceptkeyword(c, "encoder_in")) {
-    if (! acceptsym(c, tok_LBRACKET, NULL)) { goto err; }
-    if (! acceptsym(c, tok_INTEGER, NULL)) { goto err; }
-    if (acceptsym(c, tok_COLON, NULL)) { 
-      if (! acceptsym(c, tok_INTEGER, NULL)) { goto err; }
+  enum moberg_device_map_kind kind;
+  token_t min, max;
+ 
+  if (acceptkeyword(c, "analog_in")) { kind = map_analog_in; }
+  else if (acceptkeyword(c, "analog_out")) { kind = map_analog_out; }
+  else if (acceptkeyword(c, "digital_in")) { kind = map_digital_in; }
+  else if (acceptkeyword(c, "digital_out")) { kind = map_digital_out; }
+  else if (acceptkeyword(c, "encoder_in")) { kind = map_encoder_in; }
+  else { goto syntax_err; }
+  if (! acceptsym(c, tok_LBRACKET, NULL)) { goto syntax_err; }
+  if (! acceptsym(c, tok_INTEGER, &min)) { goto syntax_err; }
+  if (acceptsym(c, tok_COLON, NULL)) { 
+    if (! acceptsym(c, tok_INTEGER, &max)) { goto syntax_err; }
+  } else {
+    max = min;
+  }
+  if (! acceptsym(c, tok_RBRACKET, NULL)) { goto syntax_err; }
+  for (int i = min.u.integer.value ; i <= max.u.integer.value ; i++) {
+    switch (kind) {
+      case map_analog_in:
+        moberg_device_add_analog_in(device, NULL);
+        break;
+      case map_analog_out:
+        moberg_device_add_analog_out(device, NULL);
+        break;
+      case map_digital_in:
+        moberg_device_add_digital_in(device, NULL);
+        break;
+      case map_digital_out:
+        moberg_device_add_digital_out(device, NULL);
+        break;
+      case map_encoder_in:
+        moberg_device_add_encoder_in(device, NULL);
+        break;
     }
-    if (! acceptsym(c, tok_RBRACKET, NULL)) { goto err; }
   }
-  return result;
-err:
+  return 1;
+syntax_err:
   moberg_config_parser_failed(c, stderr);
-  exit(1);
+  return 0;
 }
 
-struct moberg_driver_module moberg_module = {
+static int config_free(struct moberg_device_config *config)
+{
+  free(config->device);
+  return 1;
+}
+
+
+struct moberg_device_driver moberg_device_driver = {
   .parse_config = parse_config,
   .parse_map = parse_map,
+  .config_free = config_free
 };
diff --git a/parse_config.c b/parse_config.c
deleted file mode 100644
index 2c2b398c0ce35f8cf63c48cceca7b4777953589e..0000000000000000000000000000000000000000
--- a/parse_config.c
+++ /dev/null
@@ -1,399 +0,0 @@
-#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>
-
-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;
-typedef struct moberg_config_parser_context 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);
-}
- 
-#define MAX_EXPECTED 10
-
-typedef struct moberg_config_parser_context {
-  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 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;
-}
-
-static int parse_device(context_t *c,
-                        struct moberg_driver *driver)
-{
-  if (! acceptsym(c, tok_LBRACE, NULL)) { goto err; }
-  for (;;) {
-    if (acceptkeyword(c, "config")) {
-      driver->module.parse_config(c);
-    } else if (acceptkeyword(c, "map")) {
-      parse_map(c, driver);
-    } else if (acceptsym(c, tok_RBRACE, NULL)) {
-      break;
-    } else {
-      goto err;
-    }
-  }
-  return 1;
-err:
-  return 0;
-}
-
-static int parse_config(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))) { goto driver_not_found; }
-      int OK = parse_device(c, driver);
-      moberg_driver_close(driver);
-      if (! OK) { goto err; }
-    }
-  }
-  return 1;
-syntax_err:  
-  moberg_config_parser_failed(c, stderr);
-  goto err;
-driver_not_found:
-  fprintf(stderr, "Could not open\n");
-  goto err;
-err:
-  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/.config/moberg.d/moberg.conf b/test/.config/moberg.d/moberg.conf
index 9523ce1625d4cfb32c01da6c34920855acf2dbd8..8db1afc61935a9f1346a1c001d81ec74d5f017d6 100644
--- a/test/.config/moberg.d/moberg.conf
+++ b/test/.config/moberg.d/moberg.conf
@@ -7,11 +7,21 @@ driver(comedi) {
     }
     /* 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_in[0:15] = { subdevice[0][0:15] };
     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] };
+    map digital_in[0:7] = { subdevice[7][15],
+                            subdevice[7][2],
+                            subdevice[7][11],
+                            subdevice[7][4],
+                            subdevice[7][3],
+                            subdevice[7][10],
+                            subdevice[7][8],
+                            subdevice[7][9] };
+    map digital_out[0:7] = { subdevice[7] route 16 [0:1],
+                             subdevice[7] route 16 [5:7],
+                             subdevice[7] route 16 [14],
+                             subdevice[7] route 16 [13],
+                             subdevice[7] route 16 [12] };
 }
 driver(serial2002) {
     config {
@@ -21,5 +31,9 @@ driver(serial2002) {
     }
     /* Moberg mapping[indices] = {driver specific}[indices]
       {driver specific} is parsed by parse_map in libmoberg_serial2002.so */
+    map analog_in[30:37] = analog_in[0:7] ;
+    map analog_out[30:37] = analog_out[0:7] ;
     map digital_in[30:37] = digital_in[0:7] ;
+    map digital_out[30:37] = digital_out[0:7] ;
+    map encoder_in[30:37] = encoder_in[0:7] ;
 }
diff --git a/test/test_start_stop.c b/test/test_start_stop.c
index d5fd56671db61aab842f20992880dd6f50d9f09b..393d9328350d50aef54094c852b7fddfbb4b9e49 100644
--- a/test/test_start_stop.c
+++ b/test/test_start_stop.c
@@ -8,5 +8,6 @@ int main(int argc, char *argv[])
   moberg_start(moberg, stdout);
   printf("STOP:\n");
   moberg_stop(moberg, stdout);
+  printf("DONE\n");
   moberg_free(moberg);
 }