Implement support for SR_CONF_SHUNT_RESISTANCE in BayLibre ACME
driver in the form of a key-value pair where key is the probe ID
and value is the desired shunt resistance in mOhms.

Signed-off-by: Bartosz Golaszewski <bgolaszew...@baylibre.com>
---
 src/hardware/baylibre-acme/api.c      |  39 ++++++++++-
 src/hardware/baylibre-acme/protocol.c | 125 +++++++++++++++++++++++++++++++++-
 src/hardware/baylibre-acme/protocol.h |   8 +++
 3 files changed, 169 insertions(+), 3 deletions(-)

diff --git a/src/hardware/baylibre-acme/api.c b/src/hardware/baylibre-acme/api.c
index 89d8b09..02bac08 100644
--- a/src/hardware/baylibre-acme/api.c
+++ b/src/hardware/baylibre-acme/api.c
@@ -27,6 +27,7 @@ static const uint32_t devopts[] = {
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_SHUNT_RESISTANCE | SR_CONF_GET | SR_CONF_SET,
 };
 
 #define MAX_SAMPLE_RATE                500 /* In Hz */
@@ -167,7 +168,7 @@ static int config_get(uint32_t key, GVariant **data,
                      const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       int ret;
+       int ret, status;
 
        (void)cg;
 
@@ -184,6 +185,11 @@ static int config_get(uint32_t key, GVariant **data,
        case SR_CONF_SAMPLERATE:
                *data = g_variant_new_uint64(devc->samplerate);
                break;
+       case SR_CONF_SHUNT_RESISTANCE:
+               status = bl_acme_read_shunt_values(sdi, data);
+               if (status < 0)
+                       ret = status;
+               break;
        default:
                return SR_ERR_NA;
        }
@@ -197,7 +203,11 @@ static int config_set(uint32_t key, GVariant *data,
 {
        struct dev_context *devc;
        uint64_t samplerate;
-       int ret;
+       int ret, status;
+       GVariantIter iter_arr, iter_pair;
+       GVariant *keyval, *probe_var, *shunt_var = NULL;
+       const char *probe_str, *shunt_str;
+       unsigned int probe, shunt;
 
        (void)cg;
 
@@ -229,6 +239,31 @@ static int config_set(uint32_t key, GVariant *data,
                devc->samplerate = samplerate;
                sr_dbg("Setting samplerate to %" PRIu64, devc->samplerate);
                break;
+       case SR_CONF_SHUNT_RESISTANCE:
+               g_variant_iter_init(&iter_arr, data);
+               keyval = g_variant_iter_next_value(&iter_arr);
+               g_variant_iter_init(&iter_pair, keyval);
+               probe_var = g_variant_iter_next_value(&iter_pair);
+               if (probe_var)
+                       shunt_var = g_variant_iter_next_value(&iter_pair);
+               if (!shunt_var) {
+                       sr_err("Invalid value");
+                       ret = SR_ERR_ARG;
+                       break;
+               }
+
+               probe_str = g_variant_get_string(probe_var, NULL);
+               shunt_str = g_variant_get_string(shunt_var, NULL);
+               probe = strtoul(probe_str, NULL, 10);
+               shunt = strtoul(shunt_str, NULL, 10);
+               status = bl_acme_set_shunt(devc, probe, shunt);
+               if (status < 0) {
+                       ret = status;
+                       break;
+               }
+
+               sr_dbg("Shunt resistance for probe %u set to %u", probe, shunt);
+               break;
        default:
                ret = SR_ERR_NA;
        }
diff --git a/src/hardware/baylibre-acme/protocol.c 
b/src/hardware/baylibre-acme/protocol.c
index 573c552..a3211a6 100644
--- a/src/hardware/baylibre-acme/protocol.c
+++ b/src/hardware/baylibre-acme/protocol.c
@@ -18,12 +18,13 @@
  */
 
 #include <string.h>
-#include <stdlib.h> /* strtol() */
 #include <errno.h>
 #include <fcntl.h> /* open(), etc... */
+#include <glib/gstdio.h>
 #include "protocol.h"
 
 struct channel_group_priv {
+       int probe_type;
        int hwmon_num;
 };
 
@@ -40,6 +41,9 @@ static const uint8_t temp_i2c_addrs[] = {
        0x0, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x4f, 0x4b,
 };
 
+#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];
@@ -199,8 +203,11 @@ SR_PRIV gboolean bl_acme_register_probe(struct sr_dev_inst 
*sdi, int type,
 {
        struct sr_channel_group *cg;
        struct channel_group_priv *cgp;
+       struct dev_context *devc;
        int hwmon;
 
+       devc = sdi->priv;
+
        /* Obtain the hwmon index. */
        hwmon = get_hwmon_index(addr);
        if (hwmon < 0)
@@ -209,6 +216,7 @@ 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;
        cg->name = g_strdup_printf("Probe_%d", prb_num);
        cg->priv = cgp;
 
@@ -224,10 +232,125 @@ SR_PRIV gboolean bl_acme_register_probe(struct 
sr_dev_inst *sdi, int type,
        }
 
        sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+       devc->probes[prb_num - 1] = cgp;
 
        return TRUE;
 }
 
+SR_PRIV int bl_acme_read_shunt_values(const struct sr_dev_inst *sdi,
+                                     GVariant **data)
+{
+       struct sr_channel_group *chg;
+       struct channel_group_priv *chgp;
+       GString *path, *buf;
+       uint32_t shunt;
+       GSList *chgl;
+       gboolean status;
+       gchar *contents;
+       gsize size;
+       GError *err = NULL;
+       int ret = SR_OK;
+
+       path = g_string_sized_new(128);
+       buf = g_string_new("");
+
+       for (chgl = sdi->channel_groups; chgl; chgl = chgl->next) {
+               chg = chgl->data;
+               chgp = chg->priv;
+
+               g_string_append_printf(buf, "%s: ", chg->name);
+               if (chgp->probe_type == PROBE_TEMP) {
+                       g_string_append_printf(buf, "N/A, ");
+               } else {
+                       g_string_printf(path,
+                               "/sys/class/hwmon/hwmon%d/shunt_resistor",
+                               chgp->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");
+                               goto out;
+                       }
+
+                       status = g_file_get_contents(path->str, &contents,
+                                                    &size, &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));
+                       g_string_append_printf(buf, "%u mOhms, ", shunt);
+                       g_free(contents);
+               }
+       }
+
+       buf->str[buf->len - 2] = '\0';
+       *data = g_variant_new_string(buf->str);
+
+out:
+       g_string_free(path, TRUE);
+       g_string_free(buf, TRUE);
+
+       return ret;
+}
+
+SR_PRIV int bl_acme_set_shunt(const struct dev_context *devc,
+                             unsigned int probe, unsigned int shunt)
+{
+       GString *path;
+       FILE *fd;
+
+       if (probe > MAX_PROBES) {
+               sr_err("Invalid probe number");
+               return SR_ERR_ARG;
+       }
+
+       if (devc->probes[probe - 1] == NULL) {
+               sr_err("Probe %u not present", probe);
+               return SR_ERR_ARG;
+       }
+
+       if (devc->probes[probe - 1]->probe_type == PROBE_TEMP) {
+               sr_err("Unable to set shunt on a temperature probe");
+               return SR_ERR_ARG;
+       }
+
+       path = g_string_sized_new(128);
+       g_string_printf(path, "/sys/class/hwmon/hwmon%u/shunt_resistor",
+                       devc->probes[probe - 1]->hwmon_num);
+
+       /*
+        * 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));
+               g_string_free(path, TRUE);
+               return SR_ERR_IO;
+       }
+
+       g_string_free(path, TRUE);
+       g_fprintf(fd, "%u\n", MOHM_TO_UOHM(shunt));
+       /*
+        * XXX There's no g_fclose() in GLib. This seems to work,
+        * but is it safe?
+        */
+       fclose(fd);
+
+       return SR_OK;
+}
+
 static int channel_to_mq(struct sr_channel *ch)
 {
        struct channel_priv *chp;
diff --git a/src/hardware/baylibre-acme/protocol.h 
b/src/hardware/baylibre-acme/protocol.h
index 5252118..642d0ee 100644
--- a/src/hardware/baylibre-acme/protocol.h
+++ b/src/hardware/baylibre-acme/protocol.h
@@ -22,6 +22,7 @@
 
 #include <stdint.h>
 #include <glib.h>
+#include <stdlib.h> /* strtol(), strtoul() */
 #include <unistd.h> /* pipe() */
 #include "libsigrok.h"
 #include "libsigrok-internal.h"
@@ -69,6 +70,8 @@ struct dev_context {
        int64_t last_sample_fin;
        int pipe_fds[2];
        GIOChannel *channel;
+
+       struct channel_group_priv *probes[MAX_PROBES];
 };
 
 SR_PRIV uint8_t bl_acme_get_enrg_addr(int index);
@@ -81,6 +84,11 @@ SR_PRIV gboolean bl_acme_detect_probe(unsigned int addr,
 SR_PRIV gboolean bl_acme_register_probe(struct sr_dev_inst *sdi, int type,
                                        unsigned int addr, int prb_num);
 
+SR_PRIV int bl_acme_read_shunt_values(const struct sr_dev_inst *sdi,
+                                     GVariant **data);
+SR_PRIV int bl_acme_set_shunt(const struct dev_context *devc,
+                             unsigned int probe, unsigned int shunt);
+
 SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data);
 
 #endif
-- 
2.1.4


------------------------------------------------------------------------------
Dive into the World of Parallel Programming. The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net/
_______________________________________________
sigrok-devel mailing list
sigrok-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sigrok-devel

Reply via email to