]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/baylibre-acme/protocol.c
baylibre-acme: Check for power-switch presence at probe's initialization.
[libsigrok.git] / src / hardware / baylibre-acme / protocol.c
index 86fe020b345115690f903345392b38288e5ebe68..4b4d5e4e6575e5b26390d0e8b8f0694a89e3682c 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
 #include "protocol.h"
 #include "gpio.h"
 
+enum channel_type {
+       ENRG_PWR = 1,
+       ENRG_CURR,
+       ENRG_VOL,
+       TEMP_IN,
+       TEMP_OUT,
+};
+
 struct channel_group_priv {
        int hwmon_num;
        int probe_type;
        int index;
+       int has_pws;
 };
 
 struct channel_priv {
        int ch_type;
+       int fd;
+       float val;
        struct channel_group_priv *probe;
 };
 
@@ -226,6 +238,7 @@ SR_PRIV gboolean bl_acme_register_probe(struct sr_dev_inst *sdi, int type,
        cgp->hwmon_num = hwmon;
        cgp->probe_type = type;
        cgp->index = prb_num - 1;
+       cgp->has_pws = sr_gpio_getval_export(pws_info_gpios[cgp->index]);
        cg->name = g_strdup_printf("Probe_%d", prb_num);
        cg->priv = cgp;
 
@@ -256,7 +269,7 @@ 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]);
+       return cgp->has_pws;
 }
 
 /*
@@ -378,7 +391,7 @@ 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));
+               sr_err("Error opening %s: %s", path->str, g_strerror(errno));
                ret = SR_ERR_IO;
                goto out;
        }
@@ -414,6 +427,7 @@ SR_PRIV int bl_acme_set_power_off(const struct sr_channel_group *cg,
                                  gboolean off)
 {
        struct channel_group_priv *cgp;
+       int val;
 
        cgp = cg->priv;
 
@@ -422,7 +436,12 @@ SR_PRIV int bl_acme_set_power_off(const struct sr_channel_group *cg,
                return SR_ERR_ARG;
        }
 
-       sr_gpio_setval_export(pws_gpios[cgp->index], off ? 0 : 1);
+       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;
 }
@@ -488,10 +507,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) {
@@ -502,40 +543,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;
@@ -551,44 +597,66 @@ 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(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;
        }
 
-       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);
+
+               /*
+                * 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);
+               framep.type = SR_DF_FRAME_END;
+               sr_session_send(cb_data, &framep);
+       }
 
        devc->samples_read++;
        if (devc->limit_samples > 0 &&