Add a key-value config option to libsigrok, which allows to
power-off/on measured devices via sigrok apps. The key argument
should identify the probe and value should signify the desired
state.

Signed-off-by: Bartosz Golaszewski <bgolaszew...@baylibre.com>
---
 include/libsigrok/libsigrok.h |   6 ++
 src/hardware/acme/acme.c      | 240 +++++++++++++++++++++++++++++++++++++++---
 src/hwdriver.c                |   2 +
 3 files changed, 236 insertions(+), 12 deletions(-)

diff --git a/include/libsigrok/libsigrok.h b/include/libsigrok/libsigrok.h
index 2f4c4fc..c8592a2 100644
--- a/include/libsigrok/libsigrok.h
+++ b/include/libsigrok/libsigrok.h
@@ -909,6 +909,12 @@ enum sr_configkey {
         */
        SR_CONF_SHUNT_RESISTANCE,
 
+       /**
+        * The device is a power-monitor supporting remote power-off/on
+        * of measured devices.
+        */
+       SR_CONF_POWER_SWITCH,
+
        /*--- Acquisition modes, sample limiting ----------------------------*/
 
        /**
diff --git a/src/hardware/acme/acme.c b/src/hardware/acme/acme.c
index 6799428..558285f 100644
--- a/src/hardware/acme/acme.c
+++ b/src/hardware/acme/acme.c
@@ -64,6 +64,7 @@ static const uint32_t devopts[] = {
        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,
+       SR_CONF_POWER_SWITCH | SR_CONF_GET | SR_CONF_SET,
 };
 
 #define MAX_SAMPLE_RATE                500 /* In HZ */
@@ -82,6 +83,14 @@ static const uint32_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,
+};
+
 struct channel_group_priv {
        int prb_type;
        int hwmon_num;
@@ -110,6 +119,132 @@ struct dev_context {
 SR_PRIV struct sr_dev_driver acme_driver_info;
 static struct sr_dev_driver *di = &acme_driver_info;
 
+/* Linux GPIO framework helpers. */
+
+enum {
+       GPIO_DIR_IN,
+       GPIO_DIR_OUT,
+};
+
+
+static int open_and_write(const char *path, const char *buf)
+{
+       int fd;
+       ssize_t wr;
+
+       fd = open(path, O_WRONLY);
+       if (fd < 0) {
+               sr_err("error opening %s: %s", path, strerror(errno));
+               return -1;
+       }
+
+       wr = write(fd, buf, strlen(buf));
+       close(fd);
+       if (wr < 0) {
+               sr_err("error writing to %s: %s", path, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int gpio_export(unsigned gpio)
+{
+       char path[256], buf[32];
+       gboolean exported;
+
+       snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d", gpio);
+       exported = g_file_test(path, G_FILE_TEST_IS_DIR);
+       if (exported)
+               return 0; /* Already exported. */
+
+       snprintf(buf, sizeof(buf), "%u", gpio);
+
+       return open_and_write("/sys/class/gpio/export", buf);
+}
+
+static int gpio_set_direction(unsigned gpio, unsigned direction)
+{
+       char path[256], buf[32];
+
+       snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", gpio);
+       snprintf(buf, sizeof(buf), "%s\n",
+                direction == GPIO_DIR_IN ? "in" : "out");
+
+       return open_and_write(path, buf);
+}
+
+static int gpio_set_value(unsigned gpio, unsigned value)
+{
+       char path[256], buf[32];
+
+       snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio);
+       snprintf(buf, sizeof(buf), "%d\n", value);
+
+       return open_and_write(path, buf);
+}
+
+static int gpio_get_value(int gpio)
+{
+       int fd;
+       ssize_t rd;
+       char path[256], buf[32];
+       int ret;
+
+       snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio);
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               sr_err("error opening %s: %s", path, strerror(errno));
+               return -1;
+       }
+
+       memset(buf, 0, sizeof(buf));
+       rd = read(fd, buf, sizeof(buf));
+       close(fd);
+       if (rd < 0) {
+               sr_err("error reading from %s: %s", path, strerror(errno));
+               return -1;
+       }
+
+       sscanf(buf, "%d", &ret);
+
+       return ret;
+}
+
+static int gpio_setval_export(int gpio, int value)
+{
+       int st;
+
+       st = gpio_export(gpio);
+       if (st < 0)
+               return st;
+
+       st = gpio_set_direction(gpio, GPIO_DIR_OUT);
+       if (st < 0)
+               return st;
+
+       st = gpio_set_value(gpio, value);
+       if (st < 0)
+               return st;
+
+       return 0;
+}
+
+static int gpio_getval_export(int gpio)
+{
+       int st;
+
+       st = gpio_export(gpio);
+       if (st < 0)
+               return st;
+
+       st = gpio_set_direction(gpio, GPIO_DIR_IN);
+       if (st < 0)
+               return st;
+
+       return gpio_get_value(gpio);
+}
+
 static int init(struct sr_context *sr_ctx)
 {
        return std_init(sr_ctx, di, LOG_PREFIX);
@@ -492,6 +627,54 @@ out:
        return ret;
 }
 
+static int set_power_state(unsigned probe, gboolean state)
+{
+       int has_pws;
+       unsigned index;
+
+       if (probe == 0 || probe > MAX_PROBES) {
+               sr_err("Invalid probe number: %u", probe);
+               return -1;
+       }
+
+       index = probe - 1;
+       has_pws = gpio_getval_export(pws_info_gpios[index]);
+       if (!has_pws) {
+               sr_err("Probe %d doesn't have a power switch");
+               return -1;
+       }
+
+       return gpio_setval_export(pws_gpios[index], state ? 1 : 0);
+}
+
+static void read_power_states(const struct sr_dev_inst *sdi, GVariant **data)
+{
+       struct dev_context *devc;
+       unsigned in = 0;
+       gchar buf[512];
+       int i;
+
+       devc = sdi->priv;
+
+       for (i = 0; i < MAX_PROBES; i++) {
+               if (devc->probes[i] == NULL) {
+                       continue;
+               }
+               in += snprintf(buf + in, sizeof(buf) - in, "Probe_%d: ", i + 1);
+               if (devc->probes[i]->prb_type == PROBE_TEMP ||
+                   gpio_getval_export(pws_info_gpios[i]) == 0) {
+                       in += snprintf(buf + in, sizeof(buf) - in, "N/A, ");
+               } else {
+                       in += snprintf(buf + in, sizeof(buf) - in, "%s, ",
+                                      gpio_getval_export(pws_gpios[i]) == 1 ?
+                                                         "ON" : "OFF");
+               }
+       }
+
+       buf[in - 2] = '\0';
+       *data = g_variant_new_string(buf);
+}
+
 static int config_get(uint32_t key, GVariant **data,
                      const struct sr_dev_inst *sdi,
                      const struct sr_channel_group *cg)
@@ -521,6 +704,9 @@ static int config_get(uint32_t key, GVariant **data,
                if (status < 0)
                        ret = SR_ERR_BUG;
                break;
+       case SR_CONF_POWER_SWITCH:
+               read_power_states(sdi, data);
+               break;
        default:
                return SR_ERR_NA;
        }
@@ -528,6 +714,26 @@ static int config_get(uint32_t key, GVariant **data,
        return ret;
 }
 
+static int parse_keyval(GVariant *variant, const char **key, const char **val)
+{
+       GVariantIter iter_arr, iter_pair;
+       GVariant *keyval, *key_var, *val_var = NULL;
+
+       g_variant_iter_init(&iter_arr, variant);
+       keyval = g_variant_iter_next_value(&iter_arr);
+       g_variant_iter_init(&iter_pair, keyval);
+       key_var = g_variant_iter_next_value(&iter_pair);
+       if (key_var)
+               val_var = g_variant_iter_next_value(&iter_pair);
+       if (!val_var)
+               return -1;
+
+       *key = g_variant_get_string(key_var, NULL);
+       *val = g_variant_get_string(val_var, NULL);
+
+       return 0;
+}
+
 static int config_set(uint32_t key, GVariant *data,
                      const struct sr_dev_inst *sdi,
                      const struct sr_channel_group *cg)
@@ -535,10 +741,9 @@ static int config_set(uint32_t key, GVariant *data,
        struct dev_context *devc;
        uint64_t samplerate;
        int ret, status;
-       GVariantIter iter_arr, iter_pair;
-       GVariant *keyval, *probe_var, *shunt_var = NULL;
-       const char *probe_str, *shunt_str;
+       const char *probe_str, *shunt_str, *pws_str;
        unsigned probe, shunt;
+       gboolean power_state;
 
        (void)data;
        (void)cg;
@@ -572,20 +777,13 @@ static int config_set(uint32_t key, GVariant *data,
                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) {
+               status = parse_keyval(data, &probe_str, &shunt_str);
+               if (status < 0) {
                        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 = set_shunt(devc, probe, shunt);
@@ -596,6 +794,24 @@ static int config_set(uint32_t key, GVariant *data,
 
                sr_dbg("Shunt resistance for probe %u set to %u", probe, shunt);
                break;
+       case SR_CONF_POWER_SWITCH:
+               status = parse_keyval(data, &probe_str, &pws_str);
+               if (status < 0) {
+                       sr_err("Invalid value");
+                       ret = SR_ERR_ARG;
+                       break;
+               }
+
+               probe = strtoul(probe_str, NULL, 10);
+               power_state = strcmp(pws_str, "on") == 0 ? TRUE : FALSE;
+               status = set_power_state(probe, power_state);
+               if (status < 0) {
+                       ret = SR_ERR_ARG;
+                       break;
+               }
+
+               sr_dbg("Probe %u power-%s", probe, power_state ? "on" : "off");
+               break;
        default:
                ret = SR_ERR_NA;
        }
diff --git a/src/hwdriver.c b/src/hwdriver.c
index 1848a08..11994a3 100644
--- a/src/hwdriver.c
+++ b/src/hwdriver.c
@@ -185,6 +185,8 @@ static struct sr_config_info sr_config_info_data[] = {
                "Data source", NULL},
        {SR_CONF_SHUNT_RESISTANCE, SR_T_KEYVALUE, "shunt",
                "Shunt resistance", NULL},
+       {SR_CONF_POWER_SWITCH, SR_T_KEYVALUE, "power_switch",
+               "Power switch", NULL},
 
        /* Acquisition modes, sample limiting */
        {SR_CONF_LIMIT_MSEC, SR_T_UINT64, "limit_time",
-- 
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