]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/baylibre-acme/protocol.c
baylibre-acme: Dynamically check per probe config options.
[libsigrok.git] / src / hardware / baylibre-acme / protocol.c
index 272a4ff655e10c48add576acd0916754d1850d4c..ca09eba069c51a0c017f851b53b31d5779346d58 100644 (file)
  */
 
 #include <string.h>
-#include <stdlib.h> /* strtol() */
+#include <stdlib.h>
 #include <errno.h>
-#include <fcntl.h> /* open(), etc... */
+#include <fcntl.h>
+#include <glib/gstdio.h>
 #include "protocol.h"
+#include "gpio.h"
 
 struct channel_group_priv {
        int hwmon_num;
+       int probe_type;
+       int index;
 };
 
 struct channel_priv {
@@ -40,6 +44,17 @@ static const uint8_t temp_i2c_addrs[] = {
        0x0, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x4f, 0x4b,
 };
 
+static const uint32_t pws_gpios[] = {
+       486, 498, 502, 482, 478, 506, 510, 474,
+};
+
+static const uint32_t pws_info_gpios[] = {
+       487, 499, 503, 483, 479, 507, 511, 475,
+};
+
+#define MOHM_TO_UOHM(x) ((x) * 1000)
+#define UOHM_TO_MOHM(x) ((x) / 1000)
+
 SR_PRIV uint8_t bl_acme_get_enrg_addr(int index)
 {
        return enrg_i2c_addrs[index];
@@ -99,10 +114,9 @@ SR_PRIV gboolean bl_acme_detect_probe(unsigned int addr,
                       prb_num, err->message);
                g_string_free(path, TRUE);
                return ret;
-
        }
 
-       if (strncmp(buf, prb_name, strlen(prb_name)) == 0) {
+       if (!strncmp(buf, prb_name, strlen(prb_name))) {
                /*
                 * Correct driver registered on this address - but is
                 * there an actual probe connected?
@@ -130,7 +144,7 @@ static int get_hwmon_index(unsigned int addr)
 
        probe_hwmon_path(addr, path);
        dir = g_dir_open(path->str, 0, &err);
-       if (dir == NULL) {
+       if (!dir) {
                sr_err("Error opening %s: %s", path->str, err->message);
                g_string_free(path, TRUE);
                return -1;
@@ -152,8 +166,7 @@ static int get_hwmon_index(unsigned int addr)
        return hwmon;
 }
 
-static void append_channel(struct sr_dev_inst *sdi,
-                          struct sr_channel_group *cg,
+static void append_channel(struct sr_dev_inst *sdi, struct sr_channel_group *cg,
                           int index, int type)
 {
        struct channel_priv *cp;
@@ -180,7 +193,7 @@ static void append_channel(struct sr_dev_inst *sdi,
                name = g_strdup_printf("P%d_TEMP_OUT", index);
                break;
        default:
-               sr_err("Bug in the code: invalid channel type!");
+               sr_err("Invalid channel type: %d.", type);
                return;
        }
 
@@ -188,13 +201,12 @@ static void append_channel(struct sr_dev_inst *sdi,
        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,
@@ -212,6 +224,8 @@ SR_PRIV gboolean bl_acme_register_probe(struct sr_dev_inst *sdi, int type,
        cg = g_malloc0(sizeof(struct sr_channel_group));
        cgp = g_malloc0(sizeof(struct channel_group_priv));
        cgp->hwmon_num = hwmon;
+       cgp->probe_type = type;
+       cgp->index = prb_num - 1;
        cg->name = g_strdup_printf("Probe_%d", prb_num);
        cg->priv = cgp;
 
@@ -223,7 +237,7 @@ SR_PRIV gboolean bl_acme_register_probe(struct sr_dev_inst *sdi, int type,
                append_channel(sdi, cg, prb_num, TEMP_IN);
                append_channel(sdi, cg, prb_num, TEMP_OUT);
        } else {
-               sr_err("Bug in the code: invalid probe type!");
+               sr_err("Invalid probe type: %d.", type);
        }
 
        sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
@@ -231,6 +245,152 @@ 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
+ * a valid GString.
+ */
+static int get_shunt_path(const struct sr_channel_group *cg, GString *path)
+{
+       struct channel_group_priv *cgp;
+       int ret = SR_OK, status;
+
+       cgp = cg->priv;
+
+       if (cgp->probe_type != PROBE_ENRG) {
+               sr_err("Probe doesn't support shunt resistance setting");
+               return SR_ERR_ARG;
+       }
+
+       g_string_append_printf(path,
+                              "/sys/class/hwmon/hwmon%d/shunt_resistor",
+                              cgp->hwmon_num);
+
+       /*
+        * The shunt_resistor sysfs attribute is available
+        * in the Linux kernel since version 3.20. We have
+        * to notify the user if this attribute is not present.
+        */
+       status = g_file_test(path->str, G_FILE_TEST_EXISTS);
+       if (!status) {
+               sr_err("shunt_resistance attribute not present, please update "
+                      "your kernel to version >=3.20");
+               return SR_ERR_NA;
+       }
+
+       return ret;
+}
+
+SR_PRIV int bl_acme_get_shunt(const struct sr_channel_group *cg,
+                             uint64_t *shunt)
+{
+       GString *path = g_string_sized_new(64);
+       gchar *contents;
+       int status, ret = SR_OK;
+       GError *err = NULL;
+
+       status = get_shunt_path(cg, path);
+       if (status != SR_OK) {
+               ret = status;
+               goto out;
+       }
+
+       status = g_file_get_contents(path->str, &contents, NULL, &err);
+       if (!status) {
+               sr_err("Error reading shunt resistance: %s", err->message);
+               ret = SR_ERR_IO;
+               goto out;
+       }
+
+       *shunt = UOHM_TO_MOHM(strtol(contents, NULL, 10));
+
+out:
+       g_string_free(path, TRUE);
+       return ret;
+}
+
+SR_PRIV int bl_acme_set_shunt(const struct sr_channel_group *cg, uint64_t shunt)
+{
+       GString *path = g_string_sized_new(64);;
+       int status, ret = SR_OK;
+       FILE *fd;
+
+       status = get_shunt_path(cg, path);
+       if (status != SR_OK) {
+               ret = status;
+               goto out;
+       }
+
+       /*
+        * Can't use g_file_set_contents() here, as it calls open() with
+        * O_EXEC flag in a sysfs directory thus failing with EACCES.
+        */
+       fd = g_fopen(path->str, "w");
+       if (!fd) {
+               sr_err("Error opening %s: %s", path->str, strerror(errno));
+               ret = SR_ERR_IO;
+               goto out;
+       }
+
+       g_fprintf(fd, "%" PRIu64 "\n", MOHM_TO_UOHM(shunt));
+       fclose(fd);
+
+out:
+       g_string_free(path, TRUE);
+       return ret;
+}
+
+SR_PRIV int bl_acme_read_power_state(const struct sr_channel_group *cg,
+                                    gboolean *off)
+{
+       struct channel_group_priv *cgp;
+       int val;
+
+       cgp = cg->priv;
+
+       if (!bl_acme_probe_has_pws(cg)) {
+               sr_err("Probe has no power-switch");
+               return SR_ERR_ARG;
+       }
+
+       val = sr_gpio_getval_export(pws_gpios[cgp->index]);
+       *off = val ? FALSE : TRUE;
+
+       return SR_OK;
+}
+
+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;
+
+       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);
+
+       return SR_OK;
+}
+
 static int channel_to_mq(struct sr_channel *ch)
 {
        struct channel_priv *chp;
@@ -244,7 +404,7 @@ static int channel_to_mq(struct sr_channel *ch)
                return SR_MQ_CURRENT;
        case ENRG_VOL:
                return SR_MQ_VOLTAGE;
-       case TEMP_IN:
+       case TEMP_IN: /* Fallthrough */
        case TEMP_OUT:
                return SR_MQ_TEMPERATURE;
        default:
@@ -265,7 +425,7 @@ static int channel_to_unit(struct sr_channel *ch)
                return SR_UNIT_AMPERE;
        case ENRG_VOL:
                return SR_UNIT_VOLT;
-       case TEMP_IN:
+       case TEMP_IN: /* Fallthrough */
        case TEMP_OUT:
                return SR_UNIT_CELSIUS;
        default:
@@ -279,9 +439,9 @@ static float adjust_data(int val, int type)
        switch (type) {
        case ENRG_PWR:
                return ((float)val) / 1000000.0;
-       case ENRG_CURR:
-       case ENRG_VOL:
-       case TEMP_IN:
+       case ENRG_CURR: /* Fallthrough */
+       case ENRG_VOL: /* Fallthrough */
+       case TEMP_IN: /* Fallthrough */
        case TEMP_OUT:
                return ((float)val) / 1000.0;
        default:
@@ -305,12 +465,11 @@ static float read_sample(struct sr_channel *ch)
        case TEMP_IN:   file = "temp1_input";   break;
        case TEMP_OUT:  file = "temp2_input";   break;
        default:
-               sr_err("Bug in the code: invalid channel type!");
+               sr_err("Invalid channel type: %d.", chp->ch_type);
                return -1.0;
        }
 
-       snprintf(path, sizeof(path),
-                "/sys/class/hwmon/hwmon%d/%s",
+       snprintf(path, sizeof(path), "/sys/class/hwmon/hwmon%d/%s",
                 chp->probe->hwmon_num, file);
        fd = open(path, O_RDONLY);
        if (fd < 0) {
@@ -322,7 +481,7 @@ static float read_sample(struct sr_channel *ch)
        len = read(fd, buf, sizeof(buf));
        close(fd);
        if (len < 0) {
-               sr_err("error reading from %s: %s", path, strerror(errno));
+               sr_err("Error reading from %s: %s", path, strerror(errno));
                ch->enabled = FALSE;
                return -1.0;
        }
@@ -355,12 +514,11 @@ 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));
+       memset(&analog, 0, sizeof(struct sr_datafeed_analog));
        analog.data = &valf;
 
        /*
-        * Reading from sysfs takes some time - try to
-        * keep up with samplerate.
+        * Reading from sysfs takes some time - try to keep up with samplerate.
         */
        if (devc->samples_read) {
                cur_time = g_get_monotonic_time();