/*
* Copyright (C) 2016 "IoT.bzh"
* Author Fulup Ar Foll <fulup@iot.bzh>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, something express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Reference:
* Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values
*/
#ifndef _CTL_CONFIG_INCLUDE_
#define _CTL_CONFIG_INCLUDE_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "ctl-plugin.h"
#include <filescan-utils.h>
#include <wrap-json.h>
#ifdef CONTROL_SUPPORT_LUA
#include "ctl-lua.h"
#endif
#ifndef CONTROL_MAXPATH_LEN
#define CONTROL_MAXPATH_LEN 255
#endif
#ifndef CONTROL_CONFIG_PRE
#define CONTROL_CONFIG_PRE "onload"
#endif
#ifndef CTL_PLUGIN_EXT
#define CTL_PLUGIN_EXT ".ctlso"
#define CTL_SCRIPT_EXT ".lua"
#endif
#define LUA_ACTION_PREFIX "lua://"
#define API_ACTION_PREFIX "api://"
#define PLUGIN_ACTION_PREFIX "plugin://"
typedef struct ConfigSectionS {
const char *key;
const char *uid;
const char *info;
const char *prefix;
int (*loadCB)(AFB_ApiT apihandle, struct ConfigSectionS *section, json_object *sectionJ);
void *handle;
CtlActionT *actions;
} CtlSectionT;
typedef struct {
const char *api;
const char *uid;
const char *info;
const char *version;
const char *author;
const char *date;
const char *prefix;
json_object *configJ;
json_object *requireJ;
CtlSectionT *sections;
CtlPluginT *ctlPlugins;
void *external;
} CtlConfigT;
// This should not be global as application may want to define their own sections
typedef enum {
CTL_SECTION_PLUGIN,
CTL_SECTION_ONLOAD,
CTL_SECTION_CONTROL,
CTL_SECTION_EVENT,
CTL_SECTION_HAL,
CTL_SECTION_ENDTAG,
} SectionEnumT;
// ctl-action.c
extern int AddActionsToSection(AFB_ApiT apiHandle, CtlSectionT *section, json_object *actionsJ, int exportApi);
extern CtlActionT *ActionConfig(AFB_ApiT apiHandle, json_object *actionsJ, int exportApi);
extern void ActionExecUID(AFB_ReqT request, CtlConfigT *ctlConfig, const char *uid, json_object *queryJ);
extern int
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
* Copyright (C) 2019 "IoT.bzh"
* Author "Clément Bénier" <clement.benier@iot.bzh>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#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 <limits.h>
#include <json-c/json.h>
#define AFB_BINDING_VERSION 3
#include <afb/afb-binding.h>
#include <iio.h>
#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;
afb_event_t *event;
struct iio_info *infos;
json_object *jobject;
struct client_sub *next;
};
/*gather all afb_event_t*/
struct event
{
struct event *next;
afb_event_t 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;
/* Static definition of supported iiodevices */
static struct iio_info iio_infos[] = {
{ "16-001d", "acceleration", "accel"},
{ "16-001d", "gyroscope", "anglvel"},
{ "16-006b", "compass", "magn"},
{ "asm330lhh_accel", "acceleration", "accel"},
{ "asm330lhh_gyro", "gyroscope", "anglvel"},
{ "scmi.iio.accel", "acceleration", "accel"},
{ "scmi.iio.gyro", "gyroscope", "anglvel"},
};
/*get event by afb_event_t*/
static struct event *event_get_event(const afb_event_t *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 afb_event_t* 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_api_make_event(afbBindingV3root, tag);
if (!afb_event_is_valid(e->event)) { free(e); return NULL; }
/* link */
e->next = events;
events = e;
return &e->event;
}
/* deletes the event of event */
static int event_del(afb_event_t *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_API_ERROR(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "client is null");
return;
}
AFB_API_DEBUG(afbBindingV3root, "iio_context_find_device %s", client->infos->dev_name);
client->dev = iio_context_find_device(ctx, client->infos->dev_name);
if(!client->dev) {
AFB_API_ERROR(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "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_API_WARNING(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "client->channels is null pointer");
return;
}
struct channels *it_chn = client->channels;
while(it_chn) {
struct channels *tmp = it_chn->next;
AFB_API_DEBUG(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "cannot find previous client");
exit(-1);
}
prev_client->next = client->next;
}
AFB_API_DEBUG(afbBindingV3root, "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_unref(*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_API_ERROR(afbBindingV3root, "client is null");
return -1;
}
char val[22];
struct channels *channel = client->channels;
while(channel) { //iterate on client channels
if(!channel->chn) {
AFB_API_ERROR(afbBindingV3root, "chn is null for cl_chn=%s", channel->name);
return -1;
}
iio_channel_attr_read(channel->chn, "raw", val, sizeof(val));
long long data = strtoll(val, NULL, 10);
AFB_API_DEBUG(afbBindingV3root, "read_data: %s %lld", iio_channel_get_id(channel->chn), data);
json_object *value = json_object_new_int64(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_API_DEBUG(afbBindingV3root, "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_api_get_event_loop(afbBindingV3root), 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_API_ERROR(afbBindingV3root, "client is null");
return;
}
if(!client->channels) {
AFB_API_ERROR(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "cannot open %s file", filename);
return;
}
sd_event_add_io(afb_api_get_event_loop(afbBindingV3root), &source, client->fd, EPOLLIN, read_data_push, client);
}
else { //frequency specified
sd_event_add_time(afb_api_get_event_loop(afbBindingV3root), &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_API_ERROR(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "client is null");
return;
}
AFB_API_DEBUG(afbBindingV3root, "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_API_DEBUG(afbBindingV3root, "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_API_ERROR(afbBindingV3root, "cannot allocate client");
return NULL;
}
//initialise client
if(!clients) {
clients = client;
client->index = 0;
}
else {
//find last client
struct client_sub * last_client = clients;
while(last_client->next!=NULL) {
last_client = last_client->next;
}
last_client->next = client;
client->index = last_client->index + 1;
}
char event_name[100];
sprintf(event_name, "%s%d", infos->id, client->index);
AFB_API_DEBUG(afbBindingV3root, "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);
return client;
}
/*subscribe verb*/
static void subscribe(afb_req_t 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_API_INFO(afbBindingV3root, "subscription with: value=%s, s_iioelts=%s, freq=%s",
value, s_iioelts, freq);
bool found_event = false;
bool found_device = false;
for(int i = 0; i < sizeof(iio_infos)/sizeof(iio_infos[0]); i++) {
if(!strcasecmp(value, iio_infos[i].id)) {
found_event = 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);
continue;
}
init_channel(client, iioelts);
if(!client->channels) {
afb_req_fail(request, "failed", "No channels found");
return;
}
init_event_io(client);
} else {
init_dev(client);
}
found_device = true;
//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;
}
break;
}
}
if(!found_event) {
afb_req_fail(request, "failed", "Invalid event");
return;
}
if(!found_device) {
afb_req_fail(request, "failed", "No device found");
return;
}
afb_req_success(request, NULL, NULL);
}
/*unsubscribe verb*/
static void unsubscribe(afb_req_t 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_API_ERROR(afbBindingV3root, "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_t 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_t afbBindingExport = {
.info = "iio devices service",
.api = "iiodevices",
.verbs = verbs
};