]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/serial-lcr/api.c
serial-lcr: move device driver code from src/lcr/ to src/hardware/
[libsigrok.git] / src / hardware / serial-lcr / api.c
index bbec3809df006722b06ca98c81ea94582445fcbc..002fb5bf3293db1142e206fbdee6aa366d745d87 100644 (file)
@@ -2,6 +2,7 @@
  * 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, \
@@ -61,17 +313,20 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                        .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,