From 21b59266ed521f0bc2cc14a446eb81a84a18cb4e Mon Sep 17 00:00:00 2001
From: Anders Blomdell <anders.blomdell@control.lth.se>
Date: Thu, 28 Feb 2019 18:02:40 +0100
Subject: [PATCH] AnalogIn works for comedi

---
 Makefile                          |   1 +
 moberg.c                          |  83 ++++++++++-----
 moberg.h                          |  58 ++++++-----
 moberg_channel.h                  |  25 +----
 moberg_device.c                   |   5 +-
 moberg_device.h                   |   6 +-
 moberg_module.h                   |   1 +
 moberg_parser.c                   |  20 ++--
 moberg_parser.h                   |   4 +-
 modules/comedi/Makefile           |   3 +-
 modules/comedi/comedi.c           | 164 ++++++++++++++++++++++++++----
 modules/serial2002/serial2002.c   |  11 +-
 test/.config/moberg.d/moberg.conf |  11 ++
 test/Makefile                     |  22 ++--
 test/test_io.c                    |  14 +++
 15 files changed, 312 insertions(+), 116 deletions(-)
 create mode 100644 test/test_io.c

diff --git a/Makefile b/Makefile
index c0c47ed..1eab439 100644
--- a/Makefile
+++ b/Makefile
@@ -44,6 +44,7 @@ test: all
 clean:
 	find build -type f -delete
 	rm -f *~
+	make -C test clean
 
 build/libmoberg.so: build/lib/moberg_config.o
 build/libmoberg.so: build/lib/moberg_device.o
diff --git a/moberg.c b/moberg.c
index ad8de2e..32437cd 100644
--- a/moberg.c
+++ b/moberg.c
@@ -13,6 +13,7 @@
 #include <moberg.h>
 #include <moberg_config.h>
 #include <moberg_parser.h>
+#include <moberg_module.h>
 
 struct moberg {
   struct moberg_config *config;
@@ -20,19 +21,19 @@ struct moberg {
     int capacity;
     struct moberg_channel **value;
   } analog_in, analog_out, digital_in, digital_out, encoder_in;
-};
+  struct deferred_action {
+    struct deferred_action *next;
+    int (*action)(void *param);
+    void *param;
+  } *deferred_action;
 
-static struct deferred_action {
-  struct deferred_action *next;
-  int (*action)(void *param);
-  void *param;
-} *deferred_action = NULL;
+};
 
