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:
parent
f9b0ab6b2d
commit
a0648b1a12
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue