#include #include #include #include #include #include #include #include #include #include #include #include #include #define AFB_BINDING_VERSION 2 #include #include #include "config_iiodevices.h" /*structure in order to handle channels connections*/ struct channels { struct iio_channel *chn; enum iio_elements iioelts; char name[PATH_MAX]; struct channels *next; }; /*handle client subscription*/ struct client_sub { struct channels *channels; int index; int fd; uint64_t u_period; enum iio_elements iioelts; struct iio_device *dev; struct afb_event *event; struct iio_info *infos; json_object *jobject; struct client_sub *next; }; /*gather all afb_event*/ struct event { struct event *next; struct afb_event event; char tag[PATH_MAX]; }; /*events*/ static struct event *events = 0; /*iio context*/ static struct iio_context *ctx = NULL; /*clients*/ static struct client_sub * clients = NULL; /*save last registered client*/ static struct client_sub * last_client = NULL; /* Static definition of supported iiodevices */ static struct iio_info iio_infos[] = { { "16-001d", "acceleration", "accel"}, { "16-001d", "gyroscope", "magn"}, { "16-006b", "compass", "anglvel"} }; /*get event by afb_event*/ static struct event *event_get_event(const struct afb_event *event) { struct event *e = events; while(&e->event != event) { e = e->next; } return e; } /* 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 &e->event; /* creation */ e = malloc(strlen(tag) + sizeof *e); if (!e) return NULL; strncpy(e->tag, tag, PATH_MAX); /* make the event */ e->event = afb_daemon_make_event(tag); if (!e->event.closure) { free(e); return NULL; } /* link */ e->next = events; 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; /* unlink */ p = &events; while(*p != e) p = &(*p)->next; *p = e->next; /* destroys */ afb_event_unref(e->event); free(e); return 0; } /*initialise iio context*/ int init_context() { ctx = iio_create_local_context(); if(!ctx) { AFB_ERROR("cannot create local iio context"); return -1; } return 0; } /*initialise device: find by its name*/ static void init_dev(struct client_sub *client) { if(!client) { AFB_ERROR("client is null"); return; } AFB_DEBUG("iio_context_find_device %s", client->infos->dev_name); client->dev = iio_context_find_device(ctx, client->infos->dev_name); if(!client->dev) { AFB_ERROR("No %s device found", client->infos->dev_name); return; } } /*read static infos from channels*/ static int read_infos(struct client_sub* client) { if(!client || !client->channels || !client->channels->chn) { AFB_ERROR("client or client->channels or client->channels->chn is null"); return -1; } json_object *jobject = NULL; size_t size = 1000; char val[size]; char keyfirst[size]; struct channels *channel = client->channels; //read all infos while(channel) { for(int i = 0; i < iio_channel_get_attrs_count(channel->chn); i++) { const char *id_attr = iio_channel_get_attr(channel->chn, i); if(!id_attr) { AFB_ERROR("cannot get attr from channel %d", i); return -1; } if(strcasecmp(id_attr, "raw")) { //do not take raw infos iio_channel_attr_read(channel->chn, id_attr, val, 100); //first channel if(client->channels == channel) { json_object *value = json_object_new_string(val); json_object_object_add(client->jobject, id_attr, value); } else { //other channels: keep same value and stored other values /*no id_attr: looking for an already changed id*/ if(!json_object_object_get_ex(client->jobject, id_attr, &jobject)) { strncpy(keyfirst, id_attr, size); set_channel_name(keyfirst, client->channels->iioelts); if(!json_object_object_get_ex(client->jobject, keyfirst, &jobject)) { AFB_WARNING("cannot find %s keyfirst in json object", keyfirst); } } const char * valfisrt = json_object_get_string(jobject); //get first value channel if(strcasecmp(val, valfisrt)) { //different value store it char key[size]; strncpy(key, id_attr, size); set_channel_name(key, channel->iioelts); //add channel suffix json_object *value = json_object_new_string(val); json_object_object_add(client->jobject, key, value); //remove previous non unique key and replace it with suffix strncpy(keyfirst, id_attr, size); set_channel_name(keyfirst, client->channels->iioelts); json_object *valuefirst = json_object_new_string(valfisrt); json_object_object_add(client->jobject, keyfirst, valuefirst); json_object_object_del(client->jobject, id_attr); } } } } channel = channel->next; } return 0; } /*close file descriptor*/ static void close_fd(struct client_sub *client) { if(!client) { AFB_ERROR("client is null pointer"); return; } if(client->fd >= 0) close(client->fd); } /*deinit client channels*/ static void deinit_channels(struct client_sub *client) { if(!client->channels) { AFB_ERROR("client->channels is null pointer"); return; } struct channels *it_chn = client->channels; while(it_chn) { struct channels *tmp = it_chn->next; AFB_DEBUG("free it_chn=%s", it_chn->name); free(it_chn); it_chn = tmp; } client->channels = NULL; } /*looking for previous client: if client is first returns client*/ static struct client_sub* looking_for_previous(struct client_sub *client) { if(!client || !clients) { AFB_ERROR("client or clients is null pointer"); return NULL; } struct client_sub* prev = clients; while(prev->next != client) { prev = prev->next; } return prev; } /*deinit client sub*/ static void deinit_client_sub(struct client_sub *client) { if(!client) { AFB_ERROR("client is null pointer"); return; } struct client_sub *prev_client; if(client == clients) {//first one clients = client->next; } else { prev_client = looking_for_previous(client); if(!prev_client) { AFB_ERROR("cannot find previous client"); exit(-1); } prev_client->next = client->next; } AFB_DEBUG("free client for %s", client->infos->id); free(client); } /*desallocate channels*/ static void desallocate_channels(sd_event_source* src, struct client_sub *client) { afb_event_drop(*client->event); if(src) { sd_event_source_set_enabled(src, SD_EVENT_OFF); sd_event_source_unref(src); } close_fd(client); event_del(client->event); deinit_channels(client); deinit_client_sub(client); } /*!read data from channels*/ static int read_data(struct client_sub *client, sd_event_source* src) { if(!client) { AFB_ERROR("client is null"); return -1; } char val[10]; struct channels *channel = client->channels; while(channel) { //iterate on client channels if(!channel->chn) { AFB_ERROR("chn is null for cl_chn=%s", channel->name); return -1; } iio_channel_attr_read(channel->chn, "raw", val, 10); int data = (int)strtol(val, NULL, 10); AFB_DEBUG("read_data: %s %d", iio_channel_get_id(channel->chn), data); json_object *value = json_object_new_int(data); json_object_object_add(client->jobject, channel->name, value); channel = channel->next; } /*if no more subscribers -> desallocate*/ int nb_subscribers = afb_event_push(*client->event, json_object_get(client->jobject)); AFB_DEBUG("nb_subscribers for %s is %d", afb_event_name(*client->event), nb_subscribers); if(nb_subscribers <= 0) desallocate_channels(src, client); return 0; } /*read data from file descriptor*/ static int read_data_push(sd_event_source* src, int fd, uint32_t revents, void* userdata) { struct client_sub * client= (struct client_sub *)userdata; read_data(client, src); return 0; } /*read data from timer*/ static int read_data_timer(sd_event_source* src, uint64_t usec, void *userdata) { struct client_sub *client = (struct client_sub *)userdata; /*set next time to trigger*/ uint64_t usecs; sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usecs); usecs += client->u_period; sd_event_source_set_time(src, usecs); read_data(client, src); return 0; } /*init event io: depending on the frequency, if no filled, triggered on file * descriptor */ static void init_event_io(struct client_sub *client) { if(!client) { AFB_ERROR("client is null"); return; } if(!client->channels) { AFB_ERROR("channel is null"); return; } read_infos(client); //get unchanged infos sd_event_source *source = NULL; if(client->u_period <= 0) { //no given frequency char filename[PATH_MAX]; snprintf(filename, PATH_MAX, IIODEVICE"%s/%s", iio_device_get_id(client->dev), iio_channel_attr_get_filename(client->channels->chn, "raw")); if((client->fd = open(filename, O_RDONLY)) < 0) { AFB_ERROR("cannot open %s file", filename); return; } sd_event_add_io(afb_daemon_get_event_loop(), &source, client->fd, EPOLLIN, read_data_push, client); } else { //frequency specified sd_event_add_time(afb_daemon_get_event_loop(), &source, CLOCK_MONOTONIC, 0, 1, read_data_timer, client); sd_event_source_set_enabled(source, SD_EVENT_ON); } } /*initialise client channel*/ static struct channels* set_channel( struct client_sub* client, struct channels *prev_chn, const enum iio_elements i) { prev_chn->next = malloc(sizeof(struct channels)); if(!prev_chn->next) { AFB_ERROR("alloc failed for channels"); return NULL; } struct channels *chn = prev_chn->next; chn->next = NULL; chn->iioelts = i; /*set channel name with iio_elements*/ strncpy(chn->name, client->infos->middlename, PATH_MAX); set_channel_name(chn->name, i); if(!(chn->chn = iio_device_find_channel(client->dev, chn->name, false))) { AFB_ERROR("cannot find %s channel", chn->name); free(chn); prev_chn->next = NULL; return prev_chn; } iio_channel_enable(chn->chn); return chn; } static void init_channel(struct client_sub* client, const unsigned int iioelts) { if(!client) { AFB_ERROR("client is null"); return; } AFB_DEBUG("size of channels=%d", get_iio_nb(iioelts)); /*looking for the last channel: could be the first*/ struct channels pre_index; pre_index.next = client->channels; struct channels *it_chn = &pre_index; while(it_chn->next) { it_chn = it_chn->next; } /*set channel*/ for(int i = 1; i <= iioelts; i = i << 1) { //iterate on enumerations if(i == (i & iioelts)) { it_chn = set_channel(client, it_chn, i); } } client->channels = pre_index.next; } /*get period in microseconds from a frequency*/ 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); } /*check if it is needed to create a new client: if NULL return it is needed, * else it is no needed but can be needed to add channels if rest_iioelts > 0*/ static struct client_sub* is_new_client_sub_needed(struct iio_info* infos, uint64_t u_period, enum iio_elements *rest_iioelts) { struct client_sub *it_client = clients; while(it_client) { if(it_client->infos == infos && u_period == it_client->u_period) { AFB_DEBUG("a client is matching"); *rest_iioelts = ~it_client->iioelts & *rest_iioelts; it_client->iioelts |= *rest_iioelts; return it_client; } it_client = it_client->next; } return NULL; } /*add new client with new event*/ static struct client_sub *add_new_client(struct iio_info *infos, const enum iio_elements iioelts, const uint64_t u_period) { struct client_sub *client = malloc(sizeof(struct client_sub)); if(!client) { AFB_ERROR("cannot allocate client"); return NULL; } //initialise client if(!clients) { clients = client; client->index = 0; } else { if(!last_client) { AFB_ERROR("last_client should not be null"); return NULL; } last_client->next = client; client->index = last_client->index + 1; } char event_name[100]; sprintf(event_name, "%s%d", infos->id, client->index); AFB_DEBUG("add_new_client with event_name=%s", event_name); client->channels = NULL; client->jobject = json_object_new_object(); client->iioelts = iioelts; client->u_period = u_period; client->infos = infos; client->fd = -1; client->next = NULL; client->dev = NULL; client->event = event_add(event_name); last_client = client; return client; } /*subscribe verb*/ static void subscribe(struct afb_req request) { init_context(); const char *value = afb_req_value(request, "event"); const char *s_iioelts = afb_req_value(request, "args"); if(!s_iioelts) { afb_req_fail(request, "failed", "args is null"); return; } const char *freq = afb_req_value(request, "frequency"); if(!value || !s_iioelts) { afb_req_fail(request, "failed", "please, fill 'event' and 'args' fields"); return; } AFB_INFO("subscription with: value=%s, s_iioelts=%s, freq=%s", value, s_iioelts, freq); bool found = false; for(int i = 0; i < sizeof(iio_infos)/sizeof(iio_infos[0]); i++) { if(!strcasecmp(value, iio_infos[i].id)) { found = true; uint64_t u_period = get_period(freq); enum iio_elements iioelts = (int)treat_iio_elts(s_iioelts); if(iioelts == 0){ afb_req_fail(request, "failed", "wrong args"); return; } struct client_sub* client = is_new_client_sub_needed(&iio_infos[i], u_period, &iioelts); if(!client || iioelts > 0) { //no client found or new channels if(!client) client = add_new_client(&iio_infos[i], iioelts, u_period); init_dev(client); if(!client->dev) { deinit_client_sub(client); afb_req_fail_f(request, "failed", "No %s device found", client->infos->dev_name); return; } init_channel(client, iioelts); if(!client->channels) { afb_req_fail(request, "failed", "No channels found"); return; } init_event_io(client); } else { init_dev(client); } //stored client in order to be get in unsubscription afb_req_context_set(request, client, NULL); if(!client->event) { afb_req_fail(request, "failed", "No event found"); return; } if(afb_req_subscribe(request, *client->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); } /*unsubscribe verb*/ static void unsubscribe(struct afb_req request) { init_context(); const char *value = afb_req_value(request, "event"); if(!value) { afb_req_fail(request, "failed", "please, fill 'event' fields"); return; } bool found = false; if(value) { for(int i = 0; i < sizeof(iio_infos)/sizeof(iio_infos[0]); i++) { if(!strcasecmp(value, iio_infos[i].id)) { found = true; struct client_sub *client = (struct client_sub *)afb_req_context_get(request); if(!client) { AFB_ERROR("cannot find %s event, it seems that there was \ no subscription", value); afb_req_fail_f(request, "failed", "cannot find %s event, it seems that there was \ no subscription", value); return; } if(afb_req_unsubscribe(request, *client->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); } /*list of api verbs*/ 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 } }; /*binding configuration*/ const afb_binding_v2 afbBindingV2 = { .info = "iio devices service", .api = "iiodevices", .verbs = verbs };