endif
if HW_SERIAL_LCR
src_libdrivers_la_SOURCES += \
+ src/hardware/serial-lcr/protocol.h \
+ src/hardware/serial-lcr/protocol.c \
src/hardware/serial-lcr/api.c
endif
if HW_SIGLENT_SDS
* This file is part of the libsigrok project.
*
* Copyright (C) 2014 Janne Huttunen <jahuttun@gmail.com>
+ * Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
*
* 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
*/
#include <config.h>
-#include <stdint.h>
-#include <string.h>
-#include <math.h>
#include <glib.h>
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
+#include <math.h>
+#include "protocol.h"
+#include <stdint.h>
+#include <string.h>
-#define LOG_PREFIX "serial-lcr-es51919"
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_LCRMETER,
+};
-struct lcr_es51919_info {
- struct sr_dev_driver di;
- const char *vendor;
- const char *model;
+static const uint32_t devopts[] = {
+ SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+ SR_CONF_OUTPUT_FREQUENCY | SR_CONF_GET | SR_CONF_LIST,
+ SR_CONF_EQUIV_CIRCUIT_MODEL | SR_CONF_GET | SR_CONF_LIST,
};
-static int dev_clear(const struct sr_dev_driver *di)
+static struct sr_dev_inst *scan_packet_check_devinst;
+
+static void scan_packet_check_setup(struct sr_dev_inst *sdi)
+{
+ scan_packet_check_devinst = sdi;
+}
+
+static gboolean scan_packet_check_func(const uint8_t *buf)
{
- return std_dev_clear_with_callback(di, es51919_serial_clean);
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ const struct lcr_info *lcr;
+ struct lcr_parse_info *info;
+
+ /* Get a reference to the LCR model that is getting checked. */
+ sdi = scan_packet_check_devinst;
+ if (!sdi)
+ return FALSE;
+ devc = sdi->priv;
+ if (!devc)
+ return FALSE;
+ lcr = devc->lcr_info;
+ if (!lcr)
+ return FALSE;
+
+ /* Synchronize to the stream of LCR packets. */
+ if (!lcr->packet_valid(buf))
+ return FALSE;
+
+ /* Have LCR packets _processed_, gather current configuration. */
+ info = &devc->parse_info;
+ memset(info, 0, sizeof(*info));
+ if (lcr->packet_parse(buf, NULL, NULL, info) == SR_OK) {
+ devc->output_freq = info->output_freq;
+ if (info->circuit_model)
+ devc->circuit_model = info->circuit_model;
+ }
+
+ return TRUE;
}
static GSList *scan(struct sr_dev_driver *di, GSList *options)
{
- struct lcr_es51919_info *lcr;
+ struct lcr_info *lcr;
+ struct sr_config *src;
+ GSList *l, *devices;
+ const char *conn, *serialcomm;
+ struct sr_serial_dev_inst *serial;
+ uint8_t buf[128];
+ size_t len, dropped;
+ int ret;
struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ size_t ch_idx;
+ char ch_name[8];
- lcr = (struct lcr_es51919_info *)di;
+ lcr = (struct lcr_info *)di;
- if (!(sdi = es51919_serial_scan(options, lcr->vendor, lcr->model)))
+ /* Get serial port name and communication parameters. */
+ conn = NULL;
+ serialcomm = lcr->comm;
+ 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;
+ case SR_CONF_SERIALCOMM:
+ serialcomm = g_variant_get_string(src->data, NULL);
+ break;
+ }
+ }
+ if (!conn)
return NULL;
- return std_scan_complete(di, g_slist_append(NULL, sdi));
+ /* Open the serial port. */
+ serial = sr_serial_dev_inst_new(conn, serialcomm);
+ if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+ return NULL;
+ sr_info("Probing serial port %s.", conn);
+
+ /*
+ * See if we can detect a device of specified type.
+ *
+ * No supported device provides a means to "identify" yet. No
+ * supported device requires "packet request" yet. They all just
+ * send data periodically. So we check if the packets match the
+ * probed device's expected format.
+ */
+ serial_flush(serial);
+ devices = NULL;
+ len = sizeof(buf);
+ ret = serial_stream_detect(serial, buf, &len,
+ lcr->packet_size, lcr->packet_valid, 3000);
+ if (ret != SR_OK)
+ goto scan_cleanup;
+
+ /*
+ * If the packets were found to match after more than two packets
+ * got dropped, something is wrong. This is worth warning about,
+ * but isn't fatal. The dropped bytes might be due to nonstandard
+ * cables that ship with some devices.
+ */
+ dropped = len - lcr->packet_size;
+ if (dropped > 2 * lcr->packet_size)
+ sr_warn("Had to drop unexpected amounts of data.");
+
+ /* Create a device instance for the found device. */
+ sr_info("Found %s %s device on port %s.", lcr->vendor, lcr->model, conn);
+ sdi = g_malloc0(sizeof(*sdi));
+ sdi->status = SR_ST_INACTIVE;
+ sdi->vendor = g_strdup(lcr->vendor);
+ sdi->model = g_strdup(lcr->model);
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+ devc = g_malloc0(sizeof(*devc));
+ sdi->priv = devc;
+ devc->lcr_info = lcr;
+ sr_sw_limits_init(&devc->limits);
+ for (ch_idx = 0; ch_idx < lcr->channel_count; ch_idx++) {
+ snprintf(ch_name, sizeof(ch_name), "P%zu", ch_idx + 1);
+ sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, ch_name);
+ }
+ devices = g_slist_append(devices, sdi);
+
+ /*
+ * Receive a few more packets (and process them!) to have the
+ * current output frequency and circuit model parameter values
+ * detected. The above "stream detect" phase only synchronized
+ * to the packets by checking their validity, but it cannot
+ * provide details. This phase here runs a modified "checker"
+ * routine which also extracts details from LCR packets after
+ * the device got detected and parameter storage was prepared.
+ */
+ sr_info("Retrieving current acquisition parameters.");
+ len = sizeof(buf);
+ scan_packet_check_setup(sdi);
+ ret = serial_stream_detect(serial, buf, &len,
+ lcr->packet_size, scan_packet_check_func, 1000);
+ scan_packet_check_setup(NULL);
+
+scan_cleanup:
+ serial_close(serial);
+
+ return std_scan_complete(di, devices);
+}
+
+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;
+ const struct lcr_info *lcr;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_LIMIT_FRAMES:
+ case SR_CONF_LIMIT_MSEC:
+ return sr_sw_limits_config_get(&devc->limits, key, data);
+ case SR_CONF_OUTPUT_FREQUENCY:
+ *data = g_variant_new_double(devc->output_freq);
+ return SR_OK;
+ case SR_CONF_EQUIV_CIRCUIT_MODEL:
+ if (!devc->circuit_model)
+ return SR_ERR_NA;
+ *data = g_variant_new_string(devc->circuit_model);
+ return SR_OK;
+ default:
+ lcr = devc->lcr_info;
+ if (!lcr)
+ return SR_ERR_NA;
+ if (!lcr->config_get)
+ return SR_ERR_NA;
+ return lcr->config_get(key, data, sdi, cg);
+ }
+ /* UNREACH */
+}
+
+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;
+ const struct lcr_info *lcr;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_LIMIT_FRAMES:
+ case SR_CONF_LIMIT_MSEC:
+ return sr_sw_limits_config_set(&devc->limits, key, data);
+ default:
+ lcr = devc->lcr_info;
+ if (!lcr)
+ return SR_ERR_NA;
+ if (!lcr->config_set)
+ return SR_ERR_NA;
+ return lcr->config_set(key, data, sdi, cg);
+ }
+ /* UNREACH */
+}
+
+static int config_list(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ const struct lcr_info *lcr;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg,
+ scanopts, drvopts, devopts);
+ default:
+ break;
+ }
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ switch (key) {
+ default:
+ lcr = devc->lcr_info;
+ if (!lcr || !lcr->config_list)
+ return SR_ERR_NA;
+ return lcr->config_list(key, data, sdi, cg);
+ }
+ /* UNREACH */
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ devc = sdi->priv;
+
+ /*
+ * Clear values that were gathered during scan or in a previous
+ * acquisition, so that this acquisition's data feed immediately
+ * starts with meta packets before first measurement values, and
+ * also communicates subsequent parameter changes.
+ */
+ devc->output_freq = 0;
+ devc->circuit_model = NULL;
+
+ sr_sw_limits_acquisition_start(&devc->limits);
+ std_session_send_df_header(sdi);
+
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 50,
+ lcr_receive_data, (void *)sdi);
+
+ return SR_OK;
}
#define LCR_ES51919(id, vendor, model) \
- &((struct lcr_es51919_info) { \
+ &((struct lcr_info) { \
{ \
.name = id, \
.longname = vendor " " model, \
.cleanup = std_cleanup, \
.scan = scan, \
.dev_list = std_dev_list, \
- .dev_clear = dev_clear, \
- .config_get = es51919_serial_config_get, \
- .config_set = es51919_serial_config_set, \
- .config_list = es51919_serial_config_list, \
+ .dev_clear = std_dev_clear, \
+ .config_get = config_get, \
+ .config_set = config_set, \
+ .config_list = config_list, \
.dev_open = std_serial_dev_open, \
.dev_close = std_serial_dev_close, \
- .dev_acquisition_start = es51919_serial_acquisition_start, \
+ .dev_acquisition_start = dev_acquisition_start, \
.dev_acquisition_stop = std_serial_dev_acquisition_stop, \
.context = NULL, \
}, \
- vendor, model, \
+ vendor, model, ES51919_CHANNEL_COUNT, \
+ ES51919_COMM_PARAM, ES51919_PACKET_SIZE, \
+ es51919_packet_valid, es51919_packet_parse, \
+ NULL, NULL, es51919_config_list, \
}).di
SR_REGISTER_DEV_DRIVER_LIST(lcr_es51919_drivers,
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Janne Huttunen <jahuttun@gmail.com>
+ * Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <libsigrok/libsigrok.h>
+#include <libsigrok-internal.h>
+#include "protocol.h"
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void send_frame_start(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct lcr_parse_info *info;
+ uint64_t freq;
+ const char *model;
+ struct sr_datafeed_packet packet;
+
+ devc = sdi->priv;
+ info = &devc->parse_info;
+
+ /* Communicate changes of frequency or model before data values. */
+ freq = info->output_freq;
+ if (freq != devc->output_freq) {
+ devc->output_freq = freq;
+ sr_session_send_meta(sdi, SR_CONF_OUTPUT_FREQUENCY,
+ g_variant_new_double(freq));
+ }
+ model = info->circuit_model;
+ if (model && model != devc->circuit_model) {
+ devc->circuit_model = model;
+ sr_session_send_meta(sdi, SR_CONF_EQUIV_CIRCUIT_MODEL,
+ g_variant_new_string(model));
+ }
+
+ /* Data is about to get sent. Start a new frame. */
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(sdi, &packet);
+}
+
+static void send_frame_end(struct sr_dev_inst *sdi)
+{
+ struct sr_datafeed_packet packet;
+
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(sdi, &packet);
+}
+
+static int handle_packet(struct sr_dev_inst *sdi, const uint8_t *pkt)
+{
+ struct dev_context *devc;
+ struct lcr_parse_info *info;
+ const struct lcr_info *lcr;
+ size_t ch_idx;
+ int rc;
+ float value;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct sr_analog_encoding encoding;
+ struct sr_analog_meaning meaning;
+ struct sr_analog_spec spec;
+ gboolean frame;
+ struct sr_channel *channel;
+
+ devc = sdi->priv;
+ info = &devc->parse_info;
+ lcr = devc->lcr_info;
+
+ /* Note: digits/spec_digits will be overridden later. */
+ sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
+ analog.num_samples = 1;
+ analog.data = &value;
+
+ frame = FALSE;
+ for (ch_idx = 0; ch_idx < lcr->channel_count; ch_idx++) {
+ channel = g_slist_nth_data(sdi->channels, ch_idx);
+ analog.meaning->channels = g_slist_append(NULL, channel);
+ info->ch_idx = ch_idx;
+ rc = lcr->packet_parse(pkt, &value, &analog, info);
+ if (sdi->session && rc == SR_OK && analog.meaning->mq && channel->enabled) {
+ if (!frame) {
+ send_frame_start(sdi);
+ frame = TRUE;
+ }
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(sdi, &packet);
+ }
+ g_slist_free(analog.meaning->channels);
+ }
+ if (frame) {
+ send_frame_end(sdi);
+ sr_sw_limits_update_frames_read(&devc->limits, 1);
+ }
+
+ return SR_OK;
+}
+
+static int handle_new_data(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ ssize_t rdsize;
+ const struct lcr_info *lcr;
+ uint8_t *pkt;
+ size_t copy_len;
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+
+ /* Read another chunk of data into the buffer. */
+ rdsize = sizeof(devc->buf) - devc->buf_rxpos;
+ rdsize = serial_read_nonblocking(serial, &devc->buf[devc->buf_rxpos], rdsize);
+ if (rdsize < 0)
+ return SR_ERR_IO;
+ devc->buf_rxpos += rdsize;
+
+ /*
+ * Process as many packets as the buffer might contain. Assume
+ * that the stream is synchronized in the typical case. Re-sync
+ * in case of mismatch (skip individual bytes until data matches
+ * the expected packet layout again).
+ */
+ lcr = devc->lcr_info;
+ while (devc->buf_rxpos >= lcr->packet_size) {
+ pkt = &devc->buf[0];
+ if (!lcr->packet_valid(pkt)) {
+ copy_len = devc->buf_rxpos - 1;
+ memmove(&devc->buf[0], &devc->buf[1], copy_len);
+ devc->buf_rxpos--;
+ continue;
+ }
+ (void)handle_packet(sdi, pkt);
+ copy_len = devc->buf_rxpos - lcr->packet_size;
+ memmove(&devc->buf[0], &devc->buf[lcr->packet_size], copy_len);
+ devc->buf_rxpos -= lcr->packet_size;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int lcr_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ if (revents == G_IO_IN)
+ handle_new_data(sdi);
+
+ if (sr_sw_limits_check(&devc->limits))
+ sr_dev_acquisition_stop(sdi);
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Janne Huttunen <jahuttun@gmail.com>
+ * Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_SERIAL_LCR_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SERIAL_LCR_PROTOCOL_H
+
+#define LOG_PREFIX "serial-lcr"
+
+#include <libsigrok/libsigrok.h>
+#include <libsigrok-internal.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+struct lcr_info {
+ struct sr_dev_driver di;
+ const char *vendor;
+ const char *model;
+ size_t channel_count;
+ const char *comm;
+ size_t packet_size;
+ gboolean (*packet_valid)(const uint8_t *pkt);
+ int (*packet_parse)(const uint8_t *pkt, float *value,
+ struct sr_datafeed_analog *analog, void *info);
+ int (*config_get)(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg);
+ int (*config_set)(uint32_t key, GVariant *data,
+ const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg);
+ int (*config_list)(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg);
+};
+
+#define LCR_BUFSIZE 128
+
+struct dev_context {
+ const struct lcr_info *lcr_info;
+ struct sr_sw_limits limits;
+ uint8_t buf[LCR_BUFSIZE];
+ size_t buf_rxpos, buf_rdpos;
+ struct lcr_parse_info parse_info;
+ uint64_t output_freq;
+ const char *circuit_model;
+};
+
+SR_PRIV int lcr_receive_data(int fd, int revents, void *cb_data);
+
+#endif
* This file is part of the libsigrok project.
*
* Copyright (C) 2014 Janne Huttunen <jahuttun@gmail.com>
+ * Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
*
* 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
*/
#include <config.h>
-#include <stdint.h>
-#include <string.h>
-#include <math.h>
#include <glib.h>
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
#define LOG_PREFIX "es51919"
#ifdef HAVE_SERIAL_COMM
-struct dev_buffer {
- /** Total size of the buffer. */
- size_t size;
- /** Amount of data currently in the buffer. */
- size_t len;
- /** Offset where the data starts in the buffer. */
- size_t offset;
- /** Space for the data. */
- uint8_t data[];
-};
-
-static struct dev_buffer *dev_buffer_new(size_t size)
-{
- struct dev_buffer *dbuf;
-
- dbuf = g_malloc0(sizeof(struct dev_buffer) + size);
- dbuf->size = size;
- dbuf->len = 0;
- dbuf->offset = 0;
-
- return dbuf;
-}
-
-static void dev_buffer_destroy(struct dev_buffer *dbuf)
-{
- g_free(dbuf);
-}
-
-static int dev_buffer_fill_serial(struct dev_buffer *dbuf,
- struct sr_dev_inst *sdi)
-{
- struct sr_serial_dev_inst *serial;
- int len;
-
- serial = sdi->conn;
-
- /* If we already have data, move it to the beginning of the buffer. */
- if (dbuf->len > 0 && dbuf->offset > 0)
- memmove(dbuf->data, dbuf->data + dbuf->offset, dbuf->len);
-
- dbuf->offset = 0;
-
- len = dbuf->size - dbuf->len;
- len = serial_read_nonblocking(serial, dbuf->data + dbuf->len, len);
- if (len < 0) {
- sr_err("Serial port read error: %d.", len);
- return len;
- }
-
- dbuf->len += len;
-
- return SR_OK;
-}
-
-static uint8_t *dev_buffer_packet_find(struct dev_buffer *dbuf,
- gboolean (*packet_valid)(const uint8_t *),
- size_t packet_size)
-{
- size_t offset;
-
- while (dbuf->len >= packet_size) {
- if (packet_valid(dbuf->data + dbuf->offset)) {
- offset = dbuf->offset;
- dbuf->offset += packet_size;
- dbuf->len -= packet_size;
- return dbuf->data + offset;
- }
- dbuf->offset++;
- dbuf->len--;
- }
-
- return NULL;
-}
-
-struct dev_limit_counter {
- /** The current number of received samples/frames/etc. */
- uint64_t count;
- /** The limit (in number of samples/frames/etc.). */
- uint64_t limit;
-};
-
-static void dev_limit_counter_start(struct dev_limit_counter *cnt)
-{
- cnt->count = 0;
-}
-
-static void dev_limit_counter_inc(struct dev_limit_counter *cnt)
-{
- cnt->count++;
-}
-
-static void dev_limit_counter_limit_set(struct dev_limit_counter *cnt,
- uint64_t limit)
-{
- cnt->limit = limit;
-}
-
-static gboolean dev_limit_counter_limit_reached(struct dev_limit_counter *cnt)
-{
- if (cnt->limit && cnt->count >= cnt->limit) {
- sr_info("Requested counter limit reached.");
- return TRUE;
- }
-
- return FALSE;
-}
-
-struct dev_time_counter {
- /** The starting time of current sampling run. */
- int64_t starttime;
- /** The time limit (in milliseconds). */
- uint64_t limit;
-};
-
-static void dev_time_counter_start(struct dev_time_counter *cnt)
-{
- cnt->starttime = g_get_monotonic_time();
-}
-
-static void dev_time_limit_set(struct dev_time_counter *cnt, uint64_t limit)
-{
- cnt->limit = limit;
-}
-
-static gboolean dev_time_limit_reached(struct dev_time_counter *cnt)
-{
- int64_t time;
-
- if (cnt->limit) {
- time = (g_get_monotonic_time() - cnt->starttime) / 1000;
- if (time > (int64_t)cnt->limit) {
- sr_info("Requested time limit reached.");
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-static void serial_conf_get(GSList *options, const char *def_serialcomm,
- const char **conn, const char **serialcomm)
-{
- struct sr_config *src;
- GSList *l;
-
- *conn = *serialcomm = 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;
- case SR_CONF_SERIALCOMM:
- *serialcomm = g_variant_get_string(src->data, NULL);
- break;
- }
- }
-
- if (*serialcomm == NULL)
- *serialcomm = def_serialcomm;
-}
-
-static struct sr_serial_dev_inst *serial_dev_new(GSList *options,
- const char *def_serialcomm)
-
-{
- const char *conn, *serialcomm;
-
- serial_conf_get(options, def_serialcomm, &conn, &serialcomm);
-
- if (!conn)
- return NULL;
-
- return sr_serial_dev_inst_new(conn, serialcomm);
-}
-
-static int serial_stream_check_buf(struct sr_serial_dev_inst *serial,
- uint8_t *buf, size_t buflen,
- size_t packet_size,
- packet_valid_callback is_valid,
- uint64_t timeout_ms)
-{
- size_t len, dropped;
- int ret;
-
- if ((ret = serial_open(serial, SERIAL_RDWR)) != SR_OK)
- return ret;
-
- serial_flush(serial);
-
- len = buflen;
- ret = serial_stream_detect(serial, buf, &len, packet_size,
- is_valid, timeout_ms);
-
- serial_close(serial);
-
- if (ret != SR_OK)
- return ret;
-
- /*
- * If we dropped more than two packets worth of data, something is
- * wrong. We shouldn't quit however, since the dropped bytes might be
- * just zeroes at the beginning of the stream. Those can occur as a
- * combination of the nonstandard cable that ships with some devices
- * and the serial port or USB to serial adapter.
- */
- dropped = len - packet_size;
- if (dropped > 2 * packet_size)
- sr_warn("Had to drop too much data.");
-
- return SR_OK;
-}
-
-static int serial_stream_check(struct sr_serial_dev_inst *serial,
- size_t packet_size,
- packet_valid_callback is_valid,
- uint64_t timeout_ms)
-{
- uint8_t buf[128];
-
- return serial_stream_check_buf(serial, buf, sizeof(buf), packet_size,
- is_valid, timeout_ms);
-}
-
/*
* Cyrustek ES51919 LCR chipset host protocol.
*
* 0x10: footer2 (0x0a) ?
*/
-#define PACKET_SIZE 17
-
static const double frequencies[] = {
- 100, 120, 1000, 10000, 100000, 0,
+ 0, 100, 120, 1000, 10000, 100000,
};
-enum { MODEL_NONE, MODEL_PAR, MODEL_SER, MODEL_AUTO, };
-
-static const char *const models[] = {
- "NONE", "PARALLEL", "SERIES", "AUTO",
+static const size_t freq_code_map[] = {
+ 1, 2, 3, 4, 5, 0,
};
-struct dev_context {
- struct dev_limit_counter frame_count;
+static uint64_t get_frequency(size_t code)
+{
+ uint64_t freq;
- struct dev_time_counter time_count;
+ if (code >= ARRAY_SIZE(freq_code_map)) {
+ sr_err("Unknown output frequency code %zu.", code);
+ return frequencies[0];
+ }
- struct dev_buffer *buf;
+ code = freq_code_map[code];
+ freq = frequencies[code];
- /** The frequency of the test signal (index to frequencies[]). */
- unsigned int freq;
+ return freq;
+}
- /** Equivalent circuit model (index to models[]). */
- unsigned int model;
+enum { MODEL_NONE, MODEL_PAR, MODEL_SER, MODEL_AUTO, };
+
+static const char *const circuit_models[] = {
+ "NONE", "PARALLEL", "SERIES", "AUTO",
};
+static const char *get_equiv_model(size_t code)
+{
+ if (code >= ARRAY_SIZE(circuit_models)) {
+ sr_err("Unknown equivalent circuit model code %zu.", code);
+ return "NONE";
+ }
+
+ return circuit_models[code];
+}
+
static const uint8_t *pkt_to_buf(const uint8_t *pkt, int is_secondary)
{
return is_secondary ? pkt + 10 : pkt + 5;
static float parse_value(const uint8_t *buf, int *digits)
{
static const int exponents[] = {0, -1, -2, -3, -4, -5, -6, -7};
+
int exponent;
int16_t val;
+ float fval;
exponent = exponents[buf[3] & 7];
*digits = -exponent;
val = (buf[1] << 8) | buf[2];
- return (float)val * powf(10, exponent);
+ fval = (float)val;
+ fval *= powf(10, exponent);
+
+ return fval;
}
static void parse_measurement(const uint8_t *pkt, float *floatval,
- struct sr_datafeed_analog *analog,
- int is_secondary)
+ struct sr_datafeed_analog *analog, int is_secondary)
{
static const struct {
int unit;
{ SR_UNIT_PERCENTAGE, 0 }, /* % */
{ SR_UNIT_DEGREE, 0 }, /* degree */
};
+
const uint8_t *buf;
int digits, exponent;
int state;
analog->spec->spec_digits = digits - exponent;
}
-static unsigned int parse_freq(const uint8_t *pkt)
+static uint64_t parse_freq(const uint8_t *pkt)
{
- unsigned int freq;
-
- freq = pkt[3] >> 5;
-
- if (freq >= ARRAY_SIZE(frequencies)) {
- sr_err("Unknown frequency %u.", freq);
- freq = ARRAY_SIZE(frequencies) - 1;
- }
-
- return freq;
+ return get_frequency(pkt[3] >> 5);
}
-static unsigned int parse_model(const uint8_t *pkt)
+static const char *parse_model(const uint8_t *pkt)
{
+ size_t code;
+
if (pkt[2] & 0x40)
- return MODEL_AUTO;
+ code = MODEL_AUTO;
else if (parse_mq(pkt, 0, 0) == SR_MQ_RESISTANCE)
- return MODEL_NONE;
- else if (pkt[2] & 0x80)
- return MODEL_PAR;
+ code = MODEL_NONE;
else
- return MODEL_SER;
-}
-
-static gboolean packet_valid(const uint8_t *pkt)
-{
- /*
- * If the first two bytes of the packet are indeed a constant
- * header, they should be checked too. Since we don't know it
- * for sure, we'll just check the last two for now since they
- * seem to be constant just like in the other Cyrustek chipset
- * protocols.
- */
- if (pkt[15] == 0xd && pkt[16] == 0xa)
- return TRUE;
-
- return FALSE;
-}
+ code = (pkt[2] & 0x80) ? MODEL_PAR : MODEL_SER;
-static int send_freq_update(struct sr_dev_inst *sdi, unsigned int freq)
-{
- return sr_session_send_meta(sdi, SR_CONF_OUTPUT_FREQUENCY,
- g_variant_new_double(frequencies[freq]));
-}
-
-static int send_model_update(struct sr_dev_inst *sdi, unsigned int model)
-{
- return sr_session_send_meta(sdi, SR_CONF_EQUIV_CIRCUIT_MODEL,
- g_variant_new_string(models[model]));
+ return get_equiv_model(code);
}
-static void handle_packet(struct sr_dev_inst *sdi, const uint8_t *pkt)
+SR_PRIV gboolean es51919_packet_valid(const uint8_t *pkt)
{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct sr_analog_encoding encoding;
- struct sr_analog_meaning meaning;
- struct sr_analog_spec spec;
- struct dev_context *devc;
- unsigned int val;
- float floatval;
- gboolean frame;
- struct sr_channel *channel;
-
- devc = sdi->priv;
-
- val = parse_freq(pkt);
- if (val != devc->freq) {
- if (send_freq_update(sdi, val) == SR_OK)
- devc->freq = val;
- else
- return;
- }
-
- val = parse_model(pkt);
- if (val != devc->model) {
- if (send_model_update(sdi, val) == SR_OK)
- devc->model = val;
- else
- return;
- }
- frame = FALSE;
+ /* Check for fixed 0x00 0x0d prefix. */
+ if (pkt[0] != 0x00 || pkt[1] != 0x0d)
+ return FALSE;
- /* Note: digits/spec_digits will be overridden later. */
- sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
-
- analog.num_samples = 1;
- analog.data = &floatval;
-
- channel = sdi->channels->data;
- analog.meaning->channels = g_slist_append(NULL, channel);
-
- parse_measurement(pkt, &floatval, &analog, 0);
- if (analog.meaning->mq != 0 && channel->enabled) {
- if (!frame) {
- packet.type = SR_DF_FRAME_BEGIN;
- sr_session_send(sdi, &packet);
- frame = TRUE;
- }
-
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
-
- sr_session_send(sdi, &packet);
- }
-
- g_slist_free(analog.meaning->channels);
-
- channel = sdi->channels->next->data;
- analog.meaning->channels = g_slist_append(NULL, channel);
-
- parse_measurement(pkt, &floatval, &analog, 1);
- if (analog.meaning->mq != 0 && channel->enabled) {
- if (!frame) {
- packet.type = SR_DF_FRAME_BEGIN;
- sr_session_send(sdi, &packet);
- frame = TRUE;
- }
-
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
-
- sr_session_send(sdi, &packet);
- }
-
- g_slist_free(analog.meaning->channels);
-
- if (frame) {
- packet.type = SR_DF_FRAME_END;
- sr_session_send(sdi, &packet);
- dev_limit_counter_inc(&devc->frame_count);
- }
-}
-
-static int handle_new_data(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- uint8_t *pkt;
- int ret;
-
- devc = sdi->priv;
-
- ret = dev_buffer_fill_serial(devc->buf, sdi);
- if (ret < 0)
- return ret;
-
- while ((pkt = dev_buffer_packet_find(devc->buf, packet_valid,
- PACKET_SIZE)))
- handle_packet(sdi, pkt);
-
- return SR_OK;
-}
-
-static int receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- if (revents == G_IO_IN) {
- /* Serial data arrived. */
- handle_new_data(sdi);
- }
-
- if (dev_limit_counter_limit_reached(&devc->frame_count) ||
- dev_time_limit_reached(&devc->time_count))
- sr_dev_acquisition_stop(sdi);
+ /* Check for fixed 0x0d 0x0a suffix. */
+ if (pkt[15] != 0x0d || pkt[16] != 0x0a)
+ return FALSE;
+ /* Packet appears to be valid. */
return TRUE;
}
-static const char *const channel_names[] = { "P1", "P2" };
-
-static int setup_channels(struct sr_dev_inst *sdi)
+SR_PRIV int es51919_packet_parse(const uint8_t *pkt, float *val,
+ struct sr_datafeed_analog *analog, void *info)
{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(channel_names); i++)
- sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel_names[i]);
-
- return SR_OK;
-}
-
-SR_PRIV void es51919_serial_clean(void *priv)
-{
- struct dev_context *devc;
-
- if (!(devc = priv))
- return;
+ struct lcr_parse_info *parse_info;
- dev_buffer_destroy(devc->buf);
-}
-
-SR_PRIV struct sr_dev_inst *es51919_serial_scan(GSList *options,
- const char *vendor,
- const char *model)
-{
- struct sr_serial_dev_inst *serial;
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- int ret;
-
- serial = NULL;
- sdi = NULL;
- devc = NULL;
-
- if (!(serial = serial_dev_new(options, "9600/8n1/rts=1/dtr=1")))
- goto scan_cleanup;
-
- ret = serial_stream_check(serial, PACKET_SIZE, packet_valid, 3000);
- if (ret != SR_OK)
- goto scan_cleanup;
-
- sr_info("Found device on port %s.", serial->port);
-
- sdi = g_malloc0(sizeof(struct sr_dev_inst));
- sdi->status = SR_ST_INACTIVE;
- sdi->vendor = g_strdup(vendor);
- sdi->model = g_strdup(model);
- devc = g_malloc0(sizeof(struct dev_context));
- devc->buf = dev_buffer_new(PACKET_SIZE * 8);
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
- sdi->priv = devc;
-
- if (setup_channels(sdi) != SR_OK)
- goto scan_cleanup;
-
- return sdi;
-
-scan_cleanup:
- es51919_serial_clean(devc);
- sr_dev_inst_free(sdi);
- sr_serial_dev_inst_free(serial);
-
- return NULL;
-}
-
-SR_PRIV int es51919_serial_config_get(uint32_t key, GVariant **data,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- devc = sdi->priv;
-
- switch (key) {
- case SR_CONF_OUTPUT_FREQUENCY:
- *data = g_variant_new_double(frequencies[devc->freq]);
- break;
- case SR_CONF_EQUIV_CIRCUIT_MODEL:
- *data = g_variant_new_string(models[devc->model]);
- break;
- default:
- return SR_ERR_NA;
+ parse_info = info;
+ if (!parse_info->ch_idx) {
+ parse_info->output_freq = parse_freq(pkt);
+ parse_info->circuit_model = parse_model(pkt);
}
+ if (val && analog)
+ parse_measurement(pkt, val, analog, parse_info->ch_idx == 1);
return SR_OK;
}
-SR_PRIV int es51919_serial_config_set(uint32_t key, GVariant *data,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+/*
+ * These are the get/set/list routines for the _chip_ specific parameters,
+ * the _device_ driver resides in src/hardware/serial-lcr/ instead.
+ */
+
+SR_PRIV int es51919_config_list(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- struct dev_context *devc;
- uint64_t val;
+ (void)sdi;
(void)cg;
- if (!(devc = sdi->priv))
- return SR_ERR_BUG;
-
switch (key) {
- case SR_CONF_LIMIT_MSEC:
- val = g_variant_get_uint64(data);
- dev_time_limit_set(&devc->time_count, val);
- sr_dbg("Setting time limit to %" PRIu64 ".", val);
- break;
- case SR_CONF_LIMIT_FRAMES:
- val = g_variant_get_uint64(data);
- dev_limit_counter_limit_set(&devc->frame_count, val);
- sr_dbg("Setting frame limit to %" PRIu64 ".", val);
- break;
- default:
- sr_spew("%s: Unsupported key %u", __func__, key);
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static const uint32_t scanopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const uint32_t drvopts[] = {
- SR_CONF_LCRMETER,
-};
-
-static const uint32_t devopts[] = {
- SR_CONF_CONTINUOUS,
- SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
- SR_CONF_LIMIT_MSEC | SR_CONF_SET,
- SR_CONF_OUTPUT_FREQUENCY | SR_CONF_GET | SR_CONF_LIST,
- SR_CONF_EQUIV_CIRCUIT_MODEL | SR_CONF_GET | SR_CONF_LIST,
-};
-
-SR_PRIV int es51919_serial_config_list(uint32_t key, GVariant **data,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- case SR_CONF_DEVICE_OPTIONS:
- return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
case SR_CONF_OUTPUT_FREQUENCY:
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_DOUBLE,
ARRAY_AND_SIZE(frequencies), sizeof(double));
- break;
+ return SR_OK;
case SR_CONF_EQUIV_CIRCUIT_MODEL:
- *data = g_variant_new_strv(ARRAY_AND_SIZE(models));
- break;
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(circuit_models));
+ return SR_OK;
default:
return SR_ERR_NA;
}
-
- return SR_OK;
-}
-
-SR_PRIV int es51919_serial_acquisition_start(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (!(devc = sdi->priv))
- return SR_ERR_BUG;
-
- dev_limit_counter_start(&devc->frame_count);
- dev_time_counter_start(&devc->time_count);
-
- std_session_send_df_header(sdi);
-
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 50,
- receive_data, (void *)sdi);
-
- return SR_OK;
+ /* UNREACH */
}
#endif
/*--- lcr/es51919.c ---------------------------------------------------------*/
-SR_PRIV void es51919_serial_clean(void *priv);
-SR_PRIV struct sr_dev_inst *es51919_serial_scan(GSList *options,
- const char *vendor,
- const char *model);
-SR_PRIV int es51919_serial_config_get(uint32_t key, GVariant **data,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg);
-SR_PRIV int es51919_serial_config_set(uint32_t key, GVariant *data,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg);
-SR_PRIV int es51919_serial_config_list(uint32_t key, GVariant **data,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg);
-SR_PRIV int es51919_serial_acquisition_start(const struct sr_dev_inst *sdi);
-SR_PRIV int es51919_serial_acquisition_stop(struct sr_dev_inst *sdi);
+/* Acquisition details which apply to all supported serial-lcr devices. */
+struct lcr_parse_info {
+ size_t ch_idx;
+ uint64_t output_freq;
+ const char *circuit_model;
+};
+
+#define ES51919_PACKET_SIZE 17
+#define ES51919_CHANNEL_COUNT 2
+#define ES51919_COMM_PARAM "9600/8n1/rts=1/dtr=1"
+
+SR_PRIV int es51919_config_get(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg);
+SR_PRIV int es51919_config_set(uint32_t key, GVariant *data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg);
+SR_PRIV int es51919_config_list(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg);
+SR_PRIV gboolean es51919_packet_valid(const uint8_t *pkt);
+SR_PRIV int es51919_packet_parse(const uint8_t *pkt, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
/*--- dmm/ut372.c -----------------------------------------------------------*/