X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Fbaylibre-acme%2Fprotocol.c;h=d9131cb09c9f3fe097366b85f3f2b178c2ebe652;hb=6ec6c43b4738dbc7091f4a49a4ec80ea6102cb52;hp=66a22ad4ffe0e93b4f2f0bcd9017dc3c80e0a7f9;hpb=391e23a97fe5b919f7f0716702bd1b151c601f66;p=libsigrok.git diff --git a/src/hardware/baylibre-acme/protocol.c b/src/hardware/baylibre-acme/protocol.c index 66a22ad4..d9131cb0 100644 --- a/src/hardware/baylibre-acme/protocol.c +++ b/src/hardware/baylibre-acme/protocol.c @@ -17,6 +17,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -33,6 +34,8 @@ struct channel_group_priv { struct channel_priv { int ch_type; + int fd; + float val; struct channel_group_priv *probe; }; @@ -201,13 +204,12 @@ static void append_channel(struct sr_dev_inst *sdi, struct sr_channel_group *cg, cp->ch_type = type; cp->probe = cg->priv; - ch = sr_channel_new(devc->num_channels++, + ch = sr_channel_new(sdi, devc->num_channels++, SR_CHANNEL_ANALOG, TRUE, name); g_free(name); ch->priv = cp; cg->channels = g_slist_append(cg->channels, ch); - sdi->channels = g_slist_append(sdi->channels, ch); } SR_PRIV gboolean bl_acme_register_probe(struct sr_dev_inst *sdi, int type, @@ -246,6 +248,20 @@ SR_PRIV gboolean bl_acme_register_probe(struct sr_dev_inst *sdi, int type, return TRUE; } +SR_PRIV int bl_acme_get_probe_type(const struct sr_channel_group *cg) +{ + struct channel_group_priv *cgp = cg->priv; + + return cgp->probe_type; +} + +SR_PRIV int bl_acme_probe_has_pws(const struct sr_channel_group *cg) +{ + struct channel_group_priv *cgp = cg->priv; + + return sr_gpio_getval_export(pws_info_gpios[cgp->index]); +} + /* * Sets path to the hwmon attribute if this channel group * supports shunt resistance setting. The caller has to supply @@ -282,6 +298,43 @@ static int get_shunt_path(const struct sr_channel_group *cg, GString *path) return ret; } +/* + * Try setting the update_interval sysfs attribute for each probe according + * to samplerate. + */ +SR_PRIV void bl_acme_maybe_set_update_interval(const struct sr_dev_inst *sdi, + uint64_t samplerate) +{ + struct sr_channel_group *cg; + struct channel_group_priv *cgp; + GString *hwmon; + GSList *l; + FILE *fd; + + for (l = sdi->channel_groups; l != NULL; l = l->next) { + cg = l->data; + cgp = cg->priv; + + hwmon = g_string_sized_new(64); + g_string_append_printf(hwmon, + "/sys/class/hwmon/hwmon%d/update_interval", + cgp->hwmon_num); + + if (g_file_test(hwmon->str, G_FILE_TEST_EXISTS)) { + fd = g_fopen(hwmon->str, "w"); + if (!fd) { + g_string_free(hwmon, TRUE); + continue; + } + + g_fprintf(fd, "%" PRIu64 "\n", 1000 / samplerate); + fclose(fd); + } + + g_string_free(hwmon, TRUE); + } +} + SR_PRIV int bl_acme_get_shunt(const struct sr_channel_group *cg, uint64_t *shunt) { @@ -328,17 +381,12 @@ SR_PRIV int bl_acme_set_shunt(const struct sr_channel_group *cg, uint64_t shunt) */ fd = g_fopen(path->str, "w"); if (!fd) { - sr_err("Error opening %s: %s", path->str, strerror(errno)); - g_string_free(path, TRUE); - return SR_ERR_IO; + sr_err("Error opening %s: %s", path->str, g_strerror(errno)); + ret = SR_ERR_IO; + goto out; } - g_string_free(path, TRUE); - g_fprintf(fd, "%llu\n", MOHM_TO_UOHM(shunt)); - /* - * XXX There's no g_fclose() in GLib. This seems to work, - * but is it safe? - */ + g_fprintf(fd, "%" PRIu64 "\n", MOHM_TO_UOHM(shunt)); fclose(fd); out: @@ -354,8 +402,7 @@ SR_PRIV int bl_acme_read_power_state(const struct sr_channel_group *cg, cgp = cg->priv; - val = sr_gpio_getval_export(pws_info_gpios[cgp->index]); - if (val != 1) { + if (!bl_acme_probe_has_pws(cg)) { sr_err("Probe has no power-switch"); return SR_ERR_ARG; } @@ -374,13 +421,17 @@ SR_PRIV int bl_acme_set_power_off(const struct sr_channel_group *cg, cgp = cg->priv; - val = sr_gpio_getval_export(pws_info_gpios[cgp->index]); - if (val != 1) { + if (!bl_acme_probe_has_pws(cg)) { sr_err("Probe has no power-switch"); return SR_ERR_ARG; } val = sr_gpio_setval_export(pws_gpios[cgp->index], off ? 0 : 1); + if (val < 0) { + sr_err("Error setting power-off state: gpio: %d", + pws_gpios[cgp->index]); + return SR_ERR_IO; + } return SR_OK; } @@ -446,10 +497,32 @@ static float adjust_data(int val, int type) static float read_sample(struct sr_channel *ch) { struct channel_priv *chp; - char path[64], *file, buf[16]; + char buf[16]; ssize_t len; int fd; + chp = ch->priv; + fd = chp->fd; + + lseek(fd, 0, SEEK_SET); + + len = read(fd, buf, sizeof(buf)); + if (len < 0) { + sr_err("Error reading from channel %s (hwmon: %d): %s", + ch->name, chp->probe->hwmon_num, g_strerror(errno)); + ch->enabled = FALSE; + return -1.0; + } + + return adjust_data(strtol(buf, NULL, 10), chp->ch_type); +} + +SR_PRIV int bl_acme_open_channel(struct sr_channel *ch) +{ + struct channel_priv *chp; + char path[64], *file; + int fd; + chp = ch->priv; switch (chp->ch_type) { @@ -460,40 +533,45 @@ static float read_sample(struct sr_channel *ch) case TEMP_OUT: file = "temp2_input"; break; default: sr_err("Invalid channel type: %d.", chp->ch_type); - return -1.0; + return SR_ERR; } snprintf(path, sizeof(path), "/sys/class/hwmon/hwmon%d/%s", chp->probe->hwmon_num, file); + fd = open(path, O_RDONLY); if (fd < 0) { - sr_err("Error opening %s: %s", path, strerror(errno)); + sr_err("Error opening %s: %s", path, g_strerror(errno)); ch->enabled = FALSE; - return -1.0; + return SR_ERR; } - len = read(fd, buf, sizeof(buf)); - close(fd); - if (len < 0) { - sr_err("Error reading from %s: %s", path, strerror(errno)); - ch->enabled = FALSE; - return -1.0; - } + chp->fd = fd; - return adjust_data(strtol(buf, NULL, 10), chp->ch_type); + return 0; +} + +SR_PRIV void bl_acme_close_channel(struct sr_channel *ch) +{ + struct channel_priv *chp; + + chp = ch->priv; + close(chp->fd); + chp->fd = -1; } 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; struct sr_channel *ch; + struct channel_priv *chp; struct dev_context *devc; GSList *chl, chonly; - float valf; + unsigned i; (void)fd; (void)revents; @@ -508,45 +586,67 @@ SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data) packet.type = SR_DF_ANALOG; packet.payload = &analog; - memset(&analog, 0, sizeof(analog)); - analog.data = &valf; + memset(&analog, 0, sizeof(struct sr_datafeed_analog)); - /* - * 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; } - framep.type = SR_DF_FRAME_BEGIN; - sr_session_send(cb_data, &framep); + /* + * We were not able to process the previous timer expiration, we are + * overloaded. + */ + if (nrexpiration > 1) + devc->samples_missed += nrexpiration - 1; /* - * Due to different units used in each channel we're sending - * samples one-by-one. + * XXX This is a nasty workaround... + * + * At high sampling rates and maximum channels we are not able to + * acquire samples fast enough, even though frontends still think + * that samples arrive on time. This causes shifts in frontend + * plots. + * + * To compensate for the delay we check if any clock events were + * missed and - if so - don't really read the next value, but send + * the same sample as fast as possible. We do it until we are back + * on schedule. + * + * At high sampling rate this doesn't seem to visibly reduce the + * accuracy. */ - for (chl = sdi->channels; chl; chl = chl->next) { - ch = chl->data; - if (!ch->enabled) - continue; - chonly.next = NULL; - chonly.data = ch; - analog.channels = &chonly; - analog.num_samples = 1; - analog.mq = channel_to_mq(chl->data); - analog.unit = channel_to_unit(ch); - - valf = read_sample(ch); - - sr_session_send(cb_data, &packet); - } + for (i = 0; i < nrexpiration; i++) { + framep.type = SR_DF_FRAME_BEGIN; + sr_session_send(cb_data, &framep); - framep.type = SR_DF_FRAME_END; - sr_session_send(cb_data, &framep); + /* + * Due to different units used in each channel we're sending + * samples one-by-one. + */ + for (chl = sdi->channels; chl; chl = chl->next) { + ch = chl->data; + chp = ch->priv; + + if (!ch->enabled) + continue; + chonly.next = NULL; + chonly.data = ch; + analog.channels = &chonly; + analog.num_samples = 1; + analog.mq = channel_to_mq(chl->data); + analog.unit = channel_to_unit(ch); + + if (i < 1) + chp->val = read_sample(ch); + + analog.data = &chp->val; + sr_session_send(cb_data, &packet); + } + + framep.type = SR_DF_FRAME_END; + sr_session_send(cb_data, &framep); + } devc->samples_read++; if (devc->limit_samples > 0 &&