*/
#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 {
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];
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?
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;
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;
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;
}
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,
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;
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);
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;
+}
+
+/*
+ * 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)
+{
+ 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);
+ if (val < 0) {
+ sr_err("Error setting power-off state: gpio: %d",
+ pws_gpios[cgp->index]);
+ return SR_ERR_IO;
+ }
+
+ return SR_OK;
+}
+
static int channel_to_mq(struct sr_channel *ch)
{
struct channel_priv *chp;
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:
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:
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:
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) {
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;
}
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();