baylibre-acme: Use timerfd instead of a fake pipe.

Currently baylibre-acme uses a fake pipe as the input channel required by
libsigrok API and calls sleep() in the data acquisition callback to create
intervals between measurements.

Switch to a more elegant approach: use Linux' timerfd and set a periodic
timer equal to the sampling rate. Then read the data every time the timer
expires.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This commit is contained in:
Daniel Lezcano 2015-06-04 19:01:27 +02:00 committed by Uwe Hermann
parent f9b0ab6b2d
commit a0648b1a12
3 changed files with 40 additions and 17 deletions

View File

@ -18,6 +18,8 @@
*/
#include "protocol.h"
#include <time.h>
#include <sys/timerfd.h>
SR_PRIV struct sr_dev_driver baylibre_acme_driver_info;
@ -349,6 +351,10 @@ static int dev_acquisition_open(const struct sr_dev_inst *sdi)
static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
{
struct dev_context *devc;
struct itimerspec tspec = {
.it_interval = { 0, 0 },
.it_value = { 0, 0 }
};
(void)cb_data;
@ -360,19 +366,30 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
devc = sdi->priv;
devc->samples_read = 0;
if (pipe(devc->pipe_fds)) {
sr_err("Error setting up pipe");
devc->samples_missed = 0;
devc->timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
if (devc->timer_fd < 0) {
sr_err("Error creating timer fd");
return SR_ERR;
}
devc->channel = g_io_channel_unix_new(devc->pipe_fds[0]);
tspec.it_interval.tv_sec = 0;
tspec.it_interval.tv_nsec = (1000000000L / devc->samplerate);
tspec.it_value = tspec.it_interval;
if (timerfd_settime(devc->timer_fd, 0, &tspec, NULL)) {
sr_err("Failed to set timer");
close(devc->timer_fd);
return SR_ERR;
}
devc->channel = g_io_channel_unix_new(devc->timer_fd);
g_io_channel_set_flags(devc->channel, G_IO_FLAG_NONBLOCK, NULL);
g_io_channel_set_encoding(devc->channel, NULL, NULL);
g_io_channel_set_buffered(devc->channel, FALSE);
sr_session_source_add_channel(sdi->session, devc->channel,
G_IO_IN | G_IO_ERR, 1, bl_acme_receive_data, (void *)sdi);
G_IO_IN | G_IO_ERR, 1000, bl_acme_receive_data, (void *)sdi);
/* Send header packet to the session bus. */
std_session_send_df_header(sdi, LOG_PREFIX);
@ -398,11 +415,15 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
g_io_channel_shutdown(devc->channel, FALSE, NULL);
g_io_channel_unref(devc->channel);
devc->channel = NULL;
close(devc->timer_fd);
/* Send last packet. */
packet.type = SR_DF_END;
sr_session_send(sdi, &packet);
if (devc->samples_missed > 0)
sr_warn("%d samples missed", devc->samples_missed);
return SR_OK;
}

View File

@ -560,8 +560,8 @@ SR_PRIV void bl_acme_close_channel(struct sr_channel *ch)
SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data)
{
uint32_t cur_time, elapsed_time, diff_time;
int64_t time_to_sleep;
uint32_t cur_time, elapsed_time;
uint64_t nrexpiration;
struct sr_datafeed_packet packet, framep;
struct sr_datafeed_analog analog;
struct sr_dev_inst *sdi;
@ -586,17 +586,18 @@ SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data)
memset(&analog, 0, sizeof(struct sr_datafeed_analog));
analog.data = &valf;
/*
* Reading from sysfs takes some time - try to keep up with samplerate.
*/
if (devc->samples_read) {
cur_time = g_get_monotonic_time();
diff_time = cur_time - devc->last_sample_fin;
time_to_sleep = G_USEC_PER_SEC / devc->samplerate - diff_time;
if (time_to_sleep > 0)
g_usleep(time_to_sleep);
if (read(devc->timer_fd, &nrexpiration, sizeof(nrexpiration)) < 0) {
sr_warn("Failed to read timer information");
return TRUE;
}
/*
* We were not able to process the previous timer expiration, we are
* overloaded.
*/
if (nrexpiration > 1)
devc->samples_missed += nrexpiration - 1;
framep.type = SR_DF_FRAME_BEGIN;
sr_session_send(cb_data, &framep);

View File

@ -65,9 +65,10 @@ struct dev_context {
uint32_t num_channels;
uint64_t samples_read;
uint64_t samples_missed;
int64_t start_time;
int64_t last_sample_fin;
int pipe_fds[2];
int timer_fd;
GIOChannel *channel;
};