diff options
author | Clément Bénier <clement.benier@iot.bzh> | 2018-06-07 12:43:27 +0200 |
---|---|---|
committer | cle©mentbeénier <clement.benier@iot.bzh> | 2018-06-21 14:23:37 +0200 |
commit | ed02e81650ab383f11622c42bb1bfdd125e511f1 (patch) | |
tree | fe422bbf48c2af77efa68ca2969921018b6470a9 /binding/iiodevices-binding.c | |
parent | 4bea2d73a5de0f8ec83c61edc37653718d34f31b (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/iiodevices-binding.c')
-rw-r--r-- | binding/iiodevices-binding.c | 571 |
1 files changed, 571 insertions, 0 deletions
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 +}; |