summaryrefslogtreecommitdiffstats
path: root/binding
diff options
context:
space:
mode:
authorClément Bénier <clement.benier@iot.bzh>2018-06-07 12:43:27 +0200
committercle©mentbeénier <clement.benier@iot.bzh>2018-06-21 14:23:37 +0200
commited02e81650ab383f11622c42bb1bfdd125e511f1 (patch)
treefe422bbf48c2af77efa68ca2969921018b6470a9 /binding
parent4bea2d73a5de0f8ec83c61edc37653718d34f31b (diff)
binding iiodevices: handles 3 iiodevices
handle accel, magn et anglvel iiodevices args key indicates the desired coordinates at subcription frequency can be also specified at subscription Example for subscription: - iiodevices subscribe { "event": "accel", "uid": "1", "args": "xyz", "frequency": "0.1" } - iiodevices subscribe { "event": "magn", "uid": "1", "args": "xz", "frequency": "0.1" } Change-Id: I7f300f56b5d69506434f31fbb6e552c7afdf7489 Signed-off-by: Clément Bénier <clement.benier@iot.bzh> Signed-off-by: clement benier <clement.benier@iot.bzh>
Diffstat (limited to 'binding')
-rw-r--r--binding/CMakeLists.txt14
-rw-r--r--binding/accelero-binding.c167
-rw-r--r--binding/config_iiodevices.c51
-rw-r--r--binding/config_iiodevices.h28
-rw-r--r--binding/iiodevices-binding.c571
5 files changed, 656 insertions, 175 deletions
diff --git a/binding/CMakeLists.txt b/binding/CMakeLists.txt
index 05992ff..4731dc5 100644
--- a/binding/CMakeLists.txt
+++ b/binding/CMakeLists.txt
@@ -18,13 +18,15 @@
###########################################################################
# Add target to project dependency list
-PROJECT_TARGET_ADD(accelero-binding)
+PROJECT_TARGET_ADD(iiodevices-binding)
# Define project Targets
- set(accelero_SOURCES
- accelero-binding.c)
+ set(iiodevices_SOURCES
+ iiodevices-binding.c
+ config_iiodevices.c
+ )
- add_library(${TARGET_NAME} MODULE ${accelero_SOURCES})
+ add_library(${TARGET_NAME} MODULE ${iiodevices_SOURCES})
# Binder exposes a unique public entry point
SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
@@ -36,7 +38,3 @@ PROJECT_TARGET_ADD(accelero-binding)
# Library dependencies (include updates automatically)
TARGET_LINK_LIBRARIES(${TARGET_NAME} ${link_libraries})
-
- # installation directory
- INSTALL(TARGETS ${TARGET_NAME}
- LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR})
diff --git a/binding/accelero-binding.c b/binding/accelero-binding.c
deleted file mode 100644
index d7f05e9..0000000
--- a/binding/accelero-binding.c
+++ /dev/null
@@ -1,167 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-#include <glib.h>
-
-#include <json-c/json.h>
-#define AFB_BINDING_VERSION 2
-#include <afb/afb-binding.h>
-
-#define IIODEVICE "/sys/bus/iio/devices/iio:device"
-#define IIODEVICE0 "/sys/bus/iio/devices/iio:device0/"
-#define IIODEVICE1 "/sys/bus/iio/devices/iio:device1/"
-
-static struct afb_event accel_event;
-static struct afb_event magn_event;
-static struct afb_event anglvel_event;
-
-static pthread_t thread;
-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static void get_buffer(const char *iio_file, const char *sub_name,
- void *buffer, size_t count)
-{
- int fd;
- fd = open(iio_file, O_RDONLY);
- if(fd < 0) {
- AFB_ERROR("Cannot open %s\n", iio_file);
- return;
- }
- if(read(fd, buffer, count) < 0){
- AFB_ERROR("cannot read %s\n", iio_file);
- return;
- }
- close(fd);
-}
-
-static void get_subdata_string(const char * iio_device_path, const char *sub_name,
- json_object *jresp) {
- char iio_file[100];
- sprintf(iio_file, "%s%s", iio_device_path, sub_name);
- size_t buffer_size = 1000;
- char buffer[buffer_size];
-
- get_buffer(iio_file, sub_name, buffer, buffer_size);
-
- json_object *value = json_object_new_string(buffer);
- if(jresp)
- json_object_object_add(jresp, sub_name, value);
-}
-
-static void get_subdata_int(const char * iio_device_path, const char *sub_name,
- json_object *jresp) {
- char iio_file[100];
- sprintf(iio_file, "%s%s", iio_device_path, sub_name);
- AFB_INFO("iio_file=%s", iio_file);
- size_t buffer_size = 1000;
- char buffer[buffer_size];
-
- get_buffer(iio_file, sub_name, buffer, buffer_size);
-
- json_object *value = json_object_new_int(atoi(buffer));
- if(jresp)
- json_object_object_add(jresp, sub_name, value);
-}
-
-static void get_data(const char *name, const int iio_device, json_object *jresp) {
- char iio_device_path[100];
- sprintf(iio_device_path, IIODEVICE"%d/in_%s_", iio_device, name);
-
- get_subdata_string(iio_device_path, "scale", jresp);
- get_subdata_string(iio_device_path, "scale_available", jresp);
- get_subdata_int(iio_device_path, "x_raw", jresp);
- get_subdata_int(iio_device_path, "y_raw", jresp);
- get_subdata_int(iio_device_path, "z_raw", jresp);
-}
-
-static int treat_event(const char *name, const int device_number,
- struct afb_event event)
-{
- json_object *jresp = json_object_new_object();
- get_data(name, device_number, jresp);
- AFB_INFO("push event %s\n", name);
- pthread_mutex_lock(&mutex);
- afb_event_push(event, json_object_get(jresp));
- pthread_mutex_unlock(&mutex);
- return 0;
-}
-
-gboolean data_poll(gpointer ptr)
-{
- AFB_INFO("data_poll");
- treat_event("accel", 0, accel_event);
- treat_event("magn", 0, magn_event);
- treat_event("anglvel", 1, anglvel_event);
- return TRUE;
-}
-
-static void *data_thread(void *ptr)
-{
- g_timeout_add_seconds(1, data_poll, NULL);
- g_main_loop_run(g_main_loop_new(NULL, FALSE));
- return NULL;
-}
-
-static void subscribe(struct afb_req request)
-{
- const char *value = afb_req_value(request, "event");
- if(value) {
- if(!strcasecmp(value, "accel")) {
- afb_req_subscribe(request, accel_event);
- } else if(!strcasecmp(value, "magn")) {
- afb_req_subscribe(request, magn_event);
- } else if(!strcasecmp(value, "anglvel")) {
- afb_req_subscribe(request, anglvel_event);
- } else {
- afb_req_fail(request, "failed", "Invalid event");
- return;
- }
- }
- afb_req_success(request, NULL, NULL);
-}
-
-static void unsubscribe(struct afb_req request)
-{
- const char *value = afb_req_value(request, "value");
- if(value) {
- if(!strcasecmp(value, "accel")) {
- afb_req_unsubscribe(request, accel_event);
- } else if(!strcasecmp(value, "magn")) {
- afb_req_unsubscribe(request, magn_event);
- } else {
- afb_req_fail(request, "failed", "Invalid event");
- return;
- }
- }
- afb_req_success(request, NULL, NULL);
-}
-
-const afb_verb_v2 verbs[] = {
- { .verb = "subscribe", .session = AFB_SESSION_NONE, .callback = subscribe, .info = "Subscribe for an event" },
- { .verb = "unsubscribe", .session = AFB_SESSION_NONE, .callback = unsubscribe, .info = "Unsubscribe for an event" },
- { .verb=NULL }
-};
-
-static int init()
-{
- accel_event = afb_daemon_make_event("accel");
- magn_event = afb_daemon_make_event("magn");
- anglvel_event = afb_daemon_make_event("anglvel");
-
- pthread_create(&thread, NULL, &data_thread, NULL);
-
- return 0;
-}
-
-const afb_binding_v2 afbBindingV2 = {
- .info = "accelero service",
- .api = "accelero",
- .verbs = verbs,
- .init = init,
-};
diff --git a/binding/config_iiodevices.c b/binding/config_iiodevices.c
new file mode 100644
index 0000000..d8f9d9e
--- /dev/null
+++ b/binding/config_iiodevices.c
@@ -0,0 +1,51 @@
+#include "config_iiodevices.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+void set_channel_name(char *name, enum iio_elements iioelts)
+{
+ switch(iioelts) {
+ case X:
+ strcat(name, "_x"); break;
+ case Y:
+ strcat(name, "_y"); break;
+ case Z:
+ strcat(name, "_z"); break;
+ default:
+ AFB_WARNING("cannot set channel name, no matching iio_elements found: %d", iioelts);
+ }
+}
+
+enum iio_elements treat_iio_elts(const char *iioelts_string)
+{
+ enum iio_elements iioelts = 0;
+ if(!iioelts_string || !strcasecmp(iioelts_string, ""))
+ iioelts = X | Y | Z;
+ else {
+ if(strchr(iioelts_string, 'x') || strchr(iioelts_string, 'X'))
+ iioelts = iioelts | X;
+ if(strchr(iioelts_string, 'y') || strchr(iioelts_string, 'Y'))
+ iioelts = iioelts | Y;
+ if(strchr(iioelts_string, 'z') || strchr(iioelts_string, 'Z'))
+ iioelts = iioelts | Z;
+ }
+ AFB_INFO("ENUMERATION::: %s %d", iioelts_string, iioelts);
+ return iioelts;
+}
+
+int get_iio_nb(enum iio_elements iioelts)
+{
+ int nb_iioelts = 0;
+ int tmp_mask = 1;
+ while(tmp_mask <= iioelts) { //the number of channels is the number of elements
+ if((iioelts & tmp_mask) == tmp_mask)
+ nb_iioelts++;
+ tmp_mask = tmp_mask << 1;
+ }
+ return nb_iioelts;
+}
diff --git a/binding/config_iiodevices.h b/binding/config_iiodevices.h
new file mode 100644
index 0000000..9e99045
--- /dev/null
+++ b/binding/config_iiodevices.h
@@ -0,0 +1,28 @@
+#ifndef _CONFIG_IIODEVICES_
+#define _CONFIG_IIODEVICES_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define IIODEVICE "/sys/bus/iio/devices/"
+
+struct iio_info {
+ const char *dev_name;
+ const char *id;
+};
+
+#define IIO_INFOS_SIZE 3
+static struct iio_info iio_infos[IIO_INFOS_SIZE] = {
+ { "16-001d", "accel"},
+ { "16-001d", "magn"},
+ { "16-006b", "anglvel"}
+};
+
+enum iio_elements { X = 1, Y = 2, Z = 4 };
+
+void set_channel_name(char *name, enum iio_elements iioelts);
+enum iio_elements treat_iio_elts(const char *iioelts_string);
+enum iio_infos treat_iio_infos(const char *infos);
+int get_iio_nb(enum iio_elements iioelts);
+
+#endif //_CONFIG_IIODEVICES_
diff --git a/binding/iiodevices-binding.c b/binding/iiodevices-binding.c
new file mode 100644
index 0000000..cb233e2
--- /dev/null
+++ b/binding/iiodevices-binding.c
@@ -0,0 +1,571 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <systemd/sd-event.h>
+#include <time.h>
+
+#include <json-c/json.h>
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+#include <iio.h>
+
+#include "config_iiodevices.h"
+
+/*structure in order to handle client_channels connections*/
+struct client_channels {
+ struct iio_channel *chn;
+ int index;
+ char uid[100];
+ int fd;
+ uint64_t u_period;
+ enum iio_elements iioelts;
+ char name[100];
+ struct iio_device *dev;
+ struct afb_event *event;
+ struct iio_info *infos;
+ json_object *jobject;
+ struct client_channels *first;
+ struct client_channels *next;
+};
+
+struct s_channels {
+ struct client_channels *channels;
+ unsigned int nb;
+};
+
+/*gather all afb_event*/
+struct event
+{
+ struct event *next;
+ struct afb_event event;
+ unsigned int nb_subscribers;
+ char tag[100];
+};
+
+static struct event *events = 0;
+static struct iio_context *ctx = NULL;
+static struct s_channels channels = { NULL, 0 };
+
+static struct event *event_get_event(const struct afb_event *event)
+{
+ struct event *e = events;
+ while(&e->event != event) {
+ e = e->next;
+ }
+ return e;
+}
+
+static void event_add_subscriber(struct afb_event *event)
+{
+ struct event *e;
+ e = event_get_event(event);
+ if(e)
+ e->nb_subscribers++;
+ else
+ AFB_WARNING("event not found");
+}
+
+/* searchs the event of tag */
+static struct event *event_get(const char *tag)
+{
+ struct event *e = events;
+ while(e && strcmp(e->tag, tag))
+ e = e->next;
+ return e;
+}
+
+/* creates the event of tag */
+static struct afb_event* event_add(const char *tag)
+{
+ struct event *e;
+
+ /* check valid tag */
+ e = event_get(tag);
+ if (e) return NULL;
+
+ /* creation */
+ e = malloc(strlen(tag) + sizeof *e);
+ if (!e) return NULL;
+ strcpy(e->tag, tag);
+
+ /* make the event */
+ e->event = afb_daemon_make_event(tag);
+ if (!e->event.closure) { free(e); return NULL; }
+
+ /* link */
+ e->next = events;
+ e->nb_subscribers++;
+ events = e;
+ return &e->event;
+}
+
+/* deletes the event of event */
+static int event_del(struct afb_event *event)
+{
+ struct event *e, **p;
+
+ /* check exists */
+ e = event_get_event(event);
+ if (!e) return -1;
+
+ e->nb_subscribers--;
+ if(e->nb_subscribers > 0)
+ return 0;
+ else {
+ /* unlink */
+ p = &events;
+ while(*p != e) p = &(*p)->next;
+ *p = e->next;
+
+ /* destroys */
+ afb_event_unref(e->event);
+ free(e);
+ }
+ return 0;
+}
+
+int init_context()
+{
+ ctx = iio_create_local_context();
+ if(!ctx)
+ {
+ AFB_ERROR("cannot create local iio context");
+ return -1;
+ }
+ return 0;
+}
+
+static struct iio_device *init_dev(const char *dev_name)
+{
+ if(!ctx)
+ init_context();
+ struct iio_device *dev = iio_context_find_device(ctx, dev_name);
+ if(!dev) {
+ AFB_ERROR("No %s device found", dev_name);
+ return NULL;
+ }
+ return dev;
+}
+
+static int read_infos(struct client_channels *cl_chn)
+{
+ if(!cl_chn) {
+ AFB_ERROR("cl_chn is null");
+ return -1;
+ }
+ struct iio_channel *chn = cl_chn->chn;
+ json_object *jobject = cl_chn->jobject;
+ char val[100];
+
+ //read all infos
+ for(int i = 0; i < iio_channel_get_attrs_count(chn); i++) {
+ const char *id_attr = iio_channel_get_attr(chn, i);
+ if(strcasecmp(id_attr, "raw")) { //do not take raw infos
+ iio_channel_attr_read(chn, id_attr, val, 100);
+ json_object *value = json_object_new_string(val);
+ json_object_object_add(jobject, id_attr, value);
+ }
+ }
+ return 0;
+}
+
+static void deinit_channels(struct client_channels *cl_chn)
+{
+ if(!cl_chn || !channels.channels || channels.nb == 0) {
+ AFB_ERROR("null pointer");
+ return;
+ }
+
+ int i = 0;
+ while(&channels.channels[i] != cl_chn->first) //looking for index matching cl_chn
+ i++;
+
+ int offset = 0;
+ while(channels.channels[i + offset].next) { //looking for number of channels a client
+ iio_channel_disable(channels.channels[i + offset].chn);
+ offset++;
+ }
+
+ while(i < channels.nb - offset) { //move above all remained channels
+ channels.channels[i] = channels.channels[i + offset + 1];
+ i++;
+ }
+
+ channels.nb -= offset + 1;
+ channels.channels = realloc(channels.channels, channels.nb * sizeof(struct client_channels));
+
+ if(channels.nb == 0)
+ channels.channels = NULL;
+ AFB_DEBUG("deinit channel: size=%d", channels.nb);
+}
+
+static void close_fd(struct client_channels *cl_chn)
+{
+ if(!cl_chn) {
+ AFB_ERROR("null pointer");
+ return;
+ }
+
+ close(cl_chn->fd);
+}
+
+static void desallocate_channels(sd_event_source* src,
+ struct client_channels *cl_chn)
+{
+ afb_event_drop(*cl_chn->event);
+ if(src) {
+ sd_event_source_set_enabled(src, SD_EVENT_OFF);
+ sd_event_source_unref(src);
+ }
+ close_fd(cl_chn);
+ event_del(cl_chn->event);
+ deinit_channels(cl_chn);
+}
+
+static int read_data(struct client_channels *cl_chn, sd_event_source* src)
+{
+ if(!cl_chn) {
+ AFB_ERROR("cl_chn is null");
+ return -1;
+ }
+ struct iio_channel *chn = NULL;
+ json_object *jobject = cl_chn->jobject;
+ char val[10];
+
+ struct client_channels *p_cl_chn = cl_chn->first;
+ AFB_DEBUG("TOTOTO %p", p_cl_chn);
+ while(p_cl_chn) {
+ chn = p_cl_chn->chn;
+ if(!chn) {
+ AFB_ERROR("chn is null for cl_chn=%s", p_cl_chn->name);
+ return -1;
+ }
+ iio_channel_attr_read(chn, "raw", val, 10);
+ int data = (int)strtol(val, NULL, 10);
+ AFB_DEBUG("read_data: %s %d", iio_channel_get_id(chn), data);
+ json_object *value = json_object_new_int(data);
+ json_object_object_add(jobject, p_cl_chn->name, value);
+ p_cl_chn = p_cl_chn->next;
+ }
+
+ /*if no more subscribers -> desallocate*/
+ if(afb_event_push(*cl_chn->event, json_object_get(cl_chn->jobject)) <= 0)
+ desallocate_channels(src, cl_chn);
+ return 0;
+}
+
+static int read_data_push(sd_event_source* src, int fd, uint32_t revents, void* userdata)
+{
+ struct client_channels *cl_chn = (struct client_channels *)userdata;
+ read_data(cl_chn, src);
+ return 0;
+}
+
+static int read_data_timer(sd_event_source* src, uint64_t usec, void *userdata)
+{
+ struct client_channels *cl_chn = (struct client_channels *)userdata;
+
+ /*set next time to trigger*/
+ uint64_t usecs;
+ sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usecs);
+ usecs += cl_chn->u_period;
+ sd_event_source_set_time(src, usecs);
+
+ read_data(cl_chn, src);
+ return 0;
+}
+
+static void init_event_io(struct iio_device *dev, struct client_channels *cl_chn)
+{
+ if(!dev || !cl_chn) {
+ AFB_ERROR("dev or cl_chn is null");
+ return;
+ }
+ read_infos(cl_chn); //get unchanged infos
+ sd_event_source *source = NULL;
+ if(cl_chn->u_period <= 0) { //no given frequency
+ char filename[100];
+ sprintf(filename, IIODEVICE"%s/%s", iio_device_get_id(dev),
+ iio_channel_attr_get_filename(cl_chn->chn, "raw"));
+ if((cl_chn->fd = open(filename, O_RDONLY)) < 0) {
+ AFB_ERROR("cannot open %s file", filename);
+ return;
+ }
+
+ sd_event_add_io(afb_daemon_get_event_loop(), &source, cl_chn->fd, EPOLLIN, read_data_push, cl_chn);
+ }
+ else {
+ sd_event_add_time(afb_daemon_get_event_loop(), &source, CLOCK_MONOTONIC, 0, 1, read_data_timer, cl_chn);
+ sd_event_source_set_enabled(source, SD_EVENT_ON);
+ }
+}
+
+/*initialise channel_fd*/
+static struct client_channels *set_channel(const int index,
+ enum iio_elements i, struct client_channels *first)
+{
+ int g_index = channels.nb + index;
+ char *channel_name = channels.channels[g_index].name;
+
+ /*initialise structure*/
+ channels.channels[g_index].infos = first->infos;
+ channels.channels[g_index].next = NULL;
+ channels.channels[g_index].first = first;
+ channels.channels[g_index].fd = -1;
+ channels.channels[g_index].iioelts = i;
+ channels.channels[g_index].index = first->index;
+ channels.channels[g_index].u_period = first->u_period;
+ channels.channels[g_index].jobject = first->jobject;
+ channels.channels[g_index].dev = first->dev;
+
+ /*set channel name with iio_elements*/
+ strcpy(channel_name, first->infos->id);
+ set_channel_name(channel_name, i);
+
+ if(!(channels.channels[g_index].chn = iio_device_find_channel(first->dev, channel_name, false))) {
+ AFB_ERROR("cannot find %s channel", channel_name);
+ return NULL;
+ }
+ if(g_index > channels.nb) {
+ channels.channels[g_index].event = channels.channels[channels.nb].event;
+ channels.channels[g_index - 1].next = &channels.channels[g_index];
+ strcpy(channels.channels[g_index].uid, first->uid);
+ }
+ iio_channel_enable(channels.channels[g_index].chn);
+ return &channels.channels[g_index];
+}
+
+static enum iio_elements get_all_iioets(struct client_channels *cl_chn)
+{
+ if(!cl_chn) {
+ AFB_ERROR("cl_chn is null");
+ return 0;
+ }
+ enum iio_elements iioelts = 0;
+ struct client_channels *it_cl_chn = cl_chn->first;
+ while(it_cl_chn) {
+ iioelts |= it_cl_chn->iioelts;
+ it_cl_chn = it_cl_chn->next;
+ }
+ return iioelts;
+}
+
+static struct afb_event* is_allocation_needed(struct iio_info *infos,
+ enum iio_elements *iioelts, struct client_channels **first)
+{
+ struct client_channels *cl_chn = channels.channels;
+ //check if already allocated
+ while(cl_chn) {
+ if(cl_chn->infos == infos) {
+ *iioelts = ~get_all_iioets(cl_chn) & *iioelts;
+ if(*iioelts == 0) { //iio elts already treated
+ AFB_DEBUG("iioelts is already treated");
+ return cl_chn->event;
+ }
+ else {
+ *first = cl_chn->first;
+ }
+ break;
+ }
+ cl_chn += sizeof(struct client_channels);
+ }
+ return NULL;
+}
+
+static void init_channel(struct iio_device *dev,
+ struct iio_info *infos, const int iioelts, struct client_channels **first,
+ const uint64_t u_period, const char* uid)
+{
+ if(!dev || !infos) {
+ AFB_ERROR("dev=%p or infos=%p is null", dev, infos);
+ return;
+ }
+ char event_name[100];
+ const char *name = infos->id;
+ int nb_iioelts = get_iio_nb(iioelts);
+
+ AFB_INFO("size of channels=%d", nb_iioelts);
+ channels.channels = realloc(channels.channels, (channels.nb + nb_iioelts) * sizeof(struct client_channels));
+ if(!channels.channels) {
+ AFB_ERROR("alloc failed for channels");
+ return;
+ }
+
+ if(!*first) { //first channel for this client
+ int index_cl_chn
+ = channels.nb == 0 ? 0 : channels.channels[channels.nb - 1].index + 1;
+ sprintf(event_name, "%s%d", name, index_cl_chn);
+ channels.channels[channels.nb].index = index_cl_chn;
+ channels.channels[channels.nb].event = event_add(event_name);
+ channels.channels[channels.nb].jobject = json_object_new_object();
+ channels.channels[channels.nb].u_period = u_period;
+ channels.channels[channels.nb].dev = dev;
+ channels.channels[channels.nb].infos = infos;
+ strcpy(channels.channels[channels.nb].uid, uid);
+ *first = &channels.channels[channels.nb];
+ }
+ else
+ event_add_subscriber((*first)->event);
+ channels.channels[channels.nb].first = *first;
+
+ int index = 0;
+ for(int i = 1; i <= iioelts; i = i << 1) { //iterate on enumerations
+ if(i == (i & iioelts)) {
+ set_channel(index, i, *first);
+ index++;
+ }
+ }
+ channels.nb = channels.nb + nb_iioelts;
+}
+
+static uint64_t get_period(const char* freq)
+{
+ double frequency = 0;
+ if(freq)
+ frequency = strtod(freq, NULL);
+ return (frequency == 0) ? 0 : (uint64_t)((1.0 / frequency) * 1000000);
+}
+
+static bool is_new_event_needed(struct iio_info *infos, const uint64_t u_period)
+{
+ if(channels.nb == 0)
+ return true;
+
+ for(int i = 0; i < channels.nb; i++) {
+ if(channels.channels[i].infos == infos)
+ if(channels.channels[i].u_period == u_period) //no need for new event
+ return false;
+ }
+ return true;
+}
+
+struct client_channels *looking_for_chn(struct iio_info *infos, const char* uid)
+{
+ struct client_channels *cl_chn = NULL;
+ int i = 0;
+ while(i < channels.nb) {
+ cl_chn = &channels.channels[i];
+ if(!strcasecmp(uid, cl_chn->uid) && cl_chn->infos == infos)
+ return cl_chn;
+ i++;
+ }
+ return NULL;
+}
+
+static bool is_uid_used(struct iio_info *infos, const char* uid)
+{
+ return looking_for_chn(infos, uid);
+}
+
+static void subscribe(struct afb_req request)
+{
+ const char *value = afb_req_value(request, "event");
+ const char *uid = afb_req_value(request, "uid");
+ const char *s_iioelts = afb_req_value(request, "args");
+ const char *freq = afb_req_value(request, "frequency");
+
+ if(!value || !uid || !s_iioelts) {
+ afb_req_fail(request, "failed", "please, fill 'event', 'uid' and 'args' fields");
+ return;
+ }
+
+ AFB_INFO("subscription with: value=%s, uid=%s, s_iioelts=%s, freq=%s",
+ value, uid, s_iioelts, freq);
+
+ bool found = false;
+ for(int i = 0; i < IIO_INFOS_SIZE; i++) {
+ if(!strcasecmp(value, iio_infos[i].id)) {
+ found = true;
+ if(is_uid_used(&iio_infos[i], uid)) {
+ afb_req_fail(request, "failed", "uid is already used for this event, please changed");
+ return;
+ }
+
+ struct iio_device *dev = init_dev(iio_infos[i].dev_name);
+
+ uint64_t u_period = get_period( freq);
+ enum iio_elements iioelts = (int)treat_iio_elts(s_iioelts);
+
+ struct client_channels *first = NULL;
+ struct afb_event* p_event = NULL;
+ bool event_needed = is_new_event_needed(&iio_infos[i], u_period);
+ if(!event_needed) {
+ /*is new client_channel needed*/
+ p_event = is_allocation_needed(&iio_infos[i],
+ &iioelts, &first);
+ }
+ if(!p_event || event_needed) { //new cl_chn or event_needed
+ init_channel(dev, &iio_infos[i], iioelts, &first, u_period,
+ uid);
+ if(!first) {
+ AFB_ERROR("first is null");
+ return;
+ }
+ init_event_io(dev, first);
+ p_event = first->event;
+ }
+
+ if(afb_req_subscribe(request, *p_event) != 0) {
+ afb_req_fail(request, "failed", "subscription failed");
+ return;
+ }
+ }
+ }
+ if(!found) {
+ afb_req_fail(request, "failed", "Invalid event");
+ return;
+ }
+ afb_req_success(request, NULL, NULL);
+}
+
+static void unsubscribe(struct afb_req request)
+{
+ const char *value = afb_req_value(request, "event");
+ const char *uid = afb_req_value(request, "uid");
+ if(!value || !uid) {
+ afb_req_fail(request, "failed", "please, fill 'event', 'uid' fields");
+ return;
+ }
+
+ bool found = false;
+ if(value) {
+ for(int i = 0; i < IIO_INFOS_SIZE; i++) {
+ if(!strcasecmp(value, iio_infos[i].id)) {
+ found = true;
+ struct client_channels *cl_chn = looking_for_chn(&iio_infos[i], uid);
+ if(!cl_chn) {
+ AFB_ERROR("cannot find %s event with uid=%s", value, uid);
+ return;
+ }
+ if(afb_req_unsubscribe(request, *cl_chn->event) != 0) {
+ afb_req_fail(request, "failed", "unsubscription failed");
+ return;
+ }
+ }
+ }
+ if(!found) {
+ afb_req_fail(request, "failed", "Invalid event");
+ return;
+ }
+ }
+ afb_req_success(request, NULL, NULL);
+}
+
+const afb_verb_v2 verbs[] = {
+ { .verb = "subscribe", .session = AFB_SESSION_NONE, .callback = subscribe, .info = "Subscribe for an event" },
+ { .verb = "unsubscribe", .session = AFB_SESSION_NONE, .callback = unsubscribe, .info = "Unsubscribe for an event" },
+ { .verb=NULL }
+};
+
+const afb_binding_v2 afbBindingV2 = {
+ .info = "iio devices service",
+ .api = "iiodevices",
+ .verbs = verbs
+};