serial-lcr: move device driver code from src/lcr/ to src/hardware/

Keep the ES51919 chip support in the src/lcr/ directory, and move device
driver specific code to the src/hardware/serial-lcr/ directory. Implement
the same driver layout for LCR meters as is used for DMM devices.

This also addresses a few issues in the serial-lcr driver: Unbreak --get
and --show, do process a few LCR packets after probing the device, to
gather current parameter values. Keep sending meta packets when these
parameters change during acquisition, like the previous implementation
did. Use common code for frame/time limits.

Note that although LCR meters usually operate with AC to classify L/C/R
components, one of the officially supported modes is DC resistance.
Which means that an output frequency of 0 is not just a fallback when
packet parsing fails, it's also a regular value of the freq parameter.
List all supported frequencies including DC in strict numerical order.

Although all currently supported devices use the same ES51919 chip, the
implementation is prepared to support other devices which use different
LCR meter chips as well. The list of known equivalent circuit models and
output frequencies is kept in src/lcr/ chip support. It's assumed that
one LCR packet communicates the data for all channels/displays similar
to the serial-dmm driver implementation.
This commit is contained in:
Gerhard Sittig 2019-05-01 22:01:48 +02:00
parent 67785f2568
commit bf5c4d46f8
6 changed files with 624 additions and 609 deletions

View File

@ -546,6 +546,8 @@ src_libdrivers_la_SOURCES += \
endif endif
if HW_SERIAL_LCR if HW_SERIAL_LCR
src_libdrivers_la_SOURCES += \ src_libdrivers_la_SOURCES += \
src/hardware/serial-lcr/protocol.h \
src/hardware/serial-lcr/protocol.c \
src/hardware/serial-lcr/api.c src/hardware/serial-lcr/api.c
endif endif
if HW_SIGLENT_SDS if HW_SIGLENT_SDS

View File