-static void run_deferred_actions()
+static void run_deferred_actions(struct moberg *moberg)
 {
-  while (deferred_action) {
-    struct deferred_action *deferred = deferred_action;
-    deferred_action = deferred_action->next;
+  while (moberg->deferred_action) {
+    struct deferred_action *deferred = moberg->deferred_action;
+    moberg->deferred_action = deferred->next;
     deferred->action(deferred->param);
     free(deferred);
   }
@@ -100,7 +101,7 @@ static void parse_config_at(
             buf[statbuf.st_size] = 0;
           }
           printf("Parsing... %s %d %d\n", pathname, dirfd, fd);
-          struct moberg_config *config = moberg_parse(buf);
+          struct moberg_config *config = moberg_parse(moberg, buf);
           printf("-> %p\n", config);
           if (config) {
             if (! moberg->config) {
@@ -236,6 +237,7 @@ struct moberg *moberg_new(
   result->digital_out.value = NULL;
   result->encoder_in.capacity = 0;
   result->encoder_in.value = NULL;
+  result->deferred_action = NULL;
   if (config) {
     result->config = config;
   } else {
@@ -263,7 +265,7 @@ struct moberg *moberg_new(
     /* Parse environment overrides */
   }
   install_config(result);
-  run_deferred_actions();
+  run_deferred_actions(result);
   
 err:
   return result;
@@ -278,37 +280,72 @@ void moberg_free(struct moberg *moberg)
     channel_list_free(&moberg->digital_in);
     channel_list_free(&moberg->digital_out);
     channel_list_free(&moberg->encoder_in);
+    run_deferred_actions(moberg);
     free(moberg);
   }
-  run_deferred_actions();
 }
 
-enum moberg_status moberg_start(
+/* Input/output */
+
+int moberg_analog_in_open(struct moberg *moberg,
+                          int index,
+                          struct moberg_analog_in *analog_in)
+{
+  struct moberg_channel *channel = NULL;
+  channel_list_get(&moberg->analog_in, index, &channel);
+  if (channel) {
+    printf("Call open\n");
+    channel->open(channel);
+    channel->up(channel);
+    *analog_in = channel->action.analog_in;
+    return 1;
+  }
+  return 0;
+}
+
+int moberg_analog_in_close(struct moberg *moberg,
+                           int index,
+                           struct moberg_analog_in analog_in)
+{
+  struct moberg_channel *channel = NULL;
+  channel_list_get(&moberg->analog_in, index, &channel);
+  if (channel && channel->action.analog_in.context == analog_in.context) {
+    printf("Call close\n");
+    channel->close(channel);
+    channel->down(channel);
+  }
+  return 1;
+}
+
+
+
+/* System init functionality (systemd/init/...) */
+
+int moberg_start(
   struct moberg *moberg,
   FILE *f)
 {
   return moberg_config_start(moberg->config, f);
-  return moberg_OK;
 }
 
-
-enum moberg_status moberg_stop(
+int moberg_stop(
   struct moberg *moberg,
   FILE *f)
 {
   return moberg_config_stop(moberg->config, f);
-  return moberg_OK;
 }
 
-void moberg_deferred_action(
-  int (*action)(void *param),
-  void *param)
+/* Intended for final cleanup actions (dlclose so far...) */
+
+void moberg_deferred_action(struct moberg *moberg,
+                            int (*action)(void *param),
+                            void *param)
 {
   struct deferred_action *deferred = malloc(sizeof(*deferred));
   if (deferred) {
-    deferred->next = deferred_action;
+    deferred->next = moberg->deferred_action;
     deferred->action = action;
     deferred->param = param;
-    deferred_action = deferred;
+    moberg->deferred_action = deferred;
   }
 }
diff --git a/moberg.h b/moberg.h
index 23e49c4..062892a 100644
--- a/moberg.h
+++ b/moberg.h
@@ -6,46 +6,54 @@
 struct moberg;
 struct moberg_config;
 
-enum moberg_status { moberg_OK };
+/* Creation & free */
 
 struct moberg *moberg_new(struct moberg_config *config);
 
 void moberg_free(struct moberg *moberg);
 
-/* Input/output functions */
+/* Input/output */
 
-enum moberg_status moberg_analog_in(
-  double *value,
-  struct moberg *moberg,
-  int channel);
+struct moberg_analog_in {
+  struct moberg_channel_analog_in *context;
+  int (*read)(struct moberg_channel_analog_in *, double *value);
+};
 
-enum moberg_status moberg_analog_out(
-  double value,
-  struct moberg *moberg,
-  int channel);
+struct moberg_analog_out {
+  struct moberg_channel_analog_out *context;
+  int (*write)(struct moberg_channel_analog_in *, double value);
+};
 
-enum moberg_status moberg_digital_in(
-  int *value,
-  struct moberg *moberg,
-  int channel);
+struct moberg_digital_in {
+  struct moberg_channel_digital_in *context;
+  int (*read)(struct moberg_channel_digital_in *, int *value);
+};
 
-enum moberg_status moberg_digital_out(
-  int value,
-  struct moberg *moberg,
-  int channel);
+struct moberg_digital_out {
+  struct moberg_channel_digital_out *context;
+  int (*write)(struct moberg_channel_digital_out *, int value);
+};
 
-enum moberg_status moberg_encoder_in(
-  long *value,
-  struct moberg *moberg,
-  int channel);
+struct moberg_encoder_in {
+  struct moberg_channel_encoder_in *context;
+  int (*read)(struct moberg_channel_encoder_in *, long *value);
+};
+
+int moberg_analog_in_open(struct moberg *moberg,
+                          int index,
+                          struct moberg_analog_in *analog_in);
+
+int moberg_analog_in_close(struct moberg *moberg,
+                           int index,
+                           struct moberg_analog_in analog_in);
 
-/* Install functionality */
+/* System init functionality (systemd/init/...) */
 
-enum moberg_status moberg_start(
+int moberg_start(
   struct moberg *moberg,
   FILE *f);
 
-enum moberg_status moberg_stop(
+int moberg_stop(
   struct moberg *moberg,
   FILE *f);
 
diff --git a/moberg_channel.h b/moberg_channel.h
index 2045b21..dab6355 100644
--- a/moberg_channel.h
+++ b/moberg_channel.h
@@ -25,26 +25,11 @@ struct moberg_channel {
   /* I/O operations */
   enum moberg_channel_kind kind;
   union moberg_channel_action {
-    struct {
-      struct moberg_channel_analog_in *context;
-      int (*read)(struct moberg_channel_analog_in *, double *value);
-    } analog_in;
-    struct  {
-      struct moberg_channel_analog_out *context;
-      int (*write)(struct moberg_channel_context *, double value);
-    } analog_out;
-    struct {
-      struct moberg_channel_digital_in *context;
-      int (*read)(struct moberg_channel_context *, int *value);
-    } digital_in;
-    struct  {
-      struct moberg_channel_digital_out *context;
-      int (*write)(struct moberg_channel_context *, int value);
-    } digital_out;
-    struct {
-      struct moberg_channel_encoder_in *context;
-      int (*read)(struct moberg_channel_context *, long *value);
-    } encoder_in;
+    struct moberg_analog_in analog_in;
+    struct moberg_analog_out analog_out;
+    struct moberg_digital_in digital_in;
+    struct moberg_digital_out digital_out;
+    struct moberg_encoder_in encoder_in;
   } action;
 };
   
diff --git a/moberg_device.c b/moberg_device.c
index 638179b..e4f339b 100644
--- a/moberg_device.c
+++ b/moberg_device.c
@@ -30,7 +30,8 @@ struct moberg_device {
   } *range;
 };
 
-struct moberg_device *moberg_device_new(const char *driver)
+struct moberg_device *moberg_device_new(struct moberg *moberg,
+                                        const char *driver)
 {
   struct moberg_device *result = NULL;
 
@@ -54,7 +55,7 @@ struct moberg_device *moberg_device_new(const char *driver)
     goto dlclose_driver;
   }
   result->driver = *device_driver;
-  result->device_context = result->driver.new(dlclose, handle);
+  result->device_context = result->driver.new(moberg, dlclose, handle);
   if (result->device_context) {
     result->driver.up(result->device_context);
   } else {
diff --git a/moberg_device.h b/moberg_device.h
index 0acafb2..449e243 100644
--- a/moberg_device.h
+++ b/moberg_device.h
@@ -13,7 +13,8 @@ struct moberg_parser_context;
 
 struct moberg_device_driver {
   /* Create new device context */
-  struct moberg_device_context *(*new)(int (*dlclose)(void *dlhandle),
+  struct moberg_device_context *(*new)(struct moberg *moberg,
+                                       int (*dlclose)(void *dlhandle),
                                        void *dlhandle);
   /* Use-count of device, when it reaches zero, device will be free'd */
   int (*up)(struct moberg_device_context *context);
@@ -41,7 +42,8 @@ struct moberg_device_driver {
 
 struct moberg_device;
 
-struct moberg_device *moberg_device_new(const char *driver);
+struct moberg_device *moberg_device_new(struct moberg *moberg,
+                                        const char *driver);
 
 void moberg_device_free(struct moberg_device *device);
 
diff --git a/moberg_module.h b/moberg_module.h
index 434f2c3..e3bf1b1 100644
--- a/moberg_module.h
+++ b/moberg_module.h
@@ -55,6 +55,7 @@ void moberg_parser_failed(
   FILE *f);
 
 void moberg_deferred_action(
+  struct moberg *moberg,
   int (*action)(void *param),
   void *param);
 
diff --git a/moberg_parser.c b/moberg_parser.c
index ac7bdbd..9ce96cc 100644
--- a/moberg_parser.c
+++ b/moberg_parser.c
@@ -361,7 +361,8 @@ err:
   return 0;
 }
 
-static int parse(context_t *c)
+static int parse(struct moberg *moberg,
+                 context_t *c)
 {
   for (;;) {
     if (acceptsym(c, tok_EOF, NULL)) {
@@ -381,18 +382,20 @@ static int parse(context_t *c)
                 t.u.idstr.length, t.u.idstr.value);
         goto err;
       }
-      device = moberg_device_new(name);
+      device = moberg_device_new(moberg, name);
       free(name);
       if (! device) { goto err; }
 
       if (! parse_device(c, device)) {
-        moberg_device_free(device);
-        goto err;
+        goto device_free;
       }
       if (! moberg_config_add_device(c->config, device)) {
-        moberg_device_free(device);
-        goto err;
+        goto device_free;
       }
+      continue;
+    device_free:
+      moberg_device_free(device);      
+      goto err;
     }
   }
   return 1;
@@ -403,7 +406,8 @@ err:
   return 0;
 }
 
-struct moberg_config *moberg_parse(const char *buf)
+struct moberg_config *moberg_parse(struct moberg *moberg,
+                                   const char *buf)
 {
   context_t context;
 
@@ -413,7 +417,7 @@ struct moberg_config *moberg_parse(const char *buf)
     context.buf = buf;
     context.p = context.buf;
     nextsym(&context);
-    if (! parse(&context)) {
+    if (! parse(moberg, &context)) {
       moberg_config_free(context.config);
       context.config = NULL;
     }
diff --git a/moberg_parser.h b/moberg_parser.h
index 6bfcebe..575f845 100644
--- a/moberg_parser.h
+++ b/moberg_parser.h
@@ -1,10 +1,12 @@
 #ifndef __MOBERG_PARSER_H__
 #define __MOBERG_PARSER_H__
 
+#include <moberg.h>
 #include <moberg_config.h>
 
 struct moberg_parser_context;
 
-struct moberg_config *moberg_parse(const char *buf);
+struct moberg_config *moberg_parse(struct moberg* moberg,
+                                   const char *buf);
 
 #endif
diff --git a/modules/comedi/Makefile b/modules/comedi/Makefile
index ca1e0e2..543d534 100644
--- a/modules/comedi/Makefile
+++ b/modules/comedi/Makefile
@@ -1,11 +1,10 @@
 LIBRARIES=libmoberg_comedi.so
 CCFLAGS+=-Wall -Werror -I../.. -g
-LDFLAGS+=-Lbuild/ -lmoberg
 MODULES=$(wildcard modules/*)
 
 all:	$(LIBRARIES:%=../../build/%) 
 
 ../../build/libmoberg_comedi.so: comedi.c Makefile
-	$(CC) -o $@ $(CCFLAGS) -shared -fPIC $<
+	$(CC) -o $@ $(CCFLAGS) -shared -fPIC -lcomedi $<
 
 ../../build/libmoberg_comedi.so: ../../moberg_module.h
diff --git a/modules/comedi/comedi.c b/modules/comedi/comedi.c
index d0ddead..32853eb 100644
--- a/modules/comedi/comedi.c
+++ b/modules/comedi/comedi.c
@@ -5,6 +5,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <comedilib.h>
 
 typedef enum moberg_parser_token_kind kind_t;
 typedef struct moberg_parser_token token_t;
@@ -12,10 +13,15 @@ typedef struct moberg_parser_ident ident_t;
 typedef struct moberg_parser_context context_t;
 
 struct moberg_device_context {
+  struct moberg *moberg;
   int (*dlclose)(void *dlhandle);
   void *dlhandle;
   int use_count;
   char *name;
+  struct {
+    int count;
+    comedi_t *handle;
+  } comedi;
   struct idstr {
     struct idstr *next;
     struct idstr *prev;
@@ -28,6 +34,15 @@ struct moberg_channel_context {
   void *to_free; /* To be free'd when use_count goes to zero */
   struct moberg_device_context *device;
   int use_count;
+  struct channel_descriptor {
+    int subdevice;
+    int subchannel;
+    int route;
+    lsampl_t maxdata;
+    double min;
+    double max;
+    double delta;
+  } descriptor;
 };
 
 struct moberg_channel_analog_in {
@@ -55,13 +70,29 @@ struct moberg_channel_encoder_in {
   struct moberg_channel_context channel_context;
 };
 
-static struct moberg_device_context *new_context(
-  int (*dlclose)(void *dlhandle),
-  void *dlhandle)
+static int analog_in_read(struct moberg_channel_analog_in *analog_in,
+                          double *value)
+{
+  struct channel_descriptor descriptor = analog_in->channel_context.descriptor;
+  lsampl_t data;
+  comedi_data_read(analog_in->channel_context.device->comedi.handle,
+                   descriptor.subdevice,
+                   descriptor.subchannel,
+                   0, 0, &data);
+        
+  *value = descriptor.min + data * descriptor.delta;
+  fprintf(stderr, "Data: %d %f\n", data, *value);
+  return 1;
+}
+
+static struct moberg_device_context *new_context(struct moberg *moberg,
+                                                 int (*dlclose)(void *dlhandle),
+                                                 void *dlhandle)
 {
   struct moberg_device_context *result = malloc(sizeof(*result));
   if (result) {
     memset(result, 0, sizeof(*result));
+    result->moberg = moberg;
     result->dlclose = dlclose;
     result->dlhandle = dlhandle;
     result->modprobe_list.next = &result->modprobe_list;
@@ -72,41 +103,67 @@ static struct moberg_device_context *new_context(
   return result;
 }
 
-static int device_up(struct moberg_device_context *context)
+static int device_up(struct moberg_device_context *device)
 {
-  context->use_count++;
-  return context->use_count;
+  device->use_count++;
+  return device->use_count;
 }
 
-static int device_down(struct moberg_device_context *context)
+static int device_down(struct moberg_device_context *device)
 {
-  context->use_count--;
-  int result = context->use_count;
-  if (context->use_count <= 0) {
-    moberg_deferred_action(context->dlclose, context->dlhandle);
-    free(context->name);
+  device->use_count--;
+  int result = device->use_count;
+  if (device->use_count <= 0) {
+    moberg_deferred_action(device->moberg,
+                           device->dlclose, device->dlhandle);
+    free(device->name);
     struct idstr *e;
     
-    e = context->modprobe_list.next;
-    while (e != &context->modprobe_list) {
+    e = device->modprobe_list.next;
+    while (e != &device->modprobe_list) {
       struct idstr *next = e->next;
       free(e->value);
       free(e);
       e = next;
     }
-    e = context->config_list.next;
-    while (e != &context->config_list) {
+    e = device->config_list.next;
+    while (e != &device->config_list) {
       struct idstr *next = e->next;
       free(e->value);
       free(e);
       e = next;
     }
-    free(context);
+    free(device);
     return 0;
   }
   return result;
 }
 
+static int device_open(struct moberg_device_context *device)
+{
+  if (device->comedi.count == 0) {
+    device->comedi.handle = comedi_open(device->name);
+    if (device->comedi.handle == NULL) {
+      goto err;
+    }
+  }
+  device->comedi.count++;
+  return 1;
+err:
+  fprintf(stderr, "Failed to open %s\n", device->name);
+  return 0;
+}
+
+static int device_close(struct moberg_device_context *device)
+{
+  device->comedi.count--;
+  if (device->comedi.count == 0) {
+    comedi_close(device->comedi.handle);
+  }
+
+  return 1;
+}
+
 static int channel_up(struct moberg_channel *channel)
 {
   device_up(channel->context->device);
@@ -125,22 +182,73 @@ static int channel_down(struct moberg_channel *channel)
   return channel->context->use_count;
 }
 
+static int channel_open(struct moberg_channel *channel)
+{
+  channel_up(channel);
+  if (! device_open(channel->context->device)) { goto err; }
+  fprintf(stderr, "Open %s[%d][%d]\n",
+          channel->context->device->name, 
+          channel->context->descriptor.subdevice, 
+          channel->context->descriptor.subchannel);
+  lsampl_t maxdata;
+  comedi_range *range;
+          
+  maxdata = comedi_get_maxdata(channel->context->device->comedi.handle,
+                               channel->context->descriptor.subdevice,
+                               channel->context->descriptor.subchannel);
+  range = comedi_get_range(channel->context->device->comedi.handle,
+                           channel->context->descriptor.subdevice,
+                           channel->context->descriptor.subchannel,
+                           0);
+
+  if (! maxdata) {
+    fprintf(stderr, "Failed to get maxdata for %s[%d][%d]\n",
+            channel->context->device->name, 
+            channel->context->descriptor.subdevice, 
+            channel->context->descriptor.subchannel);
+    goto err;
+  }
+  if (! range) {
+    fprintf(stderr, "Failed to get range for %s[%d][%d]\n",
+            channel->context->device->name, 
+            channel->context->descriptor.subdevice, 
+            channel->context->descriptor.subchannel);
+    goto err;
+  }
+  channel->context->descriptor.maxdata = maxdata;
+  channel->context->descriptor.min = range->min;
+  channel->context->descriptor.max = range->max;
+  channel->context->descriptor.delta = (range->max - range->min) / maxdata;
+  return 1;
+err:
+  return 0;
+}
+
+static int channel_close(struct moberg_channel *channel)
+{
+  device_close(channel->context->device);
+  channel_down(channel);
+  return 1;  
+}
+
 static void init_channel(struct moberg_channel *channel,
                          void *to_free,
                          struct moberg_channel_context *context,
                          struct moberg_device_context *device,
+                         struct channel_descriptor descriptor,
                          enum moberg_channel_kind kind,
                          union moberg_channel_action action)
 {
   context->to_free = to_free;
   context->device = device;
   context->use_count = 0;
+  context->descriptor = descriptor;
   
   channel->context = context;
   channel->up = channel_up;
   channel->down = channel_down;
-  channel->open = NULL;
-  channel->close = NULL;
+  channel->open = channel_open;
+  channel->close = channel_close;
   channel->kind = kind;
   channel->action = action;
 };
@@ -252,7 +360,7 @@ static int parse_map(struct moberg_device_context *device,
                      enum moberg_channel_kind kind,
                      struct moberg_channel_map *map)
 {
-  token_t min, max;
+  token_t min, max, route={ .u.integer.value=0 };
 
   if (! acceptsym(c, tok_LBRACE, NULL)) { goto err; }
   for (;;) {
@@ -262,7 +370,6 @@ static int parse_map(struct moberg_device_context *device,
     if (! acceptsym(c, tok_INTEGER, &subdevice)) { goto err; }
     if (! acceptsym(c, tok_RBRACKET, NULL)) { goto err; }
     if (acceptkeyword(c, "route")) {
-      token_t route;
       if (! acceptsym(c, tok_INTEGER, &route)) { goto err; }
     }
     if (! acceptsym(c, tok_LBRACKET, NULL)) { goto err; }
@@ -273,6 +380,14 @@ static int parse_map(struct moberg_device_context *device,
       max = min;
     }
     for (int i = min.u.integer.value ; i <= max.u.integer.value ; i++) {
+      struct channel_descriptor descriptor = {
+        .subdevice=subdevice.u.integer.value,
+        .subchannel=i,
+        .route=route.u.integer.value,
+        .maxdata=0,
+        .min=0.0,
+        .max=0.0
+      };
       switch (kind) {
         case chan_ANALOGIN: {
           struct moberg_channel_analog_in *channel =
@@ -283,10 +398,11 @@ static int parse_map(struct moberg_device_context *device,
                          channel,
                          &channel->channel_context,
                          device,
+                         descriptor,
                          kind,
                          (union moberg_channel_action) {
                            .analog_in.context=channel,
-                           .analog_in.read=NULL });
+                           .analog_in.read=analog_in_read });
             map->map(map->device, &channel->channel);
           }
         } break;
@@ -299,6 +415,7 @@ static int parse_map(struct moberg_device_context *device,
                          channel,
                          &channel->channel_context,
                          device,
+                         descriptor,
                          kind,
                          (union moberg_channel_action) {
                            .analog_out.context=channel,
@@ -315,6 +432,7 @@ static int parse_map(struct moberg_device_context *device,
                          channel,
                          &channel->channel_context,
                          device,
+                         descriptor,
                          kind,
                          (union moberg_channel_action) {
                            .digital_in.context=channel,
@@ -331,6 +449,7 @@ static int parse_map(struct moberg_device_context *device,
                          channel,
                          &channel->channel_context,
                          device,
+                         descriptor,
                          kind,
                          (union moberg_channel_action) {
                            .digital_out.context=channel,
@@ -347,6 +466,7 @@ static int parse_map(struct moberg_device_context *device,
                          channel,
                          &channel->channel_context,
                          device,
+                         descriptor,
                          kind,
                          (union moberg_channel_action) {
                            .encoder_in.context=channel,
diff --git a/modules/serial2002/serial2002.c b/modules/serial2002/serial2002.c
index 8b9404e..5674770 100644
--- a/modules/serial2002/serial2002.c
+++ b/modules/serial2002/serial2002.c
@@ -11,6 +11,7 @@ typedef struct moberg_parser_token token_t;
 typedef struct moberg_parser_context context_t;
 
 struct moberg_device_context {
+  struct moberg *moberg;
   int (*dlclose)(void *dlhandle);
   void *dlhandle;
   int use_count;
@@ -49,13 +50,14 @@ struct moberg_channel_encoder_in {
   struct moberg_channel_context channel_context;
 };
 
-static struct moberg_device_context *new_context(
-  int (*dlclose)(void *dlhandle),
-  void *dlhandle)
+static struct moberg_device_context *new_context(struct moberg *moberg,
+                                                 int (*dlclose)(void *dlhandle),
+                                                 void *dlhandle)
 {
   struct moberg_device_context *result = malloc(sizeof(*result));
   if (result) {
     memset(result, 0, sizeof(*result));
+    result->moberg = moberg;
     result->dlclose = dlclose;
     result->dlhandle = dlhandle;
   }
@@ -72,7 +74,8 @@ static int device_down(struct moberg_device_context *context)
 {
   context->use_count--;
   if (context->use_count <= 0) {
-    moberg_deferred_action(context->dlclose, context->dlhandle);
+    moberg_deferred_action(context->moberg,
+                           context->dlclose, context->dlhandle);
     free(context->name);
     free(context);
     return 0;
diff --git a/test/.config/moberg.d/moberg.conf b/test/.config/moberg.d/moberg.conf
index 8db1afc..9332da8 100644
--- a/test/.config/moberg.d/moberg.conf
+++ b/test/.config/moberg.d/moberg.conf
@@ -23,6 +23,17 @@ driver(comedi) {
                              subdevice[7] route 16 [13],
                              subdevice[7] route 16 [12] };
 }
+driver(comedi) {
+    config {
+        /* Parsed by parse_config in libmoberg_comedi.so */
+        device = "/dev/comedi0" ;
+        modprobe = [ comedi serial2002 ] ;
+        config = [ serial2002 "100" "115200" ] ;
+    }
+    /* Moberg mapping[indices] = {driver specific}[indices]
+      {driver specific} is parsed by parse_map in libmoberg_comedi.so */
+    map analog_in[0:1] = { subdevice[2][0:1] };
+}
 driver(serial2002) {
     config {
         /* Parsed by parse_config in libmoberg_serial2002.so */
diff --git a/test/Makefile b/test/Makefile
index c23d259..1515606 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,15 +1,23 @@
-TEST = test_start_stop
+TEST = test_start_stop test_io
+
+ENV_TEST = LD_LIBRARY_PATH=../build HOME=.
 
-ENV_test_start_stop = LD_LIBRARY_PATH=../build HOME=. 
 all: 
 
 .PHONY: test
 test: $(TEST:%=run_%)
-	echo Testing
-	exit 1
+	echo Tests run
+
+run_%:	build/%
+	$(ENV_TEST) valgrind --leak-check=full ./build/$*
 
-run_%:	%
-	$(ENV_$*) valgrind --leak-check=full ./$*
+.PRECIOUS: build/%
 
-%:	%.c
+build/%: %.c | build
 	$(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $<
+
+build:
+	mkdir build
+
+clean:
+	rm -f vgcore.* *~
diff --git a/test/test_io.c b/test/test_io.c
new file mode 100644
index 0000000..4e0def3
--- /dev/null
+++ b/test/test_io.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <moberg.h>
+
+int main(int argc, char *argv[])
+{
+  struct moberg *moberg = moberg_new(NULL);
+  struct moberg_analog_in ai0;
+  double ai0_value;
+  moberg_analog_in_open(moberg, 0, &ai0);
+  ai0.read(ai0.context, &ai0_value); 
+  fprintf(stderr, "READ ai0: %f\n", ai0_value);
+  moberg_analog_in_close(moberg, 0, ai0);
+  moberg_free(moberg);
+}
-- 
GitLab