/* * This file is part of the libsigrok project. * * Copyright (C) 2019 Derek Hageman * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "protocol.h" static struct sr_dev_driver mooshimeter_dmm_driver_info; static const uint32_t scanopts[] = { SR_CONF_CONN, }; static const uint32_t drvopts[] = { SR_CONF_MULTIMETER, }; static const uint32_t devopts[] = { SR_CONF_CONTINUOUS, SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_LIST, SR_CONF_LIMIT_MSEC | SR_CONF_SET | SR_CONF_LIST, SR_CONF_AVG_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_CHANNEL_CONFIG | SR_CONF_SET, }; static void init_dev(struct sr_dev_inst *sdi) { struct dev_context *devc; struct sr_channel *chan; devc = g_new0(struct dev_context, 1); sdi->priv = devc; sdi->status = SR_ST_INITIALIZING; sdi->vendor = g_strdup("Mooshim Engineering"); sdi->model = g_strdup("Mooshimeter"); sr_sw_limits_init(&devc->limits); chan = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1"); devc->channel_meaning[0].mq = SR_MQ_CURRENT; devc->channel_meaning[0].unit = SR_UNIT_AMPERE; devc->channel_meaning[0].mqflags = SR_MQFLAG_DC; devc->channel_meaning[0].channels = g_slist_prepend(NULL, chan); chan = sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "CH2"); devc->channel_meaning[1].mq = SR_MQ_VOLTAGE; devc->channel_meaning[1].unit = SR_UNIT_VOLT; devc->channel_meaning[1].mqflags = SR_MQFLAG_DC; devc->channel_meaning[1].channels = g_slist_prepend(NULL, chan); chan = sr_channel_new(sdi, 2, SR_CHANNEL_ANALOG, FALSE, "P"); devc->channel_meaning[2].mq = SR_MQ_POWER; devc->channel_meaning[2].unit = SR_UNIT_WATT; devc->channel_meaning[2].mqflags = SR_MQFLAG_RMS; devc->channel_meaning[2].channels = g_slist_prepend(NULL, chan); } static GSList *scan(struct sr_dev_driver *di, GSList *options) { struct sr_bt_desc *desc; const char *conn; struct sr_config *src; GSList *l; int ret; conn = NULL; for (l = options; l; l = l->next) { src = l->data; switch (src->key) { case SR_CONF_CONN: conn = g_variant_get_string(src->data, NULL); break; } } if (!conn) return NULL; desc = sr_bt_desc_new(); if (!desc) return NULL; ret = sr_bt_config_addr_remote(desc, conn); if (ret < 0) goto err; /* * These handles where queried with btgatt-client, since the * documentation specifies them in terms of UUIDs. * * service - start: 0x0010, end: 0xffff, type: primary, uuid: 1bc5ffa0-0200-62ab-e411-f254e005dbd4 * charac - start: 0x0011, value: 0x0012, props: 0x08, ext_props: 0x0000, uuid: 1bc5ffa1-0200-62ab-e411-f254e005dbd4 * descr - handle: 0x0013, uuid: 00002901-0000-1000-8000-00805f9b34fb * charac - start: 0x0014, value: 0x0015, props: 0x10, ext_props: 0x0000, uuid: 1bc5ffa2-0200-62ab-e411-f254e005dbd4 * descr - handle: 0x0016, uuid: 00002902-0000-1000-8000-00805f9b34fb * descr - handle: 0x0017, uuid: 00002901-0000-1000-8000-00805f9b34fb */ ret = sr_bt_config_notify(desc, 0x0015, 0x0012, 0x0016, 0x0001); if (ret < 0) goto err; ret = sr_bt_connect_ble(desc); if (ret < 0) goto err; sr_bt_disconnect(desc); struct sr_dev_inst *sdi = g_malloc0(sizeof(struct sr_dev_inst)); struct dev_context *devc = g_malloc0(sizeof(struct dev_context)); sdi->priv = devc; sdi->inst_type = SR_INST_USER; sdi->connection_id = g_strdup(conn); sdi->conn = desc; init_dev(sdi); return std_scan_complete(di, g_slist_prepend(NULL, sdi)); err: sr_bt_desc_free(desc); return NULL; } static int dev_clear(const struct sr_dev_driver *di) { struct drv_context *drvc = di->context; struct sr_dev_inst *sdi; GSList *l; if (drvc) { for (l = drvc->instances; l; l = l->next) { sdi = l->data; struct sr_bt_desc *desc = sdi->conn; if (desc) sr_bt_desc_free(desc); sdi->conn = NULL; } } return std_dev_clear(di); } static int set_channel1_mean(const struct sr_dev_inst *sdi) { struct dev_context *devc = sdi->priv; devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_RMS; devc->channel_meaning[0].mqflags |= SR_MQFLAG_DC; return mooshimeter_dmm_set_chooser(sdi, "CH1:ANALYSIS", "CH1:ANALYSIS:MEAN"); } static int set_channel1_rms(const struct sr_dev_inst *sdi) { struct dev_context *devc = sdi->priv; devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DC; devc->channel_meaning[0].mqflags |= SR_MQFLAG_RMS; return mooshimeter_dmm_set_chooser(sdi, "CH1:ANALYSIS", "CH1:ANALYSIS:RMS"); } static int set_channel1_buffer(const struct sr_dev_inst *sdi) { struct dev_context *devc = sdi->priv; devc->channel_meaning[0].mqflags &= ~(SR_MQFLAG_DC | SR_MQFLAG_RMS); return mooshimeter_dmm_set_chooser(sdi, "CH1:ANALYSIS", "CH1:ANALYSIS:BUFFER"); } static int set_channel2_mean(const struct sr_dev_inst *sdi) { struct dev_context *devc = sdi->priv; devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_RMS; devc->channel_meaning[1].mqflags |= SR_MQFLAG_DC; return mooshimeter_dmm_set_chooser(sdi, "CH2:ANALYSIS", "CH2:ANALYSIS:MEAN"); } static int set_channel2_rms(const struct sr_dev_inst *sdi) { struct dev_context *devc = sdi->priv; devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DC; devc->channel_meaning[1].mqflags |= SR_MQFLAG_RMS; return mooshimeter_dmm_set_chooser(sdi, "CH2:ANALYSIS", "CH2:ANALYSIS:RMS"); } static int set_channel2_buffer(const struct sr_dev_inst *sdi) { struct dev_context *devc = sdi->priv; devc->channel_meaning[1].mqflags &= ~(SR_MQFLAG_DC | SR_MQFLAG_RMS); return mooshimeter_dmm_set_chooser(sdi, "CH2:ANALYSIS", "CH2:ANALYSIS:BUFFER"); } static void autorange_channel1_current(const struct sr_dev_inst *sdi, float value) { mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I", "CH1:MAPPING:CURRENT", value); } static int configure_channel1_current(const struct sr_dev_inst *sdi, float range) { struct dev_context *devc = sdi->priv; int ret; ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING", "CH1:MAPPING:CURRENT"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I", "CH1:MAPPING:CURRENT", range); if (ret != SR_OK) return ret; if (range <= 0) { devc->channel_autorange[0] = autorange_channel1_current; devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE; } else { devc->channel_autorange[0] = NULL; devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE; } devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE; devc->channel_meaning[0].mq = SR_MQ_CURRENT; devc->channel_meaning[0].unit = SR_UNIT_AMPERE; return SR_OK; } static void autorange_channel1_temperature(const struct sr_dev_inst *sdi, float value) { mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I", "CH1:MAPPING:TEMP", value); } static int configure_channel1_temperature(const struct sr_dev_inst *sdi, float range) { struct dev_context *devc = sdi->priv; int ret; ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING", "CH1:MAPPING:TEMP"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I", "CH1:MAPPING:TEMP", range); if (ret != SR_OK) return ret; if (range <= 0) { devc->channel_autorange[0] = autorange_channel1_temperature; devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE; } else { devc->channel_autorange[0] = NULL; devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE; } devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE; devc->channel_meaning[0].mq = SR_MQ_TEMPERATURE; devc->channel_meaning[0].unit = SR_UNIT_KELVIN; return SR_OK; } static void autorange_channel1_auxv(const struct sr_dev_inst *sdi, float value) { mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I", "SHARED:AUX_V", value); } static int configure_channel1_auxv(const struct sr_dev_inst *sdi, float range) { struct dev_context *devc = sdi->priv; int ret; ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:AUX_V"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING", "CH1:MAPPING:SHARED"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I", "SHARED:AUX_V", range); if (ret != SR_OK) return ret; if (range <= 0) { devc->channel_autorange[0] = autorange_channel1_auxv; devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE; } else { devc->channel_autorange[0] = NULL; devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE; } devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE; devc->channel_meaning[0].mq = SR_MQ_VOLTAGE; devc->channel_meaning[0].unit = SR_UNIT_VOLT; return SR_OK; } static void autorange_channel1_resistance(const struct sr_dev_inst *sdi, float value) { mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I", "SHARED:RESISTANCE", value); } static int configure_channel1_resistance(const struct sr_dev_inst *sdi, float range) { struct dev_context *devc = sdi->priv; int ret; ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:RESISTANCE"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING", "CH1:MAPPING:SHARED"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I", "SHARED:RESISTANCE", range); if (ret != SR_OK) return ret; if (range <= 0) { devc->channel_autorange[0] = autorange_channel1_resistance; devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE; } else { devc->channel_autorange[0] = NULL; devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE; } devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE; devc->channel_meaning[0].mq = SR_MQ_RESISTANCE; devc->channel_meaning[0].unit = SR_UNIT_OHM; return SR_OK; } static void autorange_channel1_diode(const struct sr_dev_inst *sdi, float value) { mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I", "SHARED:DIODE", value); } static int configure_channel1_diode(const struct sr_dev_inst *sdi, float range) { struct dev_context *devc = sdi->priv; int ret; ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:DIODE"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING", "CH1:MAPPING:SHARED"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I", "SHARED:DIODE", range); if (ret != SR_OK) return ret; if (range <= 0) { devc->channel_autorange[0] = autorange_channel1_diode; devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE; } else { devc->channel_autorange[0] = NULL; devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE; } devc->channel_meaning[0].mqflags |= SR_MQFLAG_DIODE; devc->channel_meaning[0].mq = SR_MQ_VOLTAGE; devc->channel_meaning[0].unit = SR_UNIT_VOLT; return SR_OK; } static void autorange_channel2_voltage(const struct sr_dev_inst *sdi, float value) { mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I", "CH2:MAPPING:VOLTAGE", value); } static int configure_channel2_voltage(const struct sr_dev_inst *sdi, float range) { struct dev_context *devc = sdi->priv; int ret; ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING", "CH2:MAPPING:VOLTAGE"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I", "CH2:MAPPING:VOLTAGE", range); if (ret != SR_OK) return ret; if (range <= 0) { devc->channel_autorange[1] = autorange_channel2_voltage; devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE; } else { devc->channel_autorange[1] = NULL; devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE; } devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE; devc->channel_meaning[1].mq = SR_MQ_VOLTAGE; devc->channel_meaning[1].unit = SR_UNIT_VOLT; return SR_OK; } static void autorange_channel2_temperature(const struct sr_dev_inst *sdi, float value) { mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I", "CH2:MAPPING:TEMP", value); } static int configure_channel2_temperature(const struct sr_dev_inst *sdi, float range) { struct dev_context *devc = sdi->priv; int ret; ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING", "CH2:MAPPING:TEMP"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I", "CH2:MAPPING:TEMP", range); if (ret != SR_OK) return ret; if (range <= 0) { devc->channel_autorange[1] = autorange_channel2_temperature; devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE; } else { devc->channel_autorange[1] = NULL; devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE; } devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE; devc->channel_meaning[1].mq = SR_MQ_TEMPERATURE; devc->channel_meaning[1].unit = SR_UNIT_CELSIUS; return SR_OK; } static void autorange_channel2_auxv(const struct sr_dev_inst *sdi, float value) { mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I", "SHARED:AUX_V", value); } static int configure_channel2_auxv(const struct sr_dev_inst *sdi, float range) { struct dev_context *devc = sdi->priv; int ret; ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:AUX_V"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING", "CH2:MAPPING:SHARED"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I", "SHARED:AUX_V", range); if (ret != SR_OK) return ret; if (range <= 0) { devc->channel_autorange[1] = autorange_channel2_auxv; devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE; } else { devc->channel_autorange[1] = NULL; devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE; } devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE; devc->channel_meaning[1].mq = SR_MQ_VOLTAGE; devc->channel_meaning[1].unit = SR_UNIT_VOLT; return SR_OK; } static void autorange_channel2_resistance(const struct sr_dev_inst *sdi, float value) { mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I", "SHARED:RESISTANCE", value); } static int configure_channel2_resistance(const struct sr_dev_inst *sdi, float range) { struct dev_context *devc = sdi->priv; int ret; ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:RESISTANCE"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING", "CH2:MAPPING:SHARED"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I", "SHARED:RESISTANCE", range); if (ret != SR_OK) return ret; if (range <= 0) { devc->channel_autorange[1] = autorange_channel2_resistance; devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE; } else { devc->channel_autorange[1] = NULL; devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE; } devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE; devc->channel_meaning[1].mq = SR_MQ_RESISTANCE; devc->channel_meaning[1].unit = SR_UNIT_OHM; return SR_OK; } static void autorange_channel2_diode(const struct sr_dev_inst *sdi, float value) { mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I", "SHARED:DIODE", value); } static int configure_channel2_diode(const struct sr_dev_inst *sdi, float range) { struct dev_context *devc = sdi->priv; int ret; ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:DIODE"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING", "CH2:MAPPING:SHARED"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I", "SHARED:DIODE", range); if (ret != SR_OK) return ret; if (range <= 0) { devc->channel_autorange[1] = autorange_channel2_diode; devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE; } else { devc->channel_autorange[1] = NULL; devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE; } devc->channel_meaning[1].mqflags |= SR_MQFLAG_DIODE; devc->channel_meaning[1].mq = SR_MQ_VOLTAGE; devc->channel_meaning[1].unit = SR_UNIT_VOLT; return SR_OK; } /* * Full string: CH1,CH2 * Each channel: MODE[:RANGE[:ANALYSIS]] * Channel 1 mode: * Current, A * Temperature, T, K * Resistance, Ohm, W * Diode, D * Aux, LV * Channel 2 mode: * Voltage, V * Temperature, T, K * Resistance, Ohm, W * Diode, D * Aux, LV * Range is the upper bound of the range (e.g. 60 for 0-60 V or 600 for 0-600), * zero or absent for autoranging * Analysis: * Mean, DC * RMS, AC * Buffer, Samples */ static int apply_channel_config(const struct sr_dev_inst *sdi, const char *config) { gchar **channel_config; gchar **parameters; const gchar *param; int ret = SR_ERR; float range; gboolean shared_in_use = FALSE; channel_config = g_strsplit_set(config, ",/", -1); if (!channel_config[0]) goto err_free_channel_config; parameters = g_strsplit_set(channel_config[0], ":;", -1); if (parameters[0] && parameters[0][0]) { range = 0; if (parameters[1]) range = g_ascii_strtod(parameters[1], NULL); param = parameters[0]; if (!g_ascii_strncasecmp(param, "Resistance", 10) || !g_ascii_strncasecmp(param, "Ohm", 3) || !g_ascii_strncasecmp(param, "W", 1) || !g_ascii_strncasecmp(param, "R", 1)) { ret = configure_channel1_resistance(sdi, range); if (ret != SR_OK) goto err_free_parameters; shared_in_use = TRUE; } else if (!g_ascii_strncasecmp(param, "Diode", 5) || !g_ascii_strncasecmp(param, "D", 1)) { ret = configure_channel1_diode(sdi, range); if (ret != SR_OK) goto err_free_parameters; shared_in_use = TRUE; } else if (!g_ascii_strncasecmp(param, "Aux", 3) || !g_ascii_strncasecmp(param, "LV", 2)) { ret = configure_channel1_auxv(sdi, range); if (ret != SR_OK) goto err_free_parameters; shared_in_use = TRUE; } else if (!g_ascii_strncasecmp(param, "T", 1) || !g_ascii_strncasecmp(param, "K", 1)) { ret = configure_channel1_temperature(sdi, range); if (ret != SR_OK) goto err_free_parameters; } else if (!g_ascii_strncasecmp(param, "Current", 7) || !g_ascii_strncasecmp(param, "A", 1) || *parameters[0]) { ret = configure_channel1_current(sdi, range); if (ret != SR_OK) goto err_free_parameters; } else { sr_info("Unrecognized mode for CH1: %s.", param); ret = configure_channel1_current(sdi, range); if (ret != SR_OK) goto err_free_parameters; } if (parameters[1] && parameters[2]) { param = parameters[2]; if (!g_ascii_strcasecmp(param, "RMS") || !g_ascii_strcasecmp(param, "AC")) { ret = set_channel1_rms(sdi); if (ret != SR_OK) goto err_free_parameters; } else if (!g_ascii_strcasecmp(param, "Buffer") || !g_ascii_strcasecmp(param, "Samples")) { ret = set_channel1_buffer(sdi); if (ret != SR_OK) goto err_free_parameters; } else { ret = set_channel1_mean(sdi); if (ret != SR_OK) goto err_free_parameters; } } } g_strfreev(parameters); if (!channel_config[1]) { g_strfreev(channel_config); return SR_OK; } parameters = g_strsplit_set(channel_config[1], ":;", -1); if (parameters[0] && parameters[0][0]) { range = 0; if (parameters[1]) range = g_ascii_strtod(parameters[1], NULL); param = parameters[0]; if (!g_ascii_strncasecmp(param, "Resistance", 10) || !g_ascii_strncasecmp(param, "Ohm", 3) || !g_ascii_strncasecmp(param, "W", 1) || !g_ascii_strncasecmp(param, "R", 1)) { if (shared_in_use) { ret = SR_ERR; goto err_free_parameters; } ret = configure_channel2_resistance(sdi, range); if (ret != SR_OK) goto err_free_parameters; } else if (!g_ascii_strncasecmp(param, "Diode", 5) || !g_ascii_strncasecmp(param, "D", 1)) { if (shared_in_use) { ret = SR_ERR; goto err_free_parameters; } ret = configure_channel2_diode(sdi, range); if (ret != SR_OK) goto err_free_parameters; } else if (!g_ascii_strncasecmp(param, "Aux", 3) || !g_ascii_strncasecmp(param, "LV", 2)) { if (shared_in_use) { ret = SR_ERR; goto err_free_parameters; } ret = configure_channel2_auxv(sdi, range); if (ret != SR_OK) goto err_free_parameters; } else if (!g_ascii_strncasecmp(param, "T", 1) || !g_ascii_strncasecmp(param, "K", 1)) { ret = configure_channel2_temperature(sdi, range); if (ret != SR_OK) goto err_free_parameters; } else if (!g_ascii_strncasecmp(param, "V", 1) || !param[0]) { ret = configure_channel2_voltage(sdi, range); if (ret != SR_OK) goto err_free_parameters; } else { sr_info("Unrecognized mode for CH2: %s.", param); ret = configure_channel2_voltage(sdi, range); if (ret != SR_OK) goto err_free_parameters; } if (parameters[1] && parameters[2]) { param = parameters[2]; if (!g_ascii_strcasecmp(param, "RMS") || !g_ascii_strcasecmp(param, "AC")) { ret = set_channel2_rms(sdi); if (ret != SR_OK) goto err_free_parameters; } else if (!g_ascii_strcasecmp(param, "Buffer") || !g_ascii_strcasecmp(param, "Samples")) { ret = set_channel2_buffer(sdi); if (ret != SR_OK) goto err_free_parameters; } else { ret = set_channel2_mean(sdi); if (ret != SR_OK) goto err_free_parameters; } } } g_strfreev(parameters); g_strfreev(channel_config); return SR_OK; err_free_parameters: g_strfreev(parameters); err_free_channel_config: g_strfreev(channel_config); return ret; } static int dev_open(struct sr_dev_inst *sdi) { int ret; ret = mooshimeter_dmm_open(sdi); if (ret != SR_OK) return ret; sdi->status = SR_ST_INACTIVE; ret = mooshimeter_dmm_set_chooser(sdi, "SAMPLING:TRIGGER", "SAMPLING:TRIGGER:OFF"); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:RATE", "SAMPLING:RATE", 125); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:DEPTH", "SAMPLING:DEPTH", 64); if (ret != SR_OK) return ret; /* Looks like these sometimes get set to 8, somehow? */ ret = mooshimeter_dmm_set_integer(sdi, "CH1:BUF_BPS", 24); if (ret != SR_OK) return ret; ret = mooshimeter_dmm_set_integer(sdi, "CH2:BUF_BPS", 24); if (ret != SR_OK) return ret; ret = configure_channel1_current(sdi, 0); if (ret != SR_OK) return ret; ret = set_channel1_mean(sdi); if (ret != SR_OK) return ret; ret = configure_channel2_voltage(sdi, 0); if (ret != SR_OK) return ret; ret = set_channel2_mean(sdi); if (ret != SR_OK) return ret; sdi->status = SR_ST_ACTIVE; return SR_OK; } static int dev_close(struct sr_dev_inst *sdi) { struct dev_context *devc = sdi->priv; sdi->status = SR_ST_INACTIVE; g_slist_free(devc->channel_meaning[0].channels); devc->channel_meaning[0].channels = NULL; g_slist_free(devc->channel_meaning[1].channels); devc->channel_meaning[1].channels = NULL; g_slist_free(devc->channel_meaning[2].channels); devc->channel_meaning[2].channels = NULL; return mooshimeter_dmm_close(sdi); } static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { struct dev_context *devc = sdi->priv; int ret; float value; (void)cg; switch (key) { case SR_CONF_SAMPLERATE: ret = mooshimeter_dmm_get_chosen_number(sdi, "SAMPLING:RATE", "SAMPLING:RATE", &value); if (ret != SR_OK) return ret; *data = g_variant_new_uint64((guint64)value); return SR_OK; case SR_CONF_AVG_SAMPLES: ret = mooshimeter_dmm_get_chosen_number(sdi, "SAMPLING:DEPTH", "SAMPLING:DEPTH", &value); if (ret != SR_OK) return ret; *data = g_variant_new_uint64((guint64)value); return SR_OK; case SR_CONF_CHANNEL_CONFIG: return SR_ERR_NA; default: break; } return sr_sw_limits_config_get(&devc->limits, key, data); } static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { struct dev_context *devc = sdi->priv; (void)cg; switch (key) { case SR_CONF_SAMPLERATE: return mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:RATE", "SAMPLING:RATE", g_variant_get_uint64(data)); case SR_CONF_AVG_SAMPLES: return mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:DEPTH", "SAMPLING:DEPTH", g_variant_get_uint64(data)); case SR_CONF_CHANNEL_CONFIG: return apply_channel_config(sdi, g_variant_get_string(data, NULL)); default: break; } return sr_sw_limits_config_set(&devc->limits, key, data); } static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { int ret; float *values; size_t count; uint64_t *integers; switch (key) { case SR_CONF_SAMPLERATE: ret = mooshimeter_dmm_get_available_number_choices(sdi, "SAMPLING:RATE", &values, &count); if (ret != SR_OK) return ret; integers = g_malloc(sizeof(uint64_t) * count); for (size_t i = 0; i < count; i++) integers[i] = (uint64_t)values[i]; g_free(values); *data = std_gvar_samplerates(integers, count); g_free(integers); return SR_OK; case SR_CONF_AVG_SAMPLES: ret = mooshimeter_dmm_get_available_number_choices(sdi, "SAMPLING:DEPTH", &values, &count); if (ret != SR_OK) return ret; integers = g_malloc(sizeof(uint64_t) * count); for (size_t i = 0; i < count; i++) integers[i] = (uint64_t)values[i]; g_free(values); *data = std_gvar_array_u64(integers, count); g_free(integers); return SR_OK; case SR_CONF_CHANNEL_CONFIG: return SR_ERR_NA; default: break; } return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts); } static int dev_acquisition_start(const struct sr_dev_inst *sdi) { struct dev_context *devc = sdi->priv; int ret; ret = mooshimeter_dmm_set_chooser(sdi, "SAMPLING:TRIGGER", "SAMPLING:TRIGGER:CONTINUOUS"); if (ret) return ret; sr_sw_limits_acquisition_start(&devc->limits); std_session_send_df_header(sdi); sr_session_source_add(sdi->session, -1, 0, 10000, mooshimeter_dmm_heartbeat, (void *)sdi); /* The Bluetooth socket isn't exposed, so just poll for data. */ sr_session_source_add(sdi->session, -2, 0, 50, mooshimeter_dmm_poll, (void *)sdi); devc->enable_value_stream = TRUE; return SR_OK; } static int dev_acquisition_stop(struct sr_dev_inst *sdi) { struct dev_context *devc = sdi->priv; sr_session_source_remove(sdi->session, -1); sr_session_source_remove(sdi->session, -2); devc->enable_value_stream = FALSE; mooshimeter_dmm_set_chooser(sdi, "SAMPLING:TRIGGER", "SAMPLING:TRIGGER:OFF"); return SR_OK; } static struct sr_dev_driver mooshimeter_dmm_driver_info = { .name = "mooshimeter-dmm", .longname = "Mooshimeter DMM", .api_version = 1, .init = std_init, .cleanup = std_cleanup, .scan = scan, .dev_list = std_dev_list, .dev_clear = dev_clear, .config_get = config_get, .config_set = config_set, .config_list = config_list, .dev_open = dev_open, .dev_close = dev_close, .dev_acquisition_start = dev_acquisition_start, .dev_acquisition_stop = dev_acquisition_stop, .context = NULL, }; SR_REGISTER_DEV_DRIVER(mooshimeter_dmm_driver_info);