@ -2,6 +2,7 @@
* This file is part of the libsigrok project. * This file is part of the libsigrok project.
* *
* Copyright (C) 2014 Janne Huttunen <jahuttun@gmail.com> * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -18,41 +19,292 @@
*/ */
#include <config.h> #include <config.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <glib.h> #include <glib.h>
#include <libsigrok/libsigrok.h> #include <libsigrok/libsigrok.h>
#include "libsigrok-internal.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,
struct lcr_es51919_info { SR_CONF_SERIALCOMM,
struct sr_dev_driver di;
const char *vendor;
const char *model;
}; };
static int dev_clear(const struct sr_dev_driver *di) 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,
};
static struct sr_dev_inst *scan_packet_check_devinst;
static void scan_packet_check_setup(struct sr_dev_inst *sdi)
{ {
return std_dev_clear_with_callback(di, es51919_serial_clean); scan_packet_check_devinst = sdi;
}
static gboolean scan_packet_check_func(const uint8_t *buf)
{
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) 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 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 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) \ #define LCR_ES51919(id, vendor, model) \
&((struct lcr_es51919_info) { \ &((struct lcr_info) { \
{ \ { \
.name = id, \ .name = id, \
.longname = vendor " " model, \ .longname = vendor " " model, \
@ -61,17 +313,20 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
.cleanup = std_cleanup, \ .cleanup = std_cleanup, \
.scan = scan, \ .scan = scan, \
.dev_list = std_dev_list, \ .dev_list = std_dev_list, \
.dev_clear = dev_clear, \ .dev_clear = std_dev_clear, \
.config_get = es51919_serial_config_get, \ .config_get = config_get, \
.config_set = es51919_serial_config_set, \ .config_set = config_set, \
.config_list = es51919_serial_config_list, \ .config_list = config_list, \
.dev_open = std_serial_dev_open, \ .dev_open = std_serial_dev_open, \
.dev_close = std_serial_dev_close, \ .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, \ .dev_acquisition_stop = std_serial_dev_acquisition_stop, \
.context = NULL, \ .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 }).di
SR_REGISTER_DEV_DRIVER_LIST(lcr_es51919_drivers, SR_REGISTER_DEV_DRIVER_LIST(lcr_es51919_drivers,

View File

@ -0,0 +1,178 @@
/*
* 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;
}

View File

@ -0,0 +1,66 @@
/*
* 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

View File

@ -2,6 +2,7 @@
* This file is part of the libsigrok project. * This file is part of the libsigrok project.
* *
* Copyright (C) 2014 Janne Huttunen <jahuttun@gmail.com> * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -18,241 +19,17 @@
*/ */
#include <config.h> #include <config.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <glib.h> #include <glib.h>
#include <libsigrok/libsigrok.h> #include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h" #include "libsigrok-internal.h"
#include <math.h>
#include <stdint.h>
#include <string.h>
#define LOG_PREFIX "es51919" #define LOG_PREFIX "es51919"
#ifdef HAVE_SERIAL_COMM #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. * Cyrustek ES51919 LCR chipset host protocol.
* *
@ -346,31 +123,44 @@ static int serial_stream_check(struct sr_serial_dev_inst *serial,
* 0x10: footer2 (0x0a) ? * 0x10: footer2 (0x0a) ?
*/ */
#define PACKET_SIZE 17
static const double frequencies[] = { static const double frequencies[] = {
100, 120, 1000, 10000, 100000, 0, 0, 100, 120, 1000, 10000, 100000,
}; };
static const size_t freq_code_map[] = {
1, 2, 3, 4, 5, 0,
};
static uint64_t get_frequency(size_t code)
{
uint64_t freq;
if (code >= ARRAY_SIZE(freq_code_map)) {
sr_err("Unknown output frequency code %zu.", code);
return frequencies[0];
}
code = freq_code_map[code];
freq = frequencies[code];
return freq;
}
enum { MODEL_NONE, MODEL_PAR, MODEL_SER, MODEL_AUTO, }; enum { MODEL_NONE, MODEL_PAR, MODEL_SER, MODEL_AUTO, };
static const char *const models[] = { static const char *const circuit_models[] = {
"NONE", "PARALLEL", "SERIES", "AUTO", "NONE", "PARALLEL", "SERIES", "AUTO",
}; };
struct dev_context { static const char *get_equiv_model(size_t code)
struct dev_limit_counter frame_count; {
if (code >= ARRAY_SIZE(circuit_models)) {
sr_err("Unknown equivalent circuit model code %zu.", code);
return "NONE";
}
struct dev_time_counter time_count; return circuit_models[code];
}
struct dev_buffer *buf;
/** The frequency of the test signal (index to frequencies[]). */
unsigned int freq;
/** Equivalent circuit model (index to models[]). */
unsigned int model;
};
static const uint8_t *pkt_to_buf(const uint8_t *pkt, int is_secondary) static const uint8_t *pkt_to_buf(const uint8_t *pkt, int is_secondary)
{ {
@ -414,18 +204,22 @@ static int parse_mq(const uint8_t *pkt, int is_secondary, int is_parallel)
static float parse_value(const uint8_t *buf, int *digits) static float parse_value(const uint8_t *buf, int *digits)
{ {
static const int exponents[] = {0, -1, -2, -3, -4, -5, -6, -7}; static const int exponents[] = {0, -1, -2, -3, -4, -5, -6, -7};
int exponent; int exponent;
int16_t val; int16_t val;
float fval;
exponent = exponents[buf[3] & 7]; exponent = exponents[buf[3] & 7];
*digits = -exponent; *digits = -exponent;
val = (buf[1] << 8) | buf[2]; 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, static void parse_measurement(const uint8_t *pkt, float *floatval,
struct sr_datafeed_analog *analog, struct sr_datafeed_analog *analog, int is_secondary)
int is_secondary)
{ {
static const struct { static const struct {
int unit; int unit;
@ -447,6 +241,7 @@ static void parse_measurement(const uint8_t *pkt, float *floatval,
{ SR_UNIT_PERCENTAGE, 0 }, /* % */ { SR_UNIT_PERCENTAGE, 0 }, /* % */
{ SR_UNIT_DEGREE, 0 }, /* degree */ { SR_UNIT_DEGREE, 0 }, /* degree */
}; };
const uint8_t *buf; const uint8_t *buf;
int digits, exponent; int digits, exponent;
int state; int state;
@ -494,366 +289,80 @@ static void parse_measurement(const uint8_t *pkt, float *floatval,
analog->spec->spec_digits = digits - exponent; 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; return get_frequency(pkt[3] >> 5);
freq = pkt[3] >> 5;
if (freq >= ARRAY_SIZE(frequencies)) {
sr_err("Unknown frequency %u.", freq);
freq = ARRAY_SIZE(frequencies) - 1;
}
return freq;
} }
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) if (pkt[2] & 0x40)
return MODEL_AUTO; code = MODEL_AUTO;
else if (parse_mq(pkt, 0, 0) == SR_MQ_RESISTANCE) else if (parse_mq(pkt, 0, 0) == SR_MQ_RESISTANCE)
return MODEL_NONE; code = MODEL_NONE;
else if (pkt[2] & 0x80)
return MODEL_PAR;
else else
return MODEL_SER; code = (pkt[2] & 0x80) ? MODEL_PAR : MODEL_SER;
return get_equiv_model(code);
} }
static gboolean packet_valid(const uint8_t *pkt) SR_PRIV gboolean es51919_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; /* Check for fixed 0x00 0x0d prefix. */
} if (pkt[0] != 0x00 || pkt[1] != 0x0d)
return FALSE;
static int send_freq_update(struct sr_dev_inst *sdi, unsigned int freq) /* Check for fixed 0x0d 0x0a suffix. */
{ if (pkt[15] != 0x0d || pkt[16] != 0x0a)
return sr_session_send_meta(sdi, SR_CONF_OUTPUT_FREQUENCY, return FALSE;
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]));
}
static void handle_packet(struct sr_dev_inst *sdi, 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;
/* 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);
/* Packet appears to be valid. */
return TRUE; return TRUE;
} }
static const char *const channel_names[] = { "P1", "P2" }; SR_PRIV int es51919_packet_parse(const uint8_t *pkt, float *val,
struct sr_datafeed_analog *analog, void *info)
static int setup_channels(struct sr_dev_inst *sdi)
{ {
unsigned int i; struct lcr_parse_info *parse_info;
for (i = 0; i < ARRAY_SIZE(channel_names); i++) parse_info = info;
sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel_names[i]); 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; return SR_OK;
} }
SR_PRIV void es51919_serial_clean(void *priv) /*
* 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;
if (!(devc = priv))
return;
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)sdi;
(void)cg; (void)cg;
devc = sdi->priv;
switch (key) { 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;
}
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)
{
struct dev_context *devc;
uint64_t val;
(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: case SR_CONF_OUTPUT_FREQUENCY:
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_DOUBLE, *data = g_variant_new_fixed_array(G_VARIANT_TYPE_DOUBLE,
ARRAY_AND_SIZE(frequencies), sizeof(double)); ARRAY_AND_SIZE(frequencies), sizeof(double));
break; return SR_OK;
case SR_CONF_EQUIV_CIRCUIT_MODEL: case SR_CONF_EQUIV_CIRCUIT_MODEL:
*data = g_variant_new_strv(ARRAY_AND_SIZE(models)); *data = g_variant_new_strv(ARRAY_AND_SIZE(circuit_models));
break; return SR_OK;
default: default:
return SR_ERR_NA; return SR_ERR_NA;
} }
/* UNREACH */
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;
} }
#endif #endif

View File

@ -1619,21 +1619,26 @@ SR_PRIV int sr_vc96_parse(const uint8_t *buf, float *floatval,
/*--- lcr/es51919.c ---------------------------------------------------------*/ /*--- lcr/es51919.c ---------------------------------------------------------*/
SR_PRIV void es51919_serial_clean(void *priv); /* Acquisition details which apply to all supported serial-lcr devices. */
SR_PRIV struct sr_dev_inst *es51919_serial_scan(GSList *options, struct lcr_parse_info {
const char *vendor, size_t ch_idx;
const char *model); uint64_t output_freq;
SR_PRIV int es51919_serial_config_get(uint32_t key, GVariant **data, const char *circuit_model;
const struct sr_dev_inst *sdi, };
const struct sr_channel_group *cg);
SR_PRIV int es51919_serial_config_set(uint32_t key, GVariant *data, #define ES51919_PACKET_SIZE 17
const struct sr_dev_inst *sdi, #define ES51919_CHANNEL_COUNT 2
const struct sr_channel_group *cg); #define ES51919_COMM_PARAM "9600/8n1/rts=1/dtr=1"
SR_PRIV int es51919_serial_config_list(uint32_t key, GVariant **data,
const struct sr_dev_inst *sdi, SR_PRIV int es51919_config_get(uint32_t key, GVariant **data,
const struct sr_channel_group *cg); 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_config_set(uint32_t key, GVariant *data,
SR_PRIV int es51919_serial_acquisition_stop(struct sr_dev_inst *sdi); 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 -----------------------------------------------------------*/ /*--- dmm/ut372.c -----------------------------------------------------------*/