diff options
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | binding/iiodevices-binding.c | 491 |
2 files changed, 254 insertions, 240 deletions
@@ -34,9 +34,10 @@ Frequency is in Hertz, if the frequency is not filled, events are triggered via ## Remaining issues - Provide a json config file so that configures the device name and the channel name. -- Rework on channel structure and split it into client structure - Handle several values simultaneously, see trigger - Update it to other iiodevices +- channel static infos: add suffix in json for each different static infos between channels +- read channel values only for the maximum frequency ## M3ULCB Kingfisher diff --git a/binding/iiodevices-binding.c b/binding/iiodevices-binding.c index 45d0f61..0bd692a 100644 --- a/binding/iiodevices-binding.c +++ b/binding/iiodevices-binding.c @@ -18,25 +18,26 @@ #include "config_iiodevices.h" -/*structure in order to handle client_channels connections*/ -struct client_channels { +/*structure in order to handle channels connections*/ +struct channels { struct iio_channel *chn; + enum iio_elements iioelts; + char name[100]; + struct channels *next; +}; + +/*handle client subscription*/ +struct client_sub { + struct channels *channels; int index; 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; + struct client_sub *next; }; /*gather all afb_event*/ @@ -44,14 +45,19 @@ struct event { struct event *next; struct afb_event event; - unsigned int nb_subscribers; char tag[100]; }; +/*events*/ static struct event *events = 0; +/*iio context*/ static struct iio_context *ctx = NULL; -static struct s_channels channels = { NULL, 0 }; +/*clients*/ +static struct client_sub * clients = NULL; +/*save last registered client*/ +static struct client_sub * last_client = NULL; +/*get event by afb_event*/ static struct event *event_get_event(const struct afb_event *event) { struct event *e = events; @@ -61,16 +67,6 @@ static struct event *event_get_event(const struct afb_event *event) 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) { @@ -100,7 +96,6 @@ static struct afb_event* event_add(const char *tag) /* link */ e->next = events; - e->nb_subscribers++; events = e; return &e->event; } @@ -111,25 +106,21 @@ static int event_del(struct afb_event *event) struct event *e, **p; /* check exists */ - e = event_get_event(event); - if (!e) return -1; + 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; + /* unlink */ + p = &events; + while(*p != e) p = &(*p)->next; + *p = e->next; - /* destroys */ - afb_event_unref(e->event); - free(e); - } + /* destroys */ + afb_event_unref(e->event); + free(e); return 0; } +/*initialse iio context*/ int init_context() { ctx = iio_create_local_context(); @@ -141,26 +132,33 @@ int init_context() return 0; } -static struct iio_device *init_dev(const char *dev_name) +/*initialise device: find by its name*/ +static void init_dev(struct client_sub *client) { + if(!client) { + AFB_ERROR("client is null"); + return NULL; + } + 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); + 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 NULL; } - return dev; } -static int read_infos(struct client_channels *cl_chn) +/*read static infos from channels*/ +static int read_infos(struct client_sub* client) { - if(!cl_chn) { - AFB_ERROR("cl_chn is null"); + if(!client || !client->channels || !client->channels->chn) { + AFB_ERROR("client=%p or client->channels=%p or client->channels->chn=%p is null", client, client->channels, client->channels->chn); return -1; } - struct iio_channel *chn = cl_chn->chn; - json_object *jobject = cl_chn->jobject; + struct iio_channel *chn = client->channels->chn; + json_object *jobject = client->jobject; char val[100]; //read all infos @@ -175,252 +173,226 @@ static int read_infos(struct client_channels *cl_chn) return 0; } -static void deinit_channels(struct client_channels *cl_chn) +/*close file descriptor*/ +static void close_fd(struct client_sub *client) { - if(!cl_chn || !channels.channels || channels.nb == 0) { - AFB_ERROR("null pointer"); + if(!client) { + AFB_ERROR("client is null pointer"); return; } - int i = 0; - while(&channels.channels[i] != cl_chn->first) //looking for index matching cl_chn - i++; + if(client->fd >= 0) + close(client->fd); +} - 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++; +/*deinit client channels*/ +static void deinit_channels(struct client_sub *client) +{ + if(!client->channels) { + AFB_ERROR("client->channels is null pointer"); + return; } - while(i < channels.nb - offset) { //move above all remained channels - channels.channels[i] = channels.channels[i + offset + 1]; - i++; + 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; +} - 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); +/*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; + } + struct client_sub* prev = clients; + while(prev->next != client) { + prev = prev->next; + } + return prev; } -static void close_fd(struct client_channels *cl_chn) +/*deinit client sub*/ +static void deinit_client_sub(struct client_sub *client) { - if(!cl_chn) { - AFB_ERROR("null pointer"); + if(!client) { + AFB_ERROR("client is null pointer"); return; } - close(cl_chn->fd); + 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_channels *cl_chn) + struct client_sub *client) { - afb_event_drop(*cl_chn->event); + afb_event_drop(*client->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); + close_fd(client); + event_del(client->event); + deinit_channels(client); + deinit_client_sub(client); } -static int read_data(struct client_channels *cl_chn, sd_event_source* src) +/*!read data from channels*/ +static int read_data(struct client_sub *client, sd_event_source* src) { - if(!cl_chn) { - AFB_ERROR("cl_chn is null"); + if(!client) { + AFB_ERROR("client 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; - while(p_cl_chn) { - chn = p_cl_chn->chn; - if(!chn) { - AFB_ERROR("chn is null for cl_chn=%s", p_cl_chn->name); + 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(chn, "raw", val, 10); + + 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(chn), data); + 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(jobject, p_cl_chn->name, value); - p_cl_chn = p_cl_chn->next; + json_object_object_add(client->jobject, channel->name, value); + channel = channel->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); + 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_channels *cl_chn = (struct client_channels *)userdata; - read_data(cl_chn, src); + 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_channels *cl_chn = (struct client_channels *)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 += cl_chn->u_period; + usecs += client->u_period; sd_event_source_set_time(src, usecs); - read_data(cl_chn, src); + read_data(client, src); return 0; } -static void init_event_io(struct iio_device *dev, struct client_channels *cl_chn) +/*init event io: depending on the frequency, if no filled, triggered on file + * descriptor */ +static void init_event_io(struct client_sub *client) { - if(!dev || !cl_chn) { - AFB_ERROR("dev or cl_chn is null"); + if(!client) { + AFB_ERROR("client is null"); return; } - read_infos(cl_chn); //get unchanged infos + read_infos(client); //get unchanged infos sd_event_source *source = NULL; - if(cl_chn->u_period <= 0) { //no given frequency + if(client->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) { + sprintf(filename, 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, cl_chn->fd, EPOLLIN, read_data_push, cl_chn); + sd_event_add_io(afb_daemon_get_event_loop(), &source, client->fd, EPOLLIN, read_data_push, client); } - else { - sd_event_add_time(afb_daemon_get_event_loop(), &source, CLOCK_MONOTONIC, 0, 1, read_data_timer, cl_chn); + 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 channel_fd*/ -static struct client_channels *set_channel(const int index, - enum iio_elements i, struct client_channels *first) +/*initialise client channel*/ +static struct channels* set_channel( + struct client_sub* client, struct channels *prev_chn, const enum iio_elements i) { - 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; + prev_chn->next = malloc(sizeof(struct channels)); + if(!prev_chn->next) { + AFB_ERROR("alloc failed for channels"); + return; + } + + struct channels *chn = prev_chn->next; + chn->next = NULL; + chn->iioelts = i; /*set channel name with iio_elements*/ - strcpy(channel_name, first->infos->id); - set_channel_name(channel_name, i); + strcpy(chn->name, client->infos->id); + set_channel_name(chn->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); + if(!(chn->chn = iio_device_find_channel(client->dev, chn->name, false))) { + AFB_ERROR("cannot find %s channel", chn->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]; - } - 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; + iio_channel_enable(chn->chn); + return chn; } -static struct afb_event* is_allocation_needed(struct iio_info *infos, - enum iio_elements *iioelts, struct client_channels **first) +static void init_channel(struct client_sub* client, const unsigned int iioelts) { - 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) -{ - if(!dev || !infos) { - AFB_ERROR("dev=%p or infos=%p is null", dev, infos); + if(!client) { + AFB_ERROR("client is null"); 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; + const char *name = client->infos->id; + 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; } - 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; - *first = &channels.channels[channels.nb]; - } - else - event_add_subscriber((*first)->event); - channels.channels[channels.nb].first = *first; - - int index = 0; + /*set channel*/ for(int i = 1; i <= iioelts; i = i << 1) { //iterate on enumerations if(i == (i & iioelts)) { - set_channel(index, i, *first); - index++; + it_chn = set_channel(client, it_chn, i); } } - channels.nb = channels.nb + nb_iioelts; + client->channels = pre_index.next; } +/*get period in microseconds from a frequency*/ static uint64_t get_period(const char* freq) { double frequency = 0; @@ -429,19 +401,66 @@ static uint64_t get_period(const char* freq) 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) +/*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) { - if(channels.nb == 0) - return true; + 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; +} - 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; +/*add new client with new event*/ +static struct client_sub *add_new_client(const 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; } - return true; + + 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) { const char *value = afb_req_value(request, "event"); @@ -457,35 +476,25 @@ static void subscribe(struct afb_req request) value, s_iioelts, freq); bool found = false; - for(int i = 0; i < IIO_INFOS_SIZE; i++) { + for(int i = 0; i < sizeof(iio_infos)/sizeof(iio_infos[0]); i++) { if(!strcasecmp(value, iio_infos[i].id)) { found = true; - struct iio_device *dev = init_dev(iio_infos[i].dev_name); - - uint64_t u_period = get_period( freq); + 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); - if(!first) { - AFB_ERROR("first is null"); - return; - } - init_event_io(dev, first); - p_event = first->event; + 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); + init_channel(client, iioelts); + init_event_io(client); } - afb_req_context_set(request, first, NULL); + //stored client in order to be get in unsubscription + afb_req_context_set(request, client, NULL); - if(afb_req_subscribe(request, *p_event) != 0) { + if(afb_req_subscribe(request, *client->event) != 0) { afb_req_fail(request, "failed", "subscription failed"); return; } @@ -498,6 +507,7 @@ static void subscribe(struct afb_req request) afb_req_success(request, NULL, NULL); } +/*unsubscribe verb*/ static void unsubscribe(struct afb_req request) { const char *value = afb_req_value(request, "event"); @@ -508,16 +518,17 @@ static void unsubscribe(struct afb_req request) bool found = false; if(value) { - for(int i = 0; i < IIO_INFOS_SIZE; i++) { + for(int i = 0; i < sizeof(iio_infos)/sizeof(iio_infos[0]); i++) { if(!strcasecmp(value, iio_infos[i].id)) { found = true; - struct client_channels *cl_chn = (struct client_channels *)afb_req_context_get(request); - if(!cl_chn) { + 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); return; } - if(afb_req_unsubscribe(request, *cl_chn->event) != 0) { + if(afb_req_unsubscribe(request, *client->event) != 0) { afb_req_fail(request, "failed", "unsubscription failed"); return; } @@ -531,12 +542,14 @@ static void unsubscribe(struct afb_req request) 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", |