ACLOCAL_AMFLAGS = -I autostuff
-AM_CPPFLAGS = -DFIRMWARE_DIR='"$(FIRMWARE_DIR)"'
+AM_CPPFLAGS = -Isrc -DFIRMWARE_DIR='"$(FIRMWARE_DIR)"'
lib_LTLIBRARIES = libsigrok.la
# Backend files
libsigrok_la_SOURCES = \
- backend.c \
- device.c \
- session.c \
- session_file.c \
- session_driver.c \
- drivers.c \
- hwdriver.c \
- trigger.c \
- soft-trigger.c \
- strutil.c \
- log.c \
- version.c \
- error.c \
- std.c
-
-# Input formats
-libsigrok_la_SOURCES += \
- input/binary.c \
- input/chronovu_la8.c \
- input/csv.c \
- input/input.c \
- input/vcd.c \
- input/wav.c
-
-# Output formats
-libsigrok_la_SOURCES += \
- output/output.c \
- output/analog.c \
- output/ascii.c \
- output/bits.c \
- output/binary.c \
- output/csv.c \
- output/chronovu_la8.c \
- output/gnuplot.c \
- output/hex.c \
- output/ols.c \
- output/vcd.c
-
-# Hardware (common files)
-libsigrok_la_SOURCES += \
- hardware/common/scpi.c \
- hardware/common/scpi_tcp.c
+ src/backend.c \
+ src/device.c \
+ src/session.c \
+ src/session_file.c \
+ src/session_driver.c \
+ src/drivers.c \
+ src/hwdriver.c \
+ src/trigger.c \
+ src/soft-trigger.c \
+ src/strutil.c \
+ src/log.c \
+ src/version.c \
+ src/error.c \
+ src/std.c
+
+# Input modules
+libsigrok_la_SOURCES += \
+ src/input/binary.c \
+ src/input/chronovu_la8.c \
+ src/input/csv.c \
+ src/input/input.c \
+ src/input/vcd.c \
+ src/input/wav.c
+
+# Output modules
+libsigrok_la_SOURCES += \
+ src/output/output.c \
+ src/output/analog.c \
+ src/output/ascii.c \
+ src/output/bits.c \
+ src/output/binary.c \
+ src/output/csv.c \
+ src/output/chronovu_la8.c \
+ src/output/gnuplot.c \
+ src/output/hex.c \
+ src/output/ols.c \
+ src/output/vcd.c
+
+# SCPI support
+libsigrok_la_SOURCES += \
+ src/scpi/scpi.c \
+ src/scpi/scpi_tcp.c
if NEED_RPC
libsigrok_la_SOURCES += \
- hardware/common/scpi_vxi.c \
- hardware/common/vxi_clnt.c \
- hardware/common/vxi_xdr.c \
- hardware/common/vxi.h
+ src/scpi/scpi_vxi.c \
+ src/scpi/vxi_clnt.c \
+ src/scpi/vxi_xdr.c \
+ src/scpi/vxi.h
endif
if NEED_SERIAL
libsigrok_la_SOURCES += \
- hardware/common/serial.c \
- hardware/common/scpi_serial.c
+ src/serial.c \
+ src/scpi/scpi_serial.c
endif
if NEED_USB
libsigrok_la_SOURCES += \
- hardware/common/ezusb.c \
- hardware/common/usb.c \
- hardware/common/scpi_usbtmc_libusb.c
+ src/ezusb.c \
+ src/usb.c \
+ src/scpi/scpi_usbtmc_libusb.c
endif
if NEED_VISA
libsigrok_la_SOURCES += \
- hardware/common/scpi_visa.c
+ src/scpi/scpi_visa.c
endif
-# Hardware (DMM parsers)
+# Hardware (DMM chip parsers)
libsigrok_la_SOURCES += \
- hardware/common/dmm/es519xx.c \
- hardware/common/dmm/fs9721.c \
- hardware/common/dmm/fs9922.c \
- hardware/common/dmm/m2110.c \
- hardware/common/dmm/metex14.c \
- hardware/common/dmm/rs9lcd.c
+ src/dmm/es519xx.c \
+ src/dmm/fs9721.c \
+ src/dmm/fs9922.c \
+ src/dmm/m2110.c \
+ src/dmm/metex14.c \
+ src/dmm/rs9lcd.c
# Hardware drivers
if HW_AGILENT_DMM
libsigrok_la_SOURCES += \
- hardware/agilent-dmm/api.c \
- hardware/agilent-dmm/agilent-dmm.h \
- hardware/agilent-dmm/sched.c
+ src/hardware/agilent-dmm/api.c \
+ src/hardware/agilent-dmm/agilent-dmm.h \
+ src/hardware/agilent-dmm/sched.c
endif
if HW_APPA_55II
libsigrok_la_SOURCES += \
- hardware/appa-55ii/protocol.h \
- hardware/appa-55ii/protocol.c \
- hardware/appa-55ii/api.c
+ src/hardware/appa-55ii/protocol.h \
+ src/hardware/appa-55ii/protocol.c \
+ src/hardware/appa-55ii/api.c
endif
if HW_ASIX_SIGMA
libsigrok_la_SOURCES += \
- hardware/asix-sigma/asix-sigma.h \
- hardware/asix-sigma/asix-sigma.c
+ src/hardware/asix-sigma/asix-sigma.h \
+ src/hardware/asix-sigma/asix-sigma.c
endif
if HW_ATTEN_PPS3XXX
libsigrok_la_SOURCES += \
- hardware/atten-pps3xxx/protocol.h \
- hardware/atten-pps3xxx/protocol.c \
- hardware/atten-pps3xxx/api.c
+ src/hardware/atten-pps3xxx/protocol.h \
+ src/hardware/atten-pps3xxx/protocol.c \
+ src/hardware/atten-pps3xxx/api.c
endif
if HW_BEAGLELOGIC
libsigrok_la_SOURCES += \
- hardware/beaglelogic/protocol.h \
- hardware/beaglelogic/protocol.c \
- hardware/beaglelogic/api.c
+ src/hardware/beaglelogic/protocol.h \
+ src/hardware/beaglelogic/protocol.c \
+ src/hardware/beaglelogic/api.c
endif
if HW_BRYMEN_BM86X
libsigrok_la_SOURCES += \
- hardware/brymen-bm86x/protocol.h \
- hardware/brymen-bm86x/protocol.c \
- hardware/brymen-bm86x/api.c
+ src/hardware/brymen-bm86x/protocol.h \
+ src/hardware/brymen-bm86x/protocol.c \
+ src/hardware/brymen-bm86x/api.c
endif
if HW_BRYMEN_DMM
libsigrok_la_SOURCES += \
- hardware/brymen-dmm/parser.c \
- hardware/brymen-dmm/protocol.h \
- hardware/brymen-dmm/protocol.c \
- hardware/brymen-dmm/api.c
+ src/hardware/brymen-dmm/parser.c \
+ src/hardware/brymen-dmm/protocol.h \
+ src/hardware/brymen-dmm/protocol.c \
+ src/hardware/brymen-dmm/api.c
endif
if HW_CEM_DT_885X
libsigrok_la_SOURCES += \
- hardware/cem-dt-885x/protocol.h \
- hardware/cem-dt-885x/protocol.c \
- hardware/cem-dt-885x/api.c
+ src/hardware/cem-dt-885x/protocol.h \
+ src/hardware/cem-dt-885x/protocol.c \
+ src/hardware/cem-dt-885x/api.c
endif
if HW_CENTER_3XX
libsigrok_la_SOURCES += \
- hardware/center-3xx/protocol.h \
- hardware/center-3xx/protocol.c \
- hardware/center-3xx/api.c
+ src/hardware/center-3xx/protocol.h \
+ src/hardware/center-3xx/protocol.c \
+ src/hardware/center-3xx/api.c
endif
if HW_CHRONOVU_LA
libsigrok_la_SOURCES += \
- hardware/chronovu-la/protocol.h \
- hardware/chronovu-la/protocol.c \
- hardware/chronovu-la/api.c
+ src/hardware/chronovu-la/protocol.h \
+ src/hardware/chronovu-la/protocol.c \
+ src/hardware/chronovu-la/api.c
endif
if HW_COLEAD_SLM
libsigrok_la_SOURCES += \
- hardware/colead-slm/protocol.h \
- hardware/colead-slm/protocol.c \
- hardware/colead-slm/api.c
+ src/hardware/colead-slm/protocol.h \
+ src/hardware/colead-slm/protocol.c \
+ src/hardware/colead-slm/api.c
endif
if HW_CONRAD_DIGI_35_CPU
libsigrok_la_SOURCES += \
- hardware/conrad-digi-35-cpu/protocol.h \
- hardware/conrad-digi-35-cpu/protocol.c \
- hardware/conrad-digi-35-cpu/api.c
+ src/hardware/conrad-digi-35-cpu/protocol.h \
+ src/hardware/conrad-digi-35-cpu/protocol.c \
+ src/hardware/conrad-digi-35-cpu/api.c
endif
if HW_DEMO
libsigrok_la_SOURCES += \
- hardware/demo/demo.c
+ src/hardware/demo/demo.c
endif
if HW_FLUKE_DMM
libsigrok_la_SOURCES += \
- hardware/fluke-dmm/fluke-dmm.h \
- hardware/fluke-dmm/fluke.c \
- hardware/fluke-dmm/api.c
+ src/hardware/fluke-dmm/fluke-dmm.h \
+ src/hardware/fluke-dmm/fluke.c \
+ src/hardware/fluke-dmm/api.c
endif
if HW_FX2LAFW
libsigrok_la_SOURCES += \
- hardware/fx2lafw/protocol.h \
- hardware/fx2lafw/protocol.c \
- hardware/fx2lafw/api.c
+ src/hardware/fx2lafw/protocol.h \
+ src/hardware/fx2lafw/protocol.c \
+ src/hardware/fx2lafw/api.c
endif
if HW_GMC_MH_1X_2X
libsigrok_la_SOURCES += \
- hardware/gmc-mh-1x-2x/protocol.h \
- hardware/gmc-mh-1x-2x/protocol.c \
- hardware/gmc-mh-1x-2x/api.c
+ src/hardware/gmc-mh-1x-2x/protocol.h \
+ src/hardware/gmc-mh-1x-2x/protocol.c \
+ src/hardware/gmc-mh-1x-2x/api.c
endif
if HW_HAMEG_HMO
libsigrok_la_SOURCES += \
- hardware/hameg-hmo/protocol.h \
- hardware/hameg-hmo/protocol.c \
- hardware/hameg-hmo/api.c
+ src/hardware/hameg-hmo/protocol.h \
+ src/hardware/hameg-hmo/protocol.c \
+ src/hardware/hameg-hmo/api.c
endif
if HW_HANTEK_DSO
libsigrok_la_SOURCES += \
- hardware/hantek-dso/dso.h \
- hardware/hantek-dso/dso.c \
- hardware/hantek-dso/api.c
+ src/hardware/hantek-dso/dso.h \
+ src/hardware/hantek-dso/dso.c \
+ src/hardware/hantek-dso/api.c
endif
if HW_IKALOGIC_SCANALOGIC2
libsigrok_la_SOURCES += \
- hardware/ikalogic-scanalogic2/protocol.h \
- hardware/ikalogic-scanalogic2/protocol.c \
- hardware/ikalogic-scanalogic2/api.c
+ src/hardware/ikalogic-scanalogic2/protocol.h \
+ src/hardware/ikalogic-scanalogic2/protocol.c \
+ src/hardware/ikalogic-scanalogic2/api.c
endif
if HW_IKALOGIC_SCANAPLUS
libsigrok_la_SOURCES += \
- hardware/ikalogic-scanaplus/protocol.h \
- hardware/ikalogic-scanaplus/protocol.c \
- hardware/ikalogic-scanaplus/api.c
+ src/hardware/ikalogic-scanaplus/protocol.h \
+ src/hardware/ikalogic-scanaplus/protocol.c \
+ src/hardware/ikalogic-scanaplus/api.c
endif
if HW_KECHENG_KC_330B
libsigrok_la_SOURCES += \
- hardware/kecheng-kc-330b/protocol.h \
- hardware/kecheng-kc-330b/protocol.c \
- hardware/kecheng-kc-330b/api.c
+ src/hardware/kecheng-kc-330b/protocol.h \
+ src/hardware/kecheng-kc-330b/protocol.c \
+ src/hardware/kecheng-kc-330b/api.c
endif
if HW_LASCAR_EL_USB
libsigrok_la_SOURCES += \
- hardware/lascar-el-usb/protocol.h \
- hardware/lascar-el-usb/protocol.c \
- hardware/lascar-el-usb/api.c
+ src/hardware/lascar-el-usb/protocol.h \
+ src/hardware/lascar-el-usb/protocol.c \
+ src/hardware/lascar-el-usb/api.c
endif
if HW_MANSON_HCS_3XXX
libsigrok_la_SOURCES += \
- hardware/manson-hcs-3xxx/protocol.h \
- hardware/manson-hcs-3xxx/protocol.c \
- hardware/manson-hcs-3xxx/api.c
+ src/hardware/manson-hcs-3xxx/protocol.h \
+ src/hardware/manson-hcs-3xxx/protocol.c \
+ src/hardware/manson-hcs-3xxx/api.c
endif
if HW_MIC_985XX
libsigrok_la_SOURCES += \
- hardware/mic-985xx/protocol.h \
- hardware/mic-985xx/protocol.c \
- hardware/mic-985xx/api.c
+ src/hardware/mic-985xx/protocol.h \
+ src/hardware/mic-985xx/protocol.c \
+ src/hardware/mic-985xx/api.c
endif
if HW_MOTECH_LPS_30X
libsigrok_la_SOURCES += \
- hardware/motech-lps-30x/protocol.h \
- hardware/motech-lps-30x/protocol.c \
- hardware/motech-lps-30x/api.c
+ src/hardware/motech-lps-30x/protocol.h \
+ src/hardware/motech-lps-30x/protocol.c \
+ src/hardware/motech-lps-30x/api.c
endif
if HW_NORMA_DMM
libsigrok_la_SOURCES += \
- hardware/norma-dmm/protocol.h \
- hardware/norma-dmm/protocol.c \
- hardware/norma-dmm/api.c
+ src/hardware/norma-dmm/protocol.h \
+ src/hardware/norma-dmm/protocol.c \
+ src/hardware/norma-dmm/api.c
endif
if HW_OPENBENCH_LOGIC_SNIFFER
libsigrok_la_SOURCES += \
- hardware/openbench-logic-sniffer/protocol.h \
- hardware/openbench-logic-sniffer/protocol.c \
- hardware/openbench-logic-sniffer/api.c
+ src/hardware/openbench-logic-sniffer/protocol.h \
+ src/hardware/openbench-logic-sniffer/protocol.c \
+ src/hardware/openbench-logic-sniffer/api.c
endif
if HW_RIGOL_DS
libsigrok_la_SOURCES += \
- hardware/rigol-ds/protocol.h \
- hardware/rigol-ds/protocol.c \
- hardware/rigol-ds/api.c
+ src/hardware/rigol-ds/protocol.h \
+ src/hardware/rigol-ds/protocol.c \
+ src/hardware/rigol-ds/api.c
endif
if HW_SALEAE_LOGIC16
libsigrok_la_SOURCES += \
- hardware/saleae-logic16/protocol.h \
- hardware/saleae-logic16/protocol.c \
- hardware/saleae-logic16/api.c
+ src/hardware/saleae-logic16/protocol.h \
+ src/hardware/saleae-logic16/protocol.c \
+ src/hardware/saleae-logic16/api.c
endif
if HW_SERIAL_DMM
libsigrok_la_SOURCES += \
- hardware/serial-dmm/protocol.h \
- hardware/serial-dmm/protocol.c \
- hardware/serial-dmm/api.c
+ src/hardware/serial-dmm/protocol.h \
+ src/hardware/serial-dmm/protocol.c \
+ src/hardware/serial-dmm/api.c
endif
if HW_SYSCLK_LWLA
libsigrok_la_SOURCES += \
- hardware/sysclk-lwla/lwla.h \
- hardware/sysclk-lwla/lwla.c \
- hardware/sysclk-lwla/protocol.h \
- hardware/sysclk-lwla/protocol.c \
- hardware/sysclk-lwla/api.c
+ src/hardware/sysclk-lwla/lwla.h \
+ src/hardware/sysclk-lwla/lwla.c \
+ src/hardware/sysclk-lwla/protocol.h \
+ src/hardware/sysclk-lwla/protocol.c \
+ src/hardware/sysclk-lwla/api.c
endif
if HW_TELEINFO
libsigrok_la_SOURCES += \
- hardware/teleinfo/protocol.h \
- hardware/teleinfo/protocol.c \
- hardware/teleinfo/api.c
+ src/hardware/teleinfo/protocol.h \
+ src/hardware/teleinfo/protocol.c \
+ src/hardware/teleinfo/api.c
endif
if HW_TESTO
libsigrok_la_SOURCES += \
- hardware/testo/protocol.h \
- hardware/testo/protocol.c \
- hardware/testo/api.c
+ src/hardware/testo/protocol.h \
+ src/hardware/testo/protocol.c \
+ src/hardware/testo/api.c
endif
if HW_TONDAJ_SL_814
libsigrok_la_SOURCES += \
- hardware/tondaj-sl-814/protocol.h \
- hardware/tondaj-sl-814/protocol.c \
- hardware/tondaj-sl-814/api.c
+ src/hardware/tondaj-sl-814/protocol.h \
+ src/hardware/tondaj-sl-814/protocol.c \
+ src/hardware/tondaj-sl-814/api.c
endif
if HW_UNI_T_DMM
libsigrok_la_SOURCES += \
- hardware/uni-t-dmm/protocol.h \
- hardware/uni-t-dmm/protocol.c \
- hardware/uni-t-dmm/api.c
+ src/hardware/uni-t-dmm/protocol.h \
+ src/hardware/uni-t-dmm/protocol.c \
+ src/hardware/uni-t-dmm/api.c
endif
if HW_UNI_T_UT32X
libsigrok_la_SOURCES += \
- hardware/uni-t-ut32x/protocol.h \
- hardware/uni-t-ut32x/protocol.c \
- hardware/uni-t-ut32x/api.c
+ src/hardware/uni-t-ut32x/protocol.h \
+ src/hardware/uni-t-ut32x/protocol.c \
+ src/hardware/uni-t-ut32x/api.c
endif
if HW_VICTOR_DMM
libsigrok_la_SOURCES += \
- hardware/victor-dmm/protocol.h \
- hardware/victor-dmm/protocol.c \
- hardware/victor-dmm/api.c
+ src/hardware/victor-dmm/protocol.h \
+ src/hardware/victor-dmm/protocol.c \
+ src/hardware/victor-dmm/api.c
endif
if HW_ZEROPLUS_LOGIC_CUBE
libsigrok_la_SOURCES += \
- hardware/zeroplus-logic-cube/analyzer.c \
- hardware/zeroplus-logic-cube/analyzer.h \
- hardware/zeroplus-logic-cube/gl_usb.h \
- hardware/zeroplus-logic-cube/gl_usb.c \
- hardware/zeroplus-logic-cube/protocol.h \
- hardware/zeroplus-logic-cube/protocol.c \
- hardware/zeroplus-logic-cube/api.c
+ src/hardware/zeroplus-logic-cube/analyzer.c \
+ src/hardware/zeroplus-logic-cube/analyzer.h \
+ src/hardware/zeroplus-logic-cube/gl_usb.h \
+ src/hardware/zeroplus-logic-cube/gl_usb.c \
+ src/hardware/zeroplus-logic-cube/protocol.h \
+ src/hardware/zeroplus-logic-cube/protocol.c \
+ src/hardware/zeroplus-logic-cube/api.c
endif
libsigrok_la_LIBADD = $(LIBOBJS)
include/libsigrok/libsigrok.h \
include/libsigrok/proto.h \
include/libsigrok/version.h
-noinst_HEADERS = libsigrok-internal.h
+noinst_HEADERS = src/libsigrok-internal.h
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsigrok.pc
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2012 Peter Stuge <peter@stuge.se>
- *
- * 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 <glib.h>
-#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/** @cond PRIVATE */
-#define LOG_PREFIX "backend"
-/** @endcond */
-
-/**
- * @mainpage libsigrok API
- *
- * @section sec_intro Introduction
- *
- * The <a href="http://sigrok.org">sigrok</a> project aims at creating a
- * portable, cross-platform, Free/Libre/Open-Source signal analysis software
- * suite that supports various device types (such as logic analyzers,
- * oscilloscopes, multimeters, and more).
- *
- * <a href="http://sigrok.org/wiki/Libsigrok">libsigrok</a> is a shared
- * library written in C which provides the basic API for talking to
- * <a href="http://sigrok.org/wiki/Supported_hardware">supported hardware</a>
- * and reading/writing the acquired data into various
- * <a href="http://sigrok.org/wiki/Input_output_formats">input/output
- * file formats</a>.
- *
- * @section sec_api API reference
- *
- * See the "Modules" page for an introduction to various libsigrok
- * related topics and the detailed API documentation of the respective
- * functions.
- *
- * You can also browse the API documentation by file, or review all
- * data structures.
- *
- * @section sec_mailinglists Mailing lists
- *
- * There are two mailing lists for sigrok/libsigrok: <a href="https://lists.sourceforge.net/lists/listinfo/sigrok-devel">sigrok-devel</a> and <a href="https://lists.sourceforge.net/lists/listinfo/sigrok-commits">sigrok-commits</a>.
- *
- * @section sec_irc IRC
- *
- * You can find the sigrok developers in the
- * <a href="irc://chat.freenode.net/sigrok">\#sigrok</a>
- * IRC channel on Freenode.
- *
- * @section sec_website Website
- *
- * <a href="http://sigrok.org/wiki/Libsigrok">sigrok.org/wiki/Libsigrok</a>
- */
-
-/**
- * @file
- *
- * Initializing and shutting down libsigrok.
- */
-
-/**
- * @defgroup grp_init Initialization
- *
- * Initializing and shutting down libsigrok.
- *
- * Before using any of the libsigrok functionality (except
- * sr_log_loglevel_set() and sr_log_opts_set()), sr_init() must
- * be called to initialize the library, which will return a struct sr_context
- * when the initialization was successful.
- *
- * When libsigrok functionality is no longer needed, sr_exit() should be
- * called, which will (among other things) free the struct sr_context.
- *
- * Example for a minimal program using libsigrok:
- *
- * @code{.c}
- * #include <stdio.h>
- * #include <libsigrok/libsigrok.h>
- *
- * int main(int argc, char **argv)
- * {
- * int ret;
- * struct sr_context *sr_ctx;
- *
- * if ((ret = sr_init(&sr_ctx)) != SR_OK) {
- * printf("Error initializing libsigrok (%s): %s.\n",
- * sr_strerror_name(ret), sr_strerror(ret));
- * return 1;
- * }
- *
- * // Use libsigrok functions here...
- *
- * if ((ret = sr_exit(sr_ctx)) != SR_OK) {
- * printf("Error shutting down libsigrok (%s): %s.\n",
- * sr_strerror_name(ret), sr_strerror(ret));
- * return 1;
- * }
- *
- * return 0;
- * }
- * @endcode
- *
- * @{
- */
-
-/**
- * Sanity-check all libsigrok drivers.
- *
- * @retval SR_OK All drivers are OK
- * @retval SR_ERR One or more drivers have issues.
- */
-static int sanity_check_all_drivers(void)
-{
- int i, errors, ret = SR_OK;
- struct sr_dev_driver **drivers;
- const char *d;
-
- sr_spew("Sanity-checking all drivers.");
-
- drivers = sr_driver_list();
- for (i = 0; drivers[i]; i++) {
- errors = 0;
-
- d = (drivers[i]->name) ? drivers[i]->name : "NULL";
-
- if (!drivers[i]->name) {
- sr_err("No name in driver %d ('%s').", i, d);
- errors++;
- }
- if (!drivers[i]->longname) {
- sr_err("No longname in driver %d ('%s').", i, d);
- errors++;
- }
- if (drivers[i]->api_version < 1) {
- sr_err("API version in driver %d ('%s') < 1.", i, d);
- errors++;
- }
- if (!drivers[i]->init) {
- sr_err("No init in driver %d ('%s').", i, d);
- errors++;
- }
- if (!drivers[i]->cleanup) {
- sr_err("No cleanup in driver %d ('%s').", i, d);
- errors++;
- }
- if (!drivers[i]->scan) {
- sr_err("No scan in driver %d ('%s').", i, d);
- errors++;
- }
- if (!drivers[i]->dev_list) {
- sr_err("No dev_list in driver %d ('%s').", i, d);
- errors++;
- }
- /* Note: config_get() is optional. */
- if (!drivers[i]->config_set) {
- sr_err("No config_set in driver %d ('%s').", i, d);
- errors++;
- }
- if (!drivers[i]->config_list) {
- sr_err("No config_list in driver %d ('%s').", i, d);
- errors++;
- }
- if (!drivers[i]->dev_open) {
- sr_err("No dev_open in driver %d ('%s').", i, d);
- errors++;
- }
- if (!drivers[i]->dev_close) {
- sr_err("No dev_close in driver %d ('%s').", i, d);
- errors++;
- }
- if (!drivers[i]->dev_acquisition_start) {
- sr_err("No dev_acquisition_start in driver %d ('%s').",
- i, d);
- errors++;
- }
- if (!drivers[i]->dev_acquisition_stop) {
- sr_err("No dev_acquisition_stop in driver %d ('%s').",
- i, d);
- errors++;
- }
-
- /* Note: 'priv' is allowed to be NULL. */
-
- if (errors == 0)
- continue;
-
- ret = SR_ERR;
- }
-
- return ret;
-}
-
-/**
- * Sanity-check all libsigrok input modules.
- *
- * @retval SR_OK All modules are OK
- * @retval SR_ERR One or more modules have issues.
- */
-static int sanity_check_all_input_modules(void)
-{
- int i, errors, ret = SR_OK;
- struct sr_input_format **inputs;
- const char *d;
-
- sr_spew("Sanity-checking all input modules.");
-
- inputs = sr_input_list();
- for (i = 0; inputs[i]; i++) {
- errors = 0;
-
- d = (inputs[i]->id) ? inputs[i]->id : "NULL";
-
- if (!inputs[i]->id) {
- sr_err("No ID in module %d ('%s').", i, d);
- errors++;
- }
- if (!inputs[i]->description) {
- sr_err("No description in module %d ('%s').", i, d);
- errors++;
- }
- if (!inputs[i]->format_match) {
- sr_err("No format_match in module %d ('%s').", i, d);
- errors++;
- }
- if (!inputs[i]->init) {
- sr_err("No init in module %d ('%s').", i, d);
- errors++;
- }
- if (!inputs[i]->loadfile) {
- sr_err("No loadfile in module %d ('%s').", i, d);
- errors++;
- }
-
- if (errors == 0)
- continue;
-
- ret = SR_ERR;
- }
-
- return ret;
-}
-
-/**
- * Sanity-check all libsigrok output modules.
- *
- * @retval SR_OK All modules are OK
- * @retval SR_ERR One or more modules have issues.
- */
-static int sanity_check_all_output_modules(void)
-{
- int i, errors, ret = SR_OK;
- struct sr_output_format **outputs;
- const char *d;
-
- sr_spew("Sanity-checking all output modules.");
-
- outputs = sr_output_list();
- for (i = 0; outputs[i]; i++) {
- errors = 0;
-
- d = (outputs[i]->id) ? outputs[i]->id : "NULL";
-
- if (!outputs[i]->id) {
- sr_err("No ID in module %d ('%s').", i, d);
- errors++;
- }
- if (!outputs[i]->description) {
- sr_err("No description in module '%s'.", d);
- errors++;
- }
- if (!outputs[i]->receive) {
- sr_err("No receive in module '%s'.", d);
- errors++;
- }
-
- if (errors == 0)
- continue;
-
- ret = SR_ERR;
- }
-
- return ret;
-}
-
-/**
- * Initialize libsigrok.
- *
- * This function must be called before any other libsigrok function.
- *
- * @param ctx Pointer to a libsigrok context struct pointer. Must not be NULL.
- * This will be a pointer to a newly allocated libsigrok context
- * object upon success, and is undefined upon errors.
- *
- * @return SR_OK upon success, a (negative) error code otherwise. Upon errors
- * the 'ctx' pointer is undefined and should not be used. Upon success,
- * the context will be free'd by sr_exit() as part of the libsigrok
- * shutdown.
- *
- * @since 0.2.0
- */
-SR_API int sr_init(struct sr_context **ctx)
-{
- int ret = SR_ERR;
- struct sr_context *context;
-
- if (!ctx) {
- sr_err("%s(): libsigrok context was NULL.", __func__);
- return SR_ERR;
- }
-
- if (sanity_check_all_drivers() < 0) {
- sr_err("Internal driver error(s), aborting.");
- return ret;
- }
-
- if (sanity_check_all_input_modules() < 0) {
- sr_err("Internal input module error(s), aborting.");
- return ret;
- }
-
- if (sanity_check_all_output_modules() < 0) {
- sr_err("Internal output module error(s), aborting.");
- return ret;
- }
-
- /* + 1 to handle when struct sr_context has no members. */
- context = g_try_malloc0(sizeof(struct sr_context) + 1);
-
- if (!context) {
- ret = SR_ERR_MALLOC;
- goto done;
- }
-
-#ifdef HAVE_LIBUSB_1_0
- ret = libusb_init(&context->libusb_ctx);
- if (LIBUSB_SUCCESS != ret) {
- sr_err("libusb_init() returned %s.", libusb_error_name(ret));
- ret = SR_ERR;
- goto done;
- }
-#endif
-
- *ctx = context;
- context = NULL;
- ret = SR_OK;
-
-done:
- if (context)
- g_free(context);
- return ret;
-}
-
-/**
- * Shutdown libsigrok.
- *
- * @param ctx Pointer to a libsigrok context struct. Must not be NULL.
- *
- * @retval SR_OK Success
- * @retval other Error code SR_ERR, ...
- *
- * @since 0.2.0
- */
-SR_API int sr_exit(struct sr_context *ctx)
-{
- if (!ctx) {
- sr_err("%s(): libsigrok context was NULL.", __func__);
- return SR_ERR;
- }
-
- sr_hw_cleanup_all();
-
-#ifdef HAVE_LIBUSB_1_0
- libusb_exit(ctx->libusb_ctx);
-#endif
-
- g_free(ctx);
-
- return SR_OK;
-}
-
-/** @} */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdio.h>
-#include <glib.h>
-#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/** @cond PRIVATE */
-#define LOG_PREFIX "device"
-/** @endcond */
-
-/**
- * @file
- *
- * Device handling in libsigrok.
- */
-
-/**
- * @defgroup grp_devices Devices
- *
- * Device handling in libsigrok.
- *
- * @{
- */
-
-/** @private
- * Allocate and initialize new struct sr_channel
- * @param[in] index @copydoc sr_channel::index
- * @param[in] type @copydoc sr_channel::type
- * @param[in] enabled @copydoc sr_channel::enabled
- * @param[in] name @copydoc sr_channel::name
- *
- * @return NULL (failure) or new struct sr_channel*.
- */
-SR_PRIV struct sr_channel *sr_channel_new(int index, int type,
- gboolean enabled, const char *name)
-{
- struct sr_channel *ch;
-
- if (!(ch = g_try_malloc0(sizeof(struct sr_channel)))) {
- sr_err("Channel malloc failed.");
- return NULL;
- }
-
- ch->index = index;
- ch->type = type;
- ch->enabled = enabled;
- if (name)
- ch->name = g_strdup(name);
-
- return ch;
-}
-
-/**
- * Set the name of the specified channel in the specified device.
- *
- * If the channel already has a different name assigned to it, it will be
- * removed, and the new name will be saved instead.
- *
- * @param sdi The device instance the channel is connected to.
- * @param[in] channelnum The number of the channel whose name to set.
- * Note that the channel numbers start at 0.
- * @param[in] name The new name that the specified channel should get. A copy
- * of the string is made.
- *
- * @return SR_OK on success, or SR_ERR_ARG on invalid arguments.
- *
- * @since 0.3.0
- */
-SR_API int sr_dev_channel_name_set(const struct sr_dev_inst *sdi,
- int channelnum, const char *name)
-{
- GSList *l;
- struct sr_channel *ch;
- int ret;
-
- if (!sdi) {
- sr_err("%s: sdi was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- ret = SR_ERR_ARG;
- for (l = sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->index == channelnum) {
- g_free(ch->name);
- ch->name = g_strdup(name);
- ret = SR_OK;
- break;
- }
- }
-
- return ret;
-}
-
-/**
- * Enable or disable a channel on the specified device.
- *
- * @param sdi The device instance the channel is connected to.
- * @param channelnum The channel number, starting from 0.
- * @param state TRUE to enable the channel, FALSE to disable.
- *
- * @return SR_OK on success or SR_ERR on failure. In case of invalid
- * arguments, SR_ERR_ARG is returned and the channel enabled state
- * remains unchanged.
- *
- * @since 0.3.0
- */
-SR_API int sr_dev_channel_enable(const struct sr_dev_inst *sdi, int channelnum,
- gboolean state)
-{
- GSList *l;
- struct sr_channel *ch;
- int ret;
- gboolean was_enabled;
-
- if (!sdi)
- return SR_ERR_ARG;
-
- ret = SR_ERR_ARG;
- for (l = sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->index == channelnum) {
- was_enabled = ch->enabled;
- ch->enabled = state;
- ret = SR_OK;
- if (!state != !was_enabled && sdi->driver
- && sdi->driver->config_channel_set) {
- ret = sdi->driver->config_channel_set(
- sdi, ch, SR_CHANNEL_SET_ENABLED);
- /* Roll back change if it wasn't applicable. */
- if (ret == SR_ERR_ARG)
- ch->enabled = was_enabled;
- }
- break;
- }
- }
-
- return ret;
-}
-
-/**
- * Determine whether the specified device instance has the specified
- * capability.
- *
- * @param sdi Pointer to the device instance to be checked. Must not be NULL.
- * If the device's 'driver' field is NULL (virtual device), this
- * function will always return FALSE (virtual devices don't have
- * a hardware capabilities list).
- * @param[in] key The option that should be checked for is supported by the
- * specified device.
- *
- * @retval TRUE Device has the specified option
- * @retval FALSE Device does not have the specified option, invalid input
- * parameters or other error conditions.
- *
- * @since 0.2.0
- */
-SR_API gboolean sr_dev_has_option(const struct sr_dev_inst *sdi, int key)
-{
- GVariant *gvar;
- const int *devopts;
- gsize num_opts, i;
- int ret;
-
- if (!sdi || !sdi->driver || !sdi->driver->config_list)
- return FALSE;
-
- if (sdi->driver->config_list(SR_CONF_DEVICE_OPTIONS,
- &gvar, sdi, NULL) != SR_OK)
- return FALSE;
-
- ret = FALSE;
- devopts = g_variant_get_fixed_array(gvar, &num_opts, sizeof(int32_t));
- for (i = 0; i < num_opts; i++) {
- if (devopts[i] == key) {
- ret = TRUE;
- break;
- }
- }
- g_variant_unref(gvar);
-
- return ret;
-}
-
-/** @private
- * Allocate and init new device instance struct.
- * @param[in] index @copydoc sr_dev_inst::index
- * @param[in] status @copydoc sr_dev_inst::status
- * @param[in] vendor @copydoc sr_dev_inst::vendor
- * @param[in] model @copydoc sr_dev_inst::model
- * @param[in] version @copydoc sr_dev_inst::version
- *
- * @retval NULL Error
- * @retval struct sr_dev_inst *. Dynamically allocated, free using
- * sr_dev_inst_free().
- */
-SR_PRIV struct sr_dev_inst *sr_dev_inst_new(int index, int status,
- const char *vendor, const char *model, const char *version)
-{
- struct sr_dev_inst *sdi;
-
- if (!(sdi = g_try_malloc(sizeof(struct sr_dev_inst)))) {
- sr_err("Device instance malloc failed.");
- return NULL;
- }
-
- sdi->driver = NULL;
- sdi->index = index;
- sdi->status = status;
- sdi->inst_type = -1;
- sdi->vendor = vendor ? g_strdup(vendor) : NULL;
- sdi->model = model ? g_strdup(model) : NULL;
- sdi->version = version ? g_strdup(version) : NULL;
- sdi->channels = NULL;
- sdi->channel_groups = NULL;
- sdi->session = NULL;
- sdi->conn = NULL;
- sdi->priv = NULL;
-
- return sdi;
-}
-
-/** @private
- * Free device instance struct created by sr_dev_inst().
- * @param sdi struct* to free.
- */
-SR_PRIV void sr_dev_inst_free(struct sr_dev_inst *sdi)
-{
- struct sr_channel *ch;
- GSList *l;
-
- for (l = sdi->channels; l; l = l->next) {
- ch = l->data;
- g_free(ch->name);
- g_free(ch);
- }
- g_slist_free(sdi->channels);
-
- if (sdi->channel_groups)
- g_slist_free(sdi->channel_groups);
-
- g_free(sdi->vendor);
- g_free(sdi->model);
- g_free(sdi->version);
- g_free(sdi);
-}
-
-#ifdef HAVE_LIBUSB_1_0
-
-/** @private
- * Allocate and init struct for USB device instance.
- * @param[in] bus @copydoc sr_usb_dev_inst::bus
- * @param[in] address @copydoc sr_usb_dev_inst::address
- * @param[in] hdl @copydoc sr_usb_dev_inst::devhdl
- *
- * @retval NULL Error
- * @retval other struct sr_usb_dev_inst * for USB device instance.
- */
-SR_PRIV struct sr_usb_dev_inst *sr_usb_dev_inst_new(uint8_t bus,
- uint8_t address, struct libusb_device_handle *hdl)
-{
- struct sr_usb_dev_inst *udi;
-
- if (!(udi = g_try_malloc(sizeof(struct sr_usb_dev_inst)))) {
- sr_err("USB device instance malloc failed.");
- return NULL;
- }
-
- udi->bus = bus;
- udi->address = address;
- udi->devhdl = hdl;
-
- return udi;
-}
-
-/** @private
- * Free struct * allocated by sr_usb_dev_inst().
- * @param usb struct* to free. Must not be NULL.
- */
-SR_PRIV void sr_usb_dev_inst_free(struct sr_usb_dev_inst *usb)
-{
- g_free(usb);
-}
-
-#endif
-
-#ifdef HAVE_LIBSERIALPORT
-
-/**
- * @private
- *
- * Both parameters are copied to newly allocated strings, and freed
- * automatically by sr_serial_dev_inst_free().
- *
- * @param[in] port OS-specific serial port specification. Examples:
- * "/dev/ttyUSB0", "/dev/ttyACM1", "/dev/tty.Modem-0", "COM1".
- * @param[in] serialcomm A serial communication parameters string, in the form
- * of \<speed\>/\<data bits\>\<parity\>\<stopbits\>, for example
- * "9600/8n1" or "600/7o2". This is an optional parameter;
- * it may be filled in later.
- *
- * @return A pointer to a newly initialized struct sr_serial_dev_inst,
- * or NULL on error.
- */
-SR_PRIV struct sr_serial_dev_inst *sr_serial_dev_inst_new(const char *port,
- const char *serialcomm)
-{
- struct sr_serial_dev_inst *serial;
-
- if (!port) {
- sr_err("Serial port required.");
- return NULL;
- }
-
- if (!(serial = g_try_malloc0(sizeof(struct sr_serial_dev_inst)))) {
- sr_err("Serial device instance malloc failed.");
- return NULL;
- }
-
- serial->port = g_strdup(port);
- if (serialcomm)
- serial->serialcomm = g_strdup(serialcomm);
-
- return serial;
-}
-
-/** @private
- * Free struct sr_serial_dev_inst * allocated by sr_serial_dev_inst().
- * @param serial struct sr_serial_dev_inst * to free. Must not be NULL.
- */
-SR_PRIV void sr_serial_dev_inst_free(struct sr_serial_dev_inst *serial)
-{
- g_free(serial->port);
- g_free(serial->serialcomm);
- g_free(serial);
-}
-#endif
-
-/** @private */
-SR_PRIV struct sr_usbtmc_dev_inst *sr_usbtmc_dev_inst_new(const char *device)
-{
- struct sr_usbtmc_dev_inst *usbtmc;
-
- if (!device) {
- sr_err("Device name required.");
- return NULL;
- }
-
- if (!(usbtmc = g_try_malloc0(sizeof(struct sr_usbtmc_dev_inst)))) {
- sr_err("USBTMC device instance malloc failed.");
- return NULL;
- }
-
- usbtmc->device = g_strdup(device);
- usbtmc->fd = -1;
-
- return usbtmc;
-}
-
-/** @private */
-SR_PRIV void sr_usbtmc_dev_inst_free(struct sr_usbtmc_dev_inst *usbtmc)
-{
- g_free(usbtmc->device);
- g_free(usbtmc);
-}
-
-/**
- * Get the list of devices/instances of the specified driver.
- *
- * @param driver The driver to use. Must not be NULL.
- *
- * @return The list of devices/instances of this driver, or NULL upon errors
- * or if the list is empty.
- *
- * @since 0.2.0
- */
-SR_API GSList *sr_dev_list(const struct sr_dev_driver *driver)
-{
- if (driver && driver->dev_list)
- return driver->dev_list();
- else
- return NULL;
-}
-
-/**
- * Clear the list of device instances a driver knows about.
- *
- * @param driver The driver to use. This must be a pointer to one of
- * the entries returned by sr_driver_list(). Must not be NULL.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid driver
- *
- * @since 0.2.0
- */
-SR_API int sr_dev_clear(const struct sr_dev_driver *driver)
-{
- int ret;
-
- if (!driver) {
- sr_err("Invalid driver.");
- return SR_ERR_ARG;
- }
-
- if (driver->dev_clear)
- ret = driver->dev_clear();
- else
- ret = std_dev_clear(driver, NULL);
-
- return ret;
-}
-
-/**
- * Open the specified device.
- *
- * @param sdi Device instance to use. Must not be NULL.
- *
- * @return SR_OK upon success, a negative error code upon errors.
- *
- * @since 0.2.0
- */
-SR_API int sr_dev_open(struct sr_dev_inst *sdi)
-{
- int ret;
-
- if (!sdi || !sdi->driver || !sdi->driver->dev_open)
- return SR_ERR;
-
- ret = sdi->driver->dev_open(sdi);
-
- return ret;
-}
-
-/**
- * Close the specified device.
- *
- * @param sdi Device instance to use. Must not be NULL.
- *
- * @return SR_OK upon success, a negative error code upon errors.
- *
- * @since 0.2.0
- */
-SR_API int sr_dev_close(struct sr_dev_inst *sdi)
-{
- int ret;
-
- if (!sdi || !sdi->driver || !sdi->driver->dev_close)
- return SR_ERR;
-
- ret = sdi->driver->dev_close(sdi);
-
- return ret;
-}
-
-/** @} */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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.h"
-#include "libsigrok-internal.h"
-
-/** @cond PRIVATE */
-#ifdef HAVE_HW_AGILENT_DMM
-extern SR_PRIV struct sr_dev_driver agdmm_driver_info;
-#endif
-#ifdef HAVE_HW_APPA_55II
-extern SR_PRIV struct sr_dev_driver appa_55ii_driver_info;
-#endif
-#ifdef HAVE_HW_ASIX_SIGMA
-extern SR_PRIV struct sr_dev_driver asix_sigma_driver_info;
-#endif
-#ifdef HAVE_HW_ATTEN_PPS3XXX
-extern SR_PRIV struct sr_dev_driver atten_pps3203_driver_info;
-#endif
-#ifdef HAVE_HW_BEAGLELOGIC
-extern SR_PRIV struct sr_dev_driver beaglelogic_driver_info;
-#endif
-#ifdef HAVE_HW_BRYMEN_BM86X
-extern SR_PRIV struct sr_dev_driver brymen_bm86x_driver_info;
-#endif
-#ifdef HAVE_HW_BRYMEN_DMM
-extern SR_PRIV struct sr_dev_driver brymen_bm857_driver_info;
-#endif
-#ifdef HAVE_HW_CEM_DT_885X
-extern SR_PRIV struct sr_dev_driver cem_dt_885x_driver_info;
-#endif
-#ifdef HAVE_HW_CENTER_3XX
-extern SR_PRIV struct sr_dev_driver center_309_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_k204_driver_info;
-#endif
-#ifdef HAVE_HW_CHRONOVU_LA
-extern SR_PRIV struct sr_dev_driver chronovu_la_driver_info;
-#endif
-#ifdef HAVE_HW_COLEAD_SLM
-extern SR_PRIV struct sr_dev_driver colead_slm_driver_info;
-#endif
-#ifdef HAVE_HW_CONRAD_DIGI_35_CPU
-extern SR_PRIV struct sr_dev_driver conrad_digi_35_cpu_driver_info;
-#endif
-#ifdef HAVE_HW_DEMO
-extern SR_PRIV struct sr_dev_driver demo_driver_info;
-#endif
-#ifdef HAVE_HW_FLUKE_DMM
-extern SR_PRIV struct sr_dev_driver flukedmm_driver_info;
-#endif
-#ifdef HAVE_HW_FX2LAFW
-extern SR_PRIV struct sr_dev_driver fx2lafw_driver_info;
-#endif
-#ifdef HAVE_HW_GMC_MH_1X_2X
-extern SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info;
-extern SR_PRIV struct sr_dev_driver gmc_mh_2x_bd232_driver_info;
-#endif
-#ifdef HAVE_HW_HAMEG_HMO
-extern SR_PRIV struct sr_dev_driver hameg_hmo_driver_info;
-#endif
-#ifdef HAVE_HW_HANTEK_DSO
-extern SR_PRIV struct sr_dev_driver hantek_dso_driver_info;
-#endif
-#ifdef HAVE_HW_IKALOGIC_SCANALOGIC2
-extern SR_PRIV struct sr_dev_driver ikalogic_scanalogic2_driver_info;
-#endif
-#ifdef HAVE_HW_IKALOGIC_SCANAPLUS
-extern SR_PRIV struct sr_dev_driver ikalogic_scanaplus_driver_info;
-#endif
-#ifdef HAVE_HW_KECHENG_KC_330B
-extern SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info;
-#endif
-#ifdef HAVE_HW_LASCAR_EL_USB
-extern SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info;
-#endif
-#ifdef HAVE_HW_LINK_MSO19
-extern SR_PRIV struct sr_dev_driver link_mso19_driver_info;
-#endif
-#ifdef HAVE_HW_MANSON_HCS_3XXX
-extern SR_PRIV struct sr_dev_driver manson_hcs_3xxx_driver_info;
-#endif
-#ifdef HAVE_HW_MIC_985XX
-extern SR_PRIV struct sr_dev_driver mic_98581_driver_info;
-extern SR_PRIV struct sr_dev_driver mic_98583_driver_info;
-#endif
-#ifdef HAVE_HW_MOTECH_LPS_30X
-extern SR_PRIV struct sr_dev_driver motech_lps_301_driver_info;
-#endif
-#ifdef HAVE_HW_NORMA_DMM
-extern SR_PRIV struct sr_dev_driver norma_dmm_driver_info;
-extern SR_PRIV struct sr_dev_driver siemens_b102x_driver_info;
-#endif
-#ifdef HAVE_HW_OPENBENCH_LOGIC_SNIFFER
-extern SR_PRIV struct sr_dev_driver ols_driver_info;
-#endif
-#ifdef HAVE_HW_RIGOL_DS
-extern SR_PRIV struct sr_dev_driver rigol_ds_driver_info;
-#endif
-#ifdef HAVE_HW_SALEAE_LOGIC16
-extern SR_PRIV struct sr_dev_driver saleae_logic16_driver_info;
-#endif
-#ifdef HAVE_HW_SERIAL_DMM
-extern SR_PRIV struct sr_dev_driver bbcgm_m2110_driver_info;
-extern SR_PRIV struct sr_dev_driver digitek_dt4000zc_driver_info;
-extern SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info;
-extern SR_PRIV struct sr_dev_driver metex_me31_driver_info;
-extern SR_PRIV struct sr_dev_driver peaktech_3410_driver_info;
-extern SR_PRIV struct sr_dev_driver mastech_mas345_driver_info;
-extern SR_PRIV struct sr_dev_driver va_va18b_driver_info;
-extern SR_PRIV struct sr_dev_driver va_va40b_driver_info;
-extern SR_PRIV struct sr_dev_driver metex_m3640d_driver_info;
-extern SR_PRIV struct sr_dev_driver metex_m4650cr_driver_info;
-extern SR_PRIV struct sr_dev_driver peaktech_4370_driver_info;
-extern SR_PRIV struct sr_dev_driver pce_pce_dm32_driver_info;
-extern SR_PRIV struct sr_dev_driver radioshack_22_168_driver_info;
-extern SR_PRIV struct sr_dev_driver radioshack_22_805_driver_info;
-extern SR_PRIV struct sr_dev_driver radioshack_22_812_driver_info;
-extern SR_PRIV struct sr_dev_driver tecpel_dmm_8061_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_m3650cr_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_m3650d_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_m4650cr_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_me42_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc820_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc830_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc840_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60a_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60e_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60g_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61b_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61c_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61d_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61e_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver iso_tech_idm103n_driver_info;
-extern SR_PRIV struct sr_dev_driver tenma_72_7745_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver tenma_72_7750_ser_driver_info;
-#endif
-#ifdef HAVE_HW_SYSCLK_LWLA
-extern SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info;
-#endif
-#ifdef HAVE_HW_TELEINFO
-extern SR_PRIV struct sr_dev_driver teleinfo_driver_info;
-#endif
-#ifdef HAVE_HW_TESTO
-extern SR_PRIV struct sr_dev_driver testo_driver_info;
-#endif
-#ifdef HAVE_HW_TONDAJ_SL_814
-extern SR_PRIV struct sr_dev_driver tondaj_sl_814_driver_info;
-#endif
-#ifdef HAVE_HW_UNI_T_DMM
-extern SR_PRIV struct sr_dev_driver tecpel_dmm_8061_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60a_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60e_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60g_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61b_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61c_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61d_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61e_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc820_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc830_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc840_driver_info;
-extern SR_PRIV struct sr_dev_driver tenma_72_7745_driver_info;
-extern SR_PRIV struct sr_dev_driver tenma_72_7750_driver_info;
-#endif
-#ifdef HAVE_HW_UNI_T_UT32X
-extern SR_PRIV struct sr_dev_driver uni_t_ut32x_driver_info;
-#endif
-#ifdef HAVE_HW_VICTOR_DMM
-extern SR_PRIV struct sr_dev_driver victor_dmm_driver_info;
-#endif
-#ifdef HAVE_HW_ZEROPLUS_LOGIC_CUBE
-extern SR_PRIV struct sr_dev_driver zeroplus_logic_cube_driver_info;
-#endif
-
-SR_PRIV struct sr_dev_driver *drivers_list[] = {
-#ifdef HAVE_HW_AGILENT_DMM
- &agdmm_driver_info,
-#endif
-#ifdef HAVE_HW_APPA_55II
- &appa_55ii_driver_info,
-#endif
-#ifdef HAVE_HW_ASIX_SIGMA
- &asix_sigma_driver_info,
-#endif
-#ifdef HAVE_HW_ATTEN_PPS3XXX
- &atten_pps3203_driver_info,
-#endif
-#ifdef HAVE_HW_BEAGLELOGIC
- &beaglelogic_driver_info,
-#endif
-#ifdef HAVE_HW_BRYMEN_BM86X
- &brymen_bm86x_driver_info,
-#endif
-#ifdef HAVE_HW_BRYMEN_DMM
- &brymen_bm857_driver_info,
-#endif
-#ifdef HAVE_HW_CEM_DT_885X
- &cem_dt_885x_driver_info,
-#endif
-#ifdef HAVE_HW_CENTER_3XX
- ¢er_309_driver_info,
- &voltcraft_k204_driver_info,
-#endif
-#ifdef HAVE_HW_CHRONOVU_LA
- &chronovu_la_driver_info,
-#endif
-#ifdef HAVE_HW_COLEAD_SLM
- &colead_slm_driver_info,
-#endif
-#ifdef HAVE_HW_CONRAD_DIGI_35_CPU
- &conrad_digi_35_cpu_driver_info,
-#endif
-#ifdef HAVE_HW_DEMO
- &demo_driver_info,
-#endif
-#ifdef HAVE_HW_FLUKE_DMM
- &flukedmm_driver_info,
-#endif
-#ifdef HAVE_HW_FX2LAFW
- &fx2lafw_driver_info,
-#endif
-#ifdef HAVE_HW_GMC_MH_1X_2X
- &gmc_mh_1x_2x_rs232_driver_info,
- &gmc_mh_2x_bd232_driver_info,
-#endif
-#ifdef HAVE_HW_HAMEG_HMO
- &hameg_hmo_driver_info,
-#endif
-#ifdef HAVE_HW_HANTEK_DSO
- &hantek_dso_driver_info,
-#endif
-#ifdef HAVE_HW_IKALOGIC_SCANALOGIC2
- &ikalogic_scanalogic2_driver_info,
-#endif
-#ifdef HAVE_HW_IKALOGIC_SCANAPLUS
- &ikalogic_scanaplus_driver_info,
-#endif
-#ifdef HAVE_HW_KECHENG_KC_330B
- &kecheng_kc_330b_driver_info,
-#endif
-#ifdef HAVE_HW_LASCAR_EL_USB
- &lascar_el_usb_driver_info,
-#endif
-#ifdef HAVE_HW_LINK_MSO19
- &link_mso19_driver_info,
-#endif
-#ifdef HAVE_HW_MANSON_HCS_3XXX
- &manson_hcs_3xxx_driver_info,
-#endif
-#ifdef HAVE_HW_MIC_985XX
- &mic_98581_driver_info,
- &mic_98583_driver_info,
-#endif
-#ifdef HAVE_HW_MOTECH_LPS_30X
- &motech_lps_301_driver_info,
-#endif
-#ifdef HAVE_HW_NORMA_DMM
- &norma_dmm_driver_info,
- &siemens_b102x_driver_info,
-#endif
-#ifdef HAVE_HW_OPENBENCH_LOGIC_SNIFFER
- &ols_driver_info,
-#endif
-#ifdef HAVE_HW_RIGOL_DS
- &rigol_ds_driver_info,
-#endif
-#ifdef HAVE_HW_SALEAE_LOGIC16
- &saleae_logic16_driver_info,
-#endif
-#ifdef HAVE_HW_SERIAL_DMM
- &bbcgm_m2110_driver_info,
- &digitek_dt4000zc_driver_info,
- &tekpower_tp4000zc_driver_info,
- &metex_me31_driver_info,
- &peaktech_3410_driver_info,
- &mastech_mas345_driver_info,
- &va_va18b_driver_info,
- &va_va40b_driver_info,
- &metex_m3640d_driver_info,
- &metex_m4650cr_driver_info,
- &peaktech_4370_driver_info,
- &pce_pce_dm32_driver_info,
- &radioshack_22_168_driver_info,
- &radioshack_22_805_driver_info,
- &radioshack_22_812_driver_info,
- &tecpel_dmm_8061_ser_driver_info,
- &voltcraft_m3650cr_driver_info,
- &voltcraft_m3650d_driver_info,
- &voltcraft_m4650cr_driver_info,
- &voltcraft_me42_driver_info,
- &voltcraft_vc820_ser_driver_info,
- &voltcraft_vc830_ser_driver_info,
- &voltcraft_vc840_ser_driver_info,
- &uni_t_ut60a_ser_driver_info,
- &uni_t_ut60e_ser_driver_info,
- &uni_t_ut60g_ser_driver_info,
- &uni_t_ut61b_ser_driver_info,
- &uni_t_ut61c_ser_driver_info,
- &uni_t_ut61d_ser_driver_info,
- &uni_t_ut61e_ser_driver_info,
- &iso_tech_idm103n_driver_info,
- &tenma_72_7745_ser_driver_info,
- &tenma_72_7750_ser_driver_info,
-#endif
-#ifdef HAVE_HW_SYSCLK_LWLA
- &sysclk_lwla_driver_info,
-#endif
-#ifdef HAVE_HW_TELEINFO
- &teleinfo_driver_info,
-#endif
-#ifdef HAVE_HW_TESTO
- &testo_driver_info,
-#endif
-#ifdef HAVE_HW_TONDAJ_SL_814
- &tondaj_sl_814_driver_info,
-#endif
-#ifdef HAVE_HW_UNI_T_DMM
- &tecpel_dmm_8061_driver_info,
- &uni_t_ut60a_driver_info,
- &uni_t_ut60e_driver_info,
- &uni_t_ut60g_driver_info,
- &uni_t_ut61b_driver_info,
- &uni_t_ut61c_driver_info,
- &uni_t_ut61d_driver_info,
- &uni_t_ut61e_driver_info,
- &voltcraft_vc820_driver_info,
- &voltcraft_vc830_driver_info,
- &voltcraft_vc840_driver_info,
- &tenma_72_7745_driver_info,
- &tenma_72_7750_driver_info,
-#endif
-#ifdef HAVE_HW_UNI_T_UT32X
- &uni_t_ut32x_driver_info,
-#endif
-#ifdef HAVE_HW_VICTOR_DMM
- &victor_dmm_driver_info,
-#endif
-#ifdef HAVE_HW_ZEROPLUS_LOGIC_CUBE
- &zeroplus_logic_cube_driver_info,
-#endif
- NULL,
-};
-/** @endcond */
-
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "libsigrok.h"
-
-/**
- * @file
- *
- * Error handling in libsigrok.
- */
-
-/**
- * @defgroup grp_error Error handling
- *
- * Error handling in libsigrok.
- *
- * libsigrok functions usually return @ref SR_OK upon success, or a negative
- * error code on failure.
- *
- * @{
- */
-
-/**
- * Return a human-readable error string for the given libsigrok error code.
- *
- * @param error_code A libsigrok error code number, such as SR_ERR_MALLOC.
- *
- * @return A const string containing a short, human-readable (English)
- * description of the error, such as "memory allocation error".
- * The string must NOT be free'd by the caller!
- *
- * @see sr_strerror_name
- *
- * @since 0.2.0
- */
-SR_API const char *sr_strerror(int error_code)
-{
- /*
- * Note: All defined SR_* error macros from libsigrok.h must have
- * an entry in this function, as well as in sr_strerror_name().
- */
-
- switch (error_code) {
- case SR_OK:
- return "no error";
- case SR_ERR:
- return "generic/unspecified error";
- case SR_ERR_MALLOC:
- return "memory allocation error";
- case SR_ERR_ARG:
- return "invalid argument";
- case SR_ERR_BUG:
- return "internal error";
- case SR_ERR_SAMPLERATE:
- return "invalid samplerate";
- case SR_ERR_NA:
- return "not applicable";
- case SR_ERR_DEV_CLOSED:
- return "device closed but should be open";
- case SR_ERR_TIMEOUT:
- return "timeout occurred";
- case SR_ERR_CHANNEL_GROUP:
- return "no channel group specified";
- default:
- return "unknown error";
- }
-}
-
-/**
- * Return the "name" string of the given libsigrok error code.
- *
- * For example, the "name" of the SR_ERR_MALLOC error code is "SR_ERR_MALLOC",
- * the name of the SR_OK code is "SR_OK", and so on.
- *
- * This function can be used for various purposes where the "name" string of
- * a libsigrok error code is useful.
- *
- * @param error_code A libsigrok error code number, such as SR_ERR_MALLOC.
- *
- * @return A const string containing the "name" of the error code as string.
- * The string must NOT be free'd by the caller!
- *
- * @see sr_strerror
- *
- * @since 0.2.0
- */
-SR_API const char *sr_strerror_name(int error_code)
-{
- /*
- * Note: All defined SR_* error macros from libsigrok.h must have
- * an entry in this function, as well as in sr_strerror().
- */
-
- switch (error_code) {
- case SR_OK:
- return "SR_OK";
- case SR_ERR:
- return "SR_ERR";
- case SR_ERR_MALLOC:
- return "SR_ERR_MALLOC";
- case SR_ERR_ARG:
- return "SR_ERR_ARG";
- case SR_ERR_BUG:
- return "SR_ERR_BUG";
- case SR_ERR_SAMPLERATE:
- return "SR_ERR_SAMPLERATE";
- case SR_ERR_NA:
- return "SR_ERR_NA";
- case SR_ERR_DEV_CLOSED:
- return "SR_ERR_DEV_CLOSED";
- case SR_ERR_TIMEOUT:
- return "SR_ERR_TIMEOUT";
- case SR_ERR_CHANNEL_GROUP:
- return "SR_ERR_CHANNEL_GROUP";
- default:
- return "unknown error code";
- }
-}
-
-/** @} */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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_AGILENT_DMM_AGILENT_DMM_H
-#define LIBSIGROK_HARDWARE_AGILENT_DMM_AGILENT_DMM_H
-
-#define LOG_PREFIX "agilent-dmm"
-
-#define AGDMM_BUFSIZE 256
-
-/* Supported models */
-enum {
- AGILENT_U1231A = 1,
- AGILENT_U1232A,
- AGILENT_U1233A,
- AGILENT_U1251A,
- AGILENT_U1252A,
- AGILENT_U1253A,
-};
-
-/* Supported device profiles */
-struct agdmm_profile {
- int model;
- const char *modelname;
- const struct agdmm_job *jobs;
- const struct agdmm_recv *recvs;
-};
-
-/* Private, per-device-instance driver context. */
-struct dev_context {
- const struct agdmm_profile *profile;
- uint64_t limit_samples;
- uint64_t limit_msec;
-
- /* Opaque pointer passed in by the frontend. */
- void *cb_data;
-
- /* Runtime. */
- uint64_t num_samples;
- int64_t jobqueue[8];
- unsigned char buf[AGDMM_BUFSIZE];
- int buflen;
- int cur_mq;
- int cur_unit;
- int cur_mqflags;
- int cur_divider;
- int cur_acdc;
- int mode_tempaux;
- int mode_continuity;
-};
-
-struct agdmm_job {
- int interval;
- int (*send) (const struct sr_dev_inst *sdi);
-};
-
-struct agdmm_recv {
- const char *recv_regex;
- int (*recv) (const struct sr_dev_inst *sdi, GMatchInfo *match);
-};
-
-SR_PRIV int agdmm_receive_data(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <glib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "agilent-dmm.h"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
-};
-
-extern const struct agdmm_job agdmm_jobs_u123x[];
-extern const struct agdmm_recv agdmm_recvs_u123x[];
-extern const struct agdmm_job agdmm_jobs_u125x[];
-extern const struct agdmm_recv agdmm_recvs_u125x[];
-
-/* This works on all the Agilent U12xxA series, although the
- * U127xA can apparently also run at 19200/8n1. */
-#define SERIALCOMM "9600/8n1"
-
-static const struct agdmm_profile supported_agdmm[] = {
- { AGILENT_U1231A, "U1231A", agdmm_jobs_u123x, agdmm_recvs_u123x },
- { AGILENT_U1232A, "U1232A", agdmm_jobs_u123x, agdmm_recvs_u123x },
- { AGILENT_U1233A, "U1233A", agdmm_jobs_u123x, agdmm_recvs_u123x },
- { AGILENT_U1251A, "U1251A", agdmm_jobs_u125x, agdmm_recvs_u125x },
- { AGILENT_U1252A, "U1252A", agdmm_jobs_u125x, agdmm_recvs_u125x },
- { AGILENT_U1253A, "U1253A", agdmm_jobs_u125x, agdmm_recvs_u125x },
- { 0, NULL, NULL, NULL }
-};
-
-SR_PRIV struct sr_dev_driver agdmm_driver_info;
-static struct sr_dev_driver *di = &agdmm_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_config *src;
- struct sr_channel *ch;
- struct sr_serial_dev_inst *serial;
- GSList *l, *devices;
- int len, i;
- const char *conn, *serialcomm;
- char *buf, **tokens;
-
- drvc = di->priv;
- drvc->instances = NULL;
-
- devices = NULL;
- 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 (!conn)
- return NULL;
- if (!serialcomm)
- serialcomm = SERIALCOMM;
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- serial_flush(serial);
- if (serial_write(serial, "*IDN?\r\n", 7) == -1) {
- sr_err("Unable to send identification string: %s.",
- strerror(errno));
- return NULL;
- }
-
- len = 128;
- if (!(buf = g_try_malloc(len))) {
- sr_err("Serial buffer malloc failed.");
- return NULL;
- }
- serial_readline(serial, &buf, &len, 150);
- if (!len)
- return NULL;
-
- tokens = g_strsplit(buf, ",", 4);
- if (!strcmp("Agilent Technologies", tokens[0])
- && tokens[1] && tokens[2] && tokens[3]) {
- for (i = 0; supported_agdmm[i].model; i++) {
- if (strcmp(supported_agdmm[i].modelname, tokens[1]))
- continue;
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Agilent",
- tokens[1], tokens[3])))
- return NULL;
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
- devc->profile = &supported_agdmm[i];
- devc->cur_mq = -1;
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
- sdi->priv = devc;
- sdi->driver = di;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- break;
- }
- }
- g_strfreev(tokens);
- g_free(buf);
-
- serial_close(serial);
- if (!devices)
- sr_serial_dev_inst_free(serial);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- switch (id) {
- case SR_CONF_LIMIT_MSEC:
- /* TODO: not yet implemented */
- if (g_variant_get_uint64(data) == 0) {
- sr_err("LIMIT_MSEC can't be 0.");
- return SR_ERR;
- }
- devc->limit_msec = g_variant_get_uint64(data);
- sr_dbg("Setting time limit to %" PRIu64 "ms.",
- devc->limit_msec);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- devc->cb_data = cb_data;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Poll every 100ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 100,
- agdmm_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
- sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver agdmm_driver_info = {
- .name = "agilent-dmm",
- .longname = "Agilent U12xx series DMMs",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .config_get = NULL,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = std_serial_dev_open,
- .dev_close = std_serial_dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "agilent-dmm.h"
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <math.h>
-
-static void dispatch(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- const struct agdmm_job *jobs;
- int64_t now;
- int i;
-
- devc = sdi->priv;
- jobs = devc->profile->jobs;
- now = g_get_monotonic_time() / 1000;
- for (i = 0; (&jobs[i])->interval; i++) {
- if (now - devc->jobqueue[i] > (&jobs[i])->interval) {
- sr_spew("Running job %d.", i);
- (&jobs[i])->send(sdi);
- devc->jobqueue[i] = now;
- }
- }
-}
-
-static void receive_line(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- const struct agdmm_recv *recvs, *recv;
- GRegex *reg;
- GMatchInfo *match;
- int i;
-
- devc = sdi->priv;
-
- /* Strip CRLF */
- while (devc->buflen) {
- if (*(devc->buf + devc->buflen - 1) == '\r'
- || *(devc->buf + devc->buflen - 1) == '\n')
- *(devc->buf + --devc->buflen) = '\0';
- else
- break;
- }
- sr_spew("Received '%s'.", devc->buf);
-
- recv = NULL;
- recvs = devc->profile->recvs;
- for (i = 0; (&recvs[i])->recv_regex; i++) {
- reg = g_regex_new((&recvs[i])->recv_regex, 0, 0, NULL);
- if (g_regex_match(reg, (char *)devc->buf, 0, &match)) {
- recv = &recvs[i];
- break;
- }
- g_match_info_unref(match);
- g_regex_unref(reg);
- }
- if (recv) {
- recv->recv(sdi, match);
- g_match_info_unref(match);
- g_regex_unref(reg);
- } else
- sr_dbg("Unknown line '%s'.", devc->buf);
-
- /* Done with this. */
- devc->buflen = 0;
-}
-
-SR_PRIV int agdmm_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- int len;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- serial = sdi->conn;
- if (revents == G_IO_IN) {
- /* Serial data arrived. */
- while(AGDMM_BUFSIZE - devc->buflen - 1 > 0) {
- len = serial_read(serial, devc->buf + devc->buflen, 1);
- if (len < 1)
- break;
- devc->buflen += len;
- *(devc->buf + devc->buflen) = '\0';
- if (*(devc->buf + devc->buflen - 1) == '\n') {
- /* End of line */
- receive_line(sdi);
- break;
- }
- }
- }
-
- dispatch(sdi);
-
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
-
- return TRUE;
-}
-
-static int agdmm_send(const struct sr_dev_inst *sdi, const char *cmd)
-{
- struct sr_serial_dev_inst *serial;
- char buf[32];
-
- serial = sdi->conn;
-
- sr_spew("Sending '%s'.", cmd);
- strncpy(buf, cmd, 28);
- if (!strncmp(buf, "*IDN?", 5))
- strncat(buf, "\r\n", 32);
- else
- strncat(buf, "\n\r\n", 32);
- if (serial_write(serial, buf, strlen(buf)) == -1) {
- sr_err("Failed to send: %s.", strerror(errno));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int send_stat(const struct sr_dev_inst *sdi)
-{
- return agdmm_send(sdi, "STAT?");
-}
-
-static int recv_stat_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
-{
- struct dev_context *devc;
- char *s;
-
- devc = sdi->priv;
- s = g_match_info_fetch(match, 1);
- sr_spew("STAT response '%s'.", s);
-
- /* Max, Min or Avg mode -- no way to tell which, so we'll
- * set both flags to denote it's not a normal measurement. */
- if (s[0] == '1')
- devc->cur_mqflags |= SR_MQFLAG_MAX | SR_MQFLAG_MIN;
- else
- devc->cur_mqflags &= ~(SR_MQFLAG_MAX | SR_MQFLAG_MIN);
-
- if (s[1] == '1')
- devc->cur_mqflags |= SR_MQFLAG_RELATIVE;
- else
- devc->cur_mqflags &= ~SR_MQFLAG_RELATIVE;
-
- /* Triggered or auto hold modes. */
- if (s[2] == '1' || s[3] == '1')
- devc->cur_mqflags |= SR_MQFLAG_HOLD;
- else
- devc->cur_mqflags &= ~SR_MQFLAG_HOLD;
-
- /* Temp/aux mode. */
- if (s[7] == '1')
- devc->mode_tempaux = TRUE;
- else
- devc->mode_tempaux = FALSE;
-
- /* Continuity mode. */
- if (s[16] == '1')
- devc->mode_continuity = TRUE;
- else
- devc->mode_continuity = FALSE;
-
- g_free(s);
-
- return SR_OK;
-}
-
-static int recv_stat_u125x(const struct sr_dev_inst *sdi, GMatchInfo *match)
-{
- struct dev_context *devc;
- char *s;
-
- devc = sdi->priv;
- s = g_match_info_fetch(match, 1);
- sr_spew("STAT response '%s'.", s);
-
- /* Peak hold mode. */
- if (s[4] == '1')
- devc->cur_mqflags |= SR_MQFLAG_MAX;
- else
- devc->cur_mqflags &= ~SR_MQFLAG_MAX;
-
- /* Triggered hold mode. */
- if (s[7] == '1')
- devc->cur_mqflags |= SR_MQFLAG_HOLD;
- else
- devc->cur_mqflags &= ~SR_MQFLAG_HOLD;
-
- g_free(s);
-
- return SR_OK;
-}
-
-static int send_fetc(const struct sr_dev_inst *sdi)
-{
- return agdmm_send(sdi, "FETC?");
-}
-
-static int recv_fetc(const struct sr_dev_inst *sdi, GMatchInfo *match)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- float fvalue;
- char *mstr;
-
- sr_spew("FETC reply '%s'.", g_match_info_get_string(match));
- devc = sdi->priv;
-
- if (devc->cur_mq == -1)
- /* Haven't seen configuration yet, so can't know what
- * the fetched float means. Not really an error, we'll
- * get metadata soon enough. */
- return SR_OK;
-
- if (!strcmp(g_match_info_get_string(match), "+9.90000000E+37")) {
- /* An invalid measurement shows up on the display as "O.L", but
- * comes through like this. Since comparing 38-digit floats
- * is rather problematic, we'll cut through this here. */
- fvalue = NAN;
- } else {
- mstr = g_match_info_fetch(match, 1);
- if (sr_atof_ascii(mstr, &fvalue) != SR_OK || fvalue == 0.0) {
- g_free(mstr);
- sr_err("Invalid float.");
- return SR_ERR;
- }
- g_free(mstr);
- if (devc->cur_divider > 0)
- fvalue /= devc->cur_divider;
- }
-
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
- analog.mq = devc->cur_mq;
- analog.unit = devc->cur_unit;
- analog.mqflags = devc->cur_mqflags;
- analog.channels = sdi->channels;
- analog.num_samples = 1;
- analog.data = &fvalue;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
-
- devc->num_samples++;
-
- return SR_OK;
-}
-
-static int send_conf(const struct sr_dev_inst *sdi)
-{
- return agdmm_send(sdi, "CONF?");
-}
-
-static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
-{
- struct dev_context *devc;
- char *mstr;
-
- sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
- devc = sdi->priv;
- mstr = g_match_info_fetch(match, 1);
- if (!strcmp(mstr, "V")) {
- devc->cur_mq = SR_MQ_VOLTAGE;
- devc->cur_unit = SR_UNIT_VOLT;
- devc->cur_mqflags = 0;
- devc->cur_divider = 0;
- } else if(!strcmp(mstr, "MV")) {
- if (devc->mode_tempaux) {
- devc->cur_mq = SR_MQ_TEMPERATURE;
- /* No way to detect whether Fahrenheit or Celcius
- * is used, so we'll just default to Celcius. */
- devc->cur_unit = SR_UNIT_CELSIUS;
- devc->cur_mqflags = 0;
- devc->cur_divider = 0;
- } else {
- devc->cur_mq = SR_MQ_VOLTAGE;
- devc->cur_unit = SR_UNIT_VOLT;
- devc->cur_mqflags = 0;
- devc->cur_divider = 1000;
- }
- } else if(!strcmp(mstr, "A")) {
- devc->cur_mq = SR_MQ_CURRENT;
- devc->cur_unit = SR_UNIT_AMPERE;
- devc->cur_mqflags = 0;
- devc->cur_divider = 0;
- } else if(!strcmp(mstr, "UA")) {
- devc->cur_mq = SR_MQ_CURRENT;
- devc->cur_unit = SR_UNIT_AMPERE;
- devc->cur_mqflags = 0;
- devc->cur_divider = 1000000;
- } else if(!strcmp(mstr, "FREQ")) {
- devc->cur_mq = SR_MQ_FREQUENCY;
- devc->cur_unit = SR_UNIT_HERTZ;
- devc->cur_mqflags = 0;
- devc->cur_divider = 0;
- } else if(!strcmp(mstr, "RES")) {
- if (devc->mode_continuity) {
- devc->cur_mq = SR_MQ_CONTINUITY;
- devc->cur_unit = SR_UNIT_BOOLEAN;
- } else {
- devc->cur_mq = SR_MQ_RESISTANCE;
- devc->cur_unit = SR_UNIT_OHM;
- }
- devc->cur_mqflags = 0;
- devc->cur_divider = 0;
- } else if(!strcmp(mstr, "CAP")) {
- devc->cur_mq = SR_MQ_CAPACITANCE;
- devc->cur_unit = SR_UNIT_FARAD;
- devc->cur_mqflags = 0;
- devc->cur_divider = 0;
- } else
- sr_dbg("Unknown first argument.");
- g_free(mstr);
-
- if (g_match_info_get_match_count(match) == 4) {
- mstr = g_match_info_fetch(match, 3);
- /* Third value, if present, is always AC or DC. */
- if (!strcmp(mstr, "AC"))
- devc->cur_mqflags |= SR_MQFLAG_AC;
- else if (!strcmp(mstr, "DC"))
- devc->cur_mqflags |= SR_MQFLAG_DC;
- else
- sr_dbg("Unknown third argument.");
- g_free(mstr);
- } else
- devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
-
- return SR_OK;
-}
-
-static int recv_conf_u125x(const struct sr_dev_inst *sdi, GMatchInfo *match)
-{
- struct dev_context *devc;
- char *mstr;
-
- sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
- devc = sdi->priv;
- mstr = g_match_info_fetch(match, 1);
- if (!strncmp(mstr, "VOLT", 4)) {
- devc->cur_mq = SR_MQ_VOLTAGE;
- devc->cur_unit = SR_UNIT_VOLT;
- devc->cur_mqflags = 0;
- devc->cur_divider = 0;
- if (mstr[4] == ':') {
- if (!strcmp(mstr + 4, "AC"))
- devc->cur_mqflags |= SR_MQFLAG_AC;
- else if (!strcmp(mstr + 4, "DC"))
- devc->cur_mqflags |= SR_MQFLAG_DC;
- else
- /* "ACDC" appears as well, no idea what it means. */
- devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
- } else
- devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
- } else if(!strcmp(mstr, "CURR")) {
- devc->cur_mq = SR_MQ_CURRENT;
- devc->cur_unit = SR_UNIT_AMPERE;
- devc->cur_mqflags = 0;
- devc->cur_divider = 0;
- } else if(!strcmp(mstr, "RES")) {
- if (devc->mode_continuity) {
- devc->cur_mq = SR_MQ_CONTINUITY;
- devc->cur_unit = SR_UNIT_BOOLEAN;
- } else {
- devc->cur_mq = SR_MQ_RESISTANCE;
- devc->cur_unit = SR_UNIT_OHM;
- }
- devc->cur_mqflags = 0;
- devc->cur_divider = 0;
- } else
- sr_dbg("Unknown first argument.");
- g_free(mstr);
-
- return SR_OK;
-}
-
-/* At least the 123x and 125x appear to have this. */
-static int recv_conf(const struct sr_dev_inst *sdi, GMatchInfo *match)
-{
- struct dev_context *devc;
- char *mstr;
-
- sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
- devc = sdi->priv;
- mstr = g_match_info_fetch(match, 1);
- if(!strcmp(mstr, "DIOD")) {
- devc->cur_mq = SR_MQ_VOLTAGE;
- devc->cur_unit = SR_UNIT_VOLT;
- devc->cur_mqflags = SR_MQFLAG_DIODE;
- devc->cur_divider = 0;
- } else
- sr_dbg("Unknown single argument.");
- g_free(mstr);
-
- return SR_OK;
-}
-
-/* This comes in whenever the rotary switch is changed to a new position.
- * We could use it to determine the major measurement mode, but we already
- * have the output of CONF? for that, which is more detailed. However
- * we do need to catch this here, or it'll show up in some other output. */
-static int recv_switch(const struct sr_dev_inst *sdi, GMatchInfo *match)
-{
- (void)sdi;
-
- sr_spew("Switch '%s'.", g_match_info_get_string(match));
-
- return SR_OK;
-}
-
-SR_PRIV const struct agdmm_job agdmm_jobs_u123x[] = {
- { 143, send_stat },
- { 1000, send_conf },
- { 143, send_fetc },
- { 0, NULL }
-};
-
-SR_PRIV const struct agdmm_recv agdmm_recvs_u123x[] = {
- { "^\"(\\d\\d.{18}\\d)\"$", recv_stat_u123x },
- { "^\\*([0-9])$", recv_switch },
- { "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
- { "^\"(V|MV|A|UA|FREQ),(\\d),(AC|DC)\"$", recv_conf_u123x },
- { "^\"(RES|CAP),(\\d)\"$", recv_conf_u123x},
- { "^\"(DIOD)\"$", recv_conf },
- { NULL, NULL }
-};
-
-SR_PRIV const struct agdmm_job agdmm_jobs_u125x[] = {
- { 143, send_stat },
- { 1000, send_conf },
- { 143, send_fetc },
- { 0, NULL }
-};
-
-SR_PRIV const struct agdmm_recv agdmm_recvs_u125x[] = {
- { "^\"(\\d\\d.{18}\\d)\"$", recv_stat_u125x },
- { "^\\*([0-9])$", recv_switch },
- { "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
- { "^(VOLT|CURR|RES|CAP) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)$", recv_conf_u125x },
- { "^(VOLT:[ACD]+) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)$", recv_conf_u125x },
- { "^\"(DIOD)\"$", recv_conf },
- { NULL, NULL }
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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 <string.h>
-#include "protocol.h"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_THERMOMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
- SR_CONF_DATA_SOURCE,
-};
-
-static const char *data_sources[] = {
- "Live",
- "Memory",
-};
-
-SR_PRIV struct sr_dev_driver appa_55ii_driver_info;
-static struct sr_dev_driver *di = &appa_55ii_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- struct sr_config *src;
- GSList *devices, *l;
- const char *conn, *serialcomm;
- uint8_t buf[50];
- size_t len;
-
- len = sizeof(buf);
- devices = NULL;
- 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 (!conn)
- return NULL;
- if (!serialcomm)
- serialcomm = "9600/8n1";
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
- if (serial_open(serial, SERIAL_RDONLY | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- sr_info("Probing serial port %s.", conn);
-
- drvc = di->priv;
- drvc->instances = NULL;
- serial_flush(serial);
-
- /* Let's get a bit of data and see if we can find a packet. */
- if (serial_stream_detect(serial, buf, &len, 25,
- appa_55ii_packet_valid, 500, 9600) != SR_OK)
- goto scan_cleanup;
-
- sr_info("Found device on port %s.", conn);
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "APPA", "55II", NULL)))
- goto scan_cleanup;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- goto scan_cleanup;
- }
-
- devc->data_source = DEFAULT_DATA_SOURCE;
-
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
- sdi->priv = devc;
- sdi->driver = di;
-
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "T1")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "T2")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
-scan_cleanup:
- serial_close(serial);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_get(int 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_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- *data = g_variant_new_uint64(devc->limit_msec);
- break;
- case SR_CONF_DATA_SOURCE:
- *data = g_variant_new_string(data_sources[devc->data_source]);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- const char *tmp_str;
- unsigned int i;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".", devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- devc->limit_msec = g_variant_get_uint64(data);
- sr_dbg("Setting time limit to %" PRIu64 "ms.", devc->limit_msec);
- break;
- case SR_CONF_DATA_SOURCE: {
- tmp_str = g_variant_get_string(data, NULL);
- for (i = 0; i < ARRAY_SIZE(data_sources); i++)
- if (!strcmp(tmp_str, data_sources[i])) {
- devc->data_source = i;
- break;
- }
- if (i == ARRAY_SIZE(data_sources))
- return SR_ERR;
- break;
- }
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_DATA_SOURCE:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct sr_serial_dev_inst *serial;
- struct dev_context *devc;
-
- serial = sdi->conn;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- devc->session_cb_data = cb_data;
-
- /*
- * Reset the number of samples to take. If we've already collected our
- * quota, but we start a new session, and don't reset this, we'll just
- * quit without acquiring any new samples.
- */
- devc->num_samples = 0;
- devc->start_time = g_get_monotonic_time();
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Poll every 50ms, or whenever some data comes in. */
- serial_source_add(sdi->session, serial, G_IO_IN, 50,
- appa_55ii_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- return std_serial_dev_acquisition_stop(sdi, cb_data,
- std_serial_dev_close, sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver appa_55ii_driver_info = {
- .name = "appa-55ii",
- .longname = "APPA 55II",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .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 = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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 <string.h>
-#include <math.h>
-#include "protocol.h"
-
-typedef enum {
- LIVE_DATA = 0x00,
- LOG_METADATA = 0x11,
- LOG_DATA = 0x14,
- LOG_START = 0x18,
- LOG_END = 0x19,
-} packet_type;
-
-static gboolean appa_55ii_checksum(const uint8_t *buf)
-{
- int i, size, checksum;
-
- size = buf[3] + 4;
- checksum = 0;
- for (i = 0; i < size; i++)
- checksum += buf[i];
-
- return buf[size] == (checksum & 0xFF);
-}
-
-SR_PRIV gboolean appa_55ii_packet_valid(const uint8_t *buf)
-{
- if (buf[0] == 0x55 && buf[1] == 0x55 && buf[3] <= 32
- && appa_55ii_checksum(buf))
- return TRUE;
-
- return FALSE;
-}
-
-static uint64_t appa_55ii_flags(const uint8_t *buf)
-{
- uint8_t disp_mode;
- uint64_t flags;
-
- disp_mode = buf[4 + 13];
- flags = 0;
- if ((disp_mode & 0xF0) == 0x20)
- flags |= SR_MQFLAG_HOLD;
- if ((disp_mode & 0x0C) == 0x04)
- flags |= SR_MQFLAG_MAX;
- if ((disp_mode & 0x0C) == 0x08)
- flags |= SR_MQFLAG_MIN;
- if ((disp_mode & 0x0C) == 0x0C)
- flags |= SR_MQFLAG_AVG;
-
- return flags;
-}
-
-static float appa_55ii_temp(const uint8_t *buf, int ch)
-{
- const uint8_t *ptr;
- int16_t temp;
- uint8_t flags;
-
- ptr = buf + 4 + 14 + 3 * ch;
- temp = RL16(ptr);
- flags = ptr[2];
-
- if (flags & 0x60)
- return INFINITY;
- else if (flags & 1)
- return (float)temp / 10;
- else
- return (float)temp;
-}
-
-static void appa_55ii_live_data(struct sr_dev_inst *sdi, const uint8_t *buf)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct sr_channel *ch;
- float values[APPA_55II_NUM_CHANNELS], *val_ptr;
- int i;
-
- devc = sdi->priv;
-
- if (devc->data_source != DATA_SOURCE_LIVE)
- return;
-
- val_ptr = values;
- memset(&analog, 0, sizeof(analog));
- analog.num_samples = 1;
- analog.mq = SR_MQ_TEMPERATURE;
- analog.unit = SR_UNIT_CELSIUS;
- analog.mqflags = appa_55ii_flags(buf);
- analog.data = values;
-
- for (i = 0; i < APPA_55II_NUM_CHANNELS; i++) {
- ch = g_slist_nth_data(sdi->channels, i);
- if (!ch->enabled)
- continue;
- analog.channels = g_slist_append(analog.channels, ch);
- *val_ptr++ = appa_55ii_temp(buf, i);
- }
-
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->session_cb_data, &packet);
- g_slist_free(analog.channels);
-
- devc->num_samples++;
-}
-
-static void appa_55ii_log_metadata(struct sr_dev_inst *sdi, const uint8_t *buf)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
- devc->num_log_records = (buf[5] << 8) + buf[4];
-}
-
-static void appa_55ii_log_data_parse(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct sr_channel *ch;
- float values[APPA_55II_NUM_CHANNELS], *val_ptr;
- const uint8_t *buf;
- int16_t temp;
- int offset, i;
-
- devc = sdi->priv;
- offset = 0;
-
- while (devc->log_buf_len >= 20 && devc->num_log_records > 0) {
- buf = devc->log_buf + offset;
- val_ptr = values;
-
- /* FIXME: Timestamp should be sent in the packet. */
- sr_dbg("Timestamp: %02d:%02d:%02d", buf[2], buf[3], buf[4]);
-
- memset(&analog, 0, sizeof(analog));
- analog.num_samples = 1;
- analog.mq = SR_MQ_TEMPERATURE;
- analog.unit = SR_UNIT_CELSIUS;
- analog.data = values;
-
- for (i = 0; i < APPA_55II_NUM_CHANNELS; i++) {
- temp = RL16(buf + 12 + 2 * i);
- ch = g_slist_nth_data(sdi->channels, i);
- if (!ch->enabled)
- continue;
- analog.channels = g_slist_append(analog.channels, ch);
- *val_ptr++ = temp == 0x7FFF ? INFINITY : (float)temp / 10;
- }
-
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->session_cb_data, &packet);
- g_slist_free(analog.channels);
-
- devc->num_samples++;
- devc->log_buf_len -= 20;
- offset += 20;
- devc->num_log_records--;
- }
-
- memmove(devc->log_buf, devc->log_buf + offset, devc->log_buf_len);
-}
-
-static void appa_55ii_log_data(struct sr_dev_inst *sdi, const uint8_t *buf)
-{
- struct dev_context *devc;
- const uint8_t *ptr;
- unsigned int size;
- int s;
-
- devc = sdi->priv;
- if (devc->data_source != DATA_SOURCE_MEMORY)
- return;
-
- ptr = buf + 4;
- size = buf[3];
- while (size > 0) {
- s = MIN(size, sizeof(devc->log_buf) - devc->log_buf_len);
- memcpy(devc->log_buf + devc->log_buf_len, ptr, s);
- devc->log_buf_len += s;
- size -= s;
- ptr += s;
-
- appa_55ii_log_data_parse(sdi);
- }
-}
-
-static void appa_55ii_log_end(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
- if (devc->data_source != DATA_SOURCE_MEMORY)
- return;
-
- sdi->driver->dev_acquisition_stop(sdi, devc->session_cb_data);
-}
-
-static const uint8_t *appa_55ii_parse_data(struct sr_dev_inst *sdi,
- const uint8_t *buf, int len)
-{
- if (len < 5)
- /* Need more data. */
- return NULL;
-
- if (buf[0] != 0x55 || buf[1] != 0x55)
- /* Try to re-synchronize on a packet start. */
- return buf + 1;
-
- if (len < 5 + buf[3])
- /* Need more data. */
- return NULL;
-
- if (!appa_55ii_checksum(buf))
- /* Skip broken packet. */
- return buf + 4 + buf[3] + 1;
-
- switch ((packet_type)buf[2]) {
- case LIVE_DATA:
- appa_55ii_live_data(sdi, buf);
- break;
- case LOG_METADATA:
- appa_55ii_log_metadata(sdi, buf);
- break;
- case LOG_DATA:
- appa_55ii_log_data(sdi, buf);
- break;
- case LOG_START:
- break;
- case LOG_END:
- appa_55ii_log_end(sdi);
- break;
- default:
- sr_warn("Invalid packet type: 0x%02x.", buf[2]);
- break;
- }
-
- return buf + 4 + buf[3] + 1;
-}
-
-SR_PRIV int appa_55ii_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- int64_t time;
- const uint8_t *ptr, *next_ptr, *end_ptr;
- int len;
-
- (void)fd;
-
- if (!(sdi = cb_data) || !(devc = sdi->priv) || revents != G_IO_IN)
- return TRUE;
- serial = sdi->conn;
-
- /* Try to get as much data as the buffer can hold. */
- len = sizeof(devc->buf) - devc->buf_len;
- len = serial_read(serial, devc->buf + devc->buf_len, len);
- if (len < 1) {
- sr_err("Serial port read error: %d.", len);
- return FALSE;
- }
- devc->buf_len += len;
-
- /* Now look for packets in that data. */
- ptr = devc->buf;
- end_ptr = ptr + devc->buf_len;
- while ((next_ptr = appa_55ii_parse_data(sdi, ptr, end_ptr - ptr)))
- ptr = next_ptr;
-
- /* If we have any data left, move it to the beginning of our buffer. */
- memmove(devc->buf, ptr, end_ptr - ptr);
- devc->buf_len -= ptr - devc->buf;
-
- /* If buffer is full and no valid packet was found, wipe buffer. */
- if (devc->buf_len >= sizeof(devc->buf)) {
- devc->buf_len = 0;
- return FALSE;
- }
-
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi, devc->session_cb_data);
- return TRUE;
- }
-
- if (devc->limit_msec) {
- time = (g_get_monotonic_time() - devc->start_time) / 1000;
- if (time > (int64_t)devc->limit_msec) {
- sr_info("Requested time limit reached.");
- sdi->driver->dev_acquisition_stop(sdi,
- devc->session_cb_data);
- return TRUE;
- }
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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_APPA_55II_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_APPA_55II_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "appa-55ii"
-
-#define APPA_55II_NUM_CHANNELS 2
-#define APPA_55II_BUF_SIZE (4 + 32 + 1)
-#define DEFAULT_DATA_SOURCE DATA_SOURCE_LIVE
-
-enum {
- DATA_SOURCE_LIVE,
- DATA_SOURCE_MEMORY,
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Acquisition settings */
- uint64_t limit_samples; /**< The sampling limit (in number of samples). */
- uint64_t limit_msec; /**< The time limit (in milliseconds). */
- gboolean data_source; /**< Whether to read live samples or memory */
- void *session_cb_data; /**< Opaque pointer passed in by the frontend. */
-
- /* Operational state */
- uint64_t num_samples; /**< The number of already received samples. */
- int64_t start_time; /**< The time at which sampling started. */
-
- /* Temporary state across callbacks */
- uint8_t buf[APPA_55II_BUF_SIZE];
- unsigned int buf_len;
- uint8_t log_buf[64];
- unsigned int log_buf_len;
- unsigned int num_log_records;
-};
-
-SR_PRIV gboolean appa_55ii_packet_valid(const uint8_t *buf);
-SR_PRIV int appa_55ii_receive_data(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 HÃ¥vard Espeland <gus@ping.uio.no>,
- * Copyright (C) 2010 Martin Stensgård <mastensg@ping.uio.no>
- * Copyright (C) 2010 Carl Henrik Lunde <chlunde@ping.uio.no>
- *
- * 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/>.
- */
-
-/*
- * ASIX SIGMA/SIGMA2 logic analyzer driver
- */
-
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <ftdi.h>
-#include <string.h>
-#include <unistd.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "asix-sigma.h"
-
-#define USB_VENDOR 0xa600
-#define USB_PRODUCT 0xa000
-#define USB_DESCRIPTION "ASIX SIGMA"
-#define USB_VENDOR_NAME "ASIX"
-#define USB_MODEL_NAME "SIGMA"
-
-SR_PRIV struct sr_dev_driver asix_sigma_driver_info;
-static struct sr_dev_driver *di = &asix_sigma_driver_info;
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
-
-/*
- * The ASIX Sigma supports arbitrary integer frequency divider in
- * the 50MHz mode. The divider is in range 1...256 , allowing for
- * very precise sampling rate selection. This driver supports only
- * a subset of the sampling rates.
- */
-static const uint64_t samplerates[] = {
- SR_KHZ(200), /* div=250 */
- SR_KHZ(250), /* div=200 */
- SR_KHZ(500), /* div=100 */
- SR_MHZ(1), /* div=50 */
- SR_MHZ(5), /* div=10 */
- SR_MHZ(10), /* div=5 */
- SR_MHZ(25), /* div=2 */
- SR_MHZ(50), /* div=1 */
- SR_MHZ(100), /* Special FW needed */
- SR_MHZ(200), /* Special FW needed */
-};
-
-/*
- * Channel numbers seem to go from 1-16, according to this image:
- * http://tools.asix.net/img/sigma_sigmacab_pins_720.jpg
- * (the cable has two additional GND pins, and a TI and TO pin)
- */
-static const char *channel_names[] = {
- "1", "2", "3", "4", "5", "6", "7", "8",
- "9", "10", "11", "12", "13", "14", "15", "16",
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_SAMPLERATE,
- SR_CONF_TRIGGER_MATCH,
- SR_CONF_CAPTURE_RATIO,
- SR_CONF_LIMIT_MSEC,
-};
-
-static const int32_t trigger_matches[] = {
- SR_TRIGGER_ZERO,
- SR_TRIGGER_ONE,
- SR_TRIGGER_RISING,
- SR_TRIGGER_FALLING,
-};
-
-static const char *sigma_firmware_files[] = {
- /* 50 MHz, supports 8 bit fractions */
- FIRMWARE_DIR "/asix-sigma-50.fw",
- /* 100 MHz */
- FIRMWARE_DIR "/asix-sigma-100.fw",
- /* 200 MHz */
- FIRMWARE_DIR "/asix-sigma-200.fw",
- /* Synchronous clock from pin */
- FIRMWARE_DIR "/asix-sigma-50sync.fw",
- /* Frequency counter */
- FIRMWARE_DIR "/asix-sigma-phasor.fw",
-};
-
-static int sigma_read(void *buf, size_t size, struct dev_context *devc)
-{
- int ret;
-
- ret = ftdi_read_data(&devc->ftdic, (unsigned char *)buf, size);
- if (ret < 0) {
- sr_err("ftdi_read_data failed: %s",
- ftdi_get_error_string(&devc->ftdic));
- }
-
- return ret;
-}
-
-static int sigma_write(void *buf, size_t size, struct dev_context *devc)
-{
- int ret;
-
- ret = ftdi_write_data(&devc->ftdic, (unsigned char *)buf, size);
- if (ret < 0) {
- sr_err("ftdi_write_data failed: %s",
- ftdi_get_error_string(&devc->ftdic));
- } else if ((size_t) ret != size) {
- sr_err("ftdi_write_data did not complete write.");
- }
-
- return ret;
-}
-
-static int sigma_write_register(uint8_t reg, uint8_t *data, size_t len,
- struct dev_context *devc)
-{
- size_t i;
- uint8_t buf[len + 2];
- int idx = 0;
-
- buf[idx++] = REG_ADDR_LOW | (reg & 0xf);
- buf[idx++] = REG_ADDR_HIGH | (reg >> 4);
-
- for (i = 0; i < len; ++i) {
- buf[idx++] = REG_DATA_LOW | (data[i] & 0xf);
- buf[idx++] = REG_DATA_HIGH_WRITE | (data[i] >> 4);
- }
-
- return sigma_write(buf, idx, devc);
-}
-
-static int sigma_set_register(uint8_t reg, uint8_t value, struct dev_context *devc)
-{
- return sigma_write_register(reg, &value, 1, devc);
-}
-
-static int sigma_read_register(uint8_t reg, uint8_t *data, size_t len,
- struct dev_context *devc)
-{
- uint8_t buf[3];
-
- buf[0] = REG_ADDR_LOW | (reg & 0xf);
- buf[1] = REG_ADDR_HIGH | (reg >> 4);
- buf[2] = REG_READ_ADDR;
-
- sigma_write(buf, sizeof(buf), devc);
-
- return sigma_read(data, len, devc);
-}
-
-static uint8_t sigma_get_register(uint8_t reg, struct dev_context *devc)
-{
- uint8_t value;
-
- if (1 != sigma_read_register(reg, &value, 1, devc)) {
- sr_err("sigma_get_register: 1 byte expected");
- return 0;
- }
-
- return value;
-}
-
-static int sigma_read_pos(uint32_t *stoppos, uint32_t *triggerpos,
- struct dev_context *devc)
-{
- uint8_t buf[] = {
- REG_ADDR_LOW | READ_TRIGGER_POS_LOW,
-
- REG_READ_ADDR | NEXT_REG,
- REG_READ_ADDR | NEXT_REG,
- REG_READ_ADDR | NEXT_REG,
- REG_READ_ADDR | NEXT_REG,
- REG_READ_ADDR | NEXT_REG,
- REG_READ_ADDR | NEXT_REG,
- };
- uint8_t result[6];
-
- sigma_write(buf, sizeof(buf), devc);
-
- sigma_read(result, sizeof(result), devc);
-
- *triggerpos = result[0] | (result[1] << 8) | (result[2] << 16);
- *stoppos = result[3] | (result[4] << 8) | (result[5] << 16);
-
- /* Not really sure why this must be done, but according to spec. */
- if ((--*stoppos & 0x1ff) == 0x1ff)
- stoppos -= 64;
-
- if ((*--triggerpos & 0x1ff) == 0x1ff)
- triggerpos -= 64;
-
- return 1;
-}
-
-static int sigma_read_dram(uint16_t startchunk, size_t numchunks,
- uint8_t *data, struct dev_context *devc)
-{
- size_t i;
- uint8_t buf[4096];
- int idx = 0;
-
- /* Send the startchunk. Index start with 1. */
- buf[0] = startchunk >> 8;
- buf[1] = startchunk & 0xff;
- sigma_write_register(WRITE_MEMROW, buf, 2, devc);
-
- /* Read the DRAM. */
- buf[idx++] = REG_DRAM_BLOCK;
- buf[idx++] = REG_DRAM_WAIT_ACK;
-
- for (i = 0; i < numchunks; ++i) {
- /* Alternate bit to copy from DRAM to cache. */
- if (i != (numchunks - 1))
- buf[idx++] = REG_DRAM_BLOCK | (((i + 1) % 2) << 4);
-
- buf[idx++] = REG_DRAM_BLOCK_DATA | ((i % 2) << 4);
-
- if (i != (numchunks - 1))
- buf[idx++] = REG_DRAM_WAIT_ACK;
- }
-
- sigma_write(buf, idx, devc);
-
- return sigma_read(data, numchunks * CHUNK_SIZE, devc);
-}
-
-/* Upload trigger look-up tables to Sigma. */
-static int sigma_write_trigger_lut(struct triggerlut *lut, struct dev_context *devc)
-{
- int i;
- uint8_t tmp[2];
- uint16_t bit;
-
- /* Transpose the table and send to Sigma. */
- for (i = 0; i < 16; ++i) {
- bit = 1 << i;
-
- tmp[0] = tmp[1] = 0;
-
- if (lut->m2d[0] & bit)
- tmp[0] |= 0x01;
- if (lut->m2d[1] & bit)
- tmp[0] |= 0x02;
- if (lut->m2d[2] & bit)
- tmp[0] |= 0x04;
- if (lut->m2d[3] & bit)
- tmp[0] |= 0x08;
-
- if (lut->m3 & bit)
- tmp[0] |= 0x10;
- if (lut->m3s & bit)
- tmp[0] |= 0x20;
- if (lut->m4 & bit)
- tmp[0] |= 0x40;
-
- if (lut->m0d[0] & bit)
- tmp[1] |= 0x01;
- if (lut->m0d[1] & bit)
- tmp[1] |= 0x02;
- if (lut->m0d[2] & bit)
- tmp[1] |= 0x04;
- if (lut->m0d[3] & bit)
- tmp[1] |= 0x08;
-
- if (lut->m1d[0] & bit)
- tmp[1] |= 0x10;
- if (lut->m1d[1] & bit)
- tmp[1] |= 0x20;
- if (lut->m1d[2] & bit)
- tmp[1] |= 0x40;
- if (lut->m1d[3] & bit)
- tmp[1] |= 0x80;
-
- sigma_write_register(WRITE_TRIGGER_SELECT0, tmp, sizeof(tmp),
- devc);
- sigma_set_register(WRITE_TRIGGER_SELECT1, 0x30 | i, devc);
- }
-
- /* Send the parameters */
- sigma_write_register(WRITE_TRIGGER_SELECT0, (uint8_t *) &lut->params,
- sizeof(lut->params), devc);
-
- return SR_OK;
-}
-
-static void clear_helper(void *priv)
-{
- struct dev_context *devc;
-
- devc = priv;
-
- ftdi_deinit(&devc->ftdic);
-}
-
-static int dev_clear(void)
-{
- return std_dev_clear(di, clear_helper);
-}
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- struct drv_context *drvc;
- struct dev_context *devc;
- GSList *devices;
- struct ftdi_device_list *devlist;
- char serial_txt[10];
- uint32_t serial;
- int ret;
- unsigned int i;
-
- (void)options;
-
- drvc = di->priv;
-
- devices = NULL;
-
- if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
- sr_err("%s: devc malloc failed", __func__);
- return NULL;
- }
-
- ftdi_init(&devc->ftdic);
-
- /* Look for SIGMAs. */
-
- if ((ret = ftdi_usb_find_all(&devc->ftdic, &devlist,
- USB_VENDOR, USB_PRODUCT)) <= 0) {
- if (ret < 0)
- sr_err("ftdi_usb_find_all(): %d", ret);
- goto free;
- }
-
- /* Make sure it's a version 1 or 2 SIGMA. */
- ftdi_usb_get_strings(&devc->ftdic, devlist->dev, NULL, 0, NULL, 0,
- serial_txt, sizeof(serial_txt));
- sscanf(serial_txt, "%x", &serial);
-
- if (serial < 0xa6010000 || serial > 0xa602ffff) {
- sr_err("Only SIGMA and SIGMA2 are supported "
- "in this version of libsigrok.");
- goto free;
- }
-
- sr_info("Found ASIX SIGMA - Serial: %s", serial_txt);
-
- devc->cur_samplerate = samplerates[0];
- devc->period_ps = 0;
- devc->limit_msec = 0;
- devc->cur_firmware = -1;
- devc->num_channels = 0;
- devc->samples_per_event = 0;
- devc->capture_ratio = 50;
- devc->use_triggers = 0;
-
- /* Register SIGMA device. */
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING, USB_VENDOR_NAME,
- USB_MODEL_NAME, NULL))) {
- sr_err("%s: sdi was NULL", __func__);
- goto free;
- }
- sdi->driver = di;
-
- for (i = 0; i < ARRAY_SIZE(channel_names); i++) {
- ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
- channel_names[i]);
- if (!ch)
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- devices = g_slist_append(devices, sdi);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- sdi->priv = devc;
-
- /* We will open the device again when we need it. */
- ftdi_list_free(&devlist);
-
- return devices;
-
-free:
- ftdi_deinit(&devc->ftdic);
- g_free(devc);
- return NULL;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-/*
- * Configure the FPGA for bitbang mode.
- * This sequence is documented in section 2. of the ASIX Sigma programming
- * manual. This sequence is necessary to configure the FPGA in the Sigma
- * into Bitbang mode, in which it can be programmed with the firmware.
- */
-static int sigma_fpga_init_bitbang(struct dev_context *devc)
-{
- uint8_t suicide[] = {
- 0x84, 0x84, 0x88, 0x84, 0x88, 0x84, 0x88, 0x84,
- };
- uint8_t init_array[] = {
- 0x01, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01,
- };
- int i, ret, timeout = 10000;
- uint8_t data;
-
- /* Section 2. part 1), do the FPGA suicide. */
- sigma_write(suicide, sizeof(suicide), devc);
- sigma_write(suicide, sizeof(suicide), devc);
- sigma_write(suicide, sizeof(suicide), devc);
- sigma_write(suicide, sizeof(suicide), devc);
-
- /* Section 2. part 2), do pulse on D1. */
- sigma_write(init_array, sizeof(init_array), devc);
- ftdi_usb_purge_buffers(&devc->ftdic);
-
- /* Wait until the FPGA asserts D6/INIT_B. */
- for (i = 0; i < timeout; i++) {
- ret = sigma_read(&data, 1, devc);
- if (ret < 0)
- return ret;
- /* Test if pin D6 got asserted. */
- if (data & (1 << 5))
- return 0;
- /* The D6 was not asserted yet, wait a bit. */
- usleep(10000);
- }
-
- return SR_ERR_TIMEOUT;
-}
-
-/*
- * Configure the FPGA for logic-analyzer mode.
- */
-static int sigma_fpga_init_la(struct dev_context *devc)
-{
- /* Initialize the logic analyzer mode. */
- uint8_t logic_mode_start[] = {
- REG_ADDR_LOW | (READ_ID & 0xf),
- REG_ADDR_HIGH | (READ_ID >> 8),
- REG_READ_ADDR, /* Read ID register. */
-
- REG_ADDR_LOW | (WRITE_TEST & 0xf),
- REG_DATA_LOW | 0x5,
- REG_DATA_HIGH_WRITE | 0x5,
- REG_READ_ADDR, /* Read scratch register. */
-
- REG_DATA_LOW | 0xa,
- REG_DATA_HIGH_WRITE | 0xa,
- REG_READ_ADDR, /* Read scratch register. */
-
- REG_ADDR_LOW | (WRITE_MODE & 0xf),
- REG_DATA_LOW | 0x0,
- REG_DATA_HIGH_WRITE | 0x8,
- };
-
- uint8_t result[3];
- int ret;
-
- /* Initialize the logic analyzer mode. */
- sigma_write(logic_mode_start, sizeof(logic_mode_start), devc);
-
- /* Expect a 3 byte reply since we issued three READ requests. */
- ret = sigma_read(result, 3, devc);
- if (ret != 3)
- goto err;
-
- if (result[0] != 0xa6 || result[1] != 0x55 || result[2] != 0xaa)
- goto err;
-
- return SR_OK;
-err:
- sr_err("Configuration failed. Invalid reply received.");
- return SR_ERR;
-}
-
-/*
- * Read the firmware from a file and transform it into a series of bitbang
- * pulses used to program the FPGA. Note that the *bb_cmd must be free()'d
- * by the caller of this function.
- */
-static int sigma_fw_2_bitbang(const char *filename,
- uint8_t **bb_cmd, gsize *bb_cmd_size)
-{
- GMappedFile *file;
- GError *error;
- gsize i, file_size, bb_size;
- gchar *firmware;
- uint8_t *bb_stream, *bbs;
- uint32_t imm;
- int bit, v;
- int ret = SR_OK;
-
- /*
- * Map the file and make the mapped buffer writable.
- * NOTE: Using writable=TRUE does _NOT_ mean that file that is mapped
- * will be modified. It will not be modified until someone uses
- * g_file_set_contents() on it.
- */
- error = NULL;
- file = g_mapped_file_new(filename, TRUE, &error);
- g_assert_no_error(error);
-
- file_size = g_mapped_file_get_length(file);
- firmware = g_mapped_file_get_contents(file);
- g_assert(firmware);
-
- /* Weird magic transformation below, I have no idea what it does. */
- imm = 0x3f6df2ab;
- for (i = 0; i < file_size; i++) {
- imm = (imm + 0xa853753) % 177 + (imm * 0x8034052);
- firmware[i] ^= imm & 0xff;
- }
-
- /*
- * Now that the firmware is "transformed", we will transcribe the
- * firmware blob into a sequence of toggles of the Dx wires. This
- * sequence will be fed directly into the Sigma, which must be in
- * the FPGA bitbang programming mode.
- */
-
- /* Each bit of firmware is transcribed as two toggles of Dx wires. */
- bb_size = file_size * 8 * 2;
- bb_stream = (uint8_t *)g_try_malloc(bb_size);
- if (!bb_stream) {
- sr_err("%s: Failed to allocate bitbang stream", __func__);
- ret = SR_ERR_MALLOC;
- goto exit;
- }
-
- bbs = bb_stream;
- for (i = 0; i < file_size; i++) {
- for (bit = 7; bit >= 0; bit--) {
- v = (firmware[i] & (1 << bit)) ? 0x40 : 0x00;
- *bbs++ = v | 0x01;
- *bbs++ = v;
- }
- }
-
- /* The transformation completed successfully, return the result. */
- *bb_cmd = bb_stream;
- *bb_cmd_size = bb_size;
-
-exit:
- g_mapped_file_unref(file);
- return ret;
-}
-
-static int upload_firmware(int firmware_idx, struct dev_context *devc)
-{
- int ret;
- unsigned char *buf;
- unsigned char pins;
- size_t buf_size;
- const char *firmware = sigma_firmware_files[firmware_idx];
- struct ftdi_context *ftdic = &devc->ftdic;
-
- /* Make sure it's an ASIX SIGMA. */
- ret = ftdi_usb_open_desc(ftdic, USB_VENDOR, USB_PRODUCT,
- USB_DESCRIPTION, NULL);
- if (ret < 0) {
- sr_err("ftdi_usb_open failed: %s",
- ftdi_get_error_string(ftdic));
- return 0;
- }
-
- ret = ftdi_set_bitmode(ftdic, 0xdf, BITMODE_BITBANG);
- if (ret < 0) {
- sr_err("ftdi_set_bitmode failed: %s",
- ftdi_get_error_string(ftdic));
- return 0;
- }
-
- /* Four times the speed of sigmalogan - Works well. */
- ret = ftdi_set_baudrate(ftdic, 750000);
- if (ret < 0) {
- sr_err("ftdi_set_baudrate failed: %s",
- ftdi_get_error_string(ftdic));
- return 0;
- }
-
- /* Initialize the FPGA for firmware upload. */
- ret = sigma_fpga_init_bitbang(devc);
- if (ret)
- return ret;
-
- /* Prepare firmware. */
- ret = sigma_fw_2_bitbang(firmware, &buf, &buf_size);
- if (ret != SR_OK) {
- sr_err("An error occured while reading the firmware: %s",
- firmware);
- return ret;
- }
-
- /* Upload firmare. */
- sr_info("Uploading firmware file '%s'.", firmware);
- sigma_write(buf, buf_size, devc);
-
- g_free(buf);
-
- ret = ftdi_set_bitmode(ftdic, 0x00, BITMODE_RESET);
- if (ret < 0) {
- sr_err("ftdi_set_bitmode failed: %s",
- ftdi_get_error_string(ftdic));
- return SR_ERR;
- }
-
- ftdi_usb_purge_buffers(ftdic);
-
- /* Discard garbage. */
- while (sigma_read(&pins, 1, devc) == 1)
- ;
-
- /* Initialize the FPGA for logic-analyzer mode. */
- ret = sigma_fpga_init_la(devc);
- if (ret != SR_OK)
- return ret;
-
- devc->cur_firmware = firmware_idx;
-
- sr_info("Firmware uploaded.");
-
- return SR_OK;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int ret;
-
- devc = sdi->priv;
-
- /* Make sure it's an ASIX SIGMA. */
- if ((ret = ftdi_usb_open_desc(&devc->ftdic,
- USB_VENDOR, USB_PRODUCT, USB_DESCRIPTION, NULL)) < 0) {
-
- sr_err("ftdi_usb_open failed: %s",
- ftdi_get_error_string(&devc->ftdic));
-
- return 0;
- }
-
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-static int set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate)
-{
- struct dev_context *devc;
- unsigned int i;
- int ret;
-
- devc = sdi->priv;
- ret = SR_OK;
-
- for (i = 0; i < ARRAY_SIZE(samplerates); i++) {
- if (samplerates[i] == samplerate)
- break;
- }
- if (samplerates[i] == 0)
- return SR_ERR_SAMPLERATE;
-
- if (samplerate <= SR_MHZ(50)) {
- ret = upload_firmware(0, devc);
- devc->num_channels = 16;
- } else if (samplerate == SR_MHZ(100)) {
- ret = upload_firmware(1, devc);
- devc->num_channels = 8;
- } else if (samplerate == SR_MHZ(200)) {
- ret = upload_firmware(2, devc);
- devc->num_channels = 4;
- }
-
- if (ret == SR_OK) {
- devc->cur_samplerate = samplerate;
- devc->period_ps = 1000000000000ULL / samplerate;
- devc->samples_per_event = 16 / devc->num_channels;
- devc->state.state = SIGMA_IDLE;
- }
-
- return ret;
-}
-
-/*
- * In 100 and 200 MHz mode, only a single pin rising/falling can be
- * set as trigger. In other modes, two rising/falling triggers can be set,
- * in addition to value/mask trigger for any number of channels.
- *
- * The Sigma supports complex triggers using boolean expressions, but this
- * has not been implemented yet.
- */
-static int convert_trigger(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_trigger *trigger;
- struct sr_trigger_stage *stage;
- struct sr_trigger_match *match;
- const GSList *l, *m;
- int channelbit, trigger_set;
-
- devc = sdi->priv;
- memset(&devc->trigger, 0, sizeof(struct sigma_trigger));
- if (!(trigger = sr_session_trigger_get(sdi->session)))
- return SR_OK;
-
- trigger_set = 0;
- for (l = trigger->stages; l; l = l->next) {
- stage = l->data;
- for (m = stage->matches; m; m = m->next) {
- match = m->data;
- if (!match->channel->enabled)
- /* Ignore disabled channels with a trigger. */
- continue;
- channelbit = 1 << (match->channel->index);
- if (devc->cur_samplerate >= SR_MHZ(100)) {
- /* Fast trigger support. */
- if (trigger_set) {
- sr_err("Only a single pin trigger is "
- "supported in 100 and 200MHz mode.");
- return SR_ERR;
- }
- if (match->match == SR_TRIGGER_FALLING)
- devc->trigger.fallingmask |= channelbit;
- else if (match->match == SR_TRIGGER_RISING)
- devc->trigger.risingmask |= channelbit;
- else {
- sr_err("Only rising/falling trigger is "
- "supported in 100 and 200MHz mode.");
- return SR_ERR;
- }
-
- ++trigger_set;
- } else {
- /* Simple trigger support (event). */
- if (match->match == SR_TRIGGER_ONE) {
- devc->trigger.simplevalue |= channelbit;
- devc->trigger.simplemask |= channelbit;
- }
- else if (match->match == SR_TRIGGER_ZERO) {
- devc->trigger.simplevalue &= ~channelbit;
- devc->trigger.simplemask |= channelbit;
- }
- else if (match->match == SR_TRIGGER_FALLING) {
- devc->trigger.fallingmask |= channelbit;
- ++trigger_set;
- }
- else if (match->match == SR_TRIGGER_RISING) {
- devc->trigger.risingmask |= channelbit;
- ++trigger_set;
- }
-
- /*
- * Actually, Sigma supports 2 rising/falling triggers,
- * but they are ORed and the current trigger syntax
- * does not permit ORed triggers.
- */
- if (trigger_set > 1) {
- sr_err("Only 1 rising/falling trigger "
- "is supported.");
- return SR_ERR;
- }
- }
- }
- }
-
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- /* TODO */
- if (sdi->status == SR_ST_ACTIVE)
- ftdi_usb_close(&devc->ftdic);
-
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- return dev_clear();
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (!sdi)
- return SR_ERR;
- devc = sdi->priv;
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- *data = g_variant_new_uint64(devc->cur_samplerate);
- break;
- case SR_CONF_LIMIT_MSEC:
- *data = g_variant_new_uint64(devc->limit_msec);
- break;
- case SR_CONF_CAPTURE_RATIO:
- *data = g_variant_new_uint64(devc->capture_ratio);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- uint64_t tmp;
- int ret;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- ret = SR_OK;
- switch (id) {
- case SR_CONF_SAMPLERATE:
- ret = set_samplerate(sdi, g_variant_get_uint64(data));
- break;
- case SR_CONF_LIMIT_MSEC:
- tmp = g_variant_get_uint64(data);
- if (tmp > 0)
- devc->limit_msec = g_variant_get_uint64(data);
- else
- ret = SR_ERR;
- break;
- case SR_CONF_LIMIT_SAMPLES:
- tmp = g_variant_get_uint64(data);
- devc->limit_msec = tmp * 1000 / devc->cur_samplerate;
- break;
- case SR_CONF_CAPTURE_RATIO:
- tmp = g_variant_get_uint64(data);
- if (tmp <= 100)
- devc->capture_ratio = tmp;
- else
- ret = SR_ERR;
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- GVariant *gvar;
- GVariantBuilder gvb;
-
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
- ARRAY_SIZE(samplerates), sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TRIGGER_MATCH:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- trigger_matches, ARRAY_SIZE(trigger_matches),
- sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-/* Software trigger to determine exact trigger position. */
-static int get_trigger_offset(uint8_t *samples, uint16_t last_sample,
- struct sigma_trigger *t)
-{
- int i;
- uint16_t sample = 0;
-
- for (i = 0; i < 8; ++i) {
- if (i > 0)
- last_sample = sample;
- sample = samples[2 * i] | (samples[2 * i + 1] << 8);
-
- /* Simple triggers. */
- if ((sample & t->simplemask) != t->simplevalue)
- continue;
-
- /* Rising edge. */
- if (((last_sample & t->risingmask) != 0) ||
- ((sample & t->risingmask) != t->risingmask))
- continue;
-
- /* Falling edge. */
- if ((last_sample & t->fallingmask) != t->fallingmask ||
- (sample & t->fallingmask) != 0)
- continue;
-
- break;
- }
-
- /* If we did not match, return original trigger pos. */
- return i & 0x7;
-}
-
-
-/*
- * Return the timestamp of "DRAM cluster".
- */
-static uint16_t sigma_dram_cluster_ts(struct sigma_dram_cluster *cluster)
-{
- return (cluster->timestamp_hi << 8) | cluster->timestamp_lo;
-}
-
-static void sigma_decode_dram_cluster(struct sigma_dram_cluster *dram_cluster,
- unsigned int events_in_cluster,
- unsigned int triggered,
- struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
- struct sigma_state *ss = &devc->state;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- uint16_t tsdiff, ts;
- uint8_t samples[2048];
- unsigned int i;
-
- ts = sigma_dram_cluster_ts(dram_cluster);
- tsdiff = ts - ss->lastts;
- ss->lastts = ts;
-
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.unitsize = 2;
- logic.data = samples;
-
- /*
- * First of all, send Sigrok a copy of the last sample from
- * previous cluster as many times as needed to make up for
- * the differential characteristics of data we get from the
- * Sigma. Sigrok needs one sample of data per period.
- *
- * One DRAM cluster contains a timestamp and seven samples,
- * the units of timestamp are "devc->period_ps" , the first
- * sample in the cluster happens at the time of the timestamp
- * and the remaining samples happen at timestamp +1...+6 .
- */
- for (ts = 0; ts < tsdiff - (EVENTS_PER_CLUSTER - 1); ts++) {
- i = ts % 1024;
- samples[2 * i + 0] = ss->lastsample & 0xff;
- samples[2 * i + 1] = ss->lastsample >> 8;
-
- /*
- * If we have 1024 samples ready or we're at the
- * end of submitting the padding samples, submit
- * the packet to Sigrok.
- */
- if ((i == 1023) || (ts == (tsdiff - EVENTS_PER_CLUSTER))) {
- logic.length = (i + 1) * logic.unitsize;
- sr_session_send(sdi, &packet);
- }
- }
-
- /*
- * Parse the samples in current cluster and prepare them
- * to be submitted to Sigrok.
- */
- for (i = 0; i < events_in_cluster; i++) {
- samples[2 * i + 1] = dram_cluster->samples[i].sample_lo;
- samples[2 * i + 0] = dram_cluster->samples[i].sample_hi;
- }
-
- /* Send data up to trigger point (if triggered). */
- int trigger_offset = 0;
- if (triggered) {
- /*
- * Trigger is not always accurate to sample because of
- * pipeline delay. However, it always triggers before
- * the actual event. We therefore look at the next
- * samples to pinpoint the exact position of the trigger.
- */
- trigger_offset = get_trigger_offset(samples,
- ss->lastsample, &devc->trigger);
-
- if (trigger_offset > 0) {
- packet.type = SR_DF_LOGIC;
- logic.length = trigger_offset * logic.unitsize;
- sr_session_send(sdi, &packet);
- events_in_cluster -= trigger_offset;
- }
-
- /* Only send trigger if explicitly enabled. */
- if (devc->use_triggers) {
- packet.type = SR_DF_TRIGGER;
- sr_session_send(sdi, &packet);
- }
- }
-
- if (events_in_cluster > 0) {
- packet.type = SR_DF_LOGIC;
- logic.length = events_in_cluster * logic.unitsize;
- logic.data = samples + (trigger_offset * logic.unitsize);
- sr_session_send(sdi, &packet);
- }
-
- ss->lastsample =
- samples[2 * (events_in_cluster - 1) + 0] |
- (samples[2 * (events_in_cluster - 1) + 1] << 8);
-
-}
-
-/*
- * Decode chunk of 1024 bytes, 64 clusters, 7 events per cluster.
- * Each event is 20ns apart, and can contain multiple samples.
- *
- * For 200 MHz, events contain 4 samples for each channel, spread 5 ns apart.
- * For 100 MHz, events contain 2 samples for each channel, spread 10 ns apart.
- * For 50 MHz and below, events contain one sample for each channel,
- * spread 20 ns apart.
- */
-static int decode_chunk_ts(struct sigma_dram_line *dram_line,
- uint16_t events_in_line,
- uint32_t trigger_event,
- struct sr_dev_inst *sdi)
-{
- struct sigma_dram_cluster *dram_cluster;
- struct dev_context *devc = sdi->priv;
- unsigned int clusters_in_line =
- (events_in_line + (EVENTS_PER_CLUSTER - 1)) / EVENTS_PER_CLUSTER;
- unsigned int events_in_cluster;
- unsigned int i;
- uint32_t trigger_cluster = ~0, triggered = 0;
-
- /* Check if trigger is in this chunk. */
- if (trigger_event < (64 * 7)) {
- if (devc->cur_samplerate <= SR_MHZ(50)) {
- trigger_event -= MIN(EVENTS_PER_CLUSTER - 1,
- trigger_event);
- }
-
- /* Find in which cluster the trigger occured. */
- trigger_cluster = trigger_event / EVENTS_PER_CLUSTER;
- }
-
- /* For each full DRAM cluster. */
- for (i = 0; i < clusters_in_line; i++) {
- dram_cluster = &dram_line->cluster[i];
-
- /* The last cluster might not be full. */
- if ((i == clusters_in_line - 1) &&
- (events_in_line % EVENTS_PER_CLUSTER)) {
- events_in_cluster = events_in_line % EVENTS_PER_CLUSTER;
- } else {
- events_in_cluster = EVENTS_PER_CLUSTER;
- }
-
- triggered = (i == trigger_cluster);
- sigma_decode_dram_cluster(dram_cluster, events_in_cluster,
- triggered, sdi);
- }
-
- return SR_OK;
-}
-
-static int download_capture(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
- const uint32_t chunks_per_read = 32;
- struct sigma_dram_line *dram_line;
- int bufsz;
- uint32_t stoppos, triggerpos;
- struct sr_datafeed_packet packet;
- uint8_t modestatus;
-
- uint32_t i;
- uint32_t dl_lines_total, dl_lines_curr, dl_lines_done;
- uint32_t dl_events_in_line = 64 * 7;
- uint32_t trg_line = ~0, trg_event = ~0;
-
- dram_line = g_try_malloc0(chunks_per_read * sizeof(*dram_line));
- if (!dram_line)
- return FALSE;
-
- sr_info("Downloading sample data.");
-
- /* Stop acquisition. */
- sigma_set_register(WRITE_MODE, 0x11, devc);
-
- /* Set SDRAM Read Enable. */
- sigma_set_register(WRITE_MODE, 0x02, devc);
-
- /* Get the current position. */
- sigma_read_pos(&stoppos, &triggerpos, devc);
-
- /* Check if trigger has fired. */
- modestatus = sigma_get_register(READ_MODE, devc);
- if (modestatus & 0x20) {
- trg_line = triggerpos >> 9;
- trg_event = triggerpos & 0x1ff;
- }
-
- /*
- * Determine how many 1024b "DRAM lines" do we need to read from the
- * Sigma so we have a complete set of samples. Note that the last
- * line can be only partial, containing less than 64 clusters.
- */
- dl_lines_total = (stoppos >> 9) + 1;
-
- dl_lines_done = 0;
-
- while (dl_lines_total > dl_lines_done) {
- /* We can download only up-to 32 DRAM lines in one go! */
- dl_lines_curr = MIN(chunks_per_read, dl_lines_total);
-
- bufsz = sigma_read_dram(dl_lines_done, dl_lines_curr,
- (uint8_t *)dram_line, devc);
- /* TODO: Check bufsz. For now, just avoid compiler warnings. */
- (void)bufsz;
-
- /* This is the first DRAM line, so find the initial timestamp. */
- if (dl_lines_done == 0) {
- devc->state.lastts =
- sigma_dram_cluster_ts(&dram_line[0].cluster[0]);
- devc->state.lastsample = 0;
- }
-
- for (i = 0; i < dl_lines_curr; i++) {
- uint32_t trigger_event = ~0;
- /* The last "DRAM line" can be only partially full. */
- if (dl_lines_done + i == dl_lines_total - 1)
- dl_events_in_line = stoppos & 0x1ff;
-
- /* Test if the trigger happened on this line. */
- if (dl_lines_done + i == trg_line)
- trigger_event = trg_event;
-
- decode_chunk_ts(dram_line + i, dl_events_in_line,
- trigger_event, sdi);
- }
-
- dl_lines_done += dl_lines_curr;
- }
-
- /* All done. */
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-
- dev_acquisition_stop(sdi, sdi);
-
- g_free(dram_line);
-
- return TRUE;
-}
-
-/*
- * Handle the Sigma when in CAPTURE mode. This function checks:
- * - Sampling time ended
- * - DRAM capacity overflow
- * This function triggers download of the samples from Sigma
- * in case either of the above conditions is true.
- */
-static int sigma_capture_mode(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
-
- uint64_t running_msec;
- struct timeval tv;
-
- uint32_t stoppos, triggerpos;
-
- /* Check if the selected sampling duration passed. */
- gettimeofday(&tv, 0);
- running_msec = (tv.tv_sec - devc->start_tv.tv_sec) * 1000 +
- (tv.tv_usec - devc->start_tv.tv_usec) / 1000;
- if (running_msec >= devc->limit_msec)
- return download_capture(sdi);
-
- /* Get the position in DRAM to which the FPGA is writing now. */
- sigma_read_pos(&stoppos, &triggerpos, devc);
- /* Test if DRAM is full and if so, download the data. */
- if ((stoppos >> 9) == 32767)
- return download_capture(sdi);
-
- return TRUE;
-}
-
-static int receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
-
- (void)fd;
- (void)revents;
-
- sdi = cb_data;
- devc = sdi->priv;
-
- if (devc->state.state == SIGMA_IDLE)
- return TRUE;
-
- if (devc->state.state == SIGMA_CAPTURE)
- return sigma_capture_mode(sdi);
-
- return TRUE;
-}
-
-/* Build a LUT entry used by the trigger functions. */
-static void build_lut_entry(uint16_t value, uint16_t mask, uint16_t *entry)
-{
- int i, j, k, bit;
-
- /* For each quad channel. */
- for (i = 0; i < 4; ++i) {
- entry[i] = 0xffff;
-
- /* For each bit in LUT. */
- for (j = 0; j < 16; ++j)
-
- /* For each channel in quad. */
- for (k = 0; k < 4; ++k) {
- bit = 1 << (i * 4 + k);
-
- /* Set bit in entry */
- if ((mask & bit) &&
- ((!(value & bit)) !=
- (!(j & (1 << k)))))
- entry[i] &= ~(1 << j);
- }
- }
-}
-
-/* Add a logical function to LUT mask. */
-static void add_trigger_function(enum triggerop oper, enum triggerfunc func,
- int index, int neg, uint16_t *mask)
-{
- int i, j;
- int x[2][2], tmp, a, b, aset, bset, rset;
-
- memset(x, 0, 4 * sizeof(int));
-
- /* Trigger detect condition. */
- switch (oper) {
- case OP_LEVEL:
- x[0][1] = 1;
- x[1][1] = 1;
- break;
- case OP_NOT:
- x[0][0] = 1;
- x[1][0] = 1;
- break;
- case OP_RISE:
- x[0][1] = 1;
- break;
- case OP_FALL:
- x[1][0] = 1;
- break;
- case OP_RISEFALL:
- x[0][1] = 1;
- x[1][0] = 1;
- break;
- case OP_NOTRISE:
- x[1][1] = 1;
- x[0][0] = 1;
- x[1][0] = 1;
- break;
- case OP_NOTFALL:
- x[1][1] = 1;
- x[0][0] = 1;
- x[0][1] = 1;
- break;
- case OP_NOTRISEFALL:
- x[1][1] = 1;
- x[0][0] = 1;
- break;
- }
-
- /* Transpose if neg is set. */
- if (neg) {
- for (i = 0; i < 2; ++i) {
- for (j = 0; j < 2; ++j) {
- tmp = x[i][j];
- x[i][j] = x[1-i][1-j];
- x[1-i][1-j] = tmp;
- }
- }
- }
-
- /* Update mask with function. */
- for (i = 0; i < 16; ++i) {
- a = (i >> (2 * index + 0)) & 1;
- b = (i >> (2 * index + 1)) & 1;
-
- aset = (*mask >> i) & 1;
- bset = x[b][a];
-
- if (func == FUNC_AND || func == FUNC_NAND)
- rset = aset & bset;
- else if (func == FUNC_OR || func == FUNC_NOR)
- rset = aset | bset;
- else if (func == FUNC_XOR || func == FUNC_NXOR)
- rset = aset ^ bset;
-
- if (func == FUNC_NAND || func == FUNC_NOR || func == FUNC_NXOR)
- rset = !rset;
-
- *mask &= ~(1 << i);
-
- if (rset)
- *mask |= 1 << i;
- }
-}
-
-/*
- * Build trigger LUTs used by 50 MHz and lower sample rates for supporting
- * simple pin change and state triggers. Only two transitions (rise/fall) can be
- * set at any time, but a full mask and value can be set (0/1).
- */
-static int build_basic_trigger(struct triggerlut *lut, struct dev_context *devc)
-{
- int i,j;
- uint16_t masks[2] = { 0, 0 };
-
- memset(lut, 0, sizeof(struct triggerlut));
-
- /* Contant for simple triggers. */
- lut->m4 = 0xa000;
-
- /* Value/mask trigger support. */
- build_lut_entry(devc->trigger.simplevalue, devc->trigger.simplemask,
- lut->m2d);
-
- /* Rise/fall trigger support. */
- for (i = 0, j = 0; i < 16; ++i) {
- if (devc->trigger.risingmask & (1 << i) ||
- devc->trigger.fallingmask & (1 << i))
- masks[j++] = 1 << i;
- }
-
- build_lut_entry(masks[0], masks[0], lut->m0d);
- build_lut_entry(masks[1], masks[1], lut->m1d);
-
- /* Add glue logic */
- if (masks[0] || masks[1]) {
- /* Transition trigger. */
- if (masks[0] & devc->trigger.risingmask)
- add_trigger_function(OP_RISE, FUNC_OR, 0, 0, &lut->m3);
- if (masks[0] & devc->trigger.fallingmask)
- add_trigger_function(OP_FALL, FUNC_OR, 0, 0, &lut->m3);
- if (masks[1] & devc->trigger.risingmask)
- add_trigger_function(OP_RISE, FUNC_OR, 1, 0, &lut->m3);
- if (masks[1] & devc->trigger.fallingmask)
- add_trigger_function(OP_FALL, FUNC_OR, 1, 0, &lut->m3);
- } else {
- /* Only value/mask trigger. */
- lut->m3 = 0xffff;
- }
-
- /* Triggertype: event. */
- lut->params.selres = 3;
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct clockselect_50 clockselect;
- int frac, triggerpin, ret;
- uint8_t triggerselect = 0;
- struct triggerinout triggerinout_conf;
- struct triggerlut lut;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- if (convert_trigger(sdi) != SR_OK) {
- sr_err("Failed to configure triggers.");
- return SR_ERR;
- }
-
- /* If the samplerate has not been set, default to 200 kHz. */
- if (devc->cur_firmware == -1) {
- if ((ret = set_samplerate(sdi, SR_KHZ(200))) != SR_OK)
- return ret;
- }
-
- /* Enter trigger programming mode. */
- sigma_set_register(WRITE_TRIGGER_SELECT1, 0x20, devc);
-
- /* 100 and 200 MHz mode. */
- if (devc->cur_samplerate >= SR_MHZ(100)) {
- sigma_set_register(WRITE_TRIGGER_SELECT1, 0x81, devc);
-
- /* Find which pin to trigger on from mask. */
- for (triggerpin = 0; triggerpin < 8; ++triggerpin)
- if ((devc->trigger.risingmask | devc->trigger.fallingmask) &
- (1 << triggerpin))
- break;
-
- /* Set trigger pin and light LED on trigger. */
- triggerselect = (1 << LEDSEL1) | (triggerpin & 0x7);
-
- /* Default rising edge. */
- if (devc->trigger.fallingmask)
- triggerselect |= 1 << 3;
-
- /* All other modes. */
- } else if (devc->cur_samplerate <= SR_MHZ(50)) {
- build_basic_trigger(&lut, devc);
-
- sigma_write_trigger_lut(&lut, devc);
-
- triggerselect = (1 << LEDSEL1) | (1 << LEDSEL0);
- }
-
- /* Setup trigger in and out pins to default values. */
- memset(&triggerinout_conf, 0, sizeof(struct triggerinout));
- triggerinout_conf.trgout_bytrigger = 1;
- triggerinout_conf.trgout_enable = 1;
-
- sigma_write_register(WRITE_TRIGGER_OPTION,
- (uint8_t *) &triggerinout_conf,
- sizeof(struct triggerinout), devc);
-
- /* Go back to normal mode. */
- sigma_set_register(WRITE_TRIGGER_SELECT1, triggerselect, devc);
-
- /* Set clock select register. */
- if (devc->cur_samplerate == SR_MHZ(200))
- /* Enable 4 channels. */
- sigma_set_register(WRITE_CLOCK_SELECT, 0xf0, devc);
- else if (devc->cur_samplerate == SR_MHZ(100))
- /* Enable 8 channels. */
- sigma_set_register(WRITE_CLOCK_SELECT, 0x00, devc);
- else {
- /*
- * 50 MHz mode (or fraction thereof). Any fraction down to
- * 50 MHz / 256 can be used, but is not supported by sigrok API.
- */
- frac = SR_MHZ(50) / devc->cur_samplerate - 1;
-
- clockselect.async = 0;
- clockselect.fraction = frac;
- clockselect.disabled_channels = 0;
-
- sigma_write_register(WRITE_CLOCK_SELECT,
- (uint8_t *) &clockselect,
- sizeof(clockselect), devc);
- }
-
- /* Setup maximum post trigger time. */
- sigma_set_register(WRITE_POST_TRIGGER,
- (devc->capture_ratio * 255) / 100, devc);
-
- /* Start acqusition. */
- gettimeofday(&devc->start_tv, 0);
- sigma_set_register(WRITE_MODE, 0x0d, devc);
-
- devc->cb_data = cb_data;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(sdi, LOG_PREFIX);
-
- /* Add capture source. */
- sr_session_source_add(sdi->session, 0, G_IO_IN, 10, receive_data, (void *)sdi);
-
- devc->state.state = SIGMA_CAPTURE;
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
-
- (void)cb_data;
-
- devc = sdi->priv;
- devc->state.state = SIGMA_IDLE;
-
- sr_session_source_remove(sdi->session, 0);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver asix_sigma_driver_info = {
- .name = "asix-sigma",
- .longname = "ASIX SIGMA/SIGMA2",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = 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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010 HÃ¥vard Espeland <gus@ping.uio.no>,
- * Copyright (C) 2010 Martin Stensgård <mastensg@ping.uio.no>
- * Copyright (C) 2010 Carl Henrik Lunde <chlunde@ping.uio.no>
- *
- * 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_ASIX_SIGMA_ASIX_SIGMA_H
-#define LIBSIGROK_HARDWARE_ASIX_SIGMA_ASIX_SIGMA_H
-
-#define LOG_PREFIX "asix-sigma"
-
-enum sigma_write_register {
- WRITE_CLOCK_SELECT = 0,
- WRITE_TRIGGER_SELECT0 = 1,
- WRITE_TRIGGER_SELECT1 = 2,
- WRITE_MODE = 3,
- WRITE_MEMROW = 4,
- WRITE_POST_TRIGGER = 5,
- WRITE_TRIGGER_OPTION = 6,
- WRITE_PIN_VIEW = 7,
-
- WRITE_TEST = 15,
-};
-
-enum sigma_read_register {
- READ_ID = 0,
- READ_TRIGGER_POS_LOW = 1,
- READ_TRIGGER_POS_HIGH = 2,
- READ_TRIGGER_POS_UP = 3,
- READ_STOP_POS_LOW = 4,
- READ_STOP_POS_HIGH = 5,
- READ_STOP_POS_UP = 6,
- READ_MODE = 7,
- READ_PIN_CHANGE_LOW = 8,
- READ_PIN_CHANGE_HIGH = 9,
- READ_BLOCK_LAST_TS_LOW = 10,
- READ_BLOCK_LAST_TS_HIGH = 11,
- READ_PIN_VIEW = 12,
-
- READ_TEST = 15,
-};
-
-#define REG_ADDR_LOW (0x0 << 4)
-#define REG_ADDR_HIGH (0x1 << 4)
-#define REG_DATA_LOW (0x2 << 4)
-#define REG_DATA_HIGH_WRITE (0x3 << 4)
-#define REG_READ_ADDR (0x4 << 4)
-#define REG_DRAM_WAIT_ACK (0x5 << 4)
-
-/* Bit (1 << 4) can be low or high (double buffer / cache) */
-#define REG_DRAM_BLOCK (0x6 << 4)
-#define REG_DRAM_BLOCK_BEGIN (0x8 << 4)
-#define REG_DRAM_BLOCK_DATA (0xa << 4)
-
-#define LEDSEL0 6
-#define LEDSEL1 7
-
-#define NEXT_REG 1
-
-#define EVENTS_PER_CLUSTER 7
-
-#define CHUNK_SIZE 1024
-
-/*
- * The entire ASIX Sigma DRAM is an array of struct sigma_dram_line[1024];
- */
-
-/* One "DRAM cluster" contains a timestamp and 7 samples, 16b total. */
-struct sigma_dram_cluster {
- uint8_t timestamp_lo;
- uint8_t timestamp_hi;
- struct {
- uint8_t sample_hi;
- uint8_t sample_lo;
- } samples[7];
-};
-
-/* One "DRAM line" contains 64 "DRAM clusters", 1024b total. */
-struct sigma_dram_line {
- struct sigma_dram_cluster cluster[64];
-};
-
-struct clockselect_50 {
- uint8_t async;
- uint8_t fraction;
- uint16_t disabled_channels;
-};
-
-/* The effect of all these are still a bit unclear. */
-struct triggerinout {
- uint8_t trgout_resistor_enable : 1;
- uint8_t trgout_resistor_pullup : 1;
- uint8_t reserved1 : 1;
- uint8_t trgout_bytrigger : 1;
- uint8_t trgout_byevent : 1;
- uint8_t trgout_bytriggerin : 1;
- uint8_t reserved2 : 2;
-
- /* Should be set same as the first two */
- uint8_t trgout_resistor_enable2 : 1;
- uint8_t trgout_resistor_pullup2 : 1;
-
- uint8_t reserved3 : 1;
- uint8_t trgout_long : 1;
- uint8_t trgout_pin : 1; /* Use 1k resistor. Pullup? */
- uint8_t trgin_negate : 1;
- uint8_t trgout_enable : 1;
- uint8_t trgin_enable : 1;
-};
-
-struct triggerlut {
- /* The actual LUTs. */
- uint16_t m0d[4], m1d[4], m2d[4];
- uint16_t m3, m3s, m4;
-
- /* Paramters should be sent as a single register write. */
- struct {
- uint8_t selc : 2;
- uint8_t selpresc : 6;
-
- uint8_t selinc : 2;
- uint8_t selres : 2;
- uint8_t sela : 2;
- uint8_t selb : 2;
-
- uint16_t cmpb;
- uint16_t cmpa;
- } params;
-};
-
-/* Trigger configuration */
-struct sigma_trigger {
- /* Only two channels can be used in mask. */
- uint16_t risingmask;
- uint16_t fallingmask;
-
- /* Simple trigger support (<= 50 MHz). */
- uint16_t simplemask;
- uint16_t simplevalue;
-
- /* TODO: Advanced trigger support (boolean expressions). */
-};
-
-/* Events for trigger operation. */
-enum triggerop {
- OP_LEVEL = 1,
- OP_NOT,
- OP_RISE,
- OP_FALL,
- OP_RISEFALL,
- OP_NOTRISE,
- OP_NOTFALL,
- OP_NOTRISEFALL,
-};
-
-/* Logical functions for trigger operation. */
-enum triggerfunc {
- FUNC_AND = 1,
- FUNC_NAND,
- FUNC_OR,
- FUNC_NOR,
- FUNC_XOR,
- FUNC_NXOR,
-};
-
-struct sigma_state {
- enum {
- SIGMA_UNINITIALIZED = 0,
- SIGMA_IDLE,
- SIGMA_CAPTURE,
- SIGMA_DOWNLOAD,
- } state;
-
- uint16_t lastts;
- uint16_t lastsample;
-};
-
-/* Private, per-device-instance driver context. */
-struct dev_context {
- struct ftdi_context ftdic;
- uint64_t cur_samplerate;
- uint64_t period_ps;
- uint64_t limit_msec;
- struct timeval start_tv;
- int cur_firmware;
- int num_channels;
- int cur_channels;
- int samples_per_event;
- int capture_ratio;
- struct sigma_trigger trigger;
- int use_triggers;
- struct sigma_state state;
- void *cb_data;
-};
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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 <string.h>
-#include <errno.h>
-#include "protocol.h"
-
-/*
- * The default serial communication settings on the device are 9600
- * baud, 9 data bits. The 9th bit isn't actually used, and the vendor
- * software uses Mark parity to absorb the extra bit.
- *
- * Since 9 data bits is not a standard available in POSIX, we use two
- * stop bits to skip over the extra bit instead.
- */
-#define SERIALCOMM "9600/8n2"
-
-static const int32_t scanopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t devopts[] = {
- SR_CONF_POWER_SUPPLY,
- SR_CONF_CONTINUOUS,
- SR_CONF_OUTPUT_CHANNEL,
- SR_CONF_OVER_CURRENT_PROTECTION,
-};
-
-static const int32_t devopts_cg[] = {
- SR_CONF_OUTPUT_VOLTAGE,
- SR_CONF_OUTPUT_VOLTAGE_MAX,
- SR_CONF_OUTPUT_CURRENT,
- SR_CONF_OUTPUT_CURRENT_MAX,
- SR_CONF_OUTPUT_ENABLED,
-};
-
-static const char *channel_modes[] = {
- "Independent",
- "Series",
- "Parallel",
-};
-
-static struct pps_model models[] = {
- { PPS_3203T_3S, "PPS3203T-3S",
- CHANMODE_INDEPENDENT | CHANMODE_SERIES | CHANMODE_PARALLEL,
- 3,
- {
- /* Channel 1 */
- { { 0, 32, 0.01 }, { 0, 3, 0.001 } },
- /* Channel 2 */
- { { 0, 32, 0.01 }, { 0, 3, 0.001 } },
- /* Channel 3 */
- { { 0, 6, 0.01 }, { 0, 3, 0.001 } },
- },
- },
-};
-
-
-SR_PRIV struct sr_dev_driver atten_pps3203_driver_info;
-static struct sr_dev_driver *di = &atten_pps3203_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options, int modelid)
-{
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_config *src;
- struct sr_channel *ch;
- struct sr_channel_group *cg;
- struct sr_serial_dev_inst *serial;
- GSList *l, *devices;
- struct pps_model *model;
- uint8_t packet[PACKET_SIZE];
- unsigned int i;
- int ret;
- const char *conn, *serialcomm;
- char channel[10];
-
- devices = NULL;
- drvc = di->priv;
- drvc->instances = NULL;
-
- 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 (!conn)
- return NULL;
- if (!serialcomm)
- serialcomm = SERIALCOMM;
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
- serial_flush(serial);
-
- /* This is how the vendor software channels for hardware. */
- memset(packet, 0, PACKET_SIZE);
- packet[0] = 0xaa;
- packet[1] = 0xaa;
- if (serial_write(serial, packet, PACKET_SIZE) == -1) {
- sr_err("Unable to write while probing for hardware: %s",
- strerror(errno));
- return NULL;
- }
- /* The device responds with a 24-byte packet when it receives a packet.
- * At 9600 baud, 300ms is long enough for it to have arrived. */
- g_usleep(300 * 1000);
- memset(packet, 0, PACKET_SIZE);
- if ((ret = serial_read_nonblocking(serial, packet, PACKET_SIZE)) < 0) {
- sr_err("Unable to read while probing for hardware: %s",
- strerror(errno));
- return NULL;
- }
- if (ret != PACKET_SIZE || packet[0] != 0xaa || packet[1] != 0xaa) {
- /* Doesn't look like an Atten PPS. */
- return NULL;
- }
-
- model = NULL;
- for (i = 0; i < ARRAY_SIZE(models); i++) {
- if (models[i].modelid == modelid) {
- model = &models[i];
- break;
- }
- }
- if (!model) {
- sr_err("Unknown modelid %d", modelid);
- return NULL;
- }
-
- sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Atten", model->name, NULL);
- sdi->driver = di;
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
- for (i = 0; i < MAX_CHANNELS; i++) {
- snprintf(channel, 10, "CH%d", i + 1);
- ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, channel);
- sdi->channels = g_slist_append(sdi->channels, ch);
- cg = g_malloc(sizeof(struct sr_channel_group));
- cg->name = g_strdup(channel);
- cg->channels = g_slist_append(NULL, ch);
- cg->priv = NULL;
- sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
- }
-
- devc = g_malloc0(sizeof(struct dev_context));
- devc->model = model;
- devc->config = g_malloc0(sizeof(struct per_channel_config) * model->num_channels);
- sdi->priv = devc;
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- serial_close(serial);
- if (!devices)
- sr_serial_dev_inst_free(serial);
-
- return devices;
-}
-
-static GSList *scan_3203(GSList *options)
-{
- return scan(options, PPS_3203T_3S);
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- int channel, ret;
-
- if (!sdi)
- return SR_ERR_ARG;
-
- devc = sdi->priv;
-
- ret = SR_OK;
- if (!cg) {
- /* No channel group: global options. */
- switch (key) {
- case SR_CONF_OUTPUT_CHANNEL:
- *data = g_variant_new_string(channel_modes[devc->channel_mode]);
- break;
- case SR_CONF_OVER_CURRENT_PROTECTION:
- *data = g_variant_new_boolean(devc->over_current_protection);
- break;
- default:
- return SR_ERR_NA;
- }
- } else {
- /* We only ever have one channel per channel group in this driver. */
- ch = cg->channels->data;
- channel = ch->index;
-
- switch (key) {
- case SR_CONF_OUTPUT_VOLTAGE:
- *data = g_variant_new_double(devc->config[channel].output_voltage_last);
- break;
- case SR_CONF_OUTPUT_VOLTAGE_MAX:
- *data = g_variant_new_double(devc->config[channel].output_voltage_max);
- break;
- case SR_CONF_OUTPUT_CURRENT:
- *data = g_variant_new_double(devc->config[channel].output_current_last);
- break;
- case SR_CONF_OUTPUT_CURRENT_MAX:
- *data = g_variant_new_double(devc->config[channel].output_current_max);
- break;
- case SR_CONF_OUTPUT_ENABLED:
- *data = g_variant_new_boolean(devc->config[channel].output_enabled);
- break;
- default:
- return SR_ERR_NA;
- }
- }
-
- return ret;
-}
-
-static int find_str(const char *str, const char **strings, int array_size)
-{
- int idx, i;
-
- idx = -1;
- for (i = 0; i < array_size; i++) {
- if (!strcmp(str, strings[i])) {
- idx = i;
- break;
- }
- }
-
- return idx;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- gdouble dval;
- int channel, ret, ival;
- const char *sval;
- gboolean bval;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- ret = SR_OK;
- devc = sdi->priv;
- if (!cg) {
- /* No channel group: global options. */
- switch (key) {
- case SR_CONF_OUTPUT_CHANNEL:
- sval = g_variant_get_string(data, NULL);
- if ((ival = find_str(sval, channel_modes,
- ARRAY_SIZE(channel_modes))) == -1) {
- ret = SR_ERR_ARG;
- break;
- }
- if (devc->model->channel_modes && (1 << ival) == 0) {
- /* Not supported on this model. */
- ret = SR_ERR_ARG;
- }
- if (ival == devc->channel_mode_set)
- /* Nothing to do. */
- break;
- devc->channel_mode_set = ival;
- devc->config_dirty = TRUE;
- break;
- case SR_CONF_OVER_CURRENT_PROTECTION:
- bval = g_variant_get_boolean(data);
- if (bval == devc->over_current_protection_set)
- /* Nothing to do. */
- break;
- devc->over_current_protection_set = bval;
- devc->config_dirty = TRUE;
- break;
- default:
- return SR_ERR_NA;
- }
- } else {
- /* Channel group specified: per-channel options. */
- /* We only ever have one channel per channel group in this driver. */
- ch = cg->channels->data;
- channel = ch->index;
-
- switch (key) {
- case SR_CONF_OUTPUT_VOLTAGE_MAX:
- dval = g_variant_get_double(data);
- if (dval < 0 || dval > devc->model->channels[channel].voltage[1])
- ret = SR_ERR_ARG;
- devc->config[channel].output_voltage_max = dval;
- devc->config_dirty = TRUE;
- break;
- case SR_CONF_OUTPUT_CURRENT_MAX:
- dval = g_variant_get_double(data);
- if (dval < 0 || dval > devc->model->channels[channel].current[1])
- ret = SR_ERR_ARG;
- devc->config[channel].output_current_max = dval;
- devc->config_dirty = TRUE;
- break;
- case SR_CONF_OUTPUT_ENABLED:
- bval = g_variant_get_boolean(data);
- if (bval == devc->config[channel].output_enabled_set)
- /* Nothing to do. */
- break;
- devc->config[channel].output_enabled_set = bval;
- devc->config_dirty = TRUE;
- break;
- default:
- ret = SR_ERR_NA;
- }
- }
-
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- GVariant *gvar;
- GVariantBuilder gvb;
- int channel, ret, i;
-
- /* Always available, even without sdi. */
- if (key == SR_CONF_SCAN_OPTIONS) {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- scanopts, ARRAY_SIZE(scanopts), sizeof(int32_t));
- return SR_OK;
- }
-
- if (!sdi)
- return SR_ERR_ARG;
- devc = sdi->priv;
-
- ret = SR_OK;
- if (!cg) {
- /* No channel group: global options. */
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
- break;
- case SR_CONF_OUTPUT_CHANNEL:
- if (devc->model->channel_modes == CHANMODE_INDEPENDENT) {
- /* The 1-channel models. */
- *data = g_variant_new_strv(channel_modes, 1);
- } else {
- /* The other models support all modes. */
- *data = g_variant_new_strv(channel_modes, ARRAY_SIZE(channel_modes));
- }
- break;
- default:
- return SR_ERR_NA;
- }
- } else {
- /* Channel group specified: per-channel options. */
- if (!sdi)
- return SR_ERR_ARG;
- /* We only ever have one channel per channel group in this driver. */
- ch = cg->channels->data;
- channel = ch->index;
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(int32_t));
- break;
- case SR_CONF_OUTPUT_VOLTAGE_MAX:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- /* Min, max, step. */
- for (i = 0; i < 3; i++) {
- gvar = g_variant_new_double(devc->model->channels[channel].voltage[i]);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_OUTPUT_CURRENT_MAX:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- /* Min, max, step. */
- for (i = 0; i < 3; i++) {
- gvar = g_variant_new_double(devc->model->channels[channel].current[i]);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- default:
- return SR_ERR_NA;
- }
- }
-
- return ret;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
- if (devc->config_dirty)
- /* Some configuration changes were queued up but didn't
- * get sent to the device, likely because we were never
- * in acquisition mode. Send them out now. */
- send_config(sdi);
-
- return std_serial_dev_close(sdi);
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- uint8_t packet[PACKET_SIZE];
-
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- memset(devc->packet, 0x44, PACKET_SIZE);
- devc->packet_size = 0;
-
- devc->acquisition_running = TRUE;
-
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 50,
- atten_pps3xxx_receive_data, (void *)sdi);
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Send a "channel" configuration packet now. */
- memset(packet, 0, PACKET_SIZE);
- packet[0] = 0xaa;
- packet[1] = 0xaa;
- send_packet(sdi, packet);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
-
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- devc->acquisition_running = FALSE;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver atten_pps3203_driver_info = {
- .name = "atten-pps3203",
- .longname = "Atten PPS3203T-3S",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan_3203,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .config_get = config_get,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = std_serial_dev_open,
- .dev_close = dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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 <string.h>
-#include <errno.h>
-#include "protocol.h"
-
-static void dump_packet(char *msg, uint8_t *packet)
-{
- int i;
- char str[128];
-
- str[0] = 0;
- for (i = 0; i < PACKET_SIZE; i++)
- sprintf(str + strlen(str), "%.2x ", packet[i]);
- sr_dbg("%s: %s", msg, str);
-
-}
-
-static void handle_packet(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- float value, data[MAX_CHANNELS];
- int offset, i;
-
- devc = sdi->priv;
- dump_packet("received", devc->packet);
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- analog.channels = sdi->channels;
- analog.num_samples = 1;
-
- analog.mq = SR_MQ_VOLTAGE;
- analog.unit = SR_UNIT_VOLT;
- analog.mqflags = SR_MQFLAG_DC;
- analog.data = data;
- for (i = 0; i < devc->model->num_channels; i++) {
- offset = 2 + i * 4;
- value = ((devc->packet[offset] << 8) + devc->packet[offset + 1]) / 100.0;
- analog.data[i] = value;
- devc->config[i].output_voltage_last = value;
- }
- sr_session_send(sdi, &packet);
-
- analog.mq = SR_MQ_CURRENT;
- analog.unit = SR_UNIT_AMPERE;
- analog.mqflags = 0;
- analog.data = data;
- for (i = 0; i < devc->model->num_channels; i++) {
- offset = 4 + i * 4;
- value = ((devc->packet[offset] << 8) + devc->packet[offset + 1]) / 1000.0;
- analog.data[i] = value;
- devc->config[i].output_current_last = value;
- }
- sr_session_send(sdi, &packet);
-
- for (i = 0; i < devc->model->num_channels; i++)
- devc->config[i].output_enabled = (devc->packet[15] & (1 << i)) ? TRUE : FALSE;
-
- devc->over_current_protection = devc->packet[18] ? TRUE : FALSE;
- if (devc->packet[19] < 3)
- devc->channel_mode = devc->packet[19];
-
-}
-
-SR_PRIV void send_packet(const struct sr_dev_inst *sdi, uint8_t *packet)
-{
- struct sr_serial_dev_inst *serial;
-
- serial = sdi->conn;
- if (serial_write(serial, packet, PACKET_SIZE) == -1)
- sr_dbg("Failed to send packet: %s", strerror(errno));
- dump_packet("sent", packet);
-}
-
-SR_PRIV void send_config(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- uint8_t packet[PACKET_SIZE];
- int value, offset, i;
-
- devc = sdi->priv;
- memset(packet, 0, PACKET_SIZE);
- packet[0] = 0xaa;
- packet[1] = 0x20;
- packet[14] = 0x01;
- packet[16] = 0x01;
- for (i = 0; i < devc->model->num_channels; i++) {
- offset = 2 + i * 4;
- value = devc->config[i].output_voltage_max * 100;
- packet[offset] = (value >> 8) & 0xff;
- packet[offset + 1] = value & 0xff;
- value = devc->config[i].output_current_max * 1000;
- packet[offset + 2] = (value >> 8) & 0xff;
- packet[offset + 3] = value & 0xff;
- if (devc->config[i].output_enabled_set)
- packet[15] |= 1 << i;
- }
- packet[18] = devc->over_current_protection_set ? 1 : 0;
- packet[19] = devc->channel_mode_set;
- /* Checksum. */
- value = 0;
- for (i = 0; i < PACKET_SIZE - 1; i++)
- value += packet[i];
- packet[i] = value & 0xff;
- send_packet(sdi, packet);
- devc->config_dirty = FALSE;
-
-}
-
-SR_PRIV int atten_pps3xxx_receive_data(int fd, int revents, void *cb_data)
-{
- struct dev_context *devc;
- const struct sr_dev_inst *sdi;
- struct sr_serial_dev_inst *serial;
- struct sr_datafeed_packet packet;
- unsigned char c;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- serial = sdi->conn;
- if (revents == G_IO_IN) {
- if (serial_read_nonblocking(serial, &c, 1) < 0)
- return TRUE;
- devc->packet[devc->packet_size++] = c;
- if (devc->packet_size == PACKET_SIZE) {
- handle_packet(sdi);
- devc->packet_size = 0;
- if (devc->acquisition_running)
- send_config(sdi);
- else {
- serial_source_remove(sdi->session, serial);
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
- }
- }
- }
-
- return TRUE;
-}
-
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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_ATTEN_PPS3XXX_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_ATTEN_PPS3XXX_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "atten-pps3xxx"
-
-/* Packets to/from the device. */
-#define PACKET_SIZE 24
-
-enum {
- PPS_3203T_3S,
- PPS_3203T_2S,
- PPS_3205T_3S,
- PPS_3205T_2S,
- PPS_3003S,
- PPS_3005S,
-};
-
-/* Maximum number of output channels handled by this driver. */
-#define MAX_CHANNELS 3
-
-#define CHANMODE_INDEPENDENT 1 << 0
-#define CHANMODE_SERIES 1 << 1
-#define CHANMODE_PARALLEL 1 << 2
-
-struct channel_spec {
- /* Min, max, step. */
- gdouble voltage[3];
- gdouble current[3];
-};
-
-struct pps_model {
- int modelid;
- char *name;
- int channel_modes;
- int num_channels;
- struct channel_spec channels[MAX_CHANNELS];
-};
-
-struct per_channel_config {
- /* Received from device. */
- gdouble output_voltage_last;
- gdouble output_current_last;
- gboolean output_enabled;
- /* Set by frontend. */
- gdouble output_voltage_max;
- gdouble output_current_max;
- gboolean output_enabled_set;
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Model-specific information */
- struct pps_model *model;
-
- /* Acquisition state */
- gboolean acquisition_running;
-
- /* Operational state */
- gboolean config_dirty;
- struct per_channel_config *config;
- /* Received from device. */
- int channel_mode;
- gboolean over_current_protection;
- /* Set by frontend. */
- int channel_mode_set;
- gboolean over_current_protection_set;
-
- /* Temporary state across callbacks */
- uint8_t packet[PACKET_SIZE];
- int packet_size;
-
-};
-
-SR_PRIV int atten_pps3xxx_receive_data(int fd, int revents, void *cb_data);
-SR_PRIV void send_packet(const struct sr_dev_inst *sdi, uint8_t *packet);
-SR_PRIV void send_config(const struct sr_dev_inst *sdi);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.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 "protocol.h"
-#include "beaglelogic.h"
-
-SR_PRIV struct sr_dev_driver beaglelogic_driver_info;
-static struct sr_dev_driver *di = &beaglelogic_driver_info;
-
-/* Hardware capabiities */
-static const int32_t hwcaps[] = {
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_SAMPLERATE,
- SR_CONF_TRIGGER_MATCH,
-
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_CONTINUOUS,
-
- SR_CONF_NUM_LOGIC_CHANNELS,
-};
-
-/* Trigger matching capabilities */
-static const int32_t soft_trigger_matches[] = {
- SR_TRIGGER_ZERO,
- SR_TRIGGER_ONE,
- SR_TRIGGER_RISING,
- SR_TRIGGER_FALLING,
- SR_TRIGGER_EDGE,
-};
-
-/* Channels are numbered 0-13 */
-SR_PRIV const char *beaglelogic_channel_names[NUM_CHANNELS + 1] = {
- "P8_45", "P8_46", "P8_43", "P8_44", "P8_41", "P8_42", "P8_39", "P8_40",
- "P8_27", "P8_29", "P8_28", "P8_30", "P8_21", "P8_20", NULL,
-};
-
-/* Possible sample rates : 10 Hz to 100 MHz = (100 / x) MHz */
-static const uint64_t samplerates[] = {
- SR_HZ(10),
- SR_MHZ(100),
- SR_HZ(1),
-};
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static struct dev_context * beaglelogic_devc_alloc(void)
-{
- struct dev_context *devc;
-
- /* Allocate zeroed structure */
- devc = g_try_malloc0(sizeof(*devc));
-
- /* Default non-zero values (if any) */
- devc->fd = -1;
- devc->limit_samples = (uint64_t)-1;
-
- return devc;
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- GSList *devices, *l;
- struct sr_config *src;
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_channel *ch;
- int i, maxch;
-
- devices = NULL;
- drvc = di->priv;
- drvc->instances = NULL;
-
- /* Probe for /dev/beaglelogic */
- if (!g_file_test(BEAGLELOGIC_DEV_NODE, G_FILE_TEST_EXISTS))
- return NULL;
-
- sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, NULL, "BeagleLogic", "1.0");
- sdi->driver = di;
-
- /* Unless explicitly specified, keep max channels to 8 only */
- maxch = 8;
- for (l = options; l; l = l->next) {
- src = l->data;
- if (src->key == SR_CONF_NUM_LOGIC_CHANNELS)
- maxch = g_variant_get_int32(src->data);
- }
-
- /* We need to test for number of channels by opening the node */
- devc = beaglelogic_devc_alloc();
-
- if (beaglelogic_open_nonblock(devc) != SR_OK) {
- g_free(devc);
- sr_dev_inst_free(sdi);
-
- return NULL;
- }
-
- if (maxch > 8) {
- maxch = NUM_CHANNELS;
- devc->sampleunit = BL_SAMPLEUNIT_16_BITS;
- } else {
- maxch = 8;
- devc->sampleunit = BL_SAMPLEUNIT_8_BITS;
- }
-
- beaglelogic_set_sampleunit(devc);
- beaglelogic_close(devc);
-
- /* Signal */
- sr_info("BeagleLogic device found at "BEAGLELOGIC_DEV_NODE);
-
- /* Fill the channels */
- for (i = 0; i < maxch; i++) {
- if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
- beaglelogic_channel_names[i])))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- sdi->priv = devc;
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_clear(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
-
- /* Open BeagleLogic */
- if (beaglelogic_open_nonblock(devc))
- return SR_ERR;
-
- /* Set fd and local attributes */
- devc->pollfd.fd = devc->fd;
- devc->pollfd.events = G_IO_IN;
-
- /* Get the default attributes */
- beaglelogic_get_samplerate(devc);
- beaglelogic_get_sampleunit(devc);
- beaglelogic_get_triggerflags(devc);
- beaglelogic_get_buffersize(devc);
- beaglelogic_get_bufunitsize(devc);
-
- /* Map the kernel capture FIFO for reads, saves 1 level of memcpy */
- if (beaglelogic_mmap(devc) != SR_OK) {
- sr_err("Unable to map capture buffer");
- beaglelogic_close(devc);
- return SR_ERR;
- }
-
- /* We're good to go now */
- sdi->status = SR_ST_ACTIVE;
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
-
- if (sdi->status == SR_ST_ACTIVE) {
- /* Close the memory mapping and the file */
- beaglelogic_munmap(devc);
- beaglelogic_close(devc);
- }
- sdi->status = SR_ST_INACTIVE;
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- struct drv_context *drvc;
- struct sr_dev_inst *sdi;
- GSList *l;
-
- /* unused driver */
- if (!(drvc = di->priv))
- return SR_OK;
-
- /* Clean up the instances */
- for (l = drvc->instances; l; l = l->next) {
- sdi = l->data;
- di->dev_close(sdi);
- g_free(sdi->priv);
- sr_dev_inst_free(sdi);
- }
- g_slist_free(drvc->instances);
- drvc->instances = NULL;
-
- di->priv = NULL;
-
- return SR_OK;
-}
-
-static int config_get(int 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_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
-
- case SR_CONF_SAMPLERATE:
- *data = g_variant_new_uint64(devc->cur_samplerate);
- break;
-
- case SR_CONF_NUM_LOGIC_CHANNELS:
- *data = g_variant_new_uint32(g_slist_length(sdi->channels));
- break;
-
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc = sdi->priv;
- uint64_t tmp_u64;
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- switch (key) {
- case SR_CONF_SAMPLERATE:
- devc->cur_samplerate = g_variant_get_uint64(data);
- return beaglelogic_set_samplerate(devc);
-
- case SR_CONF_LIMIT_SAMPLES:
- tmp_u64 = g_variant_get_uint64(data);
- devc->limit_samples = tmp_u64;
- devc->triggerflags = BL_TRIGGERFLAGS_ONESHOT;
-
- /* Check if we have sufficient buffer size */
- tmp_u64 *= SAMPLEUNIT_TO_BYTES(devc->sampleunit);
- if (tmp_u64 > devc->buffersize) {
- sr_warn("Insufficient buffer space has been allocated.");
- sr_warn("Please use \'echo <size in bytes> > "\
- BEAGLELOGIC_SYSFS_ATTR(memalloc) \
- "\' as root to increase the buffer size, this"\
- " capture is now truncated to %d Msamples",
- devc->buffersize /
- (SAMPLEUNIT_TO_BYTES(devc->sampleunit) * 1000000));
- }
- return beaglelogic_set_triggerflags(devc);
-
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- int ret;
- GVariant *gvar;
- GVariantBuilder gvb;
-
- (void)sdi;
- (void)data;
- (void)cg;
-
- ret = SR_OK;
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- samplerates, ARRAY_SIZE(samplerates), sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TRIGGER_MATCH:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- soft_trigger_matches, ARRAY_SIZE(soft_trigger_matches),
- sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return ret;
-}
-
-/* get a sane timeout for poll() */
-#define BUFUNIT_TIMEOUT_MS(devc) (100 + ((devc->bufunitsize * 1000) / \
- (uint32_t)(devc->cur_samplerate)))
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- (void)cb_data;
- struct dev_context *devc = sdi->priv;
- struct sr_trigger *trigger;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- /* Save user pointer */
- devc->cb_data = cb_data;
-
- /* Clear capture state */
- devc->bytes_read = 0;
- devc->offset = 0;
-
- /* Configure channels */
- devc->sampleunit = g_slist_length(sdi->channels) > 8 ?
- BL_SAMPLEUNIT_16_BITS : BL_SAMPLEUNIT_8_BITS;
- beaglelogic_set_sampleunit(devc);
-
- /* Configure triggers & send header packet */
- if ((trigger = sr_session_trigger_get(sdi->session))) {
- devc->stl = soft_trigger_logic_new(sdi, trigger);
- devc->trigger_fired = FALSE;
- } else
- devc->trigger_fired = TRUE;
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Trigger and add poll on file */
- beaglelogic_start(devc);
- sr_session_source_add_pollfd(sdi->session, &devc->pollfd,
- BUFUNIT_TIMEOUT_MS(devc), beaglelogic_receive_data,
- (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc = sdi->priv;
- struct sr_datafeed_packet pkt;
-
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- /* Execute a stop on BeagleLogic */
- beaglelogic_stop(devc);
-
- /* lseek to offset 0, flushes the cache */
- lseek(devc->fd, 0, SEEK_SET);
-
- /* Remove session source and send EOT packet */
- sr_session_source_remove_pollfd(sdi->session, &devc->pollfd);
- pkt.type = SR_DF_END;
- pkt.payload = NULL;
- sr_session_send(sdi, &pkt);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver beaglelogic_driver_info = {
- .name = "beaglelogic",
- .longname = "BeagleLogic",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = 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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.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 BEAGLELOGIC_H_
-#define BEAGLELOGIC_H_
-
-#include <fcntl.h>
-
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/errno.h>
-#include <sys/ioctl.h>
-
-#include <stdlib.h>
-
-#include <unistd.h>
-
-/* BeagleLogic device node name */
-#define BEAGLELOGIC_DEV_NODE "/dev/beaglelogic"
-#define BEAGLELOGIC_SYSFS_ATTR(a) "/sys/devices/virtual/misc/beaglelogic/"\
- __STRING(a)
-
-/* Reproduced verbatim from beaglelogic.h in the kernel tree until the kernel
- * module hits the mainline. Contains the ABI, so DO NOT TOUCH this section */
-
-/* ioctl calls that can be issued on /dev/beaglelogic */
-#define IOCTL_BL_GET_VERSION _IOR('k', 0x20, uint32_t)
-
-#define IOCTL_BL_GET_SAMPLE_RATE _IOR('k', 0x21, uint32_t)
-#define IOCTL_BL_SET_SAMPLE_RATE _IOW('k', 0x21, uint32_t)
-
-#define IOCTL_BL_GET_SAMPLE_UNIT _IOR('k', 0x22, uint32_t)
-#define IOCTL_BL_SET_SAMPLE_UNIT _IOW('k', 0x22, uint32_t)
-
-#define IOCTL_BL_GET_TRIGGER_FLAGS _IOR('k', 0x23, uint32_t)
-#define IOCTL_BL_SET_TRIGGER_FLAGS _IOW('k', 0x23, uint32_t)
-
-#define IOCTL_BL_CACHE_INVALIDATE _IO('k', 0x25)
-
-#define IOCTL_BL_GET_BUFFER_SIZE _IOR('k', 0x26, uint32_t)
-#define IOCTL_BL_SET_BUFFER_SIZE _IOW('k', 0x26, uint32_t)
-
-#define IOCTL_BL_GET_BUFUNIT_SIZE _IOR('k', 0x27, uint32_t)
-
-#define IOCTL_BL_FILL_TEST_PATTERN _IO('k', 0x28)
-
-#define IOCTL_BL_START _IO('k', 0x29)
-#define IOCTL_BL_STOP _IO('k', 0x2A)
-
-/* Possible States of BeagleLogic */
-enum beaglelogic_states {
- STATE_BL_DISABLED, /* Powered off (at module start) */
- STATE_BL_INITIALIZED, /* Powered on */
- STATE_BL_MEMALLOCD, /* Buffers allocated */
- STATE_BL_ARMED, /* All Buffers DMA-mapped and configuration done */
- STATE_BL_RUNNING, /* Data being captured */
- STATE_BL_REQUEST_STOP, /* Stop requested */
- STATE_BL_ERROR /* Buffer overrun */
-};
-
-/* Setting attributes */
-enum beaglelogic_triggerflags {
- BL_TRIGGERFLAGS_ONESHOT = 0,
- BL_TRIGGERFLAGS_CONTINUOUS
-};
-
-/* Possible sample unit / formats */
-enum beaglelogic_sampleunit {
- BL_SAMPLEUNIT_16_BITS = 0,
- BL_SAMPLEUNIT_8_BITS
-};
-/* END beaglelogic.h */
-
-/* For all the functions below:
- * Parameters:
- * devc : Device context structure to operate on
- * Returns:
- * SR_OK or SR_ERR
- */
-
-SR_PRIV int beaglelogic_open_nonblock(struct dev_context *devc);
-SR_PRIV int beaglelogic_close(struct dev_context *devc);
-
-SR_PRIV int beaglelogic_get_buffersize(struct dev_context *devc);
-SR_PRIV int beaglelogic_set_buffersize(struct dev_context *devc);
-
-SR_PRIV int beaglelogic_get_samplerate(struct dev_context *devc);
-SR_PRIV int beaglelogic_set_samplerate(struct dev_context *devc);
-
-SR_PRIV int beaglelogic_get_sampleunit(struct dev_context *devc);
-SR_PRIV int beaglelogic_set_sampleunit(struct dev_context *devc);
-
-SR_PRIV int beaglelogic_get_triggerflags(struct dev_context *devc);
-SR_PRIV int beaglelogic_set_triggerflags(struct dev_context *devc);
-
-/* Start and stop the capture operation */
-SR_PRIV int beaglelogic_start(struct dev_context *devc);
-SR_PRIV int beaglelogic_stop(struct dev_context *devc);
-
-/* Get the last error size */
-SR_PRIV int beaglelogic_getlasterror(struct dev_context *devc);
-
-/* Gets the unit size of the capture buffer (usually 4 or 8 MB) */
-SR_PRIV int beaglelogic_get_bufunitsize(struct dev_context *devc);
-
-SR_PRIV int beaglelogic_mmap(struct dev_context *devc);
-SR_PRIV int beaglelogic_munmap(struct dev_context *devc);
-
-/* Sources */
-SR_PRIV inline int beaglelogic_open_nonblock(struct dev_context *devc) {
- devc->fd = open(BEAGLELOGIC_DEV_NODE, O_RDONLY | O_NONBLOCK);
- return (devc->fd == -1 ? SR_ERR : SR_OK);
-}
-
-SR_PRIV inline int beaglelogic_close(struct dev_context *devc) {
- return close(devc->fd);
-}
-
-SR_PRIV inline int beaglelogic_get_buffersize(struct dev_context *devc) {
- return ioctl(devc->fd, IOCTL_BL_GET_BUFFER_SIZE, &devc->buffersize);
-}
-
-SR_PRIV inline int beaglelogic_set_buffersize(struct dev_context *devc) {
- return ioctl(devc->fd, IOCTL_BL_SET_BUFFER_SIZE, devc->buffersize);
-}
-
-/* This is treated differently as it gets a uint64_t while a uint32_t is read */
-SR_PRIV inline int beaglelogic_get_samplerate(struct dev_context *devc) {
- uint32_t arg, err;
- err = ioctl(devc->fd, IOCTL_BL_GET_SAMPLE_RATE, &arg);
- devc->cur_samplerate = arg;
- return err;
-}
-
-SR_PRIV inline int beaglelogic_set_samplerate(struct dev_context *devc) {
- return ioctl(devc->fd, IOCTL_BL_SET_SAMPLE_RATE,
- (uint32_t)devc->cur_samplerate);
-}
-
-SR_PRIV inline int beaglelogic_get_sampleunit(struct dev_context *devc) {
- return ioctl(devc->fd, IOCTL_BL_GET_SAMPLE_UNIT, &devc->sampleunit);
-}
-
-SR_PRIV inline int beaglelogic_set_sampleunit(struct dev_context *devc) {
- return ioctl(devc->fd, IOCTL_BL_SET_SAMPLE_UNIT, devc->sampleunit);
-}
-
-SR_PRIV inline int beaglelogic_get_triggerflags(struct dev_context *devc) {
- return ioctl(devc->fd, IOCTL_BL_GET_TRIGGER_FLAGS, &devc->triggerflags);
-}
-
-SR_PRIV inline int beaglelogic_set_triggerflags(struct dev_context *devc) {
- return ioctl(devc->fd, IOCTL_BL_SET_TRIGGER_FLAGS, devc->triggerflags);
-}
-
-SR_PRIV int beaglelogic_getlasterror(struct dev_context *devc) {
- int fd;
- char buf[16];
- int ret;
-
- if ((fd = open(BEAGLELOGIC_SYSFS_ATTR(lasterror), O_RDONLY)) == -1)
- return SR_ERR;
-
- if ((ret = read(fd, buf, 16)) < 0)
- return SR_ERR;
-
- close(fd);
- devc->last_error = strtoul(buf, NULL, 10);
-
- return SR_OK;
-}
-
-SR_PRIV inline int beaglelogic_start(struct dev_context *devc) {
- return ioctl(devc->fd, IOCTL_BL_START);
-}
-
-SR_PRIV inline int beaglelogic_stop(struct dev_context *devc) {
- return ioctl(devc->fd, IOCTL_BL_STOP);
-}
-
-SR_PRIV int beaglelogic_get_bufunitsize(struct dev_context *devc) {
- return ioctl(devc->fd, IOCTL_BL_GET_BUFUNIT_SIZE, &devc->bufunitsize);
-}
-
-SR_PRIV int beaglelogic_mmap(struct dev_context *devc) {
- if (!devc->buffersize)
- beaglelogic_get_buffersize(devc);
- devc->sample_buf = mmap(NULL, devc->buffersize,
- PROT_READ, MAP_SHARED, devc->fd, 0);
- return (devc->sample_buf == MAP_FAILED ? -1 : SR_OK);
-}
-
-SR_PRIV int beaglelogic_munmap(struct dev_context *devc) {
- return munmap(devc->sample_buf, devc->buffersize);
-}
-
-#endif /* BEAGLELOGIC_H_ */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.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 "protocol.h"
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-/* Define data packet size independent of packet (bufunitsize bytes) size
- * from the BeagleLogic kernel module */
-#define PACKET_SIZE (512 * 1024)
-
-/* This implementation is zero copy from the libsigrok side.
- * It does not copy any data, just passes a pointer from the mmap'ed
- * kernel buffers appropriately. It is up to the application which is
- * using libsigrok to decide how to deal with the data.
- */
-SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data)
-{
- const struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
-
- int trigger_offset;
- uint32_t packetsize;
- uint64_t bytes_remaining;
-
- if (!(sdi = cb_data) || !(devc = sdi->priv))
- return TRUE;
-
- packetsize = PACKET_SIZE;
- logic.unitsize = SAMPLEUNIT_TO_BYTES(devc->sampleunit);
-
- if (revents == G_IO_IN) {
- sr_info("In callback G_IO_IN, offset=%d", devc->offset);
-
- bytes_remaining = (devc->limit_samples * logic.unitsize) -
- devc->bytes_read;
-
- /* Configure data packet */
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.data = devc->sample_buf + devc->offset;
- logic.length = MIN(packetsize, bytes_remaining);
-
- if (devc->trigger_fired) {
- /* Send the incoming transfer to the session bus. */
- sr_session_send(devc->cb_data, &packet);
- } else {
- /* Check for trigger */
- trigger_offset = soft_trigger_logic_check(devc->stl,
- logic.data,
- packetsize);
-
- if (trigger_offset > -1) {
- trigger_offset *= logic.unitsize;
- logic.length = MIN(packetsize - trigger_offset,
- bytes_remaining);
- logic.data += trigger_offset;
-
- sr_session_send(devc->cb_data, &packet);
-
- devc->trigger_fired = TRUE;
- }
- }
-
- /* Move the read pointer forward */
- lseek(fd, packetsize, SEEK_CUR);
-
- /* Update byte count and offset (roll over if needed) */
- devc->bytes_read += logic.length;
- if ((devc->offset += packetsize) >= devc->buffersize) {
- /* One shot capture, we abort and settle with less than
- * the required number of samples */
- if (devc->triggerflags)
- devc->offset = 0;
- else
- packetsize = 0;
- }
- }
-
- /* EOF Received or we have reached the limit */
- if (devc->bytes_read >= devc->limit_samples * logic.unitsize ||
- packetsize == 0) {
- /* Send EOA Packet, stop polling */
- packet.type = SR_DF_END;
- packet.payload = NULL;
- sr_session_send(devc->cb_data, &packet);
-
- sr_session_source_remove_pollfd(sdi->session, &devc->pollfd);
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.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_BEAGLELOGIC_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_BEAGLELOGIC_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "beaglelogic"
-
-/* Maximum possible input channels */
-#define NUM_CHANNELS 14
-
-#define SAMPLEUNIT_TO_BYTES(x) ((x) == 1 ? 1 : 2)
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Model-specific information */
- int max_channels;
- uint32_t fw_ver;
-
- /* Acquisition settings: see beaglelogic.h */
- uint64_t cur_samplerate;
- uint64_t limit_samples;
- uint32_t sampleunit;
- uint32_t triggerflags;
-
- /* Buffers: size of each buffer block and the total buffer area */
- uint32_t bufunitsize;
- uint32_t buffersize;
-
- /* Operational state */
- int fd;
- GPollFD pollfd;
- int last_error;
-
- uint64_t bytes_read;
- uint64_t sent_samples;
- uint32_t offset;
- uint8_t *sample_buf; /* mmap'd kernel buffer here */
-
- void *cb_data;
-
- /* Trigger logic */
- struct soft_trigger_logic *stl;
- gboolean trigger_fired;
-};
-
-SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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 "protocol.h"
-
-#define BRYMEN_BC86X "0820.0001"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
-};
-
-SR_PRIV struct sr_dev_driver brymen_bm86x_driver_info;
-static struct sr_dev_driver *di = &brymen_bm86x_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- GSList *usb_devices, *devices, *l;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_usb_dev_inst *usb;
- struct sr_config *src;
- struct sr_channel *ch;
- const char *conn;
-
- drvc = di->priv;
- drvc->instances = NULL;
-
- conn = BRYMEN_BC86X;
- 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;
- }
- }
-
- devices = NULL;
- if (!(usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn))) {
- g_slist_free_full(usb_devices, g_free);
- return NULL;
- }
-
- for (l = usb_devices; l; l = l->next) {
- usb = l->data;
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
- "Brymen", "BM869", NULL))) {
- sr_err("sr_dev_inst_new returned NULL.");
- return NULL;
- }
-
- if (!(devc = g_try_malloc0(sizeof(*devc)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
-
- sdi->priv = devc;
- sdi->driver = di;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P2")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- sdi->inst_type = SR_INST_USB;
- sdi->conn = usb;
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- }
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc = di->priv;
- struct sr_usb_dev_inst *usb;
- struct dev_context *devc;
- int ret;
-
- usb = sdi->conn;
- devc = sdi->priv;
-
- if ((ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb)) == SR_OK)
- sdi->status = SR_ST_ACTIVE;
-
- /* Detach kernel drivers which grabbed this device (if any). */
- if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
- ret = libusb_detach_kernel_driver(usb->devhdl, 0);
- if (ret < 0) {
- sr_err("Failed to detach kernel driver: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
- devc->detached_kernel_driver = 1;
- sr_dbg("Successfully detached kernel driver.");
- } else {
- sr_dbg("No need to detach a kernel driver.");
- }
-
- /* Claim interface 0. */
- if ((ret = libusb_claim_interface(usb->devhdl, 0)) < 0) {
- sr_err("Failed to claim interface 0: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
- sr_dbg("Successfully claimed interface 0.");
-
- return ret;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
- struct dev_context *devc;
- int ret;
-
- usb = sdi->conn;
- devc = sdi->priv;
-
- if ((ret = libusb_release_interface(usb->devhdl, 0)))
- sr_err("Failed to release interface 0: %s.\n", libusb_error_name(ret));
- else
- sr_dbg("Successfully released interface 0.\n");
-
- if (!ret && devc->detached_kernel_driver) {
- if ((ret = libusb_attach_kernel_driver(usb->devhdl, 0))) {
- sr_err("Failed to attach kernel driver: %s.\n",
- libusb_error_name(ret));
- } else {
- devc->detached_kernel_driver = 0;
- sr_dbg("Successfully attached kernel driver.\n");
- }
- }
-
- libusb_close(usb->devhdl);
-
- sdi->status = SR_ST_INACTIVE;
-
- return ret;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_get(int 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_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- *data = g_variant_new_uint64(devc->limit_msec);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".", devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- devc->limit_msec = g_variant_get_uint64(data);
- sr_dbg("Setting time limit to %" PRIu64 "ms.", devc->limit_msec);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct dev_context *devc;
-
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- devc->start_time = g_get_monotonic_time();
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(sdi, LOG_PREFIX);
-
- sr_session_source_add(sdi->session, 0, 0, 10,
- brymen_bm86x_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct sr_datafeed_packet packet;
-
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- /* Send end packet to the session bus. */
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-
- sr_session_source_remove(sdi->session, 0);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver brymen_bm86x_driver_info = {
- .name = "brymen-bm86x",
- .longname = "Brymen BM86X",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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 <string.h>
-#include <math.h>
-#include "protocol.h"
-
-#define USB_TIMEOUT 500
-
-static char char_map[128] = {
- [0x20] = '-',
- [0x5F] = '0',
- [0x50] = '1',
- [0x6D] = '2',
- [0x7C] = '3',
- [0x72] = '4',
- [0x3E] = '5',
- [0x3F] = '6',
- [0x54] = '7',
- [0x7F] = '8',
- [0x7E] = '9',
- [0x0F] = 'C',
- [0x27] = 'F',
- [0x0B] = 'L',
- [0x79] = 'd',
- [0x10] = 'i',
- [0x39] = 'o',
-};
-
-static int brymen_bm86x_parse_digits(const unsigned char *buf, int length,
- char *str, float *floatval,
- char *temp_unit, int flag)
-{
- char c, *p = str;
- int i, ret;
-
- if (buf[0] & flag)
- *p++ = '-';
- for (i = 0; i < length; i++) {
- if (i && i < 5 && buf[i+1] & 0x01)
- *p++ = '.';
- c = char_map[buf[i+1] >> 1];
- if (i == 5 && (c == 'C' || c == 'F'))
- *temp_unit = c;
- else if (c)
- *p++ = c;
- }
- *p = 0;
-
- if ((ret = sr_atof_ascii(str, floatval))) {
- sr_dbg("invalid float string: '%s'", str);
- return ret;
- }
-
- return SR_OK;
-}
-
-static void brymen_bm86x_parse(unsigned char *buf, float *floatval,
- struct sr_datafeed_analog *analog)
-{
- char str[16], temp_unit;
- int ret1, ret2, over_limit;
-
- ret1 = brymen_bm86x_parse_digits(buf+2, 6, str, &floatval[0],
- &temp_unit, 0x80);
- over_limit = strstr(str, "0L") || strstr(str, "0.L");
- ret2 = brymen_bm86x_parse_digits(buf+9, 4, str, &floatval[1],
- &temp_unit, 0x10);
-
- /* main display */
- if (ret1 == SR_OK || over_limit) {
- /* SI unit */
- if (buf[8] & 0x01) {
- analog[0].mq = SR_MQ_VOLTAGE;
- analog[0].unit = SR_UNIT_VOLT;
- if (!strcmp(str, "diod"))
- analog[0].mqflags |= SR_MQFLAG_DIODE;
- } else if (buf[14] & 0x80) {
- analog[0].mq = SR_MQ_CURRENT;
- analog[0].unit = SR_UNIT_AMPERE;
- } else if (buf[14] & 0x20) {
- analog[0].mq = SR_MQ_CAPACITANCE;
- analog[0].unit = SR_UNIT_FARAD;
- } else if (buf[14] & 0x10) {
- analog[0].mq = SR_MQ_CONDUCTANCE;
- analog[0].unit = SR_UNIT_SIEMENS;
- } else if (buf[15] & 0x01) {
- analog[0].mq = SR_MQ_FREQUENCY;
- analog[0].unit = SR_UNIT_HERTZ;
- } else if (buf[10] & 0x01) {
- analog[0].mq = SR_MQ_CONTINUITY;
- analog[0].unit = SR_UNIT_OHM;
- } else if (buf[15] & 0x10) {
- analog[0].mq = SR_MQ_RESISTANCE;
- analog[0].unit = SR_UNIT_OHM;
- } else if (buf[15] & 0x02) {
- analog[0].mq = SR_MQ_POWER;
- analog[0].unit = SR_UNIT_DECIBEL_MW;
- } else if (buf[15] & 0x80) {
- analog[0].mq = SR_MQ_DUTY_CYCLE;
- analog[0].unit = SR_UNIT_PERCENTAGE;
- } else if (buf[ 2] & 0x0A) {
- analog[0].mq = SR_MQ_TEMPERATURE;
- if (temp_unit == 'F')
- analog[0].unit = SR_UNIT_FAHRENHEIT;
- else
- analog[0].unit = SR_UNIT_CELSIUS;
- }
-
- /* when MIN MAX and AVG are displayed at the same time, remove them */
- if ((buf[1] & 0xE0) == 0xE0)
- buf[1] &= ~0xE0;
-
- /* AC/DC/Auto flags */
- if (buf[1] & 0x10) analog[0].mqflags |= SR_MQFLAG_DC;
- if (buf[2] & 0x01) analog[0].mqflags |= SR_MQFLAG_AC;
- if (buf[1] & 0x01) analog[0].mqflags |= SR_MQFLAG_AUTORANGE;
- if (buf[1] & 0x08) analog[0].mqflags |= SR_MQFLAG_HOLD;
- if (buf[1] & 0x20) analog[0].mqflags |= SR_MQFLAG_MAX;
- if (buf[1] & 0x40) analog[0].mqflags |= SR_MQFLAG_MIN;
- if (buf[1] & 0x80) analog[0].mqflags |= SR_MQFLAG_AVG;
- if (buf[3] & 0x01) analog[0].mqflags |= SR_MQFLAG_RELATIVE;
-
- /* when dBm is displayed, remove the m suffix so that it is
- not considered as the 10e-3 SI prefix */
- if (buf[15] & 0x02)
- buf[15] &= ~0x04;
-
- /* SI prefix */
- if (buf[14] & 0x40) floatval[0] *= 1e-9; /* n */
- if (buf[15] & 0x08) floatval[0] *= 1e-6; /* µ */
- if (buf[15] & 0x04) floatval[0] *= 1e-3; /* m */
- if (buf[15] & 0x40) floatval[0] *= 1e3; /* k */
- if (buf[15] & 0x20) floatval[0] *= 1e6; /* M */
-
- if (over_limit) floatval[0] = INFINITY;
- }
-
- /* secondary display */
- if (ret2 == SR_OK) {
- /* SI unit */
- if (buf[14] & 0x08) {
- analog[1].mq = SR_MQ_VOLTAGE;
- analog[1].unit = SR_UNIT_VOLT;
- } else if (buf[9] & 0x04) {
- analog[1].mq = SR_MQ_CURRENT;
- analog[1].unit = SR_UNIT_AMPERE;
- } else if (buf[14] & 0x04) {
- analog[1].mq = SR_MQ_FREQUENCY;
- analog[1].unit = SR_UNIT_HERTZ;
- } else if (buf[9] & 0x40) {
- analog[1].mq = SR_MQ_TEMPERATURE;
- if (temp_unit == 'F')
- analog[1].unit = SR_UNIT_FAHRENHEIT;
- else
- analog[1].unit = SR_UNIT_CELSIUS;
- }
-
- /* AC flag */
- if (buf[9] & 0x20) analog[1].mqflags |= SR_MQFLAG_AC;
-
- /* SI prefix */
- if (buf[ 9] & 0x01) floatval[1] *= 1e-6; /* µ */
- if (buf[ 9] & 0x02) floatval[1] *= 1e-3; /* m */
- if (buf[14] & 0x02) floatval[1] *= 1e3; /* k */
- if (buf[14] & 0x01) floatval[1] *= 1e6; /* M */
- }
-
- if (buf[9] & 0x80)
- sr_spew("Battery is low.");
-}
-
-static void brymen_bm86x_handle_packet(const struct sr_dev_inst *sdi,
- unsigned char *buf)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog[2];
- float floatval[2];
-
- devc = sdi->priv;
-
- analog[0].mq = -1;
- analog[0].mqflags = 0;
-
- analog[1].mq = -1;
- analog[1].mqflags = 0;
-
- brymen_bm86x_parse(buf, floatval, analog);
-
- if (analog[0].mq != -1) {
- /* Got a measurement. */
- analog[0].num_samples = 1;
- analog[0].data = &floatval[0];
- analog[0].channels = g_slist_append(NULL, sdi->channels->data);
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog[0];
- sr_session_send(sdi, &packet);
- g_slist_free(analog[0].channels);
- }
-
- if (analog[1].mq != -1) {
- /* Got a measurement. */
- analog[1].num_samples = 1;
- analog[1].data = &floatval[1];
- analog[1].channels = g_slist_append(NULL, sdi->channels->next->data);
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog[1];
- sr_session_send(sdi, &packet);
- g_slist_free(analog[1].channels);
- }
-
- if (analog[0].mq != -1 || analog[1].mq != -1)
- devc->num_samples++;
-}
-
-static int brymen_bm86x_send_command(const struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
- unsigned char buf[] = { 0x00, 0x86, 0x66 };
- int ret;
-
- usb = sdi->conn;
-
- sr_dbg("Sending HID set report.");
- ret = libusb_control_transfer(usb->devhdl,
- LIBUSB_REQUEST_TYPE_CLASS |
- LIBUSB_RECIPIENT_INTERFACE |
- LIBUSB_ENDPOINT_OUT,
- 9, /* bRequest: HID set_report */
- 0x300, /* wValue: HID feature, report num 0 */
- 0, /* wIndex: interface 0 */
- buf, sizeof(buf), USB_TIMEOUT);
-
- if (ret < 0) {
- sr_err("HID feature report error: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- if (ret != sizeof(buf)) {
- sr_err("Short packet: sent %d/%ld bytes.", ret, sizeof(buf));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int brymen_bm86x_read_interrupt(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- unsigned char buf[24];
- int ret, transferred;
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- sr_dbg("Reading HID interrupt report.");
- /* Get data from EP1 using an interrupt transfer. */
- ret = libusb_interrupt_transfer(usb->devhdl,
- LIBUSB_ENDPOINT_IN | 1, /* EP1, IN */
- buf, sizeof(buf),
- &transferred, USB_TIMEOUT);
-
- if (ret == LIBUSB_ERROR_TIMEOUT) {
- if (++devc->interrupt_pending > 3)
- devc->interrupt_pending = 0;
- return SR_OK;
- }
-
- if (ret < 0) {
- sr_err("USB receive error: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- if (transferred != sizeof(buf)) {
- sr_err("Short packet: received %d/%d bytes.", transferred, sizeof(buf));
- return SR_ERR;
- }
-
- devc->interrupt_pending = 0;
- brymen_bm86x_handle_packet(sdi, buf);
-
- return SR_OK;
-}
-
-SR_PRIV int brymen_bm86x_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- int64_t time;
-
- (void)fd;
- (void)revents;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- if (!devc->interrupt_pending) {
- if (brymen_bm86x_send_command(sdi))
- return FALSE;
- devc->interrupt_pending = 1;
- }
-
- if (brymen_bm86x_read_interrupt(sdi))
- return FALSE;
-
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sr_info("Requested number of samples reached, stopping.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
-
- if (devc->limit_msec) {
- time = (g_get_monotonic_time() - devc->start_time) / 1000;
- if (time > (int64_t)devc->limit_msec) {
- sr_info("Requested time limit reached, stopping.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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_BRYMEN_BM86X_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_BRYMEN_BM86X_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "brymen-bm86x"
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Acquisition settings */
- uint64_t limit_samples; /**< The sampling limit (in number of samples).*/
- uint64_t limit_msec; /**< The time limit (in milliseconds). */
-
- /* Operational state */
- int detached_kernel_driver;/**< Whether kernel driver was detached or not */
- uint64_t num_samples; /**< The number of already received samples. */
- int64_t start_time; /**< The time at which sampling started. */
-
- /* Temporary state across callbacks */
- int interrupt_pending;
-};
-
-SR_PRIV int brymen_bm86x_receive_data(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
- *
- * 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 "protocol.h"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_CONTINUOUS,
- SR_CONF_LIMIT_MSEC,
-};
-
-SR_PRIV struct sr_dev_driver brymen_bm857_driver_info;
-static struct sr_dev_driver *di = &brymen_bm857_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *brymen_scan(const char *conn, const char *serialcomm)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct drv_context *drvc;
- struct sr_channel *ch;
- struct sr_serial_dev_inst *serial;
- GSList *devices;
- int ret;
- uint8_t buf[128];
- size_t len;
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- sr_info("Probing port %s.", conn);
-
- devices = NULL;
-
- /* Request reading */
- if ((ret = brymen_packet_request(serial)) < 0) {
- sr_err("Unable to send command: %d.", ret);
- goto scan_cleanup;
- }
-
- len = 128;
- ret = brymen_stream_detect(serial, buf, &len, brymen_packet_length,
- brymen_packet_is_valid, 1000, 9600);
- if (ret != SR_OK)
- goto scan_cleanup;
-
- sr_info("Found device on port %s.", conn);
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Brymen", "BM85x", NULL)))
- goto scan_cleanup;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- goto scan_cleanup;
- }
-
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
- drvc = di->priv;
- sdi->priv = devc;
- sdi->driver = di;
-
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
- goto scan_cleanup;
-
- sdi->channels = g_slist_append(sdi->channels, ch);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
-scan_cleanup:
- serial_close(serial);
-
- return devices;
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct sr_config *src;
- GSList *devices, *l;
- const char *conn, *serialcomm;
-
- devices = NULL;
- drvc = di->priv;
- drvc->instances = NULL;
-
- 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 (!conn)
- return NULL;
-
- if (serialcomm) {
- /* Use the provided comm specs. */
- devices = brymen_scan(conn, serialcomm);
- } else {
- /* But 9600/8n1 should work all of the time. */
- devices = brymen_scan(conn, "9600/8n1/dtr=1/rts=1");
- }
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- int ret;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- ret = SR_OK;
- switch (id) {
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- break;
- case SR_CONF_LIMIT_MSEC:
- devc->limit_msec = g_variant_get_uint64(data);
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- devc->cb_data = cb_data;
-
- /*
- * Reset the number of samples to take. If we've already collected our
- * quota, but we start a new session, and don't reset this, we'll just
- * quit without acquiring any new samples.
- */
- devc->num_samples = 0;
- devc->starttime = g_get_monotonic_time();
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Poll every 50ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 50,
- brymen_dmm_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
- sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver brymen_bm857_driver_info = {
- .name = "brymen-bm857",
- .longname = "Brymen BM857",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .config_get = NULL,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = std_serial_dev_open,
- .dev_close = std_serial_dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
- *
- * 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 "protocol.h"
-
-#define MAX_PACKET_LEN 22
-
-/* Flags passed from the DMM. */
-struct brymen_flags {
- gboolean is_low_batt, is_decibel, is_duty_cycle, is_hertz, is_amp;
- gboolean is_beep, is_ohm, is_fahrenheit, is_celsius, is_capacitance;
- gboolean is_diode, is_volt, is_dc, is_ac;
-};
-
-struct bm850_command {
- uint8_t dle;
- uint8_t stx;
- uint8_t cmd;
- uint8_t arg[2];
- uint8_t checksum;
- uint8_t dle2;
- uint8_t etx;
-};
-
-struct brymen_header {
- uint8_t dle;
- uint8_t stx;
- uint8_t cmd;
- uint8_t len;
-};
-
-struct brymen_tail {
- uint8_t checksum;
- uint8_t dle;
- uint8_t etx;
-};
-
-/*
- * We only have one command because we only support the BM-857. However, the
- * driver is easily extensible to support more models, as the protocols are
- * very similar.
- */
-enum {
- BM_CMD_REQUEST_READING = 0x00,
-};
-
-static int bm_send_command(uint8_t command, uint8_t arg1, uint8_t arg2,
- struct sr_serial_dev_inst *serial)
-{
- struct bm850_command cmdout;
- int written;
-
- cmdout.dle = 0x10;
- cmdout.stx = 0x02;
- cmdout.cmd = command;
- cmdout.arg[0] = arg1;
- cmdout.arg[1] = arg2;
- cmdout.checksum = arg1 ^ arg2;
- cmdout.dle2 = 0x10;
- cmdout.etx = 0x03;
-
- /* TODO: How to compute the checksum? Hardware seems to ignore it. */
-
- /* Request reading. */
- written = serial_write(serial, &cmdout, sizeof(cmdout));
- if (written != sizeof(cmdout))
- return SR_ERR;
-
- return SR_OK;
-}
-
-SR_PRIV int brymen_packet_request(struct sr_serial_dev_inst *serial)
-{
- return bm_send_command(BM_CMD_REQUEST_READING, 0, 0, serial);
-}
-
-SR_PRIV int brymen_packet_length(const uint8_t *buf, int *len)
-{
- struct brymen_header *hdr;
- int packet_len;
- size_t buflen;
-
- buflen = *len;
- hdr = (void *)buf;
-
- /* Did we receive a complete header yet? */
- if (buflen < sizeof(*hdr))
- return PACKET_NEED_MORE_DATA;
-
- if (hdr->dle != 0x10 || hdr->stx != 0x02)
- return PACKET_INVALID_HEADER;
-
- /* Our packet includes the header, the payload, and the tail. */
- packet_len = sizeof(*hdr) + hdr->len + sizeof(struct brymen_tail);
-
- /* In case we pick up an invalid header, limit our search. */
- if (packet_len > MAX_PACKET_LEN) {
- sr_spew("Header specifies an invalid payload length: %i.",
- hdr->len);
- return PACKET_INVALID_HEADER;
- }
-
- *len = packet_len;
- sr_spew("Expecting a %d-byte packet.", *len);
- return PACKET_HEADER_OK;
-}
-
-SR_PRIV gboolean brymen_packet_is_valid(const uint8_t *buf)
-{
- struct brymen_header *hdr;
- struct brymen_tail *tail;
- int i;
- uint8_t chksum = 0;
- uint8_t *payload;
-
- payload = (uint8_t *)(buf + sizeof(struct brymen_header));
-
- hdr = (void *)buf;
- tail = (void *)(payload + hdr->len);
-
- for (i = 0; i< hdr->len; i++)
- chksum ^= payload[i];
-
- if (tail->checksum != chksum) {
- sr_dbg("Packet has invalid checksum 0x%.2x. Expected 0x%.2x.",
- chksum, tail->checksum);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static int parse_value(const char *strbuf, int len, float *floatval)
-{
- int s, d;
- char str[32];
-
- if (strstr(strbuf, "OL")) {
- sr_dbg("Overlimit.");
- *floatval = INFINITY;
- return SR_OK;
- }
-
- memset(str, 0, sizeof(str));
- /* Spaces may interfere with parsing the exponent. Strip them. */
- for (s = 0, d = 0; s < len; s++) {
- if (strbuf[s] != ' ')
- str[d++] = strbuf[s];
- }
- if (sr_atof_ascii(str, floatval) != SR_OK)
- return SR_ERR;
-
- return SR_OK;
-}
-
-static void parse_flags(const uint8_t *buf, struct brymen_flags *info)
-{
- info->is_low_batt = (buf[4 + 3] & (1 << 7)) != 0;
-
- info->is_decibel = (buf[4 + 1] & (1 << 5)) != 0;
- info->is_duty_cycle = (buf[4 + 1] & (1 << 3)) != 0;
- info->is_hertz = (buf[4 + 1] & (1 << 2)) != 0;
- info->is_amp = (buf[4 + 1] & (1 << 1)) != 0;
- info->is_beep = (buf[4 + 1] & (1 << 0)) != 0;
-
- info->is_ohm = (buf[4 + 0] & (1 << 7)) != 0;
- info->is_fahrenheit = (buf[4 + 0] & (1 << 6)) != 0;
- info->is_celsius = (buf[4 + 0] & (1 << 5)) != 0;
- info->is_diode = (buf[4 + 0] & (1 << 4)) != 0;
- info->is_capacitance = (buf[4 + 0] & (1 << 3)) != 0;
- info->is_volt = (buf[4 + 0] & (1 << 2)) != 0;
- info->is_dc = (buf[4 + 0] & (1 << 1)) != 0;
- info->is_ac = (buf[4 + 0] & (1 << 0)) != 0;
-}
-
-SR_PRIV int brymen_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info)
-{
- struct brymen_flags flags;
- struct brymen_header *hdr;
- uint8_t *bfunc;
- int asciilen;
-
- (void)info;
-
- hdr = (void *)buf;
- bfunc = (uint8_t *)(buf + sizeof(struct brymen_header));
-
- analog->mqflags = 0;
-
- /* Give some debug info about the package. */
- asciilen = hdr->len - 4;
- sr_dbg("DMM flags: %.2x %.2x %.2x %.2x",
- bfunc[3], bfunc[2], bfunc[1], bfunc[0]);
- /* Value is an ASCII string. */
- sr_dbg("DMM packet: \"%.*s\"", asciilen, bfunc + 4);
-
- parse_flags(buf, &flags);
- if (parse_value((const char *)(bfunc + 4), asciilen, floatval) != SR_OK)
- return SR_ERR;
-
- if (flags.is_volt) {
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- }
- if (flags.is_amp) {
- analog->mq = SR_MQ_CURRENT;
- analog->unit = SR_UNIT_AMPERE;
- }
- if (flags.is_ohm) {
- if (flags.is_beep)
- analog->mq = SR_MQ_CONTINUITY;
- else
- analog->mq = SR_MQ_RESISTANCE;
- analog->unit = SR_UNIT_OHM;
- }
- if (flags.is_hertz) {
- analog->mq = SR_MQ_FREQUENCY;
- analog->unit = SR_UNIT_HERTZ;
- }
- if (flags.is_duty_cycle) {
- analog->mq = SR_MQ_DUTY_CYCLE;
- analog->unit = SR_UNIT_PERCENTAGE;
- }
- if (flags.is_capacitance) {
- analog->mq = SR_MQ_CAPACITANCE;
- analog->unit = SR_UNIT_FARAD;
- }
- if (flags.is_fahrenheit) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_FAHRENHEIT;
- }
- if (flags.is_celsius) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_CELSIUS;
- }
- if (flags.is_capacitance) {
- analog->mq = SR_MQ_CAPACITANCE;
- analog->unit = SR_UNIT_FARAD;
- }
-
- /*
- * The high-end Brymen models have a configurable reference impedance.
- * When the reference impedance is changed, the DMM sends one packet
- * with the value of the new reference impedance. Both decibel and ohm
- * flags are set in this case, so we must be careful to correctly
- * identify the value as ohm, not dBmW.
- */
- if (flags.is_decibel && !flags.is_ohm) {
- analog->mq = SR_MQ_POWER;
- analog->unit = SR_UNIT_DECIBEL_MW;
- /*
- * For some reason, dBm measurements are sent by the multimeter
- * with a value three orders of magnitude smaller than the
- * displayed value.
- */
- *floatval *= 1000;
- }
-
- if (flags.is_diode)
- analog->mqflags |= SR_MQFLAG_DIODE;
- /* We can have both AC+DC in a single measurement. */
- if (flags.is_ac)
- analog->mqflags |= SR_MQFLAG_AC;
- if (flags.is_dc)
- analog->mqflags |= SR_MQFLAG_DC;
-
- if (flags.is_low_batt)
- sr_info("Low battery!");
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
- *
- * 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 "protocol.h"
-
-static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi)
-{
- float floatval;
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
-
- devc = sdi->priv;
-
- analog.num_samples = 1;
- analog.mq = -1;
-
- if (brymen_parse(buf, &floatval, &analog, NULL) != SR_OK)
- return;
- analog.data = &floatval;
-
- analog.channels = sdi->channels;
-
- if (analog.mq != -1) {
- /* Got a measurement. */
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
- devc->num_samples++;
- }
-}
-
-static void handle_new_data(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int len, status, offset = 0;
- struct sr_serial_dev_inst *serial;
-
- devc = sdi->priv;
- serial = sdi->conn;
-
- /* Try to get as much data as the buffer can hold. */
- len = DMM_BUFSIZE - devc->buflen;
- len = serial_read(serial, devc->buf + devc->buflen, len);
- if (len < 1) {
- sr_err("Serial port read error: %d.", len);
- return;
- }
- devc->buflen += len;
- status = PACKET_INVALID_HEADER;
-
- /* Now look for packets in that data. */
- while (status != PACKET_NEED_MORE_DATA) {
- /* We don't have a header, look for one. */
- if (devc->next_packet_len == 0) {
- len = devc->buflen - offset;
- status = brymen_packet_length(devc->buf + offset, &len);
- if (status == PACKET_HEADER_OK) {
- /* We know how large the packet will be. */
- devc->next_packet_len = len;
- } else if (status == PACKET_NEED_MORE_DATA) {
- /* We didn't yet receive the full header. */
- devc->next_packet_len = 0;
- break;
- } else {
- /* Invalid header. Move on. */
- devc->next_packet_len = 0;
- offset++;
- continue;
- }
- }
-
- /* We know how the packet size, but did we receive all of it? */
- if (devc->buflen - offset < devc->next_packet_len)
- break;
-
- /* We should have a full packet here, so we can check it. */
- if (brymen_packet_is_valid(devc->buf + offset)) {
- handle_packet(devc->buf + offset, sdi);
- offset += devc->next_packet_len;
- } else {
- offset++;
- }
-
- /* We are done with this packet. Look for a new one. */
- devc->next_packet_len = 0;
- }
-
- /* If we have any data left, move it to the beginning of our buffer. */
- memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
- devc->buflen -= offset;
-}
-
-SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- int ret;
- int64_t time;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- serial = sdi->conn;
-
- if (revents == G_IO_IN) {
- /* Serial data arrived. */
- handle_new_data(sdi);
- } else {
- /* Timeout, send another packet request. */
- if ((ret = brymen_packet_request(serial)) < 0) {
- sr_err("Failed to request packet: %d.", ret);
- return FALSE;
- }
- }
-
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sr_info("Requested number of samples reached, stopping.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
-
- if (devc->limit_msec) {
- time = (g_get_monotonic_time() - devc->starttime) / 1000;
- if (time > (int64_t)devc->limit_msec) {
- sr_info("Requested time limit reached, stopping.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
- }
-
- return TRUE;
-}
-
-/**
- * Try to find a valid packet in a serial data stream.
- *
- * @param serial Previously initialized serial port structure.
- * @param buf Buffer containing the bytes to write.
- * @param buflen Size of the buffer.
- * @param get_packet_size Callback that assesses the size of incoming packets.
- * @param is_valid Callback that assesses whether the packet is valid or not.
- * @param timeout_ms The timeout after which, if no packet is detected, to
- * abort scanning.
- * @param baudrate The baudrate of the serial port. This parameter is not
- * critical, but it helps fine tune the serial port polling
- * delay.
- *
- * @return SR_OK if a valid packet is found within the given timeout,
- * SR_ERR upon failure.
- */
-SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
- uint8_t *buf, size_t *buflen,
- packet_length_t get_packet_size,
- packet_valid_callback is_valid,
- uint64_t timeout_ms, int baudrate)
-{
- int64_t start, time, byte_delay_us;
- size_t ibuf, i, maxlen;
- int status, len, packet_len, stream_len;
-
- maxlen = *buflen;
-
- sr_dbg("Detecting packets on %s (timeout = %" PRIu64
- "ms, baudrate = %d).", serial->port, timeout_ms, baudrate);
-
- /* Assume 8n1 transmission. That is 10 bits for every byte. */
- byte_delay_us = 10 * (1000000 / baudrate);
- start = g_get_monotonic_time();
-
- packet_len = i = ibuf = len = 0;
- while (ibuf < maxlen) {
- len = serial_read(serial, &buf[ibuf], maxlen - ibuf);
- if (len > 0) {
- ibuf += len;
- sr_spew("Read %d bytes.", len);
- }
-
- time = g_get_monotonic_time() - start;
- time /= 1000;
-
- stream_len = ibuf - i;
- if (stream_len > 0 && packet_len == 0) {
- /* How large of a packet are we expecting? */
- packet_len = stream_len;
- status = get_packet_size(&buf[i], &packet_len);
- switch(status) {
- case PACKET_HEADER_OK:
- /* We know how much data we need to wait for. */
- break;
- case PACKET_NEED_MORE_DATA:
- /* We did not receive the full header. */
- packet_len = 0;
- break;
- case PACKET_INVALID_HEADER:
- default:
- /*
- * We had enough data, but here was an error in
- * parsing the header. Restart parsing from the
- * next byte.
- */
- packet_len = 0;
- i++;
- break;
- }
- }
-
- if ((stream_len >= packet_len) && (packet_len != 0)) {
- /* We have at least a packet's worth of data. */
- if (is_valid(&buf[i])) {
- sr_spew("Found valid %d-byte packet after "
- "%" PRIu64 "ms.", packet_len, time);
- *buflen = ibuf;
- return SR_OK;
- } else {
- sr_spew("Got %d bytes, but not a valid "
- "packet.", packet_len);
-
- }
-
- /* Not a valid packet. Continue searching. */
- i++;
- packet_len = 0;
- }
-
- if (time >= (int64_t)timeout_ms) {
- /* Timeout */
- sr_dbg("Detection timed out after %dms.", time);
- break;
- }
- g_usleep(byte_delay_us);
- }
-
- *buflen = ibuf;
- sr_err("Didn't find a valid packet (read %d bytes).", ibuf);
-
- return SR_ERR;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
- *
- * 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_BRYMEN_DMM_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_BRYMEN_DMM_PROTOCOL_H
-
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "brymen-dmm"
-
-#define DMM_BUFSIZE 256
-
-enum packet_len_status {
- PACKET_HEADER_OK,
- PACKET_NEED_MORE_DATA,
- PACKET_INVALID_HEADER,
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /** The current sampling limit (in number of samples). */
- uint64_t limit_samples;
-
- /** The current sampling limit (in ms). */
- uint64_t limit_msec;
-
- /** Opaque pointer passed in by the frontend. */
- void *cb_data;
-
- /** The current number of already received samples. */
- uint64_t num_samples;
-
- /** Start time of acquisition session */
- int64_t starttime;
-
- uint8_t buf[DMM_BUFSIZE];
- int bufoffset;
- int buflen;
- int next_packet_len;
-};
-
-/**
- * Callback that assesses the size and status of the incoming packet.
- *
- * @return PACKET_HEADER_OK - This is a proper packet header.
- * PACKET_NEED_MORE_DATA The buffer does not contain the entire header.
- * PACKET_INVALID_HEADER Not a valid start of packet.
- */
-typedef int (*packet_length_t)(const uint8_t *buf, int *len);
-
-SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data);
-SR_PRIV int brymen_packet_request(struct sr_serial_dev_inst *serial);
-
-SR_PRIV int brymen_packet_length(const uint8_t *buf, int *len);
-SR_PRIV gboolean brymen_packet_is_valid(const uint8_t *buf);
-
-SR_PRIV int brymen_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info);
-
-SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
- uint8_t *buf, size_t *buflen,
- packet_length_t get_packet_size,
- packet_valid_callback is_valid,
- uint64_t timeout_ms, int baudrate);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 <string.h>
-#include "protocol.h"
-
-#define SERIALCOMM "9600/8n1"
-/* 23ms is the longest interval between tokens. */
-#define MAX_SCAN_TIME 25 * 1000
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_SOUNDLEVELMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_CONTINUOUS,
- SR_CONF_SPL_WEIGHT_FREQ,
- SR_CONF_SPL_WEIGHT_TIME,
- SR_CONF_SPL_MEASUREMENT_RANGE,
- SR_CONF_DATALOG,
- SR_CONF_HOLD_MAX,
- SR_CONF_HOLD_MIN,
- SR_CONF_POWER_OFF,
- SR_CONF_DATA_SOURCE,
-};
-
-static const char *weight_freq[] = {
- "A",
- "C",
-};
-
-static const char *weight_time[] = {
- "F",
- "S",
-};
-
-static const uint64_t meas_ranges[][2] = {
- { 30, 130 },
- { 30, 80 },
- { 50, 100 },
- { 80, 130 },
-};
-
-static const char *data_sources[] = {
- "Live",
- "Memory",
-};
-SR_PRIV struct sr_dev_driver cem_dt_885x_driver_info;
-static struct sr_dev_driver *di = &cem_dt_885x_driver_info;
-
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_config *src;
- struct sr_serial_dev_inst *serial;
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- GSList *l, *devices;
- gint64 start;
- const char *conn;
- unsigned char c;
-
- conn = NULL;
- for (l = options; l; l = l->next) {
- src = l->data;
- if (src->key == SR_CONF_CONN)
- conn = g_variant_get_string(src->data, NULL);
- }
- if (!conn)
- return NULL;
-
- if (!(serial = sr_serial_dev_inst_new(conn, SERIALCOMM)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDONLY | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- devices = NULL;
- drvc = di->priv;
- start = g_get_monotonic_time();
- while (g_get_monotonic_time() - start < MAX_SCAN_TIME) {
- if (serial_read(serial, &c, 1) == 1 && c == 0xa5) {
- /* Found one. */
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "CEM",
- "DT-885x", NULL)))
- return NULL;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_dbg("Device context malloc failed.");
- return NULL;
- }
- devc->cur_mqflags = 0;
- devc->recording = -1;
- devc->cur_meas_range = 0;
- devc->cur_data_source = DATA_SOURCE_LIVE;
- devc->enable_data_source_memory = FALSE;
-
- if (!(sdi->conn = sr_serial_dev_inst_new(conn, SERIALCOMM)))
- return NULL;
-
- sdi->inst_type = SR_INST_SERIAL;
- sdi->priv = devc;
- sdi->driver = di;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "SPL")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- break;
- }
- /* It takes about 1ms for a byte to come in. */
- g_usleep(1000);
- }
-
- serial_close(serial);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct sr_serial_dev_inst *serial;
-
- serial = sdi->conn;
- if (serial_open(serial, SERIAL_RDWR) != SR_OK)
- return SR_ERR;
-
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- GVariant *range[2];
- uint64_t low, high;
- int tmp, ret;
-
- (void)cg;
-
- if (!sdi)
- return SR_ERR_ARG;
-
- devc = sdi->priv;
- ret = SR_OK;
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_DATALOG:
- if ((ret = cem_dt_885x_recording_get(sdi, &tmp)) == SR_OK)
- *data = g_variant_new_boolean(tmp);
- break;
- case SR_CONF_SPL_WEIGHT_FREQ:
- tmp = cem_dt_885x_weight_freq_get(sdi);
- if (tmp == SR_MQFLAG_SPL_FREQ_WEIGHT_A)
- *data = g_variant_new_string("A");
- else if (tmp == SR_MQFLAG_SPL_FREQ_WEIGHT_C)
- *data = g_variant_new_string("C");
- else
- return SR_ERR;
- break;
- case SR_CONF_SPL_WEIGHT_TIME:
- tmp = cem_dt_885x_weight_time_get(sdi);
- if (tmp == SR_MQFLAG_SPL_TIME_WEIGHT_F)
- *data = g_variant_new_string("F");
- else if (tmp == SR_MQFLAG_SPL_TIME_WEIGHT_S)
- *data = g_variant_new_string("S");
- else
- return SR_ERR;
- break;
- case SR_CONF_HOLD_MAX:
- if ((ret = cem_dt_885x_holdmode_get(sdi, &tmp)) == SR_OK)
- *data = g_variant_new_boolean(tmp == SR_MQFLAG_MAX);
- break;
- case SR_CONF_HOLD_MIN:
- if ((ret = cem_dt_885x_holdmode_get(sdi, &tmp)) == SR_OK)
- *data = g_variant_new_boolean(tmp == SR_MQFLAG_MIN);
- break;
- case SR_CONF_SPL_MEASUREMENT_RANGE:
- if ((ret = cem_dt_885x_meas_range_get(sdi, &low, &high)) == SR_OK) {
- range[0] = g_variant_new_uint64(low);
- range[1] = g_variant_new_uint64(high);
- *data = g_variant_new_tuple(range, 2);
- }
- break;
- case SR_CONF_POWER_OFF:
- *data = g_variant_new_boolean(FALSE);
- break;
- case SR_CONF_DATA_SOURCE:
- if (devc->cur_data_source == DATA_SOURCE_LIVE)
- *data = g_variant_new_string("Live");
- else
- *data = g_variant_new_string("Memory");
- break;
- default:
- return SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- uint64_t tmp_u64, low, high;
- unsigned int i;
- int tmp, ret;
- const char *tmp_str;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- ret = SR_OK;
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- tmp_u64 = g_variant_get_uint64(data);
- devc->limit_samples = tmp_u64;
- ret = SR_OK;
- break;
- case SR_CONF_DATALOG:
- ret = cem_dt_885x_recording_set(sdi, g_variant_get_boolean(data));
- break;
- case SR_CONF_SPL_WEIGHT_FREQ:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "A"))
- ret = cem_dt_885x_weight_freq_set(sdi,
- SR_MQFLAG_SPL_FREQ_WEIGHT_A);
- else if (!strcmp(tmp_str, "C"))
- ret = cem_dt_885x_weight_freq_set(sdi,
- SR_MQFLAG_SPL_FREQ_WEIGHT_C);
- else
- return SR_ERR_ARG;
- break;
- case SR_CONF_SPL_WEIGHT_TIME:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "F"))
- ret = cem_dt_885x_weight_time_set(sdi,
- SR_MQFLAG_SPL_TIME_WEIGHT_F);
- else if (!strcmp(tmp_str, "S"))
- ret = cem_dt_885x_weight_time_set(sdi,
- SR_MQFLAG_SPL_TIME_WEIGHT_S);
- else
- return SR_ERR_ARG;
- break;
- case SR_CONF_HOLD_MAX:
- tmp = g_variant_get_boolean(data) ? SR_MQFLAG_MAX : 0;
- ret = cem_dt_885x_holdmode_set(sdi, tmp);
- break;
- case SR_CONF_HOLD_MIN:
- tmp = g_variant_get_boolean(data) ? SR_MQFLAG_MIN : 0;
- ret = cem_dt_885x_holdmode_set(sdi, tmp);
- break;
- case SR_CONF_SPL_MEASUREMENT_RANGE:
- g_variant_get(data, "(tt)", &low, &high);
- ret = SR_ERR_ARG;
- for (i = 0; i < ARRAY_SIZE(meas_ranges); i++) {
- if (meas_ranges[i][0] == low && meas_ranges[i][1] == high) {
- ret = cem_dt_885x_meas_range_set(sdi, low, high);
- break;
- }
- }
- break;
- case SR_CONF_POWER_OFF:
- if (g_variant_get_boolean(data))
- ret = cem_dt_885x_power_off(sdi);
- break;
- case SR_CONF_DATA_SOURCE:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "Live"))
- devc->cur_data_source = DATA_SOURCE_LIVE;
- else if (!strcmp(tmp_str, "Memory"))
- devc->cur_data_source = DATA_SOURCE_MEMORY;
- else
- return SR_ERR;
- devc->enable_data_source_memory = devc->cur_data_source == DATA_SOURCE_MEMORY;
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- GVariant *tuple, *range[2];
- GVariantBuilder gvb;
- unsigned int i;
- int ret;
-
- (void)sdi;
- (void)cg;
-
- ret = SR_OK;
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SPL_WEIGHT_FREQ:
- *data = g_variant_new_strv(weight_freq, ARRAY_SIZE(weight_freq));
- break;
- case SR_CONF_SPL_WEIGHT_TIME:
- *data = g_variant_new_strv(weight_time, ARRAY_SIZE(weight_time));
- break;
- case SR_CONF_SPL_MEASUREMENT_RANGE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (i = 0; i < ARRAY_SIZE(meas_ranges); i++) {
- range[0] = g_variant_new_uint64(meas_ranges[i][0]);
- range[1] = g_variant_new_uint64(meas_ranges[i][1]);
- tuple = g_variant_new_tuple(range, 2);
- g_variant_builder_add_value(&gvb, tuple);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_DATA_SOURCE:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- devc->cb_data = cb_data;
- devc->state = ST_INIT;
- devc->num_samples = 0;
- devc->buf_len = 0;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Poll every 100ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 150,
- cem_dt_885x_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
- sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver cem_dt_885x_driver_info = {
- .name = "cem-dt-885x",
- .longname = "CEM DT-885x",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .config_get = config_get,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = dev_open,
- .dev_close = std_serial_dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 <string.h>
-#include "protocol.h"
-
-/* Length of expected payload for each token. */
-static int token_payloads[][2] = {
- { TOKEN_WEIGHT_TIME_FAST, 0 },
- { TOKEN_WEIGHT_TIME_SLOW, 0 },
- { TOKEN_HOLD_MAX, 0 },
- { TOKEN_HOLD_MIN, 0 },
- { TOKEN_TIME, 3 },
- { TOKEN_MEAS_RANGE_OVER, 0 },
- { TOKEN_MEAS_RANGE_UNDER, 0 },
- { TOKEN_STORE_FULL, 0 },
- { TOKEN_RECORDING_ON, 0 },
- { TOKEN_MEAS_WAS_READOUT, 1 },
- { TOKEN_MEAS_WAS_BARGRAPH, 0 },
- { TOKEN_MEASUREMENT, 2 },
- { TOKEN_HOLD_NONE, 0 },
- { TOKEN_BATTERY_LOW, 0 },
- { TOKEN_MEAS_RANGE_OK, 0 },
- { TOKEN_STORE_OK, 0 },
- { TOKEN_RECORDING_OFF, 0 },
- { TOKEN_WEIGHT_FREQ_A, 1 },
- { TOKEN_WEIGHT_FREQ_C, 1 },
- { TOKEN_BATTERY_OK, 0 },
- { TOKEN_MEAS_RANGE_30_80, 0 },
- { TOKEN_MEAS_RANGE_30_130, 0 },
- { TOKEN_MEAS_RANGE_50_100, 0 },
- { TOKEN_MEAS_RANGE_80_130, 0 },
-};
-
-static int find_token_payload_len(unsigned char c)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(token_payloads); i++) {
- if (token_payloads[i][0] == c)
- return token_payloads[i][1];
- }
-
- return -1;
-}
-
-/* Process measurement or setting (0xa5 command). */
-static void process_mset(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- GString *dbg;
- float fvalue;
- int i;
-
- devc = sdi->priv;
- if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
- dbg = g_string_sized_new(128);
- g_string_printf(dbg, "got command 0x%.2x token 0x%.2x",
- devc->cmd, devc->token);
- if (devc->buf_len) {
- g_string_append_printf(dbg, " payload");
- for (i = 0; i < devc->buf_len; i++)
- g_string_append_printf(dbg, " %.2x", devc->buf[i]);
- }
- sr_spew("%s", dbg->str);
- g_string_free(dbg, TRUE);
- }
-
- switch(devc->token) {
- case TOKEN_WEIGHT_TIME_FAST:
- devc->cur_mqflags |= SR_MQFLAG_SPL_TIME_WEIGHT_F;
- devc->cur_mqflags &= ~SR_MQFLAG_SPL_TIME_WEIGHT_S;
- break;
- case TOKEN_WEIGHT_TIME_SLOW:
- devc->cur_mqflags |= SR_MQFLAG_SPL_TIME_WEIGHT_S;
- devc->cur_mqflags &= ~SR_MQFLAG_SPL_TIME_WEIGHT_F;
- break;
- case TOKEN_WEIGHT_FREQ_A:
- devc->cur_mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A;
- devc->cur_mqflags &= ~SR_MQFLAG_SPL_FREQ_WEIGHT_C;
- break;
- case TOKEN_WEIGHT_FREQ_C:
- devc->cur_mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C;
- devc->cur_mqflags &= ~SR_MQFLAG_SPL_FREQ_WEIGHT_A;
- break;
- case TOKEN_HOLD_MAX:
- devc->cur_mqflags |= SR_MQFLAG_HOLD | SR_MQFLAG_MAX;
- devc->cur_mqflags &= ~SR_MQFLAG_MIN;
- break;
- case TOKEN_HOLD_MIN:
- devc->cur_mqflags |= SR_MQFLAG_HOLD | SR_MQFLAG_MIN;
- devc->cur_mqflags &= ~SR_MQFLAG_MAX;
- break;
- case TOKEN_HOLD_NONE:
- devc->cur_mqflags &= ~(SR_MQFLAG_MAX | SR_MQFLAG_MIN | SR_MQFLAG_HOLD);
- break;
- case TOKEN_MEASUREMENT:
- fvalue = ((devc->buf[0] & 0xf0) >> 4) * 100;
- fvalue += (devc->buf[0] & 0x0f) * 10;
- fvalue += ((devc->buf[1] & 0xf0) >> 4);
- fvalue += (devc->buf[1] & 0x0f) / 10.0;
- devc->last_spl = fvalue;
- break;
- case TOKEN_MEAS_WAS_READOUT:
- case TOKEN_MEAS_WAS_BARGRAPH:
- if (devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN)) {
- if (devc->token == TOKEN_MEAS_WAS_BARGRAPH) {
- /* The device still sends bargraph measurements even
- * when in max/min hold mode. Suppress them here, unless
- * they're readout values. This duplicates the behavior
- * of the device display exactly. */
- break;
- }
- }
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
- analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
- analog.mqflags = devc->cur_mqflags;
- analog.unit = SR_UNIT_DECIBEL_SPL;
- analog.channels = sdi->channels;
- analog.num_samples = 1;
- analog.data = &devc->last_spl;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
-
- devc->num_samples++;
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
- devc->cb_data);
- break;
- case TOKEN_RECORDING_ON:
- devc->recording = TRUE;
- break;
- case TOKEN_RECORDING_OFF:
- devc->recording = FALSE;
- break;
- case TOKEN_MEAS_RANGE_30_80:
- case TOKEN_MEAS_RANGE_30_130:
- case TOKEN_MEAS_RANGE_50_100:
- case TOKEN_MEAS_RANGE_80_130:
- devc->cur_meas_range = devc->token;
- break;
- case TOKEN_TIME:
- case TOKEN_STORE_OK:
- case TOKEN_STORE_FULL:
- case TOKEN_BATTERY_OK:
- case TOKEN_BATTERY_LOW:
- case TOKEN_MEAS_RANGE_OK:
- case TOKEN_MEAS_RANGE_OVER:
- case TOKEN_MEAS_RANGE_UNDER:
- /* Not useful, or not expressable in sigrok. */
- break;
- }
-
-}
-
-static void send_data(const struct sr_dev_inst *sdi, unsigned char *data,
- uint64_t num_samples)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- float fbuf[SAMPLES_PER_PACKET];
- unsigned int i;
-
- devc = sdi->priv;
-
- for (i = 0; i < num_samples; i ++) {
- fbuf[i] = ((data[i * 2] & 0xf0) >> 4) * 100;
- fbuf[i] += (data[i * 2] & 0x0f) * 10;
- fbuf[i] += ((data[i * 2 + 1] & 0xf0) >> 4);
- fbuf[i] += (data[i * 2 + 1] & 0x0f) / 10.0;
- }
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
- analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
- analog.mqflags = devc->cur_mqflags;
- analog.unit = SR_UNIT_DECIBEL_SPL;
- analog.channels = sdi->channels;
- analog.num_samples = num_samples;
- analog.data = fbuf;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
-
- devc->num_samples += analog.num_samples;
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
- devc->cb_data);
-
- return;
-}
-
-static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c,
- int handle_packets)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_meta meta;
- struct sr_config *src;
- gint64 cur_time;
- int len;
-
- if (!(devc = sdi->priv))
- return;
-
- if (c == 0xff) {
- /* Device is in hold mode */
- devc->cur_mqflags |= SR_MQFLAG_HOLD;
-
- if (devc->hold_last_sent == 0) {
- /* First hold notification. */
- devc->hold_last_sent = g_get_monotonic_time();
- /* When the device leaves hold mode, it starts from scratch. */
- devc->state = ST_INIT;
- } else {
- cur_time = g_get_monotonic_time();
- if (cur_time - devc->hold_last_sent > HOLD_REPEAT_INTERVAL) {
- /* Force the last measurement out again. */
- devc->cmd = 0xa5;
- devc->token = TOKEN_MEAS_WAS_READOUT;
- if (handle_packets)
- process_mset(sdi);
- devc->hold_last_sent = cur_time;
- }
- }
-
- return;
- }
- devc->cur_mqflags &= ~SR_MQFLAG_HOLD;
- devc->hold_last_sent = 0;
-
- if (devc->state == ST_INIT) {
- if (c == 0xa5) {
- devc->cmd = c;
- devc->token = 0x00;
- devc->state = ST_GET_TOKEN;
- } else if (c == 0xbb) {
- devc->cmd = c;
- devc->buf_len = 0;
- devc->state = ST_GET_LOG_HEADER;
- sr_dbg("got command 0xbb");
- }
- } else if (devc->state == ST_GET_TOKEN) {
- devc->token = c;
- devc->buf_len = 0;
- len = find_token_payload_len(devc->token);
- if (len == -1 || len > 0) {
- devc->buf_len = 0;
- devc->state = ST_GET_DATA;
- } else {
- if (handle_packets)
- process_mset(sdi);
- devc->state = ST_INIT;
- }
- } else if (devc->state == ST_GET_DATA) {
- len = find_token_payload_len(devc->token);
- if (len == -1) {
- /* We don't know this token. */
- sr_dbg("Unknown 0xa5 token 0x%.2x", devc->token);
- if (c == 0xa5 || c == 0xbb) {
- /* Looks like a new command however. */
- if (handle_packets)
- process_mset(sdi);
- devc->state = ST_INIT;
- } else {
- devc->buf[devc->buf_len++] = c;
- if (devc->buf_len > BUF_SIZE) {
- /* Shouldn't happen, ignore. */
- devc->state = ST_INIT;
- }
- }
- } else {
- devc->buf[devc->buf_len++] = c;
- if (devc->buf_len == len) {
- if (handle_packets)
- process_mset(sdi);
- devc->state = ST_INIT;
- } else if (devc->buf_len > BUF_SIZE) {
- /* Shouldn't happen, ignore. */
- devc->state = ST_INIT;
- }
- }
- } else if (devc->state == ST_GET_LOG_HEADER) {
- sr_dbg("log header: 0x%.2x", c);
- if (devc->buf_len < 2)
- devc->buf[devc->buf_len++] = c;
- if (devc->buf_len == 2) {
- sr_dbg("Device says it has %d bytes stored.",
- ((devc->buf[0] << 8) + devc->buf[1]) - 100);
- devc->buf_len = 0;
- devc->state = ST_GET_LOG_RECORD_META;
- }
- } else if (devc->state == ST_GET_LOG_RECORD_META) {
- sr_dbg("log meta: 0x%.2x", c);
- if (c == RECORD_END) {
- devc->state = ST_INIT;
- /* Stop acquisition after transferring all stored
- * records. Otherwise the frontend would have no
- * way to tell where stored data ends and live
- * measurements begin. */
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
- devc->cb_data);
- } else if (c == RECORD_DATA) {
- devc->buf_len = 0;
- devc->state = ST_GET_LOG_RECORD_DATA;
- } else {
- /* RECORD_DBA/RECORD_DBC + 7 bytes of metadata */
- devc->buf[devc->buf_len++] = c;
- if (devc->buf_len < 8)
- /* Keep filling up the record header. */
- return;
- if (devc->buf[0] == RECORD_DBA)
- devc->cur_mqflags = SR_MQFLAG_SPL_FREQ_WEIGHT_A;
- else if (devc->buf[0] == RECORD_DBC)
- devc->cur_mqflags = SR_MQFLAG_SPL_FREQ_WEIGHT_C;
- else {
- /* Shouldn't happen. */
- sr_dbg("Unknown record token 0x%.2x", c);
- return;
- }
- packet.type = SR_DF_META;
- packet.payload = &meta;
- src = sr_config_new(SR_CONF_SAMPLE_INTERVAL,
- g_variant_new_uint64(devc->buf[7] * 1000));
- meta.config = g_slist_append(NULL, src);
- sr_session_send(devc->cb_data, &packet);
- g_free(src);
- devc->buf_len = 0;
- }
- } else if (devc->state == ST_GET_LOG_RECORD_DATA) {
- sr_dbg("log data: 0x%.2x", c);
- if (c == RECORD_DBA || c == RECORD_DBC || c == RECORD_DATA || c == RECORD_END) {
- /* Work around off-by-one bug in device firmware. This
- * happens only on the last record, i.e. before RECORD_END */
- if (devc->buf_len & 1)
- devc->buf_len--;
- /* Done with this set of samples */
- send_data(sdi, devc->buf, devc->buf_len / 2);
- devc->buf_len = 0;
-
- /* Process this meta marker in the right state. */
- devc->state = ST_GET_LOG_RECORD_META;
- process_byte(sdi, c, handle_packets);
- } else {
- devc->buf[devc->buf_len++] = c;
- if (devc->buf_len == SAMPLES_PER_PACKET * 2) {
- send_data(sdi, devc->buf, devc->buf_len / 2);
- devc->buf_len = 0;
- }
- }
- }
-
-}
-
-SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data)
-{
- const struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- unsigned char c, cmd;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- devc = sdi->priv;
- serial = sdi->conn;
- if (revents == G_IO_IN) {
- if (serial_read(serial, &c, 1) != 1)
- return TRUE;
- process_byte(sdi, c, TRUE);
-
- if (devc->enable_data_source_memory) {
- if (devc->state == ST_GET_LOG_HEADER) {
- /* Memory transfer started. */
- devc->enable_data_source_memory = FALSE;
- } else {
- /* Tell device to start transferring from memory. */
- cmd = CMD_TRANSFER_MEMORY;
- serial_write(serial, &cmd, 1);
- }
- }
- }
-
- return TRUE;
-}
-
-
-static int wait_for_token(const struct sr_dev_inst *sdi, int8_t *tokens, int timeout)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- gint64 start_time;
- int i;
- unsigned char c;
-
- serial = sdi->conn;
- devc = sdi->priv;
- devc->state = ST_INIT;
- start_time = g_get_monotonic_time() / 1000;
- while (TRUE) {
- if (serial_read(serial, &c, 1) != 1)
- /* Device might have gone away. */
- return SR_ERR;
- process_byte(sdi, c, FALSE);
- if (devc->state != ST_INIT)
- /* Wait for a whole packet to get processed. */
- continue;
- for (i = 0; tokens[i] != -1; i++) {
- if (devc->token == tokens[i]) {
- sr_spew("wait_for_token: got token 0x%.2x", devc->token);
- return SR_OK;
- }
- }
- if (timeout && g_get_monotonic_time() / 1000 - start_time > timeout)
- return SR_ERR_TIMEOUT;
- }
-
- return SR_OK;
-}
-
-/* cmd is the command to send, tokens are the tokens that denote the state
- * which the command affects. The first token is the desired state. */
-static int cem_dt_885x_toggle(const struct sr_dev_inst *sdi, uint8_t cmd,
- int8_t *tokens, int timeout)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- serial = sdi->conn;
- devc = sdi->priv;
-
- /* The device doesn't respond to commands very well. The
- * only thing to do is wait for the token that will confirm
- * whether the command worked or not, and resend if needed. */
- while (TRUE) {
- if (serial_write(serial, (const void *)&cmd, 1) != 1)
- return SR_ERR;
- if (wait_for_token(sdi, tokens, timeout) == SR_ERR)
- return SR_ERR;
- if (devc->token == tokens[0])
- /* It worked. */
- break;
- }
-
- return SR_OK;
-}
-
-SR_PRIV gboolean cem_dt_885x_recording_get(const struct sr_dev_inst *sdi,
- int *state)
-{
- struct dev_context *devc;
- int8_t tokens[5];
-
- devc = sdi->priv;
- if (devc->recording == -1) {
- /* Didn't pick up device state yet. */
- tokens[0] = TOKEN_RECORDING_ON;
- tokens[1] = TOKEN_RECORDING_OFF;
- tokens[2] = -1;
- if (wait_for_token(sdi, tokens, 510) != SR_OK)
- return SR_ERR;
- }
- *state = devc->token == TOKEN_RECORDING_ON;
-
- return SR_OK;
-}
-
-SR_PRIV int cem_dt_885x_recording_set(const struct sr_dev_inst *sdi,
- gboolean state)
-{
- struct dev_context *devc;
- int ret;
- int8_t tokens[5];
-
- devc = sdi->priv;
-
- /* The toggle below needs the desired state in first position. */
- if (state) {
- tokens[0] = TOKEN_RECORDING_ON;
- tokens[1] = TOKEN_RECORDING_OFF;
- } else {
- tokens[0] = TOKEN_RECORDING_OFF;
- tokens[1] = TOKEN_RECORDING_ON;
- }
- tokens[2] = -1;
-
- if (devc->recording == -1) {
- /* Didn't pick up device state yet. */
- if (wait_for_token(sdi, tokens, 0) != SR_OK)
- return SR_ERR;
- if (devc->token == tokens[0])
- /* Nothing to do. */
- return SR_OK;
- } else if (devc->recording == state)
- /* Nothing to do. */
- return SR_OK;
-
- /* Recording state notifications are sent at 2Hz, so allow just over
- * that, 510ms, for the state to come in. */
- ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_RECORDING, tokens, 510);
-
- return ret;
-}
-
-SR_PRIV int cem_dt_885x_weight_freq_get(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int cur_setting;
- int8_t tokens[5];
-
- devc = sdi->priv;
-
- cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
- if (cur_setting == 0) {
- /* Didn't pick up device state yet. */
- tokens[0] = TOKEN_WEIGHT_FREQ_A;
- tokens[1] = TOKEN_WEIGHT_FREQ_C;
- tokens[2] = -1;
- if (wait_for_token(sdi, tokens, 0) != SR_OK)
- return SR_ERR;
- if (devc->token == TOKEN_WEIGHT_FREQ_A)
- return SR_MQFLAG_SPL_FREQ_WEIGHT_A;
- else
- return SR_MQFLAG_SPL_FREQ_WEIGHT_C;
- } else
- return cur_setting;
-}
-
-SR_PRIV int cem_dt_885x_weight_freq_set(const struct sr_dev_inst *sdi, int freqw)
-{
- struct dev_context *devc;
- int cur_setting, ret;
- int8_t tokens[5];
-
- devc = sdi->priv;
-
- cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
- if (cur_setting == freqw)
- /* Already set to this frequency weighting. */
- return SR_OK;
-
- /* The toggle below needs the desired state in first position. */
- if (freqw == SR_MQFLAG_SPL_FREQ_WEIGHT_A) {
- tokens[0] = TOKEN_WEIGHT_FREQ_A;
- tokens[1] = TOKEN_WEIGHT_FREQ_C;
- } else {
- tokens[0] = TOKEN_WEIGHT_FREQ_C;
- tokens[1] = TOKEN_WEIGHT_FREQ_A;
- }
- tokens[2] = -1;
-
- if (cur_setting == 0) {
- /* Didn't pick up device state yet. */
- if (wait_for_token(sdi, tokens, 0) != SR_OK)
- return SR_ERR;
- if (devc->token == tokens[0])
- /* Nothing to do. */
- return SR_OK;
- }
-
- /* 10ms timeout seems to work best for this. */
- ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_WEIGHT_FREQ, tokens, 10);
-
- return ret;
-}
-
-SR_PRIV int cem_dt_885x_weight_time_get(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int cur_setting;
- int8_t tokens[5];
-
- devc = sdi->priv;
-
- cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
- if (cur_setting == 0) {
- /* Didn't pick up device state yet. */
- tokens[0] = TOKEN_WEIGHT_TIME_FAST;
- tokens[1] = TOKEN_WEIGHT_TIME_SLOW;
- tokens[2] = -1;
- if (wait_for_token(sdi, tokens, 0) != SR_OK)
- return SR_ERR;
- if (devc->token == TOKEN_WEIGHT_TIME_FAST)
- return SR_MQFLAG_SPL_TIME_WEIGHT_F;
- else
- return SR_MQFLAG_SPL_TIME_WEIGHT_S;
- } else
- return cur_setting;
-}
-
-SR_PRIV int cem_dt_885x_weight_time_set(const struct sr_dev_inst *sdi, int timew)
-{
- struct dev_context *devc;
- int cur_setting, ret;
- int8_t tokens[5];
-
- devc = sdi->priv;
-
- cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
- if (cur_setting == timew)
- /* Already set to this time weighting. */
- return SR_OK;
-
- /* The toggle below needs the desired state in first position. */
- if (timew == SR_MQFLAG_SPL_TIME_WEIGHT_F) {
- tokens[0] = TOKEN_WEIGHT_TIME_FAST;
- tokens[1] = TOKEN_WEIGHT_TIME_SLOW;
- } else {
- tokens[0] = TOKEN_WEIGHT_TIME_SLOW;
- tokens[1] = TOKEN_WEIGHT_TIME_FAST;
- }
- tokens[2] = -1;
-
- if (cur_setting == 0) {
- /* Didn't pick up device state yet. */
- if (wait_for_token(sdi, tokens, 0) != SR_OK)
- return SR_ERR;
- if (devc->token == tokens[0])
- /* Nothing to do. */
- return SR_OK;
- }
-
- /* 51ms timeout seems to work best for this. */
- ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_WEIGHT_TIME, tokens, 51);
-
- return ret;
-}
-
-SR_PRIV int cem_dt_885x_holdmode_get(const struct sr_dev_inst *sdi,
- gboolean *holdmode)
-{
- struct dev_context *devc;
- int8_t tokens[5];
-
- devc = sdi->priv;
-
- if (devc->cur_mqflags == 0) {
- tokens[0] = TOKEN_HOLD_MAX;
- tokens[1] = TOKEN_HOLD_MIN;
- tokens[2] = TOKEN_HOLD_NONE;
- tokens[3] = -1;
- if (wait_for_token(sdi, tokens, 0) != SR_OK)
- return SR_ERR;
- if (devc->token == TOKEN_HOLD_MAX)
- devc->cur_mqflags = SR_MQFLAG_MAX;
- else if (devc->token == TOKEN_HOLD_MIN)
- devc->cur_mqflags = SR_MQFLAG_MIN;
- }
- *holdmode = devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN);
-
- return SR_OK;
-}
-
-SR_PRIV int cem_dt_885x_holdmode_set(const struct sr_dev_inst *sdi, int holdmode)
-{
- struct dev_context *devc;
- int cur_setting, ret;
- int8_t tokens[5];
-
- devc = sdi->priv;
-
- /* The toggle below needs the desired state in first position. */
- if (holdmode == SR_MQFLAG_MAX) {
- tokens[0] = TOKEN_HOLD_MAX;
- tokens[1] = TOKEN_HOLD_MIN;
- tokens[2] = TOKEN_HOLD_NONE;
- } else if (holdmode == SR_MQFLAG_MIN) {
- tokens[0] = TOKEN_HOLD_MIN;
- tokens[1] = TOKEN_HOLD_MAX;
- tokens[2] = TOKEN_HOLD_NONE;
- } else {
- tokens[0] = TOKEN_HOLD_NONE;
- tokens[1] = TOKEN_HOLD_MAX;
- tokens[2] = TOKEN_HOLD_MIN;
- }
- tokens[3] = -1;
-
- if (devc->cur_mqflags == 0) {
- /* Didn't pick up device state yet. */
- if (wait_for_token(sdi, tokens, 0) != SR_OK)
- return SR_ERR;
- if (devc->token == tokens[0])
- /* Nothing to do. */
- return SR_OK;
- } else {
- cur_setting = devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN);
- if (cur_setting == holdmode)
- /* Already set correctly. */
- return SR_OK;
- }
-
- /* 51ms timeout seems to work best for this. */
- ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_HOLD_MAX_MIN, tokens, 51);
-
- return ret;
-}
-
-SR_PRIV int cem_dt_885x_meas_range_get(const struct sr_dev_inst *sdi,
- uint64_t *low, uint64_t *high)
-{
- struct dev_context *devc;
- int8_t tokens[5];
-
- devc = sdi->priv;
- if (devc->cur_meas_range == 0) {
- tokens[0] = TOKEN_MEAS_RANGE_30_130;
- tokens[1] = TOKEN_MEAS_RANGE_30_80;
- tokens[2] = TOKEN_MEAS_RANGE_50_100;
- tokens[3] = TOKEN_MEAS_RANGE_80_130;
- tokens[4] = -1;
- if (wait_for_token(sdi, tokens, 0) != SR_OK)
- return SR_ERR;
- devc->cur_meas_range = devc->token;
- }
-
- switch (devc->cur_meas_range) {
- case TOKEN_MEAS_RANGE_30_130:
- *low = 30;
- *high = 130;
- break;
- case TOKEN_MEAS_RANGE_30_80:
- *low = 30;
- *high = 80;
- break;
- case TOKEN_MEAS_RANGE_50_100:
- *low = 50;
- *high = 100;
- break;
- case TOKEN_MEAS_RANGE_80_130:
- *low = 80;
- *high = 130;
- break;
- default:
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int cem_dt_885x_meas_range_set(const struct sr_dev_inst *sdi,
- uint64_t low, uint64_t high)
-{
- struct dev_context *devc;
- int ret;
- int8_t token, tokens[6];
-
- devc = sdi->priv;
- if (low == 30 && high == 130)
- token = TOKEN_MEAS_RANGE_30_130;
- else if (low == 30 && high == 80)
- token = TOKEN_MEAS_RANGE_30_80;
- else if (low == 50 && high == 100)
- token = TOKEN_MEAS_RANGE_50_100;
- else if (low == 80 && high == 130)
- token = TOKEN_MEAS_RANGE_80_130;
- else
- return SR_ERR;
-
- sr_dbg("want 0x%.2x", token);
- /* The toggle below needs the desired state in first position. */
- tokens[0] = token;
- tokens[1] = TOKEN_MEAS_RANGE_30_130;
- tokens[2] = TOKEN_MEAS_RANGE_30_80;
- tokens[3] = TOKEN_MEAS_RANGE_50_100;
- tokens[4] = TOKEN_MEAS_RANGE_80_130;
- tokens[5] = -1;
-
- if (devc->cur_meas_range == 0) {
- /* 110ms should be enough for two of these announcements */
- if (wait_for_token(sdi, tokens, 110) != SR_OK)
- return SR_ERR;
- devc->cur_meas_range = devc->token;
- }
-
- if (devc->cur_meas_range == token)
- /* Already set to this range. */
- return SR_OK;
-
- /* For measurement range, it works best to ignore announcements of the
- * current setting and keep resending the toggle quickly. */
- tokens[1] = -1;
- ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_MEAS_RANGE, tokens, 11);
-
- return ret;
-}
-
-SR_PRIV int cem_dt_885x_power_off(const struct sr_dev_inst *sdi)
-{
- struct sr_serial_dev_inst *serial;
- char c, cmd;
-
- serial = sdi->conn;
-
- /* Reopen the port in non-blocking mode, so we can properly
- * detect when the device stops communicating. */
- serial_close(serial);
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return SR_ERR;
-
- cmd = CMD_TOGGLE_POWER_OFF;
- while (TRUE) {
- serial_flush(serial);
- if (serial_write(serial, (const void *)&cmd, 1) != 1)
- return SR_ERR;
- /* It never takes more than 23ms for the next token to arrive. */
- g_usleep(25 * 1000);
- if (serial_read(serial, &c, 1) != 1)
- /* Device is no longer responding. Good! */
- break;
- }
-
- /* In case the user manually turns on the device again, reset
- * the port back to blocking. */
- serial_close(serial);
- serial_open(serial, SERIAL_RDWR);
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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_CEM_DT_885X_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_CEM_DT_885X_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "cem-dt-885x"
-
-/* When retrieving samples from device memory, group this many
- * together into a sigrok packet. */
-#define SAMPLES_PER_PACKET 50
-
-/* Various temporary storage, at least 8 bytes. */
-#define BUF_SIZE SAMPLES_PER_PACKET * 2
-
-/* When in hold mode, force the last measurement out at this interval.
- * We're using 50ms, which duplicates the non-hold 20Hz update rate. */
-#define HOLD_REPEAT_INTERVAL 50 * 1000
-
-enum {
- TOKEN_WEIGHT_TIME_FAST = 0x02,
- TOKEN_WEIGHT_TIME_SLOW = 0x03,
- TOKEN_HOLD_MAX = 0x04,
- TOKEN_HOLD_MIN = 0x05,
- TOKEN_TIME = 0x06,
- TOKEN_MEAS_RANGE_OVER = 0x07,
- TOKEN_MEAS_RANGE_UNDER = 0x08,
- TOKEN_STORE_FULL = 0x09,
- TOKEN_RECORDING_ON = 0x0a,
- TOKEN_MEAS_WAS_READOUT = 0x0b,
- TOKEN_MEAS_WAS_BARGRAPH = 0x0c,
- TOKEN_MEASUREMENT = 0xd,
- TOKEN_HOLD_NONE = 0x0e,
- TOKEN_BATTERY_LOW = 0x0f,
- TOKEN_MEAS_RANGE_OK = 0x11,
- TOKEN_STORE_OK = 0x19,
- TOKEN_RECORDING_OFF = 0x1a,
- TOKEN_WEIGHT_FREQ_A = 0x1b,
- TOKEN_WEIGHT_FREQ_C = 0x1c,
- TOKEN_BATTERY_OK = 0x1f,
- TOKEN_MEAS_RANGE_30_80 = 0x30,
- TOKEN_MEAS_RANGE_30_130 = 0x40,
- TOKEN_MEAS_RANGE_50_100 = 0x4b,
- TOKEN_MEAS_RANGE_80_130 = 0x4c,
-};
-
-enum {
- CMD_TOGGLE_RECORDING = 0x55,
- CMD_TOGGLE_WEIGHT_FREQ = 0x99,
- CMD_TOGGLE_WEIGHT_TIME = 0x77,
- CMD_TOGGLE_HOLD_MAX_MIN = 0x11,
- CMD_TOGGLE_MEAS_RANGE = 0x88,
- CMD_TOGGLE_POWER_OFF = 0x33,
- CMD_TRANSFER_MEMORY = 0xac,
-};
-
-enum {
- RECORD_DBA = 0xaa,
- RECORD_DBC = 0xcc,
- RECORD_DATA = 0xac,
- RECORD_END = 0xdd,
-};
-
-enum {
- DATA_SOURCE_LIVE,
- DATA_SOURCE_MEMORY,
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Device state */
- uint64_t cur_mqflags;
- int recording;
- int cur_meas_range;
- int cur_data_source;
-
- /* Acquisition settings */
- uint64_t limit_samples;
-
- /* Operational state */
- int state;
- uint64_t num_samples;
- gboolean enable_data_source_memory;
-
- /* Temporary state across callbacks */
- void *cb_data;
- unsigned char cmd;
- unsigned char token;
- int buf_len;
- unsigned char buf[BUF_SIZE];
- float last_spl;
- gint64 hold_last_sent;
-};
-
-/* Parser state machine. */
-enum {
- ST_INIT,
- ST_GET_TOKEN,
- ST_GET_DATA,
- ST_GET_LOG_HEADER,
- ST_GET_LOG_RECORD_META,
- ST_GET_LOG_RECORD_DATA,
-};
-
-SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data);
-SR_PRIV int cem_dt_885x_recording_set(const struct sr_dev_inst *sdi, gboolean start);
-SR_PRIV gboolean cem_dt_885x_recording_get(const struct sr_dev_inst *sdi,
- int *state);
-SR_PRIV int cem_dt_885x_weight_freq_get(const struct sr_dev_inst *sdi);
-SR_PRIV int cem_dt_885x_weight_freq_set(const struct sr_dev_inst *sdi, int freqw);
-SR_PRIV int cem_dt_885x_weight_time_get(const struct sr_dev_inst *sdi);
-SR_PRIV int cem_dt_885x_weight_time_set(const struct sr_dev_inst *sdi, int timew);
-SR_PRIV int cem_dt_885x_holdmode_get(const struct sr_dev_inst *sdi,
- gboolean *holdmode);
-SR_PRIV int cem_dt_885x_holdmode_set(const struct sr_dev_inst *sdi, int holdmode);
-SR_PRIV int cem_dt_885x_meas_range_get(const struct sr_dev_inst *sdi,
- uint64_t *low, uint64_t *high);
-SR_PRIV int cem_dt_885x_meas_range_set(const struct sr_dev_inst *sdi,
- uint64_t low, uint64_t high);
-SR_PRIV int cem_dt_885x_power_off(const struct sr_dev_inst *sdi);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "protocol.h"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_THERMOMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
-};
-
-static const char *channel_names[] = {
- "T1", "T2", "T3", "T4",
- NULL,
-};
-
-SR_PRIV struct sr_dev_driver center_309_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_k204_driver_info;
-
-SR_PRIV const struct center_dev_info center_devs[] = {
- {
- "Center", "309", "9600/8n1", 4, 32000, 45,
- center_3xx_packet_valid,
- ¢er_309_driver_info, receive_data_CENTER_309,
- },
- {
- "Voltcraft", "K204", "9600/8n1", 4, 32000, 45,
- center_3xx_packet_valid,
- &voltcraft_k204_driver_info, receive_data_VOLTCRAFT_K204,
- },
-};
-
-static int dev_clear(int idx)
-{
- return std_dev_clear(center_devs[idx].di, NULL);
-}
-
-static int init(struct sr_context *sr_ctx, int idx)
-{
- sr_dbg("Selected '%s' subdriver.", center_devs[idx].di->name);
-
- return std_init(sr_ctx, center_devs[idx].di, LOG_PREFIX);
-}
-
-static GSList *center_scan(const char *conn, const char *serialcomm, int idx)
-{
- int i;
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_channel *ch;
- struct sr_serial_dev_inst *serial;
- GSList *devices;
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- drvc = center_devs[idx].di->priv;
- devices = NULL;
- serial_flush(serial);
-
- sr_info("Found device on port %s.", conn);
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, center_devs[idx].vendor,
- center_devs[idx].device, NULL)))
- goto scan_cleanup;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- goto scan_cleanup;
- }
-
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
-
- sdi->priv = devc;
- sdi->driver = center_devs[idx].di;
-
- for (i = 0; i < center_devs[idx].num_channels; i++) {
- if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG,
- TRUE, channel_names[i])))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
-scan_cleanup:
- serial_close(serial);
-
- return devices;
-}
-
-static GSList *scan(GSList *options, int idx)
-{
- struct sr_config *src;
- GSList *l, *devices;
- const char *conn, *serialcomm;
-
- 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 (!conn)
- return NULL;
-
- if (serialcomm) {
- /* Use the provided comm specs. */
- devices = center_scan(conn, serialcomm, idx);
- } else {
- /* Try the default. */
- devices = center_scan(conn, center_devs[idx].conn, idx);
- }
-
- return devices;
-}
-
-static GSList *dev_list(int idx)
-{
- return ((struct drv_context *)(center_devs[idx].di->priv))->instances;
-}
-
-static int cleanup(int idx)
-{
- return dev_clear(idx);
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- switch (id) {
- case SR_CONF_LIMIT_SAMPLES:
- if (g_variant_get_uint64(data) == 0)
- return SR_ERR_ARG;
- devc->limit_samples = g_variant_get_uint64(data);
- break;
- case SR_CONF_LIMIT_MSEC:
- if (g_variant_get_uint64(data) == 0)
- return SR_ERR_ARG;
- devc->limit_msec = g_variant_get_uint64(data);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data, int idx)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- devc->cb_data = cb_data;
- devc->num_samples = 0;
- devc->starttime = g_get_monotonic_time();
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Poll every 500ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 500,
- center_devs[idx].receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- return std_serial_dev_acquisition_stop(sdi, cb_data,
- std_serial_dev_close, sdi->conn, LOG_PREFIX);
-}
-
-/* Driver-specific API function wrappers */
-#define HW_INIT(X) \
-static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
-#define HW_CLEANUP(X) \
-static int cleanup_##X(void) { return cleanup(X); }
-#define HW_SCAN(X) \
-static GSList *scan_##X(GSList *options) { return scan(options, X); }
-#define HW_DEV_LIST(X) \
-static GSList *dev_list_##X(void) { return dev_list(X); }
-#define HW_DEV_CLEAR(X) \
-static int dev_clear_##X(void) { return dev_clear(X); }
-#define HW_DEV_ACQUISITION_START(X) \
-static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
-void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
-
-/* Driver structs and API function wrappers */
-#define DRV(ID, ID_UPPER, NAME, LONGNAME) \
-HW_INIT(ID_UPPER) \
-HW_CLEANUP(ID_UPPER) \
-HW_SCAN(ID_UPPER) \
-HW_DEV_LIST(ID_UPPER) \
-HW_DEV_CLEAR(ID_UPPER) \
-HW_DEV_ACQUISITION_START(ID_UPPER) \
-SR_PRIV struct sr_dev_driver ID##_driver_info = { \
- .name = NAME, \
- .longname = LONGNAME, \
- .api_version = 1, \
- .init = init_##ID_UPPER, \
- .cleanup = cleanup_##ID_UPPER, \
- .scan = scan_##ID_UPPER, \
- .dev_list = dev_list_##ID_UPPER, \
- .dev_clear = dev_clear_##ID_UPPER, \
- .config_get = NULL, \
- .config_set = config_set, \
- .config_list = config_list, \
- .dev_open = std_serial_dev_open, \
- .dev_close = std_serial_dev_close, \
- .dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
- .dev_acquisition_stop = dev_acquisition_stop, \
- .priv = NULL, \
-};
-
-DRV(center_309, CENTER_309, "center-309", "Center 309")
-DRV(voltcraft_k204, VOLTCRAFT_K204, "voltcraft-k204", "Voltcraft K204")
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "protocol.h"
-
-struct center_info {
- float temp[4];
- gboolean rec, std, max, min, maxmin, t1t2, rel, hold, lowbat, celsius;
- gboolean memfull, autooff;
- gboolean mode_std, mode_rel, mode_max, mode_min, mode_maxmin;
-};
-
-static int center_send(struct sr_serial_dev_inst *serial, const char *cmd)
-{
- int ret;
-
- if ((ret = serial_write(serial, cmd, strlen(cmd))) < 0) {
- sr_err("Error sending '%s' command: %d.", cmd, ret);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV gboolean center_3xx_packet_valid(const uint8_t *buf)
-{
- return (buf[0] == 0x02 && buf[44] == 0x03);
-}
-
-static void log_packet(const uint8_t *buf, int idx)
-{
- int i;
- GString *s;
-
- s = g_string_sized_new(100);
- g_string_printf(s, "Packet: ");
- for (i = 0; i < center_devs[idx].packet_size; i++)
- g_string_append_printf(s, "%02x ", buf[i]);
- sr_spew("%s", s->str);
- g_string_free(s, TRUE);
-}
-
-static int packet_parse(const uint8_t *buf, int idx, struct center_info *info)
-{
- int i;
- uint16_t temp_u16;
-
- log_packet(buf, idx);
-
- /* Byte 0: Always 0x02. */
-
- /* Byte 1: Various status bits. */
- info->rec = (buf[1] & (1 << 0)) != 0;
- info->mode_std = (((buf[1] >> 1) & 0x3) == 0);
- info->mode_max = (((buf[1] >> 1) & 0x3) == 1);
- info->mode_min = (((buf[1] >> 1) & 0x3) == 2);
- info->mode_maxmin = (((buf[1] >> 1) & 0x3) == 3);
- /* TODO: Rel. Not available on all models. */
- info->t1t2 = (buf[1] & (1 << 3)) != 0;
- info->rel = (buf[1] & (1 << 4)) != 0;
- info->hold = (buf[1] & (1 << 5)) != 0;
- info->lowbat = (buf[1] & (1 << 6)) != 0;
- info->celsius = (buf[1] & (1 << 7)) != 0;
-
- /* Byte 2: Further status bits. */
- info->memfull = (buf[2] & (1 << 0)) != 0;
- info->autooff = (buf[2] & (1 << 7)) != 0;
-
- /* Byte 7+8/9+10/11+12/13+14: channel T1/T2/T3/T4 temperature. */
- for (i = 0; i < 4; i++) {
- temp_u16 = buf[8 + (i * 2)];
- temp_u16 |= ((uint16_t)buf[7 + (i * 2)] << 8);
- info->temp[i] = (float)temp_u16;
- }
-
- /* Byte 43: Specifies whether we need to divide the value(s) by 10. */
- for (i = 0; i < 4; i++) {
- /* Bit = 0: Divide by 10. Bit = 1: Don't divide by 10. */
- if ((buf[43] & (1 << i)) == 0)
- info->temp[i] /= 10;
- }
-
- /* Bytes 39-42: Overflow/overlimit bits, depending on mode. */
- for (i = 0; i < 4; i++) {
- if (info->mode_std && ((buf[39] & (1 << i)) != 0))
- info->temp[i] = INFINITY;
- /* TODO: Rel. Not available on all models. */
- // if (info->mode_rel && ((buf[40] & (1 << i)) != 0))
- // info->temp[i] = INFINITY;
- if (info->mode_max && ((buf[41] & (1 << i)) != 0))
- info->temp[i] = INFINITY;
- if (info->mode_min && ((buf[42] & (1 << i)) != 0))
- info->temp[i] = INFINITY;
- /* TODO: Minmax? */
- }
-
- /* Byte 44: Always 0x03. */
-
- return SR_OK;
-}
-
-static int handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi, int idx)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct dev_context *devc;
- struct center_info info;
- GSList *l;
- int i, ret;
-
- devc = sdi->priv;
-
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
- memset(&info, 0, sizeof(struct center_info));
-
- ret = packet_parse(buf, idx, &info);
- if (ret < 0) {
- sr_err("Failed to parse packet.");
- return SR_ERR;
- }
-
- /* Common values for all 4 channels. */
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- analog.mq = SR_MQ_TEMPERATURE;
- analog.unit = (info.celsius) ? SR_UNIT_CELSIUS : SR_UNIT_FAHRENHEIT;
- analog.num_samples = 1;
-
- /* Send the values for T1 - T4. */
- for (i = 0; i < 4; i++) {
- l = NULL;
- l = g_slist_append(l, g_slist_nth_data(sdi->channels, i));
- analog.channels = l;
- analog.data = &(info.temp[i]);
- sr_session_send(devc->cb_data, &packet);
- g_slist_free(l);
- }
-
- devc->num_samples++;
-
- return SR_OK;
-}
-
-/* Return TRUE if a full packet was parsed, FALSE otherwise. */
-static gboolean handle_new_data(struct sr_dev_inst *sdi, int idx)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- int len, i, offset = 0, ret = FALSE;
-
- devc = sdi->priv;
- serial = sdi->conn;
-
- /* Try to get as much data as the buffer can hold. */
- len = SERIAL_BUFSIZE - devc->buflen;
- len = serial_read(serial, devc->buf + devc->buflen, len);
- if (len < 1) {
- sr_err("Serial port read error: %d.", len);
- return FALSE;
- }
-
- devc->buflen += len;
-
- /* Now look for packets in that data. */
- while ((devc->buflen - offset) >= center_devs[idx].packet_size) {
- if (center_devs[idx].packet_valid(devc->buf + offset)) {
- handle_packet(devc->buf + offset, sdi, idx);
- offset += center_devs[idx].packet_size;
- ret = TRUE;
- } else {
- offset++;
- }
- }
-
- /* If we have any data left, move it to the beginning of our buffer. */
- for (i = 0; i < devc->buflen - offset; i++)
- devc->buf[i] = devc->buf[offset + i];
- devc->buflen -= offset;
-
- return ret;
-}
-
-static int receive_data(int fd, int revents, int idx, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- int64_t t;
- static gboolean request_new_packet = TRUE;
- struct sr_serial_dev_inst *serial;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- serial = sdi->conn;
-
- if (revents == G_IO_IN) {
- /* New data arrived. */
- request_new_packet = handle_new_data(sdi, idx);
- } else {
- /*
- * Timeout. Send "A" to request a packet, but then don't send
- * further "A" commands until we received a full packet first.
- */
- if (request_new_packet) {
- center_send(serial, "A");
- request_new_packet = FALSE;
- }
- }
-
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
-
- if (devc->limit_msec) {
- t = (g_get_monotonic_time() - devc->starttime) / 1000;
- if (t > (int64_t)devc->limit_msec) {
- sr_info("Requested time limit reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
- }
-
- return TRUE;
-}
-
-#define RECEIVE_DATA(ID_UPPER) \
-SR_PRIV int receive_data_##ID_UPPER(int fd, int revents, void *cb_data) { \
- return receive_data(fd, revents, ID_UPPER, cb_data); }
-
-/* Driver-specific receive_data() wrappers */
-RECEIVE_DATA(CENTER_309)
-RECEIVE_DATA(VOLTCRAFT_K204)
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef LIBSIGROK_HARDWARE_CENTER_3XX_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_CENTER_3XX_PROTOCOL_H
-
-#include <stdint.h>
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "center-3xx"
-
-/* Note: When adding entries here, don't forget to update CENTER_DEV_COUNT. */
-enum {
- CENTER_309,
- VOLTCRAFT_K204,
-};
-
-#define CENTER_DEV_COUNT 2
-
-struct center_dev_info {
- char *vendor;
- char *device;
- char *conn;
- int num_channels;
- uint32_t max_sample_points;
- uint8_t packet_size;
- gboolean (*packet_valid)(const uint8_t *);
- struct sr_dev_driver *di;
- int (*receive_data)(int, int, void *);
-};
-
-extern SR_PRIV const struct center_dev_info center_devs[CENTER_DEV_COUNT];
-
-#define SERIAL_BUFSIZE 256
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /** The current sampling limit (in number of samples). */
- uint64_t limit_samples;
-
- /** The current sampling limit (in ms). */
- uint64_t limit_msec;
-
- /** Opaque pointer passed in by the frontend. */
- void *cb_data;
-
- /** The current number of already received samples. */
- uint64_t num_samples;
-
- int64_t starttime;
-
- uint8_t buf[SERIAL_BUFSIZE];
- int bufoffset;
- int buflen;
-};
-
-SR_PRIV gboolean center_3xx_packet_valid(const uint8_t *buf);
-
-SR_PRIV int receive_data_CENTER_309(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_K204(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "protocol.h"
-
-SR_PRIV struct sr_dev_driver chronovu_la_driver_info;
-static struct sr_dev_driver *di = &chronovu_la_driver_info;
-
-static const int32_t hwcaps[] = {
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_SAMPLERATE,
- SR_CONF_TRIGGER_MATCH,
- SR_CONF_LIMIT_MSEC, /* TODO: Not yet implemented. */
- SR_CONF_LIMIT_SAMPLES, /* TODO: Not yet implemented. */
-};
-
-static const int32_t trigger_matches[] = {
- SR_TRIGGER_ZERO,
- SR_TRIGGER_ONE,
- SR_TRIGGER_RISING,
- SR_TRIGGER_FALLING,
-};
-
-/* The ChronoVu LA8/LA16 can have multiple VID/PID pairs. */
-static struct {
- uint16_t vid;
- uint16_t pid;
- int model;
- const char *iproduct;
-} vid_pid[] = {
- { 0x0403, 0x6001, CHRONOVU_LA8, "ChronoVu LA8" },
- { 0x0403, 0x8867, CHRONOVU_LA8, "ChronoVu LA8" },
- { 0x0403, 0x6001, CHRONOVU_LA16, "ChronoVu LA16" },
- { 0x0403, 0x8867, CHRONOVU_LA16, "ChronoVu LA16" },
-};
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
-
-static void clear_helper(void *priv)
-{
- struct dev_context *devc;
-
- devc = priv;
-
- ftdi_free(devc->ftdic);
- g_free(devc->final_buf);
-}
-
-static int dev_clear(void)
-{
- return std_dev_clear(di, clear_helper);
-}
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static int add_device(int idx, int model, GSList **devices)
-{
- int ret;
- unsigned int i;
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_channel *ch;
-
- ret = SR_OK;
-
- drvc = di->priv;
-
- /* Allocate memory for our private device context. */
- devc = g_try_malloc(sizeof(struct dev_context));
-
- /* Set some sane defaults. */
- devc->prof = &cv_profiles[model];
- devc->ftdic = NULL; /* Will be set in the open() API call. */
- devc->cur_samplerate = 0; /* Set later (different for LA8/LA16). */
- devc->limit_msec = 0;
- devc->limit_samples = 0;
- devc->cb_data = NULL;
- memset(devc->mangled_buf, 0, BS);
- devc->final_buf = NULL;
- devc->trigger_pattern = 0x0000; /* Irrelevant, see trigger_mask. */
- devc->trigger_mask = 0x0000; /* All channels: "don't care". */
- devc->trigger_edgemask = 0x0000; /* All channels: "state triggered". */
- devc->trigger_found = 0;
- devc->done = 0;
- devc->block_counter = 0;
- devc->divcount = 0;
- devc->usb_vid = vid_pid[idx].vid;
- devc->usb_pid = vid_pid[idx].pid;
- memset(devc->samplerates, 0, sizeof(uint64_t) * 255);
-
- /* Allocate memory where we'll store the de-mangled data. */
- if (!(devc->final_buf = g_try_malloc(SDRAM_SIZE))) {
- sr_err("Failed to allocate memory for sample buffer.");
- ret = SR_ERR_MALLOC;
- goto err_free_devc;
- }
-
- /* We now know the device, set its max. samplerate as default. */
- devc->cur_samplerate = devc->prof->max_samplerate;
-
- /* Register the device with libsigrok. */
- sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING,
- "ChronoVu", devc->prof->modelname, NULL);
- if (!sdi) {
- sr_err("Failed to create device instance.");
- ret = SR_ERR;
- goto err_free_final_buf;
- }
- sdi->driver = di;
- sdi->priv = devc;
-
- for (i = 0; i < devc->prof->num_channels; i++) {
- if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
- cv_channel_names[i]))) {
- ret = SR_ERR;
- goto err_free_dev_inst;
- }
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- *devices = g_slist_append(*devices, sdi);
- drvc->instances = g_slist_append(drvc->instances, sdi);
-
- return SR_OK;
-
-err_free_dev_inst:
- sr_dev_inst_free(sdi);
-err_free_final_buf:
- g_free(devc->final_buf);
-err_free_devc:
- g_free(devc);
-
- return ret;
-}
-
-static GSList *scan(GSList *options)
-{
- int ret;
- unsigned int i;
- GSList *devices;
- struct ftdi_context *ftdic;
-
- (void)options;
-
- devices = NULL;
-
- /* Allocate memory for the FTDI context and initialize it. */
- if (!(ftdic = ftdi_new())) {
- sr_err("Failed to initialize libftdi.");
- return NULL;
- }
-
- /* Check for LA8 and/or LA16 devices with various VID/PIDs. */
- for (i = 0; i < ARRAY_SIZE(vid_pid); i++) {
- ret = ftdi_usb_open_desc(ftdic, vid_pid[i].vid,
- vid_pid[i].pid, vid_pid[i].iproduct, NULL);
- /* Show errors other than "device not found". */
- if (ret < 0 && ret != -3)
- sr_dbg("Error finding/opening device (%d): %s.",
- ret, ftdi_get_error_string(ftdic));
- if (ret < 0)
- continue; /* No device found, or not usable. */
-
- sr_dbg("Found %s device (%04x:%04x).",
- vid_pid[i].iproduct, vid_pid[i].vid, vid_pid[i].pid);
-
- if ((ret = add_device(i, vid_pid[i].model, &devices)) < 0)
- sr_dbg("Failed to add device: %d.", ret);
-
- if ((ret = ftdi_usb_close(ftdic)) < 0)
- sr_dbg("Failed to close FTDI device (%d): %s.",
- ret, ftdi_get_error_string(ftdic));
- }
-
- /* Close USB device, deinitialize and free the FTDI context. */
- ftdi_free(ftdic);
- ftdic = NULL;
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int ret;
-
- ret = SR_ERR;
-
- if (!(devc = sdi->priv))
- return SR_ERR_BUG;
-
- /* Allocate memory for the FTDI context and initialize it. */
- if (!(devc->ftdic = ftdi_new())) {
- sr_err("Failed to initialize libftdi.");
- return SR_ERR;
- }
-
- sr_dbg("Opening %s device (%04x:%04x).", devc->prof->modelname,
- devc->usb_vid, devc->usb_pid);
-
- /* Open the device. */
- if ((ret = ftdi_usb_open_desc(devc->ftdic, devc->usb_vid,
- devc->usb_pid, devc->prof->iproduct, NULL)) < 0) {
- sr_err("Failed to open FTDI device (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- goto err_ftdi_free;
- }
- sr_dbg("Device opened successfully.");
-
- /* Purge RX/TX buffers in the FTDI chip. */
- if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
- sr_err("Failed to purge FTDI buffers (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- goto err_ftdi_free;
- }
- sr_dbg("FTDI buffers purged successfully.");
-
- /* Enable flow control in the FTDI chip. */
- if ((ret = ftdi_setflowctrl(devc->ftdic, SIO_RTS_CTS_HS)) < 0) {
- sr_err("Failed to enable FTDI flow control (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- goto err_ftdi_free;
- }
- sr_dbg("FTDI flow control enabled successfully.");
-
- /* Wait 100ms. */
- g_usleep(100 * 1000);
-
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-
-err_ftdi_free:
- ftdi_free(devc->ftdic); /* Close device (if open), free FTDI context. */
- devc->ftdic = NULL;
- return ret;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- int ret;
- struct dev_context *devc;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_OK;
-
- devc = sdi->priv;
-
- if (devc->ftdic && (ret = ftdi_usb_close(devc->ftdic)) < 0)
- sr_err("Failed to close FTDI device (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- return dev_clear();
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- if (!sdi || !(devc = sdi->priv))
- return SR_ERR_BUG;
- *data = g_variant_new_uint64(devc->cur_samplerate);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv))
- return SR_ERR_BUG;
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- if (cv_set_samplerate(sdi, g_variant_get_uint64(data)) < 0)
- return SR_ERR;
- break;
- case SR_CONF_LIMIT_MSEC:
- if (g_variant_get_uint64(data) == 0)
- return SR_ERR_ARG;
- devc->limit_msec = g_variant_get_uint64(data);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- if (g_variant_get_uint64(data) == 0)
- return SR_ERR_ARG;
- devc->limit_samples = g_variant_get_uint64(data);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- GVariant *gvar, *grange[2];
- GVariantBuilder gvb;
- struct dev_context *devc;
-
- (void)cg;
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- if (!sdi || !sdi->priv || !(devc = sdi->priv))
- return SR_ERR_BUG;
- cv_fill_samplerates_if_needed(sdi);
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- devc->samplerates,
- ARRAY_SIZE(devc->samplerates),
- sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- if (!sdi || !sdi->priv || !(devc = sdi->priv) || !devc->prof)
- return SR_ERR_BUG;
- grange[0] = g_variant_new_uint64(0);
- if (devc->prof->model == CHRONOVU_LA8)
- grange[1] = g_variant_new_uint64(MAX_NUM_SAMPLES);
- else
- grange[1] = g_variant_new_uint64(MAX_NUM_SAMPLES / 2);
- *data = g_variant_new_tuple(grange, 2);
- break;
- case SR_CONF_TRIGGER_MATCH:
- if (!sdi || !sdi->priv || !(devc = sdi->priv) || !devc->prof)
- return SR_ERR_BUG;
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- trigger_matches, devc->prof->num_trigger_matches,
- sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int receive_data(int fd, int revents, void *cb_data)
-{
- int i, ret;
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
-
- (void)fd;
- (void)revents;
-
- if (!(sdi = cb_data)) {
- sr_err("cb_data was NULL.");
- return FALSE;
- }
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return FALSE;
- }
-
- if (!devc->ftdic) {
- sr_err("devc->ftdic was NULL.");
- return FALSE;
- }
-
- /* Get one block of data. */
- if ((ret = cv_read_block(devc)) < 0) {
- sr_err("Failed to read data block: %d.", ret);
- dev_acquisition_stop(sdi, sdi);
- return FALSE;
- }
-
- /* We need to get exactly NUM_BLOCKS blocks (i.e. 8MB) of data. */
- if (devc->block_counter != (NUM_BLOCKS - 1)) {
- devc->block_counter++;
- return TRUE;
- }
-
- sr_dbg("Sampling finished, sending data to session bus now.");
-
- /*
- * All data was received and demangled, send it to the session bus.
- *
- * Note: Due to the method how data is spread across the 8MByte of
- * SDRAM, we can _not_ send it to the session bus in a streaming
- * manner while we receive it. We have to receive and de-mangle the
- * full 8MByte first, only then the whole buffer contains valid data.
- */
- for (i = 0; i < NUM_BLOCKS; i++)
- cv_send_block_to_session_bus(devc, i);
-
- dev_acquisition_stop(sdi, sdi);
-
- return TRUE;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- uint8_t buf[8];
- int bytes_to_write, bytes_written;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- if (!devc->ftdic) {
- sr_err("devc->ftdic was NULL.");
- return SR_ERR_BUG;
- }
-
- devc->divcount = cv_samplerate_to_divcount(sdi, devc->cur_samplerate);
- if (devc->divcount == 0xff) {
- sr_err("Invalid divcount/samplerate.");
- return SR_ERR;
- }
-
- if (cv_convert_trigger(sdi) != SR_OK) {
- sr_err("Failed to configure trigger.");
- return SR_ERR;
- }
-
- /* Fill acquisition parameters into buf[]. */
- if (devc->prof->model == CHRONOVU_LA8) {
- buf[0] = devc->divcount;
- buf[1] = 0xff; /* This byte must always be 0xff. */
- buf[2] = devc->trigger_pattern & 0xff;
- buf[3] = devc->trigger_mask & 0xff;
- bytes_to_write = 4;
- } else {
- buf[0] = devc->divcount;
- buf[1] = 0xff; /* This byte must always be 0xff. */
- buf[2] = (devc->trigger_pattern & 0xff00) >> 8; /* LSB */
- buf[3] = (devc->trigger_pattern & 0x00ff) >> 0; /* MSB */
- buf[4] = (devc->trigger_mask & 0xff00) >> 8; /* LSB */
- buf[5] = (devc->trigger_mask & 0x00ff) >> 0; /* MSB */
- buf[6] = (devc->trigger_edgemask & 0xff00) >> 8; /* LSB */
- buf[7] = (devc->trigger_edgemask & 0x00ff) >> 0; /* MSB */
- bytes_to_write = 8;
- }
-
- /* Start acquisition. */
- bytes_written = cv_write(devc, buf, bytes_to_write);
-
- if (bytes_written < 0 || bytes_written != bytes_to_write) {
- sr_err("Acquisition failed to start.");
- return SR_ERR;
- }
-
- sr_dbg("Hardware acquisition started successfully.");
-
- devc->cb_data = cb_data;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(sdi, LOG_PREFIX);
-
- /* Time when we should be done (for detecting trigger timeouts). */
- devc->done = (devc->divcount + 1) * devc->prof->trigger_constant +
- g_get_monotonic_time() + (10 * G_TIME_SPAN_SECOND);
- devc->block_counter = 0;
- devc->trigger_found = 0;
-
- /* Hook up a dummy handler to receive data from the device. */
- sr_session_source_add(sdi->session, -1, G_IO_IN, 0, receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct sr_datafeed_packet packet;
-
- (void)cb_data;
-
- sr_dbg("Stopping acquisition.");
- sr_session_source_remove(sdi->session, -1);
-
- /* Send end packet to the session bus. */
- sr_dbg("Sending SR_DF_END.");
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver chronovu_la_driver_info = {
- .name = "chronovu-la",
- .longname = "ChronoVu LA8/LA16",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = 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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "protocol.h"
-
-SR_PRIV const struct cv_profile cv_profiles[] = {
- { CHRONOVU_LA8, "LA8", "ChronoVu LA8", 8, SR_MHZ(100), 2, 0.8388608 },
- { CHRONOVU_LA16, "LA16", "ChronoVu LA16", 16, SR_MHZ(200), 4, 0.042 },
- { 0, NULL, NULL, 0, 0, 0, 0.0 },
-};
-
-/* LA8: channels are numbered 0-7. LA16: channels are numbered 0-15. */
-SR_PRIV const char *cv_channel_names[] = {
- "0", "1", "2", "3", "4", "5", "6", "7",
- "8", "9", "10", "11", "12", "13", "14", "15",
-};
-
-static int close_usb_reset_sequencer(struct dev_context *devc);
-
-SR_PRIV void cv_fill_samplerates_if_needed(const struct sr_dev_inst *sdi)
-{
- int i;
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- if (devc->samplerates[0] != 0)
- return;
-
- for (i = 0; i < 255; i++)
- devc->samplerates[254 - i] = devc->prof->max_samplerate / (i + 1);
-}
-
-/**
- * Check if the given samplerate is supported by the hardware.
- *
- * @param sdi Device instance.
- * @param samplerate The samplerate (in Hz) to check.
- *
- * @return 1 if the samplerate is supported/valid, 0 otherwise.
- */
-static int is_valid_samplerate(const struct sr_dev_inst *sdi,
- uint64_t samplerate)
-{
- int i;
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- cv_fill_samplerates_if_needed(sdi);
-
- for (i = 0; i < 255; i++) {
- if (devc->samplerates[i] == samplerate)
- return 1;
- }
-
- sr_err("Invalid samplerate (%" PRIu64 "Hz).", samplerate);
-
- return 0;
-}
-
-/**
- * Convert a samplerate (in Hz) to the 'divcount' value the device wants.
- *
- * The divcount value can be 0x00 - 0xfe (0xff is not valid).
- *
- * LA8:
- * sample period = (divcount + 1) * 10ns.
- * divcount = 0x00: 10ns period, 100MHz samplerate.
- * divcount = 0xfe: 2550ns period, 392.15kHz samplerate.
- *
- * LA16:
- * sample period = (divcount + 1) * 5ns.
- * divcount = 0x00: 5ns period, 200MHz samplerate.
- * divcount = 0xfe: 1275ns period, ~784.31kHz samplerate.
- *
- * @param sdi Device instance.
- * @param samplerate The samplerate in Hz.
- *
- * @return The divcount value as needed by the hardware, or 0xff upon errors.
- */
-SR_PRIV uint8_t cv_samplerate_to_divcount(const struct sr_dev_inst *sdi,
- uint64_t samplerate)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- if (samplerate == 0) {
- sr_err("Can't convert invalid samplerate of 0 Hz.");
- return 0xff;
- }
-
- if (!is_valid_samplerate(sdi, samplerate)) {
- sr_err("Can't get divcount, samplerate invalid.");
- return 0xff;
- }
-
- return (devc->prof->max_samplerate / samplerate) - 1;
-}
-
-/**
- * Write data of a certain length to the FTDI device.
- *
- * @param devc The struct containing private per-device-instance data. Must not
- * be NULL. devc->ftdic must not be NULL either.
- * @param buf The buffer containing the data to write. Must not be NULL.
- * @param size The number of bytes to write. Must be > 0.
- *
- * @return The number of bytes written, or a negative value upon errors.
- */
-SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size)
-{
- int bytes_written;
-
- /* Note: Caller ensures devc/devc->ftdic/buf != NULL and size > 0. */
-
- bytes_written = ftdi_write_data(devc->ftdic, buf, size);
-
- if (bytes_written < 0) {
- sr_err("Failed to write data (%d): %s.",
- bytes_written, ftdi_get_error_string(devc->ftdic));
- (void) close_usb_reset_sequencer(devc); /* Ignore errors. */
- } else if (bytes_written != size) {
- sr_err("Failed to write data, only %d/%d bytes written.",
- size, bytes_written);
- (void) close_usb_reset_sequencer(devc); /* Ignore errors. */
- }
-
- return bytes_written;
-}
-
-/**
- * Read a certain amount of bytes from the FTDI device.
- *
- * @param devc The struct containing private per-device-instance data. Must not
- * be NULL. devc->ftdic must not be NULL either.
- * @param buf The buffer where the received data will be stored. Must not
- * be NULL.
- * @param size The number of bytes to read. Must be >= 1.
- *
- * @return The number of bytes read, or a negative value upon errors.
- */
-static int cv_read(struct dev_context *devc, uint8_t *buf, int size)
-{
- int bytes_read;
-
- /* Note: Caller ensures devc/devc->ftdic/buf != NULL and size > 0. */
-
- bytes_read = ftdi_read_data(devc->ftdic, buf, size);
-
- if (bytes_read < 0) {
- sr_err("Failed to read data (%d): %s.",
- bytes_read, ftdi_get_error_string(devc->ftdic));
- } else if (bytes_read != size) {
- // sr_err("Failed to read data, only %d/%d bytes read.",
- // bytes_read, size);
- }
-
- return bytes_read;
-}
-
-/**
- * Close the USB port and reset the sequencer logic.
- *
- * @param devc The struct containing private per-device-instance data.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
- */
-static int close_usb_reset_sequencer(struct dev_context *devc)
-{
- /* Magic sequence of bytes for resetting the sequencer logic. */
- uint8_t buf[8] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
- int ret;
-
- /* Note: Caller checked that devc and devc->ftdic != NULL. */
-
- if (devc->ftdic->usb_dev) {
- /* Reset the sequencer logic, then wait 100ms. */
- sr_dbg("Resetting sequencer logic.");
- (void) cv_write(devc, buf, 8); /* Ignore errors. */
- g_usleep(100 * 1000);
-
- /* Purge FTDI buffers, then reset and close the FTDI device. */
- sr_dbg("Purging buffers, resetting+closing FTDI device.");
-
- /* Log errors, but ignore them (i.e., don't abort). */
- if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0)
- sr_err("Failed to purge FTDI buffers (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- if ((ret = ftdi_usb_reset(devc->ftdic)) < 0)
- sr_err("Failed to reset FTDI device (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- if ((ret = ftdi_usb_close(devc->ftdic)) < 0)
- sr_err("Failed to close FTDI device (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- }
-
- /* Close USB device, deinitialize and free the FTDI context. */
- ftdi_free(devc->ftdic);
- devc->ftdic = NULL;
-
- return SR_OK;
-}
-
-/**
- * Reset the ChronoVu device.
- *
- * A reset is required after a failed read/write operation or upon timeouts.
- *
- * @param devc The struct containing private per-device-instance data.
- *
- * @return SR_OK upon success, SR_ERR upon failure.
- */
-static int reset_device(struct dev_context *devc)
-{
- uint8_t buf[BS];
- gint64 done, now;
- int bytes_read;
-
- /* Note: Caller checked that devc and devc->ftdic != NULL. */
-
- sr_dbg("Resetting the device.");
-
- /*
- * Purge pending read data from the FTDI hardware FIFO until
- * no more data is left, or a timeout occurs (after 20s).
- */
- done = (20 * G_TIME_SPAN_SECOND) + g_get_monotonic_time();
- do {
- /* Try to read bytes until none are left (or errors occur). */
- bytes_read = cv_read(devc, (uint8_t *)&buf, BS);
- now = g_get_monotonic_time();
- } while ((done > now) && (bytes_read > 0));
-
- /* Reset the sequencer logic and close the USB port. */
- (void) close_usb_reset_sequencer(devc); /* Ignore errors. */
-
- sr_dbg("Device reset finished.");
-
- return SR_OK;
-}
-
-SR_PRIV int cv_convert_trigger(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_trigger *trigger;
- struct sr_trigger_stage *stage;
- struct sr_trigger_match *match;
- const GSList *l, *m;
- uint16_t channel_bit;
-
- devc = sdi->priv;
- devc->trigger_pattern = 0x0000; /* Default to "low" trigger. */
- devc->trigger_mask = 0x0000; /* Default to "don't care". */
- devc->trigger_edgemask = 0x0000; /* Default to "state triggered". */
-
- if (!(trigger = sr_session_trigger_get(sdi->session)))
- return SR_OK;
-
- if (g_slist_length(trigger->stages) > 1) {
- sr_err("This device only supports 1 trigger stage.");
- return SR_ERR;
- }
-
- for (l = trigger->stages; l; l = l->next) {
- stage = l->data;
- for (m = stage->matches; m; m = m->next) {
- match = m->data;
- if (!match->channel->enabled)
- /* Ignore disabled channels with a trigger. */
- continue;
- if (devc->prof->model == CHRONOVU_LA8 &&
- (match->match == SR_TRIGGER_RISING
- || match->match == SR_TRIGGER_FALLING)) {
- sr_err("This model supports only simple triggers.");
- return SR_ERR;
- }
- channel_bit = (1 << (match->channel->index));
-
- /* state: 1 == high, edge: 1 == rising edge. */
- if (match->match == SR_TRIGGER_ONE
- || match->match == SR_TRIGGER_RISING)
- devc->trigger_pattern |= channel_bit;
-
- /* LA16 (but not LA8) supports edge triggering. */
- if ((devc->prof->model == CHRONOVU_LA16)) {
- if (match->match == SR_TRIGGER_RISING
- || match->match == SR_TRIGGER_FALLING)
- devc->trigger_edgemask |= channel_bit;
- }
- }
- }
-
- sr_dbg("Trigger pattern/mask/edgemask = 0x%04x / 0x%04x / 0x%04x.",
- devc->trigger_pattern, devc->trigger_mask,
- devc->trigger_edgemask);
-
- return SR_OK;
-}
-
-SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate)
-{
- struct dev_context *devc;
-
- /* Note: Caller checked that sdi and sdi->priv != NULL. */
-
- devc = sdi->priv;
-
- sr_spew("Trying to set samplerate to %" PRIu64 "Hz.", samplerate);
-
- cv_fill_samplerates_if_needed(sdi);
-
- /* Check if this is a samplerate supported by the hardware. */
- if (!is_valid_samplerate(sdi, samplerate)) {
- sr_dbg("Failed to set invalid samplerate (%" PRIu64 "Hz).",
- samplerate);
- return SR_ERR;
- }
-
- devc->cur_samplerate = samplerate;
-
- sr_dbg("Samplerate set to %" PRIu64 "Hz.", devc->cur_samplerate);
-
- return SR_OK;
-}
-
-/**
- * Get a block of data from the device.
- *
- * @param devc The struct containing private per-device-instance data. Must not
- * be NULL. devc->ftdic must not be NULL either.
- *
- * @return SR_OK upon success, or SR_ERR upon errors.
- */
-SR_PRIV int cv_read_block(struct dev_context *devc)
-{
- int i, byte_offset, m, mi, p, q, index, bytes_read;
- gint64 now;
-
- /* Note: Caller checked that devc and devc->ftdic != NULL. */
-
- sr_spew("Reading block %d.", devc->block_counter);
-
- bytes_read = cv_read(devc, devc->mangled_buf, BS);
-
- /* If first block read got 0 bytes, retry until success or timeout. */
- if ((bytes_read == 0) && (devc->block_counter == 0)) {
- do {
- sr_spew("Reading block 0 (again).");
- /* Note: If bytes_read < 0 cv_read() will log errors. */
- bytes_read = cv_read(devc, devc->mangled_buf, BS);
- now = g_get_monotonic_time();
- } while ((devc->done > now) && (bytes_read == 0));
- }
-
- /* Check if block read was successful or a timeout occured. */
- if (bytes_read != BS) {
- sr_err("Trigger timed out. Bytes read: %d.", bytes_read);
- (void) reset_device(devc); /* Ignore errors. */
- return SR_ERR;
- }
-
- /* De-mangle the data. */
- sr_spew("Demangling block %d.", devc->block_counter);
- byte_offset = devc->block_counter * BS;
- m = byte_offset / (1024 * 1024);
- mi = m * (1024 * 1024);
- for (i = 0; i < BS; i++) {
- if (devc->prof->model == CHRONOVU_LA8) {
- p = i & (1 << 0);
- index = m * 2 + (((byte_offset + i) - mi) / 2) * 16;
- index += (devc->divcount == 0) ? p : (1 - p);
- } else {
- p = i & (1 << 0);
- q = i & (1 << 1);
- index = m * 4 + (((byte_offset + i) - mi) / 4) * 32;
- index += q + (1 - p);
- }
- devc->final_buf[index] = devc->mangled_buf[i];
- }
-
- return SR_OK;
-}
-
-SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
-{
- int i, idx;
- uint8_t sample, expected_sample, tmp8;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- int trigger_point; /* Relative trigger point (in this block). */
-
- /* Note: Caller ensures devc/devc->ftdic != NULL and block > 0. */
-
- /* TODO: Implement/test proper trigger support for the LA16. */
-
- /* Check if we can find the trigger condition in this block. */
- trigger_point = -1;
- expected_sample = devc->trigger_pattern & devc->trigger_mask;
- for (i = 0; i < BS; i++) {
- /* Don't continue if the trigger was found previously. */
- if (devc->trigger_found)
- break;
-
- /*
- * Also, don't continue if triggers are "don't care", i.e. if
- * no trigger conditions were specified by the user. In that
- * case we don't want to send an SR_DF_TRIGGER packet at all.
- */
- if (devc->trigger_mask == 0x0000)
- break;
-
- sample = *(devc->final_buf + (block * BS) + i);
-
- if ((sample & devc->trigger_mask) == expected_sample) {
- trigger_point = i;
- devc->trigger_found = 1;
- break;
- }
- }
-
- /* Swap low and high bytes of the 16-bit LA16 samples. */
- if (devc->prof->model == CHRONOVU_LA16) {
- for (i = 0; i < BS; i += 2) {
- idx = (block * BS) + i;
- tmp8 = devc->final_buf[idx];
- devc->final_buf[idx] = devc->final_buf[idx + 1];
- devc->final_buf[idx + 1] = tmp8;
- }
- }
-
- /* If no trigger was found, send one SR_DF_LOGIC packet. */
- if (trigger_point == -1) {
- /* Send an SR_DF_LOGIC packet to the session bus. */
- sr_spew("Sending SR_DF_LOGIC packet (%d bytes) for "
- "block %d.", BS, block);
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = BS;
- logic.unitsize = devc->prof->num_channels / 8;
- logic.data = devc->final_buf + (block * BS);
- sr_session_send(devc->cb_data, &packet);
- return;
- }
-
- /*
- * We found the trigger, so some special handling is needed. We have
- * to send an SR_DF_LOGIC packet with the samples before the trigger
- * (if any), then the SD_DF_TRIGGER packet itself, then another
- * SR_DF_LOGIC packet with the samples after the trigger (if any).
- */
-
- /* TODO: Send SR_DF_TRIGGER packet before or after the actual sample? */
-
- /* If at least one sample is located before the trigger... */
- if (trigger_point > 0) {
- /* Send pre-trigger SR_DF_LOGIC packet to the session bus. */
- sr_spew("Sending pre-trigger SR_DF_LOGIC packet, "
- "start = %d, length = %d.", block * BS, trigger_point);
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = trigger_point;
- logic.unitsize = devc->prof->num_channels / 8;
- logic.data = devc->final_buf + (block * BS);
- sr_session_send(devc->cb_data, &packet);
- }
-
- /* Send the SR_DF_TRIGGER packet to the session bus. */
- sr_spew("Sending SR_DF_TRIGGER packet, sample = %d.",
- (block * BS) + trigger_point);
- packet.type = SR_DF_TRIGGER;
- packet.payload = NULL;
- sr_session_send(devc->cb_data, &packet);
-
- /* If at least one sample is located after the trigger... */
- if (trigger_point < (BS - 1)) {
- /* Send post-trigger SR_DF_LOGIC packet to the session bus. */
- sr_spew("Sending post-trigger SR_DF_LOGIC packet, "
- "start = %d, length = %d.",
- (block * BS) + trigger_point, BS - trigger_point);
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = BS - trigger_point;
- logic.unitsize = devc->prof->num_channels / 8;
- logic.data = devc->final_buf + (block * BS) + trigger_point;
- sr_session_send(devc->cb_data, &packet);
- }
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef LIBSIGROK_HARDWARE_CHRONOVU_LA_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_CHRONOVU_LA_PROTOCOL_H
-
-#include <glib.h>
-#include <ftdi.h>
-#include <stdint.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "la8/la16"
-
-#define SDRAM_SIZE (8 * 1024 * 1024)
-#define MAX_NUM_SAMPLES SDRAM_SIZE
-
-#define BS 4096 /* Block size */
-#define NUM_BLOCKS 2048 /* Number of blocks */
-
-enum {
- CHRONOVU_LA8,
- CHRONOVU_LA16,
-};
-
-struct cv_profile {
- int model;
- const char *modelname;
- const char *iproduct; /* USB iProduct string */
- unsigned int num_channels;
- uint64_t max_samplerate;
- const int num_trigger_matches;
- float trigger_constant;
-};
-
-/* Private, per-device-instance driver context. */
-struct dev_context {
- /** Device profile struct for this device. */
- const struct cv_profile *prof;
-
- /** FTDI device context (used by libftdi). */
- struct ftdi_context *ftdic;
-
- /** The currently configured samplerate of the device. */
- uint64_t cur_samplerate;
-
- /** The current sampling limit (in ms). */
- uint64_t limit_msec;
-
- /** The current sampling limit (in number of samples). */
- uint64_t limit_samples;
-
- void *cb_data;
-
- /**
- * A buffer containing some (mangled) samples from the device.
- * Format: Pretty mangled-up (due to hardware reasons), see code.
- */
- uint8_t mangled_buf[BS];
-
- /**
- * An 8MB buffer where we'll store the de-mangled samples.
- * LA8: Each sample is 1 byte, MSB is channel 7, LSB is channel 0.
- * LA16: Each sample is 2 bytes, MSB is channel 15, LSB is channel 0.
- */
- uint8_t *final_buf;
-
- /**
- * Trigger pattern.
- * A 1 bit matches a high signal, 0 matches a low signal on a channel.
- *
- * If the resp. 'trigger_edgemask' bit is set, 1 means "rising edge",
- * and 0 means "falling edge".
- */
- uint16_t trigger_pattern;
-
- /**
- * Trigger mask.
- * A 1 bit means "must match trigger_pattern", 0 means "don't care".
- */
- uint16_t trigger_mask;
-
- /**
- * Trigger edge mask.
- * A 1 bit means "edge triggered", 0 means "state triggered".
- *
- * Edge triggering is only supported on LA16 (but not LA8).
- */
- uint16_t trigger_edgemask;
-
- /** Tells us whether an SR_DF_TRIGGER packet was already sent. */
- int trigger_found;
-
- /** Used for keeping track how much time has passed. */
- gint64 done;
-
- /** Counter/index for the data block to be read. */
- int block_counter;
-
- /** The divcount value (determines the sample period). */
- uint8_t divcount;
-
- /** This ChronoVu device's USB VID/PID. */
- uint16_t usb_vid;
- uint16_t usb_pid;
-
- /** Samplerates supported by this device. */
- uint64_t samplerates[255];
-};
-
-/* protocol.c */
-extern SR_PRIV const char *cv_channel_names[];
-extern const struct cv_profile cv_profiles[];
-SR_PRIV void cv_fill_samplerates_if_needed(const struct sr_dev_inst *sdi);
-SR_PRIV uint8_t cv_samplerate_to_divcount(const struct sr_dev_inst *sdi,
- uint64_t samplerate);
-SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size);
-SR_PRIV int cv_convert_trigger(const struct sr_dev_inst *sdi);
-SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
-SR_PRIV int cv_read_block(struct dev_context *devc);
-SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-
-/* The Colead SL-5868P uses this. */
-#define SERIALCOMM "2400/8n1"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_SOUNDLEVELMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
-};
-
-SR_PRIV struct sr_dev_driver colead_slm_driver_info;
-static struct sr_dev_driver *di = &colead_slm_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_config *src;
- struct sr_channel *ch;
- GSList *devices, *l;
- const char *conn, *serialcomm;
-
- drvc = di->priv;
- drvc->instances = NULL;
-
- devices = NULL;
-
- 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 (!conn)
- return NULL;
- if (!serialcomm)
- serialcomm = SERIALCOMM;
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Colead",
- "SL-5868P", NULL)))
- return NULL;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_dbg("Device context malloc failed.");
- return NULL;
- }
-
- if (!(sdi->conn = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- sdi->inst_type = SR_INST_SERIAL;
- sdi->priv = devc;
- sdi->driver = di;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct sr_serial_dev_inst *serial;
-
- serial = sdi->conn;
- if (serial_open(serial, SERIAL_RDWR) != SR_OK)
- return SR_ERR;
-
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- switch (id) {
- case SR_CONF_LIMIT_MSEC:
- /* TODO: not yet implemented */
- if (g_variant_get_uint64(data) == 0) {
- sr_err("LIMIT_MSEC can't be 0.");
- return SR_ERR;
- }
- devc->limit_msec = g_variant_get_uint64(data);;
- sr_dbg("Setting time limit to %" PRIu64 "ms.",
- devc->limit_msec);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- devc->cb_data = cb_data;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Poll every 150ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 150,
- colead_slm_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
- sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver colead_slm_driver_info = {
- .name = "colead-slm",
- .longname = "Colead SLM",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .config_get = NULL,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = dev_open,
- .dev_close = std_serial_dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdlib.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-#include <errno.h>
-#include <string.h>
-
-static void process_packet(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- GString *dbg;
- float fvalue;
- int checksum, mode, i;
-
- devc = sdi->priv;
- if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
- dbg = g_string_sized_new(128);
- g_string_printf(dbg, "received packet:");
- for (i = 0; i < 10; i++)
- g_string_append_printf(dbg, " %.2x", (devc->buf)[i]);
- sr_spew("%s", dbg->str);
- g_string_free(dbg, TRUE);
- }
-
- if (devc->buf[0] != 0x08 || devc->buf[1] != 0x04) {
- sr_dbg("invalid packet header.");
- return;
- }
-
- if (devc->buf[8] != 0x01) {
- sr_dbg("invalid measurement.");
- return;
- }
-
- checksum = 0;
- for (i = 0; i < 9; i++)
- checksum += devc->buf[i];
- if ((checksum & 0xff) != devc->buf[9]) {
- sr_dbg("invalid packet checksum.");
- return;
- }
-
- fvalue = 0.0;
- for (i = 3; i < 8; i++) {
- if (devc->buf[i] > 0x09)
- continue;
- fvalue *= 10;
- fvalue += devc->buf[i];
- }
- fvalue /= 10;
-
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
- analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
- analog.unit = SR_UNIT_DECIBEL_SPL;
- analog.channels = sdi->channels;
- analog.num_samples = 1;
- analog.data = &fvalue;
-
- /* High nibble should only have 0x01 or 0x02. */
- mode = (devc->buf[2] >> 4) & 0x0f;
- if (mode == 0x02)
- analog.mqflags |= SR_MQFLAG_HOLD;
- else if (mode != 0x01) {
- sr_dbg("unknown measurement mode 0x%.2x", mode);
- return;
- }
-
- /* Low nibble has 14 combinations of direct/long-term average,
- * time scale of that average, frequency weighting, and time
- * weighting. */
- mode = devc->buf[2] & 0x0f;
- switch (mode) {
- case 0x0:
- analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
- | SR_MQFLAG_SPL_TIME_WEIGHT_F;
- break;
- case 0x1:
- analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
- | SR_MQFLAG_SPL_TIME_WEIGHT_S;
- break;
- case 0x2:
- analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
- | SR_MQFLAG_SPL_TIME_WEIGHT_F;
- break;
- case 0x3:
- analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
- | SR_MQFLAG_SPL_TIME_WEIGHT_S;
- break;
- case 0x4:
- analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
- | SR_MQFLAG_SPL_TIME_WEIGHT_F;
- break;
- case 0x5:
- analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
- | SR_MQFLAG_SPL_TIME_WEIGHT_S;
- break;
- case 0x6:
- analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
- | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
- | SR_MQFLAG_SPL_TIME_WEIGHT_F;
- break;
- case 0x7:
- analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
- | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
- | SR_MQFLAG_SPL_TIME_WEIGHT_S;
- break;
- case 0x8:
- /* 10-second mean, but we don't have MQ flags to express it. */
- analog.mqflags |= SR_MQFLAG_SPL_LAT \
- | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
- | SR_MQFLAG_SPL_TIME_WEIGHT_F;
- break;
- case 0x9:
- /* Mean over a time period between 11 seconds and 24 hours.
- * Which is so silly that there's no point in expressing
- * either this or the previous case. */
- analog.mqflags |= SR_MQFLAG_SPL_LAT \
- | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
- | SR_MQFLAG_SPL_TIME_WEIGHT_F;
- break;
- case 0xa:
- /* 10-second mean. */
- analog.mqflags |= SR_MQFLAG_SPL_LAT \
- | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
- | SR_MQFLAG_SPL_TIME_WEIGHT_S;
- break;
- case 0xb:
- /* Mean over a time period between 11 seconds and 24 hours. */
- analog.mqflags |= SR_MQFLAG_SPL_LAT \
- | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
- | SR_MQFLAG_SPL_TIME_WEIGHT_S;
- break;
- case 0xc:
- /* Internal calibration on 1kHz sine at 94dB, not useful
- * to anything but the device. */
- analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
- break;
- case 0xd:
- /* Internal calibration on 1kHz sine at 94dB, not useful
- * to anything but the device. */
- analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
- break;
- default:
- sr_dbg("unknown configuration 0x%.2x", mode);
- return;
- break;
- }
-
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
-
- devc->num_samples++;
-
-}
-
-SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
-{
- const struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- int len;
- char buf[128];
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- if (revents != G_IO_IN)
- /* Timeout event. */
- return TRUE;
-
- serial = sdi->conn;
- if (devc->state == IDLE) {
- if (serial_read(serial, buf, 128) != 1 || buf[0] != 0x10)
- /* Nothing there, or caught the tail end of a previous packet,
- * or some garbage. Unless it's a single "data ready" byte,
- * we don't want it. */
- return TRUE;
- /* Got 0x10, "measurement ready". */
- if (serial_write(serial, "\x20", 1) == -1)
- sr_err("unable to send command: %s", strerror(errno));
- else {
- devc->state = COMMAND_SENT;
- devc->buflen = 0;
- }
- } else {
- len = serial_read(serial, devc->buf + devc->buflen,
- 10 - devc->buflen);
- if (len < 1)
- return TRUE;
- devc->buflen += len;
- if (devc->buflen > 10) {
- sr_dbg("buffer overrun");
- devc->state = IDLE;
- return TRUE;
- }
- if (devc->buflen == 10) {
- /* If we got here, we're sure the device sent a "data ready"
- * notification, we asked for data, and got it. */
- process_packet(sdi);
- devc->state = IDLE;
- }
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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_COLEAD_SLM_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_COLEAD_SLM_PROTOCOL_H
-
-#include <stdint.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "colead-slm"
-
-enum {
- IDLE,
- COMMAND_SENT,
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /** The current sampling limit (in number of samples). */
- uint64_t limit_samples;
-
- /** The current sampling limit (in ms). */
- uint64_t limit_msec;
-
- /** Opaque pointer passed in by the frontend. */
- void *cb_data;
-
- /** The current number of already received samples. */
- uint64_t num_samples;
- int state;
- char buf[10];
- int buflen;
-};
-
-SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
- * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * Cyrustek ES519XX protocol parser.
- *
- * Communication parameters: Unidirectional, 2400/7o1 or 19230/7o1
- */
-
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "es519xx"
-
-/* Factors for the respective measurement mode (0 means "invalid"). */
-static const float factors_2400_11b[9][8] = {
- {1e-4, 1e-3, 1e-2, 1e-1, 1, 0, 0, 0 }, /* V */
- {1e-7, 1e-6, 0, 0, 0, 0, 0, 0 }, /* uA */
- {1e-5, 1e-4, 0, 0, 0, 0, 0, 0 }, /* mA */
- {1e-2, 0, 0, 0, 0, 0, 0, 0 }, /* A */
- {1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 0, 0 }, /* RPM */
- {1e-1, 1, 1e1, 1e2, 1e3, 1e4, 0, 0 }, /* Resistance */
- {1, 1e1, 1e2, 1e3, 1e4, 1e5, 0, 0 }, /* Frequency */
- {1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5}, /* Capacitance */
- {1e-3, 0, 0, 0, 0, 0, 0, 0 }, /* Diode */
-};
-static const float factors_19200_11b_5digits[9][8] = {
- {1e-4, 1e-3, 1e-2, 1e-1, 1e-5, 0, 0, 0}, /* V */
- {1e-8, 1e-7, 0, 0, 0, 0, 0, 0}, /* uA */
- {1e-6, 1e-5, 0, 0, 0, 0, 0, 0}, /* mA */
- {0, 1e-3, 0, 0, 0, 0, 0, 0}, /* A */
- {1e-4, 1e-3, 1e-2, 1e-1, 1, 0, 0, 0}, /* Manual A */
- {1e-2, 1e-1, 1, 1e1, 1e2, 1e3, 1e4, 0}, /* Resistance */
- {1e-1, 0, 1, 1e1, 1e2, 1e3, 1e4, 0}, /* Frequency */
- {1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5}, /* Capacitance */
- {1e-4, 0, 0, 0, 0, 0, 0, 0 }, /* Diode */
-};
-static const float factors_19200_11b_clampmeter[9][8] = {
- {1e-3, 1e-2, 1e-1, 1, 1e-4, 0, 0, 0}, /* V */
- {1e-7, 1e-6, 0, 0, 0, 0, 0, 0}, /* uA */
- {1e-5, 1e-4, 0, 0, 0, 0, 0, 0}, /* mA */
- {1e-2, 0, 0, 0, 0, 0, 0, 0}, /* A */
- {1e-3, 1e-2, 1e-1, 1, 0, 0, 0, 0}, /* Manual A */
- {1e-1, 1, 1e1, 1e2, 1e3, 1e4, 0, 0}, /* Resistance */
- {1e-1, 0, 1, 1e1, 1e2, 1e3, 1e4, 0}, /* Frequency */
- {1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5}, /* Capacitance */
- {1e-3, 0, 0, 0, 0, 0, 0, 0 }, /* Diode */
-};
-static const float factors_19200_11b[9][8] = {
- {1e-3, 1e-2, 1e-1, 1, 1e-4, 0, 0, 0}, /* V */
- {1e-7, 1e-6, 0, 0, 0, 0, 0, 0}, /* uA */
- {1e-5, 1e-4, 0, 0, 0, 0, 0, 0}, /* mA */
- {1e-3, 1e-2, 0, 0, 0, 0, 0, 0}, /* A */
- {0, 0, 0, 0, 0, 0, 0, 0}, /* Manual A */
- {1e-1, 1, 1e1, 1e2, 1e3, 1e4, 0, 0}, /* Resistance */
- {1, 1e1, 1e2, 1e3, 1e4, 0, 0, 0}, /* Frequency */
- {1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 0}, /* Capacitance */
- {1e-3, 0, 0, 0, 0, 0, 0, 0}, /* Diode */
-};
-static const float factors_19200_14b[9][8] = {
- {1e-4, 1e-3, 1e-2, 1e-1, 1e-5, 0, 0, 0}, /* V */
- {1e-8, 1e-7, 0, 0, 0, 0, 0, 0}, /* uA */
- {1e-6, 1e-5, 0, 0, 0, 0, 0, 0}, /* mA */
- {1e-3, 0, 0, 0, 0, 0, 0, 0}, /* A */
- {1e-4, 1e-3, 1e-2, 1e-1, 1, 0, 0, 0}, /* Manual A */
- {1e-2, 1e-1, 1, 1e1, 1e2, 1e3, 1e4, 0}, /* Resistance */
- {1e-2, 1e-1, 0, 1, 1e1, 1e2, 1e3, 1e4}, /* Frequency */
- {1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5}, /* Capacitance */
- {1e-4, 0, 0, 0, 0, 0, 0, 0 }, /* Diode */
-};
-
-static int parse_value(const uint8_t *buf, struct es519xx_info *info,
- float *result)
-{
- int i, intval, num_digits;
- float floatval;
-
- num_digits = 4 + ((info->packet_size == 14) ? 1 : 0);
-
- /* Bytes 1-4 (or 5): Value (4 or 5 decimal digits) */
- if (info->is_ol) {
- sr_spew("Over limit.");
- *result = INFINITY;
- return SR_OK;
- } else if (info->is_ul) {
- sr_spew("Under limit.");
- *result = INFINITY;
- return SR_OK;
- } else if (!isdigit(buf[1]) || !isdigit(buf[2]) ||
- !isdigit(buf[3]) || !isdigit(buf[4]) ||
- (num_digits == 5 && !isdigit(buf[5]))) {
- sr_dbg("Value contained invalid digits: %02x %02x %02x %02x "
- "(%c %c %c %c).", buf[1], buf[2], buf[3], buf[4],
- buf[1], buf[2], buf[3], buf[4]);
- return SR_ERR;
- }
- intval = (info->is_digit4) ? 1 : 0;
- for (i = 0; i < num_digits; i++)
- intval = 10 * intval + (buf[i + 1] - '0');
-
- /* Apply sign. */
- intval *= info->is_sign ? -1 : 1;
-
- floatval = (float)intval;
-
- /* Note: The decimal point position will be parsed later. */
-
- sr_spew("The display value is %f.", floatval);
-
- *result = floatval;
-
- return SR_OK;
-}
-
-static int parse_range(uint8_t b, float *floatval,
- const struct es519xx_info *info)
-{
- int idx, mode;
- float factor = 0;
-
- idx = b - '0';
-
- if (idx < 0 || idx > 7) {
- sr_dbg("Invalid range byte / index: 0x%02x / 0x%02x.", b, idx);
- return SR_ERR;
- }
-
- /* Parse range byte (depends on the measurement mode). */
- if (info->is_voltage)
- mode = 0; /* V */
- else if (info->is_current && info->is_micro)
- mode = 1; /* uA */
- else if (info->is_current && info->is_milli)
- mode = 2; /* mA */
- else if (info->is_current && info->is_auto)
- mode = 3; /* A */
- else if (info->is_current && !info->is_auto)
- mode = 4; /* Manual A */
- else if (info->is_rpm)
- /* Not a typo, it's really index 4 in factors_2400_11b[][]. */
- mode = 4; /* RPM */
- else if (info->is_resistance || info->is_continuity)
- mode = 5; /* Resistance */
- else if (info->is_frequency)
- mode = 6; /* Frequency */
- else if (info->is_capacitance)
- mode = 7; /* Capacitance */
- else if (info->is_diode)
- mode = 8; /* Diode */
- else if (info->is_duty_cycle)
- mode = 0; /* Dummy, unused */
- else {
- sr_dbg("Invalid mode, range byte was: 0x%02x.", b);
- return SR_ERR;
- }
-
- if (info->is_vbar) {
- if (info->is_micro)
- factor = (const float[]){1e-1, 1}[idx];
- else if (info->is_milli)
- factor = (const float[]){1e-2, 1e-1}[idx];
- }
- else if (info->is_duty_cycle)
- factor = 1e-1;
- else if (info->baudrate == 2400)
- factor = factors_2400_11b[mode][idx];
- else if (info->fivedigits)
- factor = factors_19200_11b_5digits[mode][idx];
- else if (info->clampmeter)
- factor = factors_19200_11b_clampmeter[mode][idx];
- else if (info->packet_size == 11)
- factor = factors_19200_11b[mode][idx];
- else if (info->packet_size == 14)
- factor = factors_19200_14b[mode][idx];
-
- if (factor == 0) {
- sr_dbg("Invalid factor for range byte: 0x%02x.", b);
- return SR_ERR;
- }
-
- /* Apply respective factor (mode-dependent) on the value. */
- *floatval *= factor;
- sr_dbg("Applying factor %f, new value is %f.", factor, *floatval);
-
- return SR_OK;
-}
-
-static void parse_flags(const uint8_t *buf, struct es519xx_info *info)
-{
- int function, status;
-
- function = 5 + ((info->packet_size == 14) ? 1 : 0);
- status = function + 1;
-
- /* Status byte */
- if (info->alt_functions) {
- info->is_sign = (buf[status] & (1 << 3)) != 0;
- info->is_batt = (buf[status] & (1 << 2)) != 0; /* Bat. low */
- info->is_ol = (buf[status] & (1 << 1)) != 0; /* Overflow */
- info->is_ol |= (buf[status] & (1 << 0)) != 0; /* Overflow */
- } else {
- info->is_judge = (buf[status] & (1 << 3)) != 0;
- info->is_sign = (buf[status] & (1 << 2)) != 0;
- info->is_batt = (buf[status] & (1 << 1)) != 0; /* Bat. low */
- info->is_ol = (buf[status] & (1 << 0)) != 0; /* Overflow */
- }
-
- if (info->packet_size == 14) {
- /* Option 1 byte */
- info->is_max = (buf[8] & (1 << 3)) != 0;
- info->is_min = (buf[8] & (1 << 2)) != 0;
- info->is_rel = (buf[8] & (1 << 1)) != 0;
- info->is_rmr = (buf[8] & (1 << 0)) != 0;
-
- /* Option 2 byte */
- info->is_ul = (buf[9] & (1 << 3)) != 0; /* Underflow */
- info->is_pmax = (buf[9] & (1 << 2)) != 0; /* Max. peak value */
- info->is_pmin = (buf[9] & (1 << 1)) != 0; /* Min. peak value */
-
- /* Option 3 byte */
- info->is_dc = (buf[10] & (1 << 3)) != 0;
- info->is_ac = (buf[10] & (1 << 2)) != 0;
- info->is_auto = (buf[10] & (1 << 1)) != 0;
- info->is_vahz = (buf[10] & (1 << 0)) != 0;
-
- /* LPF: Low-pass filter(s) */
- if (info->selectable_lpf) {
- /* Option 4 byte */
- info->is_hold = (buf[11] & (1 << 3)) != 0;
- info->is_vbar = (buf[11] & (1 << 2)) != 0;
- info->is_lpf1 = (buf[11] & (1 << 1)) != 0;
- info->is_lpf0 = (buf[11] & (1 << 0)) != 0;
- } else {
- /* Option 4 byte */
- info->is_vbar = (buf[11] & (1 << 2)) != 0;
- info->is_hold = (buf[11] & (1 << 1)) != 0;
- info->is_lpf1 = (buf[11] & (1 << 0)) != 0;
- }
- } else if (info->alt_functions) {
- /* Option 2 byte */
- info->is_dc = (buf[8] & (1 << 3)) != 0;
- info->is_auto = (buf[8] & (1 << 2)) != 0;
- info->is_apo = (buf[8] & (1 << 0)) != 0;
- info->is_ac = !info->is_dc;
- } else {
- /* Option 1 byte */
- if (info->baudrate == 2400) {
- info->is_pmax = (buf[7] & (1 << 3)) != 0;
- info->is_pmin = (buf[7] & (1 << 2)) != 0;
- info->is_vahz = (buf[7] & (1 << 0)) != 0;
- } else if (info->fivedigits) {
- info->is_ul = (buf[7] & (1 << 3)) != 0;
- info->is_pmax = (buf[7] & (1 << 2)) != 0;
- info->is_pmin = (buf[7] & (1 << 1)) != 0;
- info->is_digit4 = (buf[7] & (1 << 0)) != 0;
- } else if (info->clampmeter) {
- info->is_ul = (buf[7] & (1 << 3)) != 0;
- info->is_vasel = (buf[7] & (1 << 2)) != 0;
- info->is_vbar = (buf[7] & (1 << 1)) != 0;
- } else {
- info->is_hold = (buf[7] & (1 << 3)) != 0;
- info->is_max = (buf[7] & (1 << 2)) != 0;
- info->is_min = (buf[7] & (1 << 1)) != 0;
- }
-
- /* Option 2 byte */
- info->is_dc = (buf[8] & (1 << 3)) != 0;
- info->is_ac = (buf[8] & (1 << 2)) != 0;
- info->is_auto = (buf[8] & (1 << 1)) != 0;
- if (info->baudrate == 2400)
- info->is_apo = (buf[8] & (1 << 0)) != 0;
- else
- info->is_vahz = (buf[8] & (1 << 0)) != 0;
- }
-
- /* Function byte */
- if (info->alt_functions) {
- switch (buf[function]) {
- case 0x3f: /* A */
- info->is_current = info->is_auto = TRUE;
- break;
- case 0x3e: /* uA */
- info->is_current = info->is_micro = info->is_auto = TRUE;
- break;
- case 0x3d: /* mA */
- info->is_current = info->is_milli = info->is_auto = TRUE;
- break;
- case 0x3c: /* V */
- info->is_voltage = TRUE;
- break;
- case 0x37: /* Resistance */
- info->is_resistance = TRUE;
- break;
- case 0x36: /* Continuity */
- info->is_continuity = TRUE;
- break;
- case 0x3b: /* Diode */
- info->is_diode = TRUE;
- break;
- case 0x3a: /* Frequency */
- info->is_frequency = TRUE;
- break;
- case 0x34: /* ADP0 */
- case 0x35: /* ADP0 */
- info->is_adp0 = TRUE;
- break;
- case 0x38: /* ADP1 */
- case 0x39: /* ADP1 */
- info->is_adp1 = TRUE;
- break;
- case 0x32: /* ADP2 */
- case 0x33: /* ADP2 */
- info->is_adp2 = TRUE;
- break;
- case 0x30: /* ADP3 */
- case 0x31: /* ADP3 */
- info->is_adp3 = TRUE;
- break;
- default:
- sr_dbg("Invalid function byte: 0x%02x.", buf[function]);
- break;
- }
- } else {
- /* Note: Some of these mappings are fixed up later. */
- switch (buf[function]) {
- case 0x3b: /* V */
- info->is_voltage = TRUE;
- break;
- case 0x3d: /* uA */
- info->is_current = info->is_micro = info->is_auto = TRUE;
- break;
- case 0x3f: /* mA */
- info->is_current = info->is_milli = info->is_auto = TRUE;
- break;
- case 0x30: /* A */
- info->is_current = info->is_auto = TRUE;
- break;
- case 0x39: /* Manual A */
- info->is_current = TRUE;
- info->is_auto = FALSE; /* Manual mode */
- break;
- case 0x33: /* Resistance */
- info->is_resistance = TRUE;
- break;
- case 0x35: /* Continuity */
- info->is_continuity = TRUE;
- break;
- case 0x31: /* Diode */
- info->is_diode = TRUE;
- break;
- case 0x32: /* Frequency / RPM / duty cycle */
- if (info->packet_size == 14) {
- if (info->is_judge)
- info->is_duty_cycle = TRUE;
- else
- info->is_frequency = TRUE;
- } else {
- if (info->is_judge)
- info->is_rpm = TRUE;
- else
- info->is_frequency = TRUE;
- }
- break;
- case 0x36: /* Capacitance */
- info->is_capacitance = TRUE;
- break;
- case 0x34: /* Temperature */
- info->is_temperature = TRUE;
- if (info->is_judge)
- info->is_celsius = TRUE;
- else
- info->is_fahrenheit = TRUE;
- /* IMPORTANT: The digits always represent Celsius! */
- break;
- case 0x3e: /* ADP0 */
- info->is_adp0 = TRUE;
- break;
- case 0x3c: /* ADP1 */
- info->is_adp1 = TRUE;
- break;
- case 0x38: /* ADP2 */
- info->is_adp2 = TRUE;
- break;
- case 0x3a: /* ADP3 */
- info->is_adp3 = TRUE;
- break;
- default:
- sr_dbg("Invalid function byte: 0x%02x.", buf[function]);
- break;
- }
- }
-
- if (info->is_vahz && (info->is_voltage || info->is_current)) {
- info->is_voltage = FALSE;
- info->is_current = FALSE;
- info->is_milli = info->is_micro = FALSE;
- if (info->packet_size == 14) {
- if (info->is_judge)
- info->is_duty_cycle = TRUE;
- else
- info->is_frequency = TRUE;
- } else {
- if (info->is_judge)
- info->is_rpm = TRUE;
- else
- info->is_frequency = TRUE;
- }
- }
-
- if (info->is_current && (info->is_micro || info->is_milli) && info->is_vasel) {
- info->is_current = info->is_auto = FALSE;
- info->is_voltage = TRUE;
- }
-
- if (info->baudrate == 2400) {
- /* Inverted mapping between mA and A, and no manual A. */
- if (info->is_current && (info->is_milli || !info->is_auto)) {
- info->is_milli = !info->is_milli;
- info->is_auto = TRUE;
- }
- }
-}
-
-static void handle_flags(struct sr_datafeed_analog *analog,
- float *floatval, const struct es519xx_info *info)
-{
- /*
- * Note: is_micro etc. are not used directly to multiply/divide
- * floatval, this is handled via parse_range() and factors[][].
- */
-
- /* Measurement modes */
- if (info->is_voltage) {
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- }
- if (info->is_current) {
- analog->mq = SR_MQ_CURRENT;
- analog->unit = SR_UNIT_AMPERE;
- }
- if (info->is_resistance) {
- analog->mq = SR_MQ_RESISTANCE;
- analog->unit = SR_UNIT_OHM;
- }
- if (info->is_frequency) {
- analog->mq = SR_MQ_FREQUENCY;
- analog->unit = SR_UNIT_HERTZ;
- }
- if (info->is_capacitance) {
- analog->mq = SR_MQ_CAPACITANCE;
- analog->unit = SR_UNIT_FARAD;
- }
- if (info->is_temperature && info->is_celsius) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_CELSIUS;
- }
- if (info->is_temperature && info->is_fahrenheit) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_FAHRENHEIT;
- }
- if (info->is_continuity) {
- analog->mq = SR_MQ_CONTINUITY;
- analog->unit = SR_UNIT_BOOLEAN;
- *floatval = (*floatval < 0.0 || *floatval > 25.0) ? 0.0 : 1.0;
- }
- if (info->is_diode) {
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- }
- if (info->is_rpm) {
- analog->mq = SR_MQ_FREQUENCY;
- analog->unit = SR_UNIT_REVOLUTIONS_PER_MINUTE;
- }
- if (info->is_duty_cycle) {
- analog->mq = SR_MQ_DUTY_CYCLE;
- analog->unit = SR_UNIT_PERCENTAGE;
- }
-
- /* Measurement related flags */
- if (info->is_ac)
- analog->mqflags |= SR_MQFLAG_AC;
- if (info->is_dc)
- analog->mqflags |= SR_MQFLAG_DC;
- if (info->is_auto)
- analog->mqflags |= SR_MQFLAG_AUTORANGE;
- if (info->is_diode)
- analog->mqflags |= SR_MQFLAG_DIODE;
- if (info->is_hold)
- /*
- * Note: HOLD only affects the number displayed on the LCD,
- * but not the value sent via the protocol! It also does not
- * affect the bargraph on the LCD.
- */
- analog->mqflags |= SR_MQFLAG_HOLD;
- if (info->is_max)
- analog->mqflags |= SR_MQFLAG_MAX;
- if (info->is_min)
- analog->mqflags |= SR_MQFLAG_MIN;
- if (info->is_rel)
- analog->mqflags |= SR_MQFLAG_RELATIVE;
-
- /* Other flags */
- if (info->is_judge)
- sr_spew("Judge bit is set.");
- if (info->is_batt)
- sr_spew("Battery is low.");
- if (info->is_ol)
- sr_spew("Input overflow.");
- if (info->is_ul)
- sr_spew("Input underflow.");
- if (info->is_pmax)
- sr_spew("pMAX active, LCD shows max. peak value.");
- if (info->is_pmin)
- sr_spew("pMIN active, LCD shows min. peak value.");
- if (info->is_vahz)
- sr_spew("VAHZ active.");
- if (info->is_apo)
- sr_spew("Auto-Power-Off enabled.");
- if (info->is_vbar)
- sr_spew("VBAR active.");
- if ((!info->selectable_lpf && info->is_lpf1) ||
- (info->selectable_lpf && (!info->is_lpf0 || !info->is_lpf1)))
- sr_spew("Low-pass filter feature is active.");
-}
-
-static gboolean flags_valid(const struct es519xx_info *info)
-{
- int count;
-
- /* Does the packet have more than one multiplier? */
- count = (info->is_micro) ? 1 : 0;
- count += (info->is_milli) ? 1 : 0;
- if (count > 1) {
- sr_dbg("More than one multiplier detected in packet.");
- return FALSE;
- }
-
- /* Does the packet "measure" more than one type of value? */
- count = (info->is_voltage) ? 1 : 0;
- count += (info->is_current) ? 1 : 0;
- count += (info->is_resistance) ? 1 : 0;
- count += (info->is_frequency) ? 1 : 0;
- count += (info->is_capacitance) ? 1 : 0;
- count += (info->is_temperature) ? 1 : 0;
- count += (info->is_continuity) ? 1 : 0;
- count += (info->is_diode) ? 1 : 0;
- count += (info->is_rpm) ? 1 : 0;
- if (count > 1) {
- sr_dbg("More than one measurement type detected in packet.");
- return FALSE;
- }
-
- /* Both AC and DC set? */
- if (info->is_ac && info->is_dc) {
- sr_dbg("Both AC and DC flags detected in packet.");
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean sr_es519xx_packet_valid(const uint8_t *buf,
- struct es519xx_info *info)
-{
- int s;
-
- s = info->packet_size;
-
- if (s == 11 && memcmp(buf, buf + s, s))
- return FALSE;
-
- if (buf[s - 2] != '\r' || buf[s - 1] != '\n')
- return FALSE;
-
- parse_flags(buf, info);
-
- if (!flags_valid(info))
- return FALSE;
-
- return TRUE;
-}
-
-static int sr_es519xx_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog,
- struct es519xx_info *info)
-{
- int ret;
-
- if (!sr_es519xx_packet_valid(buf, info))
- return SR_ERR;
-
- if ((ret = parse_value(buf, info, floatval)) != SR_OK) {
- sr_dbg("Error parsing value: %d.", ret);
- return ret;
- }
-
- if ((ret = parse_range(buf[0], floatval, info)) != SR_OK)
- return ret;
-
- handle_flags(analog, floatval, info);
- return SR_OK;
-}
-
-/*
- * Functions for 2400 baud / 11 bytes protocols.
- * This includes ES51962, ES51971, ES51972, ES51978 and ES51989.
- */
-SR_PRIV gboolean sr_es519xx_2400_11b_packet_valid(const uint8_t *buf)
-{
- struct es519xx_info info;
-
- memset(&info, 0, sizeof(struct es519xx_info));
- info.baudrate = 2400;
- info.packet_size = 11;
-
- return sr_es519xx_packet_valid(buf, &info);
-}
-
-SR_PRIV int sr_es519xx_2400_11b_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info)
-{
- struct es519xx_info *info_local;
-
- info_local = info;
- memset(info_local, 0, sizeof(struct es519xx_info));
- info_local->baudrate = 2400;
- info_local->packet_size = 11;
-
- return sr_es519xx_parse(buf, floatval, analog, info);
-}
-
-/*
- * Functions for 2400 baud / 11 byte protocols.
- * This includes ES51960, ES51977 and ES51988.
- */
-SR_PRIV gboolean sr_es519xx_2400_11b_altfn_packet_valid(const uint8_t *buf)
-{
- struct es519xx_info info;
-
- memset(&info, 0, sizeof(struct es519xx_info));
- info.baudrate = 2400;
- info.packet_size = 11;
- info.alt_functions = TRUE;
-
- return sr_es519xx_packet_valid(buf, &info);
-}
-
-SR_PRIV int sr_es519xx_2400_11b_altfn_parse(const uint8_t *buf,
- float *floatval, struct sr_datafeed_analog *analog, void *info)
-{
- struct es519xx_info *info_local;
-
- info_local = info;
- memset(info_local, 0, sizeof(struct es519xx_info));
- info_local->baudrate = 2400;
- info_local->packet_size = 11;
- info_local->alt_functions = TRUE;
-
- return sr_es519xx_parse(buf, floatval, analog, info);
-}
-
-/*
- * Functions for 19200 baud / 11 bytes protocols with 5 digits display.
- * This includes ES51911, ES51916 and ES51918.
- */
-SR_PRIV gboolean sr_es519xx_19200_11b_5digits_packet_valid(const uint8_t *buf)
-{
- struct es519xx_info info;
-
- memset(&info, 0, sizeof(struct es519xx_info));
- info.baudrate = 19200;
- info.packet_size = 11;
- info.fivedigits = TRUE;
-
- return sr_es519xx_packet_valid(buf, &info);
-}
-
-SR_PRIV int sr_es519xx_19200_11b_5digits_parse(const uint8_t *buf,
- float *floatval, struct sr_datafeed_analog *analog, void *info)
-{
- struct es519xx_info *info_local;
-
- info_local = info;
- memset(info_local, 0, sizeof(struct es519xx_info));
- info_local->baudrate = 19200;
- info_local->packet_size = 11;
- info_local->fivedigits = TRUE;
-
- return sr_es519xx_parse(buf, floatval, analog, info);
-}
-
-/*
- * Functions for 19200 baud / 11 bytes protocols with clamp meter support.
- * This includes ES51967 and ES51969.
- */
-SR_PRIV gboolean sr_es519xx_19200_11b_clamp_packet_valid(const uint8_t *buf)
-{
- struct es519xx_info info;
-
- memset(&info, 0, sizeof(struct es519xx_info));
- info.baudrate = 19200;
- info.packet_size = 11;
- info.clampmeter = TRUE;
-
- return sr_es519xx_packet_valid(buf, &info);
-}
-
-SR_PRIV int sr_es519xx_19200_11b_clamp_parse(const uint8_t *buf,
- float *floatval, struct sr_datafeed_analog *analog, void *info)
-{
- struct es519xx_info *info_local;
-
- info_local = info;
- memset(info_local, 0, sizeof(struct es519xx_info));
- info_local->baudrate = 19200;
- info_local->packet_size = 11;
- info_local->clampmeter = TRUE;
-
- return sr_es519xx_parse(buf, floatval, analog, info);
-}
-
-/*
- * Functions for 19200 baud / 11 bytes protocols.
- * This includes ES51981, ES51982, ES51983, ES51984 and ES51986.
- */
-SR_PRIV gboolean sr_es519xx_19200_11b_packet_valid(const uint8_t *buf)
-{
- struct es519xx_info info;
-
- memset(&info, 0, sizeof(struct es519xx_info));
- info.baudrate = 19200;
- info.packet_size = 11;
-
- return sr_es519xx_packet_valid(buf, &info);
-}
-
-SR_PRIV int sr_es519xx_19200_11b_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info)
-{
- struct es519xx_info *info_local;
-
- info_local = info;
- memset(info_local, 0, sizeof(struct es519xx_info));
- info_local->baudrate = 19200;
- info_local->packet_size = 11;
-
- return sr_es519xx_parse(buf, floatval, analog, info);
-}
-
-/*
- * Functions for 19200 baud / 14 bytes protocols.
- * This includes ES51921 and ES51922.
- */
-SR_PRIV gboolean sr_es519xx_19200_14b_packet_valid(const uint8_t *buf)
-{
- struct es519xx_info info;
-
- memset(&info, 0, sizeof(struct es519xx_info));
- info.baudrate = 19200;
- info.packet_size = 14;
-
- return sr_es519xx_packet_valid(buf, &info);
-}
-
-SR_PRIV int sr_es519xx_19200_14b_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info)
-{
- struct es519xx_info *info_local;
-
- info_local = info;
- memset(info_local, 0, sizeof(struct es519xx_info));
- info_local->baudrate = 19200;
- info_local->packet_size = 14;
-
- return sr_es519xx_parse(buf, floatval, analog, info);
-}
-
-/*
- * Functions for 19200 baud / 14 bytes protocols with selectable LPF.
- * This includes ES51931 and ES51932.
- */
-SR_PRIV gboolean sr_es519xx_19200_14b_sel_lpf_packet_valid(const uint8_t *buf)
-{
- struct es519xx_info info;
-
- memset(&info, 0, sizeof(struct es519xx_info));
- info.baudrate = 19200;
- info.packet_size = 14;
- info.selectable_lpf = TRUE;
-
- return sr_es519xx_packet_valid(buf, &info);
-}
-
-SR_PRIV int sr_es519xx_19200_14b_sel_lpf_parse(const uint8_t *buf,
- float *floatval, struct sr_datafeed_analog *analog, void *info)
-{
- struct es519xx_info *info_local;
-
- info_local = info;
- memset(info_local, 0, sizeof(struct es519xx_info));
- info_local->baudrate = 19200;
- info_local->packet_size = 14;
- info_local->selectable_lpf = TRUE;
-
- return sr_es519xx_parse(buf, floatval, analog, info);
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * Fortune Semiconductor FS9721_LP3/FS9721B protocol parser.
- *
- * FS9721_LP3: 4000 counts (3 3/4 digits)
- * FS9721B/Q100: 2400 counts (3 2/3 digits)
- *
- * Same for both chips:
- * - Packages: Bare die (78 pins) or QFP-100
- * - Communication parameters: Unidirectional, 2400/8n1
- * - The protocol seems to be exactly the same.
- */
-
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "fs9721"
-
-static int parse_digit(uint8_t b)
-{
- switch (b) {
- case 0x7d:
- return 0;
- case 0x05:
- return 1;
- case 0x5b:
- return 2;
- case 0x1f:
- return 3;
- case 0x27:
- return 4;
- case 0x3e:
- return 5;
- case 0x7e:
- return 6;
- case 0x15:
- return 7;
- case 0x7f:
- return 8;
- case 0x3f:
- return 9;
- default:
- sr_dbg("Invalid digit byte: 0x%02x.", b);
- return -1;
- }
-}
-
-static gboolean sync_nibbles_valid(const uint8_t *buf)
-{
- int i;
-
- /* Check the synchronization nibbles, and make sure they all match. */
- for (i = 0; i < FS9721_PACKET_SIZE; i++) {
- if (((buf[i] >> 4) & 0x0f) != (i + 1)) {
- sr_dbg("Sync nibble in byte %d (0x%02x) is invalid.",
- i, buf[i]);
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-static gboolean flags_valid(const struct fs9721_info *info)
-{
- int count;
-
- /* Does the packet have more than one multiplier? */
- count = 0;
- count += (info->is_nano) ? 1 : 0;
- count += (info->is_micro) ? 1 : 0;
- count += (info->is_milli) ? 1 : 0;
- count += (info->is_kilo) ? 1 : 0;
- count += (info->is_mega) ? 1 : 0;
- if (count > 1) {
- sr_dbg("More than one multiplier detected in packet.");
- return FALSE;
- }
-
- /* Does the packet "measure" more than one type of value? */
- count = 0;
- count += (info->is_hz) ? 1 : 0;
- count += (info->is_ohm) ? 1 : 0;
- count += (info->is_farad) ? 1 : 0;
- count += (info->is_ampere) ? 1 : 0;
- count += (info->is_volt) ? 1 : 0;
- count += (info->is_percent) ? 1 : 0;
- if (count > 1) {
- sr_dbg("More than one measurement type detected in packet.");
- return FALSE;
- }
-
- /* Both AC and DC set? */
- if (info->is_ac && info->is_dc) {
- sr_dbg("Both AC and DC flags detected in packet.");
- return FALSE;
- }
-
- /* RS232 flag not set? */
- if (!info->is_rs232) {
- sr_dbg("No RS232 flag detected in packet.");
- return FALSE;
- }
-
- return TRUE;
-}
-
-static int parse_value(const uint8_t *buf, float *result)
-{
- int i, sign, intval = 0, digits[4];
- uint8_t digit_bytes[4];
- float floatval;
-
- /* Byte 1: LCD SEG2 */
- sign = ((buf[1] & (1 << 3)) != 0) ? -1 : 1;
-
- /*
- * Bytes 1-8: Value (4 decimal digits, sign, decimal point)
- *
- * Over limit: "0L" (LCD), 0x00 0x7d 0x68 0x00 (digit bytes).
- */
-
- /* Merge the two nibbles for a digit into one byte. */
- for (i = 0; i < 4; i++) {
- digit_bytes[i] = ((buf[1 + (i * 2)] & 0x0f) << 4);
- digit_bytes[i] |= (buf[1 + (i * 2) + 1] & 0x0f);
-
- /* Bit 7 in the byte is not part of the digit. */
- digit_bytes[i] &= ~(1 << 7);
- }
-
- /* Check for "OL". */
- if (digit_bytes[0] == 0x00 && digit_bytes[1] == 0x7d &&
- digit_bytes[2] == 0x68 && digit_bytes[3] == 0x00) {
- sr_spew("Over limit.");
- *result = INFINITY;
- return SR_OK;
- }
-
- /* Parse the digits. */
- for (i = 0; i < 4; i++)
- digits[i] = parse_digit(digit_bytes[i]);
- sr_spew("Digits: %02x %02x %02x %02x (%d%d%d%d).",
- digit_bytes[0], digit_bytes[1], digit_bytes[2], digit_bytes[3],
- digits[0], digits[1], digits[2], digits[3]);
-
- /* Merge all digits into an integer value. */
- for (i = 0; i < 4; i++) {
- intval *= 10;
- intval += digits[i];
- }
-
- floatval = (float)intval;
-
- /* Decimal point position. */
- if ((buf[3] & (1 << 3)) != 0) {
- floatval /= 1000;
- sr_spew("Decimal point after first digit.");
- } else if ((buf[5] & (1 << 3)) != 0) {
- floatval /= 100;
- sr_spew("Decimal point after second digit.");
- } else if ((buf[7] & (1 << 3)) != 0) {
- floatval /= 10;
- sr_spew("Decimal point after third digit.");
- } else {
- sr_spew("No decimal point in the number.");
- }
-
- /* Apply sign. */
- floatval *= sign;
-
- sr_spew("The display value is %f.", floatval);
-
- *result = floatval;
-
- return SR_OK;
-}
-
-static void parse_flags(const uint8_t *buf, struct fs9721_info *info)
-{
- /* Byte 0: LCD SEG1 */
- info->is_ac = (buf[0] & (1 << 3)) != 0;
- info->is_dc = (buf[0] & (1 << 2)) != 0;
- info->is_auto = (buf[0] & (1 << 1)) != 0;
- info->is_rs232 = (buf[0] & (1 << 0)) != 0;
-
- /* Byte 1: LCD SEG2 */
- info->is_sign = (buf[1] & (1 << 3)) != 0;
-
- /* Byte 9: LCD SEG10 */
- info->is_micro = (buf[9] & (1 << 3)) != 0;
- info->is_nano = (buf[9] & (1 << 2)) != 0;
- info->is_kilo = (buf[9] & (1 << 1)) != 0;
- info->is_diode = (buf[9] & (1 << 0)) != 0;
-
- /* Byte 10: LCD SEG11 */
- info->is_milli = (buf[10] & (1 << 3)) != 0;
- info->is_percent = (buf[10] & (1 << 2)) != 0;
- info->is_mega = (buf[10] & (1 << 1)) != 0;
- info->is_beep = (buf[10] & (1 << 0)) != 0;
-
- /* Byte 11: LCD SEG12 */
- info->is_farad = (buf[11] & (1 << 3)) != 0;
- info->is_ohm = (buf[11] & (1 << 2)) != 0;
- info->is_rel = (buf[11] & (1 << 1)) != 0;
- info->is_hold = (buf[11] & (1 << 0)) != 0;
-
- /* Byte 12: LCD SEG13 */
- info->is_ampere = (buf[12] & (1 << 3)) != 0;
- info->is_volt = (buf[12] & (1 << 2)) != 0;
- info->is_hz = (buf[12] & (1 << 1)) != 0;
- info->is_bat = (buf[12] & (1 << 0)) != 0;
-
- /* Byte 13: LCD SEG14 */
- info->is_c2c1_11 = (buf[13] & (1 << 3)) != 0;
- info->is_c2c1_10 = (buf[13] & (1 << 2)) != 0;
- info->is_c2c1_01 = (buf[13] & (1 << 1)) != 0;
- info->is_c2c1_00 = (buf[13] & (1 << 0)) != 0;
-}
-
-static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
- const struct fs9721_info *info)
-{
- /* Factors */
- if (info->is_nano)
- *floatval /= 1000000000;
- if (info->is_micro)
- *floatval /= 1000000;
- if (info->is_milli)
- *floatval /= 1000;
- if (info->is_kilo)
- *floatval *= 1000;
- if (info->is_mega)
- *floatval *= 1000000;
-
- /* Measurement modes */
- if (info->is_volt) {
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- }
- if (info->is_ampere) {
- analog->mq = SR_MQ_CURRENT;
- analog->unit = SR_UNIT_AMPERE;
- }
- if (info->is_ohm) {
- analog->mq = SR_MQ_RESISTANCE;
- analog->unit = SR_UNIT_OHM;
- }
- if (info->is_hz) {
- analog->mq = SR_MQ_FREQUENCY;
- analog->unit = SR_UNIT_HERTZ;
- }
- if (info->is_farad) {
- analog->mq = SR_MQ_CAPACITANCE;
- analog->unit = SR_UNIT_FARAD;
- }
- if (info->is_beep) {
- analog->mq = SR_MQ_CONTINUITY;
- analog->unit = SR_UNIT_BOOLEAN;
- *floatval = (*floatval == INFINITY) ? 0.0 : 1.0;
- }
- if (info->is_diode) {
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- }
- if (info->is_percent) {
- analog->mq = SR_MQ_DUTY_CYCLE;
- analog->unit = SR_UNIT_PERCENTAGE;
- }
-
- /* Measurement related flags */
- if (info->is_ac)
- analog->mqflags |= SR_MQFLAG_AC;
- if (info->is_dc)
- analog->mqflags |= SR_MQFLAG_DC;
- if (info->is_auto)
- analog->mqflags |= SR_MQFLAG_AUTORANGE;
- if (info->is_diode)
- analog->mqflags |= SR_MQFLAG_DIODE;
- if (info->is_hold)
- analog->mqflags |= SR_MQFLAG_HOLD;
- if (info->is_rel)
- analog->mqflags |= SR_MQFLAG_RELATIVE;
-
- /* Other flags */
- if (info->is_rs232)
- sr_spew("RS232 enabled.");
- if (info->is_bat)
- sr_spew("Battery is low.");
- if (info->is_c2c1_00)
- sr_spew("User-defined LCD symbol 0 is active.");
- if (info->is_c2c1_01)
- sr_spew("User-defined LCD symbol 1 is active.");
- if (info->is_c2c1_10)
- sr_spew("User-defined LCD symbol 2 is active.");
- if (info->is_c2c1_11)
- sr_spew("User-defined LCD symbol 3 is active.");
-}
-
-SR_PRIV gboolean sr_fs9721_packet_valid(const uint8_t *buf)
-{
- struct fs9721_info info;
-
- parse_flags(buf, &info);
-
- return (sync_nibbles_valid(buf) && flags_valid(&info));
-}
-
-/**
- * Parse a protocol packet.
- *
- * @param buf Buffer containing the 14-byte protocol packet. Must not be NULL.
- * @param floatval Pointer to a float variable. That variable will contain the
- * result value upon parsing success. Mut not be NULL.
- * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
- * filled with data according to the protocol packet.
- * Must not be NULL.
- * @param info Pointer to a struct fs9721_info. The struct will be filled
- * with data according to the protocol packet. Must not be NULL.
- *
- * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the
- * 'analog' variable contents are undefined and should not be used.
- */
-SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info)
-{
- int ret;
- struct fs9721_info *info_local;
-
- info_local = (struct fs9721_info *)info;
-
- if ((ret = parse_value(buf, floatval)) != SR_OK) {
- sr_dbg("Error parsing value: %d.", ret);
- return ret;
- }
-
- parse_flags(buf, info_local);
- handle_flags(analog, floatval, info_local);
-
- return SR_OK;
-}
-
-SR_PRIV void sr_fs9721_00_temp_c(struct sr_datafeed_analog *analog, void *info)
-{
- struct fs9721_info *info_local;
-
- info_local = (struct fs9721_info *)info;
-
- /* User-defined FS9721_LP3 flag 'c2c1_00' means temperature (C). */
- if (info_local->is_c2c1_00) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_CELSIUS;
- }
-}
-
-SR_PRIV void sr_fs9721_01_temp_c(struct sr_datafeed_analog *analog, void *info)
-{
- struct fs9721_info *info_local;
-
- info_local = (struct fs9721_info *)info;
-
- /* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (C). */
- if (info_local->is_c2c1_01) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_CELSIUS;
- }
-}
-
-SR_PRIV void sr_fs9721_10_temp_c(struct sr_datafeed_analog *analog, void *info)
-{
- struct fs9721_info *info_local;
-
- info_local = (struct fs9721_info *)info;
-
- /* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
- if (info_local->is_c2c1_10) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_CELSIUS;
- }
-}
-
-SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog *analog, void *info)
-{
- struct fs9721_info *info_local;
-
- info_local = (struct fs9721_info *)info;
-
- /* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (F). */
- if (info_local->is_c2c1_01) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_FAHRENHEIT;
- }
-
- /* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
- if (info_local->is_c2c1_10) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_CELSIUS;
- }
-}
-
-SR_PRIV void sr_fs9721_max_c_min(struct sr_datafeed_analog *analog, void *info)
-{
- struct fs9721_info *info_local;
-
- info_local = (struct fs9721_info *)info;
-
- /* User-defined FS9721_LP3 flag 'c2c1_00' means MAX. */
- if (info_local->is_c2c1_00)
- analog->mqflags |= SR_MQFLAG_MAX;
-
- /* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (C). */
- if (info_local->is_c2c1_01) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_CELSIUS;
- }
-
- /* User-defined FS9721_LP3 flag 'c2c1_11' means MIN. */
- if (info_local->is_c2c1_11)
- analog->mqflags |= SR_MQFLAG_MIN;
-
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * Fortune Semiconductor FS9922-DMM3/FS9922-DMM4 protocol parser.
- */
-
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "fs9922"
-
-static gboolean flags_valid(const struct fs9922_info *info)
-{
- int count;
-
- /* Does the packet have more than one multiplier? */
- count = 0;
- count += (info->is_nano) ? 1 : 0;
- count += (info->is_micro) ? 1 : 0;
- count += (info->is_milli) ? 1 : 0;
- count += (info->is_kilo) ? 1 : 0;
- count += (info->is_mega) ? 1 : 0;
- if (count > 1) {
- sr_dbg("More than one multiplier detected in packet.");
- return FALSE;
- }
-
- /*
- * Does the packet "measure" more than one type of value?
- *
- * Note: In "diode mode", both is_diode and is_volt will be set.
- * That is a valid use-case, so we don't want to error out below
- * if it happens. Thus, we don't check for is_diode here.
- */
- count = 0;
- // count += (info->is_diode) ? 1 : 0;
- count += (info->is_percent) ? 1 : 0;
- count += (info->is_volt) ? 1 : 0;
- count += (info->is_ampere) ? 1 : 0;
- count += (info->is_ohm) ? 1 : 0;
- count += (info->is_hfe) ? 1 : 0;
- count += (info->is_hertz) ? 1 : 0;
- count += (info->is_farad) ? 1 : 0;
- count += (info->is_celsius) ? 1 : 0;
- count += (info->is_fahrenheit) ? 1 : 0;
- if (count > 1) {
- sr_dbg("More than one measurement type detected in packet.");
- return FALSE;
- }
-
- /* Both AC and DC set? */
- if (info->is_ac && info->is_dc) {
- sr_dbg("Both AC and DC flags detected in packet.");
- return FALSE;
- }
-
- /* Both Celsius and Fahrenheit set? */
- if (info->is_celsius && info->is_fahrenheit) {
- sr_dbg("Both Celsius and Fahrenheit flags detected in packet.");
- return FALSE;
- }
-
- return TRUE;
-}
-
-static int parse_value(const uint8_t *buf, float *result)
-{
- int sign, intval;
- float floatval;
-
- /* Byte 0: Sign ('+' or '-') */
- if (buf[0] == '+') {
- sign = 1;
- } else if (buf[0] == '-') {
- sign = -1;
- } else {
- sr_dbg("Invalid sign byte: 0x%02x.", buf[0]);
- return SR_ERR;
- }
-
- /*
- * Bytes 1-4: Value (4 decimal digits)
- *
- * Over limit: "0.L" on the display, "?0:?" as protocol "digits".
- */
- if (buf[1] == '?' && buf[2] == '0' && buf[3] == ':' && buf[4] == '?') {
- sr_spew("Over limit.");
- *result = INFINITY;
- return SR_OK;
- } else if (!isdigit(buf[1]) || !isdigit(buf[2]) ||
- !isdigit(buf[3]) || !isdigit(buf[4])) {
- sr_dbg("Value contained invalid digits: %02x %02x %02x %02x ("
- "%c %c %c %c).", buf[1], buf[2], buf[3], buf[4]);
- return SR_ERR;
- }
- intval = 0;
- intval += (buf[1] - '0') * 1000;
- intval += (buf[2] - '0') * 100;
- intval += (buf[3] - '0') * 10;
- intval += (buf[4] - '0') * 1;
-
- floatval = (float)intval;
-
- /* Byte 5: Always ' ' (space, 0x20) */
-
- /*
- * Byte 6: Decimal point position ('0', '1', '2', or '4')
- *
- * Note: The Fortune Semiconductor FS9922-DMM3/4 datasheets both have
- * an error/typo here. They claim that the values '0'/'1'/'2'/'3' are
- * used, but '0'/'1'/'2'/'4' is actually correct.
- */
- if (buf[6] != '0' && buf[6] != '1' && buf[6] != '2' && buf[6] != '4') {
- sr_dbg("Invalid decimal point value: 0x%02x.", buf[6]);
- return SR_ERR;
- }
- if (buf[6] == '0')
- floatval /= 1;
- else if (buf[6] == '1')
- floatval /= 1000;
- else if (buf[6] == '2')
- floatval /= 100;
- else if (buf[6] == '4')
- floatval /= 10;
-
- /* Apply sign. */
- floatval *= sign;
-
- sr_spew("The display value is %f.", floatval);
-
- *result = floatval;
-
- return SR_OK;
-}
-
-static void parse_flags(const uint8_t *buf, struct fs9922_info *info)
-{
- /* Z1/Z2/Z3/Z4 are bits for user-defined LCD symbols (on/off). */
-
- /* Byte 7 */
- /* Bit 7: Always 0 */
- /* Bit 6: Always 0 */
- info->is_auto = (buf[7] & (1 << 5)) != 0;
- info->is_dc = (buf[7] & (1 << 4)) != 0;
- info->is_ac = (buf[7] & (1 << 3)) != 0;
- info->is_rel = (buf[7] & (1 << 2)) != 0;
- info->is_hold = (buf[7] & (1 << 1)) != 0;
- info->is_bpn = (buf[7] & (1 << 0)) != 0; /* Bargraph shown */
-
- /* Byte 8 */
- info->is_z1 = (buf[8] & (1 << 7)) != 0; /* User symbol 1 */
- info->is_z2 = (buf[8] & (1 << 6)) != 0; /* User symbol 2 */
- info->is_max = (buf[8] & (1 << 5)) != 0;
- info->is_min = (buf[8] & (1 << 4)) != 0;
- info->is_apo = (buf[8] & (1 << 3)) != 0; /* Auto-poweroff on */
- info->is_bat = (buf[8] & (1 << 2)) != 0; /* Battery low */
- info->is_nano = (buf[8] & (1 << 1)) != 0;
- info->is_z3 = (buf[8] & (1 << 0)) != 0; /* User symbol 3 */
-
- /* Byte 9 */
- info->is_micro = (buf[9] & (1 << 7)) != 0;
- info->is_milli = (buf[9] & (1 << 6)) != 0;
- info->is_kilo = (buf[9] & (1 << 5)) != 0;
- info->is_mega = (buf[9] & (1 << 4)) != 0;
- info->is_beep = (buf[9] & (1 << 3)) != 0;
- info->is_diode = (buf[9] & (1 << 2)) != 0;
- info->is_percent = (buf[9] & (1 << 1)) != 0;
- info->is_z4 = (buf[9] & (1 << 0)) != 0; /* User symbol 4 */
-
- /* Byte 10 */
- info->is_volt = (buf[10] & (1 << 7)) != 0;
- info->is_ampere = (buf[10] & (1 << 6)) != 0;
- info->is_ohm = (buf[10] & (1 << 5)) != 0;
- info->is_hfe = (buf[10] & (1 << 4)) != 0;
- info->is_hertz = (buf[10] & (1 << 3)) != 0;
- info->is_farad = (buf[10] & (1 << 2)) != 0;
- info->is_celsius = (buf[10] & (1 << 1)) != 0; /* Only FS9922-DMM4 */
- info->is_fahrenheit = (buf[10] & (1 << 0)) != 0; /* Only FS9922-DMM4 */
-
- /*
- * Byte 11: Bar graph
- *
- * Bit 7 contains the sign of the bargraph number (if the bit is set,
- * the number is negative), bits 6..0 contain the actual number.
- * Valid range: 0-40 (FS9922-DMM3), 0-60 (FS9922-DMM4).
- *
- * Upon "over limit" the bargraph value is 1 count above the highest
- * valid number (i.e. 41 or 61, depending on chip).
- */
- if (info->is_bpn) {
- info->bargraph_sign = ((buf[11] & (1 << 7)) != 0) ? -1 : 1;
- info->bargraph_value = (buf[11] & 0x7f);
- info->bargraph_value *= info->bargraph_sign;
- }
-
- /* Byte 12: Always '\r' (carriage return, 0x0d, 13) */
-
- /* Byte 13: Always '\n' (newline, 0x0a, 10) */
-}
-
-static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
- const struct fs9922_info *info)
-{
- /* Factors */
- if (info->is_nano)
- *floatval /= 1000000000;
- if (info->is_micro)
- *floatval /= 1000000;
- if (info->is_milli)
- *floatval /= 1000;
- if (info->is_kilo)
- *floatval *= 1000;
- if (info->is_mega)
- *floatval *= 1000000;
-
- /* Measurement modes */
- if (info->is_volt || info->is_diode) {
- /* Note: In "diode mode" both is_diode and is_volt are set. */
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- }
- if (info->is_ampere) {
- analog->mq = SR_MQ_CURRENT;
- analog->unit = SR_UNIT_AMPERE;
- }
- if (info->is_ohm) {
- analog->mq = SR_MQ_RESISTANCE;
- analog->unit = SR_UNIT_OHM;
- }
- if (info->is_hfe) {
- analog->mq = SR_MQ_GAIN;
- analog->unit = SR_UNIT_UNITLESS;
- }
- if (info->is_hertz) {
- analog->mq = SR_MQ_FREQUENCY;
- analog->unit = SR_UNIT_HERTZ;
- }
- if (info->is_farad) {
- analog->mq = SR_MQ_CAPACITANCE;
- analog->unit = SR_UNIT_FARAD;
- }
- if (info->is_celsius) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_CELSIUS;
- }
- if (info->is_fahrenheit) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_FAHRENHEIT;
- }
- if (info->is_beep) {
- analog->mq = SR_MQ_CONTINUITY;
- analog->unit = SR_UNIT_BOOLEAN;
- *floatval = (*floatval == INFINITY) ? 0.0 : 1.0;
- }
- if (info->is_percent) {
- analog->mq = SR_MQ_DUTY_CYCLE;
- analog->unit = SR_UNIT_PERCENTAGE;
- }
-
- /* Measurement related flags */
- if (info->is_ac)
- analog->mqflags |= SR_MQFLAG_AC;
- if (info->is_dc)
- analog->mqflags |= SR_MQFLAG_DC;
- if (info->is_auto)
- analog->mqflags |= SR_MQFLAG_AUTORANGE;
- if (info->is_diode)
- analog->mqflags |= SR_MQFLAG_DIODE;
- if (info->is_hold)
- analog->mqflags |= SR_MQFLAG_HOLD;
- if (info->is_max)
- analog->mqflags |= SR_MQFLAG_MAX;
- if (info->is_min)
- analog->mqflags |= SR_MQFLAG_MIN;
- if (info->is_rel)
- analog->mqflags |= SR_MQFLAG_RELATIVE;
-
- /* Other flags */
- if (info->is_apo)
- sr_spew("Automatic power-off function is active.");
- if (info->is_bat)
- sr_spew("Battery is low.");
- if (info->is_z1)
- sr_spew("User-defined LCD symbol 1 is active.");
- if (info->is_z2)
- sr_spew("User-defined LCD symbol 2 is active.");
- if (info->is_z3)
- sr_spew("User-defined LCD symbol 3 is active.");
- if (info->is_z4)
- sr_spew("User-defined LCD symbol 4 is active.");
- if (info->is_bpn)
- sr_spew("The bargraph value is %d.", info->bargraph_value);
- else
- sr_spew("The bargraph is not active.");
-
-}
-
-SR_PRIV gboolean sr_fs9922_packet_valid(const uint8_t *buf)
-{
- struct fs9922_info info;
-
- /* Byte 0: Sign (must be '+' or '-') */
- if (buf[0] != '+' && buf[0] != '-')
- return FALSE;
-
- /* Byte 12: Always '\r' (carriage return, 0x0d, 13) */
- /* Byte 13: Always '\n' (newline, 0x0a, 10) */
- if (buf[12] != '\r' || buf[13] != '\n')
- return FALSE;
-
- parse_flags(buf, &info);
-
- return flags_valid(&info);
-}
-
-/**
- * Parse a protocol packet.
- *
- * @param buf Buffer containing the protocol packet. Must not be NULL.
- * @param floatval Pointer to a float variable. That variable will contain the
- * result value upon parsing success. Must not be NULL.
- * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
- * filled with data according to the protocol packet.
- * Must not be NULL.
- * @param info Pointer to a struct fs9922_info. The struct will be filled
- * with data according to the protocol packet. Must not be NULL.
- *
- * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the
- * 'analog' variable contents are undefined and should not be used.
- */
-SR_PRIV int sr_fs9922_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info)
-{
- int ret;
- struct fs9922_info *info_local;
-
- info_local = (struct fs9922_info *)info;
-
- if ((ret = parse_value(buf, floatval)) != SR_OK) {
- sr_dbg("Error parsing value: %d.", ret);
- return ret;
- }
-
- parse_flags(buf, info_local);
- handle_flags(analog, floatval, info_local);
-
- return SR_OK;
-}
-
-SR_PRIV void sr_fs9922_z1_diode(struct sr_datafeed_analog *analog, void *info)
-{
- struct fs9922_info *info_local;
-
- info_local = (struct fs9922_info *)info;
-
- /* User-defined z1 flag means "diode mode". */
- if (info_local->is_z1) {
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- analog->mqflags |= SR_MQFLAG_DIODE;
- }
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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/>.
- */
-
-/**
- * @file
- *
- * BBC Goerz Metrawatt M2110 ASCII protocol parser.
- *
- * Most probably the simplest multimeter protocol ever ;-) .
- */
-
-#include <string.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "m2110"
-
-SR_PRIV gboolean sr_m2110_packet_valid(const uint8_t *buf)
-{
- float val;
-
- if ((buf[7] != '\r') || (buf[8] != '\n'))
- return FALSE;
-
- if (!strncmp((const char *)buf, "OVERRNG", 7))
- return TRUE;
-
- if (sscanf((const char *)buf, "%f", &val) == 1)
- return TRUE;
- else
- return FALSE;
-}
-
-SR_PRIV int sr_m2110_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info)
-{
- float val;
-
- (void)info;
-
- /* We don't know the unit, so that's the best we can do. */
- analog->mq = SR_MQ_GAIN;
- analog->unit = SR_UNIT_UNITLESS;
- analog->mqflags = 0;
-
- if (!strncmp((const char *)buf, "OVERRNG", 7))
- *floatval = INFINITY;
- else if (sscanf((const char *)buf, "%f", &val) == 1)
- *floatval = val;
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/**
- * @file
- *
- * Metex 14-bytes ASCII protocol parser.
- *
- * @internal
- * This should work for various multimeters which use this kind of protocol,
- * even though there is some variation in which modes each DMM supports.
- *
- * It does _not_ work for all Metex DMMs, some use a quite different protocol.
- */
-
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "metex14"
-
-/** Parse value from buf, byte 2-8. */
-static int parse_value(const uint8_t *buf, struct metex14_info *info,
- float *result)
-{
- int i, is_ol, cnt;
- char valstr[7 + 1];
-
- /* Strip all spaces from bytes 2-8. */
- memset(&valstr, 0, 7 + 1);
- for (i = 0, cnt = 0; i < 7; i++) {
- if (buf[2 + i] != ' ')
- valstr[cnt++] = buf[2 + i];
- }
-
- /* Bytes 5-7: Over limit (various forms) */
- is_ol = 0;
- is_ol += (!strcasecmp((const char *)&valstr, ".OL")) ? 1 : 0;
- is_ol += (!strcasecmp((const char *)&valstr, "O.L")) ? 1 : 0;
- is_ol += (!strcasecmp((const char *)&valstr, "OL.")) ? 1 : 0;
- is_ol += (!strcasecmp((const char *)&valstr, "OL")) ? 1 : 0;
- is_ol += (!strcasecmp((const char *)&valstr, "-.OL")) ? 1 : 0;
- is_ol += (!strcasecmp((const char *)&valstr, "-O.L")) ? 1 : 0;
- is_ol += (!strcasecmp((const char *)&valstr, "-OL.")) ? 1 : 0;
- is_ol += (!strcasecmp((const char *)&valstr, "-OL")) ? 1 : 0;
- if (is_ol != 0) {
- sr_spew("Over limit.");
- *result = INFINITY;
- return SR_OK;
- }
-
- /* Logic functions */
- if (!strcmp((const char *)&valstr, "READY") ||
- !strcmp((const char *)&valstr, "FLOAT")) {
- *result = INFINITY;
- info->is_logic = TRUE;
- } else if (!strcmp((const char *)&valstr, "Hi")) {
- *result = 1.0;
- info->is_logic = TRUE;
- } else if (!strcmp((const char *)&valstr, "Lo")) {
- *result = 0.0;
- info->is_logic = TRUE;
- }
- if (info->is_logic)
- return SR_OK;
-
- /* Bytes 2-8: Sign, value (up to 5 digits) and decimal point */
- sscanf((const char *)&valstr, "%f", result);
-
- sr_spew("The display value is %f.", *result);
-
- return SR_OK;
-}
-
-static void parse_flags(const char *buf, struct metex14_info *info)
-{
- int i, cnt;
- char unit[4 + 1];
- const char *u;
-
- /* Bytes 0-1: Measurement mode AC, DC */
- info->is_ac = !strncmp(buf, "AC", 2);
- info->is_dc = !strncmp(buf, "DC", 2);
-
- /* Bytes 2-8: See parse_value(). */
-
- /* Strip all spaces from bytes 9-12. */
- memset(&unit, 0, 4 + 1);
- for (i = 0, cnt = 0; i < 4; i++) {
- if (buf[9 + i] != ' ')
- unit[cnt++] = buf[9 + i];
- }
-
- /* Bytes 9-12: Unit */
- u = (const char *)&unit;
- if (!strcasecmp(u, "A"))
- info->is_ampere = TRUE;
- else if (!strcasecmp(u, "mA"))
- info->is_milli = info->is_ampere = TRUE;
- else if (!strcasecmp(u, "uA"))
- info->is_micro = info->is_ampere = TRUE;
- else if (!strcasecmp(u, "V"))
- info->is_volt = TRUE;
- else if (!strcasecmp(u, "mV"))
- info->is_milli = info->is_volt = TRUE;
- else if (!strcasecmp(u, "Ohm"))
- info->is_ohm = TRUE;
- else if (!strcasecmp(u, "KOhm"))
- info->is_kilo = info->is_ohm = TRUE;
- else if (!strcasecmp(u, "MOhm"))
- info->is_mega = info->is_ohm = TRUE;
- else if (!strcasecmp(u, "pF"))
- info->is_pico = info->is_farad = TRUE;
- else if (!strcasecmp(u, "nF"))
- info->is_nano = info->is_farad = TRUE;
- else if (!strcasecmp(u, "uF"))
- info->is_micro = info->is_farad = TRUE;
- else if (!strcasecmp(u, "KHz"))
- info->is_kilo = info->is_hertz = TRUE;
- else if (!strcasecmp(u, "C"))
- info->is_celsius = TRUE;
- else if (!strcasecmp(u, "DB"))
- info->is_decibel = TRUE;
- else if (!strcasecmp(u, ""))
- info->is_unitless = TRUE;
-
- /* Bytes 0-1: Measurement mode, except AC/DC */
- info->is_resistance = !strncmp(buf, "OH", 2) ||
- (!strncmp(buf, " ", 2) && info->is_ohm);
- info->is_capacity = !strncmp(buf, "CA", 2) ||
- (!strncmp(buf, " ", 2) && info->is_farad);
- info->is_temperature = !strncmp(buf, "TE", 2);
- info->is_diode = !strncmp(buf, "DI", 2) ||
- (!strncmp(buf, " ", 2) && info->is_volt && info->is_milli);
- info->is_frequency = !strncmp(buf, "FR", 2) ||
- (!strncmp(buf, " ", 2) && info->is_hertz);
- info->is_gain = !strncmp(buf, "DB", 2);
- info->is_hfe = !strncmp(buf, "HF", 2) ||
- (!strncmp(buf, " ", 2) && !info->is_volt && !info->is_ohm &&
- !info->is_logic && !info->is_farad && !info->is_hertz);
- /*
- * Note:
- * - Protocol doesn't distinguish "resistance" from "beep" mode.
- * - "DB" shows the logarithmic ratio of input voltage to a
- * pre-stored (user-changeable) value in the DMM.
- */
-
- /* Byte 13: Always '\r' (carriage return, 0x0d, 13) */
-}
-
-static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
- const struct metex14_info *info)
-{
- /* Factors */
- if (info->is_pico)
- *floatval /= 1000000000000ULL;
- if (info->is_nano)
- *floatval /= 1000000000;
- if (info->is_micro)
- *floatval /= 1000000;
- if (info->is_milli)
- *floatval /= 1000;
- if (info->is_kilo)
- *floatval *= 1000;
- if (info->is_mega)
- *floatval *= 1000000;
-
- /* Measurement modes */
- if (info->is_volt) {
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- }
- if (info->is_ampere) {
- analog->mq = SR_MQ_CURRENT;
- analog->unit = SR_UNIT_AMPERE;
- }
- if (info->is_ohm) {
- analog->mq = SR_MQ_RESISTANCE;
- analog->unit = SR_UNIT_OHM;
- }
- if (info->is_hertz) {
- analog->mq = SR_MQ_FREQUENCY;
- analog->unit = SR_UNIT_HERTZ;
- }
- if (info->is_farad) {
- analog->mq = SR_MQ_CAPACITANCE;
- analog->unit = SR_UNIT_FARAD;
- }
- if (info->is_celsius) {
- analog->mq = SR_MQ_TEMPERATURE;
- analog->unit = SR_UNIT_CELSIUS;
- }
- if (info->is_diode) {
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- }
- if (info->is_gain) {
- analog->mq = SR_MQ_GAIN;
- analog->unit = SR_UNIT_DECIBEL_VOLT;
- }
- if (info->is_hfe) {
- analog->mq = SR_MQ_GAIN;
- analog->unit = SR_UNIT_UNITLESS;
- }
- if (info->is_logic) {
- analog->mq = SR_MQ_GAIN;
- analog->unit = SR_UNIT_UNITLESS;
- }
-
- /* Measurement related flags */
- if (info->is_ac)
- analog->mqflags |= SR_MQFLAG_AC;
- if (info->is_dc)
- analog->mqflags |= SR_MQFLAG_DC;
- if (info->is_diode)
- analog->mqflags |= SR_MQFLAG_DIODE;
-}
-
-static gboolean flags_valid(const struct metex14_info *info)
-{
- int count;
-
- /* Does the packet have more than one multiplier? */
- count = 0;
- count += (info->is_pico) ? 1 : 0;
- count += (info->is_nano) ? 1 : 0;
- count += (info->is_micro) ? 1 : 0;
- count += (info->is_milli) ? 1 : 0;
- count += (info->is_kilo) ? 1 : 0;
- count += (info->is_mega) ? 1 : 0;
- if (count > 1) {
- sr_dbg("More than one multiplier detected in packet.");
- return FALSE;
- }
-
- /* Does the packet "measure" more than one type of value? */
- count = 0;
- count += (info->is_ac) ? 1 : 0;
- count += (info->is_dc) ? 1 : 0;
- count += (info->is_resistance) ? 1 : 0;
- count += (info->is_capacity) ? 1 : 0;
- count += (info->is_temperature) ? 1 : 0;
- count += (info->is_diode) ? 1 : 0;
- count += (info->is_frequency) ? 1 : 0;
- if (count > 1) {
- sr_dbg("More than one measurement type detected in packet.");
- return FALSE;
- }
-
- /* Both AC and DC set? */
- if (info->is_ac && info->is_dc) {
- sr_dbg("Both AC and DC flags detected in packet.");
- return FALSE;
- }
-
- return TRUE;
-}
-
-#ifdef HAVE_LIBSERIALPORT
-SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial)
-{
- const uint8_t wbuf = 'D';
-
- sr_spew("Requesting DMM packet.");
-
- return (serial_write(serial, &wbuf, 1) == 1) ? SR_OK : SR_ERR;
-}
-#endif
-
-SR_PRIV gboolean sr_metex14_packet_valid(const uint8_t *buf)
-{
- struct metex14_info info;
-
- memset(&info, 0x00, sizeof(struct metex14_info));
- parse_flags((const char *)buf, &info);
-
- if (!flags_valid(&info))
- return FALSE;
-
- if (buf[13] != '\r')
- return FALSE;
-
- return TRUE;
-}
-
-/**
- * Parse a protocol packet.
- *
- * @param buf Buffer containing the protocol packet. Must not be NULL.
- * @param floatval Pointer to a float variable. That variable will be modified
- * in-place depending on the protocol packet. Must not be NULL.
- * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
- * filled with data according to the protocol packet.
- * Must not be NULL.
- * @param info Pointer to a struct metex14_info. The struct will be filled
- * with data according to the protocol packet. Must not be NULL.
- *
- * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the
- * 'analog' variable contents are undefined and should not be used.
- */
-SR_PRIV int sr_metex14_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info)
-{
- int ret;
- struct metex14_info *info_local;
-
- info_local = (struct metex14_info *)info;
-
- /* Don't print byte 13. That one contains the carriage return. */
- sr_dbg("DMM packet: \"%.13s\"", buf);
-
- memset(info_local, 0x00, sizeof(struct metex14_info));
-
- if ((ret = parse_value(buf, info_local, floatval)) != SR_OK) {
- sr_dbg("Error parsing value: %d.", ret);
- return ret;
- }
-
- parse_flags((const char *)buf, info_local);
- handle_flags(analog, floatval, info_local);
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
- *
- * 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/>.
- */
-
-/*
- * RadioShack 22-812 protocol parser.
- *
- * This protocol is currently encountered on the RadioShack 22-812 DMM.
- * It is a 9-byte packet representing a 1:1 mapping of the LCD segments, hence
- * the name rs9lcd.
- *
- * The chip is a bare die covered by a plastic blob. It is unclear if this chip
- * and protocol is used on any other device.
- */
-
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "rs9lcd"
-
-/* Byte 1 of the packet, and the modes it represents */
-#define IND1_HZ (1 << 7)
-#define IND1_OHM (1 << 6)
-#define IND1_KILO (1 << 5)
-#define IND1_MEGA (1 << 4)
-#define IND1_FARAD (1 << 3)
-#define IND1_AMP (1 << 2)
-#define IND1_VOLT (1 << 1)
-#define IND1_MILI (1 << 0)
-/* Byte 2 of the packet, and the modes it represents */
-#define IND2_MICRO (1 << 7)
-#define IND2_NANO (1 << 6)
-#define IND2_DBM (1 << 5)
-#define IND2_SEC (1 << 4)
-#define IND2_DUTY (1 << 3)
-#define IND2_HFE (1 << 2)
-#define IND2_REL (1 << 1)
-#define IND2_MIN (1 << 0)
-/* Byte 7 of the packet, and the modes it represents */
-#define INFO_BEEP (1 << 7)
-#define INFO_DIODE (1 << 6)
-#define INFO_BAT (1 << 5)
-#define INFO_HOLD (1 << 4)
-#define INFO_NEG (1 << 3)
-#define INFO_AC (1 << 2)
-#define INFO_RS232 (1 << 1)
-#define INFO_AUTO (1 << 0)
-/* Instead of a decimal point, digit 4 carries the MAX flag */
-#define DIG4_MAX (1 << 3)
-/* Mask to remove the decimal point from a digit */
-#define DP_MASK (1 << 3)
-
-/* What the LCD values represent */
-#define LCD_0 0xd7
-#define LCD_1 0x50
-#define LCD_2 0xb5
-#define LCD_3 0xf1
-#define LCD_4 0x72
-#define LCD_5 0xe3
-#define LCD_6 0xe7
-#define LCD_7 0x51
-#define LCD_8 0xf7
-#define LCD_9 0xf3
-
-#define LCD_C 0x87
-#define LCD_E
-#define LCD_F
-#define LCD_h 0x66
-#define LCD_H 0x76
-#define LCD_I
-#define LCD_n
-#define LCD_P 0x37
-#define LCD_r
-
-enum {
- MODE_DC_V = 0,
- MODE_AC_V = 1,
- MODE_DC_UA = 2,
- MODE_DC_MA = 3,
- MODE_DC_A = 4,
- MODE_AC_UA = 5,
- MODE_AC_MA = 6,
- MODE_AC_A = 7,
- MODE_OHM = 8,
- MODE_FARAD = 9,
- MODE_HZ = 10,
- MODE_VOLT_HZ = 11, /* Dial set to V, Hz selected by Hz button */
- MODE_AMP_HZ = 12, /* Dial set to A, Hz selected by Hz button */
- MODE_DUTY = 13,
- MODE_VOLT_DUTY = 14, /* Dial set to V, duty cycle selected */
- MODE_AMP_DUTY = 15, /* Dial set to A, duty cycle selected */
- MODE_WIDTH = 16,
- MODE_VOLT_WIDTH = 17, /* Dial set to V, pulse width selected */
- MODE_AMP_WIDTH = 18, /* Dial set to A, pulse width selected */
- MODE_DIODE = 19,
- MODE_CONT = 20,
- MODE_HFE = 21,
- MODE_LOGIC = 22,
- MODE_DBM = 23,
- /* MODE_EF = 24, */ /* Not encountered on any DMM */
- MODE_TEMP = 25,
- MODE_INVALID = 26,
-};
-
-enum {
- READ_ALL,
- READ_TEMP,
-};
-
-struct rs9lcd_packet {
- uint8_t mode;
- uint8_t indicatrix1;
- uint8_t indicatrix2;
- uint8_t digit4;
- uint8_t digit3;
- uint8_t digit2;
- uint8_t digit1;
- uint8_t info;
- uint8_t checksum;
-};
-
-static gboolean checksum_valid(const struct rs9lcd_packet *rs_packet)
-{
- uint8_t *raw;
- uint8_t sum = 0;
- int i;
-
- raw = (void *)rs_packet;
-
- for (i = 0; i < RS9LCD_PACKET_SIZE - 1; i++)
- sum += raw[i];
-
- /* This is just a funky constant added to the checksum. */
- sum += 57;
- sum -= rs_packet->checksum;
- return (sum == 0);
-}
-
-static gboolean selection_good(const struct rs9lcd_packet *rs_packet)
-{
- int count;
-
- /* Does the packet have more than one multiplier? */
- count = 0;
- count += (rs_packet->indicatrix1 & IND1_KILO) ? 1 : 0;
- count += (rs_packet->indicatrix1 & IND1_MEGA) ? 1 : 0;
- count += (rs_packet->indicatrix1 & IND1_MILI) ? 1 : 0;
- count += (rs_packet->indicatrix2 & IND2_MICRO) ? 1 : 0;
- count += (rs_packet->indicatrix2 & IND2_NANO) ? 1 : 0;
- if (count > 1) {
- sr_dbg("More than one multiplier detected in packet.");
- return FALSE;
- }
-
- /* Does the packet "measure" more than one type of value? */
- count = 0;
- count += (rs_packet->indicatrix1 & IND1_HZ) ? 1 : 0;
- count += (rs_packet->indicatrix1 & IND1_OHM) ? 1 : 0;
- count += (rs_packet->indicatrix1 & IND1_FARAD) ? 1 : 0;
- count += (rs_packet->indicatrix1 & IND1_AMP) ? 1 : 0;
- count += (rs_packet->indicatrix1 & IND1_VOLT) ? 1 : 0;
- count += (rs_packet->indicatrix2 & IND2_DBM) ? 1 : 0;
- count += (rs_packet->indicatrix2 & IND2_SEC) ? 1 : 0;
- count += (rs_packet->indicatrix2 & IND2_DUTY) ? 1 : 0;
- count += (rs_packet->indicatrix2 & IND2_HFE) ? 1 : 0;
- if (count > 1) {
- sr_dbg("More than one measurement type detected in packet.");
- return FALSE;
- }
-
- return TRUE;
-}
-
-/*
- * Since the 22-812 does not identify itself in any way, shape, or form,
- * we really don't know for sure who is sending the data. We must use every
- * possible check to filter out bad packets, especially since detection of the
- * 22-812 depends on how well we can filter the packets.
- */
-SR_PRIV gboolean sr_rs9lcd_packet_valid(const uint8_t *buf)
-{
- const struct rs9lcd_packet *rs_packet = (void *)buf;
-
- /*
- * Check for valid mode first, before calculating the checksum. No
- * point calculating the checksum, if we know we'll reject the packet.
- */
- if (!(rs_packet->mode < MODE_INVALID))
- return FALSE;
-
- if (!checksum_valid(rs_packet)) {
- sr_spew("Packet with invalid checksum. Discarding.");
- return FALSE;
- }
-
- if (!selection_good(rs_packet)) {
- sr_spew("Packet with invalid selection bits. Discarding.");
- return FALSE;
- }
-
- return TRUE;
-}
-
-static uint8_t decode_digit(uint8_t raw_digit)
-{
- /* Take out the decimal point, so we can use a simple switch(). */
- raw_digit &= ~DP_MASK;
-
- switch (raw_digit) {
- case 0x00:
- case LCD_0:
- return 0;
- case LCD_1:
- return 1;
- case LCD_2:
- return 2;
- case LCD_3:
- return 3;
- case LCD_4:
- return 4;
- case LCD_5:
- return 5;
- case LCD_6:
- return 6;
- case LCD_7:
- return 7;
- case LCD_8:
- return 8;
- case LCD_9:
- return 9;
- default:
- sr_dbg("Invalid digit byte: 0x%02x.", raw_digit);
- return 0xff;
- }
-}
-
-static double lcd_to_double(const struct rs9lcd_packet *rs_packet, int type)
-{
- double rawval = 0, multiplier = 1;
- uint8_t digit, raw_digit;
- gboolean dp_reached = FALSE;
- int i, end;
-
- /* end = 1: Don't parse last digit. end = 0: Parse all digits. */
- end = (type == READ_TEMP) ? 1 : 0;
-
- /* We have 4 digits, and we start from the most significant. */
- for (i = 3; i >= end; i--) {
- raw_digit = *(&(rs_packet->digit4) + i);
- digit = decode_digit(raw_digit);
- if (digit == 0xff) {
- rawval = NAN;
- break;
- }
- /*
- * Digit 1 does not have a decimal point. Instead, the decimal
- * point is used to indicate MAX, so we must avoid testing it.
- */
- if ((i < 3) && (raw_digit & DP_MASK))
- dp_reached = TRUE;
- if (dp_reached)
- multiplier /= 10;
- rawval = rawval * 10 + digit;
- }
- rawval *= multiplier;
- if (rs_packet->info & INFO_NEG)
- rawval *= -1;
-
- /* See if we need to multiply our raw value by anything. */
- if (rs_packet->indicatrix1 & IND2_NANO)
- rawval *= 1E-9;
- else if (rs_packet->indicatrix2 & IND2_MICRO)
- rawval *= 1E-6;
- else if (rs_packet->indicatrix1 & IND1_MILI)
- rawval *= 1E-3;
- else if (rs_packet->indicatrix1 & IND1_KILO)
- rawval *= 1E3;
- else if (rs_packet->indicatrix1 & IND1_MEGA)
- rawval *= 1E6;
-
- return rawval;
-}
-
-static gboolean is_celsius(const struct rs9lcd_packet *rs_packet)
-{
- return ((rs_packet->digit4 & ~DP_MASK) == LCD_C);
-}
-
-static gboolean is_shortcirc(const struct rs9lcd_packet *rs_packet)
-{
- return ((rs_packet->digit2 & ~DP_MASK) == LCD_h);
-}
-
-static gboolean is_logic_high(const struct rs9lcd_packet *rs_packet)
-{
- sr_spew("Digit 2: 0x%02x.", rs_packet->digit2 & ~DP_MASK);
- return ((rs_packet->digit2 & ~DP_MASK) == LCD_H);
-}
-
-SR_PRIV int sr_rs9lcd_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info)
-{
- const struct rs9lcd_packet *rs_packet = (void *)buf;
- double rawval;
-
- (void)info;
-
- rawval = lcd_to_double(rs_packet, READ_ALL);
-
- switch (rs_packet->mode) {
- case MODE_DC_V:
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- analog->mqflags |= SR_MQFLAG_DC;
- break;
- case MODE_AC_V:
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- analog->mqflags |= SR_MQFLAG_AC;
- break;
- case MODE_DC_UA: /* Fall through */
- case MODE_DC_MA: /* Fall through */
- case MODE_DC_A:
- analog->mq = SR_MQ_CURRENT;
- analog->unit = SR_UNIT_AMPERE;
- analog->mqflags |= SR_MQFLAG_DC;
- break;
- case MODE_AC_UA: /* Fall through */
- case MODE_AC_MA: /* Fall through */
- case MODE_AC_A:
- analog->mq = SR_MQ_CURRENT;
- analog->unit = SR_UNIT_AMPERE;
- analog->mqflags |= SR_MQFLAG_AC;
- break;
- case MODE_OHM:
- analog->mq = SR_MQ_RESISTANCE;
- analog->unit = SR_UNIT_OHM;
- break;
- case MODE_FARAD:
- analog->mq = SR_MQ_CAPACITANCE;
- analog->unit = SR_UNIT_FARAD;
- break;
- case MODE_CONT:
- analog->mq = SR_MQ_CONTINUITY;
- analog->unit = SR_UNIT_BOOLEAN;
- rawval = is_shortcirc(rs_packet);
- break;
- case MODE_DIODE:
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- analog->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
- break;
- case MODE_HZ: /* Fall through */
- case MODE_VOLT_HZ: /* Fall through */
- case MODE_AMP_HZ:
- analog->mq = SR_MQ_FREQUENCY;
- analog->unit = SR_UNIT_HERTZ;
- break;
- case MODE_LOGIC:
- /*
- * No matter whether or not we have an actual voltage reading,
- * we are measuring voltage, so we set our MQ as VOLTAGE.
- */
- analog->mq = SR_MQ_VOLTAGE;
- if (!isnan(rawval)) {
- /* We have an actual voltage. */
- analog->unit = SR_UNIT_VOLT;
- } else {
- /* We have either HI or LOW. */
- analog->unit = SR_UNIT_BOOLEAN;
- rawval = is_logic_high(rs_packet);
- }
- break;
- case MODE_HFE:
- analog->mq = SR_MQ_GAIN;
- analog->unit = SR_UNIT_UNITLESS;
- break;
- case MODE_DUTY: /* Fall through */
- case MODE_VOLT_DUTY: /* Fall through */
- case MODE_AMP_DUTY:
- analog->mq = SR_MQ_DUTY_CYCLE;
- analog->unit = SR_UNIT_PERCENTAGE;
- break;
- case MODE_WIDTH: /* Fall through */
- case MODE_VOLT_WIDTH: /* Fall through */
- case MODE_AMP_WIDTH:
- analog->mq = SR_MQ_PULSE_WIDTH;
- analog->unit = SR_UNIT_SECOND;
- break;
- case MODE_TEMP:
- analog->mq = SR_MQ_TEMPERATURE;
- /* We need to reparse. */
- rawval = lcd_to_double(rs_packet, READ_TEMP);
- analog->unit = is_celsius(rs_packet) ?
- SR_UNIT_CELSIUS : SR_UNIT_FAHRENHEIT;
- break;
- case MODE_DBM:
- analog->mq = SR_MQ_POWER;
- analog->unit = SR_UNIT_DECIBEL_MW;
- analog->mqflags |= SR_MQFLAG_AC;
- break;
- default:
- sr_dbg("Unknown mode: %d.", rs_packet->mode);
- break;
- }
-
- if (rs_packet->info & INFO_HOLD)
- analog->mqflags |= SR_MQFLAG_HOLD;
- if (rs_packet->digit4 & DIG4_MAX)
- analog->mqflags |= SR_MQFLAG_MAX;
- if (rs_packet->indicatrix2 & IND2_MIN)
- analog->mqflags |= SR_MQFLAG_MIN;
- if (rs_packet->info & INFO_AUTO)
- analog->mqflags |= SR_MQFLAG_AUTORANGE;
-
- *floatval = rawval;
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
- *
- * 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/>.
- */
-
-/*
- * Helper functions for the Cypress EZ-USB / FX2 series chips.
- */
-
-#include <libusb.h>
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "ezusb"
-
-SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear)
-{
- int ret;
- unsigned char buf[1];
-
- sr_info("setting CPU reset mode %s...",
- set_clear ? "on" : "off");
- buf[0] = set_clear ? 1 : 0;
- ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR, 0xa0,
- 0xe600, 0x0000, buf, 1, 100);
- if (ret < 0)
- sr_err("Unable to send control request: %s.",
- libusb_error_name(ret));
-
- return ret;
-}
-
-SR_PRIV int ezusb_install_firmware(libusb_device_handle *hdl,
- const char *filename)
-{
- FILE *fw;
- int offset, chunksize, ret, result;
- unsigned char buf[4096];
-
- sr_info("Uploading firmware at %s", filename);
- if ((fw = g_fopen(filename, "rb")) == NULL) {
- sr_err("Unable to open firmware file %s for reading: %s",
- filename, strerror(errno));
- return SR_ERR;
- }
-
- result = SR_OK;
- offset = 0;
- while (1) {
- chunksize = fread(buf, 1, 4096, fw);
- if (chunksize == 0)
- break;
- ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR |
- LIBUSB_ENDPOINT_OUT, 0xa0, offset,
- 0x0000, buf, chunksize, 100);
- if (ret < 0) {
- sr_err("Unable to send firmware to device: %s.",
- libusb_error_name(ret));
- result = SR_ERR;
- break;
- }
- sr_info("Uploaded %d bytes", chunksize);
- offset += chunksize;
- }
- fclose(fw);
- sr_info("Firmware upload done");
-
- return result;
-}
-
-SR_PRIV int ezusb_upload_firmware(libusb_device *dev, int configuration,
- const char *filename)
-{
- struct libusb_device_handle *hdl;
- int ret;
-
- sr_info("uploading firmware to device on %d.%d",
- libusb_get_bus_number(dev), libusb_get_device_address(dev));
-
- if ((ret = libusb_open(dev, &hdl)) < 0) {
- sr_err("failed to open device: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
-/*
- * The libusbx darwin backend is broken: it can report a kernel driver being
- * active, but detaching it always returns an error.
- */
-#if !defined(__APPLE__)
- if (libusb_kernel_driver_active(hdl, 0) == 1) {
- if ((ret = libusb_detach_kernel_driver(hdl, 0)) < 0) {
- sr_err("failed to detach kernel driver: %s",
- libusb_error_name(ret));
- return SR_ERR;
- }
- }
-#endif
-
- if ((ret = libusb_set_configuration(hdl, configuration)) < 0) {
- sr_err("Unable to set configuration: %s",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- if ((ezusb_reset(hdl, 1)) < 0)
- return SR_ERR;
-
- if (ezusb_install_firmware(hdl, filename) < 0)
- return SR_ERR;
-
- if ((ezusb_reset(hdl, 0)) < 0)
- return SR_ERR;
-
- libusb_close(hdl);
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
- *
- * 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.h"
-#include "libsigrok-internal.h"
-
-#include <glib.h>
-#include <string.h>
-
-#define LOG_PREFIX "scpi"
-
-#define SCPI_READ_RETRIES 100
-#define SCPI_READ_RETRY_TIMEOUT 10000
-
-/**
- * Parse a string representation of a boolean-like value into a gboolean.
- * Similar to sr_parse_boolstring but rejects strings which do not represent
- * a boolean-like value.
- *
- * @param str String to convert.
- * @param ret Pointer to a gboolean where the result of the conversion will be
- * stored.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-static int parse_strict_bool(const char *str, gboolean *ret)
-{
- if (!str)
- return SR_ERR_ARG;
-
- if (!g_strcmp0(str, "1") ||
- !g_ascii_strncasecmp(str, "y", 1) ||
- !g_ascii_strncasecmp(str, "t", 1) ||
- !g_ascii_strncasecmp(str, "yes", 3) ||
- !g_ascii_strncasecmp(str, "true", 4) ||
- !g_ascii_strncasecmp(str, "on", 2)) {
- *ret = TRUE;
- return SR_OK;
- } else if (!g_strcmp0(str, "0") ||
- !g_ascii_strncasecmp(str, "n", 1) ||
- !g_ascii_strncasecmp(str, "f", 1) ||
- !g_ascii_strncasecmp(str, "no", 2) ||
- !g_ascii_strncasecmp(str, "false", 5) ||
- !g_ascii_strncasecmp(str, "off", 3)) {
- *ret = FALSE;
- return SR_OK;
- }
-
- return SR_ERR;
-}
-
-SR_PRIV extern const struct sr_scpi_dev_inst scpi_serial_dev;
-SR_PRIV extern const struct sr_scpi_dev_inst scpi_tcp_raw_dev;
-SR_PRIV extern const struct sr_scpi_dev_inst scpi_tcp_rigol_dev;
-SR_PRIV extern const struct sr_scpi_dev_inst scpi_usbtmc_libusb_dev;
-SR_PRIV extern const struct sr_scpi_dev_inst scpi_vxi_dev;
-SR_PRIV extern const struct sr_scpi_dev_inst scpi_visa_dev;
-
-static const struct sr_scpi_dev_inst *scpi_devs[] = {
- &scpi_tcp_raw_dev,
- &scpi_tcp_rigol_dev,
-#ifdef HAVE_LIBUSB_1_0
- &scpi_usbtmc_libusb_dev,
-#endif
-#if HAVE_RPC
- &scpi_vxi_dev,
-#endif
-#ifdef HAVE_LIBREVISA
- &scpi_visa_dev,
-#endif
-#ifdef HAVE_LIBSERIALPORT
- &scpi_serial_dev, /* must be last as it matches any resource */
-#endif
-};
-
-static struct sr_dev_inst *sr_scpi_scan_resource(struct drv_context *drvc,
- const char *resource, const char *serialcomm,
- struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi))
-{
- struct sr_scpi_dev_inst *scpi;
- struct sr_dev_inst *sdi;
-
- if (!(scpi = scpi_dev_inst_new(drvc, resource, serialcomm)))
- return NULL;
-
- if (sr_scpi_open(scpi) != SR_OK) {
- sr_info("Couldn't open SCPI device.");
- sr_scpi_free(scpi);
- return NULL;
- };
-
- if ((sdi = probe_device(scpi)))
- return sdi;
-
- sr_scpi_close(scpi);
- sr_scpi_free(scpi);
- return NULL;
-}
-
-SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
- struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi))
-{
- GSList *resources, *l, *devices;
- struct sr_dev_inst *sdi;
- const char *resource = NULL;
- const char *serialcomm = NULL;
- gchar **res;
- unsigned i;
-
- for (l = options; l; l = l->next) {
- struct sr_config *src = l->data;
- switch (src->key) {
- case SR_CONF_CONN:
- resource = g_variant_get_string(src->data, NULL);
- break;
- case SR_CONF_SERIALCOMM:
- serialcomm = g_variant_get_string(src->data, NULL);
- break;
- }
- }
-
- devices = NULL;
- for (i = 0; i < ARRAY_SIZE(scpi_devs); i++) {
- if ((resource && strcmp(resource, scpi_devs[i]->prefix))
- || !scpi_devs[i]->scan)
- continue;
- resources = scpi_devs[i]->scan(drvc);
- for (l = resources; l; l = l->next) {
- res = g_strsplit(l->data, ":", 2);
- if (res[0] && (sdi = sr_scpi_scan_resource(drvc, res[0],
- serialcomm ? serialcomm : res[1], probe_device)))
- devices = g_slist_append(devices, sdi);
- g_strfreev(res);
- }
- g_slist_free_full(resources, g_free);
- }
-
- if (!devices && resource) {
- sdi = sr_scpi_scan_resource(drvc, resource, serialcomm, probe_device);
- devices = g_slist_append(NULL, sdi);
- }
-
- /* Tack a copy of the newly found devices onto the driver list. */
- if (devices)
- drvc->instances = g_slist_concat(drvc->instances, g_slist_copy(devices));
-
- return devices;
-}
-
-SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc,
- const char *resource, const char *serialcomm)
-{
- struct sr_scpi_dev_inst *scpi = NULL;
- const struct sr_scpi_dev_inst *scpi_dev;
- gchar **params;
- unsigned i;
-
- for (i = 0; i < ARRAY_SIZE(scpi_devs); i++) {
- scpi_dev = scpi_devs[i];
- if (!strncmp(resource, scpi_dev->prefix, strlen(scpi_dev->prefix))) {
- sr_dbg("Opening %s device %s.", scpi_dev->name, resource);
- scpi = g_malloc(sizeof(*scpi));
- *scpi = *scpi_dev;
- scpi->priv = g_malloc0(scpi->priv_size);
- params = g_strsplit(resource, "/", 0);
- if (scpi->dev_inst_new(scpi->priv, drvc, resource,
- params, serialcomm) != SR_OK) {
- sr_scpi_free(scpi);
- scpi = NULL;
- }
- g_strfreev(params);
- break;
- }
- }
-
- return scpi;
-}
-
-/**
- * Open SCPI device.
- *
- * @param scpi Previously initialized SCPI device structure.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi)
-{
- return scpi->open(scpi->priv);
-}
-
-/**
- * Add an event source for an SCPI device.
- *
- * @param scpi Previously initialized SCPI device structure.
- * @param events Events to check for.
- * @param timeout Max time to wait before the callback is called, ignored if 0.
- * @param cb Callback function to add. Must not be NULL.
- * @param cb_data Data for the callback function. Can be NULL.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- * SR_ERR_MALLOC upon memory allocation errors.
- */
-SR_PRIV int sr_scpi_source_add(struct sr_session *session,
- struct sr_scpi_dev_inst *scpi, int events, int timeout,
- sr_receive_data_callback cb, void *cb_data)
-{
- return scpi->source_add(session, scpi->priv, events, timeout, cb, cb_data);
-}
-
-/**
- * Remove event source for an SCPI device.
- *
- * @param scpi Previously initialized SCPI device structure.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- * SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
- * internal errors.
- */
-SR_PRIV int sr_scpi_source_remove(struct sr_session *session,
- struct sr_scpi_dev_inst *scpi)
-{
- return scpi->source_remove(session, scpi->priv);
-}
-
-/**
- * Send a SCPI command.
- *
- * @param scpi Previously initialized SCPI device structure.
- * @param format Format string, to be followed by any necessary arguments.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_send(struct sr_scpi_dev_inst *scpi,
- const char *format, ...)
-{
- va_list args;
- int ret;
-
- va_start(args, format);
- ret = sr_scpi_send_variadic(scpi, format, args);
- va_end(args);
-
- return ret;
-}
-
-/**
- * Send a SCPI command with a variadic argument list.
- *
- * @param scpi Previously initialized SCPI device structure.
- * @param format Format string.
- * @param args Argument list.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_send_variadic(struct sr_scpi_dev_inst *scpi,
- const char *format, va_list args)
-{
- va_list args_copy;
- char *buf;
- int len, ret;
-
- /* Get length of buffer required. */
- va_copy(args_copy, args);
- len = vsnprintf(NULL, 0, format, args_copy);
- va_end(args_copy);
-
- /* Allocate buffer and write out command. */
- buf = g_malloc(len + 1);
- vsprintf(buf, format, args);
-
- /* Send command. */
- ret = scpi->send(scpi->priv, buf);
-
- /* Free command buffer. */
- g_free(buf);
-
- return ret;
-}
-
-/**
- * Begin receiving an SCPI reply.
- *
- * @param scpi Previously initialised SCPI device structure.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_read_begin(struct sr_scpi_dev_inst *scpi)
-{
- return scpi->read_begin(scpi->priv);
-}
-
-/**
- * Read part of a response from SCPI device.
- *
- * @param scpi Previously initialised SCPI device structure.
- * @param buf Buffer to store result.
- * @param maxlen Maximum number of bytes to read.
- *
- * @return Number of bytes read, or SR_ERR upon failure.
- */
-SR_PRIV int sr_scpi_read_data(struct sr_scpi_dev_inst *scpi,
- char *buf, int maxlen)
-{
- return scpi->read_data(scpi->priv, buf, maxlen);
-}
-
-/**
- * Check whether a complete SCPI response has been received.
- *
- * @param scpi Previously initialised SCPI device structure.
- *
- * @return 1 if complete, 0 otherwise.
- */
-SR_PRIV int sr_scpi_read_complete(struct sr_scpi_dev_inst *scpi)
-{
- return scpi->read_complete(scpi->priv);
-}
-
-/**
- * Close SCPI device.
- *
- * @param scpi Previously initialized SCPI device structure.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_close(struct sr_scpi_dev_inst *scpi)
-{
- return scpi->close(scpi->priv);
-}
-
-/**
- * Free SCPI device.
- *
- * @param scpi Previously initialized SCPI device structure.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV void sr_scpi_free(struct sr_scpi_dev_inst *scpi)
-{
- scpi->free(scpi->priv);
- g_free(scpi->priv);
- g_free(scpi);
-}
-
-/**
- * Send a SCPI command, receive the reply and store the reply in scpi_response.
- *
- * @param scpi Previously initialised SCPI device structure.
- * @param command The SCPI command to send to the device (can be NULL).
- * @param scpi_response Pointer where to store the SCPI response.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
- const char *command, char **scpi_response)
-{
- char buf[256];
- int len;
- GString *response;
-
- if (command)
- if (sr_scpi_send(scpi, command) != SR_OK)
- return SR_ERR;
-
- if (sr_scpi_read_begin(scpi) != SR_OK)
- return SR_ERR;
-
- response = g_string_new("");
-
- *scpi_response = NULL;
-
- while (!sr_scpi_read_complete(scpi)) {
- len = sr_scpi_read_data(scpi, buf, sizeof(buf));
- if (len < 0) {
- g_string_free(response, TRUE);
- return SR_ERR;
- }
- g_string_append_len(response, buf, len);
- }
-
- /* Get rid of trailing linefeed if present */
- if (response->len >= 1 && response->str[response->len - 1] == '\n')
- g_string_truncate(response, response->len - 1);
-
- *scpi_response = response->str;
- g_string_free(response, FALSE);
-
- sr_spew("Got response: '%.70s'.", *scpi_response);
-
- return SR_OK;
-}
-
-/**
- * Send a SCPI command, read the reply, parse it as a bool value and store the
- * result in scpi_response.
- *
- * @param scpi Previously initialised SCPI device structure.
- * @param command The SCPI command to send to the device (can be NULL).
- * @param scpi_response Pointer where to store the parsed result.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi,
- const char *command, gboolean *scpi_response)
-{
- int ret;
- char *response;
-
- response = NULL;
-
- if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
- if (!response)
- return SR_ERR;
-
- if (parse_strict_bool(response, scpi_response) == SR_OK)
- ret = SR_OK;
- else
- ret = SR_ERR;
-
- g_free(response);
-
- return ret;
-}
-
-/**
- * Send a SCPI command, read the reply, parse it as an integer and store the
- * result in scpi_response.
- *
- * @param scpi Previously initialised SCPI device structure.
- * @param command The SCPI command to send to the device (can be NULL).
- * @param scpi_response Pointer where to store the parsed result.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_get_int(struct sr_scpi_dev_inst *scpi,
- const char *command, int *scpi_response)
-{
- int ret;
- char *response;
-
- response = NULL;
-
- if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
- if (!response)
- return SR_ERR;
-
- if (sr_atoi(response, scpi_response) == SR_OK)
- ret = SR_OK;
- else
- ret = SR_ERR;
-
- g_free(response);
-
- return ret;
-}
-
-/**
- * Send a SCPI command, read the reply, parse it as a float and store the
- * result in scpi_response.
- *
- * @param scpi Previously initialised SCPI device structure.
- * @param command The SCPI command to send to the device (can be NULL).
- * @param scpi_response Pointer where to store the parsed result.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_get_float(struct sr_scpi_dev_inst *scpi,
- const char *command, float *scpi_response)
-{
- int ret;
- char *response;
-
- response = NULL;
-
- if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
- if (!response)
- return SR_ERR;
-
- if (sr_atof_ascii(response, scpi_response) == SR_OK)
- ret = SR_OK;
- else
- ret = SR_ERR;
-
- g_free(response);
-
- return ret;
-}
-
-/**
- * Send a SCPI command, read the reply, parse it as a double and store the
- * result in scpi_response.
- *
- * @param scpi Previously initialised SCPI device structure.
- * @param command The SCPI command to send to the device (can be NULL).
- * @param scpi_response Pointer where to store the parsed result.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi,
- const char *command, double *scpi_response)
-{
- int ret;
- char *response;
-
- response = NULL;
-
- if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
- if (!response)
- return SR_ERR;
-
- if (sr_atod(response, scpi_response) == SR_OK)
- ret = SR_OK;
- else
- ret = SR_ERR;
-
- g_free(response);
-
- return ret;
-}
-
-/**
- * Send a SCPI *OPC? command, read the reply and return the result of the
- * command.
- *
- * @param scpi Previously initialised SCPI device structure.
- *
- * @return SR_OK on success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_get_opc(struct sr_scpi_dev_inst *scpi)
-{
- unsigned int i;
- gboolean opc;
-
- for (i = 0; i < SCPI_READ_RETRIES; ++i) {
- sr_scpi_get_bool(scpi, SCPI_CMD_OPC, &opc);
- if (opc)
- return SR_OK;
- g_usleep(SCPI_READ_RETRY_TIMEOUT);
- }
-
- return SR_ERR;
-}
-
-/**
- * Send a SCPI command, read the reply, parse it as comma separated list of
- * floats and store the as an result in scpi_response.
- *
- * @param scpi Previously initialised SCPI device structure.
- * @param command The SCPI command to send to the device (can be NULL).
- * @param scpi_response Pointer where to store the parsed result.
- *
- * @return SR_OK upon successfully parsing all values, SR_ERR upon a parsing
- * error or upon no response. The allocated response must be freed by
- * the caller in the case of an SR_OK as well as in the case of
- * parsing error.
- */
-SR_PRIV int sr_scpi_get_floatv(struct sr_scpi_dev_inst *scpi,
- const char *command, GArray **scpi_response)
-{
- int ret;
- float tmp;
- char *response;
- gchar **ptr, **tokens;
- GArray *response_array;
-
- ret = SR_OK;
- response = NULL;
- tokens = NULL;
-
- if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
- if (!response)
- return SR_ERR;
-
- tokens = g_strsplit(response, ",", 0);
- ptr = tokens;
-
- response_array = g_array_sized_new(TRUE, FALSE, sizeof(float), 256);
-
- while (*ptr) {
- if (sr_atof_ascii(*ptr, &tmp) == SR_OK)
- response_array = g_array_append_val(response_array,
- tmp);
- else
- ret = SR_ERR;
-
- ptr++;
- }
- g_strfreev(tokens);
- g_free(response);
-
- if (ret == SR_ERR && response_array->len == 0) {
- g_array_free(response_array, TRUE);
- *scpi_response = NULL;
- return SR_ERR;
- }
-
- *scpi_response = response_array;
-
- return ret;
-}
-
-/**
- * Send a SCPI command, read the reply, parse it as comma separated list of
- * unsigned 8 bit integers and store the as an result in scpi_response.
- *
- * @param scpi Previously initialised SCPI device structure.
- * @param command The SCPI command to send to the device (can be NULL).
- * @param scpi_response Pointer where to store the parsed result.
- *
- * @return SR_OK upon successfully parsing all values, SR_ERR upon a parsing
- * error or upon no response. The allocated response must be freed by
- * the caller in the case of an SR_OK as well as in the case of
- * parsing error.
- */
-SR_PRIV int sr_scpi_get_uint8v(struct sr_scpi_dev_inst *scpi,
- const char *command, GArray **scpi_response)
-{
- int tmp, ret;
- char *response;
- gchar **ptr, **tokens;
- GArray *response_array;
-
- ret = SR_OK;
- response = NULL;
- tokens = NULL;
-
- if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
- if (!response)
- return SR_ERR;
-
- tokens = g_strsplit(response, ",", 0);
- ptr = tokens;
-
- response_array = g_array_sized_new(TRUE, FALSE, sizeof(uint8_t), 256);
-
- while (*ptr) {
- if (sr_atoi(*ptr, &tmp) == SR_OK)
- response_array = g_array_append_val(response_array,
- tmp);
- else
- ret = SR_ERR;
-
- ptr++;
- }
- g_strfreev(tokens);
- g_free(response);
-
- if (response_array->len == 0) {
- g_array_free(response_array, TRUE);
- *scpi_response = NULL;
- return SR_ERR;
- }
-
- *scpi_response = response_array;
-
- return ret;
-}
-
-/**
- * Send the *IDN? SCPI command, receive the reply, parse it and store the
- * reply as a sr_scpi_hw_info structure in the supplied scpi_response pointer.
- *
- * The hw_info structure must be freed by the caller via sr_scpi_hw_info_free().
- *
- * @param scpi Previously initialised SCPI device structure.
- * @param scpi_response Pointer where to store the hw_info structure.
- *
- * @return SR_OK upon success, SR_ERR on failure.
- */
-SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
- struct sr_scpi_hw_info **scpi_response)
-{
- int num_tokens;
- char *response;
- gchar **tokens;
- struct sr_scpi_hw_info *hw_info;
-
- response = NULL;
- tokens = NULL;
-
- if (sr_scpi_get_string(scpi, SCPI_CMD_IDN, &response) != SR_OK)
- if (!response)
- return SR_ERR;
-
- sr_info("Got IDN string: '%s'", response);
-
- /*
- * The response to a '*IDN?' is specified by the SCPI spec. It contains
- * a comma-separated list containing the manufacturer name, instrument
- * model, serial number of the instrument and the firmware version.
- */
- tokens = g_strsplit(response, ",", 0);
-
- for (num_tokens = 0; tokens[num_tokens] != NULL; num_tokens++);
-
- if (num_tokens != 4) {
- sr_dbg("IDN response not according to spec: %80.s.", response);
- g_strfreev(tokens);
- g_free(response);
- return SR_ERR;
- }
- g_free(response);
-
- hw_info = g_try_malloc(sizeof(struct sr_scpi_hw_info));
- if (!hw_info) {
- g_strfreev(tokens);
- return SR_ERR_MALLOC;
- }
-
- hw_info->manufacturer = g_strdup(tokens[0]);
- hw_info->model = g_strdup(tokens[1]);
- hw_info->serial_number = g_strdup(tokens[2]);
- hw_info->firmware_version = g_strdup(tokens[3]);
-
- g_strfreev(tokens);
-
- *scpi_response = hw_info;
-
- return SR_OK;
-}
-
-/**
- * Free a sr_scpi_hw_info struct.
- *
- * @param hw_info Pointer to the struct to free.
- *
- * This function is safe to call with a NULL pointer.
- */
-SR_PRIV void sr_scpi_hw_info_free(struct sr_scpi_hw_info *hw_info)
-{
- if (hw_info) {
- g_free(hw_info->manufacturer);
- g_free(hw_info->model);
- g_free(hw_info->serial_number);
- g_free(hw_info->firmware_version);
- g_free(hw_info);
- }
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
- * Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
- *
- * 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.h"
-#include "libsigrok-internal.h"
-
-#include <glib.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define LOG_PREFIX "scpi_serial"
-
-#define BUFFER_SIZE 1024
-
-struct scpi_serial {
- struct sr_serial_dev_inst *serial;
- char buffer[BUFFER_SIZE];
- size_t count;
- size_t read;
-};
-
-static struct {
- uint16_t vendor_id;
- uint16_t product_id;
- const char *serialcomm;
-} scpi_serial_usb_ids[] = {
- { 0x0403, 0xed72, "115200/8n1/flow=1" }, /* Hameg HO720 */
- { 0x0403, 0xed73, "115200/8n1/flow=1" }, /* Hameg HO730 */
-};
-
-static GSList *scpi_serial_scan(struct drv_context *drvc)
-{
- GSList *l, *r, *resources = NULL;
- gchar *res;
- unsigned i;
-
- (void)drvc;
-
- for (i = 0; i < ARRAY_SIZE(scpi_serial_usb_ids); i++) {
- if ((l = sr_serial_find_usb(scpi_serial_usb_ids[i].vendor_id,
- scpi_serial_usb_ids[i].product_id)) == NULL)
- continue;
- for (r = l; r; r = r->next) {
- if (scpi_serial_usb_ids[i].serialcomm)
- res = g_strdup_printf("%s:%s", (char *) r->data,
- scpi_serial_usb_ids[i].serialcomm);
- else
- res = g_strdup(r->data);
- resources = g_slist_append(resources, res);
- }
- g_slist_free_full(l, g_free);
- }
-
- return resources;
-}
-
-static int scpi_serial_dev_inst_new(void *priv, struct drv_context *drvc,
- const char *resource, char **params, const char *serialcomm)
-{
- struct scpi_serial *sscpi = priv;
-
- (void)drvc;
- (void)params;
-
- if (!(sscpi->serial = sr_serial_dev_inst_new(resource, serialcomm)))
- return SR_ERR;
-
- return SR_OK;
-}
-
-static int scpi_serial_open(void *priv)
-{
- struct scpi_serial *sscpi = priv;
- struct sr_serial_dev_inst *serial = sscpi->serial;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return SR_ERR;
-
- if (serial_flush(serial) != SR_OK)
- return SR_ERR;
-
- sscpi->count = 0;
- sscpi->read = 0;
-
- return SR_OK;
-}
-
-static int scpi_serial_source_add(struct sr_session *session, void *priv,
- int events, int timeout, sr_receive_data_callback cb, void *cb_data)
-{
- struct scpi_serial *sscpi = priv;
- struct sr_serial_dev_inst *serial = sscpi->serial;
-
- return serial_source_add(session, serial, events, timeout, cb, cb_data);
-}
-
-static int scpi_serial_source_remove(struct sr_session *session, void *priv)
-{
- struct scpi_serial *sscpi = priv;
- struct sr_serial_dev_inst *serial = sscpi->serial;
-
- return serial_source_remove(session, serial);
-}
-
-static int scpi_serial_send(void *priv, const char *command)
-{
- int len, result, written;
- gchar *terminated_command;
- struct scpi_serial *sscpi = priv;
- struct sr_serial_dev_inst *serial = sscpi->serial;
-
- terminated_command = g_strconcat(command, "\n", NULL);
- len = strlen(terminated_command);
- written = 0;
- while (written < len) {
- result = serial_write(serial, terminated_command + written, len - written);
- if (result < 0) {
- sr_err("Error while sending SCPI command: '%s'.", command);
- g_free(terminated_command);
- return SR_ERR;
- }
- written += result;
- }
-
- g_free(terminated_command);
-
- sr_spew("Successfully sent SCPI command: '%s'.", command);
-
- return SR_OK;
-}
-
-static int scpi_serial_read_begin(void *priv)
-{
- (void) priv;
-
- return SR_OK;
-}
-
-static int scpi_serial_read_data(void *priv, char *buf, int maxlen)
-{
- struct scpi_serial *sscpi = priv;
- int len, ret;
-
- len = BUFFER_SIZE - sscpi->count;
-
- /* Try to read new data into the buffer if there is space. */
- if (len > 0) {
- ret = serial_read(sscpi->serial, sscpi->buffer + sscpi->read,
- BUFFER_SIZE - sscpi->count);
-
- if (ret < 0)
- return ret;
-
- sscpi->count += ret;
-
- if (ret > 0)
- sr_spew("Read %d bytes into buffer.", ret);
- }
-
- /* Return as many bytes as possible from buffer, excluding any trailing newline. */
- if (sscpi->read < sscpi->count) {
- len = sscpi->count - sscpi->read;
- if (len > maxlen)
- len = maxlen;
- if (sscpi->buffer[sscpi->read + len - 1] == '\n')
- len--;
- sr_spew("Returning %d bytes from buffer.", len);
- memcpy(buf, sscpi->buffer + sscpi->read, len);
- sscpi->read += len;
- if (sscpi->read == BUFFER_SIZE) {
- sr_spew("Resetting buffer.");
- sscpi->count = 0;
- sscpi->read = 0;
- }
- return len;
- }
-
- return 0;
-}
-
-static int scpi_serial_read_complete(void *priv)
-{
- struct scpi_serial *sscpi = priv;
-
- /* If the next character is a newline, discard it and report complete. */
- if (sscpi->read < sscpi->count && sscpi->buffer[sscpi->read] == '\n') {
- sscpi->read++;
- return 1;
- } else {
- return 0;
- }
-}
-
-static int scpi_serial_close(void *priv)
-{
- struct scpi_serial *sscpi = priv;
-
- return serial_close(sscpi->serial);
-}
-
-static void scpi_serial_free(void *priv)
-{
- struct scpi_serial *sscpi = priv;
-
- sr_serial_dev_inst_free(sscpi->serial);
-}
-
-SR_PRIV const struct sr_scpi_dev_inst scpi_serial_dev = {
- .name = "serial",
- .prefix = "",
- .priv_size = sizeof(struct scpi_serial),
- .scan = scpi_serial_scan,
- .dev_inst_new = scpi_serial_dev_inst_new,
- .open = scpi_serial_open,
- .source_add = scpi_serial_source_add,
- .source_remove = scpi_serial_source_remove,
- .send = scpi_serial_send,
- .read_begin = scpi_serial_read_begin,
- .read_data = scpi_serial_read_data,
- .read_complete = scpi_serial_read_complete,
- .close = scpi_serial_close,
- .free = scpi_serial_free,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
- *
- * 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/>.
- */
-
-#ifdef _WIN32
-#define _WIN32_WINNT 0x0501
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#endif
-
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#include <glib.h>
-#include <string.h>
-#include <unistd.h>
-#ifndef _WIN32
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#endif
-#include <errno.h>
-
-#define LOG_PREFIX "scpi_tcp"
-
-#define LENGTH_BYTES 4
-
-struct scpi_tcp {
- char *address;
- char *port;
- int socket;
- char length_buf[LENGTH_BYTES];
- int length_bytes_read;
- int response_length;
- int response_bytes_read;
-};
-
-static int scpi_tcp_dev_inst_new(void *priv, struct drv_context *drvc,
- const char *resource, char **params, const char *serialcomm)
-{
- struct scpi_tcp *tcp = priv;
-
- (void)drvc;
- (void)resource;
- (void)serialcomm;
-
- if (!params || !params[1] || !params[2]) {
- sr_err("Invalid parameters.");
- return SR_ERR;
- }
-
- tcp->address = g_strdup(params[1]);
- tcp->port = g_strdup(params[2]);
- tcp->socket = -1;
-
- return SR_OK;
-}
-
-static int scpi_tcp_open(void *priv)
-{
- struct scpi_tcp *tcp = priv;
- struct addrinfo hints;
- struct addrinfo *results, *res;
- int err;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = IPPROTO_TCP;
-
- err = getaddrinfo(tcp->address, tcp->port, &hints, &results);
-
- if (err) {
- sr_err("Address lookup failed: %s:%d: %s", tcp->address, tcp->port,
- gai_strerror(err));
- return SR_ERR;
- }
-
- for (res = results; res; res = res->ai_next) {
- if ((tcp->socket = socket(res->ai_family, res->ai_socktype,
- res->ai_protocol)) < 0)
- continue;
- if (connect(tcp->socket, res->ai_addr, res->ai_addrlen) != 0) {
- close(tcp->socket);
- tcp->socket = -1;
- continue;
- }
- break;
- }
-
- freeaddrinfo(results);
-
- if (tcp->socket < 0) {
- sr_err("Failed to connect to %s:%s: %s", tcp->address, tcp->port,
- strerror(errno));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int scpi_tcp_source_add(struct sr_session *session, void *priv,
- int events, int timeout, sr_receive_data_callback cb, void *cb_data)
-{
- struct scpi_tcp *tcp = priv;
-
- return sr_session_source_add(session, tcp->socket, events, timeout,
- cb, cb_data);
-}
-
-static int scpi_tcp_source_remove(struct sr_session *session, void *priv)
-{
- struct scpi_tcp *tcp = priv;
-
- return sr_session_source_remove(session, tcp->socket);
-}
-
-static int scpi_tcp_send(void *priv, const char *command)
-{
- struct scpi_tcp *tcp = priv;
- int len, out;
- char *terminated_command;
-
- terminated_command = g_strdup_printf("%s\r\n", command);
- len = strlen(terminated_command);
- out = send(tcp->socket, terminated_command, len, 0);
- g_free(terminated_command);
-
- if (out < 0) {
- sr_err("Send error: %s", strerror(errno));
- return SR_ERR;
- }
-
- if (out < len) {
- sr_dbg("Only sent %d/%d bytes of SCPI command: '%s'.", out,
- len, command);
- }
-
- sr_spew("Successfully sent SCPI command: '%s'.", command);
-
- return SR_OK;
-}
-
-static int scpi_tcp_read_begin(void *priv)
-{
- struct scpi_tcp *tcp = priv;
-
- tcp->response_bytes_read = 0;
- tcp->length_bytes_read = 0;
-
- return SR_OK;
-}
-
-static int scpi_tcp_raw_read_data(void *priv, char *buf, int maxlen)
-{
- struct scpi_tcp *tcp = priv;
- int len;
-
- len = recv(tcp->socket, buf, maxlen, 0);
-
- if (len < 0) {
- sr_err("Receive error: %s", strerror(errno));
- return SR_ERR;
- }
-
- tcp->length_bytes_read = LENGTH_BYTES;
- tcp->response_length = len < maxlen ? len : maxlen + 1;
- tcp->response_bytes_read = len;
-
- return len;
-}
-
-static int scpi_tcp_rigol_read_data(void *priv, char *buf, int maxlen)
-{
- struct scpi_tcp *tcp = priv;
- int len;
-
- if (tcp->length_bytes_read < LENGTH_BYTES) {
- len = recv(tcp->socket, tcp->length_buf + tcp->length_bytes_read,
- LENGTH_BYTES - tcp->length_bytes_read, 0);
- if (len < 0) {
- sr_err("Receive error: %s", strerror(errno));
- return SR_ERR;
- }
-
- tcp->length_bytes_read += len;
-
- if (tcp->length_bytes_read < LENGTH_BYTES)
- return 0;
- else
- tcp->response_length = RL32(tcp->length_buf);
- }
-
- if (tcp->response_bytes_read >= tcp->response_length)
- return SR_ERR;
-
- len = recv(tcp->socket, buf, maxlen, 0);
-
- if (len < 0) {
- sr_err("Receive error: %s", strerror(errno));
- return SR_ERR;
- }
-
- tcp->response_bytes_read += len;
-
- return len;
-}
-
-static int scpi_tcp_read_complete(void *priv)
-{
- struct scpi_tcp *tcp = priv;
-
- return (tcp->length_bytes_read == LENGTH_BYTES &&
- tcp->response_bytes_read >= tcp->response_length);
-}
-
-static int scpi_tcp_close(void *priv)
-{
- struct scpi_tcp *tcp = priv;
-
- if (close(tcp->socket) < 0)
- return SR_ERR;
-
- return SR_OK;
-}
-
-static void scpi_tcp_free(void *priv)
-{
- struct scpi_tcp *tcp = priv;
-
- g_free(tcp->address);
- g_free(tcp->port);
-}
-
-SR_PRIV const struct sr_scpi_dev_inst scpi_tcp_raw_dev = {
- .name = "RAW TCP",
- .prefix = "tcp-raw",
- .priv_size = sizeof(struct scpi_tcp),
- .dev_inst_new = scpi_tcp_dev_inst_new,
- .open = scpi_tcp_open,
- .source_add = scpi_tcp_source_add,
- .source_remove = scpi_tcp_source_remove,
- .send = scpi_tcp_send,
- .read_begin = scpi_tcp_read_begin,
- .read_data = scpi_tcp_raw_read_data,
- .read_complete = scpi_tcp_read_complete,
- .close = scpi_tcp_close,
- .free = scpi_tcp_free,
-};
-
-SR_PRIV const struct sr_scpi_dev_inst scpi_tcp_rigol_dev = {
- .name = "RIGOL TCP",
- .prefix = "tcp-rigol",
- .priv_size = sizeof(struct scpi_tcp),
- .dev_inst_new = scpi_tcp_dev_inst_new,
- .open = scpi_tcp_open,
- .source_add = scpi_tcp_source_add,
- .source_remove = scpi_tcp_source_remove,
- .send = scpi_tcp_send,
- .read_begin = scpi_tcp_read_begin,
- .read_data = scpi_tcp_rigol_read_data,
- .read_complete = scpi_tcp_read_complete,
- .close = scpi_tcp_close,
- .free = scpi_tcp_free,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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 <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "scpi_usbtmc"
-
-#define MAX_TRANSFER_LENGTH 2048
-#define TRANSFER_TIMEOUT 1000
-
-struct scpi_usbtmc_libusb {
- struct sr_context *ctx;
- struct sr_usb_dev_inst *usb;
- int detached_kernel_driver;
- uint8_t interface;
- uint8_t bulk_in_ep;
- uint8_t bulk_out_ep;
- uint8_t interrupt_ep;
- uint8_t usbtmc_int_cap;
- uint8_t usbtmc_dev_cap;
- uint8_t usb488_dev_cap;
- uint8_t bTag;
- uint8_t bulkin_attributes;
- uint8_t buffer[MAX_TRANSFER_LENGTH];
- int response_length;
- int response_bytes_read;
- int remaining_length;
- int rigol_ds1000;
-};
-
-/* Some USBTMC-specific enums, as defined in the USBTMC standard. */
-#define SUBCLASS_USBTMC 0x03
-#define USBTMC_USB488 0x01
-
-enum {
- /* USBTMC control requests */
- INITIATE_ABORT_BULK_OUT = 1,
- CHECK_ABORT_BULK_OUT_STATUS = 2,
- INITIATE_ABORT_BULK_IN = 3,
- CHECK_ABORT_BULK_IN_STATUS = 4,
- INITIATE_CLEAR = 5,
- CHECK_CLEAR_STATUS = 6,
- GET_CAPABILITIES = 7,
- INDICATOR_PULSE = 64,
-
- /* USB488 control requests */
- READ_STATUS_BYTE = 128,
- REN_CONTROL = 160,
- GO_TO_LOCAL = 161,
- LOCAL_LOCKOUT = 162,
-};
-
-/* USBTMC capabilities */
-#define USBTMC_INT_CAP_LISTEN_ONLY 0x01
-#define USBTMC_INT_CAP_TALK_ONLY 0x02
-#define USBTMC_INT_CAP_INDICATOR 0x04
-
-#define USBTMC_DEV_CAP_TERMCHAR 0x01
-
-#define USB488_DEV_CAP_DT1 0x01
-#define USB488_DEV_CAP_RL1 0x02
-#define USB488_DEV_CAP_SR1 0x04
-#define USB488_DEV_CAP_SCPI 0x08
-
-/* Bulk messages constants */
-#define USBTMC_BULK_HEADER_SIZE 12
-
-/* Bulk MsgID values */
-#define DEV_DEP_MSG_OUT 1
-#define REQUEST_DEV_DEP_MSG_IN 2
-#define DEV_DEP_MSG_IN 2
-
-/* bmTransferAttributes */
-#define EOM 0x01
-#define TERM_CHAR_ENABLED 0x02
-
-
-static GSList *scpi_usbtmc_libusb_scan(struct drv_context *drvc)
-{
- struct libusb_device **devlist;
- struct libusb_device_descriptor des;
- struct libusb_config_descriptor *confdes;
- const struct libusb_interface_descriptor *intfdes;
- GSList *resources = NULL;
- int confidx, intfidx, ret, i;
- char *res;
-
- ret = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- if (ret < 0) {
- sr_err("Failed to get device list: %s.",
- libusb_error_name(ret));
- return NULL;
- }
- for (i = 0; devlist[i]; i++) {
- if ((ret = libusb_get_device_descriptor(devlist[i], &des)) < 0) {
- sr_err("Failed to get device descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
-
- for (confidx = 0; confidx < des.bNumConfigurations; confidx++) {
- if ((ret = libusb_get_config_descriptor(devlist[i], confidx, &confdes)) < 0) {
- sr_dbg("Failed to get configuration descriptor: %s, "
- "ignoring device.", libusb_error_name(ret));
- break;
- }
- for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) {
- intfdes = confdes->interface[intfidx].altsetting;
- if (intfdes->bInterfaceClass != LIBUSB_CLASS_APPLICATION ||
- intfdes->bInterfaceSubClass != SUBCLASS_USBTMC ||
- intfdes->bInterfaceProtocol != USBTMC_USB488)
- continue;
- sr_dbg("Found USBTMC device (VID:PID = %04x:%04x, "
- "bus.address = %d.%d).", des.idVendor, des.idProduct,
- libusb_get_bus_number(devlist[i]),
- libusb_get_device_address(devlist[i]));
- res = g_strdup_printf("usbtmc/%d.%d",
- libusb_get_bus_number(devlist[i]),
- libusb_get_device_address(devlist[i]));
- resources = g_slist_append(resources, res);
- }
- libusb_free_config_descriptor(confdes);
- }
- }
- libusb_free_device_list(devlist, 1);
-
- sr_dbg("Found %d device(s).", g_slist_length(resources));
-
- return resources;
-}
-
-static int scpi_usbtmc_libusb_dev_inst_new(void *priv, struct drv_context *drvc,
- const char *resource, char **params, const char *serialcomm)
-{
- struct scpi_usbtmc_libusb *uscpi = priv;
- GSList *devices;
-
- (void)resource;
- (void)serialcomm;
-
- if (!params || !params[1]) {
- sr_err("Invalid parameters.");
- return SR_ERR;
- }
-
- uscpi->ctx = drvc->sr_ctx;
- devices = sr_usb_find(uscpi->ctx->libusb_ctx, params[1]);
- if (g_slist_length(devices) != 1) {
- sr_err("Failed to find USB device '%s'.", params[1]);
- g_slist_free_full(devices, (GDestroyNotify)sr_usb_dev_inst_free);
- return SR_ERR;
- }
- uscpi->usb = devices->data;
- g_slist_free(devices);
-
- return SR_OK;
-}
-
-static int scpi_usbtmc_libusb_open(void *priv)
-{
- struct scpi_usbtmc_libusb *uscpi = priv;
- struct sr_usb_dev_inst *usb = uscpi->usb;
- struct libusb_device *dev;
- struct libusb_device_descriptor des;
- struct libusb_config_descriptor *confdes;
- const struct libusb_interface_descriptor *intfdes;
- const struct libusb_endpoint_descriptor *ep;
- int confidx, intfidx, epidx, config = 0;
- uint8_t capabilities[24];
- int ret, found = 0;
-
- if (usb->devhdl)
- return SR_OK;
-
- if (sr_usb_open(uscpi->ctx->libusb_ctx, usb) != SR_OK)
- return SR_ERR;
-
- dev = libusb_get_device(usb->devhdl);
- if ((ret = libusb_get_device_descriptor(dev, &des)) < 0) {
- sr_err("Failed to get device descriptor: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- for (confidx = 0; confidx < des.bNumConfigurations; confidx++) {
- if ((ret = libusb_get_config_descriptor(dev, confidx, &confdes)) < 0) {
- sr_dbg("Failed to get configuration descriptor: %s, "
- "ignoring device.", libusb_error_name(ret));
- continue;
- }
- for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) {
- intfdes = confdes->interface[intfidx].altsetting;
- if (intfdes->bInterfaceClass != LIBUSB_CLASS_APPLICATION ||
- intfdes->bInterfaceSubClass != SUBCLASS_USBTMC ||
- intfdes->bInterfaceProtocol != USBTMC_USB488)
- continue;
- uscpi->interface = intfdes->bInterfaceNumber;
- sr_dbg("Interface %d", uscpi->interface);
- config = confdes->bConfigurationValue;
- sr_dbg("Configuration %d", config);
- for (epidx = 0; epidx < intfdes->bNumEndpoints; epidx++) {
- ep = &intfdes->endpoint[epidx];
- if (ep->bmAttributes == LIBUSB_TRANSFER_TYPE_BULK &&
- !(ep->bEndpointAddress & (LIBUSB_ENDPOINT_DIR_MASK))) {
- uscpi->bulk_out_ep = ep->bEndpointAddress;
- sr_dbg("Bulk OUT EP %d", uscpi->bulk_out_ep);
- }
- if (ep->bmAttributes == LIBUSB_TRANSFER_TYPE_BULK &&
- ep->bEndpointAddress & (LIBUSB_ENDPOINT_DIR_MASK)) {
- uscpi->bulk_in_ep = ep->bEndpointAddress;
- sr_dbg("Bulk IN EP %d", uscpi->bulk_in_ep);
- }
- if (ep->bmAttributes == LIBUSB_TRANSFER_TYPE_INTERRUPT &&
- ep->bEndpointAddress & (LIBUSB_ENDPOINT_DIR_MASK)) {
- uscpi->interrupt_ep = ep->bEndpointAddress;
- sr_dbg("Interrupt EP %d", uscpi->interrupt_ep);
- }
- }
- found = 1;
- uscpi->rigol_ds1000 = des.idVendor == 0x1ab1 &&
- des.idProduct == 0x0588;
- }
- libusb_free_config_descriptor(confdes);
- if (found)
- break;
- }
-
- if (!found) {
- sr_err("Failed to find USBTMC interface.");
- return SR_ERR;
- }
-
- if (libusb_kernel_driver_active(usb->devhdl, uscpi->interface) == 1) {
- if ((ret = libusb_detach_kernel_driver(usb->devhdl,
- uscpi->interface)) < 0) {
- sr_err("Failed to detach kernel driver: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
- uscpi->detached_kernel_driver = 1;
- }
-
- if ((ret = libusb_set_configuration(usb->devhdl, config)) < 0) {
- sr_err("Failed to set configuration: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- if ((ret = libusb_claim_interface(usb->devhdl, uscpi->interface)) < 0) {
- sr_err("Failed to claim interface: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- if (!uscpi->rigol_ds1000) {
- if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_in_ep)) < 0) {
- sr_err("Failed to clear halt/stall condition for EP %d: %s.",
- uscpi->bulk_in_ep, libusb_error_name(ret));
- return SR_ERR;
- }
- if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_out_ep)) < 0) {
- sr_err("Failed to clear halt/stall condition for EP %d: %s.",
- uscpi->bulk_out_ep, libusb_error_name(ret));
- return SR_ERR;
- }
- if ((ret = libusb_clear_halt(usb->devhdl, uscpi->interrupt_ep)) < 0) {
- sr_err("Failed to clear halt/stall condition for EP %d: %s.",
- uscpi->interrupt_ep, libusb_error_name(ret));
- return SR_ERR;
- }
- }
-
- /* Get capabilities. */
- ret = libusb_control_transfer(usb->devhdl,
- LIBUSB_ENDPOINT_IN |
- LIBUSB_REQUEST_TYPE_CLASS |
- LIBUSB_RECIPIENT_INTERFACE,
- GET_CAPABILITIES, 0,
- uscpi->interface,
- capabilities, sizeof(capabilities),
- TRANSFER_TIMEOUT);
- if (ret == sizeof(capabilities)) {
- uscpi->usbtmc_int_cap = capabilities[ 4];
- uscpi->usbtmc_dev_cap = capabilities[ 5];
- uscpi->usb488_dev_cap = capabilities[15];
- }
- sr_dbg("Device capabilities: %s%s%s%s%s, %s, %s",
- uscpi->usb488_dev_cap & USB488_DEV_CAP_SCPI ? "SCPI, " : "",
- uscpi->usbtmc_dev_cap & USBTMC_DEV_CAP_TERMCHAR ? "TermChar, ": "",
- uscpi->usbtmc_int_cap & USBTMC_INT_CAP_LISTEN_ONLY? "L3, " :
- uscpi->usbtmc_int_cap & USBTMC_INT_CAP_TALK_ONLY ? "" : "L4, ",
- uscpi->usbtmc_int_cap & USBTMC_INT_CAP_TALK_ONLY ? "T5, " :
- uscpi->usbtmc_int_cap & USBTMC_INT_CAP_LISTEN_ONLY? "" : "T6, ",
- uscpi->usb488_dev_cap & USB488_DEV_CAP_SR1 ? "SR1" : "SR0",
- uscpi->usb488_dev_cap & USB488_DEV_CAP_RL1 ? "RL1" : "RL0",
- uscpi->usb488_dev_cap & USB488_DEV_CAP_DT1 ? "DT1" : "DT0");
-
- return SR_OK;
-}
-
-static int scpi_usbtmc_libusb_source_add(struct sr_session *session,
- void *priv, int events, int timeout, sr_receive_data_callback cb,
- void *cb_data)
-{
- struct scpi_usbtmc_libusb *uscpi = priv;
- (void)events;
- return usb_source_add(session, uscpi->ctx, timeout, cb, cb_data);
-}
-
-static int scpi_usbtmc_libusb_source_remove(struct sr_session *session,
- void *priv)
-{
- struct scpi_usbtmc_libusb *uscpi = priv;
- return usb_source_remove(session, uscpi->ctx);
-}
-
-static void usbtmc_bulk_out_header_write(void *header, uint8_t MsgID,
- uint8_t bTag,
- uint32_t TransferSize,
- uint8_t bmTransferAttributes,
- char TermChar)
-{
- W8(header+ 0, MsgID);
- W8(header+ 1, bTag);
- W8(header+ 2, ~bTag);
- W8(header+ 3, 0);
- WL32(header+ 4, TransferSize);
- W8(header+ 8, bmTransferAttributes);
- W8(header+ 9, TermChar);
- WL16(header+10, 0);
-}
-
-static int usbtmc_bulk_in_header_read(void *header, uint8_t MsgID,
- unsigned char bTag,
- int32_t *TransferSize,
- uint8_t *bmTransferAttributes)
-{
- if (R8(header+0) != MsgID ||
- R8(header+1) != bTag ||
- R8(header+2) != (unsigned char)~bTag)
- return SR_ERR;
- if (TransferSize)
- *TransferSize = RL32(header+4);
- if (bmTransferAttributes)
- *bmTransferAttributes = R8(header+8);
- return SR_OK;
-}
-
-static int scpi_usbtmc_bulkout(struct scpi_usbtmc_libusb *uscpi,
- uint8_t msg_id, const void *data, int32_t size,
- uint8_t transfer_attributes)
-{
- struct sr_usb_dev_inst *usb = uscpi->usb;
- int padded_size, ret, transferred;
-
- if (data && size+USBTMC_BULK_HEADER_SIZE+3 > (int)sizeof(uscpi->buffer)) {
- sr_err("USBTMC bulk out transfer is too big.");
- return SR_ERR;
- }
-
- uscpi->bTag++;
- uscpi->bTag += !uscpi->bTag; /* bTag == 0 is invalid so avoid it. */
-
- usbtmc_bulk_out_header_write(uscpi->buffer, msg_id, uscpi->bTag,
- size, transfer_attributes, 0);
- if (data)
- memcpy(uscpi->buffer+USBTMC_BULK_HEADER_SIZE, data, size);
- else
- size = 0;
- size += USBTMC_BULK_HEADER_SIZE;
- padded_size = (size + 3) & ~0x3;
- memset(uscpi->buffer+size, 0, padded_size - size);
-
- ret = libusb_bulk_transfer(usb->devhdl, uscpi->bulk_out_ep,
- uscpi->buffer, padded_size, &transferred,
- TRANSFER_TIMEOUT);
- if (ret < 0) {
- sr_err("USBTMC bulk out transfer error: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- if (transferred < padded_size) {
- sr_dbg("USBTMC bulk out partial transfer (%d/%d bytes).",
- transferred, padded_size);
- return SR_ERR;
- }
-
- return transferred - USBTMC_BULK_HEADER_SIZE;
-}
-
-static int scpi_usbtmc_bulkin_start(struct scpi_usbtmc_libusb *uscpi,
- uint8_t msg_id, void *data, int32_t size,
- uint8_t *transfer_attributes)
-{
- struct sr_usb_dev_inst *usb = uscpi->usb;
- int ret, transferred, message_size;
-
- ret = libusb_bulk_transfer(usb->devhdl, uscpi->bulk_in_ep, data, size,
- &transferred, TRANSFER_TIMEOUT);
- if (ret < 0) {
- sr_err("USBTMC bulk in transfer error: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- if (usbtmc_bulk_in_header_read(data, msg_id, uscpi->bTag, &message_size,
- transfer_attributes) != SR_OK) {
- sr_err("USBTMC invalid bulk in header.");
- return SR_ERR;
- }
-
- message_size += USBTMC_BULK_HEADER_SIZE;
- uscpi->response_length = MIN(transferred, message_size);
- uscpi->response_bytes_read = USBTMC_BULK_HEADER_SIZE;
- uscpi->remaining_length = message_size - uscpi->response_length;
-
- return transferred - USBTMC_BULK_HEADER_SIZE;
-}
-
-static int scpi_usbtmc_bulkin_continue(struct scpi_usbtmc_libusb *uscpi,
- void *data, int size)
-{
- struct sr_usb_dev_inst *usb = uscpi->usb;
- int ret, transferred;
-
- ret = libusb_bulk_transfer(usb->devhdl, uscpi->bulk_in_ep, data, size,
- &transferred, TRANSFER_TIMEOUT);
- if (ret < 0) {
- sr_err("USBTMC bulk in transfer error: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- uscpi->response_length = MIN(transferred, uscpi->remaining_length);
- uscpi->response_bytes_read = 0;
- uscpi->remaining_length -= uscpi->response_length;
-
- return transferred;
-}
-
-static int scpi_usbtmc_libusb_send(void *priv, const char *command)
-{
- struct scpi_usbtmc_libusb *uscpi = priv;
-
- if (scpi_usbtmc_bulkout(uscpi, DEV_DEP_MSG_OUT,
- command, strlen(command), EOM) <= 0)
- return SR_ERR;
-
- sr_spew("Successfully sent SCPI command: '%s'.", command);
-
- return SR_OK;
-}
-
-static int scpi_usbtmc_libusb_read_begin(void *priv)
-{
- struct scpi_usbtmc_libusb *uscpi = priv;
-
- uscpi->remaining_length = 0;
-
- if (scpi_usbtmc_bulkout(uscpi, REQUEST_DEV_DEP_MSG_IN,
- NULL, INT32_MAX, 0) < 0)
- return SR_ERR;
- if (scpi_usbtmc_bulkin_start(uscpi, DEV_DEP_MSG_IN,
- uscpi->buffer, sizeof(uscpi->buffer),
- &uscpi->bulkin_attributes) < 0)
- return SR_ERR;
-
- return SR_OK;
-}
-
-static int scpi_usbtmc_libusb_read_data(void *priv, char *buf, int maxlen)
-{
- struct scpi_usbtmc_libusb *uscpi = priv;
- int read_length;
-
- if (uscpi->response_bytes_read >= uscpi->response_length) {
- if (uscpi->remaining_length > 0) {
- if (scpi_usbtmc_bulkin_continue(uscpi, uscpi->buffer,
- sizeof(uscpi->buffer)) <= 0)
- return SR_ERR;
- } else {
- if (uscpi->bulkin_attributes & EOM)
- return SR_ERR;
- if (scpi_usbtmc_libusb_read_begin(uscpi) < 0)
- return SR_ERR;
- }
- }
-
- read_length = MIN(uscpi->response_length - uscpi->response_bytes_read, maxlen);
-
- memcpy(buf, uscpi->buffer + uscpi->response_bytes_read, read_length);
-
- uscpi->response_bytes_read += read_length;
-
- return read_length;
-}
-
-static int scpi_usbtmc_libusb_read_complete(void *priv)
-{
- struct scpi_usbtmc_libusb *uscpi = priv;
- return uscpi->response_bytes_read >= uscpi->response_length &&
- uscpi->remaining_length <= 0 &&
- uscpi->bulkin_attributes & EOM;
-}
-
-static int scpi_usbtmc_libusb_close(void *priv)
-{
- int ret;
- struct scpi_usbtmc_libusb *uscpi = priv;
- struct sr_usb_dev_inst *usb = uscpi->usb;
-
- if (!usb->devhdl)
- return SR_ERR;
-
- if (!uscpi->rigol_ds1000) {
- if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_in_ep)) < 0)
- sr_err("Failed to clear halt/stall condition for EP %d: %s.",
- uscpi->bulk_in_ep, libusb_error_name(ret));
- if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_out_ep)) < 0)
- sr_err("Failed to clear halt/stall condition for EP %d: %s.",
- uscpi->bulk_out_ep, libusb_error_name(ret));
- if ((ret = libusb_clear_halt(usb->devhdl, uscpi->interrupt_ep)) < 0)
- sr_err("Failed to clear halt/stall condition for EP %d: %s.",
- uscpi->interrupt_ep, libusb_error_name(ret));
- }
-
- if ((ret = libusb_release_interface(usb->devhdl, uscpi->interface)) < 0)
- sr_err("Failed to release interface: %s.",
- libusb_error_name(ret));
-
- if (uscpi->detached_kernel_driver) {
- if ((ret = libusb_attach_kernel_driver(usb->devhdl,
- uscpi->interface)) < 0)
- sr_err("Failed to re-attach kernel driver: %s.",
- libusb_error_name(ret));
-
- uscpi->detached_kernel_driver = 0;
- }
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
-
- return SR_OK;
-}
-
-static void scpi_usbtmc_libusb_free(void *priv)
-{
- struct scpi_usbtmc_libusb *uscpi = priv;
- sr_usb_dev_inst_free(uscpi->usb);
-}
-
-SR_PRIV const struct sr_scpi_dev_inst scpi_usbtmc_libusb_dev = {
- .name = "USBTMC",
- .prefix = "usbtmc",
- .priv_size = sizeof(struct scpi_usbtmc_libusb),
- .scan = scpi_usbtmc_libusb_scan,
- .dev_inst_new = scpi_usbtmc_libusb_dev_inst_new,
- .open = scpi_usbtmc_libusb_open,
- .source_add = scpi_usbtmc_libusb_source_add,
- .source_remove = scpi_usbtmc_libusb_source_remove,
- .send = scpi_usbtmc_libusb_send,
- .read_begin = scpi_usbtmc_libusb_read_begin,
- .read_data = scpi_usbtmc_libusb_read_data,
- .read_complete = scpi_usbtmc_libusb_read_complete,
- .close = scpi_usbtmc_libusb_close,
- .free = scpi_usbtmc_libusb_free,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
- *
- * 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.h"
-#include "libsigrok-internal.h"
-
-#include <visa.h>
-#include <string.h>
-
-#define LOG_PREFIX "scpi_visa"
-
-struct scpi_visa {
- char *resource;
- ViSession rmgr;
- ViSession vi;
-};
-
-static int scpi_visa_dev_inst_new(void *priv, struct drv_context *drvc,
- const char *resource, char **params, const char *serialcomm)
-{
- struct scpi_visa *vscpi = priv;
-
- (void)drvc;
- (void)resource;
- (void)serialcomm;
-
- if (!params || !params[1]) {
- sr_err("Invalid parameters.");
- return SR_ERR_BUG;
- }
-
- vscpi->resource = g_strdup(params[1]);
-
- return SR_OK;
-}
-
-static int scpi_visa_open(void *priv)
-{
- struct scpi_visa *vscpi = priv;
-
- if (viOpenDefaultRM(&vscpi->rmgr) != VI_SUCCESS) {
- sr_err("Cannot open default resource manager.");
- return SR_ERR;
- }
-
- if (viOpen(vscpi->rmgr, vscpi->resource, VI_NO_LOCK, 0, &vscpi->vi) != VI_SUCCESS) {
- sr_err("Cannot open resource.");
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int scpi_visa_source_add(struct sr_session *session, void *priv,
- int events, int timeout, sr_receive_data_callback cb, void *cb_data)
-{
- (void) priv;
-
- /* Hook up a dummy handler to receive data from the device. */
- return sr_session_source_add(session, -1, events, timeout, cb, cb_data);
-}
-
-static int scpi_visa_source_remove(struct sr_session *session, void *priv)
-{
- (void) priv;
-
- return sr_session_source_remove(session, -1);
-}
-
-static int scpi_visa_send(void *priv, const char *command)
-{
- struct scpi_visa *vscpi = priv;
- gchar *terminated_command;
- ViUInt32 written = 0;
- int len;
-
- terminated_command = g_strconcat(command, "\n", NULL);
- len = strlen(terminated_command);
- if (viWrite(vscpi->vi, (ViBuf) (terminated_command + written), len,
- &written) != VI_SUCCESS) {
- sr_err("Error while sending SCPI command: '%s'.", command);
- g_free(terminated_command);
- return SR_ERR;
- }
-
- g_free(terminated_command);
-
- sr_spew("Successfully sent SCPI command: '%s'.", command);
-
- return SR_OK;
-}
-
-static int scpi_visa_read_begin(void *priv)
-{
- (void) priv;
-
- return SR_OK;
-}
-
-static int scpi_visa_read_data(void *priv, char *buf, int maxlen)
-{
- struct scpi_visa *vscpi = priv;
- ViUInt32 count;
-
- if (viRead(vscpi->vi, (ViBuf) buf, maxlen, &count) != VI_SUCCESS) {
- sr_err("Read failed.");
- return SR_ERR;
- }
-
- return count;
-}
-
-static int scpi_visa_read_complete(void *priv)
-{
- struct scpi_visa *vscpi = priv;
- ViUInt16 status;
-
- if (viReadSTB(vscpi->vi, &status) != VI_SUCCESS) {
- sr_err("Failed to read status.");
- return SR_ERR;
- }
-
- return !(status & 16);
-}
-
-static int scpi_visa_close(void *priv)
-{
- struct scpi_visa *vscpi = priv;
-
- viClose(vscpi->vi);
- viClose(vscpi->rmgr);
-
- return SR_OK;
-}
-
-static void scpi_visa_free(void *priv)
-{
- struct scpi_visa *vscpi = priv;
-
- g_free(vscpi->resource);
- g_free(vscpi);
-}
-
-SR_PRIV const struct sr_scpi_dev_inst scpi_visa_dev = {
- .name = "VISA",
- .prefix = "visa",
- .priv_size = sizeof(struct scpi_visa),
- .dev_inst_new = scpi_visa_dev_inst_new,
- .open = scpi_visa_open,
- .source_add = scpi_visa_source_add,
- .source_remove = scpi_visa_source_remove,
- .send = scpi_visa_send,
- .read_begin = scpi_visa_read_begin,
- .read_data = scpi_visa_read_data,
- .read_complete = scpi_visa_read_complete,
- .close = scpi_visa_close,
- .free = scpi_visa_free,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
- *
- * Inspired by the VXI11 Ethernet Protocol for Linux:
- * http://optics.eee.nottingham.ac.uk/vxi11/
- *
- * 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 <rpc/rpc.h>
-#include <string.h>
-
-#include "vxi.h"
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "scpi_vxi"
-#define VXI_DEFAULT_TIMEOUT 2000 /* in ms */
-
-struct scpi_vxi {
- char *address;
- char *instrument;
- CLIENT *client;
- Device_Link link;
- unsigned int max_send_size;
- unsigned int read_complete;
-};
-
-static int scpi_vxi_dev_inst_new(void *priv, struct drv_context *drvc,
- const char *resource, char **params, const char *serialcomm)
-{
- struct scpi_vxi *vxi = priv;
-
- (void)drvc;
- (void)resource;
- (void)serialcomm;
-
- if (!params || !params[1]) {
- sr_err("Invalid parameters.");
- return SR_ERR;
- }
-
- vxi->address = g_strdup(params[1]);
- vxi->instrument = g_strdup(params[2] ? params[2] : "inst0");
-
- return SR_OK;
-}
-
-static int scpi_vxi_open(void *priv)
-{
- struct scpi_vxi *vxi = priv;
- Create_LinkParms link_parms;
- Create_LinkResp *link_resp;
-
- vxi->client = clnt_create(vxi->address, DEVICE_CORE, DEVICE_CORE_VERSION, "tcp");
- if (vxi->client == NULL) {
- sr_err("Client creation failed for %s", vxi->address);
- return SR_ERR;
- }
-
- /* Set link parameters */
- link_parms.clientId = (long) vxi->client;
- link_parms.lockDevice = 0;
- link_parms.lock_timeout = VXI_DEFAULT_TIMEOUT;
- link_parms.device = "inst0";
-
- if (!(link_resp = create_link_1(&link_parms, vxi->client))) {
- sr_err("Link creation failed for %s", vxi->address);
- return SR_ERR;
- }
- vxi->link = link_resp->lid;
- vxi->max_send_size = link_resp->maxRecvSize;
-
- /* Set a default maxRecvSize for devices which do not specify it */
- if (vxi->max_send_size <= 0)
- vxi->max_send_size = 4096;
-
- return SR_OK;
-}
-
-static int scpi_vxi_source_add(struct sr_session *session, void *priv,
- int events, int timeout, sr_receive_data_callback cb, void *cb_data)
-{
- (void)priv;
-
- /* Hook up a dummy handler to receive data from the device. */
- return sr_session_source_add(session, -1, events, timeout, cb, cb_data);
-}
-
-static int scpi_vxi_source_remove(struct sr_session *session, void *priv)
-{
- (void)priv;
-
- return sr_session_source_remove(session, -1);
-}
-
-/* Operation Flags */
-#define DF_WAITLOCK 0x01 /* wait if the operation is locked by another link */
-#define DF_END 0x08 /* an END indicator is sent with last byte of buffer */
-#define DF_TERM 0x80 /* a termination char is set during a read */
-
-static int scpi_vxi_send(void *priv, const char *command)
-{
- struct scpi_vxi *vxi = priv;
- Device_WriteResp *write_resp;
- Device_WriteParms write_parms;
- char *terminated_command;
- unsigned int len;
-
- terminated_command = g_strdup_printf("%s\r\n", command);
- len = strlen(terminated_command);
-
- write_parms.lid = vxi->link;
- write_parms.io_timeout = VXI_DEFAULT_TIMEOUT;
- write_parms.lock_timeout = VXI_DEFAULT_TIMEOUT;
- write_parms.flags = DF_END;
- write_parms.data.data_len = MIN(len, vxi->max_send_size);
- write_parms.data.data_val = terminated_command;
-
- if (!(write_resp = device_write_1(&write_parms, vxi->client))
- || write_resp->error) {
- sr_err("Device write failed for %s with error %d",
- vxi->address, write_resp->error);
- return SR_ERR;
- }
-
- g_free(terminated_command);
-
- if (write_resp->size < len)
- sr_dbg("Only sent %d/%d bytes of SCPI command: '%s'.",
- write_resp->size, len, command);
- else
- sr_spew("Successfully sent SCPI command: '%s'.", command);
-
- return SR_OK;
-}
-
-static int scpi_vxi_read_begin(void *priv)
-{
- struct scpi_vxi *vxi = priv;
-
- vxi->read_complete = 0;
-
- return SR_OK;
-}
-
-/* Read Response Reason Flags */
-#define RRR_SIZE 0x01 /* requestSize bytes have been transferred */
-#define RRR_TERM 0x02 /* a termination char has been read */
-#define RRR_END 0x04 /* an END indicator has been read */
-
-static int scpi_vxi_read_data(void *priv, char *buf, int maxlen)
-{
- struct scpi_vxi *vxi = priv;
- Device_ReadParms read_parms;
- Device_ReadResp *read_resp;
-
- read_parms.lid = vxi->link;
- read_parms.io_timeout = VXI_DEFAULT_TIMEOUT;
- read_parms.lock_timeout = VXI_DEFAULT_TIMEOUT;
- read_parms.flags = 0;
- read_parms.termChar = 0;
- read_parms.requestSize = maxlen;
-
- if (!(read_resp = device_read_1(&read_parms, vxi->client))
- || read_resp->error) {
- sr_err("Device read failed for %s with error %d",
- vxi->address, read_resp->error);
- return SR_ERR;
- }
-
- memcpy(buf, read_resp->data.data_val, read_resp->data.data_len);
- vxi->read_complete = read_resp->reason & (RRR_SIZE | RRR_TERM | RRR_END);
- return read_resp->data.data_len; /* actual number of bytes received */
-}
-
-static int scpi_vxi_read_complete(void *priv)
-{
- struct scpi_vxi *vxi = priv;
-
- return vxi->read_complete;
-}
-
-static int scpi_vxi_close(void *priv)
-{
- struct scpi_vxi *vxi = priv;
- Device_Error *dev_error;
-
- if (!vxi->client)
- return SR_ERR;
-
- if (!(dev_error = destroy_link_1(&vxi->link, vxi->client))) {
- sr_err("Link destruction failed for %s", vxi->address);
- return SR_ERR;
- }
-
- clnt_destroy(vxi->client);
- vxi->client = NULL;
-
- return SR_OK;
-}
-
-static void scpi_vxi_free(void *priv)
-{
- struct scpi_vxi *vxi = priv;
-
- g_free(vxi->address);
- g_free(vxi->instrument);
-}
-
-SR_PRIV const struct sr_scpi_dev_inst scpi_vxi_dev = {
- .name = "VXI",
- .prefix = "vxi",
- .priv_size = sizeof(struct scpi_vxi),
- .dev_inst_new = scpi_vxi_dev_inst_new,
- .open = scpi_vxi_open,
- .source_add = scpi_vxi_source_add,
- .source_remove = scpi_vxi_source_remove,
- .send = scpi_vxi_send,
- .read_begin = scpi_vxi_read_begin,
- .read_data = scpi_vxi_read_data,
- .read_complete = scpi_vxi_read_complete,
- .close = scpi_vxi_close,
- .free = scpi_vxi_free,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2010-2012 Uwe Hermann <uwe@hermann-uwe.de>
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
- *
- * 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 <string.h>
-#include <stdlib.h>
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <libserialport.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "serial"
-
-/**
- * Open the specified serial port.
- *
- * @param serial Previously initialized serial port structure.
- * @param[in] flags Flags to use when opening the serial port. Possible flags
- * include SERIAL_RDWR, SERIAL_RDONLY, SERIAL_NONBLOCK.
- *
- * If the serial structure contains a serialcomm string, it will be
- * passed to serial_set_paramstr() after the port is opened.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Failure.
- */
-SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags)
-{
- int ret;
- char *error;
- int sp_flags = 0;
-
- if (!serial) {
- sr_dbg("Invalid serial port.");
- return SR_ERR;
- }
-
- sr_spew("Opening serial port '%s' (flags %d).", serial->port, flags);
-
- sp_get_port_by_name(serial->port, &serial->data);
-
- if (flags & SERIAL_RDWR)
- sp_flags = (SP_MODE_READ | SP_MODE_WRITE);
- else if (flags & SERIAL_RDONLY)
- sp_flags = SP_MODE_READ;
-
- serial->nonblocking = (flags & SERIAL_NONBLOCK) ? 1 : 0;
-
- ret = sp_open(serial->data, sp_flags);
-
- switch (ret) {
- case SP_ERR_ARG:
- sr_err("Attempt to open serial port with invalid parameters.");
- return SR_ERR_ARG;
- case SP_ERR_FAIL:
- error = sp_last_error_message();
- sr_err("Error opening port (%d): %s.",
- sp_last_error_code(), error);
- sp_free_error_message(error);
- return SR_ERR;
- }
-
- if (serial->serialcomm)
- return serial_set_paramstr(serial, serial->serialcomm);
- else
- return SR_OK;
-}
-
-/**
- * Close the specified serial port.
- *
- * @param serial Previously initialized serial port structure.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Failure.
- */
-SR_PRIV int serial_close(struct sr_serial_dev_inst *serial)
-{
- int ret;
- char *error;
-
- if (!serial) {
- sr_dbg("Invalid serial port.");
- return SR_ERR;
- }
-
- if (!serial->data) {
- sr_dbg("Cannot close unopened serial port %s.", serial->port);
- return SR_ERR;
- }
-
- sr_spew("Closing serial port %s.", serial->port);
-
- ret = sp_close(serial->data);
-
- switch (ret) {
- case SP_ERR_ARG:
- sr_err("Attempt to close an invalid serial port.");
- return SR_ERR_ARG;
- case SP_ERR_FAIL:
- error = sp_last_error_message();
- sr_err("Error closing port (%d): %s.",
- sp_last_error_code(), error);
- sp_free_error_message(error);
- return SR_ERR;
- }
-
- sp_free_port(serial->data);
- serial->data = NULL;
-
- return SR_OK;
-}
-
-/**
- * Flush serial port buffers.
- *
- * @param serial Previously initialized serial port structure.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Failure.
- */
-SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial)
-{
- int ret;
- char *error;
-
- if (!serial) {
- sr_dbg("Invalid serial port.");
- return SR_ERR;
- }
-
- if (!serial->data) {
- sr_dbg("Cannot flush unopened serial port %s.", serial->port);
- return SR_ERR;
- }
-
- sr_spew("Flushing serial port %s.", serial->port);
-
- ret = sp_flush(serial->data, SP_BUF_BOTH);
-
- switch (ret) {
- case SP_ERR_ARG:
- sr_err("Attempt to flush an invalid serial port.");
- return SR_ERR_ARG;
- case SP_ERR_FAIL:
- error = sp_last_error_message();
- sr_err("Error flushing port (%d): %s.",
- sp_last_error_code(), error);
- sp_free_error_message(error);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int _serial_write(struct sr_serial_dev_inst *serial,
- const void *buf, size_t count, int nonblocking)
-{
- ssize_t ret;
- char *error;
-
- if (!serial) {
- sr_dbg("Invalid serial port.");
- return SR_ERR;
- }
-
- if (!serial->data) {
- sr_dbg("Cannot use unopened serial port %s.", serial->port);
- return SR_ERR;
- }
-
- if (nonblocking)
- ret = sp_nonblocking_write(serial->data, buf, count);
- else
- ret = sp_blocking_write(serial->data, buf, count, 0);
-
- switch (ret) {
- case SP_ERR_ARG:
- sr_err("Attempted serial port write with invalid arguments.");
- return SR_ERR_ARG;
- case SP_ERR_FAIL:
- error = sp_last_error_message();
- sr_err("Write error (%d): %s.", sp_last_error_code(), error);
- sp_free_error_message(error);
- return SR_ERR;
- }
-
- sr_spew("Wrote %d/%d bytes.", ret, count);
-
- return ret;
-}
-
-/**
- * Write a number of bytes to the specified serial port.
- *
- * @param serial Previously initialized serial port structure.
- * @param[in] buf Buffer containing the bytes to write.
- * @param[in] count Number of bytes to write.
- *
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR Other error.
- * @retval other The number of bytes written.
- */
-SR_PRIV int serial_write(struct sr_serial_dev_inst *serial,
- const void *buf, size_t count)
-{
- return _serial_write(serial, buf, count, serial->nonblocking);
-}
-
-/**
- * Write a number of bytes to the specified serial port, blocking until finished.
- * @copydetails serial_write()
- */
-SR_PRIV int serial_write_blocking(struct sr_serial_dev_inst *serial,
- const void *buf, size_t count)
-{
- return _serial_write(serial, buf, count, 0);
-}
-
-/**
- * Write a number of bytes to the specified serial port, return immediately.
- * @copydetails serial_write()
-*/
-SR_PRIV int serial_write_nonblocking(struct sr_serial_dev_inst *serial,
- const void *buf, size_t count)
-{
- return _serial_write(serial, buf, count, 1);
-}
-
-static int _serial_read(struct sr_serial_dev_inst *serial, void *buf,
- size_t count, int nonblocking)
-{
- ssize_t ret;
- char *error;
-
- if (!serial) {
- sr_dbg("Invalid serial port.");
- return SR_ERR;
- }
-
- if (!serial->data) {
- sr_dbg("Cannot use unopened serial port %s.", serial->port);
- return SR_ERR;
- }
-
- if (nonblocking)
- ret = sp_nonblocking_read(serial->data, buf, count);
- else
- ret = sp_blocking_read(serial->data, buf, count, 0);
-
- switch (ret) {
- case SP_ERR_ARG:
- sr_err("Attempted serial port read with invalid arguments.");
- return SR_ERR_ARG;
- case SP_ERR_FAIL:
- error = sp_last_error_message();
- sr_err("Read error (%d): %s.", sp_last_error_code(), error);
- sp_free_error_message(error);
- return SR_ERR;
- }
-
- if (ret > 0)
- sr_spew("Read %d/%d bytes.", ret, count);
-
- return ret;
-}
-
-/**
- * Read a number of bytes from the specified serial port.
- *
- * @param serial Previously initialized serial port structure.
- * @param buf Buffer where to store the bytes that are read.
- * @param[in] count The number of bytes to read.
- *
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR Other error.
- * @retval other The number of bytes read.
- */
-SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf,
- size_t count)
-{
- return _serial_read(serial, buf, count, serial->nonblocking);
-}
-
-/**
- * Read a number of bytes from the specified serial port, block until finished.
- * @copydetails serial_read()
- */
-SR_PRIV int serial_read_blocking(struct sr_serial_dev_inst *serial, void *buf,
- size_t count)
-{
- return _serial_read(serial, buf, count, 0);
-}
-
-/**
- * Try to read up to @a count bytes from the specified serial port, return
- * immediately with what's available.
- * @copydetails serial_read()
- */
-SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf,
- size_t count)
-{
- return _serial_read(serial, buf, count, 1);
-}
-
-/**
- * Set serial parameters for the specified serial port.
- *
- * @param serial Previously initialized serial port structure.
- * @param[in] baudrate The baudrate to set.
- * @param[in] bits The number of data bits to use (5, 6, 7 or 8).
- * @param[in] parity The parity setting to use (0 = none, 1 = even, 2 = odd).
- * @param[in] stopbits The number of stop bits to use (1 or 2).
- * @param[in] flowcontrol The flow control settings to use (0 = none,
- * 1 = RTS/CTS, 2 = XON/XOFF).
- * @param[in] rts Status of RTS line (0 or 1; required by some interfaces).
- * @param[in] dtr Status of DTR line (0 or 1; required by some interfaces).
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Failure.
- */
-SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate,
- int bits, int parity, int stopbits,
- int flowcontrol, int rts, int dtr)
-{
- int ret;
- char *error;
- struct sp_port_config *config;
-
- if (!serial) {
- sr_dbg("Invalid serial port.");
- return SR_ERR;
- }
-
- if (!serial->data) {
- sr_dbg("Cannot configure unopened serial port %s.", serial->port);
- return SR_ERR;
- }
-
- sr_spew("Setting serial parameters on port %s.", serial->port);
-
- sp_new_config(&config);
- sp_set_config_baudrate(config, baudrate);
- sp_set_config_bits(config, bits);
- switch (parity) {
- case 0:
- sp_set_config_parity(config, SP_PARITY_NONE);
- break;
- case 1:
- sp_set_config_parity(config, SP_PARITY_EVEN);
- break;
- case 2:
- sp_set_config_parity(config, SP_PARITY_ODD);
- break;
- default:
- return SR_ERR_ARG;
- }
- sp_set_config_stopbits(config, stopbits);
- sp_set_config_rts(config, flowcontrol == 1 ? SP_RTS_FLOW_CONTROL : rts);
- sp_set_config_cts(config, flowcontrol == 1 ? SP_CTS_FLOW_CONTROL : SP_CTS_IGNORE);
- sp_set_config_dtr(config, dtr);
- sp_set_config_dsr(config, SP_DSR_IGNORE);
- sp_set_config_xon_xoff(config, flowcontrol == 2 ? SP_XONXOFF_INOUT : SP_XONXOFF_DISABLED);
-
- ret = sp_set_config(serial->data, config);
- sp_free_config(config);
-
- switch (ret) {
- case SP_ERR_ARG:
- sr_err("Invalid arguments for setting serial port parameters.");
- return SR_ERR_ARG;
- case SP_ERR_FAIL:
- error = sp_last_error_message();
- sr_err("Error setting serial port parameters (%d): %s.",
- sp_last_error_code(), error);
- sp_free_error_message(error);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-/**
- * Set serial parameters for the specified serial port from parameter string.
- *
- * @param serial Previously initialized serial port structure.
- * @param[in] paramstr A serial communication parameters string of the form
- * "<baudrate>/<bits><parity><stopbits>{/<option>}".\n
- * Examples: "9600/8n1", "600/7o2/dtr=1/rts=0" or "460800/8n1/flow=2".\n
- * \<baudrate\>=integer Baud rate.\n
- * \<bits\>=5|6|7|8 Number of data bits.\n
- * \<parity\>=n|e|o None, even, odd.\n
- * \<stopbits\>=1|2 One or two stop bits.\n
- * Options:\n
- * dtr=0|1 Set DTR off resp. on.\n
- * flow=0|1|2 Flow control. 0 for none, 1 for RTS/CTS, 2 for XON/XOFF.\n
- * rts=0|1 Set RTS off resp. on.\n
- * Please note that values and combinations of these parameters must be
- * supported by the concrete serial interface hardware and the drivers for it.
- * @retval SR_OK Success.
- * @retval SR_ERR Failure.
- */
-SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
- const char *paramstr)
-{
-#define SERIAL_COMM_SPEC "^(\\d+)/([5678])([neo])([12])(.*)$"
-
- GRegex *reg;
- GMatchInfo *match;
- int speed, databits, parity, stopbits, flow, rts, dtr, i;
- char *mstr, **opts, **kv;
-
- speed = databits = parity = stopbits = flow = 0;
- rts = dtr = -1;
- sr_spew("Parsing parameters from \"%s\".", paramstr);
- reg = g_regex_new(SERIAL_COMM_SPEC, 0, 0, NULL);
- if (g_regex_match(reg, paramstr, 0, &match)) {
- if ((mstr = g_match_info_fetch(match, 1)))
- speed = strtoul(mstr, NULL, 10);
- g_free(mstr);
- if ((mstr = g_match_info_fetch(match, 2)))
- databits = strtoul(mstr, NULL, 10);
- g_free(mstr);
- if ((mstr = g_match_info_fetch(match, 3))) {
- switch (mstr[0]) {
- case 'n':
- parity = SERIAL_PARITY_NONE;
- break;
- case 'e':
- parity = SERIAL_PARITY_EVEN;
- break;
- case 'o':
- parity = SERIAL_PARITY_ODD;
- break;
- }
- }
- g_free(mstr);
- if ((mstr = g_match_info_fetch(match, 4)))
- stopbits = strtoul(mstr, NULL, 10);
- g_free(mstr);
- if ((mstr = g_match_info_fetch(match, 5)) && mstr[0] != '\0') {
- if (mstr[0] != '/') {
- sr_dbg("missing separator before extra options");
- speed = 0;
- } else {
- /* A set of "key=value" options separated by / */
- opts = g_strsplit(mstr + 1, "/", 0);
- for (i = 0; opts[i]; i++) {
- kv = g_strsplit(opts[i], "=", 2);
- if (!strncmp(kv[0], "rts", 3)) {
- if (kv[1][0] == '1')
- rts = 1;
- else if (kv[1][0] == '0')
- rts = 0;
- else {
- sr_dbg("invalid value for rts: %c", kv[1][0]);
- speed = 0;
- }
- } else if (!strncmp(kv[0], "dtr", 3)) {
- if (kv[1][0] == '1')
- dtr = 1;
- else if (kv[1][0] == '0')
- dtr = 0;
- else {
- sr_dbg("invalid value for dtr: %c", kv[1][0]);
- speed = 0;
- }
- } else if (!strncmp(kv[0], "flow", 4)) {
- if (kv[1][0] == '0')
- flow = 0;
- else if (kv[1][0] == '1')
- flow = 1;
- else if (kv[1][0] == '2')
- flow = 2;
- else {
- sr_dbg("invalid value for flow: %c", kv[1][0]);
- speed = 0;
- }
- }
- g_strfreev(kv);
- }
- g_strfreev(opts);
- }
- }
- g_free(mstr);
- }
- g_match_info_unref(match);
- g_regex_unref(reg);
-
- if (speed) {
- return serial_set_params(serial, speed, databits, parity,
- stopbits, flow, rts, dtr);
- } else {
- sr_dbg("Could not infer speed from parameter string.");
- return SR_ERR_ARG;
- }
-}
-
-/**
- * Read a line from the specified serial port.
- *
- * @param serial Previously initialized serial port structure.
- * @param buf Buffer where to store the bytes that are read.
- * @param buflen Size of the buffer.
- * @param[in] timeout_ms How long to wait for a line to come in.
- *
- * Reading stops when CR of LR is found, which is stripped from the buffer.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Failure.
- */
-SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
- int *buflen, gint64 timeout_ms)
-{
- gint64 start;
- int maxlen, len;
-
- if (!serial) {
- sr_dbg("Invalid serial port.");
- return SR_ERR;
- }
-
- if (!serial->data) {
- sr_dbg("Cannot use unopened serial port %s.", serial->port);
- return -1;
- }
-
- timeout_ms *= 1000;
- start = g_get_monotonic_time();
-
- maxlen = *buflen;
- *buflen = len = 0;
- while(1) {
- len = maxlen - *buflen - 1;
- if (len < 1)
- break;
- len = serial_read(serial, *buf + *buflen, 1);
- if (len > 0) {
- *buflen += len;
- *(*buf + *buflen) = '\0';
- if (*buflen > 0 && (*(*buf + *buflen - 1) == '\r'
- || *(*buf + *buflen - 1) == '\n')) {
- /* Strip CR/LF and terminate. */
- *(*buf + --*buflen) = '\0';
- break;
- }
- }
- if (g_get_monotonic_time() - start > timeout_ms)
- /* Timeout */
- break;
- if (len < 1)
- g_usleep(2000);
- }
- if (*buflen)
- sr_dbg("Received %d: '%s'.", *buflen, *buf);
-
- return SR_OK;
-}
-
-/**
- * Try to find a valid packet in a serial data stream.
- *
- * @param serial Previously initialized serial port structure.
- * @param buf Buffer containing the bytes to write.
- * @param buflen Size of the buffer.
- * @param[in] packet_size Size, in bytes, of a valid packet.
- * @param is_valid Callback that assesses whether the packet is valid or not.
- * @param[in] timeout_ms The timeout after which, if no packet is detected, to
- * abort scanning.
- * @param[in] baudrate The baudrate of the serial port. This parameter is not
- * critical, but it helps fine tune the serial port polling
- * delay.
- *
- * @retval SR_OK Valid packet was found within the given timeout.
- * @retval SR_ERR Failure.
- */
-SR_PRIV int serial_stream_detect(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, int baudrate)
-{
- uint64_t start, time, byte_delay_us;
- size_t ibuf, i, maxlen;
- int len;
-
- maxlen = *buflen;
-
- sr_dbg("Detecting packets on %s (timeout = %" PRIu64
- "ms, baudrate = %d).", serial->port, timeout_ms, baudrate);
-
- if (maxlen < (packet_size / 2) ) {
- sr_err("Buffer size must be at least twice the packet size.");
- return SR_ERR;
- }
-
- /* Assume 8n1 transmission. That is 10 bits for every byte. */
- byte_delay_us = 10 * (1000000 / baudrate);
- start = g_get_monotonic_time();
-
- i = ibuf = len = 0;
- while (ibuf < maxlen) {
- len = serial_read(serial, &buf[ibuf], 1);
- if (len > 0) {
- ibuf += len;
- } else if (len == 0) {
- /* No logging, already done in serial_read(). */
- } else {
- /* Error reading byte, but continuing anyway. */
- }
-
- time = g_get_monotonic_time() - start;
- time /= 1000;
-
- if ((ibuf - i) >= packet_size) {
- /* We have at least a packet's worth of data. */
- if (is_valid(&buf[i])) {
- sr_spew("Found valid %d-byte packet after "
- "%" PRIu64 "ms.", (ibuf - i), time);
- *buflen = ibuf;
- return SR_OK;
- } else {
- sr_spew("Got %d bytes, but not a valid "
- "packet.", (ibuf - i));
- }
- /* Not a valid packet. Continue searching. */
- i++;
- }
- if (time >= timeout_ms) {
- /* Timeout */
- sr_dbg("Detection timed out after %dms.", time);
- break;
- }
- if (len < 1)
- g_usleep(byte_delay_us);
- }
-
- *buflen = ibuf;
-
- sr_err("Didn't find a valid packet (read %d bytes).", *buflen);
-
- return SR_ERR;
-}
-
-/**
- * Extract the serial device and options from the options linked list.
- *
- * @param options List of options passed from the command line.
- * @param serial_device Pointer where to store the exctracted serial device.
- * @param serial_options Pointer where to store the optional extracted serial
- * options.
- *
- * @return SR_OK if a serial_device is found, SR_ERR if no device is found. The
- * returned string should not be freed by the caller.
- */
-SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_device,
- const char **serial_options)
-{
- GSList *l;
- struct sr_config *src;
-
- *serial_device = NULL;
-
- for (l = options; l; l = l->next) {
- src = l->data;
- switch (src->key) {
- case SR_CONF_CONN:
- *serial_device = g_variant_get_string(src->data, NULL);
- sr_dbg("Parsed serial device: %s", *serial_device);
- break;
-
- case SR_CONF_SERIALCOMM:
- *serial_options = g_variant_get_string(src->data, NULL);
- sr_dbg("Parsed serial options: %s", *serial_options);
- break;
- }
- }
-
- if (!*serial_device) {
- sr_dbg("No serial device specified");
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-#ifdef _WIN32
-typedef HANDLE event_handle;
-#else
-typedef int event_handle;
-#endif
-
-SR_PRIV int serial_source_add(struct sr_session *session,
- struct sr_serial_dev_inst *serial, int events, int timeout,
- sr_receive_data_callback cb, void *cb_data)
-{
- enum sp_event mask = 0;
- unsigned int i;
-
- if (sp_new_event_set(&serial->event_set) != SP_OK)
- return SR_ERR;
-
- if (events & G_IO_IN)
- mask |= SP_EVENT_RX_READY;
- if (events & G_IO_OUT)
- mask |= SP_EVENT_TX_READY;
- if (events & G_IO_ERR)
- mask |= SP_EVENT_ERROR;
-
- if (sp_add_port_events(serial->event_set, serial->data, mask) != SP_OK) {
- sp_free_event_set(serial->event_set);
- return SR_ERR;
- }
-
- serial->pollfds = (GPollFD *) g_malloc0(sizeof(GPollFD) * serial->event_set->count);
-
- for (i = 0; i < serial->event_set->count; i++) {
-
- serial->pollfds[i].fd = ((event_handle *) serial->event_set->handles)[i];
-
- mask = serial->event_set->masks[i];
-
- if (mask & SP_EVENT_RX_READY)
- serial->pollfds[i].events |= G_IO_IN;
- if (mask & SP_EVENT_TX_READY)
- serial->pollfds[i].events |= G_IO_OUT;
- if (mask & SP_EVENT_ERROR)
- serial->pollfds[i].events |= G_IO_ERR;
-
- if (sr_session_source_add_pollfd(session, &serial->pollfds[i],
- timeout, cb, cb_data) != SR_OK)
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int serial_source_remove(struct sr_session *session,
- struct sr_serial_dev_inst *serial)
-{
- unsigned int i;
-
- for (i = 0; i < serial->event_set->count; i++)
- if (sr_session_source_remove_pollfd(session, &serial->pollfds[i]) != SR_OK)
- return SR_ERR;
-
- g_free(serial->pollfds);
- sp_free_event_set(serial->event_set);
-
- serial->pollfds = NULL;
- serial->event_set = NULL;
-
- return SR_OK;
-}
-
-/**
- * Find USB serial devices via the USB vendor ID and product ID.
- *
- * @param[in] vendor_id Vendor ID of the USB device.
- * @param[in] product_id Product ID of the USB device.
- *
- * @return A GSList of strings containing the path of the serial device or
- * NULL if no serial device is found. The returned list must be freed
- * by the caller.
- */
-SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id)
-{
-#ifdef __linux__
- const gchar *usb_dev;
- const char device_tree[] = "/sys/bus/usb/devices/";
- GDir *devices_dir, *device_dir;
- GSList *l = NULL;
- GSList *tty_devs;
- GSList *matched_paths;
- FILE *fd;
- char tmp[5];
- gchar *vendor_path, *product_path, *path_copy;
- gchar *prefix, *subdir_path, *device_path, *tty_path;
- unsigned long read_vendor_id, read_product_id;
- const char *file;
-
- l = NULL;
- tty_devs = NULL;
- matched_paths = NULL;
-
- if (!(devices_dir = g_dir_open(device_tree, 0, NULL)))
- return NULL;
-
- /*
- * Find potential candidates using the vendor ID and product ID
- * and store them in matched_paths.
- */
- while ((usb_dev = g_dir_read_name(devices_dir))) {
- vendor_path = g_strconcat(device_tree,
- usb_dev, "/idVendor", NULL);
- product_path = g_strconcat(device_tree,
- usb_dev, "/idProduct", NULL);
-
- if (!g_file_test(vendor_path, G_FILE_TEST_EXISTS) ||
- !g_file_test(product_path, G_FILE_TEST_EXISTS))
- goto skip_device;
-
- if ((fd = g_fopen(vendor_path, "r")) == NULL)
- goto skip_device;
-
- if (fgets(tmp, sizeof(tmp), fd) == NULL) {
- fclose(fd);
- goto skip_device;
- }
- read_vendor_id = strtoul(tmp, NULL, 16);
-
- fclose(fd);
-
- if ((fd = g_fopen(product_path, "r")) == NULL)
- goto skip_device;
-
- if (fgets(tmp, sizeof(tmp), fd) == NULL) {
- fclose(fd);
- goto skip_device;
- }
- read_product_id = strtoul(tmp, NULL, 16);
-
- fclose(fd);
-
- if (vendor_id == read_vendor_id &&
- product_id == read_product_id) {
- path_copy = g_strdup(usb_dev);
- matched_paths = g_slist_prepend(matched_paths,
- path_copy);
- }
-
-skip_device:
- g_free(vendor_path);
- g_free(product_path);
- }
- g_dir_close(devices_dir);
-
- /* For every matched device try to find a ttyUSBX subfolder. */
- for (l = matched_paths; l; l = l->next) {
- subdir_path = NULL;
-
- device_path = g_strconcat(device_tree, l->data, NULL);
-
- if (!(device_dir = g_dir_open(device_path, 0, NULL))) {
- g_free(device_path);
- continue;
- }
-
- prefix = g_strconcat(l->data, ":", NULL);
-
- while ((file = g_dir_read_name(device_dir))) {
- if (g_str_has_prefix(file, prefix)) {
- subdir_path = g_strconcat(device_path,
- "/", file, NULL);
- break;
- }
- }
- g_dir_close(device_dir);
-
- g_free(prefix);
- g_free(device_path);
-
- if (subdir_path) {
- if (!(device_dir = g_dir_open(subdir_path, 0, NULL))) {
- g_free(subdir_path);
- continue;
- }
- g_free(subdir_path);
-
- while ((file = g_dir_read_name(device_dir))) {
- if (g_str_has_prefix(file, "ttyUSB")) {
- tty_path = g_strconcat("/dev/",
- file, NULL);
- sr_dbg("Found USB device %04x:%04x attached to %s.",
- vendor_id, product_id, tty_path);
- tty_devs = g_slist_prepend(tty_devs,
- tty_path);
- break;
- }
- }
- g_dir_close(device_dir);
- }
- }
- g_slist_free_full(matched_paths, g_free);
-
- return tty_devs;
-#else
- (void)vendor_id;
- (void)product_id;
-
- return NULL;
-#endif
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <glib.h>
-#include <libusb.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/* SR_CONF_CONN takes one of these: */
-#define CONN_USB_VIDPID "^([0-9a-z]{4})\\.([0-9a-z]{4})$"
-#define CONN_USB_BUSADDR "^(\\d+)\\.(\\d+)$"
-
-#define LOG_PREFIX "usb"
-
-/**
- * Find USB devices according to a connection string.
- *
- * @param usb_ctx libusb context to use while scanning.
- * @param conn Connection string specifying the device(s) to match. This
- * can be of the form "<bus>.<address>", or "<vendorid>.<productid>".
- *
- * @return A GSList of struct sr_usb_dev_inst, with bus and address fields
- * matching the device that matched the connection string. The GSList and
- * its contents must be freed by the caller.
- */
-SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn)
-{
- struct sr_usb_dev_inst *usb;
- struct libusb_device **devlist;
- struct libusb_device_descriptor des;
- GSList *devices;
- GRegex *reg;
- GMatchInfo *match;
- int vid, pid, bus, addr, b, a, ret, i;
- char *mstr;
-
- vid = pid = bus = addr = 0;
- reg = g_regex_new(CONN_USB_VIDPID, 0, 0, NULL);
- if (g_regex_match(reg, conn, 0, &match)) {
- if ((mstr = g_match_info_fetch(match, 1)))
- vid = strtoul(mstr, NULL, 16);
- g_free(mstr);
-
- if ((mstr = g_match_info_fetch(match, 2)))
- pid = strtoul(mstr, NULL, 16);
- g_free(mstr);
- sr_dbg("Trying to find USB device with VID:PID = %04x:%04x.",
- vid, pid);
- } else {
- g_match_info_unref(match);
- g_regex_unref(reg);
- reg = g_regex_new(CONN_USB_BUSADDR, 0, 0, NULL);
- if (g_regex_match(reg, conn, 0, &match)) {
- if ((mstr = g_match_info_fetch(match, 1)))
- bus = strtoul(mstr, NULL, 10);
- g_free(mstr);
-
- if ((mstr = g_match_info_fetch(match, 2)))
- addr = strtoul(mstr, NULL, 10);
- g_free(mstr);
- sr_dbg("Trying to find USB device with bus.address = "
- "%d.%d.", bus, addr);
- }
- }
- g_match_info_unref(match);
- g_regex_unref(reg);
-
- if (vid + pid + bus + addr == 0) {
- sr_err("Neither VID:PID nor bus.address was specified.");
- return NULL;
- }
-
- if (bus > 64) {
- sr_err("Invalid bus specified: %d.", bus);
- return NULL;
- }
-
- if (addr > 127) {
- sr_err("Invalid address specified: %d.", addr);
- return NULL;
- }
-
- /* Looks like a valid USB device specification, but is it connected? */
- devices = NULL;
- libusb_get_device_list(usb_ctx, &devlist);
- for (i = 0; devlist[i]; i++) {
- if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
- sr_err("Failed to get device descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
-
- if (vid + pid && (des.idVendor != vid || des.idProduct != pid))
- continue;
-
- b = libusb_get_bus_number(devlist[i]);
- a = libusb_get_device_address(devlist[i]);
- if (bus + addr && (b != bus || a != addr))
- continue;
-
- sr_dbg("Found USB device (VID:PID = %04x:%04x, bus.address = "
- "%d.%d).", des.idVendor, des.idProduct, b, a);
-
- usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
- libusb_get_device_address(devlist[i]), NULL);
- devices = g_slist_append(devices, usb);
- }
- libusb_free_device_list(devlist, 1);
-
- sr_dbg("Found %d device(s).", g_slist_length(devices));
-
- return devices;
-}
-
-SR_PRIV int sr_usb_open(libusb_context *usb_ctx, struct sr_usb_dev_inst *usb)
-{
- struct libusb_device **devlist;
- struct libusb_device_descriptor des;
- int ret, r, cnt, i, a, b;
-
- sr_dbg("Trying to open USB device %d.%d.", usb->bus, usb->address);
-
- if ((cnt = libusb_get_device_list(usb_ctx, &devlist)) < 0) {
- sr_err("Failed to retrieve device list: %s.",
- libusb_error_name(cnt));
- return SR_ERR;
- }
-
- ret = SR_ERR;
- for (i = 0; i < cnt; i++) {
- if ((r = libusb_get_device_descriptor(devlist[i], &des)) < 0) {
- sr_err("Failed to get device descriptor: %s.",
- libusb_error_name(r));
- continue;
- }
-
- b = libusb_get_bus_number(devlist[i]);
- a = libusb_get_device_address(devlist[i]);
- if (b != usb->bus || a != usb->address)
- continue;
-
- if ((r = libusb_open(devlist[i], &usb->devhdl)) < 0) {
- sr_err("Failed to open device: %s.",
- libusb_error_name(r));
- break;
- }
-
- sr_dbg("Opened USB device (VID:PID = %04x:%04x, bus.address = "
- "%d.%d).", des.idVendor, des.idProduct, b, a);
-
- ret = SR_OK;
- break;
- }
-
- libusb_free_device_list(devlist, 1);
-
- return ret;
-}
-
-#ifdef _WIN32
-static gpointer usb_thread(gpointer data)
-{
- struct sr_context *ctx = data;
-
- while (ctx->usb_thread_running) {
- g_mutex_lock(&ctx->usb_mutex);
- libusb_wait_for_event(ctx->libusb_ctx, NULL);
- SetEvent(ctx->usb_event);
- g_mutex_unlock(&ctx->usb_mutex);
- g_thread_yield();
- }
-
- return NULL;
-}
-
-static int usb_callback(int fd, int revents, void *cb_data)
-{
- struct sr_context *ctx = cb_data;
- int ret;
-
- g_mutex_lock(&ctx->usb_mutex);
- ret = ctx->usb_cb(fd, revents, ctx->usb_cb_data);
-
- if (ctx->usb_thread_running) {
- ResetEvent(ctx->usb_event);
- g_mutex_unlock(&ctx->usb_mutex);
- }
-
- return ret;
-}
-#endif
-
-SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx,
- int timeout, sr_receive_data_callback cb, void *cb_data)
-{
- if (ctx->usb_source_present) {
- sr_err("A USB event source is already present.");
- return SR_ERR;
- }
-
-#ifdef _WIN32
- ctx->usb_event = CreateEvent(NULL, TRUE, FALSE, NULL);
- g_mutex_init(&ctx->usb_mutex);
- ctx->usb_thread_running = TRUE;
- ctx->usb_thread = g_thread_new("usb", usb_thread, ctx);
- ctx->usb_pollfd.fd = ctx->usb_event;
- ctx->usb_pollfd.events = G_IO_IN;
- ctx->usb_cb = cb;
- ctx->usb_cb_data = cb_data;
- sr_session_source_add_pollfd(session, &ctx->usb_pollfd, timeout,
- usb_callback, ctx);
-#else
- const struct libusb_pollfd **lupfd;
- unsigned int i;
-
- lupfd = libusb_get_pollfds(ctx->libusb_ctx);
- for (i = 0; lupfd[i]; i++)
- sr_session_source_add(session, lupfd[i]->fd, lupfd[i]->events,
- timeout, cb, cb_data);
- free(lupfd);
-#endif
- ctx->usb_source_present = TRUE;
-
- return SR_OK;
-}
-
-SR_PRIV int usb_source_remove(struct sr_session *session, struct sr_context *ctx)
-{
- if (!ctx->usb_source_present)
- return SR_OK;
-
-#ifdef _WIN32
- ctx->usb_thread_running = FALSE;
- g_mutex_unlock(&ctx->usb_mutex);
- libusb_unlock_events(ctx->libusb_ctx);
- g_thread_join(ctx->usb_thread);
- g_mutex_clear(&ctx->usb_mutex);
- sr_session_source_remove_pollfd(session, &ctx->usb_pollfd);
- CloseHandle(ctx->usb_event);
-#else
- const struct libusb_pollfd **lupfd;
- unsigned int i;
-
- lupfd = libusb_get_pollfds(ctx->libusb_ctx);
- for (i = 0; lupfd[i]; i++)
- sr_session_source_remove(session, lupfd[i]->fd);
- free(lupfd);
-#endif
- ctx->usb_source_present = FALSE;
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * Please do not edit this file.
- * It was generated using rpcgen.
- */
-
-#ifndef _VXI_H_RPCGEN
-#define _VXI_H_RPCGEN
-
-#include <rpc/rpc.h>
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-typedef long Device_Link;
-
-enum Device_AddrFamily {
- DEVICE_TCP = 0,
- DEVICE_UDP = 1,
-};
-typedef enum Device_AddrFamily Device_AddrFamily;
-
-typedef long Device_Flags;
-
-typedef long Device_ErrorCode;
-
-struct Device_Error {
- Device_ErrorCode error;
-};
-typedef struct Device_Error Device_Error;
-
-struct Create_LinkParms {
- long clientId;
- bool_t lockDevice;
- u_long lock_timeout;
- char *device;
-};
-typedef struct Create_LinkParms Create_LinkParms;
-
-struct Create_LinkResp {
- Device_ErrorCode error;
- Device_Link lid;
- u_short abortPort;
- u_long maxRecvSize;
-};
-typedef struct Create_LinkResp Create_LinkResp;
-
-struct Device_WriteParms {
- Device_Link lid;
- u_long io_timeout;
- u_long lock_timeout;
- Device_Flags flags;
- struct {
- u_int data_len;
- char *data_val;
- } data;
-};
-typedef struct Device_WriteParms Device_WriteParms;
-
-struct Device_WriteResp {
- Device_ErrorCode error;
- u_long size;
-};
-typedef struct Device_WriteResp Device_WriteResp;
-
-struct Device_ReadParms {
- Device_Link lid;
- u_long requestSize;
- u_long io_timeout;
- u_long lock_timeout;
- Device_Flags flags;
- char termChar;
-};
-typedef struct Device_ReadParms Device_ReadParms;
-
-struct Device_ReadResp {
- Device_ErrorCode error;
- long reason;
- struct {
- u_int data_len;
- char *data_val;
- } data;
-};
-typedef struct Device_ReadResp Device_ReadResp;
-
-struct Device_ReadStbResp {
- Device_ErrorCode error;
- u_char stb;
-};
-typedef struct Device_ReadStbResp Device_ReadStbResp;
-
-struct Device_GenericParms {
- Device_Link lid;
- Device_Flags flags;
- u_long lock_timeout;
- u_long io_timeout;
-};
-typedef struct Device_GenericParms Device_GenericParms;
-
-struct Device_RemoteFunc {
- u_long hostAddr;
- u_short hostPort;
- u_long progNum;
- u_long progVers;
- Device_AddrFamily progFamily;
-};
-typedef struct Device_RemoteFunc Device_RemoteFunc;
-
-struct Device_EnableSrqParms {
- Device_Link lid;
- bool_t enable;
- struct {
- u_int handle_len;
- char *handle_val;
- } handle;
-};
-typedef struct Device_EnableSrqParms Device_EnableSrqParms;
-
-struct Device_LockParms {
- Device_Link lid;
- Device_Flags flags;
- u_long lock_timeout;
-};
-typedef struct Device_LockParms Device_LockParms;
-
-struct Device_DocmdParms {
- Device_Link lid;
- Device_Flags flags;
- u_long io_timeout;
- u_long lock_timeout;
- long cmd;
- bool_t network_order;
- long datasize;
- struct {
- u_int data_in_len;
- char *data_in_val;
- } data_in;
-};
-typedef struct Device_DocmdParms Device_DocmdParms;
-
-struct Device_DocmdResp {
- Device_ErrorCode error;
- struct {
- u_int data_out_len;
- char *data_out_val;
- } data_out;
-};
-typedef struct Device_DocmdResp Device_DocmdResp;
-
-struct Device_SrqParms {
- struct {
- u_int handle_len;
- char *handle_val;
- } handle;
-};
-typedef struct Device_SrqParms Device_SrqParms;
-
-#define DEVICE_ASYNC 0x0607B0
-#define DEVICE_ASYNC_VERSION 1
-
-#if defined(__STDC__) || defined(__cplusplus)
-#define device_abort 1
-extern Device_Error * device_abort_1(Device_Link *, CLIENT *);
-extern Device_Error * device_abort_1_svc(Device_Link *, struct svc_req *);
-extern int device_async_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
-
-#else /* K&R C */
-#define device_abort 1
-extern Device_Error * device_abort_1();
-extern Device_Error * device_abort_1_svc();
-extern int device_async_1_freeresult ();
-#endif /* K&R C */
-
-#define DEVICE_CORE 0x0607AF
-#define DEVICE_CORE_VERSION 1
-
-#if defined(__STDC__) || defined(__cplusplus)
-#define create_link 10
-extern Create_LinkResp * create_link_1(Create_LinkParms *, CLIENT *);
-extern Create_LinkResp * create_link_1_svc(Create_LinkParms *, struct svc_req *);
-#define device_write 11
-extern Device_WriteResp * device_write_1(Device_WriteParms *, CLIENT *);
-extern Device_WriteResp * device_write_1_svc(Device_WriteParms *, struct svc_req *);
-#define device_read 12
-extern Device_ReadResp * device_read_1(Device_ReadParms *, CLIENT *);
-extern Device_ReadResp * device_read_1_svc(Device_ReadParms *, struct svc_req *);
-#define device_readstb 13
-extern Device_ReadStbResp * device_readstb_1(Device_GenericParms *, CLIENT *);
-extern Device_ReadStbResp * device_readstb_1_svc(Device_GenericParms *, struct svc_req *);
-#define device_trigger 14
-extern Device_Error * device_trigger_1(Device_GenericParms *, CLIENT *);
-extern Device_Error * device_trigger_1_svc(Device_GenericParms *, struct svc_req *);
-#define device_clear 15
-extern Device_Error * device_clear_1(Device_GenericParms *, CLIENT *);
-extern Device_Error * device_clear_1_svc(Device_GenericParms *, struct svc_req *);
-#define device_remote 16
-extern Device_Error * device_remote_1(Device_GenericParms *, CLIENT *);
-extern Device_Error * device_remote_1_svc(Device_GenericParms *, struct svc_req *);
-#define device_local 17
-extern Device_Error * device_local_1(Device_GenericParms *, CLIENT *);
-extern Device_Error * device_local_1_svc(Device_GenericParms *, struct svc_req *);
-#define device_lock 18
-extern Device_Error * device_lock_1(Device_LockParms *, CLIENT *);
-extern Device_Error * device_lock_1_svc(Device_LockParms *, struct svc_req *);
-#define device_unlock 19
-extern Device_Error * device_unlock_1(Device_Link *, CLIENT *);
-extern Device_Error * device_unlock_1_svc(Device_Link *, struct svc_req *);
-#define device_enable_srq 20
-extern Device_Error * device_enable_srq_1(Device_EnableSrqParms *, CLIENT *);
-extern Device_Error * device_enable_srq_1_svc(Device_EnableSrqParms *, struct svc_req *);
-#define device_docmd 22
-extern Device_DocmdResp * device_docmd_1(Device_DocmdParms *, CLIENT *);
-extern Device_DocmdResp * device_docmd_1_svc(Device_DocmdParms *, struct svc_req *);
-#define destroy_link 23
-extern Device_Error * destroy_link_1(Device_Link *, CLIENT *);
-extern Device_Error * destroy_link_1_svc(Device_Link *, struct svc_req *);
-#define create_intr_chan 25
-extern Device_Error * create_intr_chan_1(Device_RemoteFunc *, CLIENT *);
-extern Device_Error * create_intr_chan_1_svc(Device_RemoteFunc *, struct svc_req *);
-#define destroy_intr_chan 26
-extern Device_Error * destroy_intr_chan_1(void *, CLIENT *);
-extern Device_Error * destroy_intr_chan_1_svc(void *, struct svc_req *);
-extern int device_core_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
-
-#else /* K&R C */
-#define create_link 10
-extern Create_LinkResp * create_link_1();
-extern Create_LinkResp * create_link_1_svc();
-#define device_write 11
-extern Device_WriteResp * device_write_1();
-extern Device_WriteResp * device_write_1_svc();
-#define device_read 12
-extern Device_ReadResp * device_read_1();
-extern Device_ReadResp * device_read_1_svc();
-#define device_readstb 13
-extern Device_ReadStbResp * device_readstb_1();
-extern Device_ReadStbResp * device_readstb_1_svc();
-#define device_trigger 14
-extern Device_Error * device_trigger_1();
-extern Device_Error * device_trigger_1_svc();
-#define device_clear 15
-extern Device_Error * device_clear_1();
-extern Device_Error * device_clear_1_svc();
-#define device_remote 16
-extern Device_Error * device_remote_1();
-extern Device_Error * device_remote_1_svc();
-#define device_local 17
-extern Device_Error * device_local_1();
-extern Device_Error * device_local_1_svc();
-#define device_lock 18
-extern Device_Error * device_lock_1();
-extern Device_Error * device_lock_1_svc();
-#define device_unlock 19
-extern Device_Error * device_unlock_1();
-extern Device_Error * device_unlock_1_svc();
-#define device_enable_srq 20
-extern Device_Error * device_enable_srq_1();
-extern Device_Error * device_enable_srq_1_svc();
-#define device_docmd 22
-extern Device_DocmdResp * device_docmd_1();
-extern Device_DocmdResp * device_docmd_1_svc();
-#define destroy_link 23
-extern Device_Error * destroy_link_1();
-extern Device_Error * destroy_link_1_svc();
-#define create_intr_chan 25
-extern Device_Error * create_intr_chan_1();
-extern Device_Error * create_intr_chan_1_svc();
-#define destroy_intr_chan 26
-extern Device_Error * destroy_intr_chan_1();
-extern Device_Error * destroy_intr_chan_1_svc();
-extern int device_core_1_freeresult ();
-#endif /* K&R C */
-
-#define DEVICE_INTR 0x0607B1
-#define DEVICE_INTR_VERSION 1
-
-#if defined(__STDC__) || defined(__cplusplus)
-#define device_intr_srq 30
-extern void * device_intr_srq_1(Device_SrqParms *, CLIENT *);
-extern void * device_intr_srq_1_svc(Device_SrqParms *, struct svc_req *);
-extern int device_intr_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
-
-#else /* K&R C */
-#define device_intr_srq 30
-extern void * device_intr_srq_1();
-extern void * device_intr_srq_1_svc();
-extern int device_intr_1_freeresult ();
-#endif /* K&R C */
-
-/* the xdr functions */
-
-#if defined(__STDC__) || defined(__cplusplus)
-extern bool_t xdr_Device_Link (XDR *, Device_Link*);
-extern bool_t xdr_Device_AddrFamily (XDR *, Device_AddrFamily*);
-extern bool_t xdr_Device_Flags (XDR *, Device_Flags*);
-extern bool_t xdr_Device_ErrorCode (XDR *, Device_ErrorCode*);
-extern bool_t xdr_Device_Error (XDR *, Device_Error*);
-extern bool_t xdr_Create_LinkParms (XDR *, Create_LinkParms*);
-extern bool_t xdr_Create_LinkResp (XDR *, Create_LinkResp*);
-extern bool_t xdr_Device_WriteParms (XDR *, Device_WriteParms*);
-extern bool_t xdr_Device_WriteResp (XDR *, Device_WriteResp*);
-extern bool_t xdr_Device_ReadParms (XDR *, Device_ReadParms*);
-extern bool_t xdr_Device_ReadResp (XDR *, Device_ReadResp*);
-extern bool_t xdr_Device_ReadStbResp (XDR *, Device_ReadStbResp*);
-extern bool_t xdr_Device_GenericParms (XDR *, Device_GenericParms*);
-extern bool_t xdr_Device_RemoteFunc (XDR *, Device_RemoteFunc*);
-extern bool_t xdr_Device_EnableSrqParms (XDR *, Device_EnableSrqParms*);
-extern bool_t xdr_Device_LockParms (XDR *, Device_LockParms*);
-extern bool_t xdr_Device_DocmdParms (XDR *, Device_DocmdParms*);
-extern bool_t xdr_Device_DocmdResp (XDR *, Device_DocmdResp*);
-extern bool_t xdr_Device_SrqParms (XDR *, Device_SrqParms*);
-
-#else /* K&R C */
-extern bool_t xdr_Device_Link ();
-extern bool_t xdr_Device_AddrFamily ();
-extern bool_t xdr_Device_Flags ();
-extern bool_t xdr_Device_ErrorCode ();
-extern bool_t xdr_Device_Error ();
-extern bool_t xdr_Create_LinkParms ();
-extern bool_t xdr_Create_LinkResp ();
-extern bool_t xdr_Device_WriteParms ();
-extern bool_t xdr_Device_WriteResp ();
-extern bool_t xdr_Device_ReadParms ();
-extern bool_t xdr_Device_ReadResp ();
-extern bool_t xdr_Device_ReadStbResp ();
-extern bool_t xdr_Device_GenericParms ();
-extern bool_t xdr_Device_RemoteFunc ();
-extern bool_t xdr_Device_EnableSrqParms ();
-extern bool_t xdr_Device_LockParms ();
-extern bool_t xdr_Device_DocmdParms ();
-extern bool_t xdr_Device_DocmdResp ();
-extern bool_t xdr_Device_SrqParms ();
-
-#endif /* K&R C */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* !_VXI_H_RPCGEN */
+++ /dev/null
-/* This is taken straight from Appendix C of the specification */
-
-typedef long Device_Link;
-
-enum Device_AddrFamily {
- DEVICE_TCP,
- DEVICE_UDP
-};
-
-typedef long Device_Flags;
-
-typedef long Device_ErrorCode;
-
-struct Device_Error {
- Device_ErrorCode error;
-};
-
-struct Create_LinkParms {
- long clientId; /* implementation specific value */
- bool lockDevice; /* attempt to lock the device */
- unsigned long lock_timeout; /* time to wait on a lock */
- string device<>; /* name of device */
-};
-
-struct Create_LinkResp {
- Device_ErrorCode error;
- Device_Link lid;
- unsigned short abortPort; /* for the abort RPC */
- unsigned long maxRecvSize; /* specifies max data size in bytes
- device will accept on a write */
-};
-
-struct Device_WriteParms {
- Device_Link lid; /* link id from create_link */
- unsigned long io_timeout; /* time to wait for I/O */
- unsigned long lock_timeout; /* time to wait for lock */
- Device_Flags flags;
- opaque data<>; /* data length and the data itself */
-};
-
-struct Device_WriteResp {
- Device_ErrorCode error;
- unsigned long size; /* number of bytes written */
-};
-
-struct Device_ReadParms {
- Device_Link lid; /* link id from create_link */
- unsigned long requestSize; /* bytes requested */
- unsigned long io_timeout; /* time to wait for I/O */
- unsigned long lock_timeout; /* time to wait for lock */
- Device_Flags flags;
- char termChar; /* valid if flags & termchrset */
-};
-
-struct Device_ReadResp {
- Device_ErrorCode error;
- long reason; /* reason(s) read completed */
- opaque data<>; /* data.len and data.val */
-};
-
-struct Device_ReadStbResp {
- Device_ErrorCode error; /* error code */
- unsigned char stb; /* the returned status byte */
-};
-
-struct Device_GenericParms {
- Device_Link lid; /* Device_Link id from connect call */
- Device_Flags flags; /* flags with options */
- unsigned long lock_timeout; /* time to wait for lock */
- unsigned long io_timeout; /* time to wait for I/O */
-};
-
-struct Device_RemoteFunc {
- unsigned long hostAddr; /* host servicing interrupt */
- unsigned short hostPort; /* valid port # on client */
- unsigned long progNum; /* DEVICE_INTR */
- unsigned long progVers; /* DEVICE_INTR_VERSION */
- Device_AddrFamily progFamily; /* DEVICE_UDP | DEVICE_TCP */
-};
-
-struct Device_EnableSrqParms {
- Device_Link lid;
- bool enable; /* enable or disable interrupts */
- opaque handle<40>; /* host specific data */
-};
-
-struct Device_LockParms {
- Device_Link lid; /* link id from create_link */
- Device_Flags flags; /* contains the waitlock flag */
- unsigned long lock_timeout; /* time to wait to acquire lock */
-};
-
-struct Device_DocmdParms {
- Device_Link lid; /* link id from create_link */
- Device_Flags flags; /* flags specifying various options */
- unsigned long io_timeout; /* time to wait for I/O to complete */
- unsigned long lock_timeout; /* time to wait on a lock */
- long cmd; /* which command to execute */
- bool network_order; /* client's byte order */
- long datasize; /* size of individual data elements */
- opaque data_in<>; /* docmd data parameters */
-};
-
-struct Device_DocmdResp {
- Device_ErrorCode error; /* returned status */
- opaque data_out<>; /* returned data parameter */
-};
-
-struct Device_SrqParms {
- opaque handle<>;
-};
-
-program DEVICE_ASYNC{
- version DEVICE_ASYNC_VERSION {
- Device_Error device_abort(Device_Link) = 1;
- } = 1;
-} = 0x0607B0;
-
-program DEVICE_CORE {
- version DEVICE_CORE_VERSION {
- Create_LinkResp create_link(Create_LinkParms) = 10;
- Device_WriteResp device_write(Device_WriteParms) = 11;
- Device_ReadResp device_read(Device_ReadParms) = 12;
- Device_ReadStbResp device_readstb(Device_GenericParms) = 13;
- Device_Error device_trigger(Device_GenericParms) = 14;
- Device_Error device_clear(Device_GenericParms) = 15;
- Device_Error device_remote(Device_GenericParms) = 16;
- Device_Error device_local(Device_GenericParms) = 17;
- Device_Error device_lock(Device_LockParms) = 18;
- Device_Error device_unlock(Device_Link) = 19;
- Device_Error device_enable_srq(Device_EnableSrqParms)= 20;
- Device_DocmdResp device_docmd(Device_DocmdParms) = 22;
- Device_Error destroy_link(Device_Link) = 23;
- Device_Error create_intr_chan(Device_RemoteFunc) = 25;
- Device_Error destroy_intr_chan(void) = 26;
- } = 1;
-} = 0x0607AF;
-
-program DEVICE_INTR {
- version DEVICE_INTR_VERSION {
- void device_intr_srq(Device_SrqParms) = 30;
- } = 1;
-} = 0x0607B1;
+++ /dev/null
-/*
- * Please do not edit this file.
- * It was generated using rpcgen.
- */
-
-#include <memory.h> /* for memset */
-#include "vxi.h"
-
-/* Default timeout can be changed using clnt_control() */
-static struct timeval TIMEOUT = { 25, 0 };
-
-Device_Error *
-device_abort_1(Device_Link *argp, CLIENT *clnt)
-{
- static Device_Error clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_abort,
- (xdrproc_t) xdr_Device_Link, (caddr_t) argp,
- (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Create_LinkResp *
-create_link_1(Create_LinkParms *argp, CLIENT *clnt)
-{
- static Create_LinkResp clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, create_link,
- (xdrproc_t) xdr_Create_LinkParms, (caddr_t) argp,
- (xdrproc_t) xdr_Create_LinkResp, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_WriteResp *
-device_write_1(Device_WriteParms *argp, CLIENT *clnt)
-{
- static Device_WriteResp clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_write,
- (xdrproc_t) xdr_Device_WriteParms, (caddr_t) argp,
- (xdrproc_t) xdr_Device_WriteResp, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_ReadResp *
-device_read_1(Device_ReadParms *argp, CLIENT *clnt)
-{
- static Device_ReadResp clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_read,
- (xdrproc_t) xdr_Device_ReadParms, (caddr_t) argp,
- (xdrproc_t) xdr_Device_ReadResp, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_ReadStbResp *
-device_readstb_1(Device_GenericParms *argp, CLIENT *clnt)
-{
- static Device_ReadStbResp clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_readstb,
- (xdrproc_t) xdr_Device_GenericParms, (caddr_t) argp,
- (xdrproc_t) xdr_Device_ReadStbResp, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_Error *
-device_trigger_1(Device_GenericParms *argp, CLIENT *clnt)
-{
- static Device_Error clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_trigger,
- (xdrproc_t) xdr_Device_GenericParms, (caddr_t) argp,
- (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_Error *
-device_clear_1(Device_GenericParms *argp, CLIENT *clnt)
-{
- static Device_Error clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_clear,
- (xdrproc_t) xdr_Device_GenericParms, (caddr_t) argp,
- (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_Error *
-device_remote_1(Device_GenericParms *argp, CLIENT *clnt)
-{
- static Device_Error clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_remote,
- (xdrproc_t) xdr_Device_GenericParms, (caddr_t) argp,
- (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_Error *
-device_local_1(Device_GenericParms *argp, CLIENT *clnt)
-{
- static Device_Error clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_local,
- (xdrproc_t) xdr_Device_GenericParms, (caddr_t) argp,
- (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_Error *
-device_lock_1(Device_LockParms *argp, CLIENT *clnt)
-{
- static Device_Error clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_lock,
- (xdrproc_t) xdr_Device_LockParms, (caddr_t) argp,
- (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_Error *
-device_unlock_1(Device_Link *argp, CLIENT *clnt)
-{
- static Device_Error clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_unlock,
- (xdrproc_t) xdr_Device_Link, (caddr_t) argp,
- (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_Error *
-device_enable_srq_1(Device_EnableSrqParms *argp, CLIENT *clnt)
-{
- static Device_Error clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_enable_srq,
- (xdrproc_t) xdr_Device_EnableSrqParms, (caddr_t) argp,
- (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_DocmdResp *
-device_docmd_1(Device_DocmdParms *argp, CLIENT *clnt)
-{
- static Device_DocmdResp clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_docmd,
- (xdrproc_t) xdr_Device_DocmdParms, (caddr_t) argp,
- (xdrproc_t) xdr_Device_DocmdResp, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_Error *
-destroy_link_1(Device_Link *argp, CLIENT *clnt)
-{
- static Device_Error clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, destroy_link,
- (xdrproc_t) xdr_Device_Link, (caddr_t) argp,
- (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_Error *
-create_intr_chan_1(Device_RemoteFunc *argp, CLIENT *clnt)
-{
- static Device_Error clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, create_intr_chan,
- (xdrproc_t) xdr_Device_RemoteFunc, (caddr_t) argp,
- (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-Device_Error *
-destroy_intr_chan_1(void *argp, CLIENT *clnt)
-{
- static Device_Error clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, destroy_intr_chan,
- (xdrproc_t) xdr_void, (caddr_t) argp,
- (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return (&clnt_res);
-}
-
-void *
-device_intr_srq_1(Device_SrqParms *argp, CLIENT *clnt)
-{
- static char clnt_res;
-
- memset((char *)&clnt_res, 0, sizeof(clnt_res));
- if (clnt_call (clnt, device_intr_srq,
- (xdrproc_t) xdr_Device_SrqParms, (caddr_t) argp,
- (xdrproc_t) xdr_void, (caddr_t) &clnt_res,
- TIMEOUT) != RPC_SUCCESS) {
- return (NULL);
- }
- return ((void *)&clnt_res);
-}
+++ /dev/null
-/*
- * Please do not edit this file.
- * It was generated using rpcgen.
- */
-
-#include "vxi.h"
-
-bool_t
-xdr_Device_Link (XDR *xdrs, Device_Link *objp)
-{
- if (!xdr_long (xdrs, objp))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_AddrFamily (XDR *xdrs, Device_AddrFamily *objp)
-{
- if (!xdr_enum (xdrs, (enum_t *) objp))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_Flags (XDR *xdrs, Device_Flags *objp)
-{
- if (!xdr_long (xdrs, objp))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_ErrorCode (XDR *xdrs, Device_ErrorCode *objp)
-{
- if (!xdr_long (xdrs, objp))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_Error (XDR *xdrs, Device_Error *objp)
-{
- if (!xdr_Device_ErrorCode (xdrs, &objp->error))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Create_LinkParms (XDR *xdrs, Create_LinkParms *objp)
-{
- register int32_t *buf;
-
- if (xdrs->x_op == XDR_ENCODE) {
- buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
- if (buf == NULL) {
- if (!xdr_long (xdrs, &objp->clientId))
- return FALSE;
- if (!xdr_bool (xdrs, &objp->lockDevice))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
-
- } else {
- IXDR_PUT_LONG(buf, objp->clientId);
- IXDR_PUT_BOOL(buf, objp->lockDevice);
- IXDR_PUT_U_LONG(buf, objp->lock_timeout);
- }
- if (!xdr_string (xdrs, &objp->device, ~0))
- return FALSE;
- return TRUE;
- } else if (xdrs->x_op == XDR_DECODE) {
- buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
- if (buf == NULL) {
- if (!xdr_long (xdrs, &objp->clientId))
- return FALSE;
- if (!xdr_bool (xdrs, &objp->lockDevice))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
-
- } else {
- objp->clientId = IXDR_GET_LONG(buf);
- objp->lockDevice = IXDR_GET_BOOL(buf);
- objp->lock_timeout = IXDR_GET_U_LONG(buf);
- }
- if (!xdr_string (xdrs, &objp->device, ~0))
- return FALSE;
- return TRUE;
- }
-
- if (!xdr_long (xdrs, &objp->clientId))
- return FALSE;
- if (!xdr_bool (xdrs, &objp->lockDevice))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
- if (!xdr_string (xdrs, &objp->device, ~0))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Create_LinkResp (XDR *xdrs, Create_LinkResp *objp)
-{
- if (!xdr_Device_ErrorCode (xdrs, &objp->error))
- return FALSE;
- if (!xdr_Device_Link (xdrs, &objp->lid))
- return FALSE;
- if (!xdr_u_short (xdrs, &objp->abortPort))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->maxRecvSize))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_WriteParms (XDR *xdrs, Device_WriteParms *objp)
-{
- if (!xdr_Device_Link (xdrs, &objp->lid))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->io_timeout))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
- if (!xdr_Device_Flags (xdrs, &objp->flags))
- return FALSE;
- if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, ~0))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_WriteResp (XDR *xdrs, Device_WriteResp *objp)
-{
- if (!xdr_Device_ErrorCode (xdrs, &objp->error))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->size))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_ReadParms (XDR *xdrs, Device_ReadParms *objp)
-{
- register int32_t *buf;
-
- if (xdrs->x_op == XDR_ENCODE) {
- if (!xdr_Device_Link (xdrs, &objp->lid))
- return FALSE;
- buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
- if (buf == NULL) {
- if (!xdr_u_long (xdrs, &objp->requestSize))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->io_timeout))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
-
- } else {
- IXDR_PUT_U_LONG(buf, objp->requestSize);
- IXDR_PUT_U_LONG(buf, objp->io_timeout);
- IXDR_PUT_U_LONG(buf, objp->lock_timeout);
- }
- if (!xdr_Device_Flags (xdrs, &objp->flags))
- return FALSE;
- if (!xdr_char (xdrs, &objp->termChar))
- return FALSE;
- return TRUE;
- } else if (xdrs->x_op == XDR_DECODE) {
- if (!xdr_Device_Link (xdrs, &objp->lid))
- return FALSE;
- buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
- if (buf == NULL) {
- if (!xdr_u_long (xdrs, &objp->requestSize))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->io_timeout))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
-
- } else {
- objp->requestSize = IXDR_GET_U_LONG(buf);
- objp->io_timeout = IXDR_GET_U_LONG(buf);
- objp->lock_timeout = IXDR_GET_U_LONG(buf);
- }
- if (!xdr_Device_Flags (xdrs, &objp->flags))
- return FALSE;
- if (!xdr_char (xdrs, &objp->termChar))
- return FALSE;
- return TRUE;
- }
-
- if (!xdr_Device_Link (xdrs, &objp->lid))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->requestSize))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->io_timeout))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
- if (!xdr_Device_Flags (xdrs, &objp->flags))
- return FALSE;
- if (!xdr_char (xdrs, &objp->termChar))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_ReadResp (XDR *xdrs, Device_ReadResp *objp)
-{
- if (!xdr_Device_ErrorCode (xdrs, &objp->error))
- return FALSE;
- if (!xdr_long (xdrs, &objp->reason))
- return FALSE;
- if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, ~0))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_ReadStbResp (XDR *xdrs, Device_ReadStbResp *objp)
-{
- if (!xdr_Device_ErrorCode (xdrs, &objp->error))
- return FALSE;
- if (!xdr_u_char (xdrs, &objp->stb))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_GenericParms (XDR *xdrs, Device_GenericParms *objp)
-{
- if (!xdr_Device_Link (xdrs, &objp->lid))
- return FALSE;
- if (!xdr_Device_Flags (xdrs, &objp->flags))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->io_timeout))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_RemoteFunc (XDR *xdrs, Device_RemoteFunc *objp)
-{
- register int32_t *buf;
-
- if (xdrs->x_op == XDR_ENCODE) {
- buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
- if (buf == NULL) {
- if (!xdr_u_long (xdrs, &objp->hostAddr))
- return FALSE;
- if (!xdr_u_short (xdrs, &objp->hostPort))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->progNum))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->progVers))
- return FALSE;
-
- } else {
- IXDR_PUT_U_LONG(buf, objp->hostAddr);
- IXDR_PUT_U_SHORT(buf, objp->hostPort);
- IXDR_PUT_U_LONG(buf, objp->progNum);
- IXDR_PUT_U_LONG(buf, objp->progVers);
- }
- if (!xdr_Device_AddrFamily (xdrs, &objp->progFamily))
- return FALSE;
- return TRUE;
- } else if (xdrs->x_op == XDR_DECODE) {
- buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
- if (buf == NULL) {
- if (!xdr_u_long (xdrs, &objp->hostAddr))
- return FALSE;
- if (!xdr_u_short (xdrs, &objp->hostPort))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->progNum))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->progVers))
- return FALSE;
-
- } else {
- objp->hostAddr = IXDR_GET_U_LONG(buf);
- objp->hostPort = IXDR_GET_U_SHORT(buf);
- objp->progNum = IXDR_GET_U_LONG(buf);
- objp->progVers = IXDR_GET_U_LONG(buf);
- }
- if (!xdr_Device_AddrFamily (xdrs, &objp->progFamily))
- return FALSE;
- return TRUE;
- }
-
- if (!xdr_u_long (xdrs, &objp->hostAddr))
- return FALSE;
- if (!xdr_u_short (xdrs, &objp->hostPort))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->progNum))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->progVers))
- return FALSE;
- if (!xdr_Device_AddrFamily (xdrs, &objp->progFamily))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_EnableSrqParms (XDR *xdrs, Device_EnableSrqParms *objp)
-{
- if (!xdr_Device_Link (xdrs, &objp->lid))
- return FALSE;
- if (!xdr_bool (xdrs, &objp->enable))
- return FALSE;
- if (!xdr_bytes (xdrs, (char **)&objp->handle.handle_val, (u_int *) &objp->handle.handle_len, 40))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_LockParms (XDR *xdrs, Device_LockParms *objp)
-{
- if (!xdr_Device_Link (xdrs, &objp->lid))
- return FALSE;
- if (!xdr_Device_Flags (xdrs, &objp->flags))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_DocmdParms (XDR *xdrs, Device_DocmdParms *objp)
-{
- register int32_t *buf;
-
- if (xdrs->x_op == XDR_ENCODE) {
- if (!xdr_Device_Link (xdrs, &objp->lid))
- return FALSE;
- if (!xdr_Device_Flags (xdrs, &objp->flags))
- return FALSE;
- buf = XDR_INLINE (xdrs, 5 * BYTES_PER_XDR_UNIT);
- if (buf == NULL) {
- if (!xdr_u_long (xdrs, &objp->io_timeout))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
- if (!xdr_long (xdrs, &objp->cmd))
- return FALSE;
- if (!xdr_bool (xdrs, &objp->network_order))
- return FALSE;
- if (!xdr_long (xdrs, &objp->datasize))
- return FALSE;
-
- } else {
- IXDR_PUT_U_LONG(buf, objp->io_timeout);
- IXDR_PUT_U_LONG(buf, objp->lock_timeout);
- IXDR_PUT_LONG(buf, objp->cmd);
- IXDR_PUT_BOOL(buf, objp->network_order);
- IXDR_PUT_LONG(buf, objp->datasize);
- }
- if (!xdr_bytes (xdrs, (char **)&objp->data_in.data_in_val, (u_int *) &objp->data_in.data_in_len, ~0))
- return FALSE;
- return TRUE;
- } else if (xdrs->x_op == XDR_DECODE) {
- if (!xdr_Device_Link (xdrs, &objp->lid))
- return FALSE;
- if (!xdr_Device_Flags (xdrs, &objp->flags))
- return FALSE;
- buf = XDR_INLINE (xdrs, 5 * BYTES_PER_XDR_UNIT);
- if (buf == NULL) {
- if (!xdr_u_long (xdrs, &objp->io_timeout))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
- if (!xdr_long (xdrs, &objp->cmd))
- return FALSE;
- if (!xdr_bool (xdrs, &objp->network_order))
- return FALSE;
- if (!xdr_long (xdrs, &objp->datasize))
- return FALSE;
-
- } else {
- objp->io_timeout = IXDR_GET_U_LONG(buf);
- objp->lock_timeout = IXDR_GET_U_LONG(buf);
- objp->cmd = IXDR_GET_LONG(buf);
- objp->network_order = IXDR_GET_BOOL(buf);
- objp->datasize = IXDR_GET_LONG(buf);
- }
- if (!xdr_bytes (xdrs, (char **)&objp->data_in.data_in_val, (u_int *) &objp->data_in.data_in_len, ~0))
- return FALSE;
- return TRUE;
- }
-
- if (!xdr_Device_Link (xdrs, &objp->lid))
- return FALSE;
- if (!xdr_Device_Flags (xdrs, &objp->flags))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->io_timeout))
- return FALSE;
- if (!xdr_u_long (xdrs, &objp->lock_timeout))
- return FALSE;
- if (!xdr_long (xdrs, &objp->cmd))
- return FALSE;
- if (!xdr_bool (xdrs, &objp->network_order))
- return FALSE;
- if (!xdr_long (xdrs, &objp->datasize))
- return FALSE;
- if (!xdr_bytes (xdrs, (char **)&objp->data_in.data_in_val, (u_int *) &objp->data_in.data_in_len, ~0))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_DocmdResp (XDR *xdrs, Device_DocmdResp *objp)
-{
- if (!xdr_Device_ErrorCode (xdrs, &objp->error))
- return FALSE;
- if (!xdr_bytes (xdrs, (char **)&objp->data_out.data_out_val, (u_int *) &objp->data_out.data_out_len, ~0))
- return FALSE;
- return TRUE;
-}
-
-bool_t
-xdr_Device_SrqParms (XDR *xdrs, Device_SrqParms *objp)
-{
- if (!xdr_bytes (xdrs, (char **)&objp->handle.handle_val, (u_int *) &objp->handle.handle_len, ~0))
- return FALSE;
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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/>.
- */
-
-/** @file
- * <em>Conrad DIGI 35 CPU</em> power supply driver
- * @internal
- */
-
-#include "protocol.h"
-
-#define SERIALCOMM "9600/8n1"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_POWER_SUPPLY,
- SR_CONF_OUTPUT_VOLTAGE,
- SR_CONF_OUTPUT_CURRENT,
- /* There's no SR_CONF_OUTPUT_ENABLED; can't know/set status remotely. */
- SR_CONF_OVER_CURRENT_PROTECTION,
-};
-
-SR_PRIV struct sr_dev_driver conrad_digi_35_cpu_driver_info;
-static struct sr_dev_driver *di = &conrad_digi_35_cpu_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct sr_config *src;
- struct sr_channel *ch;
- struct sr_serial_dev_inst *serial;
- GSList *l, *devices;
- const char *conn, *serialcomm;
-
- devices = NULL;
- drvc = di->priv;
- drvc->instances = NULL;
- 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 (!conn)
- return NULL;
- if (!serialcomm)
- serialcomm = SERIALCOMM;
-
- /*
- * We cannot scan for this device because it is write-only.
- * So just check that the port parameters are valid and assume that
- * the device is there.
- */
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- serial_flush(serial);
- serial_close(serial);
-
- sr_spew("Conrad DIGI 35 CPU assumed at %s.", conn);
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, "Conrad", "DIGI 35 CPU", NULL)))
- return NULL;
-
- sdi->conn = serial;
- sdi->priv = NULL;
- sdi->driver = di;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "CH1")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- int ret;
- double dblval;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- ret = SR_OK;
- switch (key) {
- case SR_CONF_OUTPUT_VOLTAGE:
- dblval = g_variant_get_double(data);
- if ((dblval < 0.0) || (dblval > 35.0)) {
- sr_err("Voltage out of range (0 - 35.0)!");
- return SR_ERR_ARG;
- }
- ret = send_msg1(sdi, 'V', (int) (dblval * 10 + 0.5));
- break;
- case SR_CONF_OUTPUT_CURRENT:
- dblval = g_variant_get_double(data);
- if ((dblval < 0.01) || (dblval > 2.55)) {
- sr_err("Current out of range (0 - 2.55)!");
- return SR_ERR_ARG;
- }
- ret = send_msg1(sdi, 'C', (int) (dblval * 100 + 0.5));
- break;
- /* No SR_CONF_OUTPUT_ENABLED :-( . */
- case SR_CONF_OVER_CURRENT_PROTECTION:
- if (g_variant_get_boolean(data))
- ret = send_msg1(sdi, 'V', 900);
- else /* Constant current mode */
- ret = send_msg1(sdi, 'V', 901);
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- int ret;
-
- (void)sdi;
- (void)cg;
-
- ret = SR_OK;
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver conrad_digi_35_cpu_driver_info = {
- .name = "conrad-digi-35-cpu",
- .longname = "Conrad DIGI 35 CPU",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .config_get = NULL,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = std_serial_dev_open,
- .dev_close = std_serial_dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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/>.
- */
-
-/**
- * @file
- * <em>Conrad DIGI 35 CPU</em> power supply driver
- * @internal
- */
-
-#include "protocol.h"
-
-/**
- * Send command with parameter.
- *
- * @param[in] cmd Command
- * @param[in] param Parameter (0..999, depending on command).
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR Error.
- */
-SR_PRIV int send_msg1(const struct sr_dev_inst *sdi, char cmd, int param)
-{
- struct sr_serial_dev_inst *serial;
- char buf[5];
-
- if (!sdi || !(serial = sdi->conn))
- return SR_ERR_ARG;
-
- snprintf(buf, sizeof(buf), "%c%03d", cmd, param);
- buf[4] = '\r';
-
- sr_spew("send_msg1(): %c%c%c%c\\r", buf[0], buf[1], buf[2], buf[3]);
-
- if (serial_write(serial, buf, sizeof(buf)) == -1) {
- sr_err("Write error for cmd=%c: %d %s", cmd, errno, strerror(errno));
- return SR_ERR;
- }
-
- /*
- * Wait 50ms to ensure that the device does not swallow any of the
- * following commands.
- */
- g_usleep(50000);
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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/>.
- */
-
-/**
- * @file
- * <em>Conrad DIGI 35 CPU</em> power supply driver
- * @internal
- */
-
-#ifndef LIBSIGROK_HARDWARE_CONRAD_DIGI_35_CPU_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_CONRAD_DIGI_35_CPU_PROTOCOL_H
-
-#include <stdint.h>
-#include <errno.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "conrad-digi-35-cpu"
-
-SR_PRIV int send_msg1(const struct sr_dev_inst *sdi, char cmd, int param);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
- * Copyright (C) 2011 Olivier Fauchon <olivier@aixmarseille.com>
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <math.h>
-#ifdef _WIN32
-#include <io.h>
-#include <fcntl.h>
-#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
-#endif
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "demo"
-
-#define DEFAULT_NUM_LOGIC_CHANNELS 8
-#define DEFAULT_NUM_ANALOG_CHANNELS 4
-
-/* The size in bytes of chunks to send through the session bus. */
-#define LOGIC_BUFSIZE 4096
-/* Size of the analog pattern space per channel. */
-#define ANALOG_BUFSIZE 4096
-
-#define ANALOG_AMPLITUDE 25
-#define ANALOG_SAMPLES_PER_PERIOD 20
-
-/* Logic patterns we can generate. */
-enum {
- /**
- * Spells "sigrok" across 8 channels using '0's (with '1's as
- * "background") when displayed using the 'bits' output format.
- * The pattern is repeated every 8 channels, shifted to the right
- * in time by one bit.
- */
- PATTERN_SIGROK,
-
- /** Pseudo-random values on all channels. */
- PATTERN_RANDOM,
-
- /**
- * Incrementing number across 8 channels. The pattern is repeated
- * every 8 channels, shifted to the right in time by one bit.
- */
- PATTERN_INC,
-
- /** All channels have a low logic state. */
- PATTERN_ALL_LOW,
-
- /** All channels have a high logic state. */
- PATTERN_ALL_HIGH,
-};
-
-/* Analog patterns we can generate. */
-enum {
- /**
- * Square wave.
- */
- PATTERN_SQUARE,
- PATTERN_SINE,
- PATTERN_TRIANGLE,
- PATTERN_SAWTOOTH,
-};
-
-static const char *logic_pattern_str[] = {
- "sigrok",
- "random",
- "incremental",
- "all-low",
- "all-high",
-};
-
-static const char *analog_pattern_str[] = {
- "square",
- "sine",
- "triangle",
- "sawtooth",
-};
-
-struct analog_gen {
- int pattern;
- float pattern_data[ANALOG_BUFSIZE];
- unsigned int num_samples;
- struct sr_datafeed_analog packet;
-};
-
-/* Private, per-device-instance driver context. */
-struct dev_context {
- int pipe_fds[2];
- GIOChannel *channel;
- uint64_t cur_samplerate;
- uint64_t limit_samples;
- uint64_t limit_msec;
- uint64_t logic_counter;
- uint64_t analog_counter;
- int64_t starttime;
- uint64_t step;
- /* Logic */
- int32_t num_logic_channels;
- unsigned int logic_unitsize;
- /* There is only ever one logic channel group, so its pattern goes here. */
- uint8_t logic_pattern;
- unsigned char logic_data[LOGIC_BUFSIZE];
- /* Analog */
- int32_t num_analog_channels;
- GSList *analog_channel_groups;
-};
-
-static const int32_t scanopts[] = {
- SR_CONF_NUM_LOGIC_CHANNELS,
- SR_CONF_NUM_ANALOG_CHANNELS,
-};
-
-static const int devopts[] = {
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_DEMO_DEV,
- SR_CONF_SAMPLERATE,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
-};
-
-static const int devopts_cg[] = {
- SR_CONF_PATTERN_MODE,
-};
-
-static const uint64_t samplerates[] = {
- SR_HZ(1),
- SR_GHZ(1),
- SR_HZ(1),
-};
-
-static uint8_t pattern_sigrok[] = {
- 0x4c, 0x92, 0x92, 0x92, 0x64, 0x00, 0x00, 0x00,
- 0x82, 0xfe, 0xfe, 0x82, 0x00, 0x00, 0x00, 0x00,
- 0x7c, 0x82, 0x82, 0x92, 0x74, 0x00, 0x00, 0x00,
- 0xfe, 0x12, 0x12, 0x32, 0xcc, 0x00, 0x00, 0x00,
- 0x7c, 0x82, 0x82, 0x82, 0x7c, 0x00, 0x00, 0x00,
- 0xfe, 0x10, 0x28, 0x44, 0x82, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xbe, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-SR_PRIV struct sr_dev_driver demo_driver_info;
-static struct sr_dev_driver *di = &demo_driver_info;
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
-
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static void generate_analog_pattern(const struct sr_channel_group *cg, uint64_t sample_rate)
-{
- struct analog_gen *ag;
- double t, frequency;
- float value;
- unsigned int num_samples, i;
- int last_end;
-
- ag = cg->priv;
- num_samples = ANALOG_BUFSIZE / sizeof(float);
-
- sr_dbg("Generating %s pattern for channel group %s",
- analog_pattern_str[ag->pattern], cg->name);
-
- switch (ag->pattern) {
- case PATTERN_SQUARE:
- value = ANALOG_AMPLITUDE;
- last_end = 0;
- for (i = 0; i < num_samples; i++) {
- if (i % 5 == 0)
- value = -value;
- if (i % 10 == 0)
- last_end = i - 1;
- ag->pattern_data[i] = value;
- }
- ag->num_samples = last_end;
- break;
-
- case PATTERN_SINE:
- frequency = (double) sample_rate / ANALOG_SAMPLES_PER_PERIOD;
-
- /* Make sure the number of samples we put out is an integer
- * multiple of our period size */
- /* FIXME we actually need only one period. A ringbuffer would be
- * usefull here.*/
- while (num_samples % ANALOG_SAMPLES_PER_PERIOD != 0)
- num_samples--;
-
- for (i = 0; i < num_samples; i++) {
- t = (double) i / (double) sample_rate;
- ag->pattern_data[i] = ANALOG_AMPLITUDE *
- sin(2 * M_PI * frequency * t);
- }
-
- ag->num_samples = num_samples;
- break;
-
- case PATTERN_TRIANGLE:
- frequency = (double) sample_rate / ANALOG_SAMPLES_PER_PERIOD;
-
- while (num_samples % ANALOG_SAMPLES_PER_PERIOD != 0)
- num_samples--;
-
- for (i = 0; i < num_samples; i++) {
- t = (double) i / (double) sample_rate;
- ag->pattern_data[i] = (2 * ANALOG_AMPLITUDE / M_PI) *
- asin(sin(2 * M_PI * frequency * t));
- }
-
- ag->num_samples = num_samples;
- break;
-
- case PATTERN_SAWTOOTH:
- frequency = (double) sample_rate / ANALOG_SAMPLES_PER_PERIOD;
-
- while (num_samples % ANALOG_SAMPLES_PER_PERIOD != 0)
- num_samples--;
-
- for (i = 0; i < num_samples; i++) {
- t = (double) i / (double) sample_rate;
- ag->pattern_data[i] = 2 * ANALOG_AMPLITUDE *
- ((t * frequency) - floor(0.5f + t * frequency));
- }
-
- ag->num_samples = num_samples;
- break;
- }
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- struct sr_channel_group *cg;
- struct sr_config *src;
- struct analog_gen *ag;
- GSList *devices, *l;
- int num_logic_channels, num_analog_channels, pattern, i;
- char channel_name[16];
-
- drvc = di->priv;
-
- num_logic_channels = DEFAULT_NUM_LOGIC_CHANNELS;
- num_analog_channels = DEFAULT_NUM_ANALOG_CHANNELS;
- for (l = options; l; l = l->next) {
- src = l->data;
- switch (src->key) {
- case SR_CONF_NUM_LOGIC_CHANNELS:
- num_logic_channels = g_variant_get_int32(src->data);
- break;
- case SR_CONF_NUM_ANALOG_CHANNELS:
- num_analog_channels = g_variant_get_int32(src->data);
- break;
- }
- }
-
- devices = NULL;
- sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, "Demo device", NULL, NULL);
- if (!sdi) {
- sr_err("Device instance creation failed.");
- return NULL;
- }
- sdi->driver = di;
-
- if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
- devc->cur_samplerate = SR_KHZ(200);
- devc->limit_samples = 0;
- devc->limit_msec = 0;
- devc->step = 0;
- devc->num_logic_channels = num_logic_channels;
- devc->logic_unitsize = (devc->num_logic_channels + 7) / 8;
- devc->logic_pattern = PATTERN_SIGROK;
- devc->num_analog_channels = num_analog_channels;
- devc->analog_channel_groups = NULL;
-
- /* Logic channels, all in one channel group. */
- if (!(cg = g_try_malloc(sizeof(struct sr_channel_group))))
- return NULL;
- cg->name = g_strdup("Logic");
- cg->channels = NULL;
- cg->priv = NULL;
- for (i = 0; i < num_logic_channels; i++) {
- sprintf(channel_name, "D%d", i);
- if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, channel_name)))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- cg->channels = g_slist_append(cg->channels, ch);
- }
- sdi->channel_groups = g_slist_append(NULL, cg);
-
- /* Analog channels, channel groups and pattern generators. */
-
- pattern = 0;
- for (i = 0; i < num_analog_channels; i++) {
- sprintf(channel_name, "A%d", i);
- if (!(ch = sr_channel_new(i + num_logic_channels,
- SR_CHANNEL_ANALOG, TRUE, channel_name)))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- /* Every analog channel gets its own channel group. */
- if (!(cg = g_try_malloc(sizeof(struct sr_channel_group))))
- return NULL;
- cg->name = g_strdup(channel_name);
- cg->channels = g_slist_append(NULL, ch);
-
- /* Every channel group gets a generator struct. */
- if (!(ag = g_try_malloc(sizeof(struct analog_gen))))
- return NULL;
- ag->packet.channels = cg->channels;
- ag->packet.mq = 0;
- ag->packet.mqflags = 0;
- ag->packet.unit = SR_UNIT_VOLT;
- ag->packet.data = ag->pattern_data;
- ag->pattern = pattern;
- cg->priv = ag;
-
- sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
- devc->analog_channel_groups = g_slist_append(devc->analog_channel_groups, cg);
-
- if (++pattern == ARRAY_SIZE(analog_pattern_str))
- pattern = 0;
- }
-
- sdi->priv = devc;
- devices = g_slist_append(devices, sdi);
- drvc->instances = g_slist_append(drvc->instances, sdi);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- struct analog_gen *ag;
- int pattern;
-
- if (!sdi)
- return SR_ERR_ARG;
-
- devc = sdi->priv;
- switch (id) {
- case SR_CONF_SAMPLERATE:
- *data = g_variant_new_uint64(devc->cur_samplerate);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- *data = g_variant_new_uint64(devc->limit_msec);
- break;
- case SR_CONF_PATTERN_MODE:
- if (!cg)
- return SR_ERR_CHANNEL_GROUP;
- ch = cg->channels->data;
- if (ch->type == SR_CHANNEL_LOGIC) {
- pattern = devc->logic_pattern;
- *data = g_variant_new_string(logic_pattern_str[pattern]);
- } else if (ch->type == SR_CHANNEL_ANALOG) {
- ag = cg->priv;
- pattern = ag->pattern;
- *data = g_variant_new_string(analog_pattern_str[pattern]);
- } else
- return SR_ERR_BUG;
- break;
- case SR_CONF_NUM_LOGIC_CHANNELS:
- *data = g_variant_new_int32(devc->num_logic_channels);
- break;
- case SR_CONF_NUM_ANALOG_CHANNELS:
- *data = g_variant_new_int32(devc->num_analog_channels);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct analog_gen *ag;
- struct sr_channel *ch;
- int pattern, ret;
- unsigned int i;
- const char *stropt;
-
- devc = sdi->priv;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- ret = SR_OK;
- switch (id) {
- case SR_CONF_SAMPLERATE:
- devc->cur_samplerate = g_variant_get_uint64(data);
- sr_dbg("Setting samplerate to %" PRIu64, devc->cur_samplerate);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_msec = 0;
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64, devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- devc->limit_msec = g_variant_get_uint64(data);
- devc->limit_samples = 0;
- sr_dbg("Setting time limit to %" PRIu64"ms", devc->limit_msec);
- break;
- case SR_CONF_PATTERN_MODE:
- if (!cg)
- return SR_ERR_CHANNEL_GROUP;
- stropt = g_variant_get_string(data, NULL);
- ch = cg->channels->data;
- pattern = -1;
- if (ch->type == SR_CHANNEL_LOGIC) {
- for (i = 0; i < ARRAY_SIZE(logic_pattern_str); i++) {
- if (!strcmp(stropt, logic_pattern_str[i])) {
- pattern = i;
- break;
- }
- }
- if (pattern == -1)
- return SR_ERR_ARG;
- devc->logic_pattern = pattern;
-
- /* Might as well do this now, these are static. */
- if (pattern == PATTERN_ALL_LOW)
- memset(devc->logic_data, 0x00, LOGIC_BUFSIZE);
- else if (pattern == PATTERN_ALL_HIGH)
- memset(devc->logic_data, 0xff, LOGIC_BUFSIZE);
- sr_dbg("Setting logic pattern to %s",
- logic_pattern_str[pattern]);
- } else if (ch->type == SR_CHANNEL_ANALOG) {
- for (i = 0; i < ARRAY_SIZE(analog_pattern_str); i++) {
- if (!strcmp(stropt, analog_pattern_str[i])) {
- pattern = i;
- break;
- }
- }
- if (pattern == -1)
- return SR_ERR_ARG;
- sr_dbg("Setting analog pattern for channel group %s to %s",
- cg->name, analog_pattern_str[pattern]);
- ag = cg->priv;
- ag->pattern = pattern;
- } else
- return SR_ERR_BUG;
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct sr_channel *ch;
- GVariant *gvar;
- GVariantBuilder gvb;
-
- (void)sdi;
-
- if (key == SR_CONF_SCAN_OPTIONS) {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- scanopts, ARRAY_SIZE(scanopts), sizeof(int32_t));
- return SR_OK;
- }
-
- if (!sdi)
- return SR_ERR_ARG;
-
- if (!cg) {
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
- ARRAY_SIZE(samplerates), sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- default:
- return SR_ERR_NA;
- }
- } else {
- ch = cg->channels->data;
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(int32_t));
- break;
- case SR_CONF_PATTERN_MODE:
- if (ch->type == SR_CHANNEL_LOGIC)
- *data = g_variant_new_strv(logic_pattern_str,
- ARRAY_SIZE(logic_pattern_str));
- else if (ch->type == SR_CHANNEL_ANALOG)
- *data = g_variant_new_strv(analog_pattern_str,
- ARRAY_SIZE(analog_pattern_str));
- else
- return SR_ERR_BUG;
- break;
- default:
- return SR_ERR_NA;
- }
- }
-
- return SR_OK;
-}
-
-static void logic_generator(struct sr_dev_inst *sdi, uint64_t size)
-{
- struct dev_context *devc;
- uint64_t i, j;
- uint8_t pat;
-
- devc = sdi->priv;
-
- switch (devc->logic_pattern) {
- case PATTERN_SIGROK:
- memset(devc->logic_data, 0x00, size);
- for (i = 0; i < size; i += devc->logic_unitsize) {
- for (j = 0; j < devc->logic_unitsize; j++) {
- pat = pattern_sigrok[(devc->step + j) % sizeof(pattern_sigrok)] >> 1;
- devc->logic_data[i + j] = ~pat;
- }
- devc->step++;
- }
- break;
- case PATTERN_RANDOM:
- for (i = 0; i < size; i++)
- devc->logic_data[i] = (uint8_t)(rand() & 0xff);
- break;
- case PATTERN_INC:
- for (i = 0; i < size; i++) {
- for (j = 0; j < devc->logic_unitsize; j++) {
- devc->logic_data[i + j] = devc->step;
- }
- devc->step++;
- }
- break;
- case PATTERN_ALL_LOW:
- case PATTERN_ALL_HIGH:
- /* These were set when the pattern mode was selected. */
- break;
- default:
- sr_err("Unknown pattern: %d.", devc->logic_pattern);
- break;
- }
-}
-
-/* Callback handling data */
-static int prepare_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- struct sr_channel_group *cg;
- struct analog_gen *ag;
- GSList *l;
- uint64_t logic_todo, analog_todo, expected_samplenum, analog_samples, sending_now;
- int64_t time, elapsed;
-
- (void)fd;
- (void)revents;
-
- sdi = cb_data;
- devc = sdi->priv;
-
- /* How many "virtual" samples should we have collected by now? */
- time = g_get_monotonic_time();
- elapsed = time - devc->starttime;
- expected_samplenum = elapsed * devc->cur_samplerate / 1000000;
-
- /* Of those, how many do we still have to send? */
- logic_todo = MIN(expected_samplenum, devc->limit_samples) - devc->logic_counter;
- analog_todo = MIN(expected_samplenum, devc->limit_samples) - devc->analog_counter;
-
- while (logic_todo || analog_todo) {
- /* Logic */
- if (devc->num_logic_channels > 0 && logic_todo > 0) {
- sending_now = MIN(logic_todo,
- LOGIC_BUFSIZE / devc->logic_unitsize);
- logic_generator(sdi, sending_now * devc->logic_unitsize);
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = sending_now * devc->logic_unitsize;
- logic.unitsize = devc->logic_unitsize;
- logic.data = devc->logic_data;
- sr_session_send(sdi, &packet);
- logic_todo -= sending_now;
- devc->logic_counter += sending_now;
- }
-
- /* Analog, one channel at a time */
- if (devc->num_analog_channels > 0 && analog_todo > 0) {
- sending_now = 0;
- for (l = devc->analog_channel_groups; l; l = l->next) {
- cg = l->data;
- ag = cg->priv;
- packet.type = SR_DF_ANALOG;
- packet.payload = &ag->packet;
-
- /* FIXME we should make sure we output a whole
- * period of data before we send out again the
- * beginning of our buffer. A ring buffer would
- * help here as well */
-
- analog_samples = MIN(analog_todo, ag->num_samples);
- /* Whichever channel group gets there first. */
- sending_now = MAX(sending_now, analog_samples);
- ag->packet.num_samples = analog_samples;
- sr_session_send(sdi, &packet);
- }
- analog_todo -= sending_now;
- devc->analog_counter += sending_now;
- }
- }
-
- if (devc->logic_counter >= devc->limit_samples &&
- devc->analog_counter >= devc->limit_samples) {
- sr_dbg("Requested number of samples reached.");
- dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
-
- return TRUE;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- GSList *l;
- struct dev_context *devc;
-
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- if (devc->limit_samples == 0)
- return SR_ERR;
- devc->logic_counter = devc->analog_counter = 0;
-
- /*
- * Setting two channels connected by a pipe is a remnant from when the
- * demo driver generated data in a thread, and collected and sent the
- * data in the main program loop.
- * They are kept here because it provides a convenient way of setting
- * up a timeout-based polling mechanism.
- */
- if (pipe(devc->pipe_fds)) {
- sr_err("%s: pipe() failed", __func__);
- return SR_ERR;
- }
-
- for (l = devc->analog_channel_groups; l; l = l->next) {
- generate_analog_pattern(l->data, devc->cur_samplerate);
- }
-
- devc->channel = g_io_channel_unix_new(devc->pipe_fds[0]);
-
- g_io_channel_set_flags(devc->channel, G_IO_FLAG_NONBLOCK, NULL);
-
- /* Set channel encoding to binary (default is UTF-8). */
- g_io_channel_set_encoding(devc->channel, NULL, NULL);
-
- /* Make channels to unbuffered. */
- g_io_channel_set_buffered(devc->channel, FALSE);
-
- sr_session_source_add_channel(sdi->session, devc->channel, G_IO_IN | G_IO_ERR,
- 40, prepare_data, (void *)sdi);
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(sdi, LOG_PREFIX);
-
- /* We use this timestamp to decide how many more samples to send. */
- devc->starttime = g_get_monotonic_time();
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
-
- (void)cb_data;
-
- devc = sdi->priv;
- sr_dbg("Stopping acquisition.");
-
- sr_session_source_remove_channel(sdi->session, devc->channel);
- g_io_channel_shutdown(devc->channel, FALSE, NULL);
- g_io_channel_unref(devc->channel);
- devc->channel = NULL;
-
- /* Send last packet. */
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver demo_driver_info = {
- .name = "demo",
- .longname = "Demo driver and pattern generator",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <glib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "fluke-dmm.h"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
-};
-
-SR_PRIV struct sr_dev_driver flukedmm_driver_info;
-static struct sr_dev_driver *di = &flukedmm_driver_info;
-
-static char *scan_conn[] = {
- /* 287/289 */
- "115200/8n1",
- /* 187/189 */
- "9600/8n1",
- /* Scopemeter 190 series */
- "1200/8n1",
- NULL
-};
-
-static const struct flukedmm_profile supported_flukedmm[] = {
- { FLUKE_187, "187", 100, 1000 },
- { FLUKE_189, "189", 100, 1000 },
- { FLUKE_287, "287", 100, 1000 },
- { FLUKE_190, "199B", 1000, 3500 },
-};
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *fluke_scan(const char *conn, const char *serialcomm)
-{
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_channel *ch;
- struct sr_serial_dev_inst *serial;
- GSList *devices;
- int retry, len, i, s;
- char buf[128], *b, **tokens;
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- drvc = di->priv;
- b = buf;
- retry = 0;
- devices = NULL;
- /* We'll try the discovery sequence three times in case the device
- * is not in an idle state when we send ID. */
- while (!devices && retry < 3) {
- retry++;
- serial_flush(serial);
- if (serial_write(serial, "ID\r", 3) == -1) {
- sr_err("Unable to send ID string: %s.",
- strerror(errno));
- continue;
- }
-
- /* Response is first a CMD_ACK byte (ASCII '0' for OK,
- * or '1' to signify an error. */
- len = 128;
- serial_readline(serial, &b, &len, 150);
- if (len != 1)
- continue;
- if (buf[0] != '0')
- continue;
-
- /* If CMD_ACK was OK, ID string follows. */
- len = 128;
- serial_readline(serial, &b, &len, 850);
- if (len < 10)
- continue;
- if (strcspn(buf, ",") < 15)
- /* Looks like it's comma-separated. */
- tokens = g_strsplit(buf, ",", 3);
- else
- /* Fluke 199B, at least, uses semicolon. */
- tokens = g_strsplit(buf, ";", 3);
- if (!strncmp("FLUKE", tokens[0], 5)
- && tokens[1] && tokens[2]) {
- for (i = 0; supported_flukedmm[i].model; i++) {
- if (strcmp(supported_flukedmm[i].modelname, tokens[0] + 6))
- continue;
- /* Skip leading spaces in version number. */
- for (s = 0; tokens[1][s] == ' '; s++);
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Fluke",
- tokens[0] + 6, tokens[1] + s)))
- return NULL;
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
- devc->profile = &supported_flukedmm[i];
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
- sdi->priv = devc;
- sdi->driver = di;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- break;
- }
- }
- g_strfreev(tokens);
- if (devices)
- /* Found one. */
- break;
- }
- serial_close(serial);
- if (!devices)
- sr_serial_dev_inst_free(serial);
-
- return devices;
-}
-
-static GSList *scan(GSList *options)
-{
- struct sr_config *src;
- GSList *l, *devices;
- int i;
- const char *conn, *serialcomm;
-
- 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 (!conn)
- return NULL;
-
- if (serialcomm) {
- /* Use the provided comm specs. */
- devices = fluke_scan(conn, serialcomm);
- } else {
- for (i = 0; scan_conn[i]; i++) {
- if ((devices = fluke_scan(conn, scan_conn[i])))
- break;
- /* The Scopemeter 199B, at least, requires this
- * after all the 115k/9.6k confusion. */
- g_usleep(5000);
- }
- }
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- switch (id) {
- case SR_CONF_LIMIT_MSEC:
- /* TODO: not yet implemented */
- if (g_variant_get_uint64(data) == 0) {
- sr_err("LIMIT_MSEC can't be 0.");
- return SR_ERR;
- }
- devc->limit_msec = g_variant_get_uint64(data);
- sr_dbg("Setting time limit to %" PRIu64 "ms.",
- devc->limit_msec);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- devc->cb_data = cb_data;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Poll every 100ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 50,
- fluke_receive_data, (void *)sdi);
-
- if (serial_write(serial, "QM\r", 3) == -1) {
- sr_err("Unable to send QM: %s.", strerror(errno));
- return SR_ERR;
- }
- devc->cmd_sent_at = g_get_monotonic_time() / 1000;
- devc->expect_response = TRUE;
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
- sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver flukedmm_driver_info = {
- .name = "fluke-dmm",
- .longname = "Fluke 18x/28x series DMMs",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .config_get = NULL,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = std_serial_dev_open,
- .dev_close = std_serial_dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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_FLUKE_DMM_FLUKE_DMM_H
-#define LIBSIGROK_HARDWARE_FLUKE_DMM_FLUKE_DMM_H
-
-#define LOG_PREFIX "fluke-dmm"
-
-#define FLUKEDMM_BUFSIZE 256
-
-/* Supported models */
-enum {
- FLUKE_187 = 1,
- FLUKE_189,
- FLUKE_287,
- FLUKE_190,
-};
-
-/* Supported device profiles */
-struct flukedmm_profile {
- int model;
- const char *modelname;
- /* How often to poll, in ms. */
- int poll_period;
- /* If no response received, how long to wait before retrying. */
- int timeout;
-};
-
-/* Private, per-device-instance driver context. */
-struct dev_context {
- const struct flukedmm_profile *profile;
- uint64_t limit_samples;
- uint64_t limit_msec;
-
- /* Opaque pointer passed in by the frontend. */
- void *cb_data;
-
- /* Runtime. */
- uint64_t num_samples;
- char buf[FLUKEDMM_BUFSIZE];
- int buflen;
- int64_t cmd_sent_at;
- int expect_response;
- int meas_type;
- int is_relative;
- int mq;
- int unit;
- int mqflags;
-};
-
-SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdlib.h>
-#include <math.h>
-#include <string.h>
-#include <errno.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "fluke-dmm.h"
-
-static struct sr_datafeed_analog *handle_qm_18x(const struct sr_dev_inst *sdi,
- char **tokens)
-{
- struct sr_datafeed_analog *analog;
- float fvalue;
- char *e, *u;
- gboolean is_oor;
-
- if (strcmp(tokens[0], "QM") || !tokens[1])
- return NULL;
-
- if ((e = strstr(tokens[1], "Out of range"))) {
- is_oor = TRUE;
- fvalue = -1;
- while(*e && *e != '.')
- e++;
- } else {
- is_oor = FALSE;
- /* Delimit the float, since sr_atof_ascii() wants only
- * a valid float here. */
- e = tokens[1];
- while(*e && *e != ' ')
- e++;
- *e++ = '\0';
- if (sr_atof_ascii(tokens[1], &fvalue) != SR_OK || fvalue == 0.0) {
- /* Happens all the time, when switching modes. */
- sr_dbg("Invalid float.");
- return NULL;
- }
- }
- while(*e && *e == ' ')
- e++;
-
- if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog))))
- return NULL;
- if (!(analog->data = g_try_malloc(sizeof(float))))
- return NULL;
- analog->channels = sdi->channels;
- analog->num_samples = 1;
- if (is_oor)
- *analog->data = NAN;
- else
- *analog->data = fvalue;
- analog->mq = -1;
-
- if ((u = strstr(e, "V DC")) || (u = strstr(e, "V AC"))) {
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- if (!is_oor && e[0] == 'm')
- *analog->data /= 1000;
- /* This catches "V AC", "V DC" and "V AC+DC". */
- if (strstr(u, "AC"))
- analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
- if (strstr(u, "DC"))
- analog->mqflags |= SR_MQFLAG_DC;
- } else if ((u = strstr(e, "dBV")) || (u = strstr(e, "dBm"))) {
- analog->mq = SR_MQ_VOLTAGE;
- if (u[2] == 'm')
- analog->unit = SR_UNIT_DECIBEL_MW;
- else
- analog->unit = SR_UNIT_DECIBEL_VOLT;
- analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
- } else if ((u = strstr(e, "Ohms"))) {
- analog->mq = SR_MQ_RESISTANCE;
- analog->unit = SR_UNIT_OHM;
- if (is_oor)
- *analog->data = INFINITY;
- else if (e[0] == 'k')
- *analog->data *= 1000;
- else if (e[0] == 'M')
- *analog->data *= 1000000;
- } else if (!strcmp(e, "nS")) {
- analog->mq = SR_MQ_CONDUCTANCE;
- analog->unit = SR_UNIT_SIEMENS;
- *analog->data /= 1e+9;
- } else if ((u = strstr(e, "Farads"))) {
- analog->mq = SR_MQ_CAPACITANCE;
- analog->unit = SR_UNIT_FARAD;
- if (!is_oor) {
- if (e[0] == 'm')
- *analog->data /= 1e+3;
- else if (e[0] == 'u')
- *analog->data /= 1e+6;
- else if (e[0] == 'n')
- *analog->data /= 1e+9;
- }
- } else if ((u = strstr(e, "Deg C")) || (u = strstr(e, "Deg F"))) {
- analog->mq = SR_MQ_TEMPERATURE;
- if (u[4] == 'C')
- analog->unit = SR_UNIT_CELSIUS;
- else
- analog->unit = SR_UNIT_FAHRENHEIT;
- } else if ((u = strstr(e, "A AC")) || (u = strstr(e, "A DC"))) {
- analog->mq = SR_MQ_CURRENT;
- analog->unit = SR_UNIT_AMPERE;
- /* This catches "A AC", "A DC" and "A AC+DC". */
- if (strstr(u, "AC"))
- analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
- if (strstr(u, "DC"))
- analog->mqflags |= SR_MQFLAG_DC;
- if (!is_oor) {
- if (e[0] == 'm')
- *analog->data /= 1e+3;
- else if (e[0] == 'u')
- *analog->data /= 1e+6;
- }
- } else if ((u = strstr(e, "Hz"))) {
- analog->mq = SR_MQ_FREQUENCY;
- analog->unit = SR_UNIT_HERTZ;
- if (e[0] == 'k')
- *analog->data *= 1e+3;
- } else if (!strcmp(e, "%")) {
- analog->mq = SR_MQ_DUTY_CYCLE;
- analog->unit = SR_UNIT_PERCENTAGE;
- } else if ((u = strstr(e, "ms"))) {
- analog->mq = SR_MQ_PULSE_WIDTH;
- analog->unit = SR_UNIT_SECOND;
- *analog->data /= 1e+3;
- }
-
- if (analog->mq == -1) {
- /* Not a valid measurement. */
- g_free(analog->data);
- g_free(analog);
- analog = NULL;
- }
-
- return analog;
-}
-
-static struct sr_datafeed_analog *handle_qm_28x(const struct sr_dev_inst *sdi,
- char **tokens)
-{
- struct sr_datafeed_analog *analog;
- float fvalue;
-
- if (!tokens[1])
- return NULL;
-
- if (sr_atof_ascii(tokens[0], &fvalue) != SR_OK || fvalue == 0.0) {
- sr_err("Invalid float '%s'.", tokens[0]);
- return NULL;
- }
-
- if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog))))
- return NULL;
- if (!(analog->data = g_try_malloc(sizeof(float))))
- return NULL;
- analog->channels = sdi->channels;
- analog->num_samples = 1;
- *analog->data = fvalue;
- analog->mq = -1;
-
- if (!strcmp(tokens[1], "VAC") || !strcmp(tokens[1], "VDC")) {
- analog->mq = SR_MQ_VOLTAGE;
- analog->unit = SR_UNIT_VOLT;
- if (!strcmp(tokens[2], "NORMAL")) {
- if (tokens[1][1] == 'A') {
- analog->mqflags |= SR_MQFLAG_AC;
- analog->mqflags |= SR_MQFLAG_RMS;
- } else
- analog->mqflags |= SR_MQFLAG_DC;
- } else if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
- *analog->data = NAN;
- } else
- analog->mq = -1;
- } else if (!strcmp(tokens[1], "dBV") || !strcmp(tokens[1], "dBm")) {
- analog->mq = SR_MQ_VOLTAGE;
- if (tokens[1][2] == 'm')
- analog->unit = SR_UNIT_DECIBEL_MW;
- else
- analog->unit = SR_UNIT_DECIBEL_VOLT;
- analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
- } else if (!strcmp(tokens[1], "CEL") || !strcmp(tokens[1], "FAR")) {
- if (!strcmp(tokens[2], "NORMAL")) {
- analog->mq = SR_MQ_TEMPERATURE;
- if (tokens[1][0] == 'C')
- analog->unit = SR_UNIT_CELSIUS;
- else
- analog->unit = SR_UNIT_FAHRENHEIT;
- }
- } else if (!strcmp(tokens[1], "OHM")) {
- if (!strcmp(tokens[3], "NONE")) {
- analog->mq = SR_MQ_RESISTANCE;
- analog->unit = SR_UNIT_OHM;
- if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
- *analog->data = INFINITY;
- } else if (strcmp(tokens[2], "NORMAL"))
- analog->mq = -1;
- } else if (!strcmp(tokens[3], "OPEN_CIRCUIT")) {
- analog->mq = SR_MQ_CONTINUITY;
- analog->unit = SR_UNIT_BOOLEAN;
- *analog->data = 0.0;
- } else if (!strcmp(tokens[3], "SHORT_CIRCUIT")) {
- analog->mq = SR_MQ_CONTINUITY;
- analog->unit = SR_UNIT_BOOLEAN;
- *analog->data = 1.0;
- }
- } else if (!strcmp(tokens[1], "F")
- && !strcmp(tokens[2], "NORMAL")
- && !strcmp(tokens[3], "NONE")) {
- analog->mq = SR_MQ_CAPACITANCE;
- analog->unit = SR_UNIT_FARAD;
- } else if (!strcmp(tokens[1], "AAC") || !strcmp(tokens[1], "ADC")) {
- analog->mq = SR_MQ_CURRENT;
- analog->unit = SR_UNIT_AMPERE;
- if (!strcmp(tokens[2], "NORMAL")) {
- if (tokens[1][1] == 'A') {
- analog->mqflags |= SR_MQFLAG_AC;
- analog->mqflags |= SR_MQFLAG_RMS;
- } else
- analog->mqflags |= SR_MQFLAG_DC;
- } else if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
- *analog->data = NAN;
- } else
- analog->mq = -1;
- } if (!strcmp(tokens[1], "Hz") && !strcmp(tokens[2], "NORMAL")) {
- analog->mq = SR_MQ_FREQUENCY;
- analog->unit = SR_UNIT_HERTZ;
- } else if (!strcmp(tokens[1], "PCT") && !strcmp(tokens[2], "NORMAL")) {
- analog->mq = SR_MQ_DUTY_CYCLE;
- analog->unit = SR_UNIT_PERCENTAGE;
- } else if (!strcmp(tokens[1], "S") && !strcmp(tokens[2], "NORMAL")) {
- analog->mq = SR_MQ_PULSE_WIDTH;
- analog->unit = SR_UNIT_SECOND;
- } else if (!strcmp(tokens[1], "SIE") && !strcmp(tokens[2], "NORMAL")) {
- analog->mq = SR_MQ_CONDUCTANCE;
- analog->unit = SR_UNIT_SIEMENS;
- }
-
- if (analog->mq == -1) {
- /* Not a valid measurement. */
- g_free(analog->data);
- g_free(analog);
- analog = NULL;
- }
-
- return analog;
-}
-
-static void handle_qm_19x_meta(const struct sr_dev_inst *sdi, char **tokens)
-{
- struct dev_context *devc;
- int meas_type, meas_unit, meas_char, i;
-
- /* Make sure we have 7 valid tokens. */
- for (i = 0; tokens[i] && i < 7; i++);
- if (i != 7)
- return;
-
- if (strcmp(tokens[1], "1"))
- /* Invalid measurement. */
- return;
-
- if (strcmp(tokens[2], "3"))
- /* Only interested in input from the meter mode source. */
- return;
-
- devc = sdi->priv;
-
- /* Measurement type 11 == absolute, 19 = relative */
- meas_type = strtol(tokens[0], NULL, 10);
- if (meas_type != 11 && meas_type != 19)
- /* Device is in some mode we don't support. */
- return;
-
- /* We might get metadata for absolute and relative mode (if the device
- * is in relative mode). In that case, relative takes precedence. */
- if (meas_type == 11 && devc->meas_type == 19)
- return;
-
- meas_unit = strtol(tokens[3], NULL, 10);
- if (meas_unit == 0)
- /* Device is turned off. Really. */
- return;
- meas_char = strtol(tokens[4], NULL, 10);
-
- devc->mq = devc->unit = -1;
- devc->mqflags = 0;
- switch (meas_unit) {
- case 1:
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_VOLT;
- if (meas_char == 1)
- devc->mqflags |= SR_MQFLAG_DC;
- else if (meas_char == 2)
- devc->mqflags |= SR_MQFLAG_AC;
- else if (meas_char == 3)
- devc->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC;
- else if (meas_char == 15)
- devc->mqflags |= SR_MQFLAG_DIODE;
- break;
- case 2:
- devc->mq = SR_MQ_CURRENT;
- devc->unit = SR_UNIT_AMPERE;
- if (meas_char == 1)
- devc->mqflags |= SR_MQFLAG_DC;
- else if (meas_char == 2)
- devc->mqflags |= SR_MQFLAG_AC;
- else if (meas_char == 3)
- devc->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC;
- break;
- case 3:
- if (meas_char == 1) {
- devc->mq = SR_MQ_RESISTANCE;
- devc->unit = SR_UNIT_OHM;
- } else if (meas_char == 16) {
- devc->mq = SR_MQ_CONTINUITY;
- devc->unit = SR_UNIT_BOOLEAN;
- }
- break;
- case 12:
- devc->mq = SR_MQ_TEMPERATURE;
- devc->unit = SR_UNIT_CELSIUS;
- break;
- case 13:
- devc->mq = SR_MQ_TEMPERATURE;
- devc->unit = SR_UNIT_FAHRENHEIT;
- break;
- default:
- sr_dbg("unknown unit: %d", meas_unit);
- }
- if (devc->mq == -1 && devc->unit == -1)
- return;
-
- /* If we got here, we know how to interpret the measurement. */
- devc->meas_type = meas_type;
- if (meas_type == 11)
- /* Absolute meter reading. */
- devc->is_relative = FALSE;
- else if (!strcmp(tokens[0], "19"))
- /* Relative meter reading. */
- devc->is_relative = TRUE;
-
-}
-
-static void handle_qm_19x_data(const struct sr_dev_inst *sdi, char **tokens)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- float fvalue;
-
- if (!strcmp(tokens[0], "9.9E+37")) {
- /* An invalid measurement shows up on the display as "OL", but
- * comes through like this. Since comparing 38-digit floats
- * is rather problematic, we'll cut through this here. */
- fvalue = NAN;
- } else {
- if (sr_atof_ascii(tokens[0], &fvalue) != SR_OK || fvalue == 0.0) {
- sr_err("Invalid float '%s'.", tokens[0]);
- return;
- }
- }
-
- devc = sdi->priv;
- if (devc->mq == -1 || devc->unit == -1)
- /* Don't have valid metadata yet. */
- return;
-
-
- if (devc->mq == SR_MQ_RESISTANCE && isnan(fvalue))
- fvalue = INFINITY;
- else if (devc->mq == SR_MQ_CONTINUITY) {
- if (isnan(fvalue))
- fvalue = 0.0;
- else
- fvalue = 1.0;
- }
-
- analog.channels = sdi->channels;
- analog.num_samples = 1;
- analog.data = &fvalue;
- analog.mq = devc->mq;
- analog.unit = devc->unit;
- analog.mqflags = 0;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
- devc->num_samples++;
-
-}
-
-static void handle_line(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog *analog;
- int num_tokens, n, i;
- char cmd[16], **tokens;
-
- devc = sdi->priv;
- serial = sdi->conn;
- sr_spew("Received line '%s' (%d).", devc->buf, devc->buflen);
-
- if (devc->buflen == 1) {
- if (devc->buf[0] != '0') {
- /* Not just a CMD_ACK from the query command. */
- sr_dbg("Got CMD_ACK '%c'.", devc->buf[0]);
- devc->expect_response = FALSE;
- }
- devc->buflen = 0;
- return;
- }
-
- analog = NULL;
- tokens = g_strsplit(devc->buf, ",", 0);
- if (tokens[0]) {
- if (devc->profile->model == FLUKE_187 || devc->profile->model == FLUKE_189) {
- devc->expect_response = FALSE;
- analog = handle_qm_18x(sdi, tokens);
- } else if (devc->profile->model == FLUKE_287) {
- devc->expect_response = FALSE;
- analog = handle_qm_28x(sdi, tokens);
- } else if (devc->profile->model == FLUKE_190) {
- devc->expect_response = FALSE;
- for (num_tokens = 0; tokens[num_tokens]; num_tokens++);
- if (num_tokens >= 7) {
- /* Response to QM: this is a comma-separated list of
- * fields with metadata about the measurement. This
- * format can return multiple sets of metadata,
- * split into sets of 7 tokens each. */
- devc->meas_type = 0;
- for (i = 0; i < num_tokens; i += 7)
- handle_qm_19x_meta(sdi, tokens + i);
- if (devc->meas_type) {
- /* Slip the request in now, before the main
- * timer loop asks for metadata again. */
- n = sprintf(cmd, "QM %d\r", devc->meas_type);
- if (serial_write(serial, cmd, n) == -1)
- sr_err("Unable to send QM (measurement): %s.",
- strerror(errno));
- }
- } else {
- /* Response to QM <n> measurement request. */
- handle_qm_19x_data(sdi, tokens);
- }
- }
- }
- g_strfreev(tokens);
- devc->buflen = 0;
-
- if (analog) {
- /* Got a measurement. */
- packet.type = SR_DF_ANALOG;
- packet.payload = analog;
- sr_session_send(devc->cb_data, &packet);
- devc->num_samples++;
- g_free(analog->data);
- g_free(analog);
- }
-
-}
-
-SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- int len;
- int64_t now, elapsed;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- serial = sdi->conn;
- if (revents == G_IO_IN) {
- /* Serial data arrived. */
- while(FLUKEDMM_BUFSIZE - devc->buflen - 1 > 0) {
- len = serial_read(serial, devc->buf + devc->buflen, 1);
- if (len < 1)
- break;
- devc->buflen++;
- *(devc->buf + devc->buflen) = '\0';
- if (*(devc->buf + devc->buflen - 1) == '\r') {
- *(devc->buf + --devc->buflen) = '\0';
- handle_line(sdi);
- break;
- }
- }
- }
-
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
-
- now = g_get_monotonic_time() / 1000;
- elapsed = now - devc->cmd_sent_at;
- /* Send query command at poll_period interval, or after 1 second
- * has elapsed. This will make it easier to recover from any
- * out-of-sync or temporary disconnect issues. */
- if ((devc->expect_response == FALSE && elapsed > devc->profile->poll_period)
- || elapsed > devc->profile->timeout) {
- if (serial_write(serial, "QM\r", 3) == -1)
- sr_err("Unable to send QM: %s.", strerror(errno));
- devc->cmd_sent_at = now;
- devc->expect_response = TRUE;
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 "protocol.h"
-
-static const struct fx2lafw_profile supported_fx2[] = {
- /*
- * CWAV USBee AX
- * EE Electronics ESLA201A
- * ARMFLY AX-Pro
- */
- { 0x08a9, 0x0014, "CWAV", "USBee AX", NULL,
- FIRMWARE_DIR "/fx2lafw-cwav-usbeeax.fw",
- 0, NULL, NULL},
- /*
- * CWAV USBee DX
- * XZL-Studio DX
- */
- { 0x08a9, 0x0015, "CWAV", "USBee DX", NULL,
- FIRMWARE_DIR "/fx2lafw-cwav-usbeedx.fw",
- DEV_CAPS_16BIT, NULL, NULL },
-
- /*
- * CWAV USBee SX
- */
- { 0x08a9, 0x0009, "CWAV", "USBee SX", NULL,
- FIRMWARE_DIR "/fx2lafw-cwav-usbeesx.fw",
- 0, NULL, NULL},
-
- /*
- * Saleae Logic
- * EE Electronics ESLA100
- * Robomotic MiniLogic
- * Robomotic BugLogic 3
- */
- { 0x0925, 0x3881, "Saleae", "Logic", NULL,
- FIRMWARE_DIR "/fx2lafw-saleae-logic.fw",
- 0, NULL, NULL},
-
- /*
- * Default Cypress FX2 without EEPROM, e.g.:
- * Lcsoft Mini Board
- * Braintechnology USB Interface V2.x
- */
- { 0x04B4, 0x8613, "Cypress", "FX2", NULL,
- FIRMWARE_DIR "/fx2lafw-cypress-fx2.fw",
- DEV_CAPS_16BIT, NULL, NULL },
-
- /*
- * Braintechnology USB-LPS
- */
- { 0x16d0, 0x0498, "Braintechnology", "USB-LPS", NULL,
- FIRMWARE_DIR "/fx2lafw-braintechnology-usb-lps.fw",
- DEV_CAPS_16BIT, NULL, NULL },
-
- { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
-};
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_TRIGGER_MATCH,
- SR_CONF_SAMPLERATE,
-
- /* These are really implemented in the driver, not the hardware. */
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_CONTINUOUS,
-};
-
-static const char *channel_names[] = {
- "0", "1", "2", "3", "4", "5", "6", "7",
- "8", "9", "10", "11", "12", "13", "14", "15",
- NULL,
-};
-
-static const int32_t soft_trigger_matches[] = {
- SR_TRIGGER_ZERO,
- SR_TRIGGER_ONE,
- SR_TRIGGER_RISING,
- SR_TRIGGER_FALLING,
- SR_TRIGGER_EDGE,
-};
-
-static const uint64_t samplerates[] = {
- SR_KHZ(20),
- SR_KHZ(25),
- SR_KHZ(50),
- SR_KHZ(100),
- SR_KHZ(200),
- SR_KHZ(250),
- SR_KHZ(500),
- SR_MHZ(1),
- SR_MHZ(2),
- SR_MHZ(3),
- SR_MHZ(4),
- SR_MHZ(6),
- SR_MHZ(8),
- SR_MHZ(12),
- SR_MHZ(16),
- SR_MHZ(24),
-};
-
-SR_PRIV struct sr_dev_driver fx2lafw_driver_info;
-static struct sr_dev_driver *di = &fx2lafw_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_usb_dev_inst *usb;
- struct sr_channel *ch;
- struct sr_config *src;
- const struct fx2lafw_profile *prof;
- GSList *l, *devices, *conn_devices;
- struct libusb_device_descriptor des;
- libusb_device **devlist;
- struct libusb_device_handle *hdl;
- int devcnt, num_logic_channels, ret, i, j;
- const char *conn;
- char manufacturer[64], product[64];
-
- drvc = di->priv;
-
- 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)
- conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
- else
- conn_devices = NULL;
-
- /* Find all fx2lafw compatible devices and upload firmware to them. */
- devices = NULL;
- libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- for (i = 0; devlist[i]; i++) {
- if (conn) {
- usb = NULL;
- for (l = conn_devices; l; l = l->next) {
- usb = l->data;
- if (usb->bus == libusb_get_bus_number(devlist[i])
- && usb->address == libusb_get_device_address(devlist[i]))
- break;
- }
- if (!l)
- /* This device matched none of the ones that
- * matched the conn specification. */
- continue;
- }
-
- if ((ret = libusb_get_device_descriptor( devlist[i], &des)) != 0) {
- sr_warn("Failed to get device descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
-
- if ((ret = libusb_open(devlist[i], &hdl)) < 0)
- continue;
-
- if (des.iManufacturer == 0) {
- manufacturer[0] = '\0';
- } else if ((ret = libusb_get_string_descriptor_ascii(hdl,
- des.iManufacturer, (unsigned char *) manufacturer,
- sizeof(manufacturer))) < 0) {
- sr_warn("Failed to get manufacturer string descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
-
- if (des.iProduct == 0) {
- product[0] = '\0';
- } else if ((ret = libusb_get_string_descriptor_ascii(hdl,
- des.iProduct, (unsigned char *) product,
- sizeof(product))) < 0) {
- sr_warn("Failed to get product string descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
-
- libusb_close(hdl);
-
- prof = NULL;
- for (j = 0; supported_fx2[j].vid; j++) {
- if (des.idVendor == supported_fx2[j].vid &&
- des.idProduct == supported_fx2[j].pid &&
- (!supported_fx2[j].usb_manufacturer ||
- !strcmp(manufacturer, supported_fx2[j].usb_manufacturer)) &&
- (!supported_fx2[j].usb_manufacturer ||
- !strcmp(product, supported_fx2[j].usb_product))) {
- prof = &supported_fx2[j];
- break;
- }
- }
-
- /* Skip if the device was not found. */
- if (!prof)
- continue;
-
- devcnt = g_slist_length(drvc->instances);
- sdi = sr_dev_inst_new(devcnt, SR_ST_INITIALIZING,
- prof->vendor, prof->model, prof->model_version);
- if (!sdi)
- return NULL;
- sdi->driver = di;
-
- /* Fill in channellist according to this device's profile. */
- num_logic_channels = prof->dev_caps & DEV_CAPS_16BIT ? 16 : 8;
- for (j = 0; j < num_logic_channels; j++) {
- if (!(ch = sr_channel_new(j, SR_CHANNEL_LOGIC, TRUE,
- channel_names[j])))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- devc = fx2lafw_dev_new();
- devc->profile = prof;
- sdi->priv = devc;
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- if (fx2lafw_check_conf_profile(devlist[i])) {
- /* Already has the firmware, so fix the new address. */
- sr_dbg("Found an fx2lafw device.");
- sdi->status = SR_ST_INACTIVE;
- sdi->inst_type = SR_INST_USB;
- sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
- libusb_get_device_address(devlist[i]), NULL);
- } else {
- if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION,
- prof->firmware) == SR_OK)
- /* Store when this device's FW was updated. */
- devc->fw_updated = g_get_monotonic_time();
- else
- sr_err("Firmware upload failed for "
- "device %d.", devcnt);
- sdi->inst_type = SR_INST_USB;
- sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
- 0xff, NULL);
- }
- }
- libusb_free_device_list(devlist, 1);
- g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
- struct dev_context *devc;
- int ret;
- int64_t timediff_us, timediff_ms;
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- /*
- * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS
- * milliseconds for the FX2 to renumerate.
- */
- ret = SR_ERR;
- if (devc->fw_updated > 0) {
- sr_info("Waiting for device to reset.");
- /* Takes >= 300ms for the FX2 to be gone from the USB bus. */
- g_usleep(300 * 1000);
- timediff_ms = 0;
- while (timediff_ms < MAX_RENUM_DELAY_MS) {
- if ((ret = fx2lafw_dev_open(sdi, di)) == SR_OK)
- break;
- g_usleep(100 * 1000);
-
- timediff_us = g_get_monotonic_time() - devc->fw_updated;
- timediff_ms = timediff_us / 1000;
- sr_spew("Waited %" PRIi64 "ms.", timediff_ms);
- }
- if (ret != SR_OK) {
- sr_err("Device failed to renumerate.");
- return SR_ERR;
- }
- sr_info("Device came back after %" PRIi64 "ms.", timediff_ms);
- } else {
- sr_info("Firmware upload was not needed.");
- ret = fx2lafw_dev_open(sdi, di);
- }
-
- if (ret != SR_OK) {
- sr_err("Unable to open device.");
- return SR_ERR;
- }
-
- ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
- if (ret != 0) {
- switch (ret) {
- case LIBUSB_ERROR_BUSY:
- sr_err("Unable to claim USB interface. Another "
- "program or driver has already claimed it.");
- break;
- case LIBUSB_ERROR_NO_DEVICE:
- sr_err("Device has been disconnected.");
- break;
- default:
- sr_err("Unable to claim interface: %s.",
- libusb_error_name(ret));
- break;
- }
-
- return SR_ERR;
- }
-
- if (devc->cur_samplerate == 0) {
- /* Samplerate hasn't been set; default to the slowest one. */
- devc->cur_samplerate = samplerates[0];
- }
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
-
- usb = sdi->conn;
- if (usb->devhdl == NULL)
- return SR_ERR;
-
- sr_info("fx2lafw: Closing device %d on %d.%d interface %d.",
- sdi->index, usb->bus, usb->address, USB_INTERFACE);
- libusb_release_interface(usb->devhdl, USB_INTERFACE);
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- int ret;
- struct drv_context *drvc;
-
- if (!(drvc = di->priv))
- return SR_OK;
-
- ret = std_dev_clear(di, NULL);
-
- g_free(drvc);
- di->priv = NULL;
-
- return ret;
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- char str[128];
-
- (void)cg;
-
- if (!sdi)
- return SR_ERR_ARG;
-
- devc = sdi->priv;
-
- switch (id) {
- case SR_CONF_CONN:
- if (!sdi->conn)
- return SR_ERR_ARG;
- usb = sdi->conn;
- if (usb->address == 255)
- /* Device still needs to re-enumerate after firmware
- * upload, so we don't know its (future) address. */
- return SR_ERR;
- snprintf(str, 128, "%d.%d", usb->bus, usb->address);
- *data = g_variant_new_string(str);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_SAMPLERATE:
- *data = g_variant_new_uint64(devc->cur_samplerate);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- int ret;
-
- (void)cg;
-
- if (!sdi)
- return SR_ERR_ARG;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR;
-
- devc = sdi->priv;
-
- ret = SR_OK;
-
- switch (id)
- {
- case SR_CONF_SAMPLERATE:
- devc->cur_samplerate = g_variant_get_uint64(data);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- GVariant *gvar;
- GVariantBuilder gvb;
-
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
- ARRAY_SIZE(samplerates), sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TRIGGER_MATCH:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- soft_trigger_matches, ARRAY_SIZE(soft_trigger_matches),
- sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int receive_data(int fd, int revents, void *cb_data)
-{
- struct timeval tv;
- struct drv_context *drvc;
-
- (void)fd;
- (void)revents;
- (void)cb_data;
-
- drvc = di->priv;
-
- tv.tv_sec = tv.tv_usec = 0;
- libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
-
- return TRUE;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct drv_context *drvc;
- struct sr_usb_dev_inst *usb;
- struct sr_trigger *trigger;
- struct libusb_transfer *transfer;
- unsigned int i, timeout, num_transfers;
- int ret;
- unsigned char *buf;
- size_t size;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- drvc = di->priv;
- devc = sdi->priv;
- usb = sdi->conn;
-
- devc->cb_data = cb_data;
- devc->sent_samples = 0;
- devc->acq_aborted = FALSE;
- devc->empty_transfer_count = 0;
-
- if ((trigger = sr_session_trigger_get(sdi->session))) {
- devc->stl = soft_trigger_logic_new(sdi, trigger);
- devc->trigger_fired = FALSE;
- } else
- devc->trigger_fired = TRUE;
-
- timeout = fx2lafw_get_timeout(devc);
- num_transfers = fx2lafw_get_number_of_transfers(devc);
- size = fx2lafw_get_buffer_size(devc);
- devc->submitted_transfers = 0;
-
- devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers);
- if (!devc->transfers) {
- sr_err("USB transfers malloc failed.");
- return SR_ERR_MALLOC;
- }
-
- devc->num_transfers = num_transfers;
- for (i = 0; i < num_transfers; i++) {
- if (!(buf = g_try_malloc(size))) {
- sr_err("USB transfer buffer malloc failed.");
- return SR_ERR_MALLOC;
- }
- transfer = libusb_alloc_transfer(0);
- libusb_fill_bulk_transfer(transfer, usb->devhdl,
- 2 | LIBUSB_ENDPOINT_IN, buf, size,
- fx2lafw_receive_transfer, (void *)sdi, timeout);
- if ((ret = libusb_submit_transfer(transfer)) != 0) {
- sr_err("Failed to submit transfer: %s.",
- libusb_error_name(ret));
- libusb_free_transfer(transfer);
- g_free(buf);
- fx2lafw_abort_acquisition(devc);
- return SR_ERR;
- }
- devc->transfers[i] = transfer;
- devc->submitted_transfers++;
- }
-
- devc->ctx = drvc->sr_ctx;
-
- usb_source_add(sdi->session, devc->ctx, timeout, receive_data, NULL);
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- if ((ret = fx2lafw_command_start_acquisition(sdi)) != SR_OK) {
- fx2lafw_abort_acquisition(devc);
- return ret;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- (void)cb_data;
-
- fx2lafw_abort_acquisition(sdi->priv);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver fx2lafw_driver_info = {
- .name = "fx2lafw",
- .longname = "fx2lafw (generic driver for FX2 based LAs)",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 "protocol.h"
-
-/* Protocol commands */
-#define CMD_GET_FW_VERSION 0xb0
-#define CMD_START 0xb1
-#define CMD_GET_REVID_VERSION 0xb2
-
-#define CMD_START_FLAGS_WIDE_POS 5
-#define CMD_START_FLAGS_CLK_SRC_POS 6
-
-#define CMD_START_FLAGS_SAMPLE_8BIT (0 << CMD_START_FLAGS_WIDE_POS)
-#define CMD_START_FLAGS_SAMPLE_16BIT (1 << CMD_START_FLAGS_WIDE_POS)
-
-#define CMD_START_FLAGS_CLK_30MHZ (0 << CMD_START_FLAGS_CLK_SRC_POS)
-#define CMD_START_FLAGS_CLK_48MHZ (1 << CMD_START_FLAGS_CLK_SRC_POS)
-
-#pragma pack(push, 1)
-
-struct version_info {
- uint8_t major;
- uint8_t minor;
-};
-
-struct cmd_start_acquisition {
- uint8_t flags;
- uint8_t sample_delay_h;
- uint8_t sample_delay_l;
-};
-
-#pragma pack(pop)
-
-static int command_get_fw_version(libusb_device_handle *devhdl,
- struct version_info *vi)
-{
- int ret;
-
- ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
- LIBUSB_ENDPOINT_IN, CMD_GET_FW_VERSION, 0x0000, 0x0000,
- (unsigned char *)vi, sizeof(struct version_info), 100);
-
- if (ret < 0) {
- sr_err("Unable to get version info: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int command_get_revid_version(struct sr_dev_inst *sdi, uint8_t *revid)
-{
- struct sr_usb_dev_inst *usb = sdi->conn;
- libusb_device_handle *devhdl = usb->devhdl;
- int ret;
-
- ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
- LIBUSB_ENDPOINT_IN, CMD_GET_REVID_VERSION, 0x0000, 0x0000,
- revid, 1, 100);
-
- if (ret < 0) {
- sr_err("Unable to get REVID: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
- struct sr_usb_dev_inst *usb = sdi->conn;
- libusb_device_handle *devhdl = usb->devhdl;
- uint64_t samplerate = devc->cur_samplerate;
- gboolean samplewide = devc->sample_wide;
- struct cmd_start_acquisition cmd = { 0 };
- int delay = 0, ret;
-
- /* Compute the sample rate. */
- if (samplewide && samplerate > MAX_16BIT_SAMPLE_RATE) {
- sr_err("Unable to sample at %" PRIu64 "Hz "
- "when collecting 16-bit samples.", samplerate);
- return SR_ERR;
- }
-
- if ((SR_MHZ(48) % samplerate) == 0) {
- cmd.flags = CMD_START_FLAGS_CLK_48MHZ;
- delay = SR_MHZ(48) / samplerate - 1;
- if (delay > MAX_SAMPLE_DELAY)
- delay = 0;
- }
-
- if (delay == 0 && (SR_MHZ(30) % samplerate) == 0) {
- cmd.flags = CMD_START_FLAGS_CLK_30MHZ;
- delay = SR_MHZ(30) / samplerate - 1;
- }
-
- sr_info("GPIF delay = %d, clocksource = %sMHz.", delay,
- (cmd.flags & CMD_START_FLAGS_CLK_48MHZ) ? "48" : "30");
-
- if (delay <= 0 || delay > MAX_SAMPLE_DELAY) {
- sr_err("Unable to sample at %" PRIu64 "Hz.", samplerate);
- return SR_ERR;
- }
-
- cmd.sample_delay_h = (delay >> 8) & 0xff;
- cmd.sample_delay_l = delay & 0xff;
-
- /* Select the sampling width. */
- cmd.flags |= samplewide ? CMD_START_FLAGS_SAMPLE_16BIT :
- CMD_START_FLAGS_SAMPLE_8BIT;
-
- /* Send the control message. */
- ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
- LIBUSB_ENDPOINT_OUT, CMD_START, 0x0000, 0x0000,
- (unsigned char *)&cmd, sizeof(cmd), 100);
- if (ret < 0) {
- sr_err("Unable to send start command: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-/**
- * Check the USB configuration to determine if this is an fx2lafw device.
- *
- * @return TRUE if the device's configuration profile match fx2lafw
- * configuration, FALSE otherwise.
- */
-SR_PRIV gboolean fx2lafw_check_conf_profile(libusb_device *dev)
-{
- struct libusb_device_descriptor des;
- struct libusb_device_handle *hdl;
- gboolean ret;
- unsigned char strdesc[64];
-
- hdl = NULL;
- ret = FALSE;
- while (!ret) {
- /* Assume the FW has not been loaded, unless proven wrong. */
- if (libusb_get_device_descriptor(dev, &des) != 0)
- break;
-
- if (libusb_open(dev, &hdl) != 0)
- break;
-
- if (libusb_get_string_descriptor_ascii(hdl,
- des.iManufacturer, strdesc, sizeof(strdesc)) < 0)
- break;
- if (strncmp((const char *)strdesc, "sigrok", 6))
- break;
-
- if (libusb_get_string_descriptor_ascii(hdl,
- des.iProduct, strdesc, sizeof(strdesc)) < 0)
- break;
- if (strncmp((const char *)strdesc, "fx2lafw", 7))
- break;
-
- /* If we made it here, it must be an fx2lafw. */
- ret = TRUE;
- }
- if (hdl)
- libusb_close(hdl);
-
- return ret;
-}
-
-SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
-{
- libusb_device **devlist;
- struct sr_usb_dev_inst *usb;
- struct libusb_device_descriptor des;
- struct dev_context *devc;
- struct drv_context *drvc;
- struct version_info vi;
- int ret, skip, i, device_count;
- uint8_t revid;
-
- drvc = di->priv;
- devc = sdi->priv;
- usb = sdi->conn;
-
- if (sdi->status == SR_ST_ACTIVE)
- /* Device is already in use. */
- return SR_ERR;
-
- skip = 0;
- device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- if (device_count < 0) {
- sr_err("Failed to get device list: %s.",
- libusb_error_name(device_count));
- return SR_ERR;
- }
-
- for (i = 0; i < device_count; i++) {
- if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
- sr_err("Failed to get device descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
-
- if (des.idVendor != devc->profile->vid
- || des.idProduct != devc->profile->pid)
- continue;
-
- if (sdi->status == SR_ST_INITIALIZING) {
- if (skip != sdi->index) {
- /* Skip devices of this type that aren't the one we want. */
- skip += 1;
- continue;
- }
- } else if (sdi->status == SR_ST_INACTIVE) {
- /*
- * This device is fully enumerated, so we need to find
- * this device by vendor, product, bus and address.
- */
- if (libusb_get_bus_number(devlist[i]) != usb->bus
- || libusb_get_device_address(devlist[i]) != usb->address)
- /* This is not the one. */
- continue;
- }
-
- if (!(ret = libusb_open(devlist[i], &usb->devhdl))) {
- if (usb->address == 0xff)
- /*
- * First time we touch this device after FW
- * upload, so we don't know the address yet.
- */
- usb->address = libusb_get_device_address(devlist[i]);
- } else {
- sr_err("Failed to open device: %s.",
- libusb_error_name(ret));
- break;
- }
-
- ret = command_get_fw_version(usb->devhdl, &vi);
- if (ret != SR_OK) {
- sr_err("Failed to get firmware version.");
- break;
- }
-
- ret = command_get_revid_version(sdi, &revid);
- if (ret != SR_OK) {
- sr_err("Failed to get REVID.");
- break;
- }
-
- /*
- * Changes in major version mean incompatible/API changes, so
- * bail out if we encounter an incompatible version.
- * Different minor versions are OK, they should be compatible.
- */
- if (vi.major != FX2LAFW_REQUIRED_VERSION_MAJOR) {
- sr_err("Expected firmware version %d.x, "
- "got %d.%d.", FX2LAFW_REQUIRED_VERSION_MAJOR,
- vi.major, vi.minor);
- break;
- }
-
- sdi->status = SR_ST_ACTIVE;
- sr_info("Opened device %d on %d.%d, "
- "interface %d, firmware %d.%d.",
- sdi->index, usb->bus, usb->address,
- USB_INTERFACE, vi.major, vi.minor);
-
- sr_info("Detected REVID=%d, it's a Cypress CY7C68013%s.",
- revid, (revid != 1) ? " (FX2)" : "A (FX2LP)");
-
- break;
- }
- libusb_free_device_list(devlist, 1);
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR;
-
- return SR_OK;
-}
-
-SR_PRIV struct dev_context *fx2lafw_dev_new(void)
-{
- struct dev_context *devc;
-
- if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
-
- devc->profile = NULL;
- devc->fw_updated = 0;
- devc->cur_samplerate = 0;
- devc->limit_samples = 0;
- devc->sample_wide = FALSE;
- devc->stl = NULL;
-
- return devc;
-}
-
-SR_PRIV void fx2lafw_abort_acquisition(struct dev_context *devc)
-{
- int i;
-
- devc->acq_aborted = TRUE;
-
- for (i = devc->num_transfers - 1; i >= 0; i--) {
- if (devc->transfers[i])
- libusb_cancel_transfer(devc->transfers[i]);
- }
-}
-
-static void finish_acquisition(struct sr_dev_inst *sdi)
-{
- struct sr_datafeed_packet packet;
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- /* Terminate session. */
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-
- /* Remove fds from polling. */
- usb_source_remove(sdi->session, devc->ctx);
-
- devc->num_transfers = 0;
- g_free(devc->transfers);
-
- if (devc->stl) {
- soft_trigger_logic_free(devc->stl);
- devc->stl = NULL;
- }
-}
-
-static void free_transfer(struct libusb_transfer *transfer)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- unsigned int i;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
-
- g_free(transfer->buffer);
- transfer->buffer = NULL;
- libusb_free_transfer(transfer);
-
- for (i = 0; i < devc->num_transfers; i++) {
- if (devc->transfers[i] == transfer) {
- devc->transfers[i] = NULL;
- break;
- }
- }
-
- devc->submitted_transfers--;
- if (devc->submitted_transfers == 0)
- finish_acquisition(sdi);
-}
-
-static void resubmit_transfer(struct libusb_transfer *transfer)
-{
- int ret;
-
- if ((ret = libusb_submit_transfer(transfer)) == LIBUSB_SUCCESS)
- return;
-
- sr_err("%s: %s", __func__, libusb_error_name(ret));
- free_transfer(transfer);
-
-}
-
-SR_PRIV void fx2lafw_receive_transfer(struct libusb_transfer *transfer)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- gboolean packet_has_error = FALSE;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- unsigned int num_samples;
- int trigger_offset, cur_sample_count, unitsize;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
-
- /*
- * If acquisition has already ended, just free any queued up
- * transfer that come in.
- */
- if (devc->acq_aborted) {
- free_transfer(transfer);
- return;
- }
-
- sr_info("receive_transfer(): status %d received %d bytes.",
- transfer->status, transfer->actual_length);
-
- /* Save incoming transfer before reusing the transfer struct. */
- unitsize = devc->sample_wide ? 2 : 1;
- cur_sample_count = transfer->actual_length / unitsize;
-
- switch (transfer->status) {
- case LIBUSB_TRANSFER_NO_DEVICE:
- fx2lafw_abort_acquisition(devc);
- free_transfer(transfer);
- return;
- case LIBUSB_TRANSFER_COMPLETED:
- case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */
- break;
- default:
- packet_has_error = TRUE;
- break;
- }
-
- if (transfer->actual_length == 0 || packet_has_error) {
- devc->empty_transfer_count++;
- if (devc->empty_transfer_count > MAX_EMPTY_TRANSFERS) {
- /*
- * The FX2 gave up. End the acquisition, the frontend
- * will work out that the samplecount is short.
- */
- fx2lafw_abort_acquisition(devc);
- free_transfer(transfer);
- } else {
- resubmit_transfer(transfer);
- }
- return;
- } else {
- devc->empty_transfer_count = 0;
- }
-
- if (devc->trigger_fired) {
- if (devc->sent_samples < devc->limit_samples) {
- /* Send the incoming transfer to the session bus. */
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- if (devc->sent_samples + cur_sample_count > devc->limit_samples)
- num_samples = devc->limit_samples - devc->sent_samples;
- else
- num_samples = cur_sample_count;
- logic.length = num_samples * unitsize;
- logic.unitsize = unitsize;
- logic.data = transfer->buffer;
- sr_session_send(devc->cb_data, &packet);
- devc->sent_samples += num_samples;
- }
- } else {
- trigger_offset = soft_trigger_logic_check(devc->stl,
- transfer->buffer, transfer->actual_length);
- if (trigger_offset > -1) {
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- num_samples = cur_sample_count - trigger_offset;
- if (devc->limit_samples &&
- num_samples > devc->limit_samples - devc->sent_samples)
- num_samples = devc->limit_samples - devc->sent_samples;
- logic.length = num_samples * unitsize;
- logic.unitsize = unitsize;
- logic.data = transfer->buffer + trigger_offset * unitsize;
- sr_session_send(devc->cb_data, &packet);
- devc->sent_samples += num_samples;
-
- devc->trigger_fired = TRUE;
- }
- }
-
- if (devc->limit_samples && devc->sent_samples >= devc->limit_samples) {
- fx2lafw_abort_acquisition(devc);
- free_transfer(transfer);
- } else
- resubmit_transfer(transfer);
-}
-
-static unsigned int to_bytes_per_ms(unsigned int samplerate)
-{
- return samplerate / 1000;
-}
-
-SR_PRIV size_t fx2lafw_get_buffer_size(struct dev_context *devc)
-{
- size_t s;
-
- /*
- * The buffer should be large enough to hold 10ms of data and
- * a multiple of 512.
- */
- s = 10 * to_bytes_per_ms(devc->cur_samplerate);
- return (s + 511) & ~511;
-}
-
-SR_PRIV unsigned int fx2lafw_get_number_of_transfers(struct dev_context *devc)
-{
- unsigned int n;
-
- /* Total buffer size should be able to hold about 500ms of data. */
- n = (500 * to_bytes_per_ms(devc->cur_samplerate) /
- fx2lafw_get_buffer_size(devc));
-
- if (n > NUM_SIMUL_TRANSFERS)
- return NUM_SIMUL_TRANSFERS;
-
- return n;
-}
-
-SR_PRIV unsigned int fx2lafw_get_timeout(struct dev_context *devc)
-{
- size_t total_size;
- unsigned int timeout;
-
- total_size = fx2lafw_get_buffer_size(devc) *
- fx2lafw_get_number_of_transfers(devc);
- timeout = total_size / to_bytes_per_ms(devc->cur_samplerate);
- return timeout + timeout / 4; /* Leave a headroom of 25% percent. */
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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_FX2LAFW_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_FX2LAFW_PROTOCOL_H
-
-#include <glib.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <libusb.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "fx2lafw"
-
-#define USB_INTERFACE 0
-#define USB_CONFIGURATION 1
-#define NUM_TRIGGER_STAGES 4
-
-#define MAX_RENUM_DELAY_MS 3000
-#define NUM_SIMUL_TRANSFERS 32
-#define MAX_EMPTY_TRANSFERS (NUM_SIMUL_TRANSFERS * 2)
-
-#define FX2LAFW_REQUIRED_VERSION_MAJOR 1
-
-#define MAX_8BIT_SAMPLE_RATE SR_MHZ(24)
-#define MAX_16BIT_SAMPLE_RATE SR_MHZ(12)
-
-/* 6 delay states of up to 256 clock ticks */
-#define MAX_SAMPLE_DELAY (6 * 256)
-
-#define DEV_CAPS_16BIT_POS 0
-
-#define DEV_CAPS_16BIT (1 << DEV_CAPS_16BIT_POS)
-
-struct fx2lafw_profile {
- uint16_t vid;
- uint16_t pid;
-
- const char *vendor;
- const char *model;
- const char *model_version;
-
- const char *firmware;
-
- uint32_t dev_caps;
-
- const char *usb_manufacturer;
- const char *usb_product;
-};
-
-struct dev_context {
- const struct fx2lafw_profile *profile;
- /*
- * Since we can't keep track of an fx2lafw device after upgrading
- * the firmware (it renumerates into a different device address
- * after the upgrade) this is like a global lock. No device will open
- * until a proper delay after the last device was upgraded.
- */
- int64_t fw_updated;
-
- /* Device/capture settings */
- uint64_t cur_samplerate;
- uint64_t limit_samples;
-
- /* Operational settings */
- gboolean trigger_fired;
- gboolean acq_aborted;
- gboolean sample_wide;
- struct soft_trigger_logic *stl;
-
- unsigned int sent_samples;
- int submitted_transfers;
- int empty_transfer_count;
-
- void *cb_data;
- unsigned int num_transfers;
- struct libusb_transfer **transfers;
- struct sr_context *ctx;
-};
-
-SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV gboolean fx2lafw_check_conf_profile(libusb_device *dev);
-SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di);
-SR_PRIV struct dev_context *fx2lafw_dev_new(void);
-SR_PRIV void fx2lafw_abort_acquisition(struct dev_context *devc);
-SR_PRIV void fx2lafw_receive_transfer(struct libusb_transfer *transfer);
-SR_PRIV size_t fx2lafw_get_buffer_size(struct dev_context *devc);
-SR_PRIV unsigned int fx2lafw_get_number_of_transfers(struct dev_context *devc);
-SR_PRIV unsigned int fx2lafw_get_timeout(struct dev_context *devc);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013, 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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/>.
- */
-
-/** @file
- * Gossen Metrawatt Metrahit 1x/2x drivers
- * @internal
- */
-
-#include <string.h>
-#include "protocol.h"
-
-/* Serial communication parameters for Metrahit 1x/2x with 'RS232' adaptor */
-#define SERIALCOMM_1X_RS232 "8228/6n1/dtr=1/rts=1/flow=0" /* =8192, closer with divider */
-#define SERIALCOMM_2X_RS232 "9600/6n1/dtr=1/rts=1/flow=0"
-#define SERIALCOMM_2X "9600/8n1/dtr=1/rts=1/flow=0"
-#define VENDOR_GMC "Gossen Metrawatt"
-
-SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info;
-SR_PRIV struct sr_dev_driver gmc_mh_2x_bd232_driver_info;
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-/** Hardware capabilities for Metrahit 1x/2x devices in send mode. */
-static const int32_t hwcaps_sm[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_THERMOMETER, /**< All GMC 1x/2x multimeters seem to support this */
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
-};
-
-/** Hardware capabilities for Metrahit 2x devices in bidirectional Mode. */
-static const int32_t hwcaps_bd[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_THERMOMETER, /**< All GMC 1x/2x multimeters seem to support this */
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
- SR_CONF_POWER_OFF,
-};
-
-
-/* TODO:
- * - For the 29S SR_CONF_ENERGYMETER, too.
- * - SR_CONF_PATTERN_MODE for some 2x devices
- * - SR_CONF_DATALOG for 22M, 26M, 29S and storage adaptors.
- * Need to implement device-specific lists.
- */
-
-/** Init driver gmc_mh_1x_2x_rs232. */
-static int init_1x_2x_rs232(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, &gmc_mh_1x_2x_rs232_driver_info, LOG_PREFIX);
-}
-
-/** Init driver gmc_mh_2x_bd232. */
-static int init_2x_bd232(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, &gmc_mh_2x_bd232_driver_info, LOG_PREFIX);
-}
-
-/**
- * Read single byte from serial port.
- *
- * @retval -1 Timeout or error.
- * @retval other Byte.
- */
-static int read_byte(struct sr_serial_dev_inst *serial, gint64 timeout)
-{
- uint8_t result = 0;
- int rc = 0;
-
- for (;;) {
- rc = serial_read(serial, &result, 1);
- if (rc == 1) {
- sr_spew("read: 0x%02x/%d", result, result);
- return result;
- }
- if (g_get_monotonic_time() > timeout)
- return -1;
- g_usleep(2000);
- }
-}
-
-/**
- * Try to detect GMC 1x/2x multimeter model in send mode for max. 1 second.
- *
- * @param serial Configured, open serial port.
- *
- * @retval NULL Detection failed.
- * @retval other Model code.
- */
-static enum model scan_model_sm(struct sr_serial_dev_inst *serial)
-{
- int byte, bytecnt, cnt;
- enum model model;
- gint64 timeout_us;
-
- model = METRAHIT_NONE;
- timeout_us = g_get_monotonic_time() + 1 * 1000 * 1000;
-
- /*
- * Try to find message consisting of device code and several
- * (at least 4) data bytes.
- */
- for (bytecnt = 0; bytecnt < 100; bytecnt++) {
- byte = read_byte(serial, timeout_us);
- if ((byte == -1) || (timeout_us < g_get_monotonic_time()))
- break;
- if ((byte & MSGID_MASK) == MSGID_INF) {
- if (!(model = gmc_decode_model_sm(byte & MSGC_MASK)))
- break;
- /* Now expect (at least) 4 data bytes. */
- for (cnt = 0; cnt < 4; cnt++) {
- byte = read_byte(serial, timeout_us);
- if ((byte == -1) ||
- ((byte & MSGID_MASK) != MSGID_DATA))
- {
- model = METRAHIT_NONE;
- bytecnt = 100;
- break;
- }
- }
- break;
- }
- }
-
- return model;
-}
-
-/**
- * Scan for Metrahit 1x and Metrahit 2x in send mode using Gossen Metrawatt
- * 'RS232' interface.
- *
- * The older 1x models use 8192 baud and the newer 2x 9600 baud.
- * The DMM usually sends up to about 20 messages per second. However, depending
- * on configuration and measurement mode the intervals can be much larger and
- * then the detection might not work.
- */
-static GSList *scan_1x_2x_rs232(GSList *options)
-{
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_config *src;
- struct sr_channel *ch;
- struct sr_serial_dev_inst *serial;
- GSList *l, *devices;
- const char *conn, *serialcomm;
- enum model model;
- gboolean serialcomm_given;
-
- devices = NULL;
- drvc = (&gmc_mh_1x_2x_rs232_driver_info)->priv;
- drvc->instances = NULL;
- conn = serialcomm = NULL;
- model = METRAHIT_NONE;
- serialcomm_given = FALSE;
-
- sr_spew("scan_1x_2x_rs232() called!");
-
- 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);
- serialcomm_given = TRUE;
- break;
- }
- }
- if (!conn)
- return NULL;
- if (!serialcomm)
- serialcomm = SERIALCOMM_2X_RS232;
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK) {
- sr_serial_dev_inst_free(serial);
- return NULL;
- }
-
- serial_flush(serial);
-
- model = scan_model_sm(serial);
-
- /*
- * If detection failed and no user-supplied parameters,
- * try second baud rate.
- */
- if ((model == METRAHIT_NONE) && !serialcomm_given) {
- serialcomm = SERIALCOMM_1X_RS232;
- g_free(serial->serialcomm);
- serial->serialcomm = g_strdup(serialcomm);
- if (serial_set_paramstr(serial, serialcomm) == SR_OK) {
- serial_flush(serial);
- model = scan_model_sm(serial);
- }
- }
-
- if (model != METRAHIT_NONE) {
- sr_spew("%s %s detected!", VENDOR_GMC, gmc_model_str(model));
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR_GMC,
- gmc_model_str(model), NULL)))
- return NULL;
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
- devc->model = model;
- devc->limit_samples = 0;
- devc->limit_msec = 0;
- devc->num_samples = 0;
- devc->elapsed_msec = g_timer_new();
- devc->settings_ok = FALSE;
-
- sdi->conn = serial;
- sdi->priv = devc;
- sdi->driver = &gmc_mh_1x_2x_rs232_driver_info;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- }
-
- return devices;
-}
-
-/** Scan for Metrahit 2x in a bidirectional mode using Gossen Metrawatt 'BD 232' interface.
- *
- */
-static GSList *scan_2x_bd232(GSList *options)
-{
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_config *src;
- struct sr_channel *ch;
- struct sr_serial_dev_inst *serial;
- GSList *l, *devices;
- const char *conn, *serialcomm;
- int cnt, byte;
- gint64 timeout_us;
-
- sdi = NULL;
- devc = NULL;
- conn = serialcomm = NULL;
- devices = NULL;
-
- drvc = (&gmc_mh_2x_bd232_driver_info)->priv;
- drvc->instances = NULL;
-
- sr_spew("scan_2x_bd232() called!");
-
- 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;
- if (!serialcomm)
- serialcomm = SERIALCOMM_2X;
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- goto exit_err;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- goto exit_err;
- }
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR_GMC, NULL, NULL)))
- goto exit_err;
-
- sdi->priv = devc;
-
- /* Send message 03 "Query multimeter version and status" */
- sdi->conn = serial;
- sdi->priv = devc;
- if (req_stat14(sdi, TRUE) != SR_OK)
- goto exit_err;
-
- /* Wait for reply from device(s) for up to 2s. */
- timeout_us = g_get_monotonic_time() + 2*1000*1000;
-
- while (timeout_us > g_get_monotonic_time()) {
- /* Receive reply (14 bytes) */
- devc->buflen = 0;
- for (cnt = 0; cnt < 14; cnt++) {
- byte = read_byte(serial, timeout_us);
- if (byte != -1)
- devc->buf[devc->buflen++] = (byte & MASK_6BITS);
- }
-
- if (devc->buflen != 14)
- continue;
-
- devc->addr = devc->buf[0];
- process_msg14(sdi);
- devc->buflen = 0;
-
- if (devc->model != METRAHIT_NONE) {
- sr_spew("%s %s detected!", VENDOR_GMC, gmc_model_str(devc->model));
-
- devc->elapsed_msec = g_timer_new();
-
- sdi->model = g_strdup(gmc_model_str(devc->model));
- sdi->version = g_strdup_printf("Firmware %d.%d", devc->fw_ver_maj, devc->fw_ver_min);
- sdi->conn = serial;
- sdi->priv = devc;
- sdi->driver = &gmc_mh_2x_bd232_driver_info;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
- goto exit_err;
- sdi->channels = g_slist_append(sdi->channels, ch);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- goto exit_err;
- }
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR_GMC, NULL, NULL)))
- goto exit_err;
- }
- };
-
- /* Free last alloc if no device found */
- if (devc->model == METRAHIT_NONE) {
- g_free(devc);
- sr_dev_inst_free(sdi);
- }
-
- return devices;
-
-exit_err:
- sr_info("scan_2x_bd232(): Error!");
-
- if (serial)
- sr_serial_dev_inst_free(serial);
- if (devc)
- g_free(devc);
- if (sdi)
- sr_dev_inst_free(sdi);
-
- return NULL;
-}
-
-/** Driver device list function */
-static GSList *dev_list_1x_2x_rs232(void)
-{
- return ((struct drv_context *)(gmc_mh_1x_2x_rs232_driver_info.priv))->instances;
-}
-
-/** Driver device list function */
-static GSList *dev_list_2x_bd232(void)
-{
- return ((struct drv_context *)(gmc_mh_2x_bd232_driver_info.priv))
- ->instances;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
-
- std_serial_dev_close(sdi);
-
- sdi->status = SR_ST_INACTIVE;
-
- /* Free dynamically allocated resources. */
- if ((devc = sdi->priv) && devc->elapsed_msec) {
- g_timer_destroy(devc->elapsed_msec);
- devc->elapsed_msec = NULL;
- devc->model = METRAHIT_NONE;
- }
-
- return SR_OK;
-}
-
-static int cleanup_sm_rs232(void)
-{
- return std_dev_clear(&gmc_mh_1x_2x_rs232_driver_info, NULL);
-}
-
-static int cleanup_2x_bd232(void)
-{
- return std_dev_clear(&gmc_mh_2x_bd232_driver_info, NULL);
-}
-
-/** Get value of configuration item */
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- int ret;
- struct dev_context *devc;
-
- (void)cg;
-
- ret = SR_OK;
-
- if (!sdi || !(devc = sdi->priv))
- return SR_ERR_ARG;
-
- ret = SR_OK;
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- *data = g_variant_new_uint64(devc->limit_msec);
- break;
- case SR_CONF_POWER_OFF:
- *data = g_variant_new_boolean(FALSE);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return ret;
-}
-
-/** Implementation of config_list, auxiliary function for common parts, */
-static int config_list_common(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-/** Implementation of config_list for Metrahit 1x/2x send mode */
-static int config_list_sm(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps_sm, ARRAY_SIZE(hwcaps_sm), sizeof(int32_t));
- break;
- default:
- return config_list_common(key, data, sdi, cg);
- }
-
- return SR_OK;
-}
-
-/** Implementation of config_list for Metrahit 2x bidirectional mode */
-static int config_list_bd(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps_bd, ARRAY_SIZE(hwcaps_bd), sizeof(int32_t));
- break;
- default:
- return config_list_common(key, data, sdi, cg);
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start_1x_2x_rs232(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (!sdi || !cb_data || !(devc = sdi->priv))
- return SR_ERR_BUG;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc->cb_data = cb_data;
- devc->settings_ok = FALSE;
- devc->buflen = 0;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Start timer, if required. */
- if (devc->limit_msec)
- g_timer_start(devc->elapsed_msec);
-
- /* Poll every 40ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 40,
- gmc_mh_1x_2x_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_start_2x_bd232(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (!sdi || !cb_data || !(devc = sdi->priv))
- return SR_ERR_BUG;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc->cb_data = cb_data;
- devc->settings_ok = FALSE;
- devc->buflen = 0;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Start timer, if required. */
- if (devc->limit_msec)
- g_timer_start(devc->elapsed_msec);
-
- /* Poll every 40ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 40,
- gmc_mh_2x_receive_data, (void *)sdi);
-
- /* Send start message */
- return req_meas14(sdi);
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
-
- /* Stop timer, if required. */
- if (sdi && (devc = sdi->priv) && devc->limit_msec)
- g_timer_stop(devc->elapsed_msec);
-
- return std_serial_dev_acquisition_stop(sdi, cb_data, dev_close,
- sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info = {
- .name = "gmc-mh-1x-2x-rs232",
- .longname = "Gossen Metrawatt Metrahit 1x/2x, RS232 interface",
- .api_version = 1,
- .init = init_1x_2x_rs232,
- .cleanup = cleanup_sm_rs232,
- .scan = scan_1x_2x_rs232,
- .dev_list = dev_list_1x_2x_rs232,
- .dev_clear = NULL,
- .config_get = config_get,
- .config_set = config_set,
- .config_list = config_list_sm,
- .dev_open = std_serial_dev_open,
- .dev_close = dev_close,
- .dev_acquisition_start = dev_acquisition_start_1x_2x_rs232,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
-
-SR_PRIV struct sr_dev_driver gmc_mh_2x_bd232_driver_info = {
- .name = "gmc-mh-2x-bd232",
- .longname = "Gossen Metrawatt Metrahit 2x, BD232/SI232-II interface",
- .api_version = 1,
- .init = init_2x_bd232,
- .cleanup = cleanup_2x_bd232,
- .scan = scan_2x_bd232,
- .dev_list = dev_list_2x_bd232,
- .dev_clear = NULL,
- .config_get = config_get,
- .config_set = config_set,
- .config_list = config_list_bd,
- .dev_open = std_serial_dev_open,
- .dev_close = dev_close,
- .dev_acquisition_start = dev_acquisition_start_2x_bd232,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013, 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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/>.
- */
-
-/** @file
- * Gossen Metrawatt Metrahit 1x/2x drivers
- * @internal
- */
-
-#include <math.h>
-#include <string.h>
-#include "protocol.h"
-
-/* Internal Headers */
-static guchar calc_chksum_14(guchar* dta);
-static int chk_msg14(struct sr_dev_inst *sdi);
-
-/** Set or clear flags in devc->mqflags. */
-static void setmqf(struct dev_context *devc, uint64_t flags, gboolean set)
-{
- if (set)
- devc->mqflags |= flags;
- else
- devc->mqflags &= ~flags;
-}
-
-/** Decode current type and measured value, Metrahit 12-16. */
-static void decode_ctmv_16(uint8_t ctmv, struct dev_context *devc)
-{
- devc->mq = 0;
- devc->unit = 0;
- devc->mqflags = 0;
-
- switch (ctmv) {
- case 0x00: /* 0000 - */
- break;
- case 0x01: /* 0001 mV DC */
- devc->scale1000 = -1; /* Fall through */
- case 0x02: /* 0010 V DC */
- case 0x03: /* 0011 V AC+DC */
- case 0x04: /* 0100 V AC */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_VOLT;
- if (ctmv <= 0x03)
- devc->mqflags |= SR_MQFLAG_DC;
- if (ctmv >= 0x03) {
- devc->mqflags |= SR_MQFLAG_AC;
- if (devc->model >= METRAHIT_16S)
- devc->mqflags |= SR_MQFLAG_RMS;
- }
- break;
- case 0x05: /* 0101 Hz (15S/16S only) */
- case 0x06: /* 0110 kHz (15S/16S only) */
- devc->mq = SR_MQ_FREQUENCY;
- devc->unit = SR_UNIT_HERTZ;
- if (ctmv == 0x06)
- devc->scale1000 = 1;
- break;
- case 0x07: /* 0111 % (15S/16S only) */
- devc->mq = SR_MQ_DUTY_CYCLE;
- devc->unit = SR_UNIT_PERCENTAGE;
- break;
- case 0x08: /* 1000 Diode */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_VOLT;
- devc->mqflags |= SR_MQFLAG_DIODE;
- break;
- case 0x09: /* 1001 Ohm, °C */
- case 0x0a: /* 1010 kOhm */
- case 0x0b: /* 1011 MOhm */
- devc->mq = SR_MQ_RESISTANCE; /* Changed to temp. later if req.*/
- devc->unit = SR_UNIT_OHM;
- devc->scale1000 = ctmv - 0x09;
- break;
- case 0x0c: /* 1100 nF (15S/16S only) */
- case 0x0d: /* 1101 µF (15S/16S only) */
- devc->mq = SR_MQ_CAPACITANCE;
- devc->unit = SR_UNIT_FARAD;
- if (ctmv == 0x0c)
- devc->scale1000 = -3;
- else
- devc->scale1000 = -2;
- break;
- case 0x0e: /* mA, µA */
- devc->scale1000 = -1; /* Fall through. */
- case 0x0f: /* A */
- devc->mq = SR_MQ_CURRENT;
- devc->unit = SR_UNIT_AMPERE;
- if (devc->model == METRAHIT_16S)
- devc->mqflags |= SR_MQFLAG_RMS;
- /* 16I A only with clamp, RMS questionable. */
- break;
- }
-}
-
-/**
- * Decode range/sign/acdc byte special chars (Metrahit 12-16).
- *
- * @param[in] rs Range and sign byte.
- */
-static void decode_rs_16(uint8_t rs, struct dev_context *devc)
-{
- sr_spew("decode_rs_16(%d) scale = %f", rs, devc->scale);
-
- if (rs & 0x04) /* Sign */
- devc->scale *= -1.0;
-
- if (devc->mq == SR_MQ_CURRENT) {
- if (rs & 0x08) /* Current is AC */
- devc->mqflags |= SR_MQFLAG_AC;
- else
- devc->mqflags |= SR_MQFLAG_DC;
- }
-
- switch (rs & 0x03) {
- case 0:
- if (devc->mq == SR_MQ_VOLTAGE) /* V */
- devc->scale *= 0.1;
- else if (devc->mq == SR_MQ_CURRENT) /* 000.0 µA */
- devc->scale *= 0.00001;
- else if (devc->mq == SR_MQ_RESISTANCE) {
- if (devc->buflen >= 10) {
- /* °C with 10 byte msg type, otherwise GOhm. */
- devc->mq = SR_MQ_TEMPERATURE;
- devc->unit = SR_UNIT_CELSIUS;
- devc->scale *= 0.01;
- } else if (devc->scale1000 == 2) {
- /* 16I Iso 500/1000V 3 GOhm */
- devc->scale *= 0.1;
- }
- }
- break;
- case 1:
- devc->scale *= 0.0001;
- break;
- case 2:
- devc->scale *= 0.001;
- break;
- case 3:
- devc->scale *= 0.01;
- break;
- }
-}
-
-/**
- * Decode special chars, Metrahit 12-16.
- *
- * @param[in] spc Special characters 1 and 2 (s1 | (s2 << 4)).
- */
-static void decode_spc_16(uint8_t spc, struct dev_context *devc)
-{
- /* xxxx1xxx ON */
- /* TODO: What does that mean? Power on? The 16I sets this. */
- /* xxxxx1xx BEEP */
- /* xxxxxx1x Low battery */
- /* xxxxxxx1 FUSE */
- /* 1xxxxxxx MIN */
- setmqf(devc, SR_MQFLAG_MIN, spc & 0x80);
-
- /* x1xxxxxx MAN */
- setmqf(devc, SR_MQFLAG_AUTORANGE, !(spc & 0x40));
-
- /* xx1xxxxx DATA */
- setmqf(devc, SR_MQFLAG_HOLD, spc & 0x20);
-
- /* xxx1xxxx MAX */
- setmqf(devc, SR_MQFLAG_MAX, spc & 0x10);
-}
-
-/** Decode current type and measured value, Metrahit 18. */
-static void decode_ctmv_18(uint8_t ctmv, struct dev_context *devc)
-{
- devc->mq = 0;
- devc->unit = 0;
- devc->mqflags = 0;
-
- switch (ctmv) {
- case 0x00: /* 0000 - */
- break;
- case 0x01: /* 0001 V AC */
- case 0x02: /* 0010 V AC+DC */
- case 0x03: /* 0011 V DC */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_VOLT;
- if (ctmv <= 0x02)
- devc->mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_RMS);
- if (ctmv >= 0x02)
- devc->mqflags |= SR_MQFLAG_DC;
- break;
- case 0x04: /* 0100 Ohm/Ohm with buzzer */
- devc->mq = SR_MQ_RESISTANCE;
- devc->unit = SR_UNIT_OHM;
- break;
- case 0x05: /* 0101 Diode/Diode with buzzer */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_VOLT;
- devc->mqflags |= SR_MQFLAG_DIODE;
- break;
- case 0x06: /* 0110 °C */
- devc->mq = SR_MQ_TEMPERATURE;
- devc->unit = SR_UNIT_CELSIUS;
- break;
- case 0x07: /* 0111 F */
- devc->mq = SR_MQ_CAPACITANCE;
- devc->unit = SR_UNIT_FARAD;
- break;
- case 0x08: /* 1000 mA DC */
- case 0x09: /* 1001 A DC */
- case 0x0a: /* 1010 mA AC+DC */
- case 0x0b: /* 1011 A AC+DC */
- devc->mq = SR_MQ_CURRENT;
- devc->unit = SR_UNIT_AMPERE;
- devc->mqflags |= SR_MQFLAG_DC;
- if (ctmv >= 0x0a)
- devc->mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_RMS);
- if ((ctmv == 0x08) || (ctmv == 0x0a))
- devc->scale1000 = -1;
- break;
- case 0x0c: /* 1100 Hz */
- devc->mq = SR_MQ_FREQUENCY;
- devc->unit = SR_UNIT_HERTZ;
- break;
- case 0x0d: /* 1101 dB */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_DECIBEL_VOLT;
- devc->mqflags |= SR_MQFLAG_AC; /* dB available for AC only */
- break;
- case 0x0e: /* 1110 Events AC, Events AC+DC. Actually delivers just
- * current voltage via IR, nothing more. */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_VOLT;
- devc->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS;
- break;
- case 0x0f: /* 1111 Clock */
- devc->mq = SR_MQ_TIME;
- devc->unit = SR_UNIT_SECOND;
- devc->mqflags |= SR_MQFLAG_DURATION;
- break;
- }
-}
-
-/**
- * Decode range/sign/acdc byte special chars, Metrahit 18.
- *
- * @param[in] rs Rance/sign byte.
- */
-static void decode_rs_18(uint8_t rs, struct dev_context *devc)
-{
- int range;
-
- /* Sign */
- if (((devc->scale > 0) && (rs & 0x08)) ||
- ((devc->scale < 0) && !(rs & 0x08)))
- devc->scale *= -1.0;
-
- /* Range */
- range = rs & 0x07;
- switch (devc->mq) {
- case SR_MQ_VOLTAGE:
- if (devc->unit == SR_UNIT_DECIBEL_VOLT) {
- devc->scale *= pow(10.0, -2);
- /*
- * When entering relative mode, the device switches
- * from 10 byte to 6 byte msg format. Unfortunately
- * it switches back to 10 byte when the second value
- * is measured, so that's not sufficient to
- * identify relative mode.
- */
- }
- else if (devc->vmains_29S)
- devc->scale *= pow(10.0, range - 2);
- else
- devc->scale *= pow(10.0, range - 5);
- break;
- case SR_MQ_CURRENT:
- if (devc->scale1000 == -1)
- devc->scale *= pow(10.0, range - 5);
- else
- devc->scale *= pow(10.0, range - 4);
- break;
- case SR_MQ_RESISTANCE:
- devc->scale *= pow(10.0, range - 2);
- break;
- case SR_MQ_FREQUENCY:
- devc->scale *= pow(10.0, range - 2);
- break;
- case SR_MQ_TEMPERATURE:
- devc->scale *= pow(10.0, range - 2);
- break;
- case SR_MQ_CAPACITANCE:
- devc->scale *= pow(10.0, range - 13);
- break;
- /* TODO: 29S Mains measurements. */
- }
-}
-
-/**
- * Decode special chars, Metrahit 18.
- *
- * @param[in] spc Special characters 1 and 2 (s1 | (s2 << 4)).
- */
-static void decode_spc_18(uint8_t spc, struct dev_context *devc)
-{
- /* xxxx1xxx ZERO */
- /* xxxxx1xx BEEP */
- /* xxxxxx1x Low battery */
- /* xxxxxxx1 Fuse */
-
- if (devc->mq == SR_MQ_TIME) {
- /* xxx1xxxx Clock running: 1; stop: 0 */
- sr_spew("Clock running: %d", spc >> 4);
- } else {
- /* 1xxxxxxx MAN */
- setmqf(devc, SR_MQFLAG_AUTORANGE, !(spc & 0x80));
-
- /* x1xxxxxx MIN */
- setmqf(devc, SR_MQFLAG_MIN, spc & 0x40);
-
- /* xx1xxxxx MAX */
- setmqf(devc, SR_MQFLAG_MAX, spc & 0x20);
-
- /* xxx1xxxx DATA */
- setmqf(devc, SR_MQFLAG_HOLD, spc & 0x10);
- }
-}
-
-/**
- * Decode current type and measured value, Metrahit 2x.
- *
- * @param[in] ctmv Current type and measured value (v1 | (v2 << 4)).
- */
-static void decode_ctmv_2x(uint8_t ctmv, struct dev_context *devc)
-{
- if ((ctmv > 0x1c) || (!devc)) {
- sr_err("decode_ctmv_2x(%d): invalid param(s)!", ctmv);
- return;
- }
-
- devc->mq = 0;
- devc->unit = 0;
- devc->mqflags = 0;
-
- switch (ctmv) {
- /* 00000 unused */
- case 0x01: /* 00001 V DC */
- case 0x02: /* 00010 V AC+DC */
- case 0x03: /* 00011 V AC */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_VOLT;
- if (ctmv <= 0x02)
- devc->mqflags |= SR_MQFLAG_DC;
- if (ctmv >= 0x02) {
- devc->mqflags |= SR_MQFLAG_AC;
- if (devc->model >= METRAHIT_24S)
- devc->mqflags |= SR_MQFLAG_RMS;
- }
- break;
- case 0x04: /* 00100 mA DC */
- case 0x05: /* 00101 mA AC+DC */
- devc->scale1000 = -1;
- case 0x06: /* 00110 A DC */
- case 0x07: /* 00111 A AC+DC */
- devc->mq = SR_MQ_CURRENT;
- devc->unit = SR_UNIT_AMPERE;
- devc->mqflags |= SR_MQFLAG_DC;
- if ((ctmv == 0x05) || (ctmv == 0x07)) {
- devc->mqflags |= SR_MQFLAG_AC;
- if (devc->model >= METRAHIT_24S)
- devc->mqflags |= SR_MQFLAG_RMS;
- }
- break;
- case 0x08: /* 01000 Ohm */
- devc->mq = SR_MQ_RESISTANCE;
- devc->unit = SR_UNIT_OHM;
- break;
- case 0x09: /* 01001 F */
- devc->mq = SR_MQ_CAPACITANCE;
- devc->unit = SR_UNIT_FARAD;
- devc->scale *= 0.1;
- break;
- case 0x0a: /* 01010 V dB */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_DECIBEL_VOLT;
- devc->mqflags |= SR_MQFLAG_AC;
- if (devc->model >= METRAHIT_24S)
- devc->mqflags |= SR_MQFLAG_RMS;
- break;
- case 0x0b: /* 01011 Hz U ACDC */
- case 0x0c: /* 01100 Hz U AC */
- devc->mq = SR_MQ_FREQUENCY;
- devc->unit = SR_UNIT_HERTZ;
- devc->mqflags |= SR_MQFLAG_AC;
- if (ctmv <= 0x0b)
- devc->mqflags |= SR_MQFLAG_DC;
- break;
- case 0x0d: /* 01101 W on power, mA range (29S only) */
- devc->scale *= 0.001;
- /* Fall through! */
- case 0x0e: /* 01110 W on power, A range (29S only) */
- devc->mq = SR_MQ_POWER;
- devc->unit = SR_UNIT_WATT;
- break;
- case 0x0f: /* 01111 Diode */
- case 0x10: /* 10000 Diode with buzzer (actually cont. with voltage) */
- devc->unit = SR_UNIT_VOLT;
- if (ctmv == 0x0f) {
- devc->mq = SR_MQ_VOLTAGE;
- devc->mqflags |= SR_MQFLAG_DIODE;
- } else {
- devc->mq = SR_MQ_CONTINUITY;
- devc->scale *= 0.00001;
- }
- devc->unit = SR_UNIT_VOLT;
- break;
- case 0x11: /* 10001 Ohm with buzzer */
- devc->mq = SR_MQ_CONTINUITY;
- devc->unit = SR_UNIT_OHM;
- devc->scale1000 = -1;
- break;
- case 0x12: /* 10010 Temperature */
- devc->mq = SR_MQ_TEMPERATURE;
- devc->unit = SR_UNIT_CELSIUS;
- /* This can be Fahrenheit. That is detected by range=4 later. */
- break;
- /* 0x13 10011, 0x14 10100 unsed */
- case 0x15: /* 10101 Press (29S only) */
- /* TODO: What does that mean? Possibly phase shift?
- Then we need a unit/flag for it. */
- devc->mq = SR_MQ_GAIN;
- devc->unit = SR_UNIT_PERCENTAGE;
- break;
- case 0x16: /* 10110 Pulse W (29S only) */
- /* TODO: Own unit and flag for this! */
- devc->mq = SR_MQ_POWER;
- devc->unit = SR_UNIT_WATT;
- break;
- case 0x17: /* 10111 TRMS V on mains (29S only) */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_VOLT;
- devc->mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_RMS);
- devc->vmains_29S = TRUE;
- break;
- case 0x18: /* 11000 Counter (zero crossings of a signal) */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_UNITLESS;
- break;
- case 0x19: /* 11001 Events U ACDC */
- case 0x1a: /* 11010 Events U AC */
- /* TODO: No unit or flags for this yet! */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_UNITLESS;
- devc->mqflags |= SR_MQFLAG_AC;
- if (ctmv <= 0x19)
- devc->mqflags |= SR_MQFLAG_DC;
- break;
- case 0x1b: /* 11011 pulse on mains (29S only) */
- /* TODO: No unit or flags for this yet! */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_UNITLESS;
- devc->mqflags |= SR_MQFLAG_AC;
- break;
- case 0x1c: /* 11100 dropout on mains (29S only) */
- /* TODO: No unit or flags for this yet! */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_UNITLESS;
- devc->mqflags |= SR_MQFLAG_AC;
- break;
- case 0x1f: /* 11111 Undocumented: 25S in stopwatch mode.
- The value is voltage, not time, so treat it such. */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_VOLT;
- devc->mqflags |= SR_MQFLAG_DC;
- break;
- case 0x20: /* 100000 Undocumented: 25S in event count mode.
- Value is 0 anyway. */
- devc->mq = SR_MQ_VOLTAGE;
- devc->unit = SR_UNIT_UNITLESS;
- break;
- default:
- sr_err("decode_ctmv_2x(%d, ...): Unknown ctmv!", ctmv);
- break;
- }
-}
-
-/**
- * Decode range/sign/acdc byte special chars, Metrahit 2x, table TR.
- *
- * @param[in] rs Range/sign byte.
- */
-static void decode_rs_2x(uint8_t rs, struct dev_context *devc)
-{
- int range;
-
- /* Sign */
- if (((devc->scale > 0) && (rs & 0x08)) ||
- ((devc->scale < 0) && !(rs & 0x08)))
- devc->scale *= -1.0;
-
- /* Range */
- range = rs & 0x07;
- switch (devc->mq) {
- case SR_MQ_VOLTAGE:
- if (devc->unit == SR_UNIT_DECIBEL_VOLT)
- devc->scale *= pow(10.0, -3);
- else if (devc->vmains_29S)
- devc->scale *= pow(10.0, range - 2);
- else
- devc->scale *= pow(10.0, range - 6);
- break;
- case SR_MQ_CURRENT:
- if (devc->scale1000 != -1) /* uA, mA */
- range += 1;/* mA and A ranges differ by 10^4, not 10^3!*/
- devc->scale *= pow(10.0, range - 6);
- break;
- case SR_MQ_RESISTANCE:
- devc->scale *= pow(10.0, range - 3);
- break;
- case SR_MQ_FREQUENCY:
- devc->scale *= pow(10.0, range - 3);
- break;
- case SR_MQ_TEMPERATURE:
- if (range == 4) /* Indicator for °F */
- devc->unit = SR_UNIT_FAHRENHEIT;
- devc->scale *= pow(10.0, - 2);
- break;
- case SR_MQ_CAPACITANCE:
- if (range == 7)
- range -= 1; /* Same value as range 6 */
- devc->scale *= pow(10.0, range - 13);
- break;
- /* TODO: 29S Mains measurements. */
- }
-}
-
-/**
- * Decode range/sign/acdc byte special chars, Metrahit 2x, table TR 2.
- *
- * @param[in] rs Range/sign byte.
- */
-static void decode_rs_2x_TR2(uint8_t rs, struct dev_context *devc)
-{
- int range;
-
- /* Range */
- range = rs & 0x07;
- switch (devc->mq) {
- case SR_MQ_CURRENT:
- if (devc->scale1000 == -1) /* mA */
- switch(range) {
- case 0: case 1: /* 100, 300 µA */
- devc->scale *= pow(10.0, -6);
- break;
- case 2: case 3: /* 1, 3 mA */
- devc->scale *= pow(10.0, -5);
- break;
- case 4: case 5: /* 10, 30 mA */
- devc->scale *= pow(10.0, -4);
- break;
- case 6: case 7: /* 100, 300 mA */
- devc->scale *= pow(10.0, -3);
- break;
- }
- else /* A */
- switch(range) {
- case 0: case 1: /* 1, 3 A */
- devc->scale *= pow(10.0, -5);
- break;
- case 2: /* 10 A */
- devc->scale *= pow(10.0, -4);
- break;
- }
- break;
- default:
- decode_rs_2x(rs, devc);
- return;
- }
-
- /* Sign */
- if (((devc->scale > 0) && (rs & 0x08)) ||
- ((devc->scale < 0) && !(rs & 0x08)))
- devc->scale *= -1.0;
-}
-
-
-/**
- * Decode special chars (Metrahit 2x).
- *
- * @param[in] spc Special characters 1 and 2 (s1 | (s2 << 4)).
- */
-static void decode_spc_2x(uint8_t spc, struct dev_context *devc)
-{
- /* xxxxxxx1 Fuse */
-
- /* xxxxxx1x Low battery */
-
- /* xxxxx1xx BEEP */
-
- /* xxxx1xxx ZERO */
-
- /* xxx1xxxx DATA */
- setmqf(devc, SR_MQFLAG_HOLD, spc & 0x10);
-
- /* x11xxxxx unused */
- /* 1xxxxxxx MAN */
- setmqf(devc, SR_MQFLAG_AUTORANGE, !(spc & 0x80));
-}
-
-/** Clean range and sign. */
-static void clean_rs_v(struct dev_context *devc)
-{
- devc->value = 0.0;
- devc->scale = 1.0;
-}
-
-/** Clean current type, measured variable, range and sign. */
-static void clean_ctmv_rs_v(struct dev_context *devc)
-{
- devc->mq = 0;
- devc->unit = 0;
- devc->mqflags = 0;
- devc->scale1000 = 0;
- devc->vmains_29S = FALSE;
- clean_rs_v(devc);
-}
-
-/** Send prepared value. */
-static void send_value(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_datafeed_analog analog;
- struct sr_datafeed_packet packet;
-
- devc = sdi->priv;
-
- memset(&analog, 0, sizeof(analog));
- analog.channels = sdi->channels;
- analog.num_samples = 1;
- analog.mq = devc->mq;
- analog.unit = devc->unit;
- analog.mqflags = devc->mqflags;
- analog.data = &devc->value;
-
- memset(&packet, 0, sizeof(packet));
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
-
- devc->num_samples++;
-}
-
-/** Process 6-byte data message, Metrahit 1x/2x send mode. */
-static void process_msg_dta_6(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int cnt;
- uint8_t dgt;
-
- devc = sdi->priv;
- clean_rs_v(devc);
-
- /* Byte 0, range and sign */
- if (devc->model <= METRAHIT_16X)
- decode_rs_16(bc(devc->buf[0]), devc);
- else if (devc->model < METRAHIT_2X)
- decode_rs_18(bc(devc->buf[0]), devc);
- else {
- decode_rs_2x(bc(devc->buf[0]), devc);
- devc->scale *= 10; /* Compensate for format having only 5 digits, decode_rs_2x() assumes 6. */
- }
-
- /* Bytes 1-5, digits (ls first). */
- for (cnt = 0; cnt < 5; cnt++) {
- dgt = bc(devc->buf[1 + cnt]);
- if (dgt >= 10) {
- /* 10 Overload; on model <= 16X also 11 possible. */
- devc->value = NAN;
- devc->scale = 1.0;
- break;
- }
- devc->value += pow(10.0, cnt) * dgt;
- }
-
- sr_spew("process_msg_dta_6() value=%f scale=%f scale1000=%d",
- devc->value, devc->scale, devc->scale1000);
- if (devc->value != NAN)
- devc->value *= devc->scale * pow(1000.0, devc->scale1000);
-
- /* Create and send packet. */
- send_value(sdi);
-}
-
-/** Process 5-byte info message, Metrahit 1x/2x. */
-static void process_msg_inf_5(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- enum model model;
-
- devc = sdi->priv;
-
- clean_ctmv_rs_v(devc);
-
- /* Process byte 0 */
- model = gmc_decode_model_sm(bc(devc->buf[0]));
- if (model != devc->model) {
- sr_warn("Model mismatch in data: Detected %s, now %s",
- gmc_model_str(devc->model), gmc_model_str(model));
- }
-
- /* Process bytes 1-4 */
- if (devc->model <= METRAHIT_16X) {
- decode_ctmv_16(bc(devc->buf[1]), devc);
- decode_spc_16(bc(devc->buf[2]) | (bc(devc->buf[3]) << 4), devc);
- decode_rs_16(bc(devc->buf[4]), devc);
- } else if (devc->model <= METRAHIT_18S) {
- decode_ctmv_18(bc(devc->buf[1]), devc);
- decode_spc_18(bc(devc->buf[2]) | (bc(devc->buf[3]) << 4), devc);
- decode_rs_18(bc(devc->buf[4]), devc);
- } else { /* Must be Metrahit 2x */
- decode_ctmv_2x(bc(devc->buf[1]), devc);
- decode_spc_2x(bc(devc->buf[2]) | (bc(devc->buf[3]) << 4), devc);
- decode_rs_2x(bc(devc->buf[4]), devc);
- }
-}
-
-/** Process 10-byte info/data message, Metrahit 15+. */
-static void process_msg_inf_10(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int cnt;
- uint8_t dgt;
-
- devc = sdi->priv;
-
- process_msg_inf_5(sdi);
-
- /* Now decode numbers */
- for (cnt = 0; cnt < 5; cnt++) {
- dgt = bc(devc->buf[5 + cnt]);
- if (dgt == 11) { /* Empty digit */
- dgt = 0;
- }
- else if (dgt >= 12) { /* Overload */
- devc->value = NAN;
- devc->scale = 1.0;
- break;
- }
- devc->value += pow(10.0, cnt) * dgt;
- }
- sr_spew("process_msg_inf_10() value=%f scale=%f scalet=%d",
- devc->value, devc->scale, devc->scale1000);
-
- if (devc->value != NAN)
- devc->value *= devc->scale * pow(1000.0, devc->scale1000);
-
- /* Create and send packet. */
- send_value(sdi);
-}
-
-/** Decode send interval (Metrahit 2x only). */
-static const char *decode_send_interval(uint8_t si)
-{
- switch (si) {
- case 0x00:
- return "0.05";
- case 0x01:
- return "0.1";
- case 0x02:
- return "0.2";
- case 0x03:
- return "0.5";
- case 0x04:
- return "00:01";
- case 0x05:
- return "00:02";
- case 0x06:
- return "00:05";
- case 0x07:
- return "00:10";
- case 0x08:
- return "00:20";
- case 0x09:
- return "00:30";
- case 0x0a:
- return "01:00";
- case 0x0b:
- return "02:00";
- case 0x0c:
- return "05:00";
- case 0x0d:
- return "10:00";
- case 0x0e:
- return "----";
- case 0x0f:
- return "data";
- default:
- return "Unknown value";
- }
-}
-
-/** Process 13-byte info/data message, Metrahit 2x. */
-static void process_msg_inf_13(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- enum model model;
- int cnt;
- uint8_t dgt;
-
- devc = sdi->priv;
-
- clean_ctmv_rs_v(devc);
-
- /* Byte 0, model. */
- model = gmc_decode_model_sm(bc(devc->buf[0]));
- if (model != devc->model) {
- sr_warn("Model mismatch in data: Detected %s, now %s",
- gmc_model_str(devc->model), gmc_model_str(model));
- }
-
- /* Bytes 1-4, 11. */
- decode_ctmv_2x(bc(devc->buf[1]) | (bc(devc->buf[11]) << 4), devc);
- decode_spc_2x(bc(devc->buf[2]) | (bc(devc->buf[3]) << 4), devc);
- decode_rs_2x(bc(devc->buf[4]), devc);
-
- /* Bytes 5-10, digits (ls first). */
- for (cnt = 0; cnt < 6; cnt++) {
- dgt = bc(devc->buf[5 + cnt]);
- if (dgt == 10) { /* Overload */
- devc->value = NAN;
- devc->scale = 1.0;
- break;
- }
- devc->value += pow(10.0, cnt) * dgt;
- }
- sr_spew("process_msg_inf_13() value=%f scale=%f scale1000=%d mq=%d "
- "unit=%d mqflags=0x%02llx", devc->value, devc->scale,
- devc->scale1000, devc->mq, devc->unit, devc->mqflags);
- if (devc->value != NAN)
- devc->value *= devc->scale * pow(1000.0, devc->scale1000);
-
- /* Byte 12, Send Interval */
- sr_spew("Send interval: %s", decode_send_interval(bc(devc->buf[12])));
-
- /* Create and send packet. */
- send_value(sdi);
-}
-
-/** Dump contents of 14-byte message.
- * @param buf Pointer to array of 14 data bytes.
- * @param[in] raw Write only data bytes, no interpretation.
- */
-void dump_msg14(guchar* buf, gboolean raw)
-{
- if (!buf)
- return;
-
- if (raw)
- sr_spew("msg14: 0x %02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x",
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
- buf[7], buf[8], buf[9], buf[10], buf[11], buf[12],
- buf[13]);
- else
- sr_spew("msg14: 0x a=%d c1=%02x c2=%02x cmd=%02x dta=%02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x chs=%02x",
- buf[1] == 0x2b?buf[0] >> 2:buf[0] % 0x0f, buf[1], buf[2], buf[3], buf[4], buf[5],
- buf[6], buf[7], buf[8], buf[9], buf[10], buf[11],
- buf[12], buf[13]);
-}
-
-/** Calc checksum for 14 byte message type.
- *
- * @param[in] dta Pointer to array of 13 data bytes.
- * @return Checksum.
- */
-static guchar calc_chksum_14(guchar* dta)
-{
- guchar cnt, chs;
-
- for (chs = 0, cnt = 0; cnt < 13; cnt++)
- chs += dta[cnt];
-
- return (64 - chs) & MASK_6BITS;
-}
-
-/** Check 14-byte message, Metrahit 2x. */
-static int chk_msg14(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int retc;
- gboolean isreq; /* Message is request to multimeter (otherwise response) */
- uint8_t addr; /* Adaptor address */
-
- retc = SR_OK;
-
- /* Check parameters and message */
- if (!sdi || !(devc = sdi->priv))
- return SR_ERR_ARG;
-
- if (devc->buflen != 14) {
- sr_err("process_msg_14(): Msg len 14 expected!");
- return SR_ERR_ARG;
- }
-
- isreq = devc->buf[1] == 0x2b;
- if (isreq)
- addr = devc->buf[0] >> 2;
- else
- addr = devc->buf[0] & 0x0f;
-
- if ((devc->addr != addr) && !(isreq && (addr == 0))) {
- sr_err("process_msg_14(): Address mismatch, msg for other device!");
- retc = SR_ERR_ARG;
- }
-
- if (devc->buf[1] == 0) { /* Error msg from device! */
- retc = SR_ERR_ARG;
- switch (devc->buf[2]) {
- case 1: /* Not used */
- sr_err("Device: Illegal error code!");
- break;
- case 2: /* Incorrect check sum of received block */
- sr_err("Device: Incorrect checksum in cmd!");
- break;
- case 3: /* Incorrect length of received block */
- sr_err("Device: Incorrect block length in cmd!");
- break;
- case 4: /* Incorrect 2nd or 3rd byte */
- sr_err("Device: Incorrect byte 2 or 3 in cmd!");
- break;
- case 5: /* Parameter out of range */
- sr_err("Device: Parameter out of range!");
- break;
- default:
- sr_err("Device: Unknown error code!");
- }
- retc = SR_ERR_ARG;
- }
- else if (!isreq && ((devc->buf[1] != 0x27) || (devc->buf[2] != 0x3f))) {
- sr_err("process_msg_14(): byte 1/2 unexpected!");
- retc = SR_ERR_ARG;
- }
-
- if (calc_chksum_14(devc->buf) != devc->buf[13]) {
- sr_err("process_msg_14(): Invalid checksum!");
- retc = SR_ERR_ARG;
- }
-
- if (retc != SR_OK)
- dump_msg14(devc->buf, TRUE);
-
- return retc;
-}
-
-/** Check 14-byte message, Metrahit 2x. */
-SR_PRIV int process_msg14(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int retc;
- uint8_t addr;
- uint8_t cnt, dgt;
-
- if ((retc = chk_msg14(sdi)) != SR_OK)
- return retc;
-
- devc = sdi->priv;
-
- clean_ctmv_rs_v(devc);
- addr = devc->buf[0] & MASK_6BITS;
- if (addr != devc->addr)
- sr_info("Device address mismatch %d/%d!", addr, devc->addr);
-
- switch (devc->buf[3]) { /* That's the command this reply is for */
- /* 0 cannot occur, the respective message is not a 14-byte message */
- case 1: /* Read first free and occupied address */
- sr_spew("Cmd %d unimplemented!", devc->buf[3]);
- break;
- case 2: /* Clear all RAM in multimeter */
- sr_spew("Cmd %d unimplemented!", devc->buf[3]);
- break;
- case 3: /* Read firmware version and status */
- sr_spew("Cmd 3, Read firmware and status", devc->buf[3]);
- switch (devc->cmd_idx) {
- case 0:
- devc->fw_ver_maj = devc->buf[5];
- devc->fw_ver_min = devc->buf[4];
- sr_spew("Firmware version %d.%d", (int)devc->fw_ver_maj, (int)devc->fw_ver_min);
- sr_spew("Rotary Switch Position (1..10): %d", (int)devc->buf[6]);
- /** Docs say values 0..9, but that's not true */
- sr_spew("Measurement Function: %d ", (int)devc->buf[7]);
- decode_ctmv_2x(devc->buf[7], devc);
- sr_spew("Range: 0x%x", devc->buf[8]);
- decode_rs_2x_TR2(devc->buf[8] & 0x0f, devc); /* Docs wrong, uses conversion table TR_2! */
- devc->autorng = (devc->buf[8] & 0x20) == 0;
- // TODO 9, 10: 29S special functions
- devc->ubatt = 0.1 * (float)devc->buf[11];
- devc->model = gmc_decode_model_bd(devc->buf[12]);
- sr_spew("Model=%s, battery voltage=%2.1f V", gmc_model_str(devc->model), (double)devc->ubatt);
- break;
- case 1:
- sr_spew("Internal version %d.%d", (int)devc->buf[5], (int)devc->buf[4]);
- sr_spew("Comm mode: 0x%x", (int)devc->buf[6]);
- sr_spew("Block cnt%%64: %d", (int)devc->buf[7]);
- sr_spew("drpCi: %d drpCh: %d", (int)devc->buf[8], (int)devc->buf[9]);
- // Semantics undocumented. Possibly Metrahit 29S dropouts stuff?
- break;
- default:
- sr_spew("Cmd 3: Unknown cmd_idx=%d", devc->cmd_idx);
- break;
- }
- break;
- case 4: /* Set real time, date, sample rate, trigger, ... */
- sr_spew("Cmd %d unimplemented!", devc->buf[3]);
- break;
- case 5: /* Read real time, date, sample rate, trigger... */
- sr_spew("Cmd %d unimplemented!", devc->buf[3]);
- break;
- case 6: /* Set modes or power off */
- sr_spew("Cmd %d unimplemented!", devc->buf[3]);
- break;
- case 7: /* Set measurement function, range, autom/man. */
- sr_spew("Cmd %d unimplemented!", devc->buf[3]);
- break;
- case 8: /* Get one measurement value */
- sr_spew("Cmd 8, get one measurement value");
- sr_spew("Measurement Function: %d ", (int)devc->buf[5]);
- decode_ctmv_2x(devc->buf[5], devc);
- if (!(devc->buf[6] & 0x10)) /* If bit4=0, old data. */
- return SR_OK;
-
- decode_rs_2x_TR2(devc->buf[6] & 0x0f, devc); // The docs say conversion table TR_3, but that does not work
- setmqf(devc, SR_MQFLAG_AUTORANGE, devc->autorng);
- /* 6 digits */
- for (cnt = 0; cnt < 6; cnt++) {
- dgt = bc(devc->buf[7 + cnt]);
- if (dgt == 10) { /* Overload */
- devc->value = NAN;
- devc->scale = 1.0;
- break;
- }
- else if (dgt == 13) { /* FUSE */
- sr_err("FUSE!");
- }
- else if (dgt == 14) { /* Function recognition mode, OPEN */
- sr_info("Function recognition mode, OPEN!");
- devc->value = NAN;
- devc->scale = 1.0;
- break;
- }
- devc->value += pow(10.0, cnt) * dgt;
- }
- sr_spew("process_msg14() value=%f scale=%f scale1000=%d mq=%d "
- "unit=%d mqflags=0x%02llx", devc->value, devc->scale,
- devc->scale1000, devc->mq, devc->unit, devc->mqflags);
- if (devc->value != NAN)
- devc->value *= devc->scale * pow(1000.0, devc->scale1000);
-
- send_value(sdi);
-
- break;
- default:
- sr_spew("Unknown cmd %d!", devc->buf[3]);
- break;
- }
-
- return SR_OK;
-}
-
-/** Data reception callback function. */
-SR_PRIV int gmc_mh_1x_2x_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- uint8_t buf, msgt;
- int len;
- gdouble elapsed_s;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- serial = sdi->conn;
-
- if (revents == G_IO_IN) { /* Serial data arrived. */
- while (GMC_BUFSIZE - devc->buflen - 1 > 0) {
- len = serial_read(serial, devc->buf + devc->buflen, 1);
- if (len < 1)
- break;
- buf = *(devc->buf + devc->buflen);
- sr_spew("read 0x%02x/%d/%d", buf, buf, buf & MSGC_MASK);
- devc->buflen += len;
- if (!devc->settings_ok) {
- /*
- * If no device type/settings record processed
- * yet, wait for one.
- */
- if ((devc->buf[0] & MSGID_MASK) != MSGID_INF) {
- devc->buflen = 0;
- continue;
- }
- devc->settings_ok = TRUE;
- }
-
- msgt = devc->buf[0] & MSGID_MASK;
- switch (msgt) {
- case MSGID_INF:
- if (devc->buflen == 13) {
- process_msg_inf_13(sdi);
- devc->buflen = 0;
- continue;
- } else if ((devc->buflen == 10) &&
- (devc->model <= METRAHIT_18S)) {
- process_msg_inf_10(sdi);
- devc->buflen = 0;
- continue;
- }
- else if ((devc->buflen >= 5) &&
- (devc->buf[devc->buflen - 1] &
- MSGID_MASK) != MSGID_DATA) {
- /*
- * Char just received is beginning
- * of next message.
- */
- process_msg_inf_5(sdi);
- devc->buf[0] =
- devc->buf[devc->buflen - 1];
- devc->buflen = 1;
- continue;
- }
- break;
- case MSGID_DTA:
- case MSGID_D10:
- if (devc->buflen == 6) {
- process_msg_dta_6(sdi);
- devc->buflen = 0;
- }
- break;
- case MSGID_DATA:
- sr_err("Comm error, unexpected data byte!");
- devc->buflen = 0;
- break;
- }
- }
- }
-
- /* If number of samples or time limit reached, stop acquisition. */
- if (devc->limit_samples && (devc->num_samples >= devc->limit_samples))
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
-
- if (devc->limit_msec) {
- elapsed_s = g_timer_elapsed(devc->elapsed_msec, NULL);
- if ((elapsed_s * 1000) >= devc->limit_msec)
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- }
-
- return TRUE;
-}
-
-SR_PRIV int gmc_mh_2x_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- uint8_t buf;
- int len;
- gdouble elapsed_s;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- serial = sdi->conn;
-
- if (revents == G_IO_IN) { /* Serial data arrived. */
- while (GMC_BUFSIZE - devc->buflen - 1 > 0) {
- len = serial_read(serial, devc->buf + devc->buflen, 1);
- if (len < 1)
- break;
- buf = *(devc->buf + devc->buflen);
- sr_spew("read 0x%02x/%d/%d", buf, buf, buf & MASK_6BITS);
- devc->buf[devc->buflen] &= MASK_6BITS;
- devc->buflen += len;
-
- if (devc->buflen == 14) {
- devc->response_pending = FALSE;
- sr_spew("gmc_mh_2x_receive_data processing msg");
- process_msg14(sdi);
- devc->buflen = 0;
- }
- }
- }
-
- /* If number of samples or time limit reached, stop acquisition. */
- if (devc->limit_samples && (devc->num_samples >= devc->limit_samples))
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
-
- if (devc->limit_msec) {
- elapsed_s = g_timer_elapsed(devc->elapsed_msec, NULL);
- if ((elapsed_s * 1000) >= devc->limit_msec)
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- }
-
- /* Request next data set, if required */
- if (sdi->status == SR_ST_ACTIVE) {
- if (devc->response_pending) {
- gint64 elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
- if (elapsed_us > 1*1000*1000) /* Timeout! */
- devc->response_pending = FALSE;
- }
- if (!devc->response_pending) {
- devc->cmd_seq++;
- if (devc->cmd_seq % 10 == 0) {
- if (req_stat14(sdi, FALSE) != SR_OK)
- return FALSE;
- }
- else if (req_meas14(sdi) != SR_OK)
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-/** Create 14 (42) byte command for Metrahit 2x multimeter in bidir mode.
- *
- * Actually creates 42 bytes due to the encoding method used.
- * @param[in] addr Device address (0=adapter, 1..15 multimeter; for byte 0).
- * @param[in] func Function code (byte 3).
- * @param[in] params Further parameters (9 bytes)
- * @param[out] buf Buffer to create msg in (42 bytes).
- */
-void create_cmd_14(guchar addr, guchar func, guchar* params, guchar* buf)
-{
- uint8_t dta[14]; /* Unencoded message */
- int cnt;
-
- if (!params || !buf)
- return;
-
- /* 0: Address */
- dta[0] = ((addr << 2) | 0x03) & MASK_6BITS;
-
- /* 1-3: Set command header */
- dta[1] = 0x2b;
- dta[2] = 0x3f;
- dta[3] = func;
-
- /* 4-12: Copy further parameters */
- for (cnt = 0; cnt < 9; cnt++)
- dta[cnt+4] = (params[cnt] & MASK_6BITS);
-
- /* 13: Checksum (b complement) */
- dta[13] = calc_chksum_14(dta);
-
- /* The whole message is packed into 3 bytes per byte now (lower 6 bits only) the most
- * peculiar way I have ever seen. Possibly to improve IR communication? */
- for (cnt = 0; cnt < 14; cnt++) {
- buf[3*cnt] = (dta[cnt] & 0x01 ? 0x0f : 0) | (dta[cnt] & 0x02 ? 0xf0 : 0);
- buf[3*cnt + 1] = (dta[cnt] & 0x04 ? 0x0f : 0) | (dta[cnt] & 0x08 ? 0xf0 : 0);
- buf[3*cnt + 2] = (dta[cnt] & 0x10 ? 0x0f : 0) | (dta[cnt] & 0x20 ? 0xf0 : 0);
- }
-}
-
-/** Request one measurement from 2x multimeter (msg 8).
- *
- */
-int req_meas14(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- uint8_t params[9];
- uint8_t msg[42];
-
- if (!sdi || !(devc = sdi->priv) || !(serial = sdi->conn))
- return SR_ERR;
-
- memset(params, 0, sizeof(params));
- params[0] = 0;
- devc->cmd_idx = 0;
- create_cmd_14(devc->addr, 8, params, msg);
- devc->req_sent_at = g_get_monotonic_time();
- if (serial_write(serial, msg, sizeof(msg)) == -1) {
- return SR_ERR;
- }
-
- devc->response_pending = TRUE;
-
- return SR_OK;
-}
-
-/** Request status from 2x multimeter (msg 3).
- * @param[in] power_on Try to power on powered off multimeter by sending additional messages.
- */
-int req_stat14(const struct sr_dev_inst *sdi, gboolean power_on)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- uint8_t params[9];
- uint8_t msg[42];
-
- if (!sdi || !(devc = sdi->priv) || !(serial = sdi->conn))
- return SR_ERR;
-
- memset(params, 0, sizeof(params));
- params[0] = 0;
- devc->cmd_idx = 0;
- create_cmd_14(devc->addr, 3, params, msg);
-
- if (power_on) {
- sr_info("Write some data and wait 3s to turn on powered off device...");
- if (serial_write(serial, msg, sizeof(msg)) < 0)
- return SR_ERR;
- g_usleep(1*1000*1000);
- if (serial_write(serial, msg, sizeof(msg)) < 0)
- return SR_ERR;
- g_usleep(1*1000*1000);
- if (serial_write(serial, msg, sizeof(msg)) < 0)
- return SR_ERR;
- g_usleep(1*1000*1000);
- serial_flush(serial);
- }
-
- /* Write message and wait for reply */
- devc->req_sent_at = g_get_monotonic_time();
- if (serial_write(serial, msg, sizeof(msg)) == -1) {
- return SR_ERR;
- }
-
- devc->response_pending = TRUE;
-
- return SR_OK;
-}
-
-/** Decode model in "send mode".
- *
- * @param[in] mcode Model code.
- * @return Model code.
- */
-SR_PRIV int gmc_decode_model_sm(uint8_t mcode)
-{
- if (mcode > 0xf) {
- sr_err("decode_model(%d): Model code 0..15 expected!", mcode);
- return METRAHIT_NONE;
- }
-
- switch(mcode) {
- case 0x04: /* 0100b */
- return METRAHIT_12S;
- case 0x08: /* 1000b */
- return METRAHIT_13S14A;
- case 0x09: /* 1001b */
- return METRAHIT_14S;
- case 0x0A: /* 1010b */
- return METRAHIT_15S;
- case 0x0B: /* 1011b */
- return METRAHIT_16S;
- case 0x06: /* 0110b (undocumented by GMC!) */
- return METRAHIT_16I;
- case 0x07: /* 0111b (undocumented by GMC!) */
- return METRAHIT_16T;
- case 0x0D: /* 1101b */
- return METRAHIT_18S;
- case 0x02: /* 0010b */
- return METRAHIT_22SM;
- case 0x03: /* 0011b */
- return METRAHIT_23S;
- case 0x0F: /* 1111b */
- return METRAHIT_24S;
- case 0x05: /* 0101b */
- return METRAHIT_25S;
- case 0x01: /* 0001b */
- return METRAHIT_26SM;
- case 0x0C: /* 1100b */
- return METRAHIT_28S;
- case 0x0E: /* 1110b */
- return METRAHIT_29S;
- default:
- sr_err("Unknown model code %d!", mcode);
- return METRAHIT_NONE;
- }
-}
-
-/** Convert GMC model code in bidirectional mode to sigrok-internal one.
- *
- * @param[in] mcode Model code.
- *
- * @return Model code.
- */
-SR_PRIV int gmc_decode_model_bd(uint8_t mcode)
-{
- switch (mcode & 0x1f) {
- case 2:
- if (mcode & 0x20)
- return METRAHIT_22M;
- else
- return METRAHIT_22S;
- case 3:
- return METRAHIT_23S;
- case 4:
- return METRAHIT_24S;
- case 5:
- return METRAHIT_25S;
- case 1:
- if (mcode & 0x20)
- return METRAHIT_26M;
- else
- return METRAHIT_26S;
- case 12:
- return METRAHIT_28S;
- case 14:
- return METRAHIT_29S;
- default:
- sr_err("Unknown model code %d!", mcode);
- return METRAHIT_NONE;
- }
-}
-
-/** Convert sigrok-internal model code to string.
- *
- * @param[in] mcode Model code.
- *
- * @return Model code string.
- */
-SR_PRIV const char *gmc_model_str(enum model mcode)
-{
- switch (mcode) {
- case METRAHIT_NONE:
- return "-uninitialized model variable-";
- case METRAHIT_12S:
- return "METRAHit 12S";
- case METRAHIT_13S14A:
- return "METRAHit 13S/14A";
- case METRAHIT_14S:
- return "METRAHit 14S";
- case METRAHIT_15S:
- return "METRAHit 15S";
- case METRAHIT_16S:
- return "METRAHit 16S";
- case METRAHIT_16I:
- return "METRAHit 16I/16L";
- case METRAHIT_16T:
- return "METRAHit 16T/16U/KMM2002";
- case METRAHIT_18S:
- return "METRAHit 18S";
- case METRAHIT_22SM:
- return "METRAHit 22S/M";
- case METRAHIT_22S:
- return "METRAHit 22S";
- case METRAHIT_22M:
- return "METRAHit 22M";
- case METRAHIT_23S:
- return "METRAHit 23S";
- case METRAHIT_24S:
- return "METRAHit 24S";
- case METRAHIT_25S:
- return "METRAHit 25S";
- case METRAHIT_26SM:
- return "METRAHit 26S/M";
- case METRAHIT_26S:
- return "METRAHit 26S";
- case METRAHIT_26M:
- return "METRAHit 26M";
- case METRAHIT_28S:
- return "METRAHit 28S";
- case METRAHIT_29S:
- return "METRAHit 29S";
- default:
- return "Unknown model code";
- }
-}
-
-
-/** @copydoc sr_dev_driver.config_set
- */
-SR_PRIV int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- uint8_t params[9];
- uint8_t msg[42];
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- switch (key) {
- case SR_CONF_POWER_OFF:
- if (devc->model < METRAHIT_2X)
- return SR_ERR_NA;
- if (!g_variant_get_boolean(data))
- return SR_ERR;
- sr_info("Powering device off.");
-
- memset(params, 0, sizeof(params));
- params[0] = 5;
- params[1] = 5;
- create_cmd_14(devc->addr, 6, params, msg);
- if (serial_write(sdi->conn, msg, sizeof(msg)) == -1)
- return SR_ERR;
- else
- g_usleep(2000000); /* Wait to ensure transfer before interface switched off. */
- break;
- case SR_CONF_LIMIT_MSEC:
- if (g_variant_get_uint64(data) == 0) {
- sr_err("LIMIT_MSEC can't be 0.");
- return SR_ERR;
- }
- devc->limit_msec = g_variant_get_uint64(data);
- sr_dbg("Setting time limit to %" PRIu64 "ms.",
- devc->limit_msec);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013, 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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/>.
- */
-
-/** @file
- * Gossen Metrawatt Metrahit 1x/2x drivers
- * @internal
- */
-
-#ifndef LIBSIGROK_HARDWARE_GMC_MH_1X_2X_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_GMC_MH_1X_2X_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "gmc-mh-1x-2x"
-
-#define GMC_BUFSIZE 266
-
-/** Message ID bits 4, 5 */
-#define MSGID_MASK 0x30 /**< Mask to get message ID bits */
-#define MSGID_INF 0x00 /**< Start of message with device info */
-#define MSGID_D10 0x10 /**< Start of data message, non-displayed intermediate */
-#define MSGID_DTA 0x20 /**< Start of data message, displayed, averaged */
-#define MSGID_DATA 0x30 /**< Data byte in message */
-
-#define MSGC_MASK 0x0f /**< Mask to get message byte contents in send mode */
-
-#define MSGSRC_MASK 0xc0 /**< Mask to get bits related to message source */
-
-#define bc(x) (x & MSGC_MASK) /**< Get contents from a byte */
-
-#define MASK_6BITS 0x3f /**< Mask lower six bits. */
-
-/**
- * Internal multimeter model codes. In opposite to the multimeter models from
- * protocol (see decode_model()), these codes allow working with ranges.
- */
-enum model {
- METRAHIT_NONE = 0, /**< Value for uninitialized variable */
- METRAHIT_12S = 12,
- METRAHIT_13S14A = 13,
- METRAHIT_14S = 14,
- METRAHIT_15S = 15,
- METRAHIT_16S = 16,
- METRAHIT_16I = 17, /**< Metrahit 16I, L */
- METRAHIT_16T = 18, /**< Metrahit 16T, U, KMM2002 */
- METRAHIT_16X = METRAHIT_16T, /**< All Metrahit 16 */
- /* A Metrahit 17 exists, but seems not to have an IR interface. */
- METRAHIT_18S = 19,
- METRAHIT_2X = 20, /**< For model type comparisons */
- METRAHIT_22SM = METRAHIT_2X + 1, /**< Send mode */
- METRAHIT_22S = METRAHIT_22SM + 1, /**< Bidi mode */
- METRAHIT_22M = METRAHIT_22S + 1, /**< Bidi mode */
- METRAHIT_23S = METRAHIT_22M + 1,
- METRAHIT_24S = METRAHIT_23S + 1,
- METRAHIT_25S = METRAHIT_24S + 1,
- METRAHIT_26SM = METRAHIT_25S + 1, /**< Send mode */
- METRAHIT_26S = METRAHIT_26SM + 1, /**< Bidi mode */
- METRAHIT_26M = METRAHIT_26S + 1, /**< Bidi mode */
- METRAHIT_28S = METRAHIT_26M + 1,
- METRAHIT_29S = METRAHIT_28S + 1,
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Model-specific information */
- enum model model; /**< Model code. */
-
- /* Acquisition settings */
- uint64_t limit_samples; /**< Target number of samples */
- uint64_t limit_msec; /**< Target sampling time */
-
- /* Opaque pointer passed in by frontend. */
- void *cb_data;
-
- /* Operational state */
- gboolean settings_ok; /**< Settings msg received yet. */
- int msg_type; /**< Message type (MSGID_INF, ...). */
- int msg_len; /**< Message lengh (valid when msg, curr. type known).*/
- int mq; /**< Measured quantity */
- int unit; /**< Measured unit */
- uint64_t mqflags; /**< Measured quantity flags */
- float value; /**< Measured value */
- float scale; /**< Scale for value. */
- int8_t scale1000; /**< Additional scale factor 1000x. */
- gboolean vmains_29S; /**< Measured ctmv is V mains (29S only). */
- int addr; /**< Device address (1..15). */
- int cmd_idx; /**< Parameter "Idx" (Index) of current command, if required. */
- int cmd_seq; /**< Command sequence. Used to query status every n messages. */
- gboolean autorng; /**< Auto range enabled. */
- float ubatt; /**< Battery voltage. */
- uint8_t fw_ver_maj; /**< Firmware version major. */
- uint8_t fw_ver_min; /**< Firmware version minor. */
- int64_t req_sent_at; /**< Request sent. */
- gboolean response_pending; /**< Request sent, response is pending. */
-
- /* Temporary state across callbacks */
- uint64_t num_samples; /**< Current #samples for limit_samples */
- GTimer *elapsed_msec; /**< Used for sampling with limit_msec */
- uint8_t buf[GMC_BUFSIZE]; /**< Buffer for read callback */
- int buflen; /**< Data len in buf */
-};
-
-/* Forward declarations */
-SR_PRIV int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg);
-SR_PRIV void create_cmd_14(guchar addr, guchar func, guchar* params, guchar* buf);
-SR_PRIV void dump_msg14(guchar* buf, gboolean raw);
-SR_PRIV int gmc_decode_model_bd(uint8_t mcode);
-SR_PRIV int gmc_decode_model_sm(uint8_t mcode);
-SR_PRIV int gmc_mh_1x_2x_receive_data(int fd, int revents, void *cb_data);
-SR_PRIV int gmc_mh_2x_receive_data(int fd, int revents, void *cb_data);
-SR_PRIV const char *gmc_model_str(enum model mcode);
-SR_PRIV int process_msg14(struct sr_dev_inst *sdi);
-SR_PRIV int req_meas14(const struct sr_dev_inst *sdi);
-SR_PRIV int req_stat14(const struct sr_dev_inst *sdi, gboolean power_on);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
- *
- * 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 <stdlib.h>
-#include "protocol.h"
-
-#define SERIALCOMM "115200/8n1/flow=1"
-
-SR_PRIV struct sr_dev_driver hameg_hmo_driver_info;
-static struct sr_dev_driver *di = &hameg_hmo_driver_info;
-
-static const char *manufacturers[] = {
- "HAMEG",
-};
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-enum {
- CG_INVALID = -1,
- CG_NONE,
- CG_ANALOG,
- CG_DIGITAL,
-};
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static int check_manufacturer(const char *manufacturer)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(manufacturers); ++i)
- if (!strcmp(manufacturer, manufacturers[i]))
- return SR_OK;
-
- return SR_ERR;
-}
-
-static struct sr_dev_inst *hmo_probe_serial_device(struct sr_scpi_dev_inst *scpi)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_scpi_hw_info *hw_info;
-
- sdi = NULL;
- devc = NULL;
- hw_info = NULL;
-
- if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
- sr_info("Couldn't get IDN response.");
- goto fail;
- }
-
- if (check_manufacturer(hw_info->manufacturer) != SR_OK)
- goto fail;
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE,
- hw_info->manufacturer, hw_info->model,
- hw_info->firmware_version))) {
- goto fail;
- }
- sr_scpi_hw_info_free(hw_info);
- hw_info = NULL;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
- goto fail;
-
- sdi->driver = di;
- sdi->priv = devc;
- sdi->inst_type = SR_INST_SCPI;
- sdi->conn = scpi;
-
- if (hmo_init_device(sdi) != SR_OK)
- goto fail;
-
- sr_scpi_close(sdi->conn);
-
- sdi->status = SR_ST_INACTIVE;
-
- return sdi;
-
-fail:
- if (hw_info)
- sr_scpi_hw_info_free(hw_info);
- if (sdi)
- sr_dev_inst_free(sdi);
- if (devc)
- g_free(devc);
-
- return NULL;
-}
-
-static GSList *scan(GSList *options)
-{
- return sr_scpi_scan(di->priv, options, hmo_probe_serial_device);
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static void clear_helper(void *priv)
-{
- unsigned int i;
- struct dev_context *devc;
- struct scope_config *model;
-
- devc = priv;
- model = devc->model_config;
-
- hmo_scope_state_free(devc->model_state);
-
- for (i = 0; i < model->analog_channels; ++i)
- g_slist_free(devc->analog_groups[i].channels);
-
- for (i = 0; i < model->digital_pods; ++i) {
- g_slist_free(devc->digital_groups[i].channels);
- g_free(devc->digital_groups[i].name);
- }
-
- g_free(devc->analog_groups);
- g_free(devc->digital_groups);
-
- g_free(devc);
-}
-
-static int dev_clear(void)
-{
- return std_dev_clear(di, clear_helper);
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- if (sdi->status != SR_ST_ACTIVE && sr_scpi_open(sdi->conn) != SR_OK)
- return SR_ERR;
-
- if (hmo_scope_state_get(sdi) != SR_OK)
- return SR_ERR;
-
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- if (sdi->status == SR_ST_INACTIVE)
- return SR_OK;
-
- sr_scpi_close(sdi->conn);
-
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- dev_clear();
-
- return SR_OK;
-}
-
-static int check_channel_group(struct dev_context *devc,
- const struct sr_channel_group *cg)
-{
- unsigned int i;
- struct scope_config *model;
-
- model = devc->model_config;
-
- if (!cg)
- return CG_NONE;
-
- for (i = 0; i < model->analog_channels; ++i)
- if (cg == &devc->analog_groups[i])
- return CG_ANALOG;
-
- for (i = 0; i < model->digital_pods; ++i)
- if (cg == &devc->digital_groups[i])
- return CG_DIGITAL;
-
- sr_err("Invalid channel group specified.");
-
- return CG_INVALID;
-}
-
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- int ret, cg_type;
- unsigned int i;
- struct dev_context *devc;
- struct scope_config *model;
- struct scope_state *state;
-
- if (!sdi || !(devc = sdi->priv))
- return SR_ERR_ARG;
-
- if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
- return SR_ERR;
-
- ret = SR_ERR_NA;
- model = devc->model_config;
- state = devc->model_state;
-
- switch (key) {
- case SR_CONF_NUM_TIMEBASE:
- *data = g_variant_new_int32(model->num_xdivs);
- ret = SR_OK;
- break;
- case SR_CONF_TIMEBASE:
- *data = g_variant_new("(tt)", (*model->timebases)[state->timebase][0],
- (*model->timebases)[state->timebase][1]);
- ret = SR_OK;
- break;
- case SR_CONF_NUM_VDIV:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
- return SR_ERR_CHANNEL_GROUP;
- } else if (cg_type == CG_ANALOG) {
- for (i = 0; i < model->analog_channels; ++i) {
- if (cg != &devc->analog_groups[i])
- continue;
- *data = g_variant_new_int32(model->num_ydivs);
- ret = SR_OK;
- break;
- }
-
- } else {
- ret = SR_ERR_NA;
- }
- break;
- case SR_CONF_VDIV:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
- return SR_ERR_CHANNEL_GROUP;
- } else if (cg_type == CG_ANALOG) {
- for (i = 0; i < model->analog_channels; ++i) {
- if (cg != &devc->analog_groups[i])
- continue;
- *data = g_variant_new("(tt)",
- (*model->vdivs)[state->analog_channels[i].vdiv][0],
- (*model->vdivs)[state->analog_channels[i].vdiv][1]);
- ret = SR_OK;
- break;
- }
-
- } else {
- ret = SR_ERR_NA;
- }
- break;
- case SR_CONF_TRIGGER_SOURCE:
- *data = g_variant_new_string((*model->trigger_sources)[state->trigger_source]);
- ret = SR_OK;
- break;
- case SR_CONF_TRIGGER_SLOPE:
- *data = g_variant_new_string((*model->trigger_slopes)[state->trigger_slope]);
- ret = SR_OK;
- break;
- case SR_CONF_HORIZ_TRIGGERPOS:
- *data = g_variant_new_double(state->horiz_triggerpos);
- ret = SR_OK;
- break;
- case SR_CONF_COUPLING:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
- return SR_ERR_CHANNEL_GROUP;
- } else if (cg_type == CG_ANALOG) {
- for (i = 0; i < model->analog_channels; ++i) {
- if (cg != &devc->analog_groups[i])
- continue;
- *data = g_variant_new_string((*model->coupling_options)[state->analog_channels[i].coupling]);
- ret = SR_OK;
- break;
- }
-
- } else {
- ret = SR_ERR_NA;
- }
- break;
- case SR_CONF_SAMPLERATE:
- *data = g_variant_new_uint64(state->sample_rate);
- ret = SR_OK;
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static GVariant *build_tuples(const uint64_t (*array)[][2], unsigned int n)
-{
- unsigned int i;
- GVariant *rational[2];
- GVariantBuilder gvb;
-
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-
- for (i = 0; i < n; i++) {
- rational[0] = g_variant_new_uint64((*array)[i][0]);
- rational[1] = g_variant_new_uint64((*array)[i][1]);
-
- /* FIXME: Valgrind reports a memory leak here. */
- g_variant_builder_add_value(&gvb, g_variant_new_tuple(rational, 2));
- }
-
- return g_variant_builder_end(&gvb);
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- int ret, cg_type;
- unsigned int i, j;
- char command[MAX_COMMAND_SIZE], float_str[30];
- struct dev_context *devc;
- struct scope_config *model;
- struct scope_state *state;
- const char *tmp;
- uint64_t p, q;
- double tmp_d;
- gboolean update_sample_rate;
-
- if (!sdi || !(devc = sdi->priv))
- return SR_ERR_ARG;
-
- if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
- return SR_ERR;
-
- model = devc->model_config;
- state = devc->model_state;
- update_sample_rate = FALSE;
-
- ret = SR_ERR_NA;
-
- switch (key) {
- case SR_CONF_LIMIT_FRAMES:
- devc->frame_limit = g_variant_get_uint64(data);
- ret = SR_OK;
- break;
- case SR_CONF_TRIGGER_SOURCE:
- tmp = g_variant_get_string(data, NULL);
- for (i = 0; (*model->trigger_sources)[i]; i++) {
- if (g_strcmp0(tmp, (*model->trigger_sources)[i]) != 0)
- continue;
- state->trigger_source = i;
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SOURCE],
- (*model->trigger_sources)[i]);
-
- ret = sr_scpi_send(sdi->conn, command);
- break;
- }
- break;
- case SR_CONF_VDIV:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
- return SR_ERR_CHANNEL_GROUP;
- }
-
- g_variant_get(data, "(tt)", &p, &q);
-
- for (i = 0; i < model->num_vdivs; i++) {
- if (p != (*model->vdivs)[i][0] ||
- q != (*model->vdivs)[i][1])
- continue;
- for (j = 1; j <= model->analog_channels; ++j) {
- if (cg != &devc->analog_groups[j - 1])
- continue;
- state->analog_channels[j - 1].vdiv = i;
- g_ascii_formatd(float_str, sizeof(float_str), "%E", (float) p / q);
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_VERTICAL_DIV],
- j, float_str);
-
- if (sr_scpi_send(sdi->conn, command) != SR_OK ||
- sr_scpi_get_opc(sdi->conn) != SR_OK)
- return SR_ERR;
-
- break;
- }
-
- ret = SR_OK;
- break;
- }
- break;
- case SR_CONF_TIMEBASE:
- g_variant_get(data, "(tt)", &p, &q);
-
- for (i = 0; i < model->num_timebases; i++) {
- if (p != (*model->timebases)[i][0] ||
- q != (*model->timebases)[i][1])
- continue;
- state->timebase = i;
- g_ascii_formatd(float_str, sizeof(float_str), "%E", (float) p / q);
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_TIMEBASE],
- float_str);
-
- ret = sr_scpi_send(sdi->conn, command);
- update_sample_rate = TRUE;
- break;
- }
- break;
- case SR_CONF_HORIZ_TRIGGERPOS:
- tmp_d = g_variant_get_double(data);
-
- if (tmp_d < 0.0 || tmp_d > 1.0)
- return SR_ERR;
-
- state->horiz_triggerpos = tmp_d;
- tmp_d = -(tmp_d - 0.5) *
- ((double) (*model->timebases)[state->timebase][0] /
- (*model->timebases)[state->timebase][1])
- * model->num_xdivs;
-
- g_ascii_formatd(float_str, sizeof(float_str), "%E", tmp_d);
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_HORIZ_TRIGGERPOS],
- float_str);
-
- ret = sr_scpi_send(sdi->conn, command);
- break;
- case SR_CONF_TRIGGER_SLOPE:
- tmp = g_variant_get_string(data, NULL);
-
- if (!tmp || !(tmp[0] == 'f' || tmp[0] == 'r'))
- return SR_ERR_ARG;
-
- state->trigger_slope = (tmp[0] == 'r') ? 0 : 1;
-
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SLOPE],
- (state->trigger_slope == 0) ? "POS" : "NEG");
-
- ret = sr_scpi_send(sdi->conn, command);
- break;
- case SR_CONF_COUPLING:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
- return SR_ERR_CHANNEL_GROUP;
- }
-
- tmp = g_variant_get_string(data, NULL);
-
- for (i = 0; (*model->coupling_options)[i]; i++) {
- if (strcmp(tmp, (*model->coupling_options)[i]) != 0)
- continue;
- for (j = 1; j <= model->analog_channels; ++j) {
- if (cg != &devc->analog_groups[j - 1])
- continue;
- state->analog_channels[j-1].coupling = i;
-
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_COUPLING],
- j, tmp);
-
- if (sr_scpi_send(sdi->conn, command) != SR_OK ||
- sr_scpi_get_opc(sdi->conn) != SR_OK)
- return SR_ERR;
- break;
- }
-
- ret = SR_OK;
- break;
- }
- break;
- default:
- ret = SR_ERR_NA;
- break;
- }
-
- if (ret == SR_OK)
- ret = sr_scpi_get_opc(sdi->conn);
-
- if (ret == SR_OK && update_sample_rate)
- ret = hmo_update_sample_rate(sdi);
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- int cg_type;
- struct dev_context *devc;
- struct scope_config *model;
-
- if (!sdi || !(devc = sdi->priv))
- return SR_ERR_ARG;
-
- if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
- return SR_ERR;
-
- model = devc->model_config;
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- if (cg_type == CG_NONE) {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- model->hw_caps, model->num_hwcaps,
- sizeof(int32_t));
- } else if (cg_type == CG_ANALOG) {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- model->analog_hwcaps, model->num_analog_hwcaps,
- sizeof(int32_t));
- } else {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- NULL, 0, sizeof(int32_t));
- }
- break;
- case SR_CONF_COUPLING:
- if (cg_type == CG_NONE)
- return SR_ERR_CHANNEL_GROUP;
- *data = g_variant_new_strv(*model->coupling_options,
- g_strv_length((char **)*model->coupling_options));
- break;
- case SR_CONF_TRIGGER_SOURCE:
- *data = g_variant_new_strv(*model->trigger_sources,
- g_strv_length((char **)*model->trigger_sources));
- break;
- case SR_CONF_TRIGGER_SLOPE:
- *data = g_variant_new_strv(*model->trigger_slopes,
- g_strv_length((char **)*model->trigger_slopes));
- break;
- case SR_CONF_TIMEBASE:
- *data = build_tuples(model->timebases, model->num_timebases);
- break;
- case SR_CONF_VDIV:
- if (cg_type == CG_NONE)
- return SR_ERR_CHANNEL_GROUP;
- *data = build_tuples(model->vdivs, model->num_vdivs);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int hmo_request_data(const struct sr_dev_inst *sdi)
-{
- char command[MAX_COMMAND_SIZE];
- struct sr_channel *ch;
- struct dev_context *devc;
- struct scope_config *model;
-
- devc = sdi->priv;
- model = devc->model_config;
-
- ch = devc->current_channel->data;
-
- switch (ch->type) {
- case SR_CHANNEL_ANALOG:
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_GET_ANALOG_DATA],
- ch->index + 1);
- break;
- case SR_CHANNEL_LOGIC:
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_GET_DIG_DATA],
- ch->index < 8 ? 1 : 2);
- break;
- default:
- sr_err("Invalid channel type.");
- break;
- }
-
- return sr_scpi_send(sdi->conn, command);
-}
-
-static int hmo_check_channels(GSList *channels)
-{
- GSList *l;
- struct sr_channel *ch;
- gboolean enabled_pod1, enabled_pod2, enabled_chan3, enabled_chan4;
-
- enabled_pod1 = enabled_pod2 = enabled_chan3 = enabled_chan4 = FALSE;
-
- for (l = channels; l; l = l->next) {
- ch = l->data;
- switch (ch->type) {
- case SR_CHANNEL_ANALOG:
- if (ch->index == 2)
- enabled_chan3 = TRUE;
- else if (ch->index == 3)
- enabled_chan4 = TRUE;
- break;
- case SR_CHANNEL_LOGIC:
- if (ch->index < 8)
- enabled_pod1 = TRUE;
- else
- enabled_pod2 = TRUE;
- break;
- default:
- return SR_ERR;
- }
- }
-
- if ((enabled_pod1 && enabled_chan3) ||
- (enabled_pod2 && enabled_chan4))
- return SR_ERR;
-
- return SR_OK;
-}
-
-static int hmo_setup_channels(const struct sr_dev_inst *sdi)
-{
- GSList *l;
- unsigned int i;
- gboolean *pod_enabled, setup_changed;
- char command[MAX_COMMAND_SIZE];
- struct scope_state *state;
- struct scope_config *model;
- struct sr_channel *ch;
- struct dev_context *devc;
- struct sr_scpi_dev_inst *scpi;
-
- devc = sdi->priv;
- scpi = sdi->conn;
- state = devc->model_state;
- model = devc->model_config;
- setup_changed = FALSE;
-
- pod_enabled = g_try_malloc0(sizeof(gboolean) * model->digital_pods);
-
- for (l = sdi->channels; l; l = l->next) {
- ch = l->data;
- switch (ch->type) {
- case SR_CHANNEL_ANALOG:
- if (ch->enabled == state->analog_channels[ch->index].state)
- break;
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_ANALOG_CHAN_STATE],
- ch->index + 1, ch->enabled);
-
- if (sr_scpi_send(scpi, command) != SR_OK)
- return SR_ERR;
- state->analog_channels[ch->index].state = ch->enabled;
- setup_changed = TRUE;
- break;
- case SR_CHANNEL_LOGIC:
- /*
- * A digital POD needs to be enabled for every group of
- * 8 channels.
- */
- if (ch->enabled)
- pod_enabled[ch->index < 8 ? 0 : 1] = TRUE;
-
- if (ch->enabled == state->digital_channels[ch->index])
- break;
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_DIG_CHAN_STATE],
- ch->index, ch->enabled);
-
- if (sr_scpi_send(scpi, command) != SR_OK)
- return SR_ERR;
-
- state->digital_channels[ch->index] = ch->enabled;
- setup_changed = TRUE;
- break;
- default:
- return SR_ERR;
- }
- }
-
- for (i = 1; i <= model->digital_pods; ++i) {
- if (state->digital_pods[i - 1] == pod_enabled[i - 1])
- continue;
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_DIG_POD_STATE],
- i, pod_enabled[i - 1]);
- if (sr_scpi_send(scpi, command) != SR_OK)
- return SR_ERR;
- state->digital_pods[i - 1] = pod_enabled[i - 1];
- setup_changed = TRUE;
- }
-
- g_free(pod_enabled);
-
- if (setup_changed && hmo_update_sample_rate(sdi) != SR_OK)
- return SR_ERR;
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- GSList *l;
- gboolean digital_added;
- struct sr_channel *ch;
- struct dev_context *devc;
- struct sr_scpi_dev_inst *scpi;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- scpi = sdi->conn;
- devc = sdi->priv;
- digital_added = FALSE;
-
- for (l = sdi->channels; l; l = l->next) {
- ch = l->data;
- if (!ch->enabled)
- continue;
- /* Only add a single digital channel. */
- if (ch->type != SR_CHANNEL_LOGIC || !digital_added) {
- devc->enabled_channels = g_slist_append(
- devc->enabled_channels, ch);
- if (ch->type == SR_CHANNEL_LOGIC)
- digital_added = TRUE;
- }
- }
-
- if (!devc->enabled_channels)
- return SR_ERR;
-
- if (hmo_check_channels(devc->enabled_channels) != SR_OK) {
- sr_err("Invalid channel configuration specified!");
- return SR_ERR_NA;
- }
-
- if (hmo_setup_channels(sdi) != SR_OK) {
- sr_err("Failed to setup channel configuration!");
- return SR_ERR;
- }
-
- sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 50,
- hmo_receive_data, (void *)sdi);
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- devc->current_channel = devc->enabled_channels;
-
- return hmo_request_data(sdi);
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct sr_scpi_dev_inst *scpi;
- struct sr_datafeed_packet packet;
-
- (void)cb_data;
-
- packet.type = SR_DF_END;
- packet.payload = NULL;
- sr_session_send(sdi, &packet);
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- devc->num_frames = 0;
- g_slist_free(devc->enabled_channels);
- devc->enabled_channels = NULL;
- scpi = sdi->conn;
- sr_scpi_source_remove(sdi->session, scpi);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver hameg_hmo_driver_info = {
- .name = "hameg-hmo",
- .longname = "Hameg HMO",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = 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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
- *
- * 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 "protocol.h"
-
-static const char *hameg_scpi_dialect[] = {
- [SCPI_CMD_GET_DIG_DATA] = ":POD%d:DATA?",
- [SCPI_CMD_GET_TIMEBASE] = ":TIM:SCAL?",
- [SCPI_CMD_SET_TIMEBASE] = ":TIM:SCAL %s",
- [SCPI_CMD_GET_COUPLING] = ":CHAN%d:COUP?",
- [SCPI_CMD_SET_COUPLING] = ":CHAN%d:COUP %s",
- [SCPI_CMD_GET_SAMPLE_RATE] = ":ACQ:SRAT?",
- [SCPI_CMD_GET_SAMPLE_RATE_LIVE] = ":%s:DATA:POINTS?",
- [SCPI_CMD_GET_ANALOG_DATA] = ":CHAN%d:DATA?",
- [SCPI_CMD_GET_VERTICAL_DIV] = ":CHAN%d:SCAL?",
- [SCPI_CMD_SET_VERTICAL_DIV] = ":CHAN%d:SCAL %s",
- [SCPI_CMD_GET_DIG_POD_STATE] = ":POD%d:STAT?",
- [SCPI_CMD_SET_DIG_POD_STATE] = ":POD%d:STAT %d",
- [SCPI_CMD_GET_TRIGGER_SLOPE] = ":TRIG:A:EDGE:SLOP?",
- [SCPI_CMD_SET_TRIGGER_SLOPE] = ":TRIG:A:EDGE:SLOP %s",
- [SCPI_CMD_GET_TRIGGER_SOURCE] = ":TRIG:A:SOUR?",
- [SCPI_CMD_SET_TRIGGER_SOURCE] = ":TRIG:A:SOUR %s",
- [SCPI_CMD_GET_DIG_CHAN_STATE] = ":LOG%d:STAT?",
- [SCPI_CMD_SET_DIG_CHAN_STATE] = ":LOG%d:STAT %d",
- [SCPI_CMD_GET_VERTICAL_OFFSET] = ":CHAN%d:POS?",
- [SCPI_CMD_GET_HORIZ_TRIGGERPOS] = ":TIM:POS?",
- [SCPI_CMD_SET_HORIZ_TRIGGERPOS] = ":TIM:POS %s",
- [SCPI_CMD_GET_ANALOG_CHAN_STATE] = ":CHAN%d:STAT?",
- [SCPI_CMD_SET_ANALOG_CHAN_STATE] = ":CHAN%d:STAT %d",
-};
-
-static const int32_t hmo_hwcaps[] = {
- SR_CONF_OSCILLOSCOPE,
- SR_CONF_TRIGGER_SOURCE,
- SR_CONF_TIMEBASE,
- SR_CONF_NUM_TIMEBASE,
- SR_CONF_TRIGGER_SLOPE,
- SR_CONF_HORIZ_TRIGGERPOS,
- SR_CONF_SAMPLERATE,
- SR_CONF_LIMIT_FRAMES,
-};
-
-static const int32_t hmo_analog_caps[] = {
- SR_CONF_NUM_VDIV,
- SR_CONF_COUPLING,
- SR_CONF_VDIV,
-};
-
-static const char *hmo_coupling_options[] = {
- "AC",
- "ACL",
- "DC",
- "DCL",
- "GND",
- NULL,
-};
-
-static const char *scope_trigger_slopes[] = {
- "POS",
- "NEG",
- NULL,
-};
-
-static const char *hmo_compact2_trigger_sources[] = {
- "CH1",
- "CH2",
- "LINE",
- "EXT",
- "D0",
- "D1",
- "D2",
- "D3",
- "D4",
- "D5",
- "D6",
- "D7",
- NULL,
-};
-
-static const char *hmo_compact4_trigger_sources[] = {
- "CH1",
- "CH2",
- "CH3",
- "CH4",
- "LINE",
- "EXT",
- "D0",
- "D1",
- "D2",
- "D3",
- "D4",
- "D5",
- "D6",
- "D7",
- NULL,
-};
-
-static const uint64_t hmo_timebases[][2] = {
- /* nanoseconds */
- { 2, 1000000000 },
- { 5, 1000000000 },
- { 10, 1000000000 },
- { 20, 1000000000 },
- { 50, 1000000000 },
- { 100, 1000000000 },
- { 200, 1000000000 },
- { 500, 1000000000 },
- /* microseconds */
- { 1, 1000000 },
- { 2, 1000000 },
- { 5, 1000000 },
- { 10, 1000000 },
- { 20, 1000000 },
- { 50, 1000000 },
- { 100, 1000000 },
- { 200, 1000000 },
- { 500, 1000000 },
- /* milliseconds */
- { 1, 1000 },
- { 2, 1000 },
- { 5, 1000 },
- { 10, 1000 },
- { 20, 1000 },
- { 50, 1000 },
- { 100, 1000 },
- { 200, 1000 },
- { 500, 1000 },
- /* seconds */
- { 1, 1 },
- { 2, 1 },
- { 5, 1 },
- { 10, 1 },
- { 20, 1 },
- { 50, 1 },
-};
-
-static const uint64_t hmo_vdivs[][2] = {
- /* millivolts */
- { 1, 1000 },
- { 2, 1000 },
- { 5, 1000 },
- { 10, 1000 },
- { 20, 1000 },
- { 50, 1000 },
- { 100, 1000 },
- { 200, 1000 },
- { 500, 1000 },
- /* volts */
- { 1, 1 },
- { 2, 1 },
- { 5, 1 },
- { 10, 1 },
-};
-
-static const char *scope_analog_channel_names[] = {
- "CH1",
- "CH2",
- "CH3",
- "CH4",
-};
-
-static const char *scope_digital_channel_names[] = {
- "D0",
- "D1",
- "D2",
- "D3",
- "D4",
- "D5",
- "D6",
- "D7",
- "D8",
- "D9",
- "D10",
- "D11",
- "D12",
- "D13",
- "D14",
- "D15",
-};
-
-static struct scope_config scope_models[] = {
- {
- .name = {"HMO722", "HMO1022", "HMO1522", "HMO2022", NULL},
- .analog_channels = 2,
- .digital_channels = 8,
- .digital_pods = 1,
-
- .analog_names = &scope_analog_channel_names,
- .digital_names = &scope_digital_channel_names,
-
- .hw_caps = &hmo_hwcaps,
- .num_hwcaps = ARRAY_SIZE(hmo_hwcaps),
-
- .analog_hwcaps = &hmo_analog_caps,
- .num_analog_hwcaps = ARRAY_SIZE(hmo_analog_caps),
-
- .coupling_options = &hmo_coupling_options,
- .trigger_sources = &hmo_compact2_trigger_sources,
- .trigger_slopes = &scope_trigger_slopes,
-
- .timebases = &hmo_timebases,
- .num_timebases = ARRAY_SIZE(hmo_timebases),
-
- .vdivs = &hmo_vdivs,
- .num_vdivs = ARRAY_SIZE(hmo_vdivs),
-
- .num_xdivs = 12,
- .num_ydivs = 8,
-
- .scpi_dialect = &hameg_scpi_dialect,
- },
- {
- .name = {"HMO724", "HMO1024", "HMO1524", "HMO2024", NULL},
- .analog_channels = 4,
- .digital_channels = 8,
- .digital_pods = 1,
-
- .analog_names = &scope_analog_channel_names,
- .digital_names = &scope_digital_channel_names,
-
- .hw_caps = &hmo_hwcaps,
- .num_hwcaps = ARRAY_SIZE(hmo_hwcaps),
-
- .analog_hwcaps = &hmo_analog_caps,
- .num_analog_hwcaps = ARRAY_SIZE(hmo_analog_caps),
-
- .coupling_options = &hmo_coupling_options,
- .trigger_sources = &hmo_compact4_trigger_sources,
- .trigger_slopes = &scope_trigger_slopes,
-
- .timebases = &hmo_timebases,
- .num_timebases = ARRAY_SIZE(hmo_timebases),
-
- .vdivs = &hmo_vdivs,
- .num_vdivs = ARRAY_SIZE(hmo_vdivs),
-
- .num_xdivs = 12,
- .num_ydivs = 8,
-
- .scpi_dialect = &hameg_scpi_dialect,
- },
-};
-
-static void scope_state_dump(struct scope_config *config,
- struct scope_state *state)
-{
- unsigned int i;
- char *tmp;
-
- for (i = 0; i < config->analog_channels; ++i) {
- tmp = sr_voltage_string((*config->vdivs)[state->analog_channels[i].vdiv][0],
- (*config->vdivs)[state->analog_channels[i].vdiv][1]);
- sr_info("State of analog channel %d -> %s : %s (coupling) %s (vdiv) %2.2e (offset)",
- i + 1, state->analog_channels[i].state ? "On" : "Off",
- (*config->coupling_options)[state->analog_channels[i].coupling],
- tmp, state->analog_channels[i].vertical_offset);
- }
-
- for (i = 0; i < config->digital_channels; ++i) {
- sr_info("State of digital channel %d -> %s", i,
- state->digital_channels[i] ? "On" : "Off");
- }
-
- for (i = 0; i < config->digital_pods; ++i) {
- sr_info("State of digital POD %d -> %s", i,
- state->digital_pods[i] ? "On" : "Off");
- }
-
- tmp = sr_period_string((*config->timebases)[state->timebase][0] *
- (*config->timebases)[state->timebase][1]);
- sr_info("Current timebase: %s", tmp);
- g_free(tmp);
-
- tmp = sr_samplerate_string(state->sample_rate);
- sr_info("Current samplerate: %s", tmp);
- g_free(tmp);
-
- sr_info("Current trigger: %s (source), %s (slope) %.2f (offset)",
- (*config->trigger_sources)[state->trigger_source],
- (*config->trigger_slopes)[state->trigger_slope],
- state->horiz_triggerpos);
-}
-
-static int scope_state_get_array_option(struct sr_scpi_dev_inst *scpi,
- const char *command, const char *(*array)[], int *result)
-{
- char *tmp;
- unsigned int i;
-
- if (sr_scpi_get_string(scpi, command, &tmp) != SR_OK) {
- g_free(tmp);
- return SR_ERR;
- }
-
- for (i = 0; (*array)[i]; ++i) {
- if (!g_strcmp0(tmp, (*array)[i])) {
- *result = i;
- g_free(tmp);
- tmp = NULL;
- break;
- }
- }
-
- if (tmp) {
- g_free(tmp);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
- struct scope_config *config,
- struct scope_state *state)
-{
- unsigned int i, j;
- float tmp_float;
- char command[MAX_COMMAND_SIZE];
-
- for (i = 0; i < config->analog_channels; ++i) {
- g_snprintf(command, sizeof(command),
- (*config->scpi_dialect)[SCPI_CMD_GET_ANALOG_CHAN_STATE],
- i + 1);
-
- if (sr_scpi_get_bool(scpi, command,
- &state->analog_channels[i].state) != SR_OK)
- return SR_ERR;
-
- g_snprintf(command, sizeof(command),
- (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_DIV],
- i + 1);
-
- if (sr_scpi_get_float(scpi, command, &tmp_float) != SR_OK)
- return SR_ERR;
- for (j = 0; j < config->num_vdivs; j++) {
- if (tmp_float == ((float) (*config->vdivs)[j][0] /
- (*config->vdivs)[j][1])) {
- state->analog_channels[i].vdiv = j;
- break;
- }
- }
- if (i == config->num_vdivs)
- return SR_ERR;
-
- g_snprintf(command, sizeof(command),
- (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_OFFSET],
- i + 1);
-
- if (sr_scpi_get_float(scpi, command,
- &state->analog_channels[i].vertical_offset) != SR_OK)
- return SR_ERR;
-
- g_snprintf(command, sizeof(command),
- (*config->scpi_dialect)[SCPI_CMD_GET_COUPLING],
- i + 1);
-
- if (scope_state_get_array_option(scpi, command, config->coupling_options,
- &state->analog_channels[i].coupling) != SR_OK)
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int digital_channel_state_get(struct sr_scpi_dev_inst *scpi,
- struct scope_config *config,
- struct scope_state *state)
-{
- unsigned int i;
- char command[MAX_COMMAND_SIZE];
-
- for (i = 0; i < config->digital_channels; ++i) {
- g_snprintf(command, sizeof(command),
- (*config->scpi_dialect)[SCPI_CMD_GET_DIG_CHAN_STATE],
- i);
-
- if (sr_scpi_get_bool(scpi, command,
- &state->digital_channels[i]) != SR_OK)
- return SR_ERR;
- }
-
- for (i = 0; i < config->digital_pods; ++i) {
- g_snprintf(command, sizeof(command),
- (*config->scpi_dialect)[SCPI_CMD_GET_DIG_POD_STATE],
- i + 1);
-
- if (sr_scpi_get_bool(scpi, command,
- &state->digital_pods[i]) != SR_OK)
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct scope_state *state;
- struct scope_config *config;
-
- int tmp;
- unsigned int i;
- float tmp_float;
- gboolean channel_found;
- char tmp_str[MAX_COMMAND_SIZE];
- char chan_name[20];
-
- devc = sdi->priv;
- config = devc->model_config;
- state = devc->model_state;
- channel_found = FALSE;
-
- for (i = 0; i < config->analog_channels; ++i) {
- if (state->analog_channels[i].state) {
- g_snprintf(chan_name, sizeof(chan_name), "CHAN%d", i + 1);
- g_snprintf(tmp_str, sizeof(tmp_str),
- (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE_LIVE],
- chan_name);
- channel_found = TRUE;
- break;
- }
- }
-
- if (!channel_found) {
- for (i = 0; i < config->digital_pods; i++) {
- if (state->digital_pods[i]) {
- g_snprintf(chan_name, sizeof(chan_name), "POD%d", i);
- g_snprintf(tmp_str, sizeof(tmp_str),
- (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE_LIVE],
- chan_name);
- channel_found = TRUE;
- break;
- }
- }
- }
-
- /* No channel is active, ask the instrument for the sample rate
- * in single shot mode */
- if (!channel_found) {
- if (sr_scpi_get_float(sdi->conn,
- (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE],
- &tmp_float) != SR_OK)
- return SR_ERR;
-
- state->sample_rate = tmp_float;
- } else {
- if (sr_scpi_get_int(sdi->conn, tmp_str, &tmp) != SR_OK)
- return SR_ERR;
- state->sample_rate = tmp / (((float) (*config->timebases)[state->timebase][0] /
- (*config->timebases)[state->timebase][1]) *
- config->num_xdivs);
- }
-
- return SR_OK;
-}
-
-SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct scope_state *state;
- struct scope_config *config;
- float tmp_float;
- unsigned int i;
-
- devc = sdi->priv;
- config = devc->model_config;
- state = devc->model_state;
-
- sr_info("Fetching scope state");
-
- if (analog_channel_state_get(sdi->conn, config, state) != SR_OK)
- return SR_ERR;
-
- if (digital_channel_state_get(sdi->conn, config, state) != SR_OK)
- return SR_ERR;
-
- if (sr_scpi_get_float(sdi->conn,
- (*config->scpi_dialect)[SCPI_CMD_GET_TIMEBASE],
- &tmp_float) != SR_OK)
- return SR_ERR;
-
- for (i = 0; i < config->num_timebases; i++) {
- if (tmp_float == ((float) (*config->timebases)[i][0] /
- (*config->timebases)[i][1])) {
- state->timebase = i;
- break;
- }
- }
- if (i == config->num_timebases)
- return SR_ERR;
-
- if (sr_scpi_get_float(sdi->conn,
- (*config->scpi_dialect)[SCPI_CMD_GET_HORIZ_TRIGGERPOS],
- &tmp_float) != SR_OK)
- return SR_ERR;
- state->horiz_triggerpos = tmp_float /
- (((double) (*config->timebases)[state->timebase][0] /
- (*config->timebases)[state->timebase][1]) * config->num_xdivs);
- state->horiz_triggerpos -= 0.5;
- state->horiz_triggerpos *= -1;
-
- if (scope_state_get_array_option(sdi->conn,
- (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SOURCE],
- config->trigger_sources, &state->trigger_source) != SR_OK)
- return SR_ERR;
-
- if (scope_state_get_array_option(sdi->conn,
- (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SLOPE],
- config->trigger_slopes, &state->trigger_slope) != SR_OK)
- return SR_ERR;
-
- if (hmo_update_sample_rate(sdi) != SR_OK)
- return SR_ERR;
-
- sr_info("Fetching finished.");
-
- scope_state_dump(config, state);
-
- return SR_OK;
-}
-
-static struct scope_state *scope_state_new(struct scope_config *config)
-{
- struct scope_state *state;
-
- if (!(state = g_try_malloc0(sizeof(struct scope_state))))
- return NULL;
-
- if (!(state->analog_channels = g_try_malloc0_n(config->analog_channels,
- sizeof(struct analog_channel_state))))
- goto fail;
-
- if (!(state->digital_channels = g_try_malloc0_n(
- config->digital_channels, sizeof(gboolean))))
- goto fail;
-
- if (!(state->digital_pods = g_try_malloc0_n(config->digital_pods,
- sizeof(gboolean))))
- goto fail;
-
- return state;
-
-fail:
- if (state->analog_channels)
- g_free(state->analog_channels);
- if (state->digital_channels)
- g_free(state->digital_channels);
- if (state->digital_pods)
- g_free(state->digital_pods);
- g_free(state);
-
- return NULL;
-}
-
-SR_PRIV void hmo_scope_state_free(struct scope_state *state)
-{
- g_free(state->analog_channels);
- g_free(state->digital_channels);
- g_free(state->digital_pods);
- g_free(state);
-}
-
-SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi)
-{
- char tmp[25];
- int model_index;
- unsigned int i, j;
- struct sr_channel *ch;
- struct dev_context *devc;
-
- devc = sdi->priv;
- model_index = -1;
-
- /* Find the exact model. */
- for (i = 0; i < ARRAY_SIZE(scope_models); i++) {
- for (j = 0; scope_models[i].name[j]; j++) {
- if (!strcmp(sdi->model, scope_models[i].name[j])) {
- model_index = i;
- break;
- }
- }
- if (model_index != -1)
- break;
- }
-
- if (model_index == -1) {
- sr_dbg("Unsupported HMO device.");
- return SR_ERR_NA;
- }
-
- if (!(devc->analog_groups = g_try_malloc0(sizeof(struct sr_channel_group) *
- scope_models[model_index].analog_channels)))
- return SR_ERR_MALLOC;
-
- if (!(devc->digital_groups = g_try_malloc0(sizeof(struct sr_channel_group) *
- scope_models[model_index].digital_pods)))
- return SR_ERR_MALLOC;
-
- /* Add analog channels. */
- for (i = 0; i < scope_models[model_index].analog_channels; i++) {
- if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE,
- (*scope_models[model_index].analog_names)[i])))
- return SR_ERR_MALLOC;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- devc->analog_groups[i].name =
- (char *)(*scope_models[model_index].analog_names)[i];
- devc->analog_groups[i].channels = g_slist_append(NULL, ch);
-
- sdi->channel_groups = g_slist_append(sdi->channel_groups,
- &devc->analog_groups[i]);
- }
-
- /* Add digital channel groups. */
- for (i = 0; i < scope_models[model_index].digital_pods; ++i) {
- g_snprintf(tmp, 25, "POD%d", i);
- devc->digital_groups[i].name = g_strdup(tmp);
- sdi->channel_groups = g_slist_append(sdi->channel_groups,
- &devc->digital_groups[i < 8 ? 0 : 1]);
- }
-
- /* Add digital channels. */
- for (i = 0; i < scope_models[model_index].digital_channels; i++) {
- if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
- (*scope_models[model_index].digital_names)[i])))
- return SR_ERR_MALLOC;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- devc->digital_groups[i < 8 ? 0 : 1].channels = g_slist_append(
- devc->digital_groups[i < 8 ? 0 : 1].channels, ch);
- }
-
- devc->model_config = &scope_models[model_index];
- devc->frame_limit = 0;
-
- if (!(devc->model_state = scope_state_new(devc->model_config)))
- return SR_ERR_MALLOC;
-
- return SR_OK;
-}
-
-SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_channel *ch;
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- GArray *data;
- struct sr_datafeed_analog analog;
- struct sr_datafeed_logic logic;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- if (revents == G_IO_IN) {
- ch = devc->current_channel->data;
-
- switch (ch->type) {
- case SR_CHANNEL_ANALOG:
- if (sr_scpi_get_floatv(sdi->conn, NULL, &data) != SR_OK) {
- if (data)
- g_array_free(data, TRUE);
-
- return TRUE;
- }
-
- packet.type = SR_DF_FRAME_BEGIN;
- sr_session_send(sdi, &packet);
-
- analog.channels = g_slist_append(NULL, ch);
- analog.num_samples = data->len;
- analog.data = (float *) data->data;
- analog.mq = SR_MQ_VOLTAGE;
- analog.unit = SR_UNIT_VOLT;
- analog.mqflags = 0;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(cb_data, &packet);
- g_slist_free(analog.channels);
- g_array_free(data, TRUE);
- break;
- case SR_CHANNEL_LOGIC:
- if (sr_scpi_get_uint8v(sdi->conn, NULL, &data) != SR_OK) {
- if (data)
- g_free(data);
- return TRUE;
- }
-
- packet.type = SR_DF_FRAME_BEGIN;
- sr_session_send(sdi, &packet);
-
- logic.length = data->len;
- logic.unitsize = 1;
- logic.data = data->data;
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- sr_session_send(cb_data, &packet);
- g_array_free(data, TRUE);
- break;
- default:
- sr_err("Invalid channel type.");
- break;
- }
-
- packet.type = SR_DF_FRAME_END;
- sr_session_send(sdi, &packet);
-
- if (devc->current_channel->next) {
- devc->current_channel = devc->current_channel->next;
- hmo_request_data(sdi);
- } else if (++devc->num_frames == devc->frame_limit) {
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- } else {
- devc->current_channel = devc->enabled_channels;
- hmo_request_data(sdi);
- }
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
- *
- * 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_HAMEG_HMO_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_HAMEG_HMO_PROTOCOL_H
-
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "hameg-hmo"
-
-#define MAX_INSTRUMENT_VERSIONS 10
-#define MAX_COMMAND_SIZE 31
-
-struct scope_config {
- const char *name[MAX_INSTRUMENT_VERSIONS];
- const uint8_t analog_channels;
- const uint8_t digital_channels;
- const uint8_t digital_pods;
-
- const char *(*analog_names)[];
- const char *(*digital_names)[];
-
- const int32_t (*hw_caps)[];
- const uint8_t num_hwcaps;
-
- const int32_t (*analog_hwcaps)[];
- const uint8_t num_analog_hwcaps;
-
- const char *(*coupling_options)[];
- const uint8_t num_coupling_options;
-
- const char *(*trigger_sources)[];
- const uint8_t num_trigger_sources;
-
- const char *(*trigger_slopes)[];
-
- const uint64_t (*timebases)[][2];
- const uint8_t num_timebases;
-
- const uint64_t (*vdivs)[][2];
- const uint8_t num_vdivs;
-
- const uint8_t num_xdivs;
- const uint8_t num_ydivs;
-
- const char *(*scpi_dialect)[];
-};
-
-struct analog_channel_state {
- int coupling;
-
- int vdiv;
- float vertical_offset;
-
- gboolean state;
-};
-
-struct scope_state {
- struct analog_channel_state *analog_channels;
- gboolean *digital_channels;
- gboolean *digital_pods;
-
- int timebase;
- float horiz_triggerpos;
-
- int trigger_source;
- int trigger_slope;
- uint64_t sample_rate;
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- void *model_config;
- void *model_state;
-
- struct sr_channel_group *analog_groups;
- struct sr_channel_group *digital_groups;
-
- GSList *enabled_channels;
- GSList *current_channel;
- uint64_t num_frames;
-
- uint64_t frame_limit;
-};
-
-SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi);
-SR_PRIV int hmo_request_data(const struct sr_dev_inst *sdi);
-SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data);
-
-SR_PRIV struct scope_state *hmo_scope_state_new(struct scope_config *config);
-SR_PRIV void hmo_scope_state_free(struct scope_state *state);
-SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi);
-SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/time.h>
-#include <inttypes.h>
-#include <glib.h>
-#include <libusb.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "dso.h"
-
-/* Max time in ms before we want to check on USB events */
-/* TODO tune this properly */
-#define TICK 1
-
-#define NUM_TIMEBASE 10
-#define NUM_VDIV 8
-
-static const int32_t scanopts[] = {
- SR_CONF_CONN,
-};
-
-static const int32_t devopts[] = {
- SR_CONF_OSCILLOSCOPE,
- SR_CONF_LIMIT_FRAMES,
- SR_CONF_CONTINUOUS,
- SR_CONF_TIMEBASE,
- SR_CONF_BUFFERSIZE,
- SR_CONF_TRIGGER_SOURCE,
- SR_CONF_TRIGGER_SLOPE,
- SR_CONF_HORIZ_TRIGGERPOS,
- SR_CONF_FILTER,
- SR_CONF_VDIV,
- SR_CONF_COUPLING,
- SR_CONF_NUM_TIMEBASE,
- SR_CONF_NUM_VDIV,
-};
-
-static const char *channel_names[] = {
- "CH1", "CH2",
- NULL,
-};
-
-static const uint64_t buffersizes_32k[] = {
- 10240, 32768,
-};
-static const uint64_t buffersizes_512k[] = {
- 10240, 524288,
-};
-static const uint64_t buffersizes_14k[] = {
- 10240, 14336,
-};
-
-static const struct dso_profile dev_profiles[] = {
- { 0x04b4, 0x2090, 0x04b5, 0x2090,
- "Hantek", "DSO-2090",
- buffersizes_32k,
- FIRMWARE_DIR "/hantek-dso-2090.fw" },
- { 0x04b4, 0x2150, 0x04b5, 0x2150,
- "Hantek", "DSO-2150",
- buffersizes_32k,
- FIRMWARE_DIR "/hantek-dso-2150.fw" },
- { 0x04b4, 0x2250, 0x04b5, 0x2250,
- "Hantek", "DSO-2250",
- buffersizes_512k,
- FIRMWARE_DIR "/hantek-dso-2250.fw" },
- { 0x04b4, 0x5200, 0x04b5, 0x5200,
- "Hantek", "DSO-5200",
- buffersizes_14k,
- FIRMWARE_DIR "/hantek-dso-5200.fw" },
- { 0x04b4, 0x520a, 0x04b5, 0x520a,
- "Hantek", "DSO-5200A",
- buffersizes_512k,
- FIRMWARE_DIR "/hantek-dso-5200A.fw" },
- { 0, 0, 0, 0, 0, 0, 0, 0 },
-};
-
-static const uint64_t timebases[][2] = {
- /* microseconds */
- { 10, 1000000 },
- { 20, 1000000 },
- { 40, 1000000 },
- { 100, 1000000 },
- { 200, 1000000 },
- { 400, 1000000 },
- /* milliseconds */
- { 1, 1000 },
- { 2, 1000 },
- { 4, 1000 },
- { 10, 1000 },
- { 20, 1000 },
- { 40, 1000 },
- { 100, 1000 },
- { 200, 1000 },
- { 400, 1000 },
-};
-
-static const uint64_t vdivs[][2] = {
- /* millivolts */
- { 10, 1000 },
- { 20, 1000 },
- { 50, 1000 },
- { 100, 1000 },
- { 200, 1000 },
- { 500, 1000 },
- /* volts */
- { 1, 1 },
- { 2, 1 },
- { 5, 1 },
-};
-
-static const char *trigger_sources[] = {
- "CH1",
- "CH2",
- "EXT",
- /* TODO: forced */
-};
-
-static const char *filter_targets[] = {
- "CH1",
- "CH2",
- /* TODO: "TRIGGER", */
-};
-
-static const char *coupling[] = {
- "AC",
- "DC",
- "GND",
-};
-
-SR_PRIV struct sr_dev_driver hantek_dso_driver_info;
-static struct sr_dev_driver *di = &hantek_dso_driver_info;
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
-
-static struct sr_dev_inst *dso_dev_new(int index, const struct dso_profile *prof)
-{
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- struct drv_context *drvc;
- struct dev_context *devc;
- int i;
-
- sdi = sr_dev_inst_new(index, SR_ST_INITIALIZING,
- prof->vendor, prof->model, NULL);
- if (!sdi)
- return NULL;
- sdi->driver = di;
-
- /*
- * Add only the real channels -- EXT isn't a source of data, only
- * a trigger source internal to the device.
- */
- for (i = 0; channel_names[i]; i++) {
- if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE,
- channel_names[i])))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
-
- devc->profile = prof;
- devc->dev_state = IDLE;
- devc->timebase = DEFAULT_TIMEBASE;
- devc->ch1_enabled = TRUE;
- devc->ch2_enabled = TRUE;
- devc->voltage_ch1 = DEFAULT_VOLTAGE;
- devc->voltage_ch2 = DEFAULT_VOLTAGE;
- devc->coupling_ch1 = DEFAULT_COUPLING;
- devc->coupling_ch2 = DEFAULT_COUPLING;
- devc->voffset_ch1 = DEFAULT_VERT_OFFSET;
- devc->voffset_ch2 = DEFAULT_VERT_OFFSET;
- devc->voffset_trigger = DEFAULT_VERT_TRIGGERPOS;
- devc->framesize = DEFAULT_FRAMESIZE;
- devc->triggerslope = SLOPE_POSITIVE;
- devc->triggersource = g_strdup(DEFAULT_TRIGGER_SOURCE);
- devc->triggerposition = DEFAULT_HORIZ_TRIGGERPOS;
- sdi->priv = devc;
- drvc = di->priv;
- drvc->instances = g_slist_append(drvc->instances, sdi);
-
- return sdi;
-}
-
-static int configure_channels(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- const GSList *l;
- int p;
-
- devc = sdi->priv;
-
- g_slist_free(devc->enabled_channels);
- devc->ch1_enabled = devc->ch2_enabled = FALSE;
- for (l = sdi->channels, p = 0; l; l = l->next, p++) {
- ch = l->data;
- if (p == 0)
- devc->ch1_enabled = ch->enabled;
- else
- devc->ch2_enabled = ch->enabled;
- if (ch->enabled)
- devc->enabled_channels = g_slist_append(devc->enabled_channels, ch);
- }
-
- return SR_OK;
-}
-
-static void clear_dev_context(void *priv)
-{
- struct dev_context *devc;
-
- devc = priv;
- g_free(devc->triggersource);
- g_slist_free(devc->enabled_channels);
-
-}
-
-static int dev_clear(void)
-{
- return std_dev_clear(di, clear_dev_context);
-}
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_usb_dev_inst *usb;
- struct sr_config *src;
- const struct dso_profile *prof;
- GSList *l, *devices, *conn_devices;
- struct libusb_device_descriptor des;
- libusb_device **devlist;
- int devcnt, ret, i, j;
- const char *conn;
-
- drvc = di->priv;
-
- devcnt = 0;
- devices = 0;
-
- conn = NULL;
- for (l = options; l; l = l->next) {
- src = l->data;
- if (src->key == SR_CONF_CONN) {
- conn = g_variant_get_string(src->data, NULL);
- break;
- }
- }
- if (conn)
- conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
- else
- conn_devices = NULL;
-
- /* Find all Hantek DSO devices and upload firmware to all of them. */
- libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- for (i = 0; devlist[i]; i++) {
- if (conn) {
- usb = NULL;
- for (l = conn_devices; l; l = l->next) {
- usb = l->data;
- if (usb->bus == libusb_get_bus_number(devlist[i])
- && usb->address == libusb_get_device_address(devlist[i]))
- break;
- }
- if (!l)
- /* This device matched none of the ones that
- * matched the conn specification. */
- continue;
- }
-
- if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
- sr_err("Failed to get device descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
-
- prof = NULL;
- for (j = 0; dev_profiles[j].orig_vid; j++) {
- if (des.idVendor == dev_profiles[j].orig_vid
- && des.idProduct == dev_profiles[j].orig_pid) {
- /* Device matches the pre-firmware profile. */
- prof = &dev_profiles[j];
- sr_dbg("Found a %s %s.", prof->vendor, prof->model);
- sdi = dso_dev_new(devcnt, prof);
- devices = g_slist_append(devices, sdi);
- devc = sdi->priv;
- if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION,
- prof->firmware) == SR_OK)
- /* Remember when the firmware on this device was updated */
- devc->fw_updated = g_get_monotonic_time();
- else
- sr_err("Firmware upload failed for "
- "device %d.", devcnt);
- /* Dummy USB address of 0xff will get overwritten later. */
- sdi->conn = sr_usb_dev_inst_new(
- libusb_get_bus_number(devlist[i]), 0xff, NULL);
- devcnt++;
- break;
- } else if (des.idVendor == dev_profiles[j].fw_vid
- && des.idProduct == dev_profiles[j].fw_pid) {
- /* Device matches the post-firmware profile. */
- prof = &dev_profiles[j];
- sr_dbg("Found a %s %s.", prof->vendor, prof->model);
- sdi = dso_dev_new(devcnt, prof);
- sdi->status = SR_ST_INACTIVE;
- devices = g_slist_append(devices, sdi);
- devc = sdi->priv;
- sdi->inst_type = SR_INST_USB;
- sdi->conn = sr_usb_dev_inst_new(
- libusb_get_bus_number(devlist[i]),
- libusb_get_device_address(devlist[i]), NULL);
- devcnt++;
- break;
- }
- }
- if (!prof)
- /* not a supported VID/PID */
- continue;
- }
- libusb_free_device_list(devlist, 1);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int64_t timediff_us, timediff_ms;
- int err;
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- /*
- * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS
- * for the FX2 to renumerate.
- */
- err = SR_ERR;
- if (devc->fw_updated > 0) {
- sr_info("Waiting for device to reset.");
- /* Takes >= 300ms for the FX2 to be gone from the USB bus. */
- g_usleep(300 * 1000);
- timediff_ms = 0;
- while (timediff_ms < MAX_RENUM_DELAY_MS) {
- if ((err = dso_open(sdi)) == SR_OK)
- break;
- g_usleep(100 * 1000);
- timediff_us = g_get_monotonic_time() - devc->fw_updated;
- timediff_ms = timediff_us / 1000;
- sr_spew("Waited %" PRIi64 " ms.", timediff_ms);
- }
- sr_info("Device came back after %d ms.", timediff_ms);
- } else {
- err = dso_open(sdi);
- }
-
- if (err != SR_OK) {
- sr_err("Unable to open device.");
- return SR_ERR;
- }
-
- err = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
- if (err != 0) {
- sr_err("Unable to claim interface: %s.",
- libusb_error_name(err));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- dso_close(sdi);
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- return dev_clear();
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct sr_usb_dev_inst *usb;
- char str[128];
-
- (void)cg;
-
- switch (id) {
- case SR_CONF_CONN:
- if (!sdi || !sdi->conn)
- return SR_ERR_ARG;
- usb = sdi->conn;
- if (usb->address == 255)
- /* Device still needs to re-enumerate after firmware
- * upload, so we don't know its (future) address. */
- return SR_ERR;
- snprintf(str, 128, "%d.%d", usb->bus, usb->address);
- *data = g_variant_new_string(str);
- break;
- case SR_CONF_NUM_TIMEBASE:
- *data = g_variant_new_int32(NUM_TIMEBASE);
- break;
- case SR_CONF_NUM_VDIV:
- *data = g_variant_new_int32(NUM_VDIV);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- double tmp_double;
- uint64_t tmp_u64, p, q;
- int tmp_int, ret;
- unsigned int i;
- const char *tmp_str;
- char **targets;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- ret = SR_OK;
- devc = sdi->priv;
- switch (id) {
- case SR_CONF_LIMIT_FRAMES:
- devc->limit_frames = g_variant_get_uint64(data);
- break;
- case SR_CONF_TRIGGER_SLOPE:
- tmp_str = g_variant_get_string(data, NULL);
- if (!tmp_str || !(tmp_str[0] == 'f' || tmp_str[0] == 'r'))
- return SR_ERR_ARG;
- devc->triggerslope = (tmp_str[0] == 'r')
- ? SLOPE_POSITIVE : SLOPE_NEGATIVE;
- break;
- case SR_CONF_HORIZ_TRIGGERPOS:
- tmp_double = g_variant_get_double(data);
- if (tmp_double < 0.0 || tmp_double > 1.0) {
- sr_err("Trigger position should be between 0.0 and 1.0.");
- ret = SR_ERR_ARG;
- } else
- devc->triggerposition = tmp_double;
- break;
- case SR_CONF_BUFFERSIZE:
- tmp_u64 = g_variant_get_uint64(data);
- for (i = 0; i < 2; i++) {
- if (devc->profile->buffersizes[i] == tmp_u64) {
- devc->framesize = tmp_u64;
- break;
- }
- }
- if (i == 2)
- ret = SR_ERR_ARG;
- break;
- case SR_CONF_TIMEBASE:
- g_variant_get(data, "(tt)", &p, &q);
- tmp_int = -1;
- for (i = 0; i < ARRAY_SIZE(timebases); i++) {
- if (timebases[i][0] == p && timebases[i][1] == q) {
- tmp_int = i;
- break;
- }
- }
- if (tmp_int >= 0)
- devc->timebase = tmp_int;
- else
- ret = SR_ERR_ARG;
- break;
- case SR_CONF_TRIGGER_SOURCE:
- tmp_str = g_variant_get_string(data, NULL);
- for (i = 0; trigger_sources[i]; i++) {
- if (!strcmp(tmp_str, trigger_sources[i])) {
- devc->triggersource = g_strdup(tmp_str);
- break;
- }
- }
- if (trigger_sources[i] == 0)
- ret = SR_ERR_ARG;
- break;
- case SR_CONF_FILTER:
- tmp_str = g_variant_get_string(data, NULL);
- devc->filter_ch1 = devc->filter_ch2 = devc->filter_trigger = 0;
- targets = g_strsplit(tmp_str, ",", 0);
- for (i = 0; targets[i]; i++) {
- if (targets[i] == '\0')
- /* Empty filter string can be used to clear them all. */
- ;
- else if (!strcmp(targets[i], "CH1"))
- devc->filter_ch1 = TRUE;
- else if (!strcmp(targets[i], "CH2"))
- devc->filter_ch2 = TRUE;
- else if (!strcmp(targets[i], "TRIGGER"))
- devc->filter_trigger = TRUE;
- else {
- sr_err("Invalid filter target %s.", targets[i]);
- ret = SR_ERR_ARG;
- }
- }
- g_strfreev(targets);
- break;
- case SR_CONF_VDIV:
- /* TODO: Not supporting vdiv per channel yet. */
- g_variant_get(data, "(tt)", &p, &q);
- tmp_int = -1;
- for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
- if (vdivs[i][0] == p && vdivs[i][1] == q) {
- tmp_int = i;
- break;
- }
- }
- if (tmp_int >= 0) {
- devc->voltage_ch1 = tmp_int;
- devc->voltage_ch2 = tmp_int;
- } else
- ret = SR_ERR_ARG;
- break;
- case SR_CONF_COUPLING:
- tmp_str = g_variant_get_string(data, NULL);
- /* TODO: Not supporting coupling per channel yet. */
- for (i = 0; coupling[i]; i++) {
- if (!strcmp(tmp_str, coupling[i])) {
- devc->coupling_ch1 = i;
- devc->coupling_ch2 = i;
- break;
- }
- }
- if (coupling[i] == 0)
- ret = SR_ERR_ARG;
- break;
- default:
- ret = SR_ERR_NA;
- break;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- GVariant *tuple, *rational[2];
- GVariantBuilder gvb;
- unsigned int i;
-
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- scanopts, ARRAY_SIZE(scanopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
- break;
- case SR_CONF_BUFFERSIZE:
- if (!sdi)
- return SR_ERR_ARG;
- devc = sdi->priv;
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
- devc->profile->buffersizes, 2, sizeof(uint64_t));
- break;
- case SR_CONF_COUPLING:
- *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
- break;
- case SR_CONF_VDIV:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
- rational[0] = g_variant_new_uint64(vdivs[i][0]);
- rational[1] = g_variant_new_uint64(vdivs[i][1]);
- tuple = g_variant_new_tuple(rational, 2);
- g_variant_builder_add_value(&gvb, tuple);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_FILTER:
- *data = g_variant_new_strv(filter_targets,
- ARRAY_SIZE(filter_targets));
- break;
- case SR_CONF_TIMEBASE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (i = 0; i < ARRAY_SIZE(timebases); i++) {
- rational[0] = g_variant_new_uint64(timebases[i][0]);
- rational[1] = g_variant_new_uint64(timebases[i][1]);
- tuple = g_variant_new_tuple(rational, 2);
- g_variant_builder_add_value(&gvb, tuple);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TRIGGER_SOURCE:
- *data = g_variant_new_strv(trigger_sources,
- ARRAY_SIZE(trigger_sources));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static void send_chunk(struct sr_dev_inst *sdi, unsigned char *buf,
- int num_samples)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct dev_context *devc;
- float ch1, ch2, range;
- int num_channels, data_offset, i;
-
- devc = sdi->priv;
- num_channels = (devc->ch1_enabled && devc->ch2_enabled) ? 2 : 1;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- /* TODO: support for 5xxx series 9-bit samples */
- analog.channels = devc->enabled_channels;
- analog.num_samples = num_samples;
- analog.mq = SR_MQ_VOLTAGE;
- analog.unit = SR_UNIT_VOLT;
- /* TODO: Check malloc return value. */
- analog.data = g_try_malloc(analog.num_samples * sizeof(float) * num_channels);
- data_offset = 0;
- for (i = 0; i < analog.num_samples; i++) {
- /*
- * The device always sends data for both channels. If a channel
- * is disabled, it contains a copy of the enabled channel's
- * data. However, we only send the requested channels to
- * the bus.
- *
- * Voltage values are encoded as a value 0-255 (0-512 on the
- * DSO-5200*), where the value is a point in the range
- * represented by the vdiv setting. There are 8 vertical divs,
- * so e.g. 500mV/div represents 4V peak-to-peak where 0 = -2V
- * and 255 = +2V.
- */
- /* TODO: Support for DSO-5xxx series 9-bit samples. */
- if (devc->ch1_enabled) {
- range = ((float)vdivs[devc->voltage_ch1][0] / vdivs[devc->voltage_ch1][1]) * 8;
- ch1 = range / 255 * *(buf + i * 2 + 1);
- /* Value is centered around 0V. */
- ch1 -= range / 2;
- analog.data[data_offset++] = ch1;
- }
- if (devc->ch2_enabled) {
- range = ((float)vdivs[devc->voltage_ch2][0] / vdivs[devc->voltage_ch2][1]) * 8;
- ch2 = range / 255 * *(buf + i * 2);
- ch2 -= range / 2;
- analog.data[data_offset++] = ch2;
- }
- }
- sr_session_send(devc->cb_data, &packet);
-}
-
-/*
- * Called by libusb (as triggered by handle_event()) when a transfer comes in.
- * Only channel data comes in asynchronously, and all transfers for this are
- * queued up beforehand, so this just needs to chuck the incoming data onto
- * the libsigrok session bus.
- */
-static void receive_transfer(struct libusb_transfer *transfer)
-{
- struct sr_datafeed_packet packet;
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- int num_samples, pre;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
- sr_spew("receive_transfer(): status %d received %d bytes.",
- transfer->status, transfer->actual_length);
-
- if (transfer->actual_length == 0)
- /* Nothing to send to the bus. */
- return;
-
- num_samples = transfer->actual_length / 2;
-
- sr_spew("Got %d-%d/%d samples in frame.", devc->samp_received + 1,
- devc->samp_received + num_samples, devc->framesize);
-
- /*
- * The device always sends a full frame, but the beginning of the frame
- * doesn't represent the trigger point. The offset at which the trigger
- * happened came in with the capture state, so we need to start sending
- * from there up the session bus. The samples in the frame buffer
- * before that trigger point came after the end of the device's frame
- * buffer was reached, and it wrapped around to overwrite up until the
- * trigger point.
- */
- if (devc->samp_received < devc->trigger_offset) {
- /* Trigger point not yet reached. */
- if (devc->samp_received + num_samples < devc->trigger_offset) {
- /* The entire chunk is before the trigger point. */
- memcpy(devc->framebuf + devc->samp_buffered * 2,
- transfer->buffer, num_samples * 2);
- devc->samp_buffered += num_samples;
- } else {
- /*
- * This chunk hits or overruns the trigger point.
- * Store the part before the trigger fired, and
- * send the rest up to the session bus.
- */
- pre = devc->trigger_offset - devc->samp_received;
- memcpy(devc->framebuf + devc->samp_buffered * 2,
- transfer->buffer, pre * 2);
- devc->samp_buffered += pre;
-
- /* The rest of this chunk starts with the trigger point. */
- sr_dbg("Reached trigger point, %d samples buffered.",
- devc->samp_buffered);
-
- /* Avoid the corner case where the chunk ended at
- * exactly the trigger point. */
- if (num_samples > pre)
- send_chunk(sdi, transfer->buffer + pre * 2,
- num_samples - pre);
- }
- } else {
- /* Already past the trigger point, just send it all out. */
- send_chunk(sdi, transfer->buffer,
- num_samples);
- }
-
- devc->samp_received += num_samples;
-
- /* Everything in this transfer was either copied to the buffer or
- * sent to the session bus. */
- g_free(transfer->buffer);
- libusb_free_transfer(transfer);
-
- if (devc->samp_received >= devc->framesize) {
- /* That was the last chunk in this frame. Send the buffered
- * pre-trigger samples out now, in one big chunk. */
- sr_dbg("End of frame, sending %d pre-trigger buffered samples.",
- devc->samp_buffered);
- send_chunk(sdi, devc->framebuf, devc->samp_buffered);
-
- /* Mark the end of this frame. */
- packet.type = SR_DF_FRAME_END;
- sr_session_send(devc->cb_data, &packet);
-
- if (devc->limit_frames && ++devc->num_frames == devc->limit_frames) {
- /* Terminate session */
- devc->dev_state = STOPPING;
- } else {
- devc->dev_state = NEW_CAPTURE;
- }
- }
-}
-
-static int handle_event(int fd, int revents, void *cb_data)
-{
- const struct sr_dev_inst *sdi;
- struct sr_datafeed_packet packet;
- struct timeval tv;
- struct dev_context *devc;
- struct drv_context *drvc = di->priv;
- int num_channels;
- uint32_t trigger_offset;
- uint8_t capturestate;
-
- (void)fd;
- (void)revents;
-
- sdi = cb_data;
- devc = sdi->priv;
- if (devc->dev_state == STOPPING) {
- /* We've been told to wind up the acquisition. */
- sr_dbg("Stopping acquisition.");
- /*
- * TODO: Doesn't really cancel pending transfers so they might
- * come in after SR_DF_END is sent.
- */
- usb_source_remove(sdi->session, drvc->sr_ctx);
-
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-
- devc->dev_state = IDLE;
-
- return TRUE;
- }
-
- /* Always handle pending libusb events. */
- tv.tv_sec = tv.tv_usec = 0;
- libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
-
- /* TODO: ugh */
- if (devc->dev_state == NEW_CAPTURE) {
- if (dso_capture_start(sdi) != SR_OK)
- return TRUE;
- if (dso_enable_trigger(sdi) != SR_OK)
- return TRUE;
-// if (dso_force_trigger(sdi) != SR_OK)
-// return TRUE;
- sr_dbg("Successfully requested next chunk.");
- devc->dev_state = CAPTURE;
- return TRUE;
- }
- if (devc->dev_state != CAPTURE)
- return TRUE;
-
- if ((dso_get_capturestate(sdi, &capturestate, &trigger_offset)) != SR_OK)
- return TRUE;
-
- sr_dbg("Capturestate %d.", capturestate);
- sr_dbg("Trigger offset 0x%.6x.", trigger_offset);
- switch (capturestate) {
- case CAPTURE_EMPTY:
- if (++devc->capture_empty_count >= MAX_CAPTURE_EMPTY) {
- devc->capture_empty_count = 0;
- if (dso_capture_start(sdi) != SR_OK)
- break;
- if (dso_enable_trigger(sdi) != SR_OK)
- break;
-// if (dso_force_trigger(sdi) != SR_OK)
-// break;
- sr_dbg("Successfully requested next chunk.");
- }
- break;
- case CAPTURE_FILLING:
- /* No data yet. */
- break;
- case CAPTURE_READY_8BIT:
- /* Remember where in the captured frame the trigger is. */
- devc->trigger_offset = trigger_offset;
-
- num_channels = (devc->ch1_enabled && devc->ch2_enabled) ? 2 : 1;
- /* TODO: Check malloc return value. */
- devc->framebuf = g_try_malloc(devc->framesize * num_channels * 2);
- devc->samp_buffered = devc->samp_received = 0;
-
- /* Tell the scope to send us the first frame. */
- if (dso_get_channeldata(sdi, receive_transfer) != SR_OK)
- break;
-
- /*
- * Don't hit the state machine again until we're done fetching
- * the data we just told the scope to send.
- */
- devc->dev_state = FETCH_DATA;
-
- /* Tell the frontend a new frame is on the way. */
- packet.type = SR_DF_FRAME_BEGIN;
- sr_session_send(sdi, &packet);
- break;
- case CAPTURE_READY_9BIT:
- /* TODO */
- sr_err("Not yet supported.");
- break;
- case CAPTURE_TIMEOUT:
- /* Doesn't matter, we'll try again next time. */
- break;
- default:
- sr_dbg("Unknown capture state: %d.", capturestate);
- break;
- }
-
- return TRUE;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct drv_context *drvc = di->priv;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- devc->cb_data = cb_data;
-
- if (configure_channels(sdi) != SR_OK) {
- sr_err("Failed to configure channels.");
- return SR_ERR;
- }
-
- if (dso_init(sdi) != SR_OK)
- return SR_ERR;
-
- if (dso_capture_start(sdi) != SR_OK)
- return SR_ERR;
-
- devc->dev_state = CAPTURE;
- usb_source_add(sdi->session, drvc->sr_ctx, TICK, handle_event, (void *)sdi);
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
-
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR;
-
- devc = sdi->priv;
- devc->dev_state = STOPPING;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver hantek_dso_driver_info = {
- .name = "hantek-dso",
- .longname = "Hantek DSO",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = 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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- * With protocol information from the hantekdso project,
- * Copyright (C) 2008 Oleg Khudyakov <prcoder@gmail.com>
- *
- * 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.h"
-#include "libsigrok-internal.h"
-#include "dso.h"
-#include <string.h>
-#include <glib.h>
-#include <libusb.h>
-
-extern struct sr_dev_driver hantek_dso_driver_info;
-
-static int send_begin(const struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
- int ret;
- unsigned char buffer[] = {0x0f, 0x03, 0x03, 0x03, 0x68, 0xac, 0xfe,
- 0x00, 0x01, 0x00};
-
- sr_dbg("Sending CTRL_BEGINCOMMAND.");
-
- usb = sdi->conn;
- if ((ret = libusb_control_transfer(usb->devhdl,
- LIBUSB_REQUEST_TYPE_VENDOR, CTRL_BEGINCOMMAND,
- 0, 0, buffer, sizeof(buffer), 200)) != sizeof(buffer)) {
- sr_err("Failed to send begincommand: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int send_bulkcmd(const struct sr_dev_inst *sdi, uint8_t *cmdstring, int cmdlen)
-{
- struct sr_usb_dev_inst *usb;
- int ret, tmp;
-
- usb = sdi->conn;
-
- if (send_begin(sdi) != SR_OK)
- return SR_ERR;
-
- if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT, cmdstring,
- cmdlen, &tmp, 200)) != 0)
- return SR_ERR;
-
- return SR_OK;
-}
-
-static int dso_getmps(libusb_device *dev)
-{
- struct libusb_device_descriptor des;
- struct libusb_config_descriptor *conf_dsc;
- const struct libusb_interface_descriptor *intf_dsc;
- int mps;
-
- if (libusb_get_device_descriptor(dev, &des) != 0)
- return 0;
-
- if (des.bNumConfigurations != 1)
- return 0;
-
- if (libusb_get_config_descriptor(dev, 0, &conf_dsc) != 0)
- return 0;
-
- mps = 0;
- intf_dsc = &(conf_dsc->interface[0].altsetting[0]);
- if (intf_dsc->bNumEndpoints != 2)
- goto err;
-
- if ((intf_dsc->endpoint[0].bEndpointAddress & 0x8f) !=
- (2 | LIBUSB_ENDPOINT_OUT))
- /* The first endpoint should be 2 (outbound). */
- goto err;
-
- if ((intf_dsc->endpoint[1].bEndpointAddress & 0x8f) !=
- (6 | LIBUSB_ENDPOINT_IN))
- /* The second endpoint should be 6 (inbound). */
- goto err;
-
- mps = intf_dsc->endpoint[1].wMaxPacketSize;
-
-err:
- if (conf_dsc)
- libusb_free_config_descriptor(conf_dsc);
-
- return mps;
-}
-
-SR_PRIV int dso_open(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct drv_context *drvc = hantek_dso_driver_info.priv;
- struct sr_usb_dev_inst *usb;
- struct libusb_device_descriptor des;
- libusb_device **devlist;
- int err, skip, i;
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- if (sdi->status == SR_ST_ACTIVE)
- /* already in use */
- return SR_ERR;
-
- skip = 0;
- libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- for (i = 0; devlist[i]; i++) {
- if ((err = libusb_get_device_descriptor(devlist[i], &des))) {
- sr_err("Failed to get device descriptor: %s.",
- libusb_error_name(err));
- continue;
- }
-
- if (des.idVendor != devc->profile->fw_vid
- || des.idProduct != devc->profile->fw_pid)
- continue;
-
- if (sdi->status == SR_ST_INITIALIZING) {
- if (skip != sdi->index) {
- /* Skip devices of this type that aren't the one we want. */
- skip += 1;
- continue;
- }
- } else if (sdi->status == SR_ST_INACTIVE) {
- /*
- * This device is fully enumerated, so we need to find
- * this device by vendor, product, bus and address.
- */
- if (libusb_get_bus_number(devlist[i]) != usb->bus
- || libusb_get_device_address(devlist[i]) != usb->address)
- /* this is not the one */
- continue;
- }
-
- if (!(err = libusb_open(devlist[i], &usb->devhdl))) {
- if (usb->address == 0xff)
- /*
- * first time we touch this device after firmware upload,
- * so we don't know the address yet.
- */
- usb->address = libusb_get_device_address(devlist[i]);
-
- if (!(devc->epin_maxpacketsize = dso_getmps(devlist[i])))
- sr_err("Wrong endpoint profile.");
- else {
- sdi->status = SR_ST_ACTIVE;
- sr_info("Opened device %d on %d.%d interface %d.",
- sdi->index, usb->bus,
- usb->address, USB_INTERFACE);
- }
- } else {
- sr_err("Failed to open device: %s.",
- libusb_error_name(err));
- }
-
- /* If we made it here, we handled the device (somehow). */
- break;
- }
- libusb_free_device_list(devlist, 1);
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR;
-
- return SR_OK;
-}
-
-SR_PRIV void dso_close(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
-
- usb = sdi->conn;
-
- if (usb->devhdl == NULL)
- return;
-
- sr_info("Closing device %d on %d.%d interface %d.", sdi->index,
- usb->bus, usb->address, USB_INTERFACE);
- libusb_release_interface(usb->devhdl, USB_INTERFACE);
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
-
-}
-
-static int get_channel_offsets(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- GString *gs;
- int chan, v, ret;
-
- sr_dbg("Getting channel offsets.");
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- ret = libusb_control_transfer(usb->devhdl,
- LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR,
- CTRL_READ_EEPROM, EEPROM_CHANNEL_OFFSETS, 0,
- (unsigned char *)&devc->channel_levels,
- sizeof(devc->channel_levels), 200);
- if (ret != sizeof(devc->channel_levels)) {
- sr_err("Failed to get channel offsets: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- /* Comes in as 16-bit numbers with the second byte always 0 on
- * the DSO-2090. Guessing this is supposed to be big-endian,
- * since that's how voltage offsets are submitted back to the DSO.
- * Convert to host order now, so we can use them natively.
- */
- for (chan = 0; chan < 2; chan++) {
- for (v = 0; v < 9; v++) {
- devc->channel_levels[chan][v][0] =
- g_ntohs(devc->channel_levels[chan][v][0]);
- devc->channel_levels[chan][v][1] =
- g_ntohs(devc->channel_levels[chan][v][1]);
- }
- }
-
- if (sr_log_loglevel_get() >= SR_LOG_DBG) {
- gs = g_string_sized_new(128);
- for (chan = 0; chan < 2; chan++) {
- g_string_printf(gs, "CH%d:", chan + 1);
- for (v = 0; v < 9; v++) {
- g_string_append_printf(gs, " %.4x-%.4x",
- devc->channel_levels[chan][v][0],
- devc->channel_levels[chan][v][1]);
- }
- sr_dbg("%s", gs->str);
- }
- g_string_free(gs, TRUE);
- }
-
- return SR_OK;
-}
-
-static int dso_set_trigger_samplerate(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int ret, tmp;
- uint8_t cmdstring[12];
- uint16_t timebase_small[] = { 0xffff, 0xfffc, 0xfff7, 0xffe8, 0xffce,
- 0xff9c, 0xff07, 0xfe0d, 0xfc19, 0xf63d, 0xec79, 0xd8f1 };
- uint16_t timebase_large[] = { 0xffff, 0x0000, 0xfffc, 0xfff7, 0xffe8,
- 0xffce, 0xff9d, 0xff07, 0xfe0d, 0xfc19, 0xf63d, 0xec79 };
-
- sr_dbg("Preparing CMD_SET_TRIGGER_SAMPLERATE.");
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- memset(cmdstring, 0, sizeof(cmdstring));
- /* Command */
- cmdstring[0] = CMD_SET_TRIGGER_SAMPLERATE;
-
- /* Trigger source */
- sr_dbg("Trigger source %s.", devc->triggersource);
- if (!strcmp("CH2", devc->triggersource))
- tmp = 0;
- else if (!strcmp("CH1", devc->triggersource))
- tmp = 1;
- else if (!strcmp("EXT", devc->triggersource))
- tmp = 2;
- else {
- sr_err("Invalid trigger source: '%s'.", devc->triggersource);
- return SR_ERR_ARG;
- }
- cmdstring[2] = tmp;
-
- /* Frame size */
- sr_dbg("Frame size: %d.", devc->framesize);
- cmdstring[2] |= (devc->framesize == FRAMESIZE_SMALL ? 0x01 : 0x02) << 2;
-
- /* Timebase fast */
- sr_dbg("Time base index: %d.", devc->timebase);
- if (devc->framesize == FRAMESIZE_SMALL) {
- if (devc->timebase < TIME_20us)
- tmp = 0;
- else if (devc->timebase == TIME_20us)
- tmp = 1;
- else if (devc->timebase == TIME_40us)
- tmp = 2;
- else if (devc->timebase == TIME_100us)
- tmp = 3;
- else if (devc->timebase >= TIME_200us)
- tmp = 4;
- } else {
- if (devc->timebase < TIME_40us) {
- sr_err("Timebase < 40us only supported with 10K buffer.");
- return SR_ERR_ARG;
- }
- else if (devc->timebase == TIME_40us)
- tmp = 0;
- else if (devc->timebase == TIME_100us)
- tmp = 2;
- else if (devc->timebase == TIME_200us)
- tmp = 3;
- else if (devc->timebase >= TIME_400us)
- tmp = 4;
- }
- cmdstring[2] |= (tmp & 0x07) << 5;
-
- /* Enabled channels: 00=CH1 01=CH2 10=both */
- sr_dbg("Channels CH1=%d CH2=%d", devc->ch1_enabled, devc->ch2_enabled);
- tmp = (((devc->ch2_enabled ? 1 : 0) << 1) + (devc->ch1_enabled ? 1 : 0)) - 1;
- cmdstring[3] = tmp;
-
- /* Fast rates channel */
- /* TODO: Is this right? */
- tmp = devc->timebase < TIME_10us ? 1 : 0;
- cmdstring[3] |= tmp << 2;
-
- /* Trigger slope: 0=positive 1=negative */
- /* TODO: Does this work? */
- sr_dbg("Trigger slope: %d.", devc->triggerslope);
- cmdstring[3] |= (devc->triggerslope == SLOPE_NEGATIVE ? 1 : 0) << 3;
-
- /* Timebase slow */
- if (devc->timebase < TIME_100us)
- tmp = 0;
- else if (devc->timebase > TIME_400ms)
- tmp = 0xffed;
- else {
- if (devc->framesize == FRAMESIZE_SMALL)
- tmp = timebase_small[devc->timebase - 3];
- else
- tmp = timebase_large[devc->timebase - 3];
- }
- cmdstring[4] = tmp & 0xff;
- cmdstring[5] = (tmp >> 8) & 0xff;
-
- /* Horizontal trigger position */
- sr_dbg("Trigger position: %3.2f.", devc->triggerposition);
- tmp = 0x77fff + 0x8000 * devc->triggerposition;
- cmdstring[6] = tmp & 0xff;
- cmdstring[7] = (tmp >> 8) & 0xff;
- cmdstring[10] = (tmp >> 16) & 0xff;
-
- if (send_begin(sdi) != SR_OK)
- return SR_ERR;
-
- if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
- cmdstring, sizeof(cmdstring), &tmp, 100)) != 0) {
- sr_err("Failed to set trigger/samplerate: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
- sr_dbg("Sent CMD_SET_TRIGGER_SAMPLERATE.");
-
- return SR_OK;
-}
-
-static int dso_set_filters(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int ret, tmp;
- uint8_t cmdstring[8];
-
- sr_dbg("Preparing CMD_SET_FILTERS.");
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- memset(cmdstring, 0, sizeof(cmdstring));
- cmdstring[0] = CMD_SET_FILTERS;
- cmdstring[1] = 0x0f;
- if (devc->filter_ch1) {
- sr_dbg("Turning on CH1 filter.");
- cmdstring[2] |= 0x80;
- }
- if (devc->filter_ch2) {
- sr_dbg("Turning on CH2 filter.");
- cmdstring[2] |= 0x40;
- }
- if (devc->filter_trigger) {
- /* TODO: supported on the DSO-2090? */
- sr_dbg("Turning on trigger filter.");
- cmdstring[2] |= 0x20;
- }
-
- if (send_begin(sdi) != SR_OK)
- return SR_ERR;
-
- if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
- cmdstring, sizeof(cmdstring), &tmp, 100)) != 0) {
- sr_err("Failed to set filters: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- sr_dbg("Sent CMD_SET_FILTERS.");
-
- return SR_OK;
-}
-
-static int dso_set_voltage(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int ret, tmp;
- uint8_t cmdstring[8];
-
- sr_dbg("Preparing CMD_SET_VOLTAGE.");
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- memset(cmdstring, 0, sizeof(cmdstring));
- cmdstring[0] = CMD_SET_VOLTAGE;
- cmdstring[1] = 0x0f;
- cmdstring[2] = 0x30;
-
- /* CH1 volts/div is encoded in bits 0-1 */
- sr_dbg("CH1 vdiv index: %d.", devc->voltage_ch1);
- switch (devc->voltage_ch1) {
- case VDIV_1V:
- case VDIV_100MV:
- case VDIV_10MV:
- cmdstring[2] |= 0x00;
- break;
- case VDIV_2V:
- case VDIV_200MV:
- case VDIV_20MV:
- cmdstring[2] |= 0x01;
- break;
- case VDIV_5V:
- case VDIV_500MV:
- case VDIV_50MV:
- cmdstring[2] |= 0x02;
- break;
- }
-
- /* CH2 volts/div is encoded in bits 2-3 */
- sr_dbg("CH2 vdiv index: %d.", devc->voltage_ch2);
- switch (devc->voltage_ch2) {
- case VDIV_1V:
- case VDIV_100MV:
- case VDIV_10MV:
- cmdstring[2] |= 0x00;
- break;
- case VDIV_2V:
- case VDIV_200MV:
- case VDIV_20MV:
- cmdstring[2] |= 0x04;
- break;
- case VDIV_5V:
- case VDIV_500MV:
- case VDIV_50MV:
- cmdstring[2] |= 0x08;
- break;
- }
-
- if (send_begin(sdi) != SR_OK)
- return SR_ERR;
-
- if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
- cmdstring, sizeof(cmdstring), &tmp, 100)) != 0) {
- sr_err("Failed to set voltage: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- sr_dbg("Sent CMD_SET_VOLTAGE.");
-
- return SR_OK;
-}
-
-static int dso_set_relays(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- GString *gs;
- int ret, i;
- uint8_t relays[17] = { 0x00, 0x04, 0x08, 0x02, 0x20, 0x40, 0x10, 0x01,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
- sr_dbg("Preparing CTRL_SETRELAYS.");
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- if (devc->voltage_ch1 < VDIV_1V)
- relays[1] = ~relays[1];
-
- if (devc->voltage_ch1 < VDIV_100MV)
- relays[2] = ~relays[2];
-
- sr_dbg("CH1 coupling: %d.", devc->coupling_ch1);
- if (devc->coupling_ch1 != COUPLING_AC)
- relays[3] = ~relays[3];
-
- if (devc->voltage_ch2 < VDIV_1V)
- relays[4] = ~relays[4];
-
- if (devc->voltage_ch2 < VDIV_100MV)
- relays[5] = ~relays[5];
-
- sr_dbg("CH2 coupling: %d.", devc->coupling_ch1);
- if (devc->coupling_ch2 != COUPLING_AC)
- relays[6] = ~relays[6];
-
- if (!strcmp(devc->triggersource, "EXT"))
- relays[7] = ~relays[7];
-
- if (sr_log_loglevel_get() >= SR_LOG_DBG) {
- gs = g_string_sized_new(128);
- g_string_printf(gs, "Relays:");
- for (i = 0; i < 17; i++)
- g_string_append_printf(gs, " %.2x", relays[i]);
- sr_dbg("%s", gs->str);
- g_string_free(gs, TRUE);
- }
-
- if ((ret = libusb_control_transfer(usb->devhdl,
- LIBUSB_REQUEST_TYPE_VENDOR, CTRL_SETRELAYS,
- 0, 0, relays, 17, 100)) != sizeof(relays)) {
- sr_err("Failed to set relays: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- sr_dbg("Sent CTRL_SETRELAYS.");
-
- return SR_OK;
-}
-
-static int dso_set_voffsets(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int offset, ret;
- uint16_t *ch_levels;
- uint8_t offsets[17];
-
- sr_dbg("Preparing CTRL_SETOFFSET.");
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- memset(offsets, 0, sizeof(offsets));
- /* Channel 1 */
- ch_levels = devc->channel_levels[0][devc->voltage_ch1];
- offset = (ch_levels[1] - ch_levels[0]) * devc->voffset_ch1 + ch_levels[0];
- offsets[0] = (offset >> 8) | 0x20;
- offsets[1] = offset & 0xff;
- sr_dbg("CH1 offset: %3.2f (%.2x%.2x).", devc->voffset_ch1,
- offsets[0], offsets[1]);
-
- /* Channel 2 */
- ch_levels = devc->channel_levels[1][devc->voltage_ch2];
- offset = (ch_levels[1] - ch_levels[0]) * devc->voffset_ch2 + ch_levels[0];
- offsets[2] = (offset >> 8) | 0x20;
- offsets[3] = offset & 0xff;
- sr_dbg("CH2 offset: %3.2f (%.2x%.2x).", devc->voffset_ch2,
- offsets[2], offsets[3]);
-
- /* Trigger */
- offset = MAX_VERT_TRIGGER * devc->voffset_trigger;
- offsets[4] = (offset >> 8) | 0x20;
- offsets[5] = offset & 0xff;
- sr_dbg("Trigger offset: %3.2f (%.2x%.2x).", devc->voffset_trigger,
- offsets[4], offsets[5]);
-
- if ((ret = libusb_control_transfer(usb->devhdl,
- LIBUSB_REQUEST_TYPE_VENDOR, CTRL_SETOFFSET,
- 0, 0, offsets, sizeof(offsets), 100)) != sizeof(offsets)) {
- sr_err("Failed to set offsets: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- sr_dbg("Sent CTRL_SETOFFSET.");
-
- return SR_OK;
-}
-
-SR_PRIV int dso_enable_trigger(const struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
- int ret, tmp;
- uint8_t cmdstring[2];
-
- sr_dbg("Sending CMD_ENABLE_TRIGGER.");
-
- usb = sdi->conn;
-
- memset(cmdstring, 0, sizeof(cmdstring));
- cmdstring[0] = CMD_ENABLE_TRIGGER;
- cmdstring[1] = 0x00;
-
- if (send_begin(sdi) != SR_OK)
- return SR_ERR;
-
- if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
- cmdstring, sizeof(cmdstring), &tmp, 100)) != 0) {
- sr_err("Failed to enable trigger: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int dso_force_trigger(const struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
- int ret, tmp;
- uint8_t cmdstring[2];
-
- sr_dbg("Sending CMD_FORCE_TRIGGER.");
-
- usb = sdi->conn;
-
- memset(cmdstring, 0, sizeof(cmdstring));
- cmdstring[0] = CMD_FORCE_TRIGGER;
- cmdstring[1] = 0x00;
-
- if (send_begin(sdi) != SR_OK)
- return SR_ERR;
-
- if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
- cmdstring, sizeof(cmdstring), &tmp, 100)) != 0) {
- sr_err("Failed to force trigger: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int dso_init(const struct sr_dev_inst *sdi)
-{
-
- sr_dbg("Initializing DSO.");
-
- if (get_channel_offsets(sdi) != SR_OK)
- return SR_ERR;
-
- if (dso_set_trigger_samplerate(sdi) != SR_OK)
- return SR_ERR;
-
- if (dso_set_filters(sdi) != SR_OK)
- return SR_ERR;
-
- if (dso_set_voltage(sdi) != SR_OK)
- return SR_ERR;
-
- if (dso_set_relays(sdi) != SR_OK)
- return SR_ERR;
-
- if (dso_set_voffsets(sdi) != SR_OK)
- return SR_ERR;
-
- if (dso_enable_trigger(sdi) != SR_OK)
- return SR_ERR;
-
- return SR_OK;
-}
-
-SR_PRIV int dso_get_capturestate(const struct sr_dev_inst *sdi,
- uint8_t *capturestate, uint32_t *trigger_offset)
-{
- struct sr_usb_dev_inst *usb;
- int ret, tmp, i;
- unsigned int bitvalue, toff;
- uint8_t cmdstring[2], inbuf[512];
-
- sr_dbg("Sending CMD_GET_CAPTURESTATE.");
-
- usb = sdi->conn;
-
- cmdstring[0] = CMD_GET_CAPTURESTATE;
- cmdstring[1] = 0;
-
- if ((ret = send_bulkcmd(sdi, cmdstring, sizeof(cmdstring))) != SR_OK) {
- sr_dbg("Failed to send get_capturestate command: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_IN,
- inbuf, 512, &tmp, 100)) != 0) {
- sr_dbg("Failed to get capturestate: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
- *capturestate = inbuf[0];
- toff = (inbuf[1] << 16) | (inbuf[3] << 8) | inbuf[2];
-
- /*
- * This conversion comes from the openhantek project.
- * Each set bit in the 24-bit value inverts all bits with a lower
- * value. No idea why the device reports the trigger point this way.
- */
- bitvalue = 1;
- for (i = 0; i < 24; i++) {
- /* Each set bit inverts all bits with a lower value. */
- if(toff & bitvalue)
- toff ^= bitvalue - 1;
- bitvalue <<= 1;
- }
- *trigger_offset = toff;
-
- return SR_OK;
-}
-
-SR_PRIV int dso_capture_start(const struct sr_dev_inst *sdi)
-{
- int ret;
- uint8_t cmdstring[2];
-
- sr_dbg("Sending CMD_CAPTURE_START.");
-
- cmdstring[0] = CMD_CAPTURE_START;
- cmdstring[1] = 0;
-
- if ((ret = send_bulkcmd(sdi, cmdstring, sizeof(cmdstring))) != SR_OK) {
- sr_err("Failed to send capture_start command: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int dso_get_channeldata(const struct sr_dev_inst *sdi,
- libusb_transfer_cb_fn cb)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- struct libusb_transfer *transfer;
- int num_transfers, ret, i;
- uint8_t cmdstring[2];
- unsigned char *buf;
-
- sr_dbg("Sending CMD_GET_CHANNELDATA.");
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- cmdstring[0] = CMD_GET_CHANNELDATA;
- cmdstring[1] = 0;
-
- if ((ret = send_bulkcmd(sdi, cmdstring, sizeof(cmdstring))) != SR_OK) {
- sr_err("Failed to get channel data: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- /* TODO: DSO-2xxx only. */
- num_transfers = devc->framesize *
- sizeof(unsigned short) / devc->epin_maxpacketsize;
- sr_dbg("Queueing up %d transfers.", num_transfers);
- for (i = 0; i < num_transfers; i++) {
- if (!(buf = g_try_malloc(devc->epin_maxpacketsize))) {
- sr_err("Failed to malloc USB endpoint buffer.");
- return SR_ERR_MALLOC;
- }
- transfer = libusb_alloc_transfer(0);
- libusb_fill_bulk_transfer(transfer, usb->devhdl, DSO_EP_IN, buf,
- devc->epin_maxpacketsize, cb, (void *)sdi, 40);
- if ((ret = libusb_submit_transfer(transfer)) != 0) {
- sr_err("Failed to submit transfer: %s.",
- libusb_error_name(ret));
- /* TODO: Free them all. */
- libusb_free_transfer(transfer);
- g_free(buf);
- return SR_ERR;
- }
- }
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- * With protocol information from the hantekdso project,
- * Copyright (C) 2008 Oleg Khudyakov <prcoder@gmail.com>
- *
- * 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_HANTEK_DSO_DSO_H
-#define LIBSIGROK_HARDWARE_HANTEK_DSO_DSO_H
-
-#define LOG_PREFIX "hantek-dso"
-
-#define USB_INTERFACE 0
-#define USB_CONFIGURATION 1
-#define DSO_EP_IN 0x86
-#define DSO_EP_OUT 0x02
-
-/* FX2 renumeration delay in ms */
-#define MAX_RENUM_DELAY_MS 3000
-
-#define MAX_CAPTURE_EMPTY 3
-
-#define DEFAULT_VOLTAGE VDIV_500MV
-#define DEFAULT_FRAMESIZE FRAMESIZE_SMALL
-#define DEFAULT_TIMEBASE TIME_100us
-#define DEFAULT_TRIGGER_SOURCE "CH1"
-#define DEFAULT_COUPLING COUPLING_DC
-#define DEFAULT_HORIZ_TRIGGERPOS 0.5
-#define DEFAULT_VERT_OFFSET 0.5
-#define DEFAULT_VERT_TRIGGERPOS 0.5
-
-#define MAX_VERT_TRIGGER 0xfe
-
-/* Hantek DSO-specific protocol values */
-#define EEPROM_CHANNEL_OFFSETS 0x08
-
-/* All models have this for their "fast" mode. */
-#define FRAMESIZE_SMALL 10240
-
-enum control_requests {
- CTRL_READ_EEPROM = 0xa2,
- CTRL_GETSPEED = 0xb2,
- CTRL_BEGINCOMMAND = 0xb3,
- CTRL_SETOFFSET = 0xb4,
- CTRL_SETRELAYS = 0xb5,
-};
-
-enum dso_commands {
- CMD_SET_FILTERS = 0,
- CMD_SET_TRIGGER_SAMPLERATE,
- CMD_FORCE_TRIGGER,
- CMD_CAPTURE_START,
- CMD_ENABLE_TRIGGER,
- CMD_GET_CHANNELDATA,
- CMD_GET_CAPTURESTATE,
- CMD_SET_VOLTAGE,
- /* unused */
- CMD_SET_LOGICALDATA,
- CMD_GET_LOGICALDATA,
-};
-
-/* Must match the coupling table. */
-enum couplings {
- COUPLING_AC = 0,
- COUPLING_DC,
- /* TODO not used, how to enable? */
- COUPLING_GND,
-};
-
-/* Must match the timebases table. */
-enum time_bases {
- TIME_10us = 0,
- TIME_20us,
- TIME_40us,
- TIME_100us,
- TIME_200us,
- TIME_400us,
- TIME_1ms,
- TIME_2ms,
- TIME_4ms,
- TIME_10ms,
- TIME_20ms,
- TIME_40ms,
- TIME_100ms,
- TIME_200ms,
- TIME_400ms,
-};
-
-/* Must match the vdivs table. */
-enum {
- VDIV_10MV,
- VDIV_20MV,
- VDIV_50MV,
- VDIV_100MV,
- VDIV_200MV,
- VDIV_500MV,
- VDIV_1V,
- VDIV_2V,
- VDIV_5V,
-};
-
-enum trigger_slopes {
- SLOPE_POSITIVE = 0,
- SLOPE_NEGATIVE,
-};
-
-enum trigger_sources {
- TRIGGER_CH2 = 0,
- TRIGGER_CH1,
- TRIGGER_EXT,
-};
-
-enum capturestates {
- CAPTURE_EMPTY = 0,
- CAPTURE_FILLING = 1,
- CAPTURE_READY_8BIT = 2,
- CAPTURE_READY_9BIT = 7,
- CAPTURE_TIMEOUT = 127,
- CAPTURE_UNKNOWN = 255,
-};
-
-enum triggermodes {
- TRIGGERMODE_AUTO,
- TRIGGERMODE_NORMAL,
- TRIGGERMODE_SINGLE,
-};
-
-enum states {
- IDLE,
- NEW_CAPTURE,
- CAPTURE,
- FETCH_DATA,
- STOPPING,
-};
-
-struct dso_profile {
- /* VID/PID after cold boot */
- uint16_t orig_vid;
- uint16_t orig_pid;
- /* VID/PID after firmware upload */
- uint16_t fw_vid;
- uint16_t fw_pid;
- char *vendor;
- char *model;
- const uint64_t *buffersizes;
- char *firmware;
-};
-
-struct dev_context {
- const struct dso_profile *profile;
- void *cb_data;
- uint64_t limit_frames;
- uint64_t num_frames;
- GSList *enabled_channels;
- /* We can't keep track of an FX2-based device after upgrading
- * the firmware (it re-enumerates into a different device address
- * after the upgrade) this is like a global lock. No device will open
- * until a proper delay after the last device was upgraded.
- */
- int64_t fw_updated;
- int epin_maxpacketsize;
- int capture_empty_count;
- int dev_state;
-
- /* Oscilloscope settings. */
- int timebase;
- gboolean ch1_enabled;
- gboolean ch2_enabled;
- int voltage_ch1;
- int voltage_ch2;
- int coupling_ch1;
- int coupling_ch2;
- // voltage offset (vertical position)
- float voffset_ch1;
- float voffset_ch2;
- float voffset_trigger;
- uint16_t channel_levels[2][9][2];
- unsigned int framesize;
- gboolean filter_ch1;
- gboolean filter_ch2;
- gboolean filter_trigger;
- int triggerslope;
- char *triggersource;
- float triggerposition;
- int triggermode;
-
- /* Frame transfer */
- unsigned int samp_received;
- unsigned int samp_buffered;
- unsigned int trigger_offset;
- unsigned char *framebuf;
-};
-
-SR_PRIV int dso_open(struct sr_dev_inst *sdi);
-SR_PRIV void dso_close(struct sr_dev_inst *sdi);
-SR_PRIV int dso_enable_trigger(const struct sr_dev_inst *sdi);
-SR_PRIV int dso_force_trigger(const struct sr_dev_inst *sdi);
-SR_PRIV int dso_init(const struct sr_dev_inst *sdi);
-SR_PRIV int dso_get_capturestate(const struct sr_dev_inst *sdi,
- uint8_t *capturestate, uint32_t *trigger_offset);
-SR_PRIV int dso_capture_start(const struct sr_dev_inst *sdi);
-SR_PRIV int dso_get_channeldata(const struct sr_dev_inst *sdi,
- libusb_transfer_cb_fn cb);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Marc Schink <sigrok-dev@marcschink.de>
- *
- * 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 "protocol.h"
-
-static const int hwcaps[] = {
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_SAMPLERATE,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_TRIGGER_MATCH,
- SR_CONF_CAPTURE_RATIO,
-};
-
-static const int32_t trigger_matches[] = {
- SR_TRIGGER_RISING,
- SR_TRIGGER_FALLING,
- SR_TRIGGER_EDGE,
-};
-
-SR_PRIV const uint64_t sl2_samplerates[NUM_SAMPLERATES] = {
- SR_KHZ(1.25),
- SR_KHZ(10),
- SR_KHZ(50),
- SR_KHZ(100),
- SR_KHZ(250),
- SR_KHZ(500),
- SR_MHZ(1),
- SR_MHZ(2.5),
- SR_MHZ(5),
- SR_MHZ(10),
- SR_MHZ(20),
-};
-
-static const char *channel_names[NUM_CHANNELS + 1] = {
- "0", "1", "2", "3",
- NULL,
-};
-
-SR_PRIV struct sr_dev_driver ikalogic_scanalogic2_driver_info;
-static struct sr_dev_driver *di = &ikalogic_scanalogic2_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- GSList *usb_devices, *devices, *l;
- struct drv_context *drvc;
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- struct device_info dev_info;
- int ret, device_index, i;
- char *fw_ver_str;
-
- (void)options;
-
- devices = NULL;
- drvc = di->priv;
- drvc->instances = NULL;
- device_index = 0;
-
- usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, USB_VID_PID);
-
- if (usb_devices == NULL)
- return NULL;
-
- for (l = usb_devices; l; l = l->next) {
- usb = l->data;
-
- if ((ret = sl2_get_device_info(*usb, &dev_info)) < 0) {
- sr_warn("Failed to get device information: %d.", ret);
- sr_usb_dev_inst_free(usb);
- continue;
- }
-
- if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
- sr_err("Device instance malloc failed.");
- sr_usb_dev_inst_free(usb);
- continue;
- }
-
- if (!(devc->xfer_in = libusb_alloc_transfer(0))) {
- sr_err("Transfer malloc failed.");
- sr_usb_dev_inst_free(usb);
- g_free(devc);
- continue;
- }
-
- if (!(devc->xfer_out = libusb_alloc_transfer(0))) {
- sr_err("Transfer malloc failed.");
- sr_usb_dev_inst_free(usb);
- libusb_free_transfer(devc->xfer_in);
- g_free(devc);
- continue;
- }
-
- fw_ver_str = g_strdup_printf("%u.%u", dev_info.fw_ver_major,
- dev_info.fw_ver_minor);
- if (!fw_ver_str) {
- sr_err("Firmware string malloc failed.");
- sr_usb_dev_inst_free(usb);
- libusb_free_transfer(devc->xfer_in);
- libusb_free_transfer(devc->xfer_out);
- g_free(devc);
- continue;
- }
-
- sdi = sr_dev_inst_new(device_index, SR_ST_INACTIVE, VENDOR_NAME,
- MODEL_NAME, fw_ver_str);
- g_free(fw_ver_str);
- if (!sdi) {
- sr_err("sr_dev_inst_new failed.");
- sr_usb_dev_inst_free(usb);
- libusb_free_transfer(devc->xfer_in);
- libusb_free_transfer(devc->xfer_out);
- g_free(devc);
- continue;
- }
-
- sdi->priv = devc;
- sdi->driver = di;
- sdi->inst_type = SR_INST_USB;
- sdi->conn = usb;
-
- for (i = 0; channel_names[i]; i++) {
- ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
- channel_names[i]);
- sdi->channels = g_slist_append(sdi->channels, ch);
- devc->channels[i] = ch;
- }
-
- devc->state = STATE_IDLE;
- devc->next_state = STATE_IDLE;
-
- /* Set default samplerate. */
- sl2_set_samplerate(sdi, DEFAULT_SAMPLERATE);
-
- /* Set default capture ratio. */
- devc->capture_ratio = 0;
-
- /* Set default after trigger delay. */
- devc->after_trigger_delay = 0;
-
- memset(devc->xfer_buf_in, 0, LIBUSB_CONTROL_SETUP_SIZE +
- PACKET_LENGTH);
- memset(devc->xfer_buf_out, 0, LIBUSB_CONTROL_SETUP_SIZE +
- PACKET_LENGTH);
-
- libusb_fill_control_setup(devc->xfer_buf_in,
- USB_REQUEST_TYPE_IN, USB_HID_GET_REPORT,
- USB_HID_REPORT_TYPE_FEATURE, USB_INTERFACE,
- PACKET_LENGTH);
- libusb_fill_control_setup(devc->xfer_buf_out,
- USB_REQUEST_TYPE_OUT, USB_HID_SET_REPORT,
- USB_HID_REPORT_TYPE_FEATURE, USB_INTERFACE,
- PACKET_LENGTH);
-
- devc->xfer_data_in = devc->xfer_buf_in +
- LIBUSB_CONTROL_SETUP_SIZE;
- devc->xfer_data_out = devc->xfer_buf_out +
- LIBUSB_CONTROL_SETUP_SIZE;
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- device_index++;
- }
-
- g_slist_free(usb_devices);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static void clear_dev_context(void *priv)
-{
- struct dev_context *devc;
-
- devc = priv;
-
- sr_dbg("Device context cleared.");
-
- libusb_free_transfer(devc->xfer_in);
- libusb_free_transfer(devc->xfer_out);
- g_free(devc);
-}
-
-static int dev_clear(void)
-{
- return std_dev_clear(di, &clear_dev_context);
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- uint8_t buffer[PACKET_LENGTH];
- int ret;
-
- if (!(drvc = di->priv)) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
- devc = sdi->priv;
-
- if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
- return SR_ERR;
-
- /*
- * Determine if a kernel driver is active on this interface and, if so,
- * detach it.
- */
- if (libusb_kernel_driver_active(usb->devhdl, USB_INTERFACE) == 1) {
- ret = libusb_detach_kernel_driver(usb->devhdl, USB_INTERFACE);
- if (ret < 0) {
- sr_err("Failed to detach kernel driver: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
- }
-
- if ((ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE)) < 0) {
- sr_err("Failed to claim interface: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- libusb_fill_control_transfer(devc->xfer_in, usb->devhdl,
- devc->xfer_buf_in, sl2_receive_transfer_in,
- sdi, USB_TIMEOUT);
-
- libusb_fill_control_transfer(devc->xfer_out, usb->devhdl,
- devc->xfer_buf_out, sl2_receive_transfer_out,
- sdi, USB_TIMEOUT);
-
- memset(buffer, 0, sizeof(buffer));
-
- buffer[0] = CMD_RESET;
- if ((ret = sl2_transfer_out(usb->devhdl, buffer)) != PACKET_LENGTH) {
- sr_err("Device reset failed: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- /*
- * Set the device to idle state. If the device is not in idle state it
- * possibly will reset itself after a few seconds without being used
- * and thereby close the connection.
- */
- buffer[0] = CMD_IDLE;
- if ((ret = sl2_transfer_out(usb->devhdl, buffer)) != PACKET_LENGTH) {
- sr_err("Failed to set device in idle state: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
-
- if (!usb->devhdl)
- return SR_OK;
-
- libusb_release_interface(usb->devhdl, USB_INTERFACE);
- libusb_close(usb->devhdl);
-
- usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- return dev_clear();
-}
-
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- int ret;
-
- (void)cg;
-
- ret = SR_OK;
- devc = sdi->priv;
-
- switch (key) {
- case SR_CONF_SAMPLERATE:
- *data = g_variant_new_uint64(devc->samplerate);
- break;
- case SR_CONF_CAPTURE_RATIO:
- *data = g_variant_new_uint64(devc->capture_ratio);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- uint64_t samplerate, limit_samples, capture_ratio;
- int ret;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- ret = SR_OK;
-
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- limit_samples = g_variant_get_uint64(data);
- ret = sl2_set_limit_samples(sdi, limit_samples);
- break;
- case SR_CONF_SAMPLERATE:
- samplerate = g_variant_get_uint64(data);
- ret = sl2_set_samplerate(sdi, samplerate);
- break;
- case SR_CONF_CAPTURE_RATIO:
- capture_ratio = g_variant_get_uint64(data);
- ret = sl2_set_capture_ratio(sdi, capture_ratio);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- GVariant *gvar, *grange[2];
- GVariantBuilder gvb;
- int ret;
-
- (void)sdi;
- (void)cg;
-
- ret = SR_OK;
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, hwcaps,
- ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- sl2_samplerates, ARRAY_SIZE(sl2_samplerates),
- sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TRIGGER_MATCH:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- trigger_matches, ARRAY_SIZE(trigger_matches),
- sizeof(int32_t));
- break;
- case SR_CONF_LIMIT_SAMPLES:
- grange[0] = g_variant_new_uint64(0);
- grange[1] = g_variant_new_uint64(MAX_SAMPLES);
- *data = g_variant_new_tuple(grange, 2);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- uint16_t trigger_bytes, tmp;
- unsigned int i, j;
- int ret;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- drvc = di->priv;
-
- devc->cb_data = cb_data;
- devc->wait_data_ready_locked = TRUE;
- devc->stopping_in_progress = FALSE;
- devc->transfer_error = FALSE;
- devc->samples_processed = 0;
- devc->channel = 0;
- devc->sample_packet = 0;
-
- /*
- * The trigger must be configured first because the calculation of the
- * pre and post trigger samples depends on a configured trigger.
- */
- sl2_convert_trigger(sdi);
- sl2_calculate_trigger_samples(sdi);
-
- trigger_bytes = devc->pre_trigger_bytes + devc->post_trigger_bytes;
-
- /* Calculate the number of expected sample packets. */
- devc->num_sample_packets = trigger_bytes / PACKET_NUM_SAMPLE_BYTES;
-
- /* Round up the number of expected sample packets. */
- if (trigger_bytes % PACKET_NUM_SAMPLE_BYTES != 0)
- devc->num_sample_packets++;
-
- devc->num_enabled_channels = 0;
-
- /*
- * Count the number of enabled channels and number them for a sequential
- * access.
- */
- for (i = 0, j = 0; i < NUM_CHANNELS; i++) {
- if (devc->channels[i]->enabled) {
- devc->num_enabled_channels++;
- devc->channel_map[j] = i;
- j++;
- }
- }
-
- sr_dbg("Number of enabled channels: %i.", devc->num_enabled_channels);
-
- /* Set up the transfer buffer for the acquisition. */
- devc->xfer_data_out[0] = CMD_SAMPLE;
- devc->xfer_data_out[1] = 0x00;
-
- tmp = GUINT16_TO_LE(devc->pre_trigger_bytes);
- memcpy(devc->xfer_data_out + 2, &tmp, sizeof(tmp));
-
- tmp = GUINT16_TO_LE(devc->post_trigger_bytes);
- memcpy(devc->xfer_data_out + 4, &tmp, sizeof(tmp));
-
- devc->xfer_data_out[6] = devc->samplerate_id;
- devc->xfer_data_out[7] = devc->trigger_type;
- devc->xfer_data_out[8] = devc->trigger_channel;
- devc->xfer_data_out[9] = 0x00;
-
- tmp = GUINT16_TO_LE(devc->after_trigger_delay);
- memcpy(devc->xfer_data_out + 10, &tmp, sizeof(tmp));
-
- if ((ret = libusb_submit_transfer(devc->xfer_out)) != 0) {
- sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- usb_source_add(sdi->session, drvc->sr_ctx, 100,
- ikalogic_scanalogic2_receive_data, (void *)sdi);
-
- sr_dbg("Acquisition started successfully.");
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- devc->next_state = STATE_SAMPLE;
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- sr_dbg("Stopping acquisition.");
-
- sdi->status = SR_ST_STOPPING;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver ikalogic_scanalogic2_driver_info = {
- .name = "ikalogic-scanalogic2",
- .longname = "IKALOGIC Scanalogic-2",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = 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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Marc Schink <sigrok-dev@marcschink.de>
- *
- * 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 "protocol.h"
-
-extern struct sr_dev_driver ikalogic_scanalogic2_driver_info;
-static struct sr_dev_driver *di = &ikalogic_scanalogic2_driver_info;
-
-extern uint64_t sl2_samplerates[NUM_SAMPLERATES];
-
-static void stop_acquisition(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc = sdi->driver->priv;
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
-
- devc = sdi->priv;
-
- /* Remove USB file descriptors from polling. */
- usb_source_remove(sdi->session, drvc->sr_ctx);
-
- packet.type = SR_DF_END;
- sr_session_send(devc->cb_data, &packet);
-
- sdi->status = SR_ST_ACTIVE;
-}
-
-static void abort_acquisition(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc = sdi->driver->priv;
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
-
- devc = sdi->priv;
-
- /* Remove USB file descriptors from polling. */
- usb_source_remove(sdi->session, drvc->sr_ctx);
-
- packet.type = SR_DF_END;
- sr_session_send(devc->cb_data, &packet);
-
- sdi->driver->dev_close(sdi);
-}
-
-static void buffer_sample_data(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- unsigned int offset, packet_length;
-
- devc = sdi->priv;
-
- if (devc->channels[devc->channel]->enabled) {
- offset = devc->sample_packet * PACKET_NUM_SAMPLE_BYTES;
-
- /*
- * Determine the packet length to ensure that the last packet
- * will not exceed the buffer size.
- */
- packet_length = MIN(PACKET_NUM_SAMPLE_BYTES,
- MAX_DEV_SAMPLE_BYTES - offset);
-
- /*
- * Skip the first 4 bytes of the source buffer because they
- * contain channel and packet information only.
- */
- memcpy(devc->sample_buffer[devc->channel] + offset,
- devc->xfer_data_in + 4, packet_length);
- }
-}
-
-static void process_sample_data(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- uint8_t i, j, tmp, buffer[PACKET_NUM_SAMPLES], *ptr[NUM_CHANNELS];
- uint16_t offset, n = 0;
- int8_t k;
-
- devc = sdi->priv;
- offset = devc->sample_packet * PACKET_NUM_SAMPLE_BYTES;
-
- /*
- * Array of pointers to the sample data of all channels up to the last
- * enabled one for an uniform access to them. Note that the currently
- * received samples always belong to the last enabled channel.
- */
- for (i = 0; i < devc->num_enabled_channels - 1; i++)
- ptr[i] = devc->sample_buffer[devc->channel_map[i]] + offset;
-
- /*
- * Skip the first 4 bytes of the buffer because they contain channel
- * and packet information only.
- */
- ptr[i] = devc->xfer_data_in + 4;
-
- for (i = 0; i < PACKET_NUM_SAMPLE_BYTES; i++) {
- /* Stop processing if all requested samples are processed. */
- if (devc->samples_processed == devc->limit_samples)
- break;
-
- k = 7;
-
- if (devc->samples_processed == 0) {
- /*
- * Adjust the position of the first sample to be
- * processed because possibly more samples than
- * necessary might have been acquired. This is because
- * the number of acquired samples is always rounded up
- * to a multiple of 8.
- */
- k = k - (devc->pre_trigger_bytes * 8) +
- devc->pre_trigger_samples;
-
- sr_dbg("Start processing at sample: %d.", 7 - k);
-
- /*
- * Send the trigger before the first sample is
- * processed if no pre trigger samples were calculated
- * through the capture ratio.
- */
- if (devc->trigger_type != TRIGGER_TYPE_NONE &&
- devc->pre_trigger_samples == 0) {
- packet.type = SR_DF_TRIGGER;
- sr_session_send(devc->cb_data, &packet);
- }
- }
-
- for (; k >= 0; k--) {
- /*
- * Stop processing if all requested samples are
- * processed.
- */
- if (devc->samples_processed == devc->limit_samples)
- break;
-
- buffer[n] = 0;
-
- /*
- * Extract the current sample for each enabled channel
- * and store them in the buffer.
- */
- for (j = 0; j < devc->num_enabled_channels; j++) {
- tmp = (ptr[j][i] & (1 << k)) >> k;
- buffer[n] |= tmp << devc->channel_map[j];
- }
-
- n++;
- devc->samples_processed++;
-
- /*
- * Send all processed samples and the trigger if the
- * number of processed samples reaches the calculated
- * number of pre trigger samples.
- */
- if (devc->samples_processed == devc->pre_trigger_samples &&
- devc->trigger_type != TRIGGER_TYPE_NONE) {
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = n;
- logic.unitsize = 1;
- logic.data = buffer;
- sr_session_send(devc->cb_data, &packet);
-
- packet.type = SR_DF_TRIGGER;
- sr_session_send(devc->cb_data, &packet);
-
- n = 0;
- }
- }
- }
-
- if (n > 0) {
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = n;
- logic.unitsize = 1;
- logic.data = buffer;
- sr_session_send(devc->cb_data, &packet);
- }
-}
-
-SR_PRIV int ikalogic_scanalogic2_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct drv_context *drvc;
- struct timeval tv;
- int64_t current_time, time_elapsed;
- int ret = 0;
-
- (void)fd;
- (void)revents;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- drvc = di->priv;
- current_time = g_get_monotonic_time();
-
- if (devc->state == STATE_WAIT_DATA_READY &&
- !devc->wait_data_ready_locked) {
- time_elapsed = current_time - devc->wait_data_ready_time;
-
- /*
- * Check here for stopping in addition to the transfer
- * callback functions to avoid waiting until the
- * WAIT_DATA_READY_INTERVAL has expired.
- */
- if (sdi->status == SR_ST_STOPPING) {
- if (!devc->stopping_in_progress) {
- devc->next_state = STATE_RESET_AND_IDLE;
- devc->stopping_in_progress = TRUE;
- ret = libusb_submit_transfer(devc->xfer_in);
- }
- } else if (time_elapsed >= WAIT_DATA_READY_INTERVAL) {
- devc->wait_data_ready_locked = TRUE;
- ret = libusb_submit_transfer(devc->xfer_in);
- }
- }
-
- if (ret != 0) {
- sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
- abort_acquisition(sdi);
- return TRUE;
- }
-
- tv.tv_sec = 0;
- tv.tv_usec = 0;
-
- libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
- NULL);
-
- /* Check if an error occurred on a transfer. */
- if (devc->transfer_error)
- abort_acquisition(sdi);
-
- return TRUE;
-}
-
-SR_PRIV void sl2_receive_transfer_in( struct libusb_transfer *transfer)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- uint8_t last_channel;
- int ret = 0;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
-
- if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
- sr_err("Transfer to device failed: %i.", transfer->status);
- devc->transfer_error = TRUE;
- return;
- }
-
- if (sdi->status == SR_ST_STOPPING && !devc->stopping_in_progress) {
- devc->next_state = STATE_RESET_AND_IDLE;
- devc->stopping_in_progress = TRUE;
-
- if (libusb_submit_transfer(devc->xfer_in) != 0) {
- sr_err("Submit transfer failed: %s.",
- libusb_error_name(ret));
- devc->transfer_error = TRUE;
- }
-
- return;
- }
-
- if (devc->state != devc->next_state)
- sr_spew("State changed from %i to %i.",
- devc->state, devc->next_state);
- devc->state = devc->next_state;
-
- if (devc->state == STATE_WAIT_DATA_READY) {
- /* Check if the received data are a valid device status. */
- if (devc->xfer_data_in[0] == 0x05) {
- if (devc->xfer_data_in[1] == STATUS_WAITING_FOR_TRIGGER)
- sr_dbg("Waiting for trigger.");
- else if (devc->xfer_data_in[1] == STATUS_SAMPLING)
- sr_dbg("Sampling in progress.");
- }
-
- /*
- * Check if the received data are a valid device status and the
- * sample data are ready.
- */
- if (devc->xfer_data_in[0] == 0x05 &&
- devc->xfer_data_in[1] == STATUS_DATA_READY) {
- devc->next_state = STATE_RECEIVE_DATA;
- ret = libusb_submit_transfer(transfer);
- } else {
- devc->wait_data_ready_locked = FALSE;
- devc->wait_data_ready_time = g_get_monotonic_time();
- }
- } else if (devc->state == STATE_RECEIVE_DATA) {
- last_channel = devc->channel_map[devc->num_enabled_channels - 1];
-
- if (devc->channel < last_channel) {
- buffer_sample_data(sdi);
- } else if (devc->channel == last_channel) {
- process_sample_data(sdi);
- } else {
- /*
- * Stop acquisition because all samples of enabled
- * channels are processed.
- */
- devc->next_state = STATE_RESET_AND_IDLE;
- }
-
- devc->sample_packet++;
- devc->sample_packet %= devc->num_sample_packets;
-
- if (devc->sample_packet == 0)
- devc->channel++;
-
- ret = libusb_submit_transfer(transfer);
- } else if (devc->state == STATE_RESET_AND_IDLE) {
- /* Check if the received data are a valid device status. */
- if (devc->xfer_data_in[0] == 0x05) {
- if (devc->xfer_data_in[1] == STATUS_DEVICE_READY) {
- devc->next_state = STATE_IDLE;
- devc->xfer_data_out[0] = CMD_IDLE;
- } else {
- devc->next_state = STATE_WAIT_DEVICE_READY;
- devc->xfer_data_out[0] = CMD_RESET;
- }
-
- ret = libusb_submit_transfer(devc->xfer_out);
- } else {
- /*
- * The received device status is invalid which
- * indicates that the device is not ready to accept
- * commands. Request a new device status until a valid
- * device status is received.
- */
- ret = libusb_submit_transfer(transfer);
- }
- } else if (devc->state == STATE_WAIT_DEVICE_READY) {
- /* Check if the received data are a valid device status. */
- if (devc->xfer_data_in[0] == 0x05) {
- if (devc->xfer_data_in[1] == STATUS_DEVICE_READY) {
- devc->next_state = STATE_IDLE;
- devc->xfer_data_out[0] = CMD_IDLE;
- } else {
- /*
- * The received device status is valid but the
- * device is not ready. Probably the device did
- * not recognize the last reset. Reset the
- * device again.
- */
- devc->xfer_data_out[0] = CMD_RESET;
- }
-
- ret = libusb_submit_transfer(devc->xfer_out);
- } else {
- /*
- * The device is not ready and therefore not able to
- * change to the idle state. Request a new device
- * status until the device is ready.
- */
- ret = libusb_submit_transfer(transfer);
- }
- }
-
- if (ret != 0) {
- sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
- devc->transfer_error = TRUE;
- }
-}
-
-SR_PRIV void sl2_receive_transfer_out( struct libusb_transfer *transfer)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- int ret = 0;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
-
- if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
- sr_err("Transfer to device failed: %i.", transfer->status);
- devc->transfer_error = TRUE;
- return;
- }
-
- if (sdi->status == SR_ST_STOPPING && !devc->stopping_in_progress) {
- devc->next_state = STATE_RESET_AND_IDLE;
- devc->stopping_in_progress = TRUE;
-
- if (libusb_submit_transfer(devc->xfer_in) != 0) {
- sr_err("Submit transfer failed: %s.",
- libusb_error_name(ret));
-
- devc->transfer_error = TRUE;
- }
-
- return;
- }
-
- if (devc->state != devc->next_state)
- sr_spew("State changed from %i to %i.",
- devc->state, devc->next_state);
- devc->state = devc->next_state;
-
- if (devc->state == STATE_IDLE) {
- stop_acquisition(sdi);
- } else if (devc->state == STATE_SAMPLE) {
- devc->next_state = STATE_WAIT_DATA_READY;
- ret = libusb_submit_transfer(devc->xfer_in);
- } else if (devc->state == STATE_WAIT_DEVICE_READY) {
- ret = libusb_submit_transfer(devc->xfer_in);
- }
-
- if (ret != 0) {
- sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
- devc->transfer_error = TRUE;
- }
-}
-
-SR_PRIV int sl2_set_samplerate(const struct sr_dev_inst *sdi,
- uint64_t samplerate)
-{
- struct dev_context *devc;
- unsigned int i;
-
- devc = sdi->priv;
-
- for (i = 0; i < NUM_SAMPLERATES; i++) {
- if (sl2_samplerates[i] == samplerate) {
- devc->samplerate = samplerate;
- devc->samplerate_id = NUM_SAMPLERATES - i - 1;
- return SR_OK;
- }
- }
-
- return SR_ERR_ARG;
-}
-
-SR_PRIV int sl2_set_limit_samples(const struct sr_dev_inst *sdi,
- uint64_t limit_samples)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- if (limit_samples == 0) {
- sr_err("Invalid number of limit samples: %" PRIu64 ".",
- limit_samples);
- return SR_ERR_ARG;
- }
-
- if (limit_samples > MAX_SAMPLES)
- limit_samples = MAX_SAMPLES;
-
- sr_dbg("Limit samples set to %" PRIu64 ".", limit_samples);
-
- devc->limit_samples = limit_samples;
-
- return SR_OK;
-}
-
-SR_PRIV int sl2_convert_trigger(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_trigger *trigger;
- struct sr_trigger_stage *stage;
- struct sr_trigger_match *match;
- const GSList *l, *m;
- int num_triggers_anyedge;
-
- devc = sdi->priv;
-
- /* Disable the trigger by default. */
- devc->trigger_channel = TRIGGER_CHANNEL_0;
- devc->trigger_type = TRIGGER_TYPE_NONE;
-
- if (!(trigger = sr_session_trigger_get(sdi->session)))
- return SR_OK;
-
- if (g_slist_length(trigger->stages) > 1) {
- sr_err("This device only supports 1 trigger stage.");
- return SR_ERR;
- }
-
- num_triggers_anyedge = 0;
- for (l = trigger->stages; l; l = l->next) {
- stage = l->data;
- for (m = stage->matches; m; m = m->next) {
- match = m->data;
- if (!match->channel->enabled)
- /* Ignore disabled channels with a trigger. */
- continue;
- devc->trigger_channel = match->channel->index + 1;
- switch (match->match) {
- case SR_TRIGGER_RISING:
- devc->trigger_type = TRIGGER_TYPE_POSEDGE;
- break;
- case SR_TRIGGER_FALLING:
- devc->trigger_type = TRIGGER_TYPE_NEGEDGE;
- break;
- case SR_TRIGGER_EDGE:
- devc->trigger_type = TRIGGER_TYPE_ANYEDGE;
- num_triggers_anyedge++;
- break;
- }
- }
- }
-
- /*
- * Set trigger to any edge on all channels if the trigger for each
- * channel is set to any edge.
- */
- if (num_triggers_anyedge == NUM_CHANNELS) {
- devc->trigger_channel = TRIGGER_CHANNEL_ALL;
- devc->trigger_type = TRIGGER_TYPE_ANYEDGE;
- }
-
- sr_dbg("Trigger set to channel 0x%02x and type 0x%02x.",
- devc->trigger_channel, devc->trigger_type);
-
- return SR_OK;
-}
-
-SR_PRIV int sl2_set_capture_ratio(const struct sr_dev_inst *sdi,
- uint64_t capture_ratio)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- if (capture_ratio > 100) {
- sr_err("Invalid capture ratio: %" PRIu64 " %%.", capture_ratio);
- return SR_ERR_ARG;
- }
-
- sr_info("Capture ratio set to %" PRIu64 " %%.", capture_ratio);
-
- devc->capture_ratio = capture_ratio;
-
- return SR_OK;
-}
-
-SR_PRIV int sl2_set_after_trigger_delay(const struct sr_dev_inst *sdi,
- uint64_t after_trigger_delay)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- if (after_trigger_delay > MAX_AFTER_TRIGGER_DELAY) {
- sr_err("Invalid after trigger delay: %" PRIu64 " ms.",
- after_trigger_delay);
- return SR_ERR_ARG;
- }
-
- sr_info("After trigger delay set to %" PRIu64 " ms.",
- after_trigger_delay);
-
- devc->after_trigger_delay = after_trigger_delay;
-
- return SR_OK;
-}
-
-SR_PRIV void sl2_calculate_trigger_samples(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- uint64_t pre_trigger_samples, post_trigger_samples;
- uint16_t pre_trigger_bytes, post_trigger_bytes;
- uint8_t cr;
-
- devc = sdi->priv;
- cr = devc->capture_ratio;
-
- /* Ignore the capture ratio if no trigger is enabled. */
- if (devc->trigger_type == TRIGGER_TYPE_NONE)
- cr = 0;
-
- pre_trigger_samples = (devc->limit_samples * cr) / 100;
- post_trigger_samples = (devc->limit_samples * (100 - cr)) / 100;
-
- /*
- * Increase the number of post trigger samples by one to compensate the
- * possible loss of a sample through integer rounding.
- */
- if (pre_trigger_samples + post_trigger_samples != devc->limit_samples)
- post_trigger_samples++;
-
- /*
- * The device requires the number of samples in multiples of 8 which
- * will also be called sample bytes in the following.
- */
- pre_trigger_bytes = pre_trigger_samples / 8;
- post_trigger_bytes = post_trigger_samples / 8;
-
- /*
- * Round up the number of sample bytes to ensure that at least the
- * requested number of samples will be acquired. Note that due to this
- * rounding the buffer to store these sample bytes needs to be at least
- * one sample byte larger than the minimal number of sample bytes
- * needed to store the requested samples.
- */
- if (pre_trigger_samples % 8 != 0)
- pre_trigger_bytes++;
-
- if (post_trigger_samples % 8 != 0)
- post_trigger_bytes++;
-
- sr_info("Pre trigger samples: %" PRIu64 ".", pre_trigger_samples);
- sr_info("Post trigger samples: %" PRIu64 ".", post_trigger_samples);
- sr_dbg("Pre trigger sample bytes: %" PRIu16 ".", pre_trigger_bytes);
- sr_dbg("Post trigger sample bytes: %" PRIu16 ".", post_trigger_bytes);
-
- devc->pre_trigger_samples = pre_trigger_samples;
- devc->pre_trigger_bytes = pre_trigger_bytes;
- devc->post_trigger_bytes = post_trigger_bytes;
-}
-
-SR_PRIV int sl2_get_device_info(struct sr_usb_dev_inst usb,
- struct device_info *dev_info)
-{
- struct drv_context *drvc;
- uint8_t buffer[PACKET_LENGTH];
- int ret;
-
- drvc = di->priv;
-
- if (!dev_info)
- return SR_ERR_ARG;
-
- if (sr_usb_open(drvc->sr_ctx->libusb_ctx, &usb) != SR_OK)
- return SR_ERR;
-
- /*
- * Determine if a kernel driver is active on this interface and, if so,
- * detach it.
- */
- if (libusb_kernel_driver_active(usb.devhdl, USB_INTERFACE) == 1) {
- ret = libusb_detach_kernel_driver(usb.devhdl,
- USB_INTERFACE);
-
- if (ret < 0) {
- sr_err("Failed to detach kernel driver: %s.",
- libusb_error_name(ret));
- libusb_close(usb.devhdl);
- return SR_ERR;
- }
- }
-
- ret = libusb_claim_interface(usb.devhdl, USB_INTERFACE);
-
- if (ret) {
- sr_err("Failed to claim interface: %s.",
- libusb_error_name(ret));
- libusb_close(usb.devhdl);
- return SR_ERR;
- }
-
- memset(buffer, 0, sizeof(buffer));
-
- /*
- * Reset the device to ensure it is in a proper state to request the
- * device information.
- */
- buffer[0] = CMD_RESET;
- if ((ret = sl2_transfer_out(usb.devhdl, buffer)) != PACKET_LENGTH) {
- sr_err("Resetting of device failed: %s.",
- libusb_error_name(ret));
- libusb_release_interface(usb.devhdl, USB_INTERFACE);
- libusb_close(usb.devhdl);
- return SR_ERR;
- }
-
- buffer[0] = CMD_INFO;
- if ((ret = sl2_transfer_out(usb.devhdl, buffer)) != PACKET_LENGTH) {
- sr_err("Requesting of device information failed: %s.",
- libusb_error_name(ret));
- libusb_release_interface(usb.devhdl, USB_INTERFACE);
- libusb_close(usb.devhdl);
- return SR_ERR;
- }
-
- if ((ret = sl2_transfer_in(usb.devhdl, buffer)) != PACKET_LENGTH) {
- sr_err("Receiving of device information failed: %s.",
- libusb_error_name(ret));
- libusb_release_interface(usb.devhdl, USB_INTERFACE);
- libusb_close(usb.devhdl);
- return SR_ERR;
- }
-
- memcpy(&(dev_info->serial), buffer + 1, sizeof(uint32_t));
- dev_info->serial = GUINT32_FROM_LE(dev_info->serial);
-
- dev_info->fw_ver_major = buffer[5];
- dev_info->fw_ver_minor = buffer[6];
-
- buffer[0] = CMD_RESET;
- if ((ret = sl2_transfer_out(usb.devhdl, buffer)) != PACKET_LENGTH) {
- sr_err("Device reset failed: %s.", libusb_error_name(ret));
- libusb_release_interface(usb.devhdl, USB_INTERFACE);
- libusb_close(usb.devhdl);
- return SR_ERR;
- }
-
- /*
- * Set the device to idle state. If the device is not in idle state it
- * possibly will reset itself after a few seconds without being used
- * and thereby close the connection.
- */
- buffer[0] = CMD_IDLE;
- if ((ret = sl2_transfer_out(usb.devhdl, buffer)) != PACKET_LENGTH) {
- sr_err("Failed to set device in idle state: %s.",
- libusb_error_name(ret));
- libusb_release_interface(usb.devhdl, USB_INTERFACE);
- libusb_close(usb.devhdl);
- return SR_ERR;
- }
-
- ret = libusb_release_interface(usb.devhdl, USB_INTERFACE);
-
- if (ret < 0) {
- sr_err("Failed to release interface: %s.",
- libusb_error_name(ret));
- libusb_close(usb.devhdl);
- return SR_ERR;
- }
-
- libusb_close(usb.devhdl);
-
- return SR_OK;
-}
-
-SR_PRIV int sl2_transfer_in(libusb_device_handle *dev_handle, uint8_t *data)
-{
- return libusb_control_transfer(dev_handle, USB_REQUEST_TYPE_IN,
- USB_HID_GET_REPORT, USB_HID_REPORT_TYPE_FEATURE, USB_INTERFACE,
- (unsigned char *)data, PACKET_LENGTH, USB_TIMEOUT);
-}
-
-SR_PRIV int sl2_transfer_out(libusb_device_handle *dev_handle, uint8_t *data)
-{
- return libusb_control_transfer(dev_handle, USB_REQUEST_TYPE_OUT,
- USB_HID_SET_REPORT, USB_HID_REPORT_TYPE_FEATURE, USB_INTERFACE,
- (unsigned char *)data, PACKET_LENGTH, USB_TIMEOUT);
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Marc Schink <sigrok-dev@marcschink.de>
- *
- * 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_IKALOGIC_SCANALOGIC2_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_IKALOGIC_SCANALOGIC2_PROTOCOL_H
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "ikalogic-scanalogic2"
-
-#define VENDOR_NAME "IKALOGIC"
-#define MODEL_NAME "Scanalogic-2"
-
-#define USB_VID_PID "20a0.4123"
-#define USB_INTERFACE 0
-#define USB_TIMEOUT 5000
-
-#define USB_REQUEST_TYPE_IN (LIBUSB_REQUEST_TYPE_CLASS | \
- LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN)
-
-#define USB_REQUEST_TYPE_OUT (LIBUSB_REQUEST_TYPE_CLASS | \
- LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT)
-
-#define USB_HID_GET_REPORT 0x01
-#define USB_HID_SET_REPORT 0x09
-#define USB_HID_REPORT_TYPE_FEATURE 0x300
-
-#define NUM_SAMPLERATES 11
-#define NUM_CHANNELS 4
-
-/*
- * Number of sample bytes and samples the device can acquire. Note that the
- * vendor software can acquire 32736 sample bytes only but the device is capable
- * to acquire up to 32766 sample bytes.
- */
-#define MAX_DEV_SAMPLE_BYTES 32766
-#define MAX_DEV_SAMPLES (MAX_INT_SAMPLE_BYTES * 8)
-
-/* Number of sample bytes and samples the driver can acquire. */
-#define MAX_SAMPLE_BYTES (MAX_DEV_SAMPLE_BYTES - 1)
-#define MAX_SAMPLES (MAX_SAMPLE_BYTES * 8)
-
-/* Maximum time that the trigger can be delayed in milliseconds. */
-#define MAX_AFTER_TRIGGER_DELAY 65000
-
-#define PACKET_LENGTH 128
-
-/* Number of sample bytes per packet where a sample byte contains 8 samples. */
-#define PACKET_NUM_SAMPLE_BYTES 124
-
-/* Number of samples per packet. */
-#define PACKET_NUM_SAMPLES (PACKET_NUM_SAMPLE_BYTES * 8)
-
-#define DEFAULT_SAMPLERATE SR_KHZ(1.25)
-
-/*
- * Time interval between the last status of available data received and the
- * moment when the next status request will be sent in microseconds.
- */
-#define WAIT_DATA_READY_INTERVAL 1500000
-
-#define CMD_SAMPLE 0x01
-#define CMD_RESET 0x02
-#define CMD_IDLE 0x07
-#define CMD_INFO 0x0a
-
-#define TRIGGER_CHANNEL_ALL 0x00
-#define TRIGGER_CHANNEL_0 0x01
-#define TRIGGER_CHANNEL_1 0x02
-#define TRIGGER_CHANNEL_2 0x03
-
-#define TRIGGER_TYPE_NEGEDGE 0x00
-#define TRIGGER_TYPE_POSEDGE 0x01
-#define TRIGGER_TYPE_ANYEDGE 0x02
-#define TRIGGER_TYPE_NONE 0x03
-
-#define STATUS_DATA_READY 0x60
-#define STATUS_WAITING_FOR_TRIGGER 0x61
-#define STATUS_SAMPLING 0x62
-#define STATUS_DEVICE_READY 0x63
-
-struct device_info {
- /* Serial number of the device. */
- uint32_t serial;
-
- /* Major version of the firmware. */
- uint8_t fw_ver_major;
-
- /* Minor version of the firmware. */
- uint8_t fw_ver_minor;
-};
-
-enum {
- STATE_IDLE = 0,
- STATE_SAMPLE,
- STATE_WAIT_DATA_READY,
- STATE_RECEIVE_DATA,
- STATE_RESET_AND_IDLE,
- STATE_WAIT_DEVICE_READY
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Current selected samplerate. */
- uint64_t samplerate;
-
- /* Device specific identifier for the current samplerate. */
- uint8_t samplerate_id;
-
- /* Current sampling limit. */
- uint64_t limit_samples;
-
- /* Calculated number of pre-trigger samples. */
- uint64_t pre_trigger_samples;
-
- /* Number of pre- and post-trigger sample bytes to acquire. */
- uint16_t pre_trigger_bytes;
- uint16_t post_trigger_bytes;
-
- /* Device specific settings for the trigger. */
- uint8_t trigger_channel;
- uint8_t trigger_type;
-
- unsigned int capture_ratio;
-
- /* Time that the trigger will be delayed in milliseconds. */
- uint16_t after_trigger_delay;
-
- void *cb_data;
-
- /* Array to provide an index based access to all channels. */
- const struct sr_channel *channels[NUM_CHANNELS];
-
- struct libusb_transfer *xfer_in, *xfer_out;
-
- /*
- * Buffer to store setup and payload data for incoming and outgoing
- * transfers.
- */
- uint8_t xfer_buf_in[LIBUSB_CONTROL_SETUP_SIZE + PACKET_LENGTH];
- uint8_t xfer_buf_out[LIBUSB_CONTROL_SETUP_SIZE + PACKET_LENGTH];
-
- /* Pointers to the payload of incoming and outgoing transfers. */
- uint8_t *xfer_data_in, *xfer_data_out;
-
- /* Current state of the state machine */
- unsigned int state;
-
- /* Next state of the state machine. */
- unsigned int next_state;
-
- /*
- * Locking variable to ensure that no status about available data will
- * be requested until the last status was received.
- */
- gboolean wait_data_ready_locked;
-
- /*
- * Time when the last response about the status of available data was
- * received.
- */
- int64_t wait_data_ready_time;
-
- /*
- * Indicates that stopping of the acquisition is currently in progress.
- */
- gboolean stopping_in_progress;
-
- /*
- * Buffer which contains the samples received from the device for each
- * channel except the last one. The samples of the last channel will be
- * processed directly after they will be received.
- */
- uint8_t sample_buffer[NUM_CHANNELS - 1][MAX_DEV_SAMPLE_BYTES];
-
- /* Expected number of sample packets for each channel. */
- uint16_t num_sample_packets;
-
- /* Number of samples already processed. */
- uint64_t samples_processed;
-
- /* Sample packet number that is currently processed. */
- uint16_t sample_packet;
-
- /* Channel number that is currently processed. */
- uint8_t channel;
-
- /* Number of enabled channels. */
- unsigned int num_enabled_channels;
-
- /* Array to provide a sequential access to all enabled channel indices. */
- uint8_t channel_map[NUM_CHANNELS];
-
- /* Indicates whether a transfer failed. */
- gboolean transfer_error;
-};
-
-SR_PRIV int ikalogic_scanalogic2_receive_data(int fd, int revents, void *cb_data);
-SR_PRIV void sl2_receive_transfer_in(struct libusb_transfer *transfer);
-SR_PRIV void sl2_receive_transfer_out(struct libusb_transfer *transfer);
-SR_PRIV int sl2_set_samplerate(const struct sr_dev_inst *sdi,
- uint64_t samplerate);
-SR_PRIV int sl2_set_limit_samples(const struct sr_dev_inst *sdi,
- uint64_t limit_samples);
-SR_PRIV int sl2_convert_trigger(const struct sr_dev_inst *sdi);
-SR_PRIV int sl2_set_capture_ratio(const struct sr_dev_inst *sdi,
- uint64_t capture_ratio);
-SR_PRIV int sl2_set_after_trigger_delay(const struct sr_dev_inst *sdi,
- uint64_t after_trigger_delay);
-SR_PRIV void sl2_calculate_trigger_samples(const struct sr_dev_inst *sdi);
-SR_PRIV int sl2_get_device_info(struct sr_usb_dev_inst usb,
- struct device_info *dev_info);
-SR_PRIV int sl2_transfer_in(libusb_device_handle *dev_handle, uint8_t *data);
-SR_PRIV int sl2_transfer_out(libusb_device_handle *dev_handle, uint8_t *data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "protocol.h"
-
-#define USB_VENDOR_ID 0x0403
-#define USB_DEVICE_ID 0x6014
-#define USB_VENDOR_NAME "IKALOGIC"
-#define USB_MODEL_NAME "ScanaPLUS"
-#define USB_IPRODUCT "SCANAPLUS"
-
-#define SAMPLE_BUF_SIZE (8 * 1024 * 1024)
-
-static const int32_t hwcaps[] = {
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_SAMPLERATE,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_CONTINUOUS, // TODO?
-};
-
-/* Channels are numbered 1-9. */
-static const char *channel_names[] = {
- "1", "2", "3", "4", "5", "6", "7", "8", "9",
- NULL,
-};
-
-/* Note: The IKALOGIC ScanaPLUS always samples at 100MHz. */
-static uint64_t samplerates[1] = { SR_MHZ(100) };
-
-SR_PRIV struct sr_dev_driver ikalogic_scanaplus_driver_info;
-static struct sr_dev_driver *di = &ikalogic_scanaplus_driver_info;
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
-
-static void clear_helper(void *priv)
-{
- struct dev_context *devc;
-
- devc = priv;
-
- ftdi_free(devc->ftdic);
- g_free(devc->compressed_buf);
- g_free(devc->sample_buf);
-}
-
-static int dev_clear(void)
-{
- return std_dev_clear(di, clear_helper);
-}
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- struct drv_context *drvc;
- struct dev_context *devc;
- GSList *devices;
- unsigned int i;
- int ret;
-
- (void)options;
-
- drvc = di->priv;
-
- devices = NULL;
-
- /* Allocate memory for our private device context. */
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- goto err_free_nothing;
- }
-
- /* Allocate memory for the incoming compressed samples. */
- if (!(devc->compressed_buf = g_try_malloc0(COMPRESSED_BUF_SIZE))) {
- sr_err("compressed_buf malloc failed.");
- goto err_free_devc;
- }
-
- /* Allocate memory for the uncompressed samples. */
- if (!(devc->sample_buf = g_try_malloc0(SAMPLE_BUF_SIZE))) {
- sr_err("sample_buf malloc failed.");
- goto err_free_compressed_buf;
- }
-
- /* Allocate memory for the FTDI context (ftdic) and initialize it. */
- if (!(devc->ftdic = ftdi_new())) {
- sr_err("Failed to initialize libftdi.");
- goto err_free_sample_buf;
- }
-
- /* Check for the device and temporarily open it. */
- ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID, USB_DEVICE_ID,
- USB_IPRODUCT, NULL);
- if (ret < 0) {
- /* Log errors, except for -3 ("device not found"). */
- if (ret != -3)
- sr_err("Failed to open device (%d): %s", ret,
- ftdi_get_error_string(devc->ftdic));
- goto err_free_ftdic;
- }
-
- /* Register the device with libsigrok. */
- sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING,
- USB_VENDOR_NAME, USB_MODEL_NAME, NULL);
- if (!sdi) {
- sr_err("Failed to create device instance.");
- goto err_close_ftdic;
- }
- sdi->driver = di;
- sdi->priv = devc;
-
- for (i = 0; channel_names[i]; i++) {
- if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
- channel_names[i])))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- devices = g_slist_append(devices, sdi);
- drvc->instances = g_slist_append(drvc->instances, sdi);
-
- /* Close device. We'll reopen it again when we need it. */
- scanaplus_close(devc);
-
- return devices;
-
-err_close_ftdic:
- scanaplus_close(devc);
-err_free_ftdic:
- ftdi_free(devc->ftdic); /* NOT free() or g_free()! */
-err_free_sample_buf:
- g_free(devc->sample_buf);
-err_free_compressed_buf:
- g_free(devc->compressed_buf);
-err_free_devc:
- g_free(devc);
-err_free_nothing:
-
- return NULL;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int ret;
-
- devc = sdi->priv;
-
- /* Select interface A, otherwise communication will fail. */
- ret = ftdi_set_interface(devc->ftdic, INTERFACE_A);
- if (ret < 0) {
- sr_err("Failed to set FTDI interface A (%d): %s", ret,
- ftdi_get_error_string(devc->ftdic));
- return SR_ERR;
- }
- sr_dbg("FTDI chip interface A set successfully.");
-
- /* Open the device. */
- ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID, USB_DEVICE_ID,
- USB_IPRODUCT, NULL);
- if (ret < 0) {
- sr_err("Failed to open device (%d): %s", ret,
- ftdi_get_error_string(devc->ftdic));
- return SR_ERR;
- }
- sr_dbg("FTDI device opened successfully.");
-
- /* Purge RX/TX buffers in the FTDI chip. */
- if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
- sr_err("Failed to purge FTDI RX/TX buffers (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- goto err_dev_open_close_ftdic;
- }
- sr_dbg("FTDI chip buffers purged successfully.");
-
- /* Reset the FTDI bitmode. */
- ret = ftdi_set_bitmode(devc->ftdic, 0xff, BITMODE_RESET);
- if (ret < 0) {
- sr_err("Failed to reset the FTDI chip bitmode (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- goto err_dev_open_close_ftdic;
- }
- sr_dbg("FTDI chip bitmode reset successfully.");
-
- /* Set FTDI bitmode to "sync FIFO". */
- ret = ftdi_set_bitmode(devc->ftdic, 0xff, BITMODE_SYNCFF);
- if (ret < 0) {
- sr_err("Failed to put FTDI chip into sync FIFO mode (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- goto err_dev_open_close_ftdic;
- }
- sr_dbg("FTDI chip sync FIFO mode entered successfully.");
-
- /* Set the FTDI latency timer to 2. */
- ret = ftdi_set_latency_timer(devc->ftdic, 2);
- if (ret < 0) {
- sr_err("Failed to set FTDI latency timer (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- goto err_dev_open_close_ftdic;
- }
- sr_dbg("FTDI chip latency timer set successfully.");
-
- /* Set the FTDI read data chunk size to 64kB. */
- ret = ftdi_read_data_set_chunksize(devc->ftdic, 64 * 1024);
- if (ret < 0) {
- sr_err("Failed to set FTDI read data chunk size (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- goto err_dev_open_close_ftdic;
- }
- sr_dbg("FTDI chip read data chunk size set successfully.");
-
- /* Get the ScanaPLUS device ID from the FTDI EEPROM. */
- if ((ret = scanaplus_get_device_id(devc)) < 0) {
- sr_err("Failed to get ScanaPLUS device ID: %d.", ret);
- goto err_dev_open_close_ftdic;
- }
- sr_dbg("Received ScanaPLUS device ID successfully: %02x %02x %02x.",
- devc->devid[0], devc->devid[1], devc->devid[2]);
-
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-
-err_dev_open_close_ftdic:
- scanaplus_close(devc);
- return SR_ERR;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- int ret;
- struct dev_context *devc;
-
- ret = SR_OK;
- devc = sdi->priv;
-
- if (sdi->status == SR_ST_ACTIVE) {
- sr_dbg("Status ACTIVE, closing device.");
- ret = scanaplus_close(devc);
- } else {
- sr_spew("Status not ACTIVE, nothing to do.");
- }
-
- sdi->status = SR_ST_INACTIVE;
-
- return ret;
-}
-
-static int cleanup(void)
-{
- return dev_clear();
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- /* The ScanaPLUS samplerate is 100MHz and can't be changed. */
- *data = g_variant_new_uint64(SR_MHZ(100));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- if (g_variant_get_uint64(data) != SR_MHZ(100)) {
- sr_err("ScanaPLUS only supports samplerate = 100MHz.");
- return SR_ERR_ARG;
- }
- /* Nothing to do, the ScanaPLUS samplerate is always 100MHz. */
- break;
- case SR_CONF_LIMIT_MSEC:
- if (g_variant_get_uint64(data) == 0)
- return SR_ERR_ARG;
- devc->limit_msec = g_variant_get_uint64(data);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- if (g_variant_get_uint64(data) == 0)
- return SR_ERR_ARG;
- devc->limit_samples = g_variant_get_uint64(data);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- GVariant *gvar;
- GVariantBuilder gvb;
-
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- samplerates, ARRAY_SIZE(samplerates),
- sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- int ret;
- struct dev_context *devc;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv))
- return SR_ERR_BUG;
-
- if (!devc->ftdic)
- return SR_ERR_BUG;
-
- /* TODO: Configure channels later (thresholds etc.). */
-
- devc->cb_data = cb_data;
-
- /* Properly reset internal variables before every new acquisition. */
- devc->compressed_bytes_ignored = 0;
- devc->samples_sent = 0;
- devc->bytes_received = 0;
-
- if ((ret = scanaplus_init(devc)) < 0)
- return ret;
-
- if ((ret = scanaplus_start_acquisition(devc)) < 0)
- return ret;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(sdi, LOG_PREFIX);
-
- /* Hook up a dummy handler to receive data from the device. */
- sr_session_source_add(sdi->session, -1, G_IO_IN, 0, scanaplus_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct sr_datafeed_packet packet;
-
- (void)cb_data;
-
- sr_dbg("Stopping acquisition.");
- sr_session_source_remove(sdi->session, -1);
-
- /* Send end packet to the session bus. */
- sr_dbg("Sending SR_DF_END.");
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver ikalogic_scanaplus_driver_info = {
- .name = "ikalogic-scanaplus",
- .longname = "IKALOGIC ScanaPLUS",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = 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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "protocol.h"
-
-/*
- * Logic level thresholds.
- *
- * For each of the two channel groups (1-4 and 5-9), the logic level
- * threshold can be set independently.
- *
- * The threshold can be set to values that are usable for systems with
- * different voltage levels, e.g. for 1.8V or 3.3V systems.
- *
- * The actual threshold value is always the middle of the values below.
- * E.g. for a system voltage level of 1.8V, the threshold is at 0.9V. That
- * means that values <= 0.9V are considered to be a logic 0/low, and
- * values > 0.9V are considered to be a logic 1/high.
- *
- * - 1.2V system: threshold = 0.6V
- * - 1.5V system: threshold = 0.75V
- * - 1.8V system: threshold = 0.9V
- * - 2.8V system: threshold = 1.4V
- * - 3.3V system: threshold = 1.65V
- */
-#define THRESHOLD_1_2V_SYSTEM 0x2e
-#define THRESHOLD_1_5V_SYSTEM 0x39
-#define THRESHOLD_1_8V_SYSTEM 0x45
-#define THRESHOLD_2_8V_SYSTEM 0x6c
-#define THRESHOLD_3_3V_SYSTEM 0x7f
-
-static int scanaplus_write(struct dev_context *devc, uint8_t *buf, int size)
-{
- int i, bytes_written;
- GString *s;
-
- /* Note: Caller checks devc, devc->ftdic, buf, size. */
-
- s = g_string_sized_new(100);
- g_string_printf(s, "Writing %d bytes: ", size);
- for (i = 0; i < size; i++)
- g_string_append_printf(s, "0x%02x ", buf[i]);
- sr_spew("%s", s->str);
- g_string_free(s, TRUE);
-
- bytes_written = ftdi_write_data(devc->ftdic, buf, size);
- if (bytes_written < 0) {
- sr_err("Failed to write FTDI data (%d): %s.",
- bytes_written, ftdi_get_error_string(devc->ftdic));
- } else if (bytes_written != size) {
- sr_err("FTDI write error, only %d/%d bytes written: %s.",
- bytes_written, size, ftdi_get_error_string(devc->ftdic));
- }
-
- return bytes_written;
-}
-
-SR_PRIV int scanaplus_close(struct dev_context *devc)
-{
- int ret;
-
- /* Note: Caller checks devc and devc->ftdic. */
-
- if ((ret = ftdi_usb_close(devc->ftdic)) < 0) {
- sr_err("Failed to close FTDI device (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static void scanaplus_uncompress_block(struct dev_context *devc,
- uint64_t num_bytes)
-{
- uint64_t i, j;
- uint8_t num_samples, low, high;
-
- for (i = 0; i < num_bytes; i += 2) {
- num_samples = devc->compressed_buf[i + 0] >> 1;
-
- low = devc->compressed_buf[i + 0] & (1 << 0);
- high = devc->compressed_buf[i + 1];
-
- for (j = 0; j < num_samples; j++) {
- devc->sample_buf[devc->bytes_received++] = high;
- devc->sample_buf[devc->bytes_received++] = low;
- }
- }
-}
-
-static void send_samples(struct dev_context *devc, uint64_t samples_to_send)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
-
- sr_spew("Sending %" PRIu64 " samples.", samples_to_send);
-
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = samples_to_send * 2;
- logic.unitsize = 2; /* We need 2 bytes for 9 channels. */
- logic.data = devc->sample_buf;
- sr_session_send(devc->cb_data, &packet);
-
- devc->samples_sent += samples_to_send;
- devc->bytes_received -= samples_to_send * 2;
-}
-
-SR_PRIV int scanaplus_get_device_id(struct dev_context *devc)
-{
- int ret;
- uint16_t val1, val2;
-
- /* FTDI EEPROM indices 16+17 contain the 3 device ID bytes. */
- if ((ret = ftdi_read_eeprom_location(devc->ftdic, 16, &val1)) < 0) {
- sr_err("Failed to read EEPROM index 16 (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- return SR_ERR;
- }
- if ((ret = ftdi_read_eeprom_location(devc->ftdic, 17, &val2)) < 0) {
- sr_err("Failed to read EEPROM index 17 (%d): %s.",
- ret, ftdi_get_error_string(devc->ftdic));
- return SR_ERR;
- }
-
- /*
- * Note: Bit 7 of the three bytes must not be used, apparently.
- *
- * Even though the three bits can be either 0 or 1 (we've seen both
- * in actual ScanaPLUS devices), the device ID as sent to the FPGA
- * has bit 7 of each byte zero'd out.
- *
- * It is unknown whether bit 7 of these bytes has any meaning,
- * whether it's used somewhere, or whether it can be simply ignored.
- */
- devc->devid[0] = ((val1 >> 0) & 0xff) & ~(1 << 7);
- devc->devid[1] = ((val1 >> 8) & 0xff) & ~(1 << 7);
- devc->devid[2] = ((val2 >> 0) & 0xff) & ~(1 << 7);
-
- return SR_OK;
-}
-
-static int scanaplus_clear_device_id(struct dev_context *devc)
-{
- uint8_t buf[2];
-
- buf[0] = 0x8c;
- buf[1] = 0x00;
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- buf[0] = 0x8e;
- buf[1] = 0x00;
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- buf[0] = 0x8f;
- buf[1] = 0x00;
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- return SR_OK;
-}
-
-static int scanaplus_send_device_id(struct dev_context *devc)
-{
- uint8_t buf[2];
-
- buf[0] = 0x8c;
- buf[1] = devc->devid[0];
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- buf[0] = 0x8e;
- buf[1] = devc->devid[1];
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- buf[0] = 0x8f;
- buf[1] = devc->devid[2];
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- return SR_OK;
-}
-
-SR_PRIV int scanaplus_init(struct dev_context *devc)
-{
- int i;
- uint8_t buf[8];
-
- buf[0] = 0x88;
- buf[1] = 0x41;
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- buf[0] = 0x89;
- buf[1] = 0x64;
- buf[2] = 0x8a;
- buf[3] = 0x64;
- if (scanaplus_write(devc, (uint8_t *)&buf, 4) < 0)
- return SR_ERR;
-
- buf[0] = 0x88;
- buf[1] = 0x41;
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- buf[0] = 0x88;
- buf[1] = 0x40;
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- buf[0] = 0x8d;
- buf[1] = 0x01;
- buf[2] = 0x8d;
- buf[3] = 0x05;
- buf[4] = 0x8d;
- buf[5] = 0x01;
- buf[6] = 0x8d;
- buf[7] = 0x02;
- if (scanaplus_write(devc, (uint8_t *)&buf, 8) < 0)
- return SR_ERR;
-
- for (i = 0; i < 57; i++) {
- buf[0] = 0x8d;
- buf[1] = 0x06;
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- buf[0] = 0x8d;
- buf[1] = 0x02;
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
- }
-
- if (scanaplus_send_device_id(devc) < 0)
- return SR_ERR;
-
- buf[0] = 0x88;
- buf[1] = 0x40;
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- return SR_OK;
-}
-
-SR_PRIV int scanaplus_start_acquisition(struct dev_context *devc)
-{
- uint8_t buf[4];
-
- /* Threshold and differential channel settings not yet implemented. */
-
- buf[0] = 0x89;
- buf[1] = 0x7f; /* Logic level threshold for channels 1-4. */
- buf[2] = 0x8a;
- buf[3] = 0x7f; /* Logic level threshold for channels 5-9. */
- if (scanaplus_write(devc, (uint8_t *)&buf, 4) < 0)
- return SR_ERR;
-
- buf[0] = 0x88;
- buf[1] = 0x40; /* Special config of channels 5/6 and 7/8. */
- /* 0x40: normal, 0x50: ch56 diff, 0x48: ch78 diff, 0x58: ch5678 diff */
- if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
- return SR_ERR;
-
- if (scanaplus_clear_device_id(devc) < 0)
- return SR_ERR;
-
- if (scanaplus_send_device_id(devc) < 0)
- return SR_ERR;
-
- return SR_OK;
-}
-
-SR_PRIV int scanaplus_receive_data(int fd, int revents, void *cb_data)
-{
- int bytes_read;
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- uint64_t max, n;
-
- (void)fd;
- (void)revents;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- if (!devc->ftdic)
- return TRUE;
-
- /* Get a block of data. */
- bytes_read = ftdi_read_data(devc->ftdic, devc->compressed_buf,
- COMPRESSED_BUF_SIZE);
- if (bytes_read < 0) {
- sr_err("Failed to read FTDI data (%d): %s.",
- bytes_read, ftdi_get_error_string(devc->ftdic));
- sdi->driver->dev_acquisition_stop(sdi, sdi);
- return FALSE;
- }
- if (bytes_read == 0) {
- sr_spew("Received 0 bytes, nothing to do.");
- return TRUE;
- }
-
- /*
- * After a ScanaPLUS acquisition starts, a bunch of samples will be
- * returned as all-zero, no matter which signals are actually present
- * on the channels. This is probably due to the FPGA reconfiguring some
- * of its internal state/config during this time.
- *
- * As far as we know there is apparently no way for the PC-side to
- * know when this "reconfiguration" starts or ends. The FTDI chip
- * will return all-zero "dummy" samples during this time, which is
- * indistinguishable from actual all-zero samples.
- *
- * We currently simply ignore the first 64kB of data after an
- * acquisition starts. Empirical tests have shown that the
- * "reconfigure" time is a lot less than that usually.
- */
- if (devc->compressed_bytes_ignored < COMPRESSED_BUF_SIZE) {
- /* Ignore the first 64kB of data of every acquisition. */
- sr_spew("Ignoring first 64kB chunk of data.");
- devc->compressed_bytes_ignored += COMPRESSED_BUF_SIZE;
- return TRUE;
- }
-
- /* TODO: Handle bytes_read which is not a multiple of 2? */
- scanaplus_uncompress_block(devc, bytes_read);
-
- n = devc->samples_sent + (devc->bytes_received / 2);
- max = (SR_MHZ(100) / 1000) * devc->limit_msec;
-
- if (devc->limit_samples && (n >= devc->limit_samples)) {
- send_samples(devc, devc->limit_samples - devc->samples_sent);
- sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- } else if (devc->limit_msec && (n >= max)) {
- send_samples(devc, max - devc->samples_sent);
- sr_info("Requested time limit reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- } else {
- send_samples(devc, devc->bytes_received / 2);
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef LIBSIGROK_HARDWARE_IKALOGIC_SCANAPLUS_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_IKALOGIC_SCANAPLUS_PROTOCOL_H
-
-#include <stdint.h>
-#include <string.h>
-#include <glib.h>
-#include <ftdi.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "ikalogic-scanaplus"
-
-#define COMPRESSED_BUF_SIZE (64 * 1024)
-
-/* Private, per-device-instance driver context. */
-struct dev_context {
- /** FTDI device context (used by libftdi). */
- struct ftdi_context *ftdic;
-
- /** The current sampling limit (in ms). */
- uint64_t limit_msec;
-
- /** The current sampling limit (in number of samples). */
- uint64_t limit_samples;
-
- void *cb_data;
-
- uint8_t *compressed_buf;
- uint64_t compressed_bytes_ignored;
- uint8_t *sample_buf;
- uint64_t bytes_received;
- uint64_t samples_sent;
-
- /** ScanaPLUS unique device ID (3 bytes). */
- uint8_t devid[3];
-};
-
-SR_PRIV int scanaplus_close(struct dev_context *devc);
-SR_PRIV int scanaplus_get_device_id(struct dev_context *devc);
-SR_PRIV int scanaplus_init(struct dev_context *devc);
-SR_PRIV int scanaplus_start_acquisition(struct dev_context *devc);
-SR_PRIV int scanaplus_receive_data(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 <string.h>
-#include "protocol.h"
-
-#define USB_CONN "1041.8101"
-#define VENDOR "Kecheng"
-#define USB_INTERFACE 0
-
-static const int32_t hwcaps[] = {
- SR_CONF_SOUNDLEVELMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_CONTINUOUS,
- SR_CONF_DATALOG,
- SR_CONF_SPL_WEIGHT_FREQ,
- SR_CONF_SPL_WEIGHT_TIME,
- SR_CONF_DATA_SOURCE,
-};
-
-SR_PRIV const uint64_t kecheng_kc_330b_sample_intervals[][2] = {
- { 1, 8 },
- { 1, 2 },
- { 1, 1 },
- { 2, 1 },
- { 5, 1 },
- { 10, 1 },
- { 60, 1 },
-};
-
-static const char *weight_freq[] = {
- "A",
- "C",
-};
-
-static const char *weight_time[] = {
- "F",
- "S",
-};
-
-static const char *data_sources[] = {
- "Live",
- "Memory",
-};
-
-SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info;
-static struct sr_dev_driver *di = &kecheng_kc_330b_driver_info;
-
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static int scan_kecheng(struct sr_usb_dev_inst *usb, char **model)
-{
- struct drv_context *drvc;
- int len, ret;
- unsigned char cmd, buf[32];
-
- drvc = di->priv;
- if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
- return SR_ERR;
-
- cmd = CMD_IDENTIFY;
- ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, &cmd, 1, &len, 5);
- if (ret != 0) {
- libusb_close(usb->devhdl);
- sr_dbg("Failed to send Identify command: %s", libusb_error_name(ret));
- return SR_ERR;
- }
-
- ret = libusb_bulk_transfer(usb->devhdl, EP_IN, buf, 32, &len, 10);
- if (ret != 0) {
- libusb_close(usb->devhdl);
- sr_dbg("Failed to receive response: %s", libusb_error_name(ret));
- return SR_ERR;
- }
-
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
-
- if (len < 2 || buf[0] != (CMD_IDENTIFY | 0x80) || buf[1] > 30) {
- sr_dbg("Invalid response to Identify command");
- return SR_ERR;
- }
-
- buf[buf[1] + 2] = '\x0';
- *model = g_strndup((const gchar *)buf + 2, 30);
-
- return SR_OK;
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- GSList *usb_devices, *devices, *l;
- char *model;
-
- (void)options;
-
- drvc = di->priv;
- drvc->instances = NULL;
-
- devices = NULL;
- if ((usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, USB_CONN))) {
- /* We have a list of sr_usb_dev_inst matching the connection
- * string. Wrap them in sr_dev_inst and we're done. */
- for (l = usb_devices; l; l = l->next) {
- if (scan_kecheng(l->data, &model) != SR_OK)
- continue;
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR,
- model, NULL)))
- return NULL;
- g_free(model);
- sdi->driver = di;
- sdi->inst_type = SR_INST_USB;
- sdi->conn = l->data;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "SPL")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
- sr_dbg("Device context malloc failed.");
- return NULL;
- }
- sdi->priv = devc;
- devc->limit_samples = 0;
- /* The protocol provides no way to read the current
- * settings, so we'll enforce these. */
- devc->sample_interval = DEFAULT_SAMPLE_INTERVAL;
- devc->alarm_low = DEFAULT_ALARM_LOW;
- devc->alarm_high = DEFAULT_ALARM_HIGH;
- devc->mqflags = DEFAULT_WEIGHT_TIME | DEFAULT_WEIGHT_FREQ;
- devc->data_source = DEFAULT_DATA_SOURCE;
- devc->config_dirty = FALSE;
-
- /* TODO: Set date/time? */
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- }
- g_slist_free(usb_devices);
- } else
- g_slist_free_full(usb_devices, g_free);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc;
- struct sr_usb_dev_inst *usb;
- int ret;
-
- if (!(drvc = di->priv)) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
-
- if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
- return SR_ERR;
-
- if ((ret = libusb_set_configuration(usb->devhdl, 1))) {
- sr_err("Failed to set configuration: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- if ((ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE))) {
- sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- sdi->status = SR_ST_ACTIVE;
-
- return ret;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
-
- if (!usb->devhdl)
- /* Nothing to do. */
- return SR_OK;
-
- /* This allows a frontend to configure the device without ever
- * doing an acquisition step. */
- devc = sdi->priv;
- if (!devc->config_dirty)
- kecheng_kc_330b_configure(sdi);
-
- libusb_release_interface(usb->devhdl, USB_INTERFACE);
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- int ret;
- struct drv_context *drvc;
-
- if (!(drvc = di->priv))
- /* Can get called on an unused driver, doesn't matter. */
- return SR_OK;
-
-
- ret = std_dev_clear(di, NULL);
- g_free(drvc);
- di->priv = NULL;
-
- return ret;
-}
-
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- GVariant *rational[2];
- const uint64_t *si;
-
- (void)cg;
-
- devc = sdi->priv;
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_SAMPLE_INTERVAL:
- si = kecheng_kc_330b_sample_intervals[devc->sample_interval];
- rational[0] = g_variant_new_uint64(si[0]);
- rational[1] = g_variant_new_uint64(si[1]);
- *data = g_variant_new_tuple(rational, 2);
- break;
- case SR_CONF_DATALOG:
- /* There really isn't a way to be sure the device is logging. */
- return SR_ERR_NA;
- break;
- case SR_CONF_SPL_WEIGHT_FREQ:
- if (devc->mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A)
- *data = g_variant_new_string("A");
- else
- *data = g_variant_new_string("C");
- break;
- case SR_CONF_SPL_WEIGHT_TIME:
- if (devc->mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_F)
- *data = g_variant_new_string("F");
- else
- *data = g_variant_new_string("S");
- break;
- case SR_CONF_DATA_SOURCE:
- if (devc->data_source == DATA_SOURCE_LIVE)
- *data = g_variant_new_string("Live");
- else
- *data = g_variant_new_string("Memory");
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- uint64_t p, q;
- unsigned int i;
- int tmp, ret;
- const char *tmp_str;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- devc = sdi->priv;
- ret = SR_OK;
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- case SR_CONF_SAMPLE_INTERVAL:
- g_variant_get(data, "(tt)", &p, &q);
- for (i = 0; i < ARRAY_SIZE(kecheng_kc_330b_sample_intervals); i++) {
- if (kecheng_kc_330b_sample_intervals[i][0] != p || kecheng_kc_330b_sample_intervals[i][1] != q)
- continue;
- devc->sample_interval = i;
- devc->config_dirty = TRUE;
- break;
- }
- if (i == ARRAY_SIZE(kecheng_kc_330b_sample_intervals))
- ret = SR_ERR_ARG;
- break;
- case SR_CONF_SPL_WEIGHT_FREQ:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "A"))
- tmp = SR_MQFLAG_SPL_FREQ_WEIGHT_A;
- else if (!strcmp(tmp_str, "C"))
- tmp = SR_MQFLAG_SPL_FREQ_WEIGHT_C;
- else
- return SR_ERR_ARG;
- devc->mqflags &= ~(SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
- devc->mqflags |= tmp;
- devc->config_dirty = TRUE;
- break;
- case SR_CONF_SPL_WEIGHT_TIME:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "F"))
- tmp = SR_MQFLAG_SPL_TIME_WEIGHT_F;
- else if (!strcmp(tmp_str, "S"))
- tmp = SR_MQFLAG_SPL_TIME_WEIGHT_S;
- else
- return SR_ERR_ARG;
- devc->mqflags &= ~(SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
- devc->mqflags |= tmp;
- devc->config_dirty = TRUE;
- break;
- case SR_CONF_DATA_SOURCE:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "Live"))
- devc->data_source = DATA_SOURCE_LIVE;
- else if (!strcmp(tmp_str, "Memory"))
- devc->data_source = DATA_SOURCE_MEMORY;
- else
- return SR_ERR;
- devc->config_dirty = TRUE;
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- GVariant *tuple, *rational[2];
- GVariantBuilder gvb;
- unsigned int i;
-
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLE_INTERVAL:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (i = 0; i < ARRAY_SIZE(kecheng_kc_330b_sample_intervals); i++) {
- rational[0] = g_variant_new_uint64(kecheng_kc_330b_sample_intervals[i][0]);
- rational[1] = g_variant_new_uint64(kecheng_kc_330b_sample_intervals[i][1]);
- tuple = g_variant_new_tuple(rational, 2);
- g_variant_builder_add_value(&gvb, tuple);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_SPL_WEIGHT_FREQ:
- *data = g_variant_new_strv(weight_freq, ARRAY_SIZE(weight_freq));
- break;
- case SR_CONF_SPL_WEIGHT_TIME:
- *data = g_variant_new_strv(weight_time, ARRAY_SIZE(weight_time));
- break;
- case SR_CONF_DATA_SOURCE:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_meta meta;
- struct sr_config *src;
- struct sr_usb_dev_inst *usb;
- GVariant *gvar, *rational[2];
- const uint64_t *si;
- int stored_mqflags, req_len, buf_len, len, ret;
- unsigned char buf[9];
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- drvc = di->priv;
- devc = sdi->priv;
- usb = sdi->conn;
-
- devc->cb_data = cb_data;
- devc->num_samples = 0;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- if (devc->data_source == DATA_SOURCE_LIVE) {
- /* Force configuration. */
- kecheng_kc_330b_configure(sdi);
-
- if (kecheng_kc_330b_status_get(sdi, &ret) != SR_OK)
- return SR_ERR;
- if (ret != DEVICE_ACTIVE) {
- sr_err("Device is inactive");
- /* Still continue though, since the device will
- * just return 30.0 until the user hits the button
- * on the device -- and then start feeding good
- * samples back. */
- }
- } else {
- if (kecheng_kc_330b_log_info_get(sdi, buf) != SR_OK)
- return SR_ERR;
- stored_mqflags = buf[4] ? SR_MQFLAG_SPL_TIME_WEIGHT_S : SR_MQFLAG_SPL_TIME_WEIGHT_F;
- stored_mqflags |= buf[5] ? SR_MQFLAG_SPL_FREQ_WEIGHT_C : SR_MQFLAG_SPL_FREQ_WEIGHT_A;
- devc->stored_samples = (buf[7] << 8) | buf[8];
- if (devc->stored_samples == 0) {
- /* Notify frontend of empty log by sending start/end packets. */
- packet.type = SR_DF_END;
- sr_session_send(cb_data, &packet);
- return SR_OK;
- }
-
- if (devc->limit_samples && devc->limit_samples < devc->stored_samples)
- devc->stored_samples = devc->limit_samples;
-
- si = kecheng_kc_330b_sample_intervals[buf[1]];
- rational[0] = g_variant_new_uint64(si[0]);
- rational[1] = g_variant_new_uint64(si[1]);
- gvar = g_variant_new_tuple(rational, 2);
- src = sr_config_new(SR_CONF_SAMPLE_INTERVAL, gvar);
- packet.type = SR_DF_META;
- packet.payload = &meta;
- meta.config = g_slist_append(NULL, src);
- sr_session_send(devc->cb_data, &packet);
- g_free(src);
- }
-
- if (!(devc->xfer = libusb_alloc_transfer(0)))
- return SR_ERR;
-
- usb_source_add(sdi->session, drvc->sr_ctx, 10,
- kecheng_kc_330b_handle_events, (void *)sdi);
-
- if (devc->data_source == DATA_SOURCE_LIVE) {
- buf[0] = CMD_GET_LIVE_SPL;
- buf_len = 1;
- devc->state = LIVE_SPL_WAIT;
- devc->last_live_request = g_get_monotonic_time() / 1000;
- req_len = 3;
- } else {
- buf[0] = CMD_GET_LOG_DATA;
- buf[1] = 0;
- buf[2] = 0;
- buf_len = 4;
- devc->state = LOG_DATA_WAIT;
- if (devc->stored_samples < 63)
- buf[3] = devc->stored_samples;
- else
- buf[3] = 63;
- /* Command ack byte + 2 bytes per sample. */
- req_len = 1 + buf[3] * 2;
- }
-
- ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, buf_len, &len, 5);
- if (ret != 0 || len != 1) {
- sr_dbg("Failed to start acquisition: %s", libusb_error_name(ret));
- libusb_free_transfer(devc->xfer);
- return SR_ERR;
- }
-
- libusb_fill_bulk_transfer(devc->xfer, usb->devhdl, EP_IN, devc->buf,
- req_len, kecheng_kc_330b_receive_transfer, (void *)sdi, 15);
- if (libusb_submit_transfer(devc->xfer) != 0) {
- libusb_free_transfer(devc->xfer);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
-
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- /* Signal USB transfer handler to clean up and stop. */
- sdi->status = SR_ST_STOPPING;
-
- devc = sdi->priv;
- if (devc->data_source == DATA_SOURCE_MEMORY && devc->config_dirty) {
- /* The protocol doesn't have a command to clear stored data;
- * it clears it whenever new configuration is set. That means
- * we can't just configure the device any time we want when
- * it's in DATA_SOURCE_MEMORY mode. The only safe time to do
- * it is now, when we're sure we've pulled in all the stored
- * data. */
- kecheng_kc_330b_configure(sdi);
- }
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info = {
- .name = "kecheng-kc-330b",
- .longname = "Kecheng KC-330B",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 <string.h>
-#include "protocol.h"
-
-extern struct sr_dev_driver kecheng_kc_330b_driver_info;
-static struct sr_dev_driver *di = &kecheng_kc_330b_driver_info;
-extern const uint64_t kecheng_kc_330b_sample_intervals[][2];
-
-SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_dev_inst *sdi;
- struct sr_usb_dev_inst *usb;
- struct timeval tv;
- const uint64_t *intv_entry;
- gint64 now, interval;
- int offset, len, ret;
- unsigned char buf[4];
-
- (void)fd;
- (void)revents;
-
- drvc = di->priv;
- sdi = cb_data;
- devc = sdi->priv;
- usb = sdi->conn;
-
- memset(&tv, 0, sizeof(struct timeval));
- libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
- NULL);
-
- if (sdi->status == SR_ST_STOPPING) {
- libusb_free_transfer(devc->xfer);
- usb_source_remove(sdi->session, drvc->sr_ctx);
- packet.type = SR_DF_END;
- sr_session_send(cb_data, &packet);
- sdi->status = SR_ST_ACTIVE;
- return TRUE;
- }
-
- if (devc->state == LIVE_SPL_IDLE) {
- /* Request samples at the interval rate. */
- now = g_get_monotonic_time() / 1000;
- intv_entry = kecheng_kc_330b_sample_intervals[devc->sample_interval];
- interval = intv_entry[0] * 1000 / intv_entry[1];
- if (now - devc->last_live_request > interval) {
- buf[0] = CMD_GET_LIVE_SPL;
- ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 1, &len, 5);
- if (ret != 0 || len != 1) {
- sr_dbg("Failed to request new acquisition: %s",
- libusb_error_name(ret));
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
- devc->cb_data);
- return TRUE;
- }
- libusb_submit_transfer(devc->xfer);
- devc->last_live_request = now;
- devc->state = LIVE_SPL_WAIT;
- }
- } else if (devc->state == LIVE_SPL_IDLE) {
- buf[0] = CMD_GET_LOG_DATA;
- offset = devc->num_samples / 63;
- buf[1] = (offset >> 8) & 0xff;
- buf[2] = offset & 0xff;
- if (devc->stored_samples - devc->num_samples > 63)
- buf[3] = 63;
- else
- /* Last chunk. */
- buf[3] = devc->stored_samples - devc->num_samples;
- ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 4, &len, 5);
- if (ret != 0 || len != 4) {
- sr_dbg("Failed to request next chunk: %s",
- libusb_error_name(ret));
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
- devc->cb_data);
- return TRUE;
- }
- libusb_submit_transfer(devc->xfer);
- devc->state = LIVE_SPL_WAIT;
- }
-
- return TRUE;
-}
-
-static void send_data(const struct sr_dev_inst *sdi, void *buf, unsigned int buf_len)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
-
- devc = sdi->priv;
-
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
- analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
- analog.mqflags = devc->mqflags;
- analog.unit = SR_UNIT_DECIBEL_SPL;
- analog.channels = sdi->channels;
- analog.num_samples = buf_len;
- analog.data = buf;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
-
-}
-
-SR_PRIV void kecheng_kc_330b_receive_transfer(struct libusb_transfer *transfer)
-{
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- float fvalue[64];
- int packet_has_error, num_samples, i;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
-
- packet_has_error = FALSE;
- switch (transfer->status) {
- case LIBUSB_TRANSFER_NO_DEVICE:
- /* USB device was unplugged. */
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
- devc->cb_data);
- return;
- case LIBUSB_TRANSFER_COMPLETED:
- case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though */
- break;
- default:
- packet_has_error = TRUE;
- break;
- }
-
- if (packet_has_error)
- return;
-
- if (devc->state == LIVE_SPL_WAIT) {
- if (transfer->actual_length != 3 || transfer->buffer[0] != 0x88) {
- sr_dbg("Received invalid SPL packet.");
- } else {
- fvalue[0] = ((transfer->buffer[1] << 8) + transfer->buffer[2]) / 10.0;
- send_data(sdi, fvalue, 1);
- devc->num_samples++;
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
- devc->cb_data);
- } else {
- /* let USB event handler fire off another
- * request when the time is right. */
- devc->state = LIVE_SPL_IDLE;
- }
- }
- } else if (devc->state == LOG_DATA_WAIT) {
- if (transfer->actual_length < 1 || !(transfer->actual_length & 0x01)) {
- sr_dbg("Received invalid stored SPL packet.");
- } else {
- num_samples = (transfer->actual_length - 1) / 2;
- for (i = 0; i < num_samples; i++) {
- fvalue[i] = transfer->buffer[1 + i * 2] << 8;
- fvalue[i] += transfer->buffer[1 + i * 2 + 1];
- fvalue[i] /= 10.0;
- }
- send_data(sdi, fvalue, 1);
- devc->num_samples += num_samples;
- if (devc->num_samples >= devc->stored_samples) {
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
- devc->cb_data);
- } else {
- /* let USB event handler fire off another
- * request when the time is right. */
- devc->state = LOG_DATA_IDLE;
- }
- }
- }
-
-}
-
-SR_PRIV int kecheng_kc_330b_configure(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int len, ret;
- unsigned char buf[7];
-
- sr_dbg("Configuring device.");
-
- usb = sdi->conn;
- devc = sdi->priv;
-
- buf[0] = CMD_CONFIGURE;
- buf[1] = devc->sample_interval;
- buf[2] = devc->alarm_low;
- buf[3] = devc->alarm_high;
- buf[4] = devc->mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_F ? 0 : 1;
- buf[5] = devc->mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A ? 0 : 1;
- buf[6] = devc->data_source;
- ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 7, &len, 5);
- if (ret != 0 || len != 7) {
- sr_dbg("Failed to configure device: %s", libusb_error_name(ret));
- return SR_ERR;
- }
-
- /* The configure command ack takes about 32ms to come in. */
- ret = libusb_bulk_transfer(usb->devhdl, EP_IN, buf, 1, &len, 40);
- if (ret != 0 || len != 1) {
- sr_dbg("Failed to configure device (no ack): %s", libusb_error_name(ret));
- return SR_ERR;
- }
- if (buf[0] != (CMD_CONFIGURE | 0x80)) {
- sr_dbg("Failed to configure device: invalid response 0x%2.x", buf[0]);
- return SR_ERR;
- }
-
- devc->config_dirty = FALSE;
-
- return SR_OK;
-}
-
-SR_PRIV int kecheng_kc_330b_set_date_time(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
- GDateTime *dt;
- int len, ret;
- unsigned char buf[7];
-
- sr_dbg("Setting device date/time.");
-
- usb = sdi->conn;
-
- dt = g_date_time_new_now_local();
- buf[0] = CMD_SET_DATE_TIME;
- buf[1] = g_date_time_get_year(dt) - 2000;
- buf[2] = g_date_time_get_month(dt);
- buf[3] = g_date_time_get_day_of_month(dt);
- buf[4] = g_date_time_get_hour(dt);
- buf[5] = g_date_time_get_minute(dt);
- buf[6] = g_date_time_get_second(dt);
- g_date_time_unref(dt);
- ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 7, &len, 5);
- if (ret != 0 || len != 7) {
- sr_dbg("Failed to set date/time: %s", libusb_error_name(ret));
- return SR_ERR;
- }
-
- ret = libusb_bulk_transfer(usb->devhdl, EP_IN, buf, 1, &len, 10);
- if (ret != 0 || len != 1) {
- sr_dbg("Failed to set date/time (no ack): %s", libusb_error_name(ret));
- return SR_ERR;
- }
- if (buf[0] != (CMD_SET_DATE_TIME | 0x80)) {
- sr_dbg("Failed to set date/time: invalid response 0x%2.x", buf[0]);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int kecheng_kc_330b_status_get(const struct sr_dev_inst *sdi,
- int *status)
-{
- struct sr_usb_dev_inst *usb;
- int len, ret;
- unsigned char buf;
-
- sr_dbg("Getting device status.");
-
- usb = sdi->conn;
- buf = CMD_GET_STATUS;
- ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, &buf, 1, &len, 5);
- if (ret != 0 || len != 1) {
- sr_dbg("Failed to get status: %s", libusb_error_name(ret));
- return SR_ERR;
- }
-
- ret = libusb_bulk_transfer(usb->devhdl, EP_IN, &buf, 1, &len, 10);
- if (ret != 0 || len != 1) {
- sr_dbg("Failed to get status (no ack): %s", libusb_error_name(ret));
- return SR_ERR;
- }
- /* Need either 0x84 or 0xa4. */
- if (buf != (CMD_GET_STATUS | 0x80) && buf != (CMD_GET_STATUS | 0xa0)) {
- sr_dbg("Failed to get status: invalid response 0x%2.x", buf);
- return SR_ERR;
- }
-
- if (buf & 0x20)
- *status = DEVICE_INACTIVE;
- else
- *status = DEVICE_ACTIVE;
-
- return SR_OK;
-}
-
-SR_PRIV int kecheng_kc_330b_log_info_get(const struct sr_dev_inst *sdi,
- unsigned char *buf)
-{
- struct sr_usb_dev_inst *usb;
- int len, ret;
-
- sr_dbg("Getting logging info.");
-
- usb = sdi->conn;
- buf[0] = CMD_GET_LOG_INFO;
- ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 1, &len, 5);
- if (ret != 0 || len != 1) {
- sr_dbg("Failed to get status: %s", libusb_error_name(ret));
- return SR_ERR;
- }
-
- ret = libusb_bulk_transfer(usb->devhdl, EP_IN, buf, 9, &len, 10);
- if (ret != 0 || len != 9) {
- sr_dbg("Failed to get status (no ack): %s", libusb_error_name(ret));
- return SR_ERR;
- }
- if (buf[0] != (CMD_GET_LOG_INFO | 0x80) || buf[1] > 6) {
- sr_dbg("Failed to get log info: invalid response 0x%2.x", buf[0]);
- return SR_ERR;
- }
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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_KECHENG_KC_330B_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_KECHENG_KC_330B_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "kecheng-kc-330b"
-
-#define EP_IN 0x80 | 1
-#define EP_OUT 2
-
-/* 500ms */
-#define DEFAULT_SAMPLE_INTERVAL 0
-#define DEFAULT_ALARM_LOW 40
-#define DEFAULT_ALARM_HIGH 120
-#define DEFAULT_WEIGHT_TIME SR_MQFLAG_SPL_TIME_WEIGHT_F
-#define DEFAULT_WEIGHT_FREQ SR_MQFLAG_SPL_FREQ_WEIGHT_A
-/* Live */
-#define DEFAULT_DATA_SOURCE DATA_SOURCE_LIVE
-
-enum {
- LIVE_SPL_IDLE,
- LIVE_SPL_WAIT,
- LOG_DATA_IDLE,
- LOG_DATA_WAIT,
-};
-
-enum {
- CMD_CONFIGURE = 0x01,
- CMD_IDENTIFY = 0x02,
- CMD_SET_DATE_TIME = 0x03,
- CMD_GET_STATUS = 0x04,
- CMD_GET_LOG_INFO = 0x05,
- CMD_GET_LOG_DATA = 0x07,
- CMD_GET_LIVE_SPL = 0x08,
-};
-
-enum {
- DATA_SOURCE_LIVE,
- DATA_SOURCE_MEMORY,
-};
-
-enum {
- DEVICE_ACTIVE,
- DEVICE_INACTIVE,
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Acquisition settings */
- uint64_t limit_samples;
- int sample_interval;
- int alarm_low;
- int alarm_high;
- uint64_t mqflags;
- int data_source;
-
- /* Operational state */
- int state;
- gboolean config_dirty;
- uint64_t num_samples;
- uint64_t stored_samples;
- void *cb_data;
- struct libusb_transfer *xfer;
- unsigned char buf[128];
-
- /* Temporary state across callbacks */
- gint64 last_live_request;
-
-};
-
-SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data);
-SR_PRIV void kecheng_kc_330b_receive_transfer(struct libusb_transfer *transfer);
-SR_PRIV int kecheng_kc_330b_configure(const struct sr_dev_inst *sdi);
-SR_PRIV int kecheng_kc_330b_set_date_time(struct sr_dev_inst *sdi);
-SR_PRIV int kecheng_kc_330b_recording_get(const struct sr_dev_inst *sdi,
- gboolean *tmp);
-SR_PRIV int kecheng_kc_330b_status_get(const struct sr_dev_inst *sdi,
- int *status);
-SR_PRIV int kecheng_kc_330b_log_info_get(const struct sr_dev_inst *sdi,
- unsigned char *buf);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <glib.h>
-#include <libusb.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info;
-static struct sr_dev_driver *di = &lascar_el_usb_driver_info;
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_THERMOMETER,
- SR_CONF_HYGROMETER,
- SR_CONF_DATALOG,
- SR_CONF_LIMIT_SAMPLES,
-};
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct sr_dev_inst *sdi;
- struct sr_usb_dev_inst *usb;
- struct sr_config *src;
- GSList *usb_devices, *devices, *l;
- const char *conn;
-
- drvc = di->priv;
-
- 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;
-
- devices = NULL;
- if ((usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn))) {
- /* We have a list of sr_usb_dev_inst matching the connection
- * string. Wrap them in sr_dev_inst and we're done. */
- for (l = usb_devices; l; l = l->next) {
- usb = l->data;
- if (!(sdi = lascar_scan(usb->bus, usb->address))) {
- /* Not a Lascar EL-USB. */
- g_free(usb);
- continue;
- }
- sdi->inst_type = SR_INST_USB;
- sdi->conn = usb;
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- }
- g_slist_free(usb_devices);
- } else
- g_slist_free_full(usb_devices, g_free);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc;
- struct sr_usb_dev_inst *usb;
- int ret;
-
- if (!(drvc = di->priv)) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
-
- if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
- return SR_ERR;
-
- if ((ret = libusb_claim_interface(usb->devhdl, LASCAR_INTERFACE))) {
- sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- sdi->status = SR_ST_ACTIVE;
-
- return ret;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
-
- if (!usb->devhdl)
- /* Nothing to do. */
- return SR_OK;
-
- libusb_release_interface(usb->devhdl, LASCAR_INTERFACE);
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- int ret;
- struct drv_context *drvc;
-
- if (!(drvc = di->priv))
- /* Can get called on an unused driver, doesn't matter. */
- return SR_OK;
-
-
- ret = std_dev_clear(di, NULL);
- g_free(drvc);
- di->priv = NULL;
-
- return ret;
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int ret;
- char str[128];
-
- (void)cg;
-
- devc = sdi->priv;
- switch (id) {
- case SR_CONF_CONN:
- if (!sdi || !sdi->conn)
- return SR_ERR_ARG;
- usb = sdi->conn;
- snprintf(str, 128, "%d.%d", usb->bus, usb->address);
- *data = g_variant_new_string(str);
- break;
- case SR_CONF_DATALOG:
- if (!sdi)
- return SR_ERR_ARG;
- if ((ret = lascar_is_logging(sdi)) == -1)
- return SR_ERR;
- *data = g_variant_new_boolean(ret ? TRUE : FALSE);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- int ret;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- devc = sdi->priv;
- ret = SR_OK;
- switch (id) {
- case SR_CONF_DATALOG:
- if (g_variant_get_boolean(data)) {
- /* Start logging. */
- ret = lascar_start_logging(sdi);
- } else {
- /* Stop logging. */
- ret = lascar_stop_logging(sdi);
- }
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static void mark_xfer(struct libusb_transfer *xfer)
-{
-
- if (xfer->status == LIBUSB_TRANSFER_COMPLETED)
- xfer->user_data = GINT_TO_POINTER(1);
- else
- xfer->user_data = GINT_TO_POINTER(-1);
-
-}
-
-/* The Lascar software, in its infinite ignorance, reads a set of four
- * bytes from the device config struct and interprets it as a float.
- * That only works because they only use windows, and only on x86. However
- * we may be running on any architecture, any operating system. So we have
- * to convert these four bytes as the Lascar software would on windows/x86,
- * to the local representation of a float.
- * The source format is little-endian, with IEEE 754-2008 BINARY32 encoding. */
-static float binary32_le_to_float(unsigned char *buf)
-{
- GFloatIEEE754 f;
-
- f.v_float = 0;
- f.mpn.sign = (buf[3] & 0x80) ? 1 : 0;
- f.mpn.biased_exponent = (buf[3] << 1) | (buf[2] >> 7);
- f.mpn.mantissa = buf[0] | (buf[1] << 8) | ((buf[2] & 0x7f) << 16);
-
- return f.v_float;
-}
-
-static int lascar_proc_config(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int dummy, ret;
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- if (lascar_get_config(usb->devhdl, devc->config, &dummy) != SR_OK)
- return SR_ERR;
-
- ret = SR_OK;
- switch (devc->profile->logformat) {
- case LOG_TEMP_RH:
- devc->sample_size = 2;
- devc->temp_unit = devc->config[0x2e] | (devc->config[0x2f] << 8);
- if (devc->temp_unit != 0 && devc->temp_unit != 1) {
- sr_dbg("invalid temperature unit %d", devc->temp_unit);
- /* Default to Celcius, we're all adults here. */
- devc->temp_unit = 0;
- } else
- sr_dbg("temperature unit is %s", devc->temp_unit
- ? "Fahrenheit" : "Celcius");
- break;
- case LOG_CO:
- devc->sample_size = 2;
- devc->co_high = binary32_le_to_float(devc->config + 0x24);
- devc->co_low = binary32_le_to_float(devc->config + 0x28);
- sr_dbg("EL-USB-CO calibration high %f low %f", devc->co_high,
- devc->co_low);
- break;
- default:
- ret = SR_ERR_ARG;
- }
- devc->logged_samples = devc->config[0x1e] | (devc->config[0x1f] << 8);
- sr_dbg("device log contains %d samples.", devc->logged_samples);
-
- return ret;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_meta meta;
- struct sr_config *src;
- struct dev_context *devc;
- struct drv_context *drvc;
- struct sr_usb_dev_inst *usb;
- struct libusb_transfer *xfer_in, *xfer_out;
- struct timeval tv;
- uint64_t interval;
- int ret;
- unsigned char cmd[3], resp[4], *buf;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- drvc = di->priv;
- devc = sdi->priv;
- usb = sdi->conn;
- devc->cb_data = cb_data;
-
- if (lascar_proc_config(sdi) != SR_OK)
- return SR_ERR;
-
- sr_dbg("Starting log retrieval.");
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- interval = (devc->config[0x1c] | (devc->config[0x1d] << 8)) * 1000;
- packet.type = SR_DF_META;
- packet.payload = &meta;
- src = sr_config_new(SR_CONF_SAMPLE_INTERVAL, g_variant_new_uint64(interval));
- meta.config = g_slist_append(NULL, src);
- sr_session_send(devc->cb_data, &packet);
- g_free(src);
-
- if (devc->logged_samples == 0) {
- /* This ensures the frontend knows the session is done. */
- packet.type = SR_DF_END;
- sr_session_send(devc->cb_data, &packet);
- return SR_OK;
- }
-
- if (!(xfer_in = libusb_alloc_transfer(0)) ||
- !(xfer_out = libusb_alloc_transfer(0)))
- return SR_ERR;
-
- libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR,
- 0x00, 0xffff, 0x00, NULL, 0, 50);
- libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR,
- 0x02, 0x0002, 0x00, NULL, 0, 50);
- libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR,
- 0x02, 0x0001, 0x00, NULL, 0, 50);
-
-
- /* Flush input. The F321 requires this. */
- while (libusb_bulk_transfer(usb->devhdl, LASCAR_EP_IN, resp,
- 256, &ret, 5) == 0 && ret > 0)
- ;
-
- libusb_fill_bulk_transfer(xfer_in, usb->devhdl, LASCAR_EP_IN,
- resp, sizeof(resp), mark_xfer, 0, 10000);
- if (libusb_submit_transfer(xfer_in) != 0) {
- libusb_free_transfer(xfer_in);
- libusb_free_transfer(xfer_out);
- return SR_ERR;
- }
-
- cmd[0] = 0x03;
- cmd[1] = 0xff;
- cmd[2] = 0xff;
- libusb_fill_bulk_transfer(xfer_out, usb->devhdl, LASCAR_EP_OUT,
- cmd, 3, mark_xfer, 0, 100);
- if (libusb_submit_transfer(xfer_out) != 0) {
- libusb_free_transfer(xfer_in);
- libusb_free_transfer(xfer_out);
- return SR_ERR;
- }
-
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- while (!xfer_in->user_data || !xfer_out->user_data) {
- g_usleep(5000);
- libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
- }
- if (xfer_in->user_data != GINT_TO_POINTER(1) ||
- xfer_in->user_data != GINT_TO_POINTER(1)) {
- sr_dbg("no response to log transfer request");
- libusb_free_transfer(xfer_in);
- libusb_free_transfer(xfer_out);
- return SR_ERR;
- }
- if (xfer_in->actual_length != 3 || xfer_in->buffer[0] != 2) {
- sr_dbg("invalid response to log transfer request");
- libusb_free_transfer(xfer_in);
- libusb_free_transfer(xfer_out);
- return SR_ERR;
- }
- devc->log_size = xfer_in->buffer[1] + (xfer_in->buffer[2] << 8);
- libusb_free_transfer(xfer_out);
-
- usb_source_add(sdi->session, drvc->sr_ctx, 100,
- lascar_el_usb_handle_events, (void *)sdi);
-
- buf = g_try_malloc(4096);
- libusb_fill_bulk_transfer(xfer_in, usb->devhdl, LASCAR_EP_IN,
- buf, 4096, lascar_el_usb_receive_transfer, cb_data, 100);
- if ((ret = libusb_submit_transfer(xfer_in) != 0)) {
- sr_err("Unable to submit transfer: %s.", libusb_error_name(ret));
- libusb_free_transfer(xfer_in);
- g_free(buf);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- (void)cb_data;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- if (sdi->status != SR_ST_ACTIVE) {
- sr_err("Device inactive, can't stop acquisition.");
- return SR_ERR;
- }
-
- sdi->status = SR_ST_STOPPING;
- /* TODO: free ongoing transfers? */
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info = {
- .name = "lascar-el-usb",
- .longname = "Lascar EL-USB",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdlib.h>
-#include <sys/time.h>
-#include <string.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-extern struct sr_dev_driver lascar_el_usb_driver_info;
-static struct sr_dev_driver *di = &lascar_el_usb_driver_info;
-
-static const struct elusb_profile profiles[] = {
- { 1, "EL-USB-1", LOG_UNSUPPORTED },
- { 2, "EL-USB-1", LOG_UNSUPPORTED },
- { 3, "EL-USB-2", LOG_TEMP_RH },
- { 4, "EL-USB-3", LOG_UNSUPPORTED },
- { 5, "EL-USB-4", LOG_UNSUPPORTED },
- { 6, "EL-USB-3", LOG_UNSUPPORTED },
- { 7, "EL-USB-4", LOG_UNSUPPORTED },
- { 8, "EL-USB-LITE", LOG_UNSUPPORTED },
- { 9, "EL-USB-CO", LOG_CO },
- { 10, "EL-USB-TC", LOG_UNSUPPORTED },
- { 11, "EL-USB-CO300", LOG_CO },
- { 12, "EL-USB-2-LCD", LOG_TEMP_RH },
- { 13, "EL-USB-2+", LOG_TEMP_RH },
- { 14, "EL-USB-1-PRO", LOG_UNSUPPORTED },
- { 15, "EL-USB-TC-LCD", LOG_UNSUPPORTED },
- { 16, "EL-USB-2-LCD+", LOG_TEMP_RH },
- { 17, "EL-USB-5", LOG_UNSUPPORTED },
- { 18, "EL-USB-1-RCG", LOG_UNSUPPORTED },
- { 19, "EL-USB-1-LCD", LOG_UNSUPPORTED },
- { 20, "EL-OEM-3", LOG_UNSUPPORTED },
- { 21, "EL-USB-1-LCD", LOG_UNSUPPORTED },
- { 0, NULL, 0 }
-};
-
-
-static libusb_device_handle *lascar_open(struct libusb_device *dev)
-{
- libusb_device_handle *dev_hdl;
- int ret;
-
- if ((ret = libusb_open(dev, &dev_hdl)) != 0) {
- sr_dbg("failed to open device for scan: %s",
- libusb_error_name(ret));
- return NULL;
- }
-
- /* Some of these fail, but it needs doing -- some sort of mode
- * setup for the SILabs F32x. */
- libusb_control_transfer(dev_hdl, LIBUSB_REQUEST_TYPE_VENDOR,
- 0x00, 0xffff, 0x00, NULL, 0, 50);
- libusb_control_transfer(dev_hdl, LIBUSB_REQUEST_TYPE_VENDOR,
- 0x02, 0x0002, 0x00, NULL, 0, 50);
- libusb_control_transfer(dev_hdl, LIBUSB_REQUEST_TYPE_VENDOR,
- 0x02, 0x0001, 0x00, NULL, 0, 50);
-
- return dev_hdl;
-}
-
-static void mark_xfer(struct libusb_transfer *xfer)
-{
-
- xfer->user_data = GINT_TO_POINTER(1);
-
-}
-
-SR_PRIV int lascar_get_config(libusb_device_handle *dev_hdl,
- unsigned char *configblock, int *configlen)
-{
- struct drv_context *drvc;
- struct libusb_transfer *xfer_in, *xfer_out;
- struct timeval tv;
- int64_t start;
- int buflen;
- unsigned char cmd[3], buf[MAX_CONFIGBLOCK_SIZE];
-
- sr_spew("Reading config block.");
-
- drvc = di->priv;
- *configlen = 0;
-
- if (!(xfer_in = libusb_alloc_transfer(0)) ||
- !(xfer_out = libusb_alloc_transfer(0)))
- return SR_ERR;
-
- /* Flush anything the F321 still has queued. */
- while (libusb_bulk_transfer(dev_hdl, LASCAR_EP_IN, buf, 256, &buflen,
- 5) == 0 && buflen > 0)
- ;
-
- /* Keep a read request waiting in the wings, ready to pounce
- * the moment the device sends something. */
- libusb_fill_bulk_transfer(xfer_in, dev_hdl, LASCAR_EP_IN,
- buf, 256, mark_xfer, 0, 10000);
- if (libusb_submit_transfer(xfer_in) != 0)
- goto cleanup;
-
- /* Request device configuration structure. */
- cmd[0] = 0x00;
- cmd[1] = 0xff;
- cmd[2] = 0xff;
- libusb_fill_bulk_transfer(xfer_out, dev_hdl, LASCAR_EP_OUT,
- cmd, 3, mark_xfer, 0, 100);
- if (libusb_submit_transfer(xfer_out) != 0)
- goto cleanup;
-
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- start = g_get_monotonic_time();
- while (!xfer_in->user_data || !xfer_out->user_data) {
- if (g_get_monotonic_time() - start > SCAN_TIMEOUT) {
- start = 0;
- break;
- }
- g_usleep(5000);
- libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
- }
- if (!start) {
- sr_dbg("no response");
- goto cleanup;
- }
- if (xfer_in->actual_length != 3) {
- sr_dbg("expected 3-byte header, got %d bytes", xfer_in->actual_length);
- goto cleanup;
- }
-
- /* Got configuration structure header. */
- sr_spew("Response to config request: 0x%.2x 0x%.2x 0x%.2x ",
- buf[0], buf[1], buf[2]);
- buflen = buf[1] | (buf[2] << 8);
- if (buf[0] != 0x02 || buflen > MAX_CONFIGBLOCK_SIZE) {
- sr_dbg("Invalid response to config request: "
- "0x%.2x 0x%.2x 0x%.2x ", buf[0], buf[1], buf[2]);
- libusb_close(dev_hdl);
- goto cleanup;
- }
-
- /* Get configuration structure. */
- xfer_in->length = buflen;
- xfer_in->user_data = 0;
- if (libusb_submit_transfer(xfer_in) != 0)
- goto cleanup;
- while (!xfer_in->user_data) {
- if (g_get_monotonic_time() - start > SCAN_TIMEOUT) {
- start = 0;
- break;
- }
- g_usleep(5000);
- libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
- }
- if (!start) {
- sr_dbg("Timeout waiting for configuration structure.");
- goto cleanup;
- }
- if (xfer_in->actual_length != buflen) {
- sr_dbg("expected %d-byte structure, got %d bytes", buflen,
- xfer_in->actual_length);
- goto cleanup;
- }
-
- memcpy(configblock, buf, buflen);
- *configlen = buflen;
-
-cleanup:
- if (!xfer_in->user_data || !xfer_in->user_data) {
- if (!xfer_in->user_data)
- libusb_cancel_transfer(xfer_in);
- if (!xfer_out->user_data)
- libusb_cancel_transfer(xfer_out);
- start = g_get_monotonic_time();
- while (!xfer_in->user_data || !xfer_out->user_data) {
- if (g_get_monotonic_time() - start > 10000)
- break;
- g_usleep(1000);
- libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
- }
- }
- libusb_free_transfer(xfer_in);
- libusb_free_transfer(xfer_out);
-
- return *configlen ? SR_OK : SR_ERR;
-}
-
-static int lascar_save_config(libusb_device_handle *dev_hdl,
- unsigned char *config, int configlen)
-{
- struct drv_context *drvc;
- struct libusb_transfer *xfer_in, *xfer_out;
- struct timeval tv;
- int64_t start;
- int buflen, ret;
- unsigned char cmd[3], buf[256];
-
- sr_spew("Writing config block.");
-
- drvc = di->priv;
-
- if (!(xfer_in = libusb_alloc_transfer(0)) ||
- !(xfer_out = libusb_alloc_transfer(0)))
- return SR_ERR;
-
- /* Flush anything the F321 still has queued. */
- while (libusb_bulk_transfer(dev_hdl, LASCAR_EP_IN, buf, 256, &buflen,
- 5) == 0 && buflen > 0)
- ;
- ret = SR_OK;
-
- /* Keep a read request waiting in the wings, ready to pounce
- * the moment the device sends something. */
- libusb_fill_bulk_transfer(xfer_in, dev_hdl, LASCAR_EP_IN,
- buf, 256, mark_xfer, 0, 10000);
- if (libusb_submit_transfer(xfer_in) != 0) {
- ret = SR_ERR;
- goto cleanup;
- }
-
- /* Request device configuration structure. */
- cmd[0] = 0x01;
- cmd[1] = configlen & 0xff;
- cmd[2] = (configlen >> 8) & 0xff;
- libusb_fill_bulk_transfer(xfer_out, dev_hdl, LASCAR_EP_OUT,
- cmd, 3, mark_xfer, 0, 100);
- if (libusb_submit_transfer(xfer_out) != 0) {
- ret = SR_ERR;
- goto cleanup;
- }
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- while (!xfer_out->user_data) {
- g_usleep(5000);
- libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
- }
-
- libusb_fill_bulk_transfer(xfer_out, dev_hdl, LASCAR_EP_OUT,
- config, configlen, mark_xfer, 0, 100);
- if (libusb_submit_transfer(xfer_out) != 0) {
- ret = SR_ERR;
- goto cleanup;
- }
- while (!xfer_in->user_data || !xfer_out->user_data) {
- g_usleep(5000);
- libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
- }
-
- if (xfer_in->actual_length != 1 || buf[0] != 0xff) {
- sr_dbg("unexpected response after transfer");
- ret = SR_ERR;
- }
-
-cleanup:
- if (!xfer_in->user_data || !xfer_in->user_data) {
- if (!xfer_in->user_data)
- libusb_cancel_transfer(xfer_in);
- if (!xfer_out->user_data)
- libusb_cancel_transfer(xfer_out);
- start = g_get_monotonic_time();
- while (!xfer_in->user_data || !xfer_out->user_data) {
- if (g_get_monotonic_time() - start > 10000)
- break;
- g_usleep(1000);
- libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
- }
- }
- libusb_free_transfer(xfer_in);
- libusb_free_transfer(xfer_out);
-
- return ret;
-}
-
-static struct sr_dev_inst *lascar_identify(unsigned char *config)
-{
- struct dev_context *devc;
- const struct elusb_profile *profile;
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- int modelid, i;
- char firmware[5];
-
- modelid = config[0];
- sdi = NULL;
- if (modelid) {
- profile = NULL;
- for (i = 0; profiles[i].modelid; i++) {
- if (profiles[i].modelid == modelid) {
- profile = &profiles[i];
- break;
- }
- }
- if (!profile) {
- sr_dbg("unknown EL-USB modelid %d", modelid);
- return NULL;
- }
-
- i = config[52] | (config[53] << 8);
- memcpy(firmware, config + 0x30, 4);
- firmware[4] = '\0';
- sr_dbg("found %s with firmware version %s serial %d",
- profile->modelname, firmware, i);
-
- if (profile->logformat == LOG_UNSUPPORTED) {
- sr_dbg("unsupported EL-USB logformat for %s", profile->modelname);
- return NULL;
- }
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, LASCAR_VENDOR,
- profile->modelname, firmware)))
- return NULL;
- sdi->driver = di;
-
- if (profile->logformat == LOG_TEMP_RH) {
- /* Model this as two channels: temperature and humidity. */
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "Temp")))
- return NULL;
- sdi->channels = g_slist_append(NULL, ch);
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "Hum")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- } else if (profile->logformat == LOG_CO) {
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "CO")))
- return NULL;
- sdi->channels = g_slist_append(NULL, ch);
- } else {
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
- return NULL;
- sdi->channels = g_slist_append(NULL, ch);
- }
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
- return NULL;
- sdi->priv = devc;
- devc->profile = profile;
- }
-
- return sdi;
-}
-
-SR_PRIV struct sr_dev_inst *lascar_scan(int bus, int address)
-{
- struct drv_context *drvc;
- struct sr_dev_inst *sdi;
- struct libusb_device **devlist;
- struct libusb_device_descriptor des;
- libusb_device_handle *dev_hdl;
- int dummy, ret, i;
- unsigned char config[MAX_CONFIGBLOCK_SIZE];
-
- drvc = di->priv;
- sdi = NULL;
-
- libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- for (i = 0; devlist[i]; i++) {
- if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
- sr_err("Failed to get device descriptor: %d.", ret);
- continue;
- }
-
- if (libusb_get_bus_number(devlist[i]) != bus ||
- libusb_get_device_address(devlist[i]) != address)
- continue;
-
- if (!(dev_hdl = lascar_open(devlist[i])))
- continue;
-
- if (lascar_get_config(dev_hdl, config, &dummy) != SR_OK)
- continue;
-
- libusb_close(dev_hdl);
- sdi = lascar_identify(config);
- }
-
- return sdi;
-}
-
-static void lascar_el_usb_dispatch(struct sr_dev_inst *sdi, unsigned char *buf,
- int buflen)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct sr_channel *ch;
- float *temp, *rh;
- uint16_t s;
- int samples, samples_left, i, j;
-
- devc = sdi->priv;
-
- samples = buflen / devc->sample_size;
- samples_left = devc->logged_samples - devc->rcvd_samples;
- if (samples_left < samples)
- samples = samples_left;
- switch (devc->profile->logformat) {
- case LOG_TEMP_RH:
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- analog.mqflags = 0;
- if (!(temp = g_try_malloc(sizeof(float) * samples)))
- break;
- if (!(rh = g_try_malloc(sizeof(float) * samples)))
- break;
- for (i = 0, j = 0; i < samples; i++) {
- /* Both Celcius and Fahrenheit stored at base -40. */
- if (devc->temp_unit == 0)
- /* Celcius is stored in half-degree increments. */
- temp[j] = buf[i * 2] / 2 - 40;
- else
- temp[j] = buf[i * 2] - 40;
-
- rh[j] = buf[i * 2 + 1] / 2;
-
- if (temp[j] == 0.0 && rh[j] == 0.0)
- /* Skip invalid measurement. */
- continue;
- j++;
- }
- analog.num_samples = j;
-
- ch = sdi->channels->data;
- if (ch->enabled) {
- analog.channels = g_slist_append(NULL, ch);
- analog.mq = SR_MQ_TEMPERATURE;
- if (devc->temp_unit == 1)
- analog.unit = SR_UNIT_FAHRENHEIT;
- else
- analog.unit = SR_UNIT_CELSIUS;
- analog.data = temp;
- sr_session_send(devc->cb_data, &packet);
- }
-
- ch = sdi->channels->next->data;
- if (ch->enabled) {
- analog.channels = g_slist_append(NULL, ch);
- analog.mq = SR_MQ_RELATIVE_HUMIDITY;
- analog.unit = SR_UNIT_PERCENTAGE;
- analog.data = rh;
- sr_session_send(devc->cb_data, &packet);
- }
-
- g_free(temp);
- g_free(rh);
- break;
- case LOG_CO:
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- analog.channels = sdi->channels;
- analog.num_samples = samples;
- analog.mq = SR_MQ_CARBON_MONOXIDE;
- analog.unit = SR_UNIT_CONCENTRATION;
- analog.mqflags = 0;
- if (!(analog.data = g_try_malloc(sizeof(float) * samples)))
- break;
- for (i = 0; i < samples; i++) {
- s = (buf[i * 2] << 8) | buf[i * 2 + 1];
- analog.data[i] = (s * devc->co_high + devc->co_low) / 1000000;
- if (analog.data[i] < 0.0)
- analog.data[i] = 0.0;
- }
- sr_session_send(devc->cb_data, &packet);
- g_free(analog.data);
- break;
- default:
- /* How did we even get this far? */
- break;
- }
- devc->rcvd_samples += samples;
-
-}
-
-SR_PRIV int lascar_el_usb_handle_events(int fd, int revents, void *cb_data)
-{
- struct drv_context *drvc = di->priv;
- struct sr_datafeed_packet packet;
- struct sr_dev_inst *sdi;
- struct timeval tv;
-
- (void)fd;
- (void)revents;
-
- sdi = cb_data;
-
- if (sdi->status == SR_ST_STOPPING) {
- usb_source_remove(sdi->session, drvc->sr_ctx);
-
- packet.type = SR_DF_END;
- sr_session_send(cb_data, &packet);
- }
-
- memset(&tv, 0, sizeof(struct timeval));
- libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
- NULL);
-
- return TRUE;
-}
-
-SR_PRIV void lascar_el_usb_receive_transfer(struct libusb_transfer *transfer)
-{
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- int ret;
- gboolean packet_has_error;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
-
- packet_has_error = FALSE;
- switch (transfer->status) {
- case LIBUSB_TRANSFER_NO_DEVICE:
- /* USB device was unplugged. */
- dev_acquisition_stop(sdi, sdi);
- return;
- case LIBUSB_TRANSFER_COMPLETED:
- case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though */
- break;
- default:
- packet_has_error = TRUE;
- break;
- }
-
- if (!packet_has_error) {
- if (devc->rcvd_samples < devc->logged_samples)
- lascar_el_usb_dispatch(sdi, transfer->buffer,
- transfer->actual_length);
- devc->rcvd_bytes += transfer->actual_length;
- sr_spew("received %d/%d bytes (%d/%d samples)",
- devc->rcvd_bytes, devc->log_size,
- devc->rcvd_samples, devc->logged_samples);
- if (devc->rcvd_bytes >= devc->log_size)
- dev_acquisition_stop(sdi, sdi);
- }
-
- if (sdi->status == SR_ST_ACTIVE) {
- /* Send the same request again. */
- if ((ret = libusb_submit_transfer(transfer) != 0)) {
- sr_err("Unable to resubmit transfer: %s.",
- libusb_error_name(ret));
- g_free(transfer->buffer);
- libusb_free_transfer(transfer);
- dev_acquisition_stop(sdi, sdi);
- }
- } else {
- /* This was the last transfer we're going to receive, so
- * clean up now. */
- g_free(transfer->buffer);
- libusb_free_transfer(transfer);
- }
-
-}
-
-static int get_flags(unsigned char *configblock)
-{
- int flags;
-
- flags = (configblock[32] | (configblock[33] << 8)) & 0x1fff;
- sr_spew("Read flags (0x%.4x).", flags);
-
- return flags;
-}
-
-static int set_flags(unsigned char *configblock, int flags)
-{
-
- sr_spew("Setting flags to 0x%.4x.", flags);
- configblock[32] = flags & 0xff;
- configblock[33] = (flags >> 8) & 0x1f;
-
- return flags;
-}
-
-SR_PRIV int lascar_is_logging(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int dummy, flags, ret;
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- if (lascar_get_config(usb->devhdl, devc->config, &dummy) != SR_OK)
- return -1;
-
- flags = get_flags(devc->config);
- if (flags & 0x0100)
- ret = 1;
- else
- ret = 0;
-
- return ret;
-}
-
-SR_PRIV int lascar_start_logging(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int len, flags, ret;
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- if (lascar_get_config(usb->devhdl, devc->config, &len) != SR_OK)
- return SR_ERR;
-
- /* Turn on logging. */
- flags = get_flags(devc->config);
- flags |= 0x0100;
- set_flags(devc->config, flags);
-
- /* Start logging in 0 seconds. */
- memset(devc->config + 24, 0, 4);
-
- ret = lascar_save_config(usb->devhdl, devc->config, len);
- sr_info("Started internal logging.");
-
- return ret;
-}
-
-SR_PRIV int lascar_stop_logging(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int len, flags, ret;
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- if (lascar_get_config(usb->devhdl, devc->config, &len) != SR_OK)
- return SR_ERR;
-
- flags = get_flags(devc->config);
- flags &= ~0x0100;
- set_flags(devc->config, flags);
-
- ret = lascar_save_config(usb->devhdl, devc->config, len);
- sr_info("Stopped internal logging.");
-
- return ret;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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_LASCAR_EL_USB_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_LASCAR_EL_USB_PROTOCOL_H
-
-#include <stdint.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "lascar-el-usb"
-
-#define LASCAR_VENDOR "Lascar"
-#define LASCAR_INTERFACE 0
-#define LASCAR_EP_IN 0x82
-#define LASCAR_EP_OUT 2
-/* Max 100ms for a device to positively identify. */
-#define SCAN_TIMEOUT 100000
-#define MAX_CONFIGBLOCK_SIZE 256
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- void *cb_data;
- const struct elusb_profile *profile;
- /* Generic EL-USB */
- unsigned char config[MAX_CONFIGBLOCK_SIZE];
- unsigned int log_size;
- unsigned int rcvd_bytes;
- unsigned int sample_size;
- unsigned int logged_samples;
- unsigned int rcvd_samples;
- uint64_t limit_samples;
- /* Model-specific */
- /* EL-USB-CO: these are something like scaling and calibration values
- * fixed per device, used to convert the sample values to CO ppm. */
- float co_high;
- float co_low;
- /* Temperature units as stored in the device config. */
- int temp_unit;
-};
-
-enum {
- LOG_UNSUPPORTED,
- LOG_TEMP_RH,
- LOG_CO,
-};
-
-struct elusb_profile {
- int modelid;
- char *modelname;
- int logformat;
-};
-
-SR_PRIV int lascar_get_config(libusb_device_handle *dev_hdl,
- unsigned char *configblock, int *configlen);
-SR_PRIV struct sr_dev_inst *lascar_scan(int bus, int address);
-SR_PRIV int lascar_el_usb_handle_events(int fd, int revents, void *cb_data);
-SR_PRIV void lascar_el_usb_receive_transfer(struct libusb_transfer *transfer);
-SR_PRIV int lascar_start_logging(const struct sr_dev_inst *sdi);
-SR_PRIV int lascar_stop_logging(const struct sr_dev_inst *sdi);
-SR_PRIV int lascar_is_logging(const struct sr_dev_inst *sdi);
-SR_PRIV int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011 Daniel Ribeiro <drwyrm@gmail.com>
- * Copyright (C) 2012 Renato Caldas <rmsc@fe.up.pt>
- * Copyright (C) 2013 Lior Elazary <lelazary@yahoo.com>
- *
- * 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 "protocol.h"
-
-static const int32_t hwcaps[] = {
- SR_CONF_OSCILLOSCOPE,
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_SAMPLERATE,
- SR_CONF_TRIGGER_TYPE,
- SR_CONF_TRIGGER_SLOPE,
- SR_CONF_HORIZ_TRIGGERPOS,
-// SR_CONF_CAPTURE_RATIO,
- SR_CONF_LIMIT_SAMPLES,
-// SR_CONF_RLE,
-};
-
-/*
- * Channels are numbered 0 to 7.
- *
- * See also: http://www.linkinstruments.com/images/mso19_1113.gif
- */
-SR_PRIV const char *mso19_channel_names[NUM_CHANNELS + 1] = {
- /* Note: DSO needs to be first. */
- "DSO", "0", "1", "2", "3", "4", "5", "6", "7", NULL,
-};
-
-static const uint64_t samplerates[] = {
- SR_HZ(100),
- SR_MHZ(200),
- SR_HZ(100),
-};
-
-SR_PRIV struct sr_dev_driver link_mso19_driver_info;
-static struct sr_dev_driver *di = &link_mso19_driver_info;
-
-/* TODO: Use sr_dev_inst to store connection handle & use std_dev_clear(). */
-static int dev_clear(void)
-{
- GSList *l;
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- int ret = SR_OK;
-
- if (!(drvc = di->priv))
- return SR_OK;
-
- /* Properly close and free all devices. */
- for (l = drvc->instances; l; l = l->next) {
- if (!(sdi = l->data)) {
- /* Log error, but continue cleaning up the rest. */
- sr_err("%s: sdi was NULL, continuing", __func__);
- ret = SR_ERR_BUG;
- continue;
- }
- if (!(devc = sdi->priv)) {
- /* Log error, but continue cleaning up the rest. */
- sr_err("%s: sdi->priv was NULL, continuing", __func__);
- ret = SR_ERR_BUG;
- continue;
- }
- std_serial_dev_close(sdi);
- sr_serial_dev_inst_free(devc->serial);
- sr_dev_inst_free(sdi);
- }
- g_slist_free(drvc->instances);
- drvc->instances = NULL;
-
- return ret;
-}
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- int i;
- GSList *devices = NULL;
- const char *conn = NULL;
- const char *serialcomm = NULL;
- GSList *l;
- struct sr_config *src;
- struct udev *udev;
- int chtype;
-
- 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)
- conn = SERIALCONN;
- if (serialcomm == NULL)
- serialcomm = SERIALCOMM;
-
- udev = udev_new();
- if (!udev) {
- sr_err("Failed to initialize udev.");
- }
-
- struct udev_enumerate *enumerate = udev_enumerate_new(udev);
- udev_enumerate_add_match_subsystem(enumerate, "usb-serial");
- udev_enumerate_scan_devices(enumerate);
- struct udev_list_entry *devs = udev_enumerate_get_list_entry(enumerate);
- struct udev_list_entry *dev_list_entry;
- for (dev_list_entry = devs;
- dev_list_entry != NULL;
- dev_list_entry = udev_list_entry_get_next(dev_list_entry)) {
- const char *syspath = udev_list_entry_get_name(dev_list_entry);
- struct udev_device *dev =
- udev_device_new_from_syspath(udev, syspath);
- const char *sysname = udev_device_get_sysname(dev);
- struct udev_device *parent =
- udev_device_get_parent_with_subsystem_devtype(dev, "usb",
- "usb_device");
-
- if (!parent) {
- sr_err("Unable to find parent usb device for %s",
- sysname);
- continue;
- }
-
- const char *idVendor =
- udev_device_get_sysattr_value(parent, "idVendor");
- const char *idProduct =
- udev_device_get_sysattr_value(parent, "idProduct");
- if (strcmp(USB_VENDOR, idVendor)
- || strcmp(USB_PRODUCT, idProduct))
- continue;
-
- const char *iSerial =
- udev_device_get_sysattr_value(parent, "serial");
- const char *iProduct =
- udev_device_get_sysattr_value(parent, "product");
-
- char path[32];
- snprintf(path, sizeof(path), "/dev/%s", sysname);
- conn = path;
-
- size_t s = strcspn(iProduct, " ");
- char product[32];
- char manufacturer[32];
- if (s > sizeof(product) ||
- strlen(iProduct) - s > sizeof(manufacturer)) {
- sr_err("Could not parse iProduct: %s.", iProduct);
- continue;
- }
- strncpy(product, iProduct, s);
- product[s] = 0;
- strcpy(manufacturer, iProduct + s + 1);
-
- //Create the device context and set its params
- struct dev_context *devc;
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return devices;
- }
-
- if (mso_parse_serial(iSerial, iProduct, devc) != SR_OK) {
- sr_err("Invalid iSerial: %s.", iSerial);
- g_free(devc);
- return devices;
- }
-
- char hwrev[32];
- sprintf(hwrev, "r%d", devc->hwrev);
- devc->ctlbase1 = 0;
- devc->protocol_trigger.spimode = 0;
- for (i = 0; i < 4; i++) {
- devc->protocol_trigger.word[i] = 0;
- devc->protocol_trigger.mask[i] = 0xff;
- }
-
- if (!(devc->serial = sr_serial_dev_inst_new(conn, serialcomm))) {
- g_free(devc);
- return devices;
- }
-
- struct sr_dev_inst *sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
- manufacturer, product, hwrev);
-
- if (!sdi) {
- sr_err("Unable to create device instance for %s",
- sysname);
- sr_dev_inst_free(sdi);
- g_free(devc);
- return devices;
- }
-
- sdi->driver = di;
- sdi->priv = devc;
-
- for (i = 0; i < NUM_CHANNELS; i++) {
- struct sr_channel *ch;
- chtype = (i == 0) ? SR_CHANNEL_ANALOG : SR_CHANNEL_LOGIC;
- if (!(ch = sr_channel_new(i, chtype, TRUE,
- mso19_channel_names[i])))
- return 0;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- //Add the driver
- struct drv_context *drvc = di->priv;
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- }
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- int ret;
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- if (serial_open(devc->serial, SERIAL_RDWR) != SR_OK)
- return SR_ERR;
-
- sdi->status = SR_ST_ACTIVE;
-
- /* FIXME: discard serial buffer */
- mso_check_trigger(devc->serial, &devc->trigger_state);
- sr_dbg("Trigger state: 0x%x.", devc->trigger_state);
-
- ret = mso_reset_adc(sdi);
- if (ret != SR_OK)
- return ret;
-
- mso_check_trigger(devc->serial, &devc->trigger_state);
- sr_dbg("Trigger state: 0x%x.", devc->trigger_state);
-
- // ret = mso_reset_fsm(sdi);
- // if (ret != SR_OK)
- // return ret;
- // return SR_ERR;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- return dev_clear();
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- if (sdi) {
- devc = sdi->priv;
- *data = g_variant_new_uint64(devc->cur_rate);
- } else
- return SR_ERR;
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- int ret;
- struct dev_context *devc;
- uint64_t num_samples;
- const char *slope;
- int trigger_pos;
- double pos;
-
- (void)cg;
- devc = sdi->priv;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- // FIXME
- return mso_configure_rate(sdi, g_variant_get_uint64(data));
- ret = SR_OK;
- break;
- case SR_CONF_LIMIT_SAMPLES:
- num_samples = g_variant_get_uint64(data);
- if (num_samples != 1024) {
- sr_err("Only 1024 samples are supported.");
- ret = SR_ERR_ARG;
- } else {
- devc->limit_samples = num_samples;
- sr_dbg("setting limit_samples to %i\n",
- num_samples);
- ret = SR_OK;
- }
- break;
- case SR_CONF_CAPTURE_RATIO:
- ret = SR_OK;
- break;
- case SR_CONF_TRIGGER_SLOPE:
- slope = g_variant_get_string(data, NULL);
-
- if (!slope || !(slope[0] == 'f' || slope[0] == 'r'))
- sr_err("Invalid trigger slope");
- ret = SR_ERR_ARG;
- } else {
- devc->trigger_slope = (slope[0] == 'r')
- ? SLOPE_POSITIVE : SLOPE_NEGATIVE;
- ret = SR_OK;
- }
- break;
- case SR_CONF_HORIZ_TRIGGERPOS:
- pos = g_variant_get_double(data);
- if (pos < 0 || pos > 255) {
- sr_err("Trigger position (%f) should be between 0 and 255.", pos);
- ret = SR_ERR_ARG;
- } else {
- trigger_pos = (int)pos;
- devc->trigger_holdoff[0] = trigger_pos & 0xff;
- ret = SR_OK;
- }
- break;
- case SR_CONF_RLE:
- ret = SR_OK;
- break;
- default:
- ret = SR_ERR_NA;
- break;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- GVariant *gvar;
- GVariantBuilder gvb;
-
- (void)cg;
- (void)sdi;
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
- ARRAY_SIZE(samplerates), sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TRIGGER_TYPE:
- *data = g_variant_new_string(TRIGGER_TYPE);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- int ret = SR_ERR;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- if (mso_configure_channels(sdi) != SR_OK) {
- sr_err("Failed to configure channels.");
- return SR_ERR;
- }
-
- /* FIXME: No need to do full reconfigure every time */
-// ret = mso_reset_fsm(sdi);
-// if (ret != SR_OK)
-// return ret;
-
- /* FIXME: ACDC Mode */
- devc->ctlbase1 &= 0x7f;
-// devc->ctlbase1 |= devc->acdcmode;
-
- ret = mso_configure_rate(sdi, devc->cur_rate);
- if (ret != SR_OK)
- return ret;
-
- /* set dac offset */
- ret = mso_dac_out(sdi, devc->dac_offset);
- if (ret != SR_OK)
- return ret;
-
- ret = mso_configure_threshold_level(sdi);
- if (ret != SR_OK)
- return ret;
-
- ret = mso_configure_trigger(sdi);
- if (ret != SR_OK)
- return ret;
-
- /* END of config hardware part */
- ret = mso_arm(sdi);
- if (ret != SR_OK)
- return ret;
-
- /* Start acquisition on the device. */
- mso_check_trigger(devc->serial, &devc->trigger_state);
- ret = mso_check_trigger(devc->serial, NULL);
- if (ret != SR_OK)
- return ret;
-
- /* Reset trigger state. */
- devc->trigger_state = 0x00;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Our first channel is analog, the other 8 are of type 'logic'. */
- /* TODO. */
-
- serial_source_add(sdi->session, devc->serial, G_IO_IN, -1,
- mso_receive_data, cb_data);
-
- return SR_OK;
-}
-
-/* This stops acquisition on ALL devices, ignoring dev_index. */
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- (void)cb_data;
-
- stop_acquisition(sdi);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver link_mso19_driver_info = {
- .name = "link-mso19",
- .longname = "Link Instruments MSO-19",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = dev_clear,
- .config_get = config_get,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = dev_open,
- .dev_close = std_serial_dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011 Daniel Ribeiro <drwyrm@gmail.com>
- * Copyright (C) 2012 Renato Caldas <rmsc@fe.up.pt>
- * Copyright (C) 2013 Lior Elazary <lelazary@yahoo.com>
- *
- * 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 "protocol.h"
-
-/* serial protocol */
-#define mso_trans(a, v) \
- (((v) & 0x3f) | (((v) & 0xc0) << 6) | (((a) & 0xf) << 8) | \
- ((~(v) & 0x20) << 1) | ((~(v) & 0x80) << 7))
-
-static const char mso_head[] = { 0x40, 0x4c, 0x44, 0x53, 0x7e };
-static const char mso_foot[] = { 0x7e };
-
-extern SR_PRIV struct sr_dev_driver link_mso19_driver_info;
-static struct sr_dev_driver *di = &link_mso19_driver_info;
-
-SR_PRIV int mso_send_control_message(struct sr_serial_dev_inst *serial,
- uint16_t payload[], int n)
-{
- int i, w, ret, s = n * 2 + sizeof(mso_head) + sizeof(mso_foot);
- char *p, *buf;
-
- ret = SR_ERR;
-
- if (serial->fd < 0)
- goto ret;
-
- if (!(buf = g_try_malloc(s))) {
- sr_err("Failed to malloc message buffer.");
- ret = SR_ERR_MALLOC;
- goto ret;
- }
-
- p = buf;
- memcpy(p, mso_head, sizeof(mso_head));
- p += sizeof(mso_head);
-
- for (i = 0; i < n; i++) {
- *(uint16_t *) p = g_htons(payload[i]);
- p += 2;
- }
- memcpy(p, mso_foot, sizeof(mso_foot));
-
- w = 0;
- while (w < s) {
- ret = serial_write(serial, buf + w, s - w);
- if (ret < 0) {
- ret = SR_ERR;
- goto free;
- }
- w += ret;
- }
- ret = SR_OK;
-free:
- g_free(buf);
-ret:
- return ret;
-}
-
-SR_PRIV int mso_configure_trigger(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
- uint16_t threshold_value = mso_calc_raw_from_mv(devc);
-
- threshold_value = 0x153C;
- uint8_t trigger_config = 0;
-
- if (devc->trigger_slope)
- trigger_config |= 0x04; //Trigger on falling edge
-
- switch (devc->trigger_outsrc) {
- case 1:
- trigger_config |= 0x00; //Trigger pulse output
- break;
- case 2:
- trigger_config |= 0x08; //PWM DAC from the pattern generator buffer
- break;
- case 3:
- trigger_config |= 0x18; //White noise
- break;
- }
-
- switch (devc->trigger_chan) {
- case 0:
- trigger_config |= 0x00; //DSO level trigger //b00000000
- break;
- case 1:
- trigger_config |= 0x20; //DSO level trigger & width < trigger_width
- break;
- case 2:
- trigger_config |= 0x40; //DSO level trigger & width >= trigger_width
- break;
- case 3:
- trigger_config |= 0x60; //LA combination trigger
- break;
- }
-
- //Last bit of trigger config reg 4 needs to be 1 for trigger enable,
- //otherwise the trigger is not enabled
- if (devc->use_trigger)
- trigger_config |= 0x80;
-
- uint16_t ops[18];
- ops[0] = mso_trans(3, threshold_value & 0xff);
- //The trigger_config also holds the 2 MSB bits from the threshold value
- ops[1] = mso_trans(4, trigger_config | ((threshold_value >> 8) & 0x03));
- ops[2] = mso_trans(5, devc->la_trigger);
- ops[3] = mso_trans(6, devc->la_trigger_mask);
- ops[4] = mso_trans(7, devc->trigger_holdoff[0]);
- ops[5] = mso_trans(8, devc->trigger_holdoff[1]);
-
- ops[6] = mso_trans(11,
- devc->dso_trigger_width /
- SR_HZ_TO_NS(devc->cur_rate));
-
- /* Select the SPI/I2C trigger config bank */
- ops[7] = mso_trans(REG_CTL2, (devc->ctlbase2 | BITS_CTL2_BANK(2)));
- /* Configure the SPI/I2C protocol trigger */
- ops[8] = mso_trans(REG_PT_WORD(0), devc->protocol_trigger.word[0]);
- ops[9] = mso_trans(REG_PT_WORD(1), devc->protocol_trigger.word[1]);
- ops[10] = mso_trans(REG_PT_WORD(2), devc->protocol_trigger.word[2]);
- ops[11] = mso_trans(REG_PT_WORD(3), devc->protocol_trigger.word[3]);
- ops[12] = mso_trans(REG_PT_MASK(0), devc->protocol_trigger.mask[0]);
- ops[13] = mso_trans(REG_PT_MASK(1), devc->protocol_trigger.mask[1]);
- ops[14] = mso_trans(REG_PT_MASK(2), devc->protocol_trigger.mask[2]);
- ops[15] = mso_trans(REG_PT_MASK(3), devc->protocol_trigger.mask[3]);
- ops[16] = mso_trans(REG_PT_SPIMODE, devc->protocol_trigger.spimode);
- /* Select the default config bank */
- ops[17] = mso_trans(REG_CTL2, devc->ctlbase2);
-
- return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
-}
-
-SR_PRIV int mso_configure_threshold_level(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
-
- return mso_dac_out(sdi, la_threshold_map[devc->la_threshold]);
-}
-
-SR_PRIV int mso_read_buffer(struct sr_dev_inst *sdi)
-{
- uint16_t ops[] = { mso_trans(REG_BUFFER, 0) };
- struct dev_context *devc = sdi->priv;
-
- sr_dbg("Requesting buffer dump.");
- return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
-}
-
-SR_PRIV int mso_arm(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
- uint16_t ops[] = {
- mso_trans(REG_CTL1, devc->ctlbase1 | BIT_CTL1_RESETFSM),
- mso_trans(REG_CTL1, devc->ctlbase1 | BIT_CTL1_ARM),
- mso_trans(REG_CTL1, devc->ctlbase1),
- };
-
- sr_dbg("Requesting trigger arm.");
- return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
-}
-
-SR_PRIV int mso_force_capture(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
- uint16_t ops[] = {
- mso_trans(REG_CTL1, devc->ctlbase1 | 8),
- mso_trans(REG_CTL1, devc->ctlbase1),
- };
-
- sr_dbg("Requesting forced capture.");
- return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
-}
-
-SR_PRIV int mso_dac_out(const struct sr_dev_inst *sdi, uint16_t val)
-{
- struct dev_context *devc = sdi->priv;
- uint16_t ops[] = {
- mso_trans(REG_DAC1, (val >> 8) & 0xff),
- mso_trans(REG_DAC2, val & 0xff),
- mso_trans(REG_CTL1, devc->ctlbase1 | BIT_CTL1_RESETADC),
- };
-
- sr_dbg("Setting dac word to 0x%x.", val);
- return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
-}
-
-SR_PRIV inline uint16_t mso_calc_raw_from_mv(struct dev_context * devc)
-{
- return (uint16_t) (0x200 -
- ((devc->dso_trigger_voltage / devc->dso_probe_attn) /
- devc->vbit));
-}
-
-SR_PRIV int mso_parse_serial(const char *iSerial, const char *iProduct,
- struct dev_context *devc)
-{
- unsigned int u1, u2, u3, u4, u5, u6;
-
- (void)iProduct;
-
- /* FIXME: This code is in the original app, but I think its
- * used only for the GUI */
- /* if (strstr(iProduct, "REV_02") || strstr(iProduct, "REV_03"))
- devc->num_sample_rates = 0x16;
- else
- devc->num_sample_rates = 0x10; */
-
- /* parse iSerial */
- if (iSerial[0] != '4' || sscanf(iSerial, "%5u%3u%3u%1u%1u%6u",
- &u1, &u2, &u3, &u4, &u5, &u6) != 6)
- return SR_ERR;
- devc->hwmodel = u4;
- devc->hwrev = u5;
- devc->vbit = u1 / 10000;
- if (devc->vbit == 0)
- devc->vbit = 4.19195;
- devc->dac_offset = u2;
- if (devc->dac_offset == 0)
- devc->dac_offset = 0x1ff;
- devc->offset_range = u3;
- if (devc->offset_range == 0)
- devc->offset_range = 0x17d;
-
- /*
- * FIXME: There is more code on the original software to handle
- * bigger iSerial strings, but as I can't test on my device
- * I will not implement it yet
- */
-
- return SR_OK;
-}
-
-SR_PRIV int mso_reset_adc(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
- uint16_t ops[2];
-
- ops[0] = mso_trans(REG_CTL1, (devc->ctlbase1 | BIT_CTL1_RESETADC));
- ops[1] = mso_trans(REG_CTL1, devc->ctlbase1);
- devc->ctlbase1 |= BIT_CTL1_ADC_UNKNOWN4;
-
- sr_dbg("Requesting ADC reset.");
- return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
-}
-
-SR_PRIV int mso_reset_fsm(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
- uint16_t ops[1];
-
- devc->ctlbase1 |= BIT_CTL1_RESETFSM;
- ops[0] = mso_trans(REG_CTL1, devc->ctlbase1);
-
- sr_dbg("Requesting ADC reset.");
- return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
-}
-
-SR_PRIV int mso_toggle_led(struct sr_dev_inst *sdi, int state)
-{
- struct dev_context *devc = sdi->priv;
- uint16_t ops[1];
-
- devc->ctlbase1 &= ~BIT_CTL1_LED;
- if (state)
- devc->ctlbase1 |= BIT_CTL1_LED;
- ops[0] = mso_trans(REG_CTL1, devc->ctlbase1);
-
- sr_dbg("Requesting LED toggle.");
- return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
-}
-
-SR_PRIV void stop_acquisition(const struct sr_dev_inst *sdi)
-{
- struct sr_datafeed_packet packet;
- struct dev_context *devc;
-
- devc = sdi->priv;
- serial_source_remove(sdi->session, devc->serial);
-
- /* Terminate session */
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-}
-
-SR_PRIV int mso_clkrate_out(struct sr_serial_dev_inst *serial, uint16_t val)
-{
- uint16_t ops[] = {
- mso_trans(REG_CLKRATE1, (val >> 8) & 0xff),
- mso_trans(REG_CLKRATE2, val & 0xff),
- };
-
- sr_dbg("Setting clkrate word to 0x%x.", val);
- return mso_send_control_message(serial, ARRAY_AND_SIZE(ops));
-}
-
-SR_PRIV int mso_configure_rate(const struct sr_dev_inst *sdi, uint32_t rate)
-{
- struct dev_context *devc = sdi->priv;
- unsigned int i;
- int ret = SR_ERR;
-
- for (i = 0; i < ARRAY_SIZE(rate_map); i++) {
- if (rate_map[i].rate == rate) {
- devc->ctlbase2 = rate_map[i].slowmode;
- ret = mso_clkrate_out(devc->serial, rate_map[i].val);
- if (ret == SR_OK)
- devc->cur_rate = rate;
- return ret;
- }
- }
-
- if (ret != SR_OK)
- sr_err("Unsupported rate.");
-
- return ret;
-}
-
-SR_PRIV int mso_check_trigger(struct sr_serial_dev_inst *serial, uint8_t *info)
-{
- uint16_t ops[] = { mso_trans(REG_TRIGGER, 0) };
- int ret;
-
- sr_dbg("Requesting trigger state.");
- ret = mso_send_control_message(serial, ARRAY_AND_SIZE(ops));
- if (info == NULL || ret != SR_OK)
- return ret;
-
- uint8_t buf = 0;
- if (serial_read(serial, &buf, 1) != 1) /* FIXME: Need timeout */
- ret = SR_ERR;
- if (!info)
- *info = buf;
-
- sr_dbg("Trigger state is: 0x%x.", *info);
- return ret;
-}
-
-SR_PRIV int mso_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- struct sr_dev_inst *sdi;
- GSList *l;
- int i;
-
- struct drv_context *drvc = di->priv;
-
- /* Find this device's devc struct by its fd. */
- struct dev_context *devc = NULL;
- for (l = drvc->instances; l; l = l->next) {
- sdi = l->data;
- devc = sdi->priv;
- if (devc->serial->fd == fd)
- break;
- devc = NULL;
- }
- if (!devc)
- /* Shouldn't happen. */
- return TRUE;
-
- (void)revents;
-
- uint8_t in[1024];
- size_t s = serial_read(devc->serial, in, sizeof(in));
-
- if (s <= 0)
- return FALSE;
-
- /* Check if we triggered, then send a command that we are ready
- * to read the data */
- if (devc->trigger_state != MSO_TRIGGER_DATAREADY) {
- devc->trigger_state = in[0];
- if (devc->trigger_state == MSO_TRIGGER_DATAREADY) {
- mso_read_buffer(sdi);
- devc->buffer_n = 0;
- } else {
- mso_check_trigger(devc->serial, NULL);
- }
- return TRUE;
- }
-
- /* the hardware always dumps 1024 samples, 24bits each */
- if (devc->buffer_n < 3072) {
- memcpy(devc->buffer + devc->buffer_n, in, s);
- devc->buffer_n += s;
- }
- if (devc->buffer_n < 3072)
- return TRUE;
-
- /* do the conversion */
- uint8_t logic_out[1024];
- double analog_out[1024];
- for (i = 0; i < 1024; i++) {
- /* FIXME: Need to do conversion to mV */
- analog_out[i] = (devc->buffer[i * 3] & 0x3f) |
- ((devc->buffer[i * 3 + 1] & 0xf) << 6);
- (void)analog_out;
- logic_out[i] = ((devc->buffer[i * 3 + 1] & 0x30) >> 4) |
- ((devc->buffer[i * 3 + 2] & 0x3f) << 2);
- }
-
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = 1024;
- logic.unitsize = 1;
- logic.data = logic_out;
- sr_session_send(cb_data, &packet);
-
- devc->num_samples += 1024;
-
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- }
-
- return TRUE;
-}
-
-SR_PRIV int mso_configure_channels(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- GSList *l;
- char *tc;
-
- devc = sdi->priv;
-
- devc->la_trigger_mask = 0xFF; //the mask for the LA_TRIGGER (bits set to 0 matter, those set to 1 are ignored).
- devc->la_trigger = 0x00; //The value of the LA byte that generates a trigger event (in that mode).
- devc->dso_trigger_voltage = 3;
- devc->dso_probe_attn = 1;
- devc->trigger_outsrc = 0;
- devc->trigger_chan = 3; //LA combination trigger
- devc->use_trigger = FALSE;
-
- for (l = sdi->channels; l; l = l->next) {
- ch = (struct sr_channel *)l->data;
- if (ch->enabled == FALSE)
- continue;
-
- int channel_bit = 1 << (ch->index);
- if (!(ch->trigger))
- continue;
-
- devc->use_trigger = TRUE;
- //Configure trigger mask and value.
- for (tc = ch->trigger; *tc; tc++) {
- devc->la_trigger_mask &= ~channel_bit;
- if (*tc == '1')
- devc->la_trigger |= channel_bit;
- }
- }
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011 Daniel Ribeiro <drwyrm@gmail.com>
- * Copyright (C) 2012 Renato Caldas <rmsc@fe.up.pt>
- * Copyright (C) 2013 Lior Elazary <lelazary@yahoo.com>
- *
- * 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_LINK_MSO19_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_LINK_MSO19_PROTOCOL_H
-
-#include <stdint.h>
-#include <string.h>
-#include <glib.h>
-#include <libudev.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "link-mso19"
-
-#define USB_VENDOR "3195"
-#define USB_PRODUCT "f190"
-
-#define NUM_CHANNELS (1 + 8)
-#define NUM_TRIGGER_STAGES 4
-#define TRIGGER_TYPE "01" //the first r/f is used for the whole group
-#define SERIALCOMM "460800/8n1/flow=2"
-#define SERIALCONN "/dev/ttyUSB0"
-#define CLOCK_RATE SR_MHZ(100)
-#define MIN_NUM_SAMPLES 4
-
-#define MSO_TRIGGER_UNKNOWN '!'
-#define MSO_TRIGGER_UNKNOWN1 '1'
-#define MSO_TRIGGER_UNKNOWN2 '2'
-#define MSO_TRIGGER_UNKNOWN3 '3'
-#define MSO_TRIGGER_WAIT '4'
-#define MSO_TRIGGER_FIRED '5'
-#define MSO_TRIGGER_DATAREADY '6'
-
-enum trigger_slopes {
- SLOPE_POSITIVE = 0,
- SLOPE_NEGATIVE,
-};
-
-/* Structure for the pattern generator state */
-struct mso_patgen {
- /* Pattern generator clock config */
- uint16_t clock;
- /* Buffer start address */
- uint16_t start;
- /* Buffer end address */
- uint16_t end;
- /* Pattern generator config */
- uint8_t config;
- /* Samples buffer */
- uint8_t buffer[1024];
- /* Input/output configuration for the samples buffer (?) */
- uint8_t io[1024];
- /* Number of loops for the pattern generator */
- uint8_t loops;
- /* Bit enable mask for the I/O lines */
- uint8_t mask;
-};
-
-/* Data structure for the protocol trigger state */
-struct mso_prototrig {
- /* Word match buffer */
- uint8_t word[4];
- /* Masks for the wordmatch buffer */
- uint8_t mask[4];
- /* SPI mode 0, 1, 2, 3. Set to 0 for I2C */
- uint8_t spimode;
-};
-
-/* Private, per-device-instance driver context. */
-struct dev_context {
- /* info */
- uint8_t hwmodel;
- uint8_t hwrev;
- struct sr_serial_dev_inst *serial;
-// uint8_t num_sample_rates;
- /* calibration */
- double vbit;
- uint16_t dac_offset;
- uint16_t offset_range;
- uint64_t limit_samples;
- uint64_t num_samples;
- /* register cache */
- uint8_t ctlbase1;
- uint8_t ctlbase2;
- /* state */
- uint8_t la_threshold;
- uint64_t cur_rate;
- uint8_t dso_probe_attn;
- int8_t use_trigger;
- uint8_t trigger_chan;
- uint8_t trigger_slope;
- uint8_t trigger_outsrc;
- uint8_t trigger_state;
- uint8_t trigger_holdoff[2];
- uint8_t la_trigger;
- uint8_t la_trigger_mask;
- double dso_trigger_voltage;
- uint16_t dso_trigger_width;
- struct mso_prototrig protocol_trigger;
- void *cb_data;
- uint16_t buffer_n;
- char buffer[4096];
-};
-
-SR_PRIV int mso_parse_serial(const char *iSerial, const char *iProduct,
- struct dev_context *ctx);
-SR_PRIV int mso_check_trigger(struct sr_serial_dev_inst *serial,
- uint8_t * info);
-SR_PRIV int mso_reset_adc(struct sr_dev_inst *sdi);
-SR_PRIV int mso_clkrate_out(struct sr_serial_dev_inst *serial, uint16_t val);
-SR_PRIV int mso_configure_rate(const struct sr_dev_inst *sdi, uint32_t rate);
-SR_PRIV int mso_receive_data(int fd, int revents, void *cb_data);
-SR_PRIV int mso_configure_trigger(const struct sr_dev_inst *sdi);
-SR_PRIV int mso_configure_threshold_level(const struct sr_dev_inst *sdi);
-SR_PRIV int mso_read_buffer(struct sr_dev_inst *sdi);
-SR_PRIV int mso_arm(const struct sr_dev_inst *sdi);
-SR_PRIV int mso_force_capture(struct sr_dev_inst *sdi);
-SR_PRIV int mso_dac_out(const struct sr_dev_inst *sdi, uint16_t val);
-SR_PRIV inline uint16_t mso_calc_raw_from_mv(struct dev_context *devc);
-SR_PRIV int mso_reset_fsm(struct sr_dev_inst *sdi);
-SR_PRIV int mso_toggle_led(struct sr_dev_inst *sdi, int state);
-
-SR_PRIV int mso_configure_channels(const struct sr_dev_inst *sdi);
-SR_PRIV void stop_acquisition(const struct sr_dev_inst *sdi);
-
-/* bank agnostic registers */
-#define REG_CTL2 15
-
-/* bank 0 registers */
-#define REG_BUFFER 1
-#define REG_TRIGGER 2
-#define REG_CLKRATE1 9
-#define REG_CLKRATE2 10
-#define REG_DAC1 12
-#define REG_DAC2 13
-/* possibly bank agnostic: */
-#define REG_CTL1 14
-
-/* bank 2 registers (SPI/I2C protocol trigger) */
-#define REG_PT_WORD(x) (x)
-#define REG_PT_MASK(x) (x + 4)
-#define REG_PT_SPIMODE 8
-
-/* bits - REG_CTL1 */
-#define BIT_CTL1_RESETFSM (1 << 0)
-#define BIT_CTL1_ARM (1 << 1)
-#define BIT_CTL1_ADC_UNKNOWN4 (1 << 4) /* adc enable? */
-#define BIT_CTL1_RESETADC (1 << 6)
-#define BIT_CTL1_LED (1 << 7)
-
-/* bits - REG_CTL2 */
-#define BITS_CTL2_BANK(x) (x & 0x3)
-#define BIT_CTL2_SLOWMODE (1 << 5)
-
-struct rate_map {
- uint32_t rate;
- uint16_t val;
- uint8_t slowmode;
-};
-
-static const struct rate_map rate_map[] = {
- { SR_MHZ(200), 0x0205, 0 },
- { SR_MHZ(100), 0x0105, 0 },
- { SR_MHZ(50), 0x0005, 0 },
- { SR_MHZ(20), 0x0303, 0 },
- { SR_MHZ(10), 0x0308, 0 },
- { SR_MHZ(5), 0x030c, 0 },
- { SR_MHZ(2), 0x0330, 0 },
- { SR_MHZ(1), 0x0362, 0 },
- { SR_KHZ(500), 0x03c6, 0 },
- { SR_KHZ(200), 0x07f2, 0 },
- { SR_KHZ(100), 0x0fe6, 0 },
- { SR_KHZ(50), 0x1fce, 0 },
- { SR_KHZ(20), 0x4f86, 0 },
- { SR_KHZ(10), 0x9f0e, 0 },
- { SR_KHZ(5), 0x03c7, 0x20 },
- { SR_KHZ(2), 0x07f3, 0x20 },
- { SR_KHZ(1), 0x0fe7, 0x20 },
- { SR_HZ(500), 0x1fcf, 0x20 },
- { SR_HZ(200), 0x4f87, 0x20 },
- { SR_HZ(100), 0x9f0f, 0x20 },
-};
-
-/* FIXME: Determine corresponding voltages */
-static const uint16_t la_threshold_map[] = {
- 0x8600,
- 0x8770,
- 0x88ff,
- 0x8c70,
- 0x8eff,
- 0x8fff,
-};
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Uwe Hermann <uwe@hermann-uwe.de>
- * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/** @file
- * <em>Manson HCS-3xxx Series</em> power supply driver
- * @internal
- */
-
-#include "protocol.h"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t devopts[] = {
- /* Device class */
- SR_CONF_POWER_SUPPLY,
- /* Aquisition modes. */
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
- /* Device configuration */
- SR_CONF_OUTPUT_CURRENT,
- SR_CONF_OUTPUT_CURRENT_MAX,
- SR_CONF_OUTPUT_ENABLED,
- SR_CONF_OUTPUT_VOLTAGE,
- SR_CONF_OUTPUT_VOLTAGE_MAX,
-};
-
-/* Note: All models have one power supply output only. */
-static struct hcs_model models[] = {
- { MANSON_HCS_3100, "HCS-3100", "3100", { 1, 18, 0.1 }, { 0, 10, 0.10 } },
- { MANSON_HCS_3102, "HCS-3102", "3102", { 1, 36, 0.1 }, { 0, 5, 0.01 } },
- { MANSON_HCS_3104, "HCS-3104", "3104", { 1, 60, 0.1 }, { 0, 2.5, 0.01 } },
- { MANSON_HCS_3150, "HCS-3150", "3150", { 1, 18, 0.1 }, { 0, 15, 0.10 } },
- { MANSON_HCS_3200, "HCS-3200", "3200", { 1, 18, 0.1 }, { 0, 20, 0.10 } },
- { MANSON_HCS_3202, "HCS-3202", "3202", { 1, 36, 0.1 }, { 0, 10, 0.10 } },
- { MANSON_HCS_3204, "HCS-3204", "3204", { 1, 60, 0.1 }, { 0, 5, 0.01 } },
- { MANSON_HCS_3300, "HCS-3300-USB", "3300", { 1, 16, 0.1 }, { 0, 30, 0.10 } },
- { MANSON_HCS_3302, "HCS-3302-USB", "3302", { 1, 32, 0.1 }, { 0, 15, 0.10 } },
- { MANSON_HCS_3304, "HCS-3304-USB", "3304", { 1, 60, 0.1 }, { 0, 8, 0.10 } },
- { MANSON_HCS_3400, "HCS-3400-USB", "3400", { 1, 16, 0.1 }, { 0, 40, 0.10 } },
- { MANSON_HCS_3402, "HCS-3402-USB", "3402", { 1, 32, 0.1 }, { 0, 20, 0.10 } },
- { MANSON_HCS_3404, "HCS-3404-USB", "3404", { 1, 60, 0.1 }, { 0, 10, 0.10 } },
- { MANSON_HCS_3600, "HCS-3600-USB", "3600", { 1, 16, 0.1 }, { 0, 60, 0.10 } },
- { MANSON_HCS_3602, "HCS-3602-USB", "3602", { 1, 32, 0.1 }, { 0, 30, 0.10 } },
- { MANSON_HCS_3604, "HCS-3604-USB", "3604", { 1, 60, 0.1 }, { 0, 15, 0.10 } },
- { 0, NULL, NULL, { 0, 0, 0 }, { 0, 0, 0 }, },
-};
-
-SR_PRIV struct sr_dev_driver manson_hcs_3xxx_driver_info;
-static struct sr_dev_driver *di = &manson_hcs_3xxx_driver_info;
-
-static int dev_clear(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- int i, model_id;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_config *src;
- struct sr_channel *ch;
- GSList *devices, *l;
- const char *conn, *serialcomm;
- struct sr_serial_dev_inst *serial;
- char reply[50], **tokens, *dummy;
-
- drvc = di->priv;
- drvc->instances = NULL;
- devices = NULL;
- conn = NULL;
- serialcomm = NULL;
- devc = 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;
- default:
- sr_err("Unknown option %d, skipping.", src->key);
- break;
- }
- }
-
- if (!conn)
- return NULL;
- if (!serialcomm)
- serialcomm = "9600/8n1";
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR) != SR_OK)
- return NULL;
-
- serial_flush(serial);
-
- sr_info("Probing serial port %s.", conn);
-
- /* Get the device model. */
- memset(&reply, 0, sizeof(reply));
- if ((hcs_send_cmd(serial, "GMOD\r") < 0) ||
- (hcs_read_reply(serial, 2, reply, sizeof(reply)) < 0))
- return NULL;
- tokens = g_strsplit((const gchar *)&reply, "\r", 2);
-
- model_id = -1;
- for (i = 0; models[i].id != NULL; i++) {
- if (!strcmp(models[i].id, tokens[0]))
- model_id = i;
- }
- g_strfreev(tokens);
-
- if (model_id < 0) {
- sr_err("Unknown model id '%s' detected, aborting.", tokens[0]);
- return NULL;
- }
-
- /* Init device instance, etc. */
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Manson",
- models[model_id].name, NULL))) {
- sr_err("Failed to create device instance.");
- return NULL;
- }
-
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
- sdi->driver = di;
-
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "CH1"))) {
- sr_err("Failed to create channel.");
- goto exit_err;
- }
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- devc = g_malloc0(sizeof(struct dev_context));
- devc->model = &models[model_id];
-
- sdi->priv = devc;
-
- /* Get current voltage, current, status. */
- if ((hcs_send_cmd(serial, "GETD\r") < 0) ||
- (hcs_read_reply(serial, 2, reply, sizeof(reply)) < 0))
- goto exit_err;
- tokens = g_strsplit((const gchar *)&reply, "\r", 2);
- if (hcs_parse_volt_curr_mode(sdi, tokens) < 0)
- goto exit_err;
- g_strfreev(tokens);
-
- /* Get max. voltage and current. */
- if ((hcs_send_cmd(serial, "GMAX\r") < 0) ||
- (hcs_read_reply(serial, 2, reply, sizeof(reply)) < 0))
- goto exit_err;
- tokens = g_strsplit((const gchar *)&reply, "\r", 2);
- devc->current_max_device = g_strtod(&tokens[0][3], &dummy) * devc->model->current[2];
- tokens[0][3] = '\0';
- devc->voltage_max_device = g_strtod(tokens[0], &dummy) * devc->model->voltage[2];
- g_strfreev(tokens);
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- serial_close(serial);
- if (!devices)
- sr_serial_dev_inst_free(serial);
-
- return devices;
-
-exit_err:
- sr_dev_inst_free(sdi);
- if (devc)
- g_free(devc);
- return NULL;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int cleanup(void)
-{
- return dev_clear();
-}
-
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (!sdi)
- return SR_ERR_ARG;
-
- devc = sdi->priv;
-
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- *data = g_variant_new_uint64(devc->limit_msec);
- break;
- case SR_CONF_OUTPUT_CURRENT:
- *data = g_variant_new_double(devc->current);
- break;
- case SR_CONF_OUTPUT_CURRENT_MAX:
- *data = g_variant_new_double(devc->current_max);
- break;
- case SR_CONF_OUTPUT_ENABLED:
- *data = g_variant_new_boolean(devc->output_enabled);
- break;
- case SR_CONF_OUTPUT_VOLTAGE:
- *data = g_variant_new_double(devc->voltage);
- break;
- case SR_CONF_OUTPUT_VOLTAGE_MAX:
- *data = g_variant_new_double(devc->voltage_max);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- gboolean bval;
- gdouble dval;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- switch (key) {
- case SR_CONF_LIMIT_MSEC:
- if (g_variant_get_uint64(data) == 0)
- return SR_ERR_ARG;
- devc->limit_msec = g_variant_get_uint64(data);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- if (g_variant_get_uint64(data) == 0)
- return SR_ERR_ARG;
- devc->limit_samples = g_variant_get_uint64(data);
- break;
- case SR_CONF_OUTPUT_CURRENT_MAX:
- dval = g_variant_get_double(data);
- if (dval < devc->model->current[0] || dval > devc->current_max_device)
- return SR_ERR_ARG;
-
- if ((hcs_send_cmd(sdi->conn, "CURR%03.0f\r",
- (dval / devc->model->current[2])) < 0) ||
- (hcs_read_reply(sdi->conn, 1, devc->buf, sizeof(devc->buf)) < 0))
- return SR_ERR;
- devc->current_max = dval;
- break;
- case SR_CONF_OUTPUT_ENABLED:
- bval = g_variant_get_boolean(data);
- if (bval == devc->output_enabled) /* Nothing to do. */
- break;
- if ((hcs_send_cmd(sdi->conn, "SOUT%1d\r", !bval) < 0) ||
- (hcs_read_reply(sdi->conn, 1, devc->buf, sizeof(devc->buf)) < 0))
- return SR_ERR;
- devc->output_enabled = bval;
- break;
- case SR_CONF_OUTPUT_VOLTAGE_MAX:
- dval = g_variant_get_double(data);
- if (dval < devc->model->voltage[0] || dval > devc->voltage_max_device)
- return SR_ERR_ARG;
-
- if ((hcs_send_cmd(sdi->conn, "VOLT%03.0f\r",
- (dval / devc->model->voltage[2])) < 0) ||
- (hcs_read_reply(sdi->conn, 1, devc->buf, sizeof(devc->buf)) < 0))
- return SR_ERR;
- devc->voltage_max = dval;
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- GVariant *gvar;
- GVariantBuilder gvb;
- double dval;
- int idx;
-
- (void)cg;
-
- if (!sdi)
- return SR_ERR_ARG;
- devc = sdi->priv;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
- break;
- case SR_CONF_OUTPUT_CURRENT_MAX:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- /* Min, max, step. */
- for (idx = 0; idx < 3; idx++) {
- if (idx == 1)
- dval = devc->current_max_device;
- else
- dval = devc->model->current[idx];
- gvar = g_variant_new_double(dval);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_OUTPUT_VOLTAGE_MAX:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- /* Min, max, step. */
- for (idx = 0; idx < 3; idx++) {
- if (idx == 1)
- dval = devc->voltage_max_device;
- else
- dval = devc->model->voltage[idx];
- gvar = g_variant_new_double(dval);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- devc->cb_data = cb_data;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- devc->starttime = g_get_monotonic_time();
- devc->num_samples = 0;
- devc->reply_pending = FALSE;
- devc->req_sent_at = 0;
-
- /* Poll every 10ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 10,
- hcs_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- return std_serial_dev_acquisition_stop(sdi, cb_data,
- std_serial_dev_close, sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver manson_hcs_3xxx_driver_info = {
- .name = "manson-hcs-3xxx",
- .longname = "Manson HCS-3xxx",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = 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 = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Uwe Hermann <uwe@hermann-uwe.de>
- * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/** @file
- * <em>Manson HCS-3xxx Series</em> power supply driver
- * @internal
- */
-
-#include "protocol.h"
-
-#define REQ_TIMEOUT_MS 500
-
-SR_PRIV int hcs_send_cmd(struct sr_serial_dev_inst *serial, const char *cmd, ...)
-{
- int ret;
- char cmdbuf[50];
- char *cmd_esc;
- va_list args;
-
- va_start(args, cmd);
- vsnprintf(cmdbuf, sizeof(cmdbuf), cmd, args);
- va_end(args);
-
- cmd_esc = g_strescape(cmdbuf, NULL);
- sr_dbg("Sending '%s'.", cmd_esc);
- g_free(cmd_esc);
-
- if ((ret = serial_write_blocking(serial, cmdbuf, strlen(cmdbuf))) < 0) {
- sr_err("Error sending command: %d.", ret);
- return ret;
- }
-
- return ret;
-}
-
-/**
- * Read data from interface into buffer blocking until @a lines number of \\r chars
- * received.
- * @param serial Previously initialized serial port structure.
- * @param[in] lines Number of \\r-terminated lines to read (1-n).
- * @param buf Buffer for result. Contents is NUL-terminated on success.
- * @param[in] buflen Buffer length (>0).
- * @retval SR_OK Lines received and ending with "OK\r" (success).
- * @retval SR_ERR Error.
- * @retval SR_ERR_ARG Invalid argument.
- */
-SR_PRIV int hcs_read_reply(struct sr_serial_dev_inst *serial, int lines, char* buf, int buflen)
-{
- int l_recv = 0;
- int bufpos = 0;
- int retc;
-
- if (!serial || (lines <= 0) || !buf || (buflen <= 0))
- return SR_ERR_ARG;
-
- while ((l_recv < lines) && (bufpos < (buflen + 1))) {
- retc = serial_read_blocking(serial, &buf[bufpos], 1);
- if (retc != 1)
- return SR_ERR;
- if (buf[bufpos] == '\r')
- l_recv++;
- bufpos++;
- }
- buf[bufpos] = '\0';
-
- if ((l_recv == lines) && (g_str_has_suffix(buf, "OK\r")))
- return SR_OK;
- else
- return SR_ERR;
-}
-
-/** Interpret result of GETD command. */
-SR_PRIV int hcs_parse_volt_curr_mode(struct sr_dev_inst *sdi, char **tokens)
-{
- char *str;
- double val;
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- /* Bytes 0-3: Voltage. */
- str = g_strndup(tokens[0], 4);
- val = g_ascii_strtod(str, NULL) / 100;
- devc->voltage = val;
- g_free(str);
-
- /* Bytes 4-7: Current. */
- str = g_strndup((tokens[0] + 4), 4);
- val = g_ascii_strtod(str, NULL) / 100;
- devc->current = val;
- g_free(str);
-
- /* Byte 8: Mode ('0' means CV, '1' means CC). */
- devc->cc_mode = (tokens[0][8] == '1');
-
- /* Output enabled? Works because voltage cannot be set to 0.0 directly. */
- devc->output_enabled = devc->voltage != 0.0;
-
- return SR_OK;
-}
-
-static void send_sample(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
-
- devc = sdi->priv;
-
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- analog.channels = sdi->channels;
- analog.num_samples = 1;
-
- analog.mq = SR_MQ_VOLTAGE;
- analog.unit = SR_UNIT_VOLT;
- analog.mqflags = SR_MQFLAG_DC;
- analog.data = &devc->voltage;
- sr_session_send(sdi, &packet);
-
- analog.mq = SR_MQ_CURRENT;
- analog.unit = SR_UNIT_AMPERE;
- analog.mqflags = 0;
- analog.data = &devc->current;
- sr_session_send(sdi, &packet);
-
- devc->num_samples++;
-}
-
-static int parse_reply(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- char *reply_esc, **tokens;
- int retc;
-
- devc = sdi->priv;
-
- reply_esc = g_strescape(devc->buf, NULL);
- sr_dbg("Received '%s'.", reply_esc);
- g_free(reply_esc);
-
- tokens = g_strsplit(devc->buf, "\r", 0);
- retc = hcs_parse_volt_curr_mode(sdi, tokens);
- g_strfreev(tokens);
- if (retc < 0)
- return SR_ERR;
-
- send_sample(sdi);
-
- return SR_OK;
-}
-
-static int handle_new_data(struct sr_dev_inst *sdi)
-{
- int len;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- devc = sdi->priv;
- serial = sdi->conn;
-
- len = serial_read(serial, devc->buf + devc->buflen, 1);
- if (len < 1)
- return SR_ERR;
-
- devc->buflen += len;
- devc->buf[devc->buflen] = '\0';
-
- /* Wait until we received an "OK\r" (among other bytes). */
- if (!g_str_has_suffix(devc->buf, "OK\r"))
- return SR_OK;
-
- parse_reply(sdi);
-
- devc->buf[0] = '\0';
- devc->buflen = 0;
-
- devc->reply_pending = FALSE;
-
- return SR_OK;
-}
-
-/** Driver/serial data reception function. */
-SR_PRIV int hcs_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- int64_t t, elapsed_us;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- serial = sdi->conn;
-
- if (revents == G_IO_IN) {
- /* New data arrived. */
- handle_new_data(sdi);
- } else {
- /* Timeout. */
- }
-
- if (devc->limit_samples && (devc->num_samples >= devc->limit_samples)) {
- sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
-
- if (devc->limit_msec) {
- t = (g_get_monotonic_time() - devc->starttime) / 1000;
- if (t > (int64_t)devc->limit_msec) {
- sr_info("Requested time limit reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
- }
-
- /* Request next packet, if required. */
- if (sdi->status == SR_ST_ACTIVE) {
- if (devc->reply_pending) {
- elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
- if (elapsed_us > (REQ_TIMEOUT_MS * 1000))
- devc->reply_pending = FALSE;
- return TRUE;
- }
-
- /* Send command to get voltage, current, and mode (CC or CV). */
- if (hcs_send_cmd(serial, "GETD\r") < 0)
- return TRUE;
-
- devc->req_sent_at = g_get_monotonic_time();
- devc->reply_pending = TRUE;
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Uwe Hermann <uwe@hermann-uwe.de>
- * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/** @file
- * <em>Manson HCS-3xxx Series</em> power supply driver
- * @internal
- */
-
-#ifndef LIBSIGROK_HARDWARE_MANSON_HCS_3XXX_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_MANSON_HCS_3XXX_PROTOCOL_H
-
-#include <stdint.h>
-#include <string.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "manson-hcs-3xxx"
-
-enum {
- MANSON_HCS_3100,
- MANSON_HCS_3102,
- MANSON_HCS_3104,
- MANSON_HCS_3150,
- MANSON_HCS_3200,
- MANSON_HCS_3202,
- MANSON_HCS_3204,
- MANSON_HCS_3300,
- MANSON_HCS_3302,
- MANSON_HCS_3304,
- MANSON_HCS_3400,
- MANSON_HCS_3402,
- MANSON_HCS_3404,
- MANSON_HCS_3600,
- MANSON_HCS_3602,
- MANSON_HCS_3604,
-};
-
-/** Information on a single model. */
-struct hcs_model {
- int model_id; /**< Model info */
- char *name; /**< Model name */
- char *id; /**< Model ID, like delivered by interface */
- double voltage[3]; /**< Min, max, step */
- double current[3]; /**< Min, max, step */
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- struct hcs_model *model; /**< Model informaion. */
-
- uint64_t limit_samples;
- uint64_t limit_msec;
- uint64_t num_samples;
- int64_t starttime;
- int64_t req_sent_at;
- gboolean reply_pending;
-
- void *cb_data;
-
- float current; /**< Last current value [A] read from device. */
- float current_max; /**< Output current set. */
- float current_max_device;/**< Device-provided maximum output current. */
- float voltage; /**< Last voltage value [V] read from device. */
- float voltage_max; /**< Output voltage set. */
- float voltage_max_device;/**< Device-provided maximum output voltage. */
- gboolean cc_mode; /**< Device is in constant current mode (otherwise constant voltage). */
-
- gboolean output_enabled; /**< Is the output enabled? */
-
- char buf[50];
- int buflen;
-};
-
-SR_PRIV int hcs_parse_volt_curr_mode(struct sr_dev_inst *sdi, char **tokens);
-SR_PRIV int hcs_read_reply(struct sr_serial_dev_inst *serial, int lines, char* buf, int buflen);
-SR_PRIV int hcs_send_cmd(struct sr_serial_dev_inst *serial, const char *cmd, ...);
-SR_PRIV int hcs_receive_data(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "protocol.h"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_THERMOMETER,
- SR_CONF_HYGROMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
-};
-
-SR_PRIV struct sr_dev_driver mic_98581_driver_info;
-SR_PRIV struct sr_dev_driver mic_98583_driver_info;
-
-SR_PRIV const struct mic_dev_info mic_devs[] = {
- {
- "MIC", "98581", "38400/8n2", 32000, TRUE, FALSE, 6,
- packet_valid_temp,
- &mic_98581_driver_info, receive_data_MIC_98581,
- },
- {
- "MIC", "98583", "38400/8n2", 32000, TRUE, TRUE, 10,
- packet_valid_temp_hum,
- &mic_98583_driver_info, receive_data_MIC_98583,
- },
-};
-
-static int dev_clear(int idx)
-{
- return std_dev_clear(mic_devs[idx].di, NULL);
-}
-
-static int init(struct sr_context *sr_ctx, int idx)
-{
- sr_dbg("Selected '%s' subdriver.", mic_devs[idx].di->name);
-
- return std_init(sr_ctx, mic_devs[idx].di, LOG_PREFIX);
-}
-
-static GSList *mic_scan(const char *conn, const char *serialcomm, int idx)
-{
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_channel *ch;
- struct sr_serial_dev_inst *serial;
- GSList *devices;
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- drvc = mic_devs[idx].di->priv;
- devices = NULL;
- serial_flush(serial);
-
- /* TODO: Query device type. */
- // ret = mic_cmd_get_device_info(serial);
-
- sr_info("Found device on port %s.", conn);
-
- /* TODO: Fill in version from protocol response. */
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, mic_devs[idx].vendor,
- mic_devs[idx].device, NULL)))
- goto scan_cleanup;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- goto scan_cleanup;
- }
-
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
-
- sdi->priv = devc;
- sdi->driver = mic_devs[idx].di;
-
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "Temperature")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- if (mic_devs[idx].has_humidity) {
- if (!(ch = sr_channel_new(1, SR_CHANNEL_ANALOG, TRUE, "Humidity")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
-scan_cleanup:
- serial_close(serial);
-
- return devices;
-}
-
-static GSList *scan(GSList *options, int idx)
-{
- struct sr_config *src;
- GSList *l, *devices;
- const char *conn, *serialcomm;
-
- 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 (!conn)
- return NULL;
-
- if (serialcomm) {
- /* Use the provided comm specs. */
- devices = mic_scan(conn, serialcomm, idx);
- } else {
- /* Try the default. */
- devices = mic_scan(conn, mic_devs[idx].conn, idx);
- }
-
- return devices;
-}
-
-static GSList *dev_list(int idx)
-{
- return ((struct drv_context *)(mic_devs[idx].di->priv))->instances;
-}
-
-static int cleanup(int idx)
-{
- return dev_clear(idx);
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- switch (id) {
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- devc->limit_msec = g_variant_get_uint64(data);
- sr_dbg("Setting time limit to %" PRIu64 "ms.",
- devc->limit_msec);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data, int idx)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- devc->cb_data = cb_data;
- devc->num_samples = 0;
- devc->starttime = g_get_monotonic_time();
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Poll every 100ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 100,
- mic_devs[idx].receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
- sdi->conn, LOG_PREFIX);
-}
-
-/* Driver-specific API function wrappers */
-#define HW_INIT(X) \
-static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
-#define HW_CLEANUP(X) \
-static int cleanup_##X(void) { return cleanup(X); }
-#define HW_SCAN(X) \
-static GSList *scan_##X(GSList *options) { return scan(options, X); }
-#define HW_DEV_LIST(X) \
-static GSList *dev_list_##X(void) { return dev_list(X); }
-#define HW_DEV_CLEAR(X) \
-static int dev_clear_##X(void) { return dev_clear(X); }
-#define HW_DEV_ACQUISITION_START(X) \
-static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
-void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
-
-/* Driver structs and API function wrappers */
-#define DRV(ID, ID_UPPER, NAME, LONGNAME) \
-HW_INIT(ID_UPPER) \
-HW_CLEANUP(ID_UPPER) \
-HW_SCAN(ID_UPPER) \
-HW_DEV_LIST(ID_UPPER) \
-HW_DEV_CLEAR(ID_UPPER) \
-HW_DEV_ACQUISITION_START(ID_UPPER) \
-SR_PRIV struct sr_dev_driver ID##_driver_info = { \
- .name = NAME, \
- .longname = LONGNAME, \
- .api_version = 1, \
- .init = init_##ID_UPPER, \
- .cleanup = cleanup_##ID_UPPER, \
- .scan = scan_##ID_UPPER, \
- .dev_list = dev_list_##ID_UPPER, \
- .dev_clear = dev_clear_##ID_UPPER, \
- .config_get = NULL, \
- .config_set = config_set, \
- .config_list = config_list, \
- .dev_open = std_serial_dev_open, \
- .dev_close = std_serial_dev_close, \
- .dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
- .dev_acquisition_stop = dev_acquisition_stop, \
- .priv = NULL, \
-};
-
-DRV(mic_98581, MIC_98581, "mic-98581", "MIC 98581")
-DRV(mic_98583, MIC_98583, "mic-98583", "MIC 98583")
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "protocol.h"
-
-static int mic_send(struct sr_serial_dev_inst *serial, const char *cmd)
-{
- int ret;
-
- if ((ret = serial_write(serial, cmd, strlen(cmd))) < 0) {
- sr_err("Error sending '%s' command: %d.", cmd, ret);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int mic_cmd_get_device_info(struct sr_serial_dev_inst *serial)
-{
- return mic_send(serial, "I\r");
-}
-
-static int mic_cmd_set_realtime_mode(struct sr_serial_dev_inst *serial)
-{
- return mic_send(serial, "S 1 M 2 32 3\r");
-}
-
-SR_PRIV gboolean packet_valid_temp(const uint8_t *buf)
-{
- if (buf[0] != 'v' || buf[1] != ' ' || buf[5] != '\r')
- return FALSE;
-
- if (!isdigit(buf[2]) || !isdigit(buf[3]) || !isdigit(buf[4]))
- return FALSE;
-
- return TRUE;
-}
-
-SR_PRIV gboolean packet_valid_temp_hum(const uint8_t *buf)
-{
- if (buf[0] != 'v' || buf[1] != ' ' || buf[5] != ' ' || buf[9] != '\r')
- return FALSE;
-
- if (!isdigit(buf[2]) || !isdigit(buf[3]) || !isdigit(buf[4]))
- return FALSE;
-
- if (!isdigit(buf[6]) || !isdigit(buf[7]) || !isdigit(buf[8]))
- return FALSE;
-
- return TRUE;
-}
-
-static int packet_parse(const char *buf, int idx, float *temp, float *humidity)
-{
- char tmp[4];
-
- /* Packet format MIC98581: "v ttt\r". */
- /* Packet format MIC98583: "v ttt hhh\r". */
-
- /* TODO: Sanity check on buf. For now we assume well-formed ASCII. */
-
- tmp[3] = '\0';
-
- strncpy((char *)&tmp, &buf[2], 3);
- *temp = g_ascii_strtoull((const char *)&tmp, NULL, 10) / 10;
-
- if (mic_devs[idx].has_humidity) {
- strncpy((char *)&tmp, &buf[6], 3);
- *humidity = g_ascii_strtoull((const char *)&tmp, NULL, 10) / 10;
- }
-
- return SR_OK;
-}
-
-static int handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi, int idx)
-{
- float temperature, humidity;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct dev_context *devc;
- GSList *l;
- int ret;
-
- (void)idx;
-
- devc = sdi->priv;
-
- ret = packet_parse((const char *)buf, idx, &temperature, &humidity);
- if (ret < 0) {
- sr_err("Failed to parse packet.");
- return SR_ERR;
- }
-
- /* Clear 'analog', otherwise it'll contain random garbage. */
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
-
- /* Common values for both channels. */
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- analog.num_samples = 1;
-
- /* Temperature. */
- l = g_slist_copy(sdi->channels);
- l = g_slist_remove_link(l, g_slist_nth(l, 1));
- analog.channels = l;
- analog.mq = SR_MQ_TEMPERATURE;
- analog.unit = SR_UNIT_CELSIUS; /* TODO: Use C/F correctly. */
- analog.data = &temperature;
- sr_session_send(devc->cb_data, &packet);
- g_slist_free(l);
-
- /* Humidity. */
- if (mic_devs[idx].has_humidity) {
- l = g_slist_copy(sdi->channels);
- l = g_slist_remove_link(l, g_slist_nth(l, 0));
- analog.channels = l;
- analog.mq = SR_MQ_RELATIVE_HUMIDITY;
- analog.unit = SR_UNIT_PERCENTAGE;
- analog.data = &humidity;
- sr_session_send(devc->cb_data, &packet);
- g_slist_free(l);
- }
-
- devc->num_samples++;
-
- return SR_OK;
-}
-
-static void handle_new_data(struct sr_dev_inst *sdi, int idx)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- int len, i, offset = 0;
-
- devc = sdi->priv;
- serial = sdi->conn;
-
- /* Try to get as much data as the buffer can hold. */
- len = SERIAL_BUFSIZE - devc->buflen;
- len = serial_read(serial, devc->buf + devc->buflen, len);
- if (len < 1) {
- sr_err("Serial port read error: %d.", len);
- return;
- }
-
- devc->buflen += len;
-
- /* Now look for packets in that data. */
- while ((devc->buflen - offset) >= mic_devs[idx].packet_size) {
- if (mic_devs[idx].packet_valid(devc->buf + offset)) {
- handle_packet(devc->buf + offset, sdi, idx);
- offset += mic_devs[idx].packet_size;
- } else {
- offset++;
- }
- }
-
- /* If we have any data left, move it to the beginning of our buffer. */
- for (i = 0; i < devc->buflen - offset; i++)
- devc->buf[i] = devc->buf[offset + i];
- devc->buflen -= offset;
-}
-
-static int receive_data(int fd, int revents, int idx, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- int64_t t;
- static gboolean first_time = TRUE;
- struct sr_serial_dev_inst *serial;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- serial = sdi->conn;
-
- if (revents == G_IO_IN) {
- /* New data arrived. */
- handle_new_data(sdi, idx);
- } else {
- /* Timeout. */
- if (first_time) {
- mic_cmd_set_realtime_mode(serial);
- first_time = FALSE;
- }
- }
-
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
-
- if (devc->limit_msec) {
- t = (g_get_monotonic_time() - devc->starttime) / 1000;
- if (t > (int64_t)devc->limit_msec) {
- sr_info("Requested time limit reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
- }
-
- return TRUE;
-}
-
-#define RECEIVE_DATA(ID_UPPER) \
-SR_PRIV int receive_data_##ID_UPPER(int fd, int revents, void *cb_data) { \
- return receive_data(fd, revents, ID_UPPER, cb_data); }
-
-/* Driver-specific receive_data() wrappers */
-RECEIVE_DATA(MIC_98581)
-RECEIVE_DATA(MIC_98583)
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef LIBSIGROK_HARDWARE_MIC_985XX_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_MIC_985XX_PROTOCOL_H
-
-#include <stdint.h>
-#include <string.h>
-#include <ctype.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "mic-985xx"
-
-/* Note: When adding entries here, don't forget to update MIC_DEV_COUNT. */
-enum {
- MIC_98581,
- MIC_98583,
-};
-
-#define MIC_DEV_COUNT 2
-
-struct mic_dev_info {
- char *vendor;
- char *device;
- char *conn;
- uint32_t max_sample_points;
- gboolean has_temperature;
- gboolean has_humidity;
- uint8_t packet_size;
- gboolean (*packet_valid)(const uint8_t *);
- struct sr_dev_driver *di;
- int (*receive_data)(int, int, void *);
-};
-
-extern SR_PRIV const struct mic_dev_info mic_devs[MIC_DEV_COUNT];
-
-#define SERIAL_BUFSIZE 256
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /** The current sampling limit (in number of samples). */
- uint64_t limit_samples;
-
- /** The current sampling limit (in ms). */
- uint64_t limit_msec;
-
- /** Opaque pointer passed in by the frontend. */
- void *cb_data;
-
- /** The current number of already received samples. */
- uint64_t num_samples;
-
- int64_t starttime;
-
- uint8_t buf[SERIAL_BUFSIZE];
- int bufoffset;
- int buflen;
-};
-
-SR_PRIV gboolean packet_valid_temp(const uint8_t *buf);
-SR_PRIV gboolean packet_valid_temp_hum(const uint8_t *buf);
-
-SR_PRIV int receive_data_MIC_98581(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_MIC_98583(int fd, int revents, void *cb_data);
-
-SR_PRIV int mic_cmd_get_device_info(struct sr_serial_dev_inst *serial);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com> (code from atten-pps3xxx)
- *
- * 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/>.
- */
-
-/** @file
- * <em>Motech LPS-30x series</em> power supply driver
- * @internal
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <math.h>
-#include <string.h>
-
-#include "protocol.h"
-
-/* Forward declarations */
-SR_PRIV struct sr_dev_driver motech_lps_301_driver_info;
-SR_PRIV int lps_read_reply(struct sr_serial_dev_inst *serial, char **buf, int *buflen);
-SR_PRIV int lps_send_va(struct sr_serial_dev_inst *serial, const char* fmt, va_list args);
-SR_PRIV int lps_cmd_ok(struct sr_serial_dev_inst *serial, const char* fmt, ...);
-SR_PRIV int lps_cmd_reply(char* reply, struct sr_serial_dev_inst *serial, const char* fmt, ...);
-SR_PRIV int lps_query_status(struct sr_dev_inst* sdi);
-
-/* Serial communication parameters */
-#define SERIALCOMM "2400/8n1/dtr=1/rts=1/flow=0"
-
-#define VENDOR_MOTECH "Motech"
-
-/** Driver scanning options. */
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-/** Hardware capabilities generic. */
-static const int32_t hwcaps[] = {
- /* Device class */
- SR_CONF_POWER_SUPPLY,
- /* Aquisition modes. */
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
- /* Device configuration */
- SR_CONF_OUTPUT_CHANNEL,
-};
-
-/** Hardware capabilities channel 1, 2. */
-static const int32_t hwcaps_ch12[] = {
- SR_CONF_OUTPUT_VOLTAGE,
- SR_CONF_OUTPUT_VOLTAGE_MAX,
- SR_CONF_OUTPUT_CURRENT,
- SR_CONF_OUTPUT_CURRENT_MAX,
- SR_CONF_OUTPUT_ENABLED,
-};
-
-/** Hardware capabilities channel 3. (LPS-304/305 only). */
-static const int32_t hwcaps_ch3[] = {
- SR_CONF_OUTPUT_VOLTAGE,
- SR_CONF_OUTPUT_ENABLED,
-};
-
-static const char *channel_modes[] = {
- "Independent",
- "Track1",
- "Track2",
-};
-
-static struct lps_modelspec models[] = {
- { LPS_UNKNOWN, "Dummy", 0,
- {
-
- }
- },
- { LPS_301, "LPS-301", 1,
- {
- /* Channel 1 */
- { { 0, 32, 0.01 }, { 0.005, 2, 0.001 } },
- },
- },
- { LPS_302, "LPS-302", 1,
- {
- /* Channel 1 */
- { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
- },
- },
- { LPS_303, "LPS-303", 1,
- {
- /* Channel 1 */
- { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
- },
- },
- { LPS_304, "LPS-304", 3,
- {
- /* Channel 1 */
- { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
- /* Channel 2 */
- { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
- /* Channel 3 */
- { { 5, 5, 0.0 }, { 0.005, 3, 0.001 } },
- },
- },
- { LPS_305, "LPS-305", 3,
- {
- /* Channel 1 */
- { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
- /* Channel 2 */
- { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
- /* Channel 3 */
- { { 3.3, 5, 1.7 }, { 0.005, 3, 0.001 } },
- },
- },
-};
-
-static int init_lps301(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, &motech_lps_301_driver_info, LOG_PREFIX);
-}
-
-/** Send command to device with va_list.
- */
-SR_PRIV int lps_send_va(struct sr_serial_dev_inst *serial, const char* fmt, va_list args)
-{
- int retc;
- char auxfmt[LINELEN_MAX];
- char buf[LINELEN_MAX];
-
- snprintf(auxfmt, sizeof(auxfmt), "%s\r\n", fmt);
- vsnprintf(buf, sizeof(buf), auxfmt, args);
-
- sr_spew("lps_send_va: \"%s\"", buf);
-
- retc = serial_write_nonblocking(serial, buf, strlen(buf));
-
- if (retc < 0)
- return SR_ERR;
-
- return SR_OK;
-}
-
-/** Send command to device.
- */
-SR_PRIV int lps_send_req(struct sr_serial_dev_inst *serial, const char* fmt, ...)
-{
- int retc;
- va_list args;
-
- va_start(args, fmt);
- retc = lps_send_va(serial, fmt, args);
- va_end(args);
-
- return retc;
-}
-
-/** Send command and consume simple OK reply. */
-SR_PRIV int lps_cmd_ok(struct sr_serial_dev_inst *serial, const char* fmt, ...)
-{
- int retc;
- va_list args;
- char buf[LINELEN_MAX];
- char* bufptr;
- int buflen;
-
- /* Send command */
- va_start(args, fmt);
- retc = lps_send_va(serial, fmt, args);
- va_end(args);
-
- if (retc != SR_OK)
- return SR_ERR;
-
- /* Read reply */
- buf[0] = '\0';
- bufptr = buf;
- buflen = sizeof(buf);
- retc = lps_read_reply(serial, &bufptr, &buflen);
- if ((retc == SR_OK) && (buflen == 0))
- return SR_OK;
-
- return SR_ERR;
-}
-
-/** Send command and read reply string.
- * \param reply Pointer to buffer of size LINELEN_MAX. Will be NUL-terminated.
- */
-SR_PRIV int lps_cmd_reply(char* reply, struct sr_serial_dev_inst *serial, const char* fmt, ...)
-{
- int retc;
- va_list args;
- char buf[LINELEN_MAX];
- char* bufptr;
- int buflen;
-
- reply[0] = '\0';
-
- /* Send command */
- va_start(args, fmt);
- retc = lps_send_va(serial, fmt, args);
- va_end(args);
-
- if (retc != SR_OK)
- return SR_ERR;
-
- /* Read reply */
- buf[0] = '\0';
- bufptr = buf;
- buflen = sizeof(buf);
- retc = lps_read_reply(serial, &bufptr, &buflen);
- if ((retc == SR_OK) && (buflen > 0)) {
- strcpy(reply, buf);
- return SR_OK;
- }
-
- return SR_ERR;
-}
-
-/** Process integer value returned by STATUS command. */
-SR_PRIV int lps_process_status(struct sr_dev_inst* sdi, int stat)
-{
- struct dev_context* devc;
- int tracking_mode;
-
- devc = (struct dev_context*)sdi->priv;
-
- sr_spew("Status: %d", stat);
- devc->channel_status[0].cc_mode = (stat & 0x01) != 0;
- sr_spew("Channel 1 %s mode", devc->channel_status[0].cc_mode?"CC":"CV");
- if (devc->model->num_channels > 1) {
- devc->channel_status[1].cc_mode = (stat & 0x02) != 0;
- sr_spew("Channel 2 %s mode", devc->channel_status[1].cc_mode?"CC":"CV");
-
- tracking_mode = (stat & 0x0c) >> 2;
- switch (tracking_mode) {
- case 0: devc->tracking_mode = 0;
- break;
- case 2: devc->tracking_mode = 1;
- break;
- case 3: devc->tracking_mode = 2;
- break;
- default:
- sr_err("Illegal channel tracking mode %d!", tracking_mode);
- devc->tracking_mode = 0;
- break;
- }
-
- sr_spew("Channel tracking: %d", devc->tracking_mode);
- }
- devc->channel_status[0].output_enabled = devc->channel_status[1].output_enabled = stat&0x040?TRUE:FALSE;
- sr_spew("Channel 1%s output: %s", devc->model->num_channels > 1?"+2":"", devc->channel_status[0].output_enabled?"ON":"OFF");
- if (devc->model->num_channels > 2) {
- devc->channel_status[2].output_enabled = stat&0x010?TRUE:FALSE;
- devc->channel_status[2].output_voltage_last = stat&0x020?3.3:5;
- sr_spew("Channel 3 output: %s, U=%02f V, overload=%d",
- devc->channel_status[2].output_enabled?"ON":"OFF",
- devc->channel_status[2].output_voltage_last,
- stat&0x080?1:0);
- }
- sr_spew("Fan=%d, beep=%d, CC output compensated=%d", stat&0x0100?1:0, stat&0x0200?1:0, stat&0x0400?1:0);
-
- return SR_OK;
-}
-
-/** Send STATUS commend and process status string. */
-SR_PRIV int lps_query_status(struct sr_dev_inst* sdi)
-{
- char buf[LINELEN_MAX];
- int stat;
- struct dev_context* devc;
-
- devc = (struct dev_context*)sdi->priv;
-
- devc->req_sent_at = g_get_real_time();
-
- if (lps_cmd_reply(buf, sdi->conn, "STATUS") < 0) {
- sr_err("%s: Failed to read status: %d %s", __func__, errno, strerror(errno));
- return SR_ERR;
- }
-
- if (sr_atoi(buf, &stat) != SR_OK)
- return SR_ERR;
-
- return lps_process_status(sdi, stat);
-}
-
-static gint64 calc_timeout_ms(gint64 start_us)
-{
- gint64 result = REQ_TIMEOUT_MS - ((g_get_real_time() - start_us) / 1000);
-
- if (result < 0)
- return 0;
-
- return result;
-}
-
-/** Read message into buf until "OK" received.
- * \retval SR_OK Msg received; buf and buflen contain result, if any except OK.
- * \retval SR_ERR Error, including timeout.
-*/
-SR_PRIV int lps_read_reply(struct sr_serial_dev_inst *serial, char **buf, int *buflen)
-{
- int retries;
- char buf2[LINELEN_MAX];
- char *buf2ptr;
- int buf2len;
- gint64 timeout_start;
-
- *buf[0] = '\0';
-
- /* Read one line. It is either a data message or "OK". */
- timeout_start = g_get_real_time();
- buf2len = *buflen;
- /* Up to 5 tries because serial_readline() will consume only one CR or LF per
- * call, but device sends up to 4 in a row. */
- for (retries = 0; retries < 5; retries++) {
- *buflen = buf2len;
- if (serial_readline(serial, buf, buflen, calc_timeout_ms(timeout_start)) != SR_OK)
- return SR_ERR;
- if (!strcmp(*buf, "OK")) { /* We got an OK! */
- *buf[0] = '\0';
- *buflen = 0;
- return SR_OK;
- }
- if (*buflen > 0) /* We got a msg! */
- break;
- }
-
- /* A data msg is in buf (possibly ERROR), need to consume "OK". */
- buf2[0] = '\0';
- buf2ptr = buf2;
- for (retries = 0; retries < 5; retries++) {
- buf2len = sizeof(buf2);
- if (serial_readline(serial, &buf2ptr, &buf2len, calc_timeout_ms(timeout_start)) != SR_OK)
- return SR_ERR;
-
- if (!strcmp(buf2ptr, "OK")) { /* We got an OK! */
- if (!strcmp(*buf, "ERROR")) { /* OK came after msg ERROR! */
- sr_spew("ERROR found!");
- *buf[0] = '\0';
- *buflen = 0;
- return SR_ERR;
- }
- return SR_OK;
- }
- }
-
- return SR_ERR; /* Timeout! */
-}
-
-/** Scan for LPS-300 series device.
- */
-static GSList *do_scan(lps_modelid modelid, struct sr_dev_driver *drv, GSList *options)
-{
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- struct sr_channel *ch;
- struct sr_channel_group *cg;
- GSList *devices;
- const char *conn, *serialcomm;
- int cnt;
- gchar buf[LINELEN_MAX];
- gchar channel[10];
- char* verstr;
-
- sdi = NULL;
- devc = NULL;
- conn = serialcomm = NULL;
- devices = NULL;
-
- drvc = drv->priv;
- drvc->instances = NULL;
-
- sr_spew("scan() called!");
-
- /* Process and check options. */
- if (sr_serial_extract_options(options, &conn, &serialcomm) != SR_OK)
- return NULL;
- if (!serialcomm)
- serialcomm = SERIALCOMM;
-
- /* Init serial port. */
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- goto exit_err;
-
- /* Query and verify model string. */
- serial_flush(serial);
- if (lps_cmd_reply(buf, serial, "MODEL") != SR_OK)
- return NULL;
-
- /* Check model string. */
- if (strncmp(buf, "LPS-", 4)) {
- sr_spew("Unknown model code \"%s\"!", buf);
- return NULL;
- }
-
- /* Bug in device FW 1.17, model number is empty, so this can't work with this FW! */
- if (modelid == LPS_UNKNOWN) {
- g_strstrip(buf);
- for (cnt = LPS_301; cnt <= LPS_305; cnt++) {
- if (!strcmp(buf, models[cnt].modelstr)) {
- modelid = cnt;
- break;
- }
- }
- if (modelid == LPS_UNKNOWN) {
- sr_err("Unable to detect model from model string '%s'!", buf);
- return NULL;
- }
- }
-
- /* Query version */
- verstr = NULL;
- if (lps_cmd_reply(buf, serial, "VERSION") == SR_OK) {
- if (strncmp(buf, "Ver-", 4)) {
- sr_spew("Version string %s not recognized.", buf);
- goto exit_err;
- }
-
-
- g_strstrip(buf);
- verstr = buf + 4;
- }
- else /* Bug in device FW 1.17: Quering version string fails while output is active.
- Therefore just print an error message, but do not exit with error. */
- sr_err("Failed to query for hardware version: %d %s", errno, strerror(errno));
-
- sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR_MOTECH, models[modelid].modelstr, verstr);
- sdi->driver = drv;
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
-
- devc = g_malloc0(sizeof(struct dev_context));
- devc->model = &models[modelid];
- devc->limit_samples = 0;
- devc->limit_msec = 0;
- devc->num_samples = 0;
- devc->elapsed_msec = g_timer_new();
-
- sdi->priv = devc;
-
- /* Setup channels and channel groups. */
- for (cnt = 0; cnt < models[modelid].num_channels; cnt++) {
- snprintf(channel, sizeof(channel), "CH%d", cnt + 1);
- ch = sr_channel_new(cnt, SR_CHANNEL_ANALOG, TRUE, channel);
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- devc->channel_status[cnt].info = g_slist_append(NULL, ch);
-
- cg = g_malloc(sizeof(struct sr_channel_group));
- snprintf(channel, sizeof(channel), "CG%d", cnt+1);
- cg->name = g_strdup(channel);
- cg->priv = NULL;
- cg->channels = g_slist_append(NULL, ch);
-
- sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
- }
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- /* Query status */
- if (lps_query_status(sdi) != SR_OK)
- goto exit_err;
-
- serial_close(serial);
- if (!devices)
- sr_serial_dev_inst_free(serial);
-
- return devices;
-
-exit_err:
- sr_info("%s: Error!", __func__);
-
- if (serial) {
- serial_close(serial);
- sr_serial_dev_inst_free(serial);
- }
- if (devc)
- g_free(devc);
- if (sdi)
- sr_dev_inst_free(sdi);
-
- return NULL;
-}
-
-/** Scan for LPS-301 device. */
-static GSList *scan_lps301(GSList *options)
-{
- return do_scan(LPS_301, &motech_lps_301_driver_info, options);
-}
-
-static GSList *doDevList(struct sr_dev_driver *drv)
-{
- return ((struct drv_context *)(drv->priv))->instances;
-}
-
-static GSList *dev_list_lps301(void)
-{
- return doDevList(&motech_lps_301_driver_info);
-}
-
-static void dev_clear_private(struct dev_context* devc)
-{
- int ch_idx;
-
- /* Free channel_status.info (list only, data owned by sdi). */
- for (ch_idx = 0; ch_idx < devc->model->num_channels; ch_idx++)
- g_slist_free(devc->channel_status[ch_idx].info);
-
- g_timer_destroy(devc->elapsed_msec);
-}
-
-static int dev_clear_lps301(void)
-{
- return std_dev_clear(&motech_lps_301_driver_info, (std_dev_clear_callback)dev_clear_private);
-}
-
-static int cleanup(void)
-{
- return dev_clear_lps301();
-}
-
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- int ch_idx;
-
- if (!sdi)
- return SR_ERR_ARG;
-
- devc = sdi->priv;
-
- if (!cg) {
- /* No channel group: global options. */
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- *data = g_variant_new_uint64(devc->limit_msec);
- break;
- case SR_CONF_OUTPUT_CHANNEL:
- *data = g_variant_new_string(channel_modes[devc->tracking_mode]);
- break;
- default:
- return SR_ERR_NA;
- }
- } else {
- /* We only ever have one channel per channel group in this driver. */
- ch = cg->channels->data;
- ch_idx = ch->index;
- switch (key) {
- case SR_CONF_OUTPUT_VOLTAGE:
- *data = g_variant_new_double(devc->channel_status[ch_idx].output_voltage_last);
- break;
- case SR_CONF_OUTPUT_VOLTAGE_MAX:
- *data = g_variant_new_double(devc->channel_status[ch_idx].output_voltage_max);
- break;
- case SR_CONF_OUTPUT_CURRENT:
- *data = g_variant_new_double(devc->channel_status[ch_idx].output_current_last);
- break;
- case SR_CONF_OUTPUT_CURRENT_MAX:
- *data = g_variant_new_double(devc->channel_status[ch_idx].output_current_max);
- break;
- case SR_CONF_OUTPUT_ENABLED:
- *data = g_variant_new_boolean(devc->channel_status[ch_idx].output_enabled);
- break;
- default:
- return SR_ERR_NA;
- }
- }
-
- return SR_OK;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- gdouble dval;
- int ch_idx;
- const char *sval;
- gboolean bval;
- int idx;
- gboolean found;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- /* Cannot change settings while acquisition active, would cause a mess with commands.
- * Changing this would be possible, but tricky. */
- if (devc->acq_running)
- return SR_ERR_NA;
-
- if (!cg) {
- /* No channel group: global options. */
- switch (key) {
- case SR_CONF_LIMIT_MSEC:
- if (g_variant_get_uint64(data) == 0) {
- sr_err("LIMIT_MSEC can't be 0.");
- return SR_ERR;
- }
- devc->limit_msec = g_variant_get_uint64(data);
- sr_dbg("Setting time limit to %" PRIu64 "ms.",
- devc->limit_msec);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- case SR_CONF_OUTPUT_CHANNEL:
- sval = g_variant_get_string(data, NULL);
- found = FALSE;
- for (idx = 0; idx < (int)ARRAY_SIZE(channel_modes); idx++)
- {
- if (!strcmp(sval, channel_modes[idx])) {
- found = TRUE;
- if (devc->tracking_mode == idx)
- break; /* Nothing to do! */
- devc->tracking_mode = idx;
- if (devc->model->modelid >= LPS_304) /* No use to set anything in the smaller models. */
- return lps_cmd_ok(sdi->conn, "TRACK%1d", devc->tracking_mode);
- }
- if (devc->model->modelid <= LPS_303) /* Only first setting possible for smaller models. */
- break;
- }
- if (!found) {
- return SR_ERR_ARG;
- }
- break;
- default:
- return SR_ERR_NA;
- }
- } else {
- /* Channel group specified: per-channel options. */
- /* We only ever have one channel per channel group in this driver. */
- ch = cg->channels->data;
- ch_idx = ch->index;
-
- switch (key) {
- case SR_CONF_OUTPUT_VOLTAGE_MAX:
- dval = g_variant_get_double(data);
- if (dval < 0 || dval > devc->model->channels[ch_idx].voltage[1])
- return SR_ERR_ARG;
- if (ch_idx == 2) {
- if (devc->model->modelid < LPS_304)
- return SR_ERR_ARG;
-
- if (fabs(dval - 5.000) <= 0.001)
- dval = 5.0;
- else if ((devc->model->modelid >= LPS_305) && (fabs(dval - 3.300) <= 0.001))
- dval = 3.3;
- else return SR_ERR_ARG;
- }
-
- devc->channel_status[ch_idx].output_voltage_max = dval;
- if (ch_idx == 2)
- return lps_cmd_ok(sdi->conn, "VDD%1.0f", trunc(dval));
- else
- return lps_cmd_ok(sdi->conn, "VSET%d %05.3f", ch_idx+1, dval);
- break;
- case SR_CONF_OUTPUT_CURRENT_MAX:
- dval = g_variant_get_double(data);
- if (dval < 0 || dval > devc->model->channels[ch_idx].current[1])
- return SR_ERR_ARG;
- if (ch_idx == 2) /* No current setting for CH3. */
- return SR_ERR_NA;
- devc->channel_status[ch_idx].output_current_max = dval;
- return lps_cmd_ok(sdi->conn, "ISET%d %05.4f", ch_idx+1, dval);
- break;
- case SR_CONF_OUTPUT_ENABLED:
- bval = g_variant_get_boolean(data);
- if (bval == devc->channel_status[ch_idx].output_enabled) /* Nothing to do. */
- break;
- devc->channel_status[ch_idx].output_enabled = bval;
- if (ch_idx != 2) { /* Channels 1,2 can be set only together. */
- devc->channel_status[ch_idx^1].output_enabled = bval;
- return lps_cmd_ok(sdi->conn, "OUT%1d", (int)bval);
- } else { /* Channel 3: No command to disable output, set voltage to 0 instead. */
- if (bval)
- return lps_cmd_ok(sdi->conn, "VDD%1.0f", devc->channel_status[ch_idx].output_voltage_max);
- else
- return lps_cmd_ok(sdi->conn, "VDD0");
- }
- break;
- default:
- return SR_ERR_NA;
- }
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- int ch_idx, i;
- GVariant *gvar;
- GVariantBuilder gvb;
-
- (void)data;
-
- /* Driver options, no device instance necessary. */
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- return SR_OK;
- default:
- if (sdi == NULL)
- return SR_ERR_ARG;
-
- devc = sdi->priv;
- }
-
- /* Device options, independant from channel groups. */
- if (cg == NULL) {
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- return SR_OK;
- case SR_CONF_OUTPUT_CHANNEL:
- if (devc->model->modelid <= LPS_303) {
- /* The 1-channel models. */
- *data = g_variant_new_strv(channel_modes, 1);
- } else {
- /* The other models support all modes. */
- *data = g_variant_new_strv(channel_modes, ARRAY_SIZE(channel_modes));
- }
- return SR_OK;
- break;
- default:
- return SR_ERR_NA;
- }
- }
-
- /* Device options, depending on channel groups. */
- ch = cg->channels->data;
- ch_idx = ch->index;
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- if ((ch_idx == 0) || (ch_idx == 1)) /* CH1, CH2 */
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps_ch12, ARRAY_SIZE(hwcaps_ch12), sizeof(int32_t));
- else /* Must be CH3 */
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps_ch3, ARRAY_SIZE(hwcaps_ch3), sizeof(int32_t));
- break;
- case SR_CONF_OUTPUT_VOLTAGE_MAX:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- /* Min, max, step. */
- for (i = 0; i < 3; i++) {
- gvar = g_variant_new_double(devc->model->channels[ch_idx].voltage[i]);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_OUTPUT_CURRENT_MAX:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- /* Min, max, step. */
- for (i = 0; i < 3; i++) {
- gvar = g_variant_new_double(devc->model->channels[ch_idx].current[i]);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- devc->acq_running = TRUE;
-
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 50,
- motech_lps_30x_receive_data, (void *)sdi);
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Start timer, if required. */
- if (devc->limit_msec)
- g_timer_start(devc->elapsed_msec);
-
- devc->acq_req = AQ_NONE;
- /* Do not start polling device here, the read function will do it in 50 ms. */
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
-
- /* Stop timer, if required. */
- if (sdi && (devc = sdi->priv) && devc->limit_msec)
- g_timer_stop(devc->elapsed_msec);
-
- return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
- sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver motech_lps_301_driver_info = {
- .name = "motech-lps-301",
- .longname = "Motech LPS-301",
- .api_version = 1,
- .init = init_lps301,
- .cleanup = cleanup,
- .scan = scan_lps301,
- .dev_list = dev_list_lps301,
- .dev_clear = dev_clear_lps301,
- .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 = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com> (code from atten-pps3xxx)
- *
- * 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/>.
- */
-
-/** @file
- * <em>Motech LPS-30x series</em> power supply driver
- * @internal
- */
-
-#include <errno.h>
-#include <string.h>
-
-#include "protocol.h"
-
-/** Send data packets for current measurements. */
-static void send_data(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- int i;
- float data[MAX_CHANNELS];
-
- devc = sdi->priv;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- analog.channels = sdi->channels;
- analog.num_samples = 1;
-
- analog.mq = SR_MQ_VOLTAGE;
- analog.unit = SR_UNIT_VOLT;
- analog.mqflags = SR_MQFLAG_DC;
- analog.data = data;
- for (i = 0; i < devc->model->num_channels; i++)
- analog.data[i] = devc->channel_status[i].output_voltage_last; /* Value always 3.3 or 5 for channel 3, if present! */
- sr_session_send(sdi, &packet);
-
- analog.mq = SR_MQ_CURRENT;
- analog.unit = SR_UNIT_AMPERE;
- analog.mqflags = 0;
- analog.data = data;
- for (i = 0; i < devc->model->num_channels; i++)
- analog.data[i] = devc->channel_status[i].output_current_last; /* Value always 0 for channel 3, if present! */
- sr_session_send(sdi, &packet);
-
- devc->num_samples++;
-}
-
-/** Process a complete line (without CR/LF) in buf. */
-static void process_line(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- double dbl;
- int auxint;
-
- devc = sdi->priv;
-
- switch (devc->acq_req_pending) {
- case 0: /* Should not happen... */
- break;
- case 1: /* Waiting for data reply to request */
- /* Convert numbers */
- switch (devc->acq_req) {
- case AQ_U1: case AQ_U2: case AQ_I1: case AQ_I2:
- if (sr_atod(devc->buf, &dbl) != SR_OK) {
- sr_err("Failed to convert '%s' to double, errno=%d %s",
- devc->buf, errno, strerror(errno));
- dbl = 0.0;
- }
- break;
- case AQ_STATUS:
- if (sr_atoi(devc->buf, &auxint) != SR_OK) {
- sr_err("Failed to convert '%s' to int, errno=%d %s",
- devc->buf, errno, strerror(errno));
- auxint = 0;
- }
- break;
- default:
- break;
- }
-
- switch (devc->acq_req) {
- case AQ_U1:
- devc->channel_status[0].output_voltage_last = dbl;
- break;
- case AQ_I1:
- devc->channel_status[0].output_current_last = dbl;
- break;
- case AQ_U2:
- devc->channel_status[1].output_voltage_last = dbl;
- break;
- case AQ_I2:
- devc->channel_status[1].output_current_last = dbl;
- break;
- case AQ_STATUS: /* Process status and generate data. */
- if (lps_process_status(sdi, auxint) == SR_OK) {
- send_data(sdi);
- }
- break;
- default:
- break;
- }
-
- devc->acq_req_pending = 2;
- break;
- case 2: /* Waiting for OK after request */
- if (strcmp(devc->buf, "OK")) {
- sr_err("Unexpected reply while waiting for OK: '%s'", devc->buf);
- }
- devc->acq_req_pending = 0;
- break;
- }
-
- devc->buf[0] = '\0';
- devc->buflen = 0;
-}
-
-
-SR_PRIV int motech_lps_30x_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- int len;
- gdouble elapsed_s;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- serial = sdi->conn;
-
- if (revents == G_IO_IN) { /* Serial data arrived. */
- while (LINELEN_MAX - devc->buflen - 2 > 0) {
- len = serial_read(serial, devc->buf + devc->buflen, 1);
- if (len < 1)
- break;
-
- /* Eliminate whitespace at beginning of line. */
- if (g_ascii_isspace(devc->buf[0])) {
- devc->buf[0] = '\0';
- devc->buflen = 0;
- continue;
- }
-
- devc->buflen += len;
- devc->buf[devc->buflen] = '\0';
-
- /* If line complete, process msg. */
- if ((devc->buflen > 0) && ((devc->buf[devc->buflen-1] == '\r') || devc->buf[devc->buflen-1] == '\n')) {
- devc->buflen--;
- devc->buf[devc->buflen] = '\0';
-
- sr_spew("Line complete: \"%s\"", devc->buf);
- process_line(sdi);
- }
- }
- }
-
- /* If number of samples or time limit reached, stop acquisition. */
- if (devc->limit_samples && (devc->num_samples >= devc->limit_samples))
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
-
- if (devc->limit_msec) {
- elapsed_s = g_timer_elapsed(devc->elapsed_msec, NULL);
- if ((elapsed_s * 1000) >= devc->limit_msec)
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- }
-
- /* Request next packet, if required. */
- if ((sdi->status == SR_ST_ACTIVE) && (devc->acq_running)){
- if (devc->acq_req_pending) {
- gint64 elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
- if (elapsed_us > (REQ_TIMEOUT_MS * 1000)) {
- sr_spew("Request timeout: req=%d t=%lldus", (int)devc->acq_req, elapsed_us);
- devc->acq_req_pending = 0;
- }
- }
- if (devc->acq_req_pending == 0) {
- switch(devc->acq_req)
- {
- case AQ_NONE: /* Fall through */
- case AQ_STATUS:
- devc->acq_req = AQ_U1;
- lps_send_req(serial, "VOUT1");
- break;
- case AQ_U1:
- devc->acq_req = AQ_I1;
- lps_send_req(serial, "IOUT1");
- break;
- case AQ_I1:
- if (devc->model->num_channels == 1) {
- devc->acq_req = AQ_STATUS;
- lps_send_req(serial, "STATUS");
- } else {
- devc->acq_req = AQ_U2;
- lps_send_req(serial, "VOUT2");
- }
- break;
- case AQ_U2:
- devc->acq_req = AQ_I2;
- lps_send_req(serial, "IOUT2");
- break;
- case AQ_I2:
- devc->acq_req = AQ_STATUS;
- lps_send_req(serial, "STATUS");
- break;
- default:
- sr_err("Illegal devc->acq_req=%d", devc->acq_req);
- return SR_ERR;
- }
- devc->req_sent_at = g_get_real_time();
- devc->acq_req_pending = 1;
- }
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com> (code from atten-pps3xxx)
- *
- * 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/>.
- */
-
-/** @file
- * <em>Motech LPS-30x series</em> power supply driver
- * @internal
- */
-
-
-#ifndef LIBSIGROK_HARDWARE_MOTECH_LPS_30X_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_MOTECH_LPS_30X_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-
-SR_PRIV int lps_process_status(struct sr_dev_inst* sdi, int stat);
-SR_PRIV int lps_send_req(struct sr_serial_dev_inst *serial, const char* fmt, ...);
-
-#define LOG_PREFIX "motech-lps-30x"
-
-#define LINELEN_MAX 50 /**< Max. line length for requests */
-
-#define REQ_TIMEOUT_MS 250 /**< Timeout [ms] for single request. */
-
-#define MAX_CHANNELS 3
-
-typedef enum {
- LPS_UNKNOWN = 0,/**< Unknown model (used during detection process) */
- LPS_301, /**< Motech/Amrel LPS-301, 1 output */
- LPS_302, /**< Motech/Amrel LPS-302, 1 output */
- LPS_303, /**< Motech/Amrel LPS-303, 1 output */
- LPS_304, /**< Motech/Amrel LPS-304, 3 outputs */
- LPS_305, /**< Motech/Amrel LPS-305, 3 outputs */
-} lps_modelid;
-
-/** Channel specification */
-struct channel_spec {
- /* Min, max, step. */
- gdouble voltage[3];
- gdouble current[3];
-};
-
-/** Model properties specification */
-struct lps_modelspec {
- lps_modelid modelid;
- const char* modelstr;
- uint8_t num_channels;
- struct channel_spec channels[3];
-};
-
-/** Used to implement a little state machine to query all required values in a row. */
-typedef enum {
- AQ_NONE,
- AQ_U1,
- AQ_I1,
- AQ_I2,
- AQ_U2,
- AQ_STATUS,
-} acquisition_req;
-
-/** Status of a single channel. */
-struct channel_status {
- /* Channel information (struct channel_info*). data (struct) owned by sdi, just a reference to address a single channel. */
- GSList* info;
- /* Received from device. */
- gdouble output_voltage_last;
- gdouble output_current_last;
- gboolean output_enabled; /**< Also used when set. */
- gboolean cc_mode; /**< Constant current mode. If false, constant voltage mode. */
- /* Set by frontend. */
- gdouble output_voltage_max;
- gdouble output_current_max;
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Model-specific information */
- struct lps_modelspec* model;
-
- /* Acquisition status */
- gboolean acq_running; /**< Aquisition is running. */
- uint64_t limit_samples; /**< Target number of samples */
- uint64_t limit_msec; /**< Target sampling time */
- acquisition_req acq_req; /**< Current request. */
- uint8_t acq_req_pending; /**< Request pending. 0=none, 1=reply, 2=OK */
-
- /* Operational state */
- struct channel_status channel_status[MAX_CHANNELS];
- guint8 tracking_mode; /**< 0=off, 1=Tracking from CH1, 2=Tracking from CH2. */
-
- /* Temporary state across callbacks */
- int64_t req_sent_at; /**< Request sent. */
- uint64_t num_samples; /**< Current #samples for limit_samples */
- GTimer *elapsed_msec; /**< Used for sampling with limit_msec */
- gchar buf[LINELEN_MAX]; /**< Buffer for read callback */
- int buflen; /**< Data len in buf */
-};
-
-SR_PRIV int motech_lps_30x_receive_data(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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/>.
- */
-
-/** @file
- * Norma DM9x0/Siemens B102x DMMs driver.
- * @internal
- */
-
-#include "protocol.h"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
-};
-
-#define BUF_MAX 50
-
-#define SERIALCOMM "4800/8n1/dtr=1/rts=0/flow=1"
-
-SR_PRIV struct sr_dev_driver norma_dmm_driver_info;
-SR_PRIV struct sr_dev_driver siemens_b102x_driver_info;
-
-static const char* get_brandstr(struct sr_dev_driver* drv)
-{
- if (drv == &norma_dmm_driver_info)
- return "Norma";
- else
- return "Siemens";
-}
-
-static const char* get_typestr(int type, struct sr_dev_driver* drv)
-{
- static const char* nameref[5][2] = {
- {"DM910", "B1024"},
- {"DM920", "B1025"},
- {"DM930", "B1026"},
- {"DM940", "B1027"},
- {"DM950", "B1028"}};
-
- if ((type < 1) || (type > 5))
- return "Unknown type!";
-
- return nameref[type-1][(drv == &siemens_b102x_driver_info)];
-}
-
-static int init_norma_dmm(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, &norma_dmm_driver_info, LOG_PREFIX);
-}
-
-static int init_siemens_b102x(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, &siemens_b102x_driver_info, LOG_PREFIX);
-}
-
-static GSList *do_scan(struct sr_dev_driver* drv, GSList *options)
-{
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_config *src;
- struct sr_channel *ch;
- struct sr_serial_dev_inst *serial;
- GSList *l, *devices;
- int len, cnt;
- const char *conn, *serialcomm;
- char *buf;
- char req[10];
- int auxtype;
-
- devices = NULL;
- drvc = drv->priv;
- drvc->instances = NULL;
- 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 (!conn)
- return NULL;
- if (!serialcomm)
- serialcomm = SERIALCOMM;
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- serial_flush(serial);
-
- if (!(buf = g_try_malloc(BUF_MAX))) {
- sr_err("Serial buffer malloc failed.");
- return NULL;
- }
-
- snprintf(req, sizeof(req), "%s\r\n",
- nmadmm_requests[NMADMM_REQ_IDN].req_str);
- g_usleep(150 * 1000); /* Wait a little to allow serial port to settle. */
- for (cnt = 0; cnt < 7; cnt++) {
- if (serial_write(serial, req, strlen(req)) == -1) {
- sr_err("Unable to send identification request: %d %s.",
- errno, strerror(errno));
- return NULL;
- }
- len = BUF_MAX;
- serial_readline(serial, &buf, &len, NMADMM_TIMEOUT_MS);
- if (!len)
- continue;
- buf[BUF_MAX - 1] = '\0';
-
- /* Match ID string, e.g. "1834 065 V1.06,IF V1.02" (DM950) */
- if (g_regex_match_simple("^1834 [^,]*,IF V*", (char *)buf, 0, 0)) {
- auxtype = xgittoint(buf[7]);
- sr_spew("%s %s DMM %s detected!", get_brandstr(drv), get_typestr(auxtype, drv), buf + 9);
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
- get_brandstr(drv), get_typestr(auxtype, drv), buf + 9)))
- return NULL;
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
- devc->type = auxtype;
- devc->version = g_strdup(&buf[9]);
- devc->elapsed_msec = g_timer_new();
-
- sdi->conn = serial;
- sdi->priv = devc;
- sdi->driver = drv;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE,
- "P1")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- break;
- }
-
- /*
- * The interface of the DM9x0 contains a cap that needs to
- * charge for up to 10s before the interface works, if not
- * powered externally. Therefore wait a little to improve
- * chances.
- */
- if (cnt == 3) {
- sr_info("Waiting 5s to allow interface to settle.");
- g_usleep(5 * 1000 * 1000);
- }
- }
-
- g_free(buf);
-
- serial_close(serial);
- if (!devices)
- sr_serial_dev_inst_free(serial);
-
- return devices;
-}
-
-static GSList *scan_norma_dmm(GSList *options)
-{
- return do_scan(&norma_dmm_driver_info, options);
-}
-
-static GSList *scan_siemens_b102x(GSList *options)
-{
- return do_scan(&siemens_b102x_driver_info, options);
-}
-
-static GSList *dev_list_norma_dmm(void)
-{
- return ((struct drv_context *)(norma_dmm_driver_info.priv))->instances;
-}
-
-static GSList *dev_list_siemens_b102x(void)
-{
- return ((struct drv_context *)(siemens_b102x_driver_info.priv))->instances;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
-
- std_serial_dev_close(sdi);
-
- /* Free dynamically allocated resources. */
- if ((devc = sdi->priv) && devc->version) {
- g_free(devc->version);
- devc->version = NULL;
- g_timer_destroy(devc->elapsed_msec);
- }
-
- return SR_OK;
-}
-
-static int cleanup_norma_dmm(void)
-{
- return std_dev_clear(&norma_dmm_driver_info, NULL);
-}
-
-static int cleanup_siemens_b102x(void)
-{
- return std_dev_clear(&siemens_b102x_driver_info, NULL);
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- switch (key) {
- case SR_CONF_LIMIT_MSEC:
- if (g_variant_get_uint64(data) == 0) {
- sr_err("LIMIT_MSEC can't be 0.");
- return SR_ERR;
- }
- devc->limit_msec = g_variant_get_uint64(data);
- sr_dbg("Setting time limit to %" PRIu64 "ms.",
- devc->limit_msec);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (!sdi || !cb_data || !(devc = sdi->priv))
- return SR_ERR_BUG;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc->cb_data = cb_data;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Start timer, if required. */
- if (devc->limit_msec)
- g_timer_start(devc->elapsed_msec);
-
- /* Poll every 100ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 100,
- norma_dmm_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
-
- /* Stop timer, if required. */
- if (sdi && (devc = sdi->priv) && devc->limit_msec)
- g_timer_stop(devc->elapsed_msec);
-
- return std_serial_dev_acquisition_stop(sdi, cb_data, dev_close,
- sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver norma_dmm_driver_info = {
- .name = "norma-dmm",
- .longname = "Norma DM9x0 DMMs",
- .api_version = 1,
- .init = init_norma_dmm,
- .cleanup = cleanup_norma_dmm,
- .scan = scan_norma_dmm,
- .dev_list = dev_list_norma_dmm,
- .dev_clear = NULL,
- .config_get = NULL,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = std_serial_dev_open,
- .dev_close = dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
-
-
-SR_PRIV struct sr_dev_driver siemens_b102x_driver_info = {
- .name = "siemens-b102x",
- .longname = "Siemens B102x DMMs",
- .api_version = 1,
- .init = init_siemens_b102x,
- .cleanup = cleanup_siemens_b102x,
- .scan = scan_siemens_b102x,
- .dev_list = dev_list_siemens_b102x,
- .dev_clear = NULL,
- .config_get = NULL,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = std_serial_dev_open,
- .dev_close = dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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/>.
- */
-
-/** @file
- * Norma DM9x0/Siemens B102x DMMs driver.
- * @internal
- */
-
-#include "protocol.h"
-
-SR_PRIV const struct nmadmm_req nmadmm_requests[] = {
- { NMADMM_REQ_IDN, "IDN?" },
- { NMADMM_REQ_IDN, "STATUS?" },
- { 0, NULL },
-};
-
-static int nma_send_req(const struct sr_dev_inst *sdi, int req, char *params)
-{
- struct sr_serial_dev_inst *serial;
- struct dev_context *devc;
- char buf[NMADMM_BUFSIZE];
- int len;
-
- if (!sdi || !(serial = sdi->conn) || !(devc = sdi->priv))
- return SR_ERR_BUG;
-
- len = snprintf(buf, sizeof(buf), "%s%s\r\n",
- nmadmm_requests[req].req_str, params ? params : "");
-
- sr_spew("Sending request: '%s'.", buf);
-
- devc->last_req = req;
- devc->last_req_pending = TRUE;
-
- if (serial_write(serial, buf, len) == -1) {
- sr_err("Unable to send request: %d %s.",
- errno, strerror(errno));
- devc->last_req_pending = FALSE;
- return SR_ERR;
- }
-
- devc->req_sent_at = g_get_monotonic_time();
-
- return SR_OK;
-}
-
-/**
- * Convert hexadecimal digit to int.
- *
- * @param[in] xgit Hexadecimal digit to convert.
- * @return Int value of xgit (0 on invalid xgit).
- */
-SR_PRIV int xgittoint(char xgit)
-{
- if ((xgit >= '0') && (xgit <= '9'))
- return xgit - '0';
- xgit = tolower(xgit);
- if ((xgit >= 'a') && (xgit <= 'f'))
- return xgit - 'a';
- return 0;
-}
-
-/**
- * Process received line. It consists of 20 hex digits + \\r\\n,
- * e.g. '08100400018100400000'.
- */
-static void nma_process_line(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int pos, flags;
- int vt, range; /* Measurement value type, range in device format */
- int mmode, devstat; /* Measuring mode, device status */
- float value; /* Measured value */
- float scale; /* Scaling factor depending on range and function */
- struct sr_datafeed_analog analog;
- struct sr_datafeed_packet packet;
-
- devc = sdi->priv;
-
- devc->buf[20] = '\0';
-
- sr_spew("Received line '%s'.", devc->buf);
-
- /* Check line. */
- if (strlen((const char *)devc->buf) != 20) {
- sr_err("line: Invalid status '%s', must be 20 hex digits.",
- devc->buf);
- devc->buflen = 0;
- return;
- }
-
- for (pos = 0; pos < 20; pos++) {
- if (!isxdigit(devc->buf[pos])) {
- sr_err("line: Expected hex digit in '%s' at pos %d!",
- devc->buf, pos);
- devc->buflen = 0;
- return;
- }
- }
-
- /* Start decoding. */
- value = 0.0;
- scale = 1.0;
- memset(&analog, 0, sizeof(analog));
-
- /*
- * The numbers are hex digits, starting from 0.
- * 0: Keyboard status, currently not interesting.
- * 1: Central switch status, currently not interesting.
- * 2: Type of measured value.
- */
- vt = xgittoint(devc->buf[2]);
- switch (vt) {
- case 0:
- analog.mq = SR_MQ_VOLTAGE;
- break;
- case 1:
- analog.mq = SR_MQ_CURRENT; /* 2A */
- break;
- case 2:
- analog.mq = SR_MQ_RESISTANCE;
- break;
- case 3:
- analog.mq = SR_MQ_CAPACITANCE;
- break;
- case 4:
- analog.mq = SR_MQ_TEMPERATURE;
- break;
- case 5:
- analog.mq = SR_MQ_FREQUENCY;
- break;
- case 6:
- analog.mq = SR_MQ_CURRENT; /* 10A */
- break;
- case 7:
- analog.mq = SR_MQ_GAIN; /* TODO: Scale factor */
- break;
- case 8:
- analog.mq = SR_MQ_GAIN; /* Percentage */
- scale /= 100.0;
- break;
- case 9:
- analog.mq = SR_MQ_GAIN; /* dB */
- scale /= 100.0;
- break;
- default:
- sr_err("Unknown value type: 0x%02x.", vt);
- break;
- }
-
- /* 3: Measurement range for measured value */
- range = xgittoint(devc->buf[3]);
- switch (vt) {
- case 0: /* V */
- scale *= pow(10.0, range - 5);
- break;
- case 1: /* A */
- scale *= pow(10.0, range - 7);
- break;
- case 2: /* Ω */
- scale *= pow(10.0, range - 2);
- break;
- case 3: /* F */
- scale *= pow(10.0, range - 12);
- break;
- case 4: /* °C */
- scale *= pow(10.0, range - 1);
- break;
- case 5: /* Hz */
- scale *= pow(10.0, range - 2);
- break;
- // No default, other value types have fixed display format.
- }
-
- /* 5: Sign and 1st digit */
- flags = xgittoint(devc->buf[5]);
- value = (flags & 0x03);
- if (flags & 0x04)
- scale *= -1;
-
- /* 6-9: 2nd-4th digit */
- for (pos = 6; pos < 10; pos++)
- value = value * 10 + xgittoint(devc->buf[pos]);
- value *= scale;
-
- /* 10: Display counter */
- mmode = xgittoint(devc->buf[10]);
- switch (mmode) {
- case 0: /* Frequency */
- analog.unit = SR_UNIT_HERTZ;
- break;
- case 1: /* V TRMS, only type 5 */
- analog.unit = SR_UNIT_VOLT;
- analog.mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS);
- break;
- case 2: /* V AC */
- analog.unit = SR_UNIT_VOLT;
- analog.mqflags |= SR_MQFLAG_AC;
- if (devc->type >= 3)
- analog.mqflags |= SR_MQFLAG_RMS;
- break;
- case 3: /* V DC */
- analog.unit = SR_UNIT_VOLT;
- analog.mqflags |= SR_MQFLAG_DC;
- break;
- case 4: /* Ohm */
- analog.unit = SR_UNIT_OHM;
- break;
- case 5: /* Continuity */
- analog.unit = SR_UNIT_BOOLEAN;
- analog.mq = SR_MQ_CONTINUITY;
- /* TODO: Continuity handling is a bit odd in libsigrok. */
- break;
- case 6: /* Degree Celsius */
- analog.unit = SR_UNIT_CELSIUS;
- break;
- case 7: /* Capacity */
- analog.unit = SR_UNIT_FARAD;
- break;
- case 8: /* Current DC */
- analog.unit = SR_UNIT_AMPERE;
- analog.mqflags |= SR_MQFLAG_DC;
- break;
- case 9: /* Current AC */
- analog.unit = SR_UNIT_AMPERE;
- analog.mqflags |= SR_MQFLAG_AC;
- if (devc->type >= 3)
- analog.mqflags |= SR_MQFLAG_RMS;
- break;
- case 0xa: /* Current TRMS, only type 5 */
- analog.unit = SR_UNIT_AMPERE;
- analog.mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS);
- break;
- case 0xb: /* Diode */
- analog.unit = SR_UNIT_VOLT;
- analog.mqflags |= (SR_MQFLAG_DIODE | SR_MQFLAG_DC);
- break;
- default:
- sr_err("Unknown mmode: 0x%02x.", mmode);
- break;
- }
-
- /* 11: Device status */
- devstat = xgittoint(devc->buf[11]);
-
- switch (devstat) {
- case 1: /* Normal measurement */
- break;
- case 2: /* Input loop (limit, reference values) */
- break;
- case 3: /* TRANS/SENS */
- break;
- case 4: /* Error */
- sr_err("Device error. Fuse?"); /* TODO: Really abort? */
- devc->buflen = 0;
- return; /* Cannot continue. */
- default:
- sr_err("Unknown device status: 0x%02x", devstat);
- break;
- }
-
- /* 12-19: Flags and display symbols */
- /* 12, 13 */
- flags = (xgittoint(devc->buf[12]) << 8) | xgittoint(devc->buf[13]);
- /* 0x80: PRINT TODO: Stop polling when discovered? */
- /* 0x40: EXTR */
- if (analog.mq == SR_MQ_CONTINUITY) {
- if (flags & 0x20)
- value = 1.0; /* Beep */
- else
- value = 0.0;
- }
- /* 0x10: AVG */
- /* 0x08: Diode */
- if (flags & 0x04) /* REL */
- analog.mqflags |= SR_MQFLAG_RELATIVE;
- /* 0x02: SHIFT */
- if (flags & 0x01) /* % */
- analog.unit = SR_UNIT_PERCENTAGE;
-
- /* 14, 15 */
- flags = (xgittoint(devc->buf[14]) << 8) | xgittoint(devc->buf[15]);
- if (!(flags & 0x80)) /* MAN: Manual range */
- analog.mqflags |= SR_MQFLAG_AUTORANGE;
- if (flags & 0x40) /* LOBATT1: Low battery, measurement still within specs */
- devc->lowbatt = 1;
- /* 0x20: PEAK */
- /* 0x10: COUNT */
- if (flags & 0x08) /* HOLD */
- analog.mqflags |= SR_MQFLAG_HOLD;
- /* 0x04: LIMIT */
- if (flags & 0x02) /* MAX */
- analog.mqflags |= SR_MQFLAG_MAX;
- if (flags & 0x01) /* MIN */
- analog.mqflags |= SR_MQFLAG_MIN;
-
- /* 16, 17 */
- flags = (xgittoint(devc->buf[16]) << 8) | xgittoint(devc->buf[17]);
- /* 0xe0: undefined */
- if (flags & 0x10) { /* LOBATT2: Low battery, measurement inaccurate */
- devc->lowbatt = 2;
- sr_warn("Low battery, measurement quality degraded!");
- }
- /* 0x08: SCALED */
- /* 0x04: RATE (=lower resolution, allows higher rata rate up to 10/s. */
- /* 0x02: Current clamp */
- if (flags & 0x01) { /* dB */
- /*
- * TODO: The Norma has an adjustable dB reference value. If
- * changed from default, this is not correct.
- */
- if (analog.unit == SR_UNIT_VOLT)
- analog.unit = SR_UNIT_DECIBEL_VOLT;
- else
- analog.unit = SR_UNIT_UNITLESS;
- }
-
- /* 18, 19 */
- /* flags = (xgittoint(devc->buf[18]) << 8) | xgittoint(devc->buf[19]); */
- /* 0x80: Undefined. */
- /* 0x40: Remote mode, keyboard locked */
- /* 0x38: Undefined. */
- /* 0x04: MIN > MAX */
- /* 0x02: Measured value < Min */
- /* 0x01: Measured value > Max */
-
- /* 4: Flags. Evaluating this after setting value! */
- flags = xgittoint(devc->buf[4]);
- if (flags & 0x04) /* Invalid value */
- value = NAN;
- else if (flags & 0x01) /* Overload */
- value = INFINITY;
- if (flags & 0x02) { /* Duplicate value, has been sent before. */
- sr_spew("Duplicate value, dismissing!");
- devc->buflen = 0;
- return;
- }
-
- sr_spew("range=%d/scale=%f/value=%f", range,
- (double)scale, (double)value);
-
- /* Finish and send packet. */
- analog.channels = sdi->channels;
- analog.num_samples = 1;
- analog.data = &value;
-
- memset(&packet, 0, sizeof(packet));
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
-
- /* Finish processing. */
- devc->num_samples++;
- devc->buflen = 0;
-}
-
-SR_PRIV int norma_dmm_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- int len;
- gboolean terminating;
- gdouble elapsed_s;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- serial = sdi->conn;
- if (revents == G_IO_IN) {
- /* Serial data arrived. */
- while (NMADMM_BUFSIZE - devc->buflen - 1 > 0) {
- len = serial_read(serial, devc->buf + devc->buflen, 1);
- if (len < 1)
- break;
- devc->buflen += len;
- *(devc->buf + devc->buflen) = '\0';
- if (*(devc->buf + devc->buflen - 1) == '\n') {
- /*
- * TODO: According to specs, should be \r, but
- * then we'd have to get rid of the \n.
- */
- devc->last_req_pending = FALSE;
- nma_process_line(sdi);
- break;
- }
- }
- }
-
- /* If number of samples or time limit reached, stop acquisition. */
- terminating = FALSE;
- if (devc->limit_samples && (devc->num_samples >= devc->limit_samples)) {
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- terminating = TRUE;
- }
-
- if (devc->limit_msec) {
- elapsed_s = g_timer_elapsed(devc->elapsed_msec, NULL);
- if ((elapsed_s * 1000) >= devc->limit_msec) {
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- terminating = TRUE;
- }
- }
-
- /* Request next package. */
- if (!terminating) {
- if (devc->last_req_pending) {
- gint64 elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
- if (elapsed_us > NMADMM_TIMEOUT_MS * 1000) {/* Timeout! */
- sr_spew("Request timeout!");
- devc->last_req_pending = FALSE;
- }
- }
- if (!devc->last_req_pending) {
- if (nma_send_req(sdi, NMADMM_REQ_STATUS, NULL) != SR_OK)
- return FALSE;
- }
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Matthias Heidbrink <m-sigrok@heidbrink.biz>
- *
- * 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_NORMA_DMM_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_NORMA_DMM_PROTOCOL_H
-
-#include <stdint.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/** @file
- * Norma DM9x0/Siemens B102x DMMs driver.
- * @internal
- */
-
-#define LOG_PREFIX "norma-dmm"
-
-#define NMADMM_BUFSIZE 256
-
-#define NMADMM_TIMEOUT_MS 2000 /**< Request timeout. */
-
-/** Norma DMM request types (used ones only, the DMMs support about 50). */
-enum {
- NMADMM_REQ_IDN = 0, /**< Request identity */
- NMADMM_REQ_STATUS, /**< Request device status (value + ...) */
-};
-
-/** Defines requests used to communicate with device. */
-struct nmadmm_req {
- int req_type; /**< Request type. */
- const char *req_str; /**< Request string. */
-};
-
-/** Strings for requests. */
-extern const struct nmadmm_req nmadmm_requests[];
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Model-specific information */
- char *version; /**< Version string */
- int type; /**< DM9x0, e.g. 5 = DM950 */
-
- /* Acquisition settings */
- uint64_t limit_samples; /**< Target number of samples */
- uint64_t limit_msec; /**< Target sampling time */
-
- /* Opaque pointer passed in by frontend. */
- void *cb_data;
-
- /* Operational state */
- int last_req; /**< Last request. */
- int64_t req_sent_at; /**< Request sent. */
- gboolean last_req_pending; /**< Last request not answered yet. */
- int lowbatt; /**< Low battery. 1=low, 2=critical. */
-
- /* Temporary state across callbacks */
- uint64_t num_samples; /**< Current #samples. */
- GTimer *elapsed_msec; /**< Used for limit_msec */
- uint8_t buf[NMADMM_BUFSIZE]; /**< Buffer for read callback */
- int buflen; /**< Data len in buf */
-};
-
-SR_PRIV int norma_dmm_receive_data(int fd, int revents, void *cb_data);
-SR_PRIV int xgittoint(char xgit);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 "protocol.h"
-#include <libserialport.h>
-
-#define SERIALCOMM "115200/8n1"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_SAMPLERATE,
- SR_CONF_TRIGGER_MATCH,
- SR_CONF_CAPTURE_RATIO,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_EXTERNAL_CLOCK,
- SR_CONF_PATTERN_MODE,
- SR_CONF_SWAP,
- SR_CONF_RLE,
-};
-
-static const int32_t trigger_matches[] = {
- SR_TRIGGER_ZERO,
- SR_TRIGGER_ONE,
-};
-
-#define STR_PATTERN_NONE "None"
-#define STR_PATTERN_EXTERNAL "External"
-#define STR_PATTERN_INTERNAL "Internal"
-
-/* Supported methods of test pattern outputs */
-enum {
- /**
- * Capture pins 31:16 (unbuffered wing) output a test pattern
- * that can captured on pins 0:15.
- */
- PATTERN_EXTERNAL,
-
- /** Route test pattern internally to capture buffer. */
- PATTERN_INTERNAL,
-};
-
-static const char *patterns[] = {
- STR_PATTERN_NONE,
- STR_PATTERN_EXTERNAL,
- STR_PATTERN_INTERNAL,
-};
-
-/* Channels are numbered 0-31 (on the PCB silkscreen). */
-SR_PRIV const char *ols_channel_names[NUM_CHANNELS + 1] = {
- "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
- "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23",
- "24", "25", "26", "27", "28", "29", "30", "31",
- NULL,
-};
-
-/* Default supported samplerates, can be overridden by device metadata. */
-static const uint64_t samplerates[] = {
- SR_HZ(10),
- SR_MHZ(200),
- SR_HZ(1),
-};
-
-SR_PRIV struct sr_dev_driver ols_driver_info;
-static struct sr_dev_driver *di = &ols_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct sr_config *src;
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_channel *ch;
- struct sr_serial_dev_inst *serial;
- GPollFD probefd;
- GSList *l, *devices;
- int ret, i;
- const char *conn, *serialcomm;
- char buf[8];
-
- drvc = di->priv;
-
- devices = NULL;
-
- 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 (!conn)
- return NULL;
-
- if (serialcomm == NULL)
- serialcomm = SERIALCOMM;
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- /* The discovery procedure is like this: first send the Reset
- * command (0x00) 5 times, since the device could be anywhere
- * in a 5-byte command. Then send the ID command (0x02).
- * If the device responds with 4 bytes ("OLS1" or "SLA1"), we
- * have a match.
- */
- sr_info("Probing %s.", conn);
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- ret = SR_OK;
- for (i = 0; i < 5; i++) {
- if ((ret = send_shortcommand(serial, CMD_RESET)) != SR_OK) {
- sr_err("Port %s is not writable.", conn);
- break;
- }
- }
- if (ret != SR_OK) {
- serial_close(serial);
- sr_err("Could not use port %s. Quitting.", conn);
- return NULL;
- }
- send_shortcommand(serial, CMD_ID);
-
- /* Wait 10ms for a response. */
- g_usleep(10000);
-
- sp_get_port_handle(serial->data, &probefd.fd);
- probefd.events = G_IO_IN;
- g_poll(&probefd, 1, 1);
-
- if (probefd.revents != G_IO_IN)
- return NULL;
- if (serial_read_blocking(serial, buf, 4) != 4)
- return NULL;
- if (strncmp(buf, "1SLO", 4) && strncmp(buf, "1ALS", 4))
- return NULL;
-
- /* Definitely using the OLS protocol, check if it supports
- * the metadata command.
- */
- send_shortcommand(serial, CMD_METADATA);
- if (g_poll(&probefd, 1, 10) > 0) {
- /* Got metadata. */
- sdi = get_metadata(serial);
- sdi->index = 0;
- devc = sdi->priv;
- } else {
- /* Not an OLS -- some other board that uses the sump protocol. */
- sr_info("Device does not support metadata.");
- sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
- "Sump", "Logic Analyzer", "v1.0");
- sdi->driver = di;
- for (i = 0; i < 32; i++) {
- if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
- ols_channel_names[i])))
- return 0;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
- devc = ols_dev_new();
- sdi->priv = devc;
- }
- /* Configure samplerate and divider. */
- if (ols_set_samplerate(sdi, DEFAULT_SAMPLERATE) != SR_OK)
- sr_dbg("Failed to set default samplerate (%"PRIu64").",
- DEFAULT_SAMPLERATE);
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- serial_close(serial);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (!sdi)
- return SR_ERR_ARG;
-
- devc = sdi->priv;
- switch (id) {
- case SR_CONF_SAMPLERATE:
- *data = g_variant_new_uint64(devc->cur_samplerate);
- break;
- case SR_CONF_CAPTURE_RATIO:
- *data = g_variant_new_uint64(devc->capture_ratio);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_PATTERN_MODE:
- if (devc->flag_reg & FLAG_EXTERNAL_TEST_MODE)
- *data = g_variant_new_string(STR_PATTERN_EXTERNAL);
- else if (devc->flag_reg & FLAG_INTERNAL_TEST_MODE)
- *data = g_variant_new_string(STR_PATTERN_INTERNAL);
- else
- *data = g_variant_new_string(STR_PATTERN_NONE);
- break;
- case SR_CONF_RLE:
- *data = g_variant_new_boolean(devc->flag_reg & FLAG_RLE ? TRUE : FALSE);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- uint16_t flag;
- uint64_t tmp_u64;
- int ret;
- const char *stropt;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- tmp_u64 = g_variant_get_uint64(data);
- if (tmp_u64 < samplerates[0] || tmp_u64 > samplerates[1])
- return SR_ERR_SAMPLERATE;
- ret = ols_set_samplerate(sdi, g_variant_get_uint64(data));
- break;
- case SR_CONF_LIMIT_SAMPLES:
- tmp_u64 = g_variant_get_uint64(data);
- if (tmp_u64 < MIN_NUM_SAMPLES)
- return SR_ERR;
- devc->limit_samples = tmp_u64;
- ret = SR_OK;
- break;
- case SR_CONF_CAPTURE_RATIO:
- devc->capture_ratio = g_variant_get_uint64(data);
- if (devc->capture_ratio < 0 || devc->capture_ratio > 100) {
- devc->capture_ratio = 0;
- ret = SR_ERR;
- } else
- ret = SR_OK;
- break;
- case SR_CONF_EXTERNAL_CLOCK:
- if (g_variant_get_boolean(data)) {
- sr_info("Enabling external clock.");
- devc->flag_reg |= FLAG_CLOCK_EXTERNAL;
- } else {
- sr_info("Disabled external clock.");
- devc->flag_reg &= ~FLAG_CLOCK_EXTERNAL;
- }
- ret = SR_OK;
- break;
- case SR_CONF_PATTERN_MODE:
- stropt = g_variant_get_string(data, NULL);
- ret = SR_OK;
- flag = 0xffff;
- if (!strcmp(stropt, STR_PATTERN_NONE)) {
- sr_info("Disabling test modes.");
- flag = 0x0000;
- }else if (!strcmp(stropt, STR_PATTERN_INTERNAL)) {
- sr_info("Enabling internal test mode.");
- flag = FLAG_INTERNAL_TEST_MODE;
- } else if (!strcmp(stropt, STR_PATTERN_EXTERNAL)) {
- sr_info("Enabling external test mode.");
- flag = FLAG_EXTERNAL_TEST_MODE;
- } else {
- ret = SR_ERR;
- }
- if (flag != 0xffff) {
- devc->flag_reg &= ~(FLAG_INTERNAL_TEST_MODE | FLAG_EXTERNAL_TEST_MODE);
- devc->flag_reg |= flag;
- }
- break;
- case SR_CONF_SWAP:
- if (g_variant_get_boolean(data)) {
- sr_info("Enabling channel swapping.");
- devc->flag_reg |= FLAG_SWAP_CHANNELS;
- } else {
- sr_info("Disabling channel swapping.");
- devc->flag_reg &= ~FLAG_SWAP_CHANNELS;
- }
- ret = SR_OK;
- break;
-
- case SR_CONF_RLE:
- if (g_variant_get_boolean(data)) {
- sr_info("Enabling RLE.");
- devc->flag_reg |= FLAG_RLE;
- } else {
- sr_info("Disabling RLE.");
- devc->flag_reg &= ~FLAG_RLE;
- }
- ret = SR_OK;
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- GVariant *gvar, *grange[2];
- GVariantBuilder gvb;
- int num_ols_changrp, i;
-
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
- ARRAY_SIZE(samplerates), sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TRIGGER_MATCH:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- trigger_matches, ARRAY_SIZE(trigger_matches),
- sizeof(int32_t));
- break;
- case SR_CONF_PATTERN_MODE:
- *data = g_variant_new_strv(patterns, ARRAY_SIZE(patterns));
- break;
- case SR_CONF_LIMIT_SAMPLES:
- if (!sdi)
- return SR_ERR_ARG;
- devc = sdi->priv;
- if (devc->flag_reg & FLAG_RLE)
- return SR_ERR_NA;
- if (devc->max_samples == 0)
- /* Device didn't specify sample memory size in metadata. */
- return SR_ERR_NA;
- /*
- * Channel groups are turned off if no channels in that group are
- * enabled, making more room for samples for the enabled group.
- */
- ols_channel_mask(sdi);
- num_ols_changrp = 0;
- for (i = 0; i < 4; i++) {
- if (devc->channel_mask & (0xff << (i * 8)))
- num_ols_changrp++;
- }
- grange[0] = g_variant_new_uint64(MIN_NUM_SAMPLES);
- if (num_ols_changrp)
- grange[1] = g_variant_new_uint64(devc->max_samples / num_ols_changrp);
- else
- grange[1] = g_variant_new_uint64(MIN_NUM_SAMPLES);
- *data = g_variant_new_tuple(grange, 2);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int set_trigger(const struct sr_dev_inst *sdi, int stage)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- uint8_t cmd, arg[4];
-
- devc = sdi->priv;
- serial = sdi->conn;
-
- cmd = CMD_SET_TRIGGER_MASK + stage * 4;
- arg[0] = devc->trigger_mask[stage] & 0xff;
- arg[1] = (devc->trigger_mask[stage] >> 8) & 0xff;
- arg[2] = (devc->trigger_mask[stage] >> 16) & 0xff;
- arg[3] = (devc->trigger_mask[stage] >> 24) & 0xff;
- if (send_longcommand(serial, cmd, arg) != SR_OK)
- return SR_ERR;
-
- cmd = CMD_SET_TRIGGER_VALUE + stage * 4;
- arg[0] = devc->trigger_value[stage] & 0xff;
- arg[1] = (devc->trigger_value[stage] >> 8) & 0xff;
- arg[2] = (devc->trigger_value[stage] >> 16) & 0xff;
- arg[3] = (devc->trigger_value[stage] >> 24) & 0xff;
- if (send_longcommand(serial, cmd, arg) != SR_OK)
- return SR_ERR;
-
- cmd = CMD_SET_TRIGGER_CONFIG + stage * 4;
- arg[0] = arg[1] = arg[3] = 0x00;
- arg[2] = stage;
- if (stage == devc->num_stages)
- /* Last stage, fire when this one matches. */
- arg[3] |= TRIGGER_START;
- if (send_longcommand(serial, cmd, arg) != SR_OK)
- return SR_ERR;
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- uint16_t samplecount, readcount, delaycount;
- uint8_t ols_changrp_mask, arg[4];
- int num_ols_changrp;
- int ret, i;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- serial = sdi->conn;
-
- ols_channel_mask(sdi);
-
- num_ols_changrp = 0;
- ols_changrp_mask = 0;
- for (i = 0; i < 4; i++) {
- if (devc->channel_mask & (0xff << (i * 8))) {
- ols_changrp_mask |= (1 << i);
- num_ols_changrp++;
- }
- }
-
- /*
- * Limit readcount to prevent reading past the end of the hardware
- * buffer.
- */
- samplecount = MIN(devc->max_samples / num_ols_changrp, devc->limit_samples);
- readcount = samplecount / 4;
-
- /* Rather read too many samples than too few. */
- if (samplecount % 4 != 0)
- readcount++;
-
- /* Basic triggers. */
- if (ols_convert_trigger(sdi) != SR_OK) {
- sr_err("Failed to configure channels.");
- return SR_ERR;
- }
- if (devc->num_stages > 0) {
- delaycount = readcount * (1 - devc->capture_ratio / 100.0);
- devc->trigger_at = (readcount - delaycount) * 4 - devc->num_stages;
- for (i = 0; i <= devc->num_stages; i++) {
- sr_dbg("Setting OLS stage %d trigger.", i);
- if ((ret = set_trigger(sdi, i)) != SR_OK)
- return ret;
- }
- } else {
- /* No triggers configured, force trigger on first stage. */
- sr_dbg("Forcing trigger at stage 0.");
- if ((ret = set_trigger(sdi, 0)) != SR_OK)
- return ret;
- delaycount = readcount;
- }
-
- /* Samplerate. */
- sr_dbg("Setting samplerate to %" PRIu64 "Hz (divider %u)",
- devc->cur_samplerate, devc->cur_samplerate_divider);
- arg[0] = devc->cur_samplerate_divider & 0xff;
- arg[1] = (devc->cur_samplerate_divider & 0xff00) >> 8;
- arg[2] = (devc->cur_samplerate_divider & 0xff0000) >> 16;
- arg[3] = 0x00;
- if (send_longcommand(serial, CMD_SET_DIVIDER, arg) != SR_OK)
- return SR_ERR;
-
- /* Send sample limit and pre/post-trigger capture ratio. */
- sr_dbg("Setting sample limit %d, trigger point at %d",
- (readcount - 1) * 4, (delaycount - 1) * 4);
- arg[0] = ((readcount - 1) & 0xff);
- arg[1] = ((readcount - 1) & 0xff00) >> 8;
- arg[2] = ((delaycount - 1) & 0xff);
- arg[3] = ((delaycount - 1) & 0xff00) >> 8;
- if (send_longcommand(serial, CMD_CAPTURE_SIZE, arg) != SR_OK)
- return SR_ERR;
-
- /* Flag register. */
- sr_dbg("Setting intpat %s, extpat %s, RLE %s, noise_filter %s, demux %s",
- devc->flag_reg & FLAG_INTERNAL_TEST_MODE ? "on": "off",
- devc->flag_reg & FLAG_EXTERNAL_TEST_MODE ? "on": "off",
- devc->flag_reg & FLAG_RLE ? "on" : "off",
- devc->flag_reg & FLAG_FILTER ? "on": "off",
- devc->flag_reg & FLAG_DEMUX ? "on" : "off");
- /*
- * Enable/disable OLS channel groups in the flag register according
- * to the channel mask. 1 means "disable channel".
- */
- devc->flag_reg |= ~(ols_changrp_mask << 2) & 0x3c;
- arg[0] = devc->flag_reg & 0xff;
- arg[1] = devc->flag_reg >> 8;
- arg[2] = arg[3] = 0x00;
- if (send_longcommand(serial, CMD_SET_FLAGS, arg) != SR_OK)
- return SR_ERR;
-
- /* Start acquisition on the device. */
- if (send_shortcommand(serial, CMD_RUN) != SR_OK)
- return SR_ERR;
-
- /* Reset all operational states. */
- devc->rle_count = devc->num_transfers = 0;
- devc->num_samples = devc->num_bytes = 0;
- devc->cnt_bytes = devc->cnt_samples = devc->cnt_samples_rle = 0;
- memset(devc->sample, 0, 4);
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- serial_source_add(sdi->session, serial, G_IO_IN, -1,
- ols_receive_data, cb_data);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- (void)cb_data;
-
- abort_acquisition(sdi);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver ols_driver_info = {
- .name = "ols",
- .longname = "Openbench Logic Sniffer",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .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 = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 "protocol.h"
-#include <libserialport.h>
-
-extern SR_PRIV struct sr_dev_driver ols_driver_info;
-static struct sr_dev_driver *di = &ols_driver_info;
-
-SR_PRIV int send_shortcommand(struct sr_serial_dev_inst *serial,
- uint8_t command)
-{
- char buf[1];
-
- sr_dbg("Sending cmd 0x%.2x.", command);
- buf[0] = command;
- if (serial_write_blocking(serial, buf, 1) != 1)
- return SR_ERR;
-
- return SR_OK;
-}
-
-SR_PRIV int send_longcommand(struct sr_serial_dev_inst *serial,
- uint8_t command, uint8_t *data)
-{
- char buf[5];
-
- sr_dbg("Sending cmd 0x%.2x data 0x%.2x%.2x%.2x%.2x.", command,
- data[0], data[1], data[2], data[3]);
- buf[0] = command;
- buf[1] = data[0];
- buf[2] = data[1];
- buf[3] = data[2];
- buf[4] = data[3];
- if (serial_write_blocking(serial, buf, 5) != 5)
- return SR_ERR;
-
- return SR_OK;
-}
-
-/* Configures the channel mask based on which channels are enabled. */
-SR_PRIV void ols_channel_mask(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_channel *channel;
- const GSList *l;
-
- devc = sdi->priv;
-
- devc->channel_mask = 0;
- for (l = sdi->channels; l; l = l->next) {
- channel = l->data;
- if (channel->enabled)
- devc->channel_mask |= 1 << channel->index;
- }
-}
-
-SR_PRIV int ols_convert_trigger(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_trigger *trigger;
- struct sr_trigger_stage *stage;
- struct sr_trigger_match *match;
- const GSList *l, *m;
- int i;
-
- devc = sdi->priv;
-
- devc->num_stages = 0;
- for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
- devc->trigger_mask[i] = 0;
- devc->trigger_value[i] = 0;
- }
-
- if (!(trigger = sr_session_trigger_get(sdi->session)))
- return SR_OK;
-
- devc->num_stages = g_slist_length(trigger->stages);
- if (devc->num_stages > NUM_TRIGGER_STAGES) {
- sr_err("This device only supports %d trigger stages.",
- NUM_TRIGGER_STAGES);
- return SR_ERR;
- }
-
- for (l = trigger->stages; l; l = l->next) {
- stage = l->data;
- for (m = stage->matches; m; m = m->next) {
- match = m->data;
- if (!match->channel->enabled)
- /* Ignore disabled channels with a trigger. */
- continue;
- devc->trigger_mask[stage->stage] |= 1 << match->channel->index;
- if (match->match == SR_TRIGGER_ONE)
- devc->trigger_value[stage->stage] |= 1 << match->channel->index;
- }
- }
-
- return SR_OK;
-}
-
-SR_PRIV struct dev_context *ols_dev_new(void)
-{
- struct dev_context *devc;
-
- if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
-
- /* Device-specific settings */
- devc->max_samples = devc->max_samplerate = devc->protocol_version = 0;
-
- /* Acquisition settings */
- devc->limit_samples = devc->capture_ratio = 0;
- devc->trigger_at = -1;
- devc->channel_mask = 0xffffffff;
- devc->flag_reg = 0;
-
- return devc;
-}
-
-SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_channel *ch;
- uint32_t tmp_int, ui;
- uint8_t key, type, token;
- GString *tmp_str, *devname, *version;
- guchar tmp_c;
-
- sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, NULL, NULL, NULL);
- sdi->driver = di;
- devc = ols_dev_new();
- sdi->priv = devc;
-
- devname = g_string_new("");
- version = g_string_new("");
-
- key = 0xff;
- while (key) {
- if (serial_read_blocking(serial, &key, 1) != 1)
- break;
- if (key == 0x00) {
- sr_dbg("Got metadata key 0x00, metadata ends.");
- break;
- }
- type = key >> 5;
- token = key & 0x1f;
- switch (type) {
- case 0:
- /* NULL-terminated string */
- tmp_str = g_string_new("");
- while (serial_read_blocking(serial, &tmp_c, 1) == 1 && tmp_c != '\0')
- g_string_append_c(tmp_str, tmp_c);
- sr_dbg("Got metadata key 0x%.2x value '%s'.",
- key, tmp_str->str);
- switch (token) {
- case 0x01:
- /* Device name */
- devname = g_string_append(devname, tmp_str->str);
- break;
- case 0x02:
- /* FPGA firmware version */
- if (version->len)
- g_string_append(version, ", ");
- g_string_append(version, "FPGA version ");
- g_string_append(version, tmp_str->str);
- break;
- case 0x03:
- /* Ancillary version */
- if (version->len)
- g_string_append(version, ", ");
- g_string_append(version, "Ancillary version ");
- g_string_append(version, tmp_str->str);
- break;
- default:
- sr_info("ols: unknown token 0x%.2x: '%s'",
- token, tmp_str->str);
- break;
- }
- g_string_free(tmp_str, TRUE);
- break;
- case 1:
- /* 32-bit unsigned integer */
- if (serial_read_blocking(serial, &tmp_int, 4) != 4)
- break;
- tmp_int = RB32(&tmp_int);
- sr_dbg("Got metadata key 0x%.2x value 0x%.8x.",
- key, tmp_int);
- switch (token) {
- case 0x00:
- /* Number of usable channels */
- for (ui = 0; ui < tmp_int; ui++) {
- if (!(ch = sr_channel_new(ui, SR_CHANNEL_LOGIC, TRUE,
- ols_channel_names[ui])))
- return 0;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
- break;
- case 0x01:
- /* Amount of sample memory available (bytes) */
- devc->max_samples = tmp_int;
- break;
- case 0x02:
- /* Amount of dynamic memory available (bytes) */
- /* what is this for? */
- break;
- case 0x03:
- /* Maximum sample rate (hz) */
- devc->max_samplerate = tmp_int;
- break;
- case 0x04:
- /* protocol version */
- devc->protocol_version = tmp_int;
- break;
- default:
- sr_info("Unknown token 0x%.2x: 0x%.8x.",
- token, tmp_int);
- break;
- }
- break;
- case 2:
- /* 8-bit unsigned integer */
- if (serial_read_blocking(serial, &tmp_c, 1) != 1)
- break;
- sr_dbg("Got metadata key 0x%.2x value 0x%.2x.",
- key, tmp_c);
- switch (token) {
- case 0x00:
- /* Number of usable channels */
- for (ui = 0; ui < tmp_c; ui++) {
- if (!(ch = sr_channel_new(ui, SR_CHANNEL_LOGIC, TRUE,
- ols_channel_names[ui])))
- return 0;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
- break;
- case 0x01:
- /* protocol version */
- devc->protocol_version = tmp_c;
- break;
- default:
- sr_info("Unknown token 0x%.2x: 0x%.2x.",
- token, tmp_c);
- break;
- }
- break;
- default:
- /* unknown type */
- break;
- }
- }
-
- sdi->model = devname->str;
- sdi->version = version->str;
- g_string_free(devname, FALSE);
- g_string_free(version, FALSE);
-
- return sdi;
-}
-
-SR_PRIV int ols_set_samplerate(const struct sr_dev_inst *sdi,
- const uint64_t samplerate)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
- if (devc->max_samplerate && samplerate > devc->max_samplerate)
- return SR_ERR_SAMPLERATE;
-
- if (samplerate > CLOCK_RATE) {
- sr_info("Enabling demux mode.");
- devc->flag_reg |= FLAG_DEMUX;
- devc->flag_reg &= ~FLAG_FILTER;
- devc->max_channels = NUM_CHANNELS / 2;
- devc->cur_samplerate_divider = (CLOCK_RATE * 2 / samplerate) - 1;
- } else {
- sr_info("Disabling demux mode.");
- devc->flag_reg &= ~FLAG_DEMUX;
- devc->flag_reg |= FLAG_FILTER;
- devc->max_channels = NUM_CHANNELS;
- devc->cur_samplerate_divider = (CLOCK_RATE / samplerate) - 1;
- }
-
- /* Calculate actual samplerate used and complain if it is different
- * from the requested.
- */
- devc->cur_samplerate = CLOCK_RATE / (devc->cur_samplerate_divider + 1);
- if (devc->flag_reg & FLAG_DEMUX)
- devc->cur_samplerate *= 2;
- if (devc->cur_samplerate != samplerate)
- sr_info("Can't match samplerate %" PRIu64 ", using %"
- PRIu64 ".", samplerate, devc->cur_samplerate);
-
- return SR_OK;
-}
-
-SR_PRIV void abort_acquisition(const struct sr_dev_inst *sdi)
-{
- struct sr_datafeed_packet packet;
- struct sr_serial_dev_inst *serial;
-
- serial = sdi->conn;
- serial_source_remove(sdi->session, serial);
-
- /* Terminate session */
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-}
-
-SR_PRIV int ols_receive_data(int fd, int revents, void *cb_data)
-{
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_serial_dev_inst *serial;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- uint32_t sample;
- int num_ols_changrp, offset, j;
- unsigned int i;
- unsigned char byte;
-
- (void)fd;
-
- sdi = cb_data;
- serial = sdi->conn;
- devc = sdi->priv;
-
- if (devc->num_transfers++ == 0) {
- /*
- * First time round, means the device started sending data,
- * and will not stop until done. If it stops sending for
- * longer than it takes to send a byte, that means it's
- * finished. We'll double that to 30ms to be sure...
- */
- serial_source_remove(sdi->session, serial);
- serial_source_add(sdi->session, serial, G_IO_IN, 30,
- ols_receive_data, cb_data);
- devc->raw_sample_buf = g_try_malloc(devc->limit_samples * 4);
- if (!devc->raw_sample_buf) {
- sr_err("Sample buffer malloc failed.");
- return FALSE;
- }
- /* fill with 1010... for debugging */
- memset(devc->raw_sample_buf, 0x82, devc->limit_samples * 4);
- }
-
- num_ols_changrp = 0;
- for (i = NUM_CHANNELS; i > 0x02; i /= 2) {
- if ((devc->flag_reg & i) == 0) {
- num_ols_changrp++;
- }
- }
-
- if (revents == G_IO_IN && devc->num_samples < devc->limit_samples) {
- if (serial_read_nonblocking(serial, &byte, 1) != 1)
- return FALSE;
- devc->cnt_bytes++;
-
- /* Ignore it if we've read enough. */
- if (devc->num_samples >= devc->limit_samples)
- return TRUE;
-
- devc->sample[devc->num_bytes++] = byte;
- sr_spew("Received byte 0x%.2x.", byte);
- if (devc->num_bytes == num_ols_changrp) {
- devc->cnt_samples++;
- devc->cnt_samples_rle++;
- /*
- * Got a full sample. Convert from the OLS's little-endian
- * sample to the local format.
- */
- sample = devc->sample[0] | (devc->sample[1] << 8) \
- | (devc->sample[2] << 16) | (devc->sample[3] << 24);
- sr_dbg("Received sample 0x%.*x.", devc->num_bytes * 2, sample);
- if (devc->flag_reg & FLAG_RLE) {
- /*
- * In RLE mode the high bit of the sample is the
- * "count" flag, meaning this sample is the number
- * of times the previous sample occurred.
- */
- if (devc->sample[devc->num_bytes - 1] & 0x80) {
- /* Clear the high bit. */
- sample &= ~(0x80 << (devc->num_bytes - 1) * 8);
- devc->rle_count = sample;
- devc->cnt_samples_rle += devc->rle_count;
- sr_dbg("RLE count: %u.", devc->rle_count);
- devc->num_bytes = 0;
- return TRUE;
- }
- }
- devc->num_samples += devc->rle_count + 1;
- if (devc->num_samples > devc->limit_samples) {
- /* Save us from overrunning the buffer. */
- devc->rle_count -= devc->num_samples - devc->limit_samples;
- devc->num_samples = devc->limit_samples;
- }
-
- if (num_ols_changrp < 4) {
- /*
- * Some channel groups may have been turned
- * off, to speed up transfer between the
- * hardware and the PC. Expand that here before
- * submitting it over the session bus --
- * whatever is listening on the bus will be
- * expecting a full 32-bit sample, based on
- * the number of channels.
- */
- j = 0;
- memset(devc->tmp_sample, 0, 4);
- for (i = 0; i < 4; i++) {
- if (((devc->flag_reg >> 2) & (1 << i)) == 0) {
- /*
- * This channel group was
- * enabled, copy from received
- * sample.
- */
- devc->tmp_sample[i] = devc->sample[j++];
- } else if (devc->flag_reg & FLAG_DEMUX && (i > 2)) {
- /* group 2 & 3 get added to 0 & 1 */
- devc->tmp_sample[i - 2] = devc->sample[j++];
- }
- }
- memcpy(devc->sample, devc->tmp_sample, 4);
- sr_spew("Expanded sample: 0x%.8x.", sample);
- }
-
- /*
- * the OLS sends its sample buffer backwards.
- * store it in reverse order here, so we can dump
- * this on the session bus later.
- */
- offset = (devc->limit_samples - devc->num_samples) * 4;
- for (i = 0; i <= devc->rle_count; i++) {
- memcpy(devc->raw_sample_buf + offset + (i * 4),
- devc->sample, 4);
- }
- memset(devc->sample, 0, 4);
- devc->num_bytes = 0;
- devc->rle_count = 0;
- }
- } else {
- /*
- * This is the main loop telling us a timeout was reached, or
- * we've acquired all the samples we asked for -- we're done.
- * Send the (properly-ordered) buffer to the frontend.
- */
- sr_dbg("Received %d bytes, %d samples, %d decompressed samples.",
- devc->cnt_bytes, devc->cnt_samples,
- devc->cnt_samples_rle);
- if (devc->trigger_at != -1) {
- /*
- * A trigger was set up, so we need to tell the frontend
- * about it.
- */
- if (devc->trigger_at > 0) {
- /* There are pre-trigger samples, send those first. */
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = devc->trigger_at * 4;
- logic.unitsize = 4;
- logic.data = devc->raw_sample_buf +
- (devc->limit_samples - devc->num_samples) * 4;
- sr_session_send(cb_data, &packet);
- }
-
- /* Send the trigger. */
- packet.type = SR_DF_TRIGGER;
- sr_session_send(cb_data, &packet);
-
- /* Send post-trigger samples. */
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = (devc->num_samples * 4) - (devc->trigger_at * 4);
- logic.unitsize = 4;
- logic.data = devc->raw_sample_buf + devc->trigger_at * 4 +
- (devc->limit_samples - devc->num_samples) * 4;
- sr_session_send(cb_data, &packet);
- } else {
- /* no trigger was used */
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = devc->num_samples * 4;
- logic.unitsize = 4;
- logic.data = devc->raw_sample_buf +
- (devc->limit_samples - devc->num_samples) * 4;
- sr_session_send(cb_data, &packet);
- }
- g_free(devc->raw_sample_buf);
-
- serial_flush(serial);
- abort_acquisition(sdi);
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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_OPENBENCH_LOGIC_SNIFFER_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_OPENBENCH_LOGIC_SNIFFER_PROTOCOL_H
-
-#include <stdint.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "ols"
-
-#define NUM_CHANNELS 32
-#define NUM_TRIGGER_STAGES 4
-#define SERIAL_SPEED B115200
-#define CLOCK_RATE SR_MHZ(100)
-#define MIN_NUM_SAMPLES 4
-#define DEFAULT_SAMPLERATE SR_KHZ(200)
-
-/* Command opcodes */
-#define CMD_RESET 0x00
-#define CMD_RUN 0x01
-#define CMD_TESTMODE 0x03
-#define CMD_ID 0x02
-#define CMD_METADATA 0x04
-#define CMD_SET_FLAGS 0x82
-#define CMD_SET_DIVIDER 0x80
-#define CMD_CAPTURE_SIZE 0x81
-#define CMD_SET_TRIGGER_MASK 0xc0
-#define CMD_SET_TRIGGER_VALUE 0xc1
-#define CMD_SET_TRIGGER_CONFIG 0xc2
-
-/* Trigger config */
-#define TRIGGER_START (1 << 3)
-
-/* Bitmasks for CMD_FLAGS */
-/* 12-13 unused, 14-15 RLE mode (we hardcode mode 0). */
-#define FLAG_INTERNAL_TEST_MODE (1 << 11)
-#define FLAG_EXTERNAL_TEST_MODE (1 << 10)
-#define FLAG_SWAP_CHANNELS (1 << 9)
-#define FLAG_RLE (1 << 8)
-#define FLAG_SLOPE_FALLING (1 << 7)
-#define FLAG_CLOCK_EXTERNAL (1 << 6)
-#define FLAG_CHANNELGROUP_4 (1 << 5)
-#define FLAG_CHANNELGROUP_3 (1 << 4)
-#define FLAG_CHANNELGROUP_2 (1 << 3)
-#define FLAG_CHANNELGROUP_1 (1 << 2)
-#define FLAG_FILTER (1 << 1)
-#define FLAG_DEMUX (1 << 0)
-
-/* Private, per-device-instance driver context. */
-struct dev_context {
- /* Fixed device settings */
- int max_channels;
- uint32_t max_samples;
- uint32_t max_samplerate;
- uint32_t protocol_version;
-
- /* Acquisition settings */
- uint64_t cur_samplerate;
- uint32_t cur_samplerate_divider;
- uint64_t limit_samples;
- int capture_ratio;
- int trigger_at;
- uint32_t channel_mask;
- uint32_t trigger_mask[NUM_TRIGGER_STAGES];
- uint32_t trigger_value[NUM_TRIGGER_STAGES];
- int num_stages;
- uint16_t flag_reg;
-
- /* Operational states */
- unsigned int num_transfers;
- unsigned int num_samples;
- int num_bytes;
- int cnt_bytes;
- int cnt_samples;
- int cnt_samples_rle;
-
- /* Temporary variables */
- unsigned int rle_count;
- unsigned char sample[4];
- unsigned char tmp_sample[4];
- unsigned char *raw_sample_buf;
-};
-
-
-SR_PRIV extern const char *ols_channel_names[NUM_CHANNELS + 1];
-
-SR_PRIV int send_shortcommand(struct sr_serial_dev_inst *serial,
- uint8_t command);
-SR_PRIV int send_longcommand(struct sr_serial_dev_inst *serial,
- uint8_t command, uint8_t *data);
-SR_PRIV void ols_channel_mask(const struct sr_dev_inst *sdi);
-SR_PRIV int ols_convert_trigger(const struct sr_dev_inst *sdi);
-SR_PRIV struct dev_context *ols_dev_new(void);
-SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial);
-SR_PRIV int ols_set_samplerate(const struct sr_dev_inst *sdi,
- uint64_t samplerate);
-SR_PRIV void abort_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV int ols_receive_data(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Martin Ling <martin-git@earth.li>
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2013 Mathias Grimmberger <mgri@zaphod.sax.de>
- *
- * 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 <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_OSCILLOSCOPE,
- SR_CONF_TIMEBASE,
- SR_CONF_TRIGGER_SOURCE,
- SR_CONF_TRIGGER_SLOPE,
- SR_CONF_HORIZ_TRIGGERPOS,
- SR_CONF_NUM_TIMEBASE,
- SR_CONF_LIMIT_FRAMES,
- SR_CONF_SAMPLERATE,
-};
-
-static const int32_t analog_hwcaps[] = {
- SR_CONF_NUM_VDIV,
- SR_CONF_VDIV,
- SR_CONF_COUPLING,
- SR_CONF_DATA_SOURCE,
-};
-
-static const uint64_t timebases[][2] = {
- /* nanoseconds */
- { 1, 1000000000 },
- { 2, 1000000000 },
- { 5, 1000000000 },
- { 10, 1000000000 },
- { 20, 1000000000 },
- { 50, 1000000000 },
- { 100, 1000000000 },
- { 500, 1000000000 },
- /* microseconds */
- { 1, 1000000 },
- { 2, 1000000 },
- { 5, 1000000 },
- { 10, 1000000 },
- { 20, 1000000 },
- { 50, 1000000 },
- { 100, 1000000 },
- { 200, 1000000 },
- { 500, 1000000 },
- /* milliseconds */
- { 1, 1000 },
- { 2, 1000 },
- { 5, 1000 },
- { 10, 1000 },
- { 20, 1000 },
- { 50, 1000 },
- { 100, 1000 },
- { 200, 1000 },
- { 500, 1000 },
- /* seconds */
- { 1, 1 },
- { 2, 1 },
- { 5, 1 },
- { 10, 1 },
- { 20, 1 },
- { 50, 1 },
- { 100, 1 },
- { 200, 1 },
- { 500, 1 },
- { 1000, 1 },
-};
-
-static const uint64_t vdivs[][2] = {
- /* microvolts */
- { 500, 1000000 },
- /* millivolts */
- { 1, 1000 },
- { 2, 1000 },
- { 5, 1000 },
- { 10, 1000 },
- { 20, 1000 },
- { 50, 1000 },
- { 100, 1000 },
- { 200, 1000 },
- { 500, 1000 },
- /* volts */
- { 1, 1 },
- { 2, 1 },
- { 5, 1 },
- { 10, 1 },
-};
-
-#define NUM_TIMEBASE ARRAY_SIZE(timebases)
-#define NUM_VDIV ARRAY_SIZE(vdivs)
-
-static const char *trigger_sources[] = {
- "CH1",
- "CH2",
- "CH3",
- "CH4",
- "EXT",
- "AC Line",
- "D0",
- "D1",
- "D2",
- "D3",
- "D4",
- "D5",
- "D6",
- "D7",
- "D8",
- "D9",
- "D10",
- "D11",
- "D12",
- "D13",
- "D14",
- "D15",
-};
-
-static const char *trigger_slopes[] = {
- "r",
- "f",
-};
-
-static const char *coupling[] = {
- "AC",
- "DC",
- "GND",
-};
-
-/* Do not change the order of entries */
-static const char *data_sources[] = {
- "Live",
- "Memory",
- "Segmented",
-};
-
-enum vendor {
- RIGOL,
- AGILENT,
-};
-
-enum series {
- VS5000,
- DS1000,
- DS2000,
- DS2000A,
- DSO1000,
-};
-
-/* short name, full name */
-static const struct rigol_ds_vendor supported_vendors[] = {
- [RIGOL] = {"Rigol", "Rigol Technologies"},
- [AGILENT] = {"Agilent", "Rigol Technologies"},
-};
-
-#define VENDOR(x) &supported_vendors[x]
-/* vendor, series, protocol, max timebase, min vdiv, number of horizontal divs,
- * live waveform samples, memory buffer samples */
-static const struct rigol_ds_series supported_series[] = {
- [VS5000] = {VENDOR(RIGOL), "VS5000", PROTOCOL_V1, FORMAT_RAW,
- {50, 1}, {2, 1000}, 14, 2048, 0},
- [DS1000] = {VENDOR(RIGOL), "DS1000", PROTOCOL_V2, FORMAT_IEEE488_2,
- {50, 1}, {2, 1000}, 12, 600, 1048576},
- [DS2000] = {VENDOR(RIGOL), "DS2000", PROTOCOL_V3, FORMAT_IEEE488_2,
- {500, 1}, {2, 1000}, 14, 1400, 14000},
- [DS2000A] = {VENDOR(RIGOL), "DS2000A", PROTOCOL_V3, FORMAT_IEEE488_2,
- {1000, 1}, {500, 1000000}, 14, 1400, 14000},
- [DSO1000] = {VENDOR(AGILENT), "DSO1000", PROTOCOL_V3, FORMAT_IEEE488_2,
- {50, 1}, {2, 1000}, 12, 600, 20480},
-};
-
-#define SERIES(x) &supported_series[x]
-/* series, model, min timebase, analog channels, digital */
-static const struct rigol_ds_model supported_models[] = {
- {SERIES(VS5000), "VS5022", {20, 1000000000}, 2, false},
- {SERIES(VS5000), "VS5042", {10, 1000000000}, 2, false},
- {SERIES(VS5000), "VS5062", {5, 1000000000}, 2, false},
- {SERIES(VS5000), "VS5102", {2, 1000000000}, 2, false},
- {SERIES(VS5000), "VS5202", {2, 1000000000}, 2, false},
- {SERIES(VS5000), "VS5022D", {20, 1000000000}, 2, true},
- {SERIES(VS5000), "VS5042D", {10, 1000000000}, 2, true},
- {SERIES(VS5000), "VS5062D", {5, 1000000000}, 2, true},
- {SERIES(VS5000), "VS5102D", {2, 1000000000}, 2, true},
- {SERIES(VS5000), "VS5202D", {2, 1000000000}, 2, true},
- {SERIES(DS1000), "DS1052E", {5, 1000000000}, 2, false},
- {SERIES(DS1000), "DS1102E", {2, 1000000000}, 2, false},
- {SERIES(DS1000), "DS1152E", {2, 1000000000}, 2, false},
- {SERIES(DS1000), "DS1052D", {5, 1000000000}, 2, true},
- {SERIES(DS1000), "DS1102D", {2, 1000000000}, 2, true},
- {SERIES(DS1000), "DS1152D", {2, 1000000000}, 2, true},
- {SERIES(DS2000), "DS2072", {5, 1000000000}, 2, false},
- {SERIES(DS2000), "DS2102", {5, 1000000000}, 2, false},
- {SERIES(DS2000), "DS2202", {2, 1000000000}, 2, false},
- {SERIES(DS2000), "DS2302", {1, 1000000000}, 2, false},
- {SERIES(DS2000A), "DS2072A", {5, 1000000000}, 2, false},
- {SERIES(DS2000A), "DS2102A", {5, 1000000000}, 2, false},
- {SERIES(DS2000A), "DS2202A", {2, 1000000000}, 2, false},
- {SERIES(DS2000A), "DS2302A", {1, 1000000000}, 2, false},
- {SERIES(DSO1000), "DSO1002A", {5, 1000000000}, 2, false},
- {SERIES(DSO1000), "DSO1004A", {5, 1000000000}, 4, false},
- {SERIES(DSO1000), "DSO1012A", {2, 1000000000}, 2, false},
- {SERIES(DSO1000), "DSO1014A", {2, 1000000000}, 4, false},
- {SERIES(DSO1000), "DSO1022A", {2, 1000000000}, 2, false},
- {SERIES(DSO1000), "DSO1024A", {2, 1000000000}, 4, false},
-};
-
-SR_PRIV struct sr_dev_driver rigol_ds_driver_info;
-static struct sr_dev_driver *di = &rigol_ds_driver_info;
-
-static void clear_helper(void *priv)
-{
- struct dev_context *devc;
-
- devc = priv;
- g_free(devc->data);
- g_free(devc->buffer);
- g_free(devc->coupling[0]);
- g_free(devc->coupling[1]);
- g_free(devc->trigger_source);
- g_free(devc->trigger_slope);
- g_slist_free(devc->analog_groups[0].channels);
- g_slist_free(devc->analog_groups[1].channels);
- g_slist_free(devc->digital_group.channels);
-}
-
-static int dev_clear(void)
-{
- return std_dev_clear(di, clear_helper);
-}
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
-{
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_scpi_hw_info *hw_info;
- struct sr_channel *ch;
- long n[3];
- unsigned int i;
- const struct rigol_ds_model *model = NULL;
- gchar *channel_name, **version;
-
- if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
- sr_info("Couldn't get IDN response, retrying.");
- sr_scpi_close(scpi);
- sr_scpi_open(scpi);
- if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
- sr_info("Couldn't get IDN response.");
- return NULL;
- }
- }
-
- for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
- if (!strcasecmp(hw_info->manufacturer,
- supported_models[i].series->vendor->full_name) &&
- !strcmp(hw_info->model, supported_models[i].name)) {
- model = &supported_models[i];
- break;
- }
- }
-
- if (!model || !(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE,
- model->series->vendor->name,
- model->name,
- hw_info->firmware_version))) {
- sr_scpi_hw_info_free(hw_info);
- return NULL;
- }
-
- sdi->conn = scpi;
-
- sdi->driver = di;
- sdi->inst_type = SR_INST_SCPI;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
- return NULL;
-
- devc->limit_frames = 0;
- devc->model = model;
- devc->format = model->series->format;
-
- /* DS1000 models with firmware before 0.2.4 used the old data format. */
- if (model->series == SERIES(DS1000)) {
- version = g_strsplit(hw_info->firmware_version, ".", 0);
- do {
- if (!version[0] || !version[1] || !version[2])
- break;
- if (version[0][0] == 0 || version[1][0] == 0 || version[2][0] == 0)
- break;
- for (i = 0; i < 3; i++) {
- if (sr_atol(version[i], &n[i]) != SR_OK)
- break;
- }
- if (i != 3)
- break;
- if (n[0] != 0 || n[1] > 2)
- break;
- if (n[1] == 2 && n[2] > 3)
- break;
- sr_dbg("Found DS1000 firmware < 0.2.4, using raw data format.");
- devc->format = FORMAT_RAW;
- } while(0);
- g_strfreev(version);
- }
-
- sr_scpi_hw_info_free(hw_info);
-
- for (i = 0; i < model->analog_channels; i++) {
- if (!(channel_name = g_strdup_printf("CH%d", i + 1)))
- return NULL;
- ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, channel_name);
- sdi->channels = g_slist_append(sdi->channels, ch);
- devc->analog_groups[i].name = channel_name;
- devc->analog_groups[i].channels = g_slist_append(NULL, ch);
- sdi->channel_groups = g_slist_append(sdi->channel_groups,
- &devc->analog_groups[i]);
- }
-
- if (devc->model->has_digital) {
- for (i = 0; i < 16; i++) {
- if (!(channel_name = g_strdup_printf("D%d", i)))
- return NULL;
- ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, channel_name);
- g_free(channel_name);
- if (!ch)
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- devc->digital_group.channels = g_slist_append(
- devc->digital_group.channels, ch);
- }
- devc->digital_group.name = "LA";
- sdi->channel_groups = g_slist_append(sdi->channel_groups,
- &devc->digital_group);
- }
-
- for (i = 0; i < NUM_TIMEBASE; i++) {
- if (!memcmp(&devc->model->min_timebase, &timebases[i], sizeof(uint64_t[2])))
- devc->timebases = &timebases[i];
- if (!memcmp(&devc->model->series->max_timebase, &timebases[i], sizeof(uint64_t[2])))
- devc->num_timebases = &timebases[i] - devc->timebases + 1;
- }
-
- for (i = 0; i < NUM_VDIV; i++)
- if (!memcmp(&devc->model->series->min_vdiv, &vdivs[i], sizeof(uint64_t[2])))
- devc->vdivs = &vdivs[i];
-
- if (!(devc->buffer = g_try_malloc(ACQ_BUFFER_SIZE)))
- return NULL;
- if (!(devc->data = g_try_malloc(ACQ_BUFFER_SIZE * sizeof(float))))
- return NULL;
-
- devc->data_source = DATA_SOURCE_LIVE;
-
- sdi->priv = devc;
-
- return sdi;
-}
-
-static GSList *scan(GSList *options)
-{
- return sr_scpi_scan(di->priv, options, probe_device);
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct sr_scpi_dev_inst *scpi = sdi->conn;
-
- if (sr_scpi_open(scpi) < 0)
- return SR_ERR;
-
- if (rigol_ds_get_dev_cfg(sdi) != SR_OK)
- return SR_ERR;
-
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_scpi_dev_inst *scpi;
- struct dev_context *devc;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- scpi = sdi->conn;
- devc = sdi->priv;
-
- if (devc->model->series->protocol == PROTOCOL_V2)
- rigol_ds_config_set(sdi, ":KEY:LOCK DISABLE");
-
- if (scpi) {
- if (sr_scpi_close(scpi) < 0)
- return SR_ERR;
- sdi->status = SR_ST_INACTIVE;
- }
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- return dev_clear();
-}
-
-static int analog_frame_size(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
- struct sr_channel *ch;
- int analog_channels = 0;
- GSList *l;
-
- for (l = sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type == SR_CHANNEL_ANALOG && ch->enabled)
- analog_channels++;
- }
-
- if (analog_channels == 0)
- return 0;
-
- switch (devc->data_source) {
- case DATA_SOURCE_LIVE:
- return devc->model->series->live_samples;
- case DATA_SOURCE_MEMORY:
- return devc->model->series->buffer_samples / analog_channels;
- default:
- return 0;
- }
-}
-
-static int digital_frame_size(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc = sdi->priv;
-
- switch (devc->data_source) {
- case DATA_SOURCE_LIVE:
- return devc->model->series->live_samples * 2;
- case DATA_SOURCE_MEMORY:
- return devc->model->series->buffer_samples * 2;
- default:
- return 0;
- }
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- const char *tmp_str;
- uint64_t samplerate;
- int analog_channel = -1;
- float smallest_diff = 0.0000000001;
- int idx = -1;
- unsigned i;
-
- if (!sdi || !(devc = sdi->priv))
- return SR_ERR_ARG;
-
- /* If a channel group is specified, it must be a valid one. */
- if (cg && !g_slist_find(sdi->channel_groups, cg)) {
- sr_err("Invalid channel group specified.");
- return SR_ERR;
- }
-
- if (cg) {
- ch = g_slist_nth_data(cg->channels, 0);
- if (!ch)
- return SR_ERR;
- if (ch->type == SR_CHANNEL_ANALOG) {
- if (ch->name[2] < '1' || ch->name[2] > '4')
- return SR_ERR;
- analog_channel = ch->name[2] - '1';
- }
- }
-
- switch (id) {
- case SR_CONF_NUM_TIMEBASE:
- *data = g_variant_new_int32(devc->model->series->num_horizontal_divs);
- break;
- case SR_CONF_NUM_VDIV:
- *data = g_variant_new_int32(NUM_VDIV);
- case SR_CONF_DATA_SOURCE:
- if (devc->data_source == DATA_SOURCE_LIVE)
- *data = g_variant_new_string("Live");
- else if (devc->data_source == DATA_SOURCE_MEMORY)
- *data = g_variant_new_string("Memory");
- else
- *data = g_variant_new_string("Segmented");
- break;
- case SR_CONF_SAMPLERATE:
- if (devc->data_source == DATA_SOURCE_LIVE) {
- samplerate = analog_frame_size(sdi) /
- (devc->timebase * devc->model->series->num_horizontal_divs);
- *data = g_variant_new_uint64(samplerate);
- } else {
- return SR_ERR_NA;
- }
- break;
- case SR_CONF_TRIGGER_SOURCE:
- if (!strcmp(devc->trigger_source, "ACL"))
- tmp_str = "AC Line";
- else if (!strcmp(devc->trigger_source, "CHAN1"))
- tmp_str = "CH1";
- else if (!strcmp(devc->trigger_source, "CHAN2"))
- tmp_str = "CH2";
- else if (!strcmp(devc->trigger_source, "CHAN3"))
- tmp_str = "CH3";
- else if (!strcmp(devc->trigger_source, "CHAN4"))
- tmp_str = "CH4";
- else
- tmp_str = devc->trigger_source;
- *data = g_variant_new_string(tmp_str);
- break;
- case SR_CONF_TRIGGER_SLOPE:
- if (!strcmp(devc->trigger_slope, "POS"))
- tmp_str = "r";
- else if (!strcmp(devc->trigger_slope, "NEG"))
- tmp_str = "f";
- else
- return SR_ERR_NA;
- *data = g_variant_new_string(tmp_str);
- break;
- case SR_CONF_TIMEBASE:
- for (i = 0; i < devc->num_timebases; i++) {
- float tb = (float)devc->timebases[i][0] / devc->timebases[i][1];
- float diff = fabs(devc->timebase - tb);
- if (diff < smallest_diff) {
- smallest_diff = diff;
- idx = i;
- }
- }
- if (idx < 0)
- return SR_ERR_NA;
- *data = g_variant_new("(tt)", devc->timebases[idx][0],
- devc->timebases[idx][1]);
- break;
- case SR_CONF_VDIV:
- if (analog_channel < 0)
- return SR_ERR_NA;
- for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
- float vdiv = (float)vdivs[i][0] / vdivs[i][1];
- float diff = fabs(devc->vdiv[analog_channel] - vdiv);
- if (diff < smallest_diff) {
- smallest_diff = diff;
- idx = i;
- }
- }
- if (idx < 0)
- return SR_ERR_NA;
- *data = g_variant_new("(tt)", vdivs[idx][0], vdivs[idx][1]);
- break;
- case SR_CONF_COUPLING:
- if (analog_channel < 0)
- return SR_ERR_NA;
- *data = g_variant_new_string(devc->coupling[analog_channel]);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- uint64_t p, q;
- double t_dbl;
- unsigned int i, j;
- int ret;
- const char *tmp_str;
- char buffer[16];
-
- if (!(devc = sdi->priv))
- return SR_ERR_ARG;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- /* If a channel group is specified, it must be a valid one. */
- if (cg && !g_slist_find(sdi->channel_groups, cg)) {
- sr_err("Invalid channel group specified.");
- return SR_ERR;
- }
-
- ret = SR_OK;
- switch (id) {
- case SR_CONF_LIMIT_FRAMES:
- devc->limit_frames = g_variant_get_uint64(data);
- break;
- case SR_CONF_TRIGGER_SLOPE:
- tmp_str = g_variant_get_string(data, NULL);
-
- if (!tmp_str || !(tmp_str[0] == 'f' || tmp_str[0] == 'r'))
- return SR_ERR_ARG;
-
- g_free(devc->trigger_slope);
- devc->trigger_slope = g_strdup((tmp_str[0] == 'r') ? "POS" : "NEG");
- ret = rigol_ds_config_set(sdi, ":TRIG:EDGE:SLOP %s", devc->trigger_slope);
- break;
- case SR_CONF_HORIZ_TRIGGERPOS:
- t_dbl = g_variant_get_double(data);
- if (t_dbl < 0.0 || t_dbl > 1.0)
- return SR_ERR;
- devc->horiz_triggerpos = t_dbl;
- /* We have the trigger offset as a percentage of the frame, but
- * need to express this in seconds. */
- t_dbl = -(devc->horiz_triggerpos - 0.5) * devc->timebase * devc->num_timebases;
- g_ascii_formatd(buffer, sizeof(buffer), "%.6f", t_dbl);
- ret = rigol_ds_config_set(sdi, ":TIM:OFFS %s", buffer);
- break;
- case SR_CONF_TIMEBASE:
- g_variant_get(data, "(tt)", &p, &q);
- for (i = 0; i < devc->num_timebases; i++) {
- if (devc->timebases[i][0] == p && devc->timebases[i][1] == q) {
- devc->timebase = (float)p / q;
- g_ascii_formatd(buffer, sizeof(buffer), "%.9f",
- devc->timebase);
- ret = rigol_ds_config_set(sdi, ":TIM:SCAL %s", buffer);
- break;
- }
- }
- if (i == devc->num_timebases)
- ret = SR_ERR_ARG;
- break;
- case SR_CONF_TRIGGER_SOURCE:
- tmp_str = g_variant_get_string(data, NULL);
- for (i = 0; i < ARRAY_SIZE(trigger_sources); i++) {
- if (!strcmp(trigger_sources[i], tmp_str)) {
- g_free(devc->trigger_source);
- devc->trigger_source = g_strdup(trigger_sources[i]);
- if (!strcmp(devc->trigger_source, "AC Line"))
- tmp_str = "ACL";
- else if (!strcmp(devc->trigger_source, "CH1"))
- tmp_str = "CHAN1";
- else if (!strcmp(devc->trigger_source, "CH2"))
- tmp_str = "CHAN2";
- else if (!strcmp(devc->trigger_source, "CH3"))
- tmp_str = "CHAN3";
- else if (!strcmp(devc->trigger_source, "CH4"))
- tmp_str = "CHAN4";
- else
- tmp_str = (char *)devc->trigger_source;
- ret = rigol_ds_config_set(sdi, ":TRIG:EDGE:SOUR %s", tmp_str);
- break;
- }
- }
- if (i == ARRAY_SIZE(trigger_sources))
- ret = SR_ERR_ARG;
- break;
- case SR_CONF_VDIV:
- if (!cg) {
- sr_err("No channel group specified.");
- return SR_ERR_CHANNEL_GROUP;
- }
- g_variant_get(data, "(tt)", &p, &q);
- for (i = 0; i < 2; i++) {
- if (cg == &devc->analog_groups[i]) {
- for (j = 0; j < ARRAY_SIZE(vdivs); j++) {
- if (vdivs[j][0] != p || vdivs[j][1] != q)
- continue;
- devc->vdiv[i] = (float)p / q;
- g_ascii_formatd(buffer, sizeof(buffer), "%.3f",
- devc->vdiv[i]);
- return rigol_ds_config_set(sdi, ":CHAN%d:SCAL %s", i + 1,
- buffer);
- }
- return SR_ERR_ARG;
- }
- }
- return SR_ERR_NA;
- case SR_CONF_COUPLING:
- if (!cg) {
- sr_err("No channel group specified.");
- return SR_ERR_CHANNEL_GROUP;
- }
- tmp_str = g_variant_get_string(data, NULL);
- for (i = 0; i < 2; i++) {
- if (cg == &devc->analog_groups[i]) {
- for (j = 0; j < ARRAY_SIZE(coupling); j++) {
- if (!strcmp(tmp_str, coupling[j])) {
- g_free(devc->coupling[i]);
- devc->coupling[i] = g_strdup(coupling[j]);
- return rigol_ds_config_set(sdi, ":CHAN%d:COUP %s", i + 1,
- devc->coupling[i]);
- }
- }
- return SR_ERR_ARG;
- }
- }
- return SR_ERR_NA;
- case SR_CONF_DATA_SOURCE:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "Live"))
- devc->data_source = DATA_SOURCE_LIVE;
- else if (devc->model->series->protocol >= PROTOCOL_V2
- && !strcmp(tmp_str, "Memory"))
- devc->data_source = DATA_SOURCE_MEMORY;
- else if (devc->model->series->protocol >= PROTOCOL_V3
- && !strcmp(tmp_str, "Segmented"))
- devc->data_source = DATA_SOURCE_SEGMENTED;
- else
- return SR_ERR;
- break;
- default:
- ret = SR_ERR_NA;
- break;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- GVariant *tuple, *rational[2];
- GVariantBuilder gvb;
- unsigned int i;
- struct dev_context *devc = NULL;
-
- if (sdi)
- devc = sdi->priv;
-
- if (key == SR_CONF_SCAN_OPTIONS) {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- return SR_OK;
- } else if (key == SR_CONF_DEVICE_OPTIONS && cg == NULL) {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- return SR_OK;
- }
-
- /* Every other option requires a valid device instance. */
- if (!sdi || !(devc = sdi->priv))
- return SR_ERR_ARG;
-
- /* If a channel group is specified, it must be a valid one. */
- if (cg) {
- if (cg != &devc->analog_groups[0]
- && cg != &devc->analog_groups[1]) {
- sr_err("Invalid channel group specified.");
- return SR_ERR;
- }
- }
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- if (!cg) {
- sr_err("No channel group specified.");
- return SR_ERR_CHANNEL_GROUP;
- }
- if (cg == &devc->digital_group) {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- NULL, 0, sizeof(int32_t));
- return SR_OK;
- } else {
- for (i = 0; i < 2; i++) {
- if (cg == &devc->analog_groups[i]) {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- analog_hwcaps, ARRAY_SIZE(analog_hwcaps), sizeof(int32_t));
- return SR_OK;
- }
- }
- return SR_ERR_NA;
- }
- break;
- case SR_CONF_COUPLING:
- if (!cg) {
- sr_err("No channel group specified.");
- return SR_ERR_CHANNEL_GROUP;
- }
- *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
- break;
- case SR_CONF_VDIV:
- if (!devc)
- /* Can't know this until we have the exact model. */
- return SR_ERR_ARG;
- if (!cg) {
- sr_err("No channel group specified.");
- return SR_ERR_CHANNEL_GROUP;
- }
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (i = 0; i < NUM_VDIV; i++) {
- rational[0] = g_variant_new_uint64(devc->vdivs[i][0]);
- rational[1] = g_variant_new_uint64(devc->vdivs[i][1]);
- tuple = g_variant_new_tuple(rational, 2);
- g_variant_builder_add_value(&gvb, tuple);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TIMEBASE:
- if (!devc)
- /* Can't know this until we have the exact model. */
- return SR_ERR_ARG;
- if (devc->num_timebases <= 0)
- return SR_ERR_NA;
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (i = 0; i < devc->num_timebases; i++) {
- rational[0] = g_variant_new_uint64(devc->timebases[i][0]);
- rational[1] = g_variant_new_uint64(devc->timebases[i][1]);
- tuple = g_variant_new_tuple(rational, 2);
- g_variant_builder_add_value(&gvb, tuple);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TRIGGER_SOURCE:
- if (!devc)
- /* Can't know this until we have the exact model. */
- return SR_ERR_ARG;
- *data = g_variant_new_strv(trigger_sources,
- devc->model->has_digital ? ARRAY_SIZE(trigger_sources) : 4);
- break;
- case SR_CONF_TRIGGER_SLOPE:
- *data = g_variant_new_strv(trigger_slopes, ARRAY_SIZE(trigger_slopes));
- break;
- case SR_CONF_DATA_SOURCE:
- if (!devc)
- /* Can't know this until we have the exact model. */
- return SR_ERR_ARG;
- switch (devc->model->series->protocol) {
- case PROTOCOL_V1:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources) - 2);
- break;
- case PROTOCOL_V2:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources) - 1);
- break;
- default:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
- break;
- }
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct sr_scpi_dev_inst *scpi;
- struct dev_context *devc;
- struct sr_channel *ch;
- struct sr_datafeed_packet packet;
- GSList *l;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- scpi = sdi->conn;
- devc = sdi->priv;
-
- devc->num_frames = 0;
-
- for (l = sdi->channels; l; l = l->next) {
- ch = l->data;
- sr_dbg("handling channel %s", ch->name);
- if (ch->type == SR_CHANNEL_ANALOG) {
- if (ch->enabled)
- devc->enabled_analog_channels = g_slist_append(
- devc->enabled_analog_channels, ch);
- if (ch->enabled != devc->analog_channels[ch->index]) {
- /* Enabled channel is currently disabled, or vice versa. */
- if (rigol_ds_config_set(sdi, ":CHAN%d:DISP %s", ch->index + 1,
- ch->enabled ? "ON" : "OFF") != SR_OK)
- return SR_ERR;
- devc->analog_channels[ch->index] = ch->enabled;
- }
- } else if (ch->type == SR_CHANNEL_LOGIC) {
- if (ch->enabled) {
- devc->enabled_digital_channels = g_slist_append(
- devc->enabled_digital_channels, ch);
- /* Turn on LA module if currently off. */
- if (!devc->la_enabled) {
- if (rigol_ds_config_set(sdi, ":LA:DISP ON") != SR_OK)
- return SR_ERR;
- devc->la_enabled = TRUE;
- }
- }
- if (ch->enabled != devc->digital_channels[ch->index]) {
- /* Enabled channel is currently disabled, or vice versa. */
- if (rigol_ds_config_set(sdi, ":DIG%d:TURN %s", ch->index,
- ch->enabled ? "ON" : "OFF") != SR_OK)
- return SR_ERR;
- devc->digital_channels[ch->index] = ch->enabled;
- }
- }
- }
-
- if (!devc->enabled_analog_channels && !devc->enabled_digital_channels)
- return SR_ERR;
-
- /* Turn off LA module if on and no digital channels selected. */
- if (devc->la_enabled && !devc->enabled_digital_channels)
- if (rigol_ds_config_set(sdi, ":LA:DISP OFF") != SR_OK)
- return SR_ERR;
-
- /* Set memory mode. */
- if (devc->data_source == DATA_SOURCE_SEGMENTED) {
- sr_err("Data source 'Segmented' not yet supported");
- return SR_ERR;
- }
-
- devc->analog_frame_size = analog_frame_size(sdi);
- devc->digital_frame_size = digital_frame_size(sdi);
-
- switch (devc->model->series->protocol) {
- case PROTOCOL_V2:
- if (rigol_ds_config_set(sdi, ":ACQ:MEMD LONG") != SR_OK)
- return SR_ERR;
- break;
- case PROTOCOL_V3:
- /* Apparently for the DS2000 the memory
- * depth can only be set in Running state -
- * this matches the behaviour of the UI. */
- if (rigol_ds_config_set(sdi, ":RUN") != SR_OK)
- return SR_ERR;
- if (rigol_ds_config_set(sdi, ":ACQ:MDEP %d",
- devc->analog_frame_size) != SR_OK)
- return SR_ERR;
- if (rigol_ds_config_set(sdi, ":STOP") != SR_OK)
- return SR_ERR;
- break;
- default:
- break;
- }
-
- if (devc->data_source == DATA_SOURCE_LIVE)
- if (rigol_ds_config_set(sdi, ":RUN") != SR_OK)
- return SR_ERR;
-
- sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 50,
- rigol_ds_receive, (void *)sdi);
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- if (devc->enabled_analog_channels)
- devc->channel_entry = devc->enabled_analog_channels;
- else
- devc->channel_entry = devc->enabled_digital_channels;
-
- if (rigol_ds_capture_start(sdi) != SR_OK)
- return SR_ERR;
-
- /* Start of first frame. */
- packet.type = SR_DF_FRAME_BEGIN;
- sr_session_send(cb_data, &packet);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct sr_scpi_dev_inst *scpi;
- struct sr_datafeed_packet packet;
-
- (void)cb_data;
-
- devc = sdi->priv;
-
- if (sdi->status != SR_ST_ACTIVE) {
- sr_err("Device inactive, can't stop acquisition.");
- return SR_ERR;
- }
-
- /* End of last frame. */
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-
- g_slist_free(devc->enabled_analog_channels);
- g_slist_free(devc->enabled_digital_channels);
- devc->enabled_analog_channels = NULL;
- devc->enabled_digital_channels = NULL;
- scpi = sdi->conn;
- sr_scpi_source_remove(sdi->session, scpi);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver rigol_ds_driver_info = {
- .name = "rigol-ds",
- .longname = "Rigol DS",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = 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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Martin Ling <martin-git@earth.li>
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2013 Mathias Grimmberger <mgri@zaphod.sax.de>
- *
- * 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 <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <math.h>
-#include <ctype.h>
-#include <time.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-/*
- * This is a unified protocol driver for the DS1000 and DS2000 series.
- *
- * DS1000 support tested with a Rigol DS1102D.
- *
- * DS2000 support tested with a Rigol DS2072 using firmware version 01.01.00.02.
- *
- * The Rigol DS2000 series scopes try to adhere to the IEEE 488.2 (I think)
- * standard. If you want to read it - it costs real money...
- *
- * Every response from the scope has a linefeed appended because the
- * standard says so. In principle this could be ignored because sending the
- * next command clears the output queue of the scope. This driver tries to
- * avoid doing that because it may cause an error being generated inside the
- * scope and who knows what bugs the firmware has WRT this.
- *
- * Waveform data is transferred in a format called "arbitrary block program
- * data" specified in IEEE 488.2. See Agilents programming manuals for their
- * 2000/3000 series scopes for a nice description.
- *
- * Each data block from the scope has a header, e.g. "#900000001400".
- * The '#' marks the start of a block.
- * Next is one ASCII decimal digit between 1 and 9, this gives the number of
- * ASCII decimal digits following.
- * Last are the ASCII decimal digits giving the number of bytes (not
- * samples!) in the block.
- *
- * After this header as many data bytes as indicated follow.
- *
- * Each data block has a trailing linefeed too.
- */
-
-static int parse_int(const char *str, int *ret)
-{
- char *e;
- long tmp;
-
- errno = 0;
- tmp = strtol(str, &e, 10);
- if (e == str || *e != '\0') {
- sr_dbg("Failed to parse integer: '%s'", str);
- return SR_ERR;
- }
- if (errno) {
- sr_dbg("Failed to parse integer: '%s', numerical overflow", str);
- return SR_ERR;
- }
- if (tmp > INT_MAX || tmp < INT_MIN) {
- sr_dbg("Failed to parse integer: '%s', value to large/small", str);
- return SR_ERR;
- }
-
- *ret = (int)tmp;
- return SR_OK;
-}
-
-/* Set the next event to wait for in rigol_ds_receive */
-static void rigol_ds_set_wait_event(struct dev_context *devc, enum wait_events event)
-{
- if (event == WAIT_STOP)
- devc->wait_status = 2;
- else
- devc->wait_status = 1;
- devc->wait_event = event;
-}
-
-/*
- * Waiting for a event will return a timeout after 2 to 3 seconds in order
- * to not block the application.
- */
-static int rigol_ds_event_wait(const struct sr_dev_inst *sdi, char status1, char status2)
-{
- char *buf;
- struct dev_context *devc;
- time_t start;
-
- if (!(devc = sdi->priv))
- return SR_ERR;
-
- start = time(NULL);
-
- /*
- * Trigger status may return:
- * "TD" or "T'D" - triggered
- * "AUTO" - autotriggered
- * "RUN" - running
- * "WAIT" - waiting for trigger
- * "STOP" - stopped
- */
-
- if (devc->wait_status == 1) {
- do {
- if (time(NULL) - start >= 3) {
- sr_dbg("Timeout waiting for trigger");
- return SR_ERR_TIMEOUT;
- }
-
- if (sr_scpi_get_string(sdi->conn, ":TRIG:STAT?", &buf) != SR_OK)
- return SR_ERR;
- } while (buf[0] == status1 || buf[0] == status2);
-
- devc->wait_status = 2;
- }
- if (devc->wait_status == 2) {
- do {
- if (time(NULL) - start >= 3) {
- sr_dbg("Timeout waiting for trigger");
- return SR_ERR_TIMEOUT;
- }
-
- if (sr_scpi_get_string(sdi->conn, ":TRIG:STAT?", &buf) != SR_OK)
- return SR_ERR;
- } while (buf[0] != status1 && buf[0] != status2);
-
- rigol_ds_set_wait_event(devc, WAIT_NONE);
- }
-
- return SR_OK;
-}
-
-/*
- * For live capture we need to wait for a new trigger event to ensure that
- * sample data is not returned twice.
- *
- * Unfortunately this will never really work because for sufficiently fast
- * timebases and trigger rates it just can't catch the status changes.
- *
- * What would be needed is a trigger event register with autoreset like the
- * Agilents have. The Rigols don't seem to have anything like this.
- *
- * The workaround is to only wait for the trigger when the timebase is slow
- * enough. Of course this means that for faster timebases sample data can be
- * returned multiple times, this effect is mitigated somewhat by sleeping
- * for about one sweep time in that case.
- */
-static int rigol_ds_trigger_wait(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- long s;
-
- if (!(devc = sdi->priv))
- return SR_ERR;
-
- /*
- * If timebase < 50 msecs/DIV just sleep about one sweep time except
- * for really fast sweeps.
- */
- if (devc->timebase < 0.0499) {
- if (devc->timebase > 0.99e-6) {
- /*
- * Timebase * num hor. divs * 85(%) * 1e6(usecs) / 100
- * -> 85 percent of sweep time
- */
- s = (devc->timebase * devc->model->series->num_horizontal_divs
- * 85e6) / 100L;
- sr_spew("Sleeping for %ld usecs instead of trigger-wait", s);
- g_usleep(s);
- }
- rigol_ds_set_wait_event(devc, WAIT_NONE);
- return SR_OK;
- } else {
- return rigol_ds_event_wait(sdi, 'T', 'A');
- }
-}
-
-/* Wait for scope to got to "Stop" in single shot mode */
-static int rigol_ds_stop_wait(const struct sr_dev_inst *sdi)
-{
- return rigol_ds_event_wait(sdi, 'S', 'S');
-}
-
-/* Check that a single shot acquisition actually succeeded on the DS2000 */
-static int rigol_ds_check_stop(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- int tmp;
-
- if (!(devc = sdi->priv))
- return SR_ERR;
-
- ch = devc->channel_entry->data;
-
- if (devc->model->series->protocol <= PROTOCOL_V2)
- return SR_OK;
-
- if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d",
- ch->index + 1) != SR_OK)
- return SR_ERR;
- /* Check that the number of samples will be accepted */
- if (rigol_ds_config_set(sdi, ":WAV:POIN %d", devc->analog_frame_size) != SR_OK)
- return SR_ERR;
- if (sr_scpi_get_int(sdi->conn, "*ESR?", &tmp) != SR_OK)
- return SR_ERR;
- /*
- * If we get an "Execution error" the scope went from "Single" to
- * "Stop" without actually triggering. There is no waveform
- * displayed and trying to download one will fail - the scope thinks
- * it has 1400 samples (like display memory) and the driver thinks
- * it has a different number of samples.
- *
- * In that case just try to capture something again. Might still
- * fail in interesting ways.
- *
- * Ain't firmware fun?
- */
- if (tmp & 0x10) {
- sr_warn("Single shot acquisition failed, retrying...");
- /* Sleep a bit, otherwise the single shot will often fail */
- g_usleep(500000);
- rigol_ds_config_set(sdi, ":SING");
- rigol_ds_set_wait_event(devc, WAIT_STOP);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-/* Wait for enough data becoming available in scope output buffer */
-static int rigol_ds_block_wait(const struct sr_dev_inst *sdi)
-{
- char *buf;
- struct dev_context *devc;
- time_t start;
- int len;
-
- if (!(devc = sdi->priv))
- return SR_ERR;
-
- if (devc->model->series->protocol >= PROTOCOL_V3) {
-
- start = time(NULL);
-
- do {
- if (time(NULL) - start >= 3) {
- sr_dbg("Timeout waiting for data block");
- return SR_ERR_TIMEOUT;
- }
-
- /*
- * The scope copies data really slowly from sample
- * memory to its output buffer, so try not to bother
- * it too much with SCPI requests but don't wait too
- * long for short sample frame sizes.
- */
- g_usleep(devc->analog_frame_size < 15000 ? 100000 : 1000000);
-
- /* "READ,nnnn" (still working) or "IDLE,nnnn" (finished) */
- if (sr_scpi_get_string(sdi->conn, ":WAV:STAT?", &buf) != SR_OK)
- return SR_ERR;
-
- if (parse_int(buf + 5, &len) != SR_OK)
- return SR_ERR;
- } while (buf[0] == 'R' && len < 1000000);
- }
-
- rigol_ds_set_wait_event(devc, WAIT_NONE);
-
- return SR_OK;
-}
-
-/* Send a configuration setting. */
-SR_PRIV int rigol_ds_config_set(const struct sr_dev_inst *sdi, const char *format, ...)
-{
- struct dev_context *devc = sdi->priv;
- va_list args;
- int ret;
-
- va_start(args, format);
- ret = sr_scpi_send_variadic(sdi->conn, format, args);
- va_end(args);
-
- if (ret != SR_OK)
- return SR_ERR;
-
- if (devc->model->series->protocol == PROTOCOL_V2) {
- /* The DS1000 series needs this stupid delay, *OPC? doesn't work. */
- sr_spew("delay %dms", 100);
- g_usleep(100000);
- return SR_OK;
- } else {
- return sr_scpi_get_opc(sdi->conn);
- }
-}
-
-/* Start capturing a new frameset */
-SR_PRIV int rigol_ds_capture_start(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- gchar *trig_mode;
-
- if (!(devc = sdi->priv))
- return SR_ERR;
-
- sr_dbg("Starting data capture for frameset %lu of %lu",
- devc->num_frames + 1, devc->limit_frames);
-
- switch (devc->model->series->protocol) {
- case PROTOCOL_V1:
- rigol_ds_set_wait_event(devc, WAIT_TRIGGER);
- break;
- case PROTOCOL_V2:
- if (devc->data_source == DATA_SOURCE_LIVE) {
- if (rigol_ds_config_set(sdi, ":WAV:POIN:MODE NORMAL") != SR_OK)
- return SR_ERR;
- rigol_ds_set_wait_event(devc, WAIT_TRIGGER);
- } else {
- if (rigol_ds_config_set(sdi, ":STOP") != SR_OK)
- return SR_ERR;
- if (rigol_ds_config_set(sdi, ":WAV:POIN:MODE RAW") != SR_OK)
- return SR_ERR;
- if (sr_scpi_get_string(sdi->conn, ":TRIG:MODE?", &trig_mode) != SR_OK)
- return SR_ERR;
- if (rigol_ds_config_set(sdi, ":TRIG:%s:SWE SING", trig_mode) != SR_OK)
- return SR_ERR;
- if (rigol_ds_config_set(sdi, ":RUN") != SR_OK)
- return SR_ERR;
- rigol_ds_set_wait_event(devc, WAIT_STOP);
- }
- break;
- case PROTOCOL_V3:
- if (rigol_ds_config_set(sdi, ":WAV:FORM BYTE") != SR_OK)
- return SR_ERR;
- if (devc->data_source == DATA_SOURCE_LIVE) {
- if (rigol_ds_config_set(sdi, ":WAV:MODE NORM") != SR_OK)
- return SR_ERR;
- rigol_ds_set_wait_event(devc, WAIT_TRIGGER);
- } else {
- if (rigol_ds_config_set(sdi, ":WAV:MODE RAW") != SR_OK)
- return SR_ERR;
- if (rigol_ds_config_set(sdi, ":SING") != SR_OK)
- return SR_ERR;
- rigol_ds_set_wait_event(devc, WAIT_STOP);
- }
- break;
- }
-
- return SR_OK;
-}
-
-/* Start reading data from the current channel */
-SR_PRIV int rigol_ds_channel_start(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
-
- if (!(devc = sdi->priv))
- return SR_ERR;
-
- ch = devc->channel_entry->data;
-
- sr_dbg("Starting reading data from channel %d", ch->index + 1);
-
- if (devc->model->series->protocol <= PROTOCOL_V2) {
- if (ch->type == SR_CHANNEL_LOGIC) {
- if (sr_scpi_send(sdi->conn, ":WAV:DATA? DIG") != SR_OK)
- return SR_ERR;
- } else {
- if (sr_scpi_send(sdi->conn, ":WAV:DATA? CHAN%d",
- ch->index + 1) != SR_OK)
- return SR_ERR;
- }
- rigol_ds_set_wait_event(devc, WAIT_NONE);
- } else {
- if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d",
- ch->index + 1) != SR_OK)
- return SR_ERR;
- if (devc->data_source != DATA_SOURCE_LIVE) {
- if (rigol_ds_config_set(sdi, ":WAV:RES") != SR_OK)
- return SR_ERR;
- if (rigol_ds_config_set(sdi, ":WAV:BEG") != SR_OK)
- return SR_ERR;
- }
- }
-
- rigol_ds_set_wait_event(devc, WAIT_BLOCK);
-
- devc->num_channel_bytes = 0;
- devc->num_header_bytes = 0;
- devc->num_block_bytes = 0;
-
- return SR_OK;
-}
-
-/* Read the header of a data block */
-static int rigol_ds_read_header(struct sr_dev_inst *sdi)
-{
- struct sr_scpi_dev_inst *scpi = sdi->conn;
- struct dev_context *devc = sdi->priv;
- char *buf = (char *) devc->buffer;
- size_t header_length;
- int ret;
-
- /* Try to read the hashsign and length digit. */
- if (devc->num_header_bytes < 2) {
- ret = sr_scpi_read_data(scpi, buf + devc->num_header_bytes,
- 2 - devc->num_header_bytes);
- if (ret < 0) {
- sr_err("Read error while reading data header.");
- return SR_ERR;
- }
- devc->num_header_bytes += ret;
- }
-
- if (devc->num_header_bytes < 2)
- return 0;
-
- if (buf[0] != '#' || !isdigit(buf[1]) || buf[1] == '0') {
- sr_err("Received invalid data block header '%c%c'.", buf[0], buf[1]);
- return SR_ERR;
- }
-
- header_length = 2 + buf[1] - '0';
-
- /* Try to read the length. */
- if (devc->num_header_bytes < header_length) {
- ret = sr_scpi_read_data(scpi, buf + devc->num_header_bytes,
- header_length - devc->num_header_bytes);
- if (ret < 0) {
- sr_err("Read error while reading data header.");
- return SR_ERR;
- }
- devc->num_header_bytes += ret;
- }
-
- if (devc->num_header_bytes < header_length)
- return 0;
-
- /* Read the data length. */
- buf[header_length] = '\0';
-
- if (parse_int(buf + 2, &ret) != SR_OK) {
- sr_err("Received invalid data block length '%s'.", buf + 2);
- return -1;
- }
-
- sr_dbg("Received data block header: '%s' -> block length %d", buf, ret);
-
- return ret;
-}
-
-SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct sr_scpi_dev_inst *scpi;
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct sr_datafeed_logic logic;
- double vdiv, offset;
- int len, i, vref;
- struct sr_channel *ch;
- gsize expected_data_bytes;
-
- (void)fd;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- scpi = sdi->conn;
-
- if (revents == G_IO_IN || revents == 0) {
- switch(devc->wait_event) {
- case WAIT_NONE:
- break;
- case WAIT_TRIGGER:
- if (rigol_ds_trigger_wait(sdi) != SR_OK)
- return TRUE;
- if (rigol_ds_channel_start(sdi) != SR_OK)
- return TRUE;
- return TRUE;
- case WAIT_BLOCK:
- if (rigol_ds_block_wait(sdi) != SR_OK)
- return TRUE;
- break;
- case WAIT_STOP:
- if (rigol_ds_stop_wait(sdi) != SR_OK)
- return TRUE;
- if (rigol_ds_check_stop(sdi) != SR_OK)
- return TRUE;
- if (rigol_ds_channel_start(sdi) != SR_OK)
- return TRUE;
- return TRUE;
- default:
- sr_err("BUG: Unknown event target encountered");
- }
-
- ch = devc->channel_entry->data;
-
- expected_data_bytes = ch->type == SR_CHANNEL_ANALOG ?
- devc->analog_frame_size : devc->digital_frame_size;
-
- if (devc->num_block_bytes == 0) {
- if (devc->model->series->protocol >= PROTOCOL_V3)
- if (sr_scpi_send(sdi->conn, ":WAV:DATA?") != SR_OK)
- return TRUE;
-
- if (sr_scpi_read_begin(scpi) != SR_OK)
- return TRUE;
-
- if (devc->format == FORMAT_IEEE488_2) {
- sr_dbg("New block header expected");
- len = rigol_ds_read_header(sdi);
- if (len == 0)
- /* Still reading the header. */
- return TRUE;
- if (len == -1) {
- sr_err("Read error, aborting capture.");
- packet.type = SR_DF_FRAME_END;
- sr_session_send(cb_data, &packet);
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
- /* At slow timebases in live capture the DS2072
- * sometimes returns "short" data blocks, with
- * apparently no way to get the rest of the data.
- * Discard these, the complete data block will
- * appear eventually.
- */
- if (devc->data_source == DATA_SOURCE_LIVE
- && (unsigned)len < expected_data_bytes) {
- sr_dbg("Discarding short data block");
- sr_scpi_read_data(scpi, (char *)devc->buffer, len + 1);
- return TRUE;
- }
- devc->num_block_bytes = len;
- } else {
- devc->num_block_bytes = expected_data_bytes;
- }
- devc->num_block_read = 0;
- }
-
- len = devc->num_block_bytes - devc->num_block_read;
- if (len > ACQ_BUFFER_SIZE)
- len = ACQ_BUFFER_SIZE;
- sr_dbg("Requesting read of %d bytes", len);
-
- len = sr_scpi_read_data(scpi, (char *)devc->buffer, len);
-
- if (len == -1) {
- sr_err("Read error, aborting capture.");
- packet.type = SR_DF_FRAME_END;
- sr_session_send(cb_data, &packet);
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
-
- sr_dbg("Received %d bytes.", len);
-
- devc->num_block_read += len;
-
- if (ch->type == SR_CHANNEL_ANALOG) {
- vref = devc->vert_reference[ch->index];
- vdiv = devc->vdiv[ch->index] / 25.6;
- offset = devc->vert_offset[ch->index];
- if (devc->model->series->protocol >= PROTOCOL_V3)
- for (i = 0; i < len; i++)
- devc->data[i] = ((int)devc->buffer[i] - vref) * vdiv - offset;
- else
- for (i = 0; i < len; i++)
- devc->data[i] = (128 - devc->buffer[i]) * vdiv - offset;
- analog.channels = g_slist_append(NULL, ch);
- analog.num_samples = len;
- analog.data = devc->data;
- analog.mq = SR_MQ_VOLTAGE;
- analog.unit = SR_UNIT_VOLT;
- analog.mqflags = 0;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(cb_data, &packet);
- g_slist_free(analog.channels);
- } else {
- logic.length = len;
- logic.unitsize = 2;
- logic.data = devc->buffer;
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- sr_session_send(cb_data, &packet);
- }
-
- if (devc->num_block_read == devc->num_block_bytes) {
- sr_dbg("Block has been completed");
- if (devc->model->series->protocol >= PROTOCOL_V3) {
- /* Discard the terminating linefeed */
- sr_scpi_read_data(scpi, (char *)devc->buffer, 1);
- }
- if (devc->format == FORMAT_IEEE488_2) {
- /* Prepare for possible next block */
- devc->num_header_bytes = 0;
- devc->num_block_bytes = 0;
- if (devc->data_source != DATA_SOURCE_LIVE)
- rigol_ds_set_wait_event(devc, WAIT_BLOCK);
- }
- if (!sr_scpi_read_complete(scpi)) {
- sr_err("Read should have been completed");
- packet.type = SR_DF_FRAME_END;
- sr_session_send(cb_data, &packet);
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
- devc->num_block_read = 0;
- } else {
- sr_dbg("%d of %d block bytes read", devc->num_block_read, devc->num_block_bytes);
- }
-
- devc->num_channel_bytes += len;
-
- if (devc->num_channel_bytes < expected_data_bytes)
- /* Don't have the full data for this channel yet, re-run. */
- return TRUE;
-
- /* End of data for this channel. */
- if (devc->model->series->protocol >= PROTOCOL_V3) {
- /* Signal end of data download to scope */
- if (devc->data_source != DATA_SOURCE_LIVE)
- /*
- * This causes a query error, without it switching
- * to the next channel causes an error. Fun with
- * firmware...
- */
- rigol_ds_config_set(sdi, ":WAV:END");
- }
-
- if (ch->type == SR_CHANNEL_ANALOG
- && devc->channel_entry->next != NULL) {
- /* We got the frame for this analog channel, but
- * there's another analog channel. */
- devc->channel_entry = devc->channel_entry->next;
- rigol_ds_channel_start(sdi);
- } else {
- /* Done with all analog channels in this frame. */
- if (devc->enabled_digital_channels
- && devc->channel_entry != devc->enabled_digital_channels) {
- /* Now we need to get the digital data. */
- devc->channel_entry = devc->enabled_digital_channels;
- rigol_ds_channel_start(sdi);
- } else {
- /* Done with this frame. */
- packet.type = SR_DF_FRAME_END;
- sr_session_send(cb_data, &packet);
-
- if (++devc->num_frames == devc->limit_frames) {
- /* Last frame, stop capture. */
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- } else {
- /* Get the next frame, starting with the first analog channel. */
- if (devc->enabled_analog_channels)
- devc->channel_entry = devc->enabled_analog_channels;
- else
- devc->channel_entry = devc->enabled_digital_channels;
-
- rigol_ds_capture_start(sdi);
-
- /* Start of next frame. */
- packet.type = SR_DF_FRAME_BEGIN;
- sr_session_send(cb_data, &packet);
- }
- }
- }
- }
-
- return TRUE;
-}
-
-SR_PRIV int rigol_ds_get_dev_cfg(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- char *t_s, *cmd;
- unsigned int i;
- int res;
-
- devc = sdi->priv;
-
- /* Analog channel state. */
- for (i = 0; i < devc->model->analog_channels; i++) {
- cmd = g_strdup_printf(":CHAN%d:DISP?", i + 1);
- res = sr_scpi_get_string(sdi->conn, cmd, &t_s);
- g_free(cmd);
- if (res != SR_OK)
- return SR_ERR;
- devc->analog_channels[i] = !strcmp(t_s, "ON") || !strcmp(t_s, "1");
- }
- sr_dbg("Current analog channel state:");
- for (i = 0; i < devc->model->analog_channels; i++)
- sr_dbg("CH%d %s", i + 1, devc->analog_channels[i] ? "on" : "off");
-
- /* Digital channel state. */
- if (devc->model->has_digital) {
- if (sr_scpi_get_string(sdi->conn, ":LA:DISP?", &t_s) != SR_OK)
- return SR_ERR;
- devc->la_enabled = !strcmp(t_s, "ON") ? TRUE : FALSE;
- sr_dbg("Logic analyzer %s, current digital channel state:",
- devc->la_enabled ? "enabled" : "disabled");
- for (i = 0; i < 16; i++) {
- cmd = g_strdup_printf(":DIG%d:TURN?", i);
- res = sr_scpi_get_string(sdi->conn, cmd, &t_s);
- g_free(cmd);
- if (res != SR_OK)
- return SR_ERR;
- devc->digital_channels[i] = !strcmp(t_s, "ON") ? TRUE : FALSE;
- g_free(t_s);
- sr_dbg("D%d: %s", i, devc->digital_channels[i] ? "on" : "off");
- }
- }
-
- /* Timebase. */
- if (sr_scpi_get_float(sdi->conn, ":TIM:SCAL?", &devc->timebase) != SR_OK)
- return SR_ERR;
- sr_dbg("Current timebase %g", devc->timebase);
-
- /* Vertical gain. */
- for (i = 0; i < devc->model->analog_channels; i++) {
- cmd = g_strdup_printf(":CHAN%d:SCAL?", i + 1);
- res = sr_scpi_get_float(sdi->conn, cmd, &devc->vdiv[i]);
- g_free(cmd);
- if (res != SR_OK)
- return SR_ERR;
- }
- sr_dbg("Current vertical gain:");
- for (i = 0; i < devc->model->analog_channels; i++)
- sr_dbg("CH%d %g", i + 1, devc->vdiv[i]);
-
- sr_dbg("Current vertical reference:");
- if (devc->model->series->protocol >= PROTOCOL_V3) {
- /* Vertical reference - not certain if this is the place to read it. */
- for (i = 0; i < devc->model->analog_channels; i++) {
- if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d", i + 1) != SR_OK)
- return SR_ERR;
- if (sr_scpi_get_int(sdi->conn, ":WAV:YREF?", &devc->vert_reference[i]) != SR_OK)
- return SR_ERR;
- sr_dbg("CH%d %d", i + 1, devc->vert_reference[i]);
- }
- }
-
- /* Vertical offset. */
- for (i = 0; i < devc->model->analog_channels; i++) {
- cmd = g_strdup_printf(":CHAN%d:OFFS?", i + 1);
- res = sr_scpi_get_float(sdi->conn, cmd, &devc->vert_offset[i]);
- g_free(cmd);
- if (res != SR_OK)
- return SR_ERR;
- }
- sr_dbg("Current vertical offset:");
- for (i = 0; i < devc->model->analog_channels; i++)
- sr_dbg("CH%d %g", i + 1, devc->vert_offset[i]);
-
- /* Coupling. */
- for (i = 0; i < devc->model->analog_channels; i++) {
- cmd = g_strdup_printf(":CHAN%d:COUP?", i + 1);
- res = sr_scpi_get_string(sdi->conn, cmd, &devc->coupling[i]);
- g_free(cmd);
- if (res != SR_OK)
- return SR_ERR;
- }
- sr_dbg("Current coupling:");
- for (i = 0; i < devc->model->analog_channels; i++)
- sr_dbg("CH%d %s", i + 1, devc->coupling[i]);
-
- /* Trigger source. */
- if (sr_scpi_get_string(sdi->conn, ":TRIG:EDGE:SOUR?", &devc->trigger_source) != SR_OK)
- return SR_ERR;
- sr_dbg("Current trigger source %s", devc->trigger_source);
-
- /* Horizontal trigger position. */
- if (sr_scpi_get_float(sdi->conn, ":TIM:OFFS?", &devc->horiz_triggerpos) != SR_OK)
- return SR_ERR;
- sr_dbg("Current horizontal trigger position %g", devc->horiz_triggerpos);
-
- /* Trigger slope. */
- if (sr_scpi_get_string(sdi->conn, ":TRIG:EDGE:SLOP?", &devc->trigger_slope) != SR_OK)
- return SR_ERR;
- sr_dbg("Current trigger slope %s", devc->trigger_slope);
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Martin Ling <martin-git@earth.li>
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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_RIGOL_DS_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_RIGOL_DS_PROTOCOL_H
-
-#include <stdint.h>
-#include <stdbool.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "rigol-ds"
-
-/* Size of acquisition buffers */
-#define ACQ_BUFFER_SIZE 32768
-
-#define MAX_ANALOG_CHANNELS 4
-#define MAX_DIGITAL_CHANNELS 16
-
-enum protocol_version {
- PROTOCOL_V1, /* VS5000 */
- PROTOCOL_V2, /* DS1000 */
- PROTOCOL_V3, /* DS2000, DSO1000 */
-};
-
-enum data_format {
- /* Used by DS1000 versions up to 2.02, and VS5000 series */
- FORMAT_RAW,
- /* Used by DS1000 versions from 2.04 onwards and all later series */
- FORMAT_IEEE488_2,
-};
-
-enum data_source {
- DATA_SOURCE_LIVE,
- DATA_SOURCE_MEMORY,
- DATA_SOURCE_SEGMENTED,
-};
-
-struct rigol_ds_vendor {
- const char *name;
- const char *full_name;
-};
-
-struct rigol_ds_series {
- const struct rigol_ds_vendor *vendor;
- const char *name;
- enum protocol_version protocol;
- enum data_format format;
- uint64_t max_timebase[2];
- uint64_t min_vdiv[2];
- int num_horizontal_divs;
- int live_samples;
- int buffer_samples;
-};
-
-struct rigol_ds_model {
- const struct rigol_ds_series *series;
- const char *name;
- uint64_t min_timebase[2];
- unsigned int analog_channels;
- bool has_digital;
-};
-
-enum wait_events {
- WAIT_NONE, /* Don't wait */
- WAIT_TRIGGER, /* Wait for trigger (only live capture) */
- WAIT_BLOCK, /* Wait for block data (only when reading sample mem) */
- WAIT_STOP, /* Wait for scope stopping (only single shots) */
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Device model */
- const struct rigol_ds_model *model;
- enum data_format format;
-
- /* Device properties */
- const uint64_t (*timebases)[2];
- uint64_t num_timebases;
- const uint64_t (*vdivs)[2];
- uint64_t num_vdivs;
-
- /* Channel groups */
- struct sr_channel_group analog_groups[MAX_ANALOG_CHANNELS];
- struct sr_channel_group digital_group;
-
- /* Acquisition settings */
- GSList *enabled_analog_channels;
- GSList *enabled_digital_channels;
- uint64_t limit_frames;
- void *cb_data;
- enum data_source data_source;
- uint64_t analog_frame_size;
- uint64_t digital_frame_size;
-
- /* Device settings */
- gboolean analog_channels[MAX_ANALOG_CHANNELS];
- gboolean digital_channels[MAX_DIGITAL_CHANNELS];
- gboolean la_enabled;
- float timebase;
- float vdiv[MAX_ANALOG_CHANNELS];
- int vert_reference[MAX_ANALOG_CHANNELS];
- float vert_offset[MAX_ANALOG_CHANNELS];
- char *trigger_source;
- float horiz_triggerpos;
- char *trigger_slope;
- char *coupling[MAX_ANALOG_CHANNELS];
-
- /* Operational state */
-
- /* Number of frames received in total. */
- uint64_t num_frames;
- /* GSList entry for the current channel. */
- GSList *channel_entry;
- /* Number of bytes received for current channel. */
- uint64_t num_channel_bytes;
- /* Number of bytes of block header read */
- uint64_t num_header_bytes;
- /* Number of bytes in current data block, if 0 block header expected */
- uint64_t num_block_bytes;
- /* Number of data block bytes already read */
- uint64_t num_block_read;
- /* What to wait for in *_receive */
- enum wait_events wait_event;
- /* Trigger/block copying/stop waiting status */
- int wait_status;
- /* Acq buffers used for reading from the scope and sending data to app */
- unsigned char *buffer;
- float *data;
-};
-
-SR_PRIV int rigol_ds_config_set(const struct sr_dev_inst *sdi, const char *format, ...);
-SR_PRIV int rigol_ds_capture_start(const struct sr_dev_inst *sdi);
-SR_PRIV int rigol_ds_channel_start(const struct sr_dev_inst *sdi);
-SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data);
-SR_PRIV int rigol_ds_get_dev_cfg(const struct sr_dev_inst *sdi);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 <glib.h>
-#include <libusb.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-#define LOGIC16_VID 0x21a9
-#define LOGIC16_PID 0x1001
-
-#define USB_INTERFACE 0
-#define USB_CONFIGURATION 1
-#define FX2_FIRMWARE FIRMWARE_DIR "/saleae-logic16-fx2.fw"
-
-#define MAX_RENUM_DELAY_MS 3000
-#define NUM_SIMUL_TRANSFERS 32
-
-SR_PRIV struct sr_dev_driver saleae_logic16_driver_info;
-static struct sr_dev_driver *di = &saleae_logic16_driver_info;
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_SAMPLERATE,
- SR_CONF_VOLTAGE_THRESHOLD,
- SR_CONF_TRIGGER_MATCH,
-
- /* These are really implemented in the driver, not the hardware. */
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_CONTINUOUS,
-};
-
-static const int32_t soft_trigger_matches[] = {
- SR_TRIGGER_ZERO,
- SR_TRIGGER_ONE,
- SR_TRIGGER_RISING,
- SR_TRIGGER_FALLING,
- SR_TRIGGER_EDGE,
-};
-
-static const char *channel_names[] = {
- "0", "1", "2", "3", "4", "5", "6", "7", "8",
- "9", "10", "11", "12", "13", "14", "15",
- NULL,
-};
-
-static const struct {
- enum voltage_range range;
- gdouble low;
- gdouble high;
-} volt_thresholds[] = {
- { VOLTAGE_RANGE_18_33_V, 0.7, 1.4 },
- { VOLTAGE_RANGE_5_V, 1.4, 3.6 },
-};
-
-static const uint64_t samplerates[] = {
- SR_KHZ(500),
- SR_MHZ(1),
- SR_MHZ(2),
- SR_MHZ(4),
- SR_MHZ(5),
- SR_MHZ(8),
- SR_MHZ(10),
- SR_KHZ(12500),
- SR_MHZ(16),
- SR_MHZ(25),
- SR_MHZ(32),
- SR_MHZ(40),
- SR_MHZ(80),
- SR_MHZ(100),
-};
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static gboolean check_conf_profile(libusb_device *dev)
-{
- struct libusb_device_descriptor des;
- struct libusb_device_handle *hdl;
- gboolean ret;
- unsigned char strdesc[64];
-
- hdl = NULL;
- ret = FALSE;
- while (!ret) {
- /* Assume the FW has not been loaded, unless proven wrong. */
- if (libusb_get_device_descriptor(dev, &des) != 0)
- break;
-
- if (libusb_open(dev, &hdl) != 0)
- break;
-
- if (libusb_get_string_descriptor_ascii(hdl,
- des.iManufacturer, strdesc, sizeof(strdesc)) < 0)
- break;
- if (strcmp((const char *)strdesc, "Saleae LLC"))
- break;
-
- if (libusb_get_string_descriptor_ascii(hdl,
- des.iProduct, strdesc, sizeof(strdesc)) < 0)
- break;
- if (strcmp((const char *)strdesc, "Logic S/16"))
- break;
-
- /* If we made it here, it must be a configured Logic16. */
- ret = TRUE;
- }
- if (hdl)
- libusb_close(hdl);
-
- return ret;
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_usb_dev_inst *usb;
- struct sr_channel *ch;
- struct sr_config *src;
- GSList *l, *devices, *conn_devices;
- struct libusb_device_descriptor des;
- libusb_device **devlist;
- int devcnt, ret, i, j;
- const char *conn;
-
- drvc = di->priv;
-
- 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)
- conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
- else
- conn_devices = NULL;
-
- /* Find all Logic16 devices and upload firmware to them. */
- devices = NULL;
- libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- for (i = 0; devlist[i]; i++) {
- if (conn) {
- usb = NULL;
- for (l = conn_devices; l; l = l->next) {
- usb = l->data;
- if (usb->bus == libusb_get_bus_number(devlist[i])
- && usb->address == libusb_get_device_address(devlist[i]))
- break;
- }
- if (!l)
- /* This device matched none of the ones that
- * matched the conn specification. */
- continue;
- }
-
- if ((ret = libusb_get_device_descriptor(devlist[i], &des)) != 0) {
- sr_warn("Failed to get device descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
-
- if (des.idVendor != LOGIC16_VID || des.idProduct != LOGIC16_PID)
- continue;
-
- devcnt = g_slist_length(drvc->instances);
- sdi = sr_dev_inst_new(devcnt, SR_ST_INITIALIZING,
- "Saleae", "Logic16", NULL);
- if (!sdi)
- return NULL;
- sdi->driver = di;
-
- for (j = 0; channel_names[j]; j++) {
- if (!(ch = sr_channel_new(j, SR_CHANNEL_LOGIC, TRUE,
- channel_names[j])))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
- return NULL;
- devc->selected_voltage_range = VOLTAGE_RANGE_18_33_V;
- sdi->priv = devc;
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- if (check_conf_profile(devlist[i])) {
- /* Already has the firmware, so fix the new address. */
- sr_dbg("Found a Logic16 device.");
- sdi->status = SR_ST_INACTIVE;
- sdi->inst_type = SR_INST_USB;
- sdi->conn = sr_usb_dev_inst_new(
- libusb_get_bus_number(devlist[i]),
- libusb_get_device_address(devlist[i]), NULL);
- } else {
- if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION,
- FX2_FIRMWARE) == SR_OK)
- /* Store when this device's FW was updated. */
- devc->fw_updated = g_get_monotonic_time();
- else
- sr_err("Firmware upload failed for "
- "device %d.", devcnt);
- sdi->inst_type = SR_INST_USB;
- sdi->conn = sr_usb_dev_inst_new(
- libusb_get_bus_number(devlist[i]), 0xff, NULL);
- }
- }
- libusb_free_device_list(devlist, 1);
- g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int logic16_dev_open(struct sr_dev_inst *sdi)
-{
- libusb_device **devlist;
- struct sr_usb_dev_inst *usb;
- struct libusb_device_descriptor des;
- struct drv_context *drvc;
- int ret, skip, i, device_count;
-
- drvc = di->priv;
- usb = sdi->conn;
-
- if (sdi->status == SR_ST_ACTIVE)
- /* Device is already in use. */
- return SR_ERR;
-
- skip = 0;
- device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- if (device_count < 0) {
- sr_err("Failed to get device list: %s.",
- libusb_error_name(device_count));
- return SR_ERR;
- }
-
- for (i = 0; i < device_count; i++) {
- if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
- sr_err("Failed to get device descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
-
- if (des.idVendor != LOGIC16_VID || des.idProduct != LOGIC16_PID)
- continue;
-
- if (sdi->status == SR_ST_INITIALIZING) {
- if (skip != sdi->index) {
- /* Skip devices of this type that aren't the one we want. */
- skip += 1;
- continue;
- }
- } else if (sdi->status == SR_ST_INACTIVE) {
- /*
- * This device is fully enumerated, so we need to find
- * this device by vendor, product, bus and address.
- */
- if (libusb_get_bus_number(devlist[i]) != usb->bus
- || libusb_get_device_address(devlist[i]) != usb->address)
- /* This is not the one. */
- continue;
- }
-
- if (!(ret = libusb_open(devlist[i], &usb->devhdl))) {
- if (usb->address == 0xff)
- /*
- * First time we touch this device after FW
- * upload, so we don't know the address yet.
- */
- usb->address = libusb_get_device_address(devlist[i]);
- } else {
- sr_err("Failed to open device: %s.",
- libusb_error_name(ret));
- break;
- }
-
- ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
- if (ret == LIBUSB_ERROR_BUSY) {
- sr_err("Unable to claim USB interface. Another "
- "program or driver has already claimed it.");
- break;
- } else if (ret == LIBUSB_ERROR_NO_DEVICE) {
- sr_err("Device has been disconnected.");
- break;
- } else if (ret != 0) {
- sr_err("Unable to claim interface: %s.",
- libusb_error_name(ret));
- break;
- }
-
- if ((ret = logic16_init_device(sdi)) != SR_OK) {
- sr_err("Failed to init device.");
- break;
- }
-
- sdi->status = SR_ST_ACTIVE;
- sr_info("Opened device %d on %d.%d, interface %d.",
- sdi->index, usb->bus, usb->address, USB_INTERFACE);
-
- break;
- }
- libusb_free_device_list(devlist, 1);
-
- if (sdi->status != SR_ST_ACTIVE) {
- if (usb->devhdl) {
- libusb_release_interface(usb->devhdl, USB_INTERFACE);
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
- }
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int ret;
- int64_t timediff_us, timediff_ms;
-
- devc = sdi->priv;
-
- /*
- * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS
- * milliseconds for the FX2 to renumerate.
- */
- ret = SR_ERR;
- if (devc->fw_updated > 0) {
- sr_info("Waiting for device to reset.");
- /* Takes >= 300ms for the FX2 to be gone from the USB bus. */
- g_usleep(300 * 1000);
- timediff_ms = 0;
- while (timediff_ms < MAX_RENUM_DELAY_MS) {
- if ((ret = logic16_dev_open(sdi)) == SR_OK)
- break;
- g_usleep(100 * 1000);
-
- timediff_us = g_get_monotonic_time() - devc->fw_updated;
- timediff_ms = timediff_us / 1000;
- sr_spew("Waited %" PRIi64 "ms.", timediff_ms);
- }
- if (ret != SR_OK) {
- sr_err("Device failed to renumerate.");
- return SR_ERR;
- }
- sr_info("Device came back after %" PRIi64 "ms.", timediff_ms);
- } else {
- sr_info("Firmware upload was not needed.");
- ret = logic16_dev_open(sdi);
- }
-
- if (ret != SR_OK) {
- sr_err("Unable to open device.");
- return SR_ERR;
- }
-
- if (devc->cur_samplerate == 0) {
- /* Samplerate hasn't been set; default to the slowest one. */
- devc->cur_samplerate = samplerates[0];
- }
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
-
- usb = sdi->conn;
- if (usb->devhdl == NULL)
- return SR_ERR;
-
- sr_info("Closing device %d on %d.%d interface %d.",
- sdi->index, usb->bus, usb->address, USB_INTERFACE);
- libusb_release_interface(usb->devhdl, USB_INTERFACE);
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- int ret;
- struct drv_context *drvc;
-
- if (!(drvc = di->priv))
- /* Can get called on an unused driver, doesn't matter. */
- return SR_OK;
-
-
- ret = std_dev_clear(di, NULL);
- g_free(drvc);
- di->priv = NULL;
-
- return ret;
-}
-
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- GVariant *range[2];
- char str[128];
- int ret;
- unsigned int i;
-
- (void)cg;
-
- ret = SR_OK;
- switch (key) {
- case SR_CONF_CONN:
- if (!sdi || !sdi->conn)
- return SR_ERR_ARG;
- usb = sdi->conn;
- if (usb->address == 255)
- /* Device still needs to re-enumerate after firmware
- * upload, so we don't know its (future) address. */
- return SR_ERR;
- snprintf(str, 128, "%d.%d", usb->bus, usb->address);
- *data = g_variant_new_string(str);
- break;
- case SR_CONF_SAMPLERATE:
- if (!sdi)
- return SR_ERR;
- devc = sdi->priv;
- *data = g_variant_new_uint64(devc->cur_samplerate);
- break;
- case SR_CONF_VOLTAGE_THRESHOLD:
- if (!sdi)
- return SR_ERR;
- devc = sdi->priv;
- ret = SR_ERR;
- for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
- if (devc->selected_voltage_range !=
- volt_thresholds[i].range)
- continue;
- range[0] = g_variant_new_double(volt_thresholds[i].low);
- range[1] = g_variant_new_double(volt_thresholds[i].high);
- *data = g_variant_new_tuple(range, 2);
- ret = SR_OK;
- break;
- }
- break;
- default:
- return SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- gdouble low, high;
- int ret;
- unsigned int i;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- ret = SR_OK;
- switch (key) {
- case SR_CONF_SAMPLERATE:
- devc->cur_samplerate = g_variant_get_uint64(data);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- break;
- case SR_CONF_VOLTAGE_THRESHOLD:
- g_variant_get(data, "(dd)", &low, &high);
- ret = SR_ERR_ARG;
- for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
- if (fabs(volt_thresholds[i].low - low) < 0.1 &&
- fabs(volt_thresholds[i].high - high) < 0.1) {
- devc->selected_voltage_range =
- volt_thresholds[i].range;
- ret = SR_OK;
- break;
- }
- }
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- GVariant *gvar, *range[2];
- GVariantBuilder gvb;
- int ret;
- unsigned int i;
-
- (void)sdi;
- (void)cg;
-
- ret = SR_OK;
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- samplerates, ARRAY_SIZE(samplerates), sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_VOLTAGE_THRESHOLD:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
- range[0] = g_variant_new_double(volt_thresholds[i].low);
- range[1] = g_variant_new_double(volt_thresholds[i].high);
- gvar = g_variant_new_tuple(range, 2);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TRIGGER_MATCH:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- soft_trigger_matches, ARRAY_SIZE(soft_trigger_matches),
- sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return ret;
-}
-
-static void abort_acquisition(struct dev_context *devc)
-{
- int i;
-
- devc->sent_samples = -1;
-
- for (i = devc->num_transfers - 1; i >= 0; i--) {
- if (devc->transfers[i])
- libusb_cancel_transfer(devc->transfers[i]);
- }
-}
-
-static unsigned int bytes_per_ms(struct dev_context *devc)
-{
- return devc->cur_samplerate * devc->num_channels / 8000;
-}
-
-static size_t get_buffer_size(struct dev_context *devc)
-{
- size_t s;
-
- /*
- * The buffer should be large enough to hold 10ms of data and
- * a multiple of 512.
- */
- s = 10 * bytes_per_ms(devc);
- return (s + 511) & ~511;
-}
-
-static unsigned int get_number_of_transfers(struct dev_context *devc)
-{
- unsigned int n;
-
- /* Total buffer size should be able to hold about 500ms of data. */
- n = 500 * bytes_per_ms(devc) / get_buffer_size(devc);
-
- if (n > NUM_SIMUL_TRANSFERS)
- return NUM_SIMUL_TRANSFERS;
-
- return n;
-}
-
-static unsigned int get_timeout(struct dev_context *devc)
-{
- size_t total_size;
- unsigned int timeout;
-
- total_size = get_buffer_size(devc) * get_number_of_transfers(devc);
- timeout = total_size / bytes_per_ms(devc);
- return timeout + timeout / 4; /* Leave a headroom of 25% percent. */
-}
-
-static int configure_channels(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_channel *ch;
- GSList *l;
- uint16_t channel_bit;
-
- devc = sdi->priv;
-
- devc->cur_channels = 0;
- devc->num_channels = 0;
- for (l = sdi->channels; l; l = l->next) {
- ch = (struct sr_channel *)l->data;
- if (ch->enabled == FALSE)
- continue;
-
- channel_bit = 1 << (ch->index);
-
- devc->cur_channels |= channel_bit;
-
-#ifdef WORDS_BIGENDIAN
- /*
- * Output logic data should be stored in little endian format.
- * To speed things up during conversion, do the switcharoo
- * here instead.
- */
- channel_bit = 1 << (ch->index ^ 8);
-#endif
-
- devc->channel_masks[devc->num_channels++] = channel_bit;
- }
-
- return SR_OK;
-}
-
-static int receive_data(int fd, int revents, void *cb_data)
-{
- struct timeval tv;
- struct dev_context *devc;
- struct drv_context *drvc;
- const struct sr_dev_inst *sdi;
-
- (void)fd;
- (void)revents;
-
- sdi = cb_data;
- drvc = di->priv;
- devc = sdi->priv;
-
- tv.tv_sec = tv.tv_usec = 0;
- libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
-
- if (devc->sent_samples == -2) {
- logic16_abort_acquisition(sdi);
- abort_acquisition(devc);
- }
-
- return TRUE;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct drv_context *drvc;
- struct sr_usb_dev_inst *usb;
- struct sr_trigger *trigger;
- struct libusb_transfer *transfer;
- unsigned int i, timeout, num_transfers;
- int ret;
- unsigned char *buf;
- size_t size, convsize;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- drvc = di->priv;
- devc = sdi->priv;
- usb = sdi->conn;
-
- /* Configures devc->cur_channels. */
- if (configure_channels(sdi) != SR_OK) {
- sr_err("Failed to configure channels.");
- return SR_ERR;
- }
-
- devc->cb_data = cb_data;
- devc->sent_samples = 0;
- devc->empty_transfer_count = 0;
- devc->cur_channel = 0;
- memset(devc->channel_data, 0, sizeof(devc->channel_data));
-
- if ((trigger = sr_session_trigger_get(sdi->session))) {
- devc->stl = soft_trigger_logic_new(sdi, trigger);
- devc->trigger_fired = FALSE;
- } else
- devc->trigger_fired = TRUE;
-
- timeout = get_timeout(devc);
- num_transfers = get_number_of_transfers(devc);
- size = get_buffer_size(devc);
- convsize = (size / devc->num_channels + 2) * 16;
- devc->submitted_transfers = 0;
-
- devc->convbuffer_size = convsize;
- if (!(devc->convbuffer = g_try_malloc(convsize))) {
- sr_err("Conversion buffer malloc failed.");
- return SR_ERR_MALLOC;
- }
-
- devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers);
- if (!devc->transfers) {
- sr_err("USB transfers malloc failed.");
- g_free(devc->convbuffer);
- return SR_ERR_MALLOC;
- }
-
- if ((ret = logic16_setup_acquisition(sdi, devc->cur_samplerate,
- devc->cur_channels)) != SR_OK) {
- g_free(devc->transfers);
- g_free(devc->convbuffer);
- return ret;
- }
-
- devc->num_transfers = num_transfers;
- for (i = 0; i < num_transfers; i++) {
- if (!(buf = g_try_malloc(size))) {
- sr_err("USB transfer buffer malloc failed.");
- if (devc->submitted_transfers)
- abort_acquisition(devc);
- else {
- g_free(devc->transfers);
- g_free(devc->convbuffer);
- }
- return SR_ERR_MALLOC;
- }
- transfer = libusb_alloc_transfer(0);
- libusb_fill_bulk_transfer(transfer, usb->devhdl,
- 2 | LIBUSB_ENDPOINT_IN, buf, size,
- logic16_receive_transfer, (void *)sdi, timeout);
- if ((ret = libusb_submit_transfer(transfer)) != 0) {
- sr_err("Failed to submit transfer: %s.",
- libusb_error_name(ret));
- libusb_free_transfer(transfer);
- g_free(buf);
- abort_acquisition(devc);
- return SR_ERR;
- }
- devc->transfers[i] = transfer;
- devc->submitted_transfers++;
- }
-
- devc->ctx = drvc->sr_ctx;
-
- usb_source_add(sdi->session, devc->ctx, timeout, receive_data, (void *)sdi);
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- if ((ret = logic16_start_acquisition(sdi)) != SR_OK) {
- abort_acquisition(devc);
- return ret;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- int ret;
-
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- ret = logic16_abort_acquisition(sdi);
-
- abort_acquisition(sdi->priv);
-
- return ret;
-}
-
-SR_PRIV struct sr_dev_driver saleae_logic16_driver_info = {
- .name = "saleae-logic16",
- .longname = "Saleae Logic16",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 "protocol.h"
-
-#include <stdint.h>
-#include <string.h>
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <stdio.h>
-#include <errno.h>
-#include <math.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define FPGA_FIRMWARE_18 FIRMWARE_DIR"/saleae-logic16-fpga-18.bitstream"
-#define FPGA_FIRMWARE_33 FIRMWARE_DIR"/saleae-logic16-fpga-33.bitstream"
-
-#define MAX_SAMPLE_RATE SR_MHZ(100)
-#define MAX_4CH_SAMPLE_RATE SR_MHZ(50)
-#define MAX_7CH_SAMPLE_RATE SR_MHZ(40)
-#define MAX_8CH_SAMPLE_RATE SR_MHZ(32)
-#define MAX_10CH_SAMPLE_RATE SR_MHZ(25)
-#define MAX_13CH_SAMPLE_RATE SR_MHZ(16)
-
-#define BASE_CLOCK_0_FREQ SR_MHZ(100)
-#define BASE_CLOCK_1_FREQ SR_MHZ(160)
-
-#define COMMAND_START_ACQUISITION 1
-#define COMMAND_ABORT_ACQUISITION_ASYNC 2
-#define COMMAND_WRITE_EEPROM 6
-#define COMMAND_READ_EEPROM 7
-#define COMMAND_WRITE_LED_TABLE 0x7a
-#define COMMAND_SET_LED_MODE 0x7b
-#define COMMAND_RETURN_TO_BOOTLOADER 0x7c
-#define COMMAND_ABORT_ACQUISITION_SYNC 0x7d
-#define COMMAND_FPGA_UPLOAD_INIT 0x7e
-#define COMMAND_FPGA_UPLOAD_SEND_DATA 0x7f
-#define COMMAND_FPGA_WRITE_REGISTER 0x80
-#define COMMAND_FPGA_READ_REGISTER 0x81
-#define COMMAND_GET_REVID 0x82
-
-#define WRITE_EEPROM_COOKIE1 0x42
-#define WRITE_EEPROM_COOKIE2 0x55
-#define READ_EEPROM_COOKIE1 0x33
-#define READ_EEPROM_COOKIE2 0x81
-#define ABORT_ACQUISITION_SYNC_PATTERN 0x55
-
-#define MAX_EMPTY_TRANSFERS 64
-
-static void encrypt(uint8_t *dest, const uint8_t *src, uint8_t cnt)
-{
- uint8_t state1 = 0x9b, state2 = 0x54;
- uint8_t t, v;
- int i;
-
- for (i = 0; i < cnt; i++) {
- v = src[i];
- t = (((v ^ state2 ^ 0x2b) - 0x05) ^ 0x35) - 0x39;
- t = (((t ^ state1 ^ 0x5a) - 0xb0) ^ 0x38) - 0x45;
- dest[i] = state2 = t;
- state1 = v;
- }
-}
-
-static void decrypt(uint8_t *dest, const uint8_t *src, uint8_t cnt)
-{
- uint8_t state1 = 0x9b, state2 = 0x54;
- uint8_t t, v;
- int i;
-
- for (i = 0; i < cnt; i++) {
- v = src[i];
- t = (((v + 0x45) ^ 0x38) + 0xb0) ^ 0x5a ^ state1;
- t = (((t + 0x39) ^ 0x35) + 0x05) ^ 0x2b ^ state2;
- dest[i] = state1 = t;
- state2 = v;
- }
-}
-
-static int do_ep1_command(const struct sr_dev_inst *sdi,
- const uint8_t *command, uint8_t cmd_len,
- uint8_t *reply, uint8_t reply_len)
-{
- uint8_t buf[64];
- struct sr_usb_dev_inst *usb;
- int ret, xfer;
-
- usb = sdi->conn;
-
- if (cmd_len < 1 || cmd_len > 64 || reply_len > 64 ||
- command == NULL || (reply_len > 0 && reply == NULL))
- return SR_ERR_ARG;
-
- encrypt(buf, command, cmd_len);
-
- ret = libusb_bulk_transfer(usb->devhdl, 1, buf, cmd_len, &xfer, 1000);
- if (ret != 0) {
- sr_dbg("Failed to send EP1 command 0x%02x: %s.",
- command[0], libusb_error_name(ret));
- return SR_ERR;
- }
- if (xfer != cmd_len) {
- sr_dbg("Failed to send EP1 command 0x%02x: incorrect length "
- "%d != %d.", xfer, cmd_len);
- return SR_ERR;
- }
-
- if (reply_len == 0)
- return SR_OK;
-
- ret = libusb_bulk_transfer(usb->devhdl, 0x80 | 1, buf, reply_len,
- &xfer, 1000);
- if (ret != 0) {
- sr_dbg("Failed to receive reply to EP1 command 0x%02x: %s.",
- command[0], libusb_error_name(ret));
- return SR_ERR;
- }
- if (xfer != reply_len) {
- sr_dbg("Failed to receive reply to EP1 command 0x%02x: "
- "incorrect length %d != %d.", xfer, reply_len);
- return SR_ERR;
- }
-
- decrypt(reply, buf, reply_len);
-
- return SR_OK;
-}
-
-static int read_eeprom(const struct sr_dev_inst *sdi,
- uint8_t address, uint8_t length, uint8_t *buf)
-{
- uint8_t command[5] = {
- COMMAND_READ_EEPROM,
- READ_EEPROM_COOKIE1,
- READ_EEPROM_COOKIE2,
- address,
- length,
- };
-
- return do_ep1_command(sdi, command, 5, buf, length);
-}
-
-static int upload_led_table(const struct sr_dev_inst *sdi,
- const uint8_t *table, uint8_t offset, uint8_t cnt)
-{
- uint8_t chunk, command[64];
- int ret;
-
- if (cnt < 1 || cnt + offset > 64 || table == NULL)
- return SR_ERR_ARG;
-
- while (cnt > 0) {
- chunk = (cnt > 32 ? 32 : cnt);
-
- command[0] = COMMAND_WRITE_LED_TABLE;
- command[1] = offset;
- command[2] = chunk;
- memcpy(command + 3, table, chunk);
-
- ret = do_ep1_command(sdi, command, 3 + chunk, NULL, 0);
- if (ret != SR_OK)
- return ret;
-
- table += chunk;
- offset += chunk;
- cnt -= chunk;
- }
-
- return SR_OK;
-}
-
-static int set_led_mode(const struct sr_dev_inst *sdi,
- uint8_t animate, uint16_t t2reload, uint8_t div,
- uint8_t repeat)
-{
- uint8_t command[6] = {
- COMMAND_SET_LED_MODE,
- animate,
- t2reload & 0xff,
- t2reload >> 8,
- div,
- repeat,
- };
-
- return do_ep1_command(sdi, command, 6, NULL, 0);
-}
-
-static int read_fpga_register(const struct sr_dev_inst *sdi,
- uint8_t address, uint8_t *value)
-{
- uint8_t command[3] = {
- COMMAND_FPGA_READ_REGISTER,
- 1,
- address,
- };
-
- return do_ep1_command(sdi, command, 3, value, 1);
-}
-
-static int write_fpga_registers(const struct sr_dev_inst *sdi,
- uint8_t (*regs)[2], uint8_t cnt)
-{
- uint8_t command[64];
- int i;
-
- if (cnt < 1 || cnt > 31)
- return SR_ERR_ARG;
-
- command[0] = COMMAND_FPGA_WRITE_REGISTER;
- command[1] = cnt;
- for (i = 0; i < cnt; i++) {
- command[2 + 2 * i] = regs[i][0];
- command[3 + 2 * i] = regs[i][1];
- }
-
- return do_ep1_command(sdi, command, 2 * (cnt + 1), NULL, 0);
-}
-
-static int write_fpga_register(const struct sr_dev_inst *sdi,
- uint8_t address, uint8_t value)
-{
- uint8_t regs[2] = { address, value };
-
- return write_fpga_registers(sdi, ®s, 1);
-}
-
-static uint8_t map_eeprom_data(uint8_t v)
-{
- return (((v ^ 0x80) + 0x44) ^ 0xd5) + 0x69;
-}
-
-static int prime_fpga(const struct sr_dev_inst *sdi)
-{
- uint8_t eeprom_data[16];
- uint8_t old_reg_10, version;
- uint8_t regs[8][2] = {
- {10, 0x00},
- {10, 0x40},
- {12, 0},
- {10, 0xc0},
- {10, 0x40},
- {6, 0},
- {7, 1},
- {7, 0}
- };
- int i, ret;
-
- if ((ret = read_eeprom(sdi, 16, 16, eeprom_data)) != SR_OK)
- return ret;
-
- if ((ret = read_fpga_register(sdi, 10, &old_reg_10)) != SR_OK)
- return ret;
-
- regs[0][1] = (old_reg_10 &= 0x7f);
- regs[1][1] |= old_reg_10;
- regs[3][1] |= old_reg_10;
- regs[4][1] |= old_reg_10;
-
- for (i = 0; i < 16; i++) {
- regs[2][1] = eeprom_data[i];
- regs[5][1] = map_eeprom_data(eeprom_data[i]);
- if (i)
- ret = write_fpga_registers(sdi, ®s[2], 6);
- else
- ret = write_fpga_registers(sdi, ®s[0], 8);
- if (ret != SR_OK)
- return ret;
- }
-
- if ((ret = write_fpga_register(sdi, 10, old_reg_10)) != SR_OK)
- return ret;
-
- if ((ret = read_fpga_register(sdi, 0, &version)) != SR_OK)
- return ret;
-
- if (version != 0x10) {
- sr_err("Invalid FPGA bitstream version: 0x%02x != 0x10.", version);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static void make_heartbeat(uint8_t *table, int len)
-{
- int i, j;
-
- memset(table, 0, len);
- len >>= 3;
- for (i = 0; i < 2; i++)
- for (j = 0; j < len; j++)
- *table++ = sin(j * M_PI / len) * 255;
-}
-
-static int configure_led(const struct sr_dev_inst *sdi)
-{
- uint8_t table[64];
- int ret;
-
- make_heartbeat(table, 64);
- if ((ret = upload_led_table(sdi, table, 0, 64)) != SR_OK)
- return ret;
-
- return set_led_mode(sdi, 1, 6250, 0, 1);
-}
-
-static int upload_fpga_bitstream(const struct sr_dev_inst *sdi,
- enum voltage_range vrange)
-{
- struct dev_context *devc;
- int offset, chunksize, ret;
- const char *filename;
- uint8_t len, buf[256 * 62], command[64];
- FILE *fw;
-
- devc = sdi->priv;
-
- if (devc->cur_voltage_range == vrange)
- return SR_OK;
-
- switch (vrange) {
- case VOLTAGE_RANGE_18_33_V:
- filename = FPGA_FIRMWARE_18;
- break;
- case VOLTAGE_RANGE_5_V:
- filename = FPGA_FIRMWARE_33;
- break;
- default:
- sr_err("Unsupported voltage range.");
- return SR_ERR;
- }
-
- sr_info("Uploading FPGA bitstream at %s.", filename);
- if ((fw = g_fopen(filename, "rb")) == NULL) {
- sr_err("Unable to open bitstream file %s for reading: %s.",
- filename, strerror(errno));
- return SR_ERR;
- }
-
- buf[0] = COMMAND_FPGA_UPLOAD_INIT;
- if ((ret = do_ep1_command(sdi, buf, 1, NULL, 0)) != SR_OK) {
- fclose(fw);
- return ret;
- }
-
- while (1) {
- chunksize = fread(buf, 1, sizeof(buf), fw);
- if (chunksize == 0)
- break;
-
- for (offset = 0; offset < chunksize; offset += 62) {
- len = (offset + 62 > chunksize ?
- chunksize - offset : 62);
- command[0] = COMMAND_FPGA_UPLOAD_SEND_DATA;
- command[1] = len;
- memcpy(command + 2, buf + offset, len);
- ret = do_ep1_command(sdi, command, len + 2, NULL, 0);
- if (ret != SR_OK) {
- fclose(fw);
- return ret;
- }
- }
-
- sr_info("Uploaded %d bytes.", chunksize);
- }
- fclose(fw);
- sr_info("FPGA bitstream upload done.");
-
- if ((ret = prime_fpga(sdi)) != SR_OK)
- return ret;
-
- if ((ret = configure_led(sdi)) != SR_OK)
- return ret;
-
- devc->cur_voltage_range = vrange;
- return SR_OK;
-}
-
-static int abort_acquisition_sync(const struct sr_dev_inst *sdi)
-{
- static const uint8_t command[2] = {
- COMMAND_ABORT_ACQUISITION_SYNC,
- ABORT_ACQUISITION_SYNC_PATTERN,
- };
- uint8_t reply, expected_reply;
- int ret;
-
- if ((ret = do_ep1_command(sdi, command, 2, &reply, 1)) != SR_OK)
- return ret;
-
- expected_reply = ~command[1];
- if (reply != expected_reply) {
- sr_err("Invalid response for abort acquisition command: "
- "0x%02x != 0x%02x.", reply, expected_reply);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int logic16_setup_acquisition(const struct sr_dev_inst *sdi,
- uint64_t samplerate, uint16_t channels)
-{
- uint8_t clock_select, reg1, reg10;
- uint64_t div;
- int i, ret, nchan = 0;
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- if (samplerate == 0 || samplerate > MAX_SAMPLE_RATE) {
- sr_err("Unable to sample at %" PRIu64 "Hz.", samplerate);
- return SR_ERR;
- }
-
- if (BASE_CLOCK_0_FREQ % samplerate == 0 &&
- (div = BASE_CLOCK_0_FREQ / samplerate) <= 256) {
- clock_select = 0;
- } else if (BASE_CLOCK_1_FREQ % samplerate == 0 &&
- (div = BASE_CLOCK_1_FREQ / samplerate) <= 256) {
- clock_select = 1;
- } else {
- sr_err("Unable to sample at %" PRIu64 "Hz.", samplerate);
- return SR_ERR;
- }
-
- for (i = 0; i < 16; i++)
- if (channels & (1U << i))
- nchan++;
-
- if ((nchan >= 13 && samplerate > MAX_13CH_SAMPLE_RATE) ||
- (nchan >= 10 && samplerate > MAX_10CH_SAMPLE_RATE) ||
- (nchan >= 8 && samplerate > MAX_8CH_SAMPLE_RATE) ||
- (nchan >= 7 && samplerate > MAX_7CH_SAMPLE_RATE) ||
- (nchan >= 4 && samplerate > MAX_4CH_SAMPLE_RATE)) {
- sr_err("Unable to sample at %" PRIu64 "Hz "
- "with this many channels.", samplerate);
- return SR_ERR;
- }
-
- ret = upload_fpga_bitstream(sdi, devc->selected_voltage_range);
- if (ret != SR_OK)
- return ret;
-
- if ((ret = read_fpga_register(sdi, 1, ®1)) != SR_OK)
- return ret;
-
- if (reg1 != 0x08) {
- sr_dbg("Invalid state at acquisition setup: 0x%02x != 0x08.", reg1);
- return SR_ERR;
- }
-
- if ((ret = write_fpga_register(sdi, 1, 0x40)) != SR_OK)
- return ret;
-
- if ((ret = write_fpga_register(sdi, 10, clock_select)) != SR_OK)
- return ret;
-
- if ((ret = write_fpga_register(sdi, 4, (uint8_t)(div - 1))) != SR_OK)
- return ret;
-
- if ((ret = write_fpga_register(sdi, 2, (uint8_t)(channels & 0xff))) != SR_OK)
- return ret;
-
- if ((ret = write_fpga_register(sdi, 3, (uint8_t)(channels >> 8))) != SR_OK)
- return ret;
-
- if ((ret = write_fpga_register(sdi, 1, 0x42)) != SR_OK)
- return ret;
-
- if ((ret = write_fpga_register(sdi, 1, 0x40)) != SR_OK)
- return ret;
-
- if ((ret = read_fpga_register(sdi, 1, ®1)) != SR_OK)
- return ret;
-
- if (reg1 != 0x48) {
- sr_dbg("Invalid state at acquisition setup: 0x%02x != 0x48.", reg1);
- return SR_ERR;
- }
-
- if ((ret = read_fpga_register(sdi, 10, ®10)) != SR_OK)
- return ret;
-
- if (reg10 != clock_select) {
- sr_dbg("Invalid state at acquisition setup: 0x%02x != 0x%02x.",
- reg10, clock_select);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-SR_PRIV int logic16_start_acquisition(const struct sr_dev_inst *sdi)
-{
- static const uint8_t command[1] = {
- COMMAND_START_ACQUISITION,
- };
- int ret;
-
- if ((ret = do_ep1_command(sdi, command, 1, NULL, 0)) != SR_OK)
- return ret;
-
- return write_fpga_register(sdi, 1, 0x41);
-}
-
-SR_PRIV int logic16_abort_acquisition(const struct sr_dev_inst *sdi)
-{
- static const uint8_t command[1] = {
- COMMAND_ABORT_ACQUISITION_ASYNC,
- };
- int ret;
- uint8_t reg1, reg8, reg9;
-
- if ((ret = do_ep1_command(sdi, command, 1, NULL, 0)) != SR_OK)
- return ret;
-
- if ((ret = write_fpga_register(sdi, 1, 0x00)) != SR_OK)
- return ret;
-
- if ((ret = read_fpga_register(sdi, 1, ®1)) != SR_OK)
- return ret;
-
- if (reg1 != 0x08) {
- sr_dbg("Invalid state at acquisition stop: 0x%02x != 0x08.", reg1);
- return SR_ERR;
- }
-
- if ((ret = read_fpga_register(sdi, 8, ®8)) != SR_OK)
- return ret;
-
- if ((ret = read_fpga_register(sdi, 9, ®9)) != SR_OK)
- return ret;
-
- return SR_OK;
-}
-
-SR_PRIV int logic16_init_device(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int ret;
-
- devc = sdi->priv;
-
- devc->cur_voltage_range = VOLTAGE_RANGE_UNKNOWN;
-
- if ((ret = abort_acquisition_sync(sdi)) != SR_OK)
- return ret;
-
- if ((ret = read_eeprom(sdi, 8, 8, devc->eeprom_data)) != SR_OK)
- return ret;
-
- ret = upload_fpga_bitstream(sdi, devc->selected_voltage_range);
- if (ret != SR_OK)
- return ret;
-
- return SR_OK;
-}
-
-static void finish_acquisition(struct sr_dev_inst *sdi)
-{
- struct sr_datafeed_packet packet;
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- /* Terminate session. */
- packet.type = SR_DF_END;
- sr_session_send(devc->cb_data, &packet);
-
- /* Remove fds from polling. */
- usb_source_remove(sdi->session, devc->ctx);
-
- devc->num_transfers = 0;
- g_free(devc->transfers);
- g_free(devc->convbuffer);
- if (devc->stl) {
- soft_trigger_logic_free(devc->stl);
- devc->stl = NULL;
- }
-}
-
-static void free_transfer(struct libusb_transfer *transfer)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- unsigned int i;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
-
- g_free(transfer->buffer);
- transfer->buffer = NULL;
- libusb_free_transfer(transfer);
-
- for (i = 0; i < devc->num_transfers; i++) {
- if (devc->transfers[i] == transfer) {
- devc->transfers[i] = NULL;
- break;
- }
- }
-
- devc->submitted_transfers--;
- if (devc->submitted_transfers == 0)
- finish_acquisition(sdi);
-}
-
-static void resubmit_transfer(struct libusb_transfer *transfer)
-{
- int ret;
-
- if ((ret = libusb_submit_transfer(transfer)) == LIBUSB_SUCCESS)
- return;
-
- free_transfer(transfer);
- /* TODO: Stop session? */
-
- sr_err("%s: %s", __func__, libusb_error_name(ret));
-}
-
-static size_t convert_sample_data(struct dev_context *devc,
- uint8_t *dest, size_t destcnt, const uint8_t *src, size_t srccnt)
-{
- uint16_t *channel_data;
- int i, cur_channel;
- size_t ret = 0;
- uint16_t sample, channel_mask;
-
- srccnt /= 2;
-
- channel_data = devc->channel_data;
- cur_channel = devc->cur_channel;
-
- while (srccnt--) {
- sample = src[0] | (src[1] << 8);
- src += 2;
-
- channel_mask = devc->channel_masks[cur_channel];
-
- for (i = 15; i >= 0; --i, sample >>= 1)
- if (sample & 1)
- channel_data[i] |= channel_mask;
-
- if (++cur_channel == devc->num_channels) {
- cur_channel = 0;
- if (destcnt < 16 * 2) {
- sr_err("Conversion buffer too small!");
- break;
- }
- memcpy(dest, channel_data, 16 * 2);
- memset(channel_data, 0, 16 * 2);
- dest += 16 * 2;
- ret += 16;
- destcnt -= 16 * 2;
- }
- }
-
- devc->cur_channel = cur_channel;
-
- return ret;
-}
-
-SR_PRIV void logic16_receive_transfer(struct libusb_transfer *transfer)
-{
- gboolean packet_has_error = FALSE;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- size_t new_samples, num_samples;
- int trigger_offset;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
-
- /*
- * If acquisition has already ended, just free any queued up
- * transfer that come in.
- */
- if (devc->sent_samples < 0) {
- free_transfer(transfer);
- return;
- }
-
- sr_info("receive_transfer(): status %d received %d bytes.",
- transfer->status, transfer->actual_length);
-
- switch (transfer->status) {
- case LIBUSB_TRANSFER_NO_DEVICE:
- devc->sent_samples = -2;
- free_transfer(transfer);
- return;
- case LIBUSB_TRANSFER_COMPLETED:
- case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */
- break;
- default:
- packet_has_error = TRUE;
- break;
- }
-
- if (transfer->actual_length & 1) {
- sr_err("Got an odd number of bytes from the device. "
- "This should not happen.");
- /* Bail out right away. */
- packet_has_error = TRUE;
- devc->empty_transfer_count = MAX_EMPTY_TRANSFERS;
- }
-
- if (transfer->actual_length == 0 || packet_has_error) {
- devc->empty_transfer_count++;
- if (devc->empty_transfer_count > MAX_EMPTY_TRANSFERS) {
- /*
- * The FX2 gave up. End the acquisition, the frontend
- * will work out that the samplecount is short.
- */
- devc->sent_samples = -2;
- free_transfer(transfer);
- } else {
- resubmit_transfer(transfer);
- }
- return;
- } else {
- devc->empty_transfer_count = 0;
- }
-
- new_samples = convert_sample_data(devc, devc->convbuffer,
- devc->convbuffer_size, transfer->buffer, transfer->actual_length);
-
- if (new_samples > 0) {
- if (devc->trigger_fired) {
- /* Send the incoming transfer to the session bus. */
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- if (devc->limit_samples &&
- new_samples > devc->limit_samples - devc->sent_samples)
- new_samples = devc->limit_samples - devc->sent_samples;
- logic.length = new_samples * 2;
- logic.unitsize = 2;
- logic.data = devc->convbuffer;
- sr_session_send(devc->cb_data, &packet);
- devc->sent_samples += new_samples;
- } else {
- trigger_offset = soft_trigger_logic_check(devc->stl,
- devc->convbuffer, new_samples * 2);
- if (trigger_offset > -1) {
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- num_samples = new_samples - trigger_offset;
- if (devc->limit_samples &&
- num_samples > devc->limit_samples - devc->sent_samples)
- num_samples = devc->limit_samples - devc->sent_samples;
- logic.length = num_samples * 2;
- logic.unitsize = 2;
- logic.data = devc->convbuffer + trigger_offset * 2;
- sr_session_send(devc->cb_data, &packet);
- devc->sent_samples += num_samples;
-
- devc->trigger_fired = TRUE;
- }
- }
-
- if (devc->limit_samples &&
- (uint64_t)devc->sent_samples >= devc->limit_samples) {
- devc->sent_samples = -2;
- free_transfer(transfer);
- return;
- }
- }
-
- resubmit_transfer(transfer);
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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_SALEAE_LOGIC16_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_SALEAE_LOGIC16_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "saleae-logic16"
-
-enum voltage_range {
- VOLTAGE_RANGE_UNKNOWN,
- VOLTAGE_RANGE_18_33_V, /* 1.8V and 3.3V logic */
- VOLTAGE_RANGE_5_V, /* 5V logic */
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /*
- * Since we can't keep track of a Logic16 device after upgrading
- * the firmware (it renumerates into a different device address
- * after the upgrade) this is like a global lock. No device will open
- * until a proper delay after the last device was upgraded.
- */
- int64_t fw_updated;
-
- /** The currently configured samplerate of the device. */
- uint64_t cur_samplerate;
-
- /** Maximum number of samples to capture, if nonzero. */
- uint64_t limit_samples;
-
- /** The currently configured input voltage of the device. */
- enum voltage_range cur_voltage_range;
-
- /** The input voltage selected by the user. */
- enum voltage_range selected_voltage_range;
-
- /** Channels to use. */
- uint16_t cur_channels;
-
- /* EEPROM data from address 8. */
- uint8_t eeprom_data[8];
-
- int64_t sent_samples;
- int submitted_transfers;
- int empty_transfer_count;
- int num_channels;
- int cur_channel;
- uint16_t channel_masks[16];
- uint16_t channel_data[16];
- uint8_t *convbuffer;
- size_t convbuffer_size;
- struct soft_trigger_logic *stl;
- gboolean trigger_fired;
-
- void *cb_data;
- unsigned int num_transfers;
- struct libusb_transfer **transfers;
- struct sr_context *ctx;
-};
-
-SR_PRIV int logic16_setup_acquisition(const struct sr_dev_inst *sdi,
- uint64_t samplerate, uint16_t channels);
-SR_PRIV int logic16_start_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV int logic16_abort_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV int logic16_init_device(const struct sr_dev_inst *sdi);
-SR_PRIV void logic16_receive_transfer(struct libusb_transfer *transfer);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
- * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
-};
-
-SR_PRIV struct sr_dev_driver bbcgm_m2110_driver_info;
-SR_PRIV struct sr_dev_driver digitek_dt4000zc_driver_info;
-SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info;
-SR_PRIV struct sr_dev_driver metex_me31_driver_info;
-SR_PRIV struct sr_dev_driver peaktech_3410_driver_info;
-SR_PRIV struct sr_dev_driver mastech_mas345_driver_info;
-SR_PRIV struct sr_dev_driver va_va18b_driver_info;
-SR_PRIV struct sr_dev_driver va_va40b_driver_info;
-SR_PRIV struct sr_dev_driver metex_m3640d_driver_info;
-SR_PRIV struct sr_dev_driver metex_m4650cr_driver_info;
-SR_PRIV struct sr_dev_driver peaktech_4370_driver_info;
-SR_PRIV struct sr_dev_driver pce_pce_dm32_driver_info;
-SR_PRIV struct sr_dev_driver radioshack_22_168_driver_info;
-SR_PRIV struct sr_dev_driver radioshack_22_805_driver_info;
-SR_PRIV struct sr_dev_driver radioshack_22_812_driver_info;
-SR_PRIV struct sr_dev_driver tecpel_dmm_8061_ser_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_m3650cr_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_m3650d_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_m4650cr_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_me42_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc820_ser_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc830_ser_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc840_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60a_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60e_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60g_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61b_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61c_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61d_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61e_ser_driver_info;
-SR_PRIV struct sr_dev_driver iso_tech_idm103n_driver_info;
-SR_PRIV struct sr_dev_driver tenma_72_7745_ser_driver_info;
-SR_PRIV struct sr_dev_driver tenma_72_7750_ser_driver_info;
-
-SR_PRIV struct dmm_info dmms[] = {
- {
- "BBC Goertz Metrawatt", "M2110", "1200/7n2", 1200,
- BBCGM_M2110_PACKET_SIZE, 0, 0, NULL,
- sr_m2110_packet_valid, sr_m2110_parse,
- NULL,
- &bbcgm_m2110_driver_info, receive_data_BBCGM_M2110,
- },
- {
- "Digitek", "DT4000ZC", "2400/8n1/dtr=1", 2400,
- FS9721_PACKET_SIZE, 0, 0, NULL,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_10_temp_c,
- &digitek_dt4000zc_driver_info, receive_data_DIGITEK_DT4000ZC,
- },
- {
- "TekPower", "TP4000ZC", "2400/8n1/dtr=1", 2400,
- FS9721_PACKET_SIZE, 0, 0, NULL,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_10_temp_c,
- &tekpower_tp4000zc_driver_info, receive_data_TEKPOWER_TP4000ZC,
- },
- {
- "Metex", "ME-31", "600/7n2/rts=0/dtr=1", 600,
- METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &metex_me31_driver_info, receive_data_METEX_ME31,
- },
- {
- "Peaktech", "3410", "600/7n2/rts=0/dtr=1", 600,
- METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &peaktech_3410_driver_info, receive_data_PEAKTECH_3410,
- },
- {
- "MASTECH", "MAS345", "600/7n2/rts=0/dtr=1", 600,
- METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &mastech_mas345_driver_info, receive_data_MASTECH_MAS345,
- },
- {
- "V&A", "VA18B", "2400/8n1", 2400,
- FS9721_PACKET_SIZE, 0, 0, NULL,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_01_temp_c,
- &va_va18b_driver_info, receive_data_VA_VA18B,
- },
- {
- "V&A", "VA40B", "2400/8n1", 2400,
- FS9721_PACKET_SIZE, 0, 0, NULL,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_max_c_min,
- &va_va40b_driver_info, receive_data_VA_VA40B,
- },
- {
- "Metex", "M-3640D", "1200/7n2/rts=0/dtr=1", 1200,
- METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &metex_m3640d_driver_info, receive_data_METEX_M3640D,
- },
- {
- "Metex", "M-4650CR", "1200/7n2/rts=0/dtr=1", 1200,
- METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &metex_m4650cr_driver_info, receive_data_METEX_M4650CR,
- },
- {
- "PeakTech", "4370", "1200/7n2/rts=0/dtr=1", 1200,
- METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &peaktech_4370_driver_info, receive_data_PEAKTECH_4370,
- },
- {
- "PCE", "PCE-DM32", "2400/8n1", 2400,
- FS9721_PACKET_SIZE, 0, 0, NULL,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_01_10_temp_f_c,
- &pce_pce_dm32_driver_info, receive_data_PCE_PCE_DM32,
- },
- {
- "RadioShack", "22-168", "1200/7n2/rts=0/dtr=1", 1200,
- METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &radioshack_22_168_driver_info, receive_data_RADIOSHACK_22_168,
- },
- {
- "RadioShack", "22-805", "600/7n2/rts=0/dtr=1", 600,
- METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &radioshack_22_805_driver_info, receive_data_RADIOSHACK_22_805,
- },
- {
- "RadioShack", "22-812", "4800/8n1/rts=0/dtr=1", 4800,
- RS9LCD_PACKET_SIZE, 0, 0, NULL,
- sr_rs9lcd_packet_valid, sr_rs9lcd_parse,
- NULL,
- &radioshack_22_812_driver_info, receive_data_RADIOSHACK_22_812,
- },
- {
- "Tecpel", "DMM-8061 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
- 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_00_temp_c,
- &tecpel_dmm_8061_ser_driver_info,
- receive_data_TECPEL_DMM_8061_SER,
- },
- {
- "Voltcraft", "M-3650CR", "1200/7n2/rts=0/dtr=1", 1200,
- METEX14_PACKET_SIZE, 150, 20, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &voltcraft_m3650cr_driver_info, receive_data_VOLTCRAFT_M3650CR,
- },
- {
- "Voltcraft", "M-3650D", "1200/7n2/rts=0/dtr=1", 1200,
- METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &voltcraft_m3650d_driver_info, receive_data_VOLTCRAFT_M3650D,
- },
- {
- "Voltcraft", "M-4650CR", "1200/7n2/rts=0/dtr=1", 1200,
- METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &voltcraft_m4650cr_driver_info, receive_data_VOLTCRAFT_M4650CR,
- },
- {
- "Voltcraft", "ME-42", "600/7n2/rts=0/dtr=1", 600,
- METEX14_PACKET_SIZE, 250, 60, sr_metex14_packet_request,
- sr_metex14_packet_valid, sr_metex14_parse,
- NULL,
- &voltcraft_me42_driver_info, receive_data_VOLTCRAFT_ME42,
- },
- {
- "Voltcraft", "VC-820 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
- 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- NULL,
- &voltcraft_vc820_ser_driver_info,
- receive_data_VOLTCRAFT_VC820_SER,
- },
- {
- /*
- * Note: The VC830 doesn't set the 'volt' and 'diode' bits of
- * the FS9922 protocol. Instead, it only sets the user-defined
- * bit "z1" to indicate "diode mode" and "voltage".
- */
- "Voltcraft", "VC-830 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
- 2400, FS9922_PACKET_SIZE, 0, 0, NULL,
- sr_fs9922_packet_valid, sr_fs9922_parse,
- &sr_fs9922_z1_diode,
- &voltcraft_vc830_ser_driver_info,
- receive_data_VOLTCRAFT_VC830_SER,
- },
- {
- "Voltcraft", "VC-840 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
- 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_00_temp_c,
- &voltcraft_vc840_ser_driver_info,
- receive_data_VOLTCRAFT_VC840_SER,
- },
- {
- "UNI-T", "UT60A (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
- 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- NULL,
- &uni_t_ut60a_ser_driver_info,
- receive_data_UNI_T_UT60A_SER,
- },
- {
- "UNI-T", "UT60E (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
- 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_00_temp_c,
- &uni_t_ut60e_ser_driver_info,
- receive_data_UNI_T_UT60E_SER,
- },
- {
- /* Note: ES51986 baudrate is actually 19230! */
- "UNI-T", "UT60G (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
- 19200, ES519XX_11B_PACKET_SIZE, 0, 0, NULL,
- sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
- NULL,
- &uni_t_ut60g_ser_driver_info, receive_data_UNI_T_UT60G_SER,
- },
- {
- "UNI-T", "UT61B (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
- 2400, FS9922_PACKET_SIZE, 0, 0, NULL,
- sr_fs9922_packet_valid, sr_fs9922_parse, NULL,
- &uni_t_ut61b_ser_driver_info, receive_data_UNI_T_UT61B_SER,
- },
- {
- "UNI-T", "UT61C (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
- 2400, FS9922_PACKET_SIZE, 0, 0, NULL,
- sr_fs9922_packet_valid, sr_fs9922_parse, NULL,
- &uni_t_ut61c_ser_driver_info, receive_data_UNI_T_UT61C_SER,
- },
- {
- "UNI-T", "UT61D (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
- 2400, FS9922_PACKET_SIZE, 0, 0, NULL,
- sr_fs9922_packet_valid, sr_fs9922_parse, NULL,
- &uni_t_ut61d_ser_driver_info, receive_data_UNI_T_UT61D_SER,
- },
- {
- /* Note: ES51922 baudrate is actually 19230! */
- "UNI-T", "UT61E (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
- 19200, ES519XX_14B_PACKET_SIZE, 0, 0, NULL,
- sr_es519xx_19200_14b_packet_valid, sr_es519xx_19200_14b_parse,
- NULL,
- &uni_t_ut61e_ser_driver_info, receive_data_UNI_T_UT61E_SER,
- },
- {
- "ISO-TECH", "IDM103N", "2400/7o1/rts=0/dtr=1",
- 2400, ES519XX_11B_PACKET_SIZE, 0, 0, NULL,
- sr_es519xx_2400_11b_packet_valid, sr_es519xx_2400_11b_parse,
- NULL,
- &iso_tech_idm103n_driver_info, receive_data_ISO_TECH_IDM103N,
- },
- {
- "Tenma", "72-7745 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
- 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_00_temp_c,
- &tenma_72_7745_ser_driver_info, receive_data_TENMA_72_7745_SER,
- },
- {
- /* Note: ES51986 baudrate is actually 19230! */
- "Tenma", "72-7750 (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
- 19200, ES519XX_11B_PACKET_SIZE, 0, 0, NULL,
- sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
- NULL,
- &tenma_72_7750_ser_driver_info, receive_data_TENMA_72_7750_SER,
- },
-};
-
-static int dev_clear(int dmm)
-{
- return std_dev_clear(dmms[dmm].di, NULL);
-}
-
-static int init(struct sr_context *sr_ctx, int dmm)
-{
- sr_dbg("Selected '%s' subdriver.", dmms[dmm].di->name);
-
- return std_init(sr_ctx, dmms[dmm].di, LOG_PREFIX);
-}
-
-static GSList *sdmm_scan(const char *conn, const char *serialcomm, int dmm)
-{
- struct sr_dev_inst *sdi;
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_channel *ch;
- struct sr_serial_dev_inst *serial;
- GSList *devices;
- int dropped, ret;
- size_t len;
- uint8_t buf[128];
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- sr_info("Probing serial port %s.", conn);
-
- drvc = dmms[dmm].di->priv;
- devices = NULL;
- serial_flush(serial);
-
- /* Request a packet if the DMM requires this. */
- if (dmms[dmm].packet_request) {
- if ((ret = dmms[dmm].packet_request(serial)) < 0) {
- sr_err("Failed to request packet: %d.", ret);
- return FALSE;
- }
- }
-
- /*
- * There's no way to get an ID from the multimeter. It just sends data
- * periodically (or upon request), so the best we can do is check if
- * the packets match the expected format.
- */
-
- /* Let's get a bit of data and see if we can find a packet. */
- len = sizeof(buf);
- ret = serial_stream_detect(serial, buf, &len, dmms[dmm].packet_size,
- dmms[dmm].packet_valid, 3000,
- dmms[dmm].baudrate);
- if (ret != SR_OK)
- goto scan_cleanup;
-
- /*
- * 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 - dmms[dmm].packet_size;
- if (dropped > 2 * dmms[dmm].packet_size)
- sr_warn("Had to drop too much data.");
-
- sr_info("Found device on port %s.", conn);
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, dmms[dmm].vendor,
- dmms[dmm].device, NULL)))
- goto scan_cleanup;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- goto scan_cleanup;
- }
-
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
-
- sdi->priv = devc;
- sdi->driver = dmms[dmm].di;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
-scan_cleanup:
- serial_close(serial);
-
- return devices;
-}
-
-static GSList *scan(GSList *options, int dmm)
-{
- struct sr_config *src;
- GSList *l, *devices;
- const char *conn, *serialcomm;
-
- 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 (!conn)
- return NULL;
-
- if (serialcomm) {
- /* Use the provided comm specs. */
- devices = sdmm_scan(conn, serialcomm, dmm);
- } else {
- /* Try the default. */
- devices = sdmm_scan(conn, dmms[dmm].conn, dmm);
- }
-
- return devices;
-}
-
-static GSList *dev_list(int dmm)
-{
- return ((struct drv_context *)(dmms[dmm].di->priv))->instances;
-}
-
-static int cleanup(int dmm)
-{
- return dev_clear(dmm);
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- switch (id) {
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- devc->limit_msec = g_variant_get_uint64(data);
- sr_dbg("Setting time limit to %" PRIu64 "ms.",
- devc->limit_msec);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data, int dmm)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- devc->cb_data = cb_data;
-
- /*
- * Reset the number of samples to take. If we've already collected our
- * quota, but we start a new session, and don't reset this, we'll just
- * quit without acquiring any new samples.
- */
- devc->num_samples = 0;
- devc->starttime = g_get_monotonic_time();
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Poll every 50ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 50,
- dmms[dmm].receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
- sdi->conn, LOG_PREFIX);
-}
-
-/* Driver-specific API function wrappers */
-#define HW_INIT(X) \
-static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
-#define HW_CLEANUP(X) \
-static int cleanup_##X(void) { return cleanup(X); }
-#define HW_SCAN(X) \
-static GSList *scan_##X(GSList *options) { return scan(options, X); }
-#define HW_DEV_LIST(X) \
-static GSList *dev_list_##X(void) { return dev_list(X); }
-#define HW_DEV_CLEAR(X) \
-static int dev_clear_##X(void) { return dev_clear(X); }
-#define HW_DEV_ACQUISITION_START(X) \
-static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
-void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
-
-/* Driver structs and API function wrappers */
-#define DRV(ID, ID_UPPER, NAME, LONGNAME) \
-HW_INIT(ID_UPPER) \
-HW_CLEANUP(ID_UPPER) \
-HW_SCAN(ID_UPPER) \
-HW_DEV_LIST(ID_UPPER) \
-HW_DEV_CLEAR(ID_UPPER) \
-HW_DEV_ACQUISITION_START(ID_UPPER) \
-SR_PRIV struct sr_dev_driver ID##_driver_info = { \
- .name = NAME, \
- .longname = LONGNAME, \
- .api_version = 1, \
- .init = init_##ID_UPPER, \
- .cleanup = cleanup_##ID_UPPER, \
- .scan = scan_##ID_UPPER, \
- .dev_list = dev_list_##ID_UPPER, \
- .dev_clear = dev_clear_##ID_UPPER, \
- .config_get = NULL, \
- .config_set = config_set, \
- .config_list = config_list, \
- .dev_open = std_serial_dev_open, \
- .dev_close = std_serial_dev_close, \
- .dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
- .dev_acquisition_stop = dev_acquisition_stop, \
- .priv = NULL, \
-};
-
-DRV(bbcgm_m2110, BBCGM_M2110, "bbcgm-m2110", "BBC Goertz Metrawatt M2110")
-DRV(digitek_dt4000zc, DIGITEK_DT4000ZC, "digitek-dt4000zc", "Digitek DT4000ZC")
-DRV(tekpower_tp4000zc, TEKPOWER_TP4000ZC, "tekpower-tp4000zc", "TekPower TP4000ZC")
-DRV(metex_me31, METEX_ME31, "metex-me31", "Metex ME-31")
-DRV(peaktech_3410, PEAKTECH_3410, "peaktech-3410", "PeakTech 3410")
-DRV(mastech_mas345, MASTECH_MAS345, "mastech-mas345", "MASTECH MAS345")
-DRV(va_va18b, VA_VA18B, "va-va18b", "V&A VA18B")
-DRV(va_va40b, VA_VA40B, "va-va40b", "V&A VA40B")
-DRV(metex_m3640d, METEX_M3640D, "metex-m3640d", "Metex M-3640D")
-DRV(metex_m4650cr, METEX_M4650CR, "metex-m4650cr", "Metex M-4650CR")
-DRV(peaktech_4370, PEAKTECH_4370, "peaktech-4370", "PeakTech 4370")
-DRV(pce_pce_dm32, PCE_PCE_DM32, "pce-pce-dm32", "PCE PCE-DM32")
-DRV(radioshack_22_168, RADIOSHACK_22_168, "radioshack-22-168", "RadioShack 22-168")
-DRV(radioshack_22_805, RADIOSHACK_22_805, "radioshack-22-805", "RadioShack 22-805")
-DRV(radioshack_22_812, RADIOSHACK_22_812, "radioshack-22-812", "RadioShack 22-812")
-DRV(tecpel_dmm_8061_ser, TECPEL_DMM_8061_SER, "tecpel-dmm-8061-ser", "Tecpel DMM-8061 (UT-D02 cable)")
-DRV(voltcraft_m3650cr, VOLTCRAFT_M3650CR, "voltcraft-m3650cr", "Voltcraft M-3650CR")
-DRV(voltcraft_m3650d, VOLTCRAFT_M3650D, "voltcraft-m3650d", "Voltcraft M-3650D")
-DRV(voltcraft_m4650cr, VOLTCRAFT_M4650CR, "voltcraft-m4650cr", "Voltcraft M-4650CR")
-DRV(voltcraft_me42, VOLTCRAFT_ME42, "voltcraft-me42", "Voltcraft ME-42")
-DRV(voltcraft_vc820_ser, VOLTCRAFT_VC820_SER, "voltcraft-vc820-ser", "Voltcraft VC-820 (UT-D02 cable)")
-DRV(voltcraft_vc830_ser, VOLTCRAFT_VC830_SER, "voltcraft-vc830-ser", "Voltcraft VC-830 (UT-D02 cable)")
-DRV(voltcraft_vc840_ser, VOLTCRAFT_VC840_SER, "voltcraft-vc840-ser", "Voltcraft VC-840 (UT-D02 cable)")
-DRV(uni_t_ut60a_ser, UNI_T_UT60A_SER, "uni-t-ut60a-ser", "UNI-T UT60A (UT-D02 cable)")
-DRV(uni_t_ut60e_ser, UNI_T_UT60E_SER, "uni-t-ut60e-ser", "UNI-T UT60E (UT-D02 cable)")
-DRV(uni_t_ut60g_ser, UNI_T_UT60G_SER, "uni-t-ut60g-ser", "UNI-T UT60G (UT-D02 cable)")
-DRV(uni_t_ut61b_ser, UNI_T_UT61B_SER, "uni-t-ut61b-ser", "UNI-T UT61B (UT-D02 cable)")
-DRV(uni_t_ut61c_ser, UNI_T_UT61C_SER, "uni-t-ut61c-ser", "UNI-T UT61C (UT-D02 cable)")
-DRV(uni_t_ut61d_ser, UNI_T_UT61D_SER, "uni-t-ut61d-ser", "UNI-T UT61D (UT-D02 cable)")
-DRV(uni_t_ut61e_ser, UNI_T_UT61E_SER, "uni-t-ut61e-ser", "UNI-T UT61E (UT-D02 cable)")
-DRV(iso_tech_idm103n, ISO_TECH_IDM103N, "iso-tech-idm103n", "ISO-TECH IDM103N")
-DRV(tenma_72_7745_ser, TENMA_72_7745_SER, "tenma-72-7745-ser", "Tenma 72-7745 (UT-D02 cable)")
-DRV(tenma_72_7750_ser, TENMA_72_7750_SER, "tenma-72-7750-ser", "Tenma 72-7750 (UT-D02 cable)")
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
- * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 <stdlib.h>
-#include <math.h>
-#include <string.h>
-#include <errno.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-static void log_dmm_packet(const uint8_t *buf)
-{
- sr_dbg("DMM packet: %02x %02x %02x %02x %02x %02x %02x"
- " %02x %02x %02x %02x %02x %02x %02x",
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
- buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]);
-}
-
-static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi,
- int dmm, void *info)
-{
- float floatval;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct dev_context *devc;
-
- log_dmm_packet(buf);
- devc = sdi->priv;
-
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
-
- analog.channels = sdi->channels;
- analog.num_samples = 1;
- analog.mq = -1;
-
- dmms[dmm].packet_parse(buf, &floatval, &analog, info);
- analog.data = &floatval;
-
- /* If this DMM needs additional handling, call the resp. function. */
- if (dmms[dmm].dmm_details)
- dmms[dmm].dmm_details(&analog, info);
-
- if (analog.mq != -1) {
- /* Got a measurement. */
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
- devc->num_samples++;
- }
-}
-
-/** Request packet, if required. */
-SR_PRIV int req_packet(struct sr_dev_inst *sdi, int dmm)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- int ret;
-
- if (!dmms[dmm].packet_request)
- return SR_OK;
-
- devc = sdi->priv;
- serial = sdi->conn;
-
- if (devc->req_next_at && (devc->req_next_at > g_get_monotonic_time())) {
- sr_spew("Not requesting new packet yet, %" PRIi64 " ms left.",
- ((devc->req_next_at - g_get_monotonic_time()) / 1000));
- return SR_OK;
- }
-
- ret = dmms[dmm].packet_request(serial);
- if (ret < 0) {
- sr_err("Failed to request packet: %d.", ret);
- return ret;
- }
-
- if (dmms[dmm].req_timeout_ms)
- devc->req_next_at = g_get_monotonic_time() + (dmms[dmm].req_timeout_ms * 1000);
-
- return SR_OK;
-}
-
-static void handle_new_data(struct sr_dev_inst *sdi, int dmm, void *info)
-{
- struct dev_context *devc;
- int len, i, offset = 0;
- struct sr_serial_dev_inst *serial;
-
- devc = sdi->priv;
- serial = sdi->conn;
-
- /* Try to get as much data as the buffer can hold. */
- len = DMM_BUFSIZE - devc->buflen;
- len = serial_read(serial, devc->buf + devc->buflen, len);
- if (len == 0)
- return; /* No new bytes, nothing to do. */
- if (len < 0) {
- sr_err("Serial port read error: %d.", len);
- return;
- }
- devc->buflen += len;
-
- /* Now look for packets in that data. */
- while ((devc->buflen - offset) >= dmms[dmm].packet_size) {
- if (dmms[dmm].packet_valid(devc->buf + offset)) {
- handle_packet(devc->buf + offset, sdi, dmm, info);
- offset += dmms[dmm].packet_size;
-
- /* Request next packet, if required. */
- if (!dmms[dmm].packet_request)
- break;
- if (dmms[dmm].req_timeout_ms || dmms[dmm].req_delay_ms)
- devc->req_next_at = g_get_monotonic_time() +
- dmms[dmm].req_delay_ms * 1000;
- req_packet(sdi, dmm);
- } else {
- offset++;
- }
- }
-
- /* If we have any data left, move it to the beginning of our buffer. */
- for (i = 0; i < devc->buflen - offset; i++)
- devc->buf[i] = devc->buf[offset + i];
- devc->buflen -= offset;
-}
-
-static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- int64_t time;
-
- (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, dmm, info);
- } else {
- /* Timeout; send another packet request if DMM needs it. */
- if (dmms[dmm].packet_request && (req_packet(sdi, dmm) < 0))
- return FALSE;
- }
-
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
-
- if (devc->limit_msec) {
- time = (g_get_monotonic_time() - devc->starttime) / 1000;
- if (time > (int64_t)devc->limit_msec) {
- sr_info("Requested time limit reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
- }
-
- return TRUE;
-}
-
-#define RECEIVE_DATA(ID_UPPER, DMM_DRIVER) \
-SR_PRIV int receive_data_##ID_UPPER(int fd, int revents, void *cb_data) { \
- struct DMM_DRIVER##_info info; \
- return receive_data(fd, revents, ID_UPPER, &info, cb_data); }
-
-/* Driver-specific receive_data() wrappers */
-RECEIVE_DATA(BBCGM_M2110, metex14) /* metex14_info used as a dummy. */
-RECEIVE_DATA(DIGITEK_DT4000ZC, fs9721)
-RECEIVE_DATA(TEKPOWER_TP4000ZC, fs9721)
-RECEIVE_DATA(METEX_ME31, metex14)
-RECEIVE_DATA(PEAKTECH_3410, metex14)
-RECEIVE_DATA(MASTECH_MAS345, metex14)
-RECEIVE_DATA(VA_VA18B, fs9721)
-RECEIVE_DATA(VA_VA40B, fs9721)
-RECEIVE_DATA(METEX_M3640D, metex14)
-RECEIVE_DATA(METEX_M4650CR, metex14)
-RECEIVE_DATA(PEAKTECH_4370, metex14)
-RECEIVE_DATA(PCE_PCE_DM32, fs9721)
-RECEIVE_DATA(RADIOSHACK_22_168, metex14)
-RECEIVE_DATA(RADIOSHACK_22_805, metex14)
-RECEIVE_DATA(RADIOSHACK_22_812, rs9lcd)
-RECEIVE_DATA(TECPEL_DMM_8061_SER, fs9721)
-RECEIVE_DATA(VOLTCRAFT_M3650CR, metex14)
-RECEIVE_DATA(VOLTCRAFT_M3650D, metex14)
-RECEIVE_DATA(VOLTCRAFT_M4650CR, metex14)
-RECEIVE_DATA(VOLTCRAFT_ME42, metex14)
-RECEIVE_DATA(VOLTCRAFT_VC820_SER, fs9721)
-RECEIVE_DATA(VOLTCRAFT_VC830_SER, fs9922)
-RECEIVE_DATA(VOLTCRAFT_VC840_SER, fs9721)
-RECEIVE_DATA(UNI_T_UT60A_SER, fs9721)
-RECEIVE_DATA(UNI_T_UT60E_SER, fs9721)
-RECEIVE_DATA(UNI_T_UT60G_SER, es519xx)
-RECEIVE_DATA(UNI_T_UT61B_SER, fs9922)
-RECEIVE_DATA(UNI_T_UT61C_SER, fs9922)
-RECEIVE_DATA(UNI_T_UT61D_SER, fs9922)
-RECEIVE_DATA(UNI_T_UT61E_SER, es519xx)
-RECEIVE_DATA(ISO_TECH_IDM103N, es519xx)
-RECEIVE_DATA(TENMA_72_7745_SER, fs9721)
-RECEIVE_DATA(TENMA_72_7750_SER, es519xx)
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
- *
- * 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_DMM_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_SERIAL_DMM_PROTOCOL_H
-
-#define LOG_PREFIX "serial-dmm"
-
-enum {
- BBCGM_M2110,
- DIGITEK_DT4000ZC,
- TEKPOWER_TP4000ZC,
- METEX_ME31,
- PEAKTECH_3410,
- MASTECH_MAS345,
- VA_VA18B,
- VA_VA40B,
- METEX_M3640D,
- METEX_M4650CR,
- PEAKTECH_4370,
- PCE_PCE_DM32,
- RADIOSHACK_22_168,
- RADIOSHACK_22_805,
- RADIOSHACK_22_812,
- TECPEL_DMM_8061_SER,
- VOLTCRAFT_M3650CR,
- VOLTCRAFT_M3650D,
- VOLTCRAFT_M4650CR,
- VOLTCRAFT_ME42,
- VOLTCRAFT_VC820_SER,
- VOLTCRAFT_VC830_SER,
- VOLTCRAFT_VC840_SER,
- UNI_T_UT60A_SER,
- UNI_T_UT60E_SER,
- UNI_T_UT60G_SER,
- UNI_T_UT61B_SER,
- UNI_T_UT61C_SER,
- UNI_T_UT61D_SER,
- UNI_T_UT61E_SER,
- ISO_TECH_IDM103N,
- TENMA_72_7745_SER,
- TENMA_72_7750_SER,
-};
-
-struct dmm_info {
- /** Manufacturer/brand. */
- char *vendor;
- /** Model. */
- char *device;
- /** serialconn string. */
- char *conn;
- /** Baud rate. */
- uint32_t baudrate;
- /** Packet size in bytes. */
- int packet_size;
- /** Request timeout [ms] before request is considered lost and a new
- * one is sent. Used only if device needs polling. */
- int64_t req_timeout_ms;
- /** Delay between reception of packet and next request. Some DMMs
- * need this. Used only if device needs polling. */
- int64_t req_delay_ms;
- /** Packet request function. */
- int (*packet_request)(struct sr_serial_dev_inst *);
- /** Packet validation function. */
- gboolean (*packet_valid)(const uint8_t *);
- /** Packet parsing function. */
- int (*packet_parse)(const uint8_t *, float *,
- struct sr_datafeed_analog *, void *);
- /** */
- void (*dmm_details)(struct sr_datafeed_analog *, void *);
- /** libsigrok driver info struct. */
- struct sr_dev_driver *di;
- /** Data reception function. */
- int (*receive_data)(int, int, void *);
-};
-
-extern SR_PRIV struct dmm_info dmms[];
-
-#define DMM_BUFSIZE 256
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /** The current sampling limit (in number of samples). */
- uint64_t limit_samples;
-
- /** The time limit (in milliseconds). */
- uint64_t limit_msec;
-
- /** Opaque pointer passed in by the frontend. */
- void *cb_data;
-
- /** The current number of already received samples. */
- uint64_t num_samples;
-
- /** The starting time of current sampling run. */
- int64_t starttime;
-
- uint8_t buf[DMM_BUFSIZE];
- int bufoffset;
- int buflen;
-
- /** The timestamp [µs] to send the next request.
- * Used only if device needs polling. */
- int64_t req_next_at;
-};
-
-SR_PRIV int req_packet(struct sr_dev_inst *sdi, int dmm);
-
-SR_PRIV int receive_data_BBCGM_M2110(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_DIGITEK_DT4000ZC(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TEKPOWER_TP4000ZC(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_METEX_ME31(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_PEAKTECH_3410(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_MASTECH_MAS345(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VA_VA18B(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VA_VA40B(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_METEX_M3640D(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_METEX_M4650CR(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_PEAKTECH_4370(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_PCE_PCE_DM32(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_RADIOSHACK_22_168(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_RADIOSHACK_22_805(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_RADIOSHACK_22_812(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TECPEL_DMM_8061_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_M3650CR(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_M3650D(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_M4650CR(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_ME42(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC820_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC830_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC840_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60A_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60E_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60G_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61B_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61C_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61D_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61E_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_ISO_TECH_IDM103N(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TENMA_72_7745_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TENMA_72_7750_SER(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
- *
- * 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 "protocol.h"
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include <glib.h>
-#include <libusb.h>
-#include <stdlib.h>
-#include <string.h>
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_SAMPLERATE,
- SR_CONF_EXTERNAL_CLOCK,
- SR_CONF_CLOCK_EDGE,
- SR_CONF_TRIGGER_MATCH,
- SR_CONF_TRIGGER_SOURCE,
- SR_CONF_TRIGGER_SLOPE,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_LIMIT_SAMPLES,
-};
-
-static const int32_t trigger_matches[] = {
- SR_TRIGGER_ZERO,
- SR_TRIGGER_ONE,
- SR_TRIGGER_RISING,
- SR_TRIGGER_FALLING,
-};
-
-/* The hardware supports more samplerates than these, but these are the
- * options hardcoded into the vendor's Windows GUI.
- */
-static const uint64_t samplerates[] = {
- SR_MHZ(125), SR_MHZ(100),
- SR_MHZ(50), SR_MHZ(20), SR_MHZ(10),
- SR_MHZ(5), SR_MHZ(2), SR_MHZ(1),
- SR_KHZ(500), SR_KHZ(200), SR_KHZ(100),
- SR_KHZ(50), SR_KHZ(20), SR_KHZ(10),
- SR_KHZ(5), SR_KHZ(2), SR_KHZ(1),
- SR_HZ(500), SR_HZ(200), SR_HZ(100),
-};
-
-/* Names assigned to available trigger sources. Indices must match
- * trigger_source enum values.
- */
-static const char *const trigger_source_names[] = { "CH", "TRG" };
-
-/* Names assigned to available trigger slope choices. Indices must
- * match the signal_edge enum values.
- */
-static const char *const signal_edge_names[] = { "r", "f" };
-
-SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info;
-static struct sr_dev_driver *const di = &sysclk_lwla_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *gen_channel_list(int num_channels)
-{
- GSList *list;
- struct sr_channel *ch;
- int i;
- char name[8];
-
- list = NULL;
-
- for (i = num_channels; i > 0; --i) {
- /* The LWLA series simply number channels from CH1 to CHxx. */
- g_snprintf(name, sizeof(name), "CH%d", i);
-
- ch = sr_channel_new(i - 1, SR_CHANNEL_LOGIC, TRUE, name);
- list = g_slist_prepend(list, ch);
- }
-
- return list;
-}
-
-static struct sr_dev_inst *dev_inst_new(int device_index)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
-
- /* Allocate memory for our private driver context. */
- devc = g_try_new0(struct dev_context, 1);
- if (!devc) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
-
- /* Register the device with libsigrok. */
- sdi = sr_dev_inst_new(device_index, SR_ST_INACTIVE,
- VENDOR_NAME, MODEL_NAME, NULL);
- if (!sdi) {
- sr_err("Failed to instantiate device.");
- g_free(devc);
- return NULL;
- }
-
- /* Enable all channels to match the default channel configuration. */
- devc->channel_mask = ALL_CHANNELS_MASK;
- devc->samplerate = DEFAULT_SAMPLERATE;
-
- sdi->priv = devc;
- sdi->channels = gen_channel_list(NUM_CHANNELS);
-
- return sdi;
-}
-
-static GSList *scan(GSList *options)
-{
- GSList *usb_devices, *devices, *node;
- struct drv_context *drvc;
- struct sr_dev_inst *sdi;
- struct sr_usb_dev_inst *usb;
- struct sr_config *src;
- const char *conn;
- int device_index;
-
- drvc = di->priv;
- conn = USB_VID_PID;
-
- for (node = options; node != NULL; node = node->next) {
- src = node->data;
- if (src->key == SR_CONF_CONN) {
- conn = g_variant_get_string(src->data, NULL);
- break;
- }
- }
- usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
- devices = NULL;
- device_index = g_slist_length(drvc->instances);
-
- for (node = usb_devices; node != NULL; node = node->next) {
- usb = node->data;
-
- /* Create sigrok device instance. */
- sdi = dev_inst_new(device_index);
- if (!sdi) {
- sr_usb_dev_inst_free(usb);
- continue;
- }
- sdi->driver = di;
- sdi->inst_type = SR_INST_USB;
- sdi->conn = usb;
-
- /* Register device instance with driver. */
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- }
-
- g_slist_free(usb_devices);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- struct drv_context *drvc;
-
- drvc = di->priv;
-
- return drvc->instances;
-}
-
-static void clear_dev_context(void *priv)
-{
- struct dev_context *devc;
-
- devc = priv;
-
- sr_dbg("Device context cleared.");
-
- lwla_free_acquisition_state(devc->acquisition);
- g_free(devc);
-}
-
-static int dev_clear(void)
-{
- return std_dev_clear(di, &clear_dev_context);
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc;
- struct sr_usb_dev_inst *usb;
- int ret;
-
- drvc = di->priv;
-
- if (!drvc) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
-
- ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb);
- if (ret != SR_OK)
- return ret;
-
- ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
- if (ret < 0) {
- sr_err("Failed to claim interface: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- sdi->status = SR_ST_INITIALIZING;
-
- ret = lwla_init_device(sdi);
-
- if (ret == SR_OK)
- sdi->status = SR_ST_ACTIVE;
-
- return ret;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
- if (!usb->devhdl)
- return SR_OK;
-
- sdi->status = SR_ST_INACTIVE;
-
- /* Trigger download of the shutdown bitstream. */
- if (lwla_set_clock_config(sdi) != SR_OK)
- sr_err("Unable to shut down device.");
-
- libusb_release_interface(usb->devhdl, USB_INTERFACE);
- libusb_close(usb->devhdl);
-
- usb->devhdl = NULL;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- return dev_clear();
-}
-
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- size_t idx;
-
- (void)cg;
-
- if (!sdi)
- return SR_ERR_ARG;
-
- devc = sdi->priv;
-
- switch (key) {
- case SR_CONF_SAMPLERATE:
- *data = g_variant_new_uint64(devc->samplerate);
- break;
- case SR_CONF_LIMIT_MSEC:
- *data = g_variant_new_uint64(devc->limit_msec);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_EXTERNAL_CLOCK:
- *data = g_variant_new_boolean(devc->cfg_clock_source
- == CLOCK_EXT_CLK);
- break;
- case SR_CONF_CLOCK_EDGE:
- idx = devc->cfg_clock_edge;
- if (idx >= G_N_ELEMENTS(signal_edge_names))
- return SR_ERR_BUG;
- *data = g_variant_new_string(signal_edge_names[idx]);
- break;
- case SR_CONF_TRIGGER_SOURCE:
- idx = devc->cfg_trigger_source;
- if (idx >= G_N_ELEMENTS(trigger_source_names))
- return SR_ERR_BUG;
- *data = g_variant_new_string(trigger_source_names[idx]);
- break;
- case SR_CONF_TRIGGER_SLOPE:
- idx = devc->cfg_trigger_slope;
- if (idx >= G_N_ELEMENTS(signal_edge_names))
- return SR_ERR_BUG;
- *data = g_variant_new_string(signal_edge_names[idx]);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-/* Helper for mapping a string-typed configuration value to an index
- * within a table of possible values.
- */
-static int lookup_index(GVariant *value, const char *const *table, int len)
-{
- const char *entry;
- int i;
-
- entry = g_variant_get_string(value, NULL);
- if (!entry)
- return -1;
-
- /* Linear search is fine for very small tables. */
- for (i = 0; i < len; ++i) {
- if (strcmp(entry, table[i]) == 0)
- return i;
- }
- return -1;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- uint64_t value;
- struct dev_context *devc;
- int idx;
-
- (void)cg;
-
- devc = sdi->priv;
- if (!devc)
- return SR_ERR_DEV_CLOSED;
-
- switch (key) {
- case SR_CONF_SAMPLERATE:
- value = g_variant_get_uint64(data);
- if (value < samplerates[G_N_ELEMENTS(samplerates) - 1]
- || value > samplerates[0])
- return SR_ERR_SAMPLERATE;
- devc->samplerate = value;
- break;
- case SR_CONF_LIMIT_MSEC:
- value = g_variant_get_uint64(data);
- if (value > MAX_LIMIT_MSEC)
- return SR_ERR_ARG;
- devc->limit_msec = value;
- break;
- case SR_CONF_LIMIT_SAMPLES:
- value = g_variant_get_uint64(data);
- if (value > MAX_LIMIT_SAMPLES)
- return SR_ERR_ARG;
- devc->limit_samples = value;
- break;
- case SR_CONF_EXTERNAL_CLOCK:
- devc->cfg_clock_source = (g_variant_get_boolean(data))
- ? CLOCK_EXT_CLK : CLOCK_INTERNAL;
- break;
- case SR_CONF_CLOCK_EDGE:
- idx = lookup_index(data, signal_edge_names,
- G_N_ELEMENTS(signal_edge_names));
- if (idx < 0)
- return SR_ERR_ARG;
- devc->cfg_clock_edge = idx;
- break;
- case SR_CONF_TRIGGER_SOURCE:
- idx = lookup_index(data, trigger_source_names,
- G_N_ELEMENTS(trigger_source_names));
- if (idx < 0)
- return SR_ERR_ARG;
- devc->cfg_trigger_source = idx;
- break;
- case SR_CONF_TRIGGER_SLOPE:
- idx = lookup_index(data, signal_edge_names,
- G_N_ELEMENTS(signal_edge_names));
- if (idx < 0)
- return SR_ERR_ARG;
- devc->cfg_trigger_slope = idx;
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_channel_set(const struct sr_dev_inst *sdi,
- struct sr_channel *ch, unsigned int changes)
-{
- uint64_t channel_bit;
- struct dev_context *devc;
-
- devc = sdi->priv;
- if (!devc)
- return SR_ERR_DEV_CLOSED;
-
- if (ch->index < 0 || ch->index >= NUM_CHANNELS) {
- sr_err("Channel index %d out of range.", ch->index);
- return SR_ERR_BUG;
- }
- channel_bit = (uint64_t)1 << ch->index;
-
- if ((changes & SR_CHANNEL_SET_ENABLED) != 0) {
- /* Enable or disable input channel for this channel. */
- if (ch->enabled)
- devc->channel_mask |= channel_bit;
- else
- devc->channel_mask &= ~channel_bit;
- }
-
- return SR_OK;
-}
-
-static int config_commit(const struct sr_dev_inst *sdi)
-{
- if (sdi->status != SR_ST_ACTIVE) {
- sr_err("Device not ready (status %d).", (int)sdi->status);
- return SR_ERR;
- }
-
- return lwla_set_clock_config(sdi);
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- GVariant *gvar;
- GVariantBuilder gvb;
-
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, G_N_ELEMENTS(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, G_N_ELEMENTS(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- samplerates, G_N_ELEMENTS(samplerates),
- sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TRIGGER_MATCH:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- trigger_matches, ARRAY_SIZE(trigger_matches),
- sizeof(int32_t));
- break;
- case SR_CONF_TRIGGER_SOURCE:
- *data = g_variant_new_strv(trigger_source_names,
- G_N_ELEMENTS(trigger_source_names));
- break;
- case SR_CONF_TRIGGER_SLOPE:
- case SR_CONF_CLOCK_EDGE:
- *data = g_variant_new_strv(signal_edge_names,
- G_N_ELEMENTS(signal_edge_names));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct acquisition_state *acq;
- int ret;
-
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- drvc = di->priv;
-
- if (devc->acquisition) {
- sr_err("Acquisition still in progress?");
- return SR_ERR;
- }
- acq = lwla_alloc_acquisition_state();
- if (!acq)
- return SR_ERR_MALLOC;
-
- devc->stopping_in_progress = FALSE;
- devc->transfer_error = FALSE;
-
- sr_info("Starting acquisition.");
-
- devc->acquisition = acq;
- lwla_convert_trigger(sdi);
- ret = lwla_setup_acquisition(sdi);
- if (ret != SR_OK) {
- sr_err("Failed to set up acquisition.");
- devc->acquisition = NULL;
- lwla_free_acquisition_state(acq);
- return ret;
- }
-
- ret = lwla_start_acquisition(sdi);
- if (ret != SR_OK) {
- sr_err("Failed to start acquisition.");
- devc->acquisition = NULL;
- lwla_free_acquisition_state(acq);
- return ret;
- }
- usb_source_add(sdi->session, drvc->sr_ctx, 100, &lwla_receive_data,
- (struct sr_dev_inst *)sdi);
-
- sr_info("Waiting for data.");
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(sdi, LOG_PREFIX);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- sr_dbg("Stopping acquisition.");
-
- sdi->status = SR_ST_STOPPING;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info = {
- .name = "sysclk-lwla",
- .longname = "SysClk LWLA series",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = dev_clear,
- .config_get = config_get,
- .config_set = config_set,
- .config_channel_set = config_channel_set,
- .config_commit = config_commit,
- .config_list = config_list,
- .dev_open = dev_open,
- .dev_close = dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
- *
- * 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 "lwla.h"
-#include "protocol.h"
-#include "libsigrok-internal.h"
-#include <errno.h>
-#include <glib/gstdio.h>
-
-#define BITSTREAM_MAX_SIZE 262144 /* bitstream size limit for safety */
-#define BITSTREAM_HEADER_SIZE 4 /* transfer header size in bytes */
-
-/* Load a bitstream file into memory. Returns a newly allocated array
- * consisting of a 32-bit length field followed by the bitstream data.
- */
-static unsigned char *load_bitstream_file(const char *filename, int *length_p)
-{
- GStatBuf statbuf;
- FILE *file;
- unsigned char *stream;
- size_t length, count;
-
- /* Retrieve and validate the file size. */
- if (g_stat(filename, &statbuf) < 0) {
- sr_err("Failed to access bitstream file: %s.",
- g_strerror(errno));
- return NULL;
- }
- if (!S_ISREG(statbuf.st_mode)) {
- sr_err("Bitstream is not a regular file.");
- return NULL;
- }
- if (statbuf.st_size <= 0 || statbuf.st_size > BITSTREAM_MAX_SIZE) {
- sr_err("Refusing to load bitstream of unreasonable size "
- "(%" PRIu64 " bytes).", (uint64_t)statbuf.st_size);
- return NULL;
- }
-
- /* The message length includes the 4-byte header. */
- length = BITSTREAM_HEADER_SIZE + statbuf.st_size;
- stream = g_try_malloc(length);
- if (!stream) {
- sr_err("Failed to allocate bitstream buffer.");
- return NULL;
- }
-
- file = g_fopen(filename, "rb");
- if (!file) {
- sr_err("Failed to open bitstream file: %s.", g_strerror(errno));
- g_free(stream);
- return NULL;
- }
-
- /* Write the message length header. */
- *(uint32_t *)stream = GUINT32_TO_BE(length);
-
- count = fread(stream + BITSTREAM_HEADER_SIZE,
- length - BITSTREAM_HEADER_SIZE, 1, file);
- if (count != 1) {
- sr_err("Failed to read bitstream file: %s.", g_strerror(errno));
- fclose(file);
- g_free(stream);
- return NULL;
- }
- fclose(file);
-
- *length_p = length;
- return stream;
-}
-
-/* Load a Raw Binary File (.rbf) from the firmware directory and transfer
- * it to the device.
- */
-SR_PRIV int lwla_send_bitstream(const struct sr_usb_dev_inst *usb,
- const char *basename)
-{
- char *filename;
- unsigned char *stream;
- int ret;
- int length;
- int xfer_len;
-
- if (!usb || !basename)
- return SR_ERR_BUG;
-
- filename = g_build_filename(FIRMWARE_DIR, basename, NULL);
- sr_info("Downloading FPGA bitstream at '%s'.", filename);
-
- stream = load_bitstream_file(filename, &length);
- g_free(filename);
-
- if (!stream)
- return SR_ERR;
-
- /* Transfer the entire bitstream in one URB. */
- ret = libusb_bulk_transfer(usb->devhdl, EP_BITSTREAM,
- stream, length, &xfer_len, USB_TIMEOUT);
- g_free(stream);
-
- if (ret != 0) {
- sr_err("Failed to transfer bitstream: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
- if (xfer_len != length) {
- sr_err("Failed to transfer bitstream: incorrect length "
- "%d != %d.", xfer_len, length);
- return SR_ERR;
- }
- sr_info("FPGA bitstream download of %d bytes done.", xfer_len);
-
- /* This delay appears to be necessary for reliable operation. */
- g_usleep(30000);
-
- return SR_OK;
-}
-
-SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb,
- const uint16_t *command, int cmd_len)
-{
- int ret;
- int xfer_len;
-
- if (!usb || !command || cmd_len <= 0)
- return SR_ERR_BUG;
-
- xfer_len = 0;
- ret = libusb_bulk_transfer(usb->devhdl, EP_COMMAND,
- (unsigned char *)command, cmd_len * 2,
- &xfer_len, USB_TIMEOUT);
- if (ret != 0) {
- sr_dbg("Failed to send command %d: %s.",
- LWLA_TO_UINT16(command[0]), libusb_error_name(ret));
- return SR_ERR;
- }
- if (xfer_len != cmd_len * 2) {
- sr_dbg("Failed to send command %d: incorrect length %d != %d.",
- LWLA_TO_UINT16(command[0]), xfer_len, cmd_len * 2);
- return SR_ERR;
- }
- return SR_OK;
-}
-
-SR_PRIV int lwla_receive_reply(const struct sr_usb_dev_inst *usb,
- uint32_t *reply, int reply_len, int expect_len)
-{
- int ret;
- int xfer_len;
-
- if (!usb || !reply || reply_len <= 0)
- return SR_ERR_BUG;
-
- xfer_len = 0;
- ret = libusb_bulk_transfer(usb->devhdl, EP_REPLY,
- (unsigned char *)reply, reply_len * 4,
- &xfer_len, USB_TIMEOUT);
- if (ret != 0) {
- sr_dbg("Failed to receive reply: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- if (xfer_len != expect_len * 4) {
- sr_dbg("Failed to receive reply: incorrect length %d != %d.",
- xfer_len, expect_len * 4);
- return SR_ERR;
- }
- return SR_OK;
-}
-
-SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb,
- uint16_t reg, uint32_t *value)
-{
- int ret;
- uint16_t command[2];
- uint32_t reply[128]; /* full EP buffer to avoid overflows */
-
- command[0] = LWLA_WORD(CMD_READ_REG);
- command[1] = LWLA_WORD(reg);
-
- ret = lwla_send_command(usb, command, G_N_ELEMENTS(command));
-
- if (ret != SR_OK)
- return ret;
-
- ret = lwla_receive_reply(usb, reply, G_N_ELEMENTS(reply), 1);
-
- if (ret == SR_OK)
- *value = LWLA_TO_UINT32(reply[0]);
-
- return ret;
-}
-
-SR_PRIV int lwla_write_reg(const struct sr_usb_dev_inst *usb,
- uint16_t reg, uint32_t value)
-{
- uint16_t command[4];
-
- command[0] = LWLA_WORD(CMD_WRITE_REG);
- command[1] = LWLA_WORD(reg);
- command[2] = LWLA_WORD_0(value);
- command[3] = LWLA_WORD_1(value);
-
- return lwla_send_command(usb, command, G_N_ELEMENTS(command));
-}
-
-SR_PRIV int lwla_write_regs(const struct sr_usb_dev_inst *usb,
- const struct regval_pair *regvals, int count)
-{
- int i;
- int ret;
-
- ret = SR_OK;
-
- for (i = 0; i < count; ++i) {
- ret = lwla_write_reg(usb, regvals[i].reg, regvals[i].val);
-
- if (ret != SR_OK)
- break;
- }
-
- return ret;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
- *
- * 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_SYSCLK_LWLA_LWLA_H
-#define LIBSIGROK_HARDWARE_SYSCLK_LWLA_LWLA_H
-
-#include "libsigrok.h"
-#include <stdint.h>
-#include <libusb.h>
-#include <glib.h>
-
-struct sr_usb_dev_inst;
-
-/* Rotate argument n bits to the left.
- * This construct is an idiom recognized by GCC as bit rotation.
- */
-#define LROTATE(a, n) (((a) << (n)) | ((a) >> (CHAR_BIT * sizeof(a) - (n))))
-
-/* Convert 16-bit little endian LWLA protocol word to machine word order. */
-#define LWLA_TO_UINT16(val) GUINT16_FROM_LE(val)
-
-/* Convert 32-bit mixed endian LWLA protocol word to machine word order. */
-#define LWLA_TO_UINT32(val) LROTATE(GUINT32_FROM_LE(val), 16)
-
-/* Convert 16-bit argument to LWLA protocol word. */
-#define LWLA_WORD(val) GUINT16_TO_LE(val)
-
-/* Extract 16-bit units in mixed endian order from 32/64-bit value. */
-#define LWLA_WORD_0(val) GUINT16_TO_LE(((val) >> 16) & 0xFFFF)
-#define LWLA_WORD_1(val) GUINT16_TO_LE((val) & 0xFFFF)
-#define LWLA_WORD_2(val) GUINT16_TO_LE(((val) >> 48) & 0xFFFF)
-#define LWLA_WORD_3(val) GUINT16_TO_LE(((val) >> 32) & 0xFFFF)
-
-/** USB device end points.
- */
-enum {
- EP_COMMAND = 2,
- EP_BITSTREAM = 4,
- EP_REPLY = 6 | LIBUSB_ENDPOINT_IN
-};
-
-/** LWLA protocol command ID codes.
- */
-enum {
- CMD_READ_REG = 1,
- CMD_WRITE_REG = 2,
- CMD_READ_MEM = 6,
- CMD_CAP_SETUP = 7,
- CMD_CAP_STATUS = 8,
-};
-
-/** LWLA capture state flags.
- */
-enum {
- STATUS_CAPTURING = 1 << 1,
- STATUS_TRIGGERED = 1 << 4,
- STATUS_MEM_AVAIL = 1 << 5,
- STATUS_FLAG_MASK = 0x3F
-};
-
-/** LWLA register addresses.
- */
-enum {
- REG_MEM_CTRL2 = 0x1074, /* capture buffer control ??? */
- REG_MEM_FILL = 0x1078, /* capture buffer fill level */
- REG_MEM_CTRL4 = 0x107C, /* capture buffer control ??? */
-
- REG_DIV_BYPASS = 0x1094, /* bypass clock divider flag */
-
- REG_CMD_CTRL1 = 0x10B0, /* command control ??? */
- REG_CMD_CTRL2 = 0x10B4, /* command control ??? */
- REG_CMD_CTRL3 = 0x10B8, /* command control ??? */
- REG_CMD_CTRL4 = 0x10BC, /* command control ??? */
-
- REG_FREQ_CH1 = 0x10C0, /* channel 1 live frequency */
- REG_FREQ_CH2 = 0x10C4, /* channel 2 live frequency */
- REG_FREQ_CH3 = 0x10C8, /* channel 3 live frequency */
- REG_FREQ_CH4 = 0x10CC, /* channel 4 live frequency */
-};
-
-/** Register/value pair.
- */
-struct regval_pair {
- unsigned int reg;
- unsigned int val;
-};
-
-SR_PRIV int lwla_send_bitstream(const struct sr_usb_dev_inst *usb,
- const char *basename);
-
-SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb,
- const uint16_t *command, int cmd_len);
-
-SR_PRIV int lwla_receive_reply(const struct sr_usb_dev_inst *usb,
- uint32_t *reply, int reply_len, int expect_len);
-
-SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb,
- uint16_t reg, uint32_t *value);
-
-SR_PRIV int lwla_write_reg(const struct sr_usb_dev_inst *usb,
- uint16_t reg, uint32_t value);
-
-SR_PRIV int lwla_write_regs(const struct sr_usb_dev_inst *usb,
- const struct regval_pair *regvals, int count);
-
-#endif /* !LIBSIGROK_HARDWARE_SYSCLK_LWLA_LWLA_H */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
- *
- * 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 "protocol.h"
-#include <string.h>
-
-/* Bit mask for the RLE repeat-count-follows flag. */
-#define RLE_FLAG_LEN_FOLLOWS ((uint64_t)1 << 35)
-
-/* Start address of capture status memory area to read. */
-#define CAP_STAT_ADDR 5
-
-/* Number of 64-bit words read from the capture status memory. */
-#define CAP_STAT_LEN 5
-
-/* The bitstream filenames are indexed by the clock_config enumeration.
- */
-static const char bitstream_map[][32] = {
- "sysclk-lwla1034-off.rbf",
- "sysclk-lwla1034-int.rbf",
- "sysclk-lwla1034-extpos.rbf",
- "sysclk-lwla1034-extneg.rbf",
-};
-
-/* Submit an already filled-in USB transfer.
- */
-static int submit_transfer(struct dev_context *devc,
- struct libusb_transfer *xfer)
-{
- int ret;
-
- ret = libusb_submit_transfer(xfer);
-
- if (ret != 0) {
- sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
- devc->transfer_error = TRUE;
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-/* Set up the LWLA in preparation for an acquisition session.
- */
-static int capture_setup(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct acquisition_state *acq;
- uint64_t divider_count;
- uint64_t trigger_mask;
- uint64_t memory_limit;
- uint16_t command[3 + 10*4];
-
- devc = sdi->priv;
- acq = devc->acquisition;
-
- command[0] = LWLA_WORD(CMD_CAP_SETUP);
- command[1] = LWLA_WORD(0); /* address */
- command[2] = LWLA_WORD(10); /* length */
-
- command[3] = LWLA_WORD_0(devc->channel_mask);
- command[4] = LWLA_WORD_1(devc->channel_mask);
- command[5] = LWLA_WORD_2(devc->channel_mask);
- command[6] = LWLA_WORD_3(devc->channel_mask);
-
- /* Set the clock divide counter maximum for samplerates of up to
- * 100 MHz. At the highest samplerate of 125 MHz the clock divider
- * is bypassed.
- */
- if (!acq->bypass_clockdiv && devc->samplerate > 0)
- divider_count = SR_MHZ(100) / devc->samplerate - 1;
- else
- divider_count = 0;
-
- command[7] = LWLA_WORD_0(divider_count);
- command[8] = LWLA_WORD_1(divider_count);
- command[9] = LWLA_WORD_2(divider_count);
- command[10] = LWLA_WORD_3(divider_count);
-
- command[11] = LWLA_WORD_0(devc->trigger_values);
- command[12] = LWLA_WORD_1(devc->trigger_values);
- command[13] = LWLA_WORD_2(devc->trigger_values);
- command[14] = LWLA_WORD_3(devc->trigger_values);
-
- command[15] = LWLA_WORD_0(devc->trigger_edge_mask);
- command[16] = LWLA_WORD_1(devc->trigger_edge_mask);
- command[17] = LWLA_WORD_2(devc->trigger_edge_mask);
- command[18] = LWLA_WORD_3(devc->trigger_edge_mask);
-
- trigger_mask = devc->trigger_mask;
- /* Set bits to select external TRG input edge. */
- if (devc->cfg_trigger_source == TRIGGER_EXT_TRG)
- switch (devc->cfg_trigger_slope) {
- case EDGE_POSITIVE: trigger_mask |= (uint64_t)1 << 35; break;
- case EDGE_NEGATIVE: trigger_mask |= (uint64_t)1 << 34; break;
- }
-
- command[19] = LWLA_WORD_0(trigger_mask);
- command[20] = LWLA_WORD_1(trigger_mask);
- command[21] = LWLA_WORD_2(trigger_mask);
- command[22] = LWLA_WORD_3(trigger_mask);
-
- /* Set the capture memory full threshold. This is slightly less
- * than the actual maximum, most likely in order to compensate for
- * pipeline latency.
- */
- memory_limit = MEMORY_DEPTH - 16;
-
- command[23] = LWLA_WORD_0(memory_limit);
- command[24] = LWLA_WORD_1(memory_limit);
- command[25] = LWLA_WORD_2(memory_limit);
- command[26] = LWLA_WORD_3(memory_limit);
-
- /* Fill remaining 64-bit words with zeroes. */
- memset(&command[27], 0, 16 * sizeof(uint16_t));
-
- return lwla_send_command(sdi->conn, command, G_N_ELEMENTS(command));
-}
-
-/* Issue a register write command as an asynchronous USB transfer.
- */
-static int issue_write_reg(const struct sr_dev_inst *sdi,
- unsigned int reg, unsigned int value)
-{
- struct dev_context *devc;
- struct acquisition_state *acq;
-
- devc = sdi->priv;
- acq = devc->acquisition;
-
- acq->xfer_buf_out[0] = LWLA_WORD(CMD_WRITE_REG);
- acq->xfer_buf_out[1] = LWLA_WORD(reg);
- acq->xfer_buf_out[2] = LWLA_WORD_0(value);
- acq->xfer_buf_out[3] = LWLA_WORD_1(value);
-
- acq->xfer_out->length = 4 * sizeof(uint16_t);
-
- return submit_transfer(devc, acq->xfer_out);
-}
-
-/* Issue a register write command as an asynchronous USB transfer for the
- * next register/value pair of the currently active register write sequence.
- */
-static int issue_next_write_reg(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct regval_pair *regval;
- int ret;
-
- devc = sdi->priv;
-
- if (devc->reg_write_pos >= devc->reg_write_len) {
- sr_err("Already written all registers in sequence.");
- return SR_ERR_BUG;
- }
- regval = &devc->reg_write_seq[devc->reg_write_pos];
-
- ret = issue_write_reg(sdi, regval->reg, regval->val);
- if (ret != SR_OK)
- return ret;
-
- ++devc->reg_write_pos;
- return SR_OK;
-}
-
-/* Issue a capture status request as an asynchronous USB transfer.
- */
-static void request_capture_status(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct acquisition_state *acq;
-
- devc = sdi->priv;
- acq = devc->acquisition;
-
- acq->xfer_buf_out[0] = LWLA_WORD(CMD_CAP_STATUS);
- acq->xfer_buf_out[1] = LWLA_WORD(CAP_STAT_ADDR);
- acq->xfer_buf_out[2] = LWLA_WORD(CAP_STAT_LEN);
-
- acq->xfer_out->length = 3 * sizeof(uint16_t);
-
- if (submit_transfer(devc, acq->xfer_out) == SR_OK)
- devc->state = STATE_STATUS_REQUEST;
-}
-
-/* Issue a request for the capture buffer fill level as
- * an asynchronous USB transfer.
- */
-static void request_capture_length(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct acquisition_state *acq;
-
- devc = sdi->priv;
- acq = devc->acquisition;
-
- acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_REG);
- acq->xfer_buf_out[1] = LWLA_WORD(REG_MEM_FILL);
-
- acq->xfer_out->length = 2 * sizeof(uint16_t);
-
- if (submit_transfer(devc, acq->xfer_out) == SR_OK)
- devc->state = STATE_LENGTH_REQUEST;
-}
-
-/* Initiate the capture memory read operation: Reset the acquisition state
- * and start a sequence of register writes in order to set up the device for
- * reading from the capture buffer.
- */
-static void issue_read_start(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct acquisition_state *acq;
- struct regval_pair *regvals;
-
- devc = sdi->priv;
- acq = devc->acquisition;
-
- /* Reset RLE state. */
- acq->rle = RLE_STATE_DATA;
- acq->sample = 0;
- acq->run_len = 0;
-
- acq->samples_done = 0;
-
- /* For some reason, the start address is 4 rather than 0. */
- acq->mem_addr_done = 4;
- acq->mem_addr_next = 4;
- acq->mem_addr_stop = acq->mem_addr_fill;
-
- /* Sample position in the packet output buffer. */
- acq->out_index = 0;
-
- regvals = devc->reg_write_seq;
-
- regvals[0].reg = REG_DIV_BYPASS;
- regvals[0].val = 1;
-
- regvals[1].reg = REG_MEM_CTRL2;
- regvals[1].val = 2;
-
- regvals[2].reg = REG_MEM_CTRL4;
- regvals[2].val = 4;
-
- devc->reg_write_pos = 0;
- devc->reg_write_len = 3;
-
- if (issue_next_write_reg(sdi) == SR_OK)
- devc->state = STATE_READ_PREPARE;
-}
-
-/* Issue a command as an asynchronous USB transfer which returns the device
- * to normal state after a read operation. Sets a new device context state
- * on success.
- */
-static void issue_read_end(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- if (issue_write_reg(sdi, REG_DIV_BYPASS, 0) == SR_OK)
- devc->state = STATE_READ_END;
-}
-
-/* Decode an incoming reponse to a buffer fill level request and act on it
- * as appropriate. Note that this function changes the device context state.
- */
-static void process_capture_length(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct acquisition_state *acq;
-
- devc = sdi->priv;
- acq = devc->acquisition;
-
- if (acq->xfer_in->actual_length != 4) {
- sr_err("Received size %d doesn't match expected size 4.",
- acq->xfer_in->actual_length);
- devc->transfer_error = TRUE;
- return;
- }
- acq->mem_addr_fill = LWLA_TO_UINT32(acq->xfer_buf_in[0]);
-
- sr_dbg("%zu words in capture buffer.", acq->mem_addr_fill);
-
- if (acq->mem_addr_fill > 0 && sdi->status == SR_ST_ACTIVE)
- issue_read_start(sdi);
- else
- issue_read_end(sdi);
-}
-
-/* Initiate a sequence of register write commands with the effect of
- * cancelling a running capture operation. This sets a new device state
- * if issuing the first command succeeds.
- */
-static void issue_stop_capture(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct regval_pair *regvals;
-
- devc = sdi->priv;
-
- if (devc->stopping_in_progress)
- return;
-
- regvals = devc->reg_write_seq;
-
- regvals[0].reg = REG_CMD_CTRL2;
- regvals[0].val = 10;
-
- regvals[1].reg = REG_CMD_CTRL3;
- regvals[1].val = 0;
-
- regvals[2].reg = REG_CMD_CTRL4;
- regvals[2].val = 0;
-
- regvals[3].reg = REG_CMD_CTRL1;
- regvals[3].val = 0;
-
- regvals[4].reg = REG_DIV_BYPASS;
- regvals[4].val = 0;
-
- devc->reg_write_pos = 0;
- devc->reg_write_len = 5;
-
- if (issue_next_write_reg(sdi) == SR_OK) {
- devc->stopping_in_progress = TRUE;
- devc->state = STATE_STOP_CAPTURE;
- }
-}
-
-/* Decode an incoming capture status reponse and act on it as appropriate.
- * Note that this function changes the device state.
- */
-static void process_capture_status(const struct sr_dev_inst *sdi)
-{
- uint64_t duration;
- struct dev_context *devc;
- struct acquisition_state *acq;
- unsigned int mem_fill;
- unsigned int flags;
-
- devc = sdi->priv;
- acq = devc->acquisition;
-
- if (acq->xfer_in->actual_length != CAP_STAT_LEN * 8) {
- sr_err("Received size %d doesn't match expected size %d.",
- acq->xfer_in->actual_length, CAP_STAT_LEN * 8);
- devc->transfer_error = TRUE;
- return;
- }
-
- /* TODO: Find out the actual bit width of these fields as stored
- * in the FPGA. These fields are definitely less than 64 bit wide
- * internally, and the unused bits occasionally even contain garbage.
- */
- mem_fill = LWLA_TO_UINT32(acq->xfer_buf_in[0]);
- duration = LWLA_TO_UINT32(acq->xfer_buf_in[4]);
- flags = LWLA_TO_UINT32(acq->xfer_buf_in[8]) & STATUS_FLAG_MASK;
-
- /* The LWLA1034 runs at 125 MHz if the clock divider is bypassed.
- * However, the time base used for the duration is apparently not
- * adjusted for this "boost" mode. Whereas normally the duration
- * unit is 1 ms, it is 0.8 ms when the clock divider is bypassed.
- * As 0.8 = 100 MHz / 125 MHz, it seems that the internal cycle
- * counter period is the same as at the 100 MHz setting.
- */
- if (acq->bypass_clockdiv)
- acq->duration_now = duration * 4 / 5;
- else
- acq->duration_now = duration;
-
- sr_spew("Captured %u words, %" PRIu64 " ms, flags 0x%02X.",
- mem_fill, acq->duration_now, flags);
-
- if ((flags & STATUS_TRIGGERED) > (acq->capture_flags & STATUS_TRIGGERED))
- sr_info("Capture triggered.");
-
- acq->capture_flags = flags;
-
- if (acq->duration_now >= acq->duration_max) {
- sr_dbg("Time limit reached, stopping capture.");
- issue_stop_capture(sdi);
- return;
- }
- devc->state = STATE_STATUS_WAIT;
-
- if ((acq->capture_flags & STATUS_TRIGGERED) == 0) {
- sr_spew("Waiting for trigger.");
- } else if ((acq->capture_flags & STATUS_MEM_AVAIL) == 0) {
- sr_dbg("Capture memory filled.");
- request_capture_length(sdi);
- } else if ((acq->capture_flags & STATUS_CAPTURING) != 0) {
- sr_spew("Sampling in progress.");
- }
-}
-
-/* Issue a capture buffer read request as an asynchronous USB transfer.
- * The address and size of the memory area to read are derived from the
- * current acquisition state.
- */
-static void request_read_mem(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct acquisition_state *acq;
- size_t count;
-
- devc = sdi->priv;
- acq = devc->acquisition;
-
- if (acq->mem_addr_next >= acq->mem_addr_stop)
- return;
-
- /* Always read a multiple of 8 device words. */
- count = (acq->mem_addr_stop - acq->mem_addr_next + 7) / 8 * 8;
- count = MIN(count, READ_CHUNK_LEN);
-
- acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_MEM);
- acq->xfer_buf_out[1] = LWLA_WORD_0(acq->mem_addr_next);
- acq->xfer_buf_out[2] = LWLA_WORD_1(acq->mem_addr_next);
- acq->xfer_buf_out[3] = LWLA_WORD_0(count);
- acq->xfer_buf_out[4] = LWLA_WORD_1(count);
-
- acq->xfer_out->length = 5 * sizeof(uint16_t);
-
- if (submit_transfer(devc, acq->xfer_out) == SR_OK) {
- acq->mem_addr_next += count;
- devc->state = STATE_READ_REQUEST;
- }
-}
-
-/* Demangle and decompress incoming sample data from the capture buffer.
- * The data chunk is taken from the acquisition state, and is expected to
- * contain a multiple of 8 device words.
- * All data currently in the acquisition buffer will be processed. Packets
- * of decoded samples are sent off to the session bus whenever the output
- * buffer becomes full while decoding.
- */
-static int process_sample_data(const struct sr_dev_inst *sdi)
-{
- uint64_t sample;
- uint64_t high_nibbles;
- uint64_t word;
- struct dev_context *devc;
- struct acquisition_state *acq;
- uint8_t *out_p;
- uint32_t *slice;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- size_t expect_len;
- size_t actual_len;
- size_t out_max_samples;
- size_t out_run_samples;
- size_t ri;
- size_t in_words_left;
- size_t si;
-
- devc = sdi->priv;
- acq = devc->acquisition;
-
- if (acq->mem_addr_done >= acq->mem_addr_stop
- || acq->samples_done >= acq->samples_max)
- return SR_OK;
-
- in_words_left = MIN(acq->mem_addr_stop - acq->mem_addr_done,
- READ_CHUNK_LEN);
- expect_len = LWLA1034_MEMBUF_LEN(in_words_left) * sizeof(uint32_t);
- actual_len = acq->xfer_in->actual_length;
-
- if (actual_len != expect_len) {
- sr_err("Received size %zu does not match expected size %zu.",
- actual_len, expect_len);
- devc->transfer_error = TRUE;
- return SR_ERR;
- }
- acq->mem_addr_done += in_words_left;
-
- /* Prepare session packet. */
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.unitsize = UNIT_SIZE;
- logic.data = acq->out_packet;
-
- slice = acq->xfer_buf_in;
- si = 0; /* word index within slice */
-
- for (;;) {
- /* Calculate number of samples to write into packet. */
- out_max_samples = MIN(acq->samples_max - acq->samples_done,
- PACKET_LENGTH - acq->out_index);
- out_run_samples = MIN(acq->run_len, out_max_samples);
-
- /* Expand run-length samples into session packet. */
- sample = acq->sample;
- out_p = &acq->out_packet[acq->out_index * UNIT_SIZE];
-
- for (ri = 0; ri < out_run_samples; ++ri) {
- out_p[0] = sample & 0xFF;
- out_p[1] = (sample >> 8) & 0xFF;
- out_p[2] = (sample >> 16) & 0xFF;
- out_p[3] = (sample >> 24) & 0xFF;
- out_p[4] = (sample >> 32) & 0xFF;
- out_p += UNIT_SIZE;
- }
- acq->run_len -= out_run_samples;
- acq->out_index += out_run_samples;
- acq->samples_done += out_run_samples;
-
- /* Packet full or sample count limit reached? */
- if (out_run_samples == out_max_samples) {
- logic.length = acq->out_index * UNIT_SIZE;
- sr_session_send(sdi, &packet);
- acq->out_index = 0;
-
- if (acq->samples_done >= acq->samples_max)
- return SR_OK; /* sample limit reached */
- if (acq->run_len > 0)
- continue; /* need another packet */
- }
-
- if (in_words_left == 0)
- break; /* done with current chunk */
-
- /* Now work on the current slice. */
- high_nibbles = LWLA_TO_UINT32(slice[8]);
- word = LWLA_TO_UINT32(slice[si]);
- word |= (high_nibbles << (4 * si + 4)) & ((uint64_t)0xF << 32);
-
- if (acq->rle == RLE_STATE_DATA) {
- acq->sample = word & ALL_CHANNELS_MASK;
- acq->run_len = ((word >> NUM_CHANNELS) & 1) + 1;
- if (word & RLE_FLAG_LEN_FOLLOWS)
- acq->rle = RLE_STATE_LEN;
- } else {
- acq->run_len += word << 1;
- acq->rle = RLE_STATE_DATA;
- }
-
- /* Move to next word. */
- si = (si + 1) % 8;
- if (si == 0)
- slice += 9;
- --in_words_left;
- }
-
- /* Send out partially filled packet if this was the last chunk. */
- if (acq->mem_addr_done >= acq->mem_addr_stop && acq->out_index > 0) {
- logic.length = acq->out_index * UNIT_SIZE;
- sr_session_send(sdi, &packet);
- acq->out_index = 0;
- }
- return SR_OK;
-}
-
-/* Finish an acquisition session. This sends the end packet to the session
- * bus and removes the listener for asynchronous USB transfers.
- */
-static void end_acquisition(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
-
- drvc = sdi->driver->priv;
- devc = sdi->priv;
-
- if (devc->state == STATE_IDLE)
- return;
-
- devc->state = STATE_IDLE;
-
- /* Remove USB file descriptors from polling. */
- usb_source_remove(sdi->session, drvc->sr_ctx);
-
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-
- lwla_free_acquisition_state(devc->acquisition);
- devc->acquisition = NULL;
-
- sdi->status = SR_ST_ACTIVE;
-}
-
-/* USB output transfer completion callback.
- */
-static void receive_transfer_out(struct libusb_transfer *transfer)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
-
- if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
- sr_err("Transfer to device failed: %d.", transfer->status);
- devc->transfer_error = TRUE;
- return;
- }
-
- if (devc->reg_write_pos < devc->reg_write_len) {
- issue_next_write_reg(sdi);
- } else {
- switch (devc->state) {
- case STATE_START_CAPTURE:
- devc->state = STATE_STATUS_WAIT;
- break;
- case STATE_STATUS_REQUEST:
- devc->state = STATE_STATUS_RESPONSE;
- submit_transfer(devc, devc->acquisition->xfer_in);
- break;
- case STATE_STOP_CAPTURE:
- if (sdi->status == SR_ST_ACTIVE)
- request_capture_length(sdi);
- else
- end_acquisition(sdi);
- break;
- case STATE_LENGTH_REQUEST:
- devc->state = STATE_LENGTH_RESPONSE;
- submit_transfer(devc, devc->acquisition->xfer_in);
- break;
- case STATE_READ_PREPARE:
- request_read_mem(sdi);
- break;
- case STATE_READ_REQUEST:
- devc->state = STATE_READ_RESPONSE;
- submit_transfer(devc, devc->acquisition->xfer_in);
- break;
- case STATE_READ_END:
- end_acquisition(sdi);
- break;
- default:
- sr_err("Unexpected device state %d.", devc->state);
- break;
- }
- }
-}
-
-/* USB input transfer completion callback.
- */
-static void receive_transfer_in(struct libusb_transfer *transfer)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct acquisition_state *acq;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
- acq = devc->acquisition;
-
- if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
- sr_err("Transfer from device failed: %d.", transfer->status);
- devc->transfer_error = TRUE;
- return;
- }
-
- switch (devc->state) {
- case STATE_STATUS_RESPONSE:
- process_capture_status(sdi);
- break;
- case STATE_LENGTH_RESPONSE:
- process_capture_length(sdi);
- break;
- case STATE_READ_RESPONSE:
- if (process_sample_data(sdi) == SR_OK
- && acq->mem_addr_next < acq->mem_addr_stop
- && acq->samples_done < acq->samples_max)
- request_read_mem(sdi);
- else
- issue_read_end(sdi);
- break;
- default:
- sr_err("Unexpected device state %d.", devc->state);
- break;
- }
-}
-
-/* Initialize the LWLA. This downloads a bitstream into the FPGA
- * and executes a simple device test sequence.
- */
-SR_PRIV int lwla_init_device(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int ret;
- uint32_t value;
-
- devc = sdi->priv;
-
- /* Force reload of bitstream */
- devc->cur_clock_config = CONF_CLOCK_NONE;
-
- ret = lwla_set_clock_config(sdi);
-
- if (ret != SR_OK)
- return ret;
-
- ret = lwla_write_reg(sdi->conn, REG_CMD_CTRL2, 100);
- if (ret != SR_OK)
- return ret;
-
- ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL1, &value);
- if (ret != SR_OK)
- return ret;
- sr_dbg("Received test word 0x%08X back.", value);
- if (value != 0x12345678)
- return SR_ERR;
-
- ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL4, &value);
- if (ret != SR_OK)
- return ret;
- sr_dbg("Received test word 0x%08X back.", value);
- if (value != 0x12345678)
- return SR_ERR;
-
- ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL3, &value);
- if (ret != SR_OK)
- return ret;
- sr_dbg("Received test word 0x%08X back.", value);
- if (value != 0x87654321)
- return SR_ERR;
-
- return ret;
-}
-
-SR_PRIV int lwla_convert_trigger(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_trigger *trigger;
- struct sr_trigger_stage *stage;
- struct sr_trigger_match *match;
- const GSList *l, *m;
- uint64_t channel_index;
-
- devc = sdi->priv;
-
- devc->trigger_mask = 0;
- devc->trigger_values = 0;
- devc->trigger_edge_mask = 0;
-
- if (!(trigger = sr_session_trigger_get(sdi->session)))
- return SR_OK;
-
- if (g_slist_length(trigger->stages) > 1) {
- sr_err("This device only supports 1 trigger stage.");
- return SR_ERR;
- }
-
- for (l = trigger->stages; l; l = l->next) {
- stage = l->data;
- for (m = stage->matches; m; m = m->next) {
- match = m->data;
- if (!match->channel->enabled)
- /* Ignore disabled channels with a trigger. */
- continue;
- channel_index = 1 << match->channel->index;
- devc->trigger_mask |= channel_index;
- switch (match->match) {
- case SR_TRIGGER_ONE:
- devc->trigger_values |= channel_index;
- break;
- case SR_TRIGGER_RISING:
- devc->trigger_values |= channel_index;
- /* Fall through for edge mask. */
- case SR_TRIGGER_FALLING:
- devc->trigger_edge_mask |= channel_index;
- break;
- }
- }
- }
-
- return SR_OK;
-}
-
-/* Select the LWLA clock configuration. If the clock source changed from
- * the previous setting, this will download a new bitstream to the FPGA.
- */
-SR_PRIV int lwla_set_clock_config(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- int ret;
- enum clock_config choice;
-
- devc = sdi->priv;
-
- if (sdi->status == SR_ST_INACTIVE)
- choice = CONF_CLOCK_NONE;
- else if (devc->cfg_clock_source == CLOCK_INTERNAL)
- choice = CONF_CLOCK_INT;
- else if (devc->cfg_clock_edge == EDGE_POSITIVE)
- choice = CONF_CLOCK_EXT_RISE;
- else
- choice = CONF_CLOCK_EXT_FALL;
-
- if (choice != devc->cur_clock_config) {
- devc->cur_clock_config = CONF_CLOCK_NONE;
- ret = lwla_send_bitstream(sdi->conn, bitstream_map[choice]);
- if (ret == SR_OK)
- devc->cur_clock_config = choice;
- return ret;
- }
- return SR_OK;
-}
-
-/* Configure the LWLA in preparation for an acquisition session.
- */
-SR_PRIV int lwla_setup_acquisition(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- struct acquisition_state *acq;
- struct regval_pair regvals[7];
- int ret;
-
- devc = sdi->priv;
- usb = sdi->conn;
- acq = devc->acquisition;
-
- if (devc->limit_msec > 0) {
- acq->duration_max = devc->limit_msec;
- sr_info("Acquisition time limit %" PRIu64 " ms.",
- devc->limit_msec);
- } else
- acq->duration_max = MAX_LIMIT_MSEC;
-
- if (devc->limit_samples > 0) {
- acq->samples_max = devc->limit_samples;
- sr_info("Acquisition sample count limit %" PRIu64 ".",
- devc->limit_samples);
- } else
- acq->samples_max = MAX_LIMIT_SAMPLES;
-
- if (devc->cfg_clock_source == CLOCK_INTERNAL) {
- sr_info("Internal clock, samplerate %" PRIu64 ".",
- devc->samplerate);
- if (devc->samplerate == 0)
- return SR_ERR_BUG;
- /* At 125 MHz, the clock divider is bypassed. */
- acq->bypass_clockdiv = (devc->samplerate > SR_MHZ(100));
-
- /* If only one of the limits is set, derive the other one. */
- if (devc->limit_msec == 0 && devc->limit_samples > 0)
- acq->duration_max = devc->limit_samples
- * 1000 / devc->samplerate + 1;
- else if (devc->limit_samples == 0 && devc->limit_msec > 0)
- acq->samples_max = devc->limit_msec
- * devc->samplerate / 1000;
- } else {
- acq->bypass_clockdiv = TRUE;
-
- if (devc->cfg_clock_edge == EDGE_NEGATIVE)
- sr_info("External clock, falling edge.");
- else
- sr_info("External clock, rising edge.");
- }
-
- regvals[0].reg = REG_MEM_CTRL2;
- regvals[0].val = 2;
-
- regvals[1].reg = REG_MEM_CTRL2;
- regvals[1].val = 1;
-
- regvals[2].reg = REG_CMD_CTRL2;
- regvals[2].val = 10;
-
- regvals[3].reg = REG_CMD_CTRL3;
- regvals[3].val = 0x74;
-
- regvals[4].reg = REG_CMD_CTRL4;
- regvals[4].val = 0;
-
- regvals[5].reg = REG_CMD_CTRL1;
- regvals[5].val = 0;
-
- regvals[6].reg = REG_DIV_BYPASS;
- regvals[6].val = acq->bypass_clockdiv;
-
- ret = lwla_write_regs(usb, regvals, G_N_ELEMENTS(regvals));
- if (ret != SR_OK)
- return ret;
-
- return capture_setup(sdi);
-}
-
-/* Start the capture operation on the LWLA device. Beginning with this
- * function, all USB transfers will be asynchronous until the end of the
- * acquisition session.
- */
-SR_PRIV int lwla_start_acquisition(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- struct acquisition_state *acq;
- struct regval_pair *regvals;
-
- devc = sdi->priv;
- usb = sdi->conn;
- acq = devc->acquisition;
-
- acq->duration_now = 0;
- acq->mem_addr_fill = 0;
- acq->capture_flags = 0;
-
- libusb_fill_bulk_transfer(acq->xfer_out, usb->devhdl, EP_COMMAND,
- (unsigned char *)acq->xfer_buf_out, 0,
- &receive_transfer_out,
- (struct sr_dev_inst *)sdi, USB_TIMEOUT);
-
- libusb_fill_bulk_transfer(acq->xfer_in, usb->devhdl, EP_REPLY,
- (unsigned char *)acq->xfer_buf_in,
- sizeof acq->xfer_buf_in,
- &receive_transfer_in,
- (struct sr_dev_inst *)sdi, USB_TIMEOUT);
-
- regvals = devc->reg_write_seq;
-
- regvals[0].reg = REG_CMD_CTRL2;
- regvals[0].val = 10;
-
- regvals[1].reg = REG_CMD_CTRL3;
- regvals[1].val = 1;
-
- regvals[2].reg = REG_CMD_CTRL4;
- regvals[2].val = 0;
-
- regvals[3].reg = REG_CMD_CTRL1;
- regvals[3].val = 0;
-
- devc->reg_write_pos = 0;
- devc->reg_write_len = 4;
-
- devc->state = STATE_START_CAPTURE;
-
- return issue_next_write_reg(sdi);
-}
-
-/* Allocate an acquisition state object.
- */
-SR_PRIV struct acquisition_state *lwla_alloc_acquisition_state(void)
-{
- struct acquisition_state *acq;
-
- acq = g_try_new0(struct acquisition_state, 1);
- if (!acq) {
- sr_err("Acquisition state malloc failed.");
- return NULL;
- }
-
- acq->xfer_in = libusb_alloc_transfer(0);
- if (!acq->xfer_in) {
- sr_err("Transfer malloc failed.");
- g_free(acq);
- return NULL;
- }
-
- acq->xfer_out = libusb_alloc_transfer(0);
- if (!acq->xfer_out) {
- sr_err("Transfer malloc failed.");
- libusb_free_transfer(acq->xfer_in);
- g_free(acq);
- return NULL;
- }
-
- return acq;
-}
-
-/* Deallocate an acquisition state object.
- */
-SR_PRIV void lwla_free_acquisition_state(struct acquisition_state *acq)
-{
- if (acq) {
- libusb_free_transfer(acq->xfer_out);
- libusb_free_transfer(acq->xfer_in);
- g_free(acq);
- }
-}
-
-/* USB I/O source callback.
- */
-SR_PRIV int lwla_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct drv_context *drvc;
- struct timeval tv;
- int ret;
-
- (void)fd;
-
- sdi = cb_data;
- devc = sdi->priv;
- drvc = sdi->driver->priv;
-
- if (!devc || !drvc)
- return FALSE;
-
- /* No timeout: return immediately. */
- tv.tv_sec = 0;
- tv.tv_usec = 0;
-
- ret = libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx,
- &tv, NULL);
- if (ret != 0)
- sr_err("Event handling failed: %s.", libusb_error_name(ret));
-
- /* If no event flags are set the timeout must have expired. */
- if (revents == 0 && devc->state == STATE_STATUS_WAIT) {
- if (sdi->status == SR_ST_STOPPING)
- issue_stop_capture(sdi);
- else
- request_capture_status(sdi);
- }
-
- /* Check if an error occurred on a transfer. */
- if (devc->transfer_error)
- end_acquisition(sdi);
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
- *
- * 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_SYSCLK_LWLA_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_SYSCLK_LWLA_PROTOCOL_H
-
-#define LOG_PREFIX "sysclk-lwla"
-
-#include "lwla.h"
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include <stdint.h>
-#include <glib.h>
-
-/* For now, only the LWLA1034 is supported.
- */
-#define VENDOR_NAME "SysClk"
-#define MODEL_NAME "LWLA1034"
-
-#define USB_VID_PID "2961.6689"
-#define USB_INTERFACE 0
-#define USB_TIMEOUT 3000 /* ms */
-
-#define NUM_CHANNELS 34
-
-/* Bit mask covering all 34 channels.
- */
-#define ALL_CHANNELS_MASK (((uint64_t)1 << NUM_CHANNELS) - 1)
-
-/** Unit and packet size for the sigrok logic datafeed.
- */
-#define UNIT_SIZE ((NUM_CHANNELS + 7) / 8)
-#define PACKET_LENGTH 10000 /* units */
-
-/** Size of the acquisition buffer in device memory units.
- */
-#define MEMORY_DEPTH (256 * 1024) /* 256k x 36 bit */
-
-/** Number of device memory units (36 bit) to read at a time. Slices of 8
- * consecutive 36-bit words are mapped to 9 32-bit words each, so the chunk
- * length should be a multiple of 8 to ensure alignment to slice boundaries.
- *
- * Experimentation has shown that reading chunks larger than about 1024 bytes
- * is unreliable. The threshold seems to relate to the buffer size on the FX2
- * USB chip: The configured endpoint buffer size is 512, and with double or
- * triple buffering enabled a multiple of 512 bytes can be kept in fly.
- *
- * The vendor software limits reads to 120 words (15 slices, 540 bytes) at
- * a time. So far, it appears safe to increase this to 224 words (28 slices,
- * 1008 bytes), thus making the most of two 512 byte buffers.
- */
-#define READ_CHUNK_LEN (28 * 8)
-
-/** Calculate the required buffer size in 32-bit units for reading a given
- * number of device memory words. Rounded to a multiple of 8 device words.
- */
-#define LWLA1034_MEMBUF_LEN(count) (((count) + 7) / 8 * 9)
-
-/** Maximum number of 16-bit words sent at a time during acquisition.
- * Used for allocating the libusb transfer buffer.
- */
-#define MAX_ACQ_SEND_WORDS 8 /* 5 for memory read request plus stuffing */
-
-/** Maximum number of 32-bit words received at a time during acquisition.
- * Round to the next multiple of the endpoint buffer size to avoid nasty
- * transfer overflow conditions on hiccups.
- */
-#define MAX_ACQ_RECV_LEN ((READ_CHUNK_LEN / 8 * 9 + 127) / 128 * 128)
-
-/** Maximum length of a register write sequence.
- */
-#define MAX_REG_WRITE_SEQ_LEN 5
-
-/** Default configured samplerate.
- */
-#define DEFAULT_SAMPLERATE SR_MHZ(125)
-
-/** Maximum configurable sample count limit.
- */
-#define MAX_LIMIT_SAMPLES (UINT64_C(1) << 48)
-
-/** Maximum configurable capture duration in milliseconds.
- */
-#define MAX_LIMIT_MSEC (UINT64_C(1) << 32)
-
-/** LWLA1034 FPGA clock configurations.
- */
-enum clock_config {
- CONF_CLOCK_NONE,
- CONF_CLOCK_INT,
- CONF_CLOCK_EXT_RISE,
- CONF_CLOCK_EXT_FALL,
-};
-
-/** Available clock sources.
- */
-enum clock_source {
- CLOCK_INTERNAL,
- CLOCK_EXT_CLK,
-};
-
-/** Available trigger sources.
- */
-enum trigger_source {
- TRIGGER_CHANNELS = 0,
- TRIGGER_EXT_TRG,
-};
-
-/** Available edge choices for the external clock and trigger inputs.
- */
-enum signal_edge {
- EDGE_POSITIVE = 0,
- EDGE_NEGATIVE,
-};
-
-/** LWLA device states.
- */
-enum device_state {
- STATE_IDLE = 0,
-
- STATE_START_CAPTURE,
-
- STATE_STATUS_WAIT,
- STATE_STATUS_REQUEST,
- STATE_STATUS_RESPONSE,
-
- STATE_STOP_CAPTURE,
-
- STATE_LENGTH_REQUEST,
- STATE_LENGTH_RESPONSE,
-
- STATE_READ_PREPARE,
- STATE_READ_REQUEST,
- STATE_READ_RESPONSE,
- STATE_READ_END,
-};
-
-/** LWLA run-length encoding states.
- */
-enum rle_state {
- RLE_STATE_DATA,
- RLE_STATE_LEN
-};
-
-/** LWLA sample acquisition and decompression state.
- */
-struct acquisition_state {
- uint64_t sample;
- uint64_t run_len;
-
- /** Maximum number of samples to process. */
- uint64_t samples_max;
- /** Number of samples sent to the session bus. */
- uint64_t samples_done;
-
- /** Maximum duration of capture, in milliseconds. */
- uint64_t duration_max;
- /** Running capture duration since trigger event. */
- uint64_t duration_now;
-
- /** Capture memory fill level. */
- size_t mem_addr_fill;
-
- size_t mem_addr_done;
- size_t mem_addr_next;
- size_t mem_addr_stop;
-
- size_t out_index;
-
- struct libusb_transfer *xfer_in;
- struct libusb_transfer *xfer_out;
-
- unsigned int capture_flags;
-
- enum rle_state rle;
-
- /** Whether to bypass the clock divider. */
- gboolean bypass_clockdiv;
-
- /* Payload data buffers for incoming and outgoing transfers. */
- uint32_t xfer_buf_in[MAX_ACQ_RECV_LEN];
- uint16_t xfer_buf_out[MAX_ACQ_SEND_WORDS];
-
- /* Payload buffer for sigrok logic packets. */
- uint8_t out_packet[PACKET_LENGTH * UNIT_SIZE];
-};
-
-/** Private, per-device-instance driver context.
- */
-struct dev_context {
- /** The samplerate selected by the user. */
- uint64_t samplerate;
-
- /** The maximimum sampling duration, in milliseconds. */
- uint64_t limit_msec;
-
- /** The maximimum number of samples to acquire. */
- uint64_t limit_samples;
-
- /** Channels to use. */
- uint64_t channel_mask;
-
- uint64_t trigger_mask;
- uint64_t trigger_edge_mask;
- uint64_t trigger_values;
-
- struct acquisition_state *acquisition;
-
- struct regval_pair reg_write_seq[MAX_REG_WRITE_SEQ_LEN];
- int reg_write_pos;
- int reg_write_len;
-
- enum device_state state;
-
- /** The currently active clock configuration of the device. */
- enum clock_config cur_clock_config;
-
- /** Clock source configuration setting. */
- enum clock_source cfg_clock_source;
- /** Clock edge configuration setting. */
- enum signal_edge cfg_clock_edge;
-
- /** Trigger source configuration setting. */
- enum trigger_source cfg_trigger_source;
- /** Trigger slope configuration setting. */
- enum signal_edge cfg_trigger_slope;
-
- /* Indicates that stopping the acquisition is currently in progress. */
- gboolean stopping_in_progress;
-
- /* Indicates whether a transfer failed. */
- gboolean transfer_error;
-};
-
-SR_PRIV struct acquisition_state *lwla_alloc_acquisition_state(void);
-SR_PRIV void lwla_free_acquisition_state(struct acquisition_state *acq);
-
-SR_PRIV int lwla_init_device(const struct sr_dev_inst *sdi);
-SR_PRIV int lwla_convert_trigger(const struct sr_dev_inst *sdi);
-SR_PRIV int lwla_set_clock_config(const struct sr_dev_inst *sdi);
-SR_PRIV int lwla_setup_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV int lwla_start_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV int lwla_abort_acquisition(const struct sr_dev_inst *sdi);
-
-SR_PRIV int lwla_receive_data(int fd, int revents, void *cb_data);
-
-#endif /* !LIBSIGROK_HARDWARE_SYSCLK_LWLA_PROTOCOL_H */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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 <stdlib.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_ENERGYMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
-};
-
-SR_PRIV struct sr_dev_driver teleinfo_driver_info;
-static struct sr_dev_driver *di = &teleinfo_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- GSList *devices = NULL, *l;
- const char *conn = NULL, *serialcomm = NULL;
- uint8_t buf[292];
- size_t len;
- struct sr_config *src;
-
- len = sizeof(buf);
-
- 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;
- if (!serialcomm)
- serialcomm = "1200/7e1";
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
- if (serial_open(serial, SERIAL_RDONLY | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- sr_info("Probing serial port %s.", conn);
-
- drvc = di->priv;
- drvc->instances = NULL;
- serial_flush(serial);
-
- /* Let's get a bit of data and see if we can find a packet. */
- if (serial_stream_detect(serial, buf, &len, len,
- teleinfo_packet_valid, 3000, 1200) != SR_OK)
- goto scan_cleanup;
-
- sr_info("Found device on port %s.", conn);
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "EDF", "Teleinfo", NULL)))
- goto scan_cleanup;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- goto scan_cleanup;
- }
-
- devc->optarif = teleinfo_get_optarif(buf);
-
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
- sdi->priv = devc;
- sdi->driver = di;
-
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- if (devc->optarif == OPTARIF_BASE) {
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "BASE")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- } else if (devc->optarif == OPTARIF_HC) {
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HP")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HC")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- } else if (devc->optarif == OPTARIF_EJP) {
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HN")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPM")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- } else if (devc->optarif == OPTARIF_BBR) {
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPJB")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPJW")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPJR")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HCJB")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HCJW")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HCJR")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "IINST")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "PAPP")))
- goto scan_cleanup;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
-scan_cleanup:
- serial_close(serial);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".", devc->limit_samples);
- break;
- case SR_CONF_LIMIT_MSEC:
- devc->limit_msec = g_variant_get_uint64(data);
- sr_dbg("Setting time limit to %" PRIu64 "ms.", devc->limit_msec);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct sr_serial_dev_inst *serial = sdi->conn;
- struct dev_context *devc;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("sdi->priv was NULL.");
- return SR_ERR_BUG;
- }
-
- devc->session_cb_data = cb_data;
-
- /*
- * Reset the number of samples to take. If we've already collected our
- * quota, but we start a new session, and don't reset this, we'll just
- * quit without acquiring any new samples.
- */
- devc->num_samples = 0;
- devc->start_time = g_get_monotonic_time();
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Poll every 50ms, or whenever some data comes in. */
- serial_source_add(sdi->session, serial, G_IO_IN, 50,
- teleinfo_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- return std_serial_dev_acquisition_stop(sdi, cb_data,
- std_serial_dev_close, sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver teleinfo_driver_info = {
- .name = "teleinfo",
- .longname = "Teleinfo",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .config_get = NULL,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = std_serial_dev_open,
- .dev_close = std_serial_dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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 <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "protocol.h"
-
-#define STX 0x02
-#define ETX 0x03
-#define EOT 0x04
-#define LF 0x0A
-#define CR 0x0D
-
-static gboolean teleinfo_control_check(char *label, char *data, char control)
-{
- int sum = 0;
- while (*label)
- sum += *label++;
- sum += ' ';
- while (*data)
- sum += *data++;
- return ((sum & 0x3F) + ' ') == control;
-}
-
-static gint teleinfo_channel_compare(gconstpointer a, gconstpointer b)
-{
- const struct sr_channel *ch = a;
- const char *name = b;
- return strcmp(ch->name, name);
-}
-
-static struct sr_channel *teleinfo_find_channel(struct sr_dev_inst *sdi,
- const char *name)
-{
- GSList *elem = g_slist_find_custom(sdi->channels, name,
- teleinfo_channel_compare);
- return elem ? elem->data : NULL;
-}
-
-static void teleinfo_send_value(struct sr_dev_inst *sdi, const char *channel_name,
- float value, int mq, int unit)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct sr_channel *ch;
-
- devc = sdi->priv;
- ch = teleinfo_find_channel(sdi, channel_name);
-
- if (!ch || !ch->enabled)
- return;
-
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
- analog.channels = g_slist_append(analog.channels, ch);
- analog.num_samples = 1;
- analog.mq = mq;
- analog.unit = unit;
- analog.data = &value;
-
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->session_cb_data, &packet);
- g_slist_free(analog.channels);
-}
-
-static void teleinfo_handle_mesurement(struct sr_dev_inst *sdi,
- const char *label, const char *data,
- char *optarif)
-{
- struct dev_context *devc;
- int v = atoi(data);
-
- if (!sdi || !(devc = sdi->priv)) {
- if (optarif && !strcmp(label, "OPTARIF"))
- strcpy(optarif, data);
- return;
- }
-
- if (!strcmp(label, "ADCO")) {
- devc->num_samples++;
- } else if (!strcmp(label, "BASE")) {
- teleinfo_send_value(sdi, "BASE", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "HCHP")) {
- teleinfo_send_value(sdi, "HP" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "HCHC")) {
- teleinfo_send_value(sdi, "HC" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "EJPHN")) {
- teleinfo_send_value(sdi, "HN" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "EJPHPM")) {
- teleinfo_send_value(sdi, "HPM" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHPJB")) {
- teleinfo_send_value(sdi, "HPJB", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHPJW")) {
- teleinfo_send_value(sdi, "HPJW", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHPJR")) {
- teleinfo_send_value(sdi, "HPJR", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHCJB")) {
- teleinfo_send_value(sdi, "HCJB", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHCJW")) {
- teleinfo_send_value(sdi, "HCJW", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHCJR")) {
- teleinfo_send_value(sdi, "HCJR", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "IINST")) {
- teleinfo_send_value(sdi, "IINST", v, SR_MQ_CURRENT, SR_UNIT_AMPERE);
- } else if (!strcmp(label, "PAPP")) {
- teleinfo_send_value(sdi, "PAPP", v, SR_MQ_POWER, SR_UNIT_VOLT_AMPERE);
- }
-}
-
-static gboolean teleinfo_parse_group(struct sr_dev_inst *sdi,
- const uint8_t *group, char *optarif)
-{
- char label[9], data[13], control, cr;
- const char *str = (const char *)group;
- if (sscanf(str, "\x0A%8s %13s %c%c", label, data, &control, &cr) != 4
- || cr != CR)
- return FALSE;
- if (!teleinfo_control_check(label, data, control))
- return FALSE;
- teleinfo_handle_mesurement(sdi, label, data, optarif);
- return TRUE;
-}
-
-static const uint8_t *teleinfo_parse_data(struct sr_dev_inst *sdi,
- const uint8_t *buf, int len,
- char *optarif)
-{
- const uint8_t *group_start, *group_end;
-
- group_start = memchr(buf, LF, len);
- if (!group_start)
- return NULL;
-
- group_end = memchr(group_start, CR, len - (group_start - buf));
- if (!group_end)
- return NULL;
-
- teleinfo_parse_group(sdi, group_start, optarif);
- return group_end + 1;
-}
-
-SR_PRIV int teleinfo_get_optarif(const uint8_t *buf)
-{
- const uint8_t *ptr = buf;
- char optarif[5] = { 0 };
-
- while ((ptr = teleinfo_parse_data(NULL, ptr, 292-(ptr-buf), optarif)));
- if (!strcmp(optarif, "BASE"))
- return OPTARIF_BASE;
- else if (!strcmp(optarif, "HC.."))
- return OPTARIF_HC;
- else if (!strcmp(optarif, "EJP."))
- return OPTARIF_EJP;
- else if (!strncmp(optarif, "BBR", 3))
- return OPTARIF_BBR;
- return OPTARIF_NONE;
-}
-
-SR_PRIV gboolean teleinfo_packet_valid(const uint8_t *buf)
-{
- return !!teleinfo_get_optarif(buf);
-}
-
-SR_PRIV int teleinfo_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- const uint8_t *ptr, *next_ptr, *end_ptr;
- int len;
- int64_t time;
-
- (void)fd;
-
- if (!(sdi = cb_data) || !(devc = sdi->priv) || revents != G_IO_IN)
- return TRUE;
- serial = sdi->conn;
-
- /* Try to get as much data as the buffer can hold. */
- len = TELEINFO_BUF_SIZE - devc->buf_len;
- len = serial_read(serial, devc->buf + devc->buf_len, len);
- if (len < 1) {
- sr_err("Serial port read error: %d.", len);
- return FALSE;
- }
- devc->buf_len += len;
-
- /* Now look for packets in that data. */
- ptr = devc->buf;
- end_ptr = ptr + devc->buf_len;
- while ((next_ptr = teleinfo_parse_data(sdi, ptr, end_ptr - ptr, NULL)))
- ptr = next_ptr;
-
- /* If we have any data left, move it to the beginning of our buffer. */
- memmove(devc->buf, ptr, end_ptr - ptr);
- devc->buf_len -= ptr - devc->buf;
-
- /* If buffer is full and no valid packet was found, wipe buffer. */
- if (devc->buf_len >= TELEINFO_BUF_SIZE) {
- devc->buf_len = 0;
- return FALSE;
- }
-
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi, devc->session_cb_data);
- return TRUE;
- }
-
- if (devc->limit_msec) {
- time = (g_get_monotonic_time() - devc->start_time) / 1000;
- if (time > (int64_t)devc->limit_msec) {
- sr_info("Requested time limit reached.");
- sdi->driver->dev_acquisition_stop(sdi, devc->session_cb_data);
- return TRUE;
- }
- }
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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_TELEINFO_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_TELEINFO_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "teleinfo"
-
-enum optarif {
- OPTARIF_NONE,
- OPTARIF_BASE,
- OPTARIF_HC,
- OPTARIF_EJP,
- OPTARIF_BBR,
-};
-
-#define TELEINFO_BUF_SIZE 256
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Acquisition settings */
- uint64_t limit_samples; /**< The sampling limit (in number of samples). */
- uint64_t limit_msec; /**< The time limit (in milliseconds). */
- void *session_cb_data; /**< Opaque pointer passed in by the frontend. */
-
- /* Operational state */
- enum optarif optarif; /**< The device mode (which mesures are reported) */
- uint64_t num_samples; /**< The number of already received samples. */
- int64_t start_time; /**< The time at which sampling started. */
-
- /* Temporary state across callbacks */
- uint8_t buf[TELEINFO_BUF_SIZE];
- int buf_len;
-};
-
-SR_PRIV gboolean teleinfo_packet_valid(const uint8_t *buf);
-SR_PRIV int teleinfo_receive_data(int fd, int revents, void *cb_data);
-SR_PRIV int teleinfo_get_optarif(const uint8_t *buf);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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 <string.h>
-#include "protocol.h"
-
-#define SERIALCOMM "115200/8n1"
-
-SR_PRIV struct sr_dev_driver testo_driver_info;
-static struct sr_dev_driver *di = &testo_driver_info;
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
-
-static const int32_t scanopts[] = {
- SR_CONF_CONN,
-};
-
-static const int32_t devopts[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_CONTINUOUS,
-};
-
-unsigned char TESTO_x35_REQUEST[] = { 0x12, 0, 0, 0, 1, 1, 0x55, 0xd1, 0xb7 };
-struct testo_model models[] = {
- { "435", 9, TESTO_x35_REQUEST },
-};
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_config *src;
- struct sr_dev_inst *sdi;
- struct sr_usb_dev_inst *usb;
- struct libusb_device_descriptor des;
- libusb_device **devlist;
- struct libusb_device_handle *hdl;
- GSList *conn_devices, *devices, *l;
- int devcnt, ret, i;
- const char *str;
- char manufacturer[64], product[64];
-
- devices = NULL;
- drvc = di->priv;
- drvc->instances = NULL;
-
- conn_devices = NULL;
- for (l = options; l; l = l->next) {
- src = l->data;
- if (src->key != SR_CONF_CONN)
- continue;
- str = g_variant_get_string(src->data, NULL);
- conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, str);
- }
-
- libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- for (i = 0; devlist[i]; i++) {
- if (conn_devices) {
- usb = NULL;
- for (l = conn_devices; l; l = l->next) {
- usb = l->data;
- if (usb->bus == libusb_get_bus_number(devlist[i])
- && usb->address == libusb_get_device_address(devlist[i]))
- break;
- }
- if (!l)
- /* This device matched none of the ones that
- * matched the conn specification. */
- continue;
- }
-
- if ((ret = libusb_get_device_descriptor( devlist[i], &des)) != 0) {
- sr_warn("Failed to get device descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
-
- if ((ret = libusb_open(devlist[i], &hdl)) < 0)
- continue;
-
- manufacturer[0] = product[0] = '\0';
- if (des.iManufacturer && (ret = libusb_get_string_descriptor_ascii(
- hdl, des.iManufacturer, (unsigned char *) manufacturer,
- sizeof(manufacturer))) < 0) {
- sr_warn("Failed to get manufacturer string descriptor: %s.",
- libusb_error_name(ret));
- }
- if (des.iProduct && (ret = libusb_get_string_descriptor_ascii(
- hdl, des.iProduct, (unsigned char *) product,
- sizeof(product))) < 0) {
- sr_warn("Failed to get product string descriptor: %s.",
- libusb_error_name(ret));
- }
- libusb_close(hdl);
-
- if (strncmp(manufacturer, "testo", 5))
- continue;
-
- /* Hardcode the 435 for now.*/
- if (strcmp(product, "testo 435/635/735"))
- continue;
-
- devcnt = g_slist_length(drvc->instances);
- sdi = sr_dev_inst_new(devcnt, SR_ST_INACTIVE, "Testo",
- "435/635/735", NULL);
- sdi->driver = di;
- sdi->inst_type = SR_INST_USB;
- sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
- libusb_get_device_address(devlist[i]), NULL);
- devc = g_malloc(sizeof(struct dev_context));
- devc->model = &models[0];
- devc->limit_msec = 0;
- devc->limit_samples = 0;
- sdi->priv = devc;
- if (testo_probe_channels(sdi) != SR_OK)
- continue;
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- }
- libusb_free_device_list(devlist, 1);
- g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_clear(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc = di->priv;
- struct sr_usb_dev_inst *usb;
- libusb_device **devlist;
- int ret, i;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
- libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- for (i = 0; devlist[i]; i++) {
- if (libusb_get_bus_number(devlist[i]) != usb->bus
- || libusb_get_device_address(devlist[i]) != usb->address)
- continue;
- if ((ret = libusb_open(devlist[i], &usb->devhdl))) {
- sr_err("Failed to open device: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- break;
- }
- libusb_free_device_list(devlist, 1);
- if (!devlist[i]) {
- sr_err("Device not found.");
- return SR_ERR;
- }
-
- if (libusb_has_capability(LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER)) {
- if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
- if ((ret = libusb_detach_kernel_driver(usb->devhdl, 0)) < 0) {
- sr_err("Failed to detach kernel driver: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
- }
- }
-
- if ((ret = libusb_claim_interface(usb->devhdl, 0))) {
- sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
- if (!usb->devhdl)
- /* Nothing to do. */
- return SR_OK;
-
- libusb_release_interface(usb->devhdl, 0);
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- int ret;
- struct drv_context *drvc;
-
- if (!(drvc = di->priv))
- return SR_OK;
-
- ret = dev_clear();
- g_free(drvc);
- di->priv = NULL;
-
- return ret;
-}
-
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct sr_usb_dev_inst *usb;
- char str[128];
-
- (void)cg;
-
- switch (key) {
- case SR_CONF_CONN:
- if (!sdi || !sdi->conn)
- return SR_ERR_ARG;
- usb = sdi->conn;
- snprintf(str, 128, "%d.%d", usb->bus, usb->address);
- *data = g_variant_new_string(str);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- gint64 now;
- int ret;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- devc = sdi->priv;
- ret = SR_OK;
- switch (key) {
- case SR_CONF_LIMIT_MSEC:
- devc->limit_msec = g_variant_get_uint64(data);
- now = g_get_monotonic_time() / 1000;
- devc->end_time = now + devc->limit_msec;
- sr_dbg("Setting time limit to %" PRIu64 "ms.",
- devc->limit_msec);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- scanopts, ARRAY_SIZE(scanopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static void receive_data(struct sr_dev_inst *sdi, unsigned char *data, int len)
-{
- struct dev_context *devc;
- int packet_size;
- uint16_t crc;
-
- devc = sdi->priv;
-
- if (devc->reply_size + len > MAX_REPLY_SIZE) {
- /* Something went very wrong. */
- sr_dbg("Receive buffer overrun.");
- devc->reply_size = 0;
- return;
- }
-
- memcpy(devc->reply + devc->reply_size, data, len);
- devc->reply_size += len;
- /* Sixth byte contains the length of the packet. */
- if (devc->reply_size < 7)
- return;
-
- packet_size = 7 + devc->reply[6] * 7 + 2;
- if (devc->reply_size < packet_size)
- return;
-
- if (!testo_check_packet_prefix(devc->reply, devc->reply_size))
- return;
-
- crc = crc16_mcrf4xx(0xffff, devc->reply, devc->reply_size - 2);
- if (crc == RL16(&devc->reply[devc->reply_size - 2])) {
- testo_receive_packet(sdi);
- devc->num_samples++;
- } else {
- sr_dbg("Packet has invalid CRC.");
- }
-
- devc->reply_size = 0;
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
- dev_acquisition_stop(sdi, devc->cb_data);
- else
- testo_request_packet(sdi);
-
-}
-
-SR_PRIV void receive_transfer(struct libusb_transfer *transfer)
-{
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- int ret;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
- if (transfer == devc->out_transfer)
- /* Just the command acknowledgement. */
- return;
-
- if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
- /* USB device was unplugged. */
- dev_acquisition_stop(sdi, devc->cb_data);
- } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
- /* First two bytes in any transfer are FTDI status bytes. */
- if (transfer->actual_length > 2)
- receive_data(sdi, transfer->buffer + 2, transfer->actual_length - 2);
- }
- /* Anything else is either an error or a timeout, which is fine:
- * we were just going to send another transfer request anyway. */
-
- if (sdi->status == SR_ST_ACTIVE) {
- if ((ret = libusb_submit_transfer(transfer) != 0)) {
- sr_err("Unable to resubmit transfer: %s.",
- libusb_error_name(ret));
- g_free(transfer->buffer);
- libusb_free_transfer(transfer);
- dev_acquisition_stop(sdi, devc->cb_data);
- }
- } else {
- /* This was the last transfer we're going to receive, so
- * clean up now. */
- g_free(transfer->buffer);
- libusb_free_transfer(transfer);
- }
-}
-
-static int handle_events(int fd, int revents, void *cb_data)
-{
- struct dev_context *devc;
- struct drv_context *drvc = di->priv;
- struct sr_datafeed_packet packet;
- struct sr_dev_inst *sdi;
- struct timeval tv;
- gint64 now;
-
- (void)fd;
- (void)revents;
-
- sdi = cb_data;
- devc = sdi->priv;
-
- if (devc->limit_msec) {
- now = g_get_monotonic_time() / 1000;
- if (now > devc->end_time)
- dev_acquisition_stop(sdi, devc->cb_data);
- }
-
- if (sdi->status == SR_ST_STOPPING) {
- usb_source_remove(sdi->session, drvc->sr_ctx);
-
- dev_close(sdi);
-
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
- }
-
- memset(&tv, 0, sizeof(struct timeval));
- libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
- NULL);
-
- return TRUE;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- struct libusb_transfer *transfer;
- int ret;
- unsigned char *buf;
-
- drvc = di->priv;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- devc = sdi->priv;
- usb = sdi->conn;
- devc->cb_data = cb_data;
- devc->end_time = 0;
- devc->num_samples = 0;
- devc->reply_size = 0;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- usb_source_add(sdi->session, drvc->sr_ctx, 100,
- handle_events, (void *)sdi);
-
- if (testo_set_serial_params(usb) != SR_OK)
- return SR_ERR;
-
- devc->out_transfer = libusb_alloc_transfer(0);
- if (testo_request_packet(sdi) != SR_OK)
- return SR_ERR;
-
- buf = g_try_malloc(MAX_REPLY_SIZE);
- transfer = libusb_alloc_transfer(0);
- libusb_fill_bulk_transfer(transfer, usb->devhdl, EP_IN, buf,
- MAX_REPLY_SIZE, receive_transfer, (void *)sdi, 100);
- if ((ret = libusb_submit_transfer(transfer) != 0)) {
- sr_err("Unable to submit transfer: %s.", libusb_error_name(ret));
- libusb_free_transfer(transfer);
- g_free(buf);
- return SR_ERR;
- }
- devc->reply_size = 0;
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- (void)cb_data;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- sdi->status = SR_ST_STOPPING;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver testo_driver_info = {
- .name = "testo",
- .longname = "Testo",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = 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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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 <string.h>
-#include "protocol.h"
-
-SR_PRIV int testo_set_serial_params(struct sr_usb_dev_inst *usb)
-{
- int ret;
-
- if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_BAUDRATE,
- FTDI_BAUDRATE_115200, FTDI_INDEX, NULL, 0, 10)) < 0) {
- sr_err("Failed to set baudrate: %s", libusb_error_name(ret));
- return SR_ERR;
- }
-
- if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_PARAMS,
- FTDI_PARAMS_8N1, FTDI_INDEX, NULL, 0, 10)) < 0) {
- sr_err("Failed to set comm parameters: %s", libusb_error_name(ret));
- return SR_ERR;
- }
-
- if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_FLOWCTRL,
- FTDI_FLOW_NONE, FTDI_INDEX, NULL, 0, 10)) < 0) {
- sr_err("Failed to set flow control: %s", libusb_error_name(ret));
- return SR_ERR;
- }
-
- if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_MODEMCTRL,
- FTDI_MODEM_ALLHIGH, FTDI_INDEX, NULL, 0, 10)) < 0) {
- sr_err("Failed to set modem control: %s", libusb_error_name(ret));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-
-/* Due to the modular nature of the Testo hardware, you can't assume
- * which measurements the device will supply. Fetch a single result
- * set synchronously to see which measurements it has. */
-SR_PRIV int testo_probe_channels(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- struct sr_channel *ch;
- int unit, packet_len, len, i;
- unsigned char packet[MAX_REPLY_SIZE], buf[MAX_REPLY_SIZE];
- char *probe_name;
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- sr_dbg("Probing for channels.");
- if (sdi->driver->dev_open(sdi) != SR_OK)
- return SR_ERR;
- if (testo_set_serial_params(usb) != SR_OK)
- return SR_ERR;
-
- /* Flush anything buffered from a previous run. */
- do {
- libusb_bulk_transfer(usb->devhdl, EP_IN, buf, MAX_REPLY_SIZE, &len, 10);
- } while (len > 2);
-
- if (libusb_bulk_transfer(usb->devhdl, EP_OUT, devc->model->request,
- devc->model->request_size, &devc->reply_size, 10) < 0)
- return SR_ERR;
-
- packet_len = 0;
- while(TRUE) {
- if (libusb_bulk_transfer(usb->devhdl, EP_IN, buf, MAX_REPLY_SIZE,
- &len, 250) < 0)
- return SR_ERR;
- if (len == 2)
- /* FTDI cruft */
- continue;
- if (packet_len + len - 2 > MAX_REPLY_SIZE)
- return SR_ERR;
-
- memcpy(packet + packet_len, buf + 2, len - 2);
- packet_len += len - 2;
- if (packet_len < 5)
- /* Not even enough to check prefix yet. */
- continue;
-
- if (!testo_check_packet_prefix(packet, packet_len)) {
- /* Tail end of some previous data, drop it. */
- packet_len = 0;
- continue;
- }
-
- if (packet_len >= 7 + packet[6] * 7 + 2)
- /* Got a complete packet. */
- break;
- }
- sdi->driver->dev_close(sdi);
-
- if (packet[6] > MAX_CHANNELS) {
- sr_err("Device says it has %d channels!", packet[6]);
- return SR_ERR;
- }
-
- for (i = 0; i < packet[6]; i++) {
- unit = packet[7 + i * 7 + 4];
- devc->channel_units[i] = unit;
- switch (unit) {
- case 1:
- probe_name = "Temperature";
- break;
- case 3:
- probe_name = "Humidity";
- break;
- case 5:
- probe_name = "Windspeed";
- break;
- case 24:
- probe_name = "Pressure";
- break;
- default:
- sr_dbg("Unsupported measurement unit %d", unit);
- return SR_ERR;
- }
- ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, probe_name);
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
- devc->num_channels = packet[6];
- sr_dbg("Found %d channel%s.", devc->num_channels,
- devc->num_channels > 1 ? "s" : "");
-
- return SR_OK;
-}
-
-SR_PRIV int testo_request_packet(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int ret;
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- libusb_fill_bulk_transfer(devc->out_transfer, usb->devhdl, EP_OUT,
- devc->model->request, devc->model->request_size,
- receive_transfer, (void *)sdi, 100);
- if ((ret = libusb_submit_transfer(devc->out_transfer) != 0)) {
- sr_err("Failed to request packet: %s.", libusb_error_name(ret));
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
- devc->cb_data);
- return SR_ERR;
- }
- sr_dbg("Requested new packet.");
-
- return SR_OK;
-}
-
-/* Check if the packet is well-formed. This matches packets for the
- * Testo 175/177/400/650/950/435/635/735/445/645/945/946/545. */
-SR_PRIV gboolean testo_check_packet_prefix(unsigned char *buf, int len)
-{
- int i;
- unsigned char check[] = { 0x21, 0, 0, 0, 1 };
-
- if (len < 5)
- return FALSE;
-
- for (i = 0; i < 5; i++) {
- if (buf[i] != check[i]) {
- sr_dbg("Packet has invalid prefix.");
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-SR_PRIV uint16_t crc16_mcrf4xx(uint16_t crc, uint8_t *data, size_t len)
-{
- int i;
-
- if (!data || !len)
- return crc;
-
- while (len--) {
- crc ^= *data++;
- for (i = 0; i < 8; i++) {
- if (crc & 1)
- crc = (crc >> 1) ^ 0x8408;
- else
- crc = (crc >> 1);
- }
- }
-
- return crc;
-}
-
-static float binary32_le_to_float(unsigned char *buf)
-{
- GFloatIEEE754 f;
-
- f.v_float = 0;
- f.mpn.sign = (buf[3] & 0x80) ? 1 : 0;
- f.mpn.biased_exponent = (buf[3] << 1) | (buf[2] >> 7);
- f.mpn.mantissa = buf[0] | (buf[1] << 8) | ((buf[2] & 0x7f) << 16);
-
- return f.v_float;
-}
-
-SR_PRIV void testo_receive_packet(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct sr_channel *ch;
- GString *dbg;
- float value;
- int i;
- unsigned char *buf;
-
- devc = sdi->priv;
- sr_dbg("Got %d-byte packet.", devc->reply_size);
-
- if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
- dbg = g_string_sized_new(128);
- g_string_printf(dbg, "Packet:");
- for (i = 0; i < devc->reply_size; i++)
- g_string_append_printf(dbg, " %.2x", devc->reply[i]);
- sr_spew("%s", dbg->str);
- g_string_free(dbg, TRUE);
- }
-
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- analog.num_samples = 1;
- analog.mqflags = 0;
- analog.data = &value;
- /* Decode 7-byte values */
- for (i = 0; i < devc->reply[6]; i++) {
- buf = devc->reply + 7 + i * 7;
- value = binary32_le_to_float(buf);
- switch (buf[4]) {
- case 1:
- analog.mq = SR_MQ_TEMPERATURE;
- analog.unit = SR_UNIT_CELSIUS;
- break;
- case 3:
- analog.mq = SR_MQ_RELATIVE_HUMIDITY;
- analog.unit = SR_UNIT_HUMIDITY_293K;
- break;
- case 5:
- analog.mq = SR_MQ_WIND_SPEED;
- analog.unit = SR_UNIT_METER_SECOND;
- break;
- case 24:
- analog.mq = SR_MQ_PRESSURE;
- analog.unit = SR_UNIT_HECTOPASCAL;
- break;
- default:
- sr_dbg("Unsupported measurement unit %d.", buf[4]);
- return;
- }
-
- /* Match this measurement with its channel. */
- for (i = 0; i < devc->num_channels; i++) {
- if (devc->channel_units[i] == buf[4])
- break;
- }
- if (i == devc->num_channels) {
- /* Shouldn't happen. */
- sr_err("Some channel hotswapped in!");
- return;
- }
- ch = g_slist_nth_data(sdi->channels, i);
- analog.channels = g_slist_append(NULL, ch);
- sr_session_send(sdi, &packet);
- g_slist_free(analog.channels);
- }
-}
-
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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_TESTO_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_TESTO_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "testo"
-
-#define MAX_REPLY_SIZE 128
-#define MAX_CHANNELS 16
-
-/* FTDI commands */
-#define FTDI_SET_MODEMCTRL 0x01
-#define FTDI_SET_FLOWCTRL 0x02
-#define FTDI_SET_BAUDRATE 0x03
-#define FTDI_SET_PARAMS 0x04
-/* FTDI command values */
-#define FTDI_BAUDRATE_115200 0x001a
-#define FTDI_PARAMS_8N1 0x0008
-#define FTDI_FLOW_NONE 0x0008
-#define FTDI_MODEM_ALLHIGH 0x0303
-#define FTDI_INDEX 0x0000
-/* FTDI USB stuff */
-#define EP_IN 1 | LIBUSB_ENDPOINT_IN
-#define EP_OUT 2 | LIBUSB_ENDPOINT_OUT
-
-struct testo_model {
- char *name;
- int request_size;
- unsigned char *request;
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Model-specific information */
- struct testo_model *model;
-
- /* Acquisition settings */
- uint64_t limit_msec;
- uint64_t limit_samples;
- void *cb_data;
-
- /* Operational state */
- gint64 end_time;
- uint64_t num_samples;
- uint8_t channel_units[MAX_CHANNELS];
- int num_channels;
-
- /* Temporary state across callbacks */
- struct libusb_transfer *out_transfer;
- unsigned char reply[MAX_REPLY_SIZE];
- int reply_size;
-};
-
-SR_PRIV int testo_set_serial_params(struct sr_usb_dev_inst *usb);
-SR_PRIV int testo_probe_channels(struct sr_dev_inst *sdi);
-SR_PRIV void receive_transfer(struct libusb_transfer *transfer);
-SR_PRIV int testo_request_packet(const struct sr_dev_inst *sdi);
-SR_PRIV gboolean testo_check_packet_prefix(unsigned char *buf, int len);
-SR_PRIV uint16_t crc16_mcrf4xx(uint16_t crc, uint8_t *data, size_t len);
-SR_PRIV void testo_receive_packet(const struct sr_dev_inst *sdi);
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <fcntl.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-#define SERIALCOMM "9600/8e1"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
- SR_CONF_SERIALCOMM,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_SOUNDLEVELMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_CONTINUOUS,
-};
-
-SR_PRIV struct sr_dev_driver tondaj_sl_814_driver_info;
-static struct sr_dev_driver *di = &tondaj_sl_814_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_config *src;
- struct sr_channel *ch;
- GSList *devices, *l;
- const char *conn, *serialcomm;
- struct sr_serial_dev_inst *serial;
-
- drvc = di->priv;
- drvc->instances = NULL;
-
- devices = NULL;
-
- conn = serialcomm = NULL;
- for (l = options; l; l = l->next) {
- if (!(src = l->data)) {
- sr_err("Invalid option data, skipping.");
- continue;
- }
- 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;
- default:
- sr_err("Unknown option %d, skipping.", src->key);
- break;
- }
- }
- if (!conn)
- return NULL;
- if (!serialcomm)
- serialcomm = SERIALCOMM;
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Tondaj",
- "SL-814", NULL))) {
- sr_err("Failed to create device instance.");
- return NULL;
- }
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
-
- if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
- return NULL;
-
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return NULL;
-
- sdi->inst_type = SR_INST_SERIAL;
- sdi->conn = serial;
-
- sdi->priv = devc;
- sdi->driver = di;
- ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1");
- if (!ch) {
- sr_err("Failed to create channel.");
- return NULL;
- }
- sdi->channels = g_slist_append(sdi->channels, ch);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
-
- switch (id) {
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- devc = sdi->priv;
- devc->cb_data = cb_data;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Poll every 500ms, or whenever some data comes in. */
- serial = sdi->conn;
- serial_source_add(sdi->session, serial, G_IO_IN, 500,
- tondaj_sl_814_receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
- sdi->conn, LOG_PREFIX);
-}
-
-SR_PRIV struct sr_dev_driver tondaj_sl_814_driver_info = {
- .name = "tondaj-sl-814",
- .longname = "Tondaj SL-814",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .config_get = NULL,
- .config_set = config_set,
- .config_list = config_list,
- .dev_open = std_serial_dev_open,
- .dev_close = std_serial_dev_close,
- .dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = dev_acquisition_stop,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-/* States */
-enum {
- SEND_INIT,
- GET_INIT_REPLY,
- SEND_PACKET_REQUEST,
- GET_PACKET,
-};
-
-static void parse_packet(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog)
-{
- gboolean is_a, is_fast;
- uint16_t intval;
- uint8_t level = 0, level_bits;
-
- /* Byte 0 [7:7]: 0 = A, 1 = C */
- is_a = ((buf[0] & (1 << 7)) == 0);
-
- /* Byte 0 [6:6]: Unknown/unused? */
-
- /* Byte 0 [5:4]: Level (00 = 40, 01 = 60, 10 = 80, 11 = 100) */
- level_bits = (buf[0] >> 4) & 0x03;
- if (level_bits == 0)
- level = 40;
- else if (level_bits == 1)
- level = 60;
- else if (level_bits == 2)
- level = 80;
- else if (level_bits == 3)
- level = 100;
-
- /* Byte 0 [3:3]: 0 = fast, 1 = slow */
- is_fast = ((buf[0] & (1 << 3)) == 0);
-
- /* Byte 0 [2:0]: value[10..8] */
- /* Byte 1 [7:0]: value[7..0] */
- intval = (buf[0] & 0x7) << 8;
- intval |= buf[1];
-
- *floatval = (float)intval;
-
- /* The value on the display always has one digit after the comma. */
- *floatval /= 10;
-
- analog->mq = SR_MQ_SOUND_PRESSURE_LEVEL;
- analog->unit = SR_UNIT_DECIBEL_SPL;
-
- if (is_a)
- analog->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A;
- else
- analog->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C;
-
- if (is_fast)
- analog->mqflags |= SR_MQFLAG_SPL_TIME_WEIGHT_F;
- else
- analog->mqflags |= SR_MQFLAG_SPL_TIME_WEIGHT_S;
-
- /* TODO: How to handle level? */
- (void)level;
-}
-
-static void decode_packet(struct sr_dev_inst *sdi)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct dev_context *devc;
- float floatval;
-
- devc = sdi->priv;
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
-
- parse_packet(devc->buf, &floatval, &analog);
-
- /* Send a sample packet with one analog value. */
- analog.channels = sdi->channels;
- analog.num_samples = 1;
- analog.data = &floatval;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
-
- devc->num_samples++;
-}
-
-int tondaj_sl_814_receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct sr_serial_dev_inst *serial;
- uint8_t buf[3];
- int ret;
-
- (void)fd;
- (void)revents;
-
- sdi = cb_data;
- serial = sdi->conn;
- devc = sdi->priv;
-
- /* TODO: Parts of this code need to be improved later. */
-
- /* State machine. */
- if (devc->state == SEND_INIT) {
- /* On the first run, send the "init" command. */
- buf[0] = 0x10;
- buf[1] = 0x04;
- buf[2] = 0x0d;
- sr_spew("Sending init command: %02x %02x %02x.",
- buf[0], buf[1], buf[2]);
- if ((ret = serial_write(serial, buf, 3)) < 0) {
- sr_err("Error sending init command: %d.", ret);
- return FALSE;
- }
- devc->state = GET_INIT_REPLY;
- } else if (devc->state == GET_INIT_REPLY) {
- /* If we just sent the "init" command, get its reply. */
- if ((ret = serial_read(serial, buf, 2)) < 0) {
- sr_err("Error reading init reply: %d.", ret);
- return FALSE;
- }
- sr_spew("Received init reply: %02x %02x.", buf[0], buf[1]);
- /* Expected reply: 0x05 0x0d */
- if (buf[0] != 0x05 || buf[1] != 0x0d) {
- sr_err("Received incorrect init reply, retrying.");
- devc->state = SEND_INIT;
- return TRUE;
- }
- devc->state = SEND_PACKET_REQUEST;
- } else if (devc->state == SEND_PACKET_REQUEST) {
- /* Request a packet (send 0x30 ZZ 0x0d). */
- buf[0] = 0x30;
- buf[1] = 0x00; /* ZZ */
- buf[2] = 0x0d;
- sr_spew("Sending data request command: %02x %02x %02x.",
- buf[0], buf[1], buf[2]);
- if ((ret = serial_write(serial, buf, 3)) < 0) {
- sr_err("Error sending request command: %d.", ret);
- return FALSE;
- }
- devc->buflen = 0;
- devc->state = GET_PACKET;
- } else if (devc->state == GET_PACKET) {
- /* Read a packet from the device. */
- ret = serial_read(serial, devc->buf + devc->buflen,
- 4 - devc->buflen);
- if (ret < 0) {
- sr_err("Error reading packet: %d.", ret);
- return TRUE;
- }
-
- devc->buflen += ret;
-
- /* Didn't receive all 4 bytes, yet. */
- if (devc->buflen != 4)
- return TRUE;
-
- sr_spew("Received packet: %02x %02x %02x %02x.", devc->buf[0],
- devc->buf[1], devc->buf[2], devc->buf[3]);
-
- /* Expected reply: AA BB ZZ+1 0x0d */
- if (devc->buf[2] != 0x01 || devc->buf[3] != 0x0d) {
- sr_err("Received incorrect request reply, retrying.");
- devc->state = SEND_PACKET_REQUEST;
- return TRUE;
- }
-
- decode_packet(sdi);
-
- devc->state = SEND_PACKET_REQUEST;
- } else {
- sr_err("Invalid state: %d.", devc->state);
- return FALSE;
- }
-
- /* Stop acquisition if we acquired enough samples. */
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- }
-
- return TRUE;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef LIBSIGROK_HARDWARE_TONDAJ_SL_814_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_TONDAJ_SL_814_PROTOCOL_H
-
-#include <stdint.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "tondaj-sl-814"
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /** The current sampling limit (in number of samples). */
- uint64_t limit_samples;
-
- /** The current sampling limit (in ms). */
- uint64_t limit_msec;
-
- /** Opaque pointer passed in by the frontend. */
- void *cb_data;
-
- /** The current number of already received samples. */
- uint64_t num_samples;
-
- int state;
-
- uint8_t buf[4];
- uint8_t buflen;
-};
-
-SR_PRIV int tondaj_sl_814_receive_data(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-#define UNI_T_UT_D04_NEW "1a86.e008"
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_CONTINUOUS,
-};
-
-SR_PRIV struct sr_dev_driver tecpel_dmm_8061_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60a_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60e_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60g_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61b_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61c_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61d_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61e_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc820_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc830_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc840_driver_info;
-SR_PRIV struct sr_dev_driver tenma_72_7745_driver_info;
-SR_PRIV struct sr_dev_driver tenma_72_7750_driver_info;
-
-SR_PRIV struct dmm_info udmms[] = {
- {
- "Tecpel", "DMM-8061", 2400,
- FS9721_PACKET_SIZE,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_00_temp_c,
- &tecpel_dmm_8061_driver_info, receive_data_TECPEL_DMM_8061,
- },
- {
- "UNI-T", "UT60A", 2400,
- FS9721_PACKET_SIZE,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- NULL,
- &uni_t_ut60a_driver_info, receive_data_UNI_T_UT60A,
- },
- {
- "UNI-T", "UT60E", 2400,
- FS9721_PACKET_SIZE,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_00_temp_c,
- &uni_t_ut60e_driver_info, receive_data_UNI_T_UT60E,
- },
- {
- /* The baudrate is actually 19230, see "Note 1" below. */
- "UNI-T", "UT60G", 19200,
- ES519XX_11B_PACKET_SIZE,
- sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
- NULL,
- &uni_t_ut60g_driver_info, receive_data_UNI_T_UT60G,
- },
- {
- "UNI-T", "UT61B", 2400,
- FS9922_PACKET_SIZE,
- sr_fs9922_packet_valid, sr_fs9922_parse,
- NULL,
- &uni_t_ut61b_driver_info, receive_data_UNI_T_UT61B,
- },
- {
- "UNI-T", "UT61C", 2400,
- FS9922_PACKET_SIZE,
- sr_fs9922_packet_valid, sr_fs9922_parse,
- NULL,
- &uni_t_ut61c_driver_info, receive_data_UNI_T_UT61C,
- },
- {
- "UNI-T", "UT61D", 2400,
- FS9922_PACKET_SIZE,
- sr_fs9922_packet_valid, sr_fs9922_parse,
- NULL,
- &uni_t_ut61d_driver_info, receive_data_UNI_T_UT61D,
- },
- {
- /* The baudrate is actually 19230, see "Note 1" below. */
- "UNI-T", "UT61E", 19200,
- ES519XX_14B_PACKET_SIZE,
- sr_es519xx_19200_14b_packet_valid, sr_es519xx_19200_14b_parse,
- NULL,
- &uni_t_ut61e_driver_info, receive_data_UNI_T_UT61E,
- },
- {
- "Voltcraft", "VC-820", 2400,
- FS9721_PACKET_SIZE,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- NULL,
- &voltcraft_vc820_driver_info, receive_data_VOLTCRAFT_VC820,
- },
- {
- /*
- * Note: The VC830 doesn't set the 'volt' and 'diode' bits of
- * the FS9922 protocol. Instead, it only sets the user-defined
- * bit "z1" to indicate "diode mode" and "voltage".
- */
- "Voltcraft", "VC-830", 2400,
- FS9922_PACKET_SIZE,
- sr_fs9922_packet_valid, sr_fs9922_parse,
- &sr_fs9922_z1_diode,
- &voltcraft_vc830_driver_info, receive_data_VOLTCRAFT_VC830,
- },
- {
- "Voltcraft", "VC-840", 2400,
- FS9721_PACKET_SIZE,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_00_temp_c,
- &voltcraft_vc840_driver_info, receive_data_VOLTCRAFT_VC840,
- },
- {
- "Tenma", "72-7745", 2400,
- FS9721_PACKET_SIZE,
- sr_fs9721_packet_valid, sr_fs9721_parse,
- sr_fs9721_00_temp_c,
- &tenma_72_7745_driver_info, receive_data_TENMA_72_7745,
- },
- {
- /* The baudrate is actually 19230, see "Note 1" below. */
- "Tenma", "72-7750", 19200,
- ES519XX_11B_PACKET_SIZE,
- sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
- NULL,
- &tenma_72_7750_driver_info, receive_data_TENMA_72_7750,
- },
-};
-
-/*
- * Note 1: The actual baudrate of the Cyrustek ES519xx chip used in this DMM
- * is 19230. However, the WCH CH9325 chip (UART to USB/HID) used in (some
- * versions of) the UNI-T UT-D04 cable doesn't support 19230 baud. It only
- * supports 19200, and setting an unsupported baudrate will result in the
- * default of 2400 being used (which will not work with this DMM, of course).
- */
-
-static int dev_clear(int dmm)
-{
- return std_dev_clear(udmms[dmm].di, NULL);
-}
-
-static int init(struct sr_context *sr_ctx, int dmm)
-{
- sr_dbg("Selected '%s' subdriver.", udmms[dmm].di->name);
-
- return std_init(sr_ctx, udmms[dmm].di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options, int dmm)
-{
- GSList *usb_devices, *devices, *l;
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- struct drv_context *drvc;
- struct sr_usb_dev_inst *usb;
- struct sr_config *src;
- struct sr_channel *ch;
- const char *conn;
-
- drvc = udmms[dmm].di->priv;
-
- 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;
-
- devices = NULL;
- if (!(usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn))) {
- g_slist_free_full(usb_devices, g_free);
- return NULL;
- }
-
- for (l = usb_devices; l; l = l->next) {
- usb = l->data;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
-
- devc->first_run = TRUE;
-
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
- udmms[dmm].vendor, udmms[dmm].device, NULL))) {
- sr_err("sr_dev_inst_new returned NULL.");
- return NULL;
- }
- sdi->priv = devc;
- sdi->driver = udmms[dmm].di;
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
-
- sdi->inst_type = SR_INST_USB;
- sdi->conn = usb;
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- }
-
- return devices;
-}
-
-static GSList *dev_list(int dmm)
-{
- return ((struct drv_context *)(udmms[dmm].di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi, int dmm)
-{
- struct drv_context *drvc;
- struct sr_usb_dev_inst *usb;
- int ret;
-
- drvc = udmms[dmm].di->priv;
- usb = sdi->conn;
-
- if ((ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb)) == SR_OK)
- sdi->status = SR_ST_ACTIVE;
-
- return ret;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- /* TODO */
-
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(int dmm)
-{
- return dev_clear(dmm);
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- devc = sdi->priv;
-
- switch (id) {
- case SR_CONF_LIMIT_MSEC:
- if (g_variant_get_uint64(data) == 0) {
- sr_err("Time limit cannot be 0.");
- return SR_ERR;
- }
- devc->limit_msec = g_variant_get_uint64(data);
- sr_dbg("Setting time limit to %" PRIu64 "ms.",
- devc->limit_msec);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- if (g_variant_get_uint64(data) == 0) {
- sr_err("Sample limit cannot be 0.");
- return SR_ERR;
- }
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data, int dmm)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- devc->cb_data = cb_data;
-
- devc->starttime = g_get_monotonic_time();
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(sdi, LOG_PREFIX);
-
- sr_session_source_add(sdi->session, 0, 0, 10 /* poll_timeout */,
- udmms[dmm].receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct sr_datafeed_packet packet;
-
- (void)cb_data;
-
- sr_dbg("Stopping acquisition.");
-
- /* Send end packet to the session bus. */
- sr_dbg("Sending SR_DF_END.");
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
-
- sr_session_source_remove(sdi->session, 0);
-
- return SR_OK;
-}
-
-/* Driver-specific API function wrappers */
-#define HW_INIT(X) \
-static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
-#define HW_CLEANUP(X) \
-static int cleanup_##X(void) { return cleanup(X); }
-#define HW_SCAN(X) \
-static GSList *scan_##X(GSList *options) { return scan(options, X); }
-#define HW_DEV_LIST(X) \
-static GSList *dev_list_##X(void) { return dev_list(X); }
-#define HW_DEV_CLEAR(X) \
-static int dev_clear_##X(void) { return dev_clear(X); }
-#define HW_DEV_ACQUISITION_START(X) \
-static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
-void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
-#define HW_DEV_OPEN(X) \
-static int dev_open_##X(struct sr_dev_inst *sdi) { return dev_open(sdi, X); }
-
-/* Driver structs and API function wrappers */
-#define DRV(ID, ID_UPPER, NAME, LONGNAME) \
-HW_INIT(ID_UPPER) \
-HW_CLEANUP(ID_UPPER) \
-HW_SCAN(ID_UPPER) \
-HW_DEV_LIST(ID_UPPER) \
-HW_DEV_CLEAR(ID_UPPER) \
-HW_DEV_ACQUISITION_START(ID_UPPER) \
-HW_DEV_OPEN(ID_UPPER) \
-SR_PRIV struct sr_dev_driver ID##_driver_info = { \
- .name = NAME, \
- .longname = LONGNAME, \
- .api_version = 1, \
- .init = init_##ID_UPPER, \
- .cleanup = cleanup_##ID_UPPER, \
- .scan = scan_##ID_UPPER, \
- .dev_list = dev_list_##ID_UPPER, \
- .dev_clear = dev_clear_##ID_UPPER, \
- .config_get = NULL, \
- .config_set = config_set, \
- .config_list = config_list, \
- .dev_open = dev_open_##ID_UPPER, \
- .dev_close = dev_close, \
- .dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
- .dev_acquisition_stop = dev_acquisition_stop, \
- .priv = NULL, \
-};
-
-DRV(tecpel_dmm_8061, TECPEL_DMM_8061, "tecpel-dmm-8061", "Tecpel DMM-8061")
-DRV(uni_t_ut60a, UNI_T_UT60A, "uni-t-ut60a", "UNI-T UT60A")
-DRV(uni_t_ut60e, UNI_T_UT60E, "uni-t-ut60e", "UNI-T UT60E")
-DRV(uni_t_ut60g, UNI_T_UT60G, "uni-t-ut60g", "UNI-T UT60G")
-DRV(uni_t_ut61b, UNI_T_UT61B, "uni-t-ut61b", "UNI-T UT61B")
-DRV(uni_t_ut61c, UNI_T_UT61C, "uni-t-ut61c", "UNI-T UT61C")
-DRV(uni_t_ut61d, UNI_T_UT61D, "uni-t-ut61d", "UNI-T UT61D")
-DRV(uni_t_ut61e, UNI_T_UT61E, "uni-t-ut61e", "UNI-T UT61E")
-DRV(voltcraft_vc820, VOLTCRAFT_VC820, "voltcraft-vc820", "Voltcraft VC-820")
-DRV(voltcraft_vc830, VOLTCRAFT_VC830, "voltcraft-vc830", "Voltcraft VC-830")
-DRV(voltcraft_vc840, VOLTCRAFT_VC840, "voltcraft-vc840", "Voltcraft VC-840")
-DRV(tenma_72_7745, TENMA_72_7745, "tenma-72-7745", "Tenma 72-7745")
-DRV(tenma_72_7750, TENMA_72_7750, "tenma-72-7750", "Tenma 72-7750")
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-extern struct dmm_info udmms[];
-
-/*
- * Driver for various UNI-T multimeters (and rebranded ones).
- *
- * Most UNI-T DMMs can be used with two (three) different PC interface cables:
- * - The UT-D04 USB/HID cable, old version with Hoitek HE2325U chip.
- * - The UT-D04 USB/HID cable, new version with WCH CH9325 chip.
- * - The UT-D02 RS232 cable.
- *
- * This driver is meant to support all USB/HID cables, and various DMMs that
- * can be attached to a PC via these cables. Currently only the UT-D04 cable
- * (new version) is supported/tested.
- * The UT-D02 RS232 cable is handled by the 'serial-dmm' driver.
- *
- * The data for one DMM packet (e.g. 14 bytes if the respective DMM uses a
- * Fortune Semiconductor FS9922-DMM4 chip) is spread across multiple
- * 8-byte chunks.
- *
- * An 8-byte chunk looks like this:
- * - Byte 0: 0xfz, where z is the number of actual data bytes in this chunk.
- * - Bytes 1-7: z data bytes, the rest of the bytes should be ignored.
- *
- * Example:
- * f0 00 00 00 00 00 00 00 (no data bytes)
- * f2 55 77 00 00 00 00 00 (2 data bytes, 0x55 and 0x77)
- * f1 d1 00 00 00 00 00 00 (1 data byte, 0xd1)
- */
-
-static void decode_packet(struct sr_dev_inst *sdi, int dmm, const uint8_t *buf,
- void *info)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- float floatval;
- int ret;
-
- devc = sdi->priv;
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
-
- /* Parse the protocol packet. */
- ret = udmms[dmm].packet_parse(buf, &floatval, &analog, info);
- if (ret != SR_OK) {
- sr_dbg("Invalid DMM packet, ignoring.");
- return;
- }
-
- /* If this DMM needs additional handling, call the resp. function. */
- if (udmms[dmm].dmm_details)
- udmms[dmm].dmm_details(&analog, info);
-
- /* Send a sample packet with one analog value. */
- analog.channels = sdi->channels;
- analog.num_samples = 1;
- analog.data = &floatval;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
-
- /* Increase sample count. */
- devc->num_samples++;
-}
-
-static int hid_chip_init(struct sr_dev_inst *sdi, uint16_t baudrate)
-{
- int ret;
- uint8_t buf[5];
- struct sr_usb_dev_inst *usb;
-
- usb = sdi->conn;
-
- /* Detach kernel drivers which grabbed this device (if any). */
- if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
- ret = libusb_detach_kernel_driver(usb->devhdl, 0);
- if (ret < 0) {
- sr_err("Failed to detach kernel driver: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
- sr_dbg("Successfully detached kernel driver.");
- } else {
- sr_dbg("No need to detach a kernel driver.");
- }
-
- /* Claim interface 0. */
- if ((ret = libusb_claim_interface(usb->devhdl, 0)) < 0) {
- sr_err("Failed to claim interface 0: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
- sr_dbg("Successfully claimed interface 0.");
-
- /* Set data for the HID feature report (e.g. baudrate). */
- buf[0] = baudrate & 0xff; /* Baudrate, LSB */
- buf[1] = (baudrate >> 8) & 0xff; /* Baudrate, MSB */
- buf[2] = 0x00; /* Unknown/unused (?) */
- buf[3] = 0x00; /* Unknown/unused (?) */
- buf[4] = 0x03; /* Unknown, always 0x03. */
-
- /* Send HID feature report to setup the baudrate/chip. */
- sr_dbg("Sending initial HID feature report.");
- sr_spew("HID init = 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x (%d baud)",
- buf[0], buf[1], buf[2], buf[3], buf[4], baudrate);
- ret = libusb_control_transfer(
- usb->devhdl, /* libusb device handle */
- LIBUSB_REQUEST_TYPE_CLASS |
- LIBUSB_RECIPIENT_INTERFACE |
- LIBUSB_ENDPOINT_OUT,
- 9, /* bRequest: HID set_report */
- 0x300, /* wValue: HID feature, report number 0 */
- 0, /* wIndex: interface 0 */
- (unsigned char *)&buf, /* payload buffer */
- 5, /* wLength: 5 bytes payload */
- 1000 /* timeout (ms) */);
-
- if (ret < 0) {
- sr_err("HID feature report error: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- if (ret != 5) {
- /* TODO: Handle better by also sending the remaining bytes. */
- sr_err("Short packet: sent %d/5 bytes.", ret);
- return SR_ERR;
- }
-
- sr_dbg("Successfully sent initial HID feature report.");
-
- return SR_OK;
-}
-
-static void log_8byte_chunk(const uint8_t *buf)
-{
- sr_spew("8-byte chunk: %02x %02x %02x %02x %02x %02x %02x %02x "
- "(%d data bytes)", buf[0], buf[1], buf[2], buf[3],
- buf[4], buf[5], buf[6], buf[7], (buf[0] & 0x0f));
-}
-
-static void log_dmm_packet(const uint8_t *buf)
-{
- sr_dbg("DMM packet: %02x %02x %02x %02x %02x %02x %02x"
- " %02x %02x %02x %02x %02x %02x %02x",
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
- buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]);
-}
-
-static int get_and_handle_data(struct sr_dev_inst *sdi, int dmm, void *info)
-{
- struct dev_context *devc;
- uint8_t buf[CHUNK_SIZE], *pbuf;
- int i, ret, len, num_databytes_in_chunk;
- struct sr_usb_dev_inst *usb;
-
- devc = sdi->priv;
- usb = sdi->conn;
- pbuf = devc->protocol_buf;
-
- /* On the first run, we need to init the HID chip. */
- if (devc->first_run) {
- if ((ret = hid_chip_init(sdi, udmms[dmm].baudrate)) != SR_OK) {
- sr_err("HID chip init failed: %d.", ret);
- return SR_ERR;
- }
- memset(pbuf, 0x00, DMM_BUFSIZE);
- devc->first_run = FALSE;
- }
-
- memset(&buf, 0x00, CHUNK_SIZE);
-
- /* Get data from EP2 using an interrupt transfer. */
- ret = libusb_interrupt_transfer(
- usb->devhdl, /* libusb device handle */
- LIBUSB_ENDPOINT_IN | 2, /* EP2, IN */
- (unsigned char *)&buf, /* receive buffer */
- CHUNK_SIZE, /* wLength */
- &len, /* actually received byte count */
- 1000 /* timeout (ms) */);
-
- if (ret < 0) {
- sr_err("USB receive error: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- if (len != CHUNK_SIZE) {
- sr_err("Short packet: received %d/%d bytes.", len, CHUNK_SIZE);
- /* TODO: Print the bytes? */
- return SR_ERR;
- }
-
- log_8byte_chunk((const uint8_t *)&buf);
-
- /* If there are no data bytes just return (without error). */
- if (buf[0] == 0xf0)
- return SR_OK;
-
- devc->bufoffset = 0;
-
- /*
- * Append the 1-7 data bytes of this chunk to pbuf.
- *
- * Special case:
- * DMMs with Cyrustek ES51922 chip need serial settings of
- * 19230/7o1. The WCH CH9325 UART to USB/HID chip used in (some
- * versions of) the UNI-T UT-D04 cable however, will also send
- * the parity bit to the host in the 8-byte data chunks. This bit
- * is encoded in bit 7 of each of the 1-7 data bytes and must thus
- * be removed in order for the actual ES51922 protocol parser to
- * work properly.
- */
- num_databytes_in_chunk = buf[0] & 0x0f;
- for (i = 0; i < num_databytes_in_chunk; i++, devc->buflen++) {
- pbuf[devc->buflen] = buf[1 + i];
- if (udmms[dmm].packet_parse == sr_es519xx_19200_14b_parse)
- pbuf[devc->buflen] &= ~(1 << 7);
- }
-
- /* Now look for packets in that data. */
- while ((devc->buflen - devc->bufoffset) >= udmms[dmm].packet_size) {
- if (udmms[dmm].packet_valid(pbuf + devc->bufoffset)) {
- log_dmm_packet(pbuf + devc->bufoffset);
- decode_packet(sdi, dmm, pbuf + devc->bufoffset, info);
- devc->bufoffset += udmms[dmm].packet_size;
- } else {
- devc->bufoffset++;
- }
- }
-
- /* Move remaining bytes to beginning of buffer. */
- for (i = 0; i < devc->buflen - devc->bufoffset; i++)
- pbuf[i] = pbuf[devc->bufoffset + i];
- devc->buflen -= devc->bufoffset;
-
- return SR_OK;
-}
-
-static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data)
-{
- int ret;
- struct sr_dev_inst *sdi;
- struct dev_context *devc;
- int64_t time_ms;
-
- (void)fd;
- (void)revents;
-
- sdi = cb_data;
- devc = sdi->priv;
-
- if ((ret = get_and_handle_data(sdi, dmm, info)) != SR_OK)
- return FALSE;
-
- /* Abort acquisition if we acquired enough samples. */
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- }
-
- if (devc->limit_msec) {
- time_ms = (g_get_monotonic_time() - devc->starttime) / 1000;
- if (time_ms > (int64_t)devc->limit_msec) {
- sr_info("Requested time limit reached.");
- sdi->driver->dev_acquisition_stop(sdi, cb_data);
- return TRUE;
- }
- }
-
- return TRUE;
-}
-
-#define RECEIVE_DATA(ID_UPPER, DMM_DRIVER) \
-SR_PRIV int receive_data_##ID_UPPER(int fd, int revents, void *cb_data) { \
- struct DMM_DRIVER##_info info; \
- return receive_data(fd, revents, ID_UPPER, &info, cb_data); }
-
-/* Driver-specific receive_data() wrappers */
-RECEIVE_DATA(TECPEL_DMM_8061, fs9721)
-RECEIVE_DATA(UNI_T_UT60A, fs9721)
-RECEIVE_DATA(UNI_T_UT60E, fs9721)
-RECEIVE_DATA(UNI_T_UT60G, es519xx)
-RECEIVE_DATA(UNI_T_UT61B, fs9922)
-RECEIVE_DATA(UNI_T_UT61C, fs9922)
-RECEIVE_DATA(UNI_T_UT61D, fs9922)
-RECEIVE_DATA(UNI_T_UT61E, es519xx)
-RECEIVE_DATA(VOLTCRAFT_VC820, fs9721)
-RECEIVE_DATA(VOLTCRAFT_VC830, fs9922)
-RECEIVE_DATA(VOLTCRAFT_VC840, fs9721)
-RECEIVE_DATA(TENMA_72_7745, es519xx)
-RECEIVE_DATA(TENMA_72_7750, es519xx)
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef LIBSIGROK_HARDWARE_UNI_T_DMM_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_UNI_T_DMM_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include <libusb.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "uni-t-dmm"
-
-enum {
- TECPEL_DMM_8061,
- UNI_T_UT60A,
- UNI_T_UT60E,
- UNI_T_UT60G,
- UNI_T_UT61B,
- UNI_T_UT61C,
- UNI_T_UT61D,
- UNI_T_UT61E,
- VOLTCRAFT_VC820,
- VOLTCRAFT_VC830,
- VOLTCRAFT_VC840,
- TENMA_72_7745,
- TENMA_72_7750,
-};
-
-struct dmm_info {
- char *vendor;
- char *device;
- uint32_t baudrate;
- int packet_size;
- gboolean (*packet_valid)(const uint8_t *);
- int (*packet_parse)(const uint8_t *, float *,
- struct sr_datafeed_analog *, void *);
- void (*dmm_details)(struct sr_datafeed_analog *, void *);
- struct sr_dev_driver *di;
- int (*receive_data)(int, int, void *);
-};
-
-#define CHUNK_SIZE 8
-
-#define DMM_BUFSIZE 256
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /** The current sampling limit (in number of samples). */
- uint64_t limit_samples;
-
- /** The current sampling limit (in ms). */
- uint64_t limit_msec;
-
- /** Opaque pointer passed in by the frontend. */
- void *cb_data;
-
- /** The current number of already received samples. */
- uint64_t num_samples;
-
- int64_t starttime;
-
- gboolean first_run;
-
- uint8_t protocol_buf[DMM_BUFSIZE];
- uint8_t bufoffset;
- uint8_t buflen;
-};
-
-SR_PRIV int receive_data_TECPEL_DMM_8061(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60A(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60E(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60G(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61B(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61C(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61D(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61E(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC820(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC830(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC840(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TENMA_72_7745(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TENMA_72_7750(int fd, int revents, void *cb_data);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 "protocol.h"
-
-#include <string.h>
-
-static const int32_t hwcaps[] = {
- SR_CONF_THERMOMETER,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_CONTINUOUS,
- SR_CONF_DATA_SOURCE,
-};
-
-static char *channels[] = {
- "T1",
- "T2",
- "T1-T2",
-};
-
-static const char *data_sources[] = {
- "Live",
- "Memory",
-};
-
-SR_PRIV struct sr_dev_driver uni_t_ut32x_driver_info;
-static struct sr_dev_driver *di = &uni_t_ut32x_driver_info;
-
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- struct sr_config *src;
- GSList *usb_devices, *devices, *l;
- int i;
- const char *conn;
-
- drvc = di->priv;
- drvc->instances = NULL;
-
- 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;
-
- devices = NULL;
- if ((usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn))) {
- /* We have a list of sr_usb_dev_inst matching the connection
- * string. Wrap them in sr_dev_inst and we're done. */
- for (l = usb_devices; l; l = l->next) {
- if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR,
- MODEL, NULL)))
- return NULL;
- sdi->driver = di;
- sdi->inst_type = SR_INST_USB;
- sdi->conn = l->data;
- for (i = 0; i < 3; i++) {
- if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE,
- channels[i]))) {
- sr_dbg("Channel malloc failed.");
- return NULL;
- }
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
- sr_dbg("Device context malloc failed.");
- return NULL;
- }
- sdi->priv = devc;
- devc->limit_samples = 0;
- devc->data_source = DEFAULT_DATA_SOURCE;
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- }
- g_slist_free(usb_devices);
- } else
- g_slist_free_full(usb_devices, g_free);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc;
- struct sr_usb_dev_inst *usb;
- int ret;
-
- if (!(drvc = di->priv)) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
-
- if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
- return SR_ERR;
-
-/*
- * The libusbx 1.0.9 darwin backend is broken: it can report a kernel
- * driver being active, but detaching it always returns an error.
- */
-#if !defined(__APPLE__)
- if (libusb_kernel_driver_active(usb->devhdl, USB_INTERFACE) == 1) {
- if ((ret = libusb_detach_kernel_driver(usb->devhdl, USB_INTERFACE)) < 0) {
- sr_err("failed to detach kernel driver: %s",
- libusb_error_name(ret));
- return SR_ERR;
- }
- }
-#endif
-
- if ((ret = libusb_set_configuration(usb->devhdl, USB_CONFIGURATION))) {
- sr_err("Failed to set configuration: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- if ((ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE))) {
- sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- sdi->status = SR_ST_ACTIVE;
-
- return ret;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
- if (!usb->devhdl)
- /* Nothing to do. */
- return SR_OK;
-
- libusb_release_interface(usb->devhdl, USB_INTERFACE);
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- int ret;
- struct drv_context *drvc;
-
- if (!(drvc = di->priv))
- /* Can get called on an unused driver, doesn't matter. */
- return SR_OK;
-
-
- ret = std_dev_clear(di, NULL);
- g_free(drvc);
- di->priv = NULL;
-
- return ret;
-}
-
-static int config_get(int 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_LIMIT_SAMPLES:
- *data = g_variant_new_uint64(devc->limit_samples);
- break;
- case SR_CONF_DATA_SOURCE:
- if (devc->data_source == DATA_SOURCE_LIVE)
- *data = g_variant_new_string("Live");
- else
- *data = g_variant_new_string("Memory");
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- int ret;
- const char *tmp_str;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- devc = sdi->priv;
- ret = SR_OK;
- switch (key) {
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- case SR_CONF_DATA_SOURCE:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "Live"))
- devc->data_source = DATA_SOURCE_LIVE;
- else if (!strcmp(tmp_str, "Memory"))
- devc->data_source = DATA_SOURCE_MEMORY;
- else
- return SR_ERR;
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
-
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_DATA_SOURCE:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- int len, ret;
- unsigned char cmd[2];
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- drvc = di->priv;
- devc = sdi->priv;
- usb = sdi->conn;
-
- devc->cb_data = cb_data;
- devc->num_samples = 0;
- devc->packet_len = 0;
-
- /* Configure serial port parameters on USB-UART interface
- * chip inside the device (just baudrate 2400 actually). */
- cmd[0] = 0x09;
- cmd[1] = 0x60;
- ret = libusb_control_transfer(usb->devhdl, 0x21, 0x09, 0x0300, 0x00,
- cmd, 2, 5);
- if (ret != 2) {
- sr_dbg("Failed to configure CH9325: %s", libusb_error_name(ret));
- return SR_ERR;
- }
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- if (!(devc->xfer = libusb_alloc_transfer(0)))
- return SR_ERR;
-
- /* Length of payload to follow. */
- cmd[0] = 0x01;
- if (devc->data_source == DATA_SOURCE_LIVE)
- cmd[1] = CMD_GET_LIVE;
- else
- cmd[1] = CMD_GET_STORED;
-
- ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, cmd, 2, &len, 5);
- if (ret != 0 || len != 2) {
- sr_dbg("Failed to start acquisition: %s", libusb_error_name(ret));
- libusb_free_transfer(devc->xfer);
- return SR_ERR;
- }
-
- libusb_fill_bulk_transfer(devc->xfer, usb->devhdl, EP_IN, devc->buf,
- 8, uni_t_ut32x_receive_transfer, (void *)sdi, 15);
- if (libusb_submit_transfer(devc->xfer) != 0) {
- libusb_free_transfer(devc->xfer);
- return SR_ERR;
- }
-
- usb_source_add(sdi->session, drvc->sr_ctx, 10,
- uni_t_ut32x_handle_events, (void *)sdi);
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
-
- (void)cb_data;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- /* Signal USB transfer handler to clean up and stop. */
- sdi->status = SR_ST_STOPPING;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver uni_t_ut32x_driver_info = {
- .name = "uni-t-ut32x",
- .longname = "UNI-T UT32x",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 "protocol.h"
-
-#include <string.h>
-#include <math.h>
-
-extern struct sr_dev_driver uni_t_ut32x_driver_info;
-static struct sr_dev_driver *di = &uni_t_ut32x_driver_info;
-
-static float parse_temperature(unsigned char *buf)
-{
- float temp;
- int i;
- gboolean negative;
-
- negative = FALSE;
- temp = 0.0;
- for (i = 0; i < 4; i++) {
- if (buf[i] == 0x3a)
- continue;
- if (buf[i] == 0x3b) {
- if (negative) {
- sr_dbg("Double negative sign!");
- return NAN;
- } else {
- negative = TRUE;
- continue;
- }
- }
- if (buf[i] < 0x30 || buf[i] > 0x39) {
- sr_dbg("Invalid digit '%.2x'!", buf[i]);
- return NAN;
- }
- temp *= 10;
- temp += (buf[i] - 0x30);
- }
- temp /= 10;
- if (negative)
- temp = -temp;
-
- return temp;
-}
-
-static void process_packet(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- GString *spew;
- float temp;
- int i;
- gboolean is_valid;
-
- devc = sdi->priv;
- sr_dbg("Received full 19-byte packet.");
- if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
- spew = g_string_sized_new(60);
- for (i = 0; i < devc->packet_len; i++)
- g_string_append_printf(spew, "%.2x ", devc->packet[i]);
- sr_spew("%s", spew->str);
- g_string_free(spew, TRUE);
- }
-
- is_valid = TRUE;
- if (devc->packet[1] == 0x3b && devc->packet[2] == 0x3b
- && devc->packet[3] == 0x3b && devc->packet[4] == 0x3b)
- /* No measurement: missing channel, empty storage location, ... */
- is_valid = FALSE;
-
- temp = parse_temperature(devc->packet + 1);
- if (isnan(temp))
- is_valid = FALSE;
-
- if (is_valid) {
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
- analog.mq = SR_MQ_TEMPERATURE;
- analog.mqflags = 0;
- switch (devc->packet[5] - 0x30) {
- case 1:
- analog.unit = SR_UNIT_CELSIUS;
- break;
- case 2:
- analog.unit = SR_UNIT_FAHRENHEIT;
- break;
- case 3:
- analog.unit = SR_UNIT_KELVIN;
- break;
- default:
- /* We can still pass on the measurement, whatever it is. */
- sr_dbg("Unknown unit 0x%.2x.", devc->packet[5]);
- }
- switch (devc->packet[13] - 0x30) {
- case 0:
- /* Channel T1. */
- analog.channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, 0));
- break;
- case 1:
- /* Channel T2. */
- analog.channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, 1));
- break;
- case 2:
- case 3:
- /* Channel T1-T2. */
- analog.channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, 2));
- analog.mqflags |= SR_MQFLAG_RELATIVE;
- break;
- default:
- sr_err("Unknown channel 0x%.2x.", devc->packet[13]);
- is_valid = FALSE;
- }
- if (is_valid) {
- analog.num_samples = 1;
- analog.data = &temp;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
- g_slist_free(analog.channels);
- }
- }
-
- /* We count packets even if the temperature was invalid. This way
- * a sample limit on "Memory" data source still works: unused
- * memory slots come through as "----" measurements. */
- devc->num_samples++;
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
- devc->cb_data);
- }
-
-}
-
-SR_PRIV void uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer)
-{
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- int hid_payload_len, ret;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
- if (transfer->actual_length == 8) {
- /* CH9325 encodes length in low nibble of first byte, with
- * bytes 1-7 being the (padded) payload. */
- hid_payload_len = transfer->buffer[0] & 0x0f;
- memcpy(devc->packet + devc->packet_len, transfer->buffer + 1,
- hid_payload_len);
- devc->packet_len += hid_payload_len;
- if (devc->packet_len >= 2
- && devc->packet[devc->packet_len - 2] == 0x0d
- && devc->packet[devc->packet_len - 1] == 0x0a) {
- /* Got end of packet, but do we have a complete packet? */
- if (devc->packet_len == 19)
- process_packet(sdi);
- /* Either way, done with it. */
- devc->packet_len = 0;
- } else if (devc->packet_len > 19) {
- /* Guard against garbage from the device overrunning
- * our packet buffer. */
- sr_dbg("Buffer overrun!");
- devc->packet_len = 0;
- }
- }
-
- /* Get the next transfer (unless we're shutting down). */
- if (sdi->status != SR_ST_STOPPING) {
- if ((ret = libusb_submit_transfer(devc->xfer)) != 0) {
- sr_dbg("Failed to resubmit transfer: %s", libusb_error_name(ret));
- sdi->status = SR_ST_STOPPING;
- libusb_free_transfer(devc->xfer);
- }
- } else
- libusb_free_transfer(devc->xfer);
-
-}
-
-SR_PRIV int uni_t_ut32x_handle_events(int fd, int revents, void *cb_data)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_datafeed_packet packet;
- struct sr_usb_dev_inst *usb;
- struct timeval tv;
- int len, ret;
- unsigned char cmd[2];
-
- (void)fd;
- (void)revents;
-
- drvc = di->priv;
-
- if (!(sdi = cb_data))
- return TRUE;
-
- if (!(devc = sdi->priv))
- return TRUE;
-
- memset(&tv, 0, sizeof(struct timeval));
- libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
- NULL);
-
- if (sdi->status == SR_ST_STOPPING) {
- usb_source_remove(sdi->session, drvc->sr_ctx);
- packet.type = SR_DF_END;
- sr_session_send(cb_data, &packet);
-
- /* Tell the device to stop sending USB packets. */
- usb = sdi->conn;
- cmd[0] = 0x01;
- cmd[1] = CMD_STOP;
- ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, cmd, 2, &len, 5);
- if (ret != 0 || len != 2) {
- /* Warning only, doesn't matter. */
- sr_dbg("Failed to send stop command: %s", libusb_error_name(ret));
- }
-
- sdi->status = SR_ST_ACTIVE;
- return TRUE;
- }
-
- return TRUE;
-}
-
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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_UNI_T_UT32X_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_UNI_T_UT32X_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "uni-t-ut32x"
-
-#define DEFAULT_DATA_SOURCE DATA_SOURCE_LIVE
-#define USB_CONN "1a86.e008"
-#define VENDOR "UNI-T"
-#define MODEL "UT32x"
-#define USB_INTERFACE 0
-#define USB_CONFIGURATION 1
-
-#define EP_IN 0x80 | 2
-#define EP_OUT 2
-
-enum {
- DATA_SOURCE_LIVE,
- DATA_SOURCE_MEMORY,
-};
-
-enum {
- CMD_GET_LIVE = 1,
- CMD_STOP = 2,
- CMD_GET_STORED = 7,
-};
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /* Acquisition settings */
- uint64_t limit_samples;
- gboolean data_source;
-
- /* Operational state */
- uint64_t num_samples;
- unsigned char buf[8];
- struct libusb_transfer *xfer;
- void *cb_data;
-
- /* Temporary state across callbacks */
- unsigned char packet[32];
- int packet_len;
-};
-
-SR_PRIV int uni_t_ut32x_handle_events(int fd, int revents, void *cb_data);
-SR_PRIV void uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <glib.h>
-#include <libusb.h>
-#include <stdlib.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-#define VICTOR_VID 0x1244
-#define VICTOR_PID 0xd237
-#define VICTOR_VENDOR "Victor"
-#define VICTOR_INTERFACE 0
-#define VICTOR_ENDPOINT LIBUSB_ENDPOINT_IN | 1
-
-SR_PRIV struct sr_dev_driver victor_dmm_driver_info;
-static struct sr_dev_driver *di = &victor_dmm_driver_info;
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
-
-static const int32_t hwopts[] = {
- SR_CONF_CONN,
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_LIMIT_MSEC,
- SR_CONF_LIMIT_SAMPLES,
- SR_CONF_CONTINUOUS,
-};
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct drv_context *drvc;
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- struct libusb_device_descriptor des;
- libusb_device **devlist;
- GSList *devices;
- int ret, devcnt, i;
-
- (void)options;
-
- drvc = di->priv;
-
- devices = NULL;
- libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- for (i = 0; devlist[i]; i++) {
- if ((ret = libusb_get_device_descriptor(devlist[i], &des)) != 0) {
- sr_warn("Failed to get device descriptor: %s",
- libusb_error_name(ret));
- continue;
- }
-
- if (des.idVendor != VICTOR_VID || des.idProduct != VICTOR_PID)
- continue;
-
- devcnt = g_slist_length(drvc->instances);
- if (!(sdi = sr_dev_inst_new(devcnt, SR_ST_INACTIVE,
- VICTOR_VENDOR, NULL, NULL)))
- return NULL;
- sdi->driver = di;
-
- if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
- return NULL;
- sdi->priv = devc;
-
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
- return NULL;
- sdi->channels = g_slist_append(NULL, ch);
-
- if (!(sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
- libusb_get_device_address(devlist[i]), NULL)))
- return NULL;
- sdi->inst_type = SR_INST_USB;
-
- drvc->instances = g_slist_append(drvc->instances, sdi);
- devices = g_slist_append(devices, sdi);
- }
- libusb_free_device_list(devlist, 1);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc = di->priv;
- struct sr_usb_dev_inst *usb;
- libusb_device **devlist;
- int ret, i;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
-
- libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
- for (i = 0; devlist[i]; i++) {
- if (libusb_get_bus_number(devlist[i]) != usb->bus
- || libusb_get_device_address(devlist[i]) != usb->address)
- continue;
- if ((ret = libusb_open(devlist[i], &usb->devhdl))) {
- sr_err("Failed to open device: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- break;
- }
- libusb_free_device_list(devlist, 1);
- if (!devlist[i]) {
- sr_err("Device not found.");
- return SR_ERR;
- }
-
- /* The device reports as HID class, so the kernel would have
- * claimed it. */
- if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
- if ((ret = libusb_detach_kernel_driver(usb->devhdl, 0)) < 0) {
- sr_err("Failed to detach kernel driver: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
- }
-
- if ((ret = libusb_claim_interface(usb->devhdl,
- VICTOR_INTERFACE))) {
- sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
-
- if (!usb->devhdl)
- /* Nothing to do. */
- return SR_OK;
-
- libusb_release_interface(usb->devhdl, VICTOR_INTERFACE);
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- int ret;
- struct drv_context *drvc;
-
- if (!(drvc = di->priv))
- /* Can get called on an unused driver, doesn't matter. */
- return SR_OK;
-
-
- ret = std_dev_clear(di, NULL);
- g_free(drvc);
- di->priv = NULL;
-
- return ret;
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct sr_usb_dev_inst *usb;
- char str[128];
-
- (void)cg;
-
- switch (id) {
- case SR_CONF_CONN:
- if (!sdi || !sdi->conn)
- return SR_ERR_ARG;
- usb = sdi->conn;
- snprintf(str, 128, "%d.%d", usb->bus, usb->address);
- *data = g_variant_new_string(str);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- gint64 now;
- int ret;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- devc = sdi->priv;
- ret = SR_OK;
- switch (id) {
- case SR_CONF_LIMIT_MSEC:
- devc->limit_msec = g_variant_get_uint64(data);
- now = g_get_monotonic_time() / 1000;
- devc->end_time = now + devc->limit_msec;
- sr_dbg("Setting time limit to %" PRIu64 "ms.",
- devc->limit_msec);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- devc->limit_samples = g_variant_get_uint64(data);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
- break;
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
- break;
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static void receive_transfer(struct libusb_transfer *transfer)
-{
- struct dev_context *devc;
- struct sr_dev_inst *sdi;
- int ret;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
- if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
- /* USB device was unplugged. */
- dev_acquisition_stop(sdi, sdi);
- } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
- sr_dbg("Got %d-byte packet.", transfer->actual_length);
- if (transfer->actual_length == DMM_DATA_SIZE) {
- victor_dmm_receive_data(sdi, transfer->buffer);
- if (devc->limit_samples) {
- if (devc->num_samples >= devc->limit_samples)
- dev_acquisition_stop(sdi, sdi);
- }
- }
- }
- /* Anything else is either an error or a timeout, which is fine:
- * we were just going to send another transfer request anyway. */
-
- if (sdi->status == SR_ST_ACTIVE) {
- /* Send the same request again. */
- if ((ret = libusb_submit_transfer(transfer) != 0)) {
- sr_err("Unable to resubmit transfer: %s.",
- libusb_error_name(ret));
- g_free(transfer->buffer);
- libusb_free_transfer(transfer);
- dev_acquisition_stop(sdi, sdi);
- }
- } else {
- /* This was the last transfer we're going to receive, so
- * clean up now. */
- g_free(transfer->buffer);
- libusb_free_transfer(transfer);
- }
-}
-
-static int handle_events(int fd, int revents, void *cb_data)
-{
- struct dev_context *devc;
- struct drv_context *drvc = di->priv;
- struct sr_datafeed_packet packet;
- struct sr_dev_inst *sdi;
- struct timeval tv;
- gint64 now;
-
- (void)fd;
- (void)revents;
-
- sdi = cb_data;
- devc = sdi->priv;
-
- if (devc->limit_msec) {
- now = g_get_monotonic_time() / 1000;
- if (now > devc->end_time)
- dev_acquisition_stop(sdi, sdi);
- }
-
- if (sdi->status == SR_ST_STOPPING) {
- usb_source_remove(sdi->session, drvc->sr_ctx);
-
- dev_close(sdi);
-
- packet.type = SR_DF_END;
- sr_session_send(cb_data, &packet);
- }
-
- memset(&tv, 0, sizeof(struct timeval));
- libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
- NULL);
-
- return TRUE;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct drv_context *drvc = di->priv;
- struct sr_usb_dev_inst *usb;
- struct libusb_transfer *transfer;
- int ret;
- unsigned char *buf;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- devc = sdi->priv;
- usb = sdi->conn;
- devc->cb_data = cb_data;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- usb_source_add(sdi->session, drvc->sr_ctx, 100,
- handle_events, (void *)sdi);
-
- buf = g_try_malloc(DMM_DATA_SIZE);
- transfer = libusb_alloc_transfer(0);
- /* Each transfer request gets 100ms to arrive before it's restarted.
- * The device only sends 1 transfer/second no matter how many
- * times you ask, but we want to keep step with the USB events
- * handling above. */
- libusb_fill_interrupt_transfer(transfer, usb->devhdl,
- VICTOR_ENDPOINT, buf, DMM_DATA_SIZE, receive_transfer,
- cb_data, 100);
- if ((ret = libusb_submit_transfer(transfer) != 0)) {
- sr_err("Unable to submit transfer: %s.", libusb_error_name(ret));
- libusb_free_transfer(transfer);
- g_free(buf);
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- (void)cb_data;
-
- if (!di->priv) {
- sr_err("Driver was not initialized.");
- return SR_ERR;
- }
-
- if (sdi->status != SR_ST_ACTIVE) {
- sr_err("Device not active, can't stop acquisition.");
- return SR_ERR;
- }
-
- sdi->status = SR_ST_STOPPING;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver victor_dmm_driver_info = {
- .name = "victor-dmm",
- .longname = "Victor DMMs",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <glib.h>
-#include <string.h>
-#include <math.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-/* Reverse the high nibble into the low nibble */
-static uint8_t decode_digit(uint8_t in)
-{
- uint8_t out, i;
-
- out = 0;
- in >>= 4;
- for (i = 0x08; i; i >>= 1) {
- out >>= 1;
- if (in & i)
- out |= 0x08;
- }
-
- return out;
-}
-
-static void decode_buf(struct sr_dev_inst *sdi, unsigned char *data)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_analog analog;
- struct dev_context *devc;
- long factor, ivalue;
- uint8_t digits[4];
- gboolean is_duty, is_continuity, is_diode, is_ac, is_dc, is_auto;
- gboolean is_hold, is_max, is_min, is_relative, minus;
- float fvalue;
-
- devc = sdi->priv;
-
- digits[0] = decode_digit(data[12]);
- digits[1] = decode_digit(data[11]);
- digits[2] = decode_digit(data[10]);
- digits[3] = decode_digit(data[9]);
-
- if (digits[0] == 0x0f && digits[1] == 0x00 && digits[2] == 0x0a &&
- digits[3] == 0x0f)
- /* The "over limit" (OL) display comes through like this */
- ivalue = -1;
- else if (digits[0] > 9 || digits[1] > 9 || digits[2] > 9 || digits[3] > 9)
- /* An invalid digit in any position denotes no value. */
- ivalue = -2;
- else {
- ivalue = digits[0] * 1000;
- ivalue += digits[1] * 100;
- ivalue += digits[2] * 10;
- ivalue += digits[3];
- }
-
- /* Decimal point position */
- factor = 0;
- switch (data[7] >> 4) {
- case 0x00:
- factor = 0;
- break;
- case 0x02:
- factor = 1;
- break;
- case 0x04:
- factor = 2;
- break;
- case 0x08:
- factor = 3;
- break;
- default:
- sr_err("Unknown decimal point byte: 0x%.2x.", data[7]);
- break;
- }
-
- /* Minus flag */
- minus = data[2] & 0x01;
-
- /* Mode detail symbols on the right side of the digits */
- is_duty = is_continuity = is_diode = FALSE;
- switch (data[4]) {
- case 0x00:
- /* None. */
- break;
- case 0x01:
- /* Micro */
- factor += 6;
- break;
- case 0x02:
- /* Milli */
- factor += 3;
- break;
- case 0x04:
- /* Kilo */
- ivalue *= 1000;
- break;
- case 0x08:
- /* Mega */
- ivalue *= 1000000;
- break;
- case 0x10:
- /* Continuity shows up as Ohm + this bit */
- is_continuity = TRUE;
- break;
- case 0x20:
- /* Diode tester is Volt + this bit */
- is_diode = TRUE;
- break;
- case 0x40:
- is_duty = TRUE;
- break;
- case 0x80:
- /* Never seen */
- sr_dbg("Unknown mode right detail: 0x%.2x.", data[4]);
- break;
- default:
- sr_dbg("Unknown/invalid mode right detail: 0x%.2x.", data[4]);
- break;
- }
-
- /* Scale flags on the right, continued */
- is_max = is_min = FALSE;
- if (data[5] & 0x04)
- is_max = TRUE;
- if (data[5] & 0x08)
- is_min = TRUE;
- if (data[5] & 0x40)
- /* Nano */
- factor += 9;
-
- /* Mode detail symbols on the left side of the digits */
- is_auto = is_dc = is_ac = is_hold = is_relative = FALSE;
- if (data[6] & 0x04)
- is_auto = TRUE;
- if (data[6] & 0x08)
- is_dc = TRUE;
- if (data[6] & 0x10)
- is_ac = TRUE;
- if (data[6] & 0x20)
- is_relative = TRUE;
- if (data[6] & 0x40)
- is_hold = TRUE;
-
- fvalue = (float)ivalue / pow(10, factor);
- if (minus)
- fvalue = -fvalue;
-
- memset(&analog, 0, sizeof(struct sr_datafeed_analog));
-
- /* Measurement mode */
- analog.mq = -1;
- switch (data[3]) {
- case 0x00:
- if (is_duty) {
- analog.mq = SR_MQ_DUTY_CYCLE;
- analog.unit = SR_UNIT_PERCENTAGE;
- } else
- sr_dbg("Unknown measurement mode: %.2x.", data[3]);
- break;
- case 0x01:
- if (is_diode) {
- analog.mq = SR_MQ_VOLTAGE;
- analog.unit = SR_UNIT_VOLT;
- analog.mqflags |= SR_MQFLAG_DIODE;
- if (ivalue < 0)
- fvalue = NAN;
- } else {
- if (ivalue < 0)
- break;
- analog.mq = SR_MQ_VOLTAGE;
- analog.unit = SR_UNIT_VOLT;
- if (is_ac)
- analog.mqflags |= SR_MQFLAG_AC;
- if (is_dc)
- analog.mqflags |= SR_MQFLAG_DC;
- }
- break;
- case 0x02:
- analog.mq = SR_MQ_CURRENT;
- analog.unit = SR_UNIT_AMPERE;
- if (is_ac)
- analog.mqflags |= SR_MQFLAG_AC;
- if (is_dc)
- analog.mqflags |= SR_MQFLAG_DC;
- break;
- case 0x04:
- if (is_continuity) {
- analog.mq = SR_MQ_CONTINUITY;
- analog.unit = SR_UNIT_BOOLEAN;
- fvalue = ivalue < 0 ? 0.0 : 1.0;
- } else {
- analog.mq = SR_MQ_RESISTANCE;
- analog.unit = SR_UNIT_OHM;
- if (ivalue < 0)
- fvalue = INFINITY;
- }
- break;
- case 0x08:
- /* Never seen */
- sr_dbg("Unknown measurement mode: 0x%.2x.", data[3]);
- break;
- case 0x10:
- analog.mq = SR_MQ_FREQUENCY;
- analog.unit = SR_UNIT_HERTZ;
- break;
- case 0x20:
- analog.mq = SR_MQ_CAPACITANCE;
- analog.unit = SR_UNIT_FARAD;
- break;
- case 0x40:
- analog.mq = SR_MQ_TEMPERATURE;
- analog.unit = SR_UNIT_CELSIUS;
- break;
- case 0x80:
- analog.mq = SR_MQ_TEMPERATURE;
- analog.unit = SR_UNIT_FAHRENHEIT;
- break;
- default:
- sr_dbg("Unknown/invalid measurement mode: 0x%.2x.", data[3]);
- break;
- }
- if (analog.mq == -1)
- return;
-
- if (is_auto)
- analog.mqflags |= SR_MQFLAG_AUTORANGE;
- if (is_hold)
- analog.mqflags |= SR_MQFLAG_HOLD;
- if (is_max)
- analog.mqflags |= SR_MQFLAG_MAX;
- if (is_min)
- analog.mqflags |= SR_MQFLAG_MIN;
- if (is_relative)
- analog.mqflags |= SR_MQFLAG_RELATIVE;
-
- analog.channels = sdi->channels;
- analog.num_samples = 1;
- analog.data = &fvalue;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(devc->cb_data, &packet);
-
- devc->num_samples++;
-}
-
-SR_PRIV int victor_dmm_receive_data(struct sr_dev_inst *sdi, unsigned char *buf)
-{
- GString *dbg;
- int i;
- unsigned char data[DMM_DATA_SIZE];
- unsigned char obfuscation[DMM_DATA_SIZE] = "jodenxunickxia";
- unsigned char shuffle[DMM_DATA_SIZE] = {
- 6, 13, 5, 11, 2, 7, 9, 8, 3, 10, 12, 0, 4, 1
- };
-
- for (i = 0; i < DMM_DATA_SIZE && buf[i] == 0; i++);
- if (i == DMM_DATA_SIZE) {
- /* This DMM outputs all zeroes from time to time, just ignore it. */
- sr_dbg("Received all zeroes.");
- return SR_OK;
- }
-
- /* Deobfuscate and reorder data. */
- for (i = 0; i < DMM_DATA_SIZE; i++)
- data[shuffle[i]] = (buf[i] - obfuscation[i]) & 0xff;
-
- if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
- dbg = g_string_sized_new(128);
- g_string_printf(dbg, "Deobfuscated.");
- for (i = 0; i < DMM_DATA_SIZE; i++)
- g_string_append_printf(dbg, " %.2x", data[i]);
- sr_spew("%s", dbg->str);
- g_string_free(dbg, TRUE);
- }
-
- decode_buf(sdi, data);
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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_VICTOR_DMM_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_VICTOR_DMM_PROTOCOL_H
-
-#include <stdint.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "victor-dmm"
-
-#define DMM_DATA_SIZE 14
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
- /** The current sampling limit (in number of samples). */
- uint64_t limit_samples;
-
- /** The current sampling limit (in ms). */
- uint64_t limit_msec;
-
- /** Opaque pointer passed in by the frontend. */
- void *cb_data;
-
- /** The current number of already received samples. */
- uint64_t num_samples;
- gint64 end_time;
-};
-
-SR_PRIV int victor_dmm_receive_data(struct sr_dev_inst *sdi, unsigned char *buf);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010 Sven Peter <sven@fail0verflow.com>
- * Copyright (C) 2010 Haxx Enterprises <bushing@gmail.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <assert.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "analyzer.h"
-#include "gl_usb.h"
-#include "protocol.h"
-
-enum {
- HARD_DATA_CHECK_SUM = 0x00,
- PASS_WORD,
-
- DEV_ID0 = 0x10,
- DEV_ID1,
-
- START_STATUS = 0x20,
- DEV_STATUS = 0x21,
- FREQUENCY_REG0 = 0x30,
- FREQUENCY_REG1,
- FREQUENCY_REG2,
- FREQUENCY_REG3,
- FREQUENCY_REG4,
- MEMORY_LENGTH,
- CLOCK_SOURCE,
-
- TRIGGER_STATUS0 = 0x40,
- TRIGGER_STATUS1,
- TRIGGER_STATUS2,
- TRIGGER_STATUS3,
- TRIGGER_STATUS4,
- TRIGGER_STATUS5,
- TRIGGER_STATUS6,
- TRIGGER_STATUS7,
- TRIGGER_STATUS8,
-
- TRIGGER_COUNT0 = 0x50,
- TRIGGER_COUNT1,
-
- TRIGGER_LEVEL0 = 0x55,
- TRIGGER_LEVEL1,
- TRIGGER_LEVEL2,
- TRIGGER_LEVEL3,
-
- RAMSIZE_TRIGGERBAR_ADDRESS0 = 0x60,
- RAMSIZE_TRIGGERBAR_ADDRESS1,
- RAMSIZE_TRIGGERBAR_ADDRESS2,
- TRIGGERBAR_ADDRESS0,
- TRIGGERBAR_ADDRESS1,
- TRIGGERBAR_ADDRESS2,
- DONT_CARE_TRIGGERBAR,
-
- FILTER_ENABLE = 0x70,
- FILTER_STATUS,
-
- ENABLE_DELAY_TIME0 = 0x7a,
- ENABLE_DELAY_TIME1,
-
- ENABLE_INSERT_DATA0 = 0x80,
- ENABLE_INSERT_DATA1,
- ENABLE_INSERT_DATA2,
- ENABLE_INSERT_DATA3,
- COMPRESSION_TYPE0,
- COMPRESSION_TYPE1,
-
- TRIGGER_ADDRESS0 = 0x90,
- TRIGGER_ADDRESS1,
- TRIGGER_ADDRESS2,
-
- NOW_ADDRESS0 = 0x96,
- NOW_ADDRESS1,
- NOW_ADDRESS2,
-
- STOP_ADDRESS0 = 0x9b,
- STOP_ADDRESS1,
- STOP_ADDRESS2,
-
- READ_RAM_STATUS = 0xa0,
-};
-
-static int g_trigger_status[9] = { 0 };
-static int g_trigger_count = 1;
-static int g_filter_status[8] = { 0 };
-static int g_filter_enable = 0;
-
-static int g_freq_value = 1;
-static int g_freq_scale = FREQ_SCALE_MHZ;
-static int g_memory_size = MEMORY_SIZE_8K;
-static int g_ramsize_triggerbar_addr = 2 * 1024;
-static int g_triggerbar_addr = 0;
-static int g_compression = COMPRESSION_NONE;
-static int g_thresh = 0x31; /* 1.5V */
-
-/* Maybe unk specifies an "endpoint" or "register" of sorts. */
-static int analyzer_write_status(libusb_device_handle *devh, unsigned char unk,
- unsigned char flags)
-{
- assert(unk <= 3);
- return gl_reg_write(devh, START_STATUS, unk << 6 | flags);
-}
-
-#if 0
-static int __analyzer_set_freq(libusb_device_handle *devh, int freq, int scale)
-{
- int reg0 = 0, divisor = 0, reg2 = 0;
-
- switch (scale) {
- case FREQ_SCALE_MHZ: /* MHz */
- if (freq >= 100 && freq <= 200) {
- reg0 = freq * 0.1;
- divisor = 1;
- reg2 = 0;
- break;
- }
- if (freq >= 50 && freq < 100) {
- reg0 = freq * 0.2;
- divisor = 2;
- reg2 = 0;
- break;
- }
- if (freq >= 10 && freq < 50) {
- if (freq == 25) {
- reg0 = 25;
- divisor = 5;
- reg2 = 1;
- break;
- } else {
- reg0 = freq * 0.5;
- divisor = 5;
- reg2 = 1;
- break;
- }
- }
- if (freq >= 2 && freq < 10) {
- divisor = 5;
- reg0 = freq * 2;
- reg2 = 2;
- break;
- }
- if (freq == 1) {
- divisor = 5;
- reg2 = 16;
- reg0 = 5;
- break;
- }
- divisor = 5;
- reg0 = 5;
- reg2 = 64;
- break;
- case FREQ_SCALE_HZ: /* Hz */
- if (freq >= 500 && freq < 1000) {
- reg0 = freq * 0.01;
- divisor = 10;
- reg2 = 64;
- break;
- }
- if (freq >= 300 && freq < 500) {
- reg0 = freq * 0.005 * 8;
- divisor = 5;
- reg2 = 67;
- break;
- }
- if (freq >= 100 && freq < 300) {
- reg0 = freq * 0.005 * 16;
- divisor = 5;
- reg2 = 68;
- break;
- }
- divisor = 5;
- reg0 = 5;
- reg2 = 64;
- break;
- case FREQ_SCALE_KHZ: /* kHz */
- if (freq >= 500 && freq < 1000) {
- reg0 = freq * 0.01;
- divisor = 5;
- reg2 = 17;
- break;
- }
- if (freq >= 100 && freq < 500) {
- reg0 = freq * 0.05;
- divisor = 5;
- reg2 = 32;
- break;
- }
- if (freq >= 50 && freq < 100) {
- reg0 = freq * 0.1;
- divisor = 5;
- reg2 = 33;
- break;
- }
- if (freq >= 10 && freq < 50) {
- if (freq == 25) {
- reg0 = 25;
- divisor = 5;
- reg2 = 49;
- break;
- }
- reg0 = freq * 0.5;
- divisor = 5;
- reg2 = 48;
- break;
- }
- if (freq >= 2 && freq < 10) {
- divisor = 5;
- reg0 = freq * 2;
- reg2 = 50;
- break;
- }
- divisor = 5;
- reg0 = 5;
- reg2 = 64;
- break;
- default:
- divisor = 5;
- reg0 = 5;
- reg2 = 64;
- break;
- }
-
- sr_dbg("Setting samplerate regs (freq=%d, scale=%d): "
- "reg0: %d, reg1: %d, reg2: %d, reg3: %d.",
- freq, scale, divisor, reg0, 0x02, reg2);
-
- if (gl_reg_write(devh, FREQUENCY_REG0, divisor) < 0)
- return -1; /* Divisor maybe? */
-
- if (gl_reg_write(devh, FREQUENCY_REG1, reg0) < 0)
- return -1; /* 10 / 0.2 */
-
- if (gl_reg_write(devh, FREQUENCY_REG2, 0x02) < 0)
- return -1; /* Always 2 */
-
- if (gl_reg_write(devh, FREQUENCY_REG4, reg2) < 0)
- return -1;
-
- return 0;
-}
-#endif
-
-/*
- * It seems that ...
- * FREQUENCT_REG0 - division factor (?)
- * FREQUENCT_REG1 - multiplication factor (?)
- * FREQUENCT_REG4 - clock selection (?)
- *
- * clock selection
- * 0 10MHz 16 1MHz 32 100kHz 48 10kHz 64 1kHz
- * 1 5MHz 17 500kHz 33 50kHz 49 5kHz 65 500Hz
- * 2 2.5MHz . . 50 2.5kHz 66 250Hz
- * . . . . 67 125Hz
- * . . . . 68 62.5Hz
- */
-static int __analyzer_set_freq(libusb_device_handle *devh, int freq, int scale)
-{
- struct freq_factor {
- int freq;
- int scale;
- int sel;
- int div;
- int mul;
- };
-
- static const struct freq_factor f[] = {
- { 200, FREQ_SCALE_MHZ, 0, 1, 20 },
- { 150, FREQ_SCALE_MHZ, 0, 1, 15 },
- { 100, FREQ_SCALE_MHZ, 0, 1, 10 },
- { 80, FREQ_SCALE_MHZ, 0, 2, 16 },
- { 50, FREQ_SCALE_MHZ, 0, 2, 10 },
- { 25, FREQ_SCALE_MHZ, 1, 5, 25 },
- { 10, FREQ_SCALE_MHZ, 1, 5, 10 },
- { 1, FREQ_SCALE_MHZ, 16, 5, 5 },
- { 800, FREQ_SCALE_KHZ, 17, 5, 8 },
- { 400, FREQ_SCALE_KHZ, 32, 5, 20 },
- { 200, FREQ_SCALE_KHZ, 32, 5, 10 },
- { 100, FREQ_SCALE_KHZ, 32, 5, 5 },
- { 50, FREQ_SCALE_KHZ, 33, 5, 5 },
- { 25, FREQ_SCALE_KHZ, 49, 5, 25 },
- { 5, FREQ_SCALE_KHZ, 50, 5, 10 },
- { 1, FREQ_SCALE_KHZ, 64, 5, 5 },
- { 500, FREQ_SCALE_HZ, 64, 10, 5 },
- { 100, FREQ_SCALE_HZ, 68, 5, 8 },
- { 0, 0, 0, 0, 0 }
- };
-
- int i;
-
- for (i = 0; f[i].freq; i++) {
- if (scale == f[i].scale && freq == f[i].freq)
- break;
- }
- if (!f[i].freq)
- return -1;
-
- sr_dbg("Setting samplerate regs (freq=%d, scale=%d): "
- "reg0: %d, reg1: %d, reg2: %d, reg3: %d.",
- freq, scale, f[i].div, f[i].mul, 0x02, f[i].sel);
-
- if (gl_reg_write(devh, FREQUENCY_REG0, f[i].div) < 0)
- return -1;
-
- if (gl_reg_write(devh, FREQUENCY_REG1, f[i].mul) < 0)
- return -1;
-
- if (gl_reg_write(devh, FREQUENCY_REG2, 0x02) < 0)
- return -1;
-
- if (gl_reg_write(devh, FREQUENCY_REG4, f[i].sel) < 0)
- return -1;
-
- return 0;
-}
-
-static void __analyzer_set_ramsize_trigger_address(libusb_device_handle *devh,
- unsigned int address)
-{
- gl_reg_write(devh, RAMSIZE_TRIGGERBAR_ADDRESS0, (address >> 0) & 0xFF);
- gl_reg_write(devh, RAMSIZE_TRIGGERBAR_ADDRESS1, (address >> 8) & 0xFF);
- gl_reg_write(devh, RAMSIZE_TRIGGERBAR_ADDRESS2, (address >> 16) & 0xFF);
-}
-
-static void __analyzer_set_triggerbar_address(libusb_device_handle *devh,
- unsigned int address)
-{
- gl_reg_write(devh, TRIGGERBAR_ADDRESS0, (address >> 0) & 0xFF);
- gl_reg_write(devh, TRIGGERBAR_ADDRESS1, (address >> 8) & 0xFF);
- gl_reg_write(devh, TRIGGERBAR_ADDRESS2, (address >> 16) & 0xFF);
-}
-
-static void __analyzer_set_compression(libusb_device_handle *devh,
- unsigned int type)
-{
- gl_reg_write(devh, COMPRESSION_TYPE0, (type >> 0) & 0xFF);
- gl_reg_write(devh, COMPRESSION_TYPE1, (type >> 8) & 0xFF);
-}
-
-static void __analyzer_set_trigger_count(libusb_device_handle *devh,
- unsigned int count)
-{
- gl_reg_write(devh, TRIGGER_COUNT0, (count >> 0) & 0xFF);
- gl_reg_write(devh, TRIGGER_COUNT1, (count >> 8) & 0xFF);
-}
-
-static void analyzer_write_enable_insert_data(libusb_device_handle *devh)
-{
- gl_reg_write(devh, ENABLE_INSERT_DATA0, 0x12);
- gl_reg_write(devh, ENABLE_INSERT_DATA1, 0x34);
- gl_reg_write(devh, ENABLE_INSERT_DATA2, 0x56);
- gl_reg_write(devh, ENABLE_INSERT_DATA3, 0x78);
-}
-
-static void analyzer_set_filter(libusb_device_handle *devh)
-{
- int i;
- gl_reg_write(devh, FILTER_ENABLE, g_filter_enable);
- for (i = 0; i < 8; i++)
- gl_reg_write(devh, FILTER_STATUS + i, g_filter_status[i]);
-}
-
-SR_PRIV void analyzer_reset(libusb_device_handle *devh)
-{
- analyzer_write_status(devh, 3, STATUS_FLAG_NONE); // reset device
- analyzer_write_status(devh, 3, STATUS_FLAG_RESET); // reset device
-}
-
-SR_PRIV void analyzer_initialize(libusb_device_handle *devh)
-{
- analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
- analyzer_write_status(devh, 1, STATUS_FLAG_INIT);
- analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
-}
-
-SR_PRIV void analyzer_wait(libusb_device_handle *devh, int set, int unset)
-{
- int status;
-
- while (1) {
- status = gl_reg_read(devh, DEV_STATUS);
- if ((!set || (status & set)) && ((status & unset) == 0))
- return;
- }
-}
-
-SR_PRIV void analyzer_read_start(libusb_device_handle *devh)
-{
- analyzer_write_status(devh, 3, STATUS_FLAG_20 | STATUS_FLAG_READ);
-
- /* Prep for bulk reads */
- gl_reg_read_buf(devh, READ_RAM_STATUS, NULL, 0);
-}
-
-SR_PRIV int analyzer_read_data(libusb_device_handle *devh, void *buffer,
- unsigned int size)
-{
- return gl_read_bulk(devh, buffer, size);
-}
-
-SR_PRIV void analyzer_read_stop(libusb_device_handle *devh)
-{
- analyzer_write_status(devh, 3, STATUS_FLAG_20);
- analyzer_write_status(devh, 3, STATUS_FLAG_NONE);
-}
-
-SR_PRIV void analyzer_start(libusb_device_handle *devh)
-{
- analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
- analyzer_write_status(devh, 1, STATUS_FLAG_INIT);
- analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
- analyzer_write_status(devh, 1, STATUS_FLAG_GO);
-}
-
-SR_PRIV void analyzer_configure(libusb_device_handle *devh)
-{
- int i;
-
- /* Write_Start_Status */
- analyzer_write_status(devh, 1, STATUS_FLAG_RESET);
- analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
-
- /* Start_Config_Outside_Device ? */
- analyzer_write_status(devh, 1, STATUS_FLAG_INIT);
- analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
-
- /* SetData_To_Frequence_Reg */
- __analyzer_set_freq(devh, g_freq_value, g_freq_scale);
-
- /* SetMemory_Length */
- gl_reg_write(devh, MEMORY_LENGTH, g_memory_size);
-
- /* Sele_Inside_Outside_Clock */
- gl_reg_write(devh, CLOCK_SOURCE, 0x03);
-
- /* Set_Trigger_Status */
- for (i = 0; i < 9; i++)
- gl_reg_write(devh, TRIGGER_STATUS0 + i, g_trigger_status[i]);
-
- __analyzer_set_trigger_count(devh, g_trigger_count);
-
- /* Set_Trigger_Level */
- gl_reg_write(devh, TRIGGER_LEVEL0, g_thresh);
- gl_reg_write(devh, TRIGGER_LEVEL1, g_thresh);
- gl_reg_write(devh, TRIGGER_LEVEL2, g_thresh);
- gl_reg_write(devh, TRIGGER_LEVEL3, g_thresh);
-
- /* Size of actual memory >> 2 */
- __analyzer_set_ramsize_trigger_address(devh, g_ramsize_triggerbar_addr);
- __analyzer_set_triggerbar_address(devh, g_triggerbar_addr);
-
- /* Set_Dont_Care_TriggerBar */
- gl_reg_write(devh, DONT_CARE_TRIGGERBAR, 0x01);
-
- /* Enable_Status */
- analyzer_set_filter(devh);
-
- /* Set_Enable_Delay_Time */
- gl_reg_write(devh, 0x7a, 0x00);
- gl_reg_write(devh, 0x7b, 0x00);
- analyzer_write_enable_insert_data(devh);
- __analyzer_set_compression(devh, g_compression);
-}
-
-SR_PRIV int analyzer_add_triggers(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_trigger *trigger;
- struct sr_trigger_stage *stage;
- struct sr_trigger_match *match;
- GSList *l, *m;
- int channel;
-
- devc = sdi->priv;
-
- if (!(trigger = sr_session_trigger_get(sdi->session)))
- return SR_OK;
-
- for (l = trigger->stages; l; l = l->next) {
- stage = l->data;
- for (m = stage->matches; m; m = m->next) {
- match = m->data;
- devc->trigger = 1;
- if (!match->channel->enabled)
- /* Ignore disabled channels with a trigger. */
- continue;
- channel = match->channel->index;
- switch (match->match) {
- case SR_TRIGGER_ZERO:
- g_trigger_status[channel / 4] |= 2 << (channel % 4 * 2);
- case SR_TRIGGER_ONE:
- g_trigger_status[channel / 4] |= 1 << (channel % 4 * 2);
- break;
- default:
- sr_err("Unsupported match %d", match->match);
- return SR_ERR;
- }
- }
- }
-
- return SR_OK;
-}
-
-SR_PRIV void analyzer_add_filter(int channel, int type)
-{
- int i;
-
- if (type != FILTER_HIGH && type != FILTER_LOW)
- return;
- if ((channel & 0xf) >= 8)
- return;
-
- if (channel & CHANNEL_A)
- i = 0;
- else if (channel & CHANNEL_B)
- i = 2;
- else if (channel & CHANNEL_C)
- i = 4;
- else if (channel & CHANNEL_D)
- i = 6;
- else
- return;
-
- if ((channel & 0xf) >= 4) {
- i++;
- channel -= 4;
- }
-
- g_filter_status[i] |=
- 1 << ((2 * channel) + (type == FILTER_LOW ? 1 : 0));
-
- g_filter_enable = 1;
-}
-
-SR_PRIV void analyzer_set_trigger_count(int count)
-{
- g_trigger_count = count;
-}
-
-SR_PRIV void analyzer_set_freq(int freq, int scale)
-{
- g_freq_value = freq;
- g_freq_scale = scale;
-}
-
-SR_PRIV void analyzer_set_memory_size(unsigned int size)
-{
- g_memory_size = size;
-}
-
-SR_PRIV void analyzer_set_ramsize_trigger_address(unsigned int address)
-{
- g_ramsize_triggerbar_addr = address;
-}
-
-SR_PRIV unsigned int analyzer_get_ramsize_trigger_address(void)
-{
- return g_ramsize_triggerbar_addr;
-}
-
-SR_PRIV void analyzer_set_triggerbar_address(unsigned int address)
-{
- g_triggerbar_addr = address;
-}
-
-SR_PRIV unsigned int analyzer_get_triggerbar_address(void)
-{
- return g_triggerbar_addr;
-}
-
-SR_PRIV unsigned int analyzer_read_status(libusb_device_handle *devh)
-{
- return gl_reg_read(devh, DEV_STATUS);
-}
-
-SR_PRIV unsigned int analyzer_read_id(libusb_device_handle *devh)
-{
- return gl_reg_read(devh, DEV_ID1) << 8 | gl_reg_read(devh, DEV_ID0);
-}
-
-SR_PRIV unsigned int analyzer_get_stop_address(libusb_device_handle *devh)
-{
- return gl_reg_read(devh, STOP_ADDRESS2) << 16 | gl_reg_read(devh,
- STOP_ADDRESS1) << 8 | gl_reg_read(devh, STOP_ADDRESS0);
-}
-
-SR_PRIV unsigned int analyzer_get_now_address(libusb_device_handle *devh)
-{
- return gl_reg_read(devh, NOW_ADDRESS2) << 16 | gl_reg_read(devh,
- NOW_ADDRESS1) << 8 | gl_reg_read(devh, NOW_ADDRESS0);
-}
-
-SR_PRIV unsigned int analyzer_get_trigger_address(libusb_device_handle *devh)
-{
- return gl_reg_read(devh, TRIGGER_ADDRESS2) << 16 | gl_reg_read(devh,
- TRIGGER_ADDRESS1) << 8 | gl_reg_read(devh, TRIGGER_ADDRESS0);
-}
-
-SR_PRIV void analyzer_set_compression(unsigned int type)
-{
- g_compression = type;
-}
-
-SR_PRIV void analyzer_set_voltage_threshold(int thresh)
-{
- g_thresh = thresh;
-}
-
-SR_PRIV void analyzer_wait_button(libusb_device_handle *devh)
-{
- analyzer_wait(devh, STATUS_BUTTON_PRESSED, 0);
-}
-
-SR_PRIV void analyzer_wait_data(libusb_device_handle *devh)
-{
- analyzer_wait(devh, 0, STATUS_BUSY);
-}
-
-SR_PRIV int analyzer_decompress(void *input, unsigned int input_len,
- void *output, unsigned int output_len)
-{
- unsigned char *in = input;
- unsigned char *out = output;
- unsigned int A, B, C, count;
- unsigned int written = 0;
-
- while (input_len > 0) {
- A = *in++;
- B = *in++;
- C = *in++;
- count = (*in++) + 1;
-
- if (count > output_len)
- count = output_len;
- output_len -= count;
- written += count;
-
- while (count--) {
- *out++ = A;
- *out++ = B;
- *out++ = C;
- *out++ = 0; /* Channel D */
- }
-
- input_len -= 4;
- if (output_len == 0)
- break;
- }
-
- return written;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010 Sven Peter <sven@fail0verflow.com>
- * Copyright (C) 2010 Haxx Enterprises <bushing@gmail.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_ANALYZER_H
-#define LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_ANALYZER_H
-
-#include <libusb.h>
-#include "libsigrok.h"
-
-#define STATUS_FLAG_NONE 0x00
-#define STATUS_FLAG_RESET 0x01
-#define STATUS_FLAG_INIT 0x02
-#define STATUS_FLAG_GO 0x04
-#define STATUS_FLAG_PAUSE 0x08
-#define STATUS_FLAG_READ 0x10
-#define STATUS_FLAG_20 0x20
-
-/* In bytes */
-#define MEMORY_SIZE_8K 0x00
-#define MEMORY_SIZE_64K 0x01
-#define MEMORY_SIZE_128K 0x02
-#define MEMORY_SIZE_256K 0x03
-#define MEMORY_SIZE_512K 0x04
-#define MEMORY_SIZE_1M 0x05
-#define MEMORY_SIZE_2M 0x06
-#define MEMORY_SIZE_4M 0x07
-#define MEMORY_SIZE_8M 0x08
-
-#define STATUS_BUSY 0x01 /* WTF / ??? */
-#define STATUS_READY 0x02
-#define STATUS_BUTTON_PRESSED 0x04
-
-#define CHANNEL_A 0x1000
-#define CHANNEL_B 0x2000
-#define CHANNEL_C 0x3000
-#define CHANNEL_D 0x4000
-
-#define FREQ_SCALE_HZ 0
-#define FREQ_SCALE_KHZ 1
-#define FREQ_SCALE_MHZ 2
-
-#define FILTER_HIGH 0
-#define FILTER_LOW 1
-
-#define COMPRESSION_NONE 0x0001
-#define COMPRESSION_ENABLE 0x8001
-#define COMPRESSION_DOUBLE 0x8002
-
-SR_PRIV void analyzer_set_freq(int freq, int scale);
-SR_PRIV void analyzer_set_ramsize_trigger_address(unsigned int address);
-SR_PRIV void analyzer_set_triggerbar_address(unsigned int address);
-SR_PRIV unsigned int analyzer_get_ramsize_trigger_address(void );
-SR_PRIV unsigned int analyzer_get_triggerbar_address(void);
-SR_PRIV void analyzer_set_compression(unsigned int type);
-SR_PRIV void analyzer_set_memory_size(unsigned int size);
-SR_PRIV int analyzer_add_triggers(const struct sr_dev_inst *sdi);
-SR_PRIV void analyzer_set_trigger_count(int count);
-SR_PRIV void analyzer_add_filter(int channel, int type);
-SR_PRIV void analyzer_set_voltage_threshold(int thresh);
-
-SR_PRIV unsigned int analyzer_read_status(libusb_device_handle *devh);
-SR_PRIV unsigned int analyzer_read_id(libusb_device_handle *devh);
-SR_PRIV unsigned int analyzer_get_stop_address(libusb_device_handle *devh);
-SR_PRIV unsigned int analyzer_get_now_address(libusb_device_handle *devh);
-SR_PRIV unsigned int analyzer_get_trigger_address(libusb_device_handle *devh);
-SR_PRIV int analyzer_decompress(void *input, unsigned int input_len,
- void *output, unsigned int output_len);
-
-SR_PRIV void analyzer_reset(libusb_device_handle *devh);
-SR_PRIV void analyzer_initialize(libusb_device_handle *devh);
-SR_PRIV void analyzer_wait(libusb_device_handle *devh, int set, int unset);
-SR_PRIV void analyzer_read_start(libusb_device_handle *devh);
-SR_PRIV int analyzer_read_data(libusb_device_handle *devh, void *buffer,
- unsigned int size);
-SR_PRIV void analyzer_read_stop(libusb_device_handle *devh);
-SR_PRIV void analyzer_start(libusb_device_handle *devh);
-SR_PRIV void analyzer_configure(libusb_device_handle *devh);
-
-SR_PRIV void analyzer_wait_button(libusb_device_handle *devh);
-SR_PRIV void analyzer_wait_data(libusb_device_handle *devh);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 "protocol.h"
-
-#define VENDOR_NAME "ZEROPLUS"
-#define USB_INTERFACE 0
-#define USB_CONFIGURATION 1
-#define NUM_TRIGGER_STAGES 4
-#define PACKET_SIZE 2048 /* ?? */
-
-//#define ZP_EXPERIMENTAL
-
-struct zp_model {
- uint16_t vid;
- uint16_t pid;
- char *model_name;
- unsigned int channels;
- unsigned int sample_depth; /* In Ksamples/channel */
- unsigned int max_sampling_freq;
-};
-
-/*
- * Note -- 16032, 16064 and 16128 *usually* -- but not always -- have the
- * same 128K sample depth.
- */
-static const struct zp_model zeroplus_models[] = {
- {0x0c12, 0x7002, "LAP-16128U", 16, 128, 200},
- {0x0c12, 0x7009, "LAP-C(16064)", 16, 64, 100},
- {0x0c12, 0x700a, "LAP-C(16128)", 16, 128, 200},
- {0x0c12, 0x700b, "LAP-C(32128)", 32, 128, 200},
- {0x0c12, 0x700c, "LAP-C(321000)", 32, 1024, 200},
- {0x0c12, 0x700d, "LAP-C(322000)", 32, 2048, 200},
- {0x0c12, 0x700e, "LAP-C(16032)", 16, 32, 100},
- {0x0c12, 0x7016, "LAP-C(162000)", 16, 2048, 200},
- { 0, 0, 0, 0, 0, 0 }
-};
-
-static const int32_t hwcaps[] = {
- SR_CONF_LOGIC_ANALYZER,
- SR_CONF_SAMPLERATE,
- SR_CONF_TRIGGER_MATCH,
- SR_CONF_CAPTURE_RATIO,
- SR_CONF_VOLTAGE_THRESHOLD,
- SR_CONF_LIMIT_SAMPLES,
-};
-
-static const int32_t trigger_matches[] = {
- SR_TRIGGER_ZERO,
- SR_TRIGGER_ONE,
-};
-
-/*
- * ZEROPLUS LAP-C (16032) numbers the 16 channels A0-A7 and B0-B7.
- * We currently ignore other untested/unsupported devices here.
- */
-static const char *channel_names[] = {
- "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
- "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7",
- "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7",
- "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
- NULL,
-};
-
-SR_PRIV struct sr_dev_driver zeroplus_logic_cube_driver_info;
-static struct sr_dev_driver *di = &zeroplus_logic_cube_driver_info;
-
-/*
- * The hardware supports more samplerates than these, but these are the
- * options hardcoded into the vendor's Windows GUI.
- */
-
-static const uint64_t samplerates_100[] = {
- SR_HZ(100),
- SR_HZ(500),
- SR_KHZ(1),
- SR_KHZ(5),
- SR_KHZ(25),
- SR_KHZ(50),
- SR_KHZ(100),
- SR_KHZ(200),
- SR_KHZ(400),
- SR_KHZ(800),
- SR_MHZ(1),
- SR_MHZ(10),
- SR_MHZ(25),
- SR_MHZ(50),
- SR_MHZ(80),
- SR_MHZ(100),
-};
-
-const uint64_t samplerates_200[] = {
- SR_HZ(100),
- SR_HZ(500),
- SR_KHZ(1),
- SR_KHZ(5),
- SR_KHZ(25),
- SR_KHZ(50),
- SR_KHZ(100),
- SR_KHZ(200),
- SR_KHZ(400),
- SR_KHZ(800),
- SR_MHZ(1),
- SR_MHZ(10),
- SR_MHZ(25),
- SR_MHZ(50),
- SR_MHZ(80),
- SR_MHZ(100),
- SR_MHZ(150),
- SR_MHZ(200),
-};
-
-static int dev_close(struct sr_dev_inst *sdi);
-
-SR_PRIV int zp_set_samplerate(struct dev_context *devc, uint64_t samplerate)
-{
- int i;
-
- for (i = 0; ARRAY_SIZE(samplerates_200); i++)
- if (samplerate == samplerates_200[i])
- break;
-
- if (i == ARRAY_SIZE(samplerates_200) || samplerate > devc->max_samplerate) {
- sr_err("Unsupported samplerate: %" PRIu64 "Hz.", samplerate);
- return SR_ERR_ARG;
- }
-
- sr_info("Setting samplerate to %" PRIu64 "Hz.", samplerate);
-
- if (samplerate >= SR_MHZ(1))
- analyzer_set_freq(samplerate / SR_MHZ(1), FREQ_SCALE_MHZ);
- else if (samplerate >= SR_KHZ(1))
- analyzer_set_freq(samplerate / SR_KHZ(1), FREQ_SCALE_KHZ);
- else
- analyzer_set_freq(samplerate, FREQ_SCALE_HZ);
-
- devc->cur_samplerate = samplerate;
-
- return SR_OK;
-}
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- struct drv_context *drvc;
- struct dev_context *devc;
- const struct zp_model *prof;
- struct libusb_device_descriptor des;
- libusb_device **devlist;
- GSList *devices;
- int ret, devcnt, i, j;
-
- (void)options;
-
- drvc = di->priv;
-
- devices = NULL;
-
- /* Find all ZEROPLUS analyzers and add them to device list. */
- devcnt = 0;
- libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); /* TODO: Errors. */
-
- for (i = 0; devlist[i]; i++) {
- ret = libusb_get_device_descriptor(devlist[i], &des);
- if (ret != 0) {
- sr_err("Failed to get device descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
-
- prof = NULL;
- for (j = 0; j < zeroplus_models[j].vid; j++) {
- if (des.idVendor == zeroplus_models[j].vid &&
- des.idProduct == zeroplus_models[j].pid) {
- prof = &zeroplus_models[j];
- }
- }
- /* Skip if the device was not found. */
- if (!prof)
- continue;
- sr_info("Found ZEROPLUS %s.", prof->model_name);
-
- /* Register the device with libsigrok. */
- if (!(sdi = sr_dev_inst_new(devcnt, SR_ST_INACTIVE,
- VENDOR_NAME, prof->model_name, NULL))) {
- sr_err("%s: sr_dev_inst_new failed", __func__);
- return NULL;
- }
- sdi->driver = di;
-
- /* Allocate memory for our private driver context. */
- if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
- sr_err("Device context malloc failed.");
- return NULL;
- }
-
- sdi->priv = devc;
- devc->prof = prof;
- devc->num_channels = prof->channels;
-#ifdef ZP_EXPERIMENTAL
- devc->max_sample_depth = 128 * 1024;
- devc->max_samplerate = 200;
-#else
- devc->max_sample_depth = prof->sample_depth * 1024;
- devc->max_samplerate = prof->max_sampling_freq;
-#endif
- devc->max_samplerate *= SR_MHZ(1);
- devc->memory_size = MEMORY_SIZE_8K;
- // memset(devc->trigger_buffer, 0, NUM_TRIGGER_STAGES);
-
- /* Fill in channellist according to this device's profile. */
- for (j = 0; j < devc->num_channels; j++) {
- if (!(ch = sr_channel_new(j, SR_CHANNEL_LOGIC, TRUE,
- channel_names[j])))
- return NULL;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
-
- devices = g_slist_append(devices, sdi);
- drvc->instances = g_slist_append(drvc->instances, sdi);
- sdi->inst_type = SR_INST_USB;
- sdi->conn = sr_usb_dev_inst_new(
- libusb_get_bus_number(devlist[i]),
- libusb_get_device_address(devlist[i]), NULL);
- devcnt++;
-
- }
- libusb_free_device_list(devlist, 1);
-
- return devices;
-}
-
-static GSList *dev_list(void)
-{
- return ((struct drv_context *)(di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct drv_context *drvc;
- struct sr_usb_dev_inst *usb;
- libusb_device **devlist, *dev;
- struct libusb_device_descriptor des;
- int device_count, ret, i;
-
- drvc = di->priv;
- usb = sdi->conn;
-
- if (!(devc = sdi->priv)) {
- sr_err("%s: sdi->priv was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx,
- &devlist);
- if (device_count < 0) {
- sr_err("Failed to retrieve device list.");
- return SR_ERR;
- }
-
- dev = NULL;
- for (i = 0; i < device_count; i++) {
- if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
- sr_err("Failed to get device descriptor: %s.",
- libusb_error_name(ret));
- continue;
- }
- if (libusb_get_bus_number(devlist[i]) == usb->bus
- && libusb_get_device_address(devlist[i]) == usb->address) {
- dev = devlist[i];
- break;
- }
- }
- if (!dev) {
- sr_err("Device on bus %d address %d disappeared!",
- usb->bus, usb->address);
- return SR_ERR;
- }
-
- if (!(ret = libusb_open(dev, &(usb->devhdl)))) {
- sdi->status = SR_ST_ACTIVE;
- sr_info("Opened device %d on %d.%d interface %d.",
- sdi->index, usb->bus, usb->address, USB_INTERFACE);
- } else {
- sr_err("Failed to open device: %s.", libusb_error_name(ret));
- return SR_ERR;
- }
-
- ret = libusb_set_configuration(usb->devhdl, USB_CONFIGURATION);
- if (ret < 0) {
- sr_err("Unable to set USB configuration %d: %s.",
- USB_CONFIGURATION, libusb_error_name(ret));
- return SR_ERR;
- }
-
- ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
- if (ret != 0) {
- sr_err("Unable to claim interface: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- /* Set default configuration after power on. */
- if (analyzer_read_status(usb->devhdl) == 0)
- analyzer_configure(usb->devhdl);
-
- analyzer_reset(usb->devhdl);
- analyzer_initialize(usb->devhdl);
-
- //analyzer_set_memory_size(MEMORY_SIZE_512K);
- // analyzer_set_freq(g_freq, g_freq_scale);
- analyzer_set_trigger_count(1);
- // analyzer_set_ramsize_trigger_address((((100 - g_pre_trigger)
- // * get_memory_size(g_memory_size)) / 100) >> 2);
-
-#if 0
- if (g_double_mode == 1)
- analyzer_set_compression(COMPRESSION_DOUBLE);
- else if (g_compression == 1)
- analyzer_set_compression(COMPRESSION_ENABLE);
- else
-#endif
- analyzer_set_compression(COMPRESSION_NONE);
-
- if (devc->cur_samplerate == 0) {
- /* Samplerate hasn't been set. Default to 1MHz. */
- analyzer_set_freq(1, FREQ_SCALE_MHZ);
- devc->cur_samplerate = SR_MHZ(1);
- }
-
- if (devc->cur_threshold == 0)
- set_voltage_threshold(devc, 1.5);
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_usb_dev_inst *usb;
-
- usb = sdi->conn;
-
- if (!usb->devhdl)
- return SR_ERR;
-
- sr_info("Closing device %d on %d.%d interface %d.", sdi->index,
- usb->bus, usb->address, USB_INTERFACE);
- libusb_release_interface(usb->devhdl, USB_INTERFACE);
- libusb_reset_device(usb->devhdl);
- libusb_close(usb->devhdl);
- usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static int cleanup(void)
-{
- return std_dev_clear(di, NULL);
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
-
- (void)cg;
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- if (sdi) {
- devc = sdi->priv;
- *data = g_variant_new_uint64(devc->cur_samplerate);
- sr_spew("Returning samplerate: %" PRIu64 "Hz.",
- devc->cur_samplerate);
- } else
- return SR_ERR_ARG;
- break;
- case SR_CONF_CAPTURE_RATIO:
- if (sdi) {
- devc = sdi->priv;
- *data = g_variant_new_uint64(devc->capture_ratio);
- } else
- return SR_ERR_ARG;
- break;
- case SR_CONF_VOLTAGE_THRESHOLD:
- if (sdi) {
- GVariant *range[2];
- devc = sdi->priv;
- range[0] = g_variant_new_double(devc->cur_threshold);
- range[1] = g_variant_new_double(devc->cur_threshold);
- *data = g_variant_new_tuple(range, 2);
- } else
- return SR_ERR_ARG;
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- gdouble low, high;
-
- (void)cg;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("%s: sdi->priv was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- return zp_set_samplerate(devc, g_variant_get_uint64(data));
- case SR_CONF_LIMIT_SAMPLES:
- return set_limit_samples(devc, g_variant_get_uint64(data));
- case SR_CONF_CAPTURE_RATIO:
- return set_capture_ratio(devc, g_variant_get_uint64(data));
- case SR_CONF_VOLTAGE_THRESHOLD:
- g_variant_get(data, "(dd)", &low, &high);
- return set_voltage_threshold(devc, (low + high) / 2.0);
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct dev_context *devc;
- GVariant *gvar, *grange[2];
- GVariantBuilder gvb;
- double v;
- GVariant *range[2];
-
- (void)cg;
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- case SR_CONF_SAMPLERATE:
- devc = sdi->priv;
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- if (devc->prof->max_sampling_freq == 100) {
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- samplerates_100, ARRAY_SIZE(samplerates_100),
- sizeof(uint64_t));
- } else if (devc->prof->max_sampling_freq == 200) {
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- samplerates_200, ARRAY_SIZE(samplerates_200),
- sizeof(uint64_t));
- } else {
- sr_err("Internal error: Unknown max. samplerate: %d.",
- devc->prof->max_sampling_freq);
- return SR_ERR_ARG;
- }
- g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_TRIGGER_MATCH:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- trigger_matches, ARRAY_SIZE(trigger_matches),
- sizeof(int32_t));
- break;
- case SR_CONF_VOLTAGE_THRESHOLD:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (v = -6.0; v <= 6.0; v += 0.1) {
- range[0] = g_variant_new_double(v);
- range[1] = g_variant_new_double(v);
- gvar = g_variant_new_tuple(range, 2);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
- break;
- case SR_CONF_LIMIT_SAMPLES:
- if (!sdi)
- return SR_ERR_ARG;
- devc = sdi->priv;
- grange[0] = g_variant_new_uint64(0);
- grange[1] = g_variant_new_uint64(devc->max_sample_depth);
- *data = g_variant_new_tuple(grange, 2);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
- void *cb_data)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- unsigned int samples_read;
- int res;
- unsigned int packet_num, n;
- unsigned char *buf;
- unsigned int status;
- unsigned int stop_address;
- unsigned int now_address;
- unsigned int trigger_address;
- unsigned int trigger_offset;
- unsigned int triggerbar;
- unsigned int ramsize_trigger;
- unsigned int memory_size;
- unsigned int valid_samples;
- unsigned int discard;
- int trigger_now;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- if (!(devc = sdi->priv)) {
- sr_err("%s: sdi->priv was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- if (analyzer_add_triggers(sdi) != SR_OK) {
- sr_err("Failed to configure triggers.");
- return SR_ERR;
- }
-
- usb = sdi->conn;
-
- set_triggerbar(devc);
-
- /* Push configured settings to device. */
- analyzer_configure(usb->devhdl);
-
- analyzer_start(usb->devhdl);
- sr_info("Waiting for data.");
- analyzer_wait_data(usb->devhdl);
-
- status = analyzer_read_status(usb->devhdl);
- stop_address = analyzer_get_stop_address(usb->devhdl);
- now_address = analyzer_get_now_address(usb->devhdl);
- trigger_address = analyzer_get_trigger_address(usb->devhdl);
-
- triggerbar = analyzer_get_triggerbar_address();
- ramsize_trigger = analyzer_get_ramsize_trigger_address();
-
- n = get_memory_size(devc->memory_size);
- memory_size = n / 4;
-
- sr_info("Status = 0x%x.", status);
- sr_info("Stop address = 0x%x.", stop_address);
- sr_info("Now address = 0x%x.", now_address);
- sr_info("Trigger address = 0x%x.", trigger_address);
- sr_info("Triggerbar address = 0x%x.", triggerbar);
- sr_info("Ramsize trigger = 0x%x.", ramsize_trigger);
- sr_info("Memory size = 0x%x.", memory_size);
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(cb_data, LOG_PREFIX);
-
- /* Check for empty capture */
- if ((status & STATUS_READY) && !stop_address) {
- packet.type = SR_DF_END;
- sr_session_send(cb_data, &packet);
- return SR_OK;
- }
-
- if (!(buf = g_try_malloc(PACKET_SIZE))) {
- sr_err("Packet buffer malloc failed.");
- return SR_ERR_MALLOC;
- }
-
- /* Check if the trigger is in the samples we are throwing away */
- trigger_now = now_address == trigger_address ||
- ((now_address + 1) % memory_size) == trigger_address;
-
- /*
- * STATUS_READY doesn't clear until now_address advances past
- * addr 0, but for our logic, clear it in that case
- */
- if (!now_address)
- status &= ~STATUS_READY;
-
- analyzer_read_start(usb->devhdl);
-
- /* Calculate how much data to discard */
- discard = 0;
- if (status & STATUS_READY) {
- /*
- * We haven't wrapped around, we need to throw away data from
- * our current position to the end of the buffer.
- * Additionally, the first two samples captured are always
- * bogus.
- */
- discard += memory_size - now_address + 2;
- now_address = 2;
- }
-
- /* If we have more samples than we need, discard them */
- valid_samples = (stop_address - now_address) % memory_size;
- if (valid_samples > ramsize_trigger + triggerbar) {
- discard += valid_samples - (ramsize_trigger + triggerbar);
- now_address += valid_samples - (ramsize_trigger + triggerbar);
- }
-
- sr_info("Need to discard %d samples.", discard);
-
- /* Calculate how far in the trigger is */
- if (trigger_now)
- trigger_offset = 0;
- else
- trigger_offset = (trigger_address - now_address) % memory_size;
-
- /* Recalculate the number of samples available */
- valid_samples = (stop_address - now_address) % memory_size;
-
- /* Send the incoming transfer to the session bus. */
- samples_read = 0;
- for (packet_num = 0; packet_num < n / PACKET_SIZE; packet_num++) {
- unsigned int len;
- unsigned int buf_offset;
-
- res = analyzer_read_data(usb->devhdl, buf, PACKET_SIZE);
- sr_info("Tried to read %d bytes, actually read %d bytes.",
- PACKET_SIZE, res);
-
- if (discard >= PACKET_SIZE / 4) {
- discard -= PACKET_SIZE / 4;
- continue;
- }
-
- len = PACKET_SIZE - discard * 4;
- buf_offset = discard * 4;
- discard = 0;
-
- /* Check if we've read all the samples */
- if (samples_read + len / 4 >= valid_samples)
- len = (valid_samples - samples_read) * 4;
- if (!len)
- break;
-
- if (samples_read < trigger_offset &&
- samples_read + len / 4 > trigger_offset) {
- /* Send out samples remaining before trigger */
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = (trigger_offset - samples_read) * 4;
- logic.unitsize = 4;
- logic.data = buf + buf_offset;
- sr_session_send(cb_data, &packet);
- len -= logic.length;
- samples_read += logic.length / 4;
- buf_offset += logic.length;
- }
-
- if (samples_read == trigger_offset) {
- /* Send out trigger */
- packet.type = SR_DF_TRIGGER;
- packet.payload = NULL;
- sr_session_send(cb_data, &packet);
- }
-
- /* Send out data (or data after trigger) */
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = len;
- logic.unitsize = 4;
- logic.data = buf + buf_offset;
- sr_session_send(cb_data, &packet);
- samples_read += len / 4;
- }
- analyzer_read_stop(usb->devhdl);
- g_free(buf);
-
- packet.type = SR_DF_END;
- sr_session_send(cb_data, &packet);
-
- return SR_OK;
-}
-
-/* TODO: This stops acquisition on ALL devices, ignoring dev_index. */
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- struct sr_datafeed_packet packet;
-
- packet.type = SR_DF_END;
- sr_session_send(cb_data, &packet);
-
- if (!(devc = sdi->priv)) {
- sr_err("%s: sdi->priv was NULL", __func__);
- return SR_ERR_BUG;
- }
-
- usb = sdi->conn;
- analyzer_reset(usb->devhdl);
- /* TODO: Need to cancel and free any queued up transfers. */
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver zeroplus_logic_cube_driver_info = {
- .name = "zeroplus-logic-cube",
- .longname = "ZEROPLUS Logic Cube LAP-C series",
- .api_version = 1,
- .init = init,
- .cleanup = cleanup,
- .scan = scan,
- .dev_list = dev_list,
- .dev_clear = NULL,
- .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,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010 Sven Peter <sven@fail0verflow.com>
- * Copyright (C) 2010 Haxx Enterprises <bushing@gmail.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <libusb.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "gl_usb.h"
-#include "protocol.h"
-
-#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN | \
- LIBUSB_RECIPIENT_INTERFACE)
-#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT | \
- LIBUSB_RECIPIENT_INTERFACE)
-#define EP1_BULK_IN (LIBUSB_ENDPOINT_IN | 1)
-
-#define TIMEOUT 5000 /* Timeout in ms */
-
-enum {
- REQ_READBULK = 0x82,
- REQ_WRITEADDR,
- REQ_READDATA,
- REQ_WRITEDATA,
-};
-
-static int gl_write_address(libusb_device_handle *devh, unsigned int address)
-{
- unsigned char packet[8] = { address & 0xFF };
- int ret;
-
- ret = libusb_control_transfer(devh, CTRL_OUT, 0xc, REQ_WRITEADDR,
- 0, packet, 1, TIMEOUT);
- if (ret != 1)
- sr_err("%s: %s.", __func__, libusb_error_name(ret));
- return ret;
-}
-
-static int gl_write_data(libusb_device_handle *devh, unsigned int val)
-{
- unsigned char packet[8] = { val & 0xFF };
- int ret;
-
- ret = libusb_control_transfer(devh, CTRL_OUT, 0xc, REQ_WRITEDATA,
- 0, packet, 1, TIMEOUT);
- if (ret != 1)
- sr_err("%s: %s.", __func__, libusb_error_name(ret));
- return ret;
-}
-
-static int gl_read_data(libusb_device_handle *devh)
-{
- unsigned char packet[8] = { 0 };
- int ret;
-
- ret = libusb_control_transfer(devh, CTRL_IN, 0xc, REQ_READDATA,
- 0, packet, 1, TIMEOUT);
- if (ret != 1)
- sr_err("%s: %s, val=%hhx.", __func__,
- libusb_error_name(ret), packet[0]);
- return (ret == 1) ? packet[0] : ret;
-}
-
-SR_PRIV int gl_read_bulk(libusb_device_handle *devh, void *buffer,
- unsigned int size)
-{
- unsigned char packet[8] =
- { 0, 0, 0, 0, size & 0xff, (size & 0xff00) >> 8,
- (size & 0xff0000) >> 16, (size & 0xff000000) >> 24 };
- int ret, transferred = 0;
-
- ret = libusb_control_transfer(devh, CTRL_OUT, 0x4, REQ_READBULK,
- 0, packet, 8, TIMEOUT);
- if (ret != 8)
- sr_err("%s: libusb_control_transfer: %s.", __func__,
- libusb_error_name(ret));
-
- ret = libusb_bulk_transfer(devh, EP1_BULK_IN, buffer, size,
- &transferred, TIMEOUT);
- if (ret < 0)
- sr_err("%s: libusb_bulk_transfer: %s.", __func__,
- libusb_error_name(ret));
- return transferred;
-}
-
-SR_PRIV int gl_reg_write(libusb_device_handle *devh, unsigned int reg,
- unsigned int val)
-{
- int ret;
-
- ret = gl_write_address(devh, reg);
- if (ret < 0)
- return ret;
- ret = gl_write_data(devh, val);
- return ret;
-}
-
-SR_PRIV int gl_reg_read(libusb_device_handle *devh, unsigned int reg)
-{
- int ret;
-
- ret = gl_write_address(devh, reg);
- if (ret < 0)
- return ret;
- ret = gl_read_data(devh);
- return ret;
-}
-
-SR_PRIV int gl_reg_read_buf(libusb_device_handle *devh, unsigned int reg,
- unsigned char *buf, unsigned int len)
-{
- int ret;
- unsigned int i;
-
- ret = gl_write_address(devh, reg);
- if (ret < 0)
- return ret;
- for (i = 0; i < len; i++) {
- ret = gl_read_data(devh);
- if (ret < 0)
- return ret;
- buf[i] = ret;
- }
- return 0;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010 Sven Peter <sven@fail0verflow.com>
- * Copyright (C) 2010 Haxx Enterprises <bushing@gmail.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_GL_USB_H
-#define LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_GL_USB_H
-
-#include <libusb.h>
-#include "libsigrok.h"
-
-SR_PRIV int gl_read_bulk(libusb_device_handle *devh, void *buffer,
- unsigned int size);
-SR_PRIV int gl_reg_write(libusb_device_handle *devh, unsigned int reg,
- unsigned int val);
-SR_PRIV int gl_reg_read(libusb_device_handle *devh, unsigned int reg);
-SR_PRIV int gl_reg_read_buf(libusb_device_handle *devh, unsigned int reg,
- unsigned char *buf, unsigned int len);
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <math.h>
-#include "protocol.h"
-
-SR_PRIV unsigned int get_memory_size(int type)
-{
- if (type == MEMORY_SIZE_8K)
- return 8 * 1024;
- else if (type <= MEMORY_SIZE_8M)
- return (32 * 1024) << type;
- else
- return 0;
-}
-
-static int clz(unsigned int x)
-{
- int n = 0;
- if (x == 0)
- return 32;
- if (!(x & 0xFFFF0000)) {
- n = n + 16;
- x = x << 16;
- }
- if (!(x & 0xFF000000)) {
- n = n + 8;
- x = x << 8;
- }
- if (!(x & 0xF0000000)) {
- n = n + 4;
- x = x << 4;
- }
- if (!(x & 0xC0000000)) {
- n = n + 2;
- x = x << 2;
- }
- if (!(x & 0x80000000))
- n = n + 1;
- return n;
-}
-
-SR_PRIV int set_limit_samples(struct dev_context *devc, uint64_t samples)
-{
- if (samples > devc->max_sample_depth)
- samples = devc->max_sample_depth;
-
- devc->limit_samples = samples;
-
- if (samples <= 2 * 1024)
- devc->memory_size = MEMORY_SIZE_8K;
- else if (samples <= 16 * 1024)
- devc->memory_size = MEMORY_SIZE_64K;
- else
- devc->memory_size = 19 - clz(samples - 1);
-
- sr_info("Setting memory size to %dK.",
- get_memory_size(devc->memory_size) / 1024);
-
- analyzer_set_memory_size(devc->memory_size);
-
- return SR_OK;
-}
-
-SR_PRIV int set_capture_ratio(struct dev_context *devc, uint64_t ratio)
-{
- if (ratio > 100) {
- sr_err("Invalid capture ratio: %" PRIu64 ".", ratio);
- return SR_ERR_ARG;
- }
-
- devc->capture_ratio = ratio;
-
- sr_info("Setting capture ratio to %d%%.", devc->capture_ratio);
-
- return SR_OK;
-}
-
-SR_PRIV int set_voltage_threshold(struct dev_context *devc, double thresh)
-{
- if (thresh > 6.0)
- thresh = 6.0;
- if (thresh < -6.0)
- thresh = -6.0;
-
- devc->cur_threshold = thresh;
-
- analyzer_set_voltage_threshold((int) round(-9.1*thresh + 62.6));
-
- sr_info("Setting voltage threshold to %fV.", devc->cur_threshold);
-
- return SR_OK;
-}
-
-SR_PRIV void set_triggerbar(struct dev_context *devc)
-{
- unsigned int trigger_depth, triggerbar, ramsize_trigger;
-
- trigger_depth = get_memory_size(devc->memory_size) / 4;
- if (devc->limit_samples < trigger_depth)
- trigger_depth = devc->limit_samples;
-
- if (devc->trigger)
- triggerbar = trigger_depth * devc->capture_ratio / 100;
- else
- triggerbar = 0;
-
- ramsize_trigger = trigger_depth - triggerbar;
- /* Matches USB packet captures from official app/driver */
- if (triggerbar > 2)
- triggerbar -= 2;
- else {
- ramsize_trigger -= 1;
- triggerbar = 0;
- }
-
- analyzer_set_triggerbar_address(triggerbar);
- analyzer_set_ramsize_trigger_address(ramsize_trigger);
-
- sr_dbg("triggerbar_address = %d(0x%x)", triggerbar, triggerbar);
- sr_dbg("ramsize_triggerbar_address = %d(0x%x)",
- ramsize_trigger, ramsize_trigger);
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_PROTOCOL_H
-
-#include <stdint.h>
-#include <glib.h>
-#include <libusb.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "analyzer.h"
-
-#define LOG_PREFIX "zeroplus"
-
-/* Private, per-device-instance driver context. */
-struct dev_context {
- uint64_t cur_samplerate;
- uint64_t max_samplerate;
- uint64_t limit_samples;
- int num_channels;
- int memory_size;
- unsigned int max_sample_depth;
- //uint8_t channel_mask;
- //uint8_t trigger_mask[NUM_TRIGGER_STAGES];
- //uint8_t trigger_value[NUM_TRIGGER_STAGES];
- // uint8_t trigger_buffer[NUM_TRIGGER_STAGES];
- int trigger;
- unsigned int capture_ratio;
- double cur_threshold;
- const struct zp_model *prof;
-};
-
-SR_PRIV unsigned int get_memory_size(int type);
-SR_PRIV int zp_set_samplerate(struct dev_context *devc, uint64_t samplerate);
-SR_PRIV int set_limit_samples(struct dev_context *devc, uint64_t samples);
-SR_PRIV int set_capture_ratio(struct dev_context *devc, uint64_t ratio);
-SR_PRIV int set_voltage_threshold(struct dev_context *devc, double thresh);
-SR_PRIV void set_triggerbar(struct dev_context *devc);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdlib.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <string.h>
-#include <glib.h>
-#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/** @cond PRIVATE */
-#define LOG_PREFIX "hwdriver"
-/** @endcond */
-
-extern SR_PRIV struct sr_dev_driver *drivers_list[];
-
-/**
- * @file
- *
- * Hardware driver handling in libsigrok.
- */
-
-/**
- * @defgroup grp_driver Hardware drivers
- *
- * Hardware driver handling in libsigrok.
- *
- * @{
- */
-
-static struct sr_config_info sr_config_info_data[] = {
- {SR_CONF_CONN, SR_T_STRING, "conn",
- "Connection", NULL},
- {SR_CONF_SERIALCOMM, SR_T_STRING, "serialcomm",
- "Serial communication", NULL},
- {SR_CONF_SAMPLERATE, SR_T_UINT64, "samplerate",
- "Sample rate", NULL},
- {SR_CONF_CAPTURE_RATIO, SR_T_UINT64, "captureratio",
- "Pre-trigger capture ratio", NULL},
- {SR_CONF_PATTERN_MODE, SR_T_STRING, "pattern",
- "Pattern", NULL},
- {SR_CONF_TRIGGER_MATCH, SR_T_INT32, "triggermatch",
- "Trigger matches", NULL},
- {SR_CONF_EXTERNAL_CLOCK, SR_T_BOOL, "external_clock",
- "External clock mode", NULL},
- {SR_CONF_SWAP, SR_T_BOOL, "swap",
- "Swap channel order", NULL},
- {SR_CONF_RLE, SR_T_BOOL, "rle",
- "Run Length Encoding", NULL},
- {SR_CONF_TRIGGER_SLOPE, SR_T_STRING, "triggerslope",
- "Trigger slope", NULL},
- {SR_CONF_TRIGGER_SOURCE, SR_T_STRING, "triggersource",
- "Trigger source", NULL},
- {SR_CONF_HORIZ_TRIGGERPOS, SR_T_FLOAT, "horiz_triggerpos",
- "Horizontal trigger position", NULL},
- {SR_CONF_BUFFERSIZE, SR_T_UINT64, "buffersize",
- "Buffer size", NULL},
- {SR_CONF_TIMEBASE, SR_T_RATIONAL_PERIOD, "timebase",
- "Time base", NULL},
- {SR_CONF_FILTER, SR_T_STRING, "filter",
- "Filter targets", NULL},
- {SR_CONF_VDIV, SR_T_RATIONAL_VOLT, "vdiv",
- "Volts/div", NULL},
- {SR_CONF_COUPLING, SR_T_STRING, "coupling",
- "Coupling", NULL},
- {SR_CONF_DATALOG, SR_T_BOOL, "datalog",
- "Datalog", NULL},
- {SR_CONF_SPL_WEIGHT_FREQ, SR_T_STRING, "spl_weight_freq",
- "Sound pressure level frequency weighting", NULL},
- {SR_CONF_SPL_WEIGHT_TIME, SR_T_STRING, "spl_weight_time",
- "Sound pressure level time weighting", NULL},
- {SR_CONF_HOLD_MAX, SR_T_BOOL, "hold_max",
- "Hold max", NULL},
- {SR_CONF_HOLD_MIN, SR_T_BOOL, "hold_min",
- "Hold min", NULL},
- {SR_CONF_SPL_MEASUREMENT_RANGE, SR_T_UINT64_RANGE, "spl_meas_range",
- "Sound pressure level measurement range", NULL},
- {SR_CONF_VOLTAGE_THRESHOLD, SR_T_DOUBLE_RANGE, "voltage_threshold",
- "Voltage threshold", NULL },
- {SR_CONF_POWER_OFF, SR_T_BOOL, "power_off",
- "Power off", NULL},
- {SR_CONF_DATA_SOURCE, SR_T_STRING, "data_source",
- "Data source", NULL},
- {SR_CONF_NUM_LOGIC_CHANNELS, SR_T_INT32, "logic_channels",
- "Number of logic channels", NULL},
- {SR_CONF_NUM_ANALOG_CHANNELS, SR_T_INT32, "analog_channels",
- "Number of analog channels", NULL},
- {SR_CONF_OUTPUT_VOLTAGE, SR_T_FLOAT, "output_voltage",
- "Current output voltage", NULL},
- {SR_CONF_OUTPUT_VOLTAGE_MAX, SR_T_FLOAT, "output_voltage_max",
- "Maximum output voltage", NULL},
- {SR_CONF_OUTPUT_CURRENT, SR_T_FLOAT, "output_current",
- "Current output current", NULL},
- {SR_CONF_OUTPUT_CURRENT_MAX, SR_T_FLOAT, "output_current_max",
- "Maximum output current", NULL},
- {SR_CONF_OUTPUT_ENABLED, SR_T_BOOL, "output_enabled",
- "Output enabled", NULL},
- {SR_CONF_OUTPUT_CHANNEL, SR_T_STRING, "output_channel",
- "Output channel modes", NULL},
- {SR_CONF_OVER_VOLTAGE_PROTECTION, SR_T_BOOL, "ovp",
- "Over-voltage protection", NULL},
- {SR_CONF_OVER_CURRENT_PROTECTION, SR_T_BOOL, "ocp",
- "Over-current protection", NULL},
- {SR_CONF_LIMIT_SAMPLES, SR_T_UINT64, "limit_samples",
- "Sample limit", NULL},
- {SR_CONF_CLOCK_EDGE, SR_T_STRING, "clock_edge",
- "Clock edge", NULL},
- {0, 0, NULL, NULL, NULL},
-};
-
-/**
- * Return the list of supported hardware drivers.
- *
- * @return Pointer to the NULL-terminated list of hardware driver pointers.
- *
- * @since 0.1.0
- */
-SR_API struct sr_dev_driver **sr_driver_list(void)
-{
-
- return drivers_list;
-}
-
-/**
- * Initialize a hardware driver.
- *
- * This usually involves memory allocations and variable initializations
- * within the driver, but _not_ scanning for attached devices.
- * The API call sr_driver_scan() is used for that.
- *
- * @param ctx A libsigrok context object allocated by a previous call to
- * sr_init(). Must not be NULL.
- * @param driver The driver to initialize. This must be a pointer to one of
- * the entries returned by sr_driver_list(). Must not be NULL.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid parameter(s).
- * @retval SR_ERR_BUG Internal errors.
- * @retval other Another negative error code upon other errors.
- *
- * @since 0.2.0
- */
-SR_API int sr_driver_init(struct sr_context *ctx, struct sr_dev_driver *driver)
-{
- int ret;
-
- if (!ctx) {
- sr_err("Invalid libsigrok context, can't initialize.");
- return SR_ERR_ARG;
- }
-
- if (!driver) {
- sr_err("Invalid driver, can't initialize.");
- return SR_ERR_ARG;
- }
-
- sr_spew("Initializing driver '%s'.", driver->name);
- if ((ret = driver->init(ctx)) < 0)
- sr_err("Failed to initialize the driver: %d.", ret);
-
- return ret;
-}
-
-/**
- * Tell a hardware driver to scan for devices.
- *
- * In addition to the detection, the devices that are found are also
- * initialized automatically. On some devices, this involves a firmware upload,
- * or other such measures.
- *
- * The order in which the system is scanned for devices is not specified. The
- * caller should not assume or rely on any specific order.
- *
- * Before calling sr_driver_scan(), the user must have previously initialized
- * the driver by calling sr_driver_init().
- *
- * @param driver The driver that should scan. This must be a pointer to one of
- * the entries returned by sr_driver_list(). Must not be NULL.
- * @param options A list of 'struct sr_hwopt' options to pass to the driver's
- * scanner. Can be NULL/empty.
- *
- * @return A GSList * of 'struct sr_dev_inst', or NULL if no devices were
- * found (or errors were encountered). This list must be freed by the
- * caller using g_slist_free(), but without freeing the data pointed
- * to in the list.
- *
- * @since 0.2.0
- */
-SR_API GSList *sr_driver_scan(struct sr_dev_driver *driver, GSList *options)
-{
- GSList *l;
-
- if (!driver) {
- sr_err("Invalid driver, can't scan for devices.");
- return NULL;
- }
-
- if (!driver->priv) {
- sr_err("Driver not initialized, can't scan for devices.");
- return NULL;
- }
-
- l = driver->scan(options);
-
- sr_spew("Scan of '%s' found %d devices.", driver->name,
- g_slist_length(l));
-
- return l;
-}
-
-/** Call driver cleanup function for all drivers.
- * @private */
-SR_PRIV void sr_hw_cleanup_all(void)
-{
- int i;
- struct sr_dev_driver **drivers;
-
- drivers = sr_driver_list();
- for (i = 0; drivers[i]; i++) {
- if (drivers[i]->cleanup)
- drivers[i]->cleanup();
- }
-}
-
-/** Allocate struct sr_config.
- * A floating reference can be passed in for data.
- * @private
- */
-SR_PRIV struct sr_config *sr_config_new(int key, GVariant *data)
-{
- struct sr_config *src;
-
- if (!(src = g_try_malloc(sizeof(struct sr_config))))
- return NULL;
- src->key = key;
- src->data = g_variant_ref_sink(data);
-
- return src;
-}
-
-/** Free struct sr_config.
- * @private
- */
-SR_PRIV void sr_config_free(struct sr_config *src)
-{
-
- if (!src || !src->data) {
- sr_err("%s: invalid data!", __func__);
- return;
- }
-
- g_variant_unref(src->data);
- g_free(src);
-
-}
-
-/**
- * Query value of a configuration key at the given driver or device instance.
- *
- * @param[in] driver The sr_dev_driver struct to query.
- * @param[in] sdi (optional) If the key is specific to a device, this must
- * contain a pointer to the struct sr_dev_inst to be checked.
- * Otherwise it must be NULL.
- * @param[in] cg The channel group on the device for which to list the
- * values, or NULL.
- * @param[in] key The configuration key (SR_CONF_*).
- * @param[in,out] data Pointer to a GVariant where the value will be stored.
- * Must not be NULL. The caller is given ownership of the GVariant
- * and must thus decrease the refcount after use. However if
- * this function returns an error code, the field should be
- * considered unused, and should not be unreferenced.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Error.
- * @retval SR_ERR_ARG The driver doesn't know that key, but this is not to be
- * interpreted as an error by the caller; merely as an indication
- * that it's not applicable.
- *
- * @since 0.3.0
- */
-SR_API int sr_config_get(const struct sr_dev_driver *driver,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg,
- int key, GVariant **data)
-{
- int ret;
-
- if (!driver || !data)
- return SR_ERR;
-
- if (!driver->config_get)
- return SR_ERR_ARG;
-
- if ((ret = driver->config_get(key, data, sdi, cg)) == SR_OK) {
- /* Got a floating reference from the driver. Sink it here,
- * caller will need to unref when done with it. */
- g_variant_ref_sink(*data);
- }
-
- return ret;
-}
-
-/**
- * Set value of a configuration key in a device instance.
- *
- * @param[in] sdi The device instance.
- * @param[in] cg The channel group on the device for which to list the
- * values, or NULL.
- * @param[in] key The configuration key (SR_CONF_*).
- * @param data The new value for the key, as a GVariant with GVariantType
- * appropriate to that key. A floating reference can be passed
- * in; its refcount will be sunk and unreferenced after use.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Error.
- * @retval SR_ERR_ARG The driver doesn't know that key, but this is not to be
- * interpreted as an error by the caller; merely as an indication
- * that it's not applicable.
- *
- * @since 0.3.0
- */
-SR_API int sr_config_set(const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg,
- int key, GVariant *data)
-{
- int ret;
-
- g_variant_ref_sink(data);
-
- if (!sdi || !sdi->driver || !data)
- ret = SR_ERR;
- else if (!sdi->driver->config_set)
- ret = SR_ERR_ARG;
- else
- ret = sdi->driver->config_set(key, data, sdi, cg);
-
- g_variant_unref(data);
-
- return ret;
-}
-
-/**
- * Apply configuration settings to the device hardware.
- *
- * @param sdi The device instance.
- *
- * @return SR_OK upon success or SR_ERR in case of error.
- *
- * @since 0.3.0
- */
-SR_API int sr_config_commit(const struct sr_dev_inst *sdi)
-{
- int ret;
-
- if (!sdi || !sdi->driver)
- ret = SR_ERR;
- else if (!sdi->driver->config_commit)
- ret = SR_OK;
- else
- ret = sdi->driver->config_commit(sdi);
-
- return ret;
-}
-
-/**
- * List all possible values for a configuration key.
- *
- * @param[in] driver The sr_dev_driver struct to query.
- * @param[in] sdi (optional) If the key is specific to a device, this must
- * contain a pointer to the struct sr_dev_inst to be checked.
- * @param[in] cg The channel group on the device for which to list the
- * values, or NULL.
- * @param[in] key The configuration key (SR_CONF_*).
- * @param[in,out] data A pointer to a GVariant where the list will be stored.
- * The caller is given ownership of the GVariant and must thus
- * unref the GVariant after use. However if this function
- * returns an error code, the field should be considered
- * unused, and should not be unreferenced.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Error.
- * @retval SR_ERR_ARG The driver doesn't know that key, but this is not to be
- * interpreted as an error by the caller; merely as an indication
- * that it's not applicable.
- *
- * @since 0.3.0
- */
-SR_API int sr_config_list(const struct sr_dev_driver *driver,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg,
- int key, GVariant **data)
-{
- int ret;
-
- if (!driver || !data)
- ret = SR_ERR;
- else if (!driver->config_list)
- ret = SR_ERR_ARG;
- else if ((ret = driver->config_list(key, data, sdi, cg)) == SR_OK)
- g_variant_ref_sink(*data);
-
- return ret;
-}
-
-/**
- * Get information about a configuration key, by key.
- *
- * @param[in] key The configuration key.
- *
- * @return A pointer to a struct sr_config_info, or NULL if the key
- * was not found.
- *
- * @since 0.2.0
- */
-SR_API const struct sr_config_info *sr_config_info_get(int key)
-{
- int i;
-
- for (i = 0; sr_config_info_data[i].key; i++) {
- if (sr_config_info_data[i].key == key)
- return &sr_config_info_data[i];
- }
-
- return NULL;
-}
-
-/**
- * Get information about a configuration key, by name.
- *
- * @param[in] optname The configuration key.
- *
- * @return A pointer to a struct sr_config_info, or NULL if the key
- * was not found.
- *
- * @since 0.2.0
- */
-SR_API const struct sr_config_info *sr_config_info_name_get(const char *optname)
-{
- int i;
-
- for (i = 0; sr_config_info_data[i].key; i++) {
- if (!strcmp(sr_config_info_data[i].id, optname))
- return &sr_config_info_data[i];
- }
-
- return NULL;
-}
-
-/** @} */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/time.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "input/binary"
-
-#define CHUNKSIZE (512 * 1024)
-#define DEFAULT_NUM_CHANNELS 8
-
-struct context {
- uint64_t samplerate;
-};
-
-static int format_match(const char *filename)
-{
- (void)filename;
-
- /* This module will handle anything you throw at it. */
- return TRUE;
-}
-
-static int init(struct sr_input *in, const char *filename)
-{
- struct sr_channel *ch;
- int num_channels, i;
- char name[SR_MAX_CHANNELNAME_LEN + 1];
- char *param;
- struct context *ctx;
-
- (void)filename;
-
- if (!(ctx = g_try_malloc0(sizeof(*ctx)))) {
- sr_err("Input format context malloc failed.");
- return SR_ERR_MALLOC;
- }
-
- num_channels = DEFAULT_NUM_CHANNELS;
- ctx->samplerate = 0;
-
- if (in->param) {
- param = g_hash_table_lookup(in->param, "numchannels");
- if (param) {
- num_channels = strtoul(param, NULL, 10);
- if (num_channels < 1)
- return SR_ERR;
- }
-
- param = g_hash_table_lookup(in->param, "samplerate");
- if (param) {
- if (sr_parse_sizestring(param, &ctx->samplerate) != SR_OK)
- return SR_ERR;
- }
- }
-
- /* Create a virtual device. */
- in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
- in->internal = ctx;
-
- for (i = 0; i < num_channels; i++) {
- snprintf(name, SR_MAX_CHANNELNAME_LEN, "%d", i);
- /* TODO: Check return value. */
- if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, name)))
- return SR_ERR;
- in->sdi->channels = g_slist_append(in->sdi->channels, ch);
- }
-
- return SR_OK;
-}
-
-static int loadfile(struct sr_input *in, const char *filename)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_meta meta;
- struct sr_datafeed_logic logic;
- struct sr_config *src;
- unsigned char buffer[CHUNKSIZE];
- int fd, size, num_channels;
- struct context *ctx;
-
- ctx = in->internal;
-
- if ((fd = open(filename, O_RDONLY)) == -1)
- return SR_ERR;
-
- num_channels = g_slist_length(in->sdi->channels);
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(in->sdi, LOG_PREFIX);
-
- if (ctx->samplerate) {
- packet.type = SR_DF_META;
- packet.payload = &meta;
- src = sr_config_new(SR_CONF_SAMPLERATE,
- g_variant_new_uint64(ctx->samplerate));
- meta.config = g_slist_append(NULL, src);
- sr_session_send(in->sdi, &packet);
- sr_config_free(src);
- }
-
- /* Chop up the input file into chunks & send it to the session bus. */
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.unitsize = (num_channels + 7) / 8;
- logic.data = buffer;
- while ((size = read(fd, buffer, CHUNKSIZE)) > 0) {
- logic.length = size;
- sr_session_send(in->sdi, &packet);
- }
- close(fd);
-
- /* Send end packet to the session bus. */
- packet.type = SR_DF_END;
- sr_session_send(in->sdi, &packet);
-
- g_free(ctx);
- in->internal = NULL;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_input_format input_binary = {
- .id = "binary",
- .description = "Raw binary",
- .format_match = format_match,
- .init = init,
- .loadfile = loadfile,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "input/chronovu-la8"
-
-#define NUM_PACKETS 2048
-#define PACKET_SIZE 4096
-#define DEFAULT_NUM_CHANNELS 8
-
-/**
- * Convert the LA8 'divcount' value to the respective samplerate (in Hz).
- *
- * LA8 hardware: sample period = (divcount + 1) * 10ns.
- * Min. value for divcount: 0x00 (10ns sample period, 100MHz samplerate).
- * Max. value for divcount: 0xfe (2550ns sample period, 392.15kHz samplerate).
- *
- * @param divcount The divcount value as needed by the hardware.
- *
- * @return The samplerate in Hz, or 0xffffffffffffffff upon errors.
- */
-static uint64_t divcount_to_samplerate(uint8_t divcount)
-{
- if (divcount == 0xff)
- return 0xffffffffffffffffULL;
-
- return SR_MHZ(100) / (divcount + 1);
-}
-
-static int format_match(const char *filename)
-{
- struct stat stat_buf;
- int ret;
-
- if (!filename) {
- sr_err("%s: filename was NULL", __func__);
- // return SR_ERR; /* FIXME */
- return FALSE;
- }
-
- if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
- sr_err("%s: input file '%s' does not exist",
- __func__, filename);
- // return SR_ERR; /* FIXME */
- return FALSE;
- }
-
- if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
- sr_err("%s: input file '%s' not a regular file",
- __func__, filename);
- // return SR_ERR; /* FIXME */
- return FALSE;
- }
-
- /* Only accept files of length 8MB + 5 bytes. */
- ret = stat(filename, &stat_buf);
- if (ret != 0) {
- sr_err("%s: Error getting file size of '%s'",
- __func__, filename);
- return FALSE;
- }
- if (stat_buf.st_size != (8 * 1024 * 1024 + 5)) {
- sr_dbg("%s: File size must be exactly 8388613 bytes ("
- "it actually is %d bytes in size), so this is not a "
- "ChronoVu LA8 file.", __func__, stat_buf.st_size);
- return FALSE;
- }
-
- /* TODO: Check for divcount != 0xff. */
-
- return TRUE;
-}
-
-static int init(struct sr_input *in, const char *filename)
-{
- struct sr_channel *ch;
- int num_channels, i;
- char name[SR_MAX_CHANNELNAME_LEN + 1];
- char *param;
-
- (void)filename;
-
- num_channels = DEFAULT_NUM_CHANNELS;
-
- if (in->param) {
- param = g_hash_table_lookup(in->param, "numchannels");
- if (param) {
- num_channels = strtoul(param, NULL, 10);
- if (num_channels < 1) {
- sr_err("%s: strtoul failed", __func__);
- return SR_ERR;
- }
- }
- }
-
- /* Create a virtual device. */
- in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
-
- for (i = 0; i < num_channels; i++) {
- snprintf(name, SR_MAX_CHANNELNAME_LEN, "%d", i);
- /* TODO: Check return value. */
- if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, name)))
- return SR_ERR;
- in->sdi->channels = g_slist_append(in->sdi->channels, ch);
- }
-
- return SR_OK;
-}
-
-static int loadfile(struct sr_input *in, const char *filename)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_meta meta;
- struct sr_datafeed_logic logic;
- struct sr_config *src;
- uint8_t buf[PACKET_SIZE], divcount;
- int i, fd, size, num_channels;
- uint64_t samplerate;
-
- /* TODO: Use glib functions! GIOChannel, g_fopen, etc. */
- if ((fd = open(filename, O_RDONLY)) == -1) {
- sr_err("%s: file open failed", __func__);
- return SR_ERR;
- }
-
- num_channels = g_slist_length(in->sdi->channels);
-
- /* Seek to the end of the file, and read the divcount byte. */
- divcount = 0x00; /* TODO: Don't hardcode! */
-
- /* Convert the divcount value to a samplerate. */
- samplerate = divcount_to_samplerate(divcount);
- if (samplerate == 0xffffffffffffffffULL) {
- close(fd); /* FIXME */
- return SR_ERR;
- }
- sr_dbg("%s: samplerate is %" PRIu64, __func__, samplerate);
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(in->sdi, LOG_PREFIX);
-
- /* Send metadata about the SR_DF_LOGIC packets to come. */
- packet.type = SR_DF_META;
- packet.payload = &meta;
- src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate));
- meta.config = g_slist_append(NULL, src);
- sr_session_send(in->sdi, &packet);
- sr_config_free(src);
-
- /* TODO: Handle trigger point. */
-
- /* Send data packets to the session bus. */
- sr_dbg("%s: sending SR_DF_LOGIC data packets", __func__);
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.unitsize = (num_channels + 7) / 8;
- logic.data = buf;
-
- /* Send 8MB of total data to the session bus in small chunks. */
- for (i = 0; i < NUM_PACKETS; i++) {
- /* TODO: Handle errors, handle incomplete reads. */
- size = read(fd, buf, PACKET_SIZE);
- logic.length = size;
- sr_session_send(in->sdi, &packet);
- }
- close(fd); /* FIXME */
-
- /* Send end packet to the session bus. */
- sr_dbg("%s: sending SR_DF_END", __func__);
- packet.type = SR_DF_END;
- packet.payload = NULL;
- sr_session_send(in->sdi, &packet);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_input_format input_chronovu_la8 = {
- .id = "chronovu-la8",
- .description = "ChronoVu LA8",
- .format_match = format_match,
- .init = init,
- .loadfile = loadfile,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Marc Schink <sigrok-dev@marcschink.de>
- *
- * 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 <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "input/csv"
-
-/*
- * The CSV input module has the following options:
- *
- * single-column: Specifies the column number which stores the sample data for
- * single column mode and enables single column mode. Multi
- * column mode is used if this parameter is omitted.
- *
- * numchannels: Specifies the number of channels to use. In multi column mode
- * the number of channels are the number of columns and in single
- * column mode the number of bits (LSB first) beginning at
- * 'first-channel'.
- *
- * delimiter: Specifies the delimiter for columns. Must be at least one
- * character. Comma is used as default delimiter.
- *
- * format: Specifies the format of the sample data in single column mode.
- * Available formats are: 'bin', 'hex' and 'oct'. The binary
- * format is used by default. This option has no effect in multi
- * column mode.
- *
- * comment: Specifies the prefix character(s) for comments. No prefix
- * characters are used by default which disables removing of
- * comments.
- *
- * samplerate: Samplerate which the sample data was captured with. Default
- * value is 0.
- *
- * first-channel: Column number of the first channel in multi column mode and
- * position of the bit for the first channel in single column mode.
- * Default value is 0.
- *
- * header: Determines if the first line should be treated as header
- * and used for channel names in multi column mode. Empty header
- * names will be replaced by the channel number. If enabled in
- * single column mode the first line will be skipped. Usage of
- * header is disabled by default.
- *
- * startline: Line number to start processing sample data. Must be greater
- * than 0. The default line number to start processing is 1.
- */
-
-/* Single column formats. */
-enum {
- FORMAT_BIN,
- FORMAT_HEX,
- FORMAT_OCT
-};
-
-struct context {
- /* Current selected samplerate. */
- uint64_t samplerate;
-
- /* Number of channels. */
- gsize num_channels;
-
- /* Column delimiter character(s). */
- GString *delimiter;
-
- /* Comment prefix character(s). */
- GString *comment;
-
- /* Determines if sample data is stored in multiple columns. */
- gboolean multi_column_mode;
-
- /* Column number of the sample data in single column mode. */
- gsize single_column;
-
- /*
- * Number of the first column to parse. Equivalent to the number of the
- * first channel in multi column mode and the single column number in
- * single column mode.
- */
- gsize first_column;
-
- /*
- * Column number of the first channel in multi column mode and position of
- * the bit for the first channel in single column mode.
- */
- gsize first_channel;
-
- /* Line number to start processing. */
- gsize start_line;
-
- /*
- * Determines if the first line should be treated as header and used for
- * channel names in multi column mode.
- */
- gboolean header;
-
- /* Format sample data is stored in single column mode. */
- int format;
-
- /* Size of the sample buffer. */
- gsize sample_buffer_size;
-
- /* Buffer to store sample data. */
- uint8_t *sample_buffer;
-
- GIOChannel *channel;
-
- /* Buffer for the current line. */
- GString *buffer;
-
- /* Current line number. */
- gsize line_number;
-};
-
-static int format_match(const char *filename)
-{
- if (!filename) {
- sr_err("%s: filename was NULL.", __func__);
- return FALSE;
- }
-
- if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
- sr_err("Input file '%s' does not exist.", filename);
- return FALSE;
- }
-
- if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
- sr_err("Input file '%s' not a regular file.", filename);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static void free_context(struct context *ctx)
-{
- if (!ctx)
- return;
-
- if (ctx->delimiter)
- g_string_free(ctx->delimiter, TRUE);
-
- if (ctx->comment)
- g_string_free(ctx->comment, TRUE);
-
- if (ctx->channel) {
- g_io_channel_shutdown(ctx->channel, FALSE, NULL);
- g_io_channel_unref(ctx->channel);
- }
-
- if (ctx->sample_buffer)
- g_free(ctx->sample_buffer);
-
- if (ctx->buffer)
- g_string_free(ctx->buffer, TRUE);
-
- g_free(ctx);
-}
-
-static void strip_comment(GString *string, const GString *prefix)
-{
- char *ptr;
-
- if (!prefix->len)
- return;
-
- if (!(ptr = strstr(string->str, prefix->str)))
- return;
-
- g_string_truncate(string, ptr - string->str);
-}
-
-static int parse_binstr(const char *str, struct context *ctx)
-{
- gsize i, j, length;
-
- length = strlen(str);
-
- if (!length) {
- sr_err("Column %zu in line %zu is empty.", ctx->single_column,
- ctx->line_number);
- return SR_ERR;
- }
-
- /* Clear buffer in order to set bits only. */
- memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
-
- i = ctx->first_channel;
-
- for (j = 0; i < length && j < ctx->num_channels; i++, j++) {
- if (str[length - i - 1] == '1') {
- ctx->sample_buffer[j / 8] |= (1 << (j % 8));
- } else if (str[length - i - 1] != '0') {
- sr_err("Invalid value '%s' in column %zu in line %zu.",
- str, ctx->single_column, ctx->line_number);
- return SR_ERR;
- }
- }
-
- return SR_OK;
-}
-
-static int parse_hexstr(const char *str, struct context *ctx)
-{
- gsize i, j, k, length;
- uint8_t value;
- char c;
-
- length = strlen(str);
-
- if (!length) {
- sr_err("Column %zu in line %zu is empty.", ctx->single_column,
- ctx->line_number);
- return SR_ERR;
- }
-
- /* Clear buffer in order to set bits only. */
- memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
-
- /* Calculate the position of the first hexadecimal digit. */
- i = ctx->first_channel / 4;
-
- for (j = 0; i < length && j < ctx->num_channels; i++) {
- c = str[length - i - 1];
-
- if (!g_ascii_isxdigit(c)) {
- sr_err("Invalid value '%s' in column %zu in line %zu.",
- str, ctx->single_column, ctx->line_number);
- return SR_ERR;
- }
-
- value = g_ascii_xdigit_value(c);
-
- k = (ctx->first_channel + j) % 4;
-
- for (; j < ctx->num_channels && k < 4; k++) {
- if (value & (1 << k))
- ctx->sample_buffer[j / 8] |= (1 << (j % 8));
-
- j++;
- }
- }
-
- return SR_OK;
-}
-
-static int parse_octstr(const char *str, struct context *ctx)
-{
- gsize i, j, k, length;
- uint8_t value;
- char c;
-
- length = strlen(str);
-
- if (!length) {
- sr_err("Column %zu in line %zu is empty.", ctx->single_column,
- ctx->line_number);
- return SR_ERR;
- }
-
- /* Clear buffer in order to set bits only. */
- memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
-
- /* Calculate the position of the first octal digit. */
- i = ctx->first_channel / 3;
-
- for (j = 0; i < length && j < ctx->num_channels; i++) {
- c = str[length - i - 1];
-
- if (c < '0' || c > '7') {
- sr_err("Invalid value '%s' in column %zu in line %zu.",
- str, ctx->single_column, ctx->line_number);
- return SR_ERR;
- }
-
- value = g_ascii_xdigit_value(c);
-
- k = (ctx->first_channel + j) % 3;
-
- for (; j < ctx->num_channels && k < 3; k++) {
- if (value & (1 << k))
- ctx->sample_buffer[j / 8] |= (1 << (j % 8));
-
- j++;
- }
- }
-
- return SR_OK;
-}
-
-static char **parse_line(const struct context *ctx, int max_columns)
-{
- const char *str, *remainder;
- GSList *list, *l;
- char **columns;
- char *column;
- gsize n, k;
-
- n = 0;
- k = 0;
- list = NULL;
-
- remainder = ctx->buffer->str;
- str = strstr(remainder, ctx->delimiter->str);
-
- while (str && max_columns) {
- if (n >= ctx->first_column) {
- column = g_strndup(remainder, str - remainder);
- list = g_slist_prepend(list, g_strstrip(column));
-
- max_columns--;
- k++;
- }
-
- remainder = str + ctx->delimiter->len;
- str = strstr(remainder, ctx->delimiter->str);
- n++;
- }
-
- if (ctx->buffer->len && max_columns && n >= ctx->first_column) {
- column = g_strdup(remainder);
- list = g_slist_prepend(list, g_strstrip(column));
- k++;
- }
-
- if (!(columns = g_try_new(char *, k + 1)))
- return NULL;
-
- columns[k--] = NULL;
-
- for (l = list; l; l = l->next)
- columns[k--] = l->data;
-
- g_slist_free(list);
-
- return columns;
-}
-
-static int parse_multi_columns(char **columns, struct context *ctx)
-{
- gsize i;
-
- /* Clear buffer in order to set bits only. */
- memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
-
- for (i = 0; i < ctx->num_channels; i++) {
- if (columns[i][0] == '1') {
- ctx->sample_buffer[i / 8] |= (1 << (i % 8));
- } else if (!strlen(columns[i])) {
- sr_err("Column %zu in line %zu is empty.",
- ctx->first_channel + i, ctx->line_number);
- return SR_ERR;
- } else if (columns[i][0] != '0') {
- sr_err("Invalid value '%s' in column %zu in line %zu.",
- columns[i], ctx->first_channel + i,
- ctx->line_number);
- return SR_ERR;
- }
- }
-
- return SR_OK;
-}
-
-static int parse_single_column(const char *column, struct context *ctx)
-{
- int res;
-
- res = SR_ERR;
-
- switch(ctx->format) {
- case FORMAT_BIN:
- res = parse_binstr(column, ctx);
- break;
- case FORMAT_HEX:
- res = parse_hexstr(column, ctx);
- break;
- case FORMAT_OCT:
- res = parse_octstr(column, ctx);
- break;
- }
-
- return res;
-}
-
-static int send_samples(const struct sr_dev_inst *sdi, uint8_t *buffer,
- gsize buffer_size, gsize count)
-{
- int res;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- gsize i;
-
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.unitsize = buffer_size;
- logic.length = buffer_size;
- logic.data = buffer;
-
- for (i = 0; i < count; i++) {
- if ((res = sr_session_send(sdi, &packet)) != SR_OK)
- return res;
- }
-
- return SR_OK;
-}
-
-static int init(struct sr_input *in, const char *filename)
-{
- int res;
- struct context *ctx;
- const char *param;
- GIOStatus status;
- gsize i, term_pos;
- char channel_name[SR_MAX_CHANNELNAME_LEN + 1];
- struct sr_channel *ch;
- char **columns;
- gsize num_columns;
- char *ptr;
-
- if (!(ctx = g_try_malloc0(sizeof(struct context)))) {
- sr_err("Context malloc failed.");
- return SR_ERR_MALLOC;
- }
-
- /* Create a virtual device. */
- in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
- in->internal = ctx;
-
- /* Set default samplerate. */
- ctx->samplerate = 0;
-
- /*
- * Enable auto-detection of the number of channels in multi column mode
- * and enforce the specification of the number of channels in single
- * column mode.
- */
- ctx->num_channels = 0;
-
- /* Set default delimiter. */
- if (!(ctx->delimiter = g_string_new(","))) {
- sr_err("Delimiter malloc failed.");
- free_context(ctx);
- return SR_ERR_MALLOC;
- }
-
- /*
- * Set default comment prefix. Note that an empty comment prefix
- * disables removing of comments.
- */
- if (!(ctx->comment = g_string_new(""))) {
- sr_err("Comment malloc failed.");
- free_context(ctx);
- return SR_ERR_MALLOC;
- }
-
- /* Enable multi column mode by default. */
- ctx->multi_column_mode = TRUE;
-
- /* Use first column as default single column number. */
- ctx->single_column = 0;
-
- /*
- * In multi column mode start parsing sample data at the first column
- * and in single column mode at the first bit.
- */
- ctx->first_channel = 0;
-
- /* Start at the beginning of the file. */
- ctx->start_line = 1;
-
- /* Disable the usage of the first line as header by default. */
- ctx->header = FALSE;
-
- /* Set default format for single column mode. */
- ctx->format = FORMAT_BIN;
-
- if (!(ctx->buffer = g_string_new(""))) {
- sr_err("Line buffer malloc failed.");
- free_context(ctx);
- return SR_ERR_MALLOC;
- }
-
- if (in->param) {
- if ((param = g_hash_table_lookup(in->param, "samplerate"))) {
- res = sr_parse_sizestring(param, &ctx->samplerate);
-
- if (res != SR_OK) {
- sr_err("Invalid samplerate: %s.", param);
- free_context(ctx);
- return SR_ERR_ARG;
- }
- }
-
- if ((param = g_hash_table_lookup(in->param, "numchannels")))
- ctx->num_channels = g_ascii_strtoull(param, NULL, 10);
-
- if ((param = g_hash_table_lookup(in->param, "delimiter"))) {
- if (!strlen(param)) {
- sr_err("Delimiter must be at least one character.");
- free_context(ctx);
- return SR_ERR_ARG;
- }
-
- if (!g_ascii_strcasecmp(param, "\\t"))
- g_string_assign(ctx->delimiter, "\t");
- else
- g_string_assign(ctx->delimiter, param);
- }
-
- if ((param = g_hash_table_lookup(in->param, "comment")))
- g_string_assign(ctx->comment, param);
-
- if ((param = g_hash_table_lookup(in->param, "single-column"))) {
- ctx->single_column = g_ascii_strtoull(param, &ptr, 10);
- ctx->multi_column_mode = FALSE;
-
- if (param == ptr) {
- sr_err("Invalid single-colum number: %s.",
- param);
- free_context(ctx);
- return SR_ERR_ARG;
- }
- }
-
- if ((param = g_hash_table_lookup(in->param, "first-channel")))
- ctx->first_channel = g_ascii_strtoull(param, NULL, 10);
-
- if ((param = g_hash_table_lookup(in->param, "startline"))) {
- ctx->start_line = g_ascii_strtoull(param, NULL, 10);
-
- if (ctx->start_line < 1) {
- sr_err("Invalid start line: %s.", param);
- free_context(ctx);
- return SR_ERR_ARG;
- }
- }
-
- if ((param = g_hash_table_lookup(in->param, "header")))
- ctx->header = sr_parse_boolstring(param);
-
- if ((param = g_hash_table_lookup(in->param, "format"))) {
- if (!g_ascii_strncasecmp(param, "bin", 3)) {
- ctx->format = FORMAT_BIN;
- } else if (!g_ascii_strncasecmp(param, "hex", 3)) {
- ctx->format = FORMAT_HEX;
- } else if (!g_ascii_strncasecmp(param, "oct", 3)) {
- ctx->format = FORMAT_OCT;
- } else {
- sr_err("Invalid format: %s.", param);
- free_context(ctx);
- return SR_ERR;
- }
- }
- }
-
- if (ctx->multi_column_mode)
- ctx->first_column = ctx->first_channel;
- else
- ctx->first_column = ctx->single_column;
-
- if (!ctx->multi_column_mode && !ctx->num_channels) {
- sr_err("Number of channels needs to be specified in single column mode.");
- free_context(ctx);
- return SR_ERR;
- }
-
- if (!(ctx->channel = g_io_channel_new_file(filename, "r", NULL))) {
- sr_err("Input file '%s' could not be opened.", filename);
- free_context(ctx);
- return SR_ERR;
- }
-
- while (TRUE) {
- ctx->line_number++;
- status = g_io_channel_read_line_string(ctx->channel,
- ctx->buffer, &term_pos, NULL);
-
- if (status == G_IO_STATUS_EOF) {
- sr_err("Input file is empty.");
- free_context(ctx);
- return SR_ERR;
- }
-
- if (status != G_IO_STATUS_NORMAL) {
- sr_err("Error while reading line %zu.",
- ctx->line_number);
- free_context(ctx);
- return SR_ERR;
- }
-
- if (ctx->start_line > ctx->line_number) {
- sr_spew("Line %zu skipped.", ctx->line_number);
- continue;
- }
-
- /* Remove line termination character(s). */
- g_string_truncate(ctx->buffer, term_pos);
-
- if (!ctx->buffer->len) {
- sr_spew("Blank line %zu skipped.", ctx->line_number);
- continue;
- }
-
- /* Remove trailing comment. */
- strip_comment(ctx->buffer, ctx->comment);
-
- if (ctx->buffer->len)
- break;
-
- sr_spew("Comment-only line %zu skipped.", ctx->line_number);
- }
-
- /*
- * In order to determine the number of columns parse the current line
- * without limiting the number of columns.
- */
- if (!(columns = parse_line(ctx, -1))) {
- sr_err("Error while parsing line %zu.", ctx->line_number);
- free_context(ctx);
- return SR_ERR;
- }
-
- num_columns = g_strv_length(columns);
-
- /* Ensure that the first column is not out of bounds. */
- if (!num_columns) {
- sr_err("Column %zu in line %zu is out of bounds.",
- ctx->first_column, ctx->line_number);
- g_strfreev(columns);
- free_context(ctx);
- return SR_ERR;
- }
-
- if (ctx->multi_column_mode) {
- /*
- * Detect the number of channels in multi column mode
- * automatically if not specified.
- */
- if (!ctx->num_channels) {
- ctx->num_channels = num_columns;
- sr_info("Number of auto-detected channels: %zu.",
- ctx->num_channels);
- }
-
- /*
- * Ensure that the number of channels does not exceed the number
- * of columns in multi column mode.
- */
- if (num_columns < ctx->num_channels) {
- sr_err("Not enough columns for desired number of channels in line %zu.",
- ctx->line_number);
- g_strfreev(columns);
- free_context(ctx);
- return SR_ERR;
- }
- }
-
- for (i = 0; i < ctx->num_channels; i++) {
- if (ctx->header && ctx->multi_column_mode && strlen(columns[i]))
- snprintf(channel_name, sizeof(channel_name), "%s",
- columns[i]);
- else
- snprintf(channel_name, sizeof(channel_name), "%zu", i);
-
- ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, channel_name);
-
- if (!ch) {
- sr_err("Channel creation failed.");
- free_context(ctx);
- g_strfreev(columns);
- return SR_ERR;
- }
-
- in->sdi->channels = g_slist_append(in->sdi->channels, ch);
- }
-
- g_strfreev(columns);
-
- /*
- * Calculate the minimum buffer size to store the sample data of the
- * channels.
- */
- ctx->sample_buffer_size = (ctx->num_channels + 7) >> 3;
-
- if (!(ctx->sample_buffer = g_try_malloc(ctx->sample_buffer_size))) {
- sr_err("Sample buffer malloc failed.");
- free_context(ctx);
- return SR_ERR_MALLOC;
- }
-
- return SR_OK;
-}
-
-static int loadfile(struct sr_input *in, const char *filename)
-{
- int res;
- struct context *ctx;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_meta meta;
- struct sr_config *cfg;
- GIOStatus status;
- gboolean read_new_line;
- gsize term_pos;
- char **columns;
- gsize num_columns;
- int max_columns;
-
- (void)filename;
-
- ctx = in->internal;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(in->sdi, LOG_PREFIX);
-
- if (ctx->samplerate) {
- packet.type = SR_DF_META;
- packet.payload = &meta;
- cfg = sr_config_new(SR_CONF_SAMPLERATE,
- g_variant_new_uint64(ctx->samplerate));
- meta.config = g_slist_append(NULL, cfg);
- sr_session_send(in->sdi, &packet);
- sr_config_free(cfg);
- }
-
- read_new_line = FALSE;
-
- /* Limit the number of columns to parse. */
- if (ctx->multi_column_mode)
- max_columns = ctx->num_channels;
- else
- max_columns = 1;
-
- while (TRUE) {
- /*
- * Skip reading a new line for the first time if the last read
- * line was not a header because the sample data is not parsed
- * yet.
- */
- if (read_new_line || ctx->header) {
- ctx->line_number++;
- status = g_io_channel_read_line_string(ctx->channel,
- ctx->buffer, &term_pos, NULL);
-
- if (status == G_IO_STATUS_EOF)
- break;
-
- if (status != G_IO_STATUS_NORMAL) {
- sr_err("Error while reading line %zu.",
- ctx->line_number);
- free_context(ctx);
- return SR_ERR;
- }
-
- /* Remove line termination character(s). */
- g_string_truncate(ctx->buffer, term_pos);
- }
-
- read_new_line = TRUE;
-
- if (!ctx->buffer->len) {
- sr_spew("Blank line %zu skipped.", ctx->line_number);
- continue;
- }
-
- /* Remove trailing comment. */
- strip_comment(ctx->buffer, ctx->comment);
-
- if (!ctx->buffer->len) {
- sr_spew("Comment-only line %zu skipped.",
- ctx->line_number);
- continue;
- }
-
- if (!(columns = parse_line(ctx, max_columns))) {
- sr_err("Error while parsing line %zu.",
- ctx->line_number);
- free_context(ctx);
- return SR_ERR;
- }
-
- num_columns = g_strv_length(columns);
-
- /* Ensure that the first column is not out of bounds. */
- if (!num_columns) {
- sr_err("Column %zu in line %zu is out of bounds.",
- ctx->first_column, ctx->line_number);
- g_strfreev(columns);
- free_context(ctx);
- return SR_ERR;
- }
-
- /*
- * Ensure that the number of channels does not exceed the number
- * of columns in multi column mode.
- */
- if (ctx->multi_column_mode && num_columns < ctx->num_channels) {
- sr_err("Not enough columns for desired number of channels in line %zu.",
- ctx->line_number);
- g_strfreev(columns);
- free_context(ctx);
- return SR_ERR;
- }
-
- if (ctx->multi_column_mode)
- res = parse_multi_columns(columns, ctx);
- else
- res = parse_single_column(columns[0], ctx);
-
- if (res != SR_OK) {
- g_strfreev(columns);
- free_context(ctx);
- return SR_ERR;
- }
-
- g_strfreev(columns);
-
- /*
- * TODO: Parse sample numbers / timestamps and use it for
- * decompression.
- */
-
- /* Send sample data to the session bus. */
- res = send_samples(in->sdi, ctx->sample_buffer,
- ctx->sample_buffer_size, 1);
-
- if (res != SR_OK) {
- sr_err("Sending samples failed.");
- free_context(ctx);
- return SR_ERR;
- }
- }
-
- /* Send end packet to the session bus. */
- packet.type = SR_DF_END;
- sr_session_send(in->sdi, &packet);
-
- free_context(ctx);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_input_format input_csv = {
- .id = "csv",
- .description = "Comma-separated values (CSV)",
- .format_match = format_match,
- .init = init,
- .loadfile = loadfile,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
- *
- * 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.h"
-#include "libsigrok-internal.h"
-
-/**
- * @file
- *
- * Input file/data format handling.
- */
-
-/**
- * @defgroup grp_input Input formats
- *
- * Input file/data format handling.
- *
- * libsigrok can process acquisition data in several different ways.
- * Aside from acquiring data from a hardware device, it can also take it from
- * a file in various formats (binary, CSV, VCD, and so on).
- *
- * Like everything in libsigrok that handles data, processing is done in a
- * streaming manner -- input should be supplied to libsigrok a chunk at a time.
- * This way anything that processes data can do so in real time, without the
- * user having to wait for the whole thing to be finished.
- *
- * Every input module is "pluggable", meaning it's handled as being separate
- * from the main libsigrok, but linked in to it statically. To keep things
- * modular and separate like this, functions within an input module should be
- * declared static, with only the respective 'struct sr_input_format' being
- * exported for use into the wider libsigrok namespace.
- *
- * @{
- */
-
-/** @cond PRIVATE */
-extern SR_PRIV struct sr_input_format input_chronovu_la8;
-extern SR_PRIV struct sr_input_format input_csv;
-extern SR_PRIV struct sr_input_format input_binary;
-extern SR_PRIV struct sr_input_format input_vcd;
-extern SR_PRIV struct sr_input_format input_wav;
-/* @endcond */
-
-static struct sr_input_format *input_module_list[] = {
- &input_vcd,
- &input_chronovu_la8,
- &input_wav,
- &input_csv,
- /* This one has to be last, because it will take any input. */
- &input_binary,
- NULL,
-};
-
-/** @since 0.1.0 */
-SR_API struct sr_input_format **sr_input_list(void)
-{
- return input_module_list;
-}
-
-/** @} */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Petteri Aimonen <jpa@sr.mail.kapsi.fi>
- *
- * 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/>.
- */
-
-/* The VCD input module has the following options:
- *
- * numchannels: Maximum number of channels to use. The channels are
- * detected in the same order as they are listed
- * in the $var sections of the VCD file.
- *
- * skip: Allows skipping until given timestamp in the file.
- * This can speed up analyzing of long captures.
- *
- * Value < 0: Skip until first timestamp listed in
- * the file. (default)
- *
- * Value = 0: Do not skip, instead generate samples
- * beginning from timestamp 0.
- *
- * Value > 0: Start at the given timestamp.
- *
- * downsample: Divide the samplerate by the given factor.
- * This can speed up analyzing of long captures.
- *
- * compress: Compress idle periods longer than this value.
- * This can speed up analyzing of long captures.
- * Default 0 = don't compress.
- *
- * Based on Verilog standard IEEE Std 1364-2001 Version C
- *
- * Supported features:
- * - $var with 'wire' and 'reg' types of scalar variables
- * - $timescale definition for samplerate
- * - multiple character variable identifiers
- *
- * Most important unsupported features:
- * - vector variables (bit vectors etc.)
- * - analog, integer and real number variables
- * - $dumpvars initial value declaration
- * - $scope namespaces
- * - more than 64 channels
- */
-
-#include <stdlib.h>
-#include <glib.h>
-#include <stdio.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "input/vcd"
-
-#define DEFAULT_NUM_CHANNELS 8
-#define CHUNKSIZE 1024
-
-struct context {
- uint64_t samplerate;
- int maxchannels;
- int channelcount;
- int downsample;
- unsigned compress;
- int64_t skip;
- GSList *channels;
-};
-
-struct vcd_channel {
- gchar *name;
- gchar *identifier;
-};
-
-
-/* Read until specific type of character occurs in file.
- * Skip input if dest is NULL.
- * Modes:
- * 'W' read until whitespace
- * 'N' read until non-whitespace, and ungetc() the character
- * '$' read until $end
- */
-static gboolean read_until(FILE *file, GString *dest, char mode)
-{
- int c;
- char prev[4] = "";
-
- for(;;) {
- c = fgetc(file);
-
- if (c == EOF) {
- if (mode == '$')
- sr_err("Unexpected EOF.");
- return FALSE;
- }
-
- if (mode == 'W' && g_ascii_isspace(c))
- return TRUE;
-
- if (mode == 'N' && !g_ascii_isspace(c)) {
- ungetc(c, file);
- return TRUE;
- }
-
- if (mode == '$') {
- prev[0] = prev[1]; prev[1] = prev[2]; prev[2] = prev[3]; prev[3] = c;
- if (prev[0] == '$' && prev[1] == 'e' && prev[2] == 'n' && prev[3] == 'd') {
- if (dest != NULL)
- g_string_truncate(dest, dest->len - 3);
-
- return TRUE;
- }
- }
-
- if (dest != NULL)
- g_string_append_c(dest, c);
- }
-}
-
-/*
- * Reads a single VCD section from input file and parses it to structure.
- * e.g. $timescale 1ps $end => "timescale" "1ps"
- */
-static gboolean parse_section(FILE *file, gchar **name, gchar **contents)
-{
- gboolean status;
- GString *sname, *scontents;
-
- /* Skip any initial white-space */
- if (!read_until(file, NULL, 'N')) return FALSE;
-
- /* Section tag should start with $. */
- if (fgetc(file) != '$') {
- sr_err("Expected $ at beginning of section.");
- return FALSE;
- }
-
- /* Read the section tag */
- sname = g_string_sized_new(32);
- status = read_until(file, sname, 'W');
-
- /* Skip whitespace before content */
- status = status && read_until(file, NULL, 'N');
-
- /* Read the content */
- scontents = g_string_sized_new(128);
- status = status && read_until(file, scontents, '$');
- g_strchomp(scontents->str);
-
- /* Release strings if status is FALSE, return them if status is TRUE */
- *name = g_string_free(sname, !status);
- *contents = g_string_free(scontents, !status);
- return status;
-}
-
-static void free_channel(void *data)
-{
- struct vcd_channel *vcd_ch = data;
- g_free(vcd_ch->name);
- g_free(vcd_ch->identifier);
- g_free(vcd_ch);
-}
-
-static void release_context(struct context *ctx)
-{
- g_slist_free_full(ctx->channels, free_channel);
- g_free(ctx);
-}
-
-/* Remove empty parts from an array returned by g_strsplit. */
-static void remove_empty_parts(gchar **parts)
-{
- gchar **src = parts;
- gchar **dest = parts;
- while (*src != NULL) {
- if (**src != '\0')
- *dest++ = *src;
- src++;
- }
-
- *dest = NULL;
-}
-
-/*
- * Parse VCD header to get values for context structure.
- * The context structure should be zeroed before calling this.
- */
-static gboolean parse_header(FILE *file, struct context *ctx)
-{
- uint64_t p, q;
- gchar *name = NULL, *contents = NULL;
- gboolean status = FALSE;
- struct vcd_channel *vcd_ch;
-
- while (parse_section(file, &name, &contents)) {
- sr_dbg("Section '%s', contents '%s'.", name, contents);
-
- if (g_strcmp0(name, "enddefinitions") == 0) {
- status = TRUE;
- break;
- } else if (g_strcmp0(name, "timescale") == 0) {
- /*
- * The standard allows for values 1, 10 or 100
- * and units s, ms, us, ns, ps and fs.
- * */
- if (sr_parse_period(contents, &p, &q) == SR_OK) {
- ctx->samplerate = q / p;
- if (q % p != 0) {
- /* Does not happen unless time value is non-standard */
- sr_warn("Inexact rounding of samplerate, %" PRIu64 " / %" PRIu64 " to %" PRIu64 " Hz.",
- q, p, ctx->samplerate);
- }
-
- sr_dbg("Samplerate: %" PRIu64, ctx->samplerate);
- } else {
- sr_err("Parsing timescale failed.");
- }
- } else if (g_strcmp0(name, "var") == 0) {
- /* Format: $var type size identifier reference $end */
- gchar **parts = g_strsplit_set(contents, " \r\n\t", 0);
- remove_empty_parts(parts);
-
- if (g_strv_length(parts) != 4)
- sr_warn("$var section should have 4 items");
- else if (g_strcmp0(parts[0], "reg") != 0 && g_strcmp0(parts[0], "wire") != 0)
- sr_info("Unsupported signal type: '%s'", parts[0]);
- else if (strtol(parts[1], NULL, 10) != 1)
- sr_info("Unsupported signal size: '%s'", parts[1]);
- else if (ctx->channelcount >= ctx->maxchannels)
- sr_warn("Skipping '%s' because only %d channels requested.", parts[3], ctx->maxchannels);
- else {
- sr_info("Channel %d is '%s' identified by '%s'.", ctx->channelcount, parts[3], parts[2]);
- vcd_ch = g_malloc(sizeof(struct vcd_channel));
- vcd_ch->identifier = g_strdup(parts[2]);
- vcd_ch->name = g_strdup(parts[3]);
- ctx->channels = g_slist_append(ctx->channels, vcd_ch);
- ctx->channelcount++;
- }
-
- g_strfreev(parts);
- }
-
- g_free(name); name = NULL;
- g_free(contents); contents = NULL;
- }
-
- g_free(name);
- g_free(contents);
-
- return status;
-}
-
-static int format_match(const char *filename)
-{
- FILE *file;
- gchar *name = NULL, *contents = NULL;
- gboolean status;
-
- file = fopen(filename, "r");
- if (file == NULL)
- return FALSE;
-
- /*
- * If we can parse the first section correctly,
- * then it is assumed to be a VCD file.
- */
- status = parse_section(file, &name, &contents);
- status = status && (*name != '\0');
-
- g_free(name);
- g_free(contents);
- fclose(file);
-
- return status;
-}
-
-static int init(struct sr_input *in, const char *filename)
-{
- struct sr_channel *ch;
- int num_channels, i;
- char name[SR_MAX_CHANNELNAME_LEN + 1];
- char *param;
- struct context *ctx;
-
- (void)filename;
-
- if (!(ctx = g_try_malloc0(sizeof(*ctx)))) {
- sr_err("Input format context malloc failed.");
- return SR_ERR_MALLOC;
- }
-
- num_channels = DEFAULT_NUM_CHANNELS;
- ctx->samplerate = 0;
- ctx->downsample = 1;
- ctx->skip = -1;
-
- if (in->param) {
- param = g_hash_table_lookup(in->param, "numchannels");
- if (param) {
- num_channels = strtoul(param, NULL, 10);
- if (num_channels < 1) {
- release_context(ctx);
- return SR_ERR;
- } else if (num_channels > 64) {
- sr_err("No more than 64 channels supported.");
- return SR_ERR;
- }
- }
-
- param = g_hash_table_lookup(in->param, "downsample");
- if (param) {
- ctx->downsample = strtoul(param, NULL, 10);
- if (ctx->downsample < 1)
- ctx->downsample = 1;
- }
-
- param = g_hash_table_lookup(in->param, "compress");
- if (param)
- ctx->compress = strtoul(param, NULL, 10);
-
- param = g_hash_table_lookup(in->param, "skip");
- if (param)
- ctx->skip = strtoul(param, NULL, 10) / ctx->downsample;
- }
-
- /* Maximum number of channels to parse from the VCD */
- ctx->maxchannels = num_channels;
-
- /* Create a virtual device. */
- in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
- in->internal = ctx;
-
- for (i = 0; i < num_channels; i++) {
- snprintf(name, SR_MAX_CHANNELNAME_LEN, "%d", i);
-
- if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, name))) {
- release_context(ctx);
- return SR_ERR;
- }
-
- in->sdi->channels = g_slist_append(in->sdi->channels, ch);
- }
-
- return SR_OK;
-}
-
-/* Send N samples of the given value. */
-static void send_samples(const struct sr_dev_inst *sdi, uint64_t sample, uint64_t count)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- uint64_t buffer[CHUNKSIZE];
- uint64_t i;
- unsigned chunksize = CHUNKSIZE;
-
- if (count < chunksize)
- chunksize = count;
-
- for (i = 0; i < chunksize; i++)
- buffer[i] = sample;
-
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.unitsize = sizeof(uint64_t);
- logic.data = buffer;
-
- while (count) {
- if (count < chunksize)
- chunksize = count;
-
- logic.length = sizeof(uint64_t) * chunksize;
-
- sr_session_send(sdi, &packet);
- count -= chunksize;
- }
-}
-
-/* Parse the data section of VCD */
-static void parse_contents(FILE *file, const struct sr_dev_inst *sdi, struct context *ctx)
-{
- GString *token = g_string_sized_new(32);
-
- uint64_t prev_timestamp = 0;
- uint64_t prev_values = 0;
-
- /* Read one space-delimited token at a time. */
- while (read_until(file, NULL, 'N') && read_until(file, token, 'W')) {
- if (token->str[0] == '#' && g_ascii_isdigit(token->str[1])) {
- /* Numeric value beginning with # is a new timestamp value */
- uint64_t timestamp;
- timestamp = strtoull(token->str + 1, NULL, 10);
-
- if (ctx->downsample > 1)
- timestamp /= ctx->downsample;
-
- /*
- * Skip < 0 => skip until first timestamp.
- * Skip = 0 => don't skip
- * Skip > 0 => skip until timestamp >= skip.
- */
- if (ctx->skip < 0) {
- ctx->skip = timestamp;
- prev_timestamp = timestamp;
- } else if (ctx->skip > 0 && timestamp < (uint64_t)ctx->skip) {
- prev_timestamp = ctx->skip;
- }
- else if (timestamp == prev_timestamp) {
- /* Ignore repeated timestamps (e.g. sigrok outputs these) */
- }
- else {
- if (ctx->compress != 0 && timestamp - prev_timestamp > ctx->compress)
- {
- /* Compress long idle periods */
- prev_timestamp = timestamp - ctx->compress;
- }
-
- sr_dbg("New timestamp: %" PRIu64, timestamp);
-
- /* Generate samples from prev_timestamp up to timestamp - 1. */
- send_samples(sdi, prev_values, timestamp - prev_timestamp);
- prev_timestamp = timestamp;
- }
- } else if (token->str[0] == '$' && token->len > 1) {
- /* This is probably a $dumpvars, $comment or similar.
- * $dump* contain useful data, but other tags will be skipped until $end. */
- if (g_strcmp0(token->str, "$dumpvars") == 0
- || g_strcmp0(token->str, "$dumpon") == 0
- || g_strcmp0(token->str, "$dumpoff") == 0
- || g_strcmp0(token->str, "$end") == 0) {
- /* Ignore, parse contents as normally. */
- } else {
- /* Skip until $end */
- read_until(file, NULL, '$');
- }
- }
- else if (strchr("bBrR", token->str[0]) != NULL) {
- /* A vector value. Skip it and also the following identifier. */
- read_until(file, NULL, 'N');
- read_until(file, NULL, 'W');
- } else if (strchr("01xXzZ", token->str[0]) != NULL) {
- /* A new 1-bit sample value */
- int i, bit;
- GSList *l;
- struct vcd_channel *vcd_ch;
-
- bit = (token->str[0] == '1');
-
- g_string_erase(token, 0, 1);
- if (token->len == 0) {
- /* There was a space between value and identifier.
- * Read in the rest.
- */
- read_until(file, NULL, 'N');
- read_until(file, token, 'W');
- }
-
- for (i = 0, l = ctx->channels; i < ctx->channelcount && l; i++, l = l->next) {
- vcd_ch = l->data;
-
- if (g_strcmp0(token->str, vcd_ch->identifier) == 0) {
- /* Found our channel */
- if (bit)
- prev_values |= (uint64_t)1 << i;
- else
- prev_values &= ~((uint64_t)1 << i);
-
- break;
- }
- }
-
- if (i == ctx->channelcount)
- sr_dbg("Did not find channel for identifier '%s'.", token->str);
- } else {
- sr_warn("Skipping unknown token '%s'.", token->str);
- }
-
- g_string_truncate(token, 0);
- }
-
- g_string_free(token, TRUE);
-}
-
-static int loadfile(struct sr_input *in, const char *filename)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_meta meta;
- struct sr_config *src;
- FILE *file;
- struct context *ctx;
- uint64_t samplerate;
-
- ctx = in->internal;
-
- if ((file = fopen(filename, "r")) == NULL)
- return SR_ERR;
-
- if (!parse_header(file, ctx)) {
- sr_err("VCD parsing failed");
- fclose(file);
- return SR_ERR;
- }
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(in->sdi, LOG_PREFIX);
-
- /* Send metadata about the SR_DF_LOGIC packets to come. */
- packet.type = SR_DF_META;
- packet.payload = &meta;
- samplerate = ctx->samplerate / ctx->downsample;
- src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate));
- meta.config = g_slist_append(NULL, src);
- sr_session_send(in->sdi, &packet);
- sr_config_free(src);
-
- /* Parse the contents of the VCD file */
- parse_contents(file, in->sdi, ctx);
-
- /* Send end packet to the session bus. */
- packet.type = SR_DF_END;
- sr_session_send(in->sdi, &packet);
-
- fclose(file);
- release_context(ctx);
- in->internal = NULL;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_input_format input_vcd = {
- .id = "vcd",
- .description = "Value Change Dump",
- .format_match = format_match,
- .init = init,
- .loadfile = loadfile,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "input/wav"
-
-#define CHUNK_SIZE 4096
-
-struct context {
- uint64_t samplerate;
- int samplesize;
- int num_channels;
-};
-
-static int get_wav_header(const char *filename, char *buf)
-{
- struct stat st;
- int fd, l;
-
- l = strlen(filename);
- if (l <= 4 || strcasecmp(filename + l - 4, ".wav"))
- return SR_ERR;
-
- if (stat(filename, &st) == -1)
- return SR_ERR;
- if (st.st_size <= 45)
- /* Minimum size of header + 1 8-bit mono PCM sample. */
- return SR_ERR;
-
- if ((fd = open(filename, O_RDONLY)) == -1)
- return SR_ERR;
-
- l = read(fd, buf, 40);
- close(fd);
- if (l != 40)
- return SR_ERR;
-
- return SR_OK;
-}
-
-static int format_match(const char *filename)
-{
- char buf[40];
-
- if (get_wav_header(filename, buf) != SR_OK)
- return FALSE;
-
- if (strncmp(buf, "RIFF", 4))
- return FALSE;
- if (strncmp(buf + 8, "WAVE", 4))
- return FALSE;
- if (strncmp(buf + 12, "fmt ", 4))
- return FALSE;
- if (GUINT16_FROM_LE(*(uint16_t *)(buf + 20)) != 1)
- /* Not PCM. */
- return FALSE;
- if (strncmp(buf + 36, "data", 4))
- return FALSE;
-
- return TRUE;
-}
-
-static int init(struct sr_input *in, const char *filename)
-{
- struct sr_channel *ch;
- struct context *ctx;
- char buf[40], channelname[8];
- int i;
-
- if (get_wav_header(filename, buf) != SR_OK)
- return SR_ERR;
-
- if (!(ctx = g_try_malloc0(sizeof(struct context))))
- return SR_ERR_MALLOC;
-
- /* Create a virtual device. */
- in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
- in->sdi->priv = ctx;
-
- ctx->samplerate = GUINT32_FROM_LE(*(uint32_t *)(buf + 24));
- ctx->samplesize = GUINT16_FROM_LE(*(uint16_t *)(buf + 34)) / 8;
- if (ctx->samplesize != 1 && ctx->samplesize != 2 && ctx->samplesize != 4) {
- sr_err("only 8, 16 or 32 bits per sample supported.");
- return SR_ERR;
- }
-
- if ((ctx->num_channels = GUINT16_FROM_LE(*(uint16_t *)(buf + 22))) > 20) {
- sr_err("%d channels seems crazy.", ctx->num_channels);
- return SR_ERR;
- }
-
- for (i = 0; i < ctx->num_channels; i++) {
- snprintf(channelname, 8, "CH%d", i + 1);
- if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, channelname)))
- return SR_ERR;
- in->sdi->channels = g_slist_append(in->sdi->channels, ch);
- }
-
- return SR_OK;
-}
-
-static int loadfile(struct sr_input *in, const char *filename)
-{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_meta meta;
- struct sr_datafeed_analog analog;
- struct sr_config *src;
- struct context *ctx;
- float fdata[CHUNK_SIZE];
- uint64_t sample;
- int num_samples, chunk_samples, s, c, fd, l;
- char buf[CHUNK_SIZE];
-
- ctx = in->sdi->priv;
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(in->sdi, LOG_PREFIX);
-
- packet.type = SR_DF_META;
- packet.payload = &meta;
- src = sr_config_new(SR_CONF_SAMPLERATE,
- g_variant_new_uint64(ctx->samplerate));
- meta.config = g_slist_append(NULL, src);
- sr_session_send(in->sdi, &packet);
- sr_config_free(src);
-
- if ((fd = open(filename, O_RDONLY)) == -1)
- return SR_ERR;
-
- lseek(fd, 40, SEEK_SET);
- l = read(fd, buf, 4);
- num_samples = GUINT32_FROM_LE((uint32_t)*(buf));
- num_samples /= ctx->samplesize / ctx->num_channels;
- while (TRUE) {
- if ((l = read(fd, buf, CHUNK_SIZE)) < 1)
- break;
- chunk_samples = l / ctx->samplesize / ctx->num_channels;
- for (s = 0; s < chunk_samples; s++) {
- for (c = 0; c < ctx->num_channels; c++) {
- sample = 0;
- memcpy(&sample, buf + s * ctx->samplesize + c, ctx->samplesize);
- switch (ctx->samplesize) {
- case 1:
- /* 8-bit PCM samples are unsigned. */
- fdata[s + c] = (uint8_t)sample / 255.0;
- break;
- case 2:
- fdata[s + c] = GINT16_FROM_LE(sample) / 32767.0;
- break;
- case 4:
- fdata[s + c] = GINT32_FROM_LE(sample) / 65535.0;
- break;
- }
- }
- }
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- analog.channels = in->sdi->channels;
- analog.num_samples = chunk_samples;
- analog.mq = 0;
- analog.unit = 0;
- analog.data = fdata;
- sr_session_send(in->sdi, &packet);
- }
-
- close(fd);
- packet.type = SR_DF_END;
- sr_session_send(in->sdi, &packet);
-
- return SR_OK;
-}
-
-
-SR_PRIV struct sr_input_format input_wav = {
- .id = "wav",
- .description = "WAV file",
- .format_match = format_match,
- .init = init,
- .loadfile = loadfile,
-};
-
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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/>.
- */
-
-/** @file
- * @internal
- */
-
-#ifndef LIBSIGROK_LIBSIGROK_INTERNAL_H
-#define LIBSIGROK_LIBSIGROK_INTERNAL_H
-
-#include <stdarg.h>
-#include <glib.h>
-#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
-#ifdef HAVE_LIBUSB_1_0
-#include <libusb.h>
-#endif
-#ifdef HAVE_LIBSERIALPORT
-#include <libserialport.h>
-#endif
-
-/**
- * @file
- *
- * libsigrok private header file, only to be used internally.
- */
-
-/*--- Macros ----------------------------------------------------------------*/
-
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-#endif
-
-#ifndef ARRAY_AND_SIZE
-#define ARRAY_AND_SIZE(a) (a), ARRAY_SIZE(a)
-#endif
-
-/**
- * Read a 8 bits integer out of memory.
- * @param x a pointer to the input memory
- * @return the corresponding integer
- */
-#define R8(x) ((unsigned)((const uint8_t*)(x))[0])
-
-/**
- * Read a 16 bits big endian integer out of memory.
- * @param x a pointer to the input memory
- * @return the corresponding integer
- */
-#define RB16(x) (((unsigned)((const uint8_t*)(x))[0] << 8) | \
- (unsigned)((const uint8_t*)(x))[1])
-
-/**
- * Read a 16 bits little endian integer out of memory.
- * @param x a pointer to the input memory
- * @return the corresponding integer
- */
-#define RL16(x) (((unsigned)((const uint8_t*)(x))[1] << 8) | \
- (unsigned)((const uint8_t*)(x))[0])
-
-/**
- * Read a 32 bits big endian integer out of memory.
- * @param x a pointer to the input memory
- * @return the corresponding integer
- */
-#define RB32(x) (((unsigned)((const uint8_t*)(x))[0] << 24) | \
- ((unsigned)((const uint8_t*)(x))[1] << 16) | \
- ((unsigned)((const uint8_t*)(x))[2] << 8) | \
- (unsigned)((const uint8_t*)(x))[3])
-
-/**
- * Read a 32 bits little endian integer out of memory.
- * @param x a pointer to the input memory
- * @return the corresponding integer
- */
-#define RL32(x) (((unsigned)((const uint8_t*)(x))[3] << 24) | \
- ((unsigned)((const uint8_t*)(x))[2] << 16) | \
- ((unsigned)((const uint8_t*)(x))[1] << 8) | \
- (unsigned)((const uint8_t*)(x))[0])
-
-/**
- * Write a 8 bits integer to memory.
- * @param p a pointer to the output memory
- * @param x the input integer
- */
-#define W8(p, x) do { ((uint8_t*)(p))[0] = (uint8_t) (x); } while(0)
-
-/**
- * Write a 16 bits integer to memory stored as big endian.
- * @param p a pointer to the output memory
- * @param x the input integer
- */
-#define WB16(p, x) do { ((uint8_t*)(p))[1] = (uint8_t) (x); \
- ((uint8_t*)(p))[0] = (uint8_t)((x)>>8); } while(0)
-
-/**
- * Write a 16 bits integer to memory stored as little endian.
- * @param p a pointer to the output memory
- * @param x the input integer
- */
-#define WL16(p, x) do { ((uint8_t*)(p))[0] = (uint8_t) (x); \
- ((uint8_t*)(p))[1] = (uint8_t)((x)>>8); } while(0)
-
-/**
- * Write a 32 bits integer to memory stored as big endian.
- * @param p a pointer to the output memory
- * @param x the input integer
- */
-#define WB32(p, x) do { ((uint8_t*)(p))[3] = (uint8_t) (x); \
- ((uint8_t*)(p))[2] = (uint8_t)((x)>>8); \
- ((uint8_t*)(p))[1] = (uint8_t)((x)>>16); \
- ((uint8_t*)(p))[0] = (uint8_t)((x)>>24); } while(0)
-
-/**
- * Write a 32 bits integer to memory stored as little endian.
- * @param p a pointer to the output memory
- * @param x the input integer
- */
-#define WL32(p, x) do { ((uint8_t*)(p))[0] = (uint8_t) (x); \
- ((uint8_t*)(p))[1] = (uint8_t)((x)>>8); \
- ((uint8_t*)(p))[2] = (uint8_t)((x)>>16); \
- ((uint8_t*)(p))[3] = (uint8_t)((x)>>24); } while(0)
-
-/* Portability fixes for FreeBSD. */
-#ifdef __FreeBSD__
-#define LIBUSB_CLASS_APPLICATION 0xfe
-#define libusb_handle_events_timeout_completed(ctx, tv, c) \
- libusb_handle_events_timeout(ctx, tv)
-#endif
-
-struct sr_context {
-#ifdef HAVE_LIBUSB_1_0
- libusb_context *libusb_ctx;
- gboolean usb_source_present;
-#ifdef _WIN32
- GThread *usb_thread;
- gboolean usb_thread_running;
- GMutex usb_mutex;
- HANDLE usb_event;
- GPollFD usb_pollfd;
- sr_receive_data_callback usb_cb;
- void *usb_cb_data;
-#endif
-#endif
-};
-
-#ifdef HAVE_LIBUSB_1_0
-/** USB device instance */
-struct sr_usb_dev_inst {
- /** USB bus */
- uint8_t bus;
- /** Device address on USB bus */
- uint8_t address;
- /** libusb device handle */
- struct libusb_device_handle *devhdl;
-};
-#endif
-
-#ifdef HAVE_LIBSERIALPORT
-#define SERIAL_PARITY_NONE SP_PARITY_NONE
-#define SERIAL_PARITY_EVEN SP_PARITY_EVEN
-#define SERIAL_PARITY_ODD SP_PARITY_ODD
-struct sr_serial_dev_inst {
- /** Port name, e.g. '/dev/tty42'. */
- char *port;
- /** Comm params for serial_set_paramstr(). */
- char *serialcomm;
- /** Port is non-blocking. */
- int nonblocking;
- /** libserialport port handle */
- struct sp_port *data;
- /** libserialport event set */
- struct sp_event_set *event_set;
- /** GPollFDs for event polling */
- GPollFD *pollfds;
-};
-#endif
-
-struct sr_usbtmc_dev_inst {
- char *device;
- int fd;
-};
-
-/* Private driver context. */
-struct drv_context {
- /** sigrok context */
- struct sr_context *sr_ctx;
- GSList *instances;
-};
-
-/*--- log.c -----------------------------------------------------------------*/
-
-SR_PRIV int sr_log(int loglevel, const char *format, ...);
-SR_PRIV int sr_spew(const char *format, ...);
-SR_PRIV int sr_dbg(const char *format, ...);
-SR_PRIV int sr_info(const char *format, ...);
-SR_PRIV int sr_warn(const char *format, ...);
-SR_PRIV int sr_err(const char *format, ...);
-
-/* Message logging helpers with subsystem-specific prefix string. */
-#ifndef NO_LOG_WRAPPERS
-#define sr_log(l, s, args...) sr_log(l, "%s: " s, LOG_PREFIX, ## args)
-#define sr_spew(s, args...) sr_spew("%s: " s, LOG_PREFIX, ## args)
-#define sr_dbg(s, args...) sr_dbg("%s: " s, LOG_PREFIX, ## args)
-#define sr_info(s, args...) sr_info("%s: " s, LOG_PREFIX, ## args)
-#define sr_warn(s, args...) sr_warn("%s: " s, LOG_PREFIX, ## args)
-#define sr_err(s, args...) sr_err("%s: " s, LOG_PREFIX, ## args)
-#endif
-
-/*--- device.c --------------------------------------------------------------*/
-
-/** Values for the changes argument of sr_dev_driver.config_channel_set. */
-enum {
- /** The enabled state of the channel has been changed. */
- SR_CHANNEL_SET_ENABLED = 1 << 0,
-};
-
-SR_PRIV struct sr_channel *sr_channel_new(int index, int type,
- gboolean enabled, const char *name);
-
-/* Generic device instances */
-SR_PRIV struct sr_dev_inst *sr_dev_inst_new(int index, int status,
- const char *vendor, const char *model, const char *version);
-SR_PRIV void sr_dev_inst_free(struct sr_dev_inst *sdi);
-
-#ifdef HAVE_LIBUSB_1_0
-/* USB-specific instances */
-SR_PRIV struct sr_usb_dev_inst *sr_usb_dev_inst_new(uint8_t bus,
- uint8_t address, struct libusb_device_handle *hdl);
-SR_PRIV void sr_usb_dev_inst_free(struct sr_usb_dev_inst *usb);
-#endif
-
-#ifdef HAVE_LIBSERIALPORT
-/* Serial-specific instances */
-SR_PRIV struct sr_serial_dev_inst *sr_serial_dev_inst_new(const char *port,
- const char *serialcomm);
-SR_PRIV void sr_serial_dev_inst_free(struct sr_serial_dev_inst *serial);
-#endif
-
-/* USBTMC-specific instances */
-SR_PRIV struct sr_usbtmc_dev_inst *sr_usbtmc_dev_inst_new(const char *device);
-SR_PRIV void sr_usbtmc_dev_inst_free(struct sr_usbtmc_dev_inst *usbtmc);
-
-/*--- hwdriver.c ------------------------------------------------------------*/
-
-SR_PRIV void sr_hw_cleanup_all(void);
-SR_PRIV struct sr_config *sr_config_new(int key, GVariant *data);
-SR_PRIV void sr_config_free(struct sr_config *src);
-SR_PRIV int sr_source_remove(int fd);
-SR_PRIV int sr_source_remove_pollfd(GPollFD *pollfd);
-SR_PRIV int sr_source_remove_channel(GIOChannel *channel);
-SR_PRIV int sr_source_add(int fd, int events, int timeout,
- sr_receive_data_callback cb, void *cb_data);
-SR_PRIV int sr_source_add_pollfd(GPollFD *pollfd, int timeout,
- sr_receive_data_callback cb, void *cb_data);
-SR_PRIV int sr_source_add_channel(GIOChannel *channel, int events, int timeout,
- sr_receive_data_callback cb, void *cb_data);
-
-/*--- session.c -------------------------------------------------------------*/
-
-struct sr_session {
- /** List of struct sr_dev pointers. */
- GSList *devs;
- /** List of struct datafeed_callback pointers. */
- GSList *datafeed_callbacks;
- struct sr_trigger *trigger;
- GTimeVal starttime;
- gboolean running;
-
- unsigned int num_sources;
-
- /*
- * Both "sources" and "pollfds" are of the same size and contain pairs
- * of descriptor and callback function. We can not embed the GPollFD
- * into the source struct since we want to be able to pass the array
- * of all poll descriptors to g_poll().
- */
- struct source *sources;
- GPollFD *pollfds;
- int source_timeout;
-
- /*
- * These are our synchronization primitives for stopping the session in
- * an async fashion. We need to make sure the session is stopped from
- * within the session thread itself.
- */
- /** Mutex protecting access to abort_session. */
- GMutex stop_mutex;
- /** Abort current session. See sr_session_stop(). */
- gboolean abort_session;
-};
-
-SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
- const struct sr_datafeed_packet *packet);
-SR_PRIV int sr_session_stop_sync(struct sr_session *session);
-SR_PRIV int sr_sessionfile_check(const char *filename);
-
-/*--- std.c -----------------------------------------------------------------*/
-
-typedef int (*dev_close_callback)(struct sr_dev_inst *sdi);
-typedef void (*std_dev_clear_callback)(void *priv);
-
-SR_PRIV int std_init(struct sr_context *sr_ctx, struct sr_dev_driver *di,
- const char *prefix);
-#ifdef HAVE_LIBSERIALPORT
-SR_PRIV int std_serial_dev_open(struct sr_dev_inst *sdi);
-SR_PRIV int std_serial_dev_acquisition_stop(struct sr_dev_inst *sdi,
- void *cb_data, dev_close_callback dev_close_fn,
- struct sr_serial_dev_inst *serial, const char *prefix);
-#endif
-SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi,
- const char *prefix);
-SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
- std_dev_clear_callback clear_private);
-SR_PRIV int std_serial_dev_close(struct sr_dev_inst *sdi);
-
-/*--- strutil.c -------------------------------------------------------------*/
-
-SR_PRIV int sr_atol(const char *str, long *ret);
-SR_PRIV int sr_atoi(const char *str, int *ret);
-SR_PRIV int sr_atod(const char *str, double *ret);
-SR_PRIV int sr_atof(const char *str, float *ret);
-SR_PRIV int sr_atof_ascii(const char *str, float *ret);
-
-/*--- soft-trigger.c --------------------------------------------------------*/
-
-struct soft_trigger_logic {
- const struct sr_dev_inst *sdi;
- const struct sr_trigger *trigger;
- int count;
- int unitsize;
- int cur_stage;
- uint8_t *prev_sample;
-};
-
-SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
- const struct sr_dev_inst *sdi, struct sr_trigger *trigger);
-SR_PRIV void soft_trigger_logic_free(struct soft_trigger_logic *st);
-SR_PRIV int soft_trigger_logic_check(struct soft_trigger_logic *st, uint8_t *buf,
- int len);
-
-/*--- hardware/common/serial.c ----------------------------------------------*/
-
-#ifdef HAVE_LIBSERIALPORT
-enum {
- SERIAL_RDWR = 1,
- SERIAL_RDONLY = 2,
- SERIAL_NONBLOCK = 4,
-};
-
-typedef gboolean (*packet_valid_callback)(const uint8_t *buf);
-
-SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags);
-SR_PRIV int serial_close(struct sr_serial_dev_inst *serial);
-SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial);
-SR_PRIV int serial_write(struct sr_serial_dev_inst *serial,
- const void *buf, size_t count);
-SR_PRIV int serial_write_blocking(struct sr_serial_dev_inst *serial,
- const void *buf, size_t count);
-SR_PRIV int serial_write_nonblocking(struct sr_serial_dev_inst *serial,
- const void *buf, size_t count);
-SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf,
- size_t count);
-SR_PRIV int serial_read_blocking(struct sr_serial_dev_inst *serial, void *buf,
- size_t count);
-SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf,
- size_t count);
-SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate,
- int bits, int parity, int stopbits, int flowcontrol, int rts, int dtr);
-SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
- const char *paramstr);
-SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
- int *buflen, gint64 timeout_ms);
-SR_PRIV int serial_stream_detect(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, int baudrate);
-SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_device,
- const char **serial_options);
-SR_PRIV int serial_source_add(struct sr_session *session,
- struct sr_serial_dev_inst *serial, int events, int timeout,
- sr_receive_data_callback cb, void *cb_data);
-SR_PRIV int serial_source_remove(struct sr_session *session,
- struct sr_serial_dev_inst *serial);
-SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id);
-#endif
-
-/*--- hardware/common/ezusb.c -----------------------------------------------*/
-
-#ifdef HAVE_LIBUSB_1_0
-SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear);
-SR_PRIV int ezusb_install_firmware(libusb_device_handle *hdl,
- const char *filename);
-SR_PRIV int ezusb_upload_firmware(libusb_device *dev, int configuration,
- const char *filename);
-#endif
-
-/*--- hardware/common/usb.c -------------------------------------------------*/
-
-#ifdef HAVE_LIBUSB_1_0
-SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn);
-SR_PRIV int sr_usb_open(libusb_context *usb_ctx, struct sr_usb_dev_inst *usb);
-SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx,
- int timeout, sr_receive_data_callback cb, void *cb_data);
-SR_PRIV int usb_source_remove(struct sr_session *session, struct sr_context *ctx);
-#endif
-
-/*--- hardware/common/scpi.c ------------------------------------------------*/
-
-#define SCPI_CMD_IDN "*IDN?"
-#define SCPI_CMD_OPC "*OPC?"
-
-enum {
- SCPI_CMD_SET_TRIGGER_SOURCE,
- SCPI_CMD_SET_TIMEBASE,
- SCPI_CMD_SET_VERTICAL_DIV,
- SCPI_CMD_SET_TRIGGER_SLOPE,
- SCPI_CMD_SET_COUPLING,
- SCPI_CMD_SET_HORIZ_TRIGGERPOS,
- SCPI_CMD_GET_ANALOG_CHAN_STATE,
- SCPI_CMD_GET_DIG_CHAN_STATE,
- SCPI_CMD_GET_TIMEBASE,
- SCPI_CMD_GET_VERTICAL_DIV,
- SCPI_CMD_GET_VERTICAL_OFFSET,
- SCPI_CMD_GET_TRIGGER_SOURCE,
- SCPI_CMD_GET_HORIZ_TRIGGERPOS,
- SCPI_CMD_GET_TRIGGER_SLOPE,
- SCPI_CMD_GET_COUPLING,
- SCPI_CMD_SET_ANALOG_CHAN_STATE,
- SCPI_CMD_SET_DIG_CHAN_STATE,
- SCPI_CMD_GET_DIG_POD_STATE,
- SCPI_CMD_SET_DIG_POD_STATE,
- SCPI_CMD_GET_ANALOG_DATA,
- SCPI_CMD_GET_DIG_DATA,
- SCPI_CMD_GET_SAMPLE_RATE,
- SCPI_CMD_GET_SAMPLE_RATE_LIVE,
-};
-
-struct sr_scpi_hw_info {
- char *manufacturer;
- char *model;
- char *serial_number;
- char *firmware_version;
-};
-
-struct sr_scpi_dev_inst {
- const char *name;
- const char *prefix;
- int priv_size;
- GSList *(*scan)(struct drv_context *drvc);
- int (*dev_inst_new)(void *priv, struct drv_context *drvc,
- const char *resource, char **params, const char *serialcomm);
- int (*open)(void *priv);
- int (*source_add)(struct sr_session *session, void *priv, int events,
- int timeout, sr_receive_data_callback cb, void *cb_data);
- int (*source_remove)(struct sr_session *session, void *priv);
- int (*send)(void *priv, const char *command);
- int (*read_begin)(void *priv);
- int (*read_data)(void *priv, char *buf, int maxlen);
- int (*read_complete)(void *priv);
- int (*close)(void *priv);
- void (*free)(void *priv);
- void *priv;
-};
-
-SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
- struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi));
-SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc,
- const char *resource, const char *serialcomm);
-SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi);
-SR_PRIV int sr_scpi_source_add(struct sr_session *session,
- struct sr_scpi_dev_inst *scpi, int events, int timeout,
- sr_receive_data_callback cb, void *cb_data);
-SR_PRIV int sr_scpi_source_remove(struct sr_session *session,
- struct sr_scpi_dev_inst *scpi);
-SR_PRIV int sr_scpi_send(struct sr_scpi_dev_inst *scpi,
- const char *format, ...);
-SR_PRIV int sr_scpi_send_variadic(struct sr_scpi_dev_inst *scpi,
- const char *format, va_list args);
-SR_PRIV int sr_scpi_read_begin(struct sr_scpi_dev_inst *scpi);
-SR_PRIV int sr_scpi_read_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen);
-SR_PRIV int sr_scpi_read_complete(struct sr_scpi_dev_inst *scpi);
-SR_PRIV int sr_scpi_close(struct sr_scpi_dev_inst *scpi);
-SR_PRIV void sr_scpi_free(struct sr_scpi_dev_inst *scpi);
-
-SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
- const char *command, char **scpi_response);
-SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi,
- const char *command, gboolean *scpi_response);
-SR_PRIV int sr_scpi_get_int(struct sr_scpi_dev_inst *scpi,
- const char *command, int *scpi_response);
-SR_PRIV int sr_scpi_get_float(struct sr_scpi_dev_inst *scpi,
- const char *command, float *scpi_response);
-SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi,
- const char *command, double *scpi_response);
-SR_PRIV int sr_scpi_get_opc(struct sr_scpi_dev_inst *scpi);
-SR_PRIV int sr_scpi_get_floatv(struct sr_scpi_dev_inst *scpi,
- const char *command, GArray **scpi_response);
-SR_PRIV int sr_scpi_get_uint8v(struct sr_scpi_dev_inst *scpi,
- const char *command, GArray **scpi_response);
-SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
- struct sr_scpi_hw_info **scpi_response);
-SR_PRIV void sr_scpi_hw_info_free(struct sr_scpi_hw_info *hw_info);
-
-/*--- hardware/common/dmm/es519xx.c -----------------------------------------*/
-
-/**
- * All 11-byte es519xx chips repeat each block twice for each conversion cycle
- * so always read 2 blocks at a time.
- */
-#define ES519XX_11B_PACKET_SIZE (11 * 2)
-#define ES519XX_14B_PACKET_SIZE 14
-
-struct es519xx_info {
- gboolean is_judge, is_voltage, is_auto, is_micro, is_current;
- gboolean is_milli, is_resistance, is_continuity, is_diode;
- gboolean is_frequency, is_rpm, is_capacitance, is_duty_cycle;
- gboolean is_temperature, is_celsius, is_fahrenheit;
- gboolean is_adp0, is_adp1, is_adp2, is_adp3;
- gboolean is_sign, is_batt, is_ol, is_pmax, is_pmin, is_apo;
- gboolean is_dc, is_ac, is_vahz, is_min, is_max, is_rel, is_hold;
- gboolean is_digit4, is_ul, is_vasel, is_vbar, is_lpf1, is_lpf0, is_rmr;
- uint32_t baudrate;
- int packet_size;
- gboolean alt_functions, fivedigits, clampmeter, selectable_lpf;
-};
-
-SR_PRIV gboolean sr_es519xx_2400_11b_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_2400_11b_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_2400_11b_altfn_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_2400_11b_altfn_parse(const uint8_t *buf,
- float *floatval, struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_19200_11b_5digits_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_19200_11b_5digits_parse(const uint8_t *buf,
- float *floatval, struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_19200_11b_clamp_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_19200_11b_clamp_parse(const uint8_t *buf,
- float *floatval, struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_19200_11b_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_19200_11b_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_19200_14b_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_19200_14b_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_19200_14b_sel_lpf_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_19200_14b_sel_lpf_parse(const uint8_t *buf,
- float *floatval, struct sr_datafeed_analog *analog, void *info);
-
-/*--- hardware/common/dmm/fs9922.c ------------------------------------------*/
-
-#define FS9922_PACKET_SIZE 14
-
-struct fs9922_info {
- gboolean is_auto, is_dc, is_ac, is_rel, is_hold, is_bpn, is_z1, is_z2;
- gboolean is_max, is_min, is_apo, is_bat, is_nano, is_z3, is_micro;
- gboolean is_milli, is_kilo, is_mega, is_beep, is_diode, is_percent;
- gboolean is_z4, is_volt, is_ampere, is_ohm, is_hfe, is_hertz, is_farad;
- gboolean is_celsius, is_fahrenheit;
- int bargraph_sign, bargraph_value;
-};
-
-SR_PRIV gboolean sr_fs9922_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_fs9922_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9922_z1_diode(struct sr_datafeed_analog *analog, void *info);
-
-/*--- hardware/common/dmm/fs9721.c ------------------------------------------*/
-
-#define FS9721_PACKET_SIZE 14
-
-struct fs9721_info {
- gboolean is_ac, is_dc, is_auto, is_rs232, is_micro, is_nano, is_kilo;
- gboolean is_diode, is_milli, is_percent, is_mega, is_beep, is_farad;
- gboolean is_ohm, is_rel, is_hold, is_ampere, is_volt, is_hz, is_bat;
- gboolean is_c2c1_11, is_c2c1_10, is_c2c1_01, is_c2c1_00, is_sign;
-};
-
-SR_PRIV gboolean sr_fs9721_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9721_00_temp_c(struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9721_01_temp_c(struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9721_10_temp_c(struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9721_max_c_min(struct sr_datafeed_analog *analog, void *info);
-
-/*--- hardware/common/dmm/m2110.c -----------------------------------------*/
-
-#define BBCGM_M2110_PACKET_SIZE 9
-
-SR_PRIV gboolean sr_m2110_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_m2110_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info);
-
-/*--- hardware/common/dmm/metex14.c -----------------------------------------*/
-
-#define METEX14_PACKET_SIZE 14
-
-struct metex14_info {
- gboolean is_ac, is_dc, is_resistance, is_capacity, is_temperature;
- gboolean is_diode, is_frequency, is_ampere, is_volt, is_farad;
- gboolean is_hertz, is_ohm, is_celsius, is_pico, is_nano, is_micro;
- gboolean is_milli, is_kilo, is_mega, is_gain, is_decibel, is_hfe;
- gboolean is_unitless, is_logic;
-};
-
-#ifdef HAVE_LIBSERIALPORT
-SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial);
-#endif
-SR_PRIV gboolean sr_metex14_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_metex14_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info);
-
-/*--- hardware/common/dmm/rs9lcd.c ------------------------------------------*/
-
-#define RS9LCD_PACKET_SIZE 9
-
-/* Dummy info struct. The parser does not use it. */
-struct rs9lcd_info { int dummy; };
-
-SR_PRIV gboolean sr_rs9lcd_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_rs9lcd_parse(const uint8_t *buf, float *floatval,
- struct sr_datafeed_analog *analog, void *info);
-
-#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011-2012 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdarg.h>
-#include <stdio.h>
-#include "libsigrok.h"
-/** @cond PRIVATE */
-#define NO_LOG_WRAPPERS
-/** @endcond */
-#include "libsigrok-internal.h"
-
-/**
- * @file
- *
- * Controlling the libsigrok message logging functionality.
- */
-
-/**
- * @defgroup grp_logging Logging
- *
- * Controlling the libsigrok message logging functionality.
- *
- * @{
- */
-
-/* Currently selected libsigrok loglevel. Default: SR_LOG_WARN. */
-static int cur_loglevel = SR_LOG_WARN; /* Show errors+warnings per default. */
-
-/* Function prototype. */
-static int sr_logv(void *cb_data, int loglevel, const char *format,
- va_list args);
-
-/* Pointer to the currently selected log callback. Default: sr_logv(). */
-static sr_log_callback sr_log_cb = sr_logv;
-
-/*
- * Pointer to private data that can be passed to the log callback.
- * This can be used (for example) by C++ GUIs to pass a "this" pointer.
- */
-static void *sr_log_cb_data = NULL;
-
-/* Log domain (a short string that is used as prefix for all messages). */
-/** @cond PRIVATE */
-#define LOGDOMAIN_MAXLEN 30
-#define LOGDOMAIN_DEFAULT "sr: "
-/** @endcond */
-static char sr_log_domain[LOGDOMAIN_MAXLEN + 1] = LOGDOMAIN_DEFAULT;
-
-/**
- * Set the libsigrok loglevel.
- *
- * This influences the amount of log messages (debug messages, error messages,
- * and so on) libsigrok will output. Using SR_LOG_NONE disables all messages.
- *
- * Note that this function itself will also output log messages. After the
- * loglevel has changed, it will output a debug message with SR_LOG_DBG for
- * example. Whether this message is shown depends on the (new) loglevel.
- *
- * @param loglevel The loglevel to set (SR_LOG_NONE, SR_LOG_ERR, SR_LOG_WARN,
- * SR_LOG_INFO, SR_LOG_DBG, or SR_LOG_SPEW).
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid loglevel.
- *
- * @since 0.1.0
- */
-SR_API int sr_log_loglevel_set(int loglevel)
-{
- if (loglevel < SR_LOG_NONE || loglevel > SR_LOG_SPEW) {
- sr_err("Invalid loglevel %d.", loglevel);
- return SR_ERR_ARG;
- }
-
- cur_loglevel = loglevel;
-
- sr_dbg("libsigrok loglevel set to %d.", loglevel);
-
- return SR_OK;
-}
-
-/**
- * Get the libsigrok loglevel.
- *
- * @return The currently configured libsigrok loglevel.
- *
- * @since 0.1.0
- */
-SR_API int sr_log_loglevel_get(void)
-{
- return cur_loglevel;
-}
-
-/**
- * Set the libsigrok logdomain string.
- *
- * @param logdomain The string to use as logdomain for libsigrok log
- * messages from now on. Must not be NULL. The maximum
- * length of the string is 30 characters (this does not
- * include the trailing NUL-byte). Longer strings are
- * silently truncated.
- * In order to not use a logdomain, pass an empty string.
- * The function makes its own copy of the input string, i.e.
- * the caller does not need to keep it around.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid logdomain.
- *
- * @since 0.1.0
- */
-SR_API int sr_log_logdomain_set(const char *logdomain)
-{
- if (!logdomain) {
- sr_err("log: %s: logdomain was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- /* TODO: Error handling. */
- snprintf((char *)&sr_log_domain, LOGDOMAIN_MAXLEN, "%s", logdomain);
-
- sr_dbg("Log domain set to '%s'.", (const char *)&sr_log_domain);
-
- return SR_OK;
-}
-
-/**
- * Get the currently configured libsigrok logdomain.
- *
- * @return A copy of the currently configured libsigrok logdomain
- * string. The caller is responsible for g_free()ing the string when
- * it is no longer needed.
- *
- * @since 0.1.0
- */
-SR_API char *sr_log_logdomain_get(void)
-{
- return g_strdup((const char *)&sr_log_domain);
-}
-
-/**
- * Set the libsigrok log callback to the specified function.
- *
- * @param cb Function pointer to the log callback function to use.
- * Must not be NULL.
- * @param cb_data Pointer to private data to be passed on. This can be used by
- * the caller to pass arbitrary data to the log functions. This
- * pointer is only stored or passed on by libsigrok, and is
- * never used or interpreted in any way. The pointer is allowed
- * to be NULL if the caller doesn't need/want to pass any data.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
- *
- * @since 0.3.0
- */
-SR_API int sr_log_callback_set(sr_log_callback cb, void *cb_data)
-{
- if (!cb) {
- sr_err("log: %s: cb was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- /* Note: 'cb_data' is allowed to be NULL. */
-
- sr_log_cb = cb;
- sr_log_cb_data = cb_data;
-
- return SR_OK;
-}
-
-/**
- * Set the libsigrok log callback to the default built-in one.
- *
- * Additionally, the internal 'sr_log_cb_data' pointer is set to NULL.
- *
- * @return SR_OK upon success, a negative error code otherwise.
- *
- * @since 0.1.0
- */
-SR_API int sr_log_callback_set_default(void)
-{
- /*
- * Note: No log output in this function, as it should safely work
- * even if the currently set log callback is buggy/broken.
- */
- sr_log_cb = sr_logv;
- sr_log_cb_data = NULL;
-
- return SR_OK;
-}
-
-static int sr_logv(void *cb_data, int loglevel, const char *format, va_list args)
-{
- int ret;
-
- /* This specific log callback doesn't need the void pointer data. */
- (void)cb_data;
-
- /* Only output messages of at least the selected loglevel(s). */
- if (loglevel > cur_loglevel)
- return SR_OK; /* TODO? */
-
- if (sr_log_domain[0] != '\0')
- fprintf(stderr, "%s", sr_log_domain);
- ret = vfprintf(stderr, format, args);
- fprintf(stderr, "\n");
-
- return ret;
-}
-
-/** @private */
-SR_PRIV int sr_log(int loglevel, const char *format, ...)
-{
- int ret;
- va_list args;
-
- va_start(args, format);
- ret = sr_log_cb(sr_log_cb_data, loglevel, format, args);
- va_end(args);
-
- return ret;
-}
-
-/** @private */
-SR_PRIV int sr_spew(const char *format, ...)
-{
- int ret;
- va_list args;
-
- va_start(args, format);
- ret = sr_log_cb(sr_log_cb_data, SR_LOG_SPEW, format, args);
- va_end(args);
-
- return ret;
-}
-
-/** @private */
-SR_PRIV int sr_dbg(const char *format, ...)
-{
- int ret;
- va_list args;
-
- va_start(args, format);
- ret = sr_log_cb(sr_log_cb_data, SR_LOG_DBG, format, args);
- va_end(args);
-
- return ret;
-}
-
-/** @private */
-SR_PRIV int sr_info(const char *format, ...)
-{
- int ret;
- va_list args;
-
- va_start(args, format);
- ret = sr_log_cb(sr_log_cb_data, SR_LOG_INFO, format, args);
- va_end(args);
-
- return ret;
-}
-
-/** @private */
-SR_PRIV int sr_warn(const char *format, ...)
-{
- int ret;
- va_list args;
-
- va_start(args, format);
- ret = sr_log_cb(sr_log_cb_data, SR_LOG_WARN, format, args);
- va_end(args);
-
- return ret;
-}
-
-/** @private */
-SR_PRIV int sr_err(const char *format, ...)
-{
- int ret;
- va_list args;
-
- va_start(args, format);
- ret = sr_log_cb(sr_log_cb_data, SR_LOG_ERR, format, args);
- va_end(args);
-
- return ret;
-}
-
-/** @} */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "output/analog"
-
-struct context {
- int num_enabled_channels;
- GPtrArray *channellist;
-};
-
-static int init(struct sr_output *o)
-{
- struct context *ctx;
- struct sr_channel *ch;
- GSList *l;
-
- sr_spew("Initializing output module.");
-
- if (!o || !o->sdi)
- return SR_ERR_ARG;
-
- if (!(ctx = g_try_malloc0(sizeof(struct context)))) {
- sr_err("Output module context malloc failed.");
- return SR_ERR_MALLOC;
- }
- o->internal = ctx;
-
- /* Get the number of channels and their names. */
- ctx->channellist = g_ptr_array_new();
- for (l = o->sdi->channels; l; l = l->next) {
- ch = l->data;
- if (!ch || !ch->enabled)
- continue;
- g_ptr_array_add(ctx->channellist, ch->name);
- ctx->num_enabled_channels++;
- }
-
- return SR_OK;
-}
-
-static void si_printf(float value, GString *out, char *unitstr)
-{
- float v;
-
- if (signbit(value))
- v = -(value);
- else
- v = value;
-
- if (v < 1e-12 || v > 1e+12)
- g_string_append_printf(out, "%f %s", value, unitstr);
- else if (v > 1e+9)
- g_string_append_printf(out, "%f G%s", value / 1e+9, unitstr);
- else if (v > 1e+6)
- g_string_append_printf(out, "%f M%s", value / 1e+6, unitstr);
- else if (v > 1e+3)
- g_string_append_printf(out, "%f k%s", value / 1e+3, unitstr);
- else if (v < 1e-9)
- g_string_append_printf(out, "%f n%s", value * 1e+9, unitstr);
- else if (v < 1e-6)
- g_string_append_printf(out, "%f u%s", value * 1e+6, unitstr);
- else if (v < 1e-3)
- g_string_append_printf(out, "%f m%s", value * 1e+3, unitstr);
- else
- g_string_append_printf(out, "%f %s", value, unitstr);
-
-}
-
-static void fancyprint(int unit, int mqflags, float value, GString *out)
-{
- switch (unit) {
- case SR_UNIT_VOLT:
- si_printf(value, out, "V");
- break;
- case SR_UNIT_AMPERE:
- si_printf(value, out, "A");
- break;
- case SR_UNIT_OHM:
- si_printf(value, out, "");
- g_string_append_unichar(out, 0x2126);
- break;
- case SR_UNIT_FARAD:
- si_printf(value, out, "F");
- break;
- case SR_UNIT_KELVIN:
- si_printf(value, out, "K");
- break;
- case SR_UNIT_CELSIUS:
- si_printf(value, out, "");
- g_string_append_unichar(out, 0x00b0);
- g_string_append_c(out, 'C');
- break;
- case SR_UNIT_FAHRENHEIT:
- si_printf(value, out, "");
- g_string_append_unichar(out, 0x00b0);
- g_string_append_c(out, 'F');
- break;
- case SR_UNIT_HERTZ:
- si_printf(value, out, "Hz");
- break;
- case SR_UNIT_PERCENTAGE:
- g_string_append_printf(out, "%f %%", value);
- break;
- case SR_UNIT_BOOLEAN:
- if (value > 0)
- g_string_append_printf(out, "TRUE");
- else
- g_string_append_printf(out, "FALSE");
- break;
- case SR_UNIT_SECOND:
- si_printf(value, out, "s");
- break;
- case SR_UNIT_SIEMENS:
- si_printf(value, out, "S");
- break;
- case SR_UNIT_DECIBEL_MW:
- si_printf(value, out, "dBu");
- break;
- case SR_UNIT_DECIBEL_VOLT:
- si_printf(value, out, "dBV");
- break;
- case SR_UNIT_DECIBEL_SPL:
- if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A)
- si_printf(value, out, "dB(A)");
- else if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_C)
- si_printf(value, out, "dB(C)");
- else if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_Z)
- si_printf(value, out, "dB(Z)");
- else
- /* No frequency weighting, or non-standard "flat" */
- si_printf(value, out, "dB(SPL)");
- if (mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_S)
- g_string_append(out, " S");
- else if (mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_F)
- g_string_append(out, " F");
- if (mqflags & SR_MQFLAG_SPL_LAT)
- g_string_append(out, " LAT");
- else if (mqflags & SR_MQFLAG_SPL_PCT_OVER_ALARM)
- /* Not a standard function for SLMs, so this is
- * a made-up notation. */
- g_string_append(out, " %oA");
- break;
- case SR_UNIT_CONCENTRATION:
- g_string_append_printf(out, "%f ppm", value * 1000000);
- break;
- case SR_UNIT_REVOLUTIONS_PER_MINUTE:
- si_printf(value, out, "RPM");
- break;
- case SR_UNIT_VOLT_AMPERE:
- si_printf(value, out, "VA");
- break;
- case SR_UNIT_WATT:
- si_printf(value, out, "W");
- break;
- case SR_UNIT_WATT_HOUR:
- si_printf(value, out, "Wh");
- break;
- case SR_UNIT_METER_SECOND:
- si_printf(value, out, "m/s");
- break;
- case SR_UNIT_HECTOPASCAL:
- si_printf(value, out, "hPa");
- break;
- case SR_UNIT_HUMIDITY_293K:
- si_printf(value, out, "%rF");
- break;
- default:
- si_printf(value, out, "");
- break;
- }
-
- if (mqflags & SR_MQFLAG_AC)
- g_string_append_printf(out, " AC");
- if (mqflags & SR_MQFLAG_DC)
- g_string_append_printf(out, " DC");
- if (mqflags & SR_MQFLAG_RMS)
- g_string_append_printf(out, " RMS");
- if (mqflags & SR_MQFLAG_DIODE)
- g_string_append_printf(out, " DIODE");
- if (mqflags & SR_MQFLAG_HOLD)
- g_string_append_printf(out, " HOLD");
- if (mqflags & SR_MQFLAG_MAX)
- g_string_append_printf(out, " MAX");
- if (mqflags & SR_MQFLAG_MIN)
- g_string_append_printf(out, " MIN");
- if (mqflags & SR_MQFLAG_AUTORANGE)
- g_string_append_printf(out, " AUTO");
- if (mqflags & SR_MQFLAG_RELATIVE)
- g_string_append_printf(out, " REL");
- if (mqflags & SR_MQFLAG_AVG)
- g_string_append_printf(out, " AVG");
- g_string_append_c(out, '\n');
-}
-
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
- GString **out)
-{
- const struct sr_datafeed_analog *analog;
- struct sr_channel *ch;
- GSList *l;
- const float *fdata;
- int i, p;
-
- *out = NULL;
- if (!o || !o->sdi)
- return SR_ERR_ARG;
-
- switch (packet->type) {
- case SR_DF_FRAME_BEGIN:
- *out = g_string_new("FRAME-BEGIN\n");
- break;
- case SR_DF_FRAME_END:
- *out = g_string_new("FRAME-END\n");
- break;
- case SR_DF_ANALOG:
- analog = packet->payload;
- fdata = (const float *)analog->data;
- *out = g_string_sized_new(512);
- for (i = 0; i < analog->num_samples; i++) {
- for (l = analog->channels, p = 0; l; l = l->next, p++) {
- ch = l->data;
- g_string_append_printf(*out, "%s: ", ch->name);
- fancyprint(analog->unit, analog->mqflags,
- fdata[i + p], *out);
- }
- }
- break;
- }
-
- return SR_OK;
-}
-
-static int cleanup(struct sr_output *o)
-{
- struct context *ctx;
-
- if (!o || !o->sdi)
- return SR_ERR_ARG;
- ctx = o->internal;
-
- g_ptr_array_free(ctx->channellist, 1);
- g_free(ctx);
- o->internal = NULL;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_output_format output_analog = {
- .id = "analog",
- .description = "Analog data",
- .init = init,
- .receive = receive,
- .cleanup = cleanup
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011 HÃ¥vard Espeland <gus@ping.uio.no>
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "output/hex"
-
-#define DEFAULT_SAMPLES_PER_LINE 74
-
-struct context {
- unsigned int num_enabled_channels;
- int samples_per_line;
- int bit_cnt;
- int spl_cnt;
- int trigger;
- uint64_t samplerate;
- int *channel_index;
- char **channel_names;
- char **line_values;
- uint8_t *prev_sample;
- gboolean header_done;
- GString **lines;
- GString *header;
-};
-
-static int init(struct sr_output *o)
-{
- struct context *ctx;
- struct sr_channel *ch;
- GSList *l;
- GHashTableIter iter;
- gpointer key, value;
- unsigned int i, j;
- int spl;
-
- if (!o || !o->sdi)
- return SR_ERR_ARG;
-
- spl = DEFAULT_SAMPLES_PER_LINE;
- g_hash_table_iter_init(&iter, o->params);
- while (g_hash_table_iter_next(&iter, &key, &value)) {
- if (!strcmp(key, "width")) {
- if ((spl = strtoul(value, NULL, 10)) < 1) {
- sr_err("Invalid width.");
- return SR_ERR_ARG;
- }
- } else {
- sr_err("Unknown parameter '%s'.", key);
- return SR_ERR_ARG;
- }
- }
-
- ctx = g_malloc0(sizeof(struct context));
- o->internal = ctx;
- ctx->trigger = -1;
- ctx->samples_per_line = spl;
-
- for (l = o->sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->num_enabled_channels++;
- }
- ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
- ctx->channel_names = g_malloc(sizeof(char *) * ctx->num_enabled_channels);
- ctx->lines = g_malloc(sizeof(GString *) * ctx->num_enabled_channels);
- ctx->prev_sample = g_malloc(g_slist_length(o->sdi->channels));
-
- j = 0;
- for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->channel_index[j] = ch->index;
- ctx->channel_names[j] = ch->name;
- ctx->lines[j] = g_string_sized_new(80);
- g_string_printf(ctx->lines[j], "%s:", ch->name);
- j++;
- }
-
- return SR_OK;
-}
-
-static GString *gen_header(struct sr_output *o)
-{
- struct context *ctx;
- GVariant *gvar;
- GString *header;
- int num_channels;
- char *samplerate_s;
-
- ctx = o->internal;
- if (ctx->samplerate == 0) {
- if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
- &gvar) == SR_OK) {
- ctx->samplerate = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
- }
- }
-
- header = g_string_sized_new(512);
- g_string_printf(header, "%s\n", PACKAGE_STRING);
- num_channels = g_slist_length(o->sdi->channels);
- g_string_append_printf(header, "Acquisition with %d/%d channels",
- ctx->num_enabled_channels, num_channels);
- if (ctx->samplerate != 0) {
- samplerate_s = sr_samplerate_string(ctx->samplerate);
- g_string_append_printf(header, " at %s", samplerate_s);
- g_free(samplerate_s);
- }
- g_string_append_printf(header, "\n");
-
- return header;
-}
-
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
- GString **out)
-{
- const struct sr_datafeed_meta *meta;
- const struct sr_datafeed_logic *logic;
- const struct sr_config *src;
- GSList *l;
- struct context *ctx;
- int idx, offset, curbit, prevbit;
- uint64_t i, j;
- gchar *p, c;
-
- *out = NULL;
- if (!o || !o->sdi)
- return SR_ERR_ARG;
- if (!(ctx = o->internal))
- return SR_ERR_ARG;
-
- switch (packet->type) {
- case SR_DF_META:
- meta = packet->payload;
- for (l = meta->config; l; l = l->next) {
- src = l->data;
- if (src->key != SR_CONF_SAMPLERATE)
- continue;
- ctx->samplerate = g_variant_get_uint64(src->data);
- }
- break;
- case SR_DF_TRIGGER:
- ctx->trigger = ctx->spl_cnt;
- break;
- case SR_DF_LOGIC:
- if (!ctx->header_done) {
- *out = gen_header(o);
- ctx->header_done = TRUE;
- } else
- *out = g_string_sized_new(512);
-
- logic = packet->payload;
- for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
- ctx->spl_cnt++;
- for (j = 0; j < ctx->num_enabled_channels; j++) {
- idx = ctx->channel_index[j];
- p = logic->data + i + idx / 8;
- curbit = *p & (1 << (idx % 8));
- prevbit = (ctx->prev_sample[idx / 8] & ((uint8_t) 1 << (idx % 8)));
-
- c = curbit ? '"' : '.';
- if (ctx->spl_cnt > 1) {
- if (curbit < prevbit)
- c = '\\';
- else if (curbit > prevbit)
- c = '/';
- }
- g_string_append_c(ctx->lines[j], c);
-
- if (ctx->spl_cnt == ctx->samples_per_line) {
- /* Flush line buffers. */
- g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
- g_string_append_c(*out, '\n');
- if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
- offset = ctx->trigger + ctx->trigger / 8;
- g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
- ctx->trigger = -1;
- }
- g_string_printf(ctx->lines[j], "%s:", ctx->channel_names[j]);
- }
- }
- if (ctx->spl_cnt == ctx->samples_per_line)
- /* Line buffers were already flushed. */
- ctx->spl_cnt = 0;
- memcpy(ctx->prev_sample, logic->data + i, logic->unitsize);
- }
- break;
- case SR_DF_END:
- if (ctx->spl_cnt) {
- /* Line buffers need flushing. */
- *out = g_string_sized_new(512);
- for (i = 0; i < ctx->num_enabled_channels; i++) {
- g_string_append_len(*out, ctx->lines[i]->str, ctx->lines[i]->len);
- g_string_append_c(*out, '\n');
- }
- }
- break;
- }
-
- return SR_OK;
-}
-
-static int cleanup(struct sr_output *o)
-{
- struct context *ctx;
- unsigned int i;
-
- if (!o)
- return SR_ERR_ARG;
-
- if (!(ctx = o->internal))
- return SR_OK;
-
- g_free(ctx->channel_index);
- g_free(ctx->prev_sample);
- g_free(ctx->channel_names);
- for (i = 0; i < ctx->num_enabled_channels; i++)
- g_string_free(ctx->lines[i], TRUE);
- g_free(ctx->lines);
- g_free(ctx);
- o->internal = NULL;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_output_format output_ascii = {
- .id = "ascii",
- .description = "ASCII",
- .init = init,
- .receive = receive,
- .cleanup = cleanup,
-};
-
-
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "output/binary"
-
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
- GString **out)
-{
- const struct sr_datafeed_logic *logic;
-
- (void)o;
-
- *out = NULL;
- if (packet->type != SR_DF_LOGIC)
- return SR_OK;
- logic = packet->payload;
- *out = g_string_new_len(logic->data, logic->length);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_output_format output_binary = {
- .id = "binary",
- .description = "Raw binary",
- .receive = receive,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "output/bits"
-
-#define DEFAULT_SAMPLES_PER_LINE 64
-
-struct context {
- unsigned int num_enabled_channels;
- int samples_per_line;
- int spl_cnt;
- int trigger;
- uint64_t samplerate;
- int *channel_index;
- char **channel_names;
- gboolean header_done;
- GString **lines;
-};
-
-static int init(struct sr_output *o)
-{
- struct context *ctx;
- struct sr_channel *ch;
- GSList *l;
- GHashTableIter iter;
- gpointer key, value;
- unsigned int i, j;
- int spl;
-
- if (!o || !o->sdi)
- return SR_ERR_ARG;
-
- spl = DEFAULT_SAMPLES_PER_LINE;
- g_hash_table_iter_init(&iter, o->params);
- while (g_hash_table_iter_next(&iter, &key, &value)) {
- if (!strcmp(key, "width")) {
- if ((spl = strtoul(value, NULL, 10)) < 1) {
- sr_err("Invalid width.");
- return SR_ERR_ARG;
- }
- } else {
- sr_err("Unknown parameter '%s'.", key);
- return SR_ERR_ARG;
- }
- }
-
- ctx = g_malloc0(sizeof(struct context));
- o->internal = ctx;
- ctx->trigger = -1;
- ctx->samples_per_line = spl;
-
- for (l = o->sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->num_enabled_channels++;
- }
- ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
- ctx->channel_names = g_malloc(sizeof(char *) * ctx->num_enabled_channels);
- ctx->lines = g_malloc(sizeof(GString *) * ctx->num_enabled_channels);
-
- j = 0;
- for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->channel_index[j] = ch->index;
- ctx->channel_names[j] = ch->name;
- ctx->lines[j] = g_string_sized_new(80);
- g_string_printf(ctx->lines[j], "%s:", ch->name);
- j++;
- }
-
- return SR_OK;
-}
-
-static GString *gen_header(struct sr_output *o)
-{
- struct context *ctx;
- GVariant *gvar;
- GString *header;
- int num_channels;
- char *samplerate_s;
-
- ctx = o->internal;
- if (ctx->samplerate == 0) {
- if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
- &gvar) == SR_OK) {
- ctx->samplerate = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
- }
- }
-
- header = g_string_sized_new(512);
- g_string_printf(header, "%s\n", PACKAGE_STRING);
- num_channels = g_slist_length(o->sdi->channels);
- g_string_append_printf(header, "Acquisition with %d/%d channels",
- ctx->num_enabled_channels, num_channels);
- if (ctx->samplerate != 0) {
- samplerate_s = sr_samplerate_string(ctx->samplerate);
- g_string_append_printf(header, " at %s", samplerate_s);
- g_free(samplerate_s);
- }
- g_string_append_printf(header, "\n");
-
- return header;
-}
-
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
- GString **out)
-{
- const struct sr_datafeed_meta *meta;
- const struct sr_datafeed_logic *logic;
- const struct sr_config *src;
- struct context *ctx;
- GSList *l;
- int idx, offset;
- uint64_t i, j;
- gchar *p, c;
-
- *out = NULL;
- if (!o || !o->sdi)
- return SR_ERR_ARG;
- if (!(ctx = o->internal))
- return SR_ERR_ARG;
-
- switch (packet->type) {
- case SR_DF_META:
- meta = packet->payload;
- for (l = meta->config; l; l = l->next) {
- src = l->data;
- if (src->key != SR_CONF_SAMPLERATE)
- continue;
- ctx->samplerate = g_variant_get_uint64(src->data);
- }
- break;
- case SR_DF_TRIGGER:
- ctx->trigger = ctx->spl_cnt;
- break;
- case SR_DF_LOGIC:
- if (!ctx->header_done) {
- *out = gen_header(o);
- ctx->header_done = TRUE;
- } else
- *out = g_string_sized_new(512);
-
- logic = packet->payload;
- for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
- ctx->spl_cnt++;
- for (j = 0; j < ctx->num_enabled_channels; j++) {
- idx = ctx->channel_index[j];
- p = logic->data + i + idx / 8;
- c = (*p & (1 << (idx % 8))) ? '1' : '0';
- g_string_append_c(ctx->lines[j], c);
-
- if (ctx->spl_cnt == ctx->samples_per_line) {
- /* Flush line buffers. */
- g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
- g_string_append_c(*out, '\n');
- if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
- offset = ctx->trigger + ctx->trigger / 8;
- g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
- ctx->trigger = -1;
- }
- g_string_printf(ctx->lines[j], "%s:", ctx->channel_names[j]);
- } else if ((ctx->spl_cnt & 7) == 0) {
- /* Add a space every 8th bit. */
- g_string_append_c(ctx->lines[j], ' ');
- }
- }
- if (ctx->spl_cnt == ctx->samples_per_line)
- /* Line buffers were already flushed. */
- ctx->spl_cnt = 0;
- }
- break;
- case SR_DF_END:
- if (ctx->spl_cnt) {
- /* Line buffers need flushing. */
- *out = g_string_sized_new(512);
- for (i = 0; i < ctx->num_enabled_channels; i++) {
- g_string_append_len(*out, ctx->lines[i]->str, ctx->lines[i]->len);
- g_string_append_c(*out, '\n');
- }
- }
- break;
- }
-
- return SR_OK;
-}
-
-static int cleanup(struct sr_output *o)
-{
- struct context *ctx;
- unsigned int i;
-
- if (!o)
- return SR_ERR_ARG;
-
- if (!(ctx = o->internal))
- return SR_OK;
-
- g_free(ctx->channel_index);
- g_free(ctx->channel_names);
- for (i = 0; i < ctx->num_enabled_channels; i++)
- g_string_free(ctx->lines[i], TRUE);
- g_free(ctx->lines);
- g_free(ctx);
- o->internal = NULL;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_output_format output_bits = {
- .id = "bits",
- .description = "Bits",
- .init = init,
- .receive = receive,
- .cleanup = cleanup,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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 2 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 <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "output/chronovu-la8"
-
-struct context {
- unsigned int num_enabled_channels;
- gboolean triggered;
- uint64_t samplerate;
- uint64_t samplecount;
- int *channel_index;
- GString *pretrig_buf;
-};
-
-/**
- * Check if the given samplerate is supported by the LA8 hardware.
- *
- * @param samplerate The samplerate (in Hz) to check.
- *
- * @return 1 if the samplerate is supported/valid, 0 otherwise.
- */
-static gboolean is_valid_samplerate(uint64_t samplerate)
-{
- unsigned int i;
-
- for (i = 0; i < 255; i++) {
- if (samplerate == (SR_MHZ(100) / (i + 1)))
- return TRUE;
- }
-
- return FALSE;
-}
-
-/**
- * Convert a samplerate (in Hz) to the 'divcount' value the LA8 wants.
- *
- * LA8 hardware: sample period = (divcount + 1) * 10ns.
- * Min. value for divcount: 0x00 (10ns sample period, 100MHz samplerate).
- * Max. value for divcount: 0xfe (2550ns sample period, 392.15kHz samplerate).
- *
- * @param samplerate The samplerate in Hz.
- *
- * @return The divcount value as needed by the hardware, or 0xff upon errors.
- */
-static uint8_t samplerate_to_divcount(uint64_t samplerate)
-{
- if (samplerate == 0 || !is_valid_samplerate(samplerate)) {
- sr_warn("Invalid samplerate (%" PRIu64 "Hz)", samplerate);
- return 0xff;
- }
-
- return (SR_MHZ(100) / samplerate) - 1;
-}
-
-static int init(struct sr_output *o)
-{
- struct context *ctx;
- struct sr_channel *ch;
- GSList *l;
-
- if (!o || !o->sdi)
- return SR_ERR_ARG;
-
- ctx = g_malloc0(sizeof(struct context));
- o->internal = ctx;
-
- for (l = o->sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->num_enabled_channels++;
- }
- ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
- ctx->pretrig_buf = g_string_sized_new(1024);
-
- return SR_OK;
-}
-
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
- GString **out)
-{
- const struct sr_datafeed_logic *logic;
- struct context *ctx;
- GVariant *gvar;
- uint64_t samplerate;
- gchar c[4];
-
- *out = NULL;
- if (!o || !o->sdi)
- return SR_ERR_ARG;
- if (!(ctx = o->internal))
- return SR_ERR_ARG;
-
- switch (packet->type) {
- case SR_DF_HEADER:
- /* One byte for the 'divcount' value. */
- if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
- &gvar) == SR_OK) {
- samplerate = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
- } else
- samplerate = 0;
- c[0] = samplerate_to_divcount(samplerate);
- *out = g_string_new_len(c, 1);
- ctx->triggered = FALSE;
- break;
- case SR_DF_TRIGGER:
- /* Four bytes (little endian) for the trigger point. */
- c[0] = ctx->samplecount & 0xff;
- c[1] = (ctx->samplecount >> 8) & 0xff;
- c[2] = (ctx->samplecount >> 16) & 0xff;
- c[3] = (ctx->samplecount >> 24) & 0xff;
- *out = g_string_new_len(c, 4);
- /* Flush the pre-trigger buffer. */
- if (ctx->pretrig_buf->len)
- g_string_append_len(*out, ctx->pretrig_buf->str,
- ctx->pretrig_buf->len);
- ctx->triggered = TRUE;
- break;
- case SR_DF_LOGIC:
- logic = packet->payload;
- if (!ctx->triggered)
- g_string_append_len(ctx->pretrig_buf, logic->data, logic->length);
- else
- *out = g_string_new_len(logic->data, logic->length);
- ctx->samplecount += logic->length / logic->unitsize;
- break;
- case SR_DF_END:
- if (!ctx->triggered && ctx->pretrig_buf->len) {
- /* We never got a trigger, submit an empty one. */
- *out = g_string_sized_new(ctx->pretrig_buf->len + 4);
- g_string_append_len(*out, "\x00\x00\x00\x00", 4);
- g_string_append_len(*out, ctx->pretrig_buf->str, ctx->pretrig_buf->len);
- }
- break;
- }
-
- return SR_OK;
-}
-
-static int cleanup(struct sr_output *o)
-{
- struct context *ctx;
-
- if (!o || !o->sdi)
- return SR_ERR_ARG;
-
- if (o->internal) {
- ctx = o->internal;
- g_string_free(ctx->pretrig_buf, TRUE);
- g_free(ctx->channel_index);
- g_free(o->internal);
- o->internal = NULL;
- }
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_output_format output_chronovu_la8 = {
- .id = "chronovu-la8",
- .description = "ChronoVu LA8",
- .init = init,
- .receive = receive,
- .cleanup = cleanup,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "config.h" /* Needed for PACKAGE_STRING and others. */
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "output/csv"
-
-struct context {
- unsigned int num_enabled_channels;
- uint64_t samplerate;
- char separator;
- gboolean header_done;
- int *channel_index;
-};
-
-/*
- * TODO:
- * - Option to specify delimiter character and/or string.
- * - Option to (not) print metadata as comments.
- * - Option to specify the comment character(s), e.g. # or ; or C/C++-style.
- * - Option to (not) print samplenumber / time as extra column.
- * - Option to "compress" output (only print changed samples, VCD-like).
- * - Option to print comma-separated bits, or whole bytes/words (for 8/16
- * channel LAs) as ASCII/hex etc. etc.
- * - Trigger support.
- */
-
-static int init(struct sr_output *o)
-{
- struct context *ctx;
- struct sr_channel *ch;
- GSList *l;
- int i;
-
- if (!o || !o->sdi)
- return SR_ERR_ARG;
-
- ctx = g_malloc0(sizeof(struct context));
- o->internal = ctx;
- ctx->separator = ',';
-
- /* Get the number of channels, and the unitsize. */
- for (l = o->sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->num_enabled_channels++;
- }
- ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
-
- /* Once more to map the enabled channels. */
- for (i = 0, l = o->sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->channel_index[i++] = ch->index;
- }
-
- return SR_OK;
-}
-
-static GString *gen_header(struct sr_output *o)
-{
- struct context *ctx;
- struct sr_channel *ch;
- GVariant *gvar;
- GString *header;
- GSList *l;
- time_t t;
- int num_channels, i;
- char *samplerate_s;
-
- ctx = o->internal;
- header = g_string_sized_new(512);
-
- /* Some metadata */
- t = time(NULL);
- g_string_append_printf(header, "; CSV, generated by %s on %s",
- PACKAGE_STRING, ctime(&t));
-
- /* Columns / channels */
- num_channels = g_slist_length(o->sdi->channels);
- g_string_append_printf(header, "; Channels (%d/%d):",
- ctx->num_enabled_channels, num_channels);
- for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- g_string_append_printf(header, " %s,", ch->name);
- }
- if (o->sdi->channels)
- /* Drop last separator. */
- g_string_truncate(header, header->len - 1);
- g_string_append_printf(header, "\n");
-
- if (ctx->samplerate == 0) {
- if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
- &gvar) == SR_OK) {
- ctx->samplerate = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
- }
- }
- if (ctx->samplerate != 0) {
- samplerate_s = sr_samplerate_string(ctx->samplerate);
- g_string_append_printf(header, "; Samplerate: %s\n", samplerate_s);
- g_free(samplerate_s);
- }
-
- return header;
-}
-
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
- GString **out)
-{
- const struct sr_datafeed_meta *meta;
- const struct sr_datafeed_logic *logic;
- const struct sr_config *src;
- GSList *l;
- struct context *ctx;
- int idx;
- uint64_t i, j;
- gchar *p, c;
-
- *out = NULL;
- if (!o || !o->sdi)
- return SR_ERR_ARG;
- if (!(ctx = o->internal))
- return SR_ERR_ARG;
-
- switch (packet->type) {
- case SR_DF_META:
- meta = packet->payload;
- for (l = meta->config; l; l = l->next) {
- src = l->data;
- if (src->key != SR_CONF_SAMPLERATE)
- continue;
- ctx->samplerate = g_variant_get_uint64(src->data);
- }
- break;
- case SR_DF_LOGIC:
- logic = packet->payload;
- if (!ctx->header_done) {
- *out = gen_header(o);
- ctx->header_done = TRUE;
- } else {
- *out = g_string_sized_new(512);
- }
-
- for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
- for (j = 0; j < ctx->num_enabled_channels; j++) {
- idx = ctx->channel_index[j];
- p = logic->data + i + idx / 8;
- c = *p & (1 << (idx % 8));
- g_string_append_c(*out, c ? '1' : '0');
- g_string_append_c(*out, ctx->separator);
- }
- if (j) {
- /* Drop last separator. */
- g_string_truncate(*out, (*out)->len - 1);
- }
- g_string_append_printf(*out, "\n");
- }
- break;
- }
-
- return SR_OK;
-}
-
-static int cleanup(struct sr_output *o)
-{
- struct context *ctx;
-
- if (!o || !o->sdi)
- return SR_ERR_ARG;
-
- if (o->internal) {
- ctx = o->internal;
- g_free(ctx->channel_index);
- g_free(o->internal);
- o->internal = NULL;
- }
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_output_format output_csv = {
- .id = "csv",
- .description = "Comma-separated values (CSV)",
- .init = init,
- .receive = receive,
- .cleanup = cleanup,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "config.h" /* Needed for PACKAGE_STRING and others. */
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "output/gnuplot"
-
-struct context {
- unsigned int num_enabled_channels;
- uint64_t samplerate;
- uint64_t samplecount;
- gboolean header_done;
- uint8_t *prevsample;
- int *channel_index;
-};
-
-static const char *gnuplot_header = "\
-# Sample data in space-separated columns format usable by gnuplot.\n";
-static const char *gnuplot_header2 = "\
-#\n# Column\tChannel\n\
-# -----------------------------------------------------------------------------\n\
-# 0\t\tSample counter (for internal gnuplot purposes)\n";
-
-
-static int init(struct sr_output *o)
-{
- struct context *ctx;
- struct sr_channel *ch;
- GSList *l;
- unsigned int i;
-
- if (!o || !o->sdi)
- return SR_ERR_ARG;
-
- ctx = g_malloc0(sizeof(struct context));
- o->internal = ctx;
- ctx->num_enabled_channels = 0;
- for (l = o->sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->num_enabled_channels++;
- }
- if (ctx->num_enabled_channels <= 0) {
- sr_err("No logic channel enabled.");
- return SR_ERR;
- }
- ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
-
- /* Once more to map the enabled channels. */
- for (i = 0, l = o->sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->channel_index[i++] = ch->index;
- }
-
- return SR_OK;
-}
-
-static GString *gen_header(struct sr_output *o)
-{
- struct context *ctx;
- struct sr_channel *ch;
- GVariant *gvar;
- GString *header;
- time_t t;
- unsigned int num_channels, i;
- char *samplerate_s;
-
- ctx = o->internal;
- if (ctx->samplerate == 0) {
- if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
- &gvar) == SR_OK) {
- ctx->samplerate = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
- }
- }
-
- t = time(NULL);
- header = g_string_sized_new(512);
- g_string_printf(header, "%s", gnuplot_header);
- g_string_append_printf(header, "# Generated by %s on %s",
- PACKAGE_STRING, ctime(&t));
-
- num_channels = g_slist_length(o->sdi->channels);
- g_string_append_printf(header, "# Acquisition with %d/%d channels",
- ctx->num_enabled_channels, num_channels);
- if (ctx->samplerate != 0) {
- samplerate_s = sr_samplerate_string(ctx->samplerate);
- g_string_append_printf(header, " at %s", samplerate_s);
- g_free(samplerate_s);
- }
- g_string_append_printf(header, "\n");
-
- g_string_append_printf(header, "%s", gnuplot_header2);
-
- /* Columns / channels */
- for (i = 0; i < ctx->num_enabled_channels; i++) {
- ch = g_slist_nth_data(o->sdi->channels, ctx->channel_index[i]);
- g_string_append_printf(header, "# %d\t\t%s\n", i + 1, ch->name);
- }
-
- return header;
-}
-
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
- GString **out)
-{
- const struct sr_datafeed_meta *meta;
- const struct sr_datafeed_logic *logic;
- const struct sr_config *src;
- GSList *l;
- struct context *ctx;
- const uint8_t *sample;
- unsigned int curbit, p, idx, i;
-
- *out = NULL;
- if (!o || !o->internal)
- return SR_ERR_BUG;
- ctx = o->internal;
-
- if (packet->type == SR_DF_META) {
- meta = packet->payload;
- for (l = meta->config; l; l = l->next) {
- src = l->data;
- if (src->key != SR_CONF_SAMPLERATE)
- continue;
- ctx->samplerate = g_variant_get_uint64(src->data);
- }
- }
-
- if (packet->type != SR_DF_LOGIC)
- return SR_OK;
- logic = packet->payload;
-
- if (!ctx->prevsample) {
- /* Can't allocate this until we know the stream's unitsize. */
- ctx->prevsample = g_malloc0(logic->unitsize);
- }
-
- if (!ctx->header_done) {
- *out = gen_header(o);
- ctx->header_done = TRUE;
- } else {
- *out = g_string_sized_new(512);
- }
-
- for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
- sample = logic->data + i;
- ctx->samplecount++;
-
- /*
- * Don't output the same sample multiple times, but make
- * sure to output at least the first and last sample.
- */
- if (i > 0 && i < logic->length - logic->unitsize) {
- if (!memcmp(sample, ctx->prevsample, logic->unitsize))
- continue;
- }
- memcpy(ctx->prevsample, sample, logic->unitsize);
-
- /* The first column is a counter (needed for gnuplot). */
- g_string_append_printf(*out, "%" PRIu64 "\t", ctx->samplecount);
-
- /* The next columns are the values of all channels. */
- for (p = 0; p < ctx->num_enabled_channels; p++) {
- idx = ctx->channel_index[p];
- curbit = (sample[idx / 8] & ((uint8_t) (1 << (idx % 8)))) >> (idx % 8);
- g_string_append_printf(*out, "%d ", curbit);
- }
- g_string_append_printf(*out, "\n");
- }
-
- return SR_OK;
-}
-
-static int cleanup(struct sr_output *o)
-{
- struct context *ctx;
-
- if (!o || !o->internal)
- return SR_ERR_BUG;
- ctx = o->internal;
- g_free(ctx->channel_index);
- g_free(ctx->prevsample);
- g_free(ctx);
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_output_format output_gnuplot = {
- .id = "gnuplot",
- .description = "Gnuplot",
- .init = init,
- .receive = receive,
- .cleanup = cleanup,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "output/hex"
-
-#define DEFAULT_SAMPLES_PER_LINE 192
-
-struct context {
- unsigned int num_enabled_channels;
- int samples_per_line;
- int bit_cnt;
- int spl_cnt;
- int trigger;
- uint64_t samplerate;
- int *channel_index;
- char **channel_names;
- char **line_values;
- uint8_t *sample_buf;
- gboolean header_done;
- GString **lines;
-};
-
-static int init(struct sr_output *o)
-{
- struct context *ctx;
- struct sr_channel *ch;
- GSList *l;
- GHashTableIter iter;
- gpointer key, value;
- unsigned int i, j;
- int spl;
-
- if (!o || !o->sdi)
- return SR_ERR_ARG;
-
- spl = DEFAULT_SAMPLES_PER_LINE;
- g_hash_table_iter_init(&iter, o->params);
- while (g_hash_table_iter_next(&iter, &key, &value)) {
- if (!strcmp(key, "width")) {
- if ((spl = strtoul(value, NULL, 10)) < 1) {
- sr_err("Invalid width.");
- return SR_ERR_ARG;
- }
- } else {
- sr_err("Unknown parameter '%s'.", key);
- return SR_ERR_ARG;
- }
- }
-
- ctx = g_malloc0(sizeof(struct context));
- o->internal = ctx;
- ctx->trigger = -1;
- ctx->samples_per_line = spl;
-
- for (l = o->sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->num_enabled_channels++;
- }
- ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
- ctx->channel_names = g_malloc(sizeof(char *) * ctx->num_enabled_channels);
- ctx->lines = g_malloc(sizeof(GString *) * ctx->num_enabled_channels);
- ctx->sample_buf = g_malloc(ctx->num_enabled_channels);
-
- j = 0;
- for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->channel_index[j] = ch->index;
- ctx->channel_names[j] = ch->name;
- ctx->lines[j] = g_string_sized_new(80);
- ctx->sample_buf[j] = 0;
- g_string_printf(ctx->lines[j], "%s:", ch->name);
- j++;
- }
-
- return SR_OK;
-}
-
-static GString *gen_header(struct sr_output *o)
-{
- struct context *ctx;
- GVariant *gvar;
- GString *header;
- int num_channels;
- char *samplerate_s;
-
- ctx = o->internal;
- if (ctx->samplerate == 0) {
- if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
- &gvar) == SR_OK) {
- ctx->samplerate = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
- }
- }
-
- header = g_string_sized_new(512);
- g_string_printf(header, "%s\n", PACKAGE_STRING);
- num_channels = g_slist_length(o->sdi->channels);
- g_string_append_printf(header, "Acquisition with %d/%d channels",
- ctx->num_enabled_channels, num_channels);
- if (ctx->samplerate != 0) {
- samplerate_s = sr_samplerate_string(ctx->samplerate);
- g_string_append_printf(header, " at %s", samplerate_s);
- g_free(samplerate_s);
- }
- g_string_append_printf(header, "\n");
-
- return header;
-}
-
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
- GString **out)
-{
- const struct sr_datafeed_meta *meta;
- const struct sr_datafeed_logic *logic;
- const struct sr_config *src;
- GSList *l;
- struct context *ctx;
- int idx, pos, offset;
- uint64_t i, j;
- gchar *p;
-
- *out = NULL;
- if (!o || !o->sdi)
- return SR_ERR_ARG;
- if (!(ctx = o->internal))
- return SR_ERR_ARG;
-
- switch (packet->type) {
- case SR_DF_META:
- meta = packet->payload;
- for (l = meta->config; l; l = l->next) {
- src = l->data;
- if (src->key != SR_CONF_SAMPLERATE)
- continue;
- ctx->samplerate = g_variant_get_uint64(src->data);
- }
- break;
- case SR_DF_TRIGGER:
- ctx->trigger = ctx->spl_cnt;
- break;
- case SR_DF_LOGIC:
- if (!ctx->header_done) {
- *out = gen_header(o);
- ctx->header_done = TRUE;
- } else
- *out = g_string_sized_new(512);
-
- logic = packet->payload;
- for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
- ctx->spl_cnt++;
- pos = ctx->spl_cnt & 7;
- for (j = 0; j < ctx->num_enabled_channels; j++) {
- idx = ctx->channel_index[j];
- p = logic->data + i + idx / 8;
- ctx->sample_buf[j] <<= 1;
- if (*p & (1 << (idx % 8)))
- ctx->sample_buf[j] |= 1;
- if (ctx->spl_cnt && pos == 0) {
- /* Buffered a byte's worth, output hex. */
- g_string_append_printf(ctx->lines[j], "%.2x ",
- ctx->sample_buf[j]);
- ctx->sample_buf[j] = 0;
- }
-
- if (ctx->spl_cnt == ctx->samples_per_line) {
- /* Flush line buffers. */
- g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
- g_string_append_c(*out, '\n');
- if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
- offset = ctx->trigger + ctx->trigger / 8;
- g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
- ctx->trigger = -1;
- }
- g_string_printf(ctx->lines[j], "%s:", ctx->channel_names[j]);
- }
- }
- if (ctx->spl_cnt == ctx->samples_per_line)
- /* Line buffers were already flushed. */
- ctx->spl_cnt = 0;
- }
- break;
- case SR_DF_END:
- if (ctx->spl_cnt) {
- /* Line buffers need flushing. */
- *out = g_string_sized_new(512);
- for (i = 0; i < ctx->num_enabled_channels; i++) {
- if (ctx->spl_cnt & 7)
- g_string_append_printf(ctx->lines[i], "%.2x ",
- ctx->sample_buf[i] << (8 - (ctx->spl_cnt & 7)));
- g_string_append_len(*out, ctx->lines[i]->str, ctx->lines[i]->len);
- g_string_append_c(*out, '\n');
- }
- }
- break;
- }
-
- return SR_OK;
-}
-
-static int cleanup(struct sr_output *o)
-{
- struct context *ctx;
- unsigned int i;
-
- if (!o)
- return SR_ERR_ARG;
-
- if (!(ctx = o->internal))
- return SR_OK;
-
- g_free(ctx->channel_index);
- g_free(ctx->sample_buf);
- g_free(ctx->channel_names);
- for (i = 0; i < ctx->num_enabled_channels; i++)
- g_string_free(ctx->lines[i], TRUE);
- g_free(ctx->lines);
- g_free(ctx);
- o->internal = NULL;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_output_format output_hex = {
- .id = "hex",
- .description = "Hexadecimal",
- .init = init,
- .receive = receive,
- .cleanup = cleanup,
-};
-
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * This implements version 1.3 of the output format for the OpenBench Logic
- * Sniffer "Alternative" Java client. Details:
- * https://github.com/jawi/ols/wiki/OLS-data-file-format
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "output/ols"
-
-struct context {
- uint64_t samplerate;
- uint64_t num_samples;
-};
-
-static int init(struct sr_output *o)
-{
- struct context *ctx;
-
- if (!(ctx = g_try_malloc(sizeof(struct context)))) {
- sr_err("%s: ctx malloc failed", __func__);
- return SR_ERR_MALLOC;
- }
- o->internal = ctx;
-
- ctx->samplerate = 0;
- ctx->num_samples = 0;
-
- return SR_OK;
-}
-
-static GString *gen_header(const struct sr_dev_inst *sdi, struct context *ctx)
-{
- struct sr_channel *ch;
- GSList *l;
- GString *s;
- GVariant *gvar;
- int num_enabled_channels;
-
- if (!ctx->samplerate && sr_config_get(sdi->driver, sdi, NULL,
- SR_CONF_SAMPLERATE, &gvar) == SR_OK) {
- ctx->samplerate = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
- }
-
- num_enabled_channels = 0;
- for (l = sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- num_enabled_channels++;
- }
-
- s = g_string_sized_new(512);
- g_string_append_printf(s, ";Rate: %"PRIu64"\n", ctx->samplerate);
- g_string_append_printf(s, ";Channels: %d\n", num_enabled_channels);
- g_string_append_printf(s, ";EnabledChannels: -1\n");
- g_string_append_printf(s, ";Compressed: true\n");
- g_string_append_printf(s, ";CursorEnabled: false\n");
-
- return s;
-}
-
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
- GString **out)
-{
- struct context *ctx;
- const struct sr_datafeed_meta *meta;
- const struct sr_datafeed_logic *logic;
- const struct sr_config *src;
- GSList *l;
- unsigned int i, j;
- uint8_t c;
-
- *out = NULL;
- if (!o || !o->sdi)
- return SR_ERR_ARG;
- ctx = o->internal;
-
- switch (packet->type) {
- case SR_DF_META:
- meta = packet->payload;
- for (l = meta->config; l; l = l->next) {
- src = l->data;
- if (src->key == SR_CONF_SAMPLERATE)
- ctx->samplerate = g_variant_get_uint64(src->data);
- }
- break;
- case SR_DF_LOGIC:
- logic = packet->payload;
- if (ctx->num_samples == 0) {
- /* First logic packet in the feed. */
- *out = gen_header(o->sdi, ctx);
- } else
- *out = g_string_sized_new(512);
- for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
- for (j = 0; j < logic->unitsize; j++) {
- /* The OLS format wants the samples presented MSB first. */
- c = *((uint8_t *)logic->data + i + logic->unitsize - 1 - j);
- g_string_append_printf(*out, "%02x", c);
- }
- g_string_append_printf(*out, "@%"PRIu64"\n", ctx->num_samples++);
- }
- break;
- }
-
- return SR_OK;
-}
-
-static int cleanup(struct sr_output *o)
-{
- struct context *ctx;
-
- if (!o || !o->sdi)
- return SR_ERR_ARG;
-
- ctx = o->internal;
- g_free(ctx);
- o->internal = NULL;
-
- return SR_OK;
-}
-
-SR_PRIV struct sr_output_format output_ols = {
- .id = "ols",
- .description = "OpenBench Logic Sniffer",
- .init = init,
- .receive = receive,
- .cleanup = cleanup
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
- *
- * 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.h"
-#include "libsigrok-internal.h"
-
-/**
- * @file
- *
- * Output file/data format handling.
- */
-
-/**
- * @defgroup grp_output Output formats
- *
- * Output file/data format handling.
- *
- * libsigrok supports several output (file) formats, e.g. binary, VCD,
- * gnuplot, and so on. It provides an output API that frontends can use.
- * New output formats can be added/implemented in libsigrok without having
- * to change the frontends at all.
- *
- * All output modules are fed data in a stream. Devices that can stream data
- * into libsigrok, instead of storing and then transferring the whole buffer,
- * can thus generate output live.
- *
- * Output modules generate a newly allocated GString. The caller is then
- * expected to free this with g_string_free() when finished with it.
- *
- * @{
- */
-
-/** @cond PRIVATE */
-extern SR_PRIV struct sr_output_format output_bits;
-extern SR_PRIV struct sr_output_format output_hex;
-extern SR_PRIV struct sr_output_format output_ascii;
-extern SR_PRIV struct sr_output_format output_binary;
-extern SR_PRIV struct sr_output_format output_vcd;
-extern SR_PRIV struct sr_output_format output_ols;
-extern SR_PRIV struct sr_output_format output_gnuplot;
-extern SR_PRIV struct sr_output_format output_chronovu_la8;
-extern SR_PRIV struct sr_output_format output_csv;
-extern SR_PRIV struct sr_output_format output_analog;
-/* @endcond */
-
-static struct sr_output_format *output_module_list[] = {
- &output_ascii,
- &output_binary,
- &output_bits,
- &output_csv,
- &output_gnuplot,
- &output_hex,
- &output_ols,
- &output_vcd,
- &output_chronovu_la8,
- &output_analog,
- NULL,
-};
-
-/** @since 0.1.0 */
-SR_API struct sr_output_format **sr_output_list(void)
-{
- return output_module_list;
-}
-
-/** @since 0.3.0 */
-SR_API struct sr_output *sr_output_new(struct sr_output_format *of,
- GHashTable *params, const struct sr_dev_inst *sdi)
-{
- struct sr_output *o;
-
- o = g_malloc(sizeof(struct sr_output));
- o->format = of;
- o->sdi = sdi;
- o->params = params;
- if (o->format->init && o->format->init(o) != SR_OK) {
- g_free(o);
- o = NULL;
- }
-
- return o;
-}
-
-/** @since 0.3.0 */
-SR_API int sr_output_send(struct sr_output *o,
- const struct sr_datafeed_packet *packet, GString **out)
-{
- return o->format->receive(o, packet, out);
-}
-
-/** @since 0.3.0 */
-SR_API int sr_output_free(struct sr_output *o)
-{
- int ret;
-
- ret = SR_OK;
- if (o->format->cleanup)
- ret = o->format->cleanup(o);
- g_free(o);
-
- return ret;
-}
-
-/** @} */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "config.h" /* Needed for PACKAGE and others. */
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "output/vcd"
-
-struct context {
- int num_enabled_channels;
- GArray *channelindices;
- uint8_t *prevsample;
- gboolean header_done;
- int period;
- int *channel_index;
- uint64_t samplerate;
- uint64_t samplecount;
-};
-
-static const char *const vcd_header_comment =
- "$comment\n Acquisition with %d/%d channels at %s\n$end\n";
-
-static int init(struct sr_output *o)
-{
- struct context *ctx;
- struct sr_channel *ch;
- GSList *l;
- int num_enabled_channels, i;
-
- num_enabled_channels = 0;
- for (l = o->sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- num_enabled_channels++;
- }
- if (num_enabled_channels > 94) {
- sr_err("VCD only supports 94 channels.");
- return SR_ERR;
- }
-
- ctx = g_malloc0(sizeof(struct context));
- o->internal = ctx;
- ctx->num_enabled_channels = num_enabled_channels;
- ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
-
- /* Once more to map the enabled channels. */
- for (i = 0, l = o->sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- ctx->channel_index[i++] = ch->index;
- }
-
- return SR_OK;
-}
-
-static GString *gen_header(struct sr_output *o)
-{
- struct context *ctx;
- struct sr_channel *ch;
- GVariant *gvar;
- GString *header;
- GSList *l;
- time_t t;
- int num_channels, i;
- char *samplerate_s, *frequency_s, *timestamp;
-
- ctx = o->internal;
- header = g_string_sized_new(512);
- num_channels = g_slist_length(o->sdi->channels);
-
- /* timestamp */
- t = time(NULL);
- timestamp = g_strdup(ctime(&t));
- timestamp[strlen(timestamp)-1] = 0;
- g_string_printf(header, "$date %s $end\n", timestamp);
- g_free(timestamp);
-
- /* generator */
- g_string_append_printf(header, "$version %s %s $end\n",
- PACKAGE, PACKAGE_VERSION);
- g_string_append_printf(header, "$comment\n Acquisition with "
- "%d/%d channels", ctx->num_enabled_channels, num_channels);
-
- if (ctx->samplerate == 0) {
- if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
- &gvar) == SR_OK) {
- ctx->samplerate = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
- }
- }
- if (ctx->samplerate != 0) {
- samplerate_s = sr_samplerate_string(ctx->samplerate);
- g_string_append_printf(header, " at %s", samplerate_s);
- g_free(samplerate_s);
- }
- g_string_append_printf(header, "\n$end\n");
-
- /* timescale */
- /* VCD can only handle 1/10/100 (s - fs), so scale up first */
- if (ctx->samplerate > SR_MHZ(1))
- ctx->period = SR_GHZ(1);
- else if (ctx->samplerate > SR_KHZ(1))
- ctx->period = SR_MHZ(1);
- else
- ctx->period = SR_KHZ(1);
- frequency_s = sr_period_string(ctx->period);
- g_string_append_printf(header, "$timescale %s $end\n", frequency_s);
- g_free(frequency_s);
-
- /* scope */
- g_string_append_printf(header, "$scope module %s $end\n", PACKAGE);
-
- /* Wires / channels */
- for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (!ch->enabled)
- continue;
- g_string_append_printf(header, "$var wire 1 %c %s $end\n",
- (char)('!' + i), ch->name);
- }
-
- g_string_append(header, "$upscope $end\n$enddefinitions $end\n");
-
- return header;
-}
-
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
- GString **out)
-{
- const struct sr_datafeed_meta *meta;
- const struct sr_datafeed_logic *logic;
- const struct sr_config *src;
- GSList *l;
- struct context *ctx;
- unsigned int i;
- int p, curbit, prevbit, index;
- uint8_t *sample;
- gboolean timestamp_written;
-
- *out = NULL;
- if (!o || !o->internal)
- return SR_ERR_BUG;
- ctx = o->internal;
-
- switch (packet->type) {
- case SR_DF_META:
- meta = packet->payload;
- for (l = meta->config; l; l = l->next) {
- src = l->data;
- if (src->key != SR_CONF_SAMPLERATE)
- continue;
- ctx->samplerate = g_variant_get_uint64(src->data);
- }
- break;
- case SR_DF_LOGIC:
- logic = packet->payload;
-
- if (!ctx->header_done) {
- *out = gen_header(o);
- ctx->header_done = TRUE;
- } else {
- *out = g_string_sized_new(512);
- }
-
- if (!ctx->prevsample) {
- /* Can't allocate this until we know the stream's unitsize. */
- ctx->prevsample = g_malloc0(logic->unitsize);
- }
-
- for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
- sample = logic->data + i;
- timestamp_written = FALSE;
-
- for (p = 0; p < ctx->num_enabled_channels; p++) {
- index = ctx->channel_index[p];
-
- curbit = ((unsigned)sample[index / 8]
- >> (index % 8)) & 1;
- prevbit = ((unsigned)ctx->prevsample[index / 8]
- >> (index % 8)) & 1;
-
- /* VCD only contains deltas/changes of signals. */
- if (prevbit == curbit && ctx->samplecount > 0)
- continue;
-
- /* Output timestamp of subsequent signal changes. */
- if (!timestamp_written)
- g_string_append_printf(*out, "#%.0f",
- (double)ctx->samplecount /
- ctx->samplerate * ctx->period);
-
- /* Output which signal changed to which value. */
- g_string_append_c(*out, ' ');
- g_string_append_c(*out, '0' + curbit);
- g_string_append_c(*out, '!' + p);
-
- timestamp_written = TRUE;
- }
-
- if (timestamp_written)
- g_string_append_c(*out, '\n');
-
- ctx->samplecount++;
- memcpy(ctx->prevsample, sample, logic->unitsize);
- }
- break;
- case SR_DF_END:
- /* Write final timestamp as length indicator. */
- *out = g_string_sized_new(512);
- g_string_printf(*out, "#%.0f\n",
- (double)ctx->samplecount / ctx->samplerate * ctx->period);
- break;
- }
-
- return SR_OK;
-}
-
-static int cleanup(struct sr_output *o)
-{
- struct context *ctx;
-
- if (!o || !o->internal)
- return SR_ERR_ARG;
-
- ctx = o->internal;
- g_free(ctx->prevsample);
- g_free(ctx->channel_index);
- g_free(ctx);
-
- return SR_OK;
-}
-
-struct sr_output_format output_vcd = {
- .id = "vcd",
- .description = "Value Change Dump (VCD)",
- .init = init,
- .receive = receive,
- .cleanup = cleanup,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/** @cond PRIVATE */
-#define LOG_PREFIX "session"
-/** @endcond */
-
-/**
- * @file
- *
- * Creating, using, or destroying libsigrok sessions.
- */
-
-/**
- * @defgroup grp_session Session handling
- *
- * Creating, using, or destroying libsigrok sessions.
- *
- * @{
- */
-
-struct source {
- int timeout;
- sr_receive_data_callback cb;
- void *cb_data;
-
- /* This is used to keep track of the object (fd, pollfd or channel) which is
- * being polled and will be used to match the source when removing it again.
- */
- gintptr poll_object;
-};
-
-struct datafeed_callback {
- sr_datafeed_callback cb;
- void *cb_data;
-};
-
-/**
- * Create a new session.
- * Currently, there can be only one session at a time within the same process.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_BUG A session exists already.
- *
- * @since 0.4.0
- */
-SR_API int sr_session_new(struct sr_session **new_session)
-{
- struct sr_session *session;
-
- session = g_malloc0(sizeof(struct sr_session));
-
- session->source_timeout = -1;
- session->running = FALSE;
- session->abort_session = FALSE;
- g_mutex_init(&session->stop_mutex);
-
- *new_session = session;
-
- return SR_OK;
-}
-
-/**
- * Destroy a session.
- * This frees up all memory used by the session.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid session passed.
- *
- * @since 0.4.0
- */
-SR_API int sr_session_destroy(struct sr_session *session)
-{
- if (!session) {
- sr_err("%s: session was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- sr_session_dev_remove_all(session);
- g_mutex_clear(&session->stop_mutex);
- if (session->trigger)
- sr_trigger_free(session->trigger);
-
- g_free(session);
-
- return SR_OK;
-}
-
-/**
- * Remove all the devices from a session.
- *
- * The session itself (i.e., the struct sr_session) is not free'd and still
- * exists after this function returns.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_BUG Invalid session passed.
- *
- * @since 0.4.0
- */
-SR_API int sr_session_dev_remove_all(struct sr_session *session)
-{
- struct sr_dev_inst *sdi;
- GSList *l;
-
- if (!session) {
- sr_err("%s: session was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- for (l = session->devs; l; l = l->next) {
- sdi = (struct sr_dev_inst *) l->data;
- sdi->session = NULL;
- }
-
- g_slist_free(session->devs);
- session->devs = NULL;
-
- return SR_OK;
-}
-
-/**
- * Add a device instance to a session.
- *
- * @param sdi The device instance to add to a session. Must not
- * be NULL. Also, sdi->driver and sdi->driver->dev_open must
- * not be NULL.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- *
- * @since 0.4.0
- */
-SR_API int sr_session_dev_add(struct sr_session *session,
- struct sr_dev_inst *sdi)
-{
- int ret;
-
- if (!sdi) {
- sr_err("%s: sdi was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- if (!session) {
- sr_err("%s: session was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- /* If sdi->session is not NULL, the device is already in this or
- * another session. */
- if (sdi->session) {
- sr_err("%s: already assigned to session", __func__);
- return SR_ERR_ARG;
- }
-
- /* If sdi->driver is NULL, this is a virtual device. */
- if (!sdi->driver) {
- sr_dbg("%s: sdi->driver was NULL, this seems to be "
- "a virtual device; continuing", __func__);
- /* Just add the device, don't run dev_open(). */
- session->devs = g_slist_append(session->devs, (gpointer)sdi);
- sdi->session = session;
- return SR_OK;
- }
-
- /* sdi->driver is non-NULL (i.e. we have a real device). */
- if (!sdi->driver->dev_open) {
- sr_err("%s: sdi->driver->dev_open was NULL", __func__);
- return SR_ERR_BUG;
- }
-
- session->devs = g_slist_append(session->devs, (gpointer)sdi);
- sdi->session = session;
-
- if (session->running) {
- /* Adding a device to a running session. Commit settings
- * and start acquisition on that device now. */
- if ((ret = sr_config_commit(sdi)) != SR_OK) {
- sr_err("Failed to commit device settings before "
- "starting acquisition in running session (%s)",
- sr_strerror(ret));
- return ret;
- }
- if ((ret = sdi->driver->dev_acquisition_start(sdi,
- (void *)sdi)) != SR_OK) {
- sr_err("Failed to start acquisition of device in "
- "running session (%s)", sr_strerror(ret));
- return ret;
- }
- }
-
- return SR_OK;
-}
-
-/**
- * List all device instances attached to a session.
- *
- * @param devlist A pointer where the device instance list will be
- * stored on return. If no devices are in the session,
- * this will be NULL. Each element in the list points
- * to a struct sr_dev_inst *.
- * The list must be freed by the caller, but not the
- * elements pointed to.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- *
- * @since 0.4.0
- */
-SR_API int sr_session_dev_list(struct sr_session *session, GSList **devlist)
-{
- if (!session)
- return SR_ERR_ARG;
-
- if (!devlist)
- return SR_ERR_ARG;
-
- *devlist = g_slist_copy(session->devs);
-
- return SR_OK;
-}
-
-/**
- * Remove all datafeed callbacks in a session.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid session passed.
- *
- * @since 0.4.0
- */
-SR_API int sr_session_datafeed_callback_remove_all(struct sr_session *session)
-{
- if (!session) {
- sr_err("%s: session was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- g_slist_free_full(session->datafeed_callbacks, g_free);
- session->datafeed_callbacks = NULL;
-
- return SR_OK;
-}
-
-/**
- * Add a datafeed callback to a session.
- *
- * @param cb Function to call when a chunk of data is received.
- * Must not be NULL.
- * @param cb_data Opaque pointer passed in by the caller.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_BUG No session exists.
- *
- * @since 0.3.0
- */
-SR_API int sr_session_datafeed_callback_add(struct sr_session *session,
- sr_datafeed_callback cb, void *cb_data)
-{
- struct datafeed_callback *cb_struct;
-
- if (!session) {
- sr_err("%s: session was NULL", __func__);
- return SR_ERR_BUG;
- }
-
- if (!cb) {
- sr_err("%s: cb was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- if (!(cb_struct = g_try_malloc0(sizeof(struct datafeed_callback))))
- return SR_ERR_MALLOC;
-
- cb_struct->cb = cb;
- cb_struct->cb_data = cb_data;
-
- session->datafeed_callbacks =
- g_slist_append(session->datafeed_callbacks, cb_struct);
-
- return SR_OK;
-}
-
-SR_API struct sr_trigger *sr_session_trigger_get(struct sr_session *session)
-{
- return session->trigger;
-}
-
-SR_API int sr_session_trigger_set(struct sr_session *session, struct sr_trigger *trig)
-{
- session->trigger = trig;
-
- return SR_OK;
-}
-
-/**
- * Call every device in the current session's callback.
- *
- * For sessions not driven by select loops such as sr_session_run(),
- * but driven by another scheduler, this can be used to poll the devices
- * from within that scheduler.
- *
- * @param block If TRUE, this call will wait for any of the session's
- * sources to fire an event on the file descriptors, or
- * any of their timeouts to activate. In other words, this
- * can be used as a select loop.
- * If FALSE, all sources have their callback run, regardless
- * of file descriptor or timeout status.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Error occured.
- */
-static int sr_session_iteration(struct sr_session *session, gboolean block)
-{
- unsigned int i;
- int ret;
-
- ret = g_poll(session->pollfds, session->num_sources,
- block ? session->source_timeout : 0);
- for (i = 0; i < session->num_sources; i++) {
- if (session->pollfds[i].revents > 0 || (ret == 0
- && session->source_timeout == session->sources[i].timeout)) {
- /*
- * Invoke the source's callback on an event,
- * or if the poll timed out and this source
- * asked for that timeout.
- */
- if (!session->sources[i].cb(session->pollfds[i].fd,
- session->pollfds[i].revents,
- session->sources[i].cb_data))
- sr_session_source_remove(session,
- session->sources[i].poll_object);
- }
- /*
- * We want to take as little time as possible to stop
- * the session if we have been told to do so. Therefore,
- * we check the flag after processing every source, not
- * just once per main event loop.
- */
- g_mutex_lock(&session->stop_mutex);
- if (session->abort_session) {
- sr_session_stop_sync(session);
- /* But once is enough. */
- session->abort_session = FALSE;
- }
- g_mutex_unlock(&session->stop_mutex);
- }
-
- return SR_OK;
-}
-
-
-static int verify_trigger(struct sr_trigger *trigger)
-{
- struct sr_trigger_stage *stage;
- struct sr_trigger_match *match;
- GSList *l, *m;
-
- if (!trigger->stages) {
- sr_err("No trigger stages defined.");
- return SR_ERR;
- }
-
- sr_spew("Checking trigger:");
- for (l = trigger->stages; l; l = l->next) {
- stage = l->data;
- if (!stage->matches) {
- sr_err("Stage %d has no matches defined.", stage->stage);
- return SR_ERR;
- }
- for (m = stage->matches; m; m = m->next) {
- match = m->data;
- if (!match->channel) {
- sr_err("Stage %d match has no channel.", stage->stage);
- return SR_ERR;
- }
- if (!match->match) {
- sr_err("Stage %d match is not defined.", stage->stage);
- return SR_ERR;
- }
- sr_spew("Stage %d match on channel %s, match %d", stage->stage,
- match->channel->name, match->match);
- }
- }
-
- return SR_OK;
-}
-/**
- * Start a session.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid session passed.
- *
- * @since 0.4.0
- */
-SR_API int sr_session_start(struct sr_session *session)
-{
- struct sr_dev_inst *sdi;
- GSList *l;
- int ret;
-
- if (!session) {
- sr_err("%s: session was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- if (!session->devs) {
- sr_err("%s: session->devs was NULL; a session "
- "cannot be started without devices.", __func__);
- return SR_ERR_ARG;
- }
-
- if (session->trigger && verify_trigger(session->trigger) != SR_OK)
- return SR_ERR;
-
- sr_info("Starting.");
-
- ret = SR_OK;
- for (l = session->devs; l; l = l->next) {
- sdi = l->data;
- if ((ret = sr_config_commit(sdi)) != SR_OK) {
- sr_err("Failed to commit device settings before "
- "starting acquisition (%s)", sr_strerror(ret));
- break;
- }
- if ((ret = sdi->driver->dev_acquisition_start(sdi, sdi)) != SR_OK) {
- sr_err("%s: could not start an acquisition "
- "(%s)", __func__, sr_strerror(ret));
- break;
- }
- }
-
- /* TODO: What if there are multiple devices? Which return code? */
-
- return ret;
-}
-
-/**
- * Run a session.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid session passed.
- *
- * @since 0.4.0
- */
-SR_API int sr_session_run(struct sr_session *session)
-{
- if (!session) {
- sr_err("%s: session was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- if (!session->devs) {
- /* TODO: Actually the case? */
- sr_err("%s: session->devs was NULL; a session "
- "cannot be run without devices.", __func__);
- return SR_ERR_ARG;
- }
- session->running = TRUE;
-
- sr_info("Running.");
-
- /* Do we have real sources? */
- if (session->num_sources == 1 && session->pollfds[0].fd == -1) {
- /* Dummy source, freewheel over it. */
- while (session->num_sources)
- session->sources[0].cb(-1, 0, session->sources[0].cb_data);
- } else {
- /* Real sources, use g_poll() main loop. */
- while (session->num_sources)
- sr_session_iteration(session, TRUE);
- }
-
- return SR_OK;
-}
-
-/**
- * Stop a session.
- *
- * The session is stopped immediately, with all acquisition sessions stopped
- * and hardware drivers cleaned up.
- *
- * This must be called from within the session thread, to prevent freeing
- * resources that the session thread will try to use.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid session passed.
- *
- * @private
- */
-SR_PRIV int sr_session_stop_sync(struct sr_session *session)
-{
- struct sr_dev_inst *sdi;
- GSList *l;
-
- if (!session) {
- sr_err("%s: session was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- sr_info("Stopping.");
-
- for (l = session->devs; l; l = l->next) {
- sdi = l->data;
- if (sdi->driver) {
- if (sdi->driver->dev_acquisition_stop)
- sdi->driver->dev_acquisition_stop(sdi, sdi);
- }
- }
- session->running = FALSE;
-
- return SR_OK;
-}
-
-/**
- * Stop a session.
- *
- * The session is stopped immediately, with all acquisition sessions being
- * stopped and hardware drivers cleaned up.
- *
- * If the session is run in a separate thread, this function will not block
- * until the session is finished executing. It is the caller's responsibility
- * to wait for the session thread to return before assuming that the session is
- * completely decommissioned.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid session passed.
- *
- * @since 0.4.0
- */
-SR_API int sr_session_stop(struct sr_session *session)
-{
- if (!session) {
- sr_err("%s: session was NULL", __func__);
- return SR_ERR_BUG;
- }
-
- g_mutex_lock(&session->stop_mutex);
- session->abort_session = TRUE;
- g_mutex_unlock(&session->stop_mutex);
-
- return SR_OK;
-}
-
-/**
- * Debug helper.
- *
- * @param packet The packet to show debugging information for.
- */
-static void datafeed_dump(const struct sr_datafeed_packet *packet)
-{
- const struct sr_datafeed_logic *logic;
- const struct sr_datafeed_analog *analog;
-
- switch (packet->type) {
- case SR_DF_HEADER:
- sr_dbg("bus: Received SR_DF_HEADER packet.");
- break;
- case SR_DF_TRIGGER:
- sr_dbg("bus: Received SR_DF_TRIGGER packet.");
- break;
- case SR_DF_META:
- sr_dbg("bus: Received SR_DF_META packet.");
- break;
- case SR_DF_LOGIC:
- logic = packet->payload;
- sr_dbg("bus: Received SR_DF_LOGIC packet (%" PRIu64 " bytes, "
- "unitsize = %d).", logic->length, logic->unitsize);
- break;
- case SR_DF_ANALOG:
- analog = packet->payload;
- sr_dbg("bus: Received SR_DF_ANALOG packet (%d samples).",
- analog->num_samples);
- break;
- case SR_DF_END:
- sr_dbg("bus: Received SR_DF_END packet.");
- break;
- case SR_DF_FRAME_BEGIN:
- sr_dbg("bus: Received SR_DF_FRAME_BEGIN packet.");
- break;
- case SR_DF_FRAME_END:
- sr_dbg("bus: Received SR_DF_FRAME_END packet.");
- break;
- default:
- sr_dbg("bus: Received unknown packet type: %d.", packet->type);
- break;
- }
-}
-
-/**
- * Send a packet to whatever is listening on the datafeed bus.
- *
- * Hardware drivers use this to send a data packet to the frontend.
- *
- * @param sdi TODO.
- * @param packet The datafeed packet to send to the session bus.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- *
- * @private
- */
-SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
- const struct sr_datafeed_packet *packet)
-{
- GSList *l;
- struct datafeed_callback *cb_struct;
-
- if (!sdi) {
- sr_err("%s: sdi was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- if (!packet) {
- sr_err("%s: packet was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- for (l = sdi->session->datafeed_callbacks; l; l = l->next) {
- if (sr_log_loglevel_get() >= SR_LOG_DBG)
- datafeed_dump(packet);
- cb_struct = l->data;
- cb_struct->cb(sdi, packet, cb_struct->cb_data);
- }
-
- return SR_OK;
-}
-
-/**
- * Add an event source for a file descriptor.
- *
- * @param pollfd The GPollFD.
- * @param[in] timeout Max time to wait before the callback is called,
- * ignored if 0.
- * @param cb Callback function to add. Must not be NULL.
- * @param cb_data Data for the callback function. Can be NULL.
- * @param poll_object TODO.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
- */
-static int _sr_session_source_add(struct sr_session *session, GPollFD *pollfd,
- int timeout, sr_receive_data_callback cb, void *cb_data, gintptr poll_object)
-{
- struct source *new_sources, *s;
- GPollFD *new_pollfds;
-
- if (!cb) {
- sr_err("%s: cb was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- /* Note: cb_data can be NULL, that's not a bug. */
-
- new_pollfds = g_try_realloc(session->pollfds,
- sizeof(GPollFD) * (session->num_sources + 1));
- if (!new_pollfds) {
- sr_err("%s: new_pollfds malloc failed", __func__);
- return SR_ERR_MALLOC;
- }
-
- new_sources = g_try_realloc(session->sources, sizeof(struct source) *
- (session->num_sources + 1));
- if (!new_sources) {
- sr_err("%s: new_sources malloc failed", __func__);
- return SR_ERR_MALLOC;
- }
-
- new_pollfds[session->num_sources] = *pollfd;
- s = &new_sources[session->num_sources++];
- s->timeout = timeout;
- s->cb = cb;
- s->cb_data = cb_data;
- s->poll_object = poll_object;
- session->pollfds = new_pollfds;
- session->sources = new_sources;
-
- if (timeout != session->source_timeout && timeout > 0
- && (session->source_timeout == -1 || timeout < session->source_timeout))
- session->source_timeout = timeout;
-
- return SR_OK;
-}
-
-/**
- * Add an event source for a file descriptor.
- *
- * @param fd The file descriptor.
- * @param events Events to check for.
- * @param timeout Max time to wait before the callback is called, ignored if 0.
- * @param cb Callback function to add. Must not be NULL.
- * @param cb_data Data for the callback function. Can be NULL.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
- *
- * @since 0.3.0
- */
-SR_API int sr_session_source_add(struct sr_session *session, int fd,
- int events, int timeout, sr_receive_data_callback cb, void *cb_data)
-{
- GPollFD p;
-
- (void) session;
-
- p.fd = fd;
- p.events = events;
-
- return _sr_session_source_add(session, &p, timeout, cb, cb_data, (gintptr)fd);
-}
-
-/**
- * Add an event source for a GPollFD.
- *
- * @param pollfd The GPollFD.
- * @param timeout Max time to wait before the callback is called, ignored if 0.
- * @param cb Callback function to add. Must not be NULL.
- * @param cb_data Data for the callback function. Can be NULL.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
- *
- * @since 0.3.0
- */
-SR_API int sr_session_source_add_pollfd(struct sr_session *session,
- GPollFD *pollfd, int timeout, sr_receive_data_callback cb,
- void *cb_data)
-{
- (void) session;
-
- return _sr_session_source_add(session, pollfd, timeout, cb,
- cb_data, (gintptr)pollfd);
-}
-
-/**
- * Add an event source for a GIOChannel.
- *
- * @param channel The GIOChannel.
- * @param events Events to poll on.
- * @param timeout Max time to wait before the callback is called, ignored if 0.
- * @param cb Callback function to add. Must not be NULL.
- * @param cb_data Data for the callback function. Can be NULL.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
- *
- * @since 0.3.0
- */
-SR_API int sr_session_source_add_channel(struct sr_session *session,
- GIOChannel *channel, int events, int timeout,
- sr_receive_data_callback cb, void *cb_data)
-{
- GPollFD p;
-
- (void) session;
-
-#ifdef _WIN32
- g_io_channel_win32_make_pollfd(channel, events, &p);
-#else
- p.fd = g_io_channel_unix_get_fd(channel);
- p.events = events;
-#endif
-
- return _sr_session_source_add(session, &p, timeout, cb, cb_data, (gintptr)channel);
-}
-
-/**
- * Remove the source belonging to the specified channel.
- *
- * @todo Add more error checks and logging.
- *
- * @param poll_object The channel for which the source should be removed.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid arguments
- * @retval SR_ERR_MALLOC Memory allocation error
- * @retval SR_ERR_BUG Internal error
- */
-static int _sr_session_source_remove(struct sr_session *session, gintptr poll_object)
-{
- struct source *new_sources;
- GPollFD *new_pollfds;
- unsigned int old;
-
- if (!session->sources || !session->num_sources) {
- sr_err("%s: sources was NULL", __func__);
- return SR_ERR_BUG;
- }
-
- for (old = 0; old < session->num_sources; old++) {
- if (session->sources[old].poll_object == poll_object)
- break;
- }
-
- /* fd not found, nothing to do */
- if (old == session->num_sources)
- return SR_OK;
-
- session->num_sources -= 1;
-
- if (old != session->num_sources) {
- memmove(&session->pollfds[old], &session->pollfds[old+1],
- (session->num_sources - old) * sizeof(GPollFD));
- memmove(&session->sources[old], &session->sources[old+1],
- (session->num_sources - old) * sizeof(struct source));
- }
-
- new_pollfds = g_try_realloc(session->pollfds, sizeof(GPollFD) * session->num_sources);
- if (!new_pollfds && session->num_sources > 0) {
- sr_err("%s: new_pollfds malloc failed", __func__);
- return SR_ERR_MALLOC;
- }
-
- new_sources = g_try_realloc(session->sources, sizeof(struct source) * session->num_sources);
- if (!new_sources && session->num_sources > 0) {
- sr_err("%s: new_sources malloc failed", __func__);
- return SR_ERR_MALLOC;
- }
-
- session->pollfds = new_pollfds;
- session->sources = new_sources;
-
- return SR_OK;
-}
-
-/**
- * Remove the source belonging to the specified file descriptor.
- *
- * @param fd The file descriptor for which the source should be removed.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid argument
- * @retval SR_ERR_MALLOC Memory allocation error.
- * @retval SR_ERR_BUG Internal error.
- *
- * @since 0.3.0
- */
-SR_API int sr_session_source_remove(struct sr_session *session, int fd)
-{
- (void) session;
-
- return _sr_session_source_remove(session, (gintptr)fd);
-}
-
-/**
- * Remove the source belonging to the specified poll descriptor.
- *
- * @param pollfd The poll descriptor for which the source should be removed.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- * SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
- * internal errors.
- *
- * @since 0.2.0
- */
-SR_API int sr_session_source_remove_pollfd(struct sr_session *session,
- GPollFD *pollfd)
-{
- (void) session;
-
- return _sr_session_source_remove(session, (gintptr)pollfd);
-}
-
-/**
- * Remove the source belonging to the specified channel.
- *
- * @param channel The channel for which the source should be removed.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
- * @return SR_ERR_BUG Internal error.
- *
- * @since 0.2.0
- */
-SR_API int sr_session_source_remove_channel(struct sr_session *session,
- GIOChannel *channel)
-{
- (void) session;
-
- return _sr_session_source_remove(session, (gintptr)channel);
-}
-
-/** @} */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/time.h>
-#include <zip.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "virtual-session"
-
-/* size of payloads sent across the session bus */
-/** @cond PRIVATE */
-#define CHUNKSIZE (512 * 1024)
-/** @endcond */
-
-SR_PRIV struct sr_dev_driver session_driver_info;
-static struct sr_dev_driver *di = &session_driver_info;
-
-struct session_vdev {
- char *sessionfile;
- char *capturefile;
- struct zip *archive;
- struct zip_file *capfile;
- int bytes_read;
- uint64_t samplerate;
- int unitsize;
- int num_channels;
- int cur_chunk;
- gboolean finished;
-};
-
-static const int hwcaps[] = {
- SR_CONF_CAPTUREFILE,
- SR_CONF_CAPTURE_UNITSIZE,
- SR_CONF_SAMPLERATE,
-};
-
-static int receive_data(int fd, int revents, void *cb_data)
-{
- struct sr_dev_inst *sdi;
- struct session_vdev *vdev;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_logic logic;
- struct zip_stat zs;
- int ret, got_data;
- char capturefile[16];
- void *buf;
-
- (void)fd;
- (void)revents;
-
- sdi = cb_data;
- got_data = FALSE;
- vdev = sdi->priv;
- if (!vdev->finished) {
- if (!vdev->capfile) {
- /* No capture file opened yet, or finished with the last
- * chunked one. */
- if (vdev->cur_chunk == 0) {
- /* capturefile is always the unchunked base name. */
- if (zip_stat(vdev->archive, vdev->capturefile, 0, &zs) != -1) {
- /* No chunks, just a single capture file. */
- vdev->cur_chunk = 0;
- if (!(vdev->capfile = zip_fopen(vdev->archive,
- vdev->capturefile, 0)))
- return FALSE;
- sr_dbg("Opened %s.", vdev->capturefile);
- } else {
- /* Try as first chunk filename. */
- snprintf(capturefile, 15, "%s-1", vdev->capturefile);
- if (zip_stat(vdev->archive, capturefile, 0, &zs) != -1) {
- vdev->cur_chunk = 1;
- if (!(vdev->capfile = zip_fopen(vdev->archive,
- capturefile, 0)))
- return FALSE;
- sr_dbg("Opened %s.", capturefile);
- } else {
- sr_err("No capture file '%s' in " "session file '%s'.",
- vdev->capturefile, vdev->sessionfile);
- return FALSE;
- }
- }
- } else {
- /* Capture data is chunked, advance to the next chunk. */
- vdev->cur_chunk++;
- snprintf(capturefile, 15, "%s-%d", vdev->capturefile,
- vdev->cur_chunk);
- if (zip_stat(vdev->archive, capturefile, 0, &zs) != -1) {
- if (!(vdev->capfile = zip_fopen(vdev->archive,
- capturefile, 0)))
- return FALSE;
- sr_dbg("Opened %s.", capturefile);
- } else {
- /* We got all the chunks, finish up. */
- vdev->finished = TRUE;
- return TRUE;
- }
- }
- }
-
- if (!(buf = g_try_malloc(CHUNKSIZE))) {
- sr_err("%s: buf malloc failed", __func__);
- return FALSE;
- }
-
- ret = zip_fread(vdev->capfile, buf,
- CHUNKSIZE / vdev->unitsize * vdev->unitsize);
- if (ret > 0) {
- if (ret % vdev->unitsize != 0)
- sr_warn("Read size %d not a multiple of the"
- " unit size %d.", ret, vdev->unitsize);
- got_data = TRUE;
- packet.type = SR_DF_LOGIC;
- packet.payload = &logic;
- logic.length = ret;
- logic.unitsize = vdev->unitsize;
- logic.data = buf;
- vdev->bytes_read += ret;
- sr_session_send(sdi, &packet);
- } else {
- /* done with this capture file */
- zip_fclose(vdev->capfile);
- vdev->capfile = NULL;
- if (vdev->cur_chunk == 0) {
- /* It was the only file. */
- vdev->finished = TRUE;
- } else {
- /* There might be more chunks, so don't fall through
- * to the SR_DF_END here. */
- g_free(buf);
- return TRUE;
- }
- }
- g_free(buf);
- }
-
- if (!got_data) {
- packet.type = SR_DF_END;
- sr_session_send(sdi, &packet);
- sr_session_source_remove(sdi->session, -1);
- }
-
- return TRUE;
-}
-
-/* driver callbacks */
-
-static int init(struct sr_context *sr_ctx)
-{
- return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static int dev_clear(void)
-{
- struct drv_context *drvc;
- GSList *l;
-
- drvc = di->priv;
- for (l = drvc->instances; l; l = l->next)
- sr_dev_inst_free(l->data);
- g_slist_free(drvc->instances);
- drvc->instances = NULL;
-
- return SR_OK;
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
- struct drv_context *drvc;
- struct session_vdev *vdev;
-
- drvc = di->priv;
- vdev = g_malloc0(sizeof(struct session_vdev));
- sdi->priv = vdev;
- drvc->instances = g_slist_append(drvc->instances, sdi);
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- const struct session_vdev *const vdev = sdi->priv;
- g_free(vdev->sessionfile);
- g_free(vdev->capturefile);
-
- g_free(sdi->priv);
- sdi->priv = NULL;
-
- return SR_OK;
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct session_vdev *vdev;
-
- (void)cg;
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- if (sdi) {
- vdev = sdi->priv;
- *data = g_variant_new_uint64(vdev->samplerate);
- } else
- return SR_ERR;
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- struct session_vdev *vdev;
-
- (void)cg;
-
- vdev = sdi->priv;
-
- switch (id) {
- case SR_CONF_SAMPLERATE:
- vdev->samplerate = g_variant_get_uint64(data);
- sr_info("Setting samplerate to %" PRIu64 ".", vdev->samplerate);
- break;
- case SR_CONF_SESSIONFILE:
- g_free(vdev->sessionfile);
- vdev->sessionfile = g_strdup(g_variant_get_string(data, NULL));
- sr_info("Setting sessionfile to '%s'.", vdev->sessionfile);
- break;
- case SR_CONF_CAPTUREFILE:
- g_free(vdev->capturefile);
- vdev->capturefile = g_strdup(g_variant_get_string(data, NULL));
- sr_info("Setting capturefile to '%s'.", vdev->capturefile);
- break;
- case SR_CONF_CAPTURE_UNITSIZE:
- vdev->unitsize = g_variant_get_uint64(data);
- break;
- case SR_CONF_NUM_LOGIC_CHANNELS:
- vdev->num_channels = g_variant_get_uint64(data);
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
-{
- (void)sdi;
- (void)cg;
-
- switch (key) {
- case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
- struct session_vdev *vdev;
- int ret;
-
- (void)cb_data;
-
- vdev = sdi->priv;
- vdev->bytes_read = 0;
- vdev->cur_chunk = 0;
- vdev->finished = FALSE;
-
- sr_info("Opening archive %s file %s", vdev->sessionfile,
- vdev->capturefile);
-
- if (!(vdev->archive = zip_open(vdev->sessionfile, 0, &ret))) {
- sr_err("Failed to open session file '%s': "
- "zip error %d.", vdev->sessionfile, ret);
- return SR_ERR;
- }
-
- /* Send header packet to the session bus. */
- std_session_send_df_header(sdi, LOG_PREFIX);
-
- /* freewheeling source */
- sr_session_source_add(sdi->session, -1, 0, 0, receive_data, (void *)sdi);
-
- return SR_OK;
-}
-
-/** @private */
-SR_PRIV struct sr_dev_driver session_driver = {
- .name = "virtual-session",
- .longname = "Session-emulating driver",
- .api_version = 1,
- .init = init,
- .cleanup = dev_clear,
- .scan = NULL,
- .dev_list = NULL,
- .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 = NULL,
- .priv = NULL,
-};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- *
- * 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 <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <zip.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <glib.h>
-#include <glib/gstdio.h>
-#include "config.h" /* Needed for PACKAGE_VERSION and others. */
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/** @cond PRIVATE */
-#define LOG_PREFIX "session-file"
-/** @endcond */
-
-/**
- * @file
- *
- * Loading and saving libsigrok session files.
- */
-
-/**
- * @addtogroup grp_session
- *
- * @{
- */
-
-extern struct sr_session *session;
-extern SR_PRIV struct sr_dev_driver session_driver;
-
-/** @private */
-SR_PRIV int sr_sessionfile_check(const char *filename)
-{
- struct stat st;
- struct zip *archive;
- struct zip_file *zf;
- struct zip_stat zs;
- int version, ret;
- char s[11];
-
- if (!filename)
- return SR_ERR_ARG;
-
- if (stat(filename, &st) == -1) {
- sr_err("Couldn't stat %s: %s", filename, strerror(errno));
- return SR_ERR;
- }
-
- if (!(archive = zip_open(filename, 0, &ret)))
- /* No logging: this can be used just to check if it's
- * a sigrok session file or not. */
- return SR_ERR;
-
- /* check "version" */
- version = 0;
- if (!(zf = zip_fopen(archive, "version", 0))) {
- sr_dbg("Not a sigrok session file: no version found.");
- return SR_ERR;
- }
- if ((ret = zip_fread(zf, s, 10)) == -1)
- return SR_ERR;
- zip_fclose(zf);
- s[ret] = 0;
- version = strtoull(s, NULL, 10);
- if (version > 2) {
- sr_dbg("Cannot handle sigrok session file version %d.", version);
- return SR_ERR;
- }
- sr_spew("Detected sigrok session file version %d.", version);
-
- /* read "metadata" */
- if (zip_stat(archive, "metadata", 0, &zs) == -1) {
- sr_dbg("Not a valid sigrok session file.");
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-/**
- * Load the session from the specified filename.
- *
- * @param filename The name of the session file to load. Must not be NULL.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments,
- * SR_ERR_MALLOC upon memory allocation errors, or SR_ERR upon
- * other errors.
- */
-SR_API int sr_session_load(const char *filename, struct sr_session **session)
-{
- GKeyFile *kf;
- GPtrArray *capturefiles;
- struct zip *archive;
- struct zip_file *zf;
- struct zip_stat zs;
- struct sr_dev_inst *sdi;
- struct sr_channel *ch;
- int devcnt, ret, i, j;
- uint64_t tmp_u64, total_channels, enabled_channels, p;
- char **sections, **keys, *metafile, *val;
- char channelname[SR_MAX_CHANNELNAME_LEN + 1];
-
- if ((ret = sr_sessionfile_check(filename)) != SR_OK)
- return ret;
-
- if (!(archive = zip_open(filename, 0, &ret)))
- return SR_ERR;
-
- if (zip_stat(archive, "metadata", 0, &zs) == -1)
- return SR_ERR;
-
- if (!(metafile = g_try_malloc(zs.size))) {
- sr_err("%s: metafile malloc failed", __func__);
- return SR_ERR_MALLOC;
- }
-
- zf = zip_fopen_index(archive, zs.index, 0);
- zip_fread(zf, metafile, zs.size);
- zip_fclose(zf);
-
- kf = g_key_file_new();
- if (!g_key_file_load_from_data(kf, metafile, zs.size, 0, NULL)) {
- sr_dbg("Failed to parse metadata.");
- return SR_ERR;
- }
-
- if ((ret = sr_session_new(session)) != SR_OK)
- return ret;
-
- devcnt = 0;
- capturefiles = g_ptr_array_new_with_free_func(g_free);
- sections = g_key_file_get_groups(kf, NULL);
- for (i = 0; sections[i]; i++) {
- if (!strcmp(sections[i], "global"))
- /* nothing really interesting in here yet */
- continue;
- if (!strncmp(sections[i], "device ", 7)) {
- /* device section */
- sdi = NULL;
- enabled_channels = total_channels = 0;
- keys = g_key_file_get_keys(kf, sections[i], NULL, NULL);
- for (j = 0; keys[j]; j++) {
- val = g_key_file_get_string(kf, sections[i], keys[j], NULL);
- if (!strcmp(keys[j], "capturefile")) {
- sdi = sr_dev_inst_new(devcnt, SR_ST_ACTIVE, NULL, NULL, NULL);
- sdi->driver = &session_driver;
- if (devcnt == 0)
- /* first device, init the driver */
- sdi->driver->init(NULL);
- sr_dev_open(sdi);
- sr_session_dev_add(*session, sdi);
- sdi->driver->config_set(SR_CONF_SESSIONFILE,
- g_variant_new_string(filename), sdi, NULL);
- sdi->driver->config_set(SR_CONF_CAPTUREFILE,
- g_variant_new_string(val), sdi, NULL);
- g_ptr_array_add(capturefiles, val);
- } else if (!strcmp(keys[j], "samplerate")) {
- sr_parse_sizestring(val, &tmp_u64);
- sdi->driver->config_set(SR_CONF_SAMPLERATE,
- g_variant_new_uint64(tmp_u64), sdi, NULL);
- } else if (!strcmp(keys[j], "unitsize")) {
- tmp_u64 = strtoull(val, NULL, 10);
- sdi->driver->config_set(SR_CONF_CAPTURE_UNITSIZE,
- g_variant_new_uint64(tmp_u64), sdi, NULL);
- } else if (!strcmp(keys[j], "total probes")) {
- total_channels = strtoull(val, NULL, 10);
- sdi->driver->config_set(SR_CONF_NUM_LOGIC_CHANNELS,
- g_variant_new_uint64(total_channels), sdi, NULL);
- for (p = 0; p < total_channels; p++) {
- snprintf(channelname, SR_MAX_CHANNELNAME_LEN, "%" PRIu64, p);
- if (!(ch = sr_channel_new(p, SR_CHANNEL_LOGIC, TRUE,
- channelname)))
- return SR_ERR;
- sdi->channels = g_slist_append(sdi->channels, ch);
- }
- } else if (!strncmp(keys[j], "probe", 5)) {
- if (!sdi)
- continue;
- enabled_channels++;
- tmp_u64 = strtoul(keys[j]+5, NULL, 10);
- /* sr_session_save() */
- sr_dev_channel_name_set(sdi, tmp_u64 - 1, val);
- }
- }
- g_strfreev(keys);
- /* Disable channels not specifically listed. */
- if (total_channels)
- for (p = enabled_channels; p < total_channels; p++)
- sr_dev_channel_enable(sdi, p, FALSE);
- }
- devcnt++;
- }
- g_strfreev(sections);
- g_key_file_free(kf);
-
- return SR_OK;
-}
-
-/**
- * Save a session to the specified file.
- *
- * @param filename The name of the filename to save the session as.
- * Must not be NULL.
- * @param sdi The device instance from which the data was captured.
- * @param buf The data to be saved.
- * @param unitsize The number of bytes per sample.
- * @param units The number of samples.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid arguments
- * @retval SR_ERR Other errors
- *
- * @since 0.2.0
- */
-SR_API int sr_session_save(struct sr_session *session, const char *filename,
- const struct sr_dev_inst *sdi, unsigned char *buf, int unitsize,
- int units)
-{
- struct sr_channel *ch;
- GSList *l;
- GVariant *gvar;
- uint64_t samplerate;
- int cnt, ret;
- char **channel_names;
-
- samplerate = 0;
- if (sr_dev_has_option(sdi, SR_CONF_SAMPLERATE)) {
- if (sr_config_get(sdi->driver, sdi, NULL,
- SR_CONF_SAMPLERATE, &gvar) == SR_OK) {
- samplerate = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
- }
- }
-
- channel_names = g_malloc0(sizeof(char *) * (g_slist_length(sdi->channels) + 1));
- cnt = 0;
- for (l = sdi->channels; l; l = l->next) {
- ch = l->data;
- if (ch->type != SR_CHANNEL_LOGIC)
- continue;
- if (ch->enabled != TRUE)
- continue;
- if (!ch->name)
- continue;
- /* Just borrowing the ptr. */
- channel_names[cnt++] = ch->name;
- }
-
- if ((ret = sr_session_save_init(session, filename, samplerate,
- channel_names)) != SR_OK)
- return ret;
-
- ret = sr_session_append(session, filename, buf, unitsize, units);
-
- return ret;
-}
-
-/**
- * Initialize a saved session file.
- *
- * @param filename The name of the filename to save the session as.
- * Must not be NULL.
- * @param samplerate The samplerate to store for this session.
- * @param channels A NULL-terminated array of strings containing the names
- * of all the channels active in this session.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid arguments
- * @retval SR_ERR Other errors
- *
- * @since 0.3.0
- */
-SR_API int sr_session_save_init(struct sr_session *session,
- const char *filename, uint64_t samplerate, char **channels)
-{
- FILE *meta;
- struct zip *zipfile;
- struct zip_source *versrc, *metasrc;
- int tmpfile, cnt, ret, i;
- char version[1], metafile[32], *s;
-
- (void) session;
-
- if (!filename) {
- sr_err("%s: filename was NULL", __func__);
- return SR_ERR_ARG;
- }
-
- /* Quietly delete it first, libzip wants replace ops otherwise. */
- unlink(filename);
- if (!(zipfile = zip_open(filename, ZIP_CREATE, &ret)))
- return SR_ERR;
-
- /* "version" */
- version[0] = '2';
- if (!(versrc = zip_source_buffer(zipfile, version, 1, 0)))
- return SR_ERR;
- if (zip_add(zipfile, "version", versrc) == -1) {
- sr_info("error saving version into zipfile: %s",
- zip_strerror(zipfile));
- return SR_ERR;
- }
-
- /* init "metadata" */
- strcpy(metafile, "sigrok-meta-XXXXXX");
- if ((tmpfile = g_mkstemp(metafile)) == -1)
- return SR_ERR;
- close(tmpfile);
- meta = g_fopen(metafile, "wb");
- fprintf(meta, "[global]\n");
- fprintf(meta, "sigrok version = %s\n", PACKAGE_VERSION);
-
- /* metadata */
- fprintf(meta, "[device 1]\n");
-
- /* metadata */
- fprintf(meta, "capturefile = logic-1\n");
- cnt = 0;
- for (i = 0; channels[i]; i++)
- cnt++;
- fprintf(meta, "total probes = %d\n", cnt);
- s = sr_samplerate_string(samplerate);
- fprintf(meta, "samplerate = %s\n", s);
- g_free(s);
-
- for (i = 0; channels[i]; i++)
- fprintf(meta, "probe%d = %s\n", i + 1, channels[i]);
-
- fclose(meta);
-
- if (!(metasrc = zip_source_file(zipfile, metafile, 0, -1))) {
- unlink(metafile);
- return SR_ERR;
- }
- if (zip_add(zipfile, "metadata", metasrc) == -1) {
- unlink(metafile);
- return SR_ERR;
- }
-
- if ((ret = zip_close(zipfile)) == -1) {
- sr_info("error saving zipfile: %s", zip_strerror(zipfile));
- unlink(metafile);
- return SR_ERR;
- }
-
- unlink(metafile);
-
- return SR_OK;
-}
-
-/**
- * Append data to an existing session file.
- *
- * The session file must have been created with sr_session_save_init()
- * or sr_session_save() beforehand.
- *
- * @param filename The name of the filename to append to. Must not be NULL.
- * @param buf The data to be appended.
- * @param unitsize The number of bytes per sample.
- * @param units The number of samples.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid arguments
- * @retval SR_ERR Other errors
- *
- * @since 0.3.0
- */
-SR_API int sr_session_append(struct sr_session *session, const char *filename,
- unsigned char *buf, int unitsize, int units)
-{
- struct zip *archive;
- struct zip_source *logicsrc;
- zip_int64_t num_files;
- struct zip_file *zf;
- struct zip_stat zs;
- struct zip_source *metasrc;
- GKeyFile *kf;
- GError *error;
- gsize len;
- int chunk_num, next_chunk_num, tmpfile, ret, i;
- const char *entry_name;
- char *metafile, tmpname[32], chunkname[16];
-
- (void) session;
-
- if ((ret = sr_sessionfile_check(filename)) != SR_OK)
- return ret;
-
- if (!(archive = zip_open(filename, 0, &ret)))
- return SR_ERR;
-
- if (zip_stat(archive, "metadata", 0, &zs) == -1)
- return SR_ERR;
-
- metafile = g_malloc(zs.size);
- zf = zip_fopen_index(archive, zs.index, 0);
- zip_fread(zf, metafile, zs.size);
- zip_fclose(zf);
-
- /*
- * If the file was only initialized but doesn't yet have any
- * data it in, it won't have a unitsize field in metadata yet.
- */
- error = NULL;
- kf = g_key_file_new();
- if (!g_key_file_load_from_data(kf, metafile, zs.size, 0, &error)) {
- sr_err("Failed to parse metadata: %s.", error->message);
- return SR_ERR;
- }
- g_free(metafile);
- tmpname[0] = '\0';
- if (!g_key_file_has_key(kf, "device 1", "unitsize", &error)) {
- if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
- sr_err("Failed to check unitsize key: %s", error ? error->message : "?");
- return SR_ERR;
- }
- /* Add unitsize field. */
- g_key_file_set_integer(kf, "device 1", "unitsize", unitsize);
- metafile = g_key_file_to_data(kf, &len, &error);
- strcpy(tmpname, "sigrok-meta-XXXXXX");
- if ((tmpfile = g_mkstemp(tmpname)) == -1)
- return SR_ERR;
- if (write(tmpfile, metafile, len) < 0) {
- sr_dbg("Failed to create new metadata: %s", strerror(errno));
- g_free(metafile);
- unlink(tmpname);
- return SR_ERR;
- }
- close(tmpfile);
- if (!(metasrc = zip_source_file(archive, tmpname, 0, -1))) {
- sr_err("Failed to create zip source for metadata.");
- g_free(metafile);
- unlink(tmpname);
- return SR_ERR;
- }
- if (zip_replace(archive, zs.index, metasrc) == -1) {
- sr_err("Failed to replace metadata file.");
- g_free(metafile);
- unlink(tmpname);
- return SR_ERR;
- }
- g_free(metafile);
- }
- g_key_file_free(kf);
-
- next_chunk_num = 1;
- num_files = zip_get_num_entries(archive, 0);
- for (i = 0; i < num_files; i++) {
- entry_name = zip_get_name(archive, i, 0);
- if (strncmp(entry_name, "logic-1", 7))
- continue;
- if (strlen(entry_name) == 7) {
- /* This file has no extra chunks, just a single "logic-1".
- * Rename it to "logic-1-1" * and continue with chunk 2. */
- if (zip_rename(archive, i, "logic-1-1") == -1) {
- sr_err("Failed to rename 'logic-1' to 'logic-1-1'.");
- unlink(tmpname);
- return SR_ERR;
- }
- next_chunk_num = 2;
- break;
- } else if (strlen(entry_name) > 8 && entry_name[7] == '-') {
- chunk_num = strtoull(entry_name + 8, NULL, 10);
- if (chunk_num >= next_chunk_num)
- next_chunk_num = chunk_num + 1;
- }
- }
- snprintf(chunkname, 15, "logic-1-%d", next_chunk_num);
- if (!(logicsrc = zip_source_buffer(archive, buf, units * unitsize, FALSE))) {
- unlink(tmpname);
- return SR_ERR;
- }
- if (zip_add(archive, chunkname, logicsrc) == -1) {
- unlink(tmpname);
- return SR_ERR;
- }
- if ((ret = zip_close(archive)) == -1) {
- sr_info("error saving session file: %s", zip_strerror(archive));
- unlink(tmpname);
- return SR_ERR;
- }
- unlink(tmpname);
-
- return SR_OK;
-}
-
-/** @} */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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 <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/* @cond PRIVATE */
-#define LOG_PREFIX "soft-trigger"
-/* @endcond */
-
-SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
- const struct sr_dev_inst *sdi, struct sr_trigger *trigger)
-{
- struct soft_trigger_logic *stl;
-
- stl = g_malloc0(sizeof(struct soft_trigger_logic));
- stl->sdi = sdi;
- stl->trigger = trigger;
- stl->unitsize = (g_slist_length(sdi->channels) + 7) / 8;
- stl->prev_sample = g_malloc0(stl->unitsize);
-
- return stl;
-}
-
-SR_PRIV void soft_trigger_logic_free(struct soft_trigger_logic *stl)
-{
- g_free(stl->prev_sample);
- g_free(stl);
-}
-
-static gboolean logic_check_match(struct soft_trigger_logic *stl,
- uint8_t *sample, struct sr_trigger_match *match)
-{
- int bit, prev_bit;
- gboolean result;
-
- stl->count++;
- result = FALSE;
- bit = *(sample + match->channel->index / 8)
- & (1 << (match->channel->index % 8));
- if (match->match == SR_TRIGGER_ZERO)
- result = bit == 0;
- else if (match->match == SR_TRIGGER_ONE)
- result = bit != 0;
- else {
- /* Edge matches. */
- if (stl->count == 1)
- /* First sample, don't have enough for an edge match yet. */
- return FALSE;
- prev_bit = *(stl->prev_sample + match->channel->index / 8)
- & (1 << (match->channel->index % 8));
- if (match->match == SR_TRIGGER_RISING)
- result = prev_bit == 0 && bit != 0;
- else if (match->match == SR_TRIGGER_FALLING)
- result = prev_bit != 0 && bit == 0;
- else if (match->match == SR_TRIGGER_EDGE)
- result = prev_bit != bit;
- }
-
- return result;
-}
-
-/* Returns the offset (in samples) within buf of where the trigger
- * occurred, or -1 if not triggered. */
-SR_PRIV int soft_trigger_logic_check(struct soft_trigger_logic *stl,
- uint8_t *buf, int len)
-{
- struct sr_datafeed_packet packet;
- struct sr_trigger_stage *stage;
- struct sr_trigger_match *match;
- GSList *l, *l_stage;
- int offset;
- int i;
- gboolean match_found;
-
- offset = -1;
- for (i = 0; i < len; i += stl->unitsize) {
- l_stage = g_slist_nth(stl->trigger->stages, stl->cur_stage);
- stage = l_stage->data;
- if (!stage->matches)
- /* No matches supplied, client error. */
- return SR_ERR_ARG;
-
- match_found = TRUE;
- for (l = stage->matches; l; l = l->next) {
- match = l->data;
- if (!match->channel->enabled)
- /* Ignore disabled channels with a trigger. */
- continue;
- if (!logic_check_match(stl, buf + i, match)) {
- match_found = FALSE;
- break;
- }
- }
- memcpy(stl->prev_sample, buf + i, stl->unitsize);
- if (match_found) {
- /* Matched on the current stage. */
- if (l_stage->next) {
- /* Advance to next stage. */
- stl->cur_stage++;
- } else {
- /* Matched on last stage, fire trigger. */
- offset = i / stl->unitsize;
-
- packet.type = SR_DF_TRIGGER;
- packet.payload = NULL;
- sr_session_send(stl->sdi, &packet);
- break;
- }
- } else if (stl->cur_stage > 0) {
- /*
- * We had a match at an earlier stage, but failed on the
- * current stage. However, we may have a match on this
- * stage in the next bit -- trigger on 0001 will fail on
- * seeing 00001, so we need to go back to stage 0 -- but
- * at the next sample from the one that matched originally,
- * which the counter increment at the end of the loop
- * takes care of.
- */
- i -= stl->cur_stage * stl->unitsize;
- if (i < -1)
- i = -1; /* Oops, went back past this buffer. */
- /* Reset trigger stage. */
- stl->cur_stage = 0;
- }
- }
-
- return offset;
-}
-
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2012 Peter Stuge <peter@stuge.se>
+ *
+ * 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 <glib.h>
+#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "backend"
+/** @endcond */
+
+/**
+ * @mainpage libsigrok API
+ *
+ * @section sec_intro Introduction
+ *
+ * The <a href="http://sigrok.org">sigrok</a> project aims at creating a
+ * portable, cross-platform, Free/Libre/Open-Source signal analysis software
+ * suite that supports various device types (such as logic analyzers,
+ * oscilloscopes, multimeters, and more).
+ *
+ * <a href="http://sigrok.org/wiki/Libsigrok">libsigrok</a> is a shared
+ * library written in C which provides the basic API for talking to
+ * <a href="http://sigrok.org/wiki/Supported_hardware">supported hardware</a>
+ * and reading/writing the acquired data into various
+ * <a href="http://sigrok.org/wiki/Input_output_formats">input/output
+ * file formats</a>.
+ *
+ * @section sec_api API reference
+ *
+ * See the "Modules" page for an introduction to various libsigrok
+ * related topics and the detailed API documentation of the respective
+ * functions.
+ *
+ * You can also browse the API documentation by file, or review all
+ * data structures.
+ *
+ * @section sec_mailinglists Mailing lists
+ *
+ * There are two mailing lists for sigrok/libsigrok: <a href="https://lists.sourceforge.net/lists/listinfo/sigrok-devel">sigrok-devel</a> and <a href="https://lists.sourceforge.net/lists/listinfo/sigrok-commits">sigrok-commits</a>.
+ *
+ * @section sec_irc IRC
+ *
+ * You can find the sigrok developers in the
+ * <a href="irc://chat.freenode.net/sigrok">\#sigrok</a>
+ * IRC channel on Freenode.
+ *
+ * @section sec_website Website
+ *
+ * <a href="http://sigrok.org/wiki/Libsigrok">sigrok.org/wiki/Libsigrok</a>
+ */
+
+/**
+ * @file
+ *
+ * Initializing and shutting down libsigrok.
+ */
+
+/**
+ * @defgroup grp_init Initialization
+ *
+ * Initializing and shutting down libsigrok.
+ *
+ * Before using any of the libsigrok functionality (except
+ * sr_log_loglevel_set() and sr_log_opts_set()), sr_init() must
+ * be called to initialize the library, which will return a struct sr_context
+ * when the initialization was successful.
+ *
+ * When libsigrok functionality is no longer needed, sr_exit() should be
+ * called, which will (among other things) free the struct sr_context.
+ *
+ * Example for a minimal program using libsigrok:
+ *
+ * @code{.c}
+ * #include <stdio.h>
+ * #include <libsigrok/libsigrok.h>
+ *
+ * int main(int argc, char **argv)
+ * {
+ * int ret;
+ * struct sr_context *sr_ctx;
+ *
+ * if ((ret = sr_init(&sr_ctx)) != SR_OK) {
+ * printf("Error initializing libsigrok (%s): %s.\n",
+ * sr_strerror_name(ret), sr_strerror(ret));
+ * return 1;
+ * }
+ *
+ * // Use libsigrok functions here...
+ *
+ * if ((ret = sr_exit(sr_ctx)) != SR_OK) {
+ * printf("Error shutting down libsigrok (%s): %s.\n",
+ * sr_strerror_name(ret), sr_strerror(ret));
+ * return 1;
+ * }
+ *
+ * return 0;
+ * }
+ * @endcode
+ *
+ * @{
+ */
+
+/**
+ * Sanity-check all libsigrok drivers.
+ *
+ * @retval SR_OK All drivers are OK
+ * @retval SR_ERR One or more drivers have issues.
+ */
+static int sanity_check_all_drivers(void)
+{
+ int i, errors, ret = SR_OK;
+ struct sr_dev_driver **drivers;
+ const char *d;
+
+ sr_spew("Sanity-checking all drivers.");
+
+ drivers = sr_driver_list();
+ for (i = 0; drivers[i]; i++) {
+ errors = 0;
+
+ d = (drivers[i]->name) ? drivers[i]->name : "NULL";
+
+ if (!drivers[i]->name) {
+ sr_err("No name in driver %d ('%s').", i, d);
+ errors++;
+ }
+ if (!drivers[i]->longname) {
+ sr_err("No longname in driver %d ('%s').", i, d);
+ errors++;
+ }
+ if (drivers[i]->api_version < 1) {
+ sr_err("API version in driver %d ('%s') < 1.", i, d);
+ errors++;
+ }
+ if (!drivers[i]->init) {
+ sr_err("No init in driver %d ('%s').", i, d);
+ errors++;
+ }
+ if (!drivers[i]->cleanup) {
+ sr_err("No cleanup in driver %d ('%s').", i, d);
+ errors++;
+ }
+ if (!drivers[i]->scan) {
+ sr_err("No scan in driver %d ('%s').", i, d);
+ errors++;
+ }
+ if (!drivers[i]->dev_list) {
+ sr_err("No dev_list in driver %d ('%s').", i, d);
+ errors++;
+ }
+ /* Note: config_get() is optional. */
+ if (!drivers[i]->config_set) {
+ sr_err("No config_set in driver %d ('%s').", i, d);
+ errors++;
+ }
+ if (!drivers[i]->config_list) {
+ sr_err("No config_list in driver %d ('%s').", i, d);
+ errors++;
+ }
+ if (!drivers[i]->dev_open) {
+ sr_err("No dev_open in driver %d ('%s').", i, d);
+ errors++;
+ }
+ if (!drivers[i]->dev_close) {
+ sr_err("No dev_close in driver %d ('%s').", i, d);
+ errors++;
+ }
+ if (!drivers[i]->dev_acquisition_start) {
+ sr_err("No dev_acquisition_start in driver %d ('%s').",
+ i, d);
+ errors++;
+ }
+ if (!drivers[i]->dev_acquisition_stop) {
+ sr_err("No dev_acquisition_stop in driver %d ('%s').",
+ i, d);
+ errors++;
+ }
+
+ /* Note: 'priv' is allowed to be NULL. */
+
+ if (errors == 0)
+ continue;
+
+ ret = SR_ERR;
+ }
+
+ return ret;
+}
+
+/**
+ * Sanity-check all libsigrok input modules.
+ *
+ * @retval SR_OK All modules are OK
+ * @retval SR_ERR One or more modules have issues.
+ */
+static int sanity_check_all_input_modules(void)
+{
+ int i, errors, ret = SR_OK;
+ struct sr_input_format **inputs;
+ const char *d;
+
+ sr_spew("Sanity-checking all input modules.");
+
+ inputs = sr_input_list();
+ for (i = 0; inputs[i]; i++) {
+ errors = 0;
+
+ d = (inputs[i]->id) ? inputs[i]->id : "NULL";
+
+ if (!inputs[i]->id) {
+ sr_err("No ID in module %d ('%s').", i, d);
+ errors++;
+ }
+ if (!inputs[i]->description) {
+ sr_err("No description in module %d ('%s').", i, d);
+ errors++;
+ }
+ if (!inputs[i]->format_match) {
+ sr_err("No format_match in module %d ('%s').", i, d);
+ errors++;
+ }
+ if (!inputs[i]->init) {
+ sr_err("No init in module %d ('%s').", i, d);
+ errors++;
+ }
+ if (!inputs[i]->loadfile) {
+ sr_err("No loadfile in module %d ('%s').", i, d);
+ errors++;
+ }
+
+ if (errors == 0)
+ continue;
+
+ ret = SR_ERR;
+ }
+
+ return ret;
+}
+
+/**
+ * Sanity-check all libsigrok output modules.
+ *
+ * @retval SR_OK All modules are OK
+ * @retval SR_ERR One or more modules have issues.
+ */
+static int sanity_check_all_output_modules(void)
+{
+ int i, errors, ret = SR_OK;
+ struct sr_output_format **outputs;
+ const char *d;
+
+ sr_spew("Sanity-checking all output modules.");
+
+ outputs = sr_output_list();
+ for (i = 0; outputs[i]; i++) {
+ errors = 0;
+
+ d = (outputs[i]->id) ? outputs[i]->id : "NULL";
+
+ if (!outputs[i]->id) {
+ sr_err("No ID in module %d ('%s').", i, d);
+ errors++;
+ }
+ if (!outputs[i]->description) {
+ sr_err("No description in module '%s'.", d);
+ errors++;
+ }
+ if (!outputs[i]->receive) {
+ sr_err("No receive in module '%s'.", d);
+ errors++;
+ }
+
+ if (errors == 0)
+ continue;
+
+ ret = SR_ERR;
+ }
+
+ return ret;
+}
+
+/**
+ * Initialize libsigrok.
+ *
+ * This function must be called before any other libsigrok function.
+ *
+ * @param ctx Pointer to a libsigrok context struct pointer. Must not be NULL.
+ * This will be a pointer to a newly allocated libsigrok context
+ * object upon success, and is undefined upon errors.
+ *
+ * @return SR_OK upon success, a (negative) error code otherwise. Upon errors
+ * the 'ctx' pointer is undefined and should not be used. Upon success,
+ * the context will be free'd by sr_exit() as part of the libsigrok
+ * shutdown.
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_init(struct sr_context **ctx)
+{
+ int ret = SR_ERR;
+ struct sr_context *context;
+
+ if (!ctx) {
+ sr_err("%s(): libsigrok context was NULL.", __func__);
+ return SR_ERR;
+ }
+
+ if (sanity_check_all_drivers() < 0) {
+ sr_err("Internal driver error(s), aborting.");
+ return ret;
+ }
+
+ if (sanity_check_all_input_modules() < 0) {
+ sr_err("Internal input module error(s), aborting.");
+ return ret;
+ }
+
+ if (sanity_check_all_output_modules() < 0) {
+ sr_err("Internal output module error(s), aborting.");
+ return ret;
+ }
+
+ /* + 1 to handle when struct sr_context has no members. */
+ context = g_try_malloc0(sizeof(struct sr_context) + 1);
+
+ if (!context) {
+ ret = SR_ERR_MALLOC;
+ goto done;
+ }
+
+#ifdef HAVE_LIBUSB_1_0
+ ret = libusb_init(&context->libusb_ctx);
+ if (LIBUSB_SUCCESS != ret) {
+ sr_err("libusb_init() returned %s.", libusb_error_name(ret));
+ ret = SR_ERR;
+ goto done;
+ }
+#endif
+
+ *ctx = context;
+ context = NULL;
+ ret = SR_OK;
+
+done:
+ if (context)
+ g_free(context);
+ return ret;
+}
+
+/**
+ * Shutdown libsigrok.
+ *
+ * @param ctx Pointer to a libsigrok context struct. Must not be NULL.
+ *
+ * @retval SR_OK Success
+ * @retval other Error code SR_ERR, ...
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_exit(struct sr_context *ctx)
+{
+ if (!ctx) {
+ sr_err("%s(): libsigrok context was NULL.", __func__);
+ return SR_ERR;
+ }
+
+ sr_hw_cleanup_all();
+
+#ifdef HAVE_LIBUSB_1_0
+ libusb_exit(ctx->libusb_ctx);
+#endif
+
+ g_free(ctx);
+
+ return SR_OK;
+}
+
+/** @} */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdio.h>
+#include <glib.h>
+#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "device"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Device handling in libsigrok.
+ */
+
+/**
+ * @defgroup grp_devices Devices
+ *
+ * Device handling in libsigrok.
+ *
+ * @{
+ */
+
+/** @private
+ * Allocate and initialize new struct sr_channel
+ * @param[in] index @copydoc sr_channel::index
+ * @param[in] type @copydoc sr_channel::type
+ * @param[in] enabled @copydoc sr_channel::enabled
+ * @param[in] name @copydoc sr_channel::name
+ *
+ * @return NULL (failure) or new struct sr_channel*.
+ */
+SR_PRIV struct sr_channel *sr_channel_new(int index, int type,
+ gboolean enabled, const char *name)
+{
+ struct sr_channel *ch;
+
+ if (!(ch = g_try_malloc0(sizeof(struct sr_channel)))) {
+ sr_err("Channel malloc failed.");
+ return NULL;
+ }
+
+ ch->index = index;
+ ch->type = type;
+ ch->enabled = enabled;
+ if (name)
+ ch->name = g_strdup(name);
+
+ return ch;
+}
+
+/**
+ * Set the name of the specified channel in the specified device.
+ *
+ * If the channel already has a different name assigned to it, it will be
+ * removed, and the new name will be saved instead.
+ *
+ * @param sdi The device instance the channel is connected to.
+ * @param[in] channelnum The number of the channel whose name to set.
+ * Note that the channel numbers start at 0.
+ * @param[in] name The new name that the specified channel should get. A copy
+ * of the string is made.
+ *
+ * @return SR_OK on success, or SR_ERR_ARG on invalid arguments.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_dev_channel_name_set(const struct sr_dev_inst *sdi,
+ int channelnum, const char *name)
+{
+ GSList *l;
+ struct sr_channel *ch;
+ int ret;
+
+ if (!sdi) {
+ sr_err("%s: sdi was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ ret = SR_ERR_ARG;
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->index == channelnum) {
+ g_free(ch->name);
+ ch->name = g_strdup(name);
+ ret = SR_OK;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Enable or disable a channel on the specified device.
+ *
+ * @param sdi The device instance the channel is connected to.
+ * @param channelnum The channel number, starting from 0.
+ * @param state TRUE to enable the channel, FALSE to disable.
+ *
+ * @return SR_OK on success or SR_ERR on failure. In case of invalid
+ * arguments, SR_ERR_ARG is returned and the channel enabled state
+ * remains unchanged.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_dev_channel_enable(const struct sr_dev_inst *sdi, int channelnum,
+ gboolean state)
+{
+ GSList *l;
+ struct sr_channel *ch;
+ int ret;
+ gboolean was_enabled;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ ret = SR_ERR_ARG;
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->index == channelnum) {
+ was_enabled = ch->enabled;
+ ch->enabled = state;
+ ret = SR_OK;
+ if (!state != !was_enabled && sdi->driver
+ && sdi->driver->config_channel_set) {
+ ret = sdi->driver->config_channel_set(
+ sdi, ch, SR_CHANNEL_SET_ENABLED);
+ /* Roll back change if it wasn't applicable. */
+ if (ret == SR_ERR_ARG)
+ ch->enabled = was_enabled;
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Determine whether the specified device instance has the specified
+ * capability.
+ *
+ * @param sdi Pointer to the device instance to be checked. Must not be NULL.
+ * If the device's 'driver' field is NULL (virtual device), this
+ * function will always return FALSE (virtual devices don't have
+ * a hardware capabilities list).
+ * @param[in] key The option that should be checked for is supported by the
+ * specified device.
+ *
+ * @retval TRUE Device has the specified option
+ * @retval FALSE Device does not have the specified option, invalid input
+ * parameters or other error conditions.
+ *
+ * @since 0.2.0
+ */
+SR_API gboolean sr_dev_has_option(const struct sr_dev_inst *sdi, int key)
+{
+ GVariant *gvar;
+ const int *devopts;
+ gsize num_opts, i;
+ int ret;
+
+ if (!sdi || !sdi->driver || !sdi->driver->config_list)
+ return FALSE;
+
+ if (sdi->driver->config_list(SR_CONF_DEVICE_OPTIONS,
+ &gvar, sdi, NULL) != SR_OK)
+ return FALSE;
+
+ ret = FALSE;
+ devopts = g_variant_get_fixed_array(gvar, &num_opts, sizeof(int32_t));
+ for (i = 0; i < num_opts; i++) {
+ if (devopts[i] == key) {
+ ret = TRUE;
+ break;
+ }
+ }
+ g_variant_unref(gvar);
+
+ return ret;
+}
+
+/** @private
+ * Allocate and init new device instance struct.
+ * @param[in] index @copydoc sr_dev_inst::index
+ * @param[in] status @copydoc sr_dev_inst::status
+ * @param[in] vendor @copydoc sr_dev_inst::vendor
+ * @param[in] model @copydoc sr_dev_inst::model
+ * @param[in] version @copydoc sr_dev_inst::version
+ *
+ * @retval NULL Error
+ * @retval struct sr_dev_inst *. Dynamically allocated, free using
+ * sr_dev_inst_free().
+ */
+SR_PRIV struct sr_dev_inst *sr_dev_inst_new(int index, int status,
+ const char *vendor, const char *model, const char *version)
+{
+ struct sr_dev_inst *sdi;
+
+ if (!(sdi = g_try_malloc(sizeof(struct sr_dev_inst)))) {
+ sr_err("Device instance malloc failed.");
+ return NULL;
+ }
+
+ sdi->driver = NULL;
+ sdi->index = index;
+ sdi->status = status;
+ sdi->inst_type = -1;
+ sdi->vendor = vendor ? g_strdup(vendor) : NULL;
+ sdi->model = model ? g_strdup(model) : NULL;
+ sdi->version = version ? g_strdup(version) : NULL;
+ sdi->channels = NULL;
+ sdi->channel_groups = NULL;
+ sdi->session = NULL;
+ sdi->conn = NULL;
+ sdi->priv = NULL;
+
+ return sdi;
+}
+
+/** @private
+ * Free device instance struct created by sr_dev_inst().
+ * @param sdi struct* to free.
+ */
+SR_PRIV void sr_dev_inst_free(struct sr_dev_inst *sdi)
+{
+ struct sr_channel *ch;
+ GSList *l;
+
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ g_free(ch->name);
+ g_free(ch);
+ }
+ g_slist_free(sdi->channels);
+
+ if (sdi->channel_groups)
+ g_slist_free(sdi->channel_groups);
+
+ g_free(sdi->vendor);
+ g_free(sdi->model);
+ g_free(sdi->version);
+ g_free(sdi);
+}
+
+#ifdef HAVE_LIBUSB_1_0
+
+/** @private
+ * Allocate and init struct for USB device instance.
+ * @param[in] bus @copydoc sr_usb_dev_inst::bus
+ * @param[in] address @copydoc sr_usb_dev_inst::address
+ * @param[in] hdl @copydoc sr_usb_dev_inst::devhdl
+ *
+ * @retval NULL Error
+ * @retval other struct sr_usb_dev_inst * for USB device instance.
+ */
+SR_PRIV struct sr_usb_dev_inst *sr_usb_dev_inst_new(uint8_t bus,
+ uint8_t address, struct libusb_device_handle *hdl)
+{
+ struct sr_usb_dev_inst *udi;
+
+ if (!(udi = g_try_malloc(sizeof(struct sr_usb_dev_inst)))) {
+ sr_err("USB device instance malloc failed.");
+ return NULL;
+ }
+
+ udi->bus = bus;
+ udi->address = address;
+ udi->devhdl = hdl;
+
+ return udi;
+}
+
+/** @private
+ * Free struct * allocated by sr_usb_dev_inst().
+ * @param usb struct* to free. Must not be NULL.
+ */
+SR_PRIV void sr_usb_dev_inst_free(struct sr_usb_dev_inst *usb)
+{
+ g_free(usb);
+}
+
+#endif
+
+#ifdef HAVE_LIBSERIALPORT
+
+/**
+ * @private
+ *
+ * Both parameters are copied to newly allocated strings, and freed
+ * automatically by sr_serial_dev_inst_free().
+ *
+ * @param[in] port OS-specific serial port specification. Examples:
+ * "/dev/ttyUSB0", "/dev/ttyACM1", "/dev/tty.Modem-0", "COM1".
+ * @param[in] serialcomm A serial communication parameters string, in the form
+ * of \<speed\>/\<data bits\>\<parity\>\<stopbits\>, for example
+ * "9600/8n1" or "600/7o2". This is an optional parameter;
+ * it may be filled in later.
+ *
+ * @return A pointer to a newly initialized struct sr_serial_dev_inst,
+ * or NULL on error.
+ */
+SR_PRIV struct sr_serial_dev_inst *sr_serial_dev_inst_new(const char *port,
+ const char *serialcomm)
+{
+ struct sr_serial_dev_inst *serial;
+
+ if (!port) {
+ sr_err("Serial port required.");
+ return NULL;
+ }
+
+ if (!(serial = g_try_malloc0(sizeof(struct sr_serial_dev_inst)))) {
+ sr_err("Serial device instance malloc failed.");
+ return NULL;
+ }
+
+ serial->port = g_strdup(port);
+ if (serialcomm)
+ serial->serialcomm = g_strdup(serialcomm);
+
+ return serial;
+}
+
+/** @private
+ * Free struct sr_serial_dev_inst * allocated by sr_serial_dev_inst().
+ * @param serial struct sr_serial_dev_inst * to free. Must not be NULL.
+ */
+SR_PRIV void sr_serial_dev_inst_free(struct sr_serial_dev_inst *serial)
+{
+ g_free(serial->port);
+ g_free(serial->serialcomm);
+ g_free(serial);
+}
+#endif
+
+/** @private */
+SR_PRIV struct sr_usbtmc_dev_inst *sr_usbtmc_dev_inst_new(const char *device)
+{
+ struct sr_usbtmc_dev_inst *usbtmc;
+
+ if (!device) {
+ sr_err("Device name required.");
+ return NULL;
+ }
+
+ if (!(usbtmc = g_try_malloc0(sizeof(struct sr_usbtmc_dev_inst)))) {
+ sr_err("USBTMC device instance malloc failed.");
+ return NULL;
+ }
+
+ usbtmc->device = g_strdup(device);
+ usbtmc->fd = -1;
+
+ return usbtmc;
+}
+
+/** @private */
+SR_PRIV void sr_usbtmc_dev_inst_free(struct sr_usbtmc_dev_inst *usbtmc)
+{
+ g_free(usbtmc->device);
+ g_free(usbtmc);
+}
+
+/**
+ * Get the list of devices/instances of the specified driver.
+ *
+ * @param driver The driver to use. Must not be NULL.
+ *
+ * @return The list of devices/instances of this driver, or NULL upon errors
+ * or if the list is empty.
+ *
+ * @since 0.2.0
+ */
+SR_API GSList *sr_dev_list(const struct sr_dev_driver *driver)
+{
+ if (driver && driver->dev_list)
+ return driver->dev_list();
+ else
+ return NULL;
+}
+
+/**
+ * Clear the list of device instances a driver knows about.
+ *
+ * @param driver The driver to use. This must be a pointer to one of
+ * the entries returned by sr_driver_list(). Must not be NULL.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_ARG Invalid driver
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_dev_clear(const struct sr_dev_driver *driver)
+{
+ int ret;
+
+ if (!driver) {
+ sr_err("Invalid driver.");
+ return SR_ERR_ARG;
+ }
+
+ if (driver->dev_clear)
+ ret = driver->dev_clear();
+ else
+ ret = std_dev_clear(driver, NULL);
+
+ return ret;
+}
+
+/**
+ * Open the specified device.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return SR_OK upon success, a negative error code upon errors.
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_dev_open(struct sr_dev_inst *sdi)
+{
+ int ret;
+
+ if (!sdi || !sdi->driver || !sdi->driver->dev_open)
+ return SR_ERR;
+
+ ret = sdi->driver->dev_open(sdi);
+
+ return ret;
+}
+
+/**
+ * Close the specified device.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return SR_OK upon success, a negative error code upon errors.
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_dev_close(struct sr_dev_inst *sdi)
+{
+ int ret;
+
+ if (!sdi || !sdi->driver || !sdi->driver->dev_close)
+ return SR_ERR;
+
+ ret = sdi->driver->dev_close(sdi);
+
+ return ret;
+}
+
+/** @} */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Cyrustek ES519XX protocol parser.
+ *
+ * Communication parameters: Unidirectional, 2400/7o1 or 19230/7o1
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "es519xx"
+
+/* Factors for the respective measurement mode (0 means "invalid"). */
+static const float factors_2400_11b[9][8] = {
+ {1e-4, 1e-3, 1e-2, 1e-1, 1, 0, 0, 0 }, /* V */
+ {1e-7, 1e-6, 0, 0, 0, 0, 0, 0 }, /* uA */
+ {1e-5, 1e-4, 0, 0, 0, 0, 0, 0 }, /* mA */
+ {1e-2, 0, 0, 0, 0, 0, 0, 0 }, /* A */
+ {1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 0, 0 }, /* RPM */
+ {1e-1, 1, 1e1, 1e2, 1e3, 1e4, 0, 0 }, /* Resistance */
+ {1, 1e1, 1e2, 1e3, 1e4, 1e5, 0, 0 }, /* Frequency */
+ {1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5}, /* Capacitance */
+ {1e-3, 0, 0, 0, 0, 0, 0, 0 }, /* Diode */
+};
+static const float factors_19200_11b_5digits[9][8] = {
+ {1e-4, 1e-3, 1e-2, 1e-1, 1e-5, 0, 0, 0}, /* V */
+ {1e-8, 1e-7, 0, 0, 0, 0, 0, 0}, /* uA */
+ {1e-6, 1e-5, 0, 0, 0, 0, 0, 0}, /* mA */
+ {0, 1e-3, 0, 0, 0, 0, 0, 0}, /* A */
+ {1e-4, 1e-3, 1e-2, 1e-1, 1, 0, 0, 0}, /* Manual A */
+ {1e-2, 1e-1, 1, 1e1, 1e2, 1e3, 1e4, 0}, /* Resistance */
+ {1e-1, 0, 1, 1e1, 1e2, 1e3, 1e4, 0}, /* Frequency */
+ {1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5}, /* Capacitance */
+ {1e-4, 0, 0, 0, 0, 0, 0, 0 }, /* Diode */
+};
+static const float factors_19200_11b_clampmeter[9][8] = {
+ {1e-3, 1e-2, 1e-1, 1, 1e-4, 0, 0, 0}, /* V */
+ {1e-7, 1e-6, 0, 0, 0, 0, 0, 0}, /* uA */
+ {1e-5, 1e-4, 0, 0, 0, 0, 0, 0}, /* mA */
+ {1e-2, 0, 0, 0, 0, 0, 0, 0}, /* A */
+ {1e-3, 1e-2, 1e-1, 1, 0, 0, 0, 0}, /* Manual A */
+ {1e-1, 1, 1e1, 1e2, 1e3, 1e4, 0, 0}, /* Resistance */
+ {1e-1, 0, 1, 1e1, 1e2, 1e3, 1e4, 0}, /* Frequency */
+ {1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5}, /* Capacitance */
+ {1e-3, 0, 0, 0, 0, 0, 0, 0 }, /* Diode */
+};
+static const float factors_19200_11b[9][8] = {
+ {1e-3, 1e-2, 1e-1, 1, 1e-4, 0, 0, 0}, /* V */
+ {1e-7, 1e-6, 0, 0, 0, 0, 0, 0}, /* uA */
+ {1e-5, 1e-4, 0, 0, 0, 0, 0, 0}, /* mA */
+ {1e-3, 1e-2, 0, 0, 0, 0, 0, 0}, /* A */
+ {0, 0, 0, 0, 0, 0, 0, 0}, /* Manual A */
+ {1e-1, 1, 1e1, 1e2, 1e3, 1e4, 0, 0}, /* Resistance */
+ {1, 1e1, 1e2, 1e3, 1e4, 0, 0, 0}, /* Frequency */
+ {1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 0}, /* Capacitance */
+ {1e-3, 0, 0, 0, 0, 0, 0, 0}, /* Diode */
+};
+static const float factors_19200_14b[9][8] = {
+ {1e-4, 1e-3, 1e-2, 1e-1, 1e-5, 0, 0, 0}, /* V */
+ {1e-8, 1e-7, 0, 0, 0, 0, 0, 0}, /* uA */
+ {1e-6, 1e-5, 0, 0, 0, 0, 0, 0}, /* mA */
+ {1e-3, 0, 0, 0, 0, 0, 0, 0}, /* A */
+ {1e-4, 1e-3, 1e-2, 1e-1, 1, 0, 0, 0}, /* Manual A */
+ {1e-2, 1e-1, 1, 1e1, 1e2, 1e3, 1e4, 0}, /* Resistance */
+ {1e-2, 1e-1, 0, 1, 1e1, 1e2, 1e3, 1e4}, /* Frequency */
+ {1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5}, /* Capacitance */
+ {1e-4, 0, 0, 0, 0, 0, 0, 0 }, /* Diode */
+};
+
+static int parse_value(const uint8_t *buf, struct es519xx_info *info,
+ float *result)
+{
+ int i, intval, num_digits;
+ float floatval;
+
+ num_digits = 4 + ((info->packet_size == 14) ? 1 : 0);
+
+ /* Bytes 1-4 (or 5): Value (4 or 5 decimal digits) */
+ if (info->is_ol) {
+ sr_spew("Over limit.");
+ *result = INFINITY;
+ return SR_OK;
+ } else if (info->is_ul) {
+ sr_spew("Under limit.");
+ *result = INFINITY;
+ return SR_OK;
+ } else if (!isdigit(buf[1]) || !isdigit(buf[2]) ||
+ !isdigit(buf[3]) || !isdigit(buf[4]) ||
+ (num_digits == 5 && !isdigit(buf[5]))) {
+ sr_dbg("Value contained invalid digits: %02x %02x %02x %02x "
+ "(%c %c %c %c).", buf[1], buf[2], buf[3], buf[4],
+ buf[1], buf[2], buf[3], buf[4]);
+ return SR_ERR;
+ }
+ intval = (info->is_digit4) ? 1 : 0;
+ for (i = 0; i < num_digits; i++)
+ intval = 10 * intval + (buf[i + 1] - '0');
+
+ /* Apply sign. */
+ intval *= info->is_sign ? -1 : 1;
+
+ floatval = (float)intval;
+
+ /* Note: The decimal point position will be parsed later. */
+
+ sr_spew("The display value is %f.", floatval);
+
+ *result = floatval;
+
+ return SR_OK;
+}
+
+static int parse_range(uint8_t b, float *floatval,
+ const struct es519xx_info *info)
+{
+ int idx, mode;
+ float factor = 0;
+
+ idx = b - '0';
+
+ if (idx < 0 || idx > 7) {
+ sr_dbg("Invalid range byte / index: 0x%02x / 0x%02x.", b, idx);
+ return SR_ERR;
+ }
+
+ /* Parse range byte (depends on the measurement mode). */
+ if (info->is_voltage)
+ mode = 0; /* V */
+ else if (info->is_current && info->is_micro)
+ mode = 1; /* uA */
+ else if (info->is_current && info->is_milli)
+ mode = 2; /* mA */
+ else if (info->is_current && info->is_auto)
+ mode = 3; /* A */
+ else if (info->is_current && !info->is_auto)
+ mode = 4; /* Manual A */
+ else if (info->is_rpm)
+ /* Not a typo, it's really index 4 in factors_2400_11b[][]. */
+ mode = 4; /* RPM */
+ else if (info->is_resistance || info->is_continuity)
+ mode = 5; /* Resistance */
+ else if (info->is_frequency)
+ mode = 6; /* Frequency */
+ else if (info->is_capacitance)
+ mode = 7; /* Capacitance */
+ else if (info->is_diode)
+ mode = 8; /* Diode */
+ else if (info->is_duty_cycle)
+ mode = 0; /* Dummy, unused */
+ else {
+ sr_dbg("Invalid mode, range byte was: 0x%02x.", b);
+ return SR_ERR;
+ }
+
+ if (info->is_vbar) {
+ if (info->is_micro)
+ factor = (const float[]){1e-1, 1}[idx];
+ else if (info->is_milli)
+ factor = (const float[]){1e-2, 1e-1}[idx];
+ }
+ else if (info->is_duty_cycle)
+ factor = 1e-1;
+ else if (info->baudrate == 2400)
+ factor = factors_2400_11b[mode][idx];
+ else if (info->fivedigits)
+ factor = factors_19200_11b_5digits[mode][idx];
+ else if (info->clampmeter)
+ factor = factors_19200_11b_clampmeter[mode][idx];
+ else if (info->packet_size == 11)
+ factor = factors_19200_11b[mode][idx];
+ else if (info->packet_size == 14)
+ factor = factors_19200_14b[mode][idx];
+
+ if (factor == 0) {
+ sr_dbg("Invalid factor for range byte: 0x%02x.", b);
+ return SR_ERR;
+ }
+
+ /* Apply respective factor (mode-dependent) on the value. */
+ *floatval *= factor;
+ sr_dbg("Applying factor %f, new value is %f.", factor, *floatval);
+
+ return SR_OK;
+}
+
+static void parse_flags(const uint8_t *buf, struct es519xx_info *info)
+{
+ int function, status;
+
+ function = 5 + ((info->packet_size == 14) ? 1 : 0);
+ status = function + 1;
+
+ /* Status byte */
+ if (info->alt_functions) {
+ info->is_sign = (buf[status] & (1 << 3)) != 0;
+ info->is_batt = (buf[status] & (1 << 2)) != 0; /* Bat. low */
+ info->is_ol = (buf[status] & (1 << 1)) != 0; /* Overflow */
+ info->is_ol |= (buf[status] & (1 << 0)) != 0; /* Overflow */
+ } else {
+ info->is_judge = (buf[status] & (1 << 3)) != 0;
+ info->is_sign = (buf[status] & (1 << 2)) != 0;
+ info->is_batt = (buf[status] & (1 << 1)) != 0; /* Bat. low */
+ info->is_ol = (buf[status] & (1 << 0)) != 0; /* Overflow */
+ }
+
+ if (info->packet_size == 14) {
+ /* Option 1 byte */
+ info->is_max = (buf[8] & (1 << 3)) != 0;
+ info->is_min = (buf[8] & (1 << 2)) != 0;
+ info->is_rel = (buf[8] & (1 << 1)) != 0;
+ info->is_rmr = (buf[8] & (1 << 0)) != 0;
+
+ /* Option 2 byte */
+ info->is_ul = (buf[9] & (1 << 3)) != 0; /* Underflow */
+ info->is_pmax = (buf[9] & (1 << 2)) != 0; /* Max. peak value */
+ info->is_pmin = (buf[9] & (1 << 1)) != 0; /* Min. peak value */
+
+ /* Option 3 byte */
+ info->is_dc = (buf[10] & (1 << 3)) != 0;
+ info->is_ac = (buf[10] & (1 << 2)) != 0;
+ info->is_auto = (buf[10] & (1 << 1)) != 0;
+ info->is_vahz = (buf[10] & (1 << 0)) != 0;
+
+ /* LPF: Low-pass filter(s) */
+ if (info->selectable_lpf) {
+ /* Option 4 byte */
+ info->is_hold = (buf[11] & (1 << 3)) != 0;
+ info->is_vbar = (buf[11] & (1 << 2)) != 0;
+ info->is_lpf1 = (buf[11] & (1 << 1)) != 0;
+ info->is_lpf0 = (buf[11] & (1 << 0)) != 0;
+ } else {
+ /* Option 4 byte */
+ info->is_vbar = (buf[11] & (1 << 2)) != 0;
+ info->is_hold = (buf[11] & (1 << 1)) != 0;
+ info->is_lpf1 = (buf[11] & (1 << 0)) != 0;
+ }
+ } else if (info->alt_functions) {
+ /* Option 2 byte */
+ info->is_dc = (buf[8] & (1 << 3)) != 0;
+ info->is_auto = (buf[8] & (1 << 2)) != 0;
+ info->is_apo = (buf[8] & (1 << 0)) != 0;
+ info->is_ac = !info->is_dc;
+ } else {
+ /* Option 1 byte */
+ if (info->baudrate == 2400) {
+ info->is_pmax = (buf[7] & (1 << 3)) != 0;
+ info->is_pmin = (buf[7] & (1 << 2)) != 0;
+ info->is_vahz = (buf[7] & (1 << 0)) != 0;
+ } else if (info->fivedigits) {
+ info->is_ul = (buf[7] & (1 << 3)) != 0;
+ info->is_pmax = (buf[7] & (1 << 2)) != 0;
+ info->is_pmin = (buf[7] & (1 << 1)) != 0;
+ info->is_digit4 = (buf[7] & (1 << 0)) != 0;
+ } else if (info->clampmeter) {
+ info->is_ul = (buf[7] & (1 << 3)) != 0;
+ info->is_vasel = (buf[7] & (1 << 2)) != 0;
+ info->is_vbar = (buf[7] & (1 << 1)) != 0;
+ } else {
+ info->is_hold = (buf[7] & (1 << 3)) != 0;
+ info->is_max = (buf[7] & (1 << 2)) != 0;
+ info->is_min = (buf[7] & (1 << 1)) != 0;
+ }
+
+ /* Option 2 byte */
+ info->is_dc = (buf[8] & (1 << 3)) != 0;
+ info->is_ac = (buf[8] & (1 << 2)) != 0;
+ info->is_auto = (buf[8] & (1 << 1)) != 0;
+ if (info->baudrate == 2400)
+ info->is_apo = (buf[8] & (1 << 0)) != 0;
+ else
+ info->is_vahz = (buf[8] & (1 << 0)) != 0;
+ }
+
+ /* Function byte */
+ if (info->alt_functions) {
+ switch (buf[function]) {
+ case 0x3f: /* A */
+ info->is_current = info->is_auto = TRUE;
+ break;
+ case 0x3e: /* uA */
+ info->is_current = info->is_micro = info->is_auto = TRUE;
+ break;
+ case 0x3d: /* mA */
+ info->is_current = info->is_milli = info->is_auto = TRUE;
+ break;
+ case 0x3c: /* V */
+ info->is_voltage = TRUE;
+ break;
+ case 0x37: /* Resistance */
+ info->is_resistance = TRUE;
+ break;
+ case 0x36: /* Continuity */
+ info->is_continuity = TRUE;
+ break;
+ case 0x3b: /* Diode */
+ info->is_diode = TRUE;
+ break;
+ case 0x3a: /* Frequency */
+ info->is_frequency = TRUE;
+ break;
+ case 0x34: /* ADP0 */
+ case 0x35: /* ADP0 */
+ info->is_adp0 = TRUE;
+ break;
+ case 0x38: /* ADP1 */
+ case 0x39: /* ADP1 */
+ info->is_adp1 = TRUE;
+ break;
+ case 0x32: /* ADP2 */
+ case 0x33: /* ADP2 */
+ info->is_adp2 = TRUE;
+ break;
+ case 0x30: /* ADP3 */
+ case 0x31: /* ADP3 */
+ info->is_adp3 = TRUE;
+ break;
+ default:
+ sr_dbg("Invalid function byte: 0x%02x.", buf[function]);
+ break;
+ }
+ } else {
+ /* Note: Some of these mappings are fixed up later. */
+ switch (buf[function]) {
+ case 0x3b: /* V */
+ info->is_voltage = TRUE;
+ break;
+ case 0x3d: /* uA */
+ info->is_current = info->is_micro = info->is_auto = TRUE;
+ break;
+ case 0x3f: /* mA */
+ info->is_current = info->is_milli = info->is_auto = TRUE;
+ break;
+ case 0x30: /* A */
+ info->is_current = info->is_auto = TRUE;
+ break;
+ case 0x39: /* Manual A */
+ info->is_current = TRUE;
+ info->is_auto = FALSE; /* Manual mode */
+ break;
+ case 0x33: /* Resistance */
+ info->is_resistance = TRUE;
+ break;
+ case 0x35: /* Continuity */
+ info->is_continuity = TRUE;
+ break;
+ case 0x31: /* Diode */
+ info->is_diode = TRUE;
+ break;
+ case 0x32: /* Frequency / RPM / duty cycle */
+ if (info->packet_size == 14) {
+ if (info->is_judge)
+ info->is_duty_cycle = TRUE;
+ else
+ info->is_frequency = TRUE;
+ } else {
+ if (info->is_judge)
+ info->is_rpm = TRUE;
+ else
+ info->is_frequency = TRUE;
+ }
+ break;
+ case 0x36: /* Capacitance */
+ info->is_capacitance = TRUE;
+ break;
+ case 0x34: /* Temperature */
+ info->is_temperature = TRUE;
+ if (info->is_judge)
+ info->is_celsius = TRUE;
+ else
+ info->is_fahrenheit = TRUE;
+ /* IMPORTANT: The digits always represent Celsius! */
+ break;
+ case 0x3e: /* ADP0 */
+ info->is_adp0 = TRUE;
+ break;
+ case 0x3c: /* ADP1 */
+ info->is_adp1 = TRUE;
+ break;
+ case 0x38: /* ADP2 */
+ info->is_adp2 = TRUE;
+ break;
+ case 0x3a: /* ADP3 */
+ info->is_adp3 = TRUE;
+ break;
+ default:
+ sr_dbg("Invalid function byte: 0x%02x.", buf[function]);
+ break;
+ }
+ }
+
+ if (info->is_vahz && (info->is_voltage || info->is_current)) {
+ info->is_voltage = FALSE;
+ info->is_current = FALSE;
+ info->is_milli = info->is_micro = FALSE;
+ if (info->packet_size == 14) {
+ if (info->is_judge)
+ info->is_duty_cycle = TRUE;
+ else
+ info->is_frequency = TRUE;
+ } else {
+ if (info->is_judge)
+ info->is_rpm = TRUE;
+ else
+ info->is_frequency = TRUE;
+ }
+ }
+
+ if (info->is_current && (info->is_micro || info->is_milli) && info->is_vasel) {
+ info->is_current = info->is_auto = FALSE;
+ info->is_voltage = TRUE;
+ }
+
+ if (info->baudrate == 2400) {
+ /* Inverted mapping between mA and A, and no manual A. */
+ if (info->is_current && (info->is_milli || !info->is_auto)) {
+ info->is_milli = !info->is_milli;
+ info->is_auto = TRUE;
+ }
+ }
+}
+
+static void handle_flags(struct sr_datafeed_analog *analog,
+ float *floatval, const struct es519xx_info *info)
+{
+ /*
+ * Note: is_micro etc. are not used directly to multiply/divide
+ * floatval, this is handled via parse_range() and factors[][].
+ */
+
+ /* Measurement modes */
+ if (info->is_voltage) {
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ }
+ if (info->is_current) {
+ analog->mq = SR_MQ_CURRENT;
+ analog->unit = SR_UNIT_AMPERE;
+ }
+ if (info->is_resistance) {
+ analog->mq = SR_MQ_RESISTANCE;
+ analog->unit = SR_UNIT_OHM;
+ }
+ if (info->is_frequency) {
+ analog->mq = SR_MQ_FREQUENCY;
+ analog->unit = SR_UNIT_HERTZ;
+ }
+ if (info->is_capacitance) {
+ analog->mq = SR_MQ_CAPACITANCE;
+ analog->unit = SR_UNIT_FARAD;
+ }
+ if (info->is_temperature && info->is_celsius) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_CELSIUS;
+ }
+ if (info->is_temperature && info->is_fahrenheit) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_FAHRENHEIT;
+ }
+ if (info->is_continuity) {
+ analog->mq = SR_MQ_CONTINUITY;
+ analog->unit = SR_UNIT_BOOLEAN;
+ *floatval = (*floatval < 0.0 || *floatval > 25.0) ? 0.0 : 1.0;
+ }
+ if (info->is_diode) {
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ }
+ if (info->is_rpm) {
+ analog->mq = SR_MQ_FREQUENCY;
+ analog->unit = SR_UNIT_REVOLUTIONS_PER_MINUTE;
+ }
+ if (info->is_duty_cycle) {
+ analog->mq = SR_MQ_DUTY_CYCLE;
+ analog->unit = SR_UNIT_PERCENTAGE;
+ }
+
+ /* Measurement related flags */
+ if (info->is_ac)
+ analog->mqflags |= SR_MQFLAG_AC;
+ if (info->is_dc)
+ analog->mqflags |= SR_MQFLAG_DC;
+ if (info->is_auto)
+ analog->mqflags |= SR_MQFLAG_AUTORANGE;
+ if (info->is_diode)
+ analog->mqflags |= SR_MQFLAG_DIODE;
+ if (info->is_hold)
+ /*
+ * Note: HOLD only affects the number displayed on the LCD,
+ * but not the value sent via the protocol! It also does not
+ * affect the bargraph on the LCD.
+ */
+ analog->mqflags |= SR_MQFLAG_HOLD;
+ if (info->is_max)
+ analog->mqflags |= SR_MQFLAG_MAX;
+ if (info->is_min)
+ analog->mqflags |= SR_MQFLAG_MIN;
+ if (info->is_rel)
+ analog->mqflags |= SR_MQFLAG_RELATIVE;
+
+ /* Other flags */
+ if (info->is_judge)
+ sr_spew("Judge bit is set.");
+ if (info->is_batt)
+ sr_spew("Battery is low.");
+ if (info->is_ol)
+ sr_spew("Input overflow.");
+ if (info->is_ul)
+ sr_spew("Input underflow.");
+ if (info->is_pmax)
+ sr_spew("pMAX active, LCD shows max. peak value.");
+ if (info->is_pmin)
+ sr_spew("pMIN active, LCD shows min. peak value.");
+ if (info->is_vahz)
+ sr_spew("VAHZ active.");
+ if (info->is_apo)
+ sr_spew("Auto-Power-Off enabled.");
+ if (info->is_vbar)
+ sr_spew("VBAR active.");
+ if ((!info->selectable_lpf && info->is_lpf1) ||
+ (info->selectable_lpf && (!info->is_lpf0 || !info->is_lpf1)))
+ sr_spew("Low-pass filter feature is active.");
+}
+
+static gboolean flags_valid(const struct es519xx_info *info)
+{
+ int count;
+
+ /* Does the packet have more than one multiplier? */
+ count = (info->is_micro) ? 1 : 0;
+ count += (info->is_milli) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one multiplier detected in packet.");
+ return FALSE;
+ }
+
+ /* Does the packet "measure" more than one type of value? */
+ count = (info->is_voltage) ? 1 : 0;
+ count += (info->is_current) ? 1 : 0;
+ count += (info->is_resistance) ? 1 : 0;
+ count += (info->is_frequency) ? 1 : 0;
+ count += (info->is_capacitance) ? 1 : 0;
+ count += (info->is_temperature) ? 1 : 0;
+ count += (info->is_continuity) ? 1 : 0;
+ count += (info->is_diode) ? 1 : 0;
+ count += (info->is_rpm) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one measurement type detected in packet.");
+ return FALSE;
+ }
+
+ /* Both AC and DC set? */
+ if (info->is_ac && info->is_dc) {
+ sr_dbg("Both AC and DC flags detected in packet.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean sr_es519xx_packet_valid(const uint8_t *buf,
+ struct es519xx_info *info)
+{
+ int s;
+
+ s = info->packet_size;
+
+ if (s == 11 && memcmp(buf, buf + s, s))
+ return FALSE;
+
+ if (buf[s - 2] != '\r' || buf[s - 1] != '\n')
+ return FALSE;
+
+ parse_flags(buf, info);
+
+ if (!flags_valid(info))
+ return FALSE;
+
+ return TRUE;
+}
+
+static int sr_es519xx_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog,
+ struct es519xx_info *info)
+{
+ int ret;
+
+ if (!sr_es519xx_packet_valid(buf, info))
+ return SR_ERR;
+
+ if ((ret = parse_value(buf, info, floatval)) != SR_OK) {
+ sr_dbg("Error parsing value: %d.", ret);
+ return ret;
+ }
+
+ if ((ret = parse_range(buf[0], floatval, info)) != SR_OK)
+ return ret;
+
+ handle_flags(analog, floatval, info);
+ return SR_OK;
+}
+
+/*
+ * Functions for 2400 baud / 11 bytes protocols.
+ * This includes ES51962, ES51971, ES51972, ES51978 and ES51989.
+ */
+SR_PRIV gboolean sr_es519xx_2400_11b_packet_valid(const uint8_t *buf)
+{
+ struct es519xx_info info;
+
+ memset(&info, 0, sizeof(struct es519xx_info));
+ info.baudrate = 2400;
+ info.packet_size = 11;
+
+ return sr_es519xx_packet_valid(buf, &info);
+}
+
+SR_PRIV int sr_es519xx_2400_11b_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ struct es519xx_info *info_local;
+
+ info_local = info;
+ memset(info_local, 0, sizeof(struct es519xx_info));
+ info_local->baudrate = 2400;
+ info_local->packet_size = 11;
+
+ return sr_es519xx_parse(buf, floatval, analog, info);
+}
+
+/*
+ * Functions for 2400 baud / 11 byte protocols.
+ * This includes ES51960, ES51977 and ES51988.
+ */
+SR_PRIV gboolean sr_es519xx_2400_11b_altfn_packet_valid(const uint8_t *buf)
+{
+ struct es519xx_info info;
+
+ memset(&info, 0, sizeof(struct es519xx_info));
+ info.baudrate = 2400;
+ info.packet_size = 11;
+ info.alt_functions = TRUE;
+
+ return sr_es519xx_packet_valid(buf, &info);
+}
+
+SR_PRIV int sr_es519xx_2400_11b_altfn_parse(const uint8_t *buf,
+ float *floatval, struct sr_datafeed_analog *analog, void *info)
+{
+ struct es519xx_info *info_local;
+
+ info_local = info;
+ memset(info_local, 0, sizeof(struct es519xx_info));
+ info_local->baudrate = 2400;
+ info_local->packet_size = 11;
+ info_local->alt_functions = TRUE;
+
+ return sr_es519xx_parse(buf, floatval, analog, info);
+}
+
+/*
+ * Functions for 19200 baud / 11 bytes protocols with 5 digits display.
+ * This includes ES51911, ES51916 and ES51918.
+ */
+SR_PRIV gboolean sr_es519xx_19200_11b_5digits_packet_valid(const uint8_t *buf)
+{
+ struct es519xx_info info;
+
+ memset(&info, 0, sizeof(struct es519xx_info));
+ info.baudrate = 19200;
+ info.packet_size = 11;
+ info.fivedigits = TRUE;
+
+ return sr_es519xx_packet_valid(buf, &info);
+}
+
+SR_PRIV int sr_es519xx_19200_11b_5digits_parse(const uint8_t *buf,
+ float *floatval, struct sr_datafeed_analog *analog, void *info)
+{
+ struct es519xx_info *info_local;
+
+ info_local = info;
+ memset(info_local, 0, sizeof(struct es519xx_info));
+ info_local->baudrate = 19200;
+ info_local->packet_size = 11;
+ info_local->fivedigits = TRUE;
+
+ return sr_es519xx_parse(buf, floatval, analog, info);
+}
+
+/*
+ * Functions for 19200 baud / 11 bytes protocols with clamp meter support.
+ * This includes ES51967 and ES51969.
+ */
+SR_PRIV gboolean sr_es519xx_19200_11b_clamp_packet_valid(const uint8_t *buf)
+{
+ struct es519xx_info info;
+
+ memset(&info, 0, sizeof(struct es519xx_info));
+ info.baudrate = 19200;
+ info.packet_size = 11;
+ info.clampmeter = TRUE;
+
+ return sr_es519xx_packet_valid(buf, &info);
+}
+
+SR_PRIV int sr_es519xx_19200_11b_clamp_parse(const uint8_t *buf,
+ float *floatval, struct sr_datafeed_analog *analog, void *info)
+{
+ struct es519xx_info *info_local;
+
+ info_local = info;
+ memset(info_local, 0, sizeof(struct es519xx_info));
+ info_local->baudrate = 19200;
+ info_local->packet_size = 11;
+ info_local->clampmeter = TRUE;
+
+ return sr_es519xx_parse(buf, floatval, analog, info);
+}
+
+/*
+ * Functions for 19200 baud / 11 bytes protocols.
+ * This includes ES51981, ES51982, ES51983, ES51984 and ES51986.
+ */
+SR_PRIV gboolean sr_es519xx_19200_11b_packet_valid(const uint8_t *buf)
+{
+ struct es519xx_info info;
+
+ memset(&info, 0, sizeof(struct es519xx_info));
+ info.baudrate = 19200;
+ info.packet_size = 11;
+
+ return sr_es519xx_packet_valid(buf, &info);
+}
+
+SR_PRIV int sr_es519xx_19200_11b_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ struct es519xx_info *info_local;
+
+ info_local = info;
+ memset(info_local, 0, sizeof(struct es519xx_info));
+ info_local->baudrate = 19200;
+ info_local->packet_size = 11;
+
+ return sr_es519xx_parse(buf, floatval, analog, info);
+}
+
+/*
+ * Functions for 19200 baud / 14 bytes protocols.
+ * This includes ES51921 and ES51922.
+ */
+SR_PRIV gboolean sr_es519xx_19200_14b_packet_valid(const uint8_t *buf)
+{
+ struct es519xx_info info;
+
+ memset(&info, 0, sizeof(struct es519xx_info));
+ info.baudrate = 19200;
+ info.packet_size = 14;
+
+ return sr_es519xx_packet_valid(buf, &info);
+}
+
+SR_PRIV int sr_es519xx_19200_14b_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ struct es519xx_info *info_local;
+
+ info_local = info;
+ memset(info_local, 0, sizeof(struct es519xx_info));
+ info_local->baudrate = 19200;
+ info_local->packet_size = 14;
+
+ return sr_es519xx_parse(buf, floatval, analog, info);
+}
+
+/*
+ * Functions for 19200 baud / 14 bytes protocols with selectable LPF.
+ * This includes ES51931 and ES51932.
+ */
+SR_PRIV gboolean sr_es519xx_19200_14b_sel_lpf_packet_valid(const uint8_t *buf)
+{
+ struct es519xx_info info;
+
+ memset(&info, 0, sizeof(struct es519xx_info));
+ info.baudrate = 19200;
+ info.packet_size = 14;
+ info.selectable_lpf = TRUE;
+
+ return sr_es519xx_packet_valid(buf, &info);
+}
+
+SR_PRIV int sr_es519xx_19200_14b_sel_lpf_parse(const uint8_t *buf,
+ float *floatval, struct sr_datafeed_analog *analog, void *info)
+{
+ struct es519xx_info *info_local;
+
+ info_local = info;
+ memset(info_local, 0, sizeof(struct es519xx_info));
+ info_local->baudrate = 19200;
+ info_local->packet_size = 14;
+ info_local->selectable_lpf = TRUE;
+
+ return sr_es519xx_parse(buf, floatval, analog, info);
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Fortune Semiconductor FS9721_LP3/FS9721B protocol parser.
+ *
+ * FS9721_LP3: 4000 counts (3 3/4 digits)
+ * FS9721B/Q100: 2400 counts (3 2/3 digits)
+ *
+ * Same for both chips:
+ * - Packages: Bare die (78 pins) or QFP-100
+ * - Communication parameters: Unidirectional, 2400/8n1
+ * - The protocol seems to be exactly the same.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "fs9721"
+
+static int parse_digit(uint8_t b)
+{
+ switch (b) {
+ case 0x7d:
+ return 0;
+ case 0x05:
+ return 1;
+ case 0x5b:
+ return 2;
+ case 0x1f:
+ return 3;
+ case 0x27:
+ return 4;
+ case 0x3e:
+ return 5;
+ case 0x7e:
+ return 6;
+ case 0x15:
+ return 7;
+ case 0x7f:
+ return 8;
+ case 0x3f:
+ return 9;
+ default:
+ sr_dbg("Invalid digit byte: 0x%02x.", b);
+ return -1;
+ }
+}
+
+static gboolean sync_nibbles_valid(const uint8_t *buf)
+{
+ int i;
+
+ /* Check the synchronization nibbles, and make sure they all match. */
+ for (i = 0; i < FS9721_PACKET_SIZE; i++) {
+ if (((buf[i] >> 4) & 0x0f) != (i + 1)) {
+ sr_dbg("Sync nibble in byte %d (0x%02x) is invalid.",
+ i, buf[i]);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean flags_valid(const struct fs9721_info *info)
+{
+ int count;
+
+ /* Does the packet have more than one multiplier? */
+ count = 0;
+ count += (info->is_nano) ? 1 : 0;
+ count += (info->is_micro) ? 1 : 0;
+ count += (info->is_milli) ? 1 : 0;
+ count += (info->is_kilo) ? 1 : 0;
+ count += (info->is_mega) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one multiplier detected in packet.");
+ return FALSE;
+ }
+
+ /* Does the packet "measure" more than one type of value? */
+ count = 0;
+ count += (info->is_hz) ? 1 : 0;
+ count += (info->is_ohm) ? 1 : 0;
+ count += (info->is_farad) ? 1 : 0;
+ count += (info->is_ampere) ? 1 : 0;
+ count += (info->is_volt) ? 1 : 0;
+ count += (info->is_percent) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one measurement type detected in packet.");
+ return FALSE;
+ }
+
+ /* Both AC and DC set? */
+ if (info->is_ac && info->is_dc) {
+ sr_dbg("Both AC and DC flags detected in packet.");
+ return FALSE;
+ }
+
+ /* RS232 flag not set? */
+ if (!info->is_rs232) {
+ sr_dbg("No RS232 flag detected in packet.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int parse_value(const uint8_t *buf, float *result)
+{
+ int i, sign, intval = 0, digits[4];
+ uint8_t digit_bytes[4];
+ float floatval;
+
+ /* Byte 1: LCD SEG2 */
+ sign = ((buf[1] & (1 << 3)) != 0) ? -1 : 1;
+
+ /*
+ * Bytes 1-8: Value (4 decimal digits, sign, decimal point)
+ *
+ * Over limit: "0L" (LCD), 0x00 0x7d 0x68 0x00 (digit bytes).
+ */
+
+ /* Merge the two nibbles for a digit into one byte. */
+ for (i = 0; i < 4; i++) {
+ digit_bytes[i] = ((buf[1 + (i * 2)] & 0x0f) << 4);
+ digit_bytes[i] |= (buf[1 + (i * 2) + 1] & 0x0f);
+
+ /* Bit 7 in the byte is not part of the digit. */
+ digit_bytes[i] &= ~(1 << 7);
+ }
+
+ /* Check for "OL". */
+ if (digit_bytes[0] == 0x00 && digit_bytes[1] == 0x7d &&
+ digit_bytes[2] == 0x68 && digit_bytes[3] == 0x00) {
+ sr_spew("Over limit.");
+ *result = INFINITY;
+ return SR_OK;
+ }
+
+ /* Parse the digits. */
+ for (i = 0; i < 4; i++)
+ digits[i] = parse_digit(digit_bytes[i]);
+ sr_spew("Digits: %02x %02x %02x %02x (%d%d%d%d).",
+ digit_bytes[0], digit_bytes[1], digit_bytes[2], digit_bytes[3],
+ digits[0], digits[1], digits[2], digits[3]);
+
+ /* Merge all digits into an integer value. */
+ for (i = 0; i < 4; i++) {
+ intval *= 10;
+ intval += digits[i];
+ }
+
+ floatval = (float)intval;
+
+ /* Decimal point position. */
+ if ((buf[3] & (1 << 3)) != 0) {
+ floatval /= 1000;
+ sr_spew("Decimal point after first digit.");
+ } else if ((buf[5] & (1 << 3)) != 0) {
+ floatval /= 100;
+ sr_spew("Decimal point after second digit.");
+ } else if ((buf[7] & (1 << 3)) != 0) {
+ floatval /= 10;
+ sr_spew("Decimal point after third digit.");
+ } else {
+ sr_spew("No decimal point in the number.");
+ }
+
+ /* Apply sign. */
+ floatval *= sign;
+
+ sr_spew("The display value is %f.", floatval);
+
+ *result = floatval;
+
+ return SR_OK;
+}
+
+static void parse_flags(const uint8_t *buf, struct fs9721_info *info)
+{
+ /* Byte 0: LCD SEG1 */
+ info->is_ac = (buf[0] & (1 << 3)) != 0;
+ info->is_dc = (buf[0] & (1 << 2)) != 0;
+ info->is_auto = (buf[0] & (1 << 1)) != 0;
+ info->is_rs232 = (buf[0] & (1 << 0)) != 0;
+
+ /* Byte 1: LCD SEG2 */
+ info->is_sign = (buf[1] & (1 << 3)) != 0;
+
+ /* Byte 9: LCD SEG10 */
+ info->is_micro = (buf[9] & (1 << 3)) != 0;
+ info->is_nano = (buf[9] & (1 << 2)) != 0;
+ info->is_kilo = (buf[9] & (1 << 1)) != 0;
+ info->is_diode = (buf[9] & (1 << 0)) != 0;
+
+ /* Byte 10: LCD SEG11 */
+ info->is_milli = (buf[10] & (1 << 3)) != 0;
+ info->is_percent = (buf[10] & (1 << 2)) != 0;
+ info->is_mega = (buf[10] & (1 << 1)) != 0;
+ info->is_beep = (buf[10] & (1 << 0)) != 0;
+
+ /* Byte 11: LCD SEG12 */
+ info->is_farad = (buf[11] & (1 << 3)) != 0;
+ info->is_ohm = (buf[11] & (1 << 2)) != 0;
+ info->is_rel = (buf[11] & (1 << 1)) != 0;
+ info->is_hold = (buf[11] & (1 << 0)) != 0;
+
+ /* Byte 12: LCD SEG13 */
+ info->is_ampere = (buf[12] & (1 << 3)) != 0;
+ info->is_volt = (buf[12] & (1 << 2)) != 0;
+ info->is_hz = (buf[12] & (1 << 1)) != 0;
+ info->is_bat = (buf[12] & (1 << 0)) != 0;
+
+ /* Byte 13: LCD SEG14 */
+ info->is_c2c1_11 = (buf[13] & (1 << 3)) != 0;
+ info->is_c2c1_10 = (buf[13] & (1 << 2)) != 0;
+ info->is_c2c1_01 = (buf[13] & (1 << 1)) != 0;
+ info->is_c2c1_00 = (buf[13] & (1 << 0)) != 0;
+}
+
+static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
+ const struct fs9721_info *info)
+{
+ /* Factors */
+ if (info->is_nano)
+ *floatval /= 1000000000;
+ if (info->is_micro)
+ *floatval /= 1000000;
+ if (info->is_milli)
+ *floatval /= 1000;
+ if (info->is_kilo)
+ *floatval *= 1000;
+ if (info->is_mega)
+ *floatval *= 1000000;
+
+ /* Measurement modes */
+ if (info->is_volt) {
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ }
+ if (info->is_ampere) {
+ analog->mq = SR_MQ_CURRENT;
+ analog->unit = SR_UNIT_AMPERE;
+ }
+ if (info->is_ohm) {
+ analog->mq = SR_MQ_RESISTANCE;
+ analog->unit = SR_UNIT_OHM;
+ }
+ if (info->is_hz) {
+ analog->mq = SR_MQ_FREQUENCY;
+ analog->unit = SR_UNIT_HERTZ;
+ }
+ if (info->is_farad) {
+ analog->mq = SR_MQ_CAPACITANCE;
+ analog->unit = SR_UNIT_FARAD;
+ }
+ if (info->is_beep) {
+ analog->mq = SR_MQ_CONTINUITY;
+ analog->unit = SR_UNIT_BOOLEAN;
+ *floatval = (*floatval == INFINITY) ? 0.0 : 1.0;
+ }
+ if (info->is_diode) {
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ }
+ if (info->is_percent) {
+ analog->mq = SR_MQ_DUTY_CYCLE;
+ analog->unit = SR_UNIT_PERCENTAGE;
+ }
+
+ /* Measurement related flags */
+ if (info->is_ac)
+ analog->mqflags |= SR_MQFLAG_AC;
+ if (info->is_dc)
+ analog->mqflags |= SR_MQFLAG_DC;
+ if (info->is_auto)
+ analog->mqflags |= SR_MQFLAG_AUTORANGE;
+ if (info->is_diode)
+ analog->mqflags |= SR_MQFLAG_DIODE;
+ if (info->is_hold)
+ analog->mqflags |= SR_MQFLAG_HOLD;
+ if (info->is_rel)
+ analog->mqflags |= SR_MQFLAG_RELATIVE;
+
+ /* Other flags */
+ if (info->is_rs232)
+ sr_spew("RS232 enabled.");
+ if (info->is_bat)
+ sr_spew("Battery is low.");
+ if (info->is_c2c1_00)
+ sr_spew("User-defined LCD symbol 0 is active.");
+ if (info->is_c2c1_01)
+ sr_spew("User-defined LCD symbol 1 is active.");
+ if (info->is_c2c1_10)
+ sr_spew("User-defined LCD symbol 2 is active.");
+ if (info->is_c2c1_11)
+ sr_spew("User-defined LCD symbol 3 is active.");
+}
+
+SR_PRIV gboolean sr_fs9721_packet_valid(const uint8_t *buf)
+{
+ struct fs9721_info info;
+
+ parse_flags(buf, &info);
+
+ return (sync_nibbles_valid(buf) && flags_valid(&info));
+}
+
+/**
+ * Parse a protocol packet.
+ *
+ * @param buf Buffer containing the 14-byte protocol packet. Must not be NULL.
+ * @param floatval Pointer to a float variable. That variable will contain the
+ * result value upon parsing success. Mut not be NULL.
+ * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
+ * filled with data according to the protocol packet.
+ * Must not be NULL.
+ * @param info Pointer to a struct fs9721_info. The struct will be filled
+ * with data according to the protocol packet. Must not be NULL.
+ *
+ * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the
+ * 'analog' variable contents are undefined and should not be used.
+ */
+SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ int ret;
+ struct fs9721_info *info_local;
+
+ info_local = (struct fs9721_info *)info;
+
+ if ((ret = parse_value(buf, floatval)) != SR_OK) {
+ sr_dbg("Error parsing value: %d.", ret);
+ return ret;
+ }
+
+ parse_flags(buf, info_local);
+ handle_flags(analog, floatval, info_local);
+
+ return SR_OK;
+}
+
+SR_PRIV void sr_fs9721_00_temp_c(struct sr_datafeed_analog *analog, void *info)
+{
+ struct fs9721_info *info_local;
+
+ info_local = (struct fs9721_info *)info;
+
+ /* User-defined FS9721_LP3 flag 'c2c1_00' means temperature (C). */
+ if (info_local->is_c2c1_00) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_CELSIUS;
+ }
+}
+
+SR_PRIV void sr_fs9721_01_temp_c(struct sr_datafeed_analog *analog, void *info)
+{
+ struct fs9721_info *info_local;
+
+ info_local = (struct fs9721_info *)info;
+
+ /* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (C). */
+ if (info_local->is_c2c1_01) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_CELSIUS;
+ }
+}
+
+SR_PRIV void sr_fs9721_10_temp_c(struct sr_datafeed_analog *analog, void *info)
+{
+ struct fs9721_info *info_local;
+
+ info_local = (struct fs9721_info *)info;
+
+ /* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
+ if (info_local->is_c2c1_10) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_CELSIUS;
+ }
+}
+
+SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog *analog, void *info)
+{
+ struct fs9721_info *info_local;
+
+ info_local = (struct fs9721_info *)info;
+
+ /* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (F). */
+ if (info_local->is_c2c1_01) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_FAHRENHEIT;
+ }
+
+ /* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
+ if (info_local->is_c2c1_10) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_CELSIUS;
+ }
+}
+
+SR_PRIV void sr_fs9721_max_c_min(struct sr_datafeed_analog *analog, void *info)
+{
+ struct fs9721_info *info_local;
+
+ info_local = (struct fs9721_info *)info;
+
+ /* User-defined FS9721_LP3 flag 'c2c1_00' means MAX. */
+ if (info_local->is_c2c1_00)
+ analog->mqflags |= SR_MQFLAG_MAX;
+
+ /* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (C). */
+ if (info_local->is_c2c1_01) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_CELSIUS;
+ }
+
+ /* User-defined FS9721_LP3 flag 'c2c1_11' means MIN. */
+ if (info_local->is_c2c1_11)
+ analog->mqflags |= SR_MQFLAG_MIN;
+
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Fortune Semiconductor FS9922-DMM3/FS9922-DMM4 protocol parser.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "fs9922"
+
+static gboolean flags_valid(const struct fs9922_info *info)
+{
+ int count;
+
+ /* Does the packet have more than one multiplier? */
+ count = 0;
+ count += (info->is_nano) ? 1 : 0;
+ count += (info->is_micro) ? 1 : 0;
+ count += (info->is_milli) ? 1 : 0;
+ count += (info->is_kilo) ? 1 : 0;
+ count += (info->is_mega) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one multiplier detected in packet.");
+ return FALSE;
+ }
+
+ /*
+ * Does the packet "measure" more than one type of value?
+ *
+ * Note: In "diode mode", both is_diode and is_volt will be set.
+ * That is a valid use-case, so we don't want to error out below
+ * if it happens. Thus, we don't check for is_diode here.
+ */
+ count = 0;
+ // count += (info->is_diode) ? 1 : 0;
+ count += (info->is_percent) ? 1 : 0;
+ count += (info->is_volt) ? 1 : 0;
+ count += (info->is_ampere) ? 1 : 0;
+ count += (info->is_ohm) ? 1 : 0;
+ count += (info->is_hfe) ? 1 : 0;
+ count += (info->is_hertz) ? 1 : 0;
+ count += (info->is_farad) ? 1 : 0;
+ count += (info->is_celsius) ? 1 : 0;
+ count += (info->is_fahrenheit) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one measurement type detected in packet.");
+ return FALSE;
+ }
+
+ /* Both AC and DC set? */
+ if (info->is_ac && info->is_dc) {
+ sr_dbg("Both AC and DC flags detected in packet.");
+ return FALSE;
+ }
+
+ /* Both Celsius and Fahrenheit set? */
+ if (info->is_celsius && info->is_fahrenheit) {
+ sr_dbg("Both Celsius and Fahrenheit flags detected in packet.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int parse_value(const uint8_t *buf, float *result)
+{
+ int sign, intval;
+ float floatval;
+
+ /* Byte 0: Sign ('+' or '-') */
+ if (buf[0] == '+') {
+ sign = 1;
+ } else if (buf[0] == '-') {
+ sign = -1;
+ } else {
+ sr_dbg("Invalid sign byte: 0x%02x.", buf[0]);
+ return SR_ERR;
+ }
+
+ /*
+ * Bytes 1-4: Value (4 decimal digits)
+ *
+ * Over limit: "0.L" on the display, "?0:?" as protocol "digits".
+ */
+ if (buf[1] == '?' && buf[2] == '0' && buf[3] == ':' && buf[4] == '?') {
+ sr_spew("Over limit.");
+ *result = INFINITY;
+ return SR_OK;
+ } else if (!isdigit(buf[1]) || !isdigit(buf[2]) ||
+ !isdigit(buf[3]) || !isdigit(buf[4])) {
+ sr_dbg("Value contained invalid digits: %02x %02x %02x %02x ("
+ "%c %c %c %c).", buf[1], buf[2], buf[3], buf[4]);
+ return SR_ERR;
+ }
+ intval = 0;
+ intval += (buf[1] - '0') * 1000;
+ intval += (buf[2] - '0') * 100;
+ intval += (buf[3] - '0') * 10;
+ intval += (buf[4] - '0') * 1;
+
+ floatval = (float)intval;
+
+ /* Byte 5: Always ' ' (space, 0x20) */
+
+ /*
+ * Byte 6: Decimal point position ('0', '1', '2', or '4')
+ *
+ * Note: The Fortune Semiconductor FS9922-DMM3/4 datasheets both have
+ * an error/typo here. They claim that the values '0'/'1'/'2'/'3' are
+ * used, but '0'/'1'/'2'/'4' is actually correct.
+ */
+ if (buf[6] != '0' && buf[6] != '1' && buf[6] != '2' && buf[6] != '4') {
+ sr_dbg("Invalid decimal point value: 0x%02x.", buf[6]);
+ return SR_ERR;
+ }
+ if (buf[6] == '0')
+ floatval /= 1;
+ else if (buf[6] == '1')
+ floatval /= 1000;
+ else if (buf[6] == '2')
+ floatval /= 100;
+ else if (buf[6] == '4')
+ floatval /= 10;
+
+ /* Apply sign. */
+ floatval *= sign;
+
+ sr_spew("The display value is %f.", floatval);
+
+ *result = floatval;
+
+ return SR_OK;
+}
+
+static void parse_flags(const uint8_t *buf, struct fs9922_info *info)
+{
+ /* Z1/Z2/Z3/Z4 are bits for user-defined LCD symbols (on/off). */
+
+ /* Byte 7 */
+ /* Bit 7: Always 0 */
+ /* Bit 6: Always 0 */
+ info->is_auto = (buf[7] & (1 << 5)) != 0;
+ info->is_dc = (buf[7] & (1 << 4)) != 0;
+ info->is_ac = (buf[7] & (1 << 3)) != 0;
+ info->is_rel = (buf[7] & (1 << 2)) != 0;
+ info->is_hold = (buf[7] & (1 << 1)) != 0;
+ info->is_bpn = (buf[7] & (1 << 0)) != 0; /* Bargraph shown */
+
+ /* Byte 8 */
+ info->is_z1 = (buf[8] & (1 << 7)) != 0; /* User symbol 1 */
+ info->is_z2 = (buf[8] & (1 << 6)) != 0; /* User symbol 2 */
+ info->is_max = (buf[8] & (1 << 5)) != 0;
+ info->is_min = (buf[8] & (1 << 4)) != 0;
+ info->is_apo = (buf[8] & (1 << 3)) != 0; /* Auto-poweroff on */
+ info->is_bat = (buf[8] & (1 << 2)) != 0; /* Battery low */
+ info->is_nano = (buf[8] & (1 << 1)) != 0;
+ info->is_z3 = (buf[8] & (1 << 0)) != 0; /* User symbol 3 */
+
+ /* Byte 9 */
+ info->is_micro = (buf[9] & (1 << 7)) != 0;
+ info->is_milli = (buf[9] & (1 << 6)) != 0;
+ info->is_kilo = (buf[9] & (1 << 5)) != 0;
+ info->is_mega = (buf[9] & (1 << 4)) != 0;
+ info->is_beep = (buf[9] & (1 << 3)) != 0;
+ info->is_diode = (buf[9] & (1 << 2)) != 0;
+ info->is_percent = (buf[9] & (1 << 1)) != 0;
+ info->is_z4 = (buf[9] & (1 << 0)) != 0; /* User symbol 4 */
+
+ /* Byte 10 */
+ info->is_volt = (buf[10] & (1 << 7)) != 0;
+ info->is_ampere = (buf[10] & (1 << 6)) != 0;
+ info->is_ohm = (buf[10] & (1 << 5)) != 0;
+ info->is_hfe = (buf[10] & (1 << 4)) != 0;
+ info->is_hertz = (buf[10] & (1 << 3)) != 0;
+ info->is_farad = (buf[10] & (1 << 2)) != 0;
+ info->is_celsius = (buf[10] & (1 << 1)) != 0; /* Only FS9922-DMM4 */
+ info->is_fahrenheit = (buf[10] & (1 << 0)) != 0; /* Only FS9922-DMM4 */
+
+ /*
+ * Byte 11: Bar graph
+ *
+ * Bit 7 contains the sign of the bargraph number (if the bit is set,
+ * the number is negative), bits 6..0 contain the actual number.
+ * Valid range: 0-40 (FS9922-DMM3), 0-60 (FS9922-DMM4).
+ *
+ * Upon "over limit" the bargraph value is 1 count above the highest
+ * valid number (i.e. 41 or 61, depending on chip).
+ */
+ if (info->is_bpn) {
+ info->bargraph_sign = ((buf[11] & (1 << 7)) != 0) ? -1 : 1;
+ info->bargraph_value = (buf[11] & 0x7f);
+ info->bargraph_value *= info->bargraph_sign;
+ }
+
+ /* Byte 12: Always '\r' (carriage return, 0x0d, 13) */
+
+ /* Byte 13: Always '\n' (newline, 0x0a, 10) */
+}
+
+static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
+ const struct fs9922_info *info)
+{
+ /* Factors */
+ if (info->is_nano)
+ *floatval /= 1000000000;
+ if (info->is_micro)
+ *floatval /= 1000000;
+ if (info->is_milli)
+ *floatval /= 1000;
+ if (info->is_kilo)
+ *floatval *= 1000;
+ if (info->is_mega)
+ *floatval *= 1000000;
+
+ /* Measurement modes */
+ if (info->is_volt || info->is_diode) {
+ /* Note: In "diode mode" both is_diode and is_volt are set. */
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ }
+ if (info->is_ampere) {
+ analog->mq = SR_MQ_CURRENT;
+ analog->unit = SR_UNIT_AMPERE;
+ }
+ if (info->is_ohm) {
+ analog->mq = SR_MQ_RESISTANCE;
+ analog->unit = SR_UNIT_OHM;
+ }
+ if (info->is_hfe) {
+ analog->mq = SR_MQ_GAIN;
+ analog->unit = SR_UNIT_UNITLESS;
+ }
+ if (info->is_hertz) {
+ analog->mq = SR_MQ_FREQUENCY;
+ analog->unit = SR_UNIT_HERTZ;
+ }
+ if (info->is_farad) {
+ analog->mq = SR_MQ_CAPACITANCE;
+ analog->unit = SR_UNIT_FARAD;
+ }
+ if (info->is_celsius) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_CELSIUS;
+ }
+ if (info->is_fahrenheit) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_FAHRENHEIT;
+ }
+ if (info->is_beep) {
+ analog->mq = SR_MQ_CONTINUITY;
+ analog->unit = SR_UNIT_BOOLEAN;
+ *floatval = (*floatval == INFINITY) ? 0.0 : 1.0;
+ }
+ if (info->is_percent) {
+ analog->mq = SR_MQ_DUTY_CYCLE;
+ analog->unit = SR_UNIT_PERCENTAGE;
+ }
+
+ /* Measurement related flags */
+ if (info->is_ac)
+ analog->mqflags |= SR_MQFLAG_AC;
+ if (info->is_dc)
+ analog->mqflags |= SR_MQFLAG_DC;
+ if (info->is_auto)
+ analog->mqflags |= SR_MQFLAG_AUTORANGE;
+ if (info->is_diode)
+ analog->mqflags |= SR_MQFLAG_DIODE;
+ if (info->is_hold)
+ analog->mqflags |= SR_MQFLAG_HOLD;
+ if (info->is_max)
+ analog->mqflags |= SR_MQFLAG_MAX;
+ if (info->is_min)
+ analog->mqflags |= SR_MQFLAG_MIN;
+ if (info->is_rel)
+ analog->mqflags |= SR_MQFLAG_RELATIVE;
+
+ /* Other flags */
+ if (info->is_apo)
+ sr_spew("Automatic power-off function is active.");
+ if (info->is_bat)
+ sr_spew("Battery is low.");
+ if (info->is_z1)
+ sr_spew("User-defined LCD symbol 1 is active.");
+ if (info->is_z2)
+ sr_spew("User-defined LCD symbol 2 is active.");
+ if (info->is_z3)
+ sr_spew("User-defined LCD symbol 3 is active.");
+ if (info->is_z4)
+ sr_spew("User-defined LCD symbol 4 is active.");
+ if (info->is_bpn)
+ sr_spew("The bargraph value is %d.", info->bargraph_value);
+ else
+ sr_spew("The bargraph is not active.");
+
+}
+
+SR_PRIV gboolean sr_fs9922_packet_valid(const uint8_t *buf)
+{
+ struct fs9922_info info;
+
+ /* Byte 0: Sign (must be '+' or '-') */
+ if (buf[0] != '+' && buf[0] != '-')
+ return FALSE;
+
+ /* Byte 12: Always '\r' (carriage return, 0x0d, 13) */
+ /* Byte 13: Always '\n' (newline, 0x0a, 10) */
+ if (buf[12] != '\r' || buf[13] != '\n')
+ return FALSE;
+
+ parse_flags(buf, &info);
+
+ return flags_valid(&info);
+}
+
+/**
+ * Parse a protocol packet.
+ *
+ * @param buf Buffer containing the protocol packet. Must not be NULL.
+ * @param floatval Pointer to a float variable. That variable will contain the
+ * result value upon parsing success. Must not be NULL.
+ * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
+ * filled with data according to the protocol packet.
+ * Must not be NULL.
+ * @param info Pointer to a struct fs9922_info. The struct will be filled
+ * with data according to the protocol packet. Must not be NULL.
+ *
+ * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the
+ * 'analog' variable contents are undefined and should not be used.
+ */
+SR_PRIV int sr_fs9922_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ int ret;
+ struct fs9922_info *info_local;
+
+ info_local = (struct fs9922_info *)info;
+
+ if ((ret = parse_value(buf, floatval)) != SR_OK) {
+ sr_dbg("Error parsing value: %d.", ret);
+ return ret;
+ }
+
+ parse_flags(buf, info_local);
+ handle_flags(analog, floatval, info_local);
+
+ return SR_OK;
+}
+
+SR_PRIV void sr_fs9922_z1_diode(struct sr_datafeed_analog *analog, void *info)
+{
+ struct fs9922_info *info_local;
+
+ info_local = (struct fs9922_info *)info;
+
+ /* User-defined z1 flag means "diode mode". */
+ if (info_local->is_z1) {
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ analog->mqflags |= SR_MQFLAG_DIODE;
+ }
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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/>.
+ */
+
+/**
+ * @file
+ *
+ * BBC Goerz Metrawatt M2110 ASCII protocol parser.
+ *
+ * Most probably the simplest multimeter protocol ever ;-) .
+ */
+
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "m2110"
+
+SR_PRIV gboolean sr_m2110_packet_valid(const uint8_t *buf)
+{
+ float val;
+
+ if ((buf[7] != '\r') || (buf[8] != '\n'))
+ return FALSE;
+
+ if (!strncmp((const char *)buf, "OVERRNG", 7))
+ return TRUE;
+
+ if (sscanf((const char *)buf, "%f", &val) == 1)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+SR_PRIV int sr_m2110_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ float val;
+
+ (void)info;
+
+ /* We don't know the unit, so that's the best we can do. */
+ analog->mq = SR_MQ_GAIN;
+ analog->unit = SR_UNIT_UNITLESS;
+ analog->mqflags = 0;
+
+ if (!strncmp((const char *)buf, "OVERRNG", 7))
+ *floatval = INFINITY;
+ else if (sscanf((const char *)buf, "%f", &val) == 1)
+ *floatval = val;
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ *
+ * Metex 14-bytes ASCII protocol parser.
+ *
+ * @internal
+ * This should work for various multimeters which use this kind of protocol,
+ * even though there is some variation in which modes each DMM supports.
+ *
+ * It does _not_ work for all Metex DMMs, some use a quite different protocol.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "metex14"
+
+/** Parse value from buf, byte 2-8. */
+static int parse_value(const uint8_t *buf, struct metex14_info *info,
+ float *result)
+{
+ int i, is_ol, cnt;
+ char valstr[7 + 1];
+
+ /* Strip all spaces from bytes 2-8. */
+ memset(&valstr, 0, 7 + 1);
+ for (i = 0, cnt = 0; i < 7; i++) {
+ if (buf[2 + i] != ' ')
+ valstr[cnt++] = buf[2 + i];
+ }
+
+ /* Bytes 5-7: Over limit (various forms) */
+ is_ol = 0;
+ is_ol += (!strcasecmp((const char *)&valstr, ".OL")) ? 1 : 0;
+ is_ol += (!strcasecmp((const char *)&valstr, "O.L")) ? 1 : 0;
+ is_ol += (!strcasecmp((const char *)&valstr, "OL.")) ? 1 : 0;
+ is_ol += (!strcasecmp((const char *)&valstr, "OL")) ? 1 : 0;
+ is_ol += (!strcasecmp((const char *)&valstr, "-.OL")) ? 1 : 0;
+ is_ol += (!strcasecmp((const char *)&valstr, "-O.L")) ? 1 : 0;
+ is_ol += (!strcasecmp((const char *)&valstr, "-OL.")) ? 1 : 0;
+ is_ol += (!strcasecmp((const char *)&valstr, "-OL")) ? 1 : 0;
+ if (is_ol != 0) {
+ sr_spew("Over limit.");
+ *result = INFINITY;
+ return SR_OK;
+ }
+
+ /* Logic functions */
+ if (!strcmp((const char *)&valstr, "READY") ||
+ !strcmp((const char *)&valstr, "FLOAT")) {
+ *result = INFINITY;
+ info->is_logic = TRUE;
+ } else if (!strcmp((const char *)&valstr, "Hi")) {
+ *result = 1.0;
+ info->is_logic = TRUE;
+ } else if (!strcmp((const char *)&valstr, "Lo")) {
+ *result = 0.0;
+ info->is_logic = TRUE;
+ }
+ if (info->is_logic)
+ return SR_OK;
+
+ /* Bytes 2-8: Sign, value (up to 5 digits) and decimal point */
+ sscanf((const char *)&valstr, "%f", result);
+
+ sr_spew("The display value is %f.", *result);
+
+ return SR_OK;
+}
+
+static void parse_flags(const char *buf, struct metex14_info *info)
+{
+ int i, cnt;
+ char unit[4 + 1];
+ const char *u;
+
+ /* Bytes 0-1: Measurement mode AC, DC */
+ info->is_ac = !strncmp(buf, "AC", 2);
+ info->is_dc = !strncmp(buf, "DC", 2);
+
+ /* Bytes 2-8: See parse_value(). */
+
+ /* Strip all spaces from bytes 9-12. */
+ memset(&unit, 0, 4 + 1);
+ for (i = 0, cnt = 0; i < 4; i++) {
+ if (buf[9 + i] != ' ')
+ unit[cnt++] = buf[9 + i];
+ }
+
+ /* Bytes 9-12: Unit */
+ u = (const char *)&unit;
+ if (!strcasecmp(u, "A"))
+ info->is_ampere = TRUE;
+ else if (!strcasecmp(u, "mA"))
+ info->is_milli = info->is_ampere = TRUE;
+ else if (!strcasecmp(u, "uA"))
+ info->is_micro = info->is_ampere = TRUE;
+ else if (!strcasecmp(u, "V"))
+ info->is_volt = TRUE;
+ else if (!strcasecmp(u, "mV"))
+ info->is_milli = info->is_volt = TRUE;
+ else if (!strcasecmp(u, "Ohm"))
+ info->is_ohm = TRUE;
+ else if (!strcasecmp(u, "KOhm"))
+ info->is_kilo = info->is_ohm = TRUE;
+ else if (!strcasecmp(u, "MOhm"))
+ info->is_mega = info->is_ohm = TRUE;
+ else if (!strcasecmp(u, "pF"))
+ info->is_pico = info->is_farad = TRUE;
+ else if (!strcasecmp(u, "nF"))
+ info->is_nano = info->is_farad = TRUE;
+ else if (!strcasecmp(u, "uF"))
+ info->is_micro = info->is_farad = TRUE;
+ else if (!strcasecmp(u, "KHz"))
+ info->is_kilo = info->is_hertz = TRUE;
+ else if (!strcasecmp(u, "C"))
+ info->is_celsius = TRUE;
+ else if (!strcasecmp(u, "DB"))
+ info->is_decibel = TRUE;
+ else if (!strcasecmp(u, ""))
+ info->is_unitless = TRUE;
+
+ /* Bytes 0-1: Measurement mode, except AC/DC */
+ info->is_resistance = !strncmp(buf, "OH", 2) ||
+ (!strncmp(buf, " ", 2) && info->is_ohm);
+ info->is_capacity = !strncmp(buf, "CA", 2) ||
+ (!strncmp(buf, " ", 2) && info->is_farad);
+ info->is_temperature = !strncmp(buf, "TE", 2);
+ info->is_diode = !strncmp(buf, "DI", 2) ||
+ (!strncmp(buf, " ", 2) && info->is_volt && info->is_milli);
+ info->is_frequency = !strncmp(buf, "FR", 2) ||
+ (!strncmp(buf, " ", 2) && info->is_hertz);
+ info->is_gain = !strncmp(buf, "DB", 2);
+ info->is_hfe = !strncmp(buf, "HF", 2) ||
+ (!strncmp(buf, " ", 2) && !info->is_volt && !info->is_ohm &&
+ !info->is_logic && !info->is_farad && !info->is_hertz);
+ /*
+ * Note:
+ * - Protocol doesn't distinguish "resistance" from "beep" mode.
+ * - "DB" shows the logarithmic ratio of input voltage to a
+ * pre-stored (user-changeable) value in the DMM.
+ */
+
+ /* Byte 13: Always '\r' (carriage return, 0x0d, 13) */
+}
+
+static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
+ const struct metex14_info *info)
+{
+ /* Factors */
+ if (info->is_pico)
+ *floatval /= 1000000000000ULL;
+ if (info->is_nano)
+ *floatval /= 1000000000;
+ if (info->is_micro)
+ *floatval /= 1000000;
+ if (info->is_milli)
+ *floatval /= 1000;
+ if (info->is_kilo)
+ *floatval *= 1000;
+ if (info->is_mega)
+ *floatval *= 1000000;
+
+ /* Measurement modes */
+ if (info->is_volt) {
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ }
+ if (info->is_ampere) {
+ analog->mq = SR_MQ_CURRENT;
+ analog->unit = SR_UNIT_AMPERE;
+ }
+ if (info->is_ohm) {
+ analog->mq = SR_MQ_RESISTANCE;
+ analog->unit = SR_UNIT_OHM;
+ }
+ if (info->is_hertz) {
+ analog->mq = SR_MQ_FREQUENCY;
+ analog->unit = SR_UNIT_HERTZ;
+ }
+ if (info->is_farad) {
+ analog->mq = SR_MQ_CAPACITANCE;
+ analog->unit = SR_UNIT_FARAD;
+ }
+ if (info->is_celsius) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_CELSIUS;
+ }
+ if (info->is_diode) {
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ }
+ if (info->is_gain) {
+ analog->mq = SR_MQ_GAIN;
+ analog->unit = SR_UNIT_DECIBEL_VOLT;
+ }
+ if (info->is_hfe) {
+ analog->mq = SR_MQ_GAIN;
+ analog->unit = SR_UNIT_UNITLESS;
+ }
+ if (info->is_logic) {
+ analog->mq = SR_MQ_GAIN;
+ analog->unit = SR_UNIT_UNITLESS;
+ }
+
+ /* Measurement related flags */
+ if (info->is_ac)
+ analog->mqflags |= SR_MQFLAG_AC;
+ if (info->is_dc)
+ analog->mqflags |= SR_MQFLAG_DC;
+ if (info->is_diode)
+ analog->mqflags |= SR_MQFLAG_DIODE;
+}
+
+static gboolean flags_valid(const struct metex14_info *info)
+{
+ int count;
+
+ /* Does the packet have more than one multiplier? */
+ count = 0;
+ count += (info->is_pico) ? 1 : 0;
+ count += (info->is_nano) ? 1 : 0;
+ count += (info->is_micro) ? 1 : 0;
+ count += (info->is_milli) ? 1 : 0;
+ count += (info->is_kilo) ? 1 : 0;
+ count += (info->is_mega) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one multiplier detected in packet.");
+ return FALSE;
+ }
+
+ /* Does the packet "measure" more than one type of value? */
+ count = 0;
+ count += (info->is_ac) ? 1 : 0;
+ count += (info->is_dc) ? 1 : 0;
+ count += (info->is_resistance) ? 1 : 0;
+ count += (info->is_capacity) ? 1 : 0;
+ count += (info->is_temperature) ? 1 : 0;
+ count += (info->is_diode) ? 1 : 0;
+ count += (info->is_frequency) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one measurement type detected in packet.");
+ return FALSE;
+ }
+
+ /* Both AC and DC set? */
+ if (info->is_ac && info->is_dc) {
+ sr_dbg("Both AC and DC flags detected in packet.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#ifdef HAVE_LIBSERIALPORT
+SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial)
+{
+ const uint8_t wbuf = 'D';
+
+ sr_spew("Requesting DMM packet.");
+
+ return (serial_write(serial, &wbuf, 1) == 1) ? SR_OK : SR_ERR;
+}
+#endif
+
+SR_PRIV gboolean sr_metex14_packet_valid(const uint8_t *buf)
+{
+ struct metex14_info info;
+
+ memset(&info, 0x00, sizeof(struct metex14_info));
+ parse_flags((const char *)buf, &info);
+
+ if (!flags_valid(&info))
+ return FALSE;
+
+ if (buf[13] != '\r')
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Parse a protocol packet.
+ *
+ * @param buf Buffer containing the protocol packet. Must not be NULL.
+ * @param floatval Pointer to a float variable. That variable will be modified
+ * in-place depending on the protocol packet. Must not be NULL.
+ * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
+ * filled with data according to the protocol packet.
+ * Must not be NULL.
+ * @param info Pointer to a struct metex14_info. The struct will be filled
+ * with data according to the protocol packet. Must not be NULL.
+ *
+ * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the
+ * 'analog' variable contents are undefined and should not be used.
+ */
+SR_PRIV int sr_metex14_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ int ret;
+ struct metex14_info *info_local;
+
+ info_local = (struct metex14_info *)info;
+
+ /* Don't print byte 13. That one contains the carriage return. */
+ sr_dbg("DMM packet: \"%.13s\"", buf);
+
+ memset(info_local, 0x00, sizeof(struct metex14_info));
+
+ if ((ret = parse_value(buf, info_local, floatval)) != SR_OK) {
+ sr_dbg("Error parsing value: %d.", ret);
+ return ret;
+ }
+
+ parse_flags((const char *)buf, info_local);
+ handle_flags(analog, floatval, info_local);
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * 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/>.
+ */
+
+/*
+ * RadioShack 22-812 protocol parser.
+ *
+ * This protocol is currently encountered on the RadioShack 22-812 DMM.
+ * It is a 9-byte packet representing a 1:1 mapping of the LCD segments, hence
+ * the name rs9lcd.
+ *
+ * The chip is a bare die covered by a plastic blob. It is unclear if this chip
+ * and protocol is used on any other device.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "rs9lcd"
+
+/* Byte 1 of the packet, and the modes it represents */
+#define IND1_HZ (1 << 7)
+#define IND1_OHM (1 << 6)
+#define IND1_KILO (1 << 5)
+#define IND1_MEGA (1 << 4)
+#define IND1_FARAD (1 << 3)
+#define IND1_AMP (1 << 2)
+#define IND1_VOLT (1 << 1)
+#define IND1_MILI (1 << 0)
+/* Byte 2 of the packet, and the modes it represents */
+#define IND2_MICRO (1 << 7)
+#define IND2_NANO (1 << 6)
+#define IND2_DBM (1 << 5)
+#define IND2_SEC (1 << 4)
+#define IND2_DUTY (1 << 3)
+#define IND2_HFE (1 << 2)
+#define IND2_REL (1 << 1)
+#define IND2_MIN (1 << 0)
+/* Byte 7 of the packet, and the modes it represents */
+#define INFO_BEEP (1 << 7)
+#define INFO_DIODE (1 << 6)
+#define INFO_BAT (1 << 5)
+#define INFO_HOLD (1 << 4)
+#define INFO_NEG (1 << 3)
+#define INFO_AC (1 << 2)
+#define INFO_RS232 (1 << 1)
+#define INFO_AUTO (1 << 0)
+/* Instead of a decimal point, digit 4 carries the MAX flag */
+#define DIG4_MAX (1 << 3)
+/* Mask to remove the decimal point from a digit */
+#define DP_MASK (1 << 3)
+
+/* What the LCD values represent */
+#define LCD_0 0xd7
+#define LCD_1 0x50
+#define LCD_2 0xb5
+#define LCD_3 0xf1
+#define LCD_4 0x72
+#define LCD_5 0xe3
+#define LCD_6 0xe7
+#define LCD_7 0x51
+#define LCD_8 0xf7
+#define LCD_9 0xf3
+
+#define LCD_C 0x87
+#define LCD_E
+#define LCD_F
+#define LCD_h 0x66
+#define LCD_H 0x76
+#define LCD_I
+#define LCD_n
+#define LCD_P 0x37
+#define LCD_r
+
+enum {
+ MODE_DC_V = 0,
+ MODE_AC_V = 1,
+ MODE_DC_UA = 2,
+ MODE_DC_MA = 3,
+ MODE_DC_A = 4,
+ MODE_AC_UA = 5,
+ MODE_AC_MA = 6,
+ MODE_AC_A = 7,
+ MODE_OHM = 8,
+ MODE_FARAD = 9,
+ MODE_HZ = 10,
+ MODE_VOLT_HZ = 11, /* Dial set to V, Hz selected by Hz button */
+ MODE_AMP_HZ = 12, /* Dial set to A, Hz selected by Hz button */
+ MODE_DUTY = 13,
+ MODE_VOLT_DUTY = 14, /* Dial set to V, duty cycle selected */
+ MODE_AMP_DUTY = 15, /* Dial set to A, duty cycle selected */
+ MODE_WIDTH = 16,
+ MODE_VOLT_WIDTH = 17, /* Dial set to V, pulse width selected */
+ MODE_AMP_WIDTH = 18, /* Dial set to A, pulse width selected */
+ MODE_DIODE = 19,
+ MODE_CONT = 20,
+ MODE_HFE = 21,
+ MODE_LOGIC = 22,
+ MODE_DBM = 23,
+ /* MODE_EF = 24, */ /* Not encountered on any DMM */
+ MODE_TEMP = 25,
+ MODE_INVALID = 26,
+};
+
+enum {
+ READ_ALL,
+ READ_TEMP,
+};
+
+struct rs9lcd_packet {
+ uint8_t mode;
+ uint8_t indicatrix1;
+ uint8_t indicatrix2;
+ uint8_t digit4;
+ uint8_t digit3;
+ uint8_t digit2;
+ uint8_t digit1;
+ uint8_t info;
+ uint8_t checksum;
+};
+
+static gboolean checksum_valid(const struct rs9lcd_packet *rs_packet)
+{
+ uint8_t *raw;
+ uint8_t sum = 0;
+ int i;
+
+ raw = (void *)rs_packet;
+
+ for (i = 0; i < RS9LCD_PACKET_SIZE - 1; i++)
+ sum += raw[i];
+
+ /* This is just a funky constant added to the checksum. */
+ sum += 57;
+ sum -= rs_packet->checksum;
+ return (sum == 0);
+}
+
+static gboolean selection_good(const struct rs9lcd_packet *rs_packet)
+{
+ int count;
+
+ /* Does the packet have more than one multiplier? */
+ count = 0;
+ count += (rs_packet->indicatrix1 & IND1_KILO) ? 1 : 0;
+ count += (rs_packet->indicatrix1 & IND1_MEGA) ? 1 : 0;
+ count += (rs_packet->indicatrix1 & IND1_MILI) ? 1 : 0;
+ count += (rs_packet->indicatrix2 & IND2_MICRO) ? 1 : 0;
+ count += (rs_packet->indicatrix2 & IND2_NANO) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one multiplier detected in packet.");
+ return FALSE;
+ }
+
+ /* Does the packet "measure" more than one type of value? */
+ count = 0;
+ count += (rs_packet->indicatrix1 & IND1_HZ) ? 1 : 0;
+ count += (rs_packet->indicatrix1 & IND1_OHM) ? 1 : 0;
+ count += (rs_packet->indicatrix1 & IND1_FARAD) ? 1 : 0;
+ count += (rs_packet->indicatrix1 & IND1_AMP) ? 1 : 0;
+ count += (rs_packet->indicatrix1 & IND1_VOLT) ? 1 : 0;
+ count += (rs_packet->indicatrix2 & IND2_DBM) ? 1 : 0;
+ count += (rs_packet->indicatrix2 & IND2_SEC) ? 1 : 0;
+ count += (rs_packet->indicatrix2 & IND2_DUTY) ? 1 : 0;
+ count += (rs_packet->indicatrix2 & IND2_HFE) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one measurement type detected in packet.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Since the 22-812 does not identify itself in any way, shape, or form,
+ * we really don't know for sure who is sending the data. We must use every
+ * possible check to filter out bad packets, especially since detection of the
+ * 22-812 depends on how well we can filter the packets.
+ */
+SR_PRIV gboolean sr_rs9lcd_packet_valid(const uint8_t *buf)
+{
+ const struct rs9lcd_packet *rs_packet = (void *)buf;
+
+ /*
+ * Check for valid mode first, before calculating the checksum. No
+ * point calculating the checksum, if we know we'll reject the packet.
+ */
+ if (!(rs_packet->mode < MODE_INVALID))
+ return FALSE;
+
+ if (!checksum_valid(rs_packet)) {
+ sr_spew("Packet with invalid checksum. Discarding.");
+ return FALSE;
+ }
+
+ if (!selection_good(rs_packet)) {
+ sr_spew("Packet with invalid selection bits. Discarding.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static uint8_t decode_digit(uint8_t raw_digit)
+{
+ /* Take out the decimal point, so we can use a simple switch(). */
+ raw_digit &= ~DP_MASK;
+
+ switch (raw_digit) {
+ case 0x00:
+ case LCD_0:
+ return 0;
+ case LCD_1:
+ return 1;
+ case LCD_2:
+ return 2;
+ case LCD_3:
+ return 3;
+ case LCD_4:
+ return 4;
+ case LCD_5:
+ return 5;
+ case LCD_6:
+ return 6;
+ case LCD_7:
+ return 7;
+ case LCD_8:
+ return 8;
+ case LCD_9:
+ return 9;
+ default:
+ sr_dbg("Invalid digit byte: 0x%02x.", raw_digit);
+ return 0xff;
+ }
+}
+
+static double lcd_to_double(const struct rs9lcd_packet *rs_packet, int type)
+{
+ double rawval = 0, multiplier = 1;
+ uint8_t digit, raw_digit;
+ gboolean dp_reached = FALSE;
+ int i, end;
+
+ /* end = 1: Don't parse last digit. end = 0: Parse all digits. */
+ end = (type == READ_TEMP) ? 1 : 0;
+
+ /* We have 4 digits, and we start from the most significant. */
+ for (i = 3; i >= end; i--) {
+ raw_digit = *(&(rs_packet->digit4) + i);
+ digit = decode_digit(raw_digit);
+ if (digit == 0xff) {
+ rawval = NAN;
+ break;
+ }
+ /*
+ * Digit 1 does not have a decimal point. Instead, the decimal
+ * point is used to indicate MAX, so we must avoid testing it.
+ */
+ if ((i < 3) && (raw_digit & DP_MASK))
+ dp_reached = TRUE;
+ if (dp_reached)
+ multiplier /= 10;
+ rawval = rawval * 10 + digit;
+ }
+ rawval *= multiplier;
+ if (rs_packet->info & INFO_NEG)
+ rawval *= -1;
+
+ /* See if we need to multiply our raw value by anything. */
+ if (rs_packet->indicatrix1 & IND2_NANO)
+ rawval *= 1E-9;
+ else if (rs_packet->indicatrix2 & IND2_MICRO)
+ rawval *= 1E-6;
+ else if (rs_packet->indicatrix1 & IND1_MILI)
+ rawval *= 1E-3;
+ else if (rs_packet->indicatrix1 & IND1_KILO)
+ rawval *= 1E3;
+ else if (rs_packet->indicatrix1 & IND1_MEGA)
+ rawval *= 1E6;
+
+ return rawval;
+}
+
+static gboolean is_celsius(const struct rs9lcd_packet *rs_packet)
+{
+ return ((rs_packet->digit4 & ~DP_MASK) == LCD_C);
+}
+
+static gboolean is_shortcirc(const struct rs9lcd_packet *rs_packet)
+{
+ return ((rs_packet->digit2 & ~DP_MASK) == LCD_h);
+}
+
+static gboolean is_logic_high(const struct rs9lcd_packet *rs_packet)
+{
+ sr_spew("Digit 2: 0x%02x.", rs_packet->digit2 & ~DP_MASK);
+ return ((rs_packet->digit2 & ~DP_MASK) == LCD_H);
+}
+
+SR_PRIV int sr_rs9lcd_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ const struct rs9lcd_packet *rs_packet = (void *)buf;
+ double rawval;
+
+ (void)info;
+
+ rawval = lcd_to_double(rs_packet, READ_ALL);
+
+ switch (rs_packet->mode) {
+ case MODE_DC_V:
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ analog->mqflags |= SR_MQFLAG_DC;
+ break;
+ case MODE_AC_V:
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ analog->mqflags |= SR_MQFLAG_AC;
+ break;
+ case MODE_DC_UA: /* Fall through */
+ case MODE_DC_MA: /* Fall through */
+ case MODE_DC_A:
+ analog->mq = SR_MQ_CURRENT;
+ analog->unit = SR_UNIT_AMPERE;
+ analog->mqflags |= SR_MQFLAG_DC;
+ break;
+ case MODE_AC_UA: /* Fall through */
+ case MODE_AC_MA: /* Fall through */
+ case MODE_AC_A:
+ analog->mq = SR_MQ_CURRENT;
+ analog->unit = SR_UNIT_AMPERE;
+ analog->mqflags |= SR_MQFLAG_AC;
+ break;
+ case MODE_OHM:
+ analog->mq = SR_MQ_RESISTANCE;
+ analog->unit = SR_UNIT_OHM;
+ break;
+ case MODE_FARAD:
+ analog->mq = SR_MQ_CAPACITANCE;
+ analog->unit = SR_UNIT_FARAD;
+ break;
+ case MODE_CONT:
+ analog->mq = SR_MQ_CONTINUITY;
+ analog->unit = SR_UNIT_BOOLEAN;
+ rawval = is_shortcirc(rs_packet);
+ break;
+ case MODE_DIODE:
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ analog->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
+ break;
+ case MODE_HZ: /* Fall through */
+ case MODE_VOLT_HZ: /* Fall through */
+ case MODE_AMP_HZ:
+ analog->mq = SR_MQ_FREQUENCY;
+ analog->unit = SR_UNIT_HERTZ;
+ break;
+ case MODE_LOGIC:
+ /*
+ * No matter whether or not we have an actual voltage reading,
+ * we are measuring voltage, so we set our MQ as VOLTAGE.
+ */
+ analog->mq = SR_MQ_VOLTAGE;
+ if (!isnan(rawval)) {
+ /* We have an actual voltage. */
+ analog->unit = SR_UNIT_VOLT;
+ } else {
+ /* We have either HI or LOW. */
+ analog->unit = SR_UNIT_BOOLEAN;
+ rawval = is_logic_high(rs_packet);
+ }
+ break;
+ case MODE_HFE:
+ analog->mq = SR_MQ_GAIN;
+ analog->unit = SR_UNIT_UNITLESS;
+ break;
+ case MODE_DUTY: /* Fall through */
+ case MODE_VOLT_DUTY: /* Fall through */
+ case MODE_AMP_DUTY:
+ analog->mq = SR_MQ_DUTY_CYCLE;
+ analog->unit = SR_UNIT_PERCENTAGE;
+ break;
+ case MODE_WIDTH: /* Fall through */
+ case MODE_VOLT_WIDTH: /* Fall through */
+ case MODE_AMP_WIDTH:
+ analog->mq = SR_MQ_PULSE_WIDTH;
+ analog->unit = SR_UNIT_SECOND;
+ break;
+ case MODE_TEMP:
+ analog->mq = SR_MQ_TEMPERATURE;
+ /* We need to reparse. */
+ rawval = lcd_to_double(rs_packet, READ_TEMP);
+ analog->unit = is_celsius(rs_packet) ?
+ SR_UNIT_CELSIUS : SR_UNIT_FAHRENHEIT;
+ break;
+ case MODE_DBM:
+ analog->mq = SR_MQ_POWER;
+ analog->unit = SR_UNIT_DECIBEL_MW;
+ analog->mqflags |= SR_MQFLAG_AC;
+ break;
+ default:
+ sr_dbg("Unknown mode: %d.", rs_packet->mode);
+ break;
+ }
+
+ if (rs_packet->info & INFO_HOLD)
+ analog->mqflags |= SR_MQFLAG_HOLD;
+ if (rs_packet->digit4 & DIG4_MAX)
+ analog->mqflags |= SR_MQFLAG_MAX;
+ if (rs_packet->indicatrix2 & IND2_MIN)
+ analog->mqflags |= SR_MQFLAG_MIN;
+ if (rs_packet->info & INFO_AUTO)
+ analog->mqflags |= SR_MQFLAG_AUTORANGE;
+
+ *floatval = rawval;
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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.h"
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#ifdef HAVE_HW_AGILENT_DMM
+extern SR_PRIV struct sr_dev_driver agdmm_driver_info;
+#endif
+#ifdef HAVE_HW_APPA_55II
+extern SR_PRIV struct sr_dev_driver appa_55ii_driver_info;
+#endif
+#ifdef HAVE_HW_ASIX_SIGMA
+extern SR_PRIV struct sr_dev_driver asix_sigma_driver_info;
+#endif
+#ifdef HAVE_HW_ATTEN_PPS3XXX
+extern SR_PRIV struct sr_dev_driver atten_pps3203_driver_info;
+#endif
+#ifdef HAVE_HW_BEAGLELOGIC
+extern SR_PRIV struct sr_dev_driver beaglelogic_driver_info;
+#endif
+#ifdef HAVE_HW_BRYMEN_BM86X
+extern SR_PRIV struct sr_dev_driver brymen_bm86x_driver_info;
+#endif
+#ifdef HAVE_HW_BRYMEN_DMM
+extern SR_PRIV struct sr_dev_driver brymen_bm857_driver_info;
+#endif
+#ifdef HAVE_HW_CEM_DT_885X
+extern SR_PRIV struct sr_dev_driver cem_dt_885x_driver_info;
+#endif
+#ifdef HAVE_HW_CENTER_3XX
+extern SR_PRIV struct sr_dev_driver center_309_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_k204_driver_info;
+#endif
+#ifdef HAVE_HW_CHRONOVU_LA
+extern SR_PRIV struct sr_dev_driver chronovu_la_driver_info;
+#endif
+#ifdef HAVE_HW_COLEAD_SLM
+extern SR_PRIV struct sr_dev_driver colead_slm_driver_info;
+#endif
+#ifdef HAVE_HW_CONRAD_DIGI_35_CPU
+extern SR_PRIV struct sr_dev_driver conrad_digi_35_cpu_driver_info;
+#endif
+#ifdef HAVE_HW_DEMO
+extern SR_PRIV struct sr_dev_driver demo_driver_info;
+#endif
+#ifdef HAVE_HW_FLUKE_DMM
+extern SR_PRIV struct sr_dev_driver flukedmm_driver_info;
+#endif
+#ifdef HAVE_HW_FX2LAFW
+extern SR_PRIV struct sr_dev_driver fx2lafw_driver_info;
+#endif
+#ifdef HAVE_HW_GMC_MH_1X_2X
+extern SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info;
+extern SR_PRIV struct sr_dev_driver gmc_mh_2x_bd232_driver_info;
+#endif
+#ifdef HAVE_HW_HAMEG_HMO
+extern SR_PRIV struct sr_dev_driver hameg_hmo_driver_info;
+#endif
+#ifdef HAVE_HW_HANTEK_DSO
+extern SR_PRIV struct sr_dev_driver hantek_dso_driver_info;
+#endif
+#ifdef HAVE_HW_IKALOGIC_SCANALOGIC2
+extern SR_PRIV struct sr_dev_driver ikalogic_scanalogic2_driver_info;
+#endif
+#ifdef HAVE_HW_IKALOGIC_SCANAPLUS
+extern SR_PRIV struct sr_dev_driver ikalogic_scanaplus_driver_info;
+#endif
+#ifdef HAVE_HW_KECHENG_KC_330B
+extern SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info;
+#endif
+#ifdef HAVE_HW_LASCAR_EL_USB
+extern SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info;
+#endif
+#ifdef HAVE_HW_LINK_MSO19
+extern SR_PRIV struct sr_dev_driver link_mso19_driver_info;
+#endif
+#ifdef HAVE_HW_MANSON_HCS_3XXX
+extern SR_PRIV struct sr_dev_driver manson_hcs_3xxx_driver_info;
+#endif
+#ifdef HAVE_HW_MIC_985XX
+extern SR_PRIV struct sr_dev_driver mic_98581_driver_info;
+extern SR_PRIV struct sr_dev_driver mic_98583_driver_info;
+#endif
+#ifdef HAVE_HW_MOTECH_LPS_30X
+extern SR_PRIV struct sr_dev_driver motech_lps_301_driver_info;
+#endif
+#ifdef HAVE_HW_NORMA_DMM
+extern SR_PRIV struct sr_dev_driver norma_dmm_driver_info;
+extern SR_PRIV struct sr_dev_driver siemens_b102x_driver_info;
+#endif
+#ifdef HAVE_HW_OPENBENCH_LOGIC_SNIFFER
+extern SR_PRIV struct sr_dev_driver ols_driver_info;
+#endif
+#ifdef HAVE_HW_RIGOL_DS
+extern SR_PRIV struct sr_dev_driver rigol_ds_driver_info;
+#endif
+#ifdef HAVE_HW_SALEAE_LOGIC16
+extern SR_PRIV struct sr_dev_driver saleae_logic16_driver_info;
+#endif
+#ifdef HAVE_HW_SERIAL_DMM
+extern SR_PRIV struct sr_dev_driver bbcgm_m2110_driver_info;
+extern SR_PRIV struct sr_dev_driver digitek_dt4000zc_driver_info;
+extern SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info;
+extern SR_PRIV struct sr_dev_driver metex_me31_driver_info;
+extern SR_PRIV struct sr_dev_driver peaktech_3410_driver_info;
+extern SR_PRIV struct sr_dev_driver mastech_mas345_driver_info;
+extern SR_PRIV struct sr_dev_driver va_va18b_driver_info;
+extern SR_PRIV struct sr_dev_driver va_va40b_driver_info;
+extern SR_PRIV struct sr_dev_driver metex_m3640d_driver_info;
+extern SR_PRIV struct sr_dev_driver metex_m4650cr_driver_info;
+extern SR_PRIV struct sr_dev_driver peaktech_4370_driver_info;
+extern SR_PRIV struct sr_dev_driver pce_pce_dm32_driver_info;
+extern SR_PRIV struct sr_dev_driver radioshack_22_168_driver_info;
+extern SR_PRIV struct sr_dev_driver radioshack_22_805_driver_info;
+extern SR_PRIV struct sr_dev_driver radioshack_22_812_driver_info;
+extern SR_PRIV struct sr_dev_driver tecpel_dmm_8061_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_m3650cr_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_m3650d_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_m4650cr_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_me42_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_vc820_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_vc830_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_vc840_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut60a_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut60e_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut60g_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut61b_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut61c_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut61d_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut61e_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver iso_tech_idm103n_driver_info;
+extern SR_PRIV struct sr_dev_driver tenma_72_7745_ser_driver_info;
+extern SR_PRIV struct sr_dev_driver tenma_72_7750_ser_driver_info;
+#endif
+#ifdef HAVE_HW_SYSCLK_LWLA
+extern SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info;
+#endif
+#ifdef HAVE_HW_TELEINFO
+extern SR_PRIV struct sr_dev_driver teleinfo_driver_info;
+#endif
+#ifdef HAVE_HW_TESTO
+extern SR_PRIV struct sr_dev_driver testo_driver_info;
+#endif
+#ifdef HAVE_HW_TONDAJ_SL_814
+extern SR_PRIV struct sr_dev_driver tondaj_sl_814_driver_info;
+#endif
+#ifdef HAVE_HW_UNI_T_DMM
+extern SR_PRIV struct sr_dev_driver tecpel_dmm_8061_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut60a_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut60e_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut60g_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut61b_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut61c_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut61d_driver_info;
+extern SR_PRIV struct sr_dev_driver uni_t_ut61e_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_vc820_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_vc830_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_vc840_driver_info;
+extern SR_PRIV struct sr_dev_driver tenma_72_7745_driver_info;
+extern SR_PRIV struct sr_dev_driver tenma_72_7750_driver_info;
+#endif
+#ifdef HAVE_HW_UNI_T_UT32X
+extern SR_PRIV struct sr_dev_driver uni_t_ut32x_driver_info;
+#endif
+#ifdef HAVE_HW_VICTOR_DMM
+extern SR_PRIV struct sr_dev_driver victor_dmm_driver_info;
+#endif
+#ifdef HAVE_HW_ZEROPLUS_LOGIC_CUBE
+extern SR_PRIV struct sr_dev_driver zeroplus_logic_cube_driver_info;
+#endif
+
+SR_PRIV struct sr_dev_driver *drivers_list[] = {
+#ifdef HAVE_HW_AGILENT_DMM
+ &agdmm_driver_info,
+#endif
+#ifdef HAVE_HW_APPA_55II
+ &appa_55ii_driver_info,
+#endif
+#ifdef HAVE_HW_ASIX_SIGMA
+ &asix_sigma_driver_info,
+#endif
+#ifdef HAVE_HW_ATTEN_PPS3XXX
+ &atten_pps3203_driver_info,
+#endif
+#ifdef HAVE_HW_BEAGLELOGIC
+ &beaglelogic_driver_info,
+#endif
+#ifdef HAVE_HW_BRYMEN_BM86X
+ &brymen_bm86x_driver_info,
+#endif
+#ifdef HAVE_HW_BRYMEN_DMM
+ &brymen_bm857_driver_info,
+#endif
+#ifdef HAVE_HW_CEM_DT_885X
+ &cem_dt_885x_driver_info,
+#endif
+#ifdef HAVE_HW_CENTER_3XX
+ ¢er_309_driver_info,
+ &voltcraft_k204_driver_info,
+#endif
+#ifdef HAVE_HW_CHRONOVU_LA
+ &chronovu_la_driver_info,
+#endif
+#ifdef HAVE_HW_COLEAD_SLM
+ &colead_slm_driver_info,
+#endif
+#ifdef HAVE_HW_CONRAD_DIGI_35_CPU
+ &conrad_digi_35_cpu_driver_info,
+#endif
+#ifdef HAVE_HW_DEMO
+ &demo_driver_info,
+#endif
+#ifdef HAVE_HW_FLUKE_DMM
+ &flukedmm_driver_info,
+#endif
+#ifdef HAVE_HW_FX2LAFW
+ &fx2lafw_driver_info,
+#endif
+#ifdef HAVE_HW_GMC_MH_1X_2X
+ &gmc_mh_1x_2x_rs232_driver_info,
+ &gmc_mh_2x_bd232_driver_info,
+#endif
+#ifdef HAVE_HW_HAMEG_HMO
+ &hameg_hmo_driver_info,
+#endif
+#ifdef HAVE_HW_HANTEK_DSO
+ &hantek_dso_driver_info,
+#endif
+#ifdef HAVE_HW_IKALOGIC_SCANALOGIC2
+ &ikalogic_scanalogic2_driver_info,
+#endif
+#ifdef HAVE_HW_IKALOGIC_SCANAPLUS
+ &ikalogic_scanaplus_driver_info,
+#endif
+#ifdef HAVE_HW_KECHENG_KC_330B
+ &kecheng_kc_330b_driver_info,
+#endif
+#ifdef HAVE_HW_LASCAR_EL_USB
+ &lascar_el_usb_driver_info,
+#endif
+#ifdef HAVE_HW_LINK_MSO19
+ &link_mso19_driver_info,
+#endif
+#ifdef HAVE_HW_MANSON_HCS_3XXX
+ &manson_hcs_3xxx_driver_info,
+#endif
+#ifdef HAVE_HW_MIC_985XX
+ &mic_98581_driver_info,
+ &mic_98583_driver_info,
+#endif
+#ifdef HAVE_HW_MOTECH_LPS_30X
+ &motech_lps_301_driver_info,
+#endif
+#ifdef HAVE_HW_NORMA_DMM
+ &norma_dmm_driver_info,
+ &siemens_b102x_driver_info,
+#endif
+#ifdef HAVE_HW_OPENBENCH_LOGIC_SNIFFER
+ &ols_driver_info,
+#endif
+#ifdef HAVE_HW_RIGOL_DS
+ &rigol_ds_driver_info,
+#endif
+#ifdef HAVE_HW_SALEAE_LOGIC16
+ &saleae_logic16_driver_info,
+#endif
+#ifdef HAVE_HW_SERIAL_DMM
+ &bbcgm_m2110_driver_info,
+ &digitek_dt4000zc_driver_info,
+ &tekpower_tp4000zc_driver_info,
+ &metex_me31_driver_info,
+ &peaktech_3410_driver_info,
+ &mastech_mas345_driver_info,
+ &va_va18b_driver_info,
+ &va_va40b_driver_info,
+ &metex_m3640d_driver_info,
+ &metex_m4650cr_driver_info,
+ &peaktech_4370_driver_info,
+ &pce_pce_dm32_driver_info,
+ &radioshack_22_168_driver_info,
+ &radioshack_22_805_driver_info,
+ &radioshack_22_812_driver_info,
+ &tecpel_dmm_8061_ser_driver_info,
+ &voltcraft_m3650cr_driver_info,
+ &voltcraft_m3650d_driver_info,
+ &voltcraft_m4650cr_driver_info,
+ &voltcraft_me42_driver_info,
+ &voltcraft_vc820_ser_driver_info,
+ &voltcraft_vc830_ser_driver_info,
+ &voltcraft_vc840_ser_driver_info,
+ &uni_t_ut60a_ser_driver_info,
+ &uni_t_ut60e_ser_driver_info,
+ &uni_t_ut60g_ser_driver_info,
+ &uni_t_ut61b_ser_driver_info,
+ &uni_t_ut61c_ser_driver_info,
+ &uni_t_ut61d_ser_driver_info,
+ &uni_t_ut61e_ser_driver_info,
+ &iso_tech_idm103n_driver_info,
+ &tenma_72_7745_ser_driver_info,
+ &tenma_72_7750_ser_driver_info,
+#endif
+#ifdef HAVE_HW_SYSCLK_LWLA
+ &sysclk_lwla_driver_info,
+#endif
+#ifdef HAVE_HW_TELEINFO
+ &teleinfo_driver_info,
+#endif
+#ifdef HAVE_HW_TESTO
+ &testo_driver_info,
+#endif
+#ifdef HAVE_HW_TONDAJ_SL_814
+ &tondaj_sl_814_driver_info,
+#endif
+#ifdef HAVE_HW_UNI_T_DMM
+ &tecpel_dmm_8061_driver_info,
+ &uni_t_ut60a_driver_info,
+ &uni_t_ut60e_driver_info,
+ &uni_t_ut60g_driver_info,
+ &uni_t_ut61b_driver_info,
+ &uni_t_ut61c_driver_info,
+ &uni_t_ut61d_driver_info,
+ &uni_t_ut61e_driver_info,
+ &voltcraft_vc820_driver_info,
+ &voltcraft_vc830_driver_info,
+ &voltcraft_vc840_driver_info,
+ &tenma_72_7745_driver_info,
+ &tenma_72_7750_driver_info,
+#endif
+#ifdef HAVE_HW_UNI_T_UT32X
+ &uni_t_ut32x_driver_info,
+#endif
+#ifdef HAVE_HW_VICTOR_DMM
+ &victor_dmm_driver_info,
+#endif
+#ifdef HAVE_HW_ZEROPLUS_LOGIC_CUBE
+ &zeroplus_logic_cube_driver_info,
+#endif
+ NULL,
+};
+/** @endcond */
+
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libsigrok.h"
+
+/**
+ * @file
+ *
+ * Error handling in libsigrok.
+ */
+
+/**
+ * @defgroup grp_error Error handling
+ *
+ * Error handling in libsigrok.
+ *
+ * libsigrok functions usually return @ref SR_OK upon success, or a negative
+ * error code on failure.
+ *
+ * @{
+ */
+
+/**
+ * Return a human-readable error string for the given libsigrok error code.
+ *
+ * @param error_code A libsigrok error code number, such as SR_ERR_MALLOC.
+ *
+ * @return A const string containing a short, human-readable (English)
+ * description of the error, such as "memory allocation error".
+ * The string must NOT be free'd by the caller!
+ *
+ * @see sr_strerror_name
+ *
+ * @since 0.2.0
+ */
+SR_API const char *sr_strerror(int error_code)
+{
+ /*
+ * Note: All defined SR_* error macros from libsigrok.h must have
+ * an entry in this function, as well as in sr_strerror_name().
+ */
+
+ switch (error_code) {
+ case SR_OK:
+ return "no error";
+ case SR_ERR:
+ return "generic/unspecified error";
+ case SR_ERR_MALLOC:
+ return "memory allocation error";
+ case SR_ERR_ARG:
+ return "invalid argument";
+ case SR_ERR_BUG:
+ return "internal error";
+ case SR_ERR_SAMPLERATE:
+ return "invalid samplerate";
+ case SR_ERR_NA:
+ return "not applicable";
+ case SR_ERR_DEV_CLOSED:
+ return "device closed but should be open";
+ case SR_ERR_TIMEOUT:
+ return "timeout occurred";
+ case SR_ERR_CHANNEL_GROUP:
+ return "no channel group specified";
+ default:
+ return "unknown error";
+ }
+}
+
+/**
+ * Return the "name" string of the given libsigrok error code.
+ *
+ * For example, the "name" of the SR_ERR_MALLOC error code is "SR_ERR_MALLOC",
+ * the name of the SR_OK code is "SR_OK", and so on.
+ *
+ * This function can be used for various purposes where the "name" string of
+ * a libsigrok error code is useful.
+ *
+ * @param error_code A libsigrok error code number, such as SR_ERR_MALLOC.
+ *
+ * @return A const string containing the "name" of the error code as string.
+ * The string must NOT be free'd by the caller!
+ *
+ * @see sr_strerror
+ *
+ * @since 0.2.0
+ */
+SR_API const char *sr_strerror_name(int error_code)
+{
+ /*
+ * Note: All defined SR_* error macros from libsigrok.h must have
+ * an entry in this function, as well as in sr_strerror().
+ */
+
+ switch (error_code) {
+ case SR_OK:
+ return "SR_OK";
+ case SR_ERR:
+ return "SR_ERR";
+ case SR_ERR_MALLOC:
+ return "SR_ERR_MALLOC";
+ case SR_ERR_ARG:
+ return "SR_ERR_ARG";
+ case SR_ERR_BUG:
+ return "SR_ERR_BUG";
+ case SR_ERR_SAMPLERATE:
+ return "SR_ERR_SAMPLERATE";
+ case SR_ERR_NA:
+ return "SR_ERR_NA";
+ case SR_ERR_DEV_CLOSED:
+ return "SR_ERR_DEV_CLOSED";
+ case SR_ERR_TIMEOUT:
+ return "SR_ERR_TIMEOUT";
+ case SR_ERR_CHANNEL_GROUP:
+ return "SR_ERR_CHANNEL_GROUP";
+ default:
+ return "unknown error code";
+ }
+}
+
+/** @} */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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/>.
+ */
+
+/*
+ * Helper functions for the Cypress EZ-USB / FX2 series chips.
+ */
+
+#include <libusb.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "ezusb"
+
+SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear)
+{
+ int ret;
+ unsigned char buf[1];
+
+ sr_info("setting CPU reset mode %s...",
+ set_clear ? "on" : "off");
+ buf[0] = set_clear ? 1 : 0;
+ ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR, 0xa0,
+ 0xe600, 0x0000, buf, 1, 100);
+ if (ret < 0)
+ sr_err("Unable to send control request: %s.",
+ libusb_error_name(ret));
+
+ return ret;
+}
+
+SR_PRIV int ezusb_install_firmware(libusb_device_handle *hdl,
+ const char *filename)
+{
+ FILE *fw;
+ int offset, chunksize, ret, result;
+ unsigned char buf[4096];
+
+ sr_info("Uploading firmware at %s", filename);
+ if ((fw = g_fopen(filename, "rb")) == NULL) {
+ sr_err("Unable to open firmware file %s for reading: %s",
+ filename, strerror(errno));
+ return SR_ERR;
+ }
+
+ result = SR_OK;
+ offset = 0;
+ while (1) {
+ chunksize = fread(buf, 1, 4096, fw);
+ if (chunksize == 0)
+ break;
+ ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_ENDPOINT_OUT, 0xa0, offset,
+ 0x0000, buf, chunksize, 100);
+ if (ret < 0) {
+ sr_err("Unable to send firmware to device: %s.",
+ libusb_error_name(ret));
+ result = SR_ERR;
+ break;
+ }
+ sr_info("Uploaded %d bytes", chunksize);
+ offset += chunksize;
+ }
+ fclose(fw);
+ sr_info("Firmware upload done");
+
+ return result;
+}
+
+SR_PRIV int ezusb_upload_firmware(libusb_device *dev, int configuration,
+ const char *filename)
+{
+ struct libusb_device_handle *hdl;
+ int ret;
+
+ sr_info("uploading firmware to device on %d.%d",
+ libusb_get_bus_number(dev), libusb_get_device_address(dev));
+
+ if ((ret = libusb_open(dev, &hdl)) < 0) {
+ sr_err("failed to open device: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+/*
+ * The libusbx darwin backend is broken: it can report a kernel driver being
+ * active, but detaching it always returns an error.
+ */
+#if !defined(__APPLE__)
+ if (libusb_kernel_driver_active(hdl, 0) == 1) {
+ if ((ret = libusb_detach_kernel_driver(hdl, 0)) < 0) {
+ sr_err("failed to detach kernel driver: %s",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ }
+#endif
+
+ if ((ret = libusb_set_configuration(hdl, configuration)) < 0) {
+ sr_err("Unable to set configuration: %s",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if ((ezusb_reset(hdl, 1)) < 0)
+ return SR_ERR;
+
+ if (ezusb_install_firmware(hdl, filename) < 0)
+ return SR_ERR;
+
+ if ((ezusb_reset(hdl, 0)) < 0)
+ return SR_ERR;
+
+ libusb_close(hdl);
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_AGILENT_DMM_AGILENT_DMM_H
+#define LIBSIGROK_HARDWARE_AGILENT_DMM_AGILENT_DMM_H
+
+#define LOG_PREFIX "agilent-dmm"
+
+#define AGDMM_BUFSIZE 256
+
+/* Supported models */
+enum {
+ AGILENT_U1231A = 1,
+ AGILENT_U1232A,
+ AGILENT_U1233A,
+ AGILENT_U1251A,
+ AGILENT_U1252A,
+ AGILENT_U1253A,
+};
+
+/* Supported device profiles */
+struct agdmm_profile {
+ int model;
+ const char *modelname;
+ const struct agdmm_job *jobs;
+ const struct agdmm_recv *recvs;
+};
+
+/* Private, per-device-instance driver context. */
+struct dev_context {
+ const struct agdmm_profile *profile;
+ uint64_t limit_samples;
+ uint64_t limit_msec;
+
+ /* Opaque pointer passed in by the frontend. */
+ void *cb_data;
+
+ /* Runtime. */
+ uint64_t num_samples;
+ int64_t jobqueue[8];
+ unsigned char buf[AGDMM_BUFSIZE];
+ int buflen;
+ int cur_mq;
+ int cur_unit;
+ int cur_mqflags;
+ int cur_divider;
+ int cur_acdc;
+ int mode_tempaux;
+ int mode_continuity;
+};
+
+struct agdmm_job {
+ int interval;
+ int (*send) (const struct sr_dev_inst *sdi);
+};
+
+struct agdmm_recv {
+ const char *recv_regex;
+ int (*recv) (const struct sr_dev_inst *sdi, GMatchInfo *match);
+};
+
+SR_PRIV int agdmm_receive_data(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <glib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "agilent-dmm.h"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+extern const struct agdmm_job agdmm_jobs_u123x[];
+extern const struct agdmm_recv agdmm_recvs_u123x[];
+extern const struct agdmm_job agdmm_jobs_u125x[];
+extern const struct agdmm_recv agdmm_recvs_u125x[];
+
+/* This works on all the Agilent U12xxA series, although the
+ * U127xA can apparently also run at 19200/8n1. */
+#define SERIALCOMM "9600/8n1"
+
+static const struct agdmm_profile supported_agdmm[] = {
+ { AGILENT_U1231A, "U1231A", agdmm_jobs_u123x, agdmm_recvs_u123x },
+ { AGILENT_U1232A, "U1232A", agdmm_jobs_u123x, agdmm_recvs_u123x },
+ { AGILENT_U1233A, "U1233A", agdmm_jobs_u123x, agdmm_recvs_u123x },
+ { AGILENT_U1251A, "U1251A", agdmm_jobs_u125x, agdmm_recvs_u125x },
+ { AGILENT_U1252A, "U1252A", agdmm_jobs_u125x, agdmm_recvs_u125x },
+ { AGILENT_U1253A, "U1253A", agdmm_jobs_u125x, agdmm_recvs_u125x },
+ { 0, NULL, NULL, NULL }
+};
+
+SR_PRIV struct sr_dev_driver agdmm_driver_info;
+static struct sr_dev_driver *di = &agdmm_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_config *src;
+ struct sr_channel *ch;
+ struct sr_serial_dev_inst *serial;
+ GSList *l, *devices;
+ int len, i;
+ const char *conn, *serialcomm;
+ char *buf, **tokens;
+
+ drvc = di->priv;
+ drvc->instances = NULL;
+
+ devices = NULL;
+ 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 (!conn)
+ return NULL;
+ if (!serialcomm)
+ serialcomm = SERIALCOMM;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ serial_flush(serial);
+ if (serial_write(serial, "*IDN?\r\n", 7) == -1) {
+ sr_err("Unable to send identification string: %s.",
+ strerror(errno));
+ return NULL;
+ }
+
+ len = 128;
+ if (!(buf = g_try_malloc(len))) {
+ sr_err("Serial buffer malloc failed.");
+ return NULL;
+ }
+ serial_readline(serial, &buf, &len, 150);
+ if (!len)
+ return NULL;
+
+ tokens = g_strsplit(buf, ",", 4);
+ if (!strcmp("Agilent Technologies", tokens[0])
+ && tokens[1] && tokens[2] && tokens[3]) {
+ for (i = 0; supported_agdmm[i].model; i++) {
+ if (strcmp(supported_agdmm[i].modelname, tokens[1]))
+ continue;
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Agilent",
+ tokens[1], tokens[3])))
+ return NULL;
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+ devc->profile = &supported_agdmm[i];
+ devc->cur_mq = -1;
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+ sdi->priv = devc;
+ sdi->driver = di;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ break;
+ }
+ }
+ g_strfreev(tokens);
+ g_free(buf);
+
+ serial_close(serial);
+ if (!devices)
+ sr_serial_dev_inst_free(serial);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ switch (id) {
+ case SR_CONF_LIMIT_MSEC:
+ /* TODO: not yet implemented */
+ if (g_variant_get_uint64(data) == 0) {
+ sr_err("LIMIT_MSEC can't be 0.");
+ return SR_ERR;
+ }
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ devc->cb_data = cb_data;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Poll every 100ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 100,
+ agdmm_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver agdmm_driver_info = {
+ .name = "agilent-dmm",
+ .longname = "Agilent U12xx series DMMs",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .config_get = NULL,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = std_serial_dev_open,
+ .dev_close = std_serial_dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "agilent-dmm.h"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+
+static void dispatch(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ const struct agdmm_job *jobs;
+ int64_t now;
+ int i;
+
+ devc = sdi->priv;
+ jobs = devc->profile->jobs;
+ now = g_get_monotonic_time() / 1000;
+ for (i = 0; (&jobs[i])->interval; i++) {
+ if (now - devc->jobqueue[i] > (&jobs[i])->interval) {
+ sr_spew("Running job %d.", i);
+ (&jobs[i])->send(sdi);
+ devc->jobqueue[i] = now;
+ }
+ }
+}
+
+static void receive_line(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ const struct agdmm_recv *recvs, *recv;
+ GRegex *reg;
+ GMatchInfo *match;
+ int i;
+
+ devc = sdi->priv;
+
+ /* Strip CRLF */
+ while (devc->buflen) {
+ if (*(devc->buf + devc->buflen - 1) == '\r'
+ || *(devc->buf + devc->buflen - 1) == '\n')
+ *(devc->buf + --devc->buflen) = '\0';
+ else
+ break;
+ }
+ sr_spew("Received '%s'.", devc->buf);
+
+ recv = NULL;
+ recvs = devc->profile->recvs;
+ for (i = 0; (&recvs[i])->recv_regex; i++) {
+ reg = g_regex_new((&recvs[i])->recv_regex, 0, 0, NULL);
+ if (g_regex_match(reg, (char *)devc->buf, 0, &match)) {
+ recv = &recvs[i];
+ break;
+ }
+ g_match_info_unref(match);
+ g_regex_unref(reg);
+ }
+ if (recv) {
+ recv->recv(sdi, match);
+ g_match_info_unref(match);
+ g_regex_unref(reg);
+ } else
+ sr_dbg("Unknown line '%s'.", devc->buf);
+
+ /* Done with this. */
+ devc->buflen = 0;
+}
+
+SR_PRIV int agdmm_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int len;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+ if (revents == G_IO_IN) {
+ /* Serial data arrived. */
+ while(AGDMM_BUFSIZE - devc->buflen - 1 > 0) {
+ len = serial_read(serial, devc->buf + devc->buflen, 1);
+ if (len < 1)
+ break;
+ devc->buflen += len;
+ *(devc->buf + devc->buflen) = '\0';
+ if (*(devc->buf + devc->buflen - 1) == '\n') {
+ /* End of line */
+ receive_line(sdi);
+ break;
+ }
+ }
+ }
+
+ dispatch(sdi);
+
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+
+ return TRUE;
+}
+
+static int agdmm_send(const struct sr_dev_inst *sdi, const char *cmd)
+{
+ struct sr_serial_dev_inst *serial;
+ char buf[32];
+
+ serial = sdi->conn;
+
+ sr_spew("Sending '%s'.", cmd);
+ strncpy(buf, cmd, 28);
+ if (!strncmp(buf, "*IDN?", 5))
+ strncat(buf, "\r\n", 32);
+ else
+ strncat(buf, "\n\r\n", 32);
+ if (serial_write(serial, buf, strlen(buf)) == -1) {
+ sr_err("Failed to send: %s.", strerror(errno));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int send_stat(const struct sr_dev_inst *sdi)
+{
+ return agdmm_send(sdi, "STAT?");
+}
+
+static int recv_stat_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
+{
+ struct dev_context *devc;
+ char *s;
+
+ devc = sdi->priv;
+ s = g_match_info_fetch(match, 1);
+ sr_spew("STAT response '%s'.", s);
+
+ /* Max, Min or Avg mode -- no way to tell which, so we'll
+ * set both flags to denote it's not a normal measurement. */
+ if (s[0] == '1')
+ devc->cur_mqflags |= SR_MQFLAG_MAX | SR_MQFLAG_MIN;
+ else
+ devc->cur_mqflags &= ~(SR_MQFLAG_MAX | SR_MQFLAG_MIN);
+
+ if (s[1] == '1')
+ devc->cur_mqflags |= SR_MQFLAG_RELATIVE;
+ else
+ devc->cur_mqflags &= ~SR_MQFLAG_RELATIVE;
+
+ /* Triggered or auto hold modes. */
+ if (s[2] == '1' || s[3] == '1')
+ devc->cur_mqflags |= SR_MQFLAG_HOLD;
+ else
+ devc->cur_mqflags &= ~SR_MQFLAG_HOLD;
+
+ /* Temp/aux mode. */
+ if (s[7] == '1')
+ devc->mode_tempaux = TRUE;
+ else
+ devc->mode_tempaux = FALSE;
+
+ /* Continuity mode. */
+ if (s[16] == '1')
+ devc->mode_continuity = TRUE;
+ else
+ devc->mode_continuity = FALSE;
+
+ g_free(s);
+
+ return SR_OK;
+}
+
+static int recv_stat_u125x(const struct sr_dev_inst *sdi, GMatchInfo *match)
+{
+ struct dev_context *devc;
+ char *s;
+
+ devc = sdi->priv;
+ s = g_match_info_fetch(match, 1);
+ sr_spew("STAT response '%s'.", s);
+
+ /* Peak hold mode. */
+ if (s[4] == '1')
+ devc->cur_mqflags |= SR_MQFLAG_MAX;
+ else
+ devc->cur_mqflags &= ~SR_MQFLAG_MAX;
+
+ /* Triggered hold mode. */
+ if (s[7] == '1')
+ devc->cur_mqflags |= SR_MQFLAG_HOLD;
+ else
+ devc->cur_mqflags &= ~SR_MQFLAG_HOLD;
+
+ g_free(s);
+
+ return SR_OK;
+}
+
+static int send_fetc(const struct sr_dev_inst *sdi)
+{
+ return agdmm_send(sdi, "FETC?");
+}
+
+static int recv_fetc(const struct sr_dev_inst *sdi, GMatchInfo *match)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ float fvalue;
+ char *mstr;
+
+ sr_spew("FETC reply '%s'.", g_match_info_get_string(match));
+ devc = sdi->priv;
+
+ if (devc->cur_mq == -1)
+ /* Haven't seen configuration yet, so can't know what
+ * the fetched float means. Not really an error, we'll
+ * get metadata soon enough. */
+ return SR_OK;
+
+ if (!strcmp(g_match_info_get_string(match), "+9.90000000E+37")) {
+ /* An invalid measurement shows up on the display as "O.L", but
+ * comes through like this. Since comparing 38-digit floats
+ * is rather problematic, we'll cut through this here. */
+ fvalue = NAN;
+ } else {
+ mstr = g_match_info_fetch(match, 1);
+ if (sr_atof_ascii(mstr, &fvalue) != SR_OK || fvalue == 0.0) {
+ g_free(mstr);
+ sr_err("Invalid float.");
+ return SR_ERR;
+ }
+ g_free(mstr);
+ if (devc->cur_divider > 0)
+ fvalue /= devc->cur_divider;
+ }
+
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+ analog.mq = devc->cur_mq;
+ analog.unit = devc->cur_unit;
+ analog.mqflags = devc->cur_mqflags;
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+ analog.data = &fvalue;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+
+ devc->num_samples++;
+
+ return SR_OK;
+}
+
+static int send_conf(const struct sr_dev_inst *sdi)
+{
+ return agdmm_send(sdi, "CONF?");
+}
+
+static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
+{
+ struct dev_context *devc;
+ char *mstr;
+
+ sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
+ devc = sdi->priv;
+ mstr = g_match_info_fetch(match, 1);
+ if (!strcmp(mstr, "V")) {
+ devc->cur_mq = SR_MQ_VOLTAGE;
+ devc->cur_unit = SR_UNIT_VOLT;
+ devc->cur_mqflags = 0;
+ devc->cur_divider = 0;
+ } else if(!strcmp(mstr, "MV")) {
+ if (devc->mode_tempaux) {
+ devc->cur_mq = SR_MQ_TEMPERATURE;
+ /* No way to detect whether Fahrenheit or Celcius
+ * is used, so we'll just default to Celcius. */
+ devc->cur_unit = SR_UNIT_CELSIUS;
+ devc->cur_mqflags = 0;
+ devc->cur_divider = 0;
+ } else {
+ devc->cur_mq = SR_MQ_VOLTAGE;
+ devc->cur_unit = SR_UNIT_VOLT;
+ devc->cur_mqflags = 0;
+ devc->cur_divider = 1000;
+ }
+ } else if(!strcmp(mstr, "A")) {
+ devc->cur_mq = SR_MQ_CURRENT;
+ devc->cur_unit = SR_UNIT_AMPERE;
+ devc->cur_mqflags = 0;
+ devc->cur_divider = 0;
+ } else if(!strcmp(mstr, "UA")) {
+ devc->cur_mq = SR_MQ_CURRENT;
+ devc->cur_unit = SR_UNIT_AMPERE;
+ devc->cur_mqflags = 0;
+ devc->cur_divider = 1000000;
+ } else if(!strcmp(mstr, "FREQ")) {
+ devc->cur_mq = SR_MQ_FREQUENCY;
+ devc->cur_unit = SR_UNIT_HERTZ;
+ devc->cur_mqflags = 0;
+ devc->cur_divider = 0;
+ } else if(!strcmp(mstr, "RES")) {
+ if (devc->mode_continuity) {
+ devc->cur_mq = SR_MQ_CONTINUITY;
+ devc->cur_unit = SR_UNIT_BOOLEAN;
+ } else {
+ devc->cur_mq = SR_MQ_RESISTANCE;
+ devc->cur_unit = SR_UNIT_OHM;
+ }
+ devc->cur_mqflags = 0;
+ devc->cur_divider = 0;
+ } else if(!strcmp(mstr, "CAP")) {
+ devc->cur_mq = SR_MQ_CAPACITANCE;
+ devc->cur_unit = SR_UNIT_FARAD;
+ devc->cur_mqflags = 0;
+ devc->cur_divider = 0;
+ } else
+ sr_dbg("Unknown first argument.");
+ g_free(mstr);
+
+ if (g_match_info_get_match_count(match) == 4) {
+ mstr = g_match_info_fetch(match, 3);
+ /* Third value, if present, is always AC or DC. */
+ if (!strcmp(mstr, "AC"))
+ devc->cur_mqflags |= SR_MQFLAG_AC;
+ else if (!strcmp(mstr, "DC"))
+ devc->cur_mqflags |= SR_MQFLAG_DC;
+ else
+ sr_dbg("Unknown third argument.");
+ g_free(mstr);
+ } else
+ devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
+
+ return SR_OK;
+}
+
+static int recv_conf_u125x(const struct sr_dev_inst *sdi, GMatchInfo *match)
+{
+ struct dev_context *devc;
+ char *mstr;
+
+ sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
+ devc = sdi->priv;
+ mstr = g_match_info_fetch(match, 1);
+ if (!strncmp(mstr, "VOLT", 4)) {
+ devc->cur_mq = SR_MQ_VOLTAGE;
+ devc->cur_unit = SR_UNIT_VOLT;
+ devc->cur_mqflags = 0;
+ devc->cur_divider = 0;
+ if (mstr[4] == ':') {
+ if (!strcmp(mstr + 4, "AC"))
+ devc->cur_mqflags |= SR_MQFLAG_AC;
+ else if (!strcmp(mstr + 4, "DC"))
+ devc->cur_mqflags |= SR_MQFLAG_DC;
+ else
+ /* "ACDC" appears as well, no idea what it means. */
+ devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
+ } else
+ devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
+ } else if(!strcmp(mstr, "CURR")) {
+ devc->cur_mq = SR_MQ_CURRENT;
+ devc->cur_unit = SR_UNIT_AMPERE;
+ devc->cur_mqflags = 0;
+ devc->cur_divider = 0;
+ } else if(!strcmp(mstr, "RES")) {
+ if (devc->mode_continuity) {
+ devc->cur_mq = SR_MQ_CONTINUITY;
+ devc->cur_unit = SR_UNIT_BOOLEAN;
+ } else {
+ devc->cur_mq = SR_MQ_RESISTANCE;
+ devc->cur_unit = SR_UNIT_OHM;
+ }
+ devc->cur_mqflags = 0;
+ devc->cur_divider = 0;
+ } else
+ sr_dbg("Unknown first argument.");
+ g_free(mstr);
+
+ return SR_OK;
+}
+
+/* At least the 123x and 125x appear to have this. */
+static int recv_conf(const struct sr_dev_inst *sdi, GMatchInfo *match)
+{
+ struct dev_context *devc;
+ char *mstr;
+
+ sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
+ devc = sdi->priv;
+ mstr = g_match_info_fetch(match, 1);
+ if(!strcmp(mstr, "DIOD")) {
+ devc->cur_mq = SR_MQ_VOLTAGE;
+ devc->cur_unit = SR_UNIT_VOLT;
+ devc->cur_mqflags = SR_MQFLAG_DIODE;
+ devc->cur_divider = 0;
+ } else
+ sr_dbg("Unknown single argument.");
+ g_free(mstr);
+
+ return SR_OK;
+}
+
+/* This comes in whenever the rotary switch is changed to a new position.
+ * We could use it to determine the major measurement mode, but we already
+ * have the output of CONF? for that, which is more detailed. However
+ * we do need to catch this here, or it'll show up in some other output. */
+static int recv_switch(const struct sr_dev_inst *sdi, GMatchInfo *match)
+{
+ (void)sdi;
+
+ sr_spew("Switch '%s'.", g_match_info_get_string(match));
+
+ return SR_OK;
+}
+
+SR_PRIV const struct agdmm_job agdmm_jobs_u123x[] = {
+ { 143, send_stat },
+ { 1000, send_conf },
+ { 143, send_fetc },
+ { 0, NULL }
+};
+
+SR_PRIV const struct agdmm_recv agdmm_recvs_u123x[] = {
+ { "^\"(\\d\\d.{18}\\d)\"$", recv_stat_u123x },
+ { "^\\*([0-9])$", recv_switch },
+ { "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
+ { "^\"(V|MV|A|UA|FREQ),(\\d),(AC|DC)\"$", recv_conf_u123x },
+ { "^\"(RES|CAP),(\\d)\"$", recv_conf_u123x},
+ { "^\"(DIOD)\"$", recv_conf },
+ { NULL, NULL }
+};
+
+SR_PRIV const struct agdmm_job agdmm_jobs_u125x[] = {
+ { 143, send_stat },
+ { 1000, send_conf },
+ { 143, send_fetc },
+ { 0, NULL }
+};
+
+SR_PRIV const struct agdmm_recv agdmm_recvs_u125x[] = {
+ { "^\"(\\d\\d.{18}\\d)\"$", recv_stat_u125x },
+ { "^\\*([0-9])$", recv_switch },
+ { "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
+ { "^(VOLT|CURR|RES|CAP) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)$", recv_conf_u125x },
+ { "^(VOLT:[ACD]+) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)$", recv_conf_u125x },
+ { "^\"(DIOD)\"$", recv_conf },
+ { NULL, NULL }
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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 <string.h>
+#include "protocol.h"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_THERMOMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+ SR_CONF_DATA_SOURCE,
+};
+
+static const char *data_sources[] = {
+ "Live",
+ "Memory",
+};
+
+SR_PRIV struct sr_dev_driver appa_55ii_driver_info;
+static struct sr_dev_driver *di = &appa_55ii_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ struct sr_config *src;
+ GSList *devices, *l;
+ const char *conn, *serialcomm;
+ uint8_t buf[50];
+ size_t len;
+
+ len = sizeof(buf);
+ devices = NULL;
+ 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 (!conn)
+ return NULL;
+ if (!serialcomm)
+ serialcomm = "9600/8n1";
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+ if (serial_open(serial, SERIAL_RDONLY | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ sr_info("Probing serial port %s.", conn);
+
+ drvc = di->priv;
+ drvc->instances = NULL;
+ serial_flush(serial);
+
+ /* Let's get a bit of data and see if we can find a packet. */
+ if (serial_stream_detect(serial, buf, &len, 25,
+ appa_55ii_packet_valid, 500, 9600) != SR_OK)
+ goto scan_cleanup;
+
+ sr_info("Found device on port %s.", conn);
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "APPA", "55II", NULL)))
+ goto scan_cleanup;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ goto scan_cleanup;
+ }
+
+ devc->data_source = DEFAULT_DATA_SOURCE;
+
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+ sdi->priv = devc;
+ sdi->driver = di;
+
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "T1")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "T2")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+scan_cleanup:
+ serial_close(serial);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_get(int 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_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ *data = g_variant_new_uint64(devc->limit_msec);
+ break;
+ case SR_CONF_DATA_SOURCE:
+ *data = g_variant_new_string(data_sources[devc->data_source]);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ const char *tmp_str;
+ unsigned int i;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".", devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.", devc->limit_msec);
+ break;
+ case SR_CONF_DATA_SOURCE: {
+ tmp_str = g_variant_get_string(data, NULL);
+ for (i = 0; i < ARRAY_SIZE(data_sources); i++)
+ if (!strcmp(tmp_str, data_sources[i])) {
+ devc->data_source = i;
+ break;
+ }
+ if (i == ARRAY_SIZE(data_sources))
+ return SR_ERR;
+ break;
+ }
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_DATA_SOURCE:
+ *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct sr_serial_dev_inst *serial;
+ struct dev_context *devc;
+
+ serial = sdi->conn;
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ devc->session_cb_data = cb_data;
+
+ /*
+ * Reset the number of samples to take. If we've already collected our
+ * quota, but we start a new session, and don't reset this, we'll just
+ * quit without acquiring any new samples.
+ */
+ devc->num_samples = 0;
+ devc->start_time = g_get_monotonic_time();
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Poll every 50ms, or whenever some data comes in. */
+ serial_source_add(sdi->session, serial, G_IO_IN, 50,
+ appa_55ii_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ return std_serial_dev_acquisition_stop(sdi, cb_data,
+ std_serial_dev_close, sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver appa_55ii_driver_info = {
+ .name = "appa-55ii",
+ .longname = "APPA 55II",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .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 = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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 <string.h>
+#include <math.h>
+#include "protocol.h"
+
+typedef enum {
+ LIVE_DATA = 0x00,
+ LOG_METADATA = 0x11,
+ LOG_DATA = 0x14,
+ LOG_START = 0x18,
+ LOG_END = 0x19,
+} packet_type;
+
+static gboolean appa_55ii_checksum(const uint8_t *buf)
+{
+ int i, size, checksum;
+
+ size = buf[3] + 4;
+ checksum = 0;
+ for (i = 0; i < size; i++)
+ checksum += buf[i];
+
+ return buf[size] == (checksum & 0xFF);
+}
+
+SR_PRIV gboolean appa_55ii_packet_valid(const uint8_t *buf)
+{
+ if (buf[0] == 0x55 && buf[1] == 0x55 && buf[3] <= 32
+ && appa_55ii_checksum(buf))
+ return TRUE;
+
+ return FALSE;
+}
+
+static uint64_t appa_55ii_flags(const uint8_t *buf)
+{
+ uint8_t disp_mode;
+ uint64_t flags;
+
+ disp_mode = buf[4 + 13];
+ flags = 0;
+ if ((disp_mode & 0xF0) == 0x20)
+ flags |= SR_MQFLAG_HOLD;
+ if ((disp_mode & 0x0C) == 0x04)
+ flags |= SR_MQFLAG_MAX;
+ if ((disp_mode & 0x0C) == 0x08)
+ flags |= SR_MQFLAG_MIN;
+ if ((disp_mode & 0x0C) == 0x0C)
+ flags |= SR_MQFLAG_AVG;
+
+ return flags;
+}
+
+static float appa_55ii_temp(const uint8_t *buf, int ch)
+{
+ const uint8_t *ptr;
+ int16_t temp;
+ uint8_t flags;
+
+ ptr = buf + 4 + 14 + 3 * ch;
+ temp = RL16(ptr);
+ flags = ptr[2];
+
+ if (flags & 0x60)
+ return INFINITY;
+ else if (flags & 1)
+ return (float)temp / 10;
+ else
+ return (float)temp;
+}
+
+static void appa_55ii_live_data(struct sr_dev_inst *sdi, const uint8_t *buf)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct sr_channel *ch;
+ float values[APPA_55II_NUM_CHANNELS], *val_ptr;
+ int i;
+
+ devc = sdi->priv;
+
+ if (devc->data_source != DATA_SOURCE_LIVE)
+ return;
+
+ val_ptr = values;
+ memset(&analog, 0, sizeof(analog));
+ analog.num_samples = 1;
+ analog.mq = SR_MQ_TEMPERATURE;
+ analog.unit = SR_UNIT_CELSIUS;
+ analog.mqflags = appa_55ii_flags(buf);
+ analog.data = values;
+
+ for (i = 0; i < APPA_55II_NUM_CHANNELS; i++) {
+ ch = g_slist_nth_data(sdi->channels, i);
+ if (!ch->enabled)
+ continue;
+ analog.channels = g_slist_append(analog.channels, ch);
+ *val_ptr++ = appa_55ii_temp(buf, i);
+ }
+
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->session_cb_data, &packet);
+ g_slist_free(analog.channels);
+
+ devc->num_samples++;
+}
+
+static void appa_55ii_log_metadata(struct sr_dev_inst *sdi, const uint8_t *buf)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+ devc->num_log_records = (buf[5] << 8) + buf[4];
+}
+
+static void appa_55ii_log_data_parse(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct sr_channel *ch;
+ float values[APPA_55II_NUM_CHANNELS], *val_ptr;
+ const uint8_t *buf;
+ int16_t temp;
+ int offset, i;
+
+ devc = sdi->priv;
+ offset = 0;
+
+ while (devc->log_buf_len >= 20 && devc->num_log_records > 0) {
+ buf = devc->log_buf + offset;
+ val_ptr = values;
+
+ /* FIXME: Timestamp should be sent in the packet. */
+ sr_dbg("Timestamp: %02d:%02d:%02d", buf[2], buf[3], buf[4]);
+
+ memset(&analog, 0, sizeof(analog));
+ analog.num_samples = 1;
+ analog.mq = SR_MQ_TEMPERATURE;
+ analog.unit = SR_UNIT_CELSIUS;
+ analog.data = values;
+
+ for (i = 0; i < APPA_55II_NUM_CHANNELS; i++) {
+ temp = RL16(buf + 12 + 2 * i);
+ ch = g_slist_nth_data(sdi->channels, i);
+ if (!ch->enabled)
+ continue;
+ analog.channels = g_slist_append(analog.channels, ch);
+ *val_ptr++ = temp == 0x7FFF ? INFINITY : (float)temp / 10;
+ }
+
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->session_cb_data, &packet);
+ g_slist_free(analog.channels);
+
+ devc->num_samples++;
+ devc->log_buf_len -= 20;
+ offset += 20;
+ devc->num_log_records--;
+ }
+
+ memmove(devc->log_buf, devc->log_buf + offset, devc->log_buf_len);
+}
+
+static void appa_55ii_log_data(struct sr_dev_inst *sdi, const uint8_t *buf)
+{
+ struct dev_context *devc;
+ const uint8_t *ptr;
+ unsigned int size;
+ int s;
+
+ devc = sdi->priv;
+ if (devc->data_source != DATA_SOURCE_MEMORY)
+ return;
+
+ ptr = buf + 4;
+ size = buf[3];
+ while (size > 0) {
+ s = MIN(size, sizeof(devc->log_buf) - devc->log_buf_len);
+ memcpy(devc->log_buf + devc->log_buf_len, ptr, s);
+ devc->log_buf_len += s;
+ size -= s;
+ ptr += s;
+
+ appa_55ii_log_data_parse(sdi);
+ }
+}
+
+static void appa_55ii_log_end(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+ if (devc->data_source != DATA_SOURCE_MEMORY)
+ return;
+
+ sdi->driver->dev_acquisition_stop(sdi, devc->session_cb_data);
+}
+
+static const uint8_t *appa_55ii_parse_data(struct sr_dev_inst *sdi,
+ const uint8_t *buf, int len)
+{
+ if (len < 5)
+ /* Need more data. */
+ return NULL;
+
+ if (buf[0] != 0x55 || buf[1] != 0x55)
+ /* Try to re-synchronize on a packet start. */
+ return buf + 1;
+
+ if (len < 5 + buf[3])
+ /* Need more data. */
+ return NULL;
+
+ if (!appa_55ii_checksum(buf))
+ /* Skip broken packet. */
+ return buf + 4 + buf[3] + 1;
+
+ switch ((packet_type)buf[2]) {
+ case LIVE_DATA:
+ appa_55ii_live_data(sdi, buf);
+ break;
+ case LOG_METADATA:
+ appa_55ii_log_metadata(sdi, buf);
+ break;
+ case LOG_DATA:
+ appa_55ii_log_data(sdi, buf);
+ break;
+ case LOG_START:
+ break;
+ case LOG_END:
+ appa_55ii_log_end(sdi);
+ break;
+ default:
+ sr_warn("Invalid packet type: 0x%02x.", buf[2]);
+ break;
+ }
+
+ return buf + 4 + buf[3] + 1;
+}
+
+SR_PRIV int appa_55ii_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int64_t time;
+ const uint8_t *ptr, *next_ptr, *end_ptr;
+ int len;
+
+ (void)fd;
+
+ if (!(sdi = cb_data) || !(devc = sdi->priv) || revents != G_IO_IN)
+ return TRUE;
+ serial = sdi->conn;
+
+ /* Try to get as much data as the buffer can hold. */
+ len = sizeof(devc->buf) - devc->buf_len;
+ len = serial_read(serial, devc->buf + devc->buf_len, len);
+ if (len < 1) {
+ sr_err("Serial port read error: %d.", len);
+ return FALSE;
+ }
+ devc->buf_len += len;
+
+ /* Now look for packets in that data. */
+ ptr = devc->buf;
+ end_ptr = ptr + devc->buf_len;
+ while ((next_ptr = appa_55ii_parse_data(sdi, ptr, end_ptr - ptr)))
+ ptr = next_ptr;
+
+ /* If we have any data left, move it to the beginning of our buffer. */
+ memmove(devc->buf, ptr, end_ptr - ptr);
+ devc->buf_len -= ptr - devc->buf;
+
+ /* If buffer is full and no valid packet was found, wipe buffer. */
+ if (devc->buf_len >= sizeof(devc->buf)) {
+ devc->buf_len = 0;
+ return FALSE;
+ }
+
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sr_info("Requested number of samples reached.");
+ sdi->driver->dev_acquisition_stop(sdi, devc->session_cb_data);
+ return TRUE;
+ }
+
+ if (devc->limit_msec) {
+ time = (g_get_monotonic_time() - devc->start_time) / 1000;
+ if (time > (int64_t)devc->limit_msec) {
+ sr_info("Requested time limit reached.");
+ sdi->driver->dev_acquisition_stop(sdi,
+ devc->session_cb_data);
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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_APPA_55II_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_APPA_55II_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "appa-55ii"
+
+#define APPA_55II_NUM_CHANNELS 2
+#define APPA_55II_BUF_SIZE (4 + 32 + 1)
+#define DEFAULT_DATA_SOURCE DATA_SOURCE_LIVE
+
+enum {
+ DATA_SOURCE_LIVE,
+ DATA_SOURCE_MEMORY,
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Acquisition settings */
+ uint64_t limit_samples; /**< The sampling limit (in number of samples). */
+ uint64_t limit_msec; /**< The time limit (in milliseconds). */
+ gboolean data_source; /**< Whether to read live samples or memory */
+ void *session_cb_data; /**< Opaque pointer passed in by the frontend. */
+
+ /* Operational state */
+ uint64_t num_samples; /**< The number of already received samples. */
+ int64_t start_time; /**< The time at which sampling started. */
+
+ /* Temporary state across callbacks */
+ uint8_t buf[APPA_55II_BUF_SIZE];
+ unsigned int buf_len;
+ uint8_t log_buf[64];
+ unsigned int log_buf_len;
+ unsigned int num_log_records;
+};
+
+SR_PRIV gboolean appa_55ii_packet_valid(const uint8_t *buf);
+SR_PRIV int appa_55ii_receive_data(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010-2012 HÃ¥vard Espeland <gus@ping.uio.no>,
+ * Copyright (C) 2010 Martin Stensgård <mastensg@ping.uio.no>
+ * Copyright (C) 2010 Carl Henrik Lunde <chlunde@ping.uio.no>
+ *
+ * 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/>.
+ */
+
+/*
+ * ASIX SIGMA/SIGMA2 logic analyzer driver
+ */
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <ftdi.h>
+#include <string.h>
+#include <unistd.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "asix-sigma.h"
+
+#define USB_VENDOR 0xa600
+#define USB_PRODUCT 0xa000
+#define USB_DESCRIPTION "ASIX SIGMA"
+#define USB_VENDOR_NAME "ASIX"
+#define USB_MODEL_NAME "SIGMA"
+
+SR_PRIV struct sr_dev_driver asix_sigma_driver_info;
+static struct sr_dev_driver *di = &asix_sigma_driver_info;
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
+
+/*
+ * The ASIX Sigma supports arbitrary integer frequency divider in
+ * the 50MHz mode. The divider is in range 1...256 , allowing for
+ * very precise sampling rate selection. This driver supports only
+ * a subset of the sampling rates.
+ */
+static const uint64_t samplerates[] = {
+ SR_KHZ(200), /* div=250 */
+ SR_KHZ(250), /* div=200 */
+ SR_KHZ(500), /* div=100 */
+ SR_MHZ(1), /* div=50 */
+ SR_MHZ(5), /* div=10 */
+ SR_MHZ(10), /* div=5 */
+ SR_MHZ(25), /* div=2 */
+ SR_MHZ(50), /* div=1 */
+ SR_MHZ(100), /* Special FW needed */
+ SR_MHZ(200), /* Special FW needed */
+};
+
+/*
+ * Channel numbers seem to go from 1-16, according to this image:
+ * http://tools.asix.net/img/sigma_sigmacab_pins_720.jpg
+ * (the cable has two additional GND pins, and a TI and TO pin)
+ */
+static const char *channel_names[] = {
+ "1", "2", "3", "4", "5", "6", "7", "8",
+ "9", "10", "11", "12", "13", "14", "15", "16",
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_TRIGGER_MATCH,
+ SR_CONF_CAPTURE_RATIO,
+ SR_CONF_LIMIT_MSEC,
+};
+
+static const int32_t trigger_matches[] = {
+ SR_TRIGGER_ZERO,
+ SR_TRIGGER_ONE,
+ SR_TRIGGER_RISING,
+ SR_TRIGGER_FALLING,
+};
+
+static const char *sigma_firmware_files[] = {
+ /* 50 MHz, supports 8 bit fractions */
+ FIRMWARE_DIR "/asix-sigma-50.fw",
+ /* 100 MHz */
+ FIRMWARE_DIR "/asix-sigma-100.fw",
+ /* 200 MHz */
+ FIRMWARE_DIR "/asix-sigma-200.fw",
+ /* Synchronous clock from pin */
+ FIRMWARE_DIR "/asix-sigma-50sync.fw",
+ /* Frequency counter */
+ FIRMWARE_DIR "/asix-sigma-phasor.fw",
+};
+
+static int sigma_read(void *buf, size_t size, struct dev_context *devc)
+{
+ int ret;
+
+ ret = ftdi_read_data(&devc->ftdic, (unsigned char *)buf, size);
+ if (ret < 0) {
+ sr_err("ftdi_read_data failed: %s",
+ ftdi_get_error_string(&devc->ftdic));
+ }
+
+ return ret;
+}
+
+static int sigma_write(void *buf, size_t size, struct dev_context *devc)
+{
+ int ret;
+
+ ret = ftdi_write_data(&devc->ftdic, (unsigned char *)buf, size);
+ if (ret < 0) {
+ sr_err("ftdi_write_data failed: %s",
+ ftdi_get_error_string(&devc->ftdic));
+ } else if ((size_t) ret != size) {
+ sr_err("ftdi_write_data did not complete write.");
+ }
+
+ return ret;
+}
+
+static int sigma_write_register(uint8_t reg, uint8_t *data, size_t len,
+ struct dev_context *devc)
+{
+ size_t i;
+ uint8_t buf[len + 2];
+ int idx = 0;
+
+ buf[idx++] = REG_ADDR_LOW | (reg & 0xf);
+ buf[idx++] = REG_ADDR_HIGH | (reg >> 4);
+
+ for (i = 0; i < len; ++i) {
+ buf[idx++] = REG_DATA_LOW | (data[i] & 0xf);
+ buf[idx++] = REG_DATA_HIGH_WRITE | (data[i] >> 4);
+ }
+
+ return sigma_write(buf, idx, devc);
+}
+
+static int sigma_set_register(uint8_t reg, uint8_t value, struct dev_context *devc)
+{
+ return sigma_write_register(reg, &value, 1, devc);
+}
+
+static int sigma_read_register(uint8_t reg, uint8_t *data, size_t len,
+ struct dev_context *devc)
+{
+ uint8_t buf[3];
+
+ buf[0] = REG_ADDR_LOW | (reg & 0xf);
+ buf[1] = REG_ADDR_HIGH | (reg >> 4);
+ buf[2] = REG_READ_ADDR;
+
+ sigma_write(buf, sizeof(buf), devc);
+
+ return sigma_read(data, len, devc);
+}
+
+static uint8_t sigma_get_register(uint8_t reg, struct dev_context *devc)
+{
+ uint8_t value;
+
+ if (1 != sigma_read_register(reg, &value, 1, devc)) {
+ sr_err("sigma_get_register: 1 byte expected");
+ return 0;
+ }
+
+ return value;
+}
+
+static int sigma_read_pos(uint32_t *stoppos, uint32_t *triggerpos,
+ struct dev_context *devc)
+{
+ uint8_t buf[] = {
+ REG_ADDR_LOW | READ_TRIGGER_POS_LOW,
+
+ REG_READ_ADDR | NEXT_REG,
+ REG_READ_ADDR | NEXT_REG,
+ REG_READ_ADDR | NEXT_REG,
+ REG_READ_ADDR | NEXT_REG,
+ REG_READ_ADDR | NEXT_REG,
+ REG_READ_ADDR | NEXT_REG,
+ };
+ uint8_t result[6];
+
+ sigma_write(buf, sizeof(buf), devc);
+
+ sigma_read(result, sizeof(result), devc);
+
+ *triggerpos = result[0] | (result[1] << 8) | (result[2] << 16);
+ *stoppos = result[3] | (result[4] << 8) | (result[5] << 16);
+
+ /* Not really sure why this must be done, but according to spec. */
+ if ((--*stoppos & 0x1ff) == 0x1ff)
+ stoppos -= 64;
+
+ if ((*--triggerpos & 0x1ff) == 0x1ff)
+ triggerpos -= 64;
+
+ return 1;
+}
+
+static int sigma_read_dram(uint16_t startchunk, size_t numchunks,
+ uint8_t *data, struct dev_context *devc)
+{
+ size_t i;
+ uint8_t buf[4096];
+ int idx = 0;
+
+ /* Send the startchunk. Index start with 1. */
+ buf[0] = startchunk >> 8;
+ buf[1] = startchunk & 0xff;
+ sigma_write_register(WRITE_MEMROW, buf, 2, devc);
+
+ /* Read the DRAM. */
+ buf[idx++] = REG_DRAM_BLOCK;
+ buf[idx++] = REG_DRAM_WAIT_ACK;
+
+ for (i = 0; i < numchunks; ++i) {
+ /* Alternate bit to copy from DRAM to cache. */
+ if (i != (numchunks - 1))
+ buf[idx++] = REG_DRAM_BLOCK | (((i + 1) % 2) << 4);
+
+ buf[idx++] = REG_DRAM_BLOCK_DATA | ((i % 2) << 4);
+
+ if (i != (numchunks - 1))
+ buf[idx++] = REG_DRAM_WAIT_ACK;
+ }
+
+ sigma_write(buf, idx, devc);
+
+ return sigma_read(data, numchunks * CHUNK_SIZE, devc);
+}
+
+/* Upload trigger look-up tables to Sigma. */
+static int sigma_write_trigger_lut(struct triggerlut *lut, struct dev_context *devc)
+{
+ int i;
+ uint8_t tmp[2];
+ uint16_t bit;
+
+ /* Transpose the table and send to Sigma. */
+ for (i = 0; i < 16; ++i) {
+ bit = 1 << i;
+
+ tmp[0] = tmp[1] = 0;
+
+ if (lut->m2d[0] & bit)
+ tmp[0] |= 0x01;
+ if (lut->m2d[1] & bit)
+ tmp[0] |= 0x02;
+ if (lut->m2d[2] & bit)
+ tmp[0] |= 0x04;
+ if (lut->m2d[3] & bit)
+ tmp[0] |= 0x08;
+
+ if (lut->m3 & bit)
+ tmp[0] |= 0x10;
+ if (lut->m3s & bit)
+ tmp[0] |= 0x20;
+ if (lut->m4 & bit)
+ tmp[0] |= 0x40;
+
+ if (lut->m0d[0] & bit)
+ tmp[1] |= 0x01;
+ if (lut->m0d[1] & bit)
+ tmp[1] |= 0x02;
+ if (lut->m0d[2] & bit)
+ tmp[1] |= 0x04;
+ if (lut->m0d[3] & bit)
+ tmp[1] |= 0x08;
+
+ if (lut->m1d[0] & bit)
+ tmp[1] |= 0x10;
+ if (lut->m1d[1] & bit)
+ tmp[1] |= 0x20;
+ if (lut->m1d[2] & bit)
+ tmp[1] |= 0x40;
+ if (lut->m1d[3] & bit)
+ tmp[1] |= 0x80;
+
+ sigma_write_register(WRITE_TRIGGER_SELECT0, tmp, sizeof(tmp),
+ devc);
+ sigma_set_register(WRITE_TRIGGER_SELECT1, 0x30 | i, devc);
+ }
+
+ /* Send the parameters */
+ sigma_write_register(WRITE_TRIGGER_SELECT0, (uint8_t *) &lut->params,
+ sizeof(lut->params), devc);
+
+ return SR_OK;
+}
+
+static void clear_helper(void *priv)
+{
+ struct dev_context *devc;
+
+ devc = priv;
+
+ ftdi_deinit(&devc->ftdic);
+}
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, clear_helper);
+}
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ GSList *devices;
+ struct ftdi_device_list *devlist;
+ char serial_txt[10];
+ uint32_t serial;
+ int ret;
+ unsigned int i;
+
+ (void)options;
+
+ drvc = di->priv;
+
+ devices = NULL;
+
+ if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
+ sr_err("%s: devc malloc failed", __func__);
+ return NULL;
+ }
+
+ ftdi_init(&devc->ftdic);
+
+ /* Look for SIGMAs. */
+
+ if ((ret = ftdi_usb_find_all(&devc->ftdic, &devlist,
+ USB_VENDOR, USB_PRODUCT)) <= 0) {
+ if (ret < 0)
+ sr_err("ftdi_usb_find_all(): %d", ret);
+ goto free;
+ }
+
+ /* Make sure it's a version 1 or 2 SIGMA. */
+ ftdi_usb_get_strings(&devc->ftdic, devlist->dev, NULL, 0, NULL, 0,
+ serial_txt, sizeof(serial_txt));
+ sscanf(serial_txt, "%x", &serial);
+
+ if (serial < 0xa6010000 || serial > 0xa602ffff) {
+ sr_err("Only SIGMA and SIGMA2 are supported "
+ "in this version of libsigrok.");
+ goto free;
+ }
+
+ sr_info("Found ASIX SIGMA - Serial: %s", serial_txt);
+
+ devc->cur_samplerate = samplerates[0];
+ devc->period_ps = 0;
+ devc->limit_msec = 0;
+ devc->cur_firmware = -1;
+ devc->num_channels = 0;
+ devc->samples_per_event = 0;
+ devc->capture_ratio = 50;
+ devc->use_triggers = 0;
+
+ /* Register SIGMA device. */
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING, USB_VENDOR_NAME,
+ USB_MODEL_NAME, NULL))) {
+ sr_err("%s: sdi was NULL", __func__);
+ goto free;
+ }
+ sdi->driver = di;
+
+ for (i = 0; i < ARRAY_SIZE(channel_names); i++) {
+ ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
+ channel_names[i]);
+ if (!ch)
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ devices = g_slist_append(devices, sdi);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ sdi->priv = devc;
+
+ /* We will open the device again when we need it. */
+ ftdi_list_free(&devlist);
+
+ return devices;
+
+free:
+ ftdi_deinit(&devc->ftdic);
+ g_free(devc);
+ return NULL;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+/*
+ * Configure the FPGA for bitbang mode.
+ * This sequence is documented in section 2. of the ASIX Sigma programming
+ * manual. This sequence is necessary to configure the FPGA in the Sigma
+ * into Bitbang mode, in which it can be programmed with the firmware.
+ */
+static int sigma_fpga_init_bitbang(struct dev_context *devc)
+{
+ uint8_t suicide[] = {
+ 0x84, 0x84, 0x88, 0x84, 0x88, 0x84, 0x88, 0x84,
+ };
+ uint8_t init_array[] = {
+ 0x01, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01,
+ };
+ int i, ret, timeout = 10000;
+ uint8_t data;
+
+ /* Section 2. part 1), do the FPGA suicide. */
+ sigma_write(suicide, sizeof(suicide), devc);
+ sigma_write(suicide, sizeof(suicide), devc);
+ sigma_write(suicide, sizeof(suicide), devc);
+ sigma_write(suicide, sizeof(suicide), devc);
+
+ /* Section 2. part 2), do pulse on D1. */
+ sigma_write(init_array, sizeof(init_array), devc);
+ ftdi_usb_purge_buffers(&devc->ftdic);
+
+ /* Wait until the FPGA asserts D6/INIT_B. */
+ for (i = 0; i < timeout; i++) {
+ ret = sigma_read(&data, 1, devc);
+ if (ret < 0)
+ return ret;
+ /* Test if pin D6 got asserted. */
+ if (data & (1 << 5))
+ return 0;
+ /* The D6 was not asserted yet, wait a bit. */
+ usleep(10000);
+ }
+
+ return SR_ERR_TIMEOUT;
+}
+
+/*
+ * Configure the FPGA for logic-analyzer mode.
+ */
+static int sigma_fpga_init_la(struct dev_context *devc)
+{
+ /* Initialize the logic analyzer mode. */
+ uint8_t logic_mode_start[] = {
+ REG_ADDR_LOW | (READ_ID & 0xf),
+ REG_ADDR_HIGH | (READ_ID >> 8),
+ REG_READ_ADDR, /* Read ID register. */
+
+ REG_ADDR_LOW | (WRITE_TEST & 0xf),
+ REG_DATA_LOW | 0x5,
+ REG_DATA_HIGH_WRITE | 0x5,
+ REG_READ_ADDR, /* Read scratch register. */
+
+ REG_DATA_LOW | 0xa,
+ REG_DATA_HIGH_WRITE | 0xa,
+ REG_READ_ADDR, /* Read scratch register. */
+
+ REG_ADDR_LOW | (WRITE_MODE & 0xf),
+ REG_DATA_LOW | 0x0,
+ REG_DATA_HIGH_WRITE | 0x8,
+ };
+
+ uint8_t result[3];
+ int ret;
+
+ /* Initialize the logic analyzer mode. */
+ sigma_write(logic_mode_start, sizeof(logic_mode_start), devc);
+
+ /* Expect a 3 byte reply since we issued three READ requests. */
+ ret = sigma_read(result, 3, devc);
+ if (ret != 3)
+ goto err;
+
+ if (result[0] != 0xa6 || result[1] != 0x55 || result[2] != 0xaa)
+ goto err;
+
+ return SR_OK;
+err:
+ sr_err("Configuration failed. Invalid reply received.");
+ return SR_ERR;
+}
+
+/*
+ * Read the firmware from a file and transform it into a series of bitbang
+ * pulses used to program the FPGA. Note that the *bb_cmd must be free()'d
+ * by the caller of this function.
+ */
+static int sigma_fw_2_bitbang(const char *filename,
+ uint8_t **bb_cmd, gsize *bb_cmd_size)
+{
+ GMappedFile *file;
+ GError *error;
+ gsize i, file_size, bb_size;
+ gchar *firmware;
+ uint8_t *bb_stream, *bbs;
+ uint32_t imm;
+ int bit, v;
+ int ret = SR_OK;
+
+ /*
+ * Map the file and make the mapped buffer writable.
+ * NOTE: Using writable=TRUE does _NOT_ mean that file that is mapped
+ * will be modified. It will not be modified until someone uses
+ * g_file_set_contents() on it.
+ */
+ error = NULL;
+ file = g_mapped_file_new(filename, TRUE, &error);
+ g_assert_no_error(error);
+
+ file_size = g_mapped_file_get_length(file);
+ firmware = g_mapped_file_get_contents(file);
+ g_assert(firmware);
+
+ /* Weird magic transformation below, I have no idea what it does. */
+ imm = 0x3f6df2ab;
+ for (i = 0; i < file_size; i++) {
+ imm = (imm + 0xa853753) % 177 + (imm * 0x8034052);
+ firmware[i] ^= imm & 0xff;
+ }
+
+ /*
+ * Now that the firmware is "transformed", we will transcribe the
+ * firmware blob into a sequence of toggles of the Dx wires. This
+ * sequence will be fed directly into the Sigma, which must be in
+ * the FPGA bitbang programming mode.
+ */
+
+ /* Each bit of firmware is transcribed as two toggles of Dx wires. */
+ bb_size = file_size * 8 * 2;
+ bb_stream = (uint8_t *)g_try_malloc(bb_size);
+ if (!bb_stream) {
+ sr_err("%s: Failed to allocate bitbang stream", __func__);
+ ret = SR_ERR_MALLOC;
+ goto exit;
+ }
+
+ bbs = bb_stream;
+ for (i = 0; i < file_size; i++) {
+ for (bit = 7; bit >= 0; bit--) {
+ v = (firmware[i] & (1 << bit)) ? 0x40 : 0x00;
+ *bbs++ = v | 0x01;
+ *bbs++ = v;
+ }
+ }
+
+ /* The transformation completed successfully, return the result. */
+ *bb_cmd = bb_stream;
+ *bb_cmd_size = bb_size;
+
+exit:
+ g_mapped_file_unref(file);
+ return ret;
+}
+
+static int upload_firmware(int firmware_idx, struct dev_context *devc)
+{
+ int ret;
+ unsigned char *buf;
+ unsigned char pins;
+ size_t buf_size;
+ const char *firmware = sigma_firmware_files[firmware_idx];
+ struct ftdi_context *ftdic = &devc->ftdic;
+
+ /* Make sure it's an ASIX SIGMA. */
+ ret = ftdi_usb_open_desc(ftdic, USB_VENDOR, USB_PRODUCT,
+ USB_DESCRIPTION, NULL);
+ if (ret < 0) {
+ sr_err("ftdi_usb_open failed: %s",
+ ftdi_get_error_string(ftdic));
+ return 0;
+ }
+
+ ret = ftdi_set_bitmode(ftdic, 0xdf, BITMODE_BITBANG);
+ if (ret < 0) {
+ sr_err("ftdi_set_bitmode failed: %s",
+ ftdi_get_error_string(ftdic));
+ return 0;
+ }
+
+ /* Four times the speed of sigmalogan - Works well. */
+ ret = ftdi_set_baudrate(ftdic, 750000);
+ if (ret < 0) {
+ sr_err("ftdi_set_baudrate failed: %s",
+ ftdi_get_error_string(ftdic));
+ return 0;
+ }
+
+ /* Initialize the FPGA for firmware upload. */
+ ret = sigma_fpga_init_bitbang(devc);
+ if (ret)
+ return ret;
+
+ /* Prepare firmware. */
+ ret = sigma_fw_2_bitbang(firmware, &buf, &buf_size);
+ if (ret != SR_OK) {
+ sr_err("An error occured while reading the firmware: %s",
+ firmware);
+ return ret;
+ }
+
+ /* Upload firmare. */
+ sr_info("Uploading firmware file '%s'.", firmware);
+ sigma_write(buf, buf_size, devc);
+
+ g_free(buf);
+
+ ret = ftdi_set_bitmode(ftdic, 0x00, BITMODE_RESET);
+ if (ret < 0) {
+ sr_err("ftdi_set_bitmode failed: %s",
+ ftdi_get_error_string(ftdic));
+ return SR_ERR;
+ }
+
+ ftdi_usb_purge_buffers(ftdic);
+
+ /* Discard garbage. */
+ while (sigma_read(&pins, 1, devc) == 1)
+ ;
+
+ /* Initialize the FPGA for logic-analyzer mode. */
+ ret = sigma_fpga_init_la(devc);
+ if (ret != SR_OK)
+ return ret;
+
+ devc->cur_firmware = firmware_idx;
+
+ sr_info("Firmware uploaded.");
+
+ return SR_OK;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int ret;
+
+ devc = sdi->priv;
+
+ /* Make sure it's an ASIX SIGMA. */
+ if ((ret = ftdi_usb_open_desc(&devc->ftdic,
+ USB_VENDOR, USB_PRODUCT, USB_DESCRIPTION, NULL)) < 0) {
+
+ sr_err("ftdi_usb_open failed: %s",
+ ftdi_get_error_string(&devc->ftdic));
+
+ return 0;
+ }
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate)
+{
+ struct dev_context *devc;
+ unsigned int i;
+ int ret;
+
+ devc = sdi->priv;
+ ret = SR_OK;
+
+ for (i = 0; i < ARRAY_SIZE(samplerates); i++) {
+ if (samplerates[i] == samplerate)
+ break;
+ }
+ if (samplerates[i] == 0)
+ return SR_ERR_SAMPLERATE;
+
+ if (samplerate <= SR_MHZ(50)) {
+ ret = upload_firmware(0, devc);
+ devc->num_channels = 16;
+ } else if (samplerate == SR_MHZ(100)) {
+ ret = upload_firmware(1, devc);
+ devc->num_channels = 8;
+ } else if (samplerate == SR_MHZ(200)) {
+ ret = upload_firmware(2, devc);
+ devc->num_channels = 4;
+ }
+
+ if (ret == SR_OK) {
+ devc->cur_samplerate = samplerate;
+ devc->period_ps = 1000000000000ULL / samplerate;
+ devc->samples_per_event = 16 / devc->num_channels;
+ devc->state.state = SIGMA_IDLE;
+ }
+
+ return ret;
+}
+
+/*
+ * In 100 and 200 MHz mode, only a single pin rising/falling can be
+ * set as trigger. In other modes, two rising/falling triggers can be set,
+ * in addition to value/mask trigger for any number of channels.
+ *
+ * The Sigma supports complex triggers using boolean expressions, but this
+ * has not been implemented yet.
+ */
+static int convert_trigger(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_trigger *trigger;
+ struct sr_trigger_stage *stage;
+ struct sr_trigger_match *match;
+ const GSList *l, *m;
+ int channelbit, trigger_set;
+
+ devc = sdi->priv;
+ memset(&devc->trigger, 0, sizeof(struct sigma_trigger));
+ if (!(trigger = sr_session_trigger_get(sdi->session)))
+ return SR_OK;
+
+ trigger_set = 0;
+ for (l = trigger->stages; l; l = l->next) {
+ stage = l->data;
+ for (m = stage->matches; m; m = m->next) {
+ match = m->data;
+ if (!match->channel->enabled)
+ /* Ignore disabled channels with a trigger. */
+ continue;
+ channelbit = 1 << (match->channel->index);
+ if (devc->cur_samplerate >= SR_MHZ(100)) {
+ /* Fast trigger support. */
+ if (trigger_set) {
+ sr_err("Only a single pin trigger is "
+ "supported in 100 and 200MHz mode.");
+ return SR_ERR;
+ }
+ if (match->match == SR_TRIGGER_FALLING)
+ devc->trigger.fallingmask |= channelbit;
+ else if (match->match == SR_TRIGGER_RISING)
+ devc->trigger.risingmask |= channelbit;
+ else {
+ sr_err("Only rising/falling trigger is "
+ "supported in 100 and 200MHz mode.");
+ return SR_ERR;
+ }
+
+ ++trigger_set;
+ } else {
+ /* Simple trigger support (event). */
+ if (match->match == SR_TRIGGER_ONE) {
+ devc->trigger.simplevalue |= channelbit;
+ devc->trigger.simplemask |= channelbit;
+ }
+ else if (match->match == SR_TRIGGER_ZERO) {
+ devc->trigger.simplevalue &= ~channelbit;
+ devc->trigger.simplemask |= channelbit;
+ }
+ else if (match->match == SR_TRIGGER_FALLING) {
+ devc->trigger.fallingmask |= channelbit;
+ ++trigger_set;
+ }
+ else if (match->match == SR_TRIGGER_RISING) {
+ devc->trigger.risingmask |= channelbit;
+ ++trigger_set;
+ }
+
+ /*
+ * Actually, Sigma supports 2 rising/falling triggers,
+ * but they are ORed and the current trigger syntax
+ * does not permit ORed triggers.
+ */
+ if (trigger_set > 1) {
+ sr_err("Only 1 rising/falling trigger "
+ "is supported.");
+ return SR_ERR;
+ }
+ }
+ }
+ }
+
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ /* TODO */
+ if (sdi->status == SR_ST_ACTIVE)
+ ftdi_usb_close(&devc->ftdic);
+
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return dev_clear();
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR;
+ devc = sdi->priv;
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ *data = g_variant_new_uint64(devc->cur_samplerate);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ *data = g_variant_new_uint64(devc->limit_msec);
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ *data = g_variant_new_uint64(devc->capture_ratio);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ uint64_t tmp;
+ int ret;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ ret = SR_OK;
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ ret = set_samplerate(sdi, g_variant_get_uint64(data));
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ tmp = g_variant_get_uint64(data);
+ if (tmp > 0)
+ devc->limit_msec = g_variant_get_uint64(data);
+ else
+ ret = SR_ERR;
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ tmp = g_variant_get_uint64(data);
+ devc->limit_msec = tmp * 1000 / devc->cur_samplerate;
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ tmp = g_variant_get_uint64(data);
+ if (tmp <= 100)
+ devc->capture_ratio = tmp;
+ else
+ ret = SR_ERR;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ GVariant *gvar;
+ GVariantBuilder gvb;
+
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
+ ARRAY_SIZE(samplerates), sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ trigger_matches, ARRAY_SIZE(trigger_matches),
+ sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+/* Software trigger to determine exact trigger position. */
+static int get_trigger_offset(uint8_t *samples, uint16_t last_sample,
+ struct sigma_trigger *t)
+{
+ int i;
+ uint16_t sample = 0;
+
+ for (i = 0; i < 8; ++i) {
+ if (i > 0)
+ last_sample = sample;
+ sample = samples[2 * i] | (samples[2 * i + 1] << 8);
+
+ /* Simple triggers. */
+ if ((sample & t->simplemask) != t->simplevalue)
+ continue;
+
+ /* Rising edge. */
+ if (((last_sample & t->risingmask) != 0) ||
+ ((sample & t->risingmask) != t->risingmask))
+ continue;
+
+ /* Falling edge. */
+ if ((last_sample & t->fallingmask) != t->fallingmask ||
+ (sample & t->fallingmask) != 0)
+ continue;
+
+ break;
+ }
+
+ /* If we did not match, return original trigger pos. */
+ return i & 0x7;
+}
+
+
+/*
+ * Return the timestamp of "DRAM cluster".
+ */
+static uint16_t sigma_dram_cluster_ts(struct sigma_dram_cluster *cluster)
+{
+ return (cluster->timestamp_hi << 8) | cluster->timestamp_lo;
+}
+
+static void sigma_decode_dram_cluster(struct sigma_dram_cluster *dram_cluster,
+ unsigned int events_in_cluster,
+ unsigned int triggered,
+ struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ struct sigma_state *ss = &devc->state;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ uint16_t tsdiff, ts;
+ uint8_t samples[2048];
+ unsigned int i;
+
+ ts = sigma_dram_cluster_ts(dram_cluster);
+ tsdiff = ts - ss->lastts;
+ ss->lastts = ts;
+
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.unitsize = 2;
+ logic.data = samples;
+
+ /*
+ * First of all, send Sigrok a copy of the last sample from
+ * previous cluster as many times as needed to make up for
+ * the differential characteristics of data we get from the
+ * Sigma. Sigrok needs one sample of data per period.
+ *
+ * One DRAM cluster contains a timestamp and seven samples,
+ * the units of timestamp are "devc->period_ps" , the first
+ * sample in the cluster happens at the time of the timestamp
+ * and the remaining samples happen at timestamp +1...+6 .
+ */
+ for (ts = 0; ts < tsdiff - (EVENTS_PER_CLUSTER - 1); ts++) {
+ i = ts % 1024;
+ samples[2 * i + 0] = ss->lastsample & 0xff;
+ samples[2 * i + 1] = ss->lastsample >> 8;
+
+ /*
+ * If we have 1024 samples ready or we're at the
+ * end of submitting the padding samples, submit
+ * the packet to Sigrok.
+ */
+ if ((i == 1023) || (ts == (tsdiff - EVENTS_PER_CLUSTER))) {
+ logic.length = (i + 1) * logic.unitsize;
+ sr_session_send(sdi, &packet);
+ }
+ }
+
+ /*
+ * Parse the samples in current cluster and prepare them
+ * to be submitted to Sigrok.
+ */
+ for (i = 0; i < events_in_cluster; i++) {
+ samples[2 * i + 1] = dram_cluster->samples[i].sample_lo;
+ samples[2 * i + 0] = dram_cluster->samples[i].sample_hi;
+ }
+
+ /* Send data up to trigger point (if triggered). */
+ int trigger_offset = 0;
+ if (triggered) {
+ /*
+ * Trigger is not always accurate to sample because of
+ * pipeline delay. However, it always triggers before
+ * the actual event. We therefore look at the next
+ * samples to pinpoint the exact position of the trigger.
+ */
+ trigger_offset = get_trigger_offset(samples,
+ ss->lastsample, &devc->trigger);
+
+ if (trigger_offset > 0) {
+ packet.type = SR_DF_LOGIC;
+ logic.length = trigger_offset * logic.unitsize;
+ sr_session_send(sdi, &packet);
+ events_in_cluster -= trigger_offset;
+ }
+
+ /* Only send trigger if explicitly enabled. */
+ if (devc->use_triggers) {
+ packet.type = SR_DF_TRIGGER;
+ sr_session_send(sdi, &packet);
+ }
+ }
+
+ if (events_in_cluster > 0) {
+ packet.type = SR_DF_LOGIC;
+ logic.length = events_in_cluster * logic.unitsize;
+ logic.data = samples + (trigger_offset * logic.unitsize);
+ sr_session_send(sdi, &packet);
+ }
+
+ ss->lastsample =
+ samples[2 * (events_in_cluster - 1) + 0] |
+ (samples[2 * (events_in_cluster - 1) + 1] << 8);
+
+}
+
+/*
+ * Decode chunk of 1024 bytes, 64 clusters, 7 events per cluster.
+ * Each event is 20ns apart, and can contain multiple samples.
+ *
+ * For 200 MHz, events contain 4 samples for each channel, spread 5 ns apart.
+ * For 100 MHz, events contain 2 samples for each channel, spread 10 ns apart.
+ * For 50 MHz and below, events contain one sample for each channel,
+ * spread 20 ns apart.
+ */
+static int decode_chunk_ts(struct sigma_dram_line *dram_line,
+ uint16_t events_in_line,
+ uint32_t trigger_event,
+ struct sr_dev_inst *sdi)
+{
+ struct sigma_dram_cluster *dram_cluster;
+ struct dev_context *devc = sdi->priv;
+ unsigned int clusters_in_line =
+ (events_in_line + (EVENTS_PER_CLUSTER - 1)) / EVENTS_PER_CLUSTER;
+ unsigned int events_in_cluster;
+ unsigned int i;
+ uint32_t trigger_cluster = ~0, triggered = 0;
+
+ /* Check if trigger is in this chunk. */
+ if (trigger_event < (64 * 7)) {
+ if (devc->cur_samplerate <= SR_MHZ(50)) {
+ trigger_event -= MIN(EVENTS_PER_CLUSTER - 1,
+ trigger_event);
+ }
+
+ /* Find in which cluster the trigger occured. */
+ trigger_cluster = trigger_event / EVENTS_PER_CLUSTER;
+ }
+
+ /* For each full DRAM cluster. */
+ for (i = 0; i < clusters_in_line; i++) {
+ dram_cluster = &dram_line->cluster[i];
+
+ /* The last cluster might not be full. */
+ if ((i == clusters_in_line - 1) &&
+ (events_in_line % EVENTS_PER_CLUSTER)) {
+ events_in_cluster = events_in_line % EVENTS_PER_CLUSTER;
+ } else {
+ events_in_cluster = EVENTS_PER_CLUSTER;
+ }
+
+ triggered = (i == trigger_cluster);
+ sigma_decode_dram_cluster(dram_cluster, events_in_cluster,
+ triggered, sdi);
+ }
+
+ return SR_OK;
+}
+
+static int download_capture(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ const uint32_t chunks_per_read = 32;
+ struct sigma_dram_line *dram_line;
+ int bufsz;
+ uint32_t stoppos, triggerpos;
+ struct sr_datafeed_packet packet;
+ uint8_t modestatus;
+
+ uint32_t i;
+ uint32_t dl_lines_total, dl_lines_curr, dl_lines_done;
+ uint32_t dl_events_in_line = 64 * 7;
+ uint32_t trg_line = ~0, trg_event = ~0;
+
+ dram_line = g_try_malloc0(chunks_per_read * sizeof(*dram_line));
+ if (!dram_line)
+ return FALSE;
+
+ sr_info("Downloading sample data.");
+
+ /* Stop acquisition. */
+ sigma_set_register(WRITE_MODE, 0x11, devc);
+
+ /* Set SDRAM Read Enable. */
+ sigma_set_register(WRITE_MODE, 0x02, devc);
+
+ /* Get the current position. */
+ sigma_read_pos(&stoppos, &triggerpos, devc);
+
+ /* Check if trigger has fired. */
+ modestatus = sigma_get_register(READ_MODE, devc);
+ if (modestatus & 0x20) {
+ trg_line = triggerpos >> 9;
+ trg_event = triggerpos & 0x1ff;
+ }
+
+ /*
+ * Determine how many 1024b "DRAM lines" do we need to read from the
+ * Sigma so we have a complete set of samples. Note that the last
+ * line can be only partial, containing less than 64 clusters.
+ */
+ dl_lines_total = (stoppos >> 9) + 1;
+
+ dl_lines_done = 0;
+
+ while (dl_lines_total > dl_lines_done) {
+ /* We can download only up-to 32 DRAM lines in one go! */
+ dl_lines_curr = MIN(chunks_per_read, dl_lines_total);
+
+ bufsz = sigma_read_dram(dl_lines_done, dl_lines_curr,
+ (uint8_t *)dram_line, devc);
+ /* TODO: Check bufsz. For now, just avoid compiler warnings. */
+ (void)bufsz;
+
+ /* This is the first DRAM line, so find the initial timestamp. */
+ if (dl_lines_done == 0) {
+ devc->state.lastts =
+ sigma_dram_cluster_ts(&dram_line[0].cluster[0]);
+ devc->state.lastsample = 0;
+ }
+
+ for (i = 0; i < dl_lines_curr; i++) {
+ uint32_t trigger_event = ~0;
+ /* The last "DRAM line" can be only partially full. */
+ if (dl_lines_done + i == dl_lines_total - 1)
+ dl_events_in_line = stoppos & 0x1ff;
+
+ /* Test if the trigger happened on this line. */
+ if (dl_lines_done + i == trg_line)
+ trigger_event = trg_event;
+
+ decode_chunk_ts(dram_line + i, dl_events_in_line,
+ trigger_event, sdi);
+ }
+
+ dl_lines_done += dl_lines_curr;
+ }
+
+ /* All done. */
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+
+ dev_acquisition_stop(sdi, sdi);
+
+ g_free(dram_line);
+
+ return TRUE;
+}
+
+/*
+ * Handle the Sigma when in CAPTURE mode. This function checks:
+ * - Sampling time ended
+ * - DRAM capacity overflow
+ * This function triggers download of the samples from Sigma
+ * in case either of the above conditions is true.
+ */
+static int sigma_capture_mode(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+
+ uint64_t running_msec;
+ struct timeval tv;
+
+ uint32_t stoppos, triggerpos;
+
+ /* Check if the selected sampling duration passed. */
+ gettimeofday(&tv, 0);
+ running_msec = (tv.tv_sec - devc->start_tv.tv_sec) * 1000 +
+ (tv.tv_usec - devc->start_tv.tv_usec) / 1000;
+ if (running_msec >= devc->limit_msec)
+ return download_capture(sdi);
+
+ /* Get the position in DRAM to which the FPGA is writing now. */
+ sigma_read_pos(&stoppos, &triggerpos, devc);
+ /* Test if DRAM is full and if so, download the data. */
+ if ((stoppos >> 9) == 32767)
+ return download_capture(sdi);
+
+ return TRUE;
+}
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+ devc = sdi->priv;
+
+ if (devc->state.state == SIGMA_IDLE)
+ return TRUE;
+
+ if (devc->state.state == SIGMA_CAPTURE)
+ return sigma_capture_mode(sdi);
+
+ return TRUE;
+}
+
+/* Build a LUT entry used by the trigger functions. */
+static void build_lut_entry(uint16_t value, uint16_t mask, uint16_t *entry)
+{
+ int i, j, k, bit;
+
+ /* For each quad channel. */
+ for (i = 0; i < 4; ++i) {
+ entry[i] = 0xffff;
+
+ /* For each bit in LUT. */
+ for (j = 0; j < 16; ++j)
+
+ /* For each channel in quad. */
+ for (k = 0; k < 4; ++k) {
+ bit = 1 << (i * 4 + k);
+
+ /* Set bit in entry */
+ if ((mask & bit) &&
+ ((!(value & bit)) !=
+ (!(j & (1 << k)))))
+ entry[i] &= ~(1 << j);
+ }
+ }
+}
+
+/* Add a logical function to LUT mask. */
+static void add_trigger_function(enum triggerop oper, enum triggerfunc func,
+ int index, int neg, uint16_t *mask)
+{
+ int i, j;
+ int x[2][2], tmp, a, b, aset, bset, rset;
+
+ memset(x, 0, 4 * sizeof(int));
+
+ /* Trigger detect condition. */
+ switch (oper) {
+ case OP_LEVEL:
+ x[0][1] = 1;
+ x[1][1] = 1;
+ break;
+ case OP_NOT:
+ x[0][0] = 1;
+ x[1][0] = 1;
+ break;
+ case OP_RISE:
+ x[0][1] = 1;
+ break;
+ case OP_FALL:
+ x[1][0] = 1;
+ break;
+ case OP_RISEFALL:
+ x[0][1] = 1;
+ x[1][0] = 1;
+ break;
+ case OP_NOTRISE:
+ x[1][1] = 1;
+ x[0][0] = 1;
+ x[1][0] = 1;
+ break;
+ case OP_NOTFALL:
+ x[1][1] = 1;
+ x[0][0] = 1;
+ x[0][1] = 1;
+ break;
+ case OP_NOTRISEFALL:
+ x[1][1] = 1;
+ x[0][0] = 1;
+ break;
+ }
+
+ /* Transpose if neg is set. */
+ if (neg) {
+ for (i = 0; i < 2; ++i) {
+ for (j = 0; j < 2; ++j) {
+ tmp = x[i][j];
+ x[i][j] = x[1-i][1-j];
+ x[1-i][1-j] = tmp;
+ }
+ }
+ }
+
+ /* Update mask with function. */
+ for (i = 0; i < 16; ++i) {
+ a = (i >> (2 * index + 0)) & 1;
+ b = (i >> (2 * index + 1)) & 1;
+
+ aset = (*mask >> i) & 1;
+ bset = x[b][a];
+
+ if (func == FUNC_AND || func == FUNC_NAND)
+ rset = aset & bset;
+ else if (func == FUNC_OR || func == FUNC_NOR)
+ rset = aset | bset;
+ else if (func == FUNC_XOR || func == FUNC_NXOR)
+ rset = aset ^ bset;
+
+ if (func == FUNC_NAND || func == FUNC_NOR || func == FUNC_NXOR)
+ rset = !rset;
+
+ *mask &= ~(1 << i);
+
+ if (rset)
+ *mask |= 1 << i;
+ }
+}
+
+/*
+ * Build trigger LUTs used by 50 MHz and lower sample rates for supporting
+ * simple pin change and state triggers. Only two transitions (rise/fall) can be
+ * set at any time, but a full mask and value can be set (0/1).
+ */
+static int build_basic_trigger(struct triggerlut *lut, struct dev_context *devc)
+{
+ int i,j;
+ uint16_t masks[2] = { 0, 0 };
+
+ memset(lut, 0, sizeof(struct triggerlut));
+
+ /* Contant for simple triggers. */
+ lut->m4 = 0xa000;
+
+ /* Value/mask trigger support. */
+ build_lut_entry(devc->trigger.simplevalue, devc->trigger.simplemask,
+ lut->m2d);
+
+ /* Rise/fall trigger support. */
+ for (i = 0, j = 0; i < 16; ++i) {
+ if (devc->trigger.risingmask & (1 << i) ||
+ devc->trigger.fallingmask & (1 << i))
+ masks[j++] = 1 << i;
+ }
+
+ build_lut_entry(masks[0], masks[0], lut->m0d);
+ build_lut_entry(masks[1], masks[1], lut->m1d);
+
+ /* Add glue logic */
+ if (masks[0] || masks[1]) {
+ /* Transition trigger. */
+ if (masks[0] & devc->trigger.risingmask)
+ add_trigger_function(OP_RISE, FUNC_OR, 0, 0, &lut->m3);
+ if (masks[0] & devc->trigger.fallingmask)
+ add_trigger_function(OP_FALL, FUNC_OR, 0, 0, &lut->m3);
+ if (masks[1] & devc->trigger.risingmask)
+ add_trigger_function(OP_RISE, FUNC_OR, 1, 0, &lut->m3);
+ if (masks[1] & devc->trigger.fallingmask)
+ add_trigger_function(OP_FALL, FUNC_OR, 1, 0, &lut->m3);
+ } else {
+ /* Only value/mask trigger. */
+ lut->m3 = 0xffff;
+ }
+
+ /* Triggertype: event. */
+ lut->params.selres = 3;
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct clockselect_50 clockselect;
+ int frac, triggerpin, ret;
+ uint8_t triggerselect = 0;
+ struct triggerinout triggerinout_conf;
+ struct triggerlut lut;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ if (convert_trigger(sdi) != SR_OK) {
+ sr_err("Failed to configure triggers.");
+ return SR_ERR;
+ }
+
+ /* If the samplerate has not been set, default to 200 kHz. */
+ if (devc->cur_firmware == -1) {
+ if ((ret = set_samplerate(sdi, SR_KHZ(200))) != SR_OK)
+ return ret;
+ }
+
+ /* Enter trigger programming mode. */
+ sigma_set_register(WRITE_TRIGGER_SELECT1, 0x20, devc);
+
+ /* 100 and 200 MHz mode. */
+ if (devc->cur_samplerate >= SR_MHZ(100)) {
+ sigma_set_register(WRITE_TRIGGER_SELECT1, 0x81, devc);
+
+ /* Find which pin to trigger on from mask. */
+ for (triggerpin = 0; triggerpin < 8; ++triggerpin)
+ if ((devc->trigger.risingmask | devc->trigger.fallingmask) &
+ (1 << triggerpin))
+ break;
+
+ /* Set trigger pin and light LED on trigger. */
+ triggerselect = (1 << LEDSEL1) | (triggerpin & 0x7);
+
+ /* Default rising edge. */
+ if (devc->trigger.fallingmask)
+ triggerselect |= 1 << 3;
+
+ /* All other modes. */
+ } else if (devc->cur_samplerate <= SR_MHZ(50)) {
+ build_basic_trigger(&lut, devc);
+
+ sigma_write_trigger_lut(&lut, devc);
+
+ triggerselect = (1 << LEDSEL1) | (1 << LEDSEL0);
+ }
+
+ /* Setup trigger in and out pins to default values. */
+ memset(&triggerinout_conf, 0, sizeof(struct triggerinout));
+ triggerinout_conf.trgout_bytrigger = 1;
+ triggerinout_conf.trgout_enable = 1;
+
+ sigma_write_register(WRITE_TRIGGER_OPTION,
+ (uint8_t *) &triggerinout_conf,
+ sizeof(struct triggerinout), devc);
+
+ /* Go back to normal mode. */
+ sigma_set_register(WRITE_TRIGGER_SELECT1, triggerselect, devc);
+
+ /* Set clock select register. */
+ if (devc->cur_samplerate == SR_MHZ(200))
+ /* Enable 4 channels. */
+ sigma_set_register(WRITE_CLOCK_SELECT, 0xf0, devc);
+ else if (devc->cur_samplerate == SR_MHZ(100))
+ /* Enable 8 channels. */
+ sigma_set_register(WRITE_CLOCK_SELECT, 0x00, devc);
+ else {
+ /*
+ * 50 MHz mode (or fraction thereof). Any fraction down to
+ * 50 MHz / 256 can be used, but is not supported by sigrok API.
+ */
+ frac = SR_MHZ(50) / devc->cur_samplerate - 1;
+
+ clockselect.async = 0;
+ clockselect.fraction = frac;
+ clockselect.disabled_channels = 0;
+
+ sigma_write_register(WRITE_CLOCK_SELECT,
+ (uint8_t *) &clockselect,
+ sizeof(clockselect), devc);
+ }
+
+ /* Setup maximum post trigger time. */
+ sigma_set_register(WRITE_POST_TRIGGER,
+ (devc->capture_ratio * 255) / 100, devc);
+
+ /* Start acqusition. */
+ gettimeofday(&devc->start_tv, 0);
+ sigma_set_register(WRITE_MODE, 0x0d, devc);
+
+ devc->cb_data = cb_data;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(sdi, LOG_PREFIX);
+
+ /* Add capture source. */
+ sr_session_source_add(sdi->session, 0, G_IO_IN, 10, receive_data, (void *)sdi);
+
+ devc->state.state = SIGMA_CAPTURE;
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+
+ (void)cb_data;
+
+ devc = sdi->priv;
+ devc->state.state = SIGMA_IDLE;
+
+ sr_session_source_remove(sdi->session, 0);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver asix_sigma_driver_info = {
+ .name = "asix-sigma",
+ .longname = "ASIX SIGMA/SIGMA2",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = 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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010 HÃ¥vard Espeland <gus@ping.uio.no>,
+ * Copyright (C) 2010 Martin Stensgård <mastensg@ping.uio.no>
+ * Copyright (C) 2010 Carl Henrik Lunde <chlunde@ping.uio.no>
+ *
+ * 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_ASIX_SIGMA_ASIX_SIGMA_H
+#define LIBSIGROK_HARDWARE_ASIX_SIGMA_ASIX_SIGMA_H
+
+#define LOG_PREFIX "asix-sigma"
+
+enum sigma_write_register {
+ WRITE_CLOCK_SELECT = 0,
+ WRITE_TRIGGER_SELECT0 = 1,
+ WRITE_TRIGGER_SELECT1 = 2,
+ WRITE_MODE = 3,
+ WRITE_MEMROW = 4,
+ WRITE_POST_TRIGGER = 5,
+ WRITE_TRIGGER_OPTION = 6,
+ WRITE_PIN_VIEW = 7,
+
+ WRITE_TEST = 15,
+};
+
+enum sigma_read_register {
+ READ_ID = 0,
+ READ_TRIGGER_POS_LOW = 1,
+ READ_TRIGGER_POS_HIGH = 2,
+ READ_TRIGGER_POS_UP = 3,
+ READ_STOP_POS_LOW = 4,
+ READ_STOP_POS_HIGH = 5,
+ READ_STOP_POS_UP = 6,
+ READ_MODE = 7,
+ READ_PIN_CHANGE_LOW = 8,
+ READ_PIN_CHANGE_HIGH = 9,
+ READ_BLOCK_LAST_TS_LOW = 10,
+ READ_BLOCK_LAST_TS_HIGH = 11,
+ READ_PIN_VIEW = 12,
+
+ READ_TEST = 15,
+};
+
+#define REG_ADDR_LOW (0x0 << 4)
+#define REG_ADDR_HIGH (0x1 << 4)
+#define REG_DATA_LOW (0x2 << 4)
+#define REG_DATA_HIGH_WRITE (0x3 << 4)
+#define REG_READ_ADDR (0x4 << 4)
+#define REG_DRAM_WAIT_ACK (0x5 << 4)
+
+/* Bit (1 << 4) can be low or high (double buffer / cache) */
+#define REG_DRAM_BLOCK (0x6 << 4)
+#define REG_DRAM_BLOCK_BEGIN (0x8 << 4)
+#define REG_DRAM_BLOCK_DATA (0xa << 4)
+
+#define LEDSEL0 6
+#define LEDSEL1 7
+
+#define NEXT_REG 1
+
+#define EVENTS_PER_CLUSTER 7
+
+#define CHUNK_SIZE 1024
+
+/*
+ * The entire ASIX Sigma DRAM is an array of struct sigma_dram_line[1024];
+ */
+
+/* One "DRAM cluster" contains a timestamp and 7 samples, 16b total. */
+struct sigma_dram_cluster {
+ uint8_t timestamp_lo;
+ uint8_t timestamp_hi;
+ struct {
+ uint8_t sample_hi;
+ uint8_t sample_lo;
+ } samples[7];
+};
+
+/* One "DRAM line" contains 64 "DRAM clusters", 1024b total. */
+struct sigma_dram_line {
+ struct sigma_dram_cluster cluster[64];
+};
+
+struct clockselect_50 {
+ uint8_t async;
+ uint8_t fraction;
+ uint16_t disabled_channels;
+};
+
+/* The effect of all these are still a bit unclear. */
+struct triggerinout {
+ uint8_t trgout_resistor_enable : 1;
+ uint8_t trgout_resistor_pullup : 1;
+ uint8_t reserved1 : 1;
+ uint8_t trgout_bytrigger : 1;
+ uint8_t trgout_byevent : 1;
+ uint8_t trgout_bytriggerin : 1;
+ uint8_t reserved2 : 2;
+
+ /* Should be set same as the first two */
+ uint8_t trgout_resistor_enable2 : 1;
+ uint8_t trgout_resistor_pullup2 : 1;
+
+ uint8_t reserved3 : 1;
+ uint8_t trgout_long : 1;
+ uint8_t trgout_pin : 1; /* Use 1k resistor. Pullup? */
+ uint8_t trgin_negate : 1;
+ uint8_t trgout_enable : 1;
+ uint8_t trgin_enable : 1;
+};
+
+struct triggerlut {
+ /* The actual LUTs. */
+ uint16_t m0d[4], m1d[4], m2d[4];
+ uint16_t m3, m3s, m4;
+
+ /* Paramters should be sent as a single register write. */
+ struct {
+ uint8_t selc : 2;
+ uint8_t selpresc : 6;
+
+ uint8_t selinc : 2;
+ uint8_t selres : 2;
+ uint8_t sela : 2;
+ uint8_t selb : 2;
+
+ uint16_t cmpb;
+ uint16_t cmpa;
+ } params;
+};
+
+/* Trigger configuration */
+struct sigma_trigger {
+ /* Only two channels can be used in mask. */
+ uint16_t risingmask;
+ uint16_t fallingmask;
+
+ /* Simple trigger support (<= 50 MHz). */
+ uint16_t simplemask;
+ uint16_t simplevalue;
+
+ /* TODO: Advanced trigger support (boolean expressions). */
+};
+
+/* Events for trigger operation. */
+enum triggerop {
+ OP_LEVEL = 1,
+ OP_NOT,
+ OP_RISE,
+ OP_FALL,
+ OP_RISEFALL,
+ OP_NOTRISE,
+ OP_NOTFALL,
+ OP_NOTRISEFALL,
+};
+
+/* Logical functions for trigger operation. */
+enum triggerfunc {
+ FUNC_AND = 1,
+ FUNC_NAND,
+ FUNC_OR,
+ FUNC_NOR,
+ FUNC_XOR,
+ FUNC_NXOR,
+};
+
+struct sigma_state {
+ enum {
+ SIGMA_UNINITIALIZED = 0,
+ SIGMA_IDLE,
+ SIGMA_CAPTURE,
+ SIGMA_DOWNLOAD,
+ } state;
+
+ uint16_t lastts;
+ uint16_t lastsample;
+};
+
+/* Private, per-device-instance driver context. */
+struct dev_context {
+ struct ftdi_context ftdic;
+ uint64_t cur_samplerate;
+ uint64_t period_ps;
+ uint64_t limit_msec;
+ struct timeval start_tv;
+ int cur_firmware;
+ int num_channels;
+ int cur_channels;
+ int samples_per_event;
+ int capture_ratio;
+ struct sigma_trigger trigger;
+ int use_triggers;
+ struct sigma_state state;
+ void *cb_data;
+};
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <string.h>
+#include <errno.h>
+#include "protocol.h"
+
+/*
+ * The default serial communication settings on the device are 9600
+ * baud, 9 data bits. The 9th bit isn't actually used, and the vendor
+ * software uses Mark parity to absorb the extra bit.
+ *
+ * Since 9 data bits is not a standard available in POSIX, we use two
+ * stop bits to skip over the extra bit instead.
+ */
+#define SERIALCOMM "9600/8n2"
+
+static const int32_t scanopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t devopts[] = {
+ SR_CONF_POWER_SUPPLY,
+ SR_CONF_CONTINUOUS,
+ SR_CONF_OUTPUT_CHANNEL,
+ SR_CONF_OVER_CURRENT_PROTECTION,
+};
+
+static const int32_t devopts_cg[] = {
+ SR_CONF_OUTPUT_VOLTAGE,
+ SR_CONF_OUTPUT_VOLTAGE_MAX,
+ SR_CONF_OUTPUT_CURRENT,
+ SR_CONF_OUTPUT_CURRENT_MAX,
+ SR_CONF_OUTPUT_ENABLED,
+};
+
+static const char *channel_modes[] = {
+ "Independent",
+ "Series",
+ "Parallel",
+};
+
+static struct pps_model models[] = {
+ { PPS_3203T_3S, "PPS3203T-3S",
+ CHANMODE_INDEPENDENT | CHANMODE_SERIES | CHANMODE_PARALLEL,
+ 3,
+ {
+ /* Channel 1 */
+ { { 0, 32, 0.01 }, { 0, 3, 0.001 } },
+ /* Channel 2 */
+ { { 0, 32, 0.01 }, { 0, 3, 0.001 } },
+ /* Channel 3 */
+ { { 0, 6, 0.01 }, { 0, 3, 0.001 } },
+ },
+ },
+};
+
+
+SR_PRIV struct sr_dev_driver atten_pps3203_driver_info;
+static struct sr_dev_driver *di = &atten_pps3203_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options, int modelid)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_config *src;
+ struct sr_channel *ch;
+ struct sr_channel_group *cg;
+ struct sr_serial_dev_inst *serial;
+ GSList *l, *devices;
+ struct pps_model *model;
+ uint8_t packet[PACKET_SIZE];
+ unsigned int i;
+ int ret;
+ const char *conn, *serialcomm;
+ char channel[10];
+
+ devices = NULL;
+ drvc = di->priv;
+ drvc->instances = NULL;
+
+ 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 (!conn)
+ return NULL;
+ if (!serialcomm)
+ serialcomm = SERIALCOMM;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+ serial_flush(serial);
+
+ /* This is how the vendor software channels for hardware. */
+ memset(packet, 0, PACKET_SIZE);
+ packet[0] = 0xaa;
+ packet[1] = 0xaa;
+ if (serial_write(serial, packet, PACKET_SIZE) == -1) {
+ sr_err("Unable to write while probing for hardware: %s",
+ strerror(errno));
+ return NULL;
+ }
+ /* The device responds with a 24-byte packet when it receives a packet.
+ * At 9600 baud, 300ms is long enough for it to have arrived. */
+ g_usleep(300 * 1000);
+ memset(packet, 0, PACKET_SIZE);
+ if ((ret = serial_read_nonblocking(serial, packet, PACKET_SIZE)) < 0) {
+ sr_err("Unable to read while probing for hardware: %s",
+ strerror(errno));
+ return NULL;
+ }
+ if (ret != PACKET_SIZE || packet[0] != 0xaa || packet[1] != 0xaa) {
+ /* Doesn't look like an Atten PPS. */
+ return NULL;
+ }
+
+ model = NULL;
+ for (i = 0; i < ARRAY_SIZE(models); i++) {
+ if (models[i].modelid == modelid) {
+ model = &models[i];
+ break;
+ }
+ }
+ if (!model) {
+ sr_err("Unknown modelid %d", modelid);
+ return NULL;
+ }
+
+ sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Atten", model->name, NULL);
+ sdi->driver = di;
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ snprintf(channel, 10, "CH%d", i + 1);
+ ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, channel);
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ cg = g_malloc(sizeof(struct sr_channel_group));
+ cg->name = g_strdup(channel);
+ cg->channels = g_slist_append(NULL, ch);
+ cg->priv = NULL;
+ sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+ }
+
+ devc = g_malloc0(sizeof(struct dev_context));
+ devc->model = model;
+ devc->config = g_malloc0(sizeof(struct per_channel_config) * model->num_channels);
+ sdi->priv = devc;
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ serial_close(serial);
+ if (!devices)
+ sr_serial_dev_inst_free(serial);
+
+ return devices;
+}
+
+static GSList *scan_3203(GSList *options)
+{
+ return scan(options, PPS_3203T_3S);
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ int channel, ret;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ ret = SR_OK;
+ if (!cg) {
+ /* No channel group: global options. */
+ switch (key) {
+ case SR_CONF_OUTPUT_CHANNEL:
+ *data = g_variant_new_string(channel_modes[devc->channel_mode]);
+ break;
+ case SR_CONF_OVER_CURRENT_PROTECTION:
+ *data = g_variant_new_boolean(devc->over_current_protection);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ } else {
+ /* We only ever have one channel per channel group in this driver. */
+ ch = cg->channels->data;
+ channel = ch->index;
+
+ switch (key) {
+ case SR_CONF_OUTPUT_VOLTAGE:
+ *data = g_variant_new_double(devc->config[channel].output_voltage_last);
+ break;
+ case SR_CONF_OUTPUT_VOLTAGE_MAX:
+ *data = g_variant_new_double(devc->config[channel].output_voltage_max);
+ break;
+ case SR_CONF_OUTPUT_CURRENT:
+ *data = g_variant_new_double(devc->config[channel].output_current_last);
+ break;
+ case SR_CONF_OUTPUT_CURRENT_MAX:
+ *data = g_variant_new_double(devc->config[channel].output_current_max);
+ break;
+ case SR_CONF_OUTPUT_ENABLED:
+ *data = g_variant_new_boolean(devc->config[channel].output_enabled);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ }
+
+ return ret;
+}
+
+static int find_str(const char *str, const char **strings, int array_size)
+{
+ int idx, i;
+
+ idx = -1;
+ for (i = 0; i < array_size; i++) {
+ if (!strcmp(str, strings[i])) {
+ idx = i;
+ break;
+ }
+ }
+
+ return idx;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ gdouble dval;
+ int channel, ret, ival;
+ const char *sval;
+ gboolean bval;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ ret = SR_OK;
+ devc = sdi->priv;
+ if (!cg) {
+ /* No channel group: global options. */
+ switch (key) {
+ case SR_CONF_OUTPUT_CHANNEL:
+ sval = g_variant_get_string(data, NULL);
+ if ((ival = find_str(sval, channel_modes,
+ ARRAY_SIZE(channel_modes))) == -1) {
+ ret = SR_ERR_ARG;
+ break;
+ }
+ if (devc->model->channel_modes && (1 << ival) == 0) {
+ /* Not supported on this model. */
+ ret = SR_ERR_ARG;
+ }
+ if (ival == devc->channel_mode_set)
+ /* Nothing to do. */
+ break;
+ devc->channel_mode_set = ival;
+ devc->config_dirty = TRUE;
+ break;
+ case SR_CONF_OVER_CURRENT_PROTECTION:
+ bval = g_variant_get_boolean(data);
+ if (bval == devc->over_current_protection_set)
+ /* Nothing to do. */
+ break;
+ devc->over_current_protection_set = bval;
+ devc->config_dirty = TRUE;
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ } else {
+ /* Channel group specified: per-channel options. */
+ /* We only ever have one channel per channel group in this driver. */
+ ch = cg->channels->data;
+ channel = ch->index;
+
+ switch (key) {
+ case SR_CONF_OUTPUT_VOLTAGE_MAX:
+ dval = g_variant_get_double(data);
+ if (dval < 0 || dval > devc->model->channels[channel].voltage[1])
+ ret = SR_ERR_ARG;
+ devc->config[channel].output_voltage_max = dval;
+ devc->config_dirty = TRUE;
+ break;
+ case SR_CONF_OUTPUT_CURRENT_MAX:
+ dval = g_variant_get_double(data);
+ if (dval < 0 || dval > devc->model->channels[channel].current[1])
+ ret = SR_ERR_ARG;
+ devc->config[channel].output_current_max = dval;
+ devc->config_dirty = TRUE;
+ break;
+ case SR_CONF_OUTPUT_ENABLED:
+ bval = g_variant_get_boolean(data);
+ if (bval == devc->config[channel].output_enabled_set)
+ /* Nothing to do. */
+ break;
+ devc->config[channel].output_enabled_set = bval;
+ devc->config_dirty = TRUE;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+ }
+
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ GVariant *gvar;
+ GVariantBuilder gvb;
+ int channel, ret, i;
+
+ /* Always available, even without sdi. */
+ if (key == SR_CONF_SCAN_OPTIONS) {
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ scanopts, ARRAY_SIZE(scanopts), sizeof(int32_t));
+ return SR_OK;
+ }
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+
+ ret = SR_OK;
+ if (!cg) {
+ /* No channel group: global options. */
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
+ break;
+ case SR_CONF_OUTPUT_CHANNEL:
+ if (devc->model->channel_modes == CHANMODE_INDEPENDENT) {
+ /* The 1-channel models. */
+ *data = g_variant_new_strv(channel_modes, 1);
+ } else {
+ /* The other models support all modes. */
+ *data = g_variant_new_strv(channel_modes, ARRAY_SIZE(channel_modes));
+ }
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ } else {
+ /* Channel group specified: per-channel options. */
+ if (!sdi)
+ return SR_ERR_ARG;
+ /* We only ever have one channel per channel group in this driver. */
+ ch = cg->channels->data;
+ channel = ch->index;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(int32_t));
+ break;
+ case SR_CONF_OUTPUT_VOLTAGE_MAX:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ /* Min, max, step. */
+ for (i = 0; i < 3; i++) {
+ gvar = g_variant_new_double(devc->model->channels[channel].voltage[i]);
+ g_variant_builder_add_value(&gvb, gvar);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_OUTPUT_CURRENT_MAX:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ /* Min, max, step. */
+ for (i = 0; i < 3; i++) {
+ gvar = g_variant_new_double(devc->model->channels[channel].current[i]);
+ g_variant_builder_add_value(&gvb, gvar);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ }
+
+ return ret;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+ if (devc->config_dirty)
+ /* Some configuration changes were queued up but didn't
+ * get sent to the device, likely because we were never
+ * in acquisition mode. Send them out now. */
+ send_config(sdi);
+
+ return std_serial_dev_close(sdi);
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ uint8_t packet[PACKET_SIZE];
+
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ memset(devc->packet, 0x44, PACKET_SIZE);
+ devc->packet_size = 0;
+
+ devc->acquisition_running = TRUE;
+
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 50,
+ atten_pps3xxx_receive_data, (void *)sdi);
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Send a "channel" configuration packet now. */
+ memset(packet, 0, PACKET_SIZE);
+ packet[0] = 0xaa;
+ packet[1] = 0xaa;
+ send_packet(sdi, packet);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ devc->acquisition_running = FALSE;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver atten_pps3203_driver_info = {
+ .name = "atten-pps3203",
+ .longname = "Atten PPS3203T-3S",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan_3203,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = std_serial_dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <string.h>
+#include <errno.h>
+#include "protocol.h"
+
+static void dump_packet(char *msg, uint8_t *packet)
+{
+ int i;
+ char str[128];
+
+ str[0] = 0;
+ for (i = 0; i < PACKET_SIZE; i++)
+ sprintf(str + strlen(str), "%.2x ", packet[i]);
+ sr_dbg("%s: %s", msg, str);
+
+}
+
+static void handle_packet(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ float value, data[MAX_CHANNELS];
+ int offset, i;
+
+ devc = sdi->priv;
+ dump_packet("received", devc->packet);
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+
+ analog.mq = SR_MQ_VOLTAGE;
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags = SR_MQFLAG_DC;
+ analog.data = data;
+ for (i = 0; i < devc->model->num_channels; i++) {
+ offset = 2 + i * 4;
+ value = ((devc->packet[offset] << 8) + devc->packet[offset + 1]) / 100.0;
+ analog.data[i] = value;
+ devc->config[i].output_voltage_last = value;
+ }
+ sr_session_send(sdi, &packet);
+
+ analog.mq = SR_MQ_CURRENT;
+ analog.unit = SR_UNIT_AMPERE;
+ analog.mqflags = 0;
+ analog.data = data;
+ for (i = 0; i < devc->model->num_channels; i++) {
+ offset = 4 + i * 4;
+ value = ((devc->packet[offset] << 8) + devc->packet[offset + 1]) / 1000.0;
+ analog.data[i] = value;
+ devc->config[i].output_current_last = value;
+ }
+ sr_session_send(sdi, &packet);
+
+ for (i = 0; i < devc->model->num_channels; i++)
+ devc->config[i].output_enabled = (devc->packet[15] & (1 << i)) ? TRUE : FALSE;
+
+ devc->over_current_protection = devc->packet[18] ? TRUE : FALSE;
+ if (devc->packet[19] < 3)
+ devc->channel_mode = devc->packet[19];
+
+}
+
+SR_PRIV void send_packet(const struct sr_dev_inst *sdi, uint8_t *packet)
+{
+ struct sr_serial_dev_inst *serial;
+
+ serial = sdi->conn;
+ if (serial_write(serial, packet, PACKET_SIZE) == -1)
+ sr_dbg("Failed to send packet: %s", strerror(errno));
+ dump_packet("sent", packet);
+}
+
+SR_PRIV void send_config(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ uint8_t packet[PACKET_SIZE];
+ int value, offset, i;
+
+ devc = sdi->priv;
+ memset(packet, 0, PACKET_SIZE);
+ packet[0] = 0xaa;
+ packet[1] = 0x20;
+ packet[14] = 0x01;
+ packet[16] = 0x01;
+ for (i = 0; i < devc->model->num_channels; i++) {
+ offset = 2 + i * 4;
+ value = devc->config[i].output_voltage_max * 100;
+ packet[offset] = (value >> 8) & 0xff;
+ packet[offset + 1] = value & 0xff;
+ value = devc->config[i].output_current_max * 1000;
+ packet[offset + 2] = (value >> 8) & 0xff;
+ packet[offset + 3] = value & 0xff;
+ if (devc->config[i].output_enabled_set)
+ packet[15] |= 1 << i;
+ }
+ packet[18] = devc->over_current_protection_set ? 1 : 0;
+ packet[19] = devc->channel_mode_set;
+ /* Checksum. */
+ value = 0;
+ for (i = 0; i < PACKET_SIZE - 1; i++)
+ value += packet[i];
+ packet[i] = value & 0xff;
+ send_packet(sdi, packet);
+ devc->config_dirty = FALSE;
+
+}
+
+SR_PRIV int atten_pps3xxx_receive_data(int fd, int revents, void *cb_data)
+{
+ struct dev_context *devc;
+ const struct sr_dev_inst *sdi;
+ struct sr_serial_dev_inst *serial;
+ struct sr_datafeed_packet packet;
+ unsigned char c;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+ if (revents == G_IO_IN) {
+ if (serial_read_nonblocking(serial, &c, 1) < 0)
+ return TRUE;
+ devc->packet[devc->packet_size++] = c;
+ if (devc->packet_size == PACKET_SIZE) {
+ handle_packet(sdi);
+ devc->packet_size = 0;
+ if (devc->acquisition_running)
+ send_config(sdi);
+ else {
+ serial_source_remove(sdi->session, serial);
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_ATTEN_PPS3XXX_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_ATTEN_PPS3XXX_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "atten-pps3xxx"
+
+/* Packets to/from the device. */
+#define PACKET_SIZE 24
+
+enum {
+ PPS_3203T_3S,
+ PPS_3203T_2S,
+ PPS_3205T_3S,
+ PPS_3205T_2S,
+ PPS_3003S,
+ PPS_3005S,
+};
+
+/* Maximum number of output channels handled by this driver. */
+#define MAX_CHANNELS 3
+
+#define CHANMODE_INDEPENDENT 1 << 0
+#define CHANMODE_SERIES 1 << 1
+#define CHANMODE_PARALLEL 1 << 2
+
+struct channel_spec {
+ /* Min, max, step. */
+ gdouble voltage[3];
+ gdouble current[3];
+};
+
+struct pps_model {
+ int modelid;
+ char *name;
+ int channel_modes;
+ int num_channels;
+ struct channel_spec channels[MAX_CHANNELS];
+};
+
+struct per_channel_config {
+ /* Received from device. */
+ gdouble output_voltage_last;
+ gdouble output_current_last;
+ gboolean output_enabled;
+ /* Set by frontend. */
+ gdouble output_voltage_max;
+ gdouble output_current_max;
+ gboolean output_enabled_set;
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Model-specific information */
+ struct pps_model *model;
+
+ /* Acquisition state */
+ gboolean acquisition_running;
+
+ /* Operational state */
+ gboolean config_dirty;
+ struct per_channel_config *config;
+ /* Received from device. */
+ int channel_mode;
+ gboolean over_current_protection;
+ /* Set by frontend. */
+ int channel_mode_set;
+ gboolean over_current_protection_set;
+
+ /* Temporary state across callbacks */
+ uint8_t packet[PACKET_SIZE];
+ int packet_size;
+
+};
+
+SR_PRIV int atten_pps3xxx_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV void send_packet(const struct sr_dev_inst *sdi, uint8_t *packet);
+SR_PRIV void send_config(const struct sr_dev_inst *sdi);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.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 "protocol.h"
+#include "beaglelogic.h"
+
+SR_PRIV struct sr_dev_driver beaglelogic_driver_info;
+static struct sr_dev_driver *di = &beaglelogic_driver_info;
+
+/* Hardware capabiities */
+static const int32_t hwcaps[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_TRIGGER_MATCH,
+
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_CONTINUOUS,
+
+ SR_CONF_NUM_LOGIC_CHANNELS,
+};
+
+/* Trigger matching capabilities */
+static const int32_t soft_trigger_matches[] = {
+ SR_TRIGGER_ZERO,
+ SR_TRIGGER_ONE,
+ SR_TRIGGER_RISING,
+ SR_TRIGGER_FALLING,
+ SR_TRIGGER_EDGE,
+};
+
+/* Channels are numbered 0-13 */
+SR_PRIV const char *beaglelogic_channel_names[NUM_CHANNELS + 1] = {
+ "P8_45", "P8_46", "P8_43", "P8_44", "P8_41", "P8_42", "P8_39", "P8_40",
+ "P8_27", "P8_29", "P8_28", "P8_30", "P8_21", "P8_20", NULL,
+};
+
+/* Possible sample rates : 10 Hz to 100 MHz = (100 / x) MHz */
+static const uint64_t samplerates[] = {
+ SR_HZ(10),
+ SR_MHZ(100),
+ SR_HZ(1),
+};
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static struct dev_context * beaglelogic_devc_alloc(void)
+{
+ struct dev_context *devc;
+
+ /* Allocate zeroed structure */
+ devc = g_try_malloc0(sizeof(*devc));
+
+ /* Default non-zero values (if any) */
+ devc->fd = -1;
+ devc->limit_samples = (uint64_t)-1;
+
+ return devc;
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ GSList *devices, *l;
+ struct sr_config *src;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ int i, maxch;
+
+ devices = NULL;
+ drvc = di->priv;
+ drvc->instances = NULL;
+
+ /* Probe for /dev/beaglelogic */
+ if (!g_file_test(BEAGLELOGIC_DEV_NODE, G_FILE_TEST_EXISTS))
+ return NULL;
+
+ sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, NULL, "BeagleLogic", "1.0");
+ sdi->driver = di;
+
+ /* Unless explicitly specified, keep max channels to 8 only */
+ maxch = 8;
+ for (l = options; l; l = l->next) {
+ src = l->data;
+ if (src->key == SR_CONF_NUM_LOGIC_CHANNELS)
+ maxch = g_variant_get_int32(src->data);
+ }
+
+ /* We need to test for number of channels by opening the node */
+ devc = beaglelogic_devc_alloc();
+
+ if (beaglelogic_open_nonblock(devc) != SR_OK) {
+ g_free(devc);
+ sr_dev_inst_free(sdi);
+
+ return NULL;
+ }
+
+ if (maxch > 8) {
+ maxch = NUM_CHANNELS;
+ devc->sampleunit = BL_SAMPLEUNIT_16_BITS;
+ } else {
+ maxch = 8;
+ devc->sampleunit = BL_SAMPLEUNIT_8_BITS;
+ }
+
+ beaglelogic_set_sampleunit(devc);
+ beaglelogic_close(devc);
+
+ /* Signal */
+ sr_info("BeagleLogic device found at "BEAGLELOGIC_DEV_NODE);
+
+ /* Fill the channels */
+ for (i = 0; i < maxch; i++) {
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
+ beaglelogic_channel_names[i])))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ sdi->priv = devc;
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+
+ /* Open BeagleLogic */
+ if (beaglelogic_open_nonblock(devc))
+ return SR_ERR;
+
+ /* Set fd and local attributes */
+ devc->pollfd.fd = devc->fd;
+ devc->pollfd.events = G_IO_IN;
+
+ /* Get the default attributes */
+ beaglelogic_get_samplerate(devc);
+ beaglelogic_get_sampleunit(devc);
+ beaglelogic_get_triggerflags(devc);
+ beaglelogic_get_buffersize(devc);
+ beaglelogic_get_bufunitsize(devc);
+
+ /* Map the kernel capture FIFO for reads, saves 1 level of memcpy */
+ if (beaglelogic_mmap(devc) != SR_OK) {
+ sr_err("Unable to map capture buffer");
+ beaglelogic_close(devc);
+ return SR_ERR;
+ }
+
+ /* We're good to go now */
+ sdi->status = SR_ST_ACTIVE;
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+
+ if (sdi->status == SR_ST_ACTIVE) {
+ /* Close the memory mapping and the file */
+ beaglelogic_munmap(devc);
+ beaglelogic_close(devc);
+ }
+ sdi->status = SR_ST_INACTIVE;
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ struct drv_context *drvc;
+ struct sr_dev_inst *sdi;
+ GSList *l;
+
+ /* unused driver */
+ if (!(drvc = di->priv))
+ return SR_OK;
+
+ /* Clean up the instances */
+ for (l = drvc->instances; l; l = l->next) {
+ sdi = l->data;
+ di->dev_close(sdi);
+ g_free(sdi->priv);
+ sr_dev_inst_free(sdi);
+ }
+ g_slist_free(drvc->instances);
+ drvc->instances = NULL;
+
+ di->priv = NULL;
+
+ return SR_OK;
+}
+
+static int config_get(int 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_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+
+ case SR_CONF_SAMPLERATE:
+ *data = g_variant_new_uint64(devc->cur_samplerate);
+ break;
+
+ case SR_CONF_NUM_LOGIC_CHANNELS:
+ *data = g_variant_new_uint32(g_slist_length(sdi->channels));
+ break;
+
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc = sdi->priv;
+ uint64_t tmp_u64;
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ switch (key) {
+ case SR_CONF_SAMPLERATE:
+ devc->cur_samplerate = g_variant_get_uint64(data);
+ return beaglelogic_set_samplerate(devc);
+
+ case SR_CONF_LIMIT_SAMPLES:
+ tmp_u64 = g_variant_get_uint64(data);
+ devc->limit_samples = tmp_u64;
+ devc->triggerflags = BL_TRIGGERFLAGS_ONESHOT;
+
+ /* Check if we have sufficient buffer size */
+ tmp_u64 *= SAMPLEUNIT_TO_BYTES(devc->sampleunit);
+ if (tmp_u64 > devc->buffersize) {
+ sr_warn("Insufficient buffer space has been allocated.");
+ sr_warn("Please use \'echo <size in bytes> > "\
+ BEAGLELOGIC_SYSFS_ATTR(memalloc) \
+ "\' as root to increase the buffer size, this"\
+ " capture is now truncated to %d Msamples",
+ devc->buffersize /
+ (SAMPLEUNIT_TO_BYTES(devc->sampleunit) * 1000000));
+ }
+ return beaglelogic_set_triggerflags(devc);
+
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ int ret;
+ GVariant *gvar;
+ GVariantBuilder gvb;
+
+ (void)sdi;
+ (void)data;
+ (void)cg;
+
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
+ samplerates, ARRAY_SIZE(samplerates), sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ soft_trigger_matches, ARRAY_SIZE(soft_trigger_matches),
+ sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+/* get a sane timeout for poll() */
+#define BUFUNIT_TIMEOUT_MS(devc) (100 + ((devc->bufunitsize * 1000) / \
+ (uint32_t)(devc->cur_samplerate)))
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ (void)cb_data;
+ struct dev_context *devc = sdi->priv;
+ struct sr_trigger *trigger;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ /* Save user pointer */
+ devc->cb_data = cb_data;
+
+ /* Clear capture state */
+ devc->bytes_read = 0;
+ devc->offset = 0;
+
+ /* Configure channels */
+ devc->sampleunit = g_slist_length(sdi->channels) > 8 ?
+ BL_SAMPLEUNIT_16_BITS : BL_SAMPLEUNIT_8_BITS;
+ beaglelogic_set_sampleunit(devc);
+
+ /* Configure triggers & send header packet */
+ if ((trigger = sr_session_trigger_get(sdi->session))) {
+ devc->stl = soft_trigger_logic_new(sdi, trigger);
+ devc->trigger_fired = FALSE;
+ } else
+ devc->trigger_fired = TRUE;
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Trigger and add poll on file */
+ beaglelogic_start(devc);
+ sr_session_source_add_pollfd(sdi->session, &devc->pollfd,
+ BUFUNIT_TIMEOUT_MS(devc), beaglelogic_receive_data,
+ (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc = sdi->priv;
+ struct sr_datafeed_packet pkt;
+
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ /* Execute a stop on BeagleLogic */
+ beaglelogic_stop(devc);
+
+ /* lseek to offset 0, flushes the cache */
+ lseek(devc->fd, 0, SEEK_SET);
+
+ /* Remove session source and send EOT packet */
+ sr_session_source_remove_pollfd(sdi->session, &devc->pollfd);
+ pkt.type = SR_DF_END;
+ pkt.payload = NULL;
+ sr_session_send(sdi, &pkt);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver beaglelogic_driver_info = {
+ .name = "beaglelogic",
+ .longname = "BeagleLogic",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = 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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.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 BEAGLELOGIC_H_
+#define BEAGLELOGIC_H_
+
+#include <fcntl.h>
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+
+#include <stdlib.h>
+
+#include <unistd.h>
+
+/* BeagleLogic device node name */
+#define BEAGLELOGIC_DEV_NODE "/dev/beaglelogic"
+#define BEAGLELOGIC_SYSFS_ATTR(a) "/sys/devices/virtual/misc/beaglelogic/"\
+ __STRING(a)
+
+/* Reproduced verbatim from beaglelogic.h in the kernel tree until the kernel
+ * module hits the mainline. Contains the ABI, so DO NOT TOUCH this section */
+
+/* ioctl calls that can be issued on /dev/beaglelogic */
+#define IOCTL_BL_GET_VERSION _IOR('k', 0x20, uint32_t)
+
+#define IOCTL_BL_GET_SAMPLE_RATE _IOR('k', 0x21, uint32_t)
+#define IOCTL_BL_SET_SAMPLE_RATE _IOW('k', 0x21, uint32_t)
+
+#define IOCTL_BL_GET_SAMPLE_UNIT _IOR('k', 0x22, uint32_t)
+#define IOCTL_BL_SET_SAMPLE_UNIT _IOW('k', 0x22, uint32_t)
+
+#define IOCTL_BL_GET_TRIGGER_FLAGS _IOR('k', 0x23, uint32_t)
+#define IOCTL_BL_SET_TRIGGER_FLAGS _IOW('k', 0x23, uint32_t)
+
+#define IOCTL_BL_CACHE_INVALIDATE _IO('k', 0x25)
+
+#define IOCTL_BL_GET_BUFFER_SIZE _IOR('k', 0x26, uint32_t)
+#define IOCTL_BL_SET_BUFFER_SIZE _IOW('k', 0x26, uint32_t)
+
+#define IOCTL_BL_GET_BUFUNIT_SIZE _IOR('k', 0x27, uint32_t)
+
+#define IOCTL_BL_FILL_TEST_PATTERN _IO('k', 0x28)
+
+#define IOCTL_BL_START _IO('k', 0x29)
+#define IOCTL_BL_STOP _IO('k', 0x2A)
+
+/* Possible States of BeagleLogic */
+enum beaglelogic_states {
+ STATE_BL_DISABLED, /* Powered off (at module start) */
+ STATE_BL_INITIALIZED, /* Powered on */
+ STATE_BL_MEMALLOCD, /* Buffers allocated */
+ STATE_BL_ARMED, /* All Buffers DMA-mapped and configuration done */
+ STATE_BL_RUNNING, /* Data being captured */
+ STATE_BL_REQUEST_STOP, /* Stop requested */
+ STATE_BL_ERROR /* Buffer overrun */
+};
+
+/* Setting attributes */
+enum beaglelogic_triggerflags {
+ BL_TRIGGERFLAGS_ONESHOT = 0,
+ BL_TRIGGERFLAGS_CONTINUOUS
+};
+
+/* Possible sample unit / formats */
+enum beaglelogic_sampleunit {
+ BL_SAMPLEUNIT_16_BITS = 0,
+ BL_SAMPLEUNIT_8_BITS
+};
+/* END beaglelogic.h */
+
+/* For all the functions below:
+ * Parameters:
+ * devc : Device context structure to operate on
+ * Returns:
+ * SR_OK or SR_ERR
+ */
+
+SR_PRIV int beaglelogic_open_nonblock(struct dev_context *devc);
+SR_PRIV int beaglelogic_close(struct dev_context *devc);
+
+SR_PRIV int beaglelogic_get_buffersize(struct dev_context *devc);
+SR_PRIV int beaglelogic_set_buffersize(struct dev_context *devc);
+
+SR_PRIV int beaglelogic_get_samplerate(struct dev_context *devc);
+SR_PRIV int beaglelogic_set_samplerate(struct dev_context *devc);
+
+SR_PRIV int beaglelogic_get_sampleunit(struct dev_context *devc);
+SR_PRIV int beaglelogic_set_sampleunit(struct dev_context *devc);
+
+SR_PRIV int beaglelogic_get_triggerflags(struct dev_context *devc);
+SR_PRIV int beaglelogic_set_triggerflags(struct dev_context *devc);
+
+/* Start and stop the capture operation */
+SR_PRIV int beaglelogic_start(struct dev_context *devc);
+SR_PRIV int beaglelogic_stop(struct dev_context *devc);
+
+/* Get the last error size */
+SR_PRIV int beaglelogic_getlasterror(struct dev_context *devc);
+
+/* Gets the unit size of the capture buffer (usually 4 or 8 MB) */
+SR_PRIV int beaglelogic_get_bufunitsize(struct dev_context *devc);
+
+SR_PRIV int beaglelogic_mmap(struct dev_context *devc);
+SR_PRIV int beaglelogic_munmap(struct dev_context *devc);
+
+/* Sources */
+SR_PRIV inline int beaglelogic_open_nonblock(struct dev_context *devc) {
+ devc->fd = open(BEAGLELOGIC_DEV_NODE, O_RDONLY | O_NONBLOCK);
+ return (devc->fd == -1 ? SR_ERR : SR_OK);
+}
+
+SR_PRIV inline int beaglelogic_close(struct dev_context *devc) {
+ return close(devc->fd);
+}
+
+SR_PRIV inline int beaglelogic_get_buffersize(struct dev_context *devc) {
+ return ioctl(devc->fd, IOCTL_BL_GET_BUFFER_SIZE, &devc->buffersize);
+}
+
+SR_PRIV inline int beaglelogic_set_buffersize(struct dev_context *devc) {
+ return ioctl(devc->fd, IOCTL_BL_SET_BUFFER_SIZE, devc->buffersize);
+}
+
+/* This is treated differently as it gets a uint64_t while a uint32_t is read */
+SR_PRIV inline int beaglelogic_get_samplerate(struct dev_context *devc) {
+ uint32_t arg, err;
+ err = ioctl(devc->fd, IOCTL_BL_GET_SAMPLE_RATE, &arg);
+ devc->cur_samplerate = arg;
+ return err;
+}
+
+SR_PRIV inline int beaglelogic_set_samplerate(struct dev_context *devc) {
+ return ioctl(devc->fd, IOCTL_BL_SET_SAMPLE_RATE,
+ (uint32_t)devc->cur_samplerate);
+}
+
+SR_PRIV inline int beaglelogic_get_sampleunit(struct dev_context *devc) {
+ return ioctl(devc->fd, IOCTL_BL_GET_SAMPLE_UNIT, &devc->sampleunit);
+}
+
+SR_PRIV inline int beaglelogic_set_sampleunit(struct dev_context *devc) {
+ return ioctl(devc->fd, IOCTL_BL_SET_SAMPLE_UNIT, devc->sampleunit);
+}
+
+SR_PRIV inline int beaglelogic_get_triggerflags(struct dev_context *devc) {
+ return ioctl(devc->fd, IOCTL_BL_GET_TRIGGER_FLAGS, &devc->triggerflags);
+}
+
+SR_PRIV inline int beaglelogic_set_triggerflags(struct dev_context *devc) {
+ return ioctl(devc->fd, IOCTL_BL_SET_TRIGGER_FLAGS, devc->triggerflags);
+}
+
+SR_PRIV int beaglelogic_getlasterror(struct dev_context *devc) {
+ int fd;
+ char buf[16];
+ int ret;
+
+ if ((fd = open(BEAGLELOGIC_SYSFS_ATTR(lasterror), O_RDONLY)) == -1)
+ return SR_ERR;
+
+ if ((ret = read(fd, buf, 16)) < 0)
+ return SR_ERR;
+
+ close(fd);
+ devc->last_error = strtoul(buf, NULL, 10);
+
+ return SR_OK;
+}
+
+SR_PRIV inline int beaglelogic_start(struct dev_context *devc) {
+ return ioctl(devc->fd, IOCTL_BL_START);
+}
+
+SR_PRIV inline int beaglelogic_stop(struct dev_context *devc) {
+ return ioctl(devc->fd, IOCTL_BL_STOP);
+}
+
+SR_PRIV int beaglelogic_get_bufunitsize(struct dev_context *devc) {
+ return ioctl(devc->fd, IOCTL_BL_GET_BUFUNIT_SIZE, &devc->bufunitsize);
+}
+
+SR_PRIV int beaglelogic_mmap(struct dev_context *devc) {
+ if (!devc->buffersize)
+ beaglelogic_get_buffersize(devc);
+ devc->sample_buf = mmap(NULL, devc->buffersize,
+ PROT_READ, MAP_SHARED, devc->fd, 0);
+ return (devc->sample_buf == MAP_FAILED ? -1 : SR_OK);
+}
+
+SR_PRIV int beaglelogic_munmap(struct dev_context *devc) {
+ return munmap(devc->sample_buf, devc->buffersize);
+}
+
+#endif /* BEAGLELOGIC_H_ */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.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 "protocol.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Define data packet size independent of packet (bufunitsize bytes) size
+ * from the BeagleLogic kernel module */
+#define PACKET_SIZE (512 * 1024)
+
+/* This implementation is zero copy from the libsigrok side.
+ * It does not copy any data, just passes a pointer from the mmap'ed
+ * kernel buffers appropriately. It is up to the application which is
+ * using libsigrok to decide how to deal with the data.
+ */
+SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data)
+{
+ const struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+
+ int trigger_offset;
+ uint32_t packetsize;
+ uint64_t bytes_remaining;
+
+ if (!(sdi = cb_data) || !(devc = sdi->priv))
+ return TRUE;
+
+ packetsize = PACKET_SIZE;
+ logic.unitsize = SAMPLEUNIT_TO_BYTES(devc->sampleunit);
+
+ if (revents == G_IO_IN) {
+ sr_info("In callback G_IO_IN, offset=%d", devc->offset);
+
+ bytes_remaining = (devc->limit_samples * logic.unitsize) -
+ devc->bytes_read;
+
+ /* Configure data packet */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.data = devc->sample_buf + devc->offset;
+ logic.length = MIN(packetsize, bytes_remaining);
+
+ if (devc->trigger_fired) {
+ /* Send the incoming transfer to the session bus. */
+ sr_session_send(devc->cb_data, &packet);
+ } else {
+ /* Check for trigger */
+ trigger_offset = soft_trigger_logic_check(devc->stl,
+ logic.data,
+ packetsize);
+
+ if (trigger_offset > -1) {
+ trigger_offset *= logic.unitsize;
+ logic.length = MIN(packetsize - trigger_offset,
+ bytes_remaining);
+ logic.data += trigger_offset;
+
+ sr_session_send(devc->cb_data, &packet);
+
+ devc->trigger_fired = TRUE;
+ }
+ }
+
+ /* Move the read pointer forward */
+ lseek(fd, packetsize, SEEK_CUR);
+
+ /* Update byte count and offset (roll over if needed) */
+ devc->bytes_read += logic.length;
+ if ((devc->offset += packetsize) >= devc->buffersize) {
+ /* One shot capture, we abort and settle with less than
+ * the required number of samples */
+ if (devc->triggerflags)
+ devc->offset = 0;
+ else
+ packetsize = 0;
+ }
+ }
+
+ /* EOF Received or we have reached the limit */
+ if (devc->bytes_read >= devc->limit_samples * logic.unitsize ||
+ packetsize == 0) {
+ /* Send EOA Packet, stop polling */
+ packet.type = SR_DF_END;
+ packet.payload = NULL;
+ sr_session_send(devc->cb_data, &packet);
+
+ sr_session_source_remove_pollfd(sdi->session, &devc->pollfd);
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.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_BEAGLELOGIC_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_BEAGLELOGIC_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "beaglelogic"
+
+/* Maximum possible input channels */
+#define NUM_CHANNELS 14
+
+#define SAMPLEUNIT_TO_BYTES(x) ((x) == 1 ? 1 : 2)
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Model-specific information */
+ int max_channels;
+ uint32_t fw_ver;
+
+ /* Acquisition settings: see beaglelogic.h */
+ uint64_t cur_samplerate;
+ uint64_t limit_samples;
+ uint32_t sampleunit;
+ uint32_t triggerflags;
+
+ /* Buffers: size of each buffer block and the total buffer area */
+ uint32_t bufunitsize;
+ uint32_t buffersize;
+
+ /* Operational state */
+ int fd;
+ GPollFD pollfd;
+ int last_error;
+
+ uint64_t bytes_read;
+ uint64_t sent_samples;
+ uint32_t offset;
+ uint8_t *sample_buf; /* mmap'd kernel buffer here */
+
+ void *cb_data;
+
+ /* Trigger logic */
+ struct soft_trigger_logic *stl;
+ gboolean trigger_fired;
+};
+
+SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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 "protocol.h"
+
+#define BRYMEN_BC86X "0820.0001"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+SR_PRIV struct sr_dev_driver brymen_bm86x_driver_info;
+static struct sr_dev_driver *di = &brymen_bm86x_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ GSList *usb_devices, *devices, *l;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_usb_dev_inst *usb;
+ struct sr_config *src;
+ struct sr_channel *ch;
+ const char *conn;
+
+ drvc = di->priv;
+ drvc->instances = NULL;
+
+ conn = BRYMEN_BC86X;
+ 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;
+ }
+ }
+
+ devices = NULL;
+ if (!(usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn))) {
+ g_slist_free_full(usb_devices, g_free);
+ return NULL;
+ }
+
+ for (l = usb_devices; l; l = l->next) {
+ usb = l->data;
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
+ "Brymen", "BM869", NULL))) {
+ sr_err("sr_dev_inst_new returned NULL.");
+ return NULL;
+ }
+
+ if (!(devc = g_try_malloc0(sizeof(*devc)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+
+ sdi->priv = devc;
+ sdi->driver = di;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P2")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = usb;
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ }
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc = di->priv;
+ struct sr_usb_dev_inst *usb;
+ struct dev_context *devc;
+ int ret;
+
+ usb = sdi->conn;
+ devc = sdi->priv;
+
+ if ((ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb)) == SR_OK)
+ sdi->status = SR_ST_ACTIVE;
+
+ /* Detach kernel drivers which grabbed this device (if any). */
+ if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
+ ret = libusb_detach_kernel_driver(usb->devhdl, 0);
+ if (ret < 0) {
+ sr_err("Failed to detach kernel driver: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ devc->detached_kernel_driver = 1;
+ sr_dbg("Successfully detached kernel driver.");
+ } else {
+ sr_dbg("No need to detach a kernel driver.");
+ }
+
+ /* Claim interface 0. */
+ if ((ret = libusb_claim_interface(usb->devhdl, 0)) < 0) {
+ sr_err("Failed to claim interface 0: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sr_dbg("Successfully claimed interface 0.");
+
+ return ret;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ struct dev_context *devc;
+ int ret;
+
+ usb = sdi->conn;
+ devc = sdi->priv;
+
+ if ((ret = libusb_release_interface(usb->devhdl, 0)))
+ sr_err("Failed to release interface 0: %s.\n", libusb_error_name(ret));
+ else
+ sr_dbg("Successfully released interface 0.\n");
+
+ if (!ret && devc->detached_kernel_driver) {
+ if ((ret = libusb_attach_kernel_driver(usb->devhdl, 0))) {
+ sr_err("Failed to attach kernel driver: %s.\n",
+ libusb_error_name(ret));
+ } else {
+ devc->detached_kernel_driver = 0;
+ sr_dbg("Successfully attached kernel driver.\n");
+ }
+ }
+
+ libusb_close(usb->devhdl);
+
+ sdi->status = SR_ST_INACTIVE;
+
+ return ret;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_get(int 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_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ *data = g_variant_new_uint64(devc->limit_msec);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".", devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.", devc->limit_msec);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct dev_context *devc;
+
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ devc->start_time = g_get_monotonic_time();
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(sdi, LOG_PREFIX);
+
+ sr_session_source_add(sdi->session, 0, 0, 10,
+ brymen_bm86x_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct sr_datafeed_packet packet;
+
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ /* Send end packet to the session bus. */
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+
+ sr_session_source_remove(sdi->session, 0);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver brymen_bm86x_driver_info = {
+ .name = "brymen-bm86x",
+ .longname = "Brymen BM86X",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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 <string.h>
+#include <math.h>
+#include "protocol.h"
+
+#define USB_TIMEOUT 500
+
+static char char_map[128] = {
+ [0x20] = '-',
+ [0x5F] = '0',
+ [0x50] = '1',
+ [0x6D] = '2',
+ [0x7C] = '3',
+ [0x72] = '4',
+ [0x3E] = '5',
+ [0x3F] = '6',
+ [0x54] = '7',
+ [0x7F] = '8',
+ [0x7E] = '9',
+ [0x0F] = 'C',
+ [0x27] = 'F',
+ [0x0B] = 'L',
+ [0x79] = 'd',
+ [0x10] = 'i',
+ [0x39] = 'o',
+};
+
+static int brymen_bm86x_parse_digits(const unsigned char *buf, int length,
+ char *str, float *floatval,
+ char *temp_unit, int flag)
+{
+ char c, *p = str;
+ int i, ret;
+
+ if (buf[0] & flag)
+ *p++ = '-';
+ for (i = 0; i < length; i++) {
+ if (i && i < 5 && buf[i+1] & 0x01)
+ *p++ = '.';
+ c = char_map[buf[i+1] >> 1];
+ if (i == 5 && (c == 'C' || c == 'F'))
+ *temp_unit = c;
+ else if (c)
+ *p++ = c;
+ }
+ *p = 0;
+
+ if ((ret = sr_atof_ascii(str, floatval))) {
+ sr_dbg("invalid float string: '%s'", str);
+ return ret;
+ }
+
+ return SR_OK;
+}
+
+static void brymen_bm86x_parse(unsigned char *buf, float *floatval,
+ struct sr_datafeed_analog *analog)
+{
+ char str[16], temp_unit;
+ int ret1, ret2, over_limit;
+
+ ret1 = brymen_bm86x_parse_digits(buf+2, 6, str, &floatval[0],
+ &temp_unit, 0x80);
+ over_limit = strstr(str, "0L") || strstr(str, "0.L");
+ ret2 = brymen_bm86x_parse_digits(buf+9, 4, str, &floatval[1],
+ &temp_unit, 0x10);
+
+ /* main display */
+ if (ret1 == SR_OK || over_limit) {
+ /* SI unit */
+ if (buf[8] & 0x01) {
+ analog[0].mq = SR_MQ_VOLTAGE;
+ analog[0].unit = SR_UNIT_VOLT;
+ if (!strcmp(str, "diod"))
+ analog[0].mqflags |= SR_MQFLAG_DIODE;
+ } else if (buf[14] & 0x80) {
+ analog[0].mq = SR_MQ_CURRENT;
+ analog[0].unit = SR_UNIT_AMPERE;
+ } else if (buf[14] & 0x20) {
+ analog[0].mq = SR_MQ_CAPACITANCE;
+ analog[0].unit = SR_UNIT_FARAD;
+ } else if (buf[14] & 0x10) {
+ analog[0].mq = SR_MQ_CONDUCTANCE;
+ analog[0].unit = SR_UNIT_SIEMENS;
+ } else if (buf[15] & 0x01) {
+ analog[0].mq = SR_MQ_FREQUENCY;
+ analog[0].unit = SR_UNIT_HERTZ;
+ } else if (buf[10] & 0x01) {
+ analog[0].mq = SR_MQ_CONTINUITY;
+ analog[0].unit = SR_UNIT_OHM;
+ } else if (buf[15] & 0x10) {
+ analog[0].mq = SR_MQ_RESISTANCE;
+ analog[0].unit = SR_UNIT_OHM;
+ } else if (buf[15] & 0x02) {
+ analog[0].mq = SR_MQ_POWER;
+ analog[0].unit = SR_UNIT_DECIBEL_MW;
+ } else if (buf[15] & 0x80) {
+ analog[0].mq = SR_MQ_DUTY_CYCLE;
+ analog[0].unit = SR_UNIT_PERCENTAGE;
+ } else if (buf[ 2] & 0x0A) {
+ analog[0].mq = SR_MQ_TEMPERATURE;
+ if (temp_unit == 'F')
+ analog[0].unit = SR_UNIT_FAHRENHEIT;
+ else
+ analog[0].unit = SR_UNIT_CELSIUS;
+ }
+
+ /* when MIN MAX and AVG are displayed at the same time, remove them */
+ if ((buf[1] & 0xE0) == 0xE0)
+ buf[1] &= ~0xE0;
+
+ /* AC/DC/Auto flags */
+ if (buf[1] & 0x10) analog[0].mqflags |= SR_MQFLAG_DC;
+ if (buf[2] & 0x01) analog[0].mqflags |= SR_MQFLAG_AC;
+ if (buf[1] & 0x01) analog[0].mqflags |= SR_MQFLAG_AUTORANGE;
+ if (buf[1] & 0x08) analog[0].mqflags |= SR_MQFLAG_HOLD;
+ if (buf[1] & 0x20) analog[0].mqflags |= SR_MQFLAG_MAX;
+ if (buf[1] & 0x40) analog[0].mqflags |= SR_MQFLAG_MIN;
+ if (buf[1] & 0x80) analog[0].mqflags |= SR_MQFLAG_AVG;
+ if (buf[3] & 0x01) analog[0].mqflags |= SR_MQFLAG_RELATIVE;
+
+ /* when dBm is displayed, remove the m suffix so that it is
+ not considered as the 10e-3 SI prefix */
+ if (buf[15] & 0x02)
+ buf[15] &= ~0x04;
+
+ /* SI prefix */
+ if (buf[14] & 0x40) floatval[0] *= 1e-9; /* n */
+ if (buf[15] & 0x08) floatval[0] *= 1e-6; /* µ */
+ if (buf[15] & 0x04) floatval[0] *= 1e-3; /* m */
+ if (buf[15] & 0x40) floatval[0] *= 1e3; /* k */
+ if (buf[15] & 0x20) floatval[0] *= 1e6; /* M */
+
+ if (over_limit) floatval[0] = INFINITY;
+ }
+
+ /* secondary display */
+ if (ret2 == SR_OK) {
+ /* SI unit */
+ if (buf[14] & 0x08) {
+ analog[1].mq = SR_MQ_VOLTAGE;
+ analog[1].unit = SR_UNIT_VOLT;
+ } else if (buf[9] & 0x04) {
+ analog[1].mq = SR_MQ_CURRENT;
+ analog[1].unit = SR_UNIT_AMPERE;
+ } else if (buf[14] & 0x04) {
+ analog[1].mq = SR_MQ_FREQUENCY;
+ analog[1].unit = SR_UNIT_HERTZ;
+ } else if (buf[9] & 0x40) {
+ analog[1].mq = SR_MQ_TEMPERATURE;
+ if (temp_unit == 'F')
+ analog[1].unit = SR_UNIT_FAHRENHEIT;
+ else
+ analog[1].unit = SR_UNIT_CELSIUS;
+ }
+
+ /* AC flag */
+ if (buf[9] & 0x20) analog[1].mqflags |= SR_MQFLAG_AC;
+
+ /* SI prefix */
+ if (buf[ 9] & 0x01) floatval[1] *= 1e-6; /* µ */
+ if (buf[ 9] & 0x02) floatval[1] *= 1e-3; /* m */
+ if (buf[14] & 0x02) floatval[1] *= 1e3; /* k */
+ if (buf[14] & 0x01) floatval[1] *= 1e6; /* M */
+ }
+
+ if (buf[9] & 0x80)
+ sr_spew("Battery is low.");
+}
+
+static void brymen_bm86x_handle_packet(const struct sr_dev_inst *sdi,
+ unsigned char *buf)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog[2];
+ float floatval[2];
+
+ devc = sdi->priv;
+
+ analog[0].mq = -1;
+ analog[0].mqflags = 0;
+
+ analog[1].mq = -1;
+ analog[1].mqflags = 0;
+
+ brymen_bm86x_parse(buf, floatval, analog);
+
+ if (analog[0].mq != -1) {
+ /* Got a measurement. */
+ analog[0].num_samples = 1;
+ analog[0].data = &floatval[0];
+ analog[0].channels = g_slist_append(NULL, sdi->channels->data);
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog[0];
+ sr_session_send(sdi, &packet);
+ g_slist_free(analog[0].channels);
+ }
+
+ if (analog[1].mq != -1) {
+ /* Got a measurement. */
+ analog[1].num_samples = 1;
+ analog[1].data = &floatval[1];
+ analog[1].channels = g_slist_append(NULL, sdi->channels->next->data);
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog[1];
+ sr_session_send(sdi, &packet);
+ g_slist_free(analog[1].channels);
+ }
+
+ if (analog[0].mq != -1 || analog[1].mq != -1)
+ devc->num_samples++;
+}
+
+static int brymen_bm86x_send_command(const struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ unsigned char buf[] = { 0x00, 0x86, 0x66 };
+ int ret;
+
+ usb = sdi->conn;
+
+ sr_dbg("Sending HID set report.");
+ ret = libusb_control_transfer(usb->devhdl,
+ LIBUSB_REQUEST_TYPE_CLASS |
+ LIBUSB_RECIPIENT_INTERFACE |
+ LIBUSB_ENDPOINT_OUT,
+ 9, /* bRequest: HID set_report */
+ 0x300, /* wValue: HID feature, report num 0 */
+ 0, /* wIndex: interface 0 */
+ buf, sizeof(buf), USB_TIMEOUT);
+
+ if (ret < 0) {
+ sr_err("HID feature report error: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if (ret != sizeof(buf)) {
+ sr_err("Short packet: sent %d/%ld bytes.", ret, sizeof(buf));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int brymen_bm86x_read_interrupt(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ unsigned char buf[24];
+ int ret, transferred;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ sr_dbg("Reading HID interrupt report.");
+ /* Get data from EP1 using an interrupt transfer. */
+ ret = libusb_interrupt_transfer(usb->devhdl,
+ LIBUSB_ENDPOINT_IN | 1, /* EP1, IN */
+ buf, sizeof(buf),
+ &transferred, USB_TIMEOUT);
+
+ if (ret == LIBUSB_ERROR_TIMEOUT) {
+ if (++devc->interrupt_pending > 3)
+ devc->interrupt_pending = 0;
+ return SR_OK;
+ }
+
+ if (ret < 0) {
+ sr_err("USB receive error: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if (transferred != sizeof(buf)) {
+ sr_err("Short packet: received %d/%d bytes.", transferred, sizeof(buf));
+ return SR_ERR;
+ }
+
+ devc->interrupt_pending = 0;
+ brymen_bm86x_handle_packet(sdi, buf);
+
+ return SR_OK;
+}
+
+SR_PRIV int brymen_bm86x_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ int64_t time;
+
+ (void)fd;
+ (void)revents;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ if (!devc->interrupt_pending) {
+ if (brymen_bm86x_send_command(sdi))
+ return FALSE;
+ devc->interrupt_pending = 1;
+ }
+
+ if (brymen_bm86x_read_interrupt(sdi))
+ return FALSE;
+
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sr_info("Requested number of samples reached, stopping.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+
+ if (devc->limit_msec) {
+ time = (g_get_monotonic_time() - devc->start_time) / 1000;
+ if (time > (int64_t)devc->limit_msec) {
+ sr_info("Requested time limit reached, stopping.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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_BRYMEN_BM86X_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_BRYMEN_BM86X_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "brymen-bm86x"
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Acquisition settings */
+ uint64_t limit_samples; /**< The sampling limit (in number of samples).*/
+ uint64_t limit_msec; /**< The time limit (in milliseconds). */
+
+ /* Operational state */
+ int detached_kernel_driver;/**< Whether kernel driver was detached or not */
+ uint64_t num_samples; /**< The number of already received samples. */
+ int64_t start_time; /**< The time at which sampling started. */
+
+ /* Temporary state across callbacks */
+ int interrupt_pending;
+};
+
+SR_PRIV int brymen_bm86x_receive_data(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * 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 "protocol.h"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_MSEC,
+};
+
+SR_PRIV struct sr_dev_driver brymen_bm857_driver_info;
+static struct sr_dev_driver *di = &brymen_bm857_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *brymen_scan(const char *conn, const char *serialcomm)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ struct sr_channel *ch;
+ struct sr_serial_dev_inst *serial;
+ GSList *devices;
+ int ret;
+ uint8_t buf[128];
+ size_t len;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ sr_info("Probing port %s.", conn);
+
+ devices = NULL;
+
+ /* Request reading */
+ if ((ret = brymen_packet_request(serial)) < 0) {
+ sr_err("Unable to send command: %d.", ret);
+ goto scan_cleanup;
+ }
+
+ len = 128;
+ ret = brymen_stream_detect(serial, buf, &len, brymen_packet_length,
+ brymen_packet_is_valid, 1000, 9600);
+ if (ret != SR_OK)
+ goto scan_cleanup;
+
+ sr_info("Found device on port %s.", conn);
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Brymen", "BM85x", NULL)))
+ goto scan_cleanup;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ goto scan_cleanup;
+ }
+
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+ drvc = di->priv;
+ sdi->priv = devc;
+ sdi->driver = di;
+
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
+ goto scan_cleanup;
+
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+scan_cleanup:
+ serial_close(serial);
+
+ return devices;
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct sr_config *src;
+ GSList *devices, *l;
+ const char *conn, *serialcomm;
+
+ devices = NULL;
+ drvc = di->priv;
+ drvc->instances = NULL;
+
+ 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 (!conn)
+ return NULL;
+
+ if (serialcomm) {
+ /* Use the provided comm specs. */
+ devices = brymen_scan(conn, serialcomm);
+ } else {
+ /* But 9600/8n1 should work all of the time. */
+ devices = brymen_scan(conn, "9600/8n1/dtr=1/rts=1");
+ }
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ int ret;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ ret = SR_OK;
+ switch (id) {
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ devc->limit_msec = g_variant_get_uint64(data);
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ devc->cb_data = cb_data;
+
+ /*
+ * Reset the number of samples to take. If we've already collected our
+ * quota, but we start a new session, and don't reset this, we'll just
+ * quit without acquiring any new samples.
+ */
+ devc->num_samples = 0;
+ devc->starttime = g_get_monotonic_time();
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Poll every 50ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 50,
+ brymen_dmm_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver brymen_bm857_driver_info = {
+ .name = "brymen-bm857",
+ .longname = "Brymen BM857",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .config_get = NULL,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = std_serial_dev_open,
+ .dev_close = std_serial_dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * 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 "protocol.h"
+
+#define MAX_PACKET_LEN 22
+
+/* Flags passed from the DMM. */
+struct brymen_flags {
+ gboolean is_low_batt, is_decibel, is_duty_cycle, is_hertz, is_amp;
+ gboolean is_beep, is_ohm, is_fahrenheit, is_celsius, is_capacitance;
+ gboolean is_diode, is_volt, is_dc, is_ac;
+};
+
+struct bm850_command {
+ uint8_t dle;
+ uint8_t stx;
+ uint8_t cmd;
+ uint8_t arg[2];
+ uint8_t checksum;
+ uint8_t dle2;
+ uint8_t etx;
+};
+
+struct brymen_header {
+ uint8_t dle;
+ uint8_t stx;
+ uint8_t cmd;
+ uint8_t len;
+};
+
+struct brymen_tail {
+ uint8_t checksum;
+ uint8_t dle;
+ uint8_t etx;
+};
+
+/*
+ * We only have one command because we only support the BM-857. However, the
+ * driver is easily extensible to support more models, as the protocols are
+ * very similar.
+ */
+enum {
+ BM_CMD_REQUEST_READING = 0x00,
+};
+
+static int bm_send_command(uint8_t command, uint8_t arg1, uint8_t arg2,
+ struct sr_serial_dev_inst *serial)
+{
+ struct bm850_command cmdout;
+ int written;
+
+ cmdout.dle = 0x10;
+ cmdout.stx = 0x02;
+ cmdout.cmd = command;
+ cmdout.arg[0] = arg1;
+ cmdout.arg[1] = arg2;
+ cmdout.checksum = arg1 ^ arg2;
+ cmdout.dle2 = 0x10;
+ cmdout.etx = 0x03;
+
+ /* TODO: How to compute the checksum? Hardware seems to ignore it. */
+
+ /* Request reading. */
+ written = serial_write(serial, &cmdout, sizeof(cmdout));
+ if (written != sizeof(cmdout))
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+SR_PRIV int brymen_packet_request(struct sr_serial_dev_inst *serial)
+{
+ return bm_send_command(BM_CMD_REQUEST_READING, 0, 0, serial);
+}
+
+SR_PRIV int brymen_packet_length(const uint8_t *buf, int *len)
+{
+ struct brymen_header *hdr;
+ int packet_len;
+ size_t buflen;
+
+ buflen = *len;
+ hdr = (void *)buf;
+
+ /* Did we receive a complete header yet? */
+ if (buflen < sizeof(*hdr))
+ return PACKET_NEED_MORE_DATA;
+
+ if (hdr->dle != 0x10 || hdr->stx != 0x02)
+ return PACKET_INVALID_HEADER;
+
+ /* Our packet includes the header, the payload, and the tail. */
+ packet_len = sizeof(*hdr) + hdr->len + sizeof(struct brymen_tail);
+
+ /* In case we pick up an invalid header, limit our search. */
+ if (packet_len > MAX_PACKET_LEN) {
+ sr_spew("Header specifies an invalid payload length: %i.",
+ hdr->len);
+ return PACKET_INVALID_HEADER;
+ }
+
+ *len = packet_len;
+ sr_spew("Expecting a %d-byte packet.", *len);
+ return PACKET_HEADER_OK;
+}
+
+SR_PRIV gboolean brymen_packet_is_valid(const uint8_t *buf)
+{
+ struct brymen_header *hdr;
+ struct brymen_tail *tail;
+ int i;
+ uint8_t chksum = 0;
+ uint8_t *payload;
+
+ payload = (uint8_t *)(buf + sizeof(struct brymen_header));
+
+ hdr = (void *)buf;
+ tail = (void *)(payload + hdr->len);
+
+ for (i = 0; i< hdr->len; i++)
+ chksum ^= payload[i];
+
+ if (tail->checksum != chksum) {
+ sr_dbg("Packet has invalid checksum 0x%.2x. Expected 0x%.2x.",
+ chksum, tail->checksum);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int parse_value(const char *strbuf, int len, float *floatval)
+{
+ int s, d;
+ char str[32];
+
+ if (strstr(strbuf, "OL")) {
+ sr_dbg("Overlimit.");
+ *floatval = INFINITY;
+ return SR_OK;
+ }
+
+ memset(str, 0, sizeof(str));
+ /* Spaces may interfere with parsing the exponent. Strip them. */
+ for (s = 0, d = 0; s < len; s++) {
+ if (strbuf[s] != ' ')
+ str[d++] = strbuf[s];
+ }
+ if (sr_atof_ascii(str, floatval) != SR_OK)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static void parse_flags(const uint8_t *buf, struct brymen_flags *info)
+{
+ info->is_low_batt = (buf[4 + 3] & (1 << 7)) != 0;
+
+ info->is_decibel = (buf[4 + 1] & (1 << 5)) != 0;
+ info->is_duty_cycle = (buf[4 + 1] & (1 << 3)) != 0;
+ info->is_hertz = (buf[4 + 1] & (1 << 2)) != 0;
+ info->is_amp = (buf[4 + 1] & (1 << 1)) != 0;
+ info->is_beep = (buf[4 + 1] & (1 << 0)) != 0;
+
+ info->is_ohm = (buf[4 + 0] & (1 << 7)) != 0;
+ info->is_fahrenheit = (buf[4 + 0] & (1 << 6)) != 0;
+ info->is_celsius = (buf[4 + 0] & (1 << 5)) != 0;
+ info->is_diode = (buf[4 + 0] & (1 << 4)) != 0;
+ info->is_capacitance = (buf[4 + 0] & (1 << 3)) != 0;
+ info->is_volt = (buf[4 + 0] & (1 << 2)) != 0;
+ info->is_dc = (buf[4 + 0] & (1 << 1)) != 0;
+ info->is_ac = (buf[4 + 0] & (1 << 0)) != 0;
+}
+
+SR_PRIV int brymen_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ struct brymen_flags flags;
+ struct brymen_header *hdr;
+ uint8_t *bfunc;
+ int asciilen;
+
+ (void)info;
+
+ hdr = (void *)buf;
+ bfunc = (uint8_t *)(buf + sizeof(struct brymen_header));
+
+ analog->mqflags = 0;
+
+ /* Give some debug info about the package. */
+ asciilen = hdr->len - 4;
+ sr_dbg("DMM flags: %.2x %.2x %.2x %.2x",
+ bfunc[3], bfunc[2], bfunc[1], bfunc[0]);
+ /* Value is an ASCII string. */
+ sr_dbg("DMM packet: \"%.*s\"", asciilen, bfunc + 4);
+
+ parse_flags(buf, &flags);
+ if (parse_value((const char *)(bfunc + 4), asciilen, floatval) != SR_OK)
+ return SR_ERR;
+
+ if (flags.is_volt) {
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ }
+ if (flags.is_amp) {
+ analog->mq = SR_MQ_CURRENT;
+ analog->unit = SR_UNIT_AMPERE;
+ }
+ if (flags.is_ohm) {
+ if (flags.is_beep)
+ analog->mq = SR_MQ_CONTINUITY;
+ else
+ analog->mq = SR_MQ_RESISTANCE;
+ analog->unit = SR_UNIT_OHM;
+ }
+ if (flags.is_hertz) {
+ analog->mq = SR_MQ_FREQUENCY;
+ analog->unit = SR_UNIT_HERTZ;
+ }
+ if (flags.is_duty_cycle) {
+ analog->mq = SR_MQ_DUTY_CYCLE;
+ analog->unit = SR_UNIT_PERCENTAGE;
+ }
+ if (flags.is_capacitance) {
+ analog->mq = SR_MQ_CAPACITANCE;
+ analog->unit = SR_UNIT_FARAD;
+ }
+ if (flags.is_fahrenheit) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_FAHRENHEIT;
+ }
+ if (flags.is_celsius) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ analog->unit = SR_UNIT_CELSIUS;
+ }
+ if (flags.is_capacitance) {
+ analog->mq = SR_MQ_CAPACITANCE;
+ analog->unit = SR_UNIT_FARAD;
+ }
+
+ /*
+ * The high-end Brymen models have a configurable reference impedance.
+ * When the reference impedance is changed, the DMM sends one packet
+ * with the value of the new reference impedance. Both decibel and ohm
+ * flags are set in this case, so we must be careful to correctly
+ * identify the value as ohm, not dBmW.
+ */
+ if (flags.is_decibel && !flags.is_ohm) {
+ analog->mq = SR_MQ_POWER;
+ analog->unit = SR_UNIT_DECIBEL_MW;
+ /*
+ * For some reason, dBm measurements are sent by the multimeter
+ * with a value three orders of magnitude smaller than the
+ * displayed value.
+ */
+ *floatval *= 1000;
+ }
+
+ if (flags.is_diode)
+ analog->mqflags |= SR_MQFLAG_DIODE;
+ /* We can have both AC+DC in a single measurement. */
+ if (flags.is_ac)
+ analog->mqflags |= SR_MQFLAG_AC;
+ if (flags.is_dc)
+ analog->mqflags |= SR_MQFLAG_DC;
+
+ if (flags.is_low_batt)
+ sr_info("Low battery!");
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * 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 "protocol.h"
+
+static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi)
+{
+ float floatval;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+
+ devc = sdi->priv;
+
+ analog.num_samples = 1;
+ analog.mq = -1;
+
+ if (brymen_parse(buf, &floatval, &analog, NULL) != SR_OK)
+ return;
+ analog.data = &floatval;
+
+ analog.channels = sdi->channels;
+
+ if (analog.mq != -1) {
+ /* Got a measurement. */
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+ devc->num_samples++;
+ }
+}
+
+static void handle_new_data(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int len, status, offset = 0;
+ struct sr_serial_dev_inst *serial;
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+
+ /* Try to get as much data as the buffer can hold. */
+ len = DMM_BUFSIZE - devc->buflen;
+ len = serial_read(serial, devc->buf + devc->buflen, len);
+ if (len < 1) {
+ sr_err("Serial port read error: %d.", len);
+ return;
+ }
+ devc->buflen += len;
+ status = PACKET_INVALID_HEADER;
+
+ /* Now look for packets in that data. */
+ while (status != PACKET_NEED_MORE_DATA) {
+ /* We don't have a header, look for one. */
+ if (devc->next_packet_len == 0) {
+ len = devc->buflen - offset;
+ status = brymen_packet_length(devc->buf + offset, &len);
+ if (status == PACKET_HEADER_OK) {
+ /* We know how large the packet will be. */
+ devc->next_packet_len = len;
+ } else if (status == PACKET_NEED_MORE_DATA) {
+ /* We didn't yet receive the full header. */
+ devc->next_packet_len = 0;
+ break;
+ } else {
+ /* Invalid header. Move on. */
+ devc->next_packet_len = 0;
+ offset++;
+ continue;
+ }
+ }
+
+ /* We know how the packet size, but did we receive all of it? */
+ if (devc->buflen - offset < devc->next_packet_len)
+ break;
+
+ /* We should have a full packet here, so we can check it. */
+ if (brymen_packet_is_valid(devc->buf + offset)) {
+ handle_packet(devc->buf + offset, sdi);
+ offset += devc->next_packet_len;
+ } else {
+ offset++;
+ }
+
+ /* We are done with this packet. Look for a new one. */
+ devc->next_packet_len = 0;
+ }
+
+ /* If we have any data left, move it to the beginning of our buffer. */
+ memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
+ devc->buflen -= offset;
+}
+
+SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int ret;
+ int64_t time;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+
+ if (revents == G_IO_IN) {
+ /* Serial data arrived. */
+ handle_new_data(sdi);
+ } else {
+ /* Timeout, send another packet request. */
+ if ((ret = brymen_packet_request(serial)) < 0) {
+ sr_err("Failed to request packet: %d.", ret);
+ return FALSE;
+ }
+ }
+
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sr_info("Requested number of samples reached, stopping.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+
+ if (devc->limit_msec) {
+ time = (g_get_monotonic_time() - devc->starttime) / 1000;
+ if (time > (int64_t)devc->limit_msec) {
+ sr_info("Requested time limit reached, stopping.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * Try to find a valid packet in a serial data stream.
+ *
+ * @param serial Previously initialized serial port structure.
+ * @param buf Buffer containing the bytes to write.
+ * @param buflen Size of the buffer.
+ * @param get_packet_size Callback that assesses the size of incoming packets.
+ * @param is_valid Callback that assesses whether the packet is valid or not.
+ * @param timeout_ms The timeout after which, if no packet is detected, to
+ * abort scanning.
+ * @param baudrate The baudrate of the serial port. This parameter is not
+ * critical, but it helps fine tune the serial port polling
+ * delay.
+ *
+ * @return SR_OK if a valid packet is found within the given timeout,
+ * SR_ERR upon failure.
+ */
+SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
+ uint8_t *buf, size_t *buflen,
+ packet_length_t get_packet_size,
+ packet_valid_callback is_valid,
+ uint64_t timeout_ms, int baudrate)
+{
+ int64_t start, time, byte_delay_us;
+ size_t ibuf, i, maxlen;
+ int status, len, packet_len, stream_len;
+
+ maxlen = *buflen;
+
+ sr_dbg("Detecting packets on %s (timeout = %" PRIu64
+ "ms, baudrate = %d).", serial->port, timeout_ms, baudrate);
+
+ /* Assume 8n1 transmission. That is 10 bits for every byte. */
+ byte_delay_us = 10 * (1000000 / baudrate);
+ start = g_get_monotonic_time();
+
+ packet_len = i = ibuf = len = 0;
+ while (ibuf < maxlen) {
+ len = serial_read(serial, &buf[ibuf], maxlen - ibuf);
+ if (len > 0) {
+ ibuf += len;
+ sr_spew("Read %d bytes.", len);
+ }
+
+ time = g_get_monotonic_time() - start;
+ time /= 1000;
+
+ stream_len = ibuf - i;
+ if (stream_len > 0 && packet_len == 0) {
+ /* How large of a packet are we expecting? */
+ packet_len = stream_len;
+ status = get_packet_size(&buf[i], &packet_len);
+ switch(status) {
+ case PACKET_HEADER_OK:
+ /* We know how much data we need to wait for. */
+ break;
+ case PACKET_NEED_MORE_DATA:
+ /* We did not receive the full header. */
+ packet_len = 0;
+ break;
+ case PACKET_INVALID_HEADER:
+ default:
+ /*
+ * We had enough data, but here was an error in
+ * parsing the header. Restart parsing from the
+ * next byte.
+ */
+ packet_len = 0;
+ i++;
+ break;
+ }
+ }
+
+ if ((stream_len >= packet_len) && (packet_len != 0)) {
+ /* We have at least a packet's worth of data. */
+ if (is_valid(&buf[i])) {
+ sr_spew("Found valid %d-byte packet after "
+ "%" PRIu64 "ms.", packet_len, time);
+ *buflen = ibuf;
+ return SR_OK;
+ } else {
+ sr_spew("Got %d bytes, but not a valid "
+ "packet.", packet_len);
+
+ }
+
+ /* Not a valid packet. Continue searching. */
+ i++;
+ packet_len = 0;
+ }
+
+ if (time >= (int64_t)timeout_ms) {
+ /* Timeout */
+ sr_dbg("Detection timed out after %dms.", time);
+ break;
+ }
+ g_usleep(byte_delay_us);
+ }
+
+ *buflen = ibuf;
+ sr_err("Didn't find a valid packet (read %d bytes).", ibuf);
+
+ return SR_ERR;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * 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_BRYMEN_DMM_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_BRYMEN_DMM_PROTOCOL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "brymen-dmm"
+
+#define DMM_BUFSIZE 256
+
+enum packet_len_status {
+ PACKET_HEADER_OK,
+ PACKET_NEED_MORE_DATA,
+ PACKET_INVALID_HEADER,
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /** The current sampling limit (in number of samples). */
+ uint64_t limit_samples;
+
+ /** The current sampling limit (in ms). */
+ uint64_t limit_msec;
+
+ /** Opaque pointer passed in by the frontend. */
+ void *cb_data;
+
+ /** The current number of already received samples. */
+ uint64_t num_samples;
+
+ /** Start time of acquisition session */
+ int64_t starttime;
+
+ uint8_t buf[DMM_BUFSIZE];
+ int bufoffset;
+ int buflen;
+ int next_packet_len;
+};
+
+/**
+ * Callback that assesses the size and status of the incoming packet.
+ *
+ * @return PACKET_HEADER_OK - This is a proper packet header.
+ * PACKET_NEED_MORE_DATA The buffer does not contain the entire header.
+ * PACKET_INVALID_HEADER Not a valid start of packet.
+ */
+typedef int (*packet_length_t)(const uint8_t *buf, int *len);
+
+SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int brymen_packet_request(struct sr_serial_dev_inst *serial);
+
+SR_PRIV int brymen_packet_length(const uint8_t *buf, int *len);
+SR_PRIV gboolean brymen_packet_is_valid(const uint8_t *buf);
+
+SR_PRIV int brymen_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+
+SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
+ uint8_t *buf, size_t *buflen,
+ packet_length_t get_packet_size,
+ packet_valid_callback is_valid,
+ uint64_t timeout_ms, int baudrate);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <string.h>
+#include "protocol.h"
+
+#define SERIALCOMM "9600/8n1"
+/* 23ms is the longest interval between tokens. */
+#define MAX_SCAN_TIME 25 * 1000
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_SOUNDLEVELMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_CONTINUOUS,
+ SR_CONF_SPL_WEIGHT_FREQ,
+ SR_CONF_SPL_WEIGHT_TIME,
+ SR_CONF_SPL_MEASUREMENT_RANGE,
+ SR_CONF_DATALOG,
+ SR_CONF_HOLD_MAX,
+ SR_CONF_HOLD_MIN,
+ SR_CONF_POWER_OFF,
+ SR_CONF_DATA_SOURCE,
+};
+
+static const char *weight_freq[] = {
+ "A",
+ "C",
+};
+
+static const char *weight_time[] = {
+ "F",
+ "S",
+};
+
+static const uint64_t meas_ranges[][2] = {
+ { 30, 130 },
+ { 30, 80 },
+ { 50, 100 },
+ { 80, 130 },
+};
+
+static const char *data_sources[] = {
+ "Live",
+ "Memory",
+};
+SR_PRIV struct sr_dev_driver cem_dt_885x_driver_info;
+static struct sr_dev_driver *di = &cem_dt_885x_driver_info;
+
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_config *src;
+ struct sr_serial_dev_inst *serial;
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ GSList *l, *devices;
+ gint64 start;
+ const char *conn;
+ unsigned char c;
+
+ conn = NULL;
+ for (l = options; l; l = l->next) {
+ src = l->data;
+ if (src->key == SR_CONF_CONN)
+ conn = g_variant_get_string(src->data, NULL);
+ }
+ if (!conn)
+ return NULL;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, SERIALCOMM)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDONLY | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ devices = NULL;
+ drvc = di->priv;
+ start = g_get_monotonic_time();
+ while (g_get_monotonic_time() - start < MAX_SCAN_TIME) {
+ if (serial_read(serial, &c, 1) == 1 && c == 0xa5) {
+ /* Found one. */
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "CEM",
+ "DT-885x", NULL)))
+ return NULL;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_dbg("Device context malloc failed.");
+ return NULL;
+ }
+ devc->cur_mqflags = 0;
+ devc->recording = -1;
+ devc->cur_meas_range = 0;
+ devc->cur_data_source = DATA_SOURCE_LIVE;
+ devc->enable_data_source_memory = FALSE;
+
+ if (!(sdi->conn = sr_serial_dev_inst_new(conn, SERIALCOMM)))
+ return NULL;
+
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->priv = devc;
+ sdi->driver = di;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "SPL")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ break;
+ }
+ /* It takes about 1ms for a byte to come in. */
+ g_usleep(1000);
+ }
+
+ serial_close(serial);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct sr_serial_dev_inst *serial;
+
+ serial = sdi->conn;
+ if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+ return SR_ERR;
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ GVariant *range[2];
+ uint64_t low, high;
+ int tmp, ret;
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_DATALOG:
+ if ((ret = cem_dt_885x_recording_get(sdi, &tmp)) == SR_OK)
+ *data = g_variant_new_boolean(tmp);
+ break;
+ case SR_CONF_SPL_WEIGHT_FREQ:
+ tmp = cem_dt_885x_weight_freq_get(sdi);
+ if (tmp == SR_MQFLAG_SPL_FREQ_WEIGHT_A)
+ *data = g_variant_new_string("A");
+ else if (tmp == SR_MQFLAG_SPL_FREQ_WEIGHT_C)
+ *data = g_variant_new_string("C");
+ else
+ return SR_ERR;
+ break;
+ case SR_CONF_SPL_WEIGHT_TIME:
+ tmp = cem_dt_885x_weight_time_get(sdi);
+ if (tmp == SR_MQFLAG_SPL_TIME_WEIGHT_F)
+ *data = g_variant_new_string("F");
+ else if (tmp == SR_MQFLAG_SPL_TIME_WEIGHT_S)
+ *data = g_variant_new_string("S");
+ else
+ return SR_ERR;
+ break;
+ case SR_CONF_HOLD_MAX:
+ if ((ret = cem_dt_885x_holdmode_get(sdi, &tmp)) == SR_OK)
+ *data = g_variant_new_boolean(tmp == SR_MQFLAG_MAX);
+ break;
+ case SR_CONF_HOLD_MIN:
+ if ((ret = cem_dt_885x_holdmode_get(sdi, &tmp)) == SR_OK)
+ *data = g_variant_new_boolean(tmp == SR_MQFLAG_MIN);
+ break;
+ case SR_CONF_SPL_MEASUREMENT_RANGE:
+ if ((ret = cem_dt_885x_meas_range_get(sdi, &low, &high)) == SR_OK) {
+ range[0] = g_variant_new_uint64(low);
+ range[1] = g_variant_new_uint64(high);
+ *data = g_variant_new_tuple(range, 2);
+ }
+ break;
+ case SR_CONF_POWER_OFF:
+ *data = g_variant_new_boolean(FALSE);
+ break;
+ case SR_CONF_DATA_SOURCE:
+ if (devc->cur_data_source == DATA_SOURCE_LIVE)
+ *data = g_variant_new_string("Live");
+ else
+ *data = g_variant_new_string("Memory");
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ uint64_t tmp_u64, low, high;
+ unsigned int i;
+ int tmp, ret;
+ const char *tmp_str;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ tmp_u64 = g_variant_get_uint64(data);
+ devc->limit_samples = tmp_u64;
+ ret = SR_OK;
+ break;
+ case SR_CONF_DATALOG:
+ ret = cem_dt_885x_recording_set(sdi, g_variant_get_boolean(data));
+ break;
+ case SR_CONF_SPL_WEIGHT_FREQ:
+ tmp_str = g_variant_get_string(data, NULL);
+ if (!strcmp(tmp_str, "A"))
+ ret = cem_dt_885x_weight_freq_set(sdi,
+ SR_MQFLAG_SPL_FREQ_WEIGHT_A);
+ else if (!strcmp(tmp_str, "C"))
+ ret = cem_dt_885x_weight_freq_set(sdi,
+ SR_MQFLAG_SPL_FREQ_WEIGHT_C);
+ else
+ return SR_ERR_ARG;
+ break;
+ case SR_CONF_SPL_WEIGHT_TIME:
+ tmp_str = g_variant_get_string(data, NULL);
+ if (!strcmp(tmp_str, "F"))
+ ret = cem_dt_885x_weight_time_set(sdi,
+ SR_MQFLAG_SPL_TIME_WEIGHT_F);
+ else if (!strcmp(tmp_str, "S"))
+ ret = cem_dt_885x_weight_time_set(sdi,
+ SR_MQFLAG_SPL_TIME_WEIGHT_S);
+ else
+ return SR_ERR_ARG;
+ break;
+ case SR_CONF_HOLD_MAX:
+ tmp = g_variant_get_boolean(data) ? SR_MQFLAG_MAX : 0;
+ ret = cem_dt_885x_holdmode_set(sdi, tmp);
+ break;
+ case SR_CONF_HOLD_MIN:
+ tmp = g_variant_get_boolean(data) ? SR_MQFLAG_MIN : 0;
+ ret = cem_dt_885x_holdmode_set(sdi, tmp);
+ break;
+ case SR_CONF_SPL_MEASUREMENT_RANGE:
+ g_variant_get(data, "(tt)", &low, &high);
+ ret = SR_ERR_ARG;
+ for (i = 0; i < ARRAY_SIZE(meas_ranges); i++) {
+ if (meas_ranges[i][0] == low && meas_ranges[i][1] == high) {
+ ret = cem_dt_885x_meas_range_set(sdi, low, high);
+ break;
+ }
+ }
+ break;
+ case SR_CONF_POWER_OFF:
+ if (g_variant_get_boolean(data))
+ ret = cem_dt_885x_power_off(sdi);
+ break;
+ case SR_CONF_DATA_SOURCE:
+ tmp_str = g_variant_get_string(data, NULL);
+ if (!strcmp(tmp_str, "Live"))
+ devc->cur_data_source = DATA_SOURCE_LIVE;
+ else if (!strcmp(tmp_str, "Memory"))
+ devc->cur_data_source = DATA_SOURCE_MEMORY;
+ else
+ return SR_ERR;
+ devc->enable_data_source_memory = devc->cur_data_source == DATA_SOURCE_MEMORY;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ GVariant *tuple, *range[2];
+ GVariantBuilder gvb;
+ unsigned int i;
+ int ret;
+
+ (void)sdi;
+ (void)cg;
+
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SPL_WEIGHT_FREQ:
+ *data = g_variant_new_strv(weight_freq, ARRAY_SIZE(weight_freq));
+ break;
+ case SR_CONF_SPL_WEIGHT_TIME:
+ *data = g_variant_new_strv(weight_time, ARRAY_SIZE(weight_time));
+ break;
+ case SR_CONF_SPL_MEASUREMENT_RANGE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < ARRAY_SIZE(meas_ranges); i++) {
+ range[0] = g_variant_new_uint64(meas_ranges[i][0]);
+ range[1] = g_variant_new_uint64(meas_ranges[i][1]);
+ tuple = g_variant_new_tuple(range, 2);
+ g_variant_builder_add_value(&gvb, tuple);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_DATA_SOURCE:
+ *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ devc->cb_data = cb_data;
+ devc->state = ST_INIT;
+ devc->num_samples = 0;
+ devc->buf_len = 0;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Poll every 100ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 150,
+ cem_dt_885x_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver cem_dt_885x_driver_info = {
+ .name = "cem-dt-885x",
+ .longname = "CEM DT-885x",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = std_serial_dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <string.h>
+#include "protocol.h"
+
+/* Length of expected payload for each token. */
+static int token_payloads[][2] = {
+ { TOKEN_WEIGHT_TIME_FAST, 0 },
+ { TOKEN_WEIGHT_TIME_SLOW, 0 },
+ { TOKEN_HOLD_MAX, 0 },
+ { TOKEN_HOLD_MIN, 0 },
+ { TOKEN_TIME, 3 },
+ { TOKEN_MEAS_RANGE_OVER, 0 },
+ { TOKEN_MEAS_RANGE_UNDER, 0 },
+ { TOKEN_STORE_FULL, 0 },
+ { TOKEN_RECORDING_ON, 0 },
+ { TOKEN_MEAS_WAS_READOUT, 1 },
+ { TOKEN_MEAS_WAS_BARGRAPH, 0 },
+ { TOKEN_MEASUREMENT, 2 },
+ { TOKEN_HOLD_NONE, 0 },
+ { TOKEN_BATTERY_LOW, 0 },
+ { TOKEN_MEAS_RANGE_OK, 0 },
+ { TOKEN_STORE_OK, 0 },
+ { TOKEN_RECORDING_OFF, 0 },
+ { TOKEN_WEIGHT_FREQ_A, 1 },
+ { TOKEN_WEIGHT_FREQ_C, 1 },
+ { TOKEN_BATTERY_OK, 0 },
+ { TOKEN_MEAS_RANGE_30_80, 0 },
+ { TOKEN_MEAS_RANGE_30_130, 0 },
+ { TOKEN_MEAS_RANGE_50_100, 0 },
+ { TOKEN_MEAS_RANGE_80_130, 0 },
+};
+
+static int find_token_payload_len(unsigned char c)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(token_payloads); i++) {
+ if (token_payloads[i][0] == c)
+ return token_payloads[i][1];
+ }
+
+ return -1;
+}
+
+/* Process measurement or setting (0xa5 command). */
+static void process_mset(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ GString *dbg;
+ float fvalue;
+ int i;
+
+ devc = sdi->priv;
+ if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+ dbg = g_string_sized_new(128);
+ g_string_printf(dbg, "got command 0x%.2x token 0x%.2x",
+ devc->cmd, devc->token);
+ if (devc->buf_len) {
+ g_string_append_printf(dbg, " payload");
+ for (i = 0; i < devc->buf_len; i++)
+ g_string_append_printf(dbg, " %.2x", devc->buf[i]);
+ }
+ sr_spew("%s", dbg->str);
+ g_string_free(dbg, TRUE);
+ }
+
+ switch(devc->token) {
+ case TOKEN_WEIGHT_TIME_FAST:
+ devc->cur_mqflags |= SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ devc->cur_mqflags &= ~SR_MQFLAG_SPL_TIME_WEIGHT_S;
+ break;
+ case TOKEN_WEIGHT_TIME_SLOW:
+ devc->cur_mqflags |= SR_MQFLAG_SPL_TIME_WEIGHT_S;
+ devc->cur_mqflags &= ~SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ break;
+ case TOKEN_WEIGHT_FREQ_A:
+ devc->cur_mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A;
+ devc->cur_mqflags &= ~SR_MQFLAG_SPL_FREQ_WEIGHT_C;
+ break;
+ case TOKEN_WEIGHT_FREQ_C:
+ devc->cur_mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C;
+ devc->cur_mqflags &= ~SR_MQFLAG_SPL_FREQ_WEIGHT_A;
+ break;
+ case TOKEN_HOLD_MAX:
+ devc->cur_mqflags |= SR_MQFLAG_HOLD | SR_MQFLAG_MAX;
+ devc->cur_mqflags &= ~SR_MQFLAG_MIN;
+ break;
+ case TOKEN_HOLD_MIN:
+ devc->cur_mqflags |= SR_MQFLAG_HOLD | SR_MQFLAG_MIN;
+ devc->cur_mqflags &= ~SR_MQFLAG_MAX;
+ break;
+ case TOKEN_HOLD_NONE:
+ devc->cur_mqflags &= ~(SR_MQFLAG_MAX | SR_MQFLAG_MIN | SR_MQFLAG_HOLD);
+ break;
+ case TOKEN_MEASUREMENT:
+ fvalue = ((devc->buf[0] & 0xf0) >> 4) * 100;
+ fvalue += (devc->buf[0] & 0x0f) * 10;
+ fvalue += ((devc->buf[1] & 0xf0) >> 4);
+ fvalue += (devc->buf[1] & 0x0f) / 10.0;
+ devc->last_spl = fvalue;
+ break;
+ case TOKEN_MEAS_WAS_READOUT:
+ case TOKEN_MEAS_WAS_BARGRAPH:
+ if (devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN)) {
+ if (devc->token == TOKEN_MEAS_WAS_BARGRAPH) {
+ /* The device still sends bargraph measurements even
+ * when in max/min hold mode. Suppress them here, unless
+ * they're readout values. This duplicates the behavior
+ * of the device display exactly. */
+ break;
+ }
+ }
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+ analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
+ analog.mqflags = devc->cur_mqflags;
+ analog.unit = SR_UNIT_DECIBEL_SPL;
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+ analog.data = &devc->last_spl;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+
+ devc->num_samples++;
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
+ sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+ devc->cb_data);
+ break;
+ case TOKEN_RECORDING_ON:
+ devc->recording = TRUE;
+ break;
+ case TOKEN_RECORDING_OFF:
+ devc->recording = FALSE;
+ break;
+ case TOKEN_MEAS_RANGE_30_80:
+ case TOKEN_MEAS_RANGE_30_130:
+ case TOKEN_MEAS_RANGE_50_100:
+ case TOKEN_MEAS_RANGE_80_130:
+ devc->cur_meas_range = devc->token;
+ break;
+ case TOKEN_TIME:
+ case TOKEN_STORE_OK:
+ case TOKEN_STORE_FULL:
+ case TOKEN_BATTERY_OK:
+ case TOKEN_BATTERY_LOW:
+ case TOKEN_MEAS_RANGE_OK:
+ case TOKEN_MEAS_RANGE_OVER:
+ case TOKEN_MEAS_RANGE_UNDER:
+ /* Not useful, or not expressable in sigrok. */
+ break;
+ }
+
+}
+
+static void send_data(const struct sr_dev_inst *sdi, unsigned char *data,
+ uint64_t num_samples)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ float fbuf[SAMPLES_PER_PACKET];
+ unsigned int i;
+
+ devc = sdi->priv;
+
+ for (i = 0; i < num_samples; i ++) {
+ fbuf[i] = ((data[i * 2] & 0xf0) >> 4) * 100;
+ fbuf[i] += (data[i * 2] & 0x0f) * 10;
+ fbuf[i] += ((data[i * 2 + 1] & 0xf0) >> 4);
+ fbuf[i] += (data[i * 2 + 1] & 0x0f) / 10.0;
+ }
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+ analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
+ analog.mqflags = devc->cur_mqflags;
+ analog.unit = SR_UNIT_DECIBEL_SPL;
+ analog.channels = sdi->channels;
+ analog.num_samples = num_samples;
+ analog.data = fbuf;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+
+ devc->num_samples += analog.num_samples;
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
+ sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+ devc->cb_data);
+
+ return;
+}
+
+static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c,
+ int handle_packets)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_meta meta;
+ struct sr_config *src;
+ gint64 cur_time;
+ int len;
+
+ if (!(devc = sdi->priv))
+ return;
+
+ if (c == 0xff) {
+ /* Device is in hold mode */
+ devc->cur_mqflags |= SR_MQFLAG_HOLD;
+
+ if (devc->hold_last_sent == 0) {
+ /* First hold notification. */
+ devc->hold_last_sent = g_get_monotonic_time();
+ /* When the device leaves hold mode, it starts from scratch. */
+ devc->state = ST_INIT;
+ } else {
+ cur_time = g_get_monotonic_time();
+ if (cur_time - devc->hold_last_sent > HOLD_REPEAT_INTERVAL) {
+ /* Force the last measurement out again. */
+ devc->cmd = 0xa5;
+ devc->token = TOKEN_MEAS_WAS_READOUT;
+ if (handle_packets)
+ process_mset(sdi);
+ devc->hold_last_sent = cur_time;
+ }
+ }
+
+ return;
+ }
+ devc->cur_mqflags &= ~SR_MQFLAG_HOLD;
+ devc->hold_last_sent = 0;
+
+ if (devc->state == ST_INIT) {
+ if (c == 0xa5) {
+ devc->cmd = c;
+ devc->token = 0x00;
+ devc->state = ST_GET_TOKEN;
+ } else if (c == 0xbb) {
+ devc->cmd = c;
+ devc->buf_len = 0;
+ devc->state = ST_GET_LOG_HEADER;
+ sr_dbg("got command 0xbb");
+ }
+ } else if (devc->state == ST_GET_TOKEN) {
+ devc->token = c;
+ devc->buf_len = 0;
+ len = find_token_payload_len(devc->token);
+ if (len == -1 || len > 0) {
+ devc->buf_len = 0;
+ devc->state = ST_GET_DATA;
+ } else {
+ if (handle_packets)
+ process_mset(sdi);
+ devc->state = ST_INIT;
+ }
+ } else if (devc->state == ST_GET_DATA) {
+ len = find_token_payload_len(devc->token);
+ if (len == -1) {
+ /* We don't know this token. */
+ sr_dbg("Unknown 0xa5 token 0x%.2x", devc->token);
+ if (c == 0xa5 || c == 0xbb) {
+ /* Looks like a new command however. */
+ if (handle_packets)
+ process_mset(sdi);
+ devc->state = ST_INIT;
+ } else {
+ devc->buf[devc->buf_len++] = c;
+ if (devc->buf_len > BUF_SIZE) {
+ /* Shouldn't happen, ignore. */
+ devc->state = ST_INIT;
+ }
+ }
+ } else {
+ devc->buf[devc->buf_len++] = c;
+ if (devc->buf_len == len) {
+ if (handle_packets)
+ process_mset(sdi);
+ devc->state = ST_INIT;
+ } else if (devc->buf_len > BUF_SIZE) {
+ /* Shouldn't happen, ignore. */
+ devc->state = ST_INIT;
+ }
+ }
+ } else if (devc->state == ST_GET_LOG_HEADER) {
+ sr_dbg("log header: 0x%.2x", c);
+ if (devc->buf_len < 2)
+ devc->buf[devc->buf_len++] = c;
+ if (devc->buf_len == 2) {
+ sr_dbg("Device says it has %d bytes stored.",
+ ((devc->buf[0] << 8) + devc->buf[1]) - 100);
+ devc->buf_len = 0;
+ devc->state = ST_GET_LOG_RECORD_META;
+ }
+ } else if (devc->state == ST_GET_LOG_RECORD_META) {
+ sr_dbg("log meta: 0x%.2x", c);
+ if (c == RECORD_END) {
+ devc->state = ST_INIT;
+ /* Stop acquisition after transferring all stored
+ * records. Otherwise the frontend would have no
+ * way to tell where stored data ends and live
+ * measurements begin. */
+ sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+ devc->cb_data);
+ } else if (c == RECORD_DATA) {
+ devc->buf_len = 0;
+ devc->state = ST_GET_LOG_RECORD_DATA;
+ } else {
+ /* RECORD_DBA/RECORD_DBC + 7 bytes of metadata */
+ devc->buf[devc->buf_len++] = c;
+ if (devc->buf_len < 8)
+ /* Keep filling up the record header. */
+ return;
+ if (devc->buf[0] == RECORD_DBA)
+ devc->cur_mqflags = SR_MQFLAG_SPL_FREQ_WEIGHT_A;
+ else if (devc->buf[0] == RECORD_DBC)
+ devc->cur_mqflags = SR_MQFLAG_SPL_FREQ_WEIGHT_C;
+ else {
+ /* Shouldn't happen. */
+ sr_dbg("Unknown record token 0x%.2x", c);
+ return;
+ }
+ packet.type = SR_DF_META;
+ packet.payload = &meta;
+ src = sr_config_new(SR_CONF_SAMPLE_INTERVAL,
+ g_variant_new_uint64(devc->buf[7] * 1000));
+ meta.config = g_slist_append(NULL, src);
+ sr_session_send(devc->cb_data, &packet);
+ g_free(src);
+ devc->buf_len = 0;
+ }
+ } else if (devc->state == ST_GET_LOG_RECORD_DATA) {
+ sr_dbg("log data: 0x%.2x", c);
+ if (c == RECORD_DBA || c == RECORD_DBC || c == RECORD_DATA || c == RECORD_END) {
+ /* Work around off-by-one bug in device firmware. This
+ * happens only on the last record, i.e. before RECORD_END */
+ if (devc->buf_len & 1)
+ devc->buf_len--;
+ /* Done with this set of samples */
+ send_data(sdi, devc->buf, devc->buf_len / 2);
+ devc->buf_len = 0;
+
+ /* Process this meta marker in the right state. */
+ devc->state = ST_GET_LOG_RECORD_META;
+ process_byte(sdi, c, handle_packets);
+ } else {
+ devc->buf[devc->buf_len++] = c;
+ if (devc->buf_len == SAMPLES_PER_PACKET * 2) {
+ send_data(sdi, devc->buf, devc->buf_len / 2);
+ devc->buf_len = 0;
+ }
+ }
+ }
+
+}
+
+SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data)
+{
+ const struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ unsigned char c, cmd;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+ if (revents == G_IO_IN) {
+ if (serial_read(serial, &c, 1) != 1)
+ return TRUE;
+ process_byte(sdi, c, TRUE);
+
+ if (devc->enable_data_source_memory) {
+ if (devc->state == ST_GET_LOG_HEADER) {
+ /* Memory transfer started. */
+ devc->enable_data_source_memory = FALSE;
+ } else {
+ /* Tell device to start transferring from memory. */
+ cmd = CMD_TRANSFER_MEMORY;
+ serial_write(serial, &cmd, 1);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+static int wait_for_token(const struct sr_dev_inst *sdi, int8_t *tokens, int timeout)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ gint64 start_time;
+ int i;
+ unsigned char c;
+
+ serial = sdi->conn;
+ devc = sdi->priv;
+ devc->state = ST_INIT;
+ start_time = g_get_monotonic_time() / 1000;
+ while (TRUE) {
+ if (serial_read(serial, &c, 1) != 1)
+ /* Device might have gone away. */
+ return SR_ERR;
+ process_byte(sdi, c, FALSE);
+ if (devc->state != ST_INIT)
+ /* Wait for a whole packet to get processed. */
+ continue;
+ for (i = 0; tokens[i] != -1; i++) {
+ if (devc->token == tokens[i]) {
+ sr_spew("wait_for_token: got token 0x%.2x", devc->token);
+ return SR_OK;
+ }
+ }
+ if (timeout && g_get_monotonic_time() / 1000 - start_time > timeout)
+ return SR_ERR_TIMEOUT;
+ }
+
+ return SR_OK;
+}
+
+/* cmd is the command to send, tokens are the tokens that denote the state
+ * which the command affects. The first token is the desired state. */
+static int cem_dt_885x_toggle(const struct sr_dev_inst *sdi, uint8_t cmd,
+ int8_t *tokens, int timeout)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ serial = sdi->conn;
+ devc = sdi->priv;
+
+ /* The device doesn't respond to commands very well. The
+ * only thing to do is wait for the token that will confirm
+ * whether the command worked or not, and resend if needed. */
+ while (TRUE) {
+ if (serial_write(serial, (const void *)&cmd, 1) != 1)
+ return SR_ERR;
+ if (wait_for_token(sdi, tokens, timeout) == SR_ERR)
+ return SR_ERR;
+ if (devc->token == tokens[0])
+ /* It worked. */
+ break;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV gboolean cem_dt_885x_recording_get(const struct sr_dev_inst *sdi,
+ int *state)
+{
+ struct dev_context *devc;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+ if (devc->recording == -1) {
+ /* Didn't pick up device state yet. */
+ tokens[0] = TOKEN_RECORDING_ON;
+ tokens[1] = TOKEN_RECORDING_OFF;
+ tokens[2] = -1;
+ if (wait_for_token(sdi, tokens, 510) != SR_OK)
+ return SR_ERR;
+ }
+ *state = devc->token == TOKEN_RECORDING_ON;
+
+ return SR_OK;
+}
+
+SR_PRIV int cem_dt_885x_recording_set(const struct sr_dev_inst *sdi,
+ gboolean state)
+{
+ struct dev_context *devc;
+ int ret;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+
+ /* The toggle below needs the desired state in first position. */
+ if (state) {
+ tokens[0] = TOKEN_RECORDING_ON;
+ tokens[1] = TOKEN_RECORDING_OFF;
+ } else {
+ tokens[0] = TOKEN_RECORDING_OFF;
+ tokens[1] = TOKEN_RECORDING_ON;
+ }
+ tokens[2] = -1;
+
+ if (devc->recording == -1) {
+ /* Didn't pick up device state yet. */
+ if (wait_for_token(sdi, tokens, 0) != SR_OK)
+ return SR_ERR;
+ if (devc->token == tokens[0])
+ /* Nothing to do. */
+ return SR_OK;
+ } else if (devc->recording == state)
+ /* Nothing to do. */
+ return SR_OK;
+
+ /* Recording state notifications are sent at 2Hz, so allow just over
+ * that, 510ms, for the state to come in. */
+ ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_RECORDING, tokens, 510);
+
+ return ret;
+}
+
+SR_PRIV int cem_dt_885x_weight_freq_get(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int cur_setting;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+
+ cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
+ if (cur_setting == 0) {
+ /* Didn't pick up device state yet. */
+ tokens[0] = TOKEN_WEIGHT_FREQ_A;
+ tokens[1] = TOKEN_WEIGHT_FREQ_C;
+ tokens[2] = -1;
+ if (wait_for_token(sdi, tokens, 0) != SR_OK)
+ return SR_ERR;
+ if (devc->token == TOKEN_WEIGHT_FREQ_A)
+ return SR_MQFLAG_SPL_FREQ_WEIGHT_A;
+ else
+ return SR_MQFLAG_SPL_FREQ_WEIGHT_C;
+ } else
+ return cur_setting;
+}
+
+SR_PRIV int cem_dt_885x_weight_freq_set(const struct sr_dev_inst *sdi, int freqw)
+{
+ struct dev_context *devc;
+ int cur_setting, ret;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+
+ cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
+ if (cur_setting == freqw)
+ /* Already set to this frequency weighting. */
+ return SR_OK;
+
+ /* The toggle below needs the desired state in first position. */
+ if (freqw == SR_MQFLAG_SPL_FREQ_WEIGHT_A) {
+ tokens[0] = TOKEN_WEIGHT_FREQ_A;
+ tokens[1] = TOKEN_WEIGHT_FREQ_C;
+ } else {
+ tokens[0] = TOKEN_WEIGHT_FREQ_C;
+ tokens[1] = TOKEN_WEIGHT_FREQ_A;
+ }
+ tokens[2] = -1;
+
+ if (cur_setting == 0) {
+ /* Didn't pick up device state yet. */
+ if (wait_for_token(sdi, tokens, 0) != SR_OK)
+ return SR_ERR;
+ if (devc->token == tokens[0])
+ /* Nothing to do. */
+ return SR_OK;
+ }
+
+ /* 10ms timeout seems to work best for this. */
+ ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_WEIGHT_FREQ, tokens, 10);
+
+ return ret;
+}
+
+SR_PRIV int cem_dt_885x_weight_time_get(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int cur_setting;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+
+ cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
+ if (cur_setting == 0) {
+ /* Didn't pick up device state yet. */
+ tokens[0] = TOKEN_WEIGHT_TIME_FAST;
+ tokens[1] = TOKEN_WEIGHT_TIME_SLOW;
+ tokens[2] = -1;
+ if (wait_for_token(sdi, tokens, 0) != SR_OK)
+ return SR_ERR;
+ if (devc->token == TOKEN_WEIGHT_TIME_FAST)
+ return SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ else
+ return SR_MQFLAG_SPL_TIME_WEIGHT_S;
+ } else
+ return cur_setting;
+}
+
+SR_PRIV int cem_dt_885x_weight_time_set(const struct sr_dev_inst *sdi, int timew)
+{
+ struct dev_context *devc;
+ int cur_setting, ret;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+
+ cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
+ if (cur_setting == timew)
+ /* Already set to this time weighting. */
+ return SR_OK;
+
+ /* The toggle below needs the desired state in first position. */
+ if (timew == SR_MQFLAG_SPL_TIME_WEIGHT_F) {
+ tokens[0] = TOKEN_WEIGHT_TIME_FAST;
+ tokens[1] = TOKEN_WEIGHT_TIME_SLOW;
+ } else {
+ tokens[0] = TOKEN_WEIGHT_TIME_SLOW;
+ tokens[1] = TOKEN_WEIGHT_TIME_FAST;
+ }
+ tokens[2] = -1;
+
+ if (cur_setting == 0) {
+ /* Didn't pick up device state yet. */
+ if (wait_for_token(sdi, tokens, 0) != SR_OK)
+ return SR_ERR;
+ if (devc->token == tokens[0])
+ /* Nothing to do. */
+ return SR_OK;
+ }
+
+ /* 51ms timeout seems to work best for this. */
+ ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_WEIGHT_TIME, tokens, 51);
+
+ return ret;
+}
+
+SR_PRIV int cem_dt_885x_holdmode_get(const struct sr_dev_inst *sdi,
+ gboolean *holdmode)
+{
+ struct dev_context *devc;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+
+ if (devc->cur_mqflags == 0) {
+ tokens[0] = TOKEN_HOLD_MAX;
+ tokens[1] = TOKEN_HOLD_MIN;
+ tokens[2] = TOKEN_HOLD_NONE;
+ tokens[3] = -1;
+ if (wait_for_token(sdi, tokens, 0) != SR_OK)
+ return SR_ERR;
+ if (devc->token == TOKEN_HOLD_MAX)
+ devc->cur_mqflags = SR_MQFLAG_MAX;
+ else if (devc->token == TOKEN_HOLD_MIN)
+ devc->cur_mqflags = SR_MQFLAG_MIN;
+ }
+ *holdmode = devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN);
+
+ return SR_OK;
+}
+
+SR_PRIV int cem_dt_885x_holdmode_set(const struct sr_dev_inst *sdi, int holdmode)
+{
+ struct dev_context *devc;
+ int cur_setting, ret;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+
+ /* The toggle below needs the desired state in first position. */
+ if (holdmode == SR_MQFLAG_MAX) {
+ tokens[0] = TOKEN_HOLD_MAX;
+ tokens[1] = TOKEN_HOLD_MIN;
+ tokens[2] = TOKEN_HOLD_NONE;
+ } else if (holdmode == SR_MQFLAG_MIN) {
+ tokens[0] = TOKEN_HOLD_MIN;
+ tokens[1] = TOKEN_HOLD_MAX;
+ tokens[2] = TOKEN_HOLD_NONE;
+ } else {
+ tokens[0] = TOKEN_HOLD_NONE;
+ tokens[1] = TOKEN_HOLD_MAX;
+ tokens[2] = TOKEN_HOLD_MIN;
+ }
+ tokens[3] = -1;
+
+ if (devc->cur_mqflags == 0) {
+ /* Didn't pick up device state yet. */
+ if (wait_for_token(sdi, tokens, 0) != SR_OK)
+ return SR_ERR;
+ if (devc->token == tokens[0])
+ /* Nothing to do. */
+ return SR_OK;
+ } else {
+ cur_setting = devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN);
+ if (cur_setting == holdmode)
+ /* Already set correctly. */
+ return SR_OK;
+ }
+
+ /* 51ms timeout seems to work best for this. */
+ ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_HOLD_MAX_MIN, tokens, 51);
+
+ return ret;
+}
+
+SR_PRIV int cem_dt_885x_meas_range_get(const struct sr_dev_inst *sdi,
+ uint64_t *low, uint64_t *high)
+{
+ struct dev_context *devc;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+ if (devc->cur_meas_range == 0) {
+ tokens[0] = TOKEN_MEAS_RANGE_30_130;
+ tokens[1] = TOKEN_MEAS_RANGE_30_80;
+ tokens[2] = TOKEN_MEAS_RANGE_50_100;
+ tokens[3] = TOKEN_MEAS_RANGE_80_130;
+ tokens[4] = -1;
+ if (wait_for_token(sdi, tokens, 0) != SR_OK)
+ return SR_ERR;
+ devc->cur_meas_range = devc->token;
+ }
+
+ switch (devc->cur_meas_range) {
+ case TOKEN_MEAS_RANGE_30_130:
+ *low = 30;
+ *high = 130;
+ break;
+ case TOKEN_MEAS_RANGE_30_80:
+ *low = 30;
+ *high = 80;
+ break;
+ case TOKEN_MEAS_RANGE_50_100:
+ *low = 50;
+ *high = 100;
+ break;
+ case TOKEN_MEAS_RANGE_80_130:
+ *low = 80;
+ *high = 130;
+ break;
+ default:
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int cem_dt_885x_meas_range_set(const struct sr_dev_inst *sdi,
+ uint64_t low, uint64_t high)
+{
+ struct dev_context *devc;
+ int ret;
+ int8_t token, tokens[6];
+
+ devc = sdi->priv;
+ if (low == 30 && high == 130)
+ token = TOKEN_MEAS_RANGE_30_130;
+ else if (low == 30 && high == 80)
+ token = TOKEN_MEAS_RANGE_30_80;
+ else if (low == 50 && high == 100)
+ token = TOKEN_MEAS_RANGE_50_100;
+ else if (low == 80 && high == 130)
+ token = TOKEN_MEAS_RANGE_80_130;
+ else
+ return SR_ERR;
+
+ sr_dbg("want 0x%.2x", token);
+ /* The toggle below needs the desired state in first position. */
+ tokens[0] = token;
+ tokens[1] = TOKEN_MEAS_RANGE_30_130;
+ tokens[2] = TOKEN_MEAS_RANGE_30_80;
+ tokens[3] = TOKEN_MEAS_RANGE_50_100;
+ tokens[4] = TOKEN_MEAS_RANGE_80_130;
+ tokens[5] = -1;
+
+ if (devc->cur_meas_range == 0) {
+ /* 110ms should be enough for two of these announcements */
+ if (wait_for_token(sdi, tokens, 110) != SR_OK)
+ return SR_ERR;
+ devc->cur_meas_range = devc->token;
+ }
+
+ if (devc->cur_meas_range == token)
+ /* Already set to this range. */
+ return SR_OK;
+
+ /* For measurement range, it works best to ignore announcements of the
+ * current setting and keep resending the toggle quickly. */
+ tokens[1] = -1;
+ ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_MEAS_RANGE, tokens, 11);
+
+ return ret;
+}
+
+SR_PRIV int cem_dt_885x_power_off(const struct sr_dev_inst *sdi)
+{
+ struct sr_serial_dev_inst *serial;
+ char c, cmd;
+
+ serial = sdi->conn;
+
+ /* Reopen the port in non-blocking mode, so we can properly
+ * detect when the device stops communicating. */
+ serial_close(serial);
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return SR_ERR;
+
+ cmd = CMD_TOGGLE_POWER_OFF;
+ while (TRUE) {
+ serial_flush(serial);
+ if (serial_write(serial, (const void *)&cmd, 1) != 1)
+ return SR_ERR;
+ /* It never takes more than 23ms for the next token to arrive. */
+ g_usleep(25 * 1000);
+ if (serial_read(serial, &c, 1) != 1)
+ /* Device is no longer responding. Good! */
+ break;
+ }
+
+ /* In case the user manually turns on the device again, reset
+ * the port back to blocking. */
+ serial_close(serial);
+ serial_open(serial, SERIAL_RDWR);
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_CEM_DT_885X_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_CEM_DT_885X_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "cem-dt-885x"
+
+/* When retrieving samples from device memory, group this many
+ * together into a sigrok packet. */
+#define SAMPLES_PER_PACKET 50
+
+/* Various temporary storage, at least 8 bytes. */
+#define BUF_SIZE SAMPLES_PER_PACKET * 2
+
+/* When in hold mode, force the last measurement out at this interval.
+ * We're using 50ms, which duplicates the non-hold 20Hz update rate. */
+#define HOLD_REPEAT_INTERVAL 50 * 1000
+
+enum {
+ TOKEN_WEIGHT_TIME_FAST = 0x02,
+ TOKEN_WEIGHT_TIME_SLOW = 0x03,
+ TOKEN_HOLD_MAX = 0x04,
+ TOKEN_HOLD_MIN = 0x05,
+ TOKEN_TIME = 0x06,
+ TOKEN_MEAS_RANGE_OVER = 0x07,
+ TOKEN_MEAS_RANGE_UNDER = 0x08,
+ TOKEN_STORE_FULL = 0x09,
+ TOKEN_RECORDING_ON = 0x0a,
+ TOKEN_MEAS_WAS_READOUT = 0x0b,
+ TOKEN_MEAS_WAS_BARGRAPH = 0x0c,
+ TOKEN_MEASUREMENT = 0xd,
+ TOKEN_HOLD_NONE = 0x0e,
+ TOKEN_BATTERY_LOW = 0x0f,
+ TOKEN_MEAS_RANGE_OK = 0x11,
+ TOKEN_STORE_OK = 0x19,
+ TOKEN_RECORDING_OFF = 0x1a,
+ TOKEN_WEIGHT_FREQ_A = 0x1b,
+ TOKEN_WEIGHT_FREQ_C = 0x1c,
+ TOKEN_BATTERY_OK = 0x1f,
+ TOKEN_MEAS_RANGE_30_80 = 0x30,
+ TOKEN_MEAS_RANGE_30_130 = 0x40,
+ TOKEN_MEAS_RANGE_50_100 = 0x4b,
+ TOKEN_MEAS_RANGE_80_130 = 0x4c,
+};
+
+enum {
+ CMD_TOGGLE_RECORDING = 0x55,
+ CMD_TOGGLE_WEIGHT_FREQ = 0x99,
+ CMD_TOGGLE_WEIGHT_TIME = 0x77,
+ CMD_TOGGLE_HOLD_MAX_MIN = 0x11,
+ CMD_TOGGLE_MEAS_RANGE = 0x88,
+ CMD_TOGGLE_POWER_OFF = 0x33,
+ CMD_TRANSFER_MEMORY = 0xac,
+};
+
+enum {
+ RECORD_DBA = 0xaa,
+ RECORD_DBC = 0xcc,
+ RECORD_DATA = 0xac,
+ RECORD_END = 0xdd,
+};
+
+enum {
+ DATA_SOURCE_LIVE,
+ DATA_SOURCE_MEMORY,
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Device state */
+ uint64_t cur_mqflags;
+ int recording;
+ int cur_meas_range;
+ int cur_data_source;
+
+ /* Acquisition settings */
+ uint64_t limit_samples;
+
+ /* Operational state */
+ int state;
+ uint64_t num_samples;
+ gboolean enable_data_source_memory;
+
+ /* Temporary state across callbacks */
+ void *cb_data;
+ unsigned char cmd;
+ unsigned char token;
+ int buf_len;
+ unsigned char buf[BUF_SIZE];
+ float last_spl;
+ gint64 hold_last_sent;
+};
+
+/* Parser state machine. */
+enum {
+ ST_INIT,
+ ST_GET_TOKEN,
+ ST_GET_DATA,
+ ST_GET_LOG_HEADER,
+ ST_GET_LOG_RECORD_META,
+ ST_GET_LOG_RECORD_DATA,
+};
+
+SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int cem_dt_885x_recording_set(const struct sr_dev_inst *sdi, gboolean start);
+SR_PRIV gboolean cem_dt_885x_recording_get(const struct sr_dev_inst *sdi,
+ int *state);
+SR_PRIV int cem_dt_885x_weight_freq_get(const struct sr_dev_inst *sdi);
+SR_PRIV int cem_dt_885x_weight_freq_set(const struct sr_dev_inst *sdi, int freqw);
+SR_PRIV int cem_dt_885x_weight_time_get(const struct sr_dev_inst *sdi);
+SR_PRIV int cem_dt_885x_weight_time_set(const struct sr_dev_inst *sdi, int timew);
+SR_PRIV int cem_dt_885x_holdmode_get(const struct sr_dev_inst *sdi,
+ gboolean *holdmode);
+SR_PRIV int cem_dt_885x_holdmode_set(const struct sr_dev_inst *sdi, int holdmode);
+SR_PRIV int cem_dt_885x_meas_range_get(const struct sr_dev_inst *sdi,
+ uint64_t *low, uint64_t *high);
+SR_PRIV int cem_dt_885x_meas_range_set(const struct sr_dev_inst *sdi,
+ uint64_t low, uint64_t high);
+SR_PRIV int cem_dt_885x_power_off(const struct sr_dev_inst *sdi);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "protocol.h"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_THERMOMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+static const char *channel_names[] = {
+ "T1", "T2", "T3", "T4",
+ NULL,
+};
+
+SR_PRIV struct sr_dev_driver center_309_driver_info;
+SR_PRIV struct sr_dev_driver voltcraft_k204_driver_info;
+
+SR_PRIV const struct center_dev_info center_devs[] = {
+ {
+ "Center", "309", "9600/8n1", 4, 32000, 45,
+ center_3xx_packet_valid,
+ ¢er_309_driver_info, receive_data_CENTER_309,
+ },
+ {
+ "Voltcraft", "K204", "9600/8n1", 4, 32000, 45,
+ center_3xx_packet_valid,
+ &voltcraft_k204_driver_info, receive_data_VOLTCRAFT_K204,
+ },
+};
+
+static int dev_clear(int idx)
+{
+ return std_dev_clear(center_devs[idx].di, NULL);
+}
+
+static int init(struct sr_context *sr_ctx, int idx)
+{
+ sr_dbg("Selected '%s' subdriver.", center_devs[idx].di->name);
+
+ return std_init(sr_ctx, center_devs[idx].di, LOG_PREFIX);
+}
+
+static GSList *center_scan(const char *conn, const char *serialcomm, int idx)
+{
+ int i;
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ struct sr_serial_dev_inst *serial;
+ GSList *devices;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ drvc = center_devs[idx].di->priv;
+ devices = NULL;
+ serial_flush(serial);
+
+ sr_info("Found device on port %s.", conn);
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, center_devs[idx].vendor,
+ center_devs[idx].device, NULL)))
+ goto scan_cleanup;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ goto scan_cleanup;
+ }
+
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+
+ sdi->priv = devc;
+ sdi->driver = center_devs[idx].di;
+
+ for (i = 0; i < center_devs[idx].num_channels; i++) {
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG,
+ TRUE, channel_names[i])))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+scan_cleanup:
+ serial_close(serial);
+
+ return devices;
+}
+
+static GSList *scan(GSList *options, int idx)
+{
+ struct sr_config *src;
+ GSList *l, *devices;
+ const char *conn, *serialcomm;
+
+ 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 (!conn)
+ return NULL;
+
+ if (serialcomm) {
+ /* Use the provided comm specs. */
+ devices = center_scan(conn, serialcomm, idx);
+ } else {
+ /* Try the default. */
+ devices = center_scan(conn, center_devs[idx].conn, idx);
+ }
+
+ return devices;
+}
+
+static GSList *dev_list(int idx)
+{
+ return ((struct drv_context *)(center_devs[idx].di->priv))->instances;
+}
+
+static int cleanup(int idx)
+{
+ return dev_clear(idx);
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ switch (id) {
+ case SR_CONF_LIMIT_SAMPLES:
+ if (g_variant_get_uint64(data) == 0)
+ return SR_ERR_ARG;
+ devc->limit_samples = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ if (g_variant_get_uint64(data) == 0)
+ return SR_ERR_ARG;
+ devc->limit_msec = g_variant_get_uint64(data);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data, int idx)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ devc->cb_data = cb_data;
+ devc->num_samples = 0;
+ devc->starttime = g_get_monotonic_time();
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Poll every 500ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 500,
+ center_devs[idx].receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ return std_serial_dev_acquisition_stop(sdi, cb_data,
+ std_serial_dev_close, sdi->conn, LOG_PREFIX);
+}
+
+/* Driver-specific API function wrappers */
+#define HW_INIT(X) \
+static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
+#define HW_CLEANUP(X) \
+static int cleanup_##X(void) { return cleanup(X); }
+#define HW_SCAN(X) \
+static GSList *scan_##X(GSList *options) { return scan(options, X); }
+#define HW_DEV_LIST(X) \
+static GSList *dev_list_##X(void) { return dev_list(X); }
+#define HW_DEV_CLEAR(X) \
+static int dev_clear_##X(void) { return dev_clear(X); }
+#define HW_DEV_ACQUISITION_START(X) \
+static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
+void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
+
+/* Driver structs and API function wrappers */
+#define DRV(ID, ID_UPPER, NAME, LONGNAME) \
+HW_INIT(ID_UPPER) \
+HW_CLEANUP(ID_UPPER) \
+HW_SCAN(ID_UPPER) \
+HW_DEV_LIST(ID_UPPER) \
+HW_DEV_CLEAR(ID_UPPER) \
+HW_DEV_ACQUISITION_START(ID_UPPER) \
+SR_PRIV struct sr_dev_driver ID##_driver_info = { \
+ .name = NAME, \
+ .longname = LONGNAME, \
+ .api_version = 1, \
+ .init = init_##ID_UPPER, \
+ .cleanup = cleanup_##ID_UPPER, \
+ .scan = scan_##ID_UPPER, \
+ .dev_list = dev_list_##ID_UPPER, \
+ .dev_clear = dev_clear_##ID_UPPER, \
+ .config_get = NULL, \
+ .config_set = config_set, \
+ .config_list = config_list, \
+ .dev_open = std_serial_dev_open, \
+ .dev_close = std_serial_dev_close, \
+ .dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
+ .dev_acquisition_stop = dev_acquisition_stop, \
+ .priv = NULL, \
+};
+
+DRV(center_309, CENTER_309, "center-309", "Center 309")
+DRV(voltcraft_k204, VOLTCRAFT_K204, "voltcraft-k204", "Voltcraft K204")
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "protocol.h"
+
+struct center_info {
+ float temp[4];
+ gboolean rec, std, max, min, maxmin, t1t2, rel, hold, lowbat, celsius;
+ gboolean memfull, autooff;
+ gboolean mode_std, mode_rel, mode_max, mode_min, mode_maxmin;
+};
+
+static int center_send(struct sr_serial_dev_inst *serial, const char *cmd)
+{
+ int ret;
+
+ if ((ret = serial_write(serial, cmd, strlen(cmd))) < 0) {
+ sr_err("Error sending '%s' command: %d.", cmd, ret);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV gboolean center_3xx_packet_valid(const uint8_t *buf)
+{
+ return (buf[0] == 0x02 && buf[44] == 0x03);
+}
+
+static void log_packet(const uint8_t *buf, int idx)
+{
+ int i;
+ GString *s;
+
+ s = g_string_sized_new(100);
+ g_string_printf(s, "Packet: ");
+ for (i = 0; i < center_devs[idx].packet_size; i++)
+ g_string_append_printf(s, "%02x ", buf[i]);
+ sr_spew("%s", s->str);
+ g_string_free(s, TRUE);
+}
+
+static int packet_parse(const uint8_t *buf, int idx, struct center_info *info)
+{
+ int i;
+ uint16_t temp_u16;
+
+ log_packet(buf, idx);
+
+ /* Byte 0: Always 0x02. */
+
+ /* Byte 1: Various status bits. */
+ info->rec = (buf[1] & (1 << 0)) != 0;
+ info->mode_std = (((buf[1] >> 1) & 0x3) == 0);
+ info->mode_max = (((buf[1] >> 1) & 0x3) == 1);
+ info->mode_min = (((buf[1] >> 1) & 0x3) == 2);
+ info->mode_maxmin = (((buf[1] >> 1) & 0x3) == 3);
+ /* TODO: Rel. Not available on all models. */
+ info->t1t2 = (buf[1] & (1 << 3)) != 0;
+ info->rel = (buf[1] & (1 << 4)) != 0;
+ info->hold = (buf[1] & (1 << 5)) != 0;
+ info->lowbat = (buf[1] & (1 << 6)) != 0;
+ info->celsius = (buf[1] & (1 << 7)) != 0;
+
+ /* Byte 2: Further status bits. */
+ info->memfull = (buf[2] & (1 << 0)) != 0;
+ info->autooff = (buf[2] & (1 << 7)) != 0;
+
+ /* Byte 7+8/9+10/11+12/13+14: channel T1/T2/T3/T4 temperature. */
+ for (i = 0; i < 4; i++) {
+ temp_u16 = buf[8 + (i * 2)];
+ temp_u16 |= ((uint16_t)buf[7 + (i * 2)] << 8);
+ info->temp[i] = (float)temp_u16;
+ }
+
+ /* Byte 43: Specifies whether we need to divide the value(s) by 10. */
+ for (i = 0; i < 4; i++) {
+ /* Bit = 0: Divide by 10. Bit = 1: Don't divide by 10. */
+ if ((buf[43] & (1 << i)) == 0)
+ info->temp[i] /= 10;
+ }
+
+ /* Bytes 39-42: Overflow/overlimit bits, depending on mode. */
+ for (i = 0; i < 4; i++) {
+ if (info->mode_std && ((buf[39] & (1 << i)) != 0))
+ info->temp[i] = INFINITY;
+ /* TODO: Rel. Not available on all models. */
+ // if (info->mode_rel && ((buf[40] & (1 << i)) != 0))
+ // info->temp[i] = INFINITY;
+ if (info->mode_max && ((buf[41] & (1 << i)) != 0))
+ info->temp[i] = INFINITY;
+ if (info->mode_min && ((buf[42] & (1 << i)) != 0))
+ info->temp[i] = INFINITY;
+ /* TODO: Minmax? */
+ }
+
+ /* Byte 44: Always 0x03. */
+
+ return SR_OK;
+}
+
+static int handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi, int idx)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct dev_context *devc;
+ struct center_info info;
+ GSList *l;
+ int i, ret;
+
+ devc = sdi->priv;
+
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+ memset(&info, 0, sizeof(struct center_info));
+
+ ret = packet_parse(buf, idx, &info);
+ if (ret < 0) {
+ sr_err("Failed to parse packet.");
+ return SR_ERR;
+ }
+
+ /* Common values for all 4 channels. */
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ analog.mq = SR_MQ_TEMPERATURE;
+ analog.unit = (info.celsius) ? SR_UNIT_CELSIUS : SR_UNIT_FAHRENHEIT;
+ analog.num_samples = 1;
+
+ /* Send the values for T1 - T4. */
+ for (i = 0; i < 4; i++) {
+ l = NULL;
+ l = g_slist_append(l, g_slist_nth_data(sdi->channels, i));
+ analog.channels = l;
+ analog.data = &(info.temp[i]);
+ sr_session_send(devc->cb_data, &packet);
+ g_slist_free(l);
+ }
+
+ devc->num_samples++;
+
+ return SR_OK;
+}
+
+/* Return TRUE if a full packet was parsed, FALSE otherwise. */
+static gboolean handle_new_data(struct sr_dev_inst *sdi, int idx)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int len, i, offset = 0, ret = FALSE;
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+
+ /* Try to get as much data as the buffer can hold. */
+ len = SERIAL_BUFSIZE - devc->buflen;
+ len = serial_read(serial, devc->buf + devc->buflen, len);
+ if (len < 1) {
+ sr_err("Serial port read error: %d.", len);
+ return FALSE;
+ }
+
+ devc->buflen += len;
+
+ /* Now look for packets in that data. */
+ while ((devc->buflen - offset) >= center_devs[idx].packet_size) {
+ if (center_devs[idx].packet_valid(devc->buf + offset)) {
+ handle_packet(devc->buf + offset, sdi, idx);
+ offset += center_devs[idx].packet_size;
+ ret = TRUE;
+ } else {
+ offset++;
+ }
+ }
+
+ /* If we have any data left, move it to the beginning of our buffer. */
+ for (i = 0; i < devc->buflen - offset; i++)
+ devc->buf[i] = devc->buf[offset + i];
+ devc->buflen -= offset;
+
+ return ret;
+}
+
+static int receive_data(int fd, int revents, int idx, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ int64_t t;
+ static gboolean request_new_packet = TRUE;
+ struct sr_serial_dev_inst *serial;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+
+ if (revents == G_IO_IN) {
+ /* New data arrived. */
+ request_new_packet = handle_new_data(sdi, idx);
+ } else {
+ /*
+ * Timeout. Send "A" to request a packet, but then don't send
+ * further "A" commands until we received a full packet first.
+ */
+ if (request_new_packet) {
+ center_send(serial, "A");
+ request_new_packet = FALSE;
+ }
+ }
+
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sr_info("Requested number of samples reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+
+ if (devc->limit_msec) {
+ t = (g_get_monotonic_time() - devc->starttime) / 1000;
+ if (t > (int64_t)devc->limit_msec) {
+ sr_info("Requested time limit reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#define RECEIVE_DATA(ID_UPPER) \
+SR_PRIV int receive_data_##ID_UPPER(int fd, int revents, void *cb_data) { \
+ return receive_data(fd, revents, ID_UPPER, cb_data); }
+
+/* Driver-specific receive_data() wrappers */
+RECEIVE_DATA(CENTER_309)
+RECEIVE_DATA(VOLTCRAFT_K204)
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBSIGROK_HARDWARE_CENTER_3XX_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_CENTER_3XX_PROTOCOL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "center-3xx"
+
+/* Note: When adding entries here, don't forget to update CENTER_DEV_COUNT. */
+enum {
+ CENTER_309,
+ VOLTCRAFT_K204,
+};
+
+#define CENTER_DEV_COUNT 2
+
+struct center_dev_info {
+ char *vendor;
+ char *device;
+ char *conn;
+ int num_channels;
+ uint32_t max_sample_points;
+ uint8_t packet_size;
+ gboolean (*packet_valid)(const uint8_t *);
+ struct sr_dev_driver *di;
+ int (*receive_data)(int, int, void *);
+};
+
+extern SR_PRIV const struct center_dev_info center_devs[CENTER_DEV_COUNT];
+
+#define SERIAL_BUFSIZE 256
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /** The current sampling limit (in number of samples). */
+ uint64_t limit_samples;
+
+ /** The current sampling limit (in ms). */
+ uint64_t limit_msec;
+
+ /** Opaque pointer passed in by the frontend. */
+ void *cb_data;
+
+ /** The current number of already received samples. */
+ uint64_t num_samples;
+
+ int64_t starttime;
+
+ uint8_t buf[SERIAL_BUFSIZE];
+ int bufoffset;
+ int buflen;
+};
+
+SR_PRIV gboolean center_3xx_packet_valid(const uint8_t *buf);
+
+SR_PRIV int receive_data_CENTER_309(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VOLTCRAFT_K204(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "protocol.h"
+
+SR_PRIV struct sr_dev_driver chronovu_la_driver_info;
+static struct sr_dev_driver *di = &chronovu_la_driver_info;
+
+static const int32_t hwcaps[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_TRIGGER_MATCH,
+ SR_CONF_LIMIT_MSEC, /* TODO: Not yet implemented. */
+ SR_CONF_LIMIT_SAMPLES, /* TODO: Not yet implemented. */
+};
+
+static const int32_t trigger_matches[] = {
+ SR_TRIGGER_ZERO,
+ SR_TRIGGER_ONE,
+ SR_TRIGGER_RISING,
+ SR_TRIGGER_FALLING,
+};
+
+/* The ChronoVu LA8/LA16 can have multiple VID/PID pairs. */
+static struct {
+ uint16_t vid;
+ uint16_t pid;
+ int model;
+ const char *iproduct;
+} vid_pid[] = {
+ { 0x0403, 0x6001, CHRONOVU_LA8, "ChronoVu LA8" },
+ { 0x0403, 0x8867, CHRONOVU_LA8, "ChronoVu LA8" },
+ { 0x0403, 0x6001, CHRONOVU_LA16, "ChronoVu LA16" },
+ { 0x0403, 0x8867, CHRONOVU_LA16, "ChronoVu LA16" },
+};
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
+
+static void clear_helper(void *priv)
+{
+ struct dev_context *devc;
+
+ devc = priv;
+
+ ftdi_free(devc->ftdic);
+ g_free(devc->final_buf);
+}
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, clear_helper);
+}
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static int add_device(int idx, int model, GSList **devices)
+{
+ int ret;
+ unsigned int i;
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_channel *ch;
+
+ ret = SR_OK;
+
+ drvc = di->priv;
+
+ /* Allocate memory for our private device context. */
+ devc = g_try_malloc(sizeof(struct dev_context));
+
+ /* Set some sane defaults. */
+ devc->prof = &cv_profiles[model];
+ devc->ftdic = NULL; /* Will be set in the open() API call. */
+ devc->cur_samplerate = 0; /* Set later (different for LA8/LA16). */
+ devc->limit_msec = 0;
+ devc->limit_samples = 0;
+ devc->cb_data = NULL;
+ memset(devc->mangled_buf, 0, BS);
+ devc->final_buf = NULL;
+ devc->trigger_pattern = 0x0000; /* Irrelevant, see trigger_mask. */
+ devc->trigger_mask = 0x0000; /* All channels: "don't care". */
+ devc->trigger_edgemask = 0x0000; /* All channels: "state triggered". */
+ devc->trigger_found = 0;
+ devc->done = 0;
+ devc->block_counter = 0;
+ devc->divcount = 0;
+ devc->usb_vid = vid_pid[idx].vid;
+ devc->usb_pid = vid_pid[idx].pid;
+ memset(devc->samplerates, 0, sizeof(uint64_t) * 255);
+
+ /* Allocate memory where we'll store the de-mangled data. */
+ if (!(devc->final_buf = g_try_malloc(SDRAM_SIZE))) {
+ sr_err("Failed to allocate memory for sample buffer.");
+ ret = SR_ERR_MALLOC;
+ goto err_free_devc;
+ }
+
+ /* We now know the device, set its max. samplerate as default. */
+ devc->cur_samplerate = devc->prof->max_samplerate;
+
+ /* Register the device with libsigrok. */
+ sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING,
+ "ChronoVu", devc->prof->modelname, NULL);
+ if (!sdi) {
+ sr_err("Failed to create device instance.");
+ ret = SR_ERR;
+ goto err_free_final_buf;
+ }
+ sdi->driver = di;
+ sdi->priv = devc;
+
+ for (i = 0; i < devc->prof->num_channels; i++) {
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
+ cv_channel_names[i]))) {
+ ret = SR_ERR;
+ goto err_free_dev_inst;
+ }
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ *devices = g_slist_append(*devices, sdi);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+
+ return SR_OK;
+
+err_free_dev_inst:
+ sr_dev_inst_free(sdi);
+err_free_final_buf:
+ g_free(devc->final_buf);
+err_free_devc:
+ g_free(devc);
+
+ return ret;
+}
+
+static GSList *scan(GSList *options)
+{
+ int ret;
+ unsigned int i;
+ GSList *devices;
+ struct ftdi_context *ftdic;
+
+ (void)options;
+
+ devices = NULL;
+
+ /* Allocate memory for the FTDI context and initialize it. */
+ if (!(ftdic = ftdi_new())) {
+ sr_err("Failed to initialize libftdi.");
+ return NULL;
+ }
+
+ /* Check for LA8 and/or LA16 devices with various VID/PIDs. */
+ for (i = 0; i < ARRAY_SIZE(vid_pid); i++) {
+ ret = ftdi_usb_open_desc(ftdic, vid_pid[i].vid,
+ vid_pid[i].pid, vid_pid[i].iproduct, NULL);
+ /* Show errors other than "device not found". */
+ if (ret < 0 && ret != -3)
+ sr_dbg("Error finding/opening device (%d): %s.",
+ ret, ftdi_get_error_string(ftdic));
+ if (ret < 0)
+ continue; /* No device found, or not usable. */
+
+ sr_dbg("Found %s device (%04x:%04x).",
+ vid_pid[i].iproduct, vid_pid[i].vid, vid_pid[i].pid);
+
+ if ((ret = add_device(i, vid_pid[i].model, &devices)) < 0)
+ sr_dbg("Failed to add device: %d.", ret);
+
+ if ((ret = ftdi_usb_close(ftdic)) < 0)
+ sr_dbg("Failed to close FTDI device (%d): %s.",
+ ret, ftdi_get_error_string(ftdic));
+ }
+
+ /* Close USB device, deinitialize and free the FTDI context. */
+ ftdi_free(ftdic);
+ ftdic = NULL;
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int ret;
+
+ ret = SR_ERR;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR_BUG;
+
+ /* Allocate memory for the FTDI context and initialize it. */
+ if (!(devc->ftdic = ftdi_new())) {
+ sr_err("Failed to initialize libftdi.");
+ return SR_ERR;
+ }
+
+ sr_dbg("Opening %s device (%04x:%04x).", devc->prof->modelname,
+ devc->usb_vid, devc->usb_pid);
+
+ /* Open the device. */
+ if ((ret = ftdi_usb_open_desc(devc->ftdic, devc->usb_vid,
+ devc->usb_pid, devc->prof->iproduct, NULL)) < 0) {
+ sr_err("Failed to open FTDI device (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ goto err_ftdi_free;
+ }
+ sr_dbg("Device opened successfully.");
+
+ /* Purge RX/TX buffers in the FTDI chip. */
+ if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
+ sr_err("Failed to purge FTDI buffers (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ goto err_ftdi_free;
+ }
+ sr_dbg("FTDI buffers purged successfully.");
+
+ /* Enable flow control in the FTDI chip. */
+ if ((ret = ftdi_setflowctrl(devc->ftdic, SIO_RTS_CTS_HS)) < 0) {
+ sr_err("Failed to enable FTDI flow control (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ goto err_ftdi_free;
+ }
+ sr_dbg("FTDI flow control enabled successfully.");
+
+ /* Wait 100ms. */
+ g_usleep(100 * 1000);
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+
+err_ftdi_free:
+ ftdi_free(devc->ftdic); /* Close device (if open), free FTDI context. */
+ devc->ftdic = NULL;
+ return ret;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ int ret;
+ struct dev_context *devc;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_OK;
+
+ devc = sdi->priv;
+
+ if (devc->ftdic && (ret = ftdi_usb_close(devc->ftdic)) < 0)
+ sr_err("Failed to close FTDI device (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return dev_clear();
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ if (!sdi || !(devc = sdi->priv))
+ return SR_ERR_BUG;
+ *data = g_variant_new_uint64(devc->cur_samplerate);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR_BUG;
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ if (cv_set_samplerate(sdi, g_variant_get_uint64(data)) < 0)
+ return SR_ERR;
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ if (g_variant_get_uint64(data) == 0)
+ return SR_ERR_ARG;
+ devc->limit_msec = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ if (g_variant_get_uint64(data) == 0)
+ return SR_ERR_ARG;
+ devc->limit_samples = g_variant_get_uint64(data);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ GVariant *gvar, *grange[2];
+ GVariantBuilder gvb;
+ struct dev_context *devc;
+
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ if (!sdi || !sdi->priv || !(devc = sdi->priv))
+ return SR_ERR_BUG;
+ cv_fill_samplerates_if_needed(sdi);
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
+ devc->samplerates,
+ ARRAY_SIZE(devc->samplerates),
+ sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ if (!sdi || !sdi->priv || !(devc = sdi->priv) || !devc->prof)
+ return SR_ERR_BUG;
+ grange[0] = g_variant_new_uint64(0);
+ if (devc->prof->model == CHRONOVU_LA8)
+ grange[1] = g_variant_new_uint64(MAX_NUM_SAMPLES);
+ else
+ grange[1] = g_variant_new_uint64(MAX_NUM_SAMPLES / 2);
+ *data = g_variant_new_tuple(grange, 2);
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ if (!sdi || !sdi->priv || !(devc = sdi->priv) || !devc->prof)
+ return SR_ERR_BUG;
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ trigger_matches, devc->prof->num_trigger_matches,
+ sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+ int i, ret;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+
+ (void)fd;
+ (void)revents;
+
+ if (!(sdi = cb_data)) {
+ sr_err("cb_data was NULL.");
+ return FALSE;
+ }
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return FALSE;
+ }
+
+ if (!devc->ftdic) {
+ sr_err("devc->ftdic was NULL.");
+ return FALSE;
+ }
+
+ /* Get one block of data. */
+ if ((ret = cv_read_block(devc)) < 0) {
+ sr_err("Failed to read data block: %d.", ret);
+ dev_acquisition_stop(sdi, sdi);
+ return FALSE;
+ }
+
+ /* We need to get exactly NUM_BLOCKS blocks (i.e. 8MB) of data. */
+ if (devc->block_counter != (NUM_BLOCKS - 1)) {
+ devc->block_counter++;
+ return TRUE;
+ }
+
+ sr_dbg("Sampling finished, sending data to session bus now.");
+
+ /*
+ * All data was received and demangled, send it to the session bus.
+ *
+ * Note: Due to the method how data is spread across the 8MByte of
+ * SDRAM, we can _not_ send it to the session bus in a streaming
+ * manner while we receive it. We have to receive and de-mangle the
+ * full 8MByte first, only then the whole buffer contains valid data.
+ */
+ for (i = 0; i < NUM_BLOCKS; i++)
+ cv_send_block_to_session_bus(devc, i);
+
+ dev_acquisition_stop(sdi, sdi);
+
+ return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ uint8_t buf[8];
+ int bytes_to_write, bytes_written;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ if (!devc->ftdic) {
+ sr_err("devc->ftdic was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ devc->divcount = cv_samplerate_to_divcount(sdi, devc->cur_samplerate);
+ if (devc->divcount == 0xff) {
+ sr_err("Invalid divcount/samplerate.");
+ return SR_ERR;
+ }
+
+ if (cv_convert_trigger(sdi) != SR_OK) {
+ sr_err("Failed to configure trigger.");
+ return SR_ERR;
+ }
+
+ /* Fill acquisition parameters into buf[]. */
+ if (devc->prof->model == CHRONOVU_LA8) {
+ buf[0] = devc->divcount;
+ buf[1] = 0xff; /* This byte must always be 0xff. */
+ buf[2] = devc->trigger_pattern & 0xff;
+ buf[3] = devc->trigger_mask & 0xff;
+ bytes_to_write = 4;
+ } else {
+ buf[0] = devc->divcount;
+ buf[1] = 0xff; /* This byte must always be 0xff. */
+ buf[2] = (devc->trigger_pattern & 0xff00) >> 8; /* LSB */
+ buf[3] = (devc->trigger_pattern & 0x00ff) >> 0; /* MSB */
+ buf[4] = (devc->trigger_mask & 0xff00) >> 8; /* LSB */
+ buf[5] = (devc->trigger_mask & 0x00ff) >> 0; /* MSB */
+ buf[6] = (devc->trigger_edgemask & 0xff00) >> 8; /* LSB */
+ buf[7] = (devc->trigger_edgemask & 0x00ff) >> 0; /* MSB */
+ bytes_to_write = 8;
+ }
+
+ /* Start acquisition. */
+ bytes_written = cv_write(devc, buf, bytes_to_write);
+
+ if (bytes_written < 0 || bytes_written != bytes_to_write) {
+ sr_err("Acquisition failed to start.");
+ return SR_ERR;
+ }
+
+ sr_dbg("Hardware acquisition started successfully.");
+
+ devc->cb_data = cb_data;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(sdi, LOG_PREFIX);
+
+ /* Time when we should be done (for detecting trigger timeouts). */
+ devc->done = (devc->divcount + 1) * devc->prof->trigger_constant +
+ g_get_monotonic_time() + (10 * G_TIME_SPAN_SECOND);
+ devc->block_counter = 0;
+ devc->trigger_found = 0;
+
+ /* Hook up a dummy handler to receive data from the device. */
+ sr_session_source_add(sdi->session, -1, G_IO_IN, 0, receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct sr_datafeed_packet packet;
+
+ (void)cb_data;
+
+ sr_dbg("Stopping acquisition.");
+ sr_session_source_remove(sdi->session, -1);
+
+ /* Send end packet to the session bus. */
+ sr_dbg("Sending SR_DF_END.");
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver chronovu_la_driver_info = {
+ .name = "chronovu-la",
+ .longname = "ChronoVu LA8/LA16",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = 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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "protocol.h"
+
+SR_PRIV const struct cv_profile cv_profiles[] = {
+ { CHRONOVU_LA8, "LA8", "ChronoVu LA8", 8, SR_MHZ(100), 2, 0.8388608 },
+ { CHRONOVU_LA16, "LA16", "ChronoVu LA16", 16, SR_MHZ(200), 4, 0.042 },
+ { 0, NULL, NULL, 0, 0, 0, 0.0 },
+};
+
+/* LA8: channels are numbered 0-7. LA16: channels are numbered 0-15. */
+SR_PRIV const char *cv_channel_names[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", "10", "11", "12", "13", "14", "15",
+};
+
+static int close_usb_reset_sequencer(struct dev_context *devc);
+
+SR_PRIV void cv_fill_samplerates_if_needed(const struct sr_dev_inst *sdi)
+{
+ int i;
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ if (devc->samplerates[0] != 0)
+ return;
+
+ for (i = 0; i < 255; i++)
+ devc->samplerates[254 - i] = devc->prof->max_samplerate / (i + 1);
+}
+
+/**
+ * Check if the given samplerate is supported by the hardware.
+ *
+ * @param sdi Device instance.
+ * @param samplerate The samplerate (in Hz) to check.
+ *
+ * @return 1 if the samplerate is supported/valid, 0 otherwise.
+ */
+static int is_valid_samplerate(const struct sr_dev_inst *sdi,
+ uint64_t samplerate)
+{
+ int i;
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ cv_fill_samplerates_if_needed(sdi);
+
+ for (i = 0; i < 255; i++) {
+ if (devc->samplerates[i] == samplerate)
+ return 1;
+ }
+
+ sr_err("Invalid samplerate (%" PRIu64 "Hz).", samplerate);
+
+ return 0;
+}
+
+/**
+ * Convert a samplerate (in Hz) to the 'divcount' value the device wants.
+ *
+ * The divcount value can be 0x00 - 0xfe (0xff is not valid).
+ *
+ * LA8:
+ * sample period = (divcount + 1) * 10ns.
+ * divcount = 0x00: 10ns period, 100MHz samplerate.
+ * divcount = 0xfe: 2550ns period, 392.15kHz samplerate.
+ *
+ * LA16:
+ * sample period = (divcount + 1) * 5ns.
+ * divcount = 0x00: 5ns period, 200MHz samplerate.
+ * divcount = 0xfe: 1275ns period, ~784.31kHz samplerate.
+ *
+ * @param sdi Device instance.
+ * @param samplerate The samplerate in Hz.
+ *
+ * @return The divcount value as needed by the hardware, or 0xff upon errors.
+ */
+SR_PRIV uint8_t cv_samplerate_to_divcount(const struct sr_dev_inst *sdi,
+ uint64_t samplerate)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ if (samplerate == 0) {
+ sr_err("Can't convert invalid samplerate of 0 Hz.");
+ return 0xff;
+ }
+
+ if (!is_valid_samplerate(sdi, samplerate)) {
+ sr_err("Can't get divcount, samplerate invalid.");
+ return 0xff;
+ }
+
+ return (devc->prof->max_samplerate / samplerate) - 1;
+}
+
+/**
+ * Write data of a certain length to the FTDI device.
+ *
+ * @param devc The struct containing private per-device-instance data. Must not
+ * be NULL. devc->ftdic must not be NULL either.
+ * @param buf The buffer containing the data to write. Must not be NULL.
+ * @param size The number of bytes to write. Must be > 0.
+ *
+ * @return The number of bytes written, or a negative value upon errors.
+ */
+SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size)
+{
+ int bytes_written;
+
+ /* Note: Caller ensures devc/devc->ftdic/buf != NULL and size > 0. */
+
+ bytes_written = ftdi_write_data(devc->ftdic, buf, size);
+
+ if (bytes_written < 0) {
+ sr_err("Failed to write data (%d): %s.",
+ bytes_written, ftdi_get_error_string(devc->ftdic));
+ (void) close_usb_reset_sequencer(devc); /* Ignore errors. */
+ } else if (bytes_written != size) {
+ sr_err("Failed to write data, only %d/%d bytes written.",
+ size, bytes_written);
+ (void) close_usb_reset_sequencer(devc); /* Ignore errors. */
+ }
+
+ return bytes_written;
+}
+
+/**
+ * Read a certain amount of bytes from the FTDI device.
+ *
+ * @param devc The struct containing private per-device-instance data. Must not
+ * be NULL. devc->ftdic must not be NULL either.
+ * @param buf The buffer where the received data will be stored. Must not
+ * be NULL.
+ * @param size The number of bytes to read. Must be >= 1.
+ *
+ * @return The number of bytes read, or a negative value upon errors.
+ */
+static int cv_read(struct dev_context *devc, uint8_t *buf, int size)
+{
+ int bytes_read;
+
+ /* Note: Caller ensures devc/devc->ftdic/buf != NULL and size > 0. */
+
+ bytes_read = ftdi_read_data(devc->ftdic, buf, size);
+
+ if (bytes_read < 0) {
+ sr_err("Failed to read data (%d): %s.",
+ bytes_read, ftdi_get_error_string(devc->ftdic));
+ } else if (bytes_read != size) {
+ // sr_err("Failed to read data, only %d/%d bytes read.",
+ // bytes_read, size);
+ }
+
+ return bytes_read;
+}
+
+/**
+ * Close the USB port and reset the sequencer logic.
+ *
+ * @param devc The struct containing private per-device-instance data.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
+ */
+static int close_usb_reset_sequencer(struct dev_context *devc)
+{
+ /* Magic sequence of bytes for resetting the sequencer logic. */
+ uint8_t buf[8] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+ int ret;
+
+ /* Note: Caller checked that devc and devc->ftdic != NULL. */
+
+ if (devc->ftdic->usb_dev) {
+ /* Reset the sequencer logic, then wait 100ms. */
+ sr_dbg("Resetting sequencer logic.");
+ (void) cv_write(devc, buf, 8); /* Ignore errors. */
+ g_usleep(100 * 1000);
+
+ /* Purge FTDI buffers, then reset and close the FTDI device. */
+ sr_dbg("Purging buffers, resetting+closing FTDI device.");
+
+ /* Log errors, but ignore them (i.e., don't abort). */
+ if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0)
+ sr_err("Failed to purge FTDI buffers (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ if ((ret = ftdi_usb_reset(devc->ftdic)) < 0)
+ sr_err("Failed to reset FTDI device (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ if ((ret = ftdi_usb_close(devc->ftdic)) < 0)
+ sr_err("Failed to close FTDI device (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ }
+
+ /* Close USB device, deinitialize and free the FTDI context. */
+ ftdi_free(devc->ftdic);
+ devc->ftdic = NULL;
+
+ return SR_OK;
+}
+
+/**
+ * Reset the ChronoVu device.
+ *
+ * A reset is required after a failed read/write operation or upon timeouts.
+ *
+ * @param devc The struct containing private per-device-instance data.
+ *
+ * @return SR_OK upon success, SR_ERR upon failure.
+ */
+static int reset_device(struct dev_context *devc)
+{
+ uint8_t buf[BS];
+ gint64 done, now;
+ int bytes_read;
+
+ /* Note: Caller checked that devc and devc->ftdic != NULL. */
+
+ sr_dbg("Resetting the device.");
+
+ /*
+ * Purge pending read data from the FTDI hardware FIFO until
+ * no more data is left, or a timeout occurs (after 20s).
+ */
+ done = (20 * G_TIME_SPAN_SECOND) + g_get_monotonic_time();
+ do {
+ /* Try to read bytes until none are left (or errors occur). */
+ bytes_read = cv_read(devc, (uint8_t *)&buf, BS);
+ now = g_get_monotonic_time();
+ } while ((done > now) && (bytes_read > 0));
+
+ /* Reset the sequencer logic and close the USB port. */
+ (void) close_usb_reset_sequencer(devc); /* Ignore errors. */
+
+ sr_dbg("Device reset finished.");
+
+ return SR_OK;
+}
+
+SR_PRIV int cv_convert_trigger(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_trigger *trigger;
+ struct sr_trigger_stage *stage;
+ struct sr_trigger_match *match;
+ const GSList *l, *m;
+ uint16_t channel_bit;
+
+ devc = sdi->priv;
+ devc->trigger_pattern = 0x0000; /* Default to "low" trigger. */
+ devc->trigger_mask = 0x0000; /* Default to "don't care". */
+ devc->trigger_edgemask = 0x0000; /* Default to "state triggered". */
+
+ if (!(trigger = sr_session_trigger_get(sdi->session)))
+ return SR_OK;
+
+ if (g_slist_length(trigger->stages) > 1) {
+ sr_err("This device only supports 1 trigger stage.");
+ return SR_ERR;
+ }
+
+ for (l = trigger->stages; l; l = l->next) {
+ stage = l->data;
+ for (m = stage->matches; m; m = m->next) {
+ match = m->data;
+ if (!match->channel->enabled)
+ /* Ignore disabled channels with a trigger. */
+ continue;
+ if (devc->prof->model == CHRONOVU_LA8 &&
+ (match->match == SR_TRIGGER_RISING
+ || match->match == SR_TRIGGER_FALLING)) {
+ sr_err("This model supports only simple triggers.");
+ return SR_ERR;
+ }
+ channel_bit = (1 << (match->channel->index));
+
+ /* state: 1 == high, edge: 1 == rising edge. */
+ if (match->match == SR_TRIGGER_ONE
+ || match->match == SR_TRIGGER_RISING)
+ devc->trigger_pattern |= channel_bit;
+
+ /* LA16 (but not LA8) supports edge triggering. */
+ if ((devc->prof->model == CHRONOVU_LA16)) {
+ if (match->match == SR_TRIGGER_RISING
+ || match->match == SR_TRIGGER_FALLING)
+ devc->trigger_edgemask |= channel_bit;
+ }
+ }
+ }
+
+ sr_dbg("Trigger pattern/mask/edgemask = 0x%04x / 0x%04x / 0x%04x.",
+ devc->trigger_pattern, devc->trigger_mask,
+ devc->trigger_edgemask);
+
+ return SR_OK;
+}
+
+SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate)
+{
+ struct dev_context *devc;
+
+ /* Note: Caller checked that sdi and sdi->priv != NULL. */
+
+ devc = sdi->priv;
+
+ sr_spew("Trying to set samplerate to %" PRIu64 "Hz.", samplerate);
+
+ cv_fill_samplerates_if_needed(sdi);
+
+ /* Check if this is a samplerate supported by the hardware. */
+ if (!is_valid_samplerate(sdi, samplerate)) {
+ sr_dbg("Failed to set invalid samplerate (%" PRIu64 "Hz).",
+ samplerate);
+ return SR_ERR;
+ }
+
+ devc->cur_samplerate = samplerate;
+
+ sr_dbg("Samplerate set to %" PRIu64 "Hz.", devc->cur_samplerate);
+
+ return SR_OK;
+}
+
+/**
+ * Get a block of data from the device.
+ *
+ * @param devc The struct containing private per-device-instance data. Must not
+ * be NULL. devc->ftdic must not be NULL either.
+ *
+ * @return SR_OK upon success, or SR_ERR upon errors.
+ */
+SR_PRIV int cv_read_block(struct dev_context *devc)
+{
+ int i, byte_offset, m, mi, p, q, index, bytes_read;
+ gint64 now;
+
+ /* Note: Caller checked that devc and devc->ftdic != NULL. */
+
+ sr_spew("Reading block %d.", devc->block_counter);
+
+ bytes_read = cv_read(devc, devc->mangled_buf, BS);
+
+ /* If first block read got 0 bytes, retry until success or timeout. */
+ if ((bytes_read == 0) && (devc->block_counter == 0)) {
+ do {
+ sr_spew("Reading block 0 (again).");
+ /* Note: If bytes_read < 0 cv_read() will log errors. */
+ bytes_read = cv_read(devc, devc->mangled_buf, BS);
+ now = g_get_monotonic_time();
+ } while ((devc->done > now) && (bytes_read == 0));
+ }
+
+ /* Check if block read was successful or a timeout occured. */
+ if (bytes_read != BS) {
+ sr_err("Trigger timed out. Bytes read: %d.", bytes_read);
+ (void) reset_device(devc); /* Ignore errors. */
+ return SR_ERR;
+ }
+
+ /* De-mangle the data. */
+ sr_spew("Demangling block %d.", devc->block_counter);
+ byte_offset = devc->block_counter * BS;
+ m = byte_offset / (1024 * 1024);
+ mi = m * (1024 * 1024);
+ for (i = 0; i < BS; i++) {
+ if (devc->prof->model == CHRONOVU_LA8) {
+ p = i & (1 << 0);
+ index = m * 2 + (((byte_offset + i) - mi) / 2) * 16;
+ index += (devc->divcount == 0) ? p : (1 - p);
+ } else {
+ p = i & (1 << 0);
+ q = i & (1 << 1);
+ index = m * 4 + (((byte_offset + i) - mi) / 4) * 32;
+ index += q + (1 - p);
+ }
+ devc->final_buf[index] = devc->mangled_buf[i];
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
+{
+ int i, idx;
+ uint8_t sample, expected_sample, tmp8;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ int trigger_point; /* Relative trigger point (in this block). */
+
+ /* Note: Caller ensures devc/devc->ftdic != NULL and block > 0. */
+
+ /* TODO: Implement/test proper trigger support for the LA16. */
+
+ /* Check if we can find the trigger condition in this block. */
+ trigger_point = -1;
+ expected_sample = devc->trigger_pattern & devc->trigger_mask;
+ for (i = 0; i < BS; i++) {
+ /* Don't continue if the trigger was found previously. */
+ if (devc->trigger_found)
+ break;
+
+ /*
+ * Also, don't continue if triggers are "don't care", i.e. if
+ * no trigger conditions were specified by the user. In that
+ * case we don't want to send an SR_DF_TRIGGER packet at all.
+ */
+ if (devc->trigger_mask == 0x0000)
+ break;
+
+ sample = *(devc->final_buf + (block * BS) + i);
+
+ if ((sample & devc->trigger_mask) == expected_sample) {
+ trigger_point = i;
+ devc->trigger_found = 1;
+ break;
+ }
+ }
+
+ /* Swap low and high bytes of the 16-bit LA16 samples. */
+ if (devc->prof->model == CHRONOVU_LA16) {
+ for (i = 0; i < BS; i += 2) {
+ idx = (block * BS) + i;
+ tmp8 = devc->final_buf[idx];
+ devc->final_buf[idx] = devc->final_buf[idx + 1];
+ devc->final_buf[idx + 1] = tmp8;
+ }
+ }
+
+ /* If no trigger was found, send one SR_DF_LOGIC packet. */
+ if (trigger_point == -1) {
+ /* Send an SR_DF_LOGIC packet to the session bus. */
+ sr_spew("Sending SR_DF_LOGIC packet (%d bytes) for "
+ "block %d.", BS, block);
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = BS;
+ logic.unitsize = devc->prof->num_channels / 8;
+ logic.data = devc->final_buf + (block * BS);
+ sr_session_send(devc->cb_data, &packet);
+ return;
+ }
+
+ /*
+ * We found the trigger, so some special handling is needed. We have
+ * to send an SR_DF_LOGIC packet with the samples before the trigger
+ * (if any), then the SD_DF_TRIGGER packet itself, then another
+ * SR_DF_LOGIC packet with the samples after the trigger (if any).
+ */
+
+ /* TODO: Send SR_DF_TRIGGER packet before or after the actual sample? */
+
+ /* If at least one sample is located before the trigger... */
+ if (trigger_point > 0) {
+ /* Send pre-trigger SR_DF_LOGIC packet to the session bus. */
+ sr_spew("Sending pre-trigger SR_DF_LOGIC packet, "
+ "start = %d, length = %d.", block * BS, trigger_point);
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = trigger_point;
+ logic.unitsize = devc->prof->num_channels / 8;
+ logic.data = devc->final_buf + (block * BS);
+ sr_session_send(devc->cb_data, &packet);
+ }
+
+ /* Send the SR_DF_TRIGGER packet to the session bus. */
+ sr_spew("Sending SR_DF_TRIGGER packet, sample = %d.",
+ (block * BS) + trigger_point);
+ packet.type = SR_DF_TRIGGER;
+ packet.payload = NULL;
+ sr_session_send(devc->cb_data, &packet);
+
+ /* If at least one sample is located after the trigger... */
+ if (trigger_point < (BS - 1)) {
+ /* Send post-trigger SR_DF_LOGIC packet to the session bus. */
+ sr_spew("Sending post-trigger SR_DF_LOGIC packet, "
+ "start = %d, length = %d.",
+ (block * BS) + trigger_point, BS - trigger_point);
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = BS - trigger_point;
+ logic.unitsize = devc->prof->num_channels / 8;
+ logic.data = devc->final_buf + (block * BS) + trigger_point;
+ sr_session_send(devc->cb_data, &packet);
+ }
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBSIGROK_HARDWARE_CHRONOVU_LA_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_CHRONOVU_LA_PROTOCOL_H
+
+#include <glib.h>
+#include <ftdi.h>
+#include <stdint.h>
+#include <string.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "la8/la16"
+
+#define SDRAM_SIZE (8 * 1024 * 1024)
+#define MAX_NUM_SAMPLES SDRAM_SIZE
+
+#define BS 4096 /* Block size */
+#define NUM_BLOCKS 2048 /* Number of blocks */
+
+enum {
+ CHRONOVU_LA8,
+ CHRONOVU_LA16,
+};
+
+struct cv_profile {
+ int model;
+ const char *modelname;
+ const char *iproduct; /* USB iProduct string */
+ unsigned int num_channels;
+ uint64_t max_samplerate;
+ const int num_trigger_matches;
+ float trigger_constant;
+};
+
+/* Private, per-device-instance driver context. */
+struct dev_context {
+ /** Device profile struct for this device. */
+ const struct cv_profile *prof;
+
+ /** FTDI device context (used by libftdi). */
+ struct ftdi_context *ftdic;
+
+ /** The currently configured samplerate of the device. */
+ uint64_t cur_samplerate;
+
+ /** The current sampling limit (in ms). */
+ uint64_t limit_msec;
+
+ /** The current sampling limit (in number of samples). */
+ uint64_t limit_samples;
+
+ void *cb_data;
+
+ /**
+ * A buffer containing some (mangled) samples from the device.
+ * Format: Pretty mangled-up (due to hardware reasons), see code.
+ */
+ uint8_t mangled_buf[BS];
+
+ /**
+ * An 8MB buffer where we'll store the de-mangled samples.
+ * LA8: Each sample is 1 byte, MSB is channel 7, LSB is channel 0.
+ * LA16: Each sample is 2 bytes, MSB is channel 15, LSB is channel 0.
+ */
+ uint8_t *final_buf;
+
+ /**
+ * Trigger pattern.
+ * A 1 bit matches a high signal, 0 matches a low signal on a channel.
+ *
+ * If the resp. 'trigger_edgemask' bit is set, 1 means "rising edge",
+ * and 0 means "falling edge".
+ */
+ uint16_t trigger_pattern;
+
+ /**
+ * Trigger mask.
+ * A 1 bit means "must match trigger_pattern", 0 means "don't care".
+ */
+ uint16_t trigger_mask;
+
+ /**
+ * Trigger edge mask.
+ * A 1 bit means "edge triggered", 0 means "state triggered".
+ *
+ * Edge triggering is only supported on LA16 (but not LA8).
+ */
+ uint16_t trigger_edgemask;
+
+ /** Tells us whether an SR_DF_TRIGGER packet was already sent. */
+ int trigger_found;
+
+ /** Used for keeping track how much time has passed. */
+ gint64 done;
+
+ /** Counter/index for the data block to be read. */
+ int block_counter;
+
+ /** The divcount value (determines the sample period). */
+ uint8_t divcount;
+
+ /** This ChronoVu device's USB VID/PID. */
+ uint16_t usb_vid;
+ uint16_t usb_pid;
+
+ /** Samplerates supported by this device. */
+ uint64_t samplerates[255];
+};
+
+/* protocol.c */
+extern SR_PRIV const char *cv_channel_names[];
+extern const struct cv_profile cv_profiles[];
+SR_PRIV void cv_fill_samplerates_if_needed(const struct sr_dev_inst *sdi);
+SR_PRIV uint8_t cv_samplerate_to_divcount(const struct sr_dev_inst *sdi,
+ uint64_t samplerate);
+SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size);
+SR_PRIV int cv_convert_trigger(const struct sr_dev_inst *sdi);
+SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
+SR_PRIV int cv_read_block(struct dev_context *devc);
+SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+/* The Colead SL-5868P uses this. */
+#define SERIALCOMM "2400/8n1"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_SOUNDLEVELMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+SR_PRIV struct sr_dev_driver colead_slm_driver_info;
+static struct sr_dev_driver *di = &colead_slm_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_config *src;
+ struct sr_channel *ch;
+ GSList *devices, *l;
+ const char *conn, *serialcomm;
+
+ drvc = di->priv;
+ drvc->instances = NULL;
+
+ devices = NULL;
+
+ 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 (!conn)
+ return NULL;
+ if (!serialcomm)
+ serialcomm = SERIALCOMM;
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Colead",
+ "SL-5868P", NULL)))
+ return NULL;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_dbg("Device context malloc failed.");
+ return NULL;
+ }
+
+ if (!(sdi->conn = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->priv = devc;
+ sdi->driver = di;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct sr_serial_dev_inst *serial;
+
+ serial = sdi->conn;
+ if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+ return SR_ERR;
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ switch (id) {
+ case SR_CONF_LIMIT_MSEC:
+ /* TODO: not yet implemented */
+ if (g_variant_get_uint64(data) == 0) {
+ sr_err("LIMIT_MSEC can't be 0.");
+ return SR_ERR;
+ }
+ devc->limit_msec = g_variant_get_uint64(data);;
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ devc->cb_data = cb_data;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Poll every 150ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 150,
+ colead_slm_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver colead_slm_driver_info = {
+ .name = "colead-slm",
+ .longname = "Colead SLM",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .config_get = NULL,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = std_serial_dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdlib.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+#include <errno.h>
+#include <string.h>
+
+static void process_packet(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ GString *dbg;
+ float fvalue;
+ int checksum, mode, i;
+
+ devc = sdi->priv;
+ if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+ dbg = g_string_sized_new(128);
+ g_string_printf(dbg, "received packet:");
+ for (i = 0; i < 10; i++)
+ g_string_append_printf(dbg, " %.2x", (devc->buf)[i]);
+ sr_spew("%s", dbg->str);
+ g_string_free(dbg, TRUE);
+ }
+
+ if (devc->buf[0] != 0x08 || devc->buf[1] != 0x04) {
+ sr_dbg("invalid packet header.");
+ return;
+ }
+
+ if (devc->buf[8] != 0x01) {
+ sr_dbg("invalid measurement.");
+ return;
+ }
+
+ checksum = 0;
+ for (i = 0; i < 9; i++)
+ checksum += devc->buf[i];
+ if ((checksum & 0xff) != devc->buf[9]) {
+ sr_dbg("invalid packet checksum.");
+ return;
+ }
+
+ fvalue = 0.0;
+ for (i = 3; i < 8; i++) {
+ if (devc->buf[i] > 0x09)
+ continue;
+ fvalue *= 10;
+ fvalue += devc->buf[i];
+ }
+ fvalue /= 10;
+
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+ analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
+ analog.unit = SR_UNIT_DECIBEL_SPL;
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+ analog.data = &fvalue;
+
+ /* High nibble should only have 0x01 or 0x02. */
+ mode = (devc->buf[2] >> 4) & 0x0f;
+ if (mode == 0x02)
+ analog.mqflags |= SR_MQFLAG_HOLD;
+ else if (mode != 0x01) {
+ sr_dbg("unknown measurement mode 0x%.2x", mode);
+ return;
+ }
+
+ /* Low nibble has 14 combinations of direct/long-term average,
+ * time scale of that average, frequency weighting, and time
+ * weighting. */
+ mode = devc->buf[2] & 0x0f;
+ switch (mode) {
+ case 0x0:
+ analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ break;
+ case 0x1:
+ analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_S;
+ break;
+ case 0x2:
+ analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ break;
+ case 0x3:
+ analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_S;
+ break;
+ case 0x4:
+ analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ break;
+ case 0x5:
+ analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_S;
+ break;
+ case 0x6:
+ analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
+ | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ break;
+ case 0x7:
+ analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
+ | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_S;
+ break;
+ case 0x8:
+ /* 10-second mean, but we don't have MQ flags to express it. */
+ analog.mqflags |= SR_MQFLAG_SPL_LAT \
+ | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ break;
+ case 0x9:
+ /* Mean over a time period between 11 seconds and 24 hours.
+ * Which is so silly that there's no point in expressing
+ * either this or the previous case. */
+ analog.mqflags |= SR_MQFLAG_SPL_LAT \
+ | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ break;
+ case 0xa:
+ /* 10-second mean. */
+ analog.mqflags |= SR_MQFLAG_SPL_LAT \
+ | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_S;
+ break;
+ case 0xb:
+ /* Mean over a time period between 11 seconds and 24 hours. */
+ analog.mqflags |= SR_MQFLAG_SPL_LAT \
+ | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+ | SR_MQFLAG_SPL_TIME_WEIGHT_S;
+ break;
+ case 0xc:
+ /* Internal calibration on 1kHz sine at 94dB, not useful
+ * to anything but the device. */
+ analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
+ break;
+ case 0xd:
+ /* Internal calibration on 1kHz sine at 94dB, not useful
+ * to anything but the device. */
+ analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
+ break;
+ default:
+ sr_dbg("unknown configuration 0x%.2x", mode);
+ return;
+ break;
+ }
+
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+
+ devc->num_samples++;
+
+}
+
+SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
+{
+ const struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int len;
+ char buf[128];
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ if (revents != G_IO_IN)
+ /* Timeout event. */
+ return TRUE;
+
+ serial = sdi->conn;
+ if (devc->state == IDLE) {
+ if (serial_read(serial, buf, 128) != 1 || buf[0] != 0x10)
+ /* Nothing there, or caught the tail end of a previous packet,
+ * or some garbage. Unless it's a single "data ready" byte,
+ * we don't want it. */
+ return TRUE;
+ /* Got 0x10, "measurement ready". */
+ if (serial_write(serial, "\x20", 1) == -1)
+ sr_err("unable to send command: %s", strerror(errno));
+ else {
+ devc->state = COMMAND_SENT;
+ devc->buflen = 0;
+ }
+ } else {
+ len = serial_read(serial, devc->buf + devc->buflen,
+ 10 - devc->buflen);
+ if (len < 1)
+ return TRUE;
+ devc->buflen += len;
+ if (devc->buflen > 10) {
+ sr_dbg("buffer overrun");
+ devc->state = IDLE;
+ return TRUE;
+ }
+ if (devc->buflen == 10) {
+ /* If we got here, we're sure the device sent a "data ready"
+ * notification, we asked for data, and got it. */
+ process_packet(sdi);
+ devc->state = IDLE;
+ }
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_COLEAD_SLM_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_COLEAD_SLM_PROTOCOL_H
+
+#include <stdint.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "colead-slm"
+
+enum {
+ IDLE,
+ COMMAND_SENT,
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /** The current sampling limit (in number of samples). */
+ uint64_t limit_samples;
+
+ /** The current sampling limit (in ms). */
+ uint64_t limit_msec;
+
+ /** Opaque pointer passed in by the frontend. */
+ void *cb_data;
+
+ /** The current number of already received samples. */
+ uint64_t num_samples;
+ int state;
+ char buf[10];
+ int buflen;
+};
+
+SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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/>.
+ */
+
+/** @file
+ * <em>Conrad DIGI 35 CPU</em> power supply driver
+ * @internal
+ */
+
+#include "protocol.h"
+
+#define SERIALCOMM "9600/8n1"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_POWER_SUPPLY,
+ SR_CONF_OUTPUT_VOLTAGE,
+ SR_CONF_OUTPUT_CURRENT,
+ /* There's no SR_CONF_OUTPUT_ENABLED; can't know/set status remotely. */
+ SR_CONF_OVER_CURRENT_PROTECTION,
+};
+
+SR_PRIV struct sr_dev_driver conrad_digi_35_cpu_driver_info;
+static struct sr_dev_driver *di = &conrad_digi_35_cpu_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct sr_config *src;
+ struct sr_channel *ch;
+ struct sr_serial_dev_inst *serial;
+ GSList *l, *devices;
+ const char *conn, *serialcomm;
+
+ devices = NULL;
+ drvc = di->priv;
+ drvc->instances = NULL;
+ 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 (!conn)
+ return NULL;
+ if (!serialcomm)
+ serialcomm = SERIALCOMM;
+
+ /*
+ * We cannot scan for this device because it is write-only.
+ * So just check that the port parameters are valid and assume that
+ * the device is there.
+ */
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ serial_flush(serial);
+ serial_close(serial);
+
+ sr_spew("Conrad DIGI 35 CPU assumed at %s.", conn);
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, "Conrad", "DIGI 35 CPU", NULL)))
+ return NULL;
+
+ sdi->conn = serial;
+ sdi->priv = NULL;
+ sdi->driver = di;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "CH1")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ int ret;
+ double dblval;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_OUTPUT_VOLTAGE:
+ dblval = g_variant_get_double(data);
+ if ((dblval < 0.0) || (dblval > 35.0)) {
+ sr_err("Voltage out of range (0 - 35.0)!");
+ return SR_ERR_ARG;
+ }
+ ret = send_msg1(sdi, 'V', (int) (dblval * 10 + 0.5));
+ break;
+ case SR_CONF_OUTPUT_CURRENT:
+ dblval = g_variant_get_double(data);
+ if ((dblval < 0.01) || (dblval > 2.55)) {
+ sr_err("Current out of range (0 - 2.55)!");
+ return SR_ERR_ARG;
+ }
+ ret = send_msg1(sdi, 'C', (int) (dblval * 100 + 0.5));
+ break;
+ /* No SR_CONF_OUTPUT_ENABLED :-( . */
+ case SR_CONF_OVER_CURRENT_PROTECTION:
+ if (g_variant_get_boolean(data))
+ ret = send_msg1(sdi, 'V', 900);
+ else /* Constant current mode */
+ ret = send_msg1(sdi, 'V', 901);
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ int ret;
+
+ (void)sdi;
+ (void)cg;
+
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver conrad_digi_35_cpu_driver_info = {
+ .name = "conrad-digi-35-cpu",
+ .longname = "Conrad DIGI 35 CPU",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .config_get = NULL,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = std_serial_dev_open,
+ .dev_close = std_serial_dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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/>.
+ */
+
+/**
+ * @file
+ * <em>Conrad DIGI 35 CPU</em> power supply driver
+ * @internal
+ */
+
+#include "protocol.h"
+
+/**
+ * Send command with parameter.
+ *
+ * @param[in] cmd Command
+ * @param[in] param Parameter (0..999, depending on command).
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR Error.
+ */
+SR_PRIV int send_msg1(const struct sr_dev_inst *sdi, char cmd, int param)
+{
+ struct sr_serial_dev_inst *serial;
+ char buf[5];
+
+ if (!sdi || !(serial = sdi->conn))
+ return SR_ERR_ARG;
+
+ snprintf(buf, sizeof(buf), "%c%03d", cmd, param);
+ buf[4] = '\r';
+
+ sr_spew("send_msg1(): %c%c%c%c\\r", buf[0], buf[1], buf[2], buf[3]);
+
+ if (serial_write(serial, buf, sizeof(buf)) == -1) {
+ sr_err("Write error for cmd=%c: %d %s", cmd, errno, strerror(errno));
+ return SR_ERR;
+ }
+
+ /*
+ * Wait 50ms to ensure that the device does not swallow any of the
+ * following commands.
+ */
+ g_usleep(50000);
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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/>.
+ */
+
+/**
+ * @file
+ * <em>Conrad DIGI 35 CPU</em> power supply driver
+ * @internal
+ */
+
+#ifndef LIBSIGROK_HARDWARE_CONRAD_DIGI_35_CPU_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_CONRAD_DIGI_35_CPU_PROTOCOL_H
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "conrad-digi-35-cpu"
+
+SR_PRIV int send_msg1(const struct sr_dev_inst *sdi, char cmd, int param);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2011 Olivier Fauchon <olivier@aixmarseille.com>
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#ifdef _WIN32
+#include <io.h>
+#include <fcntl.h>
+#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
+#endif
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "demo"
+
+#define DEFAULT_NUM_LOGIC_CHANNELS 8
+#define DEFAULT_NUM_ANALOG_CHANNELS 4
+
+/* The size in bytes of chunks to send through the session bus. */
+#define LOGIC_BUFSIZE 4096
+/* Size of the analog pattern space per channel. */
+#define ANALOG_BUFSIZE 4096
+
+#define ANALOG_AMPLITUDE 25
+#define ANALOG_SAMPLES_PER_PERIOD 20
+
+/* Logic patterns we can generate. */
+enum {
+ /**
+ * Spells "sigrok" across 8 channels using '0's (with '1's as
+ * "background") when displayed using the 'bits' output format.
+ * The pattern is repeated every 8 channels, shifted to the right
+ * in time by one bit.
+ */
+ PATTERN_SIGROK,
+
+ /** Pseudo-random values on all channels. */
+ PATTERN_RANDOM,
+
+ /**
+ * Incrementing number across 8 channels. The pattern is repeated
+ * every 8 channels, shifted to the right in time by one bit.
+ */
+ PATTERN_INC,
+
+ /** All channels have a low logic state. */
+ PATTERN_ALL_LOW,
+
+ /** All channels have a high logic state. */
+ PATTERN_ALL_HIGH,
+};
+
+/* Analog patterns we can generate. */
+enum {
+ /**
+ * Square wave.
+ */
+ PATTERN_SQUARE,
+ PATTERN_SINE,
+ PATTERN_TRIANGLE,
+ PATTERN_SAWTOOTH,
+};
+
+static const char *logic_pattern_str[] = {
+ "sigrok",
+ "random",
+ "incremental",
+ "all-low",
+ "all-high",
+};
+
+static const char *analog_pattern_str[] = {
+ "square",
+ "sine",
+ "triangle",
+ "sawtooth",
+};
+
+struct analog_gen {
+ int pattern;
+ float pattern_data[ANALOG_BUFSIZE];
+ unsigned int num_samples;
+ struct sr_datafeed_analog packet;
+};
+
+/* Private, per-device-instance driver context. */
+struct dev_context {
+ int pipe_fds[2];
+ GIOChannel *channel;
+ uint64_t cur_samplerate;
+ uint64_t limit_samples;
+ uint64_t limit_msec;
+ uint64_t logic_counter;
+ uint64_t analog_counter;
+ int64_t starttime;
+ uint64_t step;
+ /* Logic */
+ int32_t num_logic_channels;
+ unsigned int logic_unitsize;
+ /* There is only ever one logic channel group, so its pattern goes here. */
+ uint8_t logic_pattern;
+ unsigned char logic_data[LOGIC_BUFSIZE];
+ /* Analog */
+ int32_t num_analog_channels;
+ GSList *analog_channel_groups;
+};
+
+static const int32_t scanopts[] = {
+ SR_CONF_NUM_LOGIC_CHANNELS,
+ SR_CONF_NUM_ANALOG_CHANNELS,
+};
+
+static const int devopts[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_DEMO_DEV,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+};
+
+static const int devopts_cg[] = {
+ SR_CONF_PATTERN_MODE,
+};
+
+static const uint64_t samplerates[] = {
+ SR_HZ(1),
+ SR_GHZ(1),
+ SR_HZ(1),
+};
+
+static uint8_t pattern_sigrok[] = {
+ 0x4c, 0x92, 0x92, 0x92, 0x64, 0x00, 0x00, 0x00,
+ 0x82, 0xfe, 0xfe, 0x82, 0x00, 0x00, 0x00, 0x00,
+ 0x7c, 0x82, 0x82, 0x92, 0x74, 0x00, 0x00, 0x00,
+ 0xfe, 0x12, 0x12, 0x32, 0xcc, 0x00, 0x00, 0x00,
+ 0x7c, 0x82, 0x82, 0x82, 0x7c, 0x00, 0x00, 0x00,
+ 0xfe, 0x10, 0x28, 0x44, 0x82, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xbe, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+SR_PRIV struct sr_dev_driver demo_driver_info;
+static struct sr_dev_driver *di = &demo_driver_info;
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
+
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static void generate_analog_pattern(const struct sr_channel_group *cg, uint64_t sample_rate)
+{
+ struct analog_gen *ag;
+ double t, frequency;
+ float value;
+ unsigned int num_samples, i;
+ int last_end;
+
+ ag = cg->priv;
+ num_samples = ANALOG_BUFSIZE / sizeof(float);
+
+ sr_dbg("Generating %s pattern for channel group %s",
+ analog_pattern_str[ag->pattern], cg->name);
+
+ switch (ag->pattern) {
+ case PATTERN_SQUARE:
+ value = ANALOG_AMPLITUDE;
+ last_end = 0;
+ for (i = 0; i < num_samples; i++) {
+ if (i % 5 == 0)
+ value = -value;
+ if (i % 10 == 0)
+ last_end = i - 1;
+ ag->pattern_data[i] = value;
+ }
+ ag->num_samples = last_end;
+ break;
+
+ case PATTERN_SINE:
+ frequency = (double) sample_rate / ANALOG_SAMPLES_PER_PERIOD;
+
+ /* Make sure the number of samples we put out is an integer
+ * multiple of our period size */
+ /* FIXME we actually need only one period. A ringbuffer would be
+ * usefull here.*/
+ while (num_samples % ANALOG_SAMPLES_PER_PERIOD != 0)
+ num_samples--;
+
+ for (i = 0; i < num_samples; i++) {
+ t = (double) i / (double) sample_rate;
+ ag->pattern_data[i] = ANALOG_AMPLITUDE *
+ sin(2 * M_PI * frequency * t);
+ }
+
+ ag->num_samples = num_samples;
+ break;
+
+ case PATTERN_TRIANGLE:
+ frequency = (double) sample_rate / ANALOG_SAMPLES_PER_PERIOD;
+
+ while (num_samples % ANALOG_SAMPLES_PER_PERIOD != 0)
+ num_samples--;
+
+ for (i = 0; i < num_samples; i++) {
+ t = (double) i / (double) sample_rate;
+ ag->pattern_data[i] = (2 * ANALOG_AMPLITUDE / M_PI) *
+ asin(sin(2 * M_PI * frequency * t));
+ }
+
+ ag->num_samples = num_samples;
+ break;
+
+ case PATTERN_SAWTOOTH:
+ frequency = (double) sample_rate / ANALOG_SAMPLES_PER_PERIOD;
+
+ while (num_samples % ANALOG_SAMPLES_PER_PERIOD != 0)
+ num_samples--;
+
+ for (i = 0; i < num_samples; i++) {
+ t = (double) i / (double) sample_rate;
+ ag->pattern_data[i] = 2 * ANALOG_AMPLITUDE *
+ ((t * frequency) - floor(0.5f + t * frequency));
+ }
+
+ ag->num_samples = num_samples;
+ break;
+ }
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ struct sr_channel_group *cg;
+ struct sr_config *src;
+ struct analog_gen *ag;
+ GSList *devices, *l;
+ int num_logic_channels, num_analog_channels, pattern, i;
+ char channel_name[16];
+
+ drvc = di->priv;
+
+ num_logic_channels = DEFAULT_NUM_LOGIC_CHANNELS;
+ num_analog_channels = DEFAULT_NUM_ANALOG_CHANNELS;
+ for (l = options; l; l = l->next) {
+ src = l->data;
+ switch (src->key) {
+ case SR_CONF_NUM_LOGIC_CHANNELS:
+ num_logic_channels = g_variant_get_int32(src->data);
+ break;
+ case SR_CONF_NUM_ANALOG_CHANNELS:
+ num_analog_channels = g_variant_get_int32(src->data);
+ break;
+ }
+ }
+
+ devices = NULL;
+ sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, "Demo device", NULL, NULL);
+ if (!sdi) {
+ sr_err("Device instance creation failed.");
+ return NULL;
+ }
+ sdi->driver = di;
+
+ if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+ devc->cur_samplerate = SR_KHZ(200);
+ devc->limit_samples = 0;
+ devc->limit_msec = 0;
+ devc->step = 0;
+ devc->num_logic_channels = num_logic_channels;
+ devc->logic_unitsize = (devc->num_logic_channels + 7) / 8;
+ devc->logic_pattern = PATTERN_SIGROK;
+ devc->num_analog_channels = num_analog_channels;
+ devc->analog_channel_groups = NULL;
+
+ /* Logic channels, all in one channel group. */
+ if (!(cg = g_try_malloc(sizeof(struct sr_channel_group))))
+ return NULL;
+ cg->name = g_strdup("Logic");
+ cg->channels = NULL;
+ cg->priv = NULL;
+ for (i = 0; i < num_logic_channels; i++) {
+ sprintf(channel_name, "D%d", i);
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, channel_name)))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ cg->channels = g_slist_append(cg->channels, ch);
+ }
+ sdi->channel_groups = g_slist_append(NULL, cg);
+
+ /* Analog channels, channel groups and pattern generators. */
+
+ pattern = 0;
+ for (i = 0; i < num_analog_channels; i++) {
+ sprintf(channel_name, "A%d", i);
+ if (!(ch = sr_channel_new(i + num_logic_channels,
+ SR_CHANNEL_ANALOG, TRUE, channel_name)))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ /* Every analog channel gets its own channel group. */
+ if (!(cg = g_try_malloc(sizeof(struct sr_channel_group))))
+ return NULL;
+ cg->name = g_strdup(channel_name);
+ cg->channels = g_slist_append(NULL, ch);
+
+ /* Every channel group gets a generator struct. */
+ if (!(ag = g_try_malloc(sizeof(struct analog_gen))))
+ return NULL;
+ ag->packet.channels = cg->channels;
+ ag->packet.mq = 0;
+ ag->packet.mqflags = 0;
+ ag->packet.unit = SR_UNIT_VOLT;
+ ag->packet.data = ag->pattern_data;
+ ag->pattern = pattern;
+ cg->priv = ag;
+
+ sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+ devc->analog_channel_groups = g_slist_append(devc->analog_channel_groups, cg);
+
+ if (++pattern == ARRAY_SIZE(analog_pattern_str))
+ pattern = 0;
+ }
+
+ sdi->priv = devc;
+ devices = g_slist_append(devices, sdi);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ struct analog_gen *ag;
+ int pattern;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ *data = g_variant_new_uint64(devc->cur_samplerate);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ *data = g_variant_new_uint64(devc->limit_msec);
+ break;
+ case SR_CONF_PATTERN_MODE:
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ ch = cg->channels->data;
+ if (ch->type == SR_CHANNEL_LOGIC) {
+ pattern = devc->logic_pattern;
+ *data = g_variant_new_string(logic_pattern_str[pattern]);
+ } else if (ch->type == SR_CHANNEL_ANALOG) {
+ ag = cg->priv;
+ pattern = ag->pattern;
+ *data = g_variant_new_string(analog_pattern_str[pattern]);
+ } else
+ return SR_ERR_BUG;
+ break;
+ case SR_CONF_NUM_LOGIC_CHANNELS:
+ *data = g_variant_new_int32(devc->num_logic_channels);
+ break;
+ case SR_CONF_NUM_ANALOG_CHANNELS:
+ *data = g_variant_new_int32(devc->num_analog_channels);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct analog_gen *ag;
+ struct sr_channel *ch;
+ int pattern, ret;
+ unsigned int i;
+ const char *stropt;
+
+ devc = sdi->priv;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ ret = SR_OK;
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ devc->cur_samplerate = g_variant_get_uint64(data);
+ sr_dbg("Setting samplerate to %" PRIu64, devc->cur_samplerate);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_msec = 0;
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64, devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ devc->limit_msec = g_variant_get_uint64(data);
+ devc->limit_samples = 0;
+ sr_dbg("Setting time limit to %" PRIu64"ms", devc->limit_msec);
+ break;
+ case SR_CONF_PATTERN_MODE:
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ stropt = g_variant_get_string(data, NULL);
+ ch = cg->channels->data;
+ pattern = -1;
+ if (ch->type == SR_CHANNEL_LOGIC) {
+ for (i = 0; i < ARRAY_SIZE(logic_pattern_str); i++) {
+ if (!strcmp(stropt, logic_pattern_str[i])) {
+ pattern = i;
+ break;
+ }
+ }
+ if (pattern == -1)
+ return SR_ERR_ARG;
+ devc->logic_pattern = pattern;
+
+ /* Might as well do this now, these are static. */
+ if (pattern == PATTERN_ALL_LOW)
+ memset(devc->logic_data, 0x00, LOGIC_BUFSIZE);
+ else if (pattern == PATTERN_ALL_HIGH)
+ memset(devc->logic_data, 0xff, LOGIC_BUFSIZE);
+ sr_dbg("Setting logic pattern to %s",
+ logic_pattern_str[pattern]);
+ } else if (ch->type == SR_CHANNEL_ANALOG) {
+ for (i = 0; i < ARRAY_SIZE(analog_pattern_str); i++) {
+ if (!strcmp(stropt, analog_pattern_str[i])) {
+ pattern = i;
+ break;
+ }
+ }
+ if (pattern == -1)
+ return SR_ERR_ARG;
+ sr_dbg("Setting analog pattern for channel group %s to %s",
+ cg->name, analog_pattern_str[pattern]);
+ ag = cg->priv;
+ ag->pattern = pattern;
+ } else
+ return SR_ERR_BUG;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct sr_channel *ch;
+ GVariant *gvar;
+ GVariantBuilder gvb;
+
+ (void)sdi;
+
+ if (key == SR_CONF_SCAN_OPTIONS) {
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ scanopts, ARRAY_SIZE(scanopts), sizeof(int32_t));
+ return SR_OK;
+ }
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ if (!cg) {
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
+ ARRAY_SIZE(samplerates), sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ } else {
+ ch = cg->channels->data;
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(int32_t));
+ break;
+ case SR_CONF_PATTERN_MODE:
+ if (ch->type == SR_CHANNEL_LOGIC)
+ *data = g_variant_new_strv(logic_pattern_str,
+ ARRAY_SIZE(logic_pattern_str));
+ else if (ch->type == SR_CHANNEL_ANALOG)
+ *data = g_variant_new_strv(analog_pattern_str,
+ ARRAY_SIZE(analog_pattern_str));
+ else
+ return SR_ERR_BUG;
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ }
+
+ return SR_OK;
+}
+
+static void logic_generator(struct sr_dev_inst *sdi, uint64_t size)
+{
+ struct dev_context *devc;
+ uint64_t i, j;
+ uint8_t pat;
+
+ devc = sdi->priv;
+
+ switch (devc->logic_pattern) {
+ case PATTERN_SIGROK:
+ memset(devc->logic_data, 0x00, size);
+ for (i = 0; i < size; i += devc->logic_unitsize) {
+ for (j = 0; j < devc->logic_unitsize; j++) {
+ pat = pattern_sigrok[(devc->step + j) % sizeof(pattern_sigrok)] >> 1;
+ devc->logic_data[i + j] = ~pat;
+ }
+ devc->step++;
+ }
+ break;
+ case PATTERN_RANDOM:
+ for (i = 0; i < size; i++)
+ devc->logic_data[i] = (uint8_t)(rand() & 0xff);
+ break;
+ case PATTERN_INC:
+ for (i = 0; i < size; i++) {
+ for (j = 0; j < devc->logic_unitsize; j++) {
+ devc->logic_data[i + j] = devc->step;
+ }
+ devc->step++;
+ }
+ break;
+ case PATTERN_ALL_LOW:
+ case PATTERN_ALL_HIGH:
+ /* These were set when the pattern mode was selected. */
+ break;
+ default:
+ sr_err("Unknown pattern: %d.", devc->logic_pattern);
+ break;
+ }
+}
+
+/* Callback handling data */
+static int prepare_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ struct sr_channel_group *cg;
+ struct analog_gen *ag;
+ GSList *l;
+ uint64_t logic_todo, analog_todo, expected_samplenum, analog_samples, sending_now;
+ int64_t time, elapsed;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+ devc = sdi->priv;
+
+ /* How many "virtual" samples should we have collected by now? */
+ time = g_get_monotonic_time();
+ elapsed = time - devc->starttime;
+ expected_samplenum = elapsed * devc->cur_samplerate / 1000000;
+
+ /* Of those, how many do we still have to send? */
+ logic_todo = MIN(expected_samplenum, devc->limit_samples) - devc->logic_counter;
+ analog_todo = MIN(expected_samplenum, devc->limit_samples) - devc->analog_counter;
+
+ while (logic_todo || analog_todo) {
+ /* Logic */
+ if (devc->num_logic_channels > 0 && logic_todo > 0) {
+ sending_now = MIN(logic_todo,
+ LOGIC_BUFSIZE / devc->logic_unitsize);
+ logic_generator(sdi, sending_now * devc->logic_unitsize);
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = sending_now * devc->logic_unitsize;
+ logic.unitsize = devc->logic_unitsize;
+ logic.data = devc->logic_data;
+ sr_session_send(sdi, &packet);
+ logic_todo -= sending_now;
+ devc->logic_counter += sending_now;
+ }
+
+ /* Analog, one channel at a time */
+ if (devc->num_analog_channels > 0 && analog_todo > 0) {
+ sending_now = 0;
+ for (l = devc->analog_channel_groups; l; l = l->next) {
+ cg = l->data;
+ ag = cg->priv;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &ag->packet;
+
+ /* FIXME we should make sure we output a whole
+ * period of data before we send out again the
+ * beginning of our buffer. A ring buffer would
+ * help here as well */
+
+ analog_samples = MIN(analog_todo, ag->num_samples);
+ /* Whichever channel group gets there first. */
+ sending_now = MAX(sending_now, analog_samples);
+ ag->packet.num_samples = analog_samples;
+ sr_session_send(sdi, &packet);
+ }
+ analog_todo -= sending_now;
+ devc->analog_counter += sending_now;
+ }
+ }
+
+ if (devc->logic_counter >= devc->limit_samples &&
+ devc->analog_counter >= devc->limit_samples) {
+ sr_dbg("Requested number of samples reached.");
+ dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ GSList *l;
+ struct dev_context *devc;
+
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ if (devc->limit_samples == 0)
+ return SR_ERR;
+ devc->logic_counter = devc->analog_counter = 0;
+
+ /*
+ * Setting two channels connected by a pipe is a remnant from when the
+ * demo driver generated data in a thread, and collected and sent the
+ * data in the main program loop.
+ * They are kept here because it provides a convenient way of setting
+ * up a timeout-based polling mechanism.
+ */
+ if (pipe(devc->pipe_fds)) {
+ sr_err("%s: pipe() failed", __func__);
+ return SR_ERR;
+ }
+
+ for (l = devc->analog_channel_groups; l; l = l->next) {
+ generate_analog_pattern(l->data, devc->cur_samplerate);
+ }
+
+ devc->channel = g_io_channel_unix_new(devc->pipe_fds[0]);
+
+ g_io_channel_set_flags(devc->channel, G_IO_FLAG_NONBLOCK, NULL);
+
+ /* Set channel encoding to binary (default is UTF-8). */
+ g_io_channel_set_encoding(devc->channel, NULL, NULL);
+
+ /* Make channels to unbuffered. */
+ g_io_channel_set_buffered(devc->channel, FALSE);
+
+ sr_session_source_add_channel(sdi->session, devc->channel, G_IO_IN | G_IO_ERR,
+ 40, prepare_data, (void *)sdi);
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(sdi, LOG_PREFIX);
+
+ /* We use this timestamp to decide how many more samples to send. */
+ devc->starttime = g_get_monotonic_time();
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+
+ (void)cb_data;
+
+ devc = sdi->priv;
+ sr_dbg("Stopping acquisition.");
+
+ sr_session_source_remove_channel(sdi->session, devc->channel);
+ g_io_channel_shutdown(devc->channel, FALSE, NULL);
+ g_io_channel_unref(devc->channel);
+ devc->channel = NULL;
+
+ /* Send last packet. */
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver demo_driver_info = {
+ .name = "demo",
+ .longname = "Demo driver and pattern generator",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <glib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "fluke-dmm.h"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+SR_PRIV struct sr_dev_driver flukedmm_driver_info;
+static struct sr_dev_driver *di = &flukedmm_driver_info;
+
+static char *scan_conn[] = {
+ /* 287/289 */
+ "115200/8n1",
+ /* 187/189 */
+ "9600/8n1",
+ /* Scopemeter 190 series */
+ "1200/8n1",
+ NULL
+};
+
+static const struct flukedmm_profile supported_flukedmm[] = {
+ { FLUKE_187, "187", 100, 1000 },
+ { FLUKE_189, "189", 100, 1000 },
+ { FLUKE_287, "287", 100, 1000 },
+ { FLUKE_190, "199B", 1000, 3500 },
+};
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *fluke_scan(const char *conn, const char *serialcomm)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ struct sr_serial_dev_inst *serial;
+ GSList *devices;
+ int retry, len, i, s;
+ char buf[128], *b, **tokens;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ drvc = di->priv;
+ b = buf;
+ retry = 0;
+ devices = NULL;
+ /* We'll try the discovery sequence three times in case the device
+ * is not in an idle state when we send ID. */
+ while (!devices && retry < 3) {
+ retry++;
+ serial_flush(serial);
+ if (serial_write(serial, "ID\r", 3) == -1) {
+ sr_err("Unable to send ID string: %s.",
+ strerror(errno));
+ continue;
+ }
+
+ /* Response is first a CMD_ACK byte (ASCII '0' for OK,
+ * or '1' to signify an error. */
+ len = 128;
+ serial_readline(serial, &b, &len, 150);
+ if (len != 1)
+ continue;
+ if (buf[0] != '0')
+ continue;
+
+ /* If CMD_ACK was OK, ID string follows. */
+ len = 128;
+ serial_readline(serial, &b, &len, 850);
+ if (len < 10)
+ continue;
+ if (strcspn(buf, ",") < 15)
+ /* Looks like it's comma-separated. */
+ tokens = g_strsplit(buf, ",", 3);
+ else
+ /* Fluke 199B, at least, uses semicolon. */
+ tokens = g_strsplit(buf, ";", 3);
+ if (!strncmp("FLUKE", tokens[0], 5)
+ && tokens[1] && tokens[2]) {
+ for (i = 0; supported_flukedmm[i].model; i++) {
+ if (strcmp(supported_flukedmm[i].modelname, tokens[0] + 6))
+ continue;
+ /* Skip leading spaces in version number. */
+ for (s = 0; tokens[1][s] == ' '; s++);
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Fluke",
+ tokens[0] + 6, tokens[1] + s)))
+ return NULL;
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+ devc->profile = &supported_flukedmm[i];
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+ sdi->priv = devc;
+ sdi->driver = di;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ break;
+ }
+ }
+ g_strfreev(tokens);
+ if (devices)
+ /* Found one. */
+ break;
+ }
+ serial_close(serial);
+ if (!devices)
+ sr_serial_dev_inst_free(serial);
+
+ return devices;
+}
+
+static GSList *scan(GSList *options)
+{
+ struct sr_config *src;
+ GSList *l, *devices;
+ int i;
+ const char *conn, *serialcomm;
+
+ 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 (!conn)
+ return NULL;
+
+ if (serialcomm) {
+ /* Use the provided comm specs. */
+ devices = fluke_scan(conn, serialcomm);
+ } else {
+ for (i = 0; scan_conn[i]; i++) {
+ if ((devices = fluke_scan(conn, scan_conn[i])))
+ break;
+ /* The Scopemeter 199B, at least, requires this
+ * after all the 115k/9.6k confusion. */
+ g_usleep(5000);
+ }
+ }
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ switch (id) {
+ case SR_CONF_LIMIT_MSEC:
+ /* TODO: not yet implemented */
+ if (g_variant_get_uint64(data) == 0) {
+ sr_err("LIMIT_MSEC can't be 0.");
+ return SR_ERR;
+ }
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ devc->cb_data = cb_data;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Poll every 100ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 50,
+ fluke_receive_data, (void *)sdi);
+
+ if (serial_write(serial, "QM\r", 3) == -1) {
+ sr_err("Unable to send QM: %s.", strerror(errno));
+ return SR_ERR;
+ }
+ devc->cmd_sent_at = g_get_monotonic_time() / 1000;
+ devc->expect_response = TRUE;
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver flukedmm_driver_info = {
+ .name = "fluke-dmm",
+ .longname = "Fluke 18x/28x series DMMs",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .config_get = NULL,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = std_serial_dev_open,
+ .dev_close = std_serial_dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_FLUKE_DMM_FLUKE_DMM_H
+#define LIBSIGROK_HARDWARE_FLUKE_DMM_FLUKE_DMM_H
+
+#define LOG_PREFIX "fluke-dmm"
+
+#define FLUKEDMM_BUFSIZE 256
+
+/* Supported models */
+enum {
+ FLUKE_187 = 1,
+ FLUKE_189,
+ FLUKE_287,
+ FLUKE_190,
+};
+
+/* Supported device profiles */
+struct flukedmm_profile {
+ int model;
+ const char *modelname;
+ /* How often to poll, in ms. */
+ int poll_period;
+ /* If no response received, how long to wait before retrying. */
+ int timeout;
+};
+
+/* Private, per-device-instance driver context. */
+struct dev_context {
+ const struct flukedmm_profile *profile;
+ uint64_t limit_samples;
+ uint64_t limit_msec;
+
+ /* Opaque pointer passed in by the frontend. */
+ void *cb_data;
+
+ /* Runtime. */
+ uint64_t num_samples;
+ char buf[FLUKEDMM_BUFSIZE];
+ int buflen;
+ int64_t cmd_sent_at;
+ int expect_response;
+ int meas_type;
+ int is_relative;
+ int mq;
+ int unit;
+ int mqflags;
+};
+
+SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "fluke-dmm.h"
+
+static struct sr_datafeed_analog *handle_qm_18x(const struct sr_dev_inst *sdi,
+ char **tokens)
+{
+ struct sr_datafeed_analog *analog;
+ float fvalue;
+ char *e, *u;
+ gboolean is_oor;
+
+ if (strcmp(tokens[0], "QM") || !tokens[1])
+ return NULL;
+
+ if ((e = strstr(tokens[1], "Out of range"))) {
+ is_oor = TRUE;
+ fvalue = -1;
+ while(*e && *e != '.')
+ e++;
+ } else {
+ is_oor = FALSE;
+ /* Delimit the float, since sr_atof_ascii() wants only
+ * a valid float here. */
+ e = tokens[1];
+ while(*e && *e != ' ')
+ e++;
+ *e++ = '\0';
+ if (sr_atof_ascii(tokens[1], &fvalue) != SR_OK || fvalue == 0.0) {
+ /* Happens all the time, when switching modes. */
+ sr_dbg("Invalid float.");
+ return NULL;
+ }
+ }
+ while(*e && *e == ' ')
+ e++;
+
+ if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog))))
+ return NULL;
+ if (!(analog->data = g_try_malloc(sizeof(float))))
+ return NULL;
+ analog->channels = sdi->channels;
+ analog->num_samples = 1;
+ if (is_oor)
+ *analog->data = NAN;
+ else
+ *analog->data = fvalue;
+ analog->mq = -1;
+
+ if ((u = strstr(e, "V DC")) || (u = strstr(e, "V AC"))) {
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ if (!is_oor && e[0] == 'm')
+ *analog->data /= 1000;
+ /* This catches "V AC", "V DC" and "V AC+DC". */
+ if (strstr(u, "AC"))
+ analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
+ if (strstr(u, "DC"))
+ analog->mqflags |= SR_MQFLAG_DC;
+ } else if ((u = strstr(e, "dBV")) || (u = strstr(e, "dBm"))) {
+ analog->mq = SR_MQ_VOLTAGE;
+ if (u[2] == 'm')
+ analog->unit = SR_UNIT_DECIBEL_MW;
+ else
+ analog->unit = SR_UNIT_DECIBEL_VOLT;
+ analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
+ } else if ((u = strstr(e, "Ohms"))) {
+ analog->mq = SR_MQ_RESISTANCE;
+ analog->unit = SR_UNIT_OHM;
+ if (is_oor)
+ *analog->data = INFINITY;
+ else if (e[0] == 'k')
+ *analog->data *= 1000;
+ else if (e[0] == 'M')
+ *analog->data *= 1000000;
+ } else if (!strcmp(e, "nS")) {
+ analog->mq = SR_MQ_CONDUCTANCE;
+ analog->unit = SR_UNIT_SIEMENS;
+ *analog->data /= 1e+9;
+ } else if ((u = strstr(e, "Farads"))) {
+ analog->mq = SR_MQ_CAPACITANCE;
+ analog->unit = SR_UNIT_FARAD;
+ if (!is_oor) {
+ if (e[0] == 'm')
+ *analog->data /= 1e+3;
+ else if (e[0] == 'u')
+ *analog->data /= 1e+6;
+ else if (e[0] == 'n')
+ *analog->data /= 1e+9;
+ }
+ } else if ((u = strstr(e, "Deg C")) || (u = strstr(e, "Deg F"))) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ if (u[4] == 'C')
+ analog->unit = SR_UNIT_CELSIUS;
+ else
+ analog->unit = SR_UNIT_FAHRENHEIT;
+ } else if ((u = strstr(e, "A AC")) || (u = strstr(e, "A DC"))) {
+ analog->mq = SR_MQ_CURRENT;
+ analog->unit = SR_UNIT_AMPERE;
+ /* This catches "A AC", "A DC" and "A AC+DC". */
+ if (strstr(u, "AC"))
+ analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
+ if (strstr(u, "DC"))
+ analog->mqflags |= SR_MQFLAG_DC;
+ if (!is_oor) {
+ if (e[0] == 'm')
+ *analog->data /= 1e+3;
+ else if (e[0] == 'u')
+ *analog->data /= 1e+6;
+ }
+ } else if ((u = strstr(e, "Hz"))) {
+ analog->mq = SR_MQ_FREQUENCY;
+ analog->unit = SR_UNIT_HERTZ;
+ if (e[0] == 'k')
+ *analog->data *= 1e+3;
+ } else if (!strcmp(e, "%")) {
+ analog->mq = SR_MQ_DUTY_CYCLE;
+ analog->unit = SR_UNIT_PERCENTAGE;
+ } else if ((u = strstr(e, "ms"))) {
+ analog->mq = SR_MQ_PULSE_WIDTH;
+ analog->unit = SR_UNIT_SECOND;
+ *analog->data /= 1e+3;
+ }
+
+ if (analog->mq == -1) {
+ /* Not a valid measurement. */
+ g_free(analog->data);
+ g_free(analog);
+ analog = NULL;
+ }
+
+ return analog;
+}
+
+static struct sr_datafeed_analog *handle_qm_28x(const struct sr_dev_inst *sdi,
+ char **tokens)
+{
+ struct sr_datafeed_analog *analog;
+ float fvalue;
+
+ if (!tokens[1])
+ return NULL;
+
+ if (sr_atof_ascii(tokens[0], &fvalue) != SR_OK || fvalue == 0.0) {
+ sr_err("Invalid float '%s'.", tokens[0]);
+ return NULL;
+ }
+
+ if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog))))
+ return NULL;
+ if (!(analog->data = g_try_malloc(sizeof(float))))
+ return NULL;
+ analog->channels = sdi->channels;
+ analog->num_samples = 1;
+ *analog->data = fvalue;
+ analog->mq = -1;
+
+ if (!strcmp(tokens[1], "VAC") || !strcmp(tokens[1], "VDC")) {
+ analog->mq = SR_MQ_VOLTAGE;
+ analog->unit = SR_UNIT_VOLT;
+ if (!strcmp(tokens[2], "NORMAL")) {
+ if (tokens[1][1] == 'A') {
+ analog->mqflags |= SR_MQFLAG_AC;
+ analog->mqflags |= SR_MQFLAG_RMS;
+ } else
+ analog->mqflags |= SR_MQFLAG_DC;
+ } else if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
+ *analog->data = NAN;
+ } else
+ analog->mq = -1;
+ } else if (!strcmp(tokens[1], "dBV") || !strcmp(tokens[1], "dBm")) {
+ analog->mq = SR_MQ_VOLTAGE;
+ if (tokens[1][2] == 'm')
+ analog->unit = SR_UNIT_DECIBEL_MW;
+ else
+ analog->unit = SR_UNIT_DECIBEL_VOLT;
+ analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
+ } else if (!strcmp(tokens[1], "CEL") || !strcmp(tokens[1], "FAR")) {
+ if (!strcmp(tokens[2], "NORMAL")) {
+ analog->mq = SR_MQ_TEMPERATURE;
+ if (tokens[1][0] == 'C')
+ analog->unit = SR_UNIT_CELSIUS;
+ else
+ analog->unit = SR_UNIT_FAHRENHEIT;
+ }
+ } else if (!strcmp(tokens[1], "OHM")) {
+ if (!strcmp(tokens[3], "NONE")) {
+ analog->mq = SR_MQ_RESISTANCE;
+ analog->unit = SR_UNIT_OHM;
+ if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
+ *analog->data = INFINITY;
+ } else if (strcmp(tokens[2], "NORMAL"))
+ analog->mq = -1;
+ } else if (!strcmp(tokens[3], "OPEN_CIRCUIT")) {
+ analog->mq = SR_MQ_CONTINUITY;
+ analog->unit = SR_UNIT_BOOLEAN;
+ *analog->data = 0.0;
+ } else if (!strcmp(tokens[3], "SHORT_CIRCUIT")) {
+ analog->mq = SR_MQ_CONTINUITY;
+ analog->unit = SR_UNIT_BOOLEAN;
+ *analog->data = 1.0;
+ }
+ } else if (!strcmp(tokens[1], "F")
+ && !strcmp(tokens[2], "NORMAL")
+ && !strcmp(tokens[3], "NONE")) {
+ analog->mq = SR_MQ_CAPACITANCE;
+ analog->unit = SR_UNIT_FARAD;
+ } else if (!strcmp(tokens[1], "AAC") || !strcmp(tokens[1], "ADC")) {
+ analog->mq = SR_MQ_CURRENT;
+ analog->unit = SR_UNIT_AMPERE;
+ if (!strcmp(tokens[2], "NORMAL")) {
+ if (tokens[1][1] == 'A') {
+ analog->mqflags |= SR_MQFLAG_AC;
+ analog->mqflags |= SR_MQFLAG_RMS;
+ } else
+ analog->mqflags |= SR_MQFLAG_DC;
+ } else if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
+ *analog->data = NAN;
+ } else
+ analog->mq = -1;
+ } if (!strcmp(tokens[1], "Hz") && !strcmp(tokens[2], "NORMAL")) {
+ analog->mq = SR_MQ_FREQUENCY;
+ analog->unit = SR_UNIT_HERTZ;
+ } else if (!strcmp(tokens[1], "PCT") && !strcmp(tokens[2], "NORMAL")) {
+ analog->mq = SR_MQ_DUTY_CYCLE;
+ analog->unit = SR_UNIT_PERCENTAGE;
+ } else if (!strcmp(tokens[1], "S") && !strcmp(tokens[2], "NORMAL")) {
+ analog->mq = SR_MQ_PULSE_WIDTH;
+ analog->unit = SR_UNIT_SECOND;
+ } else if (!strcmp(tokens[1], "SIE") && !strcmp(tokens[2], "NORMAL")) {
+ analog->mq = SR_MQ_CONDUCTANCE;
+ analog->unit = SR_UNIT_SIEMENS;
+ }
+
+ if (analog->mq == -1) {
+ /* Not a valid measurement. */
+ g_free(analog->data);
+ g_free(analog);
+ analog = NULL;
+ }
+
+ return analog;
+}
+
+static void handle_qm_19x_meta(const struct sr_dev_inst *sdi, char **tokens)
+{
+ struct dev_context *devc;
+ int meas_type, meas_unit, meas_char, i;
+
+ /* Make sure we have 7 valid tokens. */
+ for (i = 0; tokens[i] && i < 7; i++);
+ if (i != 7)
+ return;
+
+ if (strcmp(tokens[1], "1"))
+ /* Invalid measurement. */
+ return;
+
+ if (strcmp(tokens[2], "3"))
+ /* Only interested in input from the meter mode source. */
+ return;
+
+ devc = sdi->priv;
+
+ /* Measurement type 11 == absolute, 19 = relative */
+ meas_type = strtol(tokens[0], NULL, 10);
+ if (meas_type != 11 && meas_type != 19)
+ /* Device is in some mode we don't support. */
+ return;
+
+ /* We might get metadata for absolute and relative mode (if the device
+ * is in relative mode). In that case, relative takes precedence. */
+ if (meas_type == 11 && devc->meas_type == 19)
+ return;
+
+ meas_unit = strtol(tokens[3], NULL, 10);
+ if (meas_unit == 0)
+ /* Device is turned off. Really. */
+ return;
+ meas_char = strtol(tokens[4], NULL, 10);
+
+ devc->mq = devc->unit = -1;
+ devc->mqflags = 0;
+ switch (meas_unit) {
+ case 1:
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_VOLT;
+ if (meas_char == 1)
+ devc->mqflags |= SR_MQFLAG_DC;
+ else if (meas_char == 2)
+ devc->mqflags |= SR_MQFLAG_AC;
+ else if (meas_char == 3)
+ devc->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC;
+ else if (meas_char == 15)
+ devc->mqflags |= SR_MQFLAG_DIODE;
+ break;
+ case 2:
+ devc->mq = SR_MQ_CURRENT;
+ devc->unit = SR_UNIT_AMPERE;
+ if (meas_char == 1)
+ devc->mqflags |= SR_MQFLAG_DC;
+ else if (meas_char == 2)
+ devc->mqflags |= SR_MQFLAG_AC;
+ else if (meas_char == 3)
+ devc->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC;
+ break;
+ case 3:
+ if (meas_char == 1) {
+ devc->mq = SR_MQ_RESISTANCE;
+ devc->unit = SR_UNIT_OHM;
+ } else if (meas_char == 16) {
+ devc->mq = SR_MQ_CONTINUITY;
+ devc->unit = SR_UNIT_BOOLEAN;
+ }
+ break;
+ case 12:
+ devc->mq = SR_MQ_TEMPERATURE;
+ devc->unit = SR_UNIT_CELSIUS;
+ break;
+ case 13:
+ devc->mq = SR_MQ_TEMPERATURE;
+ devc->unit = SR_UNIT_FAHRENHEIT;
+ break;
+ default:
+ sr_dbg("unknown unit: %d", meas_unit);
+ }
+ if (devc->mq == -1 && devc->unit == -1)
+ return;
+
+ /* If we got here, we know how to interpret the measurement. */
+ devc->meas_type = meas_type;
+ if (meas_type == 11)
+ /* Absolute meter reading. */
+ devc->is_relative = FALSE;
+ else if (!strcmp(tokens[0], "19"))
+ /* Relative meter reading. */
+ devc->is_relative = TRUE;
+
+}
+
+static void handle_qm_19x_data(const struct sr_dev_inst *sdi, char **tokens)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ float fvalue;
+
+ if (!strcmp(tokens[0], "9.9E+37")) {
+ /* An invalid measurement shows up on the display as "OL", but
+ * comes through like this. Since comparing 38-digit floats
+ * is rather problematic, we'll cut through this here. */
+ fvalue = NAN;
+ } else {
+ if (sr_atof_ascii(tokens[0], &fvalue) != SR_OK || fvalue == 0.0) {
+ sr_err("Invalid float '%s'.", tokens[0]);
+ return;
+ }
+ }
+
+ devc = sdi->priv;
+ if (devc->mq == -1 || devc->unit == -1)
+ /* Don't have valid metadata yet. */
+ return;
+
+
+ if (devc->mq == SR_MQ_RESISTANCE && isnan(fvalue))
+ fvalue = INFINITY;
+ else if (devc->mq == SR_MQ_CONTINUITY) {
+ if (isnan(fvalue))
+ fvalue = 0.0;
+ else
+ fvalue = 1.0;
+ }
+
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+ analog.data = &fvalue;
+ analog.mq = devc->mq;
+ analog.unit = devc->unit;
+ analog.mqflags = 0;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+ devc->num_samples++;
+
+}
+
+static void handle_line(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog *analog;
+ int num_tokens, n, i;
+ char cmd[16], **tokens;
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+ sr_spew("Received line '%s' (%d).", devc->buf, devc->buflen);
+
+ if (devc->buflen == 1) {
+ if (devc->buf[0] != '0') {
+ /* Not just a CMD_ACK from the query command. */
+ sr_dbg("Got CMD_ACK '%c'.", devc->buf[0]);
+ devc->expect_response = FALSE;
+ }
+ devc->buflen = 0;
+ return;
+ }
+
+ analog = NULL;
+ tokens = g_strsplit(devc->buf, ",", 0);
+ if (tokens[0]) {
+ if (devc->profile->model == FLUKE_187 || devc->profile->model == FLUKE_189) {
+ devc->expect_response = FALSE;
+ analog = handle_qm_18x(sdi, tokens);
+ } else if (devc->profile->model == FLUKE_287) {
+ devc->expect_response = FALSE;
+ analog = handle_qm_28x(sdi, tokens);
+ } else if (devc->profile->model == FLUKE_190) {
+ devc->expect_response = FALSE;
+ for (num_tokens = 0; tokens[num_tokens]; num_tokens++);
+ if (num_tokens >= 7) {
+ /* Response to QM: this is a comma-separated list of
+ * fields with metadata about the measurement. This
+ * format can return multiple sets of metadata,
+ * split into sets of 7 tokens each. */
+ devc->meas_type = 0;
+ for (i = 0; i < num_tokens; i += 7)
+ handle_qm_19x_meta(sdi, tokens + i);
+ if (devc->meas_type) {
+ /* Slip the request in now, before the main
+ * timer loop asks for metadata again. */
+ n = sprintf(cmd, "QM %d\r", devc->meas_type);
+ if (serial_write(serial, cmd, n) == -1)
+ sr_err("Unable to send QM (measurement): %s.",
+ strerror(errno));
+ }
+ } else {
+ /* Response to QM <n> measurement request. */
+ handle_qm_19x_data(sdi, tokens);
+ }
+ }
+ }
+ g_strfreev(tokens);
+ devc->buflen = 0;
+
+ if (analog) {
+ /* Got a measurement. */
+ packet.type = SR_DF_ANALOG;
+ packet.payload = analog;
+ sr_session_send(devc->cb_data, &packet);
+ devc->num_samples++;
+ g_free(analog->data);
+ g_free(analog);
+ }
+
+}
+
+SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int len;
+ int64_t now, elapsed;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+ if (revents == G_IO_IN) {
+ /* Serial data arrived. */
+ while(FLUKEDMM_BUFSIZE - devc->buflen - 1 > 0) {
+ len = serial_read(serial, devc->buf + devc->buflen, 1);
+ if (len < 1)
+ break;
+ devc->buflen++;
+ *(devc->buf + devc->buflen) = '\0';
+ if (*(devc->buf + devc->buflen - 1) == '\r') {
+ *(devc->buf + --devc->buflen) = '\0';
+ handle_line(sdi);
+ break;
+ }
+ }
+ }
+
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+
+ now = g_get_monotonic_time() / 1000;
+ elapsed = now - devc->cmd_sent_at;
+ /* Send query command at poll_period interval, or after 1 second
+ * has elapsed. This will make it easier to recover from any
+ * out-of-sync or temporary disconnect issues. */
+ if ((devc->expect_response == FALSE && elapsed > devc->profile->poll_period)
+ || elapsed > devc->profile->timeout) {
+ if (serial_write(serial, "QM\r", 3) == -1)
+ sr_err("Unable to send QM: %s.", strerror(errno));
+ devc->cmd_sent_at = now;
+ devc->expect_response = TRUE;
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 "protocol.h"
+
+static const struct fx2lafw_profile supported_fx2[] = {
+ /*
+ * CWAV USBee AX
+ * EE Electronics ESLA201A
+ * ARMFLY AX-Pro
+ */
+ { 0x08a9, 0x0014, "CWAV", "USBee AX", NULL,
+ FIRMWARE_DIR "/fx2lafw-cwav-usbeeax.fw",
+ 0, NULL, NULL},
+ /*
+ * CWAV USBee DX
+ * XZL-Studio DX
+ */
+ { 0x08a9, 0x0015, "CWAV", "USBee DX", NULL,
+ FIRMWARE_DIR "/fx2lafw-cwav-usbeedx.fw",
+ DEV_CAPS_16BIT, NULL, NULL },
+
+ /*
+ * CWAV USBee SX
+ */
+ { 0x08a9, 0x0009, "CWAV", "USBee SX", NULL,
+ FIRMWARE_DIR "/fx2lafw-cwav-usbeesx.fw",
+ 0, NULL, NULL},
+
+ /*
+ * Saleae Logic
+ * EE Electronics ESLA100
+ * Robomotic MiniLogic
+ * Robomotic BugLogic 3
+ */
+ { 0x0925, 0x3881, "Saleae", "Logic", NULL,
+ FIRMWARE_DIR "/fx2lafw-saleae-logic.fw",
+ 0, NULL, NULL},
+
+ /*
+ * Default Cypress FX2 without EEPROM, e.g.:
+ * Lcsoft Mini Board
+ * Braintechnology USB Interface V2.x
+ */
+ { 0x04B4, 0x8613, "Cypress", "FX2", NULL,
+ FIRMWARE_DIR "/fx2lafw-cypress-fx2.fw",
+ DEV_CAPS_16BIT, NULL, NULL },
+
+ /*
+ * Braintechnology USB-LPS
+ */
+ { 0x16d0, 0x0498, "Braintechnology", "USB-LPS", NULL,
+ FIRMWARE_DIR "/fx2lafw-braintechnology-usb-lps.fw",
+ DEV_CAPS_16BIT, NULL, NULL },
+
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_TRIGGER_MATCH,
+ SR_CONF_SAMPLERATE,
+
+ /* These are really implemented in the driver, not the hardware. */
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_CONTINUOUS,
+};
+
+static const char *channel_names[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", "10", "11", "12", "13", "14", "15",
+ NULL,
+};
+
+static const int32_t soft_trigger_matches[] = {
+ SR_TRIGGER_ZERO,
+ SR_TRIGGER_ONE,
+ SR_TRIGGER_RISING,
+ SR_TRIGGER_FALLING,
+ SR_TRIGGER_EDGE,
+};
+
+static const uint64_t samplerates[] = {
+ SR_KHZ(20),
+ SR_KHZ(25),
+ SR_KHZ(50),
+ SR_KHZ(100),
+ SR_KHZ(200),
+ SR_KHZ(250),
+ SR_KHZ(500),
+ SR_MHZ(1),
+ SR_MHZ(2),
+ SR_MHZ(3),
+ SR_MHZ(4),
+ SR_MHZ(6),
+ SR_MHZ(8),
+ SR_MHZ(12),
+ SR_MHZ(16),
+ SR_MHZ(24),
+};
+
+SR_PRIV struct sr_dev_driver fx2lafw_driver_info;
+static struct sr_dev_driver *di = &fx2lafw_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_usb_dev_inst *usb;
+ struct sr_channel *ch;
+ struct sr_config *src;
+ const struct fx2lafw_profile *prof;
+ GSList *l, *devices, *conn_devices;
+ struct libusb_device_descriptor des;
+ libusb_device **devlist;
+ struct libusb_device_handle *hdl;
+ int devcnt, num_logic_channels, ret, i, j;
+ const char *conn;
+ char manufacturer[64], product[64];
+
+ drvc = di->priv;
+
+ 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)
+ conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
+ else
+ conn_devices = NULL;
+
+ /* Find all fx2lafw compatible devices and upload firmware to them. */
+ devices = NULL;
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if (conn) {
+ usb = NULL;
+ for (l = conn_devices; l; l = l->next) {
+ usb = l->data;
+ if (usb->bus == libusb_get_bus_number(devlist[i])
+ && usb->address == libusb_get_device_address(devlist[i]))
+ break;
+ }
+ if (!l)
+ /* This device matched none of the ones that
+ * matched the conn specification. */
+ continue;
+ }
+
+ if ((ret = libusb_get_device_descriptor( devlist[i], &des)) != 0) {
+ sr_warn("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ if ((ret = libusb_open(devlist[i], &hdl)) < 0)
+ continue;
+
+ if (des.iManufacturer == 0) {
+ manufacturer[0] = '\0';
+ } else if ((ret = libusb_get_string_descriptor_ascii(hdl,
+ des.iManufacturer, (unsigned char *) manufacturer,
+ sizeof(manufacturer))) < 0) {
+ sr_warn("Failed to get manufacturer string descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ if (des.iProduct == 0) {
+ product[0] = '\0';
+ } else if ((ret = libusb_get_string_descriptor_ascii(hdl,
+ des.iProduct, (unsigned char *) product,
+ sizeof(product))) < 0) {
+ sr_warn("Failed to get product string descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ libusb_close(hdl);
+
+ prof = NULL;
+ for (j = 0; supported_fx2[j].vid; j++) {
+ if (des.idVendor == supported_fx2[j].vid &&
+ des.idProduct == supported_fx2[j].pid &&
+ (!supported_fx2[j].usb_manufacturer ||
+ !strcmp(manufacturer, supported_fx2[j].usb_manufacturer)) &&
+ (!supported_fx2[j].usb_manufacturer ||
+ !strcmp(product, supported_fx2[j].usb_product))) {
+ prof = &supported_fx2[j];
+ break;
+ }
+ }
+
+ /* Skip if the device was not found. */
+ if (!prof)
+ continue;
+
+ devcnt = g_slist_length(drvc->instances);
+ sdi = sr_dev_inst_new(devcnt, SR_ST_INITIALIZING,
+ prof->vendor, prof->model, prof->model_version);
+ if (!sdi)
+ return NULL;
+ sdi->driver = di;
+
+ /* Fill in channellist according to this device's profile. */
+ num_logic_channels = prof->dev_caps & DEV_CAPS_16BIT ? 16 : 8;
+ for (j = 0; j < num_logic_channels; j++) {
+ if (!(ch = sr_channel_new(j, SR_CHANNEL_LOGIC, TRUE,
+ channel_names[j])))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ devc = fx2lafw_dev_new();
+ devc->profile = prof;
+ sdi->priv = devc;
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ if (fx2lafw_check_conf_profile(devlist[i])) {
+ /* Already has the firmware, so fix the new address. */
+ sr_dbg("Found an fx2lafw device.");
+ sdi->status = SR_ST_INACTIVE;
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+ libusb_get_device_address(devlist[i]), NULL);
+ } else {
+ if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION,
+ prof->firmware) == SR_OK)
+ /* Store when this device's FW was updated. */
+ devc->fw_updated = g_get_monotonic_time();
+ else
+ sr_err("Firmware upload failed for "
+ "device %d.", devcnt);
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+ 0xff, NULL);
+ }
+ }
+ libusb_free_device_list(devlist, 1);
+ g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ struct dev_context *devc;
+ int ret;
+ int64_t timediff_us, timediff_ms;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ /*
+ * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS
+ * milliseconds for the FX2 to renumerate.
+ */
+ ret = SR_ERR;
+ if (devc->fw_updated > 0) {
+ sr_info("Waiting for device to reset.");
+ /* Takes >= 300ms for the FX2 to be gone from the USB bus. */
+ g_usleep(300 * 1000);
+ timediff_ms = 0;
+ while (timediff_ms < MAX_RENUM_DELAY_MS) {
+ if ((ret = fx2lafw_dev_open(sdi, di)) == SR_OK)
+ break;
+ g_usleep(100 * 1000);
+
+ timediff_us = g_get_monotonic_time() - devc->fw_updated;
+ timediff_ms = timediff_us / 1000;
+ sr_spew("Waited %" PRIi64 "ms.", timediff_ms);
+ }
+ if (ret != SR_OK) {
+ sr_err("Device failed to renumerate.");
+ return SR_ERR;
+ }
+ sr_info("Device came back after %" PRIi64 "ms.", timediff_ms);
+ } else {
+ sr_info("Firmware upload was not needed.");
+ ret = fx2lafw_dev_open(sdi, di);
+ }
+
+ if (ret != SR_OK) {
+ sr_err("Unable to open device.");
+ return SR_ERR;
+ }
+
+ ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
+ if (ret != 0) {
+ switch (ret) {
+ case LIBUSB_ERROR_BUSY:
+ sr_err("Unable to claim USB interface. Another "
+ "program or driver has already claimed it.");
+ break;
+ case LIBUSB_ERROR_NO_DEVICE:
+ sr_err("Device has been disconnected.");
+ break;
+ default:
+ sr_err("Unable to claim interface: %s.",
+ libusb_error_name(ret));
+ break;
+ }
+
+ return SR_ERR;
+ }
+
+ if (devc->cur_samplerate == 0) {
+ /* Samplerate hasn't been set; default to the slowest one. */
+ devc->cur_samplerate = samplerates[0];
+ }
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ usb = sdi->conn;
+ if (usb->devhdl == NULL)
+ return SR_ERR;
+
+ sr_info("fx2lafw: Closing device %d on %d.%d interface %d.",
+ sdi->index, usb->bus, usb->address, USB_INTERFACE);
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ int ret;
+ struct drv_context *drvc;
+
+ if (!(drvc = di->priv))
+ return SR_OK;
+
+ ret = std_dev_clear(di, NULL);
+
+ g_free(drvc);
+ di->priv = NULL;
+
+ return ret;
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ char str[128];
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ switch (id) {
+ case SR_CONF_CONN:
+ if (!sdi->conn)
+ return SR_ERR_ARG;
+ usb = sdi->conn;
+ if (usb->address == 255)
+ /* Device still needs to re-enumerate after firmware
+ * upload, so we don't know its (future) address. */
+ return SR_ERR;
+ snprintf(str, 128, "%d.%d", usb->bus, usb->address);
+ *data = g_variant_new_string(str);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_SAMPLERATE:
+ *data = g_variant_new_uint64(devc->cur_samplerate);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ int ret;
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR;
+
+ devc = sdi->priv;
+
+ ret = SR_OK;
+
+ switch (id)
+ {
+ case SR_CONF_SAMPLERATE:
+ devc->cur_samplerate = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ GVariant *gvar;
+ GVariantBuilder gvb;
+
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
+ ARRAY_SIZE(samplerates), sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ soft_trigger_matches, ARRAY_SIZE(soft_trigger_matches),
+ sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+ struct timeval tv;
+ struct drv_context *drvc;
+
+ (void)fd;
+ (void)revents;
+ (void)cb_data;
+
+ drvc = di->priv;
+
+ tv.tv_sec = tv.tv_usec = 0;
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+ return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ struct sr_trigger *trigger;
+ struct libusb_transfer *transfer;
+ unsigned int i, timeout, num_transfers;
+ int ret;
+ unsigned char *buf;
+ size_t size;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ drvc = di->priv;
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ devc->cb_data = cb_data;
+ devc->sent_samples = 0;
+ devc->acq_aborted = FALSE;
+ devc->empty_transfer_count = 0;
+
+ if ((trigger = sr_session_trigger_get(sdi->session))) {
+ devc->stl = soft_trigger_logic_new(sdi, trigger);
+ devc->trigger_fired = FALSE;
+ } else
+ devc->trigger_fired = TRUE;
+
+ timeout = fx2lafw_get_timeout(devc);
+ num_transfers = fx2lafw_get_number_of_transfers(devc);
+ size = fx2lafw_get_buffer_size(devc);
+ devc->submitted_transfers = 0;
+
+ devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers);
+ if (!devc->transfers) {
+ sr_err("USB transfers malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+
+ devc->num_transfers = num_transfers;
+ for (i = 0; i < num_transfers; i++) {
+ if (!(buf = g_try_malloc(size))) {
+ sr_err("USB transfer buffer malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, usb->devhdl,
+ 2 | LIBUSB_ENDPOINT_IN, buf, size,
+ fx2lafw_receive_transfer, (void *)sdi, timeout);
+ if ((ret = libusb_submit_transfer(transfer)) != 0) {
+ sr_err("Failed to submit transfer: %s.",
+ libusb_error_name(ret));
+ libusb_free_transfer(transfer);
+ g_free(buf);
+ fx2lafw_abort_acquisition(devc);
+ return SR_ERR;
+ }
+ devc->transfers[i] = transfer;
+ devc->submitted_transfers++;
+ }
+
+ devc->ctx = drvc->sr_ctx;
+
+ usb_source_add(sdi->session, devc->ctx, timeout, receive_data, NULL);
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ if ((ret = fx2lafw_command_start_acquisition(sdi)) != SR_OK) {
+ fx2lafw_abort_acquisition(devc);
+ return ret;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ (void)cb_data;
+
+ fx2lafw_abort_acquisition(sdi->priv);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver fx2lafw_driver_info = {
+ .name = "fx2lafw",
+ .longname = "fx2lafw (generic driver for FX2 based LAs)",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 "protocol.h"
+
+/* Protocol commands */
+#define CMD_GET_FW_VERSION 0xb0
+#define CMD_START 0xb1
+#define CMD_GET_REVID_VERSION 0xb2
+
+#define CMD_START_FLAGS_WIDE_POS 5
+#define CMD_START_FLAGS_CLK_SRC_POS 6
+
+#define CMD_START_FLAGS_SAMPLE_8BIT (0 << CMD_START_FLAGS_WIDE_POS)
+#define CMD_START_FLAGS_SAMPLE_16BIT (1 << CMD_START_FLAGS_WIDE_POS)
+
+#define CMD_START_FLAGS_CLK_30MHZ (0 << CMD_START_FLAGS_CLK_SRC_POS)
+#define CMD_START_FLAGS_CLK_48MHZ (1 << CMD_START_FLAGS_CLK_SRC_POS)
+
+#pragma pack(push, 1)
+
+struct version_info {
+ uint8_t major;
+ uint8_t minor;
+};
+
+struct cmd_start_acquisition {
+ uint8_t flags;
+ uint8_t sample_delay_h;
+ uint8_t sample_delay_l;
+};
+
+#pragma pack(pop)
+
+static int command_get_fw_version(libusb_device_handle *devhdl,
+ struct version_info *vi)
+{
+ int ret;
+
+ ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_ENDPOINT_IN, CMD_GET_FW_VERSION, 0x0000, 0x0000,
+ (unsigned char *)vi, sizeof(struct version_info), 100);
+
+ if (ret < 0) {
+ sr_err("Unable to get version info: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int command_get_revid_version(struct sr_dev_inst *sdi, uint8_t *revid)
+{
+ struct sr_usb_dev_inst *usb = sdi->conn;
+ libusb_device_handle *devhdl = usb->devhdl;
+ int ret;
+
+ ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_ENDPOINT_IN, CMD_GET_REVID_VERSION, 0x0000, 0x0000,
+ revid, 1, 100);
+
+ if (ret < 0) {
+ sr_err("Unable to get REVID: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ struct sr_usb_dev_inst *usb = sdi->conn;
+ libusb_device_handle *devhdl = usb->devhdl;
+ uint64_t samplerate = devc->cur_samplerate;
+ gboolean samplewide = devc->sample_wide;
+ struct cmd_start_acquisition cmd = { 0 };
+ int delay = 0, ret;
+
+ /* Compute the sample rate. */
+ if (samplewide && samplerate > MAX_16BIT_SAMPLE_RATE) {
+ sr_err("Unable to sample at %" PRIu64 "Hz "
+ "when collecting 16-bit samples.", samplerate);
+ return SR_ERR;
+ }
+
+ if ((SR_MHZ(48) % samplerate) == 0) {
+ cmd.flags = CMD_START_FLAGS_CLK_48MHZ;
+ delay = SR_MHZ(48) / samplerate - 1;
+ if (delay > MAX_SAMPLE_DELAY)
+ delay = 0;
+ }
+
+ if (delay == 0 && (SR_MHZ(30) % samplerate) == 0) {
+ cmd.flags = CMD_START_FLAGS_CLK_30MHZ;
+ delay = SR_MHZ(30) / samplerate - 1;
+ }
+
+ sr_info("GPIF delay = %d, clocksource = %sMHz.", delay,
+ (cmd.flags & CMD_START_FLAGS_CLK_48MHZ) ? "48" : "30");
+
+ if (delay <= 0 || delay > MAX_SAMPLE_DELAY) {
+ sr_err("Unable to sample at %" PRIu64 "Hz.", samplerate);
+ return SR_ERR;
+ }
+
+ cmd.sample_delay_h = (delay >> 8) & 0xff;
+ cmd.sample_delay_l = delay & 0xff;
+
+ /* Select the sampling width. */
+ cmd.flags |= samplewide ? CMD_START_FLAGS_SAMPLE_16BIT :
+ CMD_START_FLAGS_SAMPLE_8BIT;
+
+ /* Send the control message. */
+ ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_ENDPOINT_OUT, CMD_START, 0x0000, 0x0000,
+ (unsigned char *)&cmd, sizeof(cmd), 100);
+ if (ret < 0) {
+ sr_err("Unable to send start command: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+/**
+ * Check the USB configuration to determine if this is an fx2lafw device.
+ *
+ * @return TRUE if the device's configuration profile match fx2lafw
+ * configuration, FALSE otherwise.
+ */
+SR_PRIV gboolean fx2lafw_check_conf_profile(libusb_device *dev)
+{
+ struct libusb_device_descriptor des;
+ struct libusb_device_handle *hdl;
+ gboolean ret;
+ unsigned char strdesc[64];
+
+ hdl = NULL;
+ ret = FALSE;
+ while (!ret) {
+ /* Assume the FW has not been loaded, unless proven wrong. */
+ if (libusb_get_device_descriptor(dev, &des) != 0)
+ break;
+
+ if (libusb_open(dev, &hdl) != 0)
+ break;
+
+ if (libusb_get_string_descriptor_ascii(hdl,
+ des.iManufacturer, strdesc, sizeof(strdesc)) < 0)
+ break;
+ if (strncmp((const char *)strdesc, "sigrok", 6))
+ break;
+
+ if (libusb_get_string_descriptor_ascii(hdl,
+ des.iProduct, strdesc, sizeof(strdesc)) < 0)
+ break;
+ if (strncmp((const char *)strdesc, "fx2lafw", 7))
+ break;
+
+ /* If we made it here, it must be an fx2lafw. */
+ ret = TRUE;
+ }
+ if (hdl)
+ libusb_close(hdl);
+
+ return ret;
+}
+
+SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
+{
+ libusb_device **devlist;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_device_descriptor des;
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ struct version_info vi;
+ int ret, skip, i, device_count;
+ uint8_t revid;
+
+ drvc = di->priv;
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ if (sdi->status == SR_ST_ACTIVE)
+ /* Device is already in use. */
+ return SR_ERR;
+
+ skip = 0;
+ device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ if (device_count < 0) {
+ sr_err("Failed to get device list: %s.",
+ libusb_error_name(device_count));
+ return SR_ERR;
+ }
+
+ for (i = 0; i < device_count; i++) {
+ if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
+ sr_err("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ if (des.idVendor != devc->profile->vid
+ || des.idProduct != devc->profile->pid)
+ continue;
+
+ if (sdi->status == SR_ST_INITIALIZING) {
+ if (skip != sdi->index) {
+ /* Skip devices of this type that aren't the one we want. */
+ skip += 1;
+ continue;
+ }
+ } else if (sdi->status == SR_ST_INACTIVE) {
+ /*
+ * This device is fully enumerated, so we need to find
+ * this device by vendor, product, bus and address.
+ */
+ if (libusb_get_bus_number(devlist[i]) != usb->bus
+ || libusb_get_device_address(devlist[i]) != usb->address)
+ /* This is not the one. */
+ continue;
+ }
+
+ if (!(ret = libusb_open(devlist[i], &usb->devhdl))) {
+ if (usb->address == 0xff)
+ /*
+ * First time we touch this device after FW
+ * upload, so we don't know the address yet.
+ */
+ usb->address = libusb_get_device_address(devlist[i]);
+ } else {
+ sr_err("Failed to open device: %s.",
+ libusb_error_name(ret));
+ break;
+ }
+
+ ret = command_get_fw_version(usb->devhdl, &vi);
+ if (ret != SR_OK) {
+ sr_err("Failed to get firmware version.");
+ break;
+ }
+
+ ret = command_get_revid_version(sdi, &revid);
+ if (ret != SR_OK) {
+ sr_err("Failed to get REVID.");
+ break;
+ }
+
+ /*
+ * Changes in major version mean incompatible/API changes, so
+ * bail out if we encounter an incompatible version.
+ * Different minor versions are OK, they should be compatible.
+ */
+ if (vi.major != FX2LAFW_REQUIRED_VERSION_MAJOR) {
+ sr_err("Expected firmware version %d.x, "
+ "got %d.%d.", FX2LAFW_REQUIRED_VERSION_MAJOR,
+ vi.major, vi.minor);
+ break;
+ }
+
+ sdi->status = SR_ST_ACTIVE;
+ sr_info("Opened device %d on %d.%d, "
+ "interface %d, firmware %d.%d.",
+ sdi->index, usb->bus, usb->address,
+ USB_INTERFACE, vi.major, vi.minor);
+
+ sr_info("Detected REVID=%d, it's a Cypress CY7C68013%s.",
+ revid, (revid != 1) ? " (FX2)" : "A (FX2LP)");
+
+ break;
+ }
+ libusb_free_device_list(devlist, 1);
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+SR_PRIV struct dev_context *fx2lafw_dev_new(void)
+{
+ struct dev_context *devc;
+
+ if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+
+ devc->profile = NULL;
+ devc->fw_updated = 0;
+ devc->cur_samplerate = 0;
+ devc->limit_samples = 0;
+ devc->sample_wide = FALSE;
+ devc->stl = NULL;
+
+ return devc;
+}
+
+SR_PRIV void fx2lafw_abort_acquisition(struct dev_context *devc)
+{
+ int i;
+
+ devc->acq_aborted = TRUE;
+
+ for (i = devc->num_transfers - 1; i >= 0; i--) {
+ if (devc->transfers[i])
+ libusb_cancel_transfer(devc->transfers[i]);
+ }
+}
+
+static void finish_acquisition(struct sr_dev_inst *sdi)
+{
+ struct sr_datafeed_packet packet;
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ /* Terminate session. */
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+
+ /* Remove fds from polling. */
+ usb_source_remove(sdi->session, devc->ctx);
+
+ devc->num_transfers = 0;
+ g_free(devc->transfers);
+
+ if (devc->stl) {
+ soft_trigger_logic_free(devc->stl);
+ devc->stl = NULL;
+ }
+}
+
+static void free_transfer(struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ unsigned int i;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+
+ g_free(transfer->buffer);
+ transfer->buffer = NULL;
+ libusb_free_transfer(transfer);
+
+ for (i = 0; i < devc->num_transfers; i++) {
+ if (devc->transfers[i] == transfer) {
+ devc->transfers[i] = NULL;
+ break;
+ }
+ }
+
+ devc->submitted_transfers--;
+ if (devc->submitted_transfers == 0)
+ finish_acquisition(sdi);
+}
+
+static void resubmit_transfer(struct libusb_transfer *transfer)
+{
+ int ret;
+
+ if ((ret = libusb_submit_transfer(transfer)) == LIBUSB_SUCCESS)
+ return;
+
+ sr_err("%s: %s", __func__, libusb_error_name(ret));
+ free_transfer(transfer);
+
+}
+
+SR_PRIV void fx2lafw_receive_transfer(struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ gboolean packet_has_error = FALSE;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ unsigned int num_samples;
+ int trigger_offset, cur_sample_count, unitsize;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+
+ /*
+ * If acquisition has already ended, just free any queued up
+ * transfer that come in.
+ */
+ if (devc->acq_aborted) {
+ free_transfer(transfer);
+ return;
+ }
+
+ sr_info("receive_transfer(): status %d received %d bytes.",
+ transfer->status, transfer->actual_length);
+
+ /* Save incoming transfer before reusing the transfer struct. */
+ unitsize = devc->sample_wide ? 2 : 1;
+ cur_sample_count = transfer->actual_length / unitsize;
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ fx2lafw_abort_acquisition(devc);
+ free_transfer(transfer);
+ return;
+ case LIBUSB_TRANSFER_COMPLETED:
+ case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */
+ break;
+ default:
+ packet_has_error = TRUE;
+ break;
+ }
+
+ if (transfer->actual_length == 0 || packet_has_error) {
+ devc->empty_transfer_count++;
+ if (devc->empty_transfer_count > MAX_EMPTY_TRANSFERS) {
+ /*
+ * The FX2 gave up. End the acquisition, the frontend
+ * will work out that the samplecount is short.
+ */
+ fx2lafw_abort_acquisition(devc);
+ free_transfer(transfer);
+ } else {
+ resubmit_transfer(transfer);
+ }
+ return;
+ } else {
+ devc->empty_transfer_count = 0;
+ }
+
+ if (devc->trigger_fired) {
+ if (devc->sent_samples < devc->limit_samples) {
+ /* Send the incoming transfer to the session bus. */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ if (devc->sent_samples + cur_sample_count > devc->limit_samples)
+ num_samples = devc->limit_samples - devc->sent_samples;
+ else
+ num_samples = cur_sample_count;
+ logic.length = num_samples * unitsize;
+ logic.unitsize = unitsize;
+ logic.data = transfer->buffer;
+ sr_session_send(devc->cb_data, &packet);
+ devc->sent_samples += num_samples;
+ }
+ } else {
+ trigger_offset = soft_trigger_logic_check(devc->stl,
+ transfer->buffer, transfer->actual_length);
+ if (trigger_offset > -1) {
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ num_samples = cur_sample_count - trigger_offset;
+ if (devc->limit_samples &&
+ num_samples > devc->limit_samples - devc->sent_samples)
+ num_samples = devc->limit_samples - devc->sent_samples;
+ logic.length = num_samples * unitsize;
+ logic.unitsize = unitsize;
+ logic.data = transfer->buffer + trigger_offset * unitsize;
+ sr_session_send(devc->cb_data, &packet);
+ devc->sent_samples += num_samples;
+
+ devc->trigger_fired = TRUE;
+ }
+ }
+
+ if (devc->limit_samples && devc->sent_samples >= devc->limit_samples) {
+ fx2lafw_abort_acquisition(devc);
+ free_transfer(transfer);
+ } else
+ resubmit_transfer(transfer);
+}
+
+static unsigned int to_bytes_per_ms(unsigned int samplerate)
+{
+ return samplerate / 1000;
+}
+
+SR_PRIV size_t fx2lafw_get_buffer_size(struct dev_context *devc)
+{
+ size_t s;
+
+ /*
+ * The buffer should be large enough to hold 10ms of data and
+ * a multiple of 512.
+ */
+ s = 10 * to_bytes_per_ms(devc->cur_samplerate);
+ return (s + 511) & ~511;
+}
+
+SR_PRIV unsigned int fx2lafw_get_number_of_transfers(struct dev_context *devc)
+{
+ unsigned int n;
+
+ /* Total buffer size should be able to hold about 500ms of data. */
+ n = (500 * to_bytes_per_ms(devc->cur_samplerate) /
+ fx2lafw_get_buffer_size(devc));
+
+ if (n > NUM_SIMUL_TRANSFERS)
+ return NUM_SIMUL_TRANSFERS;
+
+ return n;
+}
+
+SR_PRIV unsigned int fx2lafw_get_timeout(struct dev_context *devc)
+{
+ size_t total_size;
+ unsigned int timeout;
+
+ total_size = fx2lafw_get_buffer_size(devc) *
+ fx2lafw_get_number_of_transfers(devc);
+ timeout = total_size / to_bytes_per_ms(devc->cur_samplerate);
+ return timeout + timeout / 4; /* Leave a headroom of 25% percent. */
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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_FX2LAFW_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_FX2LAFW_PROTOCOL_H
+
+#include <glib.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libusb.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "fx2lafw"
+
+#define USB_INTERFACE 0
+#define USB_CONFIGURATION 1
+#define NUM_TRIGGER_STAGES 4
+
+#define MAX_RENUM_DELAY_MS 3000
+#define NUM_SIMUL_TRANSFERS 32
+#define MAX_EMPTY_TRANSFERS (NUM_SIMUL_TRANSFERS * 2)
+
+#define FX2LAFW_REQUIRED_VERSION_MAJOR 1
+
+#define MAX_8BIT_SAMPLE_RATE SR_MHZ(24)
+#define MAX_16BIT_SAMPLE_RATE SR_MHZ(12)
+
+/* 6 delay states of up to 256 clock ticks */
+#define MAX_SAMPLE_DELAY (6 * 256)
+
+#define DEV_CAPS_16BIT_POS 0
+
+#define DEV_CAPS_16BIT (1 << DEV_CAPS_16BIT_POS)
+
+struct fx2lafw_profile {
+ uint16_t vid;
+ uint16_t pid;
+
+ const char *vendor;
+ const char *model;
+ const char *model_version;
+
+ const char *firmware;
+
+ uint32_t dev_caps;
+
+ const char *usb_manufacturer;
+ const char *usb_product;
+};
+
+struct dev_context {
+ const struct fx2lafw_profile *profile;
+ /*
+ * Since we can't keep track of an fx2lafw device after upgrading
+ * the firmware (it renumerates into a different device address
+ * after the upgrade) this is like a global lock. No device will open
+ * until a proper delay after the last device was upgraded.
+ */
+ int64_t fw_updated;
+
+ /* Device/capture settings */
+ uint64_t cur_samplerate;
+ uint64_t limit_samples;
+
+ /* Operational settings */
+ gboolean trigger_fired;
+ gboolean acq_aborted;
+ gboolean sample_wide;
+ struct soft_trigger_logic *stl;
+
+ unsigned int sent_samples;
+ int submitted_transfers;
+ int empty_transfer_count;
+
+ void *cb_data;
+ unsigned int num_transfers;
+ struct libusb_transfer **transfers;
+ struct sr_context *ctx;
+};
+
+SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi);
+SR_PRIV gboolean fx2lafw_check_conf_profile(libusb_device *dev);
+SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di);
+SR_PRIV struct dev_context *fx2lafw_dev_new(void);
+SR_PRIV void fx2lafw_abort_acquisition(struct dev_context *devc);
+SR_PRIV void fx2lafw_receive_transfer(struct libusb_transfer *transfer);
+SR_PRIV size_t fx2lafw_get_buffer_size(struct dev_context *devc);
+SR_PRIV unsigned int fx2lafw_get_number_of_transfers(struct dev_context *devc);
+SR_PRIV unsigned int fx2lafw_get_timeout(struct dev_context *devc);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013, 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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/>.
+ */
+
+/** @file
+ * Gossen Metrawatt Metrahit 1x/2x drivers
+ * @internal
+ */
+
+#include <string.h>
+#include "protocol.h"
+
+/* Serial communication parameters for Metrahit 1x/2x with 'RS232' adaptor */
+#define SERIALCOMM_1X_RS232 "8228/6n1/dtr=1/rts=1/flow=0" /* =8192, closer with divider */
+#define SERIALCOMM_2X_RS232 "9600/6n1/dtr=1/rts=1/flow=0"
+#define SERIALCOMM_2X "9600/8n1/dtr=1/rts=1/flow=0"
+#define VENDOR_GMC "Gossen Metrawatt"
+
+SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info;
+SR_PRIV struct sr_dev_driver gmc_mh_2x_bd232_driver_info;
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+/** Hardware capabilities for Metrahit 1x/2x devices in send mode. */
+static const int32_t hwcaps_sm[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_THERMOMETER, /**< All GMC 1x/2x multimeters seem to support this */
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+/** Hardware capabilities for Metrahit 2x devices in bidirectional Mode. */
+static const int32_t hwcaps_bd[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_THERMOMETER, /**< All GMC 1x/2x multimeters seem to support this */
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+ SR_CONF_POWER_OFF,
+};
+
+
+/* TODO:
+ * - For the 29S SR_CONF_ENERGYMETER, too.
+ * - SR_CONF_PATTERN_MODE for some 2x devices
+ * - SR_CONF_DATALOG for 22M, 26M, 29S and storage adaptors.
+ * Need to implement device-specific lists.
+ */
+
+/** Init driver gmc_mh_1x_2x_rs232. */
+static int init_1x_2x_rs232(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, &gmc_mh_1x_2x_rs232_driver_info, LOG_PREFIX);
+}
+
+/** Init driver gmc_mh_2x_bd232. */
+static int init_2x_bd232(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, &gmc_mh_2x_bd232_driver_info, LOG_PREFIX);
+}
+
+/**
+ * Read single byte from serial port.
+ *
+ * @retval -1 Timeout or error.
+ * @retval other Byte.
+ */
+static int read_byte(struct sr_serial_dev_inst *serial, gint64 timeout)
+{
+ uint8_t result = 0;
+ int rc = 0;
+
+ for (;;) {
+ rc = serial_read(serial, &result, 1);
+ if (rc == 1) {
+ sr_spew("read: 0x%02x/%d", result, result);
+ return result;
+ }
+ if (g_get_monotonic_time() > timeout)
+ return -1;
+ g_usleep(2000);
+ }
+}
+
+/**
+ * Try to detect GMC 1x/2x multimeter model in send mode for max. 1 second.
+ *
+ * @param serial Configured, open serial port.
+ *
+ * @retval NULL Detection failed.
+ * @retval other Model code.
+ */
+static enum model scan_model_sm(struct sr_serial_dev_inst *serial)
+{
+ int byte, bytecnt, cnt;
+ enum model model;
+ gint64 timeout_us;
+
+ model = METRAHIT_NONE;
+ timeout_us = g_get_monotonic_time() + 1 * 1000 * 1000;
+
+ /*
+ * Try to find message consisting of device code and several
+ * (at least 4) data bytes.
+ */
+ for (bytecnt = 0; bytecnt < 100; bytecnt++) {
+ byte = read_byte(serial, timeout_us);
+ if ((byte == -1) || (timeout_us < g_get_monotonic_time()))
+ break;
+ if ((byte & MSGID_MASK) == MSGID_INF) {
+ if (!(model = gmc_decode_model_sm(byte & MSGC_MASK)))
+ break;
+ /* Now expect (at least) 4 data bytes. */
+ for (cnt = 0; cnt < 4; cnt++) {
+ byte = read_byte(serial, timeout_us);
+ if ((byte == -1) ||
+ ((byte & MSGID_MASK) != MSGID_DATA))
+ {
+ model = METRAHIT_NONE;
+ bytecnt = 100;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return model;
+}
+
+/**
+ * Scan for Metrahit 1x and Metrahit 2x in send mode using Gossen Metrawatt
+ * 'RS232' interface.
+ *
+ * The older 1x models use 8192 baud and the newer 2x 9600 baud.
+ * The DMM usually sends up to about 20 messages per second. However, depending
+ * on configuration and measurement mode the intervals can be much larger and
+ * then the detection might not work.
+ */
+static GSList *scan_1x_2x_rs232(GSList *options)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_config *src;
+ struct sr_channel *ch;
+ struct sr_serial_dev_inst *serial;
+ GSList *l, *devices;
+ const char *conn, *serialcomm;
+ enum model model;
+ gboolean serialcomm_given;
+
+ devices = NULL;
+ drvc = (&gmc_mh_1x_2x_rs232_driver_info)->priv;
+ drvc->instances = NULL;
+ conn = serialcomm = NULL;
+ model = METRAHIT_NONE;
+ serialcomm_given = FALSE;
+
+ sr_spew("scan_1x_2x_rs232() called!");
+
+ 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);
+ serialcomm_given = TRUE;
+ break;
+ }
+ }
+ if (!conn)
+ return NULL;
+ if (!serialcomm)
+ serialcomm = SERIALCOMM_2X_RS232;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK) {
+ sr_serial_dev_inst_free(serial);
+ return NULL;
+ }
+
+ serial_flush(serial);
+
+ model = scan_model_sm(serial);
+
+ /*
+ * If detection failed and no user-supplied parameters,
+ * try second baud rate.
+ */
+ if ((model == METRAHIT_NONE) && !serialcomm_given) {
+ serialcomm = SERIALCOMM_1X_RS232;
+ g_free(serial->serialcomm);
+ serial->serialcomm = g_strdup(serialcomm);
+ if (serial_set_paramstr(serial, serialcomm) == SR_OK) {
+ serial_flush(serial);
+ model = scan_model_sm(serial);
+ }
+ }
+
+ if (model != METRAHIT_NONE) {
+ sr_spew("%s %s detected!", VENDOR_GMC, gmc_model_str(model));
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR_GMC,
+ gmc_model_str(model), NULL)))
+ return NULL;
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+ devc->model = model;
+ devc->limit_samples = 0;
+ devc->limit_msec = 0;
+ devc->num_samples = 0;
+ devc->elapsed_msec = g_timer_new();
+ devc->settings_ok = FALSE;
+
+ sdi->conn = serial;
+ sdi->priv = devc;
+ sdi->driver = &gmc_mh_1x_2x_rs232_driver_info;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ }
+
+ return devices;
+}
+
+/** Scan for Metrahit 2x in a bidirectional mode using Gossen Metrawatt 'BD 232' interface.
+ *
+ */
+static GSList *scan_2x_bd232(GSList *options)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_config *src;
+ struct sr_channel *ch;
+ struct sr_serial_dev_inst *serial;
+ GSList *l, *devices;
+ const char *conn, *serialcomm;
+ int cnt, byte;
+ gint64 timeout_us;
+
+ sdi = NULL;
+ devc = NULL;
+ conn = serialcomm = NULL;
+ devices = NULL;
+
+ drvc = (&gmc_mh_2x_bd232_driver_info)->priv;
+ drvc->instances = NULL;
+
+ sr_spew("scan_2x_bd232() called!");
+
+ 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;
+ if (!serialcomm)
+ serialcomm = SERIALCOMM_2X;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ goto exit_err;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ goto exit_err;
+ }
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR_GMC, NULL, NULL)))
+ goto exit_err;
+
+ sdi->priv = devc;
+
+ /* Send message 03 "Query multimeter version and status" */
+ sdi->conn = serial;
+ sdi->priv = devc;
+ if (req_stat14(sdi, TRUE) != SR_OK)
+ goto exit_err;
+
+ /* Wait for reply from device(s) for up to 2s. */
+ timeout_us = g_get_monotonic_time() + 2*1000*1000;
+
+ while (timeout_us > g_get_monotonic_time()) {
+ /* Receive reply (14 bytes) */
+ devc->buflen = 0;
+ for (cnt = 0; cnt < 14; cnt++) {
+ byte = read_byte(serial, timeout_us);
+ if (byte != -1)
+ devc->buf[devc->buflen++] = (byte & MASK_6BITS);
+ }
+
+ if (devc->buflen != 14)
+ continue;
+
+ devc->addr = devc->buf[0];
+ process_msg14(sdi);
+ devc->buflen = 0;
+
+ if (devc->model != METRAHIT_NONE) {
+ sr_spew("%s %s detected!", VENDOR_GMC, gmc_model_str(devc->model));
+
+ devc->elapsed_msec = g_timer_new();
+
+ sdi->model = g_strdup(gmc_model_str(devc->model));
+ sdi->version = g_strdup_printf("Firmware %d.%d", devc->fw_ver_maj, devc->fw_ver_min);
+ sdi->conn = serial;
+ sdi->priv = devc;
+ sdi->driver = &gmc_mh_2x_bd232_driver_info;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
+ goto exit_err;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ goto exit_err;
+ }
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR_GMC, NULL, NULL)))
+ goto exit_err;
+ }
+ };
+
+ /* Free last alloc if no device found */
+ if (devc->model == METRAHIT_NONE) {
+ g_free(devc);
+ sr_dev_inst_free(sdi);
+ }
+
+ return devices;
+
+exit_err:
+ sr_info("scan_2x_bd232(): Error!");
+
+ if (serial)
+ sr_serial_dev_inst_free(serial);
+ if (devc)
+ g_free(devc);
+ if (sdi)
+ sr_dev_inst_free(sdi);
+
+ return NULL;
+}
+
+/** Driver device list function */
+static GSList *dev_list_1x_2x_rs232(void)
+{
+ return ((struct drv_context *)(gmc_mh_1x_2x_rs232_driver_info.priv))->instances;
+}
+
+/** Driver device list function */
+static GSList *dev_list_2x_bd232(void)
+{
+ return ((struct drv_context *)(gmc_mh_2x_bd232_driver_info.priv))
+ ->instances;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ std_serial_dev_close(sdi);
+
+ sdi->status = SR_ST_INACTIVE;
+
+ /* Free dynamically allocated resources. */
+ if ((devc = sdi->priv) && devc->elapsed_msec) {
+ g_timer_destroy(devc->elapsed_msec);
+ devc->elapsed_msec = NULL;
+ devc->model = METRAHIT_NONE;
+ }
+
+ return SR_OK;
+}
+
+static int cleanup_sm_rs232(void)
+{
+ return std_dev_clear(&gmc_mh_1x_2x_rs232_driver_info, NULL);
+}
+
+static int cleanup_2x_bd232(void)
+{
+ return std_dev_clear(&gmc_mh_2x_bd232_driver_info, NULL);
+}
+
+/** Get value of configuration item */
+static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ int ret;
+ struct dev_context *devc;
+
+ (void)cg;
+
+ ret = SR_OK;
+
+ if (!sdi || !(devc = sdi->priv))
+ return SR_ERR_ARG;
+
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ *data = g_variant_new_uint64(devc->limit_msec);
+ break;
+ case SR_CONF_POWER_OFF:
+ *data = g_variant_new_boolean(FALSE);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+/** Implementation of config_list, auxiliary function for common parts, */
+static int config_list_common(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+/** Implementation of config_list for Metrahit 1x/2x send mode */
+static int config_list_sm(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps_sm, ARRAY_SIZE(hwcaps_sm), sizeof(int32_t));
+ break;
+ default:
+ return config_list_common(key, data, sdi, cg);
+ }
+
+ return SR_OK;
+}
+
+/** Implementation of config_list for Metrahit 2x bidirectional mode */
+static int config_list_bd(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps_bd, ARRAY_SIZE(hwcaps_bd), sizeof(int32_t));
+ break;
+ default:
+ return config_list_common(key, data, sdi, cg);
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start_1x_2x_rs232(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (!sdi || !cb_data || !(devc = sdi->priv))
+ return SR_ERR_BUG;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc->cb_data = cb_data;
+ devc->settings_ok = FALSE;
+ devc->buflen = 0;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Start timer, if required. */
+ if (devc->limit_msec)
+ g_timer_start(devc->elapsed_msec);
+
+ /* Poll every 40ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 40,
+ gmc_mh_1x_2x_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start_2x_bd232(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (!sdi || !cb_data || !(devc = sdi->priv))
+ return SR_ERR_BUG;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc->cb_data = cb_data;
+ devc->settings_ok = FALSE;
+ devc->buflen = 0;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Start timer, if required. */
+ if (devc->limit_msec)
+ g_timer_start(devc->elapsed_msec);
+
+ /* Poll every 40ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 40,
+ gmc_mh_2x_receive_data, (void *)sdi);
+
+ /* Send start message */
+ return req_meas14(sdi);
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+
+ /* Stop timer, if required. */
+ if (sdi && (devc = sdi->priv) && devc->limit_msec)
+ g_timer_stop(devc->elapsed_msec);
+
+ return std_serial_dev_acquisition_stop(sdi, cb_data, dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info = {
+ .name = "gmc-mh-1x-2x-rs232",
+ .longname = "Gossen Metrawatt Metrahit 1x/2x, RS232 interface",
+ .api_version = 1,
+ .init = init_1x_2x_rs232,
+ .cleanup = cleanup_sm_rs232,
+ .scan = scan_1x_2x_rs232,
+ .dev_list = dev_list_1x_2x_rs232,
+ .dev_clear = NULL,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list_sm,
+ .dev_open = std_serial_dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start_1x_2x_rs232,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
+
+SR_PRIV struct sr_dev_driver gmc_mh_2x_bd232_driver_info = {
+ .name = "gmc-mh-2x-bd232",
+ .longname = "Gossen Metrawatt Metrahit 2x, BD232/SI232-II interface",
+ .api_version = 1,
+ .init = init_2x_bd232,
+ .cleanup = cleanup_2x_bd232,
+ .scan = scan_2x_bd232,
+ .dev_list = dev_list_2x_bd232,
+ .dev_clear = NULL,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list_bd,
+ .dev_open = std_serial_dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start_2x_bd232,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013, 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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/>.
+ */
+
+/** @file
+ * Gossen Metrawatt Metrahit 1x/2x drivers
+ * @internal
+ */
+
+#include <math.h>
+#include <string.h>
+#include "protocol.h"
+
+/* Internal Headers */
+static guchar calc_chksum_14(guchar* dta);
+static int chk_msg14(struct sr_dev_inst *sdi);
+
+/** Set or clear flags in devc->mqflags. */
+static void setmqf(struct dev_context *devc, uint64_t flags, gboolean set)
+{
+ if (set)
+ devc->mqflags |= flags;
+ else
+ devc->mqflags &= ~flags;
+}
+
+/** Decode current type and measured value, Metrahit 12-16. */
+static void decode_ctmv_16(uint8_t ctmv, struct dev_context *devc)
+{
+ devc->mq = 0;
+ devc->unit = 0;
+ devc->mqflags = 0;
+
+ switch (ctmv) {
+ case 0x00: /* 0000 - */
+ break;
+ case 0x01: /* 0001 mV DC */
+ devc->scale1000 = -1; /* Fall through */
+ case 0x02: /* 0010 V DC */
+ case 0x03: /* 0011 V AC+DC */
+ case 0x04: /* 0100 V AC */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_VOLT;
+ if (ctmv <= 0x03)
+ devc->mqflags |= SR_MQFLAG_DC;
+ if (ctmv >= 0x03) {
+ devc->mqflags |= SR_MQFLAG_AC;
+ if (devc->model >= METRAHIT_16S)
+ devc->mqflags |= SR_MQFLAG_RMS;
+ }
+ break;
+ case 0x05: /* 0101 Hz (15S/16S only) */
+ case 0x06: /* 0110 kHz (15S/16S only) */
+ devc->mq = SR_MQ_FREQUENCY;
+ devc->unit = SR_UNIT_HERTZ;
+ if (ctmv == 0x06)
+ devc->scale1000 = 1;
+ break;
+ case 0x07: /* 0111 % (15S/16S only) */
+ devc->mq = SR_MQ_DUTY_CYCLE;
+ devc->unit = SR_UNIT_PERCENTAGE;
+ break;
+ case 0x08: /* 1000 Diode */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_VOLT;
+ devc->mqflags |= SR_MQFLAG_DIODE;
+ break;
+ case 0x09: /* 1001 Ohm, °C */
+ case 0x0a: /* 1010 kOhm */
+ case 0x0b: /* 1011 MOhm */
+ devc->mq = SR_MQ_RESISTANCE; /* Changed to temp. later if req.*/
+ devc->unit = SR_UNIT_OHM;
+ devc->scale1000 = ctmv - 0x09;
+ break;
+ case 0x0c: /* 1100 nF (15S/16S only) */
+ case 0x0d: /* 1101 µF (15S/16S only) */
+ devc->mq = SR_MQ_CAPACITANCE;
+ devc->unit = SR_UNIT_FARAD;
+ if (ctmv == 0x0c)
+ devc->scale1000 = -3;
+ else
+ devc->scale1000 = -2;
+ break;
+ case 0x0e: /* mA, µA */
+ devc->scale1000 = -1; /* Fall through. */
+ case 0x0f: /* A */
+ devc->mq = SR_MQ_CURRENT;
+ devc->unit = SR_UNIT_AMPERE;
+ if (devc->model == METRAHIT_16S)
+ devc->mqflags |= SR_MQFLAG_RMS;
+ /* 16I A only with clamp, RMS questionable. */
+ break;
+ }
+}
+
+/**
+ * Decode range/sign/acdc byte special chars (Metrahit 12-16).
+ *
+ * @param[in] rs Range and sign byte.
+ */
+static void decode_rs_16(uint8_t rs, struct dev_context *devc)
+{
+ sr_spew("decode_rs_16(%d) scale = %f", rs, devc->scale);
+
+ if (rs & 0x04) /* Sign */
+ devc->scale *= -1.0;
+
+ if (devc->mq == SR_MQ_CURRENT) {
+ if (rs & 0x08) /* Current is AC */
+ devc->mqflags |= SR_MQFLAG_AC;
+ else
+ devc->mqflags |= SR_MQFLAG_DC;
+ }
+
+ switch (rs & 0x03) {
+ case 0:
+ if (devc->mq == SR_MQ_VOLTAGE) /* V */
+ devc->scale *= 0.1;
+ else if (devc->mq == SR_MQ_CURRENT) /* 000.0 µA */
+ devc->scale *= 0.00001;
+ else if (devc->mq == SR_MQ_RESISTANCE) {
+ if (devc->buflen >= 10) {
+ /* °C with 10 byte msg type, otherwise GOhm. */
+ devc->mq = SR_MQ_TEMPERATURE;
+ devc->unit = SR_UNIT_CELSIUS;
+ devc->scale *= 0.01;
+ } else if (devc->scale1000 == 2) {
+ /* 16I Iso 500/1000V 3 GOhm */
+ devc->scale *= 0.1;
+ }
+ }
+ break;
+ case 1:
+ devc->scale *= 0.0001;
+ break;
+ case 2:
+ devc->scale *= 0.001;
+ break;
+ case 3:
+ devc->scale *= 0.01;
+ break;
+ }
+}
+
+/**
+ * Decode special chars, Metrahit 12-16.
+ *
+ * @param[in] spc Special characters 1 and 2 (s1 | (s2 << 4)).
+ */
+static void decode_spc_16(uint8_t spc, struct dev_context *devc)
+{
+ /* xxxx1xxx ON */
+ /* TODO: What does that mean? Power on? The 16I sets this. */
+ /* xxxxx1xx BEEP */
+ /* xxxxxx1x Low battery */
+ /* xxxxxxx1 FUSE */
+ /* 1xxxxxxx MIN */
+ setmqf(devc, SR_MQFLAG_MIN, spc & 0x80);
+
+ /* x1xxxxxx MAN */
+ setmqf(devc, SR_MQFLAG_AUTORANGE, !(spc & 0x40));
+
+ /* xx1xxxxx DATA */
+ setmqf(devc, SR_MQFLAG_HOLD, spc & 0x20);
+
+ /* xxx1xxxx MAX */
+ setmqf(devc, SR_MQFLAG_MAX, spc & 0x10);
+}
+
+/** Decode current type and measured value, Metrahit 18. */
+static void decode_ctmv_18(uint8_t ctmv, struct dev_context *devc)
+{
+ devc->mq = 0;
+ devc->unit = 0;
+ devc->mqflags = 0;
+
+ switch (ctmv) {
+ case 0x00: /* 0000 - */
+ break;
+ case 0x01: /* 0001 V AC */
+ case 0x02: /* 0010 V AC+DC */
+ case 0x03: /* 0011 V DC */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_VOLT;
+ if (ctmv <= 0x02)
+ devc->mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_RMS);
+ if (ctmv >= 0x02)
+ devc->mqflags |= SR_MQFLAG_DC;
+ break;
+ case 0x04: /* 0100 Ohm/Ohm with buzzer */
+ devc->mq = SR_MQ_RESISTANCE;
+ devc->unit = SR_UNIT_OHM;
+ break;
+ case 0x05: /* 0101 Diode/Diode with buzzer */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_VOLT;
+ devc->mqflags |= SR_MQFLAG_DIODE;
+ break;
+ case 0x06: /* 0110 °C */
+ devc->mq = SR_MQ_TEMPERATURE;
+ devc->unit = SR_UNIT_CELSIUS;
+ break;
+ case 0x07: /* 0111 F */
+ devc->mq = SR_MQ_CAPACITANCE;
+ devc->unit = SR_UNIT_FARAD;
+ break;
+ case 0x08: /* 1000 mA DC */
+ case 0x09: /* 1001 A DC */
+ case 0x0a: /* 1010 mA AC+DC */
+ case 0x0b: /* 1011 A AC+DC */
+ devc->mq = SR_MQ_CURRENT;
+ devc->unit = SR_UNIT_AMPERE;
+ devc->mqflags |= SR_MQFLAG_DC;
+ if (ctmv >= 0x0a)
+ devc->mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_RMS);
+ if ((ctmv == 0x08) || (ctmv == 0x0a))
+ devc->scale1000 = -1;
+ break;
+ case 0x0c: /* 1100 Hz */
+ devc->mq = SR_MQ_FREQUENCY;
+ devc->unit = SR_UNIT_HERTZ;
+ break;
+ case 0x0d: /* 1101 dB */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_DECIBEL_VOLT;
+ devc->mqflags |= SR_MQFLAG_AC; /* dB available for AC only */
+ break;
+ case 0x0e: /* 1110 Events AC, Events AC+DC. Actually delivers just
+ * current voltage via IR, nothing more. */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_VOLT;
+ devc->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS;
+ break;
+ case 0x0f: /* 1111 Clock */
+ devc->mq = SR_MQ_TIME;
+ devc->unit = SR_UNIT_SECOND;
+ devc->mqflags |= SR_MQFLAG_DURATION;
+ break;
+ }
+}
+
+/**
+ * Decode range/sign/acdc byte special chars, Metrahit 18.
+ *
+ * @param[in] rs Rance/sign byte.
+ */
+static void decode_rs_18(uint8_t rs, struct dev_context *devc)
+{
+ int range;
+
+ /* Sign */
+ if (((devc->scale > 0) && (rs & 0x08)) ||
+ ((devc->scale < 0) && !(rs & 0x08)))
+ devc->scale *= -1.0;
+
+ /* Range */
+ range = rs & 0x07;
+ switch (devc->mq) {
+ case SR_MQ_VOLTAGE:
+ if (devc->unit == SR_UNIT_DECIBEL_VOLT) {
+ devc->scale *= pow(10.0, -2);
+ /*
+ * When entering relative mode, the device switches
+ * from 10 byte to 6 byte msg format. Unfortunately
+ * it switches back to 10 byte when the second value
+ * is measured, so that's not sufficient to
+ * identify relative mode.
+ */
+ }
+ else if (devc->vmains_29S)
+ devc->scale *= pow(10.0, range - 2);
+ else
+ devc->scale *= pow(10.0, range - 5);
+ break;
+ case SR_MQ_CURRENT:
+ if (devc->scale1000 == -1)
+ devc->scale *= pow(10.0, range - 5);
+ else
+ devc->scale *= pow(10.0, range - 4);
+ break;
+ case SR_MQ_RESISTANCE:
+ devc->scale *= pow(10.0, range - 2);
+ break;
+ case SR_MQ_FREQUENCY:
+ devc->scale *= pow(10.0, range - 2);
+ break;
+ case SR_MQ_TEMPERATURE:
+ devc->scale *= pow(10.0, range - 2);
+ break;
+ case SR_MQ_CAPACITANCE:
+ devc->scale *= pow(10.0, range - 13);
+ break;
+ /* TODO: 29S Mains measurements. */
+ }
+}
+
+/**
+ * Decode special chars, Metrahit 18.
+ *
+ * @param[in] spc Special characters 1 and 2 (s1 | (s2 << 4)).
+ */
+static void decode_spc_18(uint8_t spc, struct dev_context *devc)
+{
+ /* xxxx1xxx ZERO */
+ /* xxxxx1xx BEEP */
+ /* xxxxxx1x Low battery */
+ /* xxxxxxx1 Fuse */
+
+ if (devc->mq == SR_MQ_TIME) {
+ /* xxx1xxxx Clock running: 1; stop: 0 */
+ sr_spew("Clock running: %d", spc >> 4);
+ } else {
+ /* 1xxxxxxx MAN */
+ setmqf(devc, SR_MQFLAG_AUTORANGE, !(spc & 0x80));
+
+ /* x1xxxxxx MIN */
+ setmqf(devc, SR_MQFLAG_MIN, spc & 0x40);
+
+ /* xx1xxxxx MAX */
+ setmqf(devc, SR_MQFLAG_MAX, spc & 0x20);
+
+ /* xxx1xxxx DATA */
+ setmqf(devc, SR_MQFLAG_HOLD, spc & 0x10);
+ }
+}
+
+/**
+ * Decode current type and measured value, Metrahit 2x.
+ *
+ * @param[in] ctmv Current type and measured value (v1 | (v2 << 4)).
+ */
+static void decode_ctmv_2x(uint8_t ctmv, struct dev_context *devc)
+{
+ if ((ctmv > 0x1c) || (!devc)) {
+ sr_err("decode_ctmv_2x(%d): invalid param(s)!", ctmv);
+ return;
+ }
+
+ devc->mq = 0;
+ devc->unit = 0;
+ devc->mqflags = 0;
+
+ switch (ctmv) {
+ /* 00000 unused */
+ case 0x01: /* 00001 V DC */
+ case 0x02: /* 00010 V AC+DC */
+ case 0x03: /* 00011 V AC */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_VOLT;
+ if (ctmv <= 0x02)
+ devc->mqflags |= SR_MQFLAG_DC;
+ if (ctmv >= 0x02) {
+ devc->mqflags |= SR_MQFLAG_AC;
+ if (devc->model >= METRAHIT_24S)
+ devc->mqflags |= SR_MQFLAG_RMS;
+ }
+ break;
+ case 0x04: /* 00100 mA DC */
+ case 0x05: /* 00101 mA AC+DC */
+ devc->scale1000 = -1;
+ case 0x06: /* 00110 A DC */
+ case 0x07: /* 00111 A AC+DC */
+ devc->mq = SR_MQ_CURRENT;
+ devc->unit = SR_UNIT_AMPERE;
+ devc->mqflags |= SR_MQFLAG_DC;
+ if ((ctmv == 0x05) || (ctmv == 0x07)) {
+ devc->mqflags |= SR_MQFLAG_AC;
+ if (devc->model >= METRAHIT_24S)
+ devc->mqflags |= SR_MQFLAG_RMS;
+ }
+ break;
+ case 0x08: /* 01000 Ohm */
+ devc->mq = SR_MQ_RESISTANCE;
+ devc->unit = SR_UNIT_OHM;
+ break;
+ case 0x09: /* 01001 F */
+ devc->mq = SR_MQ_CAPACITANCE;
+ devc->unit = SR_UNIT_FARAD;
+ devc->scale *= 0.1;
+ break;
+ case 0x0a: /* 01010 V dB */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_DECIBEL_VOLT;
+ devc->mqflags |= SR_MQFLAG_AC;
+ if (devc->model >= METRAHIT_24S)
+ devc->mqflags |= SR_MQFLAG_RMS;
+ break;
+ case 0x0b: /* 01011 Hz U ACDC */
+ case 0x0c: /* 01100 Hz U AC */
+ devc->mq = SR_MQ_FREQUENCY;
+ devc->unit = SR_UNIT_HERTZ;
+ devc->mqflags |= SR_MQFLAG_AC;
+ if (ctmv <= 0x0b)
+ devc->mqflags |= SR_MQFLAG_DC;
+ break;
+ case 0x0d: /* 01101 W on power, mA range (29S only) */
+ devc->scale *= 0.001;
+ /* Fall through! */
+ case 0x0e: /* 01110 W on power, A range (29S only) */
+ devc->mq = SR_MQ_POWER;
+ devc->unit = SR_UNIT_WATT;
+ break;
+ case 0x0f: /* 01111 Diode */
+ case 0x10: /* 10000 Diode with buzzer (actually cont. with voltage) */
+ devc->unit = SR_UNIT_VOLT;
+ if (ctmv == 0x0f) {
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->mqflags |= SR_MQFLAG_DIODE;
+ } else {
+ devc->mq = SR_MQ_CONTINUITY;
+ devc->scale *= 0.00001;
+ }
+ devc->unit = SR_UNIT_VOLT;
+ break;
+ case 0x11: /* 10001 Ohm with buzzer */
+ devc->mq = SR_MQ_CONTINUITY;
+ devc->unit = SR_UNIT_OHM;
+ devc->scale1000 = -1;
+ break;
+ case 0x12: /* 10010 Temperature */
+ devc->mq = SR_MQ_TEMPERATURE;
+ devc->unit = SR_UNIT_CELSIUS;
+ /* This can be Fahrenheit. That is detected by range=4 later. */
+ break;
+ /* 0x13 10011, 0x14 10100 unsed */
+ case 0x15: /* 10101 Press (29S only) */
+ /* TODO: What does that mean? Possibly phase shift?
+ Then we need a unit/flag for it. */
+ devc->mq = SR_MQ_GAIN;
+ devc->unit = SR_UNIT_PERCENTAGE;
+ break;
+ case 0x16: /* 10110 Pulse W (29S only) */
+ /* TODO: Own unit and flag for this! */
+ devc->mq = SR_MQ_POWER;
+ devc->unit = SR_UNIT_WATT;
+ break;
+ case 0x17: /* 10111 TRMS V on mains (29S only) */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_VOLT;
+ devc->mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_RMS);
+ devc->vmains_29S = TRUE;
+ break;
+ case 0x18: /* 11000 Counter (zero crossings of a signal) */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_UNITLESS;
+ break;
+ case 0x19: /* 11001 Events U ACDC */
+ case 0x1a: /* 11010 Events U AC */
+ /* TODO: No unit or flags for this yet! */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_UNITLESS;
+ devc->mqflags |= SR_MQFLAG_AC;
+ if (ctmv <= 0x19)
+ devc->mqflags |= SR_MQFLAG_DC;
+ break;
+ case 0x1b: /* 11011 pulse on mains (29S only) */
+ /* TODO: No unit or flags for this yet! */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_UNITLESS;
+ devc->mqflags |= SR_MQFLAG_AC;
+ break;
+ case 0x1c: /* 11100 dropout on mains (29S only) */
+ /* TODO: No unit or flags for this yet! */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_UNITLESS;
+ devc->mqflags |= SR_MQFLAG_AC;
+ break;
+ case 0x1f: /* 11111 Undocumented: 25S in stopwatch mode.
+ The value is voltage, not time, so treat it such. */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_VOLT;
+ devc->mqflags |= SR_MQFLAG_DC;
+ break;
+ case 0x20: /* 100000 Undocumented: 25S in event count mode.
+ Value is 0 anyway. */
+ devc->mq = SR_MQ_VOLTAGE;
+ devc->unit = SR_UNIT_UNITLESS;
+ break;
+ default:
+ sr_err("decode_ctmv_2x(%d, ...): Unknown ctmv!", ctmv);
+ break;
+ }
+}
+
+/**
+ * Decode range/sign/acdc byte special chars, Metrahit 2x, table TR.
+ *
+ * @param[in] rs Range/sign byte.
+ */
+static void decode_rs_2x(uint8_t rs, struct dev_context *devc)
+{
+ int range;
+
+ /* Sign */
+ if (((devc->scale > 0) && (rs & 0x08)) ||
+ ((devc->scale < 0) && !(rs & 0x08)))
+ devc->scale *= -1.0;
+
+ /* Range */
+ range = rs & 0x07;
+ switch (devc->mq) {
+ case SR_MQ_VOLTAGE:
+ if (devc->unit == SR_UNIT_DECIBEL_VOLT)
+ devc->scale *= pow(10.0, -3);
+ else if (devc->vmains_29S)
+ devc->scale *= pow(10.0, range - 2);
+ else
+ devc->scale *= pow(10.0, range - 6);
+ break;
+ case SR_MQ_CURRENT:
+ if (devc->scale1000 != -1) /* uA, mA */
+ range += 1;/* mA and A ranges differ by 10^4, not 10^3!*/
+ devc->scale *= pow(10.0, range - 6);
+ break;
+ case SR_MQ_RESISTANCE:
+ devc->scale *= pow(10.0, range - 3);
+ break;
+ case SR_MQ_FREQUENCY:
+ devc->scale *= pow(10.0, range - 3);
+ break;
+ case SR_MQ_TEMPERATURE:
+ if (range == 4) /* Indicator for °F */
+ devc->unit = SR_UNIT_FAHRENHEIT;
+ devc->scale *= pow(10.0, - 2);
+ break;
+ case SR_MQ_CAPACITANCE:
+ if (range == 7)
+ range -= 1; /* Same value as range 6 */
+ devc->scale *= pow(10.0, range - 13);
+ break;
+ /* TODO: 29S Mains measurements. */
+ }
+}
+
+/**
+ * Decode range/sign/acdc byte special chars, Metrahit 2x, table TR 2.
+ *
+ * @param[in] rs Range/sign byte.
+ */
+static void decode_rs_2x_TR2(uint8_t rs, struct dev_context *devc)
+{
+ int range;
+
+ /* Range */
+ range = rs & 0x07;
+ switch (devc->mq) {
+ case SR_MQ_CURRENT:
+ if (devc->scale1000 == -1) /* mA */
+ switch(range) {
+ case 0: case 1: /* 100, 300 µA */
+ devc->scale *= pow(10.0, -6);
+ break;
+ case 2: case 3: /* 1, 3 mA */
+ devc->scale *= pow(10.0, -5);
+ break;
+ case 4: case 5: /* 10, 30 mA */
+ devc->scale *= pow(10.0, -4);
+ break;
+ case 6: case 7: /* 100, 300 mA */
+ devc->scale *= pow(10.0, -3);
+ break;
+ }
+ else /* A */
+ switch(range) {
+ case 0: case 1: /* 1, 3 A */
+ devc->scale *= pow(10.0, -5);
+ break;
+ case 2: /* 10 A */
+ devc->scale *= pow(10.0, -4);
+ break;
+ }
+ break;
+ default:
+ decode_rs_2x(rs, devc);
+ return;
+ }
+
+ /* Sign */
+ if (((devc->scale > 0) && (rs & 0x08)) ||
+ ((devc->scale < 0) && !(rs & 0x08)))
+ devc->scale *= -1.0;
+}
+
+
+/**
+ * Decode special chars (Metrahit 2x).
+ *
+ * @param[in] spc Special characters 1 and 2 (s1 | (s2 << 4)).
+ */
+static void decode_spc_2x(uint8_t spc, struct dev_context *devc)
+{
+ /* xxxxxxx1 Fuse */
+
+ /* xxxxxx1x Low battery */
+
+ /* xxxxx1xx BEEP */
+
+ /* xxxx1xxx ZERO */
+
+ /* xxx1xxxx DATA */
+ setmqf(devc, SR_MQFLAG_HOLD, spc & 0x10);
+
+ /* x11xxxxx unused */
+ /* 1xxxxxxx MAN */
+ setmqf(devc, SR_MQFLAG_AUTORANGE, !(spc & 0x80));
+}
+
+/** Clean range and sign. */
+static void clean_rs_v(struct dev_context *devc)
+{
+ devc->value = 0.0;
+ devc->scale = 1.0;
+}
+
+/** Clean current type, measured variable, range and sign. */
+static void clean_ctmv_rs_v(struct dev_context *devc)
+{
+ devc->mq = 0;
+ devc->unit = 0;
+ devc->mqflags = 0;
+ devc->scale1000 = 0;
+ devc->vmains_29S = FALSE;
+ clean_rs_v(devc);
+}
+
+/** Send prepared value. */
+static void send_value(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_analog analog;
+ struct sr_datafeed_packet packet;
+
+ devc = sdi->priv;
+
+ memset(&analog, 0, sizeof(analog));
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+ analog.mq = devc->mq;
+ analog.unit = devc->unit;
+ analog.mqflags = devc->mqflags;
+ analog.data = &devc->value;
+
+ memset(&packet, 0, sizeof(packet));
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+
+ devc->num_samples++;
+}
+
+/** Process 6-byte data message, Metrahit 1x/2x send mode. */
+static void process_msg_dta_6(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int cnt;
+ uint8_t dgt;
+
+ devc = sdi->priv;
+ clean_rs_v(devc);
+
+ /* Byte 0, range and sign */
+ if (devc->model <= METRAHIT_16X)
+ decode_rs_16(bc(devc->buf[0]), devc);
+ else if (devc->model < METRAHIT_2X)
+ decode_rs_18(bc(devc->buf[0]), devc);
+ else {
+ decode_rs_2x(bc(devc->buf[0]), devc);
+ devc->scale *= 10; /* Compensate for format having only 5 digits, decode_rs_2x() assumes 6. */
+ }
+
+ /* Bytes 1-5, digits (ls first). */
+ for (cnt = 0; cnt < 5; cnt++) {
+ dgt = bc(devc->buf[1 + cnt]);
+ if (dgt >= 10) {
+ /* 10 Overload; on model <= 16X also 11 possible. */
+ devc->value = NAN;
+ devc->scale = 1.0;
+ break;
+ }
+ devc->value += pow(10.0, cnt) * dgt;
+ }
+
+ sr_spew("process_msg_dta_6() value=%f scale=%f scale1000=%d",
+ devc->value, devc->scale, devc->scale1000);
+ if (devc->value != NAN)
+ devc->value *= devc->scale * pow(1000.0, devc->scale1000);
+
+ /* Create and send packet. */
+ send_value(sdi);
+}
+
+/** Process 5-byte info message, Metrahit 1x/2x. */
+static void process_msg_inf_5(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ enum model model;
+
+ devc = sdi->priv;
+
+ clean_ctmv_rs_v(devc);
+
+ /* Process byte 0 */
+ model = gmc_decode_model_sm(bc(devc->buf[0]));
+ if (model != devc->model) {
+ sr_warn("Model mismatch in data: Detected %s, now %s",
+ gmc_model_str(devc->model), gmc_model_str(model));
+ }
+
+ /* Process bytes 1-4 */
+ if (devc->model <= METRAHIT_16X) {
+ decode_ctmv_16(bc(devc->buf[1]), devc);
+ decode_spc_16(bc(devc->buf[2]) | (bc(devc->buf[3]) << 4), devc);
+ decode_rs_16(bc(devc->buf[4]), devc);
+ } else if (devc->model <= METRAHIT_18S) {
+ decode_ctmv_18(bc(devc->buf[1]), devc);
+ decode_spc_18(bc(devc->buf[2]) | (bc(devc->buf[3]) << 4), devc);
+ decode_rs_18(bc(devc->buf[4]), devc);
+ } else { /* Must be Metrahit 2x */
+ decode_ctmv_2x(bc(devc->buf[1]), devc);
+ decode_spc_2x(bc(devc->buf[2]) | (bc(devc->buf[3]) << 4), devc);
+ decode_rs_2x(bc(devc->buf[4]), devc);
+ }
+}
+
+/** Process 10-byte info/data message, Metrahit 15+. */
+static void process_msg_inf_10(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int cnt;
+ uint8_t dgt;
+
+ devc = sdi->priv;
+
+ process_msg_inf_5(sdi);
+
+ /* Now decode numbers */
+ for (cnt = 0; cnt < 5; cnt++) {
+ dgt = bc(devc->buf[5 + cnt]);
+ if (dgt == 11) { /* Empty digit */
+ dgt = 0;
+ }
+ else if (dgt >= 12) { /* Overload */
+ devc->value = NAN;
+ devc->scale = 1.0;
+ break;
+ }
+ devc->value += pow(10.0, cnt) * dgt;
+ }
+ sr_spew("process_msg_inf_10() value=%f scale=%f scalet=%d",
+ devc->value, devc->scale, devc->scale1000);
+
+ if (devc->value != NAN)
+ devc->value *= devc->scale * pow(1000.0, devc->scale1000);
+
+ /* Create and send packet. */
+ send_value(sdi);
+}
+
+/** Decode send interval (Metrahit 2x only). */
+static const char *decode_send_interval(uint8_t si)
+{
+ switch (si) {
+ case 0x00:
+ return "0.05";
+ case 0x01:
+ return "0.1";
+ case 0x02:
+ return "0.2";
+ case 0x03:
+ return "0.5";
+ case 0x04:
+ return "00:01";
+ case 0x05:
+ return "00:02";
+ case 0x06:
+ return "00:05";
+ case 0x07:
+ return "00:10";
+ case 0x08:
+ return "00:20";
+ case 0x09:
+ return "00:30";
+ case 0x0a:
+ return "01:00";
+ case 0x0b:
+ return "02:00";
+ case 0x0c:
+ return "05:00";
+ case 0x0d:
+ return "10:00";
+ case 0x0e:
+ return "----";
+ case 0x0f:
+ return "data";
+ default:
+ return "Unknown value";
+ }
+}
+
+/** Process 13-byte info/data message, Metrahit 2x. */
+static void process_msg_inf_13(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ enum model model;
+ int cnt;
+ uint8_t dgt;
+
+ devc = sdi->priv;
+
+ clean_ctmv_rs_v(devc);
+
+ /* Byte 0, model. */
+ model = gmc_decode_model_sm(bc(devc->buf[0]));
+ if (model != devc->model) {
+ sr_warn("Model mismatch in data: Detected %s, now %s",
+ gmc_model_str(devc->model), gmc_model_str(model));
+ }
+
+ /* Bytes 1-4, 11. */
+ decode_ctmv_2x(bc(devc->buf[1]) | (bc(devc->buf[11]) << 4), devc);
+ decode_spc_2x(bc(devc->buf[2]) | (bc(devc->buf[3]) << 4), devc);
+ decode_rs_2x(bc(devc->buf[4]), devc);
+
+ /* Bytes 5-10, digits (ls first). */
+ for (cnt = 0; cnt < 6; cnt++) {
+ dgt = bc(devc->buf[5 + cnt]);
+ if (dgt == 10) { /* Overload */
+ devc->value = NAN;
+ devc->scale = 1.0;
+ break;
+ }
+ devc->value += pow(10.0, cnt) * dgt;
+ }
+ sr_spew("process_msg_inf_13() value=%f scale=%f scale1000=%d mq=%d "
+ "unit=%d mqflags=0x%02llx", devc->value, devc->scale,
+ devc->scale1000, devc->mq, devc->unit, devc->mqflags);
+ if (devc->value != NAN)
+ devc->value *= devc->scale * pow(1000.0, devc->scale1000);
+
+ /* Byte 12, Send Interval */
+ sr_spew("Send interval: %s", decode_send_interval(bc(devc->buf[12])));
+
+ /* Create and send packet. */
+ send_value(sdi);
+}
+
+/** Dump contents of 14-byte message.
+ * @param buf Pointer to array of 14 data bytes.
+ * @param[in] raw Write only data bytes, no interpretation.
+ */
+void dump_msg14(guchar* buf, gboolean raw)
+{
+ if (!buf)
+ return;
+
+ if (raw)
+ sr_spew("msg14: 0x %02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
+ buf[7], buf[8], buf[9], buf[10], buf[11], buf[12],
+ buf[13]);
+ else
+ sr_spew("msg14: 0x a=%d c1=%02x c2=%02x cmd=%02x dta=%02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x chs=%02x",
+ buf[1] == 0x2b?buf[0] >> 2:buf[0] % 0x0f, buf[1], buf[2], buf[3], buf[4], buf[5],
+ buf[6], buf[7], buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13]);
+}
+
+/** Calc checksum for 14 byte message type.
+ *
+ * @param[in] dta Pointer to array of 13 data bytes.
+ * @return Checksum.
+ */
+static guchar calc_chksum_14(guchar* dta)
+{
+ guchar cnt, chs;
+
+ for (chs = 0, cnt = 0; cnt < 13; cnt++)
+ chs += dta[cnt];
+
+ return (64 - chs) & MASK_6BITS;
+}
+
+/** Check 14-byte message, Metrahit 2x. */
+static int chk_msg14(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int retc;
+ gboolean isreq; /* Message is request to multimeter (otherwise response) */
+ uint8_t addr; /* Adaptor address */
+
+ retc = SR_OK;
+
+ /* Check parameters and message */
+ if (!sdi || !(devc = sdi->priv))
+ return SR_ERR_ARG;
+
+ if (devc->buflen != 14) {
+ sr_err("process_msg_14(): Msg len 14 expected!");
+ return SR_ERR_ARG;
+ }
+
+ isreq = devc->buf[1] == 0x2b;
+ if (isreq)
+ addr = devc->buf[0] >> 2;
+ else
+ addr = devc->buf[0] & 0x0f;
+
+ if ((devc->addr != addr) && !(isreq && (addr == 0))) {
+ sr_err("process_msg_14(): Address mismatch, msg for other device!");
+ retc = SR_ERR_ARG;
+ }
+
+ if (devc->buf[1] == 0) { /* Error msg from device! */
+ retc = SR_ERR_ARG;
+ switch (devc->buf[2]) {
+ case 1: /* Not used */
+ sr_err("Device: Illegal error code!");
+ break;
+ case 2: /* Incorrect check sum of received block */
+ sr_err("Device: Incorrect checksum in cmd!");
+ break;
+ case 3: /* Incorrect length of received block */
+ sr_err("Device: Incorrect block length in cmd!");
+ break;
+ case 4: /* Incorrect 2nd or 3rd byte */
+ sr_err("Device: Incorrect byte 2 or 3 in cmd!");
+ break;
+ case 5: /* Parameter out of range */
+ sr_err("Device: Parameter out of range!");
+ break;
+ default:
+ sr_err("Device: Unknown error code!");
+ }
+ retc = SR_ERR_ARG;
+ }
+ else if (!isreq && ((devc->buf[1] != 0x27) || (devc->buf[2] != 0x3f))) {
+ sr_err("process_msg_14(): byte 1/2 unexpected!");
+ retc = SR_ERR_ARG;
+ }
+
+ if (calc_chksum_14(devc->buf) != devc->buf[13]) {
+ sr_err("process_msg_14(): Invalid checksum!");
+ retc = SR_ERR_ARG;
+ }
+
+ if (retc != SR_OK)
+ dump_msg14(devc->buf, TRUE);
+
+ return retc;
+}
+
+/** Check 14-byte message, Metrahit 2x. */
+SR_PRIV int process_msg14(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int retc;
+ uint8_t addr;
+ uint8_t cnt, dgt;
+
+ if ((retc = chk_msg14(sdi)) != SR_OK)
+ return retc;
+
+ devc = sdi->priv;
+
+ clean_ctmv_rs_v(devc);
+ addr = devc->buf[0] & MASK_6BITS;
+ if (addr != devc->addr)
+ sr_info("Device address mismatch %d/%d!", addr, devc->addr);
+
+ switch (devc->buf[3]) { /* That's the command this reply is for */
+ /* 0 cannot occur, the respective message is not a 14-byte message */
+ case 1: /* Read first free and occupied address */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 2: /* Clear all RAM in multimeter */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 3: /* Read firmware version and status */
+ sr_spew("Cmd 3, Read firmware and status", devc->buf[3]);
+ switch (devc->cmd_idx) {
+ case 0:
+ devc->fw_ver_maj = devc->buf[5];
+ devc->fw_ver_min = devc->buf[4];
+ sr_spew("Firmware version %d.%d", (int)devc->fw_ver_maj, (int)devc->fw_ver_min);
+ sr_spew("Rotary Switch Position (1..10): %d", (int)devc->buf[6]);
+ /** Docs say values 0..9, but that's not true */
+ sr_spew("Measurement Function: %d ", (int)devc->buf[7]);
+ decode_ctmv_2x(devc->buf[7], devc);
+ sr_spew("Range: 0x%x", devc->buf[8]);
+ decode_rs_2x_TR2(devc->buf[8] & 0x0f, devc); /* Docs wrong, uses conversion table TR_2! */
+ devc->autorng = (devc->buf[8] & 0x20) == 0;
+ // TODO 9, 10: 29S special functions
+ devc->ubatt = 0.1 * (float)devc->buf[11];
+ devc->model = gmc_decode_model_bd(devc->buf[12]);
+ sr_spew("Model=%s, battery voltage=%2.1f V", gmc_model_str(devc->model), (double)devc->ubatt);
+ break;
+ case 1:
+ sr_spew("Internal version %d.%d", (int)devc->buf[5], (int)devc->buf[4]);
+ sr_spew("Comm mode: 0x%x", (int)devc->buf[6]);
+ sr_spew("Block cnt%%64: %d", (int)devc->buf[7]);
+ sr_spew("drpCi: %d drpCh: %d", (int)devc->buf[8], (int)devc->buf[9]);
+ // Semantics undocumented. Possibly Metrahit 29S dropouts stuff?
+ break;
+ default:
+ sr_spew("Cmd 3: Unknown cmd_idx=%d", devc->cmd_idx);
+ break;
+ }
+ break;
+ case 4: /* Set real time, date, sample rate, trigger, ... */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 5: /* Read real time, date, sample rate, trigger... */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 6: /* Set modes or power off */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 7: /* Set measurement function, range, autom/man. */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 8: /* Get one measurement value */
+ sr_spew("Cmd 8, get one measurement value");
+ sr_spew("Measurement Function: %d ", (int)devc->buf[5]);
+ decode_ctmv_2x(devc->buf[5], devc);
+ if (!(devc->buf[6] & 0x10)) /* If bit4=0, old data. */
+ return SR_OK;
+
+ decode_rs_2x_TR2(devc->buf[6] & 0x0f, devc); // The docs say conversion table TR_3, but that does not work
+ setmqf(devc, SR_MQFLAG_AUTORANGE, devc->autorng);
+ /* 6 digits */
+ for (cnt = 0; cnt < 6; cnt++) {
+ dgt = bc(devc->buf[7 + cnt]);
+ if (dgt == 10) { /* Overload */
+ devc->value = NAN;
+ devc->scale = 1.0;
+ break;
+ }
+ else if (dgt == 13) { /* FUSE */
+ sr_err("FUSE!");
+ }
+ else if (dgt == 14) { /* Function recognition mode, OPEN */
+ sr_info("Function recognition mode, OPEN!");
+ devc->value = NAN;
+ devc->scale = 1.0;
+ break;
+ }
+ devc->value += pow(10.0, cnt) * dgt;
+ }
+ sr_spew("process_msg14() value=%f scale=%f scale1000=%d mq=%d "
+ "unit=%d mqflags=0x%02llx", devc->value, devc->scale,
+ devc->scale1000, devc->mq, devc->unit, devc->mqflags);
+ if (devc->value != NAN)
+ devc->value *= devc->scale * pow(1000.0, devc->scale1000);
+
+ send_value(sdi);
+
+ break;
+ default:
+ sr_spew("Unknown cmd %d!", devc->buf[3]);
+ break;
+ }
+
+ return SR_OK;
+}
+
+/** Data reception callback function. */
+SR_PRIV int gmc_mh_1x_2x_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ uint8_t buf, msgt;
+ int len;
+ gdouble elapsed_s;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+
+ if (revents == G_IO_IN) { /* Serial data arrived. */
+ while (GMC_BUFSIZE - devc->buflen - 1 > 0) {
+ len = serial_read(serial, devc->buf + devc->buflen, 1);
+ if (len < 1)
+ break;
+ buf = *(devc->buf + devc->buflen);
+ sr_spew("read 0x%02x/%d/%d", buf, buf, buf & MSGC_MASK);
+ devc->buflen += len;
+ if (!devc->settings_ok) {
+ /*
+ * If no device type/settings record processed
+ * yet, wait for one.
+ */
+ if ((devc->buf[0] & MSGID_MASK) != MSGID_INF) {
+ devc->buflen = 0;
+ continue;
+ }
+ devc->settings_ok = TRUE;
+ }
+
+ msgt = devc->buf[0] & MSGID_MASK;
+ switch (msgt) {
+ case MSGID_INF:
+ if (devc->buflen == 13) {
+ process_msg_inf_13(sdi);
+ devc->buflen = 0;
+ continue;
+ } else if ((devc->buflen == 10) &&
+ (devc->model <= METRAHIT_18S)) {
+ process_msg_inf_10(sdi);
+ devc->buflen = 0;
+ continue;
+ }
+ else if ((devc->buflen >= 5) &&
+ (devc->buf[devc->buflen - 1] &
+ MSGID_MASK) != MSGID_DATA) {
+ /*
+ * Char just received is beginning
+ * of next message.
+ */
+ process_msg_inf_5(sdi);
+ devc->buf[0] =
+ devc->buf[devc->buflen - 1];
+ devc->buflen = 1;
+ continue;
+ }
+ break;
+ case MSGID_DTA:
+ case MSGID_D10:
+ if (devc->buflen == 6) {
+ process_msg_dta_6(sdi);
+ devc->buflen = 0;
+ }
+ break;
+ case MSGID_DATA:
+ sr_err("Comm error, unexpected data byte!");
+ devc->buflen = 0;
+ break;
+ }
+ }
+ }
+
+ /* If number of samples or time limit reached, stop acquisition. */
+ if (devc->limit_samples && (devc->num_samples >= devc->limit_samples))
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+
+ if (devc->limit_msec) {
+ elapsed_s = g_timer_elapsed(devc->elapsed_msec, NULL);
+ if ((elapsed_s * 1000) >= devc->limit_msec)
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ }
+
+ return TRUE;
+}
+
+SR_PRIV int gmc_mh_2x_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ uint8_t buf;
+ int len;
+ gdouble elapsed_s;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+
+ if (revents == G_IO_IN) { /* Serial data arrived. */
+ while (GMC_BUFSIZE - devc->buflen - 1 > 0) {
+ len = serial_read(serial, devc->buf + devc->buflen, 1);
+ if (len < 1)
+ break;
+ buf = *(devc->buf + devc->buflen);
+ sr_spew("read 0x%02x/%d/%d", buf, buf, buf & MASK_6BITS);
+ devc->buf[devc->buflen] &= MASK_6BITS;
+ devc->buflen += len;
+
+ if (devc->buflen == 14) {
+ devc->response_pending = FALSE;
+ sr_spew("gmc_mh_2x_receive_data processing msg");
+ process_msg14(sdi);
+ devc->buflen = 0;
+ }
+ }
+ }
+
+ /* If number of samples or time limit reached, stop acquisition. */
+ if (devc->limit_samples && (devc->num_samples >= devc->limit_samples))
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+
+ if (devc->limit_msec) {
+ elapsed_s = g_timer_elapsed(devc->elapsed_msec, NULL);
+ if ((elapsed_s * 1000) >= devc->limit_msec)
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ }
+
+ /* Request next data set, if required */
+ if (sdi->status == SR_ST_ACTIVE) {
+ if (devc->response_pending) {
+ gint64 elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
+ if (elapsed_us > 1*1000*1000) /* Timeout! */
+ devc->response_pending = FALSE;
+ }
+ if (!devc->response_pending) {
+ devc->cmd_seq++;
+ if (devc->cmd_seq % 10 == 0) {
+ if (req_stat14(sdi, FALSE) != SR_OK)
+ return FALSE;
+ }
+ else if (req_meas14(sdi) != SR_OK)
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/** Create 14 (42) byte command for Metrahit 2x multimeter in bidir mode.
+ *
+ * Actually creates 42 bytes due to the encoding method used.
+ * @param[in] addr Device address (0=adapter, 1..15 multimeter; for byte 0).
+ * @param[in] func Function code (byte 3).
+ * @param[in] params Further parameters (9 bytes)
+ * @param[out] buf Buffer to create msg in (42 bytes).
+ */
+void create_cmd_14(guchar addr, guchar func, guchar* params, guchar* buf)
+{
+ uint8_t dta[14]; /* Unencoded message */
+ int cnt;
+
+ if (!params || !buf)
+ return;
+
+ /* 0: Address */
+ dta[0] = ((addr << 2) | 0x03) & MASK_6BITS;
+
+ /* 1-3: Set command header */
+ dta[1] = 0x2b;
+ dta[2] = 0x3f;
+ dta[3] = func;
+
+ /* 4-12: Copy further parameters */
+ for (cnt = 0; cnt < 9; cnt++)
+ dta[cnt+4] = (params[cnt] & MASK_6BITS);
+
+ /* 13: Checksum (b complement) */
+ dta[13] = calc_chksum_14(dta);
+
+ /* The whole message is packed into 3 bytes per byte now (lower 6 bits only) the most
+ * peculiar way I have ever seen. Possibly to improve IR communication? */
+ for (cnt = 0; cnt < 14; cnt++) {
+ buf[3*cnt] = (dta[cnt] & 0x01 ? 0x0f : 0) | (dta[cnt] & 0x02 ? 0xf0 : 0);
+ buf[3*cnt + 1] = (dta[cnt] & 0x04 ? 0x0f : 0) | (dta[cnt] & 0x08 ? 0xf0 : 0);
+ buf[3*cnt + 2] = (dta[cnt] & 0x10 ? 0x0f : 0) | (dta[cnt] & 0x20 ? 0xf0 : 0);
+ }
+}
+
+/** Request one measurement from 2x multimeter (msg 8).
+ *
+ */
+int req_meas14(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ uint8_t params[9];
+ uint8_t msg[42];
+
+ if (!sdi || !(devc = sdi->priv) || !(serial = sdi->conn))
+ return SR_ERR;
+
+ memset(params, 0, sizeof(params));
+ params[0] = 0;
+ devc->cmd_idx = 0;
+ create_cmd_14(devc->addr, 8, params, msg);
+ devc->req_sent_at = g_get_monotonic_time();
+ if (serial_write(serial, msg, sizeof(msg)) == -1) {
+ return SR_ERR;
+ }
+
+ devc->response_pending = TRUE;
+
+ return SR_OK;
+}
+
+/** Request status from 2x multimeter (msg 3).
+ * @param[in] power_on Try to power on powered off multimeter by sending additional messages.
+ */
+int req_stat14(const struct sr_dev_inst *sdi, gboolean power_on)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ uint8_t params[9];
+ uint8_t msg[42];
+
+ if (!sdi || !(devc = sdi->priv) || !(serial = sdi->conn))
+ return SR_ERR;
+
+ memset(params, 0, sizeof(params));
+ params[0] = 0;
+ devc->cmd_idx = 0;
+ create_cmd_14(devc->addr, 3, params, msg);
+
+ if (power_on) {
+ sr_info("Write some data and wait 3s to turn on powered off device...");
+ if (serial_write(serial, msg, sizeof(msg)) < 0)
+ return SR_ERR;
+ g_usleep(1*1000*1000);
+ if (serial_write(serial, msg, sizeof(msg)) < 0)
+ return SR_ERR;
+ g_usleep(1*1000*1000);
+ if (serial_write(serial, msg, sizeof(msg)) < 0)
+ return SR_ERR;
+ g_usleep(1*1000*1000);
+ serial_flush(serial);
+ }
+
+ /* Write message and wait for reply */
+ devc->req_sent_at = g_get_monotonic_time();
+ if (serial_write(serial, msg, sizeof(msg)) == -1) {
+ return SR_ERR;
+ }
+
+ devc->response_pending = TRUE;
+
+ return SR_OK;
+}
+
+/** Decode model in "send mode".
+ *
+ * @param[in] mcode Model code.
+ * @return Model code.
+ */
+SR_PRIV int gmc_decode_model_sm(uint8_t mcode)
+{
+ if (mcode > 0xf) {
+ sr_err("decode_model(%d): Model code 0..15 expected!", mcode);
+ return METRAHIT_NONE;
+ }
+
+ switch(mcode) {
+ case 0x04: /* 0100b */
+ return METRAHIT_12S;
+ case 0x08: /* 1000b */
+ return METRAHIT_13S14A;
+ case 0x09: /* 1001b */
+ return METRAHIT_14S;
+ case 0x0A: /* 1010b */
+ return METRAHIT_15S;
+ case 0x0B: /* 1011b */
+ return METRAHIT_16S;
+ case 0x06: /* 0110b (undocumented by GMC!) */
+ return METRAHIT_16I;
+ case 0x07: /* 0111b (undocumented by GMC!) */
+ return METRAHIT_16T;
+ case 0x0D: /* 1101b */
+ return METRAHIT_18S;
+ case 0x02: /* 0010b */
+ return METRAHIT_22SM;
+ case 0x03: /* 0011b */
+ return METRAHIT_23S;
+ case 0x0F: /* 1111b */
+ return METRAHIT_24S;
+ case 0x05: /* 0101b */
+ return METRAHIT_25S;
+ case 0x01: /* 0001b */
+ return METRAHIT_26SM;
+ case 0x0C: /* 1100b */
+ return METRAHIT_28S;
+ case 0x0E: /* 1110b */
+ return METRAHIT_29S;
+ default:
+ sr_err("Unknown model code %d!", mcode);
+ return METRAHIT_NONE;
+ }
+}
+
+/** Convert GMC model code in bidirectional mode to sigrok-internal one.
+ *
+ * @param[in] mcode Model code.
+ *
+ * @return Model code.
+ */
+SR_PRIV int gmc_decode_model_bd(uint8_t mcode)
+{
+ switch (mcode & 0x1f) {
+ case 2:
+ if (mcode & 0x20)
+ return METRAHIT_22M;
+ else
+ return METRAHIT_22S;
+ case 3:
+ return METRAHIT_23S;
+ case 4:
+ return METRAHIT_24S;
+ case 5:
+ return METRAHIT_25S;
+ case 1:
+ if (mcode & 0x20)
+ return METRAHIT_26M;
+ else
+ return METRAHIT_26S;
+ case 12:
+ return METRAHIT_28S;
+ case 14:
+ return METRAHIT_29S;
+ default:
+ sr_err("Unknown model code %d!", mcode);
+ return METRAHIT_NONE;
+ }
+}
+
+/** Convert sigrok-internal model code to string.
+ *
+ * @param[in] mcode Model code.
+ *
+ * @return Model code string.
+ */
+SR_PRIV const char *gmc_model_str(enum model mcode)
+{
+ switch (mcode) {
+ case METRAHIT_NONE:
+ return "-uninitialized model variable-";
+ case METRAHIT_12S:
+ return "METRAHit 12S";
+ case METRAHIT_13S14A:
+ return "METRAHit 13S/14A";
+ case METRAHIT_14S:
+ return "METRAHit 14S";
+ case METRAHIT_15S:
+ return "METRAHit 15S";
+ case METRAHIT_16S:
+ return "METRAHit 16S";
+ case METRAHIT_16I:
+ return "METRAHit 16I/16L";
+ case METRAHIT_16T:
+ return "METRAHit 16T/16U/KMM2002";
+ case METRAHIT_18S:
+ return "METRAHit 18S";
+ case METRAHIT_22SM:
+ return "METRAHit 22S/M";
+ case METRAHIT_22S:
+ return "METRAHit 22S";
+ case METRAHIT_22M:
+ return "METRAHit 22M";
+ case METRAHIT_23S:
+ return "METRAHit 23S";
+ case METRAHIT_24S:
+ return "METRAHit 24S";
+ case METRAHIT_25S:
+ return "METRAHit 25S";
+ case METRAHIT_26SM:
+ return "METRAHit 26S/M";
+ case METRAHIT_26S:
+ return "METRAHit 26S";
+ case METRAHIT_26M:
+ return "METRAHit 26M";
+ case METRAHIT_28S:
+ return "METRAHit 28S";
+ case METRAHIT_29S:
+ return "METRAHit 29S";
+ default:
+ return "Unknown model code";
+ }
+}
+
+
+/** @copydoc sr_dev_driver.config_set
+ */
+SR_PRIV int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ uint8_t params[9];
+ uint8_t msg[42];
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ switch (key) {
+ case SR_CONF_POWER_OFF:
+ if (devc->model < METRAHIT_2X)
+ return SR_ERR_NA;
+ if (!g_variant_get_boolean(data))
+ return SR_ERR;
+ sr_info("Powering device off.");
+
+ memset(params, 0, sizeof(params));
+ params[0] = 5;
+ params[1] = 5;
+ create_cmd_14(devc->addr, 6, params, msg);
+ if (serial_write(sdi->conn, msg, sizeof(msg)) == -1)
+ return SR_ERR;
+ else
+ g_usleep(2000000); /* Wait to ensure transfer before interface switched off. */
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ if (g_variant_get_uint64(data) == 0) {
+ sr_err("LIMIT_MSEC can't be 0.");
+ return SR_ERR;
+ }
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013, 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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/>.
+ */
+
+/** @file
+ * Gossen Metrawatt Metrahit 1x/2x drivers
+ * @internal
+ */
+
+#ifndef LIBSIGROK_HARDWARE_GMC_MH_1X_2X_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_GMC_MH_1X_2X_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "gmc-mh-1x-2x"
+
+#define GMC_BUFSIZE 266
+
+/** Message ID bits 4, 5 */
+#define MSGID_MASK 0x30 /**< Mask to get message ID bits */
+#define MSGID_INF 0x00 /**< Start of message with device info */
+#define MSGID_D10 0x10 /**< Start of data message, non-displayed intermediate */
+#define MSGID_DTA 0x20 /**< Start of data message, displayed, averaged */
+#define MSGID_DATA 0x30 /**< Data byte in message */
+
+#define MSGC_MASK 0x0f /**< Mask to get message byte contents in send mode */
+
+#define MSGSRC_MASK 0xc0 /**< Mask to get bits related to message source */
+
+#define bc(x) (x & MSGC_MASK) /**< Get contents from a byte */
+
+#define MASK_6BITS 0x3f /**< Mask lower six bits. */
+
+/**
+ * Internal multimeter model codes. In opposite to the multimeter models from
+ * protocol (see decode_model()), these codes allow working with ranges.
+ */
+enum model {
+ METRAHIT_NONE = 0, /**< Value for uninitialized variable */
+ METRAHIT_12S = 12,
+ METRAHIT_13S14A = 13,
+ METRAHIT_14S = 14,
+ METRAHIT_15S = 15,
+ METRAHIT_16S = 16,
+ METRAHIT_16I = 17, /**< Metrahit 16I, L */
+ METRAHIT_16T = 18, /**< Metrahit 16T, U, KMM2002 */
+ METRAHIT_16X = METRAHIT_16T, /**< All Metrahit 16 */
+ /* A Metrahit 17 exists, but seems not to have an IR interface. */
+ METRAHIT_18S = 19,
+ METRAHIT_2X = 20, /**< For model type comparisons */
+ METRAHIT_22SM = METRAHIT_2X + 1, /**< Send mode */
+ METRAHIT_22S = METRAHIT_22SM + 1, /**< Bidi mode */
+ METRAHIT_22M = METRAHIT_22S + 1, /**< Bidi mode */
+ METRAHIT_23S = METRAHIT_22M + 1,
+ METRAHIT_24S = METRAHIT_23S + 1,
+ METRAHIT_25S = METRAHIT_24S + 1,
+ METRAHIT_26SM = METRAHIT_25S + 1, /**< Send mode */
+ METRAHIT_26S = METRAHIT_26SM + 1, /**< Bidi mode */
+ METRAHIT_26M = METRAHIT_26S + 1, /**< Bidi mode */
+ METRAHIT_28S = METRAHIT_26M + 1,
+ METRAHIT_29S = METRAHIT_28S + 1,
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Model-specific information */
+ enum model model; /**< Model code. */
+
+ /* Acquisition settings */
+ uint64_t limit_samples; /**< Target number of samples */
+ uint64_t limit_msec; /**< Target sampling time */
+
+ /* Opaque pointer passed in by frontend. */
+ void *cb_data;
+
+ /* Operational state */
+ gboolean settings_ok; /**< Settings msg received yet. */
+ int msg_type; /**< Message type (MSGID_INF, ...). */
+ int msg_len; /**< Message lengh (valid when msg, curr. type known).*/
+ int mq; /**< Measured quantity */
+ int unit; /**< Measured unit */
+ uint64_t mqflags; /**< Measured quantity flags */
+ float value; /**< Measured value */
+ float scale; /**< Scale for value. */
+ int8_t scale1000; /**< Additional scale factor 1000x. */
+ gboolean vmains_29S; /**< Measured ctmv is V mains (29S only). */
+ int addr; /**< Device address (1..15). */
+ int cmd_idx; /**< Parameter "Idx" (Index) of current command, if required. */
+ int cmd_seq; /**< Command sequence. Used to query status every n messages. */
+ gboolean autorng; /**< Auto range enabled. */
+ float ubatt; /**< Battery voltage. */
+ uint8_t fw_ver_maj; /**< Firmware version major. */
+ uint8_t fw_ver_min; /**< Firmware version minor. */
+ int64_t req_sent_at; /**< Request sent. */
+ gboolean response_pending; /**< Request sent, response is pending. */
+
+ /* Temporary state across callbacks */
+ uint64_t num_samples; /**< Current #samples for limit_samples */
+ GTimer *elapsed_msec; /**< Used for sampling with limit_msec */
+ uint8_t buf[GMC_BUFSIZE]; /**< Buffer for read callback */
+ int buflen; /**< Data len in buf */
+};
+
+/* Forward declarations */
+SR_PRIV int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg);
+SR_PRIV void create_cmd_14(guchar addr, guchar func, guchar* params, guchar* buf);
+SR_PRIV void dump_msg14(guchar* buf, gboolean raw);
+SR_PRIV int gmc_decode_model_bd(uint8_t mcode);
+SR_PRIV int gmc_decode_model_sm(uint8_t mcode);
+SR_PRIV int gmc_mh_1x_2x_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int gmc_mh_2x_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV const char *gmc_model_str(enum model mcode);
+SR_PRIV int process_msg14(struct sr_dev_inst *sdi);
+SR_PRIV int req_meas14(const struct sr_dev_inst *sdi);
+SR_PRIV int req_stat14(const struct sr_dev_inst *sdi, gboolean power_on);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
+ *
+ * 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 <stdlib.h>
+#include "protocol.h"
+
+#define SERIALCOMM "115200/8n1/flow=1"
+
+SR_PRIV struct sr_dev_driver hameg_hmo_driver_info;
+static struct sr_dev_driver *di = &hameg_hmo_driver_info;
+
+static const char *manufacturers[] = {
+ "HAMEG",
+};
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+enum {
+ CG_INVALID = -1,
+ CG_NONE,
+ CG_ANALOG,
+ CG_DIGITAL,
+};
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static int check_manufacturer(const char *manufacturer)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(manufacturers); ++i)
+ if (!strcmp(manufacturer, manufacturers[i]))
+ return SR_OK;
+
+ return SR_ERR;
+}
+
+static struct sr_dev_inst *hmo_probe_serial_device(struct sr_scpi_dev_inst *scpi)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_scpi_hw_info *hw_info;
+
+ sdi = NULL;
+ devc = NULL;
+ hw_info = NULL;
+
+ if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+ sr_info("Couldn't get IDN response.");
+ goto fail;
+ }
+
+ if (check_manufacturer(hw_info->manufacturer) != SR_OK)
+ goto fail;
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE,
+ hw_info->manufacturer, hw_info->model,
+ hw_info->firmware_version))) {
+ goto fail;
+ }
+ sr_scpi_hw_info_free(hw_info);
+ hw_info = NULL;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
+ goto fail;
+
+ sdi->driver = di;
+ sdi->priv = devc;
+ sdi->inst_type = SR_INST_SCPI;
+ sdi->conn = scpi;
+
+ if (hmo_init_device(sdi) != SR_OK)
+ goto fail;
+
+ sr_scpi_close(sdi->conn);
+
+ sdi->status = SR_ST_INACTIVE;
+
+ return sdi;
+
+fail:
+ if (hw_info)
+ sr_scpi_hw_info_free(hw_info);
+ if (sdi)
+ sr_dev_inst_free(sdi);
+ if (devc)
+ g_free(devc);
+
+ return NULL;
+}
+
+static GSList *scan(GSList *options)
+{
+ return sr_scpi_scan(di->priv, options, hmo_probe_serial_device);
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static void clear_helper(void *priv)
+{
+ unsigned int i;
+ struct dev_context *devc;
+ struct scope_config *model;
+
+ devc = priv;
+ model = devc->model_config;
+
+ hmo_scope_state_free(devc->model_state);
+
+ for (i = 0; i < model->analog_channels; ++i)
+ g_slist_free(devc->analog_groups[i].channels);
+
+ for (i = 0; i < model->digital_pods; ++i) {
+ g_slist_free(devc->digital_groups[i].channels);
+ g_free(devc->digital_groups[i].name);
+ }
+
+ g_free(devc->analog_groups);
+ g_free(devc->digital_groups);
+
+ g_free(devc);
+}
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, clear_helper);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ if (sdi->status != SR_ST_ACTIVE && sr_scpi_open(sdi->conn) != SR_OK)
+ return SR_ERR;
+
+ if (hmo_scope_state_get(sdi) != SR_OK)
+ return SR_ERR;
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ if (sdi->status == SR_ST_INACTIVE)
+ return SR_OK;
+
+ sr_scpi_close(sdi->conn);
+
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ dev_clear();
+
+ return SR_OK;
+}
+
+static int check_channel_group(struct dev_context *devc,
+ const struct sr_channel_group *cg)
+{
+ unsigned int i;
+ struct scope_config *model;
+
+ model = devc->model_config;
+
+ if (!cg)
+ return CG_NONE;
+
+ for (i = 0; i < model->analog_channels; ++i)
+ if (cg == &devc->analog_groups[i])
+ return CG_ANALOG;
+
+ for (i = 0; i < model->digital_pods; ++i)
+ if (cg == &devc->digital_groups[i])
+ return CG_DIGITAL;
+
+ sr_err("Invalid channel group specified.");
+
+ return CG_INVALID;
+}
+
+static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ int ret, cg_type;
+ unsigned int i;
+ struct dev_context *devc;
+ struct scope_config *model;
+ struct scope_state *state;
+
+ if (!sdi || !(devc = sdi->priv))
+ return SR_ERR_ARG;
+
+ if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
+ return SR_ERR;
+
+ ret = SR_ERR_NA;
+ model = devc->model_config;
+ state = devc->model_state;
+
+ switch (key) {
+ case SR_CONF_NUM_TIMEBASE:
+ *data = g_variant_new_int32(model->num_xdivs);
+ ret = SR_OK;
+ break;
+ case SR_CONF_TIMEBASE:
+ *data = g_variant_new("(tt)", (*model->timebases)[state->timebase][0],
+ (*model->timebases)[state->timebase][1]);
+ ret = SR_OK;
+ break;
+ case SR_CONF_NUM_VDIV:
+ if (cg_type == CG_NONE) {
+ sr_err("No channel group specified.");
+ return SR_ERR_CHANNEL_GROUP;
+ } else if (cg_type == CG_ANALOG) {
+ for (i = 0; i < model->analog_channels; ++i) {
+ if (cg != &devc->analog_groups[i])
+ continue;
+ *data = g_variant_new_int32(model->num_ydivs);
+ ret = SR_OK;
+ break;
+ }
+
+ } else {
+ ret = SR_ERR_NA;
+ }
+ break;
+ case SR_CONF_VDIV:
+ if (cg_type == CG_NONE) {
+ sr_err("No channel group specified.");
+ return SR_ERR_CHANNEL_GROUP;
+ } else if (cg_type == CG_ANALOG) {
+ for (i = 0; i < model->analog_channels; ++i) {
+ if (cg != &devc->analog_groups[i])
+ continue;
+ *data = g_variant_new("(tt)",
+ (*model->vdivs)[state->analog_channels[i].vdiv][0],
+ (*model->vdivs)[state->analog_channels[i].vdiv][1]);
+ ret = SR_OK;
+ break;
+ }
+
+ } else {
+ ret = SR_ERR_NA;
+ }
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ *data = g_variant_new_string((*model->trigger_sources)[state->trigger_source]);
+ ret = SR_OK;
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ *data = g_variant_new_string((*model->trigger_slopes)[state->trigger_slope]);
+ ret = SR_OK;
+ break;
+ case SR_CONF_HORIZ_TRIGGERPOS:
+ *data = g_variant_new_double(state->horiz_triggerpos);
+ ret = SR_OK;
+ break;
+ case SR_CONF_COUPLING:
+ if (cg_type == CG_NONE) {
+ sr_err("No channel group specified.");
+ return SR_ERR_CHANNEL_GROUP;
+ } else if (cg_type == CG_ANALOG) {
+ for (i = 0; i < model->analog_channels; ++i) {
+ if (cg != &devc->analog_groups[i])
+ continue;
+ *data = g_variant_new_string((*model->coupling_options)[state->analog_channels[i].coupling]);
+ ret = SR_OK;
+ break;
+ }
+
+ } else {
+ ret = SR_ERR_NA;
+ }
+ break;
+ case SR_CONF_SAMPLERATE:
+ *data = g_variant_new_uint64(state->sample_rate);
+ ret = SR_OK;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static GVariant *build_tuples(const uint64_t (*array)[][2], unsigned int n)
+{
+ unsigned int i;
+ GVariant *rational[2];
+ GVariantBuilder gvb;
+
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+
+ for (i = 0; i < n; i++) {
+ rational[0] = g_variant_new_uint64((*array)[i][0]);
+ rational[1] = g_variant_new_uint64((*array)[i][1]);
+
+ /* FIXME: Valgrind reports a memory leak here. */
+ g_variant_builder_add_value(&gvb, g_variant_new_tuple(rational, 2));
+ }
+
+ return g_variant_builder_end(&gvb);
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ int ret, cg_type;
+ unsigned int i, j;
+ char command[MAX_COMMAND_SIZE], float_str[30];
+ struct dev_context *devc;
+ struct scope_config *model;
+ struct scope_state *state;
+ const char *tmp;
+ uint64_t p, q;
+ double tmp_d;
+ gboolean update_sample_rate;
+
+ if (!sdi || !(devc = sdi->priv))
+ return SR_ERR_ARG;
+
+ if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
+ return SR_ERR;
+
+ model = devc->model_config;
+ state = devc->model_state;
+ update_sample_rate = FALSE;
+
+ ret = SR_ERR_NA;
+
+ switch (key) {
+ case SR_CONF_LIMIT_FRAMES:
+ devc->frame_limit = g_variant_get_uint64(data);
+ ret = SR_OK;
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ tmp = g_variant_get_string(data, NULL);
+ for (i = 0; (*model->trigger_sources)[i]; i++) {
+ if (g_strcmp0(tmp, (*model->trigger_sources)[i]) != 0)
+ continue;
+ state->trigger_source = i;
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SOURCE],
+ (*model->trigger_sources)[i]);
+
+ ret = sr_scpi_send(sdi->conn, command);
+ break;
+ }
+ break;
+ case SR_CONF_VDIV:
+ if (cg_type == CG_NONE) {
+ sr_err("No channel group specified.");
+ return SR_ERR_CHANNEL_GROUP;
+ }
+
+ g_variant_get(data, "(tt)", &p, &q);
+
+ for (i = 0; i < model->num_vdivs; i++) {
+ if (p != (*model->vdivs)[i][0] ||
+ q != (*model->vdivs)[i][1])
+ continue;
+ for (j = 1; j <= model->analog_channels; ++j) {
+ if (cg != &devc->analog_groups[j - 1])
+ continue;
+ state->analog_channels[j - 1].vdiv = i;
+ g_ascii_formatd(float_str, sizeof(float_str), "%E", (float) p / q);
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_VERTICAL_DIV],
+ j, float_str);
+
+ if (sr_scpi_send(sdi->conn, command) != SR_OK ||
+ sr_scpi_get_opc(sdi->conn) != SR_OK)
+ return SR_ERR;
+
+ break;
+ }
+
+ ret = SR_OK;
+ break;
+ }
+ break;
+ case SR_CONF_TIMEBASE:
+ g_variant_get(data, "(tt)", &p, &q);
+
+ for (i = 0; i < model->num_timebases; i++) {
+ if (p != (*model->timebases)[i][0] ||
+ q != (*model->timebases)[i][1])
+ continue;
+ state->timebase = i;
+ g_ascii_formatd(float_str, sizeof(float_str), "%E", (float) p / q);
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_TIMEBASE],
+ float_str);
+
+ ret = sr_scpi_send(sdi->conn, command);
+ update_sample_rate = TRUE;
+ break;
+ }
+ break;
+ case SR_CONF_HORIZ_TRIGGERPOS:
+ tmp_d = g_variant_get_double(data);
+
+ if (tmp_d < 0.0 || tmp_d > 1.0)
+ return SR_ERR;
+
+ state->horiz_triggerpos = tmp_d;
+ tmp_d = -(tmp_d - 0.5) *
+ ((double) (*model->timebases)[state->timebase][0] /
+ (*model->timebases)[state->timebase][1])
+ * model->num_xdivs;
+
+ g_ascii_formatd(float_str, sizeof(float_str), "%E", tmp_d);
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_HORIZ_TRIGGERPOS],
+ float_str);
+
+ ret = sr_scpi_send(sdi->conn, command);
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ tmp = g_variant_get_string(data, NULL);
+
+ if (!tmp || !(tmp[0] == 'f' || tmp[0] == 'r'))
+ return SR_ERR_ARG;
+
+ state->trigger_slope = (tmp[0] == 'r') ? 0 : 1;
+
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SLOPE],
+ (state->trigger_slope == 0) ? "POS" : "NEG");
+
+ ret = sr_scpi_send(sdi->conn, command);
+ break;
+ case SR_CONF_COUPLING:
+ if (cg_type == CG_NONE) {
+ sr_err("No channel group specified.");
+ return SR_ERR_CHANNEL_GROUP;
+ }
+
+ tmp = g_variant_get_string(data, NULL);
+
+ for (i = 0; (*model->coupling_options)[i]; i++) {
+ if (strcmp(tmp, (*model->coupling_options)[i]) != 0)
+ continue;
+ for (j = 1; j <= model->analog_channels; ++j) {
+ if (cg != &devc->analog_groups[j - 1])
+ continue;
+ state->analog_channels[j-1].coupling = i;
+
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_COUPLING],
+ j, tmp);
+
+ if (sr_scpi_send(sdi->conn, command) != SR_OK ||
+ sr_scpi_get_opc(sdi->conn) != SR_OK)
+ return SR_ERR;
+ break;
+ }
+
+ ret = SR_OK;
+ break;
+ }
+ break;
+ default:
+ ret = SR_ERR_NA;
+ break;
+ }
+
+ if (ret == SR_OK)
+ ret = sr_scpi_get_opc(sdi->conn);
+
+ if (ret == SR_OK && update_sample_rate)
+ ret = hmo_update_sample_rate(sdi);
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ int cg_type;
+ struct dev_context *devc;
+ struct scope_config *model;
+
+ if (!sdi || !(devc = sdi->priv))
+ return SR_ERR_ARG;
+
+ if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
+ return SR_ERR;
+
+ model = devc->model_config;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ if (cg_type == CG_NONE) {
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ model->hw_caps, model->num_hwcaps,
+ sizeof(int32_t));
+ } else if (cg_type == CG_ANALOG) {
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ model->analog_hwcaps, model->num_analog_hwcaps,
+ sizeof(int32_t));
+ } else {
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ NULL, 0, sizeof(int32_t));
+ }
+ break;
+ case SR_CONF_COUPLING:
+ if (cg_type == CG_NONE)
+ return SR_ERR_CHANNEL_GROUP;
+ *data = g_variant_new_strv(*model->coupling_options,
+ g_strv_length((char **)*model->coupling_options));
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ *data = g_variant_new_strv(*model->trigger_sources,
+ g_strv_length((char **)*model->trigger_sources));
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ *data = g_variant_new_strv(*model->trigger_slopes,
+ g_strv_length((char **)*model->trigger_slopes));
+ break;
+ case SR_CONF_TIMEBASE:
+ *data = build_tuples(model->timebases, model->num_timebases);
+ break;
+ case SR_CONF_VDIV:
+ if (cg_type == CG_NONE)
+ return SR_ERR_CHANNEL_GROUP;
+ *data = build_tuples(model->vdivs, model->num_vdivs);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int hmo_request_data(const struct sr_dev_inst *sdi)
+{
+ char command[MAX_COMMAND_SIZE];
+ struct sr_channel *ch;
+ struct dev_context *devc;
+ struct scope_config *model;
+
+ devc = sdi->priv;
+ model = devc->model_config;
+
+ ch = devc->current_channel->data;
+
+ switch (ch->type) {
+ case SR_CHANNEL_ANALOG:
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_GET_ANALOG_DATA],
+ ch->index + 1);
+ break;
+ case SR_CHANNEL_LOGIC:
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_GET_DIG_DATA],
+ ch->index < 8 ? 1 : 2);
+ break;
+ default:
+ sr_err("Invalid channel type.");
+ break;
+ }
+
+ return sr_scpi_send(sdi->conn, command);
+}
+
+static int hmo_check_channels(GSList *channels)
+{
+ GSList *l;
+ struct sr_channel *ch;
+ gboolean enabled_pod1, enabled_pod2, enabled_chan3, enabled_chan4;
+
+ enabled_pod1 = enabled_pod2 = enabled_chan3 = enabled_chan4 = FALSE;
+
+ for (l = channels; l; l = l->next) {
+ ch = l->data;
+ switch (ch->type) {
+ case SR_CHANNEL_ANALOG:
+ if (ch->index == 2)
+ enabled_chan3 = TRUE;
+ else if (ch->index == 3)
+ enabled_chan4 = TRUE;
+ break;
+ case SR_CHANNEL_LOGIC:
+ if (ch->index < 8)
+ enabled_pod1 = TRUE;
+ else
+ enabled_pod2 = TRUE;
+ break;
+ default:
+ return SR_ERR;
+ }
+ }
+
+ if ((enabled_pod1 && enabled_chan3) ||
+ (enabled_pod2 && enabled_chan4))
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static int hmo_setup_channels(const struct sr_dev_inst *sdi)
+{
+ GSList *l;
+ unsigned int i;
+ gboolean *pod_enabled, setup_changed;
+ char command[MAX_COMMAND_SIZE];
+ struct scope_state *state;
+ struct scope_config *model;
+ struct sr_channel *ch;
+ struct dev_context *devc;
+ struct sr_scpi_dev_inst *scpi;
+
+ devc = sdi->priv;
+ scpi = sdi->conn;
+ state = devc->model_state;
+ model = devc->model_config;
+ setup_changed = FALSE;
+
+ pod_enabled = g_try_malloc0(sizeof(gboolean) * model->digital_pods);
+
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ switch (ch->type) {
+ case SR_CHANNEL_ANALOG:
+ if (ch->enabled == state->analog_channels[ch->index].state)
+ break;
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_ANALOG_CHAN_STATE],
+ ch->index + 1, ch->enabled);
+
+ if (sr_scpi_send(scpi, command) != SR_OK)
+ return SR_ERR;
+ state->analog_channels[ch->index].state = ch->enabled;
+ setup_changed = TRUE;
+ break;
+ case SR_CHANNEL_LOGIC:
+ /*
+ * A digital POD needs to be enabled for every group of
+ * 8 channels.
+ */
+ if (ch->enabled)
+ pod_enabled[ch->index < 8 ? 0 : 1] = TRUE;
+
+ if (ch->enabled == state->digital_channels[ch->index])
+ break;
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_DIG_CHAN_STATE],
+ ch->index, ch->enabled);
+
+ if (sr_scpi_send(scpi, command) != SR_OK)
+ return SR_ERR;
+
+ state->digital_channels[ch->index] = ch->enabled;
+ setup_changed = TRUE;
+ break;
+ default:
+ return SR_ERR;
+ }
+ }
+
+ for (i = 1; i <= model->digital_pods; ++i) {
+ if (state->digital_pods[i - 1] == pod_enabled[i - 1])
+ continue;
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_DIG_POD_STATE],
+ i, pod_enabled[i - 1]);
+ if (sr_scpi_send(scpi, command) != SR_OK)
+ return SR_ERR;
+ state->digital_pods[i - 1] = pod_enabled[i - 1];
+ setup_changed = TRUE;
+ }
+
+ g_free(pod_enabled);
+
+ if (setup_changed && hmo_update_sample_rate(sdi) != SR_OK)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ GSList *l;
+ gboolean digital_added;
+ struct sr_channel *ch;
+ struct dev_context *devc;
+ struct sr_scpi_dev_inst *scpi;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ scpi = sdi->conn;
+ devc = sdi->priv;
+ digital_added = FALSE;
+
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (!ch->enabled)
+ continue;
+ /* Only add a single digital channel. */
+ if (ch->type != SR_CHANNEL_LOGIC || !digital_added) {
+ devc->enabled_channels = g_slist_append(
+ devc->enabled_channels, ch);
+ if (ch->type == SR_CHANNEL_LOGIC)
+ digital_added = TRUE;
+ }
+ }
+
+ if (!devc->enabled_channels)
+ return SR_ERR;
+
+ if (hmo_check_channels(devc->enabled_channels) != SR_OK) {
+ sr_err("Invalid channel configuration specified!");
+ return SR_ERR_NA;
+ }
+
+ if (hmo_setup_channels(sdi) != SR_OK) {
+ sr_err("Failed to setup channel configuration!");
+ return SR_ERR;
+ }
+
+ sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 50,
+ hmo_receive_data, (void *)sdi);
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ devc->current_channel = devc->enabled_channels;
+
+ return hmo_request_data(sdi);
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_scpi_dev_inst *scpi;
+ struct sr_datafeed_packet packet;
+
+ (void)cb_data;
+
+ packet.type = SR_DF_END;
+ packet.payload = NULL;
+ sr_session_send(sdi, &packet);
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ devc->num_frames = 0;
+ g_slist_free(devc->enabled_channels);
+ devc->enabled_channels = NULL;
+ scpi = sdi->conn;
+ sr_scpi_source_remove(sdi->session, scpi);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver hameg_hmo_driver_info = {
+ .name = "hameg-hmo",
+ .longname = "Hameg HMO",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = 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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
+ *
+ * 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 "protocol.h"
+
+static const char *hameg_scpi_dialect[] = {
+ [SCPI_CMD_GET_DIG_DATA] = ":POD%d:DATA?",
+ [SCPI_CMD_GET_TIMEBASE] = ":TIM:SCAL?",
+ [SCPI_CMD_SET_TIMEBASE] = ":TIM:SCAL %s",
+ [SCPI_CMD_GET_COUPLING] = ":CHAN%d:COUP?",
+ [SCPI_CMD_SET_COUPLING] = ":CHAN%d:COUP %s",
+ [SCPI_CMD_GET_SAMPLE_RATE] = ":ACQ:SRAT?",
+ [SCPI_CMD_GET_SAMPLE_RATE_LIVE] = ":%s:DATA:POINTS?",
+ [SCPI_CMD_GET_ANALOG_DATA] = ":CHAN%d:DATA?",
+ [SCPI_CMD_GET_VERTICAL_DIV] = ":CHAN%d:SCAL?",
+ [SCPI_CMD_SET_VERTICAL_DIV] = ":CHAN%d:SCAL %s",
+ [SCPI_CMD_GET_DIG_POD_STATE] = ":POD%d:STAT?",
+ [SCPI_CMD_SET_DIG_POD_STATE] = ":POD%d:STAT %d",
+ [SCPI_CMD_GET_TRIGGER_SLOPE] = ":TRIG:A:EDGE:SLOP?",
+ [SCPI_CMD_SET_TRIGGER_SLOPE] = ":TRIG:A:EDGE:SLOP %s",
+ [SCPI_CMD_GET_TRIGGER_SOURCE] = ":TRIG:A:SOUR?",
+ [SCPI_CMD_SET_TRIGGER_SOURCE] = ":TRIG:A:SOUR %s",
+ [SCPI_CMD_GET_DIG_CHAN_STATE] = ":LOG%d:STAT?",
+ [SCPI_CMD_SET_DIG_CHAN_STATE] = ":LOG%d:STAT %d",
+ [SCPI_CMD_GET_VERTICAL_OFFSET] = ":CHAN%d:POS?",
+ [SCPI_CMD_GET_HORIZ_TRIGGERPOS] = ":TIM:POS?",
+ [SCPI_CMD_SET_HORIZ_TRIGGERPOS] = ":TIM:POS %s",
+ [SCPI_CMD_GET_ANALOG_CHAN_STATE] = ":CHAN%d:STAT?",
+ [SCPI_CMD_SET_ANALOG_CHAN_STATE] = ":CHAN%d:STAT %d",
+};
+
+static const int32_t hmo_hwcaps[] = {
+ SR_CONF_OSCILLOSCOPE,
+ SR_CONF_TRIGGER_SOURCE,
+ SR_CONF_TIMEBASE,
+ SR_CONF_NUM_TIMEBASE,
+ SR_CONF_TRIGGER_SLOPE,
+ SR_CONF_HORIZ_TRIGGERPOS,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_LIMIT_FRAMES,
+};
+
+static const int32_t hmo_analog_caps[] = {
+ SR_CONF_NUM_VDIV,
+ SR_CONF_COUPLING,
+ SR_CONF_VDIV,
+};
+
+static const char *hmo_coupling_options[] = {
+ "AC",
+ "ACL",
+ "DC",
+ "DCL",
+ "GND",
+ NULL,
+};
+
+static const char *scope_trigger_slopes[] = {
+ "POS",
+ "NEG",
+ NULL,
+};
+
+static const char *hmo_compact2_trigger_sources[] = {
+ "CH1",
+ "CH2",
+ "LINE",
+ "EXT",
+ "D0",
+ "D1",
+ "D2",
+ "D3",
+ "D4",
+ "D5",
+ "D6",
+ "D7",
+ NULL,
+};
+
+static const char *hmo_compact4_trigger_sources[] = {
+ "CH1",
+ "CH2",
+ "CH3",
+ "CH4",
+ "LINE",
+ "EXT",
+ "D0",
+ "D1",
+ "D2",
+ "D3",
+ "D4",
+ "D5",
+ "D6",
+ "D7",
+ NULL,
+};
+
+static const uint64_t hmo_timebases[][2] = {
+ /* nanoseconds */
+ { 2, 1000000000 },
+ { 5, 1000000000 },
+ { 10, 1000000000 },
+ { 20, 1000000000 },
+ { 50, 1000000000 },
+ { 100, 1000000000 },
+ { 200, 1000000000 },
+ { 500, 1000000000 },
+ /* microseconds */
+ { 1, 1000000 },
+ { 2, 1000000 },
+ { 5, 1000000 },
+ { 10, 1000000 },
+ { 20, 1000000 },
+ { 50, 1000000 },
+ { 100, 1000000 },
+ { 200, 1000000 },
+ { 500, 1000000 },
+ /* milliseconds */
+ { 1, 1000 },
+ { 2, 1000 },
+ { 5, 1000 },
+ { 10, 1000 },
+ { 20, 1000 },
+ { 50, 1000 },
+ { 100, 1000 },
+ { 200, 1000 },
+ { 500, 1000 },
+ /* seconds */
+ { 1, 1 },
+ { 2, 1 },
+ { 5, 1 },
+ { 10, 1 },
+ { 20, 1 },
+ { 50, 1 },
+};
+
+static const uint64_t hmo_vdivs[][2] = {
+ /* millivolts */
+ { 1, 1000 },
+ { 2, 1000 },
+ { 5, 1000 },
+ { 10, 1000 },
+ { 20, 1000 },
+ { 50, 1000 },
+ { 100, 1000 },
+ { 200, 1000 },
+ { 500, 1000 },
+ /* volts */
+ { 1, 1 },
+ { 2, 1 },
+ { 5, 1 },
+ { 10, 1 },
+};
+
+static const char *scope_analog_channel_names[] = {
+ "CH1",
+ "CH2",
+ "CH3",
+ "CH4",
+};
+
+static const char *scope_digital_channel_names[] = {
+ "D0",
+ "D1",
+ "D2",
+ "D3",
+ "D4",
+ "D5",
+ "D6",
+ "D7",
+ "D8",
+ "D9",
+ "D10",
+ "D11",
+ "D12",
+ "D13",
+ "D14",
+ "D15",
+};
+
+static struct scope_config scope_models[] = {
+ {
+ .name = {"HMO722", "HMO1022", "HMO1522", "HMO2022", NULL},
+ .analog_channels = 2,
+ .digital_channels = 8,
+ .digital_pods = 1,
+
+ .analog_names = &scope_analog_channel_names,
+ .digital_names = &scope_digital_channel_names,
+
+ .hw_caps = &hmo_hwcaps,
+ .num_hwcaps = ARRAY_SIZE(hmo_hwcaps),
+
+ .analog_hwcaps = &hmo_analog_caps,
+ .num_analog_hwcaps = ARRAY_SIZE(hmo_analog_caps),
+
+ .coupling_options = &hmo_coupling_options,
+ .trigger_sources = &hmo_compact2_trigger_sources,
+ .trigger_slopes = &scope_trigger_slopes,
+
+ .timebases = &hmo_timebases,
+ .num_timebases = ARRAY_SIZE(hmo_timebases),
+
+ .vdivs = &hmo_vdivs,
+ .num_vdivs = ARRAY_SIZE(hmo_vdivs),
+
+ .num_xdivs = 12,
+ .num_ydivs = 8,
+
+ .scpi_dialect = &hameg_scpi_dialect,
+ },
+ {
+ .name = {"HMO724", "HMO1024", "HMO1524", "HMO2024", NULL},
+ .analog_channels = 4,
+ .digital_channels = 8,
+ .digital_pods = 1,
+
+ .analog_names = &scope_analog_channel_names,
+ .digital_names = &scope_digital_channel_names,
+
+ .hw_caps = &hmo_hwcaps,
+ .num_hwcaps = ARRAY_SIZE(hmo_hwcaps),
+
+ .analog_hwcaps = &hmo_analog_caps,
+ .num_analog_hwcaps = ARRAY_SIZE(hmo_analog_caps),
+
+ .coupling_options = &hmo_coupling_options,
+ .trigger_sources = &hmo_compact4_trigger_sources,
+ .trigger_slopes = &scope_trigger_slopes,
+
+ .timebases = &hmo_timebases,
+ .num_timebases = ARRAY_SIZE(hmo_timebases),
+
+ .vdivs = &hmo_vdivs,
+ .num_vdivs = ARRAY_SIZE(hmo_vdivs),
+
+ .num_xdivs = 12,
+ .num_ydivs = 8,
+
+ .scpi_dialect = &hameg_scpi_dialect,
+ },
+};
+
+static void scope_state_dump(struct scope_config *config,
+ struct scope_state *state)
+{
+ unsigned int i;
+ char *tmp;
+
+ for (i = 0; i < config->analog_channels; ++i) {
+ tmp = sr_voltage_string((*config->vdivs)[state->analog_channels[i].vdiv][0],
+ (*config->vdivs)[state->analog_channels[i].vdiv][1]);
+ sr_info("State of analog channel %d -> %s : %s (coupling) %s (vdiv) %2.2e (offset)",
+ i + 1, state->analog_channels[i].state ? "On" : "Off",
+ (*config->coupling_options)[state->analog_channels[i].coupling],
+ tmp, state->analog_channels[i].vertical_offset);
+ }
+
+ for (i = 0; i < config->digital_channels; ++i) {
+ sr_info("State of digital channel %d -> %s", i,
+ state->digital_channels[i] ? "On" : "Off");
+ }
+
+ for (i = 0; i < config->digital_pods; ++i) {
+ sr_info("State of digital POD %d -> %s", i,
+ state->digital_pods[i] ? "On" : "Off");
+ }
+
+ tmp = sr_period_string((*config->timebases)[state->timebase][0] *
+ (*config->timebases)[state->timebase][1]);
+ sr_info("Current timebase: %s", tmp);
+ g_free(tmp);
+
+ tmp = sr_samplerate_string(state->sample_rate);
+ sr_info("Current samplerate: %s", tmp);
+ g_free(tmp);
+
+ sr_info("Current trigger: %s (source), %s (slope) %.2f (offset)",
+ (*config->trigger_sources)[state->trigger_source],
+ (*config->trigger_slopes)[state->trigger_slope],
+ state->horiz_triggerpos);
+}
+
+static int scope_state_get_array_option(struct sr_scpi_dev_inst *scpi,
+ const char *command, const char *(*array)[], int *result)
+{
+ char *tmp;
+ unsigned int i;
+
+ if (sr_scpi_get_string(scpi, command, &tmp) != SR_OK) {
+ g_free(tmp);
+ return SR_ERR;
+ }
+
+ for (i = 0; (*array)[i]; ++i) {
+ if (!g_strcmp0(tmp, (*array)[i])) {
+ *result = i;
+ g_free(tmp);
+ tmp = NULL;
+ break;
+ }
+ }
+
+ if (tmp) {
+ g_free(tmp);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
+ struct scope_config *config,
+ struct scope_state *state)
+{
+ unsigned int i, j;
+ float tmp_float;
+ char command[MAX_COMMAND_SIZE];
+
+ for (i = 0; i < config->analog_channels; ++i) {
+ g_snprintf(command, sizeof(command),
+ (*config->scpi_dialect)[SCPI_CMD_GET_ANALOG_CHAN_STATE],
+ i + 1);
+
+ if (sr_scpi_get_bool(scpi, command,
+ &state->analog_channels[i].state) != SR_OK)
+ return SR_ERR;
+
+ g_snprintf(command, sizeof(command),
+ (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_DIV],
+ i + 1);
+
+ if (sr_scpi_get_float(scpi, command, &tmp_float) != SR_OK)
+ return SR_ERR;
+ for (j = 0; j < config->num_vdivs; j++) {
+ if (tmp_float == ((float) (*config->vdivs)[j][0] /
+ (*config->vdivs)[j][1])) {
+ state->analog_channels[i].vdiv = j;
+ break;
+ }
+ }
+ if (i == config->num_vdivs)
+ return SR_ERR;
+
+ g_snprintf(command, sizeof(command),
+ (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_OFFSET],
+ i + 1);
+
+ if (sr_scpi_get_float(scpi, command,
+ &state->analog_channels[i].vertical_offset) != SR_OK)
+ return SR_ERR;
+
+ g_snprintf(command, sizeof(command),
+ (*config->scpi_dialect)[SCPI_CMD_GET_COUPLING],
+ i + 1);
+
+ if (scope_state_get_array_option(scpi, command, config->coupling_options,
+ &state->analog_channels[i].coupling) != SR_OK)
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int digital_channel_state_get(struct sr_scpi_dev_inst *scpi,
+ struct scope_config *config,
+ struct scope_state *state)
+{
+ unsigned int i;
+ char command[MAX_COMMAND_SIZE];
+
+ for (i = 0; i < config->digital_channels; ++i) {
+ g_snprintf(command, sizeof(command),
+ (*config->scpi_dialect)[SCPI_CMD_GET_DIG_CHAN_STATE],
+ i);
+
+ if (sr_scpi_get_bool(scpi, command,
+ &state->digital_channels[i]) != SR_OK)
+ return SR_ERR;
+ }
+
+ for (i = 0; i < config->digital_pods; ++i) {
+ g_snprintf(command, sizeof(command),
+ (*config->scpi_dialect)[SCPI_CMD_GET_DIG_POD_STATE],
+ i + 1);
+
+ if (sr_scpi_get_bool(scpi, command,
+ &state->digital_pods[i]) != SR_OK)
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct scope_state *state;
+ struct scope_config *config;
+
+ int tmp;
+ unsigned int i;
+ float tmp_float;
+ gboolean channel_found;
+ char tmp_str[MAX_COMMAND_SIZE];
+ char chan_name[20];
+
+ devc = sdi->priv;
+ config = devc->model_config;
+ state = devc->model_state;
+ channel_found = FALSE;
+
+ for (i = 0; i < config->analog_channels; ++i) {
+ if (state->analog_channels[i].state) {
+ g_snprintf(chan_name, sizeof(chan_name), "CHAN%d", i + 1);
+ g_snprintf(tmp_str, sizeof(tmp_str),
+ (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE_LIVE],
+ chan_name);
+ channel_found = TRUE;
+ break;
+ }
+ }
+
+ if (!channel_found) {
+ for (i = 0; i < config->digital_pods; i++) {
+ if (state->digital_pods[i]) {
+ g_snprintf(chan_name, sizeof(chan_name), "POD%d", i);
+ g_snprintf(tmp_str, sizeof(tmp_str),
+ (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE_LIVE],
+ chan_name);
+ channel_found = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* No channel is active, ask the instrument for the sample rate
+ * in single shot mode */
+ if (!channel_found) {
+ if (sr_scpi_get_float(sdi->conn,
+ (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE],
+ &tmp_float) != SR_OK)
+ return SR_ERR;
+
+ state->sample_rate = tmp_float;
+ } else {
+ if (sr_scpi_get_int(sdi->conn, tmp_str, &tmp) != SR_OK)
+ return SR_ERR;
+ state->sample_rate = tmp / (((float) (*config->timebases)[state->timebase][0] /
+ (*config->timebases)[state->timebase][1]) *
+ config->num_xdivs);
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct scope_state *state;
+ struct scope_config *config;
+ float tmp_float;
+ unsigned int i;
+
+ devc = sdi->priv;
+ config = devc->model_config;
+ state = devc->model_state;
+
+ sr_info("Fetching scope state");
+
+ if (analog_channel_state_get(sdi->conn, config, state) != SR_OK)
+ return SR_ERR;
+
+ if (digital_channel_state_get(sdi->conn, config, state) != SR_OK)
+ return SR_ERR;
+
+ if (sr_scpi_get_float(sdi->conn,
+ (*config->scpi_dialect)[SCPI_CMD_GET_TIMEBASE],
+ &tmp_float) != SR_OK)
+ return SR_ERR;
+
+ for (i = 0; i < config->num_timebases; i++) {
+ if (tmp_float == ((float) (*config->timebases)[i][0] /
+ (*config->timebases)[i][1])) {
+ state->timebase = i;
+ break;
+ }
+ }
+ if (i == config->num_timebases)
+ return SR_ERR;
+
+ if (sr_scpi_get_float(sdi->conn,
+ (*config->scpi_dialect)[SCPI_CMD_GET_HORIZ_TRIGGERPOS],
+ &tmp_float) != SR_OK)
+ return SR_ERR;
+ state->horiz_triggerpos = tmp_float /
+ (((double) (*config->timebases)[state->timebase][0] /
+ (*config->timebases)[state->timebase][1]) * config->num_xdivs);
+ state->horiz_triggerpos -= 0.5;
+ state->horiz_triggerpos *= -1;
+
+ if (scope_state_get_array_option(sdi->conn,
+ (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SOURCE],
+ config->trigger_sources, &state->trigger_source) != SR_OK)
+ return SR_ERR;
+
+ if (scope_state_get_array_option(sdi->conn,
+ (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SLOPE],
+ config->trigger_slopes, &state->trigger_slope) != SR_OK)
+ return SR_ERR;
+
+ if (hmo_update_sample_rate(sdi) != SR_OK)
+ return SR_ERR;
+
+ sr_info("Fetching finished.");
+
+ scope_state_dump(config, state);
+
+ return SR_OK;
+}
+
+static struct scope_state *scope_state_new(struct scope_config *config)
+{
+ struct scope_state *state;
+
+ if (!(state = g_try_malloc0(sizeof(struct scope_state))))
+ return NULL;
+
+ if (!(state->analog_channels = g_try_malloc0_n(config->analog_channels,
+ sizeof(struct analog_channel_state))))
+ goto fail;
+
+ if (!(state->digital_channels = g_try_malloc0_n(
+ config->digital_channels, sizeof(gboolean))))
+ goto fail;
+
+ if (!(state->digital_pods = g_try_malloc0_n(config->digital_pods,
+ sizeof(gboolean))))
+ goto fail;
+
+ return state;
+
+fail:
+ if (state->analog_channels)
+ g_free(state->analog_channels);
+ if (state->digital_channels)
+ g_free(state->digital_channels);
+ if (state->digital_pods)
+ g_free(state->digital_pods);
+ g_free(state);
+
+ return NULL;
+}
+
+SR_PRIV void hmo_scope_state_free(struct scope_state *state)
+{
+ g_free(state->analog_channels);
+ g_free(state->digital_channels);
+ g_free(state->digital_pods);
+ g_free(state);
+}
+
+SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi)
+{
+ char tmp[25];
+ int model_index;
+ unsigned int i, j;
+ struct sr_channel *ch;
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+ model_index = -1;
+
+ /* Find the exact model. */
+ for (i = 0; i < ARRAY_SIZE(scope_models); i++) {
+ for (j = 0; scope_models[i].name[j]; j++) {
+ if (!strcmp(sdi->model, scope_models[i].name[j])) {
+ model_index = i;
+ break;
+ }
+ }
+ if (model_index != -1)
+ break;
+ }
+
+ if (model_index == -1) {
+ sr_dbg("Unsupported HMO device.");
+ return SR_ERR_NA;
+ }
+
+ if (!(devc->analog_groups = g_try_malloc0(sizeof(struct sr_channel_group) *
+ scope_models[model_index].analog_channels)))
+ return SR_ERR_MALLOC;
+
+ if (!(devc->digital_groups = g_try_malloc0(sizeof(struct sr_channel_group) *
+ scope_models[model_index].digital_pods)))
+ return SR_ERR_MALLOC;
+
+ /* Add analog channels. */
+ for (i = 0; i < scope_models[model_index].analog_channels; i++) {
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE,
+ (*scope_models[model_index].analog_names)[i])))
+ return SR_ERR_MALLOC;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ devc->analog_groups[i].name =
+ (char *)(*scope_models[model_index].analog_names)[i];
+ devc->analog_groups[i].channels = g_slist_append(NULL, ch);
+
+ sdi->channel_groups = g_slist_append(sdi->channel_groups,
+ &devc->analog_groups[i]);
+ }
+
+ /* Add digital channel groups. */
+ for (i = 0; i < scope_models[model_index].digital_pods; ++i) {
+ g_snprintf(tmp, 25, "POD%d", i);
+ devc->digital_groups[i].name = g_strdup(tmp);
+ sdi->channel_groups = g_slist_append(sdi->channel_groups,
+ &devc->digital_groups[i < 8 ? 0 : 1]);
+ }
+
+ /* Add digital channels. */
+ for (i = 0; i < scope_models[model_index].digital_channels; i++) {
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
+ (*scope_models[model_index].digital_names)[i])))
+ return SR_ERR_MALLOC;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ devc->digital_groups[i < 8 ? 0 : 1].channels = g_slist_append(
+ devc->digital_groups[i < 8 ? 0 : 1].channels, ch);
+ }
+
+ devc->model_config = &scope_models[model_index];
+ devc->frame_limit = 0;
+
+ if (!(devc->model_state = scope_state_new(devc->model_config)))
+ return SR_ERR_MALLOC;
+
+ return SR_OK;
+}
+
+SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_channel *ch;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ GArray *data;
+ struct sr_datafeed_analog analog;
+ struct sr_datafeed_logic logic;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ if (revents == G_IO_IN) {
+ ch = devc->current_channel->data;
+
+ switch (ch->type) {
+ case SR_CHANNEL_ANALOG:
+ if (sr_scpi_get_floatv(sdi->conn, NULL, &data) != SR_OK) {
+ if (data)
+ g_array_free(data, TRUE);
+
+ return TRUE;
+ }
+
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(sdi, &packet);
+
+ analog.channels = g_slist_append(NULL, ch);
+ analog.num_samples = data->len;
+ analog.data = (float *) data->data;
+ analog.mq = SR_MQ_VOLTAGE;
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags = 0;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(cb_data, &packet);
+ g_slist_free(analog.channels);
+ g_array_free(data, TRUE);
+ break;
+ case SR_CHANNEL_LOGIC:
+ if (sr_scpi_get_uint8v(sdi->conn, NULL, &data) != SR_OK) {
+ if (data)
+ g_free(data);
+ return TRUE;
+ }
+
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(sdi, &packet);
+
+ logic.length = data->len;
+ logic.unitsize = 1;
+ logic.data = data->data;
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ sr_session_send(cb_data, &packet);
+ g_array_free(data, TRUE);
+ break;
+ default:
+ sr_err("Invalid channel type.");
+ break;
+ }
+
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(sdi, &packet);
+
+ if (devc->current_channel->next) {
+ devc->current_channel = devc->current_channel->next;
+ hmo_request_data(sdi);
+ } else if (++devc->num_frames == devc->frame_limit) {
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ } else {
+ devc->current_channel = devc->enabled_channels;
+ hmo_request_data(sdi);
+ }
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
+ *
+ * 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_HAMEG_HMO_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_HAMEG_HMO_PROTOCOL_H
+
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "hameg-hmo"
+
+#define MAX_INSTRUMENT_VERSIONS 10
+#define MAX_COMMAND_SIZE 31
+
+struct scope_config {
+ const char *name[MAX_INSTRUMENT_VERSIONS];
+ const uint8_t analog_channels;
+ const uint8_t digital_channels;
+ const uint8_t digital_pods;
+
+ const char *(*analog_names)[];
+ const char *(*digital_names)[];
+
+ const int32_t (*hw_caps)[];
+ const uint8_t num_hwcaps;
+
+ const int32_t (*analog_hwcaps)[];
+ const uint8_t num_analog_hwcaps;
+
+ const char *(*coupling_options)[];
+ const uint8_t num_coupling_options;
+
+ const char *(*trigger_sources)[];
+ const uint8_t num_trigger_sources;
+
+ const char *(*trigger_slopes)[];
+
+ const uint64_t (*timebases)[][2];
+ const uint8_t num_timebases;
+
+ const uint64_t (*vdivs)[][2];
+ const uint8_t num_vdivs;
+
+ const uint8_t num_xdivs;
+ const uint8_t num_ydivs;
+
+ const char *(*scpi_dialect)[];
+};
+
+struct analog_channel_state {
+ int coupling;
+
+ int vdiv;
+ float vertical_offset;
+
+ gboolean state;
+};
+
+struct scope_state {
+ struct analog_channel_state *analog_channels;
+ gboolean *digital_channels;
+ gboolean *digital_pods;
+
+ int timebase;
+ float horiz_triggerpos;
+
+ int trigger_source;
+ int trigger_slope;
+ uint64_t sample_rate;
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ void *model_config;
+ void *model_state;
+
+ struct sr_channel_group *analog_groups;
+ struct sr_channel_group *digital_groups;
+
+ GSList *enabled_channels;
+ GSList *current_channel;
+ uint64_t num_frames;
+
+ uint64_t frame_limit;
+};
+
+SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi);
+SR_PRIV int hmo_request_data(const struct sr_dev_inst *sdi);
+SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data);
+
+SR_PRIV struct scope_state *hmo_scope_state_new(struct scope_config *config);
+SR_PRIV void hmo_scope_state_free(struct scope_state *state);
+SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi);
+SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#include <inttypes.h>
+#include <glib.h>
+#include <libusb.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "dso.h"
+
+/* Max time in ms before we want to check on USB events */
+/* TODO tune this properly */
+#define TICK 1
+
+#define NUM_TIMEBASE 10
+#define NUM_VDIV 8
+
+static const int32_t scanopts[] = {
+ SR_CONF_CONN,
+};
+
+static const int32_t devopts[] = {
+ SR_CONF_OSCILLOSCOPE,
+ SR_CONF_LIMIT_FRAMES,
+ SR_CONF_CONTINUOUS,
+ SR_CONF_TIMEBASE,
+ SR_CONF_BUFFERSIZE,
+ SR_CONF_TRIGGER_SOURCE,
+ SR_CONF_TRIGGER_SLOPE,
+ SR_CONF_HORIZ_TRIGGERPOS,
+ SR_CONF_FILTER,
+ SR_CONF_VDIV,
+ SR_CONF_COUPLING,
+ SR_CONF_NUM_TIMEBASE,
+ SR_CONF_NUM_VDIV,
+};
+
+static const char *channel_names[] = {
+ "CH1", "CH2",
+ NULL,
+};
+
+static const uint64_t buffersizes_32k[] = {
+ 10240, 32768,
+};
+static const uint64_t buffersizes_512k[] = {
+ 10240, 524288,
+};
+static const uint64_t buffersizes_14k[] = {
+ 10240, 14336,
+};
+
+static const struct dso_profile dev_profiles[] = {
+ { 0x04b4, 0x2090, 0x04b5, 0x2090,
+ "Hantek", "DSO-2090",
+ buffersizes_32k,
+ FIRMWARE_DIR "/hantek-dso-2090.fw" },
+ { 0x04b4, 0x2150, 0x04b5, 0x2150,
+ "Hantek", "DSO-2150",
+ buffersizes_32k,
+ FIRMWARE_DIR "/hantek-dso-2150.fw" },
+ { 0x04b4, 0x2250, 0x04b5, 0x2250,
+ "Hantek", "DSO-2250",
+ buffersizes_512k,
+ FIRMWARE_DIR "/hantek-dso-2250.fw" },
+ { 0x04b4, 0x5200, 0x04b5, 0x5200,
+ "Hantek", "DSO-5200",
+ buffersizes_14k,
+ FIRMWARE_DIR "/hantek-dso-5200.fw" },
+ { 0x04b4, 0x520a, 0x04b5, 0x520a,
+ "Hantek", "DSO-5200A",
+ buffersizes_512k,
+ FIRMWARE_DIR "/hantek-dso-5200A.fw" },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+static const uint64_t timebases[][2] = {
+ /* microseconds */
+ { 10, 1000000 },
+ { 20, 1000000 },
+ { 40, 1000000 },
+ { 100, 1000000 },
+ { 200, 1000000 },
+ { 400, 1000000 },
+ /* milliseconds */
+ { 1, 1000 },
+ { 2, 1000 },
+ { 4, 1000 },
+ { 10, 1000 },
+ { 20, 1000 },
+ { 40, 1000 },
+ { 100, 1000 },
+ { 200, 1000 },
+ { 400, 1000 },
+};
+
+static const uint64_t vdivs[][2] = {
+ /* millivolts */
+ { 10, 1000 },
+ { 20, 1000 },
+ { 50, 1000 },
+ { 100, 1000 },
+ { 200, 1000 },
+ { 500, 1000 },
+ /* volts */
+ { 1, 1 },
+ { 2, 1 },
+ { 5, 1 },
+};
+
+static const char *trigger_sources[] = {
+ "CH1",
+ "CH2",
+ "EXT",
+ /* TODO: forced */
+};
+
+static const char *filter_targets[] = {
+ "CH1",
+ "CH2",
+ /* TODO: "TRIGGER", */
+};
+
+static const char *coupling[] = {
+ "AC",
+ "DC",
+ "GND",
+};
+
+SR_PRIV struct sr_dev_driver hantek_dso_driver_info;
+static struct sr_dev_driver *di = &hantek_dso_driver_info;
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
+
+static struct sr_dev_inst *dso_dev_new(int index, const struct dso_profile *prof)
+{
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ int i;
+
+ sdi = sr_dev_inst_new(index, SR_ST_INITIALIZING,
+ prof->vendor, prof->model, NULL);
+ if (!sdi)
+ return NULL;
+ sdi->driver = di;
+
+ /*
+ * Add only the real channels -- EXT isn't a source of data, only
+ * a trigger source internal to the device.
+ */
+ for (i = 0; channel_names[i]; i++) {
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE,
+ channel_names[i])))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+
+ devc->profile = prof;
+ devc->dev_state = IDLE;
+ devc->timebase = DEFAULT_TIMEBASE;
+ devc->ch1_enabled = TRUE;
+ devc->ch2_enabled = TRUE;
+ devc->voltage_ch1 = DEFAULT_VOLTAGE;
+ devc->voltage_ch2 = DEFAULT_VOLTAGE;
+ devc->coupling_ch1 = DEFAULT_COUPLING;
+ devc->coupling_ch2 = DEFAULT_COUPLING;
+ devc->voffset_ch1 = DEFAULT_VERT_OFFSET;
+ devc->voffset_ch2 = DEFAULT_VERT_OFFSET;
+ devc->voffset_trigger = DEFAULT_VERT_TRIGGERPOS;
+ devc->framesize = DEFAULT_FRAMESIZE;
+ devc->triggerslope = SLOPE_POSITIVE;
+ devc->triggersource = g_strdup(DEFAULT_TRIGGER_SOURCE);
+ devc->triggerposition = DEFAULT_HORIZ_TRIGGERPOS;
+ sdi->priv = devc;
+ drvc = di->priv;
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+
+ return sdi;
+}
+
+static int configure_channels(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ const GSList *l;
+ int p;
+
+ devc = sdi->priv;
+
+ g_slist_free(devc->enabled_channels);
+ devc->ch1_enabled = devc->ch2_enabled = FALSE;
+ for (l = sdi->channels, p = 0; l; l = l->next, p++) {
+ ch = l->data;
+ if (p == 0)
+ devc->ch1_enabled = ch->enabled;
+ else
+ devc->ch2_enabled = ch->enabled;
+ if (ch->enabled)
+ devc->enabled_channels = g_slist_append(devc->enabled_channels, ch);
+ }
+
+ return SR_OK;
+}
+
+static void clear_dev_context(void *priv)
+{
+ struct dev_context *devc;
+
+ devc = priv;
+ g_free(devc->triggersource);
+ g_slist_free(devc->enabled_channels);
+
+}
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, clear_dev_context);
+}
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_usb_dev_inst *usb;
+ struct sr_config *src;
+ const struct dso_profile *prof;
+ GSList *l, *devices, *conn_devices;
+ struct libusb_device_descriptor des;
+ libusb_device **devlist;
+ int devcnt, ret, i, j;
+ const char *conn;
+
+ drvc = di->priv;
+
+ devcnt = 0;
+ devices = 0;
+
+ conn = NULL;
+ for (l = options; l; l = l->next) {
+ src = l->data;
+ if (src->key == SR_CONF_CONN) {
+ conn = g_variant_get_string(src->data, NULL);
+ break;
+ }
+ }
+ if (conn)
+ conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
+ else
+ conn_devices = NULL;
+
+ /* Find all Hantek DSO devices and upload firmware to all of them. */
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if (conn) {
+ usb = NULL;
+ for (l = conn_devices; l; l = l->next) {
+ usb = l->data;
+ if (usb->bus == libusb_get_bus_number(devlist[i])
+ && usb->address == libusb_get_device_address(devlist[i]))
+ break;
+ }
+ if (!l)
+ /* This device matched none of the ones that
+ * matched the conn specification. */
+ continue;
+ }
+
+ if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
+ sr_err("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ prof = NULL;
+ for (j = 0; dev_profiles[j].orig_vid; j++) {
+ if (des.idVendor == dev_profiles[j].orig_vid
+ && des.idProduct == dev_profiles[j].orig_pid) {
+ /* Device matches the pre-firmware profile. */
+ prof = &dev_profiles[j];
+ sr_dbg("Found a %s %s.", prof->vendor, prof->model);
+ sdi = dso_dev_new(devcnt, prof);
+ devices = g_slist_append(devices, sdi);
+ devc = sdi->priv;
+ if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION,
+ prof->firmware) == SR_OK)
+ /* Remember when the firmware on this device was updated */
+ devc->fw_updated = g_get_monotonic_time();
+ else
+ sr_err("Firmware upload failed for "
+ "device %d.", devcnt);
+ /* Dummy USB address of 0xff will get overwritten later. */
+ sdi->conn = sr_usb_dev_inst_new(
+ libusb_get_bus_number(devlist[i]), 0xff, NULL);
+ devcnt++;
+ break;
+ } else if (des.idVendor == dev_profiles[j].fw_vid
+ && des.idProduct == dev_profiles[j].fw_pid) {
+ /* Device matches the post-firmware profile. */
+ prof = &dev_profiles[j];
+ sr_dbg("Found a %s %s.", prof->vendor, prof->model);
+ sdi = dso_dev_new(devcnt, prof);
+ sdi->status = SR_ST_INACTIVE;
+ devices = g_slist_append(devices, sdi);
+ devc = sdi->priv;
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = sr_usb_dev_inst_new(
+ libusb_get_bus_number(devlist[i]),
+ libusb_get_device_address(devlist[i]), NULL);
+ devcnt++;
+ break;
+ }
+ }
+ if (!prof)
+ /* not a supported VID/PID */
+ continue;
+ }
+ libusb_free_device_list(devlist, 1);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int64_t timediff_us, timediff_ms;
+ int err;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ /*
+ * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS
+ * for the FX2 to renumerate.
+ */
+ err = SR_ERR;
+ if (devc->fw_updated > 0) {
+ sr_info("Waiting for device to reset.");
+ /* Takes >= 300ms for the FX2 to be gone from the USB bus. */
+ g_usleep(300 * 1000);
+ timediff_ms = 0;
+ while (timediff_ms < MAX_RENUM_DELAY_MS) {
+ if ((err = dso_open(sdi)) == SR_OK)
+ break;
+ g_usleep(100 * 1000);
+ timediff_us = g_get_monotonic_time() - devc->fw_updated;
+ timediff_ms = timediff_us / 1000;
+ sr_spew("Waited %" PRIi64 " ms.", timediff_ms);
+ }
+ sr_info("Device came back after %d ms.", timediff_ms);
+ } else {
+ err = dso_open(sdi);
+ }
+
+ if (err != SR_OK) {
+ sr_err("Unable to open device.");
+ return SR_ERR;
+ }
+
+ err = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
+ if (err != 0) {
+ sr_err("Unable to claim interface: %s.",
+ libusb_error_name(err));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ dso_close(sdi);
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return dev_clear();
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct sr_usb_dev_inst *usb;
+ char str[128];
+
+ (void)cg;
+
+ switch (id) {
+ case SR_CONF_CONN:
+ if (!sdi || !sdi->conn)
+ return SR_ERR_ARG;
+ usb = sdi->conn;
+ if (usb->address == 255)
+ /* Device still needs to re-enumerate after firmware
+ * upload, so we don't know its (future) address. */
+ return SR_ERR;
+ snprintf(str, 128, "%d.%d", usb->bus, usb->address);
+ *data = g_variant_new_string(str);
+ break;
+ case SR_CONF_NUM_TIMEBASE:
+ *data = g_variant_new_int32(NUM_TIMEBASE);
+ break;
+ case SR_CONF_NUM_VDIV:
+ *data = g_variant_new_int32(NUM_VDIV);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ double tmp_double;
+ uint64_t tmp_u64, p, q;
+ int tmp_int, ret;
+ unsigned int i;
+ const char *tmp_str;
+ char **targets;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ ret = SR_OK;
+ devc = sdi->priv;
+ switch (id) {
+ case SR_CONF_LIMIT_FRAMES:
+ devc->limit_frames = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ tmp_str = g_variant_get_string(data, NULL);
+ if (!tmp_str || !(tmp_str[0] == 'f' || tmp_str[0] == 'r'))
+ return SR_ERR_ARG;
+ devc->triggerslope = (tmp_str[0] == 'r')
+ ? SLOPE_POSITIVE : SLOPE_NEGATIVE;
+ break;
+ case SR_CONF_HORIZ_TRIGGERPOS:
+ tmp_double = g_variant_get_double(data);
+ if (tmp_double < 0.0 || tmp_double > 1.0) {
+ sr_err("Trigger position should be between 0.0 and 1.0.");
+ ret = SR_ERR_ARG;
+ } else
+ devc->triggerposition = tmp_double;
+ break;
+ case SR_CONF_BUFFERSIZE:
+ tmp_u64 = g_variant_get_uint64(data);
+ for (i = 0; i < 2; i++) {
+ if (devc->profile->buffersizes[i] == tmp_u64) {
+ devc->framesize = tmp_u64;
+ break;
+ }
+ }
+ if (i == 2)
+ ret = SR_ERR_ARG;
+ break;
+ case SR_CONF_TIMEBASE:
+ g_variant_get(data, "(tt)", &p, &q);
+ tmp_int = -1;
+ for (i = 0; i < ARRAY_SIZE(timebases); i++) {
+ if (timebases[i][0] == p && timebases[i][1] == q) {
+ tmp_int = i;
+ break;
+ }
+ }
+ if (tmp_int >= 0)
+ devc->timebase = tmp_int;
+ else
+ ret = SR_ERR_ARG;
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ tmp_str = g_variant_get_string(data, NULL);
+ for (i = 0; trigger_sources[i]; i++) {
+ if (!strcmp(tmp_str, trigger_sources[i])) {
+ devc->triggersource = g_strdup(tmp_str);
+ break;
+ }
+ }
+ if (trigger_sources[i] == 0)
+ ret = SR_ERR_ARG;
+ break;
+ case SR_CONF_FILTER:
+ tmp_str = g_variant_get_string(data, NULL);
+ devc->filter_ch1 = devc->filter_ch2 = devc->filter_trigger = 0;
+ targets = g_strsplit(tmp_str, ",", 0);
+ for (i = 0; targets[i]; i++) {
+ if (targets[i] == '\0')
+ /* Empty filter string can be used to clear them all. */
+ ;
+ else if (!strcmp(targets[i], "CH1"))
+ devc->filter_ch1 = TRUE;
+ else if (!strcmp(targets[i], "CH2"))
+ devc->filter_ch2 = TRUE;
+ else if (!strcmp(targets[i], "TRIGGER"))
+ devc->filter_trigger = TRUE;
+ else {
+ sr_err("Invalid filter target %s.", targets[i]);
+ ret = SR_ERR_ARG;
+ }
+ }
+ g_strfreev(targets);
+ break;
+ case SR_CONF_VDIV:
+ /* TODO: Not supporting vdiv per channel yet. */
+ g_variant_get(data, "(tt)", &p, &q);
+ tmp_int = -1;
+ for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+ if (vdivs[i][0] == p && vdivs[i][1] == q) {
+ tmp_int = i;
+ break;
+ }
+ }
+ if (tmp_int >= 0) {
+ devc->voltage_ch1 = tmp_int;
+ devc->voltage_ch2 = tmp_int;
+ } else
+ ret = SR_ERR_ARG;
+ break;
+ case SR_CONF_COUPLING:
+ tmp_str = g_variant_get_string(data, NULL);
+ /* TODO: Not supporting coupling per channel yet. */
+ for (i = 0; coupling[i]; i++) {
+ if (!strcmp(tmp_str, coupling[i])) {
+ devc->coupling_ch1 = i;
+ devc->coupling_ch2 = i;
+ break;
+ }
+ }
+ if (coupling[i] == 0)
+ ret = SR_ERR_ARG;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ break;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ GVariant *tuple, *rational[2];
+ GVariantBuilder gvb;
+ unsigned int i;
+
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ scanopts, ARRAY_SIZE(scanopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
+ break;
+ case SR_CONF_BUFFERSIZE:
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
+ devc->profile->buffersizes, 2, sizeof(uint64_t));
+ break;
+ case SR_CONF_COUPLING:
+ *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+ break;
+ case SR_CONF_VDIV:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+ rational[0] = g_variant_new_uint64(vdivs[i][0]);
+ rational[1] = g_variant_new_uint64(vdivs[i][1]);
+ tuple = g_variant_new_tuple(rational, 2);
+ g_variant_builder_add_value(&gvb, tuple);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_FILTER:
+ *data = g_variant_new_strv(filter_targets,
+ ARRAY_SIZE(filter_targets));
+ break;
+ case SR_CONF_TIMEBASE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < ARRAY_SIZE(timebases); i++) {
+ rational[0] = g_variant_new_uint64(timebases[i][0]);
+ rational[1] = g_variant_new_uint64(timebases[i][1]);
+ tuple = g_variant_new_tuple(rational, 2);
+ g_variant_builder_add_value(&gvb, tuple);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ *data = g_variant_new_strv(trigger_sources,
+ ARRAY_SIZE(trigger_sources));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static void send_chunk(struct sr_dev_inst *sdi, unsigned char *buf,
+ int num_samples)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct dev_context *devc;
+ float ch1, ch2, range;
+ int num_channels, data_offset, i;
+
+ devc = sdi->priv;
+ num_channels = (devc->ch1_enabled && devc->ch2_enabled) ? 2 : 1;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ /* TODO: support for 5xxx series 9-bit samples */
+ analog.channels = devc->enabled_channels;
+ analog.num_samples = num_samples;
+ analog.mq = SR_MQ_VOLTAGE;
+ analog.unit = SR_UNIT_VOLT;
+ /* TODO: Check malloc return value. */
+ analog.data = g_try_malloc(analog.num_samples * sizeof(float) * num_channels);
+ data_offset = 0;
+ for (i = 0; i < analog.num_samples; i++) {
+ /*
+ * The device always sends data for both channels. If a channel
+ * is disabled, it contains a copy of the enabled channel's
+ * data. However, we only send the requested channels to
+ * the bus.
+ *
+ * Voltage values are encoded as a value 0-255 (0-512 on the
+ * DSO-5200*), where the value is a point in the range
+ * represented by the vdiv setting. There are 8 vertical divs,
+ * so e.g. 500mV/div represents 4V peak-to-peak where 0 = -2V
+ * and 255 = +2V.
+ */
+ /* TODO: Support for DSO-5xxx series 9-bit samples. */
+ if (devc->ch1_enabled) {
+ range = ((float)vdivs[devc->voltage_ch1][0] / vdivs[devc->voltage_ch1][1]) * 8;
+ ch1 = range / 255 * *(buf + i * 2 + 1);
+ /* Value is centered around 0V. */
+ ch1 -= range / 2;
+ analog.data[data_offset++] = ch1;
+ }
+ if (devc->ch2_enabled) {
+ range = ((float)vdivs[devc->voltage_ch2][0] / vdivs[devc->voltage_ch2][1]) * 8;
+ ch2 = range / 255 * *(buf + i * 2);
+ ch2 -= range / 2;
+ analog.data[data_offset++] = ch2;
+ }
+ }
+ sr_session_send(devc->cb_data, &packet);
+}
+
+/*
+ * Called by libusb (as triggered by handle_event()) when a transfer comes in.
+ * Only channel data comes in asynchronously, and all transfers for this are
+ * queued up beforehand, so this just needs to chuck the incoming data onto
+ * the libsigrok session bus.
+ */
+static void receive_transfer(struct libusb_transfer *transfer)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ int num_samples, pre;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+ sr_spew("receive_transfer(): status %d received %d bytes.",
+ transfer->status, transfer->actual_length);
+
+ if (transfer->actual_length == 0)
+ /* Nothing to send to the bus. */
+ return;
+
+ num_samples = transfer->actual_length / 2;
+
+ sr_spew("Got %d-%d/%d samples in frame.", devc->samp_received + 1,
+ devc->samp_received + num_samples, devc->framesize);
+
+ /*
+ * The device always sends a full frame, but the beginning of the frame
+ * doesn't represent the trigger point. The offset at which the trigger
+ * happened came in with the capture state, so we need to start sending
+ * from there up the session bus. The samples in the frame buffer
+ * before that trigger point came after the end of the device's frame
+ * buffer was reached, and it wrapped around to overwrite up until the
+ * trigger point.
+ */
+ if (devc->samp_received < devc->trigger_offset) {
+ /* Trigger point not yet reached. */
+ if (devc->samp_received + num_samples < devc->trigger_offset) {
+ /* The entire chunk is before the trigger point. */
+ memcpy(devc->framebuf + devc->samp_buffered * 2,
+ transfer->buffer, num_samples * 2);
+ devc->samp_buffered += num_samples;
+ } else {
+ /*
+ * This chunk hits or overruns the trigger point.
+ * Store the part before the trigger fired, and
+ * send the rest up to the session bus.
+ */
+ pre = devc->trigger_offset - devc->samp_received;
+ memcpy(devc->framebuf + devc->samp_buffered * 2,
+ transfer->buffer, pre * 2);
+ devc->samp_buffered += pre;
+
+ /* The rest of this chunk starts with the trigger point. */
+ sr_dbg("Reached trigger point, %d samples buffered.",
+ devc->samp_buffered);
+
+ /* Avoid the corner case where the chunk ended at
+ * exactly the trigger point. */
+ if (num_samples > pre)
+ send_chunk(sdi, transfer->buffer + pre * 2,
+ num_samples - pre);
+ }
+ } else {
+ /* Already past the trigger point, just send it all out. */
+ send_chunk(sdi, transfer->buffer,
+ num_samples);
+ }
+
+ devc->samp_received += num_samples;
+
+ /* Everything in this transfer was either copied to the buffer or
+ * sent to the session bus. */
+ g_free(transfer->buffer);
+ libusb_free_transfer(transfer);
+
+ if (devc->samp_received >= devc->framesize) {
+ /* That was the last chunk in this frame. Send the buffered
+ * pre-trigger samples out now, in one big chunk. */
+ sr_dbg("End of frame, sending %d pre-trigger buffered samples.",
+ devc->samp_buffered);
+ send_chunk(sdi, devc->framebuf, devc->samp_buffered);
+
+ /* Mark the end of this frame. */
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(devc->cb_data, &packet);
+
+ if (devc->limit_frames && ++devc->num_frames == devc->limit_frames) {
+ /* Terminate session */
+ devc->dev_state = STOPPING;
+ } else {
+ devc->dev_state = NEW_CAPTURE;
+ }
+ }
+}
+
+static int handle_event(int fd, int revents, void *cb_data)
+{
+ const struct sr_dev_inst *sdi;
+ struct sr_datafeed_packet packet;
+ struct timeval tv;
+ struct dev_context *devc;
+ struct drv_context *drvc = di->priv;
+ int num_channels;
+ uint32_t trigger_offset;
+ uint8_t capturestate;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+ devc = sdi->priv;
+ if (devc->dev_state == STOPPING) {
+ /* We've been told to wind up the acquisition. */
+ sr_dbg("Stopping acquisition.");
+ /*
+ * TODO: Doesn't really cancel pending transfers so they might
+ * come in after SR_DF_END is sent.
+ */
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+
+ devc->dev_state = IDLE;
+
+ return TRUE;
+ }
+
+ /* Always handle pending libusb events. */
+ tv.tv_sec = tv.tv_usec = 0;
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+ /* TODO: ugh */
+ if (devc->dev_state == NEW_CAPTURE) {
+ if (dso_capture_start(sdi) != SR_OK)
+ return TRUE;
+ if (dso_enable_trigger(sdi) != SR_OK)
+ return TRUE;
+// if (dso_force_trigger(sdi) != SR_OK)
+// return TRUE;
+ sr_dbg("Successfully requested next chunk.");
+ devc->dev_state = CAPTURE;
+ return TRUE;
+ }
+ if (devc->dev_state != CAPTURE)
+ return TRUE;
+
+ if ((dso_get_capturestate(sdi, &capturestate, &trigger_offset)) != SR_OK)
+ return TRUE;
+
+ sr_dbg("Capturestate %d.", capturestate);
+ sr_dbg("Trigger offset 0x%.6x.", trigger_offset);
+ switch (capturestate) {
+ case CAPTURE_EMPTY:
+ if (++devc->capture_empty_count >= MAX_CAPTURE_EMPTY) {
+ devc->capture_empty_count = 0;
+ if (dso_capture_start(sdi) != SR_OK)
+ break;
+ if (dso_enable_trigger(sdi) != SR_OK)
+ break;
+// if (dso_force_trigger(sdi) != SR_OK)
+// break;
+ sr_dbg("Successfully requested next chunk.");
+ }
+ break;
+ case CAPTURE_FILLING:
+ /* No data yet. */
+ break;
+ case CAPTURE_READY_8BIT:
+ /* Remember where in the captured frame the trigger is. */
+ devc->trigger_offset = trigger_offset;
+
+ num_channels = (devc->ch1_enabled && devc->ch2_enabled) ? 2 : 1;
+ /* TODO: Check malloc return value. */
+ devc->framebuf = g_try_malloc(devc->framesize * num_channels * 2);
+ devc->samp_buffered = devc->samp_received = 0;
+
+ /* Tell the scope to send us the first frame. */
+ if (dso_get_channeldata(sdi, receive_transfer) != SR_OK)
+ break;
+
+ /*
+ * Don't hit the state machine again until we're done fetching
+ * the data we just told the scope to send.
+ */
+ devc->dev_state = FETCH_DATA;
+
+ /* Tell the frontend a new frame is on the way. */
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(sdi, &packet);
+ break;
+ case CAPTURE_READY_9BIT:
+ /* TODO */
+ sr_err("Not yet supported.");
+ break;
+ case CAPTURE_TIMEOUT:
+ /* Doesn't matter, we'll try again next time. */
+ break;
+ default:
+ sr_dbg("Unknown capture state: %d.", capturestate);
+ break;
+ }
+
+ return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct drv_context *drvc = di->priv;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ devc->cb_data = cb_data;
+
+ if (configure_channels(sdi) != SR_OK) {
+ sr_err("Failed to configure channels.");
+ return SR_ERR;
+ }
+
+ if (dso_init(sdi) != SR_OK)
+ return SR_ERR;
+
+ if (dso_capture_start(sdi) != SR_OK)
+ return SR_ERR;
+
+ devc->dev_state = CAPTURE;
+ usb_source_add(sdi->session, drvc->sr_ctx, TICK, handle_event, (void *)sdi);
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR;
+
+ devc = sdi->priv;
+ devc->dev_state = STOPPING;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver hantek_dso_driver_info = {
+ .name = "hantek-dso",
+ .longname = "Hantek DSO",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = 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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ * With protocol information from the hantekdso project,
+ * Copyright (C) 2008 Oleg Khudyakov <prcoder@gmail.com>
+ *
+ * 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.h"
+#include "libsigrok-internal.h"
+#include "dso.h"
+#include <string.h>
+#include <glib.h>
+#include <libusb.h>
+
+extern struct sr_dev_driver hantek_dso_driver_info;
+
+static int send_begin(const struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ int ret;
+ unsigned char buffer[] = {0x0f, 0x03, 0x03, 0x03, 0x68, 0xac, 0xfe,
+ 0x00, 0x01, 0x00};
+
+ sr_dbg("Sending CTRL_BEGINCOMMAND.");
+
+ usb = sdi->conn;
+ if ((ret = libusb_control_transfer(usb->devhdl,
+ LIBUSB_REQUEST_TYPE_VENDOR, CTRL_BEGINCOMMAND,
+ 0, 0, buffer, sizeof(buffer), 200)) != sizeof(buffer)) {
+ sr_err("Failed to send begincommand: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int send_bulkcmd(const struct sr_dev_inst *sdi, uint8_t *cmdstring, int cmdlen)
+{
+ struct sr_usb_dev_inst *usb;
+ int ret, tmp;
+
+ usb = sdi->conn;
+
+ if (send_begin(sdi) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT, cmdstring,
+ cmdlen, &tmp, 200)) != 0)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static int dso_getmps(libusb_device *dev)
+{
+ struct libusb_device_descriptor des;
+ struct libusb_config_descriptor *conf_dsc;
+ const struct libusb_interface_descriptor *intf_dsc;
+ int mps;
+
+ if (libusb_get_device_descriptor(dev, &des) != 0)
+ return 0;
+
+ if (des.bNumConfigurations != 1)
+ return 0;
+
+ if (libusb_get_config_descriptor(dev, 0, &conf_dsc) != 0)
+ return 0;
+
+ mps = 0;
+ intf_dsc = &(conf_dsc->interface[0].altsetting[0]);
+ if (intf_dsc->bNumEndpoints != 2)
+ goto err;
+
+ if ((intf_dsc->endpoint[0].bEndpointAddress & 0x8f) !=
+ (2 | LIBUSB_ENDPOINT_OUT))
+ /* The first endpoint should be 2 (outbound). */
+ goto err;
+
+ if ((intf_dsc->endpoint[1].bEndpointAddress & 0x8f) !=
+ (6 | LIBUSB_ENDPOINT_IN))
+ /* The second endpoint should be 6 (inbound). */
+ goto err;
+
+ mps = intf_dsc->endpoint[1].wMaxPacketSize;
+
+err:
+ if (conf_dsc)
+ libusb_free_config_descriptor(conf_dsc);
+
+ return mps;
+}
+
+SR_PRIV int dso_open(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct drv_context *drvc = hantek_dso_driver_info.priv;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_device_descriptor des;
+ libusb_device **devlist;
+ int err, skip, i;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ if (sdi->status == SR_ST_ACTIVE)
+ /* already in use */
+ return SR_ERR;
+
+ skip = 0;
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if ((err = libusb_get_device_descriptor(devlist[i], &des))) {
+ sr_err("Failed to get device descriptor: %s.",
+ libusb_error_name(err));
+ continue;
+ }
+
+ if (des.idVendor != devc->profile->fw_vid
+ || des.idProduct != devc->profile->fw_pid)
+ continue;
+
+ if (sdi->status == SR_ST_INITIALIZING) {
+ if (skip != sdi->index) {
+ /* Skip devices of this type that aren't the one we want. */
+ skip += 1;
+ continue;
+ }
+ } else if (sdi->status == SR_ST_INACTIVE) {
+ /*
+ * This device is fully enumerated, so we need to find
+ * this device by vendor, product, bus and address.
+ */
+ if (libusb_get_bus_number(devlist[i]) != usb->bus
+ || libusb_get_device_address(devlist[i]) != usb->address)
+ /* this is not the one */
+ continue;
+ }
+
+ if (!(err = libusb_open(devlist[i], &usb->devhdl))) {
+ if (usb->address == 0xff)
+ /*
+ * first time we touch this device after firmware upload,
+ * so we don't know the address yet.
+ */
+ usb->address = libusb_get_device_address(devlist[i]);
+
+ if (!(devc->epin_maxpacketsize = dso_getmps(devlist[i])))
+ sr_err("Wrong endpoint profile.");
+ else {
+ sdi->status = SR_ST_ACTIVE;
+ sr_info("Opened device %d on %d.%d interface %d.",
+ sdi->index, usb->bus,
+ usb->address, USB_INTERFACE);
+ }
+ } else {
+ sr_err("Failed to open device: %s.",
+ libusb_error_name(err));
+ }
+
+ /* If we made it here, we handled the device (somehow). */
+ break;
+ }
+ libusb_free_device_list(devlist, 1);
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+SR_PRIV void dso_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ usb = sdi->conn;
+
+ if (usb->devhdl == NULL)
+ return;
+
+ sr_info("Closing device %d on %d.%d interface %d.", sdi->index,
+ usb->bus, usb->address, USB_INTERFACE);
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+ sdi->status = SR_ST_INACTIVE;
+
+}
+
+static int get_channel_offsets(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ GString *gs;
+ int chan, v, ret;
+
+ sr_dbg("Getting channel offsets.");
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ ret = libusb_control_transfer(usb->devhdl,
+ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR,
+ CTRL_READ_EEPROM, EEPROM_CHANNEL_OFFSETS, 0,
+ (unsigned char *)&devc->channel_levels,
+ sizeof(devc->channel_levels), 200);
+ if (ret != sizeof(devc->channel_levels)) {
+ sr_err("Failed to get channel offsets: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ /* Comes in as 16-bit numbers with the second byte always 0 on
+ * the DSO-2090. Guessing this is supposed to be big-endian,
+ * since that's how voltage offsets are submitted back to the DSO.
+ * Convert to host order now, so we can use them natively.
+ */
+ for (chan = 0; chan < 2; chan++) {
+ for (v = 0; v < 9; v++) {
+ devc->channel_levels[chan][v][0] =
+ g_ntohs(devc->channel_levels[chan][v][0]);
+ devc->channel_levels[chan][v][1] =
+ g_ntohs(devc->channel_levels[chan][v][1]);
+ }
+ }
+
+ if (sr_log_loglevel_get() >= SR_LOG_DBG) {
+ gs = g_string_sized_new(128);
+ for (chan = 0; chan < 2; chan++) {
+ g_string_printf(gs, "CH%d:", chan + 1);
+ for (v = 0; v < 9; v++) {
+ g_string_append_printf(gs, " %.4x-%.4x",
+ devc->channel_levels[chan][v][0],
+ devc->channel_levels[chan][v][1]);
+ }
+ sr_dbg("%s", gs->str);
+ }
+ g_string_free(gs, TRUE);
+ }
+
+ return SR_OK;
+}
+
+static int dso_set_trigger_samplerate(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int ret, tmp;
+ uint8_t cmdstring[12];
+ uint16_t timebase_small[] = { 0xffff, 0xfffc, 0xfff7, 0xffe8, 0xffce,
+ 0xff9c, 0xff07, 0xfe0d, 0xfc19, 0xf63d, 0xec79, 0xd8f1 };
+ uint16_t timebase_large[] = { 0xffff, 0x0000, 0xfffc, 0xfff7, 0xffe8,
+ 0xffce, 0xff9d, 0xff07, 0xfe0d, 0xfc19, 0xf63d, 0xec79 };
+
+ sr_dbg("Preparing CMD_SET_TRIGGER_SAMPLERATE.");
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ memset(cmdstring, 0, sizeof(cmdstring));
+ /* Command */
+ cmdstring[0] = CMD_SET_TRIGGER_SAMPLERATE;
+
+ /* Trigger source */
+ sr_dbg("Trigger source %s.", devc->triggersource);
+ if (!strcmp("CH2", devc->triggersource))
+ tmp = 0;
+ else if (!strcmp("CH1", devc->triggersource))
+ tmp = 1;
+ else if (!strcmp("EXT", devc->triggersource))
+ tmp = 2;
+ else {
+ sr_err("Invalid trigger source: '%s'.", devc->triggersource);
+ return SR_ERR_ARG;
+ }
+ cmdstring[2] = tmp;
+
+ /* Frame size */
+ sr_dbg("Frame size: %d.", devc->framesize);
+ cmdstring[2] |= (devc->framesize == FRAMESIZE_SMALL ? 0x01 : 0x02) << 2;
+
+ /* Timebase fast */
+ sr_dbg("Time base index: %d.", devc->timebase);
+ if (devc->framesize == FRAMESIZE_SMALL) {
+ if (devc->timebase < TIME_20us)
+ tmp = 0;
+ else if (devc->timebase == TIME_20us)
+ tmp = 1;
+ else if (devc->timebase == TIME_40us)
+ tmp = 2;
+ else if (devc->timebase == TIME_100us)
+ tmp = 3;
+ else if (devc->timebase >= TIME_200us)
+ tmp = 4;
+ } else {
+ if (devc->timebase < TIME_40us) {
+ sr_err("Timebase < 40us only supported with 10K buffer.");
+ return SR_ERR_ARG;
+ }
+ else if (devc->timebase == TIME_40us)
+ tmp = 0;
+ else if (devc->timebase == TIME_100us)
+ tmp = 2;
+ else if (devc->timebase == TIME_200us)
+ tmp = 3;
+ else if (devc->timebase >= TIME_400us)
+ tmp = 4;
+ }
+ cmdstring[2] |= (tmp & 0x07) << 5;
+
+ /* Enabled channels: 00=CH1 01=CH2 10=both */
+ sr_dbg("Channels CH1=%d CH2=%d", devc->ch1_enabled, devc->ch2_enabled);
+ tmp = (((devc->ch2_enabled ? 1 : 0) << 1) + (devc->ch1_enabled ? 1 : 0)) - 1;
+ cmdstring[3] = tmp;
+
+ /* Fast rates channel */
+ /* TODO: Is this right? */
+ tmp = devc->timebase < TIME_10us ? 1 : 0;
+ cmdstring[3] |= tmp << 2;
+
+ /* Trigger slope: 0=positive 1=negative */
+ /* TODO: Does this work? */
+ sr_dbg("Trigger slope: %d.", devc->triggerslope);
+ cmdstring[3] |= (devc->triggerslope == SLOPE_NEGATIVE ? 1 : 0) << 3;
+
+ /* Timebase slow */
+ if (devc->timebase < TIME_100us)
+ tmp = 0;
+ else if (devc->timebase > TIME_400ms)
+ tmp = 0xffed;
+ else {
+ if (devc->framesize == FRAMESIZE_SMALL)
+ tmp = timebase_small[devc->timebase - 3];
+ else
+ tmp = timebase_large[devc->timebase - 3];
+ }
+ cmdstring[4] = tmp & 0xff;
+ cmdstring[5] = (tmp >> 8) & 0xff;
+
+ /* Horizontal trigger position */
+ sr_dbg("Trigger position: %3.2f.", devc->triggerposition);
+ tmp = 0x77fff + 0x8000 * devc->triggerposition;
+ cmdstring[6] = tmp & 0xff;
+ cmdstring[7] = (tmp >> 8) & 0xff;
+ cmdstring[10] = (tmp >> 16) & 0xff;
+
+ if (send_begin(sdi) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+ cmdstring, sizeof(cmdstring), &tmp, 100)) != 0) {
+ sr_err("Failed to set trigger/samplerate: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sr_dbg("Sent CMD_SET_TRIGGER_SAMPLERATE.");
+
+ return SR_OK;
+}
+
+static int dso_set_filters(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int ret, tmp;
+ uint8_t cmdstring[8];
+
+ sr_dbg("Preparing CMD_SET_FILTERS.");
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ memset(cmdstring, 0, sizeof(cmdstring));
+ cmdstring[0] = CMD_SET_FILTERS;
+ cmdstring[1] = 0x0f;
+ if (devc->filter_ch1) {
+ sr_dbg("Turning on CH1 filter.");
+ cmdstring[2] |= 0x80;
+ }
+ if (devc->filter_ch2) {
+ sr_dbg("Turning on CH2 filter.");
+ cmdstring[2] |= 0x40;
+ }
+ if (devc->filter_trigger) {
+ /* TODO: supported on the DSO-2090? */
+ sr_dbg("Turning on trigger filter.");
+ cmdstring[2] |= 0x20;
+ }
+
+ if (send_begin(sdi) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+ cmdstring, sizeof(cmdstring), &tmp, 100)) != 0) {
+ sr_err("Failed to set filters: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sr_dbg("Sent CMD_SET_FILTERS.");
+
+ return SR_OK;
+}
+
+static int dso_set_voltage(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int ret, tmp;
+ uint8_t cmdstring[8];
+
+ sr_dbg("Preparing CMD_SET_VOLTAGE.");
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ memset(cmdstring, 0, sizeof(cmdstring));
+ cmdstring[0] = CMD_SET_VOLTAGE;
+ cmdstring[1] = 0x0f;
+ cmdstring[2] = 0x30;
+
+ /* CH1 volts/div is encoded in bits 0-1 */
+ sr_dbg("CH1 vdiv index: %d.", devc->voltage_ch1);
+ switch (devc->voltage_ch1) {
+ case VDIV_1V:
+ case VDIV_100MV:
+ case VDIV_10MV:
+ cmdstring[2] |= 0x00;
+ break;
+ case VDIV_2V:
+ case VDIV_200MV:
+ case VDIV_20MV:
+ cmdstring[2] |= 0x01;
+ break;
+ case VDIV_5V:
+ case VDIV_500MV:
+ case VDIV_50MV:
+ cmdstring[2] |= 0x02;
+ break;
+ }
+
+ /* CH2 volts/div is encoded in bits 2-3 */
+ sr_dbg("CH2 vdiv index: %d.", devc->voltage_ch2);
+ switch (devc->voltage_ch2) {
+ case VDIV_1V:
+ case VDIV_100MV:
+ case VDIV_10MV:
+ cmdstring[2] |= 0x00;
+ break;
+ case VDIV_2V:
+ case VDIV_200MV:
+ case VDIV_20MV:
+ cmdstring[2] |= 0x04;
+ break;
+ case VDIV_5V:
+ case VDIV_500MV:
+ case VDIV_50MV:
+ cmdstring[2] |= 0x08;
+ break;
+ }
+
+ if (send_begin(sdi) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+ cmdstring, sizeof(cmdstring), &tmp, 100)) != 0) {
+ sr_err("Failed to set voltage: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sr_dbg("Sent CMD_SET_VOLTAGE.");
+
+ return SR_OK;
+}
+
+static int dso_set_relays(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ GString *gs;
+ int ret, i;
+ uint8_t relays[17] = { 0x00, 0x04, 0x08, 0x02, 0x20, 0x40, 0x10, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ sr_dbg("Preparing CTRL_SETRELAYS.");
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ if (devc->voltage_ch1 < VDIV_1V)
+ relays[1] = ~relays[1];
+
+ if (devc->voltage_ch1 < VDIV_100MV)
+ relays[2] = ~relays[2];
+
+ sr_dbg("CH1 coupling: %d.", devc->coupling_ch1);
+ if (devc->coupling_ch1 != COUPLING_AC)
+ relays[3] = ~relays[3];
+
+ if (devc->voltage_ch2 < VDIV_1V)
+ relays[4] = ~relays[4];
+
+ if (devc->voltage_ch2 < VDIV_100MV)
+ relays[5] = ~relays[5];
+
+ sr_dbg("CH2 coupling: %d.", devc->coupling_ch1);
+ if (devc->coupling_ch2 != COUPLING_AC)
+ relays[6] = ~relays[6];
+
+ if (!strcmp(devc->triggersource, "EXT"))
+ relays[7] = ~relays[7];
+
+ if (sr_log_loglevel_get() >= SR_LOG_DBG) {
+ gs = g_string_sized_new(128);
+ g_string_printf(gs, "Relays:");
+ for (i = 0; i < 17; i++)
+ g_string_append_printf(gs, " %.2x", relays[i]);
+ sr_dbg("%s", gs->str);
+ g_string_free(gs, TRUE);
+ }
+
+ if ((ret = libusb_control_transfer(usb->devhdl,
+ LIBUSB_REQUEST_TYPE_VENDOR, CTRL_SETRELAYS,
+ 0, 0, relays, 17, 100)) != sizeof(relays)) {
+ sr_err("Failed to set relays: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sr_dbg("Sent CTRL_SETRELAYS.");
+
+ return SR_OK;
+}
+
+static int dso_set_voffsets(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int offset, ret;
+ uint16_t *ch_levels;
+ uint8_t offsets[17];
+
+ sr_dbg("Preparing CTRL_SETOFFSET.");
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ memset(offsets, 0, sizeof(offsets));
+ /* Channel 1 */
+ ch_levels = devc->channel_levels[0][devc->voltage_ch1];
+ offset = (ch_levels[1] - ch_levels[0]) * devc->voffset_ch1 + ch_levels[0];
+ offsets[0] = (offset >> 8) | 0x20;
+ offsets[1] = offset & 0xff;
+ sr_dbg("CH1 offset: %3.2f (%.2x%.2x).", devc->voffset_ch1,
+ offsets[0], offsets[1]);
+
+ /* Channel 2 */
+ ch_levels = devc->channel_levels[1][devc->voltage_ch2];
+ offset = (ch_levels[1] - ch_levels[0]) * devc->voffset_ch2 + ch_levels[0];
+ offsets[2] = (offset >> 8) | 0x20;
+ offsets[3] = offset & 0xff;
+ sr_dbg("CH2 offset: %3.2f (%.2x%.2x).", devc->voffset_ch2,
+ offsets[2], offsets[3]);
+
+ /* Trigger */
+ offset = MAX_VERT_TRIGGER * devc->voffset_trigger;
+ offsets[4] = (offset >> 8) | 0x20;
+ offsets[5] = offset & 0xff;
+ sr_dbg("Trigger offset: %3.2f (%.2x%.2x).", devc->voffset_trigger,
+ offsets[4], offsets[5]);
+
+ if ((ret = libusb_control_transfer(usb->devhdl,
+ LIBUSB_REQUEST_TYPE_VENDOR, CTRL_SETOFFSET,
+ 0, 0, offsets, sizeof(offsets), 100)) != sizeof(offsets)) {
+ sr_err("Failed to set offsets: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sr_dbg("Sent CTRL_SETOFFSET.");
+
+ return SR_OK;
+}
+
+SR_PRIV int dso_enable_trigger(const struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ int ret, tmp;
+ uint8_t cmdstring[2];
+
+ sr_dbg("Sending CMD_ENABLE_TRIGGER.");
+
+ usb = sdi->conn;
+
+ memset(cmdstring, 0, sizeof(cmdstring));
+ cmdstring[0] = CMD_ENABLE_TRIGGER;
+ cmdstring[1] = 0x00;
+
+ if (send_begin(sdi) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+ cmdstring, sizeof(cmdstring), &tmp, 100)) != 0) {
+ sr_err("Failed to enable trigger: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int dso_force_trigger(const struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ int ret, tmp;
+ uint8_t cmdstring[2];
+
+ sr_dbg("Sending CMD_FORCE_TRIGGER.");
+
+ usb = sdi->conn;
+
+ memset(cmdstring, 0, sizeof(cmdstring));
+ cmdstring[0] = CMD_FORCE_TRIGGER;
+ cmdstring[1] = 0x00;
+
+ if (send_begin(sdi) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+ cmdstring, sizeof(cmdstring), &tmp, 100)) != 0) {
+ sr_err("Failed to force trigger: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int dso_init(const struct sr_dev_inst *sdi)
+{
+
+ sr_dbg("Initializing DSO.");
+
+ if (get_channel_offsets(sdi) != SR_OK)
+ return SR_ERR;
+
+ if (dso_set_trigger_samplerate(sdi) != SR_OK)
+ return SR_ERR;
+
+ if (dso_set_filters(sdi) != SR_OK)
+ return SR_ERR;
+
+ if (dso_set_voltage(sdi) != SR_OK)
+ return SR_ERR;
+
+ if (dso_set_relays(sdi) != SR_OK)
+ return SR_ERR;
+
+ if (dso_set_voffsets(sdi) != SR_OK)
+ return SR_ERR;
+
+ if (dso_enable_trigger(sdi) != SR_OK)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+SR_PRIV int dso_get_capturestate(const struct sr_dev_inst *sdi,
+ uint8_t *capturestate, uint32_t *trigger_offset)
+{
+ struct sr_usb_dev_inst *usb;
+ int ret, tmp, i;
+ unsigned int bitvalue, toff;
+ uint8_t cmdstring[2], inbuf[512];
+
+ sr_dbg("Sending CMD_GET_CAPTURESTATE.");
+
+ usb = sdi->conn;
+
+ cmdstring[0] = CMD_GET_CAPTURESTATE;
+ cmdstring[1] = 0;
+
+ if ((ret = send_bulkcmd(sdi, cmdstring, sizeof(cmdstring))) != SR_OK) {
+ sr_dbg("Failed to send get_capturestate command: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_IN,
+ inbuf, 512, &tmp, 100)) != 0) {
+ sr_dbg("Failed to get capturestate: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ *capturestate = inbuf[0];
+ toff = (inbuf[1] << 16) | (inbuf[3] << 8) | inbuf[2];
+
+ /*
+ * This conversion comes from the openhantek project.
+ * Each set bit in the 24-bit value inverts all bits with a lower
+ * value. No idea why the device reports the trigger point this way.
+ */
+ bitvalue = 1;
+ for (i = 0; i < 24; i++) {
+ /* Each set bit inverts all bits with a lower value. */
+ if(toff & bitvalue)
+ toff ^= bitvalue - 1;
+ bitvalue <<= 1;
+ }
+ *trigger_offset = toff;
+
+ return SR_OK;
+}
+
+SR_PRIV int dso_capture_start(const struct sr_dev_inst *sdi)
+{
+ int ret;
+ uint8_t cmdstring[2];
+
+ sr_dbg("Sending CMD_CAPTURE_START.");
+
+ cmdstring[0] = CMD_CAPTURE_START;
+ cmdstring[1] = 0;
+
+ if ((ret = send_bulkcmd(sdi, cmdstring, sizeof(cmdstring))) != SR_OK) {
+ sr_err("Failed to send capture_start command: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int dso_get_channeldata(const struct sr_dev_inst *sdi,
+ libusb_transfer_cb_fn cb)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_transfer *transfer;
+ int num_transfers, ret, i;
+ uint8_t cmdstring[2];
+ unsigned char *buf;
+
+ sr_dbg("Sending CMD_GET_CHANNELDATA.");
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ cmdstring[0] = CMD_GET_CHANNELDATA;
+ cmdstring[1] = 0;
+
+ if ((ret = send_bulkcmd(sdi, cmdstring, sizeof(cmdstring))) != SR_OK) {
+ sr_err("Failed to get channel data: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ /* TODO: DSO-2xxx only. */
+ num_transfers = devc->framesize *
+ sizeof(unsigned short) / devc->epin_maxpacketsize;
+ sr_dbg("Queueing up %d transfers.", num_transfers);
+ for (i = 0; i < num_transfers; i++) {
+ if (!(buf = g_try_malloc(devc->epin_maxpacketsize))) {
+ sr_err("Failed to malloc USB endpoint buffer.");
+ return SR_ERR_MALLOC;
+ }
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, usb->devhdl, DSO_EP_IN, buf,
+ devc->epin_maxpacketsize, cb, (void *)sdi, 40);
+ if ((ret = libusb_submit_transfer(transfer)) != 0) {
+ sr_err("Failed to submit transfer: %s.",
+ libusb_error_name(ret));
+ /* TODO: Free them all. */
+ libusb_free_transfer(transfer);
+ g_free(buf);
+ return SR_ERR;
+ }
+ }
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ * With protocol information from the hantekdso project,
+ * Copyright (C) 2008 Oleg Khudyakov <prcoder@gmail.com>
+ *
+ * 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_HANTEK_DSO_DSO_H
+#define LIBSIGROK_HARDWARE_HANTEK_DSO_DSO_H
+
+#define LOG_PREFIX "hantek-dso"
+
+#define USB_INTERFACE 0
+#define USB_CONFIGURATION 1
+#define DSO_EP_IN 0x86
+#define DSO_EP_OUT 0x02
+
+/* FX2 renumeration delay in ms */
+#define MAX_RENUM_DELAY_MS 3000
+
+#define MAX_CAPTURE_EMPTY 3
+
+#define DEFAULT_VOLTAGE VDIV_500MV
+#define DEFAULT_FRAMESIZE FRAMESIZE_SMALL
+#define DEFAULT_TIMEBASE TIME_100us
+#define DEFAULT_TRIGGER_SOURCE "CH1"
+#define DEFAULT_COUPLING COUPLING_DC
+#define DEFAULT_HORIZ_TRIGGERPOS 0.5
+#define DEFAULT_VERT_OFFSET 0.5
+#define DEFAULT_VERT_TRIGGERPOS 0.5
+
+#define MAX_VERT_TRIGGER 0xfe
+
+/* Hantek DSO-specific protocol values */
+#define EEPROM_CHANNEL_OFFSETS 0x08
+
+/* All models have this for their "fast" mode. */
+#define FRAMESIZE_SMALL 10240
+
+enum control_requests {
+ CTRL_READ_EEPROM = 0xa2,
+ CTRL_GETSPEED = 0xb2,
+ CTRL_BEGINCOMMAND = 0xb3,
+ CTRL_SETOFFSET = 0xb4,
+ CTRL_SETRELAYS = 0xb5,
+};
+
+enum dso_commands {
+ CMD_SET_FILTERS = 0,
+ CMD_SET_TRIGGER_SAMPLERATE,
+ CMD_FORCE_TRIGGER,
+ CMD_CAPTURE_START,
+ CMD_ENABLE_TRIGGER,
+ CMD_GET_CHANNELDATA,
+ CMD_GET_CAPTURESTATE,
+ CMD_SET_VOLTAGE,
+ /* unused */
+ CMD_SET_LOGICALDATA,
+ CMD_GET_LOGICALDATA,
+};
+
+/* Must match the coupling table. */
+enum couplings {
+ COUPLING_AC = 0,
+ COUPLING_DC,
+ /* TODO not used, how to enable? */
+ COUPLING_GND,
+};
+
+/* Must match the timebases table. */
+enum time_bases {
+ TIME_10us = 0,
+ TIME_20us,
+ TIME_40us,
+ TIME_100us,
+ TIME_200us,
+ TIME_400us,
+ TIME_1ms,
+ TIME_2ms,
+ TIME_4ms,
+ TIME_10ms,
+ TIME_20ms,
+ TIME_40ms,
+ TIME_100ms,
+ TIME_200ms,
+ TIME_400ms,
+};
+
+/* Must match the vdivs table. */
+enum {
+ VDIV_10MV,
+ VDIV_20MV,
+ VDIV_50MV,
+ VDIV_100MV,
+ VDIV_200MV,
+ VDIV_500MV,
+ VDIV_1V,
+ VDIV_2V,
+ VDIV_5V,
+};
+
+enum trigger_slopes {
+ SLOPE_POSITIVE = 0,
+ SLOPE_NEGATIVE,
+};
+
+enum trigger_sources {
+ TRIGGER_CH2 = 0,
+ TRIGGER_CH1,
+ TRIGGER_EXT,
+};
+
+enum capturestates {
+ CAPTURE_EMPTY = 0,
+ CAPTURE_FILLING = 1,
+ CAPTURE_READY_8BIT = 2,
+ CAPTURE_READY_9BIT = 7,
+ CAPTURE_TIMEOUT = 127,
+ CAPTURE_UNKNOWN = 255,
+};
+
+enum triggermodes {
+ TRIGGERMODE_AUTO,
+ TRIGGERMODE_NORMAL,
+ TRIGGERMODE_SINGLE,
+};
+
+enum states {
+ IDLE,
+ NEW_CAPTURE,
+ CAPTURE,
+ FETCH_DATA,
+ STOPPING,
+};
+
+struct dso_profile {
+ /* VID/PID after cold boot */
+ uint16_t orig_vid;
+ uint16_t orig_pid;
+ /* VID/PID after firmware upload */
+ uint16_t fw_vid;
+ uint16_t fw_pid;
+ char *vendor;
+ char *model;
+ const uint64_t *buffersizes;
+ char *firmware;
+};
+
+struct dev_context {
+ const struct dso_profile *profile;
+ void *cb_data;
+ uint64_t limit_frames;
+ uint64_t num_frames;
+ GSList *enabled_channels;
+ /* We can't keep track of an FX2-based device after upgrading
+ * the firmware (it re-enumerates into a different device address
+ * after the upgrade) this is like a global lock. No device will open
+ * until a proper delay after the last device was upgraded.
+ */
+ int64_t fw_updated;
+ int epin_maxpacketsize;
+ int capture_empty_count;
+ int dev_state;
+
+ /* Oscilloscope settings. */
+ int timebase;
+ gboolean ch1_enabled;
+ gboolean ch2_enabled;
+ int voltage_ch1;
+ int voltage_ch2;
+ int coupling_ch1;
+ int coupling_ch2;
+ // voltage offset (vertical position)
+ float voffset_ch1;
+ float voffset_ch2;
+ float voffset_trigger;
+ uint16_t channel_levels[2][9][2];
+ unsigned int framesize;
+ gboolean filter_ch1;
+ gboolean filter_ch2;
+ gboolean filter_trigger;
+ int triggerslope;
+ char *triggersource;
+ float triggerposition;
+ int triggermode;
+
+ /* Frame transfer */
+ unsigned int samp_received;
+ unsigned int samp_buffered;
+ unsigned int trigger_offset;
+ unsigned char *framebuf;
+};
+
+SR_PRIV int dso_open(struct sr_dev_inst *sdi);
+SR_PRIV void dso_close(struct sr_dev_inst *sdi);
+SR_PRIV int dso_enable_trigger(const struct sr_dev_inst *sdi);
+SR_PRIV int dso_force_trigger(const struct sr_dev_inst *sdi);
+SR_PRIV int dso_init(const struct sr_dev_inst *sdi);
+SR_PRIV int dso_get_capturestate(const struct sr_dev_inst *sdi,
+ uint8_t *capturestate, uint32_t *trigger_offset);
+SR_PRIV int dso_capture_start(const struct sr_dev_inst *sdi);
+SR_PRIV int dso_get_channeldata(const struct sr_dev_inst *sdi,
+ libusb_transfer_cb_fn cb);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Marc Schink <sigrok-dev@marcschink.de>
+ *
+ * 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 "protocol.h"
+
+static const int hwcaps[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_TRIGGER_MATCH,
+ SR_CONF_CAPTURE_RATIO,
+};
+
+static const int32_t trigger_matches[] = {
+ SR_TRIGGER_RISING,
+ SR_TRIGGER_FALLING,
+ SR_TRIGGER_EDGE,
+};
+
+SR_PRIV const uint64_t sl2_samplerates[NUM_SAMPLERATES] = {
+ SR_KHZ(1.25),
+ SR_KHZ(10),
+ SR_KHZ(50),
+ SR_KHZ(100),
+ SR_KHZ(250),
+ SR_KHZ(500),
+ SR_MHZ(1),
+ SR_MHZ(2.5),
+ SR_MHZ(5),
+ SR_MHZ(10),
+ SR_MHZ(20),
+};
+
+static const char *channel_names[NUM_CHANNELS + 1] = {
+ "0", "1", "2", "3",
+ NULL,
+};
+
+SR_PRIV struct sr_dev_driver ikalogic_scanalogic2_driver_info;
+static struct sr_dev_driver *di = &ikalogic_scanalogic2_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ GSList *usb_devices, *devices, *l;
+ struct drv_context *drvc;
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ struct device_info dev_info;
+ int ret, device_index, i;
+ char *fw_ver_str;
+
+ (void)options;
+
+ devices = NULL;
+ drvc = di->priv;
+ drvc->instances = NULL;
+ device_index = 0;
+
+ usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, USB_VID_PID);
+
+ if (usb_devices == NULL)
+ return NULL;
+
+ for (l = usb_devices; l; l = l->next) {
+ usb = l->data;
+
+ if ((ret = sl2_get_device_info(*usb, &dev_info)) < 0) {
+ sr_warn("Failed to get device information: %d.", ret);
+ sr_usb_dev_inst_free(usb);
+ continue;
+ }
+
+ if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
+ sr_err("Device instance malloc failed.");
+ sr_usb_dev_inst_free(usb);
+ continue;
+ }
+
+ if (!(devc->xfer_in = libusb_alloc_transfer(0))) {
+ sr_err("Transfer malloc failed.");
+ sr_usb_dev_inst_free(usb);
+ g_free(devc);
+ continue;
+ }
+
+ if (!(devc->xfer_out = libusb_alloc_transfer(0))) {
+ sr_err("Transfer malloc failed.");
+ sr_usb_dev_inst_free(usb);
+ libusb_free_transfer(devc->xfer_in);
+ g_free(devc);
+ continue;
+ }
+
+ fw_ver_str = g_strdup_printf("%u.%u", dev_info.fw_ver_major,
+ dev_info.fw_ver_minor);
+ if (!fw_ver_str) {
+ sr_err("Firmware string malloc failed.");
+ sr_usb_dev_inst_free(usb);
+ libusb_free_transfer(devc->xfer_in);
+ libusb_free_transfer(devc->xfer_out);
+ g_free(devc);
+ continue;
+ }
+
+ sdi = sr_dev_inst_new(device_index, SR_ST_INACTIVE, VENDOR_NAME,
+ MODEL_NAME, fw_ver_str);
+ g_free(fw_ver_str);
+ if (!sdi) {
+ sr_err("sr_dev_inst_new failed.");
+ sr_usb_dev_inst_free(usb);
+ libusb_free_transfer(devc->xfer_in);
+ libusb_free_transfer(devc->xfer_out);
+ g_free(devc);
+ continue;
+ }
+
+ sdi->priv = devc;
+ sdi->driver = di;
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = usb;
+
+ for (i = 0; channel_names[i]; i++) {
+ ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
+ channel_names[i]);
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ devc->channels[i] = ch;
+ }
+
+ devc->state = STATE_IDLE;
+ devc->next_state = STATE_IDLE;
+
+ /* Set default samplerate. */
+ sl2_set_samplerate(sdi, DEFAULT_SAMPLERATE);
+
+ /* Set default capture ratio. */
+ devc->capture_ratio = 0;
+
+ /* Set default after trigger delay. */
+ devc->after_trigger_delay = 0;
+
+ memset(devc->xfer_buf_in, 0, LIBUSB_CONTROL_SETUP_SIZE +
+ PACKET_LENGTH);
+ memset(devc->xfer_buf_out, 0, LIBUSB_CONTROL_SETUP_SIZE +
+ PACKET_LENGTH);
+
+ libusb_fill_control_setup(devc->xfer_buf_in,
+ USB_REQUEST_TYPE_IN, USB_HID_GET_REPORT,
+ USB_HID_REPORT_TYPE_FEATURE, USB_INTERFACE,
+ PACKET_LENGTH);
+ libusb_fill_control_setup(devc->xfer_buf_out,
+ USB_REQUEST_TYPE_OUT, USB_HID_SET_REPORT,
+ USB_HID_REPORT_TYPE_FEATURE, USB_INTERFACE,
+ PACKET_LENGTH);
+
+ devc->xfer_data_in = devc->xfer_buf_in +
+ LIBUSB_CONTROL_SETUP_SIZE;
+ devc->xfer_data_out = devc->xfer_buf_out +
+ LIBUSB_CONTROL_SETUP_SIZE;
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ device_index++;
+ }
+
+ g_slist_free(usb_devices);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static void clear_dev_context(void *priv)
+{
+ struct dev_context *devc;
+
+ devc = priv;
+
+ sr_dbg("Device context cleared.");
+
+ libusb_free_transfer(devc->xfer_in);
+ libusb_free_transfer(devc->xfer_out);
+ g_free(devc);
+}
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, &clear_dev_context);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ uint8_t buffer[PACKET_LENGTH];
+ int ret;
+
+ if (!(drvc = di->priv)) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+ devc = sdi->priv;
+
+ if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
+ return SR_ERR;
+
+ /*
+ * Determine if a kernel driver is active on this interface and, if so,
+ * detach it.
+ */
+ if (libusb_kernel_driver_active(usb->devhdl, USB_INTERFACE) == 1) {
+ ret = libusb_detach_kernel_driver(usb->devhdl, USB_INTERFACE);
+ if (ret < 0) {
+ sr_err("Failed to detach kernel driver: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ }
+
+ if ((ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE)) < 0) {
+ sr_err("Failed to claim interface: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ libusb_fill_control_transfer(devc->xfer_in, usb->devhdl,
+ devc->xfer_buf_in, sl2_receive_transfer_in,
+ sdi, USB_TIMEOUT);
+
+ libusb_fill_control_transfer(devc->xfer_out, usb->devhdl,
+ devc->xfer_buf_out, sl2_receive_transfer_out,
+ sdi, USB_TIMEOUT);
+
+ memset(buffer, 0, sizeof(buffer));
+
+ buffer[0] = CMD_RESET;
+ if ((ret = sl2_transfer_out(usb->devhdl, buffer)) != PACKET_LENGTH) {
+ sr_err("Device reset failed: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ /*
+ * Set the device to idle state. If the device is not in idle state it
+ * possibly will reset itself after a few seconds without being used
+ * and thereby close the connection.
+ */
+ buffer[0] = CMD_IDLE;
+ if ((ret = sl2_transfer_out(usb->devhdl, buffer)) != PACKET_LENGTH) {
+ sr_err("Failed to set device in idle state: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+
+ if (!usb->devhdl)
+ return SR_OK;
+
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+
+ usb->devhdl = NULL;
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return dev_clear();
+}
+
+static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ int ret;
+
+ (void)cg;
+
+ ret = SR_OK;
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_SAMPLERATE:
+ *data = g_variant_new_uint64(devc->samplerate);
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ *data = g_variant_new_uint64(devc->capture_ratio);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ uint64_t samplerate, limit_samples, capture_ratio;
+ int ret;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ ret = SR_OK;
+
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ limit_samples = g_variant_get_uint64(data);
+ ret = sl2_set_limit_samples(sdi, limit_samples);
+ break;
+ case SR_CONF_SAMPLERATE:
+ samplerate = g_variant_get_uint64(data);
+ ret = sl2_set_samplerate(sdi, samplerate);
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ capture_ratio = g_variant_get_uint64(data);
+ ret = sl2_set_capture_ratio(sdi, capture_ratio);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ GVariant *gvar, *grange[2];
+ GVariantBuilder gvb;
+ int ret;
+
+ (void)sdi;
+ (void)cg;
+
+ ret = SR_OK;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, hwcaps,
+ ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
+ sl2_samplerates, ARRAY_SIZE(sl2_samplerates),
+ sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ trigger_matches, ARRAY_SIZE(trigger_matches),
+ sizeof(int32_t));
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ grange[0] = g_variant_new_uint64(0);
+ grange[1] = g_variant_new_uint64(MAX_SAMPLES);
+ *data = g_variant_new_tuple(grange, 2);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ uint16_t trigger_bytes, tmp;
+ unsigned int i, j;
+ int ret;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ drvc = di->priv;
+
+ devc->cb_data = cb_data;
+ devc->wait_data_ready_locked = TRUE;
+ devc->stopping_in_progress = FALSE;
+ devc->transfer_error = FALSE;
+ devc->samples_processed = 0;
+ devc->channel = 0;
+ devc->sample_packet = 0;
+
+ /*
+ * The trigger must be configured first because the calculation of the
+ * pre and post trigger samples depends on a configured trigger.
+ */
+ sl2_convert_trigger(sdi);
+ sl2_calculate_trigger_samples(sdi);
+
+ trigger_bytes = devc->pre_trigger_bytes + devc->post_trigger_bytes;
+
+ /* Calculate the number of expected sample packets. */
+ devc->num_sample_packets = trigger_bytes / PACKET_NUM_SAMPLE_BYTES;
+
+ /* Round up the number of expected sample packets. */
+ if (trigger_bytes % PACKET_NUM_SAMPLE_BYTES != 0)
+ devc->num_sample_packets++;
+
+ devc->num_enabled_channels = 0;
+
+ /*
+ * Count the number of enabled channels and number them for a sequential
+ * access.
+ */
+ for (i = 0, j = 0; i < NUM_CHANNELS; i++) {
+ if (devc->channels[i]->enabled) {
+ devc->num_enabled_channels++;
+ devc->channel_map[j] = i;
+ j++;
+ }
+ }
+
+ sr_dbg("Number of enabled channels: %i.", devc->num_enabled_channels);
+
+ /* Set up the transfer buffer for the acquisition. */
+ devc->xfer_data_out[0] = CMD_SAMPLE;
+ devc->xfer_data_out[1] = 0x00;
+
+ tmp = GUINT16_TO_LE(devc->pre_trigger_bytes);
+ memcpy(devc->xfer_data_out + 2, &tmp, sizeof(tmp));
+
+ tmp = GUINT16_TO_LE(devc->post_trigger_bytes);
+ memcpy(devc->xfer_data_out + 4, &tmp, sizeof(tmp));
+
+ devc->xfer_data_out[6] = devc->samplerate_id;
+ devc->xfer_data_out[7] = devc->trigger_type;
+ devc->xfer_data_out[8] = devc->trigger_channel;
+ devc->xfer_data_out[9] = 0x00;
+
+ tmp = GUINT16_TO_LE(devc->after_trigger_delay);
+ memcpy(devc->xfer_data_out + 10, &tmp, sizeof(tmp));
+
+ if ((ret = libusb_submit_transfer(devc->xfer_out)) != 0) {
+ sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ usb_source_add(sdi->session, drvc->sr_ctx, 100,
+ ikalogic_scanalogic2_receive_data, (void *)sdi);
+
+ sr_dbg("Acquisition started successfully.");
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ devc->next_state = STATE_SAMPLE;
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ sr_dbg("Stopping acquisition.");
+
+ sdi->status = SR_ST_STOPPING;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver ikalogic_scanalogic2_driver_info = {
+ .name = "ikalogic-scanalogic2",
+ .longname = "IKALOGIC Scanalogic-2",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = 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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Marc Schink <sigrok-dev@marcschink.de>
+ *
+ * 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 "protocol.h"
+
+extern struct sr_dev_driver ikalogic_scanalogic2_driver_info;
+static struct sr_dev_driver *di = &ikalogic_scanalogic2_driver_info;
+
+extern uint64_t sl2_samplerates[NUM_SAMPLERATES];
+
+static void stop_acquisition(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc = sdi->driver->priv;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+
+ devc = sdi->priv;
+
+ /* Remove USB file descriptors from polling. */
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+
+ packet.type = SR_DF_END;
+ sr_session_send(devc->cb_data, &packet);
+
+ sdi->status = SR_ST_ACTIVE;
+}
+
+static void abort_acquisition(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc = sdi->driver->priv;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+
+ devc = sdi->priv;
+
+ /* Remove USB file descriptors from polling. */
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+
+ packet.type = SR_DF_END;
+ sr_session_send(devc->cb_data, &packet);
+
+ sdi->driver->dev_close(sdi);
+}
+
+static void buffer_sample_data(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ unsigned int offset, packet_length;
+
+ devc = sdi->priv;
+
+ if (devc->channels[devc->channel]->enabled) {
+ offset = devc->sample_packet * PACKET_NUM_SAMPLE_BYTES;
+
+ /*
+ * Determine the packet length to ensure that the last packet
+ * will not exceed the buffer size.
+ */
+ packet_length = MIN(PACKET_NUM_SAMPLE_BYTES,
+ MAX_DEV_SAMPLE_BYTES - offset);
+
+ /*
+ * Skip the first 4 bytes of the source buffer because they
+ * contain channel and packet information only.
+ */
+ memcpy(devc->sample_buffer[devc->channel] + offset,
+ devc->xfer_data_in + 4, packet_length);
+ }
+}
+
+static void process_sample_data(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ uint8_t i, j, tmp, buffer[PACKET_NUM_SAMPLES], *ptr[NUM_CHANNELS];
+ uint16_t offset, n = 0;
+ int8_t k;
+
+ devc = sdi->priv;
+ offset = devc->sample_packet * PACKET_NUM_SAMPLE_BYTES;
+
+ /*
+ * Array of pointers to the sample data of all channels up to the last
+ * enabled one for an uniform access to them. Note that the currently
+ * received samples always belong to the last enabled channel.
+ */
+ for (i = 0; i < devc->num_enabled_channels - 1; i++)
+ ptr[i] = devc->sample_buffer[devc->channel_map[i]] + offset;
+
+ /*
+ * Skip the first 4 bytes of the buffer because they contain channel
+ * and packet information only.
+ */
+ ptr[i] = devc->xfer_data_in + 4;
+
+ for (i = 0; i < PACKET_NUM_SAMPLE_BYTES; i++) {
+ /* Stop processing if all requested samples are processed. */
+ if (devc->samples_processed == devc->limit_samples)
+ break;
+
+ k = 7;
+
+ if (devc->samples_processed == 0) {
+ /*
+ * Adjust the position of the first sample to be
+ * processed because possibly more samples than
+ * necessary might have been acquired. This is because
+ * the number of acquired samples is always rounded up
+ * to a multiple of 8.
+ */
+ k = k - (devc->pre_trigger_bytes * 8) +
+ devc->pre_trigger_samples;
+
+ sr_dbg("Start processing at sample: %d.", 7 - k);
+
+ /*
+ * Send the trigger before the first sample is
+ * processed if no pre trigger samples were calculated
+ * through the capture ratio.
+ */
+ if (devc->trigger_type != TRIGGER_TYPE_NONE &&
+ devc->pre_trigger_samples == 0) {
+ packet.type = SR_DF_TRIGGER;
+ sr_session_send(devc->cb_data, &packet);
+ }
+ }
+
+ for (; k >= 0; k--) {
+ /*
+ * Stop processing if all requested samples are
+ * processed.
+ */
+ if (devc->samples_processed == devc->limit_samples)
+ break;
+
+ buffer[n] = 0;
+
+ /*
+ * Extract the current sample for each enabled channel
+ * and store them in the buffer.
+ */
+ for (j = 0; j < devc->num_enabled_channels; j++) {
+ tmp = (ptr[j][i] & (1 << k)) >> k;
+ buffer[n] |= tmp << devc->channel_map[j];
+ }
+
+ n++;
+ devc->samples_processed++;
+
+ /*
+ * Send all processed samples and the trigger if the
+ * number of processed samples reaches the calculated
+ * number of pre trigger samples.
+ */
+ if (devc->samples_processed == devc->pre_trigger_samples &&
+ devc->trigger_type != TRIGGER_TYPE_NONE) {
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = n;
+ logic.unitsize = 1;
+ logic.data = buffer;
+ sr_session_send(devc->cb_data, &packet);
+
+ packet.type = SR_DF_TRIGGER;
+ sr_session_send(devc->cb_data, &packet);
+
+ n = 0;
+ }
+ }
+ }
+
+ if (n > 0) {
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = n;
+ logic.unitsize = 1;
+ logic.data = buffer;
+ sr_session_send(devc->cb_data, &packet);
+ }
+}
+
+SR_PRIV int ikalogic_scanalogic2_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ struct timeval tv;
+ int64_t current_time, time_elapsed;
+ int ret = 0;
+
+ (void)fd;
+ (void)revents;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ drvc = di->priv;
+ current_time = g_get_monotonic_time();
+
+ if (devc->state == STATE_WAIT_DATA_READY &&
+ !devc->wait_data_ready_locked) {
+ time_elapsed = current_time - devc->wait_data_ready_time;
+
+ /*
+ * Check here for stopping in addition to the transfer
+ * callback functions to avoid waiting until the
+ * WAIT_DATA_READY_INTERVAL has expired.
+ */
+ if (sdi->status == SR_ST_STOPPING) {
+ if (!devc->stopping_in_progress) {
+ devc->next_state = STATE_RESET_AND_IDLE;
+ devc->stopping_in_progress = TRUE;
+ ret = libusb_submit_transfer(devc->xfer_in);
+ }
+ } else if (time_elapsed >= WAIT_DATA_READY_INTERVAL) {
+ devc->wait_data_ready_locked = TRUE;
+ ret = libusb_submit_transfer(devc->xfer_in);
+ }
+ }
+
+ if (ret != 0) {
+ sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
+ abort_acquisition(sdi);
+ return TRUE;
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
+ NULL);
+
+ /* Check if an error occurred on a transfer. */
+ if (devc->transfer_error)
+ abort_acquisition(sdi);
+
+ return TRUE;
+}
+
+SR_PRIV void sl2_receive_transfer_in( struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ uint8_t last_channel;
+ int ret = 0;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ sr_err("Transfer to device failed: %i.", transfer->status);
+ devc->transfer_error = TRUE;
+ return;
+ }
+
+ if (sdi->status == SR_ST_STOPPING && !devc->stopping_in_progress) {
+ devc->next_state = STATE_RESET_AND_IDLE;
+ devc->stopping_in_progress = TRUE;
+
+ if (libusb_submit_transfer(devc->xfer_in) != 0) {
+ sr_err("Submit transfer failed: %s.",
+ libusb_error_name(ret));
+ devc->transfer_error = TRUE;
+ }
+
+ return;
+ }
+
+ if (devc->state != devc->next_state)
+ sr_spew("State changed from %i to %i.",
+ devc->state, devc->next_state);
+ devc->state = devc->next_state;
+
+ if (devc->state == STATE_WAIT_DATA_READY) {
+ /* Check if the received data are a valid device status. */
+ if (devc->xfer_data_in[0] == 0x05) {
+ if (devc->xfer_data_in[1] == STATUS_WAITING_FOR_TRIGGER)
+ sr_dbg("Waiting for trigger.");
+ else if (devc->xfer_data_in[1] == STATUS_SAMPLING)
+ sr_dbg("Sampling in progress.");
+ }
+
+ /*
+ * Check if the received data are a valid device status and the
+ * sample data are ready.
+ */
+ if (devc->xfer_data_in[0] == 0x05 &&
+ devc->xfer_data_in[1] == STATUS_DATA_READY) {
+ devc->next_state = STATE_RECEIVE_DATA;
+ ret = libusb_submit_transfer(transfer);
+ } else {
+ devc->wait_data_ready_locked = FALSE;
+ devc->wait_data_ready_time = g_get_monotonic_time();
+ }
+ } else if (devc->state == STATE_RECEIVE_DATA) {
+ last_channel = devc->channel_map[devc->num_enabled_channels - 1];
+
+ if (devc->channel < last_channel) {
+ buffer_sample_data(sdi);
+ } else if (devc->channel == last_channel) {
+ process_sample_data(sdi);
+ } else {
+ /*
+ * Stop acquisition because all samples of enabled
+ * channels are processed.
+ */
+ devc->next_state = STATE_RESET_AND_IDLE;
+ }
+
+ devc->sample_packet++;
+ devc->sample_packet %= devc->num_sample_packets;
+
+ if (devc->sample_packet == 0)
+ devc->channel++;
+
+ ret = libusb_submit_transfer(transfer);
+ } else if (devc->state == STATE_RESET_AND_IDLE) {
+ /* Check if the received data are a valid device status. */
+ if (devc->xfer_data_in[0] == 0x05) {
+ if (devc->xfer_data_in[1] == STATUS_DEVICE_READY) {
+ devc->next_state = STATE_IDLE;
+ devc->xfer_data_out[0] = CMD_IDLE;
+ } else {
+ devc->next_state = STATE_WAIT_DEVICE_READY;
+ devc->xfer_data_out[0] = CMD_RESET;
+ }
+
+ ret = libusb_submit_transfer(devc->xfer_out);
+ } else {
+ /*
+ * The received device status is invalid which
+ * indicates that the device is not ready to accept
+ * commands. Request a new device status until a valid
+ * device status is received.
+ */
+ ret = libusb_submit_transfer(transfer);
+ }
+ } else if (devc->state == STATE_WAIT_DEVICE_READY) {
+ /* Check if the received data are a valid device status. */
+ if (devc->xfer_data_in[0] == 0x05) {
+ if (devc->xfer_data_in[1] == STATUS_DEVICE_READY) {
+ devc->next_state = STATE_IDLE;
+ devc->xfer_data_out[0] = CMD_IDLE;
+ } else {
+ /*
+ * The received device status is valid but the
+ * device is not ready. Probably the device did
+ * not recognize the last reset. Reset the
+ * device again.
+ */
+ devc->xfer_data_out[0] = CMD_RESET;
+ }
+
+ ret = libusb_submit_transfer(devc->xfer_out);
+ } else {
+ /*
+ * The device is not ready and therefore not able to
+ * change to the idle state. Request a new device
+ * status until the device is ready.
+ */
+ ret = libusb_submit_transfer(transfer);
+ }
+ }
+
+ if (ret != 0) {
+ sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
+ devc->transfer_error = TRUE;
+ }
+}
+
+SR_PRIV void sl2_receive_transfer_out( struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ int ret = 0;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ sr_err("Transfer to device failed: %i.", transfer->status);
+ devc->transfer_error = TRUE;
+ return;
+ }
+
+ if (sdi->status == SR_ST_STOPPING && !devc->stopping_in_progress) {
+ devc->next_state = STATE_RESET_AND_IDLE;
+ devc->stopping_in_progress = TRUE;
+
+ if (libusb_submit_transfer(devc->xfer_in) != 0) {
+ sr_err("Submit transfer failed: %s.",
+ libusb_error_name(ret));
+
+ devc->transfer_error = TRUE;
+ }
+
+ return;
+ }
+
+ if (devc->state != devc->next_state)
+ sr_spew("State changed from %i to %i.",
+ devc->state, devc->next_state);
+ devc->state = devc->next_state;
+
+ if (devc->state == STATE_IDLE) {
+ stop_acquisition(sdi);
+ } else if (devc->state == STATE_SAMPLE) {
+ devc->next_state = STATE_WAIT_DATA_READY;
+ ret = libusb_submit_transfer(devc->xfer_in);
+ } else if (devc->state == STATE_WAIT_DEVICE_READY) {
+ ret = libusb_submit_transfer(devc->xfer_in);
+ }
+
+ if (ret != 0) {
+ sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
+ devc->transfer_error = TRUE;
+ }
+}
+
+SR_PRIV int sl2_set_samplerate(const struct sr_dev_inst *sdi,
+ uint64_t samplerate)
+{
+ struct dev_context *devc;
+ unsigned int i;
+
+ devc = sdi->priv;
+
+ for (i = 0; i < NUM_SAMPLERATES; i++) {
+ if (sl2_samplerates[i] == samplerate) {
+ devc->samplerate = samplerate;
+ devc->samplerate_id = NUM_SAMPLERATES - i - 1;
+ return SR_OK;
+ }
+ }
+
+ return SR_ERR_ARG;
+}
+
+SR_PRIV int sl2_set_limit_samples(const struct sr_dev_inst *sdi,
+ uint64_t limit_samples)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ if (limit_samples == 0) {
+ sr_err("Invalid number of limit samples: %" PRIu64 ".",
+ limit_samples);
+ return SR_ERR_ARG;
+ }
+
+ if (limit_samples > MAX_SAMPLES)
+ limit_samples = MAX_SAMPLES;
+
+ sr_dbg("Limit samples set to %" PRIu64 ".", limit_samples);
+
+ devc->limit_samples = limit_samples;
+
+ return SR_OK;
+}
+
+SR_PRIV int sl2_convert_trigger(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_trigger *trigger;
+ struct sr_trigger_stage *stage;
+ struct sr_trigger_match *match;
+ const GSList *l, *m;
+ int num_triggers_anyedge;
+
+ devc = sdi->priv;
+
+ /* Disable the trigger by default. */
+ devc->trigger_channel = TRIGGER_CHANNEL_0;
+ devc->trigger_type = TRIGGER_TYPE_NONE;
+
+ if (!(trigger = sr_session_trigger_get(sdi->session)))
+ return SR_OK;
+
+ if (g_slist_length(trigger->stages) > 1) {
+ sr_err("This device only supports 1 trigger stage.");
+ return SR_ERR;
+ }
+
+ num_triggers_anyedge = 0;
+ for (l = trigger->stages; l; l = l->next) {
+ stage = l->data;
+ for (m = stage->matches; m; m = m->next) {
+ match = m->data;
+ if (!match->channel->enabled)
+ /* Ignore disabled channels with a trigger. */
+ continue;
+ devc->trigger_channel = match->channel->index + 1;
+ switch (match->match) {
+ case SR_TRIGGER_RISING:
+ devc->trigger_type = TRIGGER_TYPE_POSEDGE;
+ break;
+ case SR_TRIGGER_FALLING:
+ devc->trigger_type = TRIGGER_TYPE_NEGEDGE;
+ break;
+ case SR_TRIGGER_EDGE:
+ devc->trigger_type = TRIGGER_TYPE_ANYEDGE;
+ num_triggers_anyedge++;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Set trigger to any edge on all channels if the trigger for each
+ * channel is set to any edge.
+ */
+ if (num_triggers_anyedge == NUM_CHANNELS) {
+ devc->trigger_channel = TRIGGER_CHANNEL_ALL;
+ devc->trigger_type = TRIGGER_TYPE_ANYEDGE;
+ }
+
+ sr_dbg("Trigger set to channel 0x%02x and type 0x%02x.",
+ devc->trigger_channel, devc->trigger_type);
+
+ return SR_OK;
+}
+
+SR_PRIV int sl2_set_capture_ratio(const struct sr_dev_inst *sdi,
+ uint64_t capture_ratio)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ if (capture_ratio > 100) {
+ sr_err("Invalid capture ratio: %" PRIu64 " %%.", capture_ratio);
+ return SR_ERR_ARG;
+ }
+
+ sr_info("Capture ratio set to %" PRIu64 " %%.", capture_ratio);
+
+ devc->capture_ratio = capture_ratio;
+
+ return SR_OK;
+}
+
+SR_PRIV int sl2_set_after_trigger_delay(const struct sr_dev_inst *sdi,
+ uint64_t after_trigger_delay)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ if (after_trigger_delay > MAX_AFTER_TRIGGER_DELAY) {
+ sr_err("Invalid after trigger delay: %" PRIu64 " ms.",
+ after_trigger_delay);
+ return SR_ERR_ARG;
+ }
+
+ sr_info("After trigger delay set to %" PRIu64 " ms.",
+ after_trigger_delay);
+
+ devc->after_trigger_delay = after_trigger_delay;
+
+ return SR_OK;
+}
+
+SR_PRIV void sl2_calculate_trigger_samples(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ uint64_t pre_trigger_samples, post_trigger_samples;
+ uint16_t pre_trigger_bytes, post_trigger_bytes;
+ uint8_t cr;
+
+ devc = sdi->priv;
+ cr = devc->capture_ratio;
+
+ /* Ignore the capture ratio if no trigger is enabled. */
+ if (devc->trigger_type == TRIGGER_TYPE_NONE)
+ cr = 0;
+
+ pre_trigger_samples = (devc->limit_samples * cr) / 100;
+ post_trigger_samples = (devc->limit_samples * (100 - cr)) / 100;
+
+ /*
+ * Increase the number of post trigger samples by one to compensate the
+ * possible loss of a sample through integer rounding.
+ */
+ if (pre_trigger_samples + post_trigger_samples != devc->limit_samples)
+ post_trigger_samples++;
+
+ /*
+ * The device requires the number of samples in multiples of 8 which
+ * will also be called sample bytes in the following.
+ */
+ pre_trigger_bytes = pre_trigger_samples / 8;
+ post_trigger_bytes = post_trigger_samples / 8;
+
+ /*
+ * Round up the number of sample bytes to ensure that at least the
+ * requested number of samples will be acquired. Note that due to this
+ * rounding the buffer to store these sample bytes needs to be at least
+ * one sample byte larger than the minimal number of sample bytes
+ * needed to store the requested samples.
+ */
+ if (pre_trigger_samples % 8 != 0)
+ pre_trigger_bytes++;
+
+ if (post_trigger_samples % 8 != 0)
+ post_trigger_bytes++;
+
+ sr_info("Pre trigger samples: %" PRIu64 ".", pre_trigger_samples);
+ sr_info("Post trigger samples: %" PRIu64 ".", post_trigger_samples);
+ sr_dbg("Pre trigger sample bytes: %" PRIu16 ".", pre_trigger_bytes);
+ sr_dbg("Post trigger sample bytes: %" PRIu16 ".", post_trigger_bytes);
+
+ devc->pre_trigger_samples = pre_trigger_samples;
+ devc->pre_trigger_bytes = pre_trigger_bytes;
+ devc->post_trigger_bytes = post_trigger_bytes;
+}
+
+SR_PRIV int sl2_get_device_info(struct sr_usb_dev_inst usb,
+ struct device_info *dev_info)
+{
+ struct drv_context *drvc;
+ uint8_t buffer[PACKET_LENGTH];
+ int ret;
+
+ drvc = di->priv;
+
+ if (!dev_info)
+ return SR_ERR_ARG;
+
+ if (sr_usb_open(drvc->sr_ctx->libusb_ctx, &usb) != SR_OK)
+ return SR_ERR;
+
+ /*
+ * Determine if a kernel driver is active on this interface and, if so,
+ * detach it.
+ */
+ if (libusb_kernel_driver_active(usb.devhdl, USB_INTERFACE) == 1) {
+ ret = libusb_detach_kernel_driver(usb.devhdl,
+ USB_INTERFACE);
+
+ if (ret < 0) {
+ sr_err("Failed to detach kernel driver: %s.",
+ libusb_error_name(ret));
+ libusb_close(usb.devhdl);
+ return SR_ERR;
+ }
+ }
+
+ ret = libusb_claim_interface(usb.devhdl, USB_INTERFACE);
+
+ if (ret) {
+ sr_err("Failed to claim interface: %s.",
+ libusb_error_name(ret));
+ libusb_close(usb.devhdl);
+ return SR_ERR;
+ }
+
+ memset(buffer, 0, sizeof(buffer));
+
+ /*
+ * Reset the device to ensure it is in a proper state to request the
+ * device information.
+ */
+ buffer[0] = CMD_RESET;
+ if ((ret = sl2_transfer_out(usb.devhdl, buffer)) != PACKET_LENGTH) {
+ sr_err("Resetting of device failed: %s.",
+ libusb_error_name(ret));
+ libusb_release_interface(usb.devhdl, USB_INTERFACE);
+ libusb_close(usb.devhdl);
+ return SR_ERR;
+ }
+
+ buffer[0] = CMD_INFO;
+ if ((ret = sl2_transfer_out(usb.devhdl, buffer)) != PACKET_LENGTH) {
+ sr_err("Requesting of device information failed: %s.",
+ libusb_error_name(ret));
+ libusb_release_interface(usb.devhdl, USB_INTERFACE);
+ libusb_close(usb.devhdl);
+ return SR_ERR;
+ }
+
+ if ((ret = sl2_transfer_in(usb.devhdl, buffer)) != PACKET_LENGTH) {
+ sr_err("Receiving of device information failed: %s.",
+ libusb_error_name(ret));
+ libusb_release_interface(usb.devhdl, USB_INTERFACE);
+ libusb_close(usb.devhdl);
+ return SR_ERR;
+ }
+
+ memcpy(&(dev_info->serial), buffer + 1, sizeof(uint32_t));
+ dev_info->serial = GUINT32_FROM_LE(dev_info->serial);
+
+ dev_info->fw_ver_major = buffer[5];
+ dev_info->fw_ver_minor = buffer[6];
+
+ buffer[0] = CMD_RESET;
+ if ((ret = sl2_transfer_out(usb.devhdl, buffer)) != PACKET_LENGTH) {
+ sr_err("Device reset failed: %s.", libusb_error_name(ret));
+ libusb_release_interface(usb.devhdl, USB_INTERFACE);
+ libusb_close(usb.devhdl);
+ return SR_ERR;
+ }
+
+ /*
+ * Set the device to idle state. If the device is not in idle state it
+ * possibly will reset itself after a few seconds without being used
+ * and thereby close the connection.
+ */
+ buffer[0] = CMD_IDLE;
+ if ((ret = sl2_transfer_out(usb.devhdl, buffer)) != PACKET_LENGTH) {
+ sr_err("Failed to set device in idle state: %s.",
+ libusb_error_name(ret));
+ libusb_release_interface(usb.devhdl, USB_INTERFACE);
+ libusb_close(usb.devhdl);
+ return SR_ERR;
+ }
+
+ ret = libusb_release_interface(usb.devhdl, USB_INTERFACE);
+
+ if (ret < 0) {
+ sr_err("Failed to release interface: %s.",
+ libusb_error_name(ret));
+ libusb_close(usb.devhdl);
+ return SR_ERR;
+ }
+
+ libusb_close(usb.devhdl);
+
+ return SR_OK;
+}
+
+SR_PRIV int sl2_transfer_in(libusb_device_handle *dev_handle, uint8_t *data)
+{
+ return libusb_control_transfer(dev_handle, USB_REQUEST_TYPE_IN,
+ USB_HID_GET_REPORT, USB_HID_REPORT_TYPE_FEATURE, USB_INTERFACE,
+ (unsigned char *)data, PACKET_LENGTH, USB_TIMEOUT);
+}
+
+SR_PRIV int sl2_transfer_out(libusb_device_handle *dev_handle, uint8_t *data)
+{
+ return libusb_control_transfer(dev_handle, USB_REQUEST_TYPE_OUT,
+ USB_HID_SET_REPORT, USB_HID_REPORT_TYPE_FEATURE, USB_INTERFACE,
+ (unsigned char *)data, PACKET_LENGTH, USB_TIMEOUT);
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Marc Schink <sigrok-dev@marcschink.de>
+ *
+ * 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_IKALOGIC_SCANALOGIC2_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_IKALOGIC_SCANALOGIC2_PROTOCOL_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "ikalogic-scanalogic2"
+
+#define VENDOR_NAME "IKALOGIC"
+#define MODEL_NAME "Scanalogic-2"
+
+#define USB_VID_PID "20a0.4123"
+#define USB_INTERFACE 0
+#define USB_TIMEOUT 5000
+
+#define USB_REQUEST_TYPE_IN (LIBUSB_REQUEST_TYPE_CLASS | \
+ LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN)
+
+#define USB_REQUEST_TYPE_OUT (LIBUSB_REQUEST_TYPE_CLASS | \
+ LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT)
+
+#define USB_HID_GET_REPORT 0x01
+#define USB_HID_SET_REPORT 0x09
+#define USB_HID_REPORT_TYPE_FEATURE 0x300
+
+#define NUM_SAMPLERATES 11
+#define NUM_CHANNELS 4
+
+/*
+ * Number of sample bytes and samples the device can acquire. Note that the
+ * vendor software can acquire 32736 sample bytes only but the device is capable
+ * to acquire up to 32766 sample bytes.
+ */
+#define MAX_DEV_SAMPLE_BYTES 32766
+#define MAX_DEV_SAMPLES (MAX_INT_SAMPLE_BYTES * 8)
+
+/* Number of sample bytes and samples the driver can acquire. */
+#define MAX_SAMPLE_BYTES (MAX_DEV_SAMPLE_BYTES - 1)
+#define MAX_SAMPLES (MAX_SAMPLE_BYTES * 8)
+
+/* Maximum time that the trigger can be delayed in milliseconds. */
+#define MAX_AFTER_TRIGGER_DELAY 65000
+
+#define PACKET_LENGTH 128
+
+/* Number of sample bytes per packet where a sample byte contains 8 samples. */
+#define PACKET_NUM_SAMPLE_BYTES 124
+
+/* Number of samples per packet. */
+#define PACKET_NUM_SAMPLES (PACKET_NUM_SAMPLE_BYTES * 8)
+
+#define DEFAULT_SAMPLERATE SR_KHZ(1.25)
+
+/*
+ * Time interval between the last status of available data received and the
+ * moment when the next status request will be sent in microseconds.
+ */
+#define WAIT_DATA_READY_INTERVAL 1500000
+
+#define CMD_SAMPLE 0x01
+#define CMD_RESET 0x02
+#define CMD_IDLE 0x07
+#define CMD_INFO 0x0a
+
+#define TRIGGER_CHANNEL_ALL 0x00
+#define TRIGGER_CHANNEL_0 0x01
+#define TRIGGER_CHANNEL_1 0x02
+#define TRIGGER_CHANNEL_2 0x03
+
+#define TRIGGER_TYPE_NEGEDGE 0x00
+#define TRIGGER_TYPE_POSEDGE 0x01
+#define TRIGGER_TYPE_ANYEDGE 0x02
+#define TRIGGER_TYPE_NONE 0x03
+
+#define STATUS_DATA_READY 0x60
+#define STATUS_WAITING_FOR_TRIGGER 0x61
+#define STATUS_SAMPLING 0x62
+#define STATUS_DEVICE_READY 0x63
+
+struct device_info {
+ /* Serial number of the device. */
+ uint32_t serial;
+
+ /* Major version of the firmware. */
+ uint8_t fw_ver_major;
+
+ /* Minor version of the firmware. */
+ uint8_t fw_ver_minor;
+};
+
+enum {
+ STATE_IDLE = 0,
+ STATE_SAMPLE,
+ STATE_WAIT_DATA_READY,
+ STATE_RECEIVE_DATA,
+ STATE_RESET_AND_IDLE,
+ STATE_WAIT_DEVICE_READY
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Current selected samplerate. */
+ uint64_t samplerate;
+
+ /* Device specific identifier for the current samplerate. */
+ uint8_t samplerate_id;
+
+ /* Current sampling limit. */
+ uint64_t limit_samples;
+
+ /* Calculated number of pre-trigger samples. */
+ uint64_t pre_trigger_samples;
+
+ /* Number of pre- and post-trigger sample bytes to acquire. */
+ uint16_t pre_trigger_bytes;
+ uint16_t post_trigger_bytes;
+
+ /* Device specific settings for the trigger. */
+ uint8_t trigger_channel;
+ uint8_t trigger_type;
+
+ unsigned int capture_ratio;
+
+ /* Time that the trigger will be delayed in milliseconds. */
+ uint16_t after_trigger_delay;
+
+ void *cb_data;
+
+ /* Array to provide an index based access to all channels. */
+ const struct sr_channel *channels[NUM_CHANNELS];
+
+ struct libusb_transfer *xfer_in, *xfer_out;
+
+ /*
+ * Buffer to store setup and payload data for incoming and outgoing
+ * transfers.
+ */
+ uint8_t xfer_buf_in[LIBUSB_CONTROL_SETUP_SIZE + PACKET_LENGTH];
+ uint8_t xfer_buf_out[LIBUSB_CONTROL_SETUP_SIZE + PACKET_LENGTH];
+
+ /* Pointers to the payload of incoming and outgoing transfers. */
+ uint8_t *xfer_data_in, *xfer_data_out;
+
+ /* Current state of the state machine */
+ unsigned int state;
+
+ /* Next state of the state machine. */
+ unsigned int next_state;
+
+ /*
+ * Locking variable to ensure that no status about available data will
+ * be requested until the last status was received.
+ */
+ gboolean wait_data_ready_locked;
+
+ /*
+ * Time when the last response about the status of available data was
+ * received.
+ */
+ int64_t wait_data_ready_time;
+
+ /*
+ * Indicates that stopping of the acquisition is currently in progress.
+ */
+ gboolean stopping_in_progress;
+
+ /*
+ * Buffer which contains the samples received from the device for each
+ * channel except the last one. The samples of the last channel will be
+ * processed directly after they will be received.
+ */
+ uint8_t sample_buffer[NUM_CHANNELS - 1][MAX_DEV_SAMPLE_BYTES];
+
+ /* Expected number of sample packets for each channel. */
+ uint16_t num_sample_packets;
+
+ /* Number of samples already processed. */
+ uint64_t samples_processed;
+
+ /* Sample packet number that is currently processed. */
+ uint16_t sample_packet;
+
+ /* Channel number that is currently processed. */
+ uint8_t channel;
+
+ /* Number of enabled channels. */
+ unsigned int num_enabled_channels;
+
+ /* Array to provide a sequential access to all enabled channel indices. */
+ uint8_t channel_map[NUM_CHANNELS];
+
+ /* Indicates whether a transfer failed. */
+ gboolean transfer_error;
+};
+
+SR_PRIV int ikalogic_scanalogic2_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV void sl2_receive_transfer_in(struct libusb_transfer *transfer);
+SR_PRIV void sl2_receive_transfer_out(struct libusb_transfer *transfer);
+SR_PRIV int sl2_set_samplerate(const struct sr_dev_inst *sdi,
+ uint64_t samplerate);
+SR_PRIV int sl2_set_limit_samples(const struct sr_dev_inst *sdi,
+ uint64_t limit_samples);
+SR_PRIV int sl2_convert_trigger(const struct sr_dev_inst *sdi);
+SR_PRIV int sl2_set_capture_ratio(const struct sr_dev_inst *sdi,
+ uint64_t capture_ratio);
+SR_PRIV int sl2_set_after_trigger_delay(const struct sr_dev_inst *sdi,
+ uint64_t after_trigger_delay);
+SR_PRIV void sl2_calculate_trigger_samples(const struct sr_dev_inst *sdi);
+SR_PRIV int sl2_get_device_info(struct sr_usb_dev_inst usb,
+ struct device_info *dev_info);
+SR_PRIV int sl2_transfer_in(libusb_device_handle *dev_handle, uint8_t *data);
+SR_PRIV int sl2_transfer_out(libusb_device_handle *dev_handle, uint8_t *data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "protocol.h"
+
+#define USB_VENDOR_ID 0x0403
+#define USB_DEVICE_ID 0x6014
+#define USB_VENDOR_NAME "IKALOGIC"
+#define USB_MODEL_NAME "ScanaPLUS"
+#define USB_IPRODUCT "SCANAPLUS"
+
+#define SAMPLE_BUF_SIZE (8 * 1024 * 1024)
+
+static const int32_t hwcaps[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_CONTINUOUS, // TODO?
+};
+
+/* Channels are numbered 1-9. */
+static const char *channel_names[] = {
+ "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ NULL,
+};
+
+/* Note: The IKALOGIC ScanaPLUS always samples at 100MHz. */
+static uint64_t samplerates[1] = { SR_MHZ(100) };
+
+SR_PRIV struct sr_dev_driver ikalogic_scanaplus_driver_info;
+static struct sr_dev_driver *di = &ikalogic_scanaplus_driver_info;
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
+
+static void clear_helper(void *priv)
+{
+ struct dev_context *devc;
+
+ devc = priv;
+
+ ftdi_free(devc->ftdic);
+ g_free(devc->compressed_buf);
+ g_free(devc->sample_buf);
+}
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, clear_helper);
+}
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ GSList *devices;
+ unsigned int i;
+ int ret;
+
+ (void)options;
+
+ drvc = di->priv;
+
+ devices = NULL;
+
+ /* Allocate memory for our private device context. */
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ goto err_free_nothing;
+ }
+
+ /* Allocate memory for the incoming compressed samples. */
+ if (!(devc->compressed_buf = g_try_malloc0(COMPRESSED_BUF_SIZE))) {
+ sr_err("compressed_buf malloc failed.");
+ goto err_free_devc;
+ }
+
+ /* Allocate memory for the uncompressed samples. */
+ if (!(devc->sample_buf = g_try_malloc0(SAMPLE_BUF_SIZE))) {
+ sr_err("sample_buf malloc failed.");
+ goto err_free_compressed_buf;
+ }
+
+ /* Allocate memory for the FTDI context (ftdic) and initialize it. */
+ if (!(devc->ftdic = ftdi_new())) {
+ sr_err("Failed to initialize libftdi.");
+ goto err_free_sample_buf;
+ }
+
+ /* Check for the device and temporarily open it. */
+ ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID, USB_DEVICE_ID,
+ USB_IPRODUCT, NULL);
+ if (ret < 0) {
+ /* Log errors, except for -3 ("device not found"). */
+ if (ret != -3)
+ sr_err("Failed to open device (%d): %s", ret,
+ ftdi_get_error_string(devc->ftdic));
+ goto err_free_ftdic;
+ }
+
+ /* Register the device with libsigrok. */
+ sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING,
+ USB_VENDOR_NAME, USB_MODEL_NAME, NULL);
+ if (!sdi) {
+ sr_err("Failed to create device instance.");
+ goto err_close_ftdic;
+ }
+ sdi->driver = di;
+ sdi->priv = devc;
+
+ for (i = 0; channel_names[i]; i++) {
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
+ channel_names[i])))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ devices = g_slist_append(devices, sdi);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+
+ /* Close device. We'll reopen it again when we need it. */
+ scanaplus_close(devc);
+
+ return devices;
+
+err_close_ftdic:
+ scanaplus_close(devc);
+err_free_ftdic:
+ ftdi_free(devc->ftdic); /* NOT free() or g_free()! */
+err_free_sample_buf:
+ g_free(devc->sample_buf);
+err_free_compressed_buf:
+ g_free(devc->compressed_buf);
+err_free_devc:
+ g_free(devc);
+err_free_nothing:
+
+ return NULL;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int ret;
+
+ devc = sdi->priv;
+
+ /* Select interface A, otherwise communication will fail. */
+ ret = ftdi_set_interface(devc->ftdic, INTERFACE_A);
+ if (ret < 0) {
+ sr_err("Failed to set FTDI interface A (%d): %s", ret,
+ ftdi_get_error_string(devc->ftdic));
+ return SR_ERR;
+ }
+ sr_dbg("FTDI chip interface A set successfully.");
+
+ /* Open the device. */
+ ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID, USB_DEVICE_ID,
+ USB_IPRODUCT, NULL);
+ if (ret < 0) {
+ sr_err("Failed to open device (%d): %s", ret,
+ ftdi_get_error_string(devc->ftdic));
+ return SR_ERR;
+ }
+ sr_dbg("FTDI device opened successfully.");
+
+ /* Purge RX/TX buffers in the FTDI chip. */
+ if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
+ sr_err("Failed to purge FTDI RX/TX buffers (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ goto err_dev_open_close_ftdic;
+ }
+ sr_dbg("FTDI chip buffers purged successfully.");
+
+ /* Reset the FTDI bitmode. */
+ ret = ftdi_set_bitmode(devc->ftdic, 0xff, BITMODE_RESET);
+ if (ret < 0) {
+ sr_err("Failed to reset the FTDI chip bitmode (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ goto err_dev_open_close_ftdic;
+ }
+ sr_dbg("FTDI chip bitmode reset successfully.");
+
+ /* Set FTDI bitmode to "sync FIFO". */
+ ret = ftdi_set_bitmode(devc->ftdic, 0xff, BITMODE_SYNCFF);
+ if (ret < 0) {
+ sr_err("Failed to put FTDI chip into sync FIFO mode (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ goto err_dev_open_close_ftdic;
+ }
+ sr_dbg("FTDI chip sync FIFO mode entered successfully.");
+
+ /* Set the FTDI latency timer to 2. */
+ ret = ftdi_set_latency_timer(devc->ftdic, 2);
+ if (ret < 0) {
+ sr_err("Failed to set FTDI latency timer (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ goto err_dev_open_close_ftdic;
+ }
+ sr_dbg("FTDI chip latency timer set successfully.");
+
+ /* Set the FTDI read data chunk size to 64kB. */
+ ret = ftdi_read_data_set_chunksize(devc->ftdic, 64 * 1024);
+ if (ret < 0) {
+ sr_err("Failed to set FTDI read data chunk size (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ goto err_dev_open_close_ftdic;
+ }
+ sr_dbg("FTDI chip read data chunk size set successfully.");
+
+ /* Get the ScanaPLUS device ID from the FTDI EEPROM. */
+ if ((ret = scanaplus_get_device_id(devc)) < 0) {
+ sr_err("Failed to get ScanaPLUS device ID: %d.", ret);
+ goto err_dev_open_close_ftdic;
+ }
+ sr_dbg("Received ScanaPLUS device ID successfully: %02x %02x %02x.",
+ devc->devid[0], devc->devid[1], devc->devid[2]);
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+
+err_dev_open_close_ftdic:
+ scanaplus_close(devc);
+ return SR_ERR;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ int ret;
+ struct dev_context *devc;
+
+ ret = SR_OK;
+ devc = sdi->priv;
+
+ if (sdi->status == SR_ST_ACTIVE) {
+ sr_dbg("Status ACTIVE, closing device.");
+ ret = scanaplus_close(devc);
+ } else {
+ sr_spew("Status not ACTIVE, nothing to do.");
+ }
+
+ sdi->status = SR_ST_INACTIVE;
+
+ return ret;
+}
+
+static int cleanup(void)
+{
+ return dev_clear();
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ /* The ScanaPLUS samplerate is 100MHz and can't be changed. */
+ *data = g_variant_new_uint64(SR_MHZ(100));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ if (g_variant_get_uint64(data) != SR_MHZ(100)) {
+ sr_err("ScanaPLUS only supports samplerate = 100MHz.");
+ return SR_ERR_ARG;
+ }
+ /* Nothing to do, the ScanaPLUS samplerate is always 100MHz. */
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ if (g_variant_get_uint64(data) == 0)
+ return SR_ERR_ARG;
+ devc->limit_msec = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ if (g_variant_get_uint64(data) == 0)
+ return SR_ERR_ARG;
+ devc->limit_samples = g_variant_get_uint64(data);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ GVariant *gvar;
+ GVariantBuilder gvb;
+
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
+ samplerates, ARRAY_SIZE(samplerates),
+ sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ int ret;
+ struct dev_context *devc;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR_BUG;
+
+ if (!devc->ftdic)
+ return SR_ERR_BUG;
+
+ /* TODO: Configure channels later (thresholds etc.). */
+
+ devc->cb_data = cb_data;
+
+ /* Properly reset internal variables before every new acquisition. */
+ devc->compressed_bytes_ignored = 0;
+ devc->samples_sent = 0;
+ devc->bytes_received = 0;
+
+ if ((ret = scanaplus_init(devc)) < 0)
+ return ret;
+
+ if ((ret = scanaplus_start_acquisition(devc)) < 0)
+ return ret;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(sdi, LOG_PREFIX);
+
+ /* Hook up a dummy handler to receive data from the device. */
+ sr_session_source_add(sdi->session, -1, G_IO_IN, 0, scanaplus_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct sr_datafeed_packet packet;
+
+ (void)cb_data;
+
+ sr_dbg("Stopping acquisition.");
+ sr_session_source_remove(sdi->session, -1);
+
+ /* Send end packet to the session bus. */
+ sr_dbg("Sending SR_DF_END.");
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver ikalogic_scanaplus_driver_info = {
+ .name = "ikalogic-scanaplus",
+ .longname = "IKALOGIC ScanaPLUS",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = 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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "protocol.h"
+
+/*
+ * Logic level thresholds.
+ *
+ * For each of the two channel groups (1-4 and 5-9), the logic level
+ * threshold can be set independently.
+ *
+ * The threshold can be set to values that are usable for systems with
+ * different voltage levels, e.g. for 1.8V or 3.3V systems.
+ *
+ * The actual threshold value is always the middle of the values below.
+ * E.g. for a system voltage level of 1.8V, the threshold is at 0.9V. That
+ * means that values <= 0.9V are considered to be a logic 0/low, and
+ * values > 0.9V are considered to be a logic 1/high.
+ *
+ * - 1.2V system: threshold = 0.6V
+ * - 1.5V system: threshold = 0.75V
+ * - 1.8V system: threshold = 0.9V
+ * - 2.8V system: threshold = 1.4V
+ * - 3.3V system: threshold = 1.65V
+ */
+#define THRESHOLD_1_2V_SYSTEM 0x2e
+#define THRESHOLD_1_5V_SYSTEM 0x39
+#define THRESHOLD_1_8V_SYSTEM 0x45
+#define THRESHOLD_2_8V_SYSTEM 0x6c
+#define THRESHOLD_3_3V_SYSTEM 0x7f
+
+static int scanaplus_write(struct dev_context *devc, uint8_t *buf, int size)
+{
+ int i, bytes_written;
+ GString *s;
+
+ /* Note: Caller checks devc, devc->ftdic, buf, size. */
+
+ s = g_string_sized_new(100);
+ g_string_printf(s, "Writing %d bytes: ", size);
+ for (i = 0; i < size; i++)
+ g_string_append_printf(s, "0x%02x ", buf[i]);
+ sr_spew("%s", s->str);
+ g_string_free(s, TRUE);
+
+ bytes_written = ftdi_write_data(devc->ftdic, buf, size);
+ if (bytes_written < 0) {
+ sr_err("Failed to write FTDI data (%d): %s.",
+ bytes_written, ftdi_get_error_string(devc->ftdic));
+ } else if (bytes_written != size) {
+ sr_err("FTDI write error, only %d/%d bytes written: %s.",
+ bytes_written, size, ftdi_get_error_string(devc->ftdic));
+ }
+
+ return bytes_written;
+}
+
+SR_PRIV int scanaplus_close(struct dev_context *devc)
+{
+ int ret;
+
+ /* Note: Caller checks devc and devc->ftdic. */
+
+ if ((ret = ftdi_usb_close(devc->ftdic)) < 0) {
+ sr_err("Failed to close FTDI device (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static void scanaplus_uncompress_block(struct dev_context *devc,
+ uint64_t num_bytes)
+{
+ uint64_t i, j;
+ uint8_t num_samples, low, high;
+
+ for (i = 0; i < num_bytes; i += 2) {
+ num_samples = devc->compressed_buf[i + 0] >> 1;
+
+ low = devc->compressed_buf[i + 0] & (1 << 0);
+ high = devc->compressed_buf[i + 1];
+
+ for (j = 0; j < num_samples; j++) {
+ devc->sample_buf[devc->bytes_received++] = high;
+ devc->sample_buf[devc->bytes_received++] = low;
+ }
+ }
+}
+
+static void send_samples(struct dev_context *devc, uint64_t samples_to_send)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+
+ sr_spew("Sending %" PRIu64 " samples.", samples_to_send);
+
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = samples_to_send * 2;
+ logic.unitsize = 2; /* We need 2 bytes for 9 channels. */
+ logic.data = devc->sample_buf;
+ sr_session_send(devc->cb_data, &packet);
+
+ devc->samples_sent += samples_to_send;
+ devc->bytes_received -= samples_to_send * 2;
+}
+
+SR_PRIV int scanaplus_get_device_id(struct dev_context *devc)
+{
+ int ret;
+ uint16_t val1, val2;
+
+ /* FTDI EEPROM indices 16+17 contain the 3 device ID bytes. */
+ if ((ret = ftdi_read_eeprom_location(devc->ftdic, 16, &val1)) < 0) {
+ sr_err("Failed to read EEPROM index 16 (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ return SR_ERR;
+ }
+ if ((ret = ftdi_read_eeprom_location(devc->ftdic, 17, &val2)) < 0) {
+ sr_err("Failed to read EEPROM index 17 (%d): %s.",
+ ret, ftdi_get_error_string(devc->ftdic));
+ return SR_ERR;
+ }
+
+ /*
+ * Note: Bit 7 of the three bytes must not be used, apparently.
+ *
+ * Even though the three bits can be either 0 or 1 (we've seen both
+ * in actual ScanaPLUS devices), the device ID as sent to the FPGA
+ * has bit 7 of each byte zero'd out.
+ *
+ * It is unknown whether bit 7 of these bytes has any meaning,
+ * whether it's used somewhere, or whether it can be simply ignored.
+ */
+ devc->devid[0] = ((val1 >> 0) & 0xff) & ~(1 << 7);
+ devc->devid[1] = ((val1 >> 8) & 0xff) & ~(1 << 7);
+ devc->devid[2] = ((val2 >> 0) & 0xff) & ~(1 << 7);
+
+ return SR_OK;
+}
+
+static int scanaplus_clear_device_id(struct dev_context *devc)
+{
+ uint8_t buf[2];
+
+ buf[0] = 0x8c;
+ buf[1] = 0x00;
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ buf[0] = 0x8e;
+ buf[1] = 0x00;
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ buf[0] = 0x8f;
+ buf[1] = 0x00;
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static int scanaplus_send_device_id(struct dev_context *devc)
+{
+ uint8_t buf[2];
+
+ buf[0] = 0x8c;
+ buf[1] = devc->devid[0];
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ buf[0] = 0x8e;
+ buf[1] = devc->devid[1];
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ buf[0] = 0x8f;
+ buf[1] = devc->devid[2];
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+SR_PRIV int scanaplus_init(struct dev_context *devc)
+{
+ int i;
+ uint8_t buf[8];
+
+ buf[0] = 0x88;
+ buf[1] = 0x41;
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ buf[0] = 0x89;
+ buf[1] = 0x64;
+ buf[2] = 0x8a;
+ buf[3] = 0x64;
+ if (scanaplus_write(devc, (uint8_t *)&buf, 4) < 0)
+ return SR_ERR;
+
+ buf[0] = 0x88;
+ buf[1] = 0x41;
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ buf[0] = 0x88;
+ buf[1] = 0x40;
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ buf[0] = 0x8d;
+ buf[1] = 0x01;
+ buf[2] = 0x8d;
+ buf[3] = 0x05;
+ buf[4] = 0x8d;
+ buf[5] = 0x01;
+ buf[6] = 0x8d;
+ buf[7] = 0x02;
+ if (scanaplus_write(devc, (uint8_t *)&buf, 8) < 0)
+ return SR_ERR;
+
+ for (i = 0; i < 57; i++) {
+ buf[0] = 0x8d;
+ buf[1] = 0x06;
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ buf[0] = 0x8d;
+ buf[1] = 0x02;
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+ }
+
+ if (scanaplus_send_device_id(devc) < 0)
+ return SR_ERR;
+
+ buf[0] = 0x88;
+ buf[1] = 0x40;
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+SR_PRIV int scanaplus_start_acquisition(struct dev_context *devc)
+{
+ uint8_t buf[4];
+
+ /* Threshold and differential channel settings not yet implemented. */
+
+ buf[0] = 0x89;
+ buf[1] = 0x7f; /* Logic level threshold for channels 1-4. */
+ buf[2] = 0x8a;
+ buf[3] = 0x7f; /* Logic level threshold for channels 5-9. */
+ if (scanaplus_write(devc, (uint8_t *)&buf, 4) < 0)
+ return SR_ERR;
+
+ buf[0] = 0x88;
+ buf[1] = 0x40; /* Special config of channels 5/6 and 7/8. */
+ /* 0x40: normal, 0x50: ch56 diff, 0x48: ch78 diff, 0x58: ch5678 diff */
+ if (scanaplus_write(devc, (uint8_t *)&buf, 2) < 0)
+ return SR_ERR;
+
+ if (scanaplus_clear_device_id(devc) < 0)
+ return SR_ERR;
+
+ if (scanaplus_send_device_id(devc) < 0)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+SR_PRIV int scanaplus_receive_data(int fd, int revents, void *cb_data)
+{
+ int bytes_read;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ uint64_t max, n;
+
+ (void)fd;
+ (void)revents;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ if (!devc->ftdic)
+ return TRUE;
+
+ /* Get a block of data. */
+ bytes_read = ftdi_read_data(devc->ftdic, devc->compressed_buf,
+ COMPRESSED_BUF_SIZE);
+ if (bytes_read < 0) {
+ sr_err("Failed to read FTDI data (%d): %s.",
+ bytes_read, ftdi_get_error_string(devc->ftdic));
+ sdi->driver->dev_acquisition_stop(sdi, sdi);
+ return FALSE;
+ }
+ if (bytes_read == 0) {
+ sr_spew("Received 0 bytes, nothing to do.");
+ return TRUE;
+ }
+
+ /*
+ * After a ScanaPLUS acquisition starts, a bunch of samples will be
+ * returned as all-zero, no matter which signals are actually present
+ * on the channels. This is probably due to the FPGA reconfiguring some
+ * of its internal state/config during this time.
+ *
+ * As far as we know there is apparently no way for the PC-side to
+ * know when this "reconfiguration" starts or ends. The FTDI chip
+ * will return all-zero "dummy" samples during this time, which is
+ * indistinguishable from actual all-zero samples.
+ *
+ * We currently simply ignore the first 64kB of data after an
+ * acquisition starts. Empirical tests have shown that the
+ * "reconfigure" time is a lot less than that usually.
+ */
+ if (devc->compressed_bytes_ignored < COMPRESSED_BUF_SIZE) {
+ /* Ignore the first 64kB of data of every acquisition. */
+ sr_spew("Ignoring first 64kB chunk of data.");
+ devc->compressed_bytes_ignored += COMPRESSED_BUF_SIZE;
+ return TRUE;
+ }
+
+ /* TODO: Handle bytes_read which is not a multiple of 2? */
+ scanaplus_uncompress_block(devc, bytes_read);
+
+ n = devc->samples_sent + (devc->bytes_received / 2);
+ max = (SR_MHZ(100) / 1000) * devc->limit_msec;
+
+ if (devc->limit_samples && (n >= devc->limit_samples)) {
+ send_samples(devc, devc->limit_samples - devc->samples_sent);
+ sr_info("Requested number of samples reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ } else if (devc->limit_msec && (n >= max)) {
+ send_samples(devc, max - devc->samples_sent);
+ sr_info("Requested time limit reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ } else {
+ send_samples(devc, devc->bytes_received / 2);
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBSIGROK_HARDWARE_IKALOGIC_SCANAPLUS_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_IKALOGIC_SCANAPLUS_PROTOCOL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include <ftdi.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "ikalogic-scanaplus"
+
+#define COMPRESSED_BUF_SIZE (64 * 1024)
+
+/* Private, per-device-instance driver context. */
+struct dev_context {
+ /** FTDI device context (used by libftdi). */
+ struct ftdi_context *ftdic;
+
+ /** The current sampling limit (in ms). */
+ uint64_t limit_msec;
+
+ /** The current sampling limit (in number of samples). */
+ uint64_t limit_samples;
+
+ void *cb_data;
+
+ uint8_t *compressed_buf;
+ uint64_t compressed_bytes_ignored;
+ uint8_t *sample_buf;
+ uint64_t bytes_received;
+ uint64_t samples_sent;
+
+ /** ScanaPLUS unique device ID (3 bytes). */
+ uint8_t devid[3];
+};
+
+SR_PRIV int scanaplus_close(struct dev_context *devc);
+SR_PRIV int scanaplus_get_device_id(struct dev_context *devc);
+SR_PRIV int scanaplus_init(struct dev_context *devc);
+SR_PRIV int scanaplus_start_acquisition(struct dev_context *devc);
+SR_PRIV int scanaplus_receive_data(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <string.h>
+#include "protocol.h"
+
+#define USB_CONN "1041.8101"
+#define VENDOR "Kecheng"
+#define USB_INTERFACE 0
+
+static const int32_t hwcaps[] = {
+ SR_CONF_SOUNDLEVELMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_CONTINUOUS,
+ SR_CONF_DATALOG,
+ SR_CONF_SPL_WEIGHT_FREQ,
+ SR_CONF_SPL_WEIGHT_TIME,
+ SR_CONF_DATA_SOURCE,
+};
+
+SR_PRIV const uint64_t kecheng_kc_330b_sample_intervals[][2] = {
+ { 1, 8 },
+ { 1, 2 },
+ { 1, 1 },
+ { 2, 1 },
+ { 5, 1 },
+ { 10, 1 },
+ { 60, 1 },
+};
+
+static const char *weight_freq[] = {
+ "A",
+ "C",
+};
+
+static const char *weight_time[] = {
+ "F",
+ "S",
+};
+
+static const char *data_sources[] = {
+ "Live",
+ "Memory",
+};
+
+SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info;
+static struct sr_dev_driver *di = &kecheng_kc_330b_driver_info;
+
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static int scan_kecheng(struct sr_usb_dev_inst *usb, char **model)
+{
+ struct drv_context *drvc;
+ int len, ret;
+ unsigned char cmd, buf[32];
+
+ drvc = di->priv;
+ if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
+ return SR_ERR;
+
+ cmd = CMD_IDENTIFY;
+ ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, &cmd, 1, &len, 5);
+ if (ret != 0) {
+ libusb_close(usb->devhdl);
+ sr_dbg("Failed to send Identify command: %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ ret = libusb_bulk_transfer(usb->devhdl, EP_IN, buf, 32, &len, 10);
+ if (ret != 0) {
+ libusb_close(usb->devhdl);
+ sr_dbg("Failed to receive response: %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+
+ if (len < 2 || buf[0] != (CMD_IDENTIFY | 0x80) || buf[1] > 30) {
+ sr_dbg("Invalid response to Identify command");
+ return SR_ERR;
+ }
+
+ buf[buf[1] + 2] = '\x0';
+ *model = g_strndup((const gchar *)buf + 2, 30);
+
+ return SR_OK;
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ GSList *usb_devices, *devices, *l;
+ char *model;
+
+ (void)options;
+
+ drvc = di->priv;
+ drvc->instances = NULL;
+
+ devices = NULL;
+ if ((usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, USB_CONN))) {
+ /* We have a list of sr_usb_dev_inst matching the connection
+ * string. Wrap them in sr_dev_inst and we're done. */
+ for (l = usb_devices; l; l = l->next) {
+ if (scan_kecheng(l->data, &model) != SR_OK)
+ continue;
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR,
+ model, NULL)))
+ return NULL;
+ g_free(model);
+ sdi->driver = di;
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = l->data;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "SPL")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
+ sr_dbg("Device context malloc failed.");
+ return NULL;
+ }
+ sdi->priv = devc;
+ devc->limit_samples = 0;
+ /* The protocol provides no way to read the current
+ * settings, so we'll enforce these. */
+ devc->sample_interval = DEFAULT_SAMPLE_INTERVAL;
+ devc->alarm_low = DEFAULT_ALARM_LOW;
+ devc->alarm_high = DEFAULT_ALARM_HIGH;
+ devc->mqflags = DEFAULT_WEIGHT_TIME | DEFAULT_WEIGHT_FREQ;
+ devc->data_source = DEFAULT_DATA_SOURCE;
+ devc->config_dirty = FALSE;
+
+ /* TODO: Set date/time? */
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ }
+ g_slist_free(usb_devices);
+ } else
+ g_slist_free_full(usb_devices, g_free);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ int ret;
+
+ if (!(drvc = di->priv)) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+
+ if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_set_configuration(usb->devhdl, 1))) {
+ sr_err("Failed to set configuration: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if ((ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE))) {
+ sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sdi->status = SR_ST_ACTIVE;
+
+ return ret;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+
+ if (!usb->devhdl)
+ /* Nothing to do. */
+ return SR_OK;
+
+ /* This allows a frontend to configure the device without ever
+ * doing an acquisition step. */
+ devc = sdi->priv;
+ if (!devc->config_dirty)
+ kecheng_kc_330b_configure(sdi);
+
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ int ret;
+ struct drv_context *drvc;
+
+ if (!(drvc = di->priv))
+ /* Can get called on an unused driver, doesn't matter. */
+ return SR_OK;
+
+
+ ret = std_dev_clear(di, NULL);
+ g_free(drvc);
+ di->priv = NULL;
+
+ return ret;
+}
+
+static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ GVariant *rational[2];
+ const uint64_t *si;
+
+ (void)cg;
+
+ devc = sdi->priv;
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_SAMPLE_INTERVAL:
+ si = kecheng_kc_330b_sample_intervals[devc->sample_interval];
+ rational[0] = g_variant_new_uint64(si[0]);
+ rational[1] = g_variant_new_uint64(si[1]);
+ *data = g_variant_new_tuple(rational, 2);
+ break;
+ case SR_CONF_DATALOG:
+ /* There really isn't a way to be sure the device is logging. */
+ return SR_ERR_NA;
+ break;
+ case SR_CONF_SPL_WEIGHT_FREQ:
+ if (devc->mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A)
+ *data = g_variant_new_string("A");
+ else
+ *data = g_variant_new_string("C");
+ break;
+ case SR_CONF_SPL_WEIGHT_TIME:
+ if (devc->mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_F)
+ *data = g_variant_new_string("F");
+ else
+ *data = g_variant_new_string("S");
+ break;
+ case SR_CONF_DATA_SOURCE:
+ if (devc->data_source == DATA_SOURCE_LIVE)
+ *data = g_variant_new_string("Live");
+ else
+ *data = g_variant_new_string("Memory");
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ uint64_t p, q;
+ unsigned int i;
+ int tmp, ret;
+ const char *tmp_str;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ devc = sdi->priv;
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ case SR_CONF_SAMPLE_INTERVAL:
+ g_variant_get(data, "(tt)", &p, &q);
+ for (i = 0; i < ARRAY_SIZE(kecheng_kc_330b_sample_intervals); i++) {
+ if (kecheng_kc_330b_sample_intervals[i][0] != p || kecheng_kc_330b_sample_intervals[i][1] != q)
+ continue;
+ devc->sample_interval = i;
+ devc->config_dirty = TRUE;
+ break;
+ }
+ if (i == ARRAY_SIZE(kecheng_kc_330b_sample_intervals))
+ ret = SR_ERR_ARG;
+ break;
+ case SR_CONF_SPL_WEIGHT_FREQ:
+ tmp_str = g_variant_get_string(data, NULL);
+ if (!strcmp(tmp_str, "A"))
+ tmp = SR_MQFLAG_SPL_FREQ_WEIGHT_A;
+ else if (!strcmp(tmp_str, "C"))
+ tmp = SR_MQFLAG_SPL_FREQ_WEIGHT_C;
+ else
+ return SR_ERR_ARG;
+ devc->mqflags &= ~(SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
+ devc->mqflags |= tmp;
+ devc->config_dirty = TRUE;
+ break;
+ case SR_CONF_SPL_WEIGHT_TIME:
+ tmp_str = g_variant_get_string(data, NULL);
+ if (!strcmp(tmp_str, "F"))
+ tmp = SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ else if (!strcmp(tmp_str, "S"))
+ tmp = SR_MQFLAG_SPL_TIME_WEIGHT_S;
+ else
+ return SR_ERR_ARG;
+ devc->mqflags &= ~(SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
+ devc->mqflags |= tmp;
+ devc->config_dirty = TRUE;
+ break;
+ case SR_CONF_DATA_SOURCE:
+ tmp_str = g_variant_get_string(data, NULL);
+ if (!strcmp(tmp_str, "Live"))
+ devc->data_source = DATA_SOURCE_LIVE;
+ else if (!strcmp(tmp_str, "Memory"))
+ devc->data_source = DATA_SOURCE_MEMORY;
+ else
+ return SR_ERR;
+ devc->config_dirty = TRUE;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ GVariant *tuple, *rational[2];
+ GVariantBuilder gvb;
+ unsigned int i;
+
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLE_INTERVAL:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < ARRAY_SIZE(kecheng_kc_330b_sample_intervals); i++) {
+ rational[0] = g_variant_new_uint64(kecheng_kc_330b_sample_intervals[i][0]);
+ rational[1] = g_variant_new_uint64(kecheng_kc_330b_sample_intervals[i][1]);
+ tuple = g_variant_new_tuple(rational, 2);
+ g_variant_builder_add_value(&gvb, tuple);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_SPL_WEIGHT_FREQ:
+ *data = g_variant_new_strv(weight_freq, ARRAY_SIZE(weight_freq));
+ break;
+ case SR_CONF_SPL_WEIGHT_TIME:
+ *data = g_variant_new_strv(weight_time, ARRAY_SIZE(weight_time));
+ break;
+ case SR_CONF_DATA_SOURCE:
+ *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_meta meta;
+ struct sr_config *src;
+ struct sr_usb_dev_inst *usb;
+ GVariant *gvar, *rational[2];
+ const uint64_t *si;
+ int stored_mqflags, req_len, buf_len, len, ret;
+ unsigned char buf[9];
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ drvc = di->priv;
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ devc->cb_data = cb_data;
+ devc->num_samples = 0;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ if (devc->data_source == DATA_SOURCE_LIVE) {
+ /* Force configuration. */
+ kecheng_kc_330b_configure(sdi);
+
+ if (kecheng_kc_330b_status_get(sdi, &ret) != SR_OK)
+ return SR_ERR;
+ if (ret != DEVICE_ACTIVE) {
+ sr_err("Device is inactive");
+ /* Still continue though, since the device will
+ * just return 30.0 until the user hits the button
+ * on the device -- and then start feeding good
+ * samples back. */
+ }
+ } else {
+ if (kecheng_kc_330b_log_info_get(sdi, buf) != SR_OK)
+ return SR_ERR;
+ stored_mqflags = buf[4] ? SR_MQFLAG_SPL_TIME_WEIGHT_S : SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ stored_mqflags |= buf[5] ? SR_MQFLAG_SPL_FREQ_WEIGHT_C : SR_MQFLAG_SPL_FREQ_WEIGHT_A;
+ devc->stored_samples = (buf[7] << 8) | buf[8];
+ if (devc->stored_samples == 0) {
+ /* Notify frontend of empty log by sending start/end packets. */
+ packet.type = SR_DF_END;
+ sr_session_send(cb_data, &packet);
+ return SR_OK;
+ }
+
+ if (devc->limit_samples && devc->limit_samples < devc->stored_samples)
+ devc->stored_samples = devc->limit_samples;
+
+ si = kecheng_kc_330b_sample_intervals[buf[1]];
+ rational[0] = g_variant_new_uint64(si[0]);
+ rational[1] = g_variant_new_uint64(si[1]);
+ gvar = g_variant_new_tuple(rational, 2);
+ src = sr_config_new(SR_CONF_SAMPLE_INTERVAL, gvar);
+ packet.type = SR_DF_META;
+ packet.payload = &meta;
+ meta.config = g_slist_append(NULL, src);
+ sr_session_send(devc->cb_data, &packet);
+ g_free(src);
+ }
+
+ if (!(devc->xfer = libusb_alloc_transfer(0)))
+ return SR_ERR;
+
+ usb_source_add(sdi->session, drvc->sr_ctx, 10,
+ kecheng_kc_330b_handle_events, (void *)sdi);
+
+ if (devc->data_source == DATA_SOURCE_LIVE) {
+ buf[0] = CMD_GET_LIVE_SPL;
+ buf_len = 1;
+ devc->state = LIVE_SPL_WAIT;
+ devc->last_live_request = g_get_monotonic_time() / 1000;
+ req_len = 3;
+ } else {
+ buf[0] = CMD_GET_LOG_DATA;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf_len = 4;
+ devc->state = LOG_DATA_WAIT;
+ if (devc->stored_samples < 63)
+ buf[3] = devc->stored_samples;
+ else
+ buf[3] = 63;
+ /* Command ack byte + 2 bytes per sample. */
+ req_len = 1 + buf[3] * 2;
+ }
+
+ ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, buf_len, &len, 5);
+ if (ret != 0 || len != 1) {
+ sr_dbg("Failed to start acquisition: %s", libusb_error_name(ret));
+ libusb_free_transfer(devc->xfer);
+ return SR_ERR;
+ }
+
+ libusb_fill_bulk_transfer(devc->xfer, usb->devhdl, EP_IN, devc->buf,
+ req_len, kecheng_kc_330b_receive_transfer, (void *)sdi, 15);
+ if (libusb_submit_transfer(devc->xfer) != 0) {
+ libusb_free_transfer(devc->xfer);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ /* Signal USB transfer handler to clean up and stop. */
+ sdi->status = SR_ST_STOPPING;
+
+ devc = sdi->priv;
+ if (devc->data_source == DATA_SOURCE_MEMORY && devc->config_dirty) {
+ /* The protocol doesn't have a command to clear stored data;
+ * it clears it whenever new configuration is set. That means
+ * we can't just configure the device any time we want when
+ * it's in DATA_SOURCE_MEMORY mode. The only safe time to do
+ * it is now, when we're sure we've pulled in all the stored
+ * data. */
+ kecheng_kc_330b_configure(sdi);
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info = {
+ .name = "kecheng-kc-330b",
+ .longname = "Kecheng KC-330B",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <string.h>
+#include "protocol.h"
+
+extern struct sr_dev_driver kecheng_kc_330b_driver_info;
+static struct sr_dev_driver *di = &kecheng_kc_330b_driver_info;
+extern const uint64_t kecheng_kc_330b_sample_intervals[][2];
+
+SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_dev_inst *sdi;
+ struct sr_usb_dev_inst *usb;
+ struct timeval tv;
+ const uint64_t *intv_entry;
+ gint64 now, interval;
+ int offset, len, ret;
+ unsigned char buf[4];
+
+ (void)fd;
+ (void)revents;
+
+ drvc = di->priv;
+ sdi = cb_data;
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ memset(&tv, 0, sizeof(struct timeval));
+ libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
+ NULL);
+
+ if (sdi->status == SR_ST_STOPPING) {
+ libusb_free_transfer(devc->xfer);
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+ packet.type = SR_DF_END;
+ sr_session_send(cb_data, &packet);
+ sdi->status = SR_ST_ACTIVE;
+ return TRUE;
+ }
+
+ if (devc->state == LIVE_SPL_IDLE) {
+ /* Request samples at the interval rate. */
+ now = g_get_monotonic_time() / 1000;
+ intv_entry = kecheng_kc_330b_sample_intervals[devc->sample_interval];
+ interval = intv_entry[0] * 1000 / intv_entry[1];
+ if (now - devc->last_live_request > interval) {
+ buf[0] = CMD_GET_LIVE_SPL;
+ ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 1, &len, 5);
+ if (ret != 0 || len != 1) {
+ sr_dbg("Failed to request new acquisition: %s",
+ libusb_error_name(ret));
+ sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+ devc->cb_data);
+ return TRUE;
+ }
+ libusb_submit_transfer(devc->xfer);
+ devc->last_live_request = now;
+ devc->state = LIVE_SPL_WAIT;
+ }
+ } else if (devc->state == LIVE_SPL_IDLE) {
+ buf[0] = CMD_GET_LOG_DATA;
+ offset = devc->num_samples / 63;
+ buf[1] = (offset >> 8) & 0xff;
+ buf[2] = offset & 0xff;
+ if (devc->stored_samples - devc->num_samples > 63)
+ buf[3] = 63;
+ else
+ /* Last chunk. */
+ buf[3] = devc->stored_samples - devc->num_samples;
+ ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 4, &len, 5);
+ if (ret != 0 || len != 4) {
+ sr_dbg("Failed to request next chunk: %s",
+ libusb_error_name(ret));
+ sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+ devc->cb_data);
+ return TRUE;
+ }
+ libusb_submit_transfer(devc->xfer);
+ devc->state = LIVE_SPL_WAIT;
+ }
+
+ return TRUE;
+}
+
+static void send_data(const struct sr_dev_inst *sdi, void *buf, unsigned int buf_len)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+
+ devc = sdi->priv;
+
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+ analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
+ analog.mqflags = devc->mqflags;
+ analog.unit = SR_UNIT_DECIBEL_SPL;
+ analog.channels = sdi->channels;
+ analog.num_samples = buf_len;
+ analog.data = buf;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+
+}
+
+SR_PRIV void kecheng_kc_330b_receive_transfer(struct libusb_transfer *transfer)
+{
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ float fvalue[64];
+ int packet_has_error, num_samples, i;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+
+ packet_has_error = FALSE;
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ /* USB device was unplugged. */
+ sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+ devc->cb_data);
+ return;
+ case LIBUSB_TRANSFER_COMPLETED:
+ case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though */
+ break;
+ default:
+ packet_has_error = TRUE;
+ break;
+ }
+
+ if (packet_has_error)
+ return;
+
+ if (devc->state == LIVE_SPL_WAIT) {
+ if (transfer->actual_length != 3 || transfer->buffer[0] != 0x88) {
+ sr_dbg("Received invalid SPL packet.");
+ } else {
+ fvalue[0] = ((transfer->buffer[1] << 8) + transfer->buffer[2]) / 10.0;
+ send_data(sdi, fvalue, 1);
+ devc->num_samples++;
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+ devc->cb_data);
+ } else {
+ /* let USB event handler fire off another
+ * request when the time is right. */
+ devc->state = LIVE_SPL_IDLE;
+ }
+ }
+ } else if (devc->state == LOG_DATA_WAIT) {
+ if (transfer->actual_length < 1 || !(transfer->actual_length & 0x01)) {
+ sr_dbg("Received invalid stored SPL packet.");
+ } else {
+ num_samples = (transfer->actual_length - 1) / 2;
+ for (i = 0; i < num_samples; i++) {
+ fvalue[i] = transfer->buffer[1 + i * 2] << 8;
+ fvalue[i] += transfer->buffer[1 + i * 2 + 1];
+ fvalue[i] /= 10.0;
+ }
+ send_data(sdi, fvalue, 1);
+ devc->num_samples += num_samples;
+ if (devc->num_samples >= devc->stored_samples) {
+ sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+ devc->cb_data);
+ } else {
+ /* let USB event handler fire off another
+ * request when the time is right. */
+ devc->state = LOG_DATA_IDLE;
+ }
+ }
+ }
+
+}
+
+SR_PRIV int kecheng_kc_330b_configure(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int len, ret;
+ unsigned char buf[7];
+
+ sr_dbg("Configuring device.");
+
+ usb = sdi->conn;
+ devc = sdi->priv;
+
+ buf[0] = CMD_CONFIGURE;
+ buf[1] = devc->sample_interval;
+ buf[2] = devc->alarm_low;
+ buf[3] = devc->alarm_high;
+ buf[4] = devc->mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_F ? 0 : 1;
+ buf[5] = devc->mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A ? 0 : 1;
+ buf[6] = devc->data_source;
+ ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 7, &len, 5);
+ if (ret != 0 || len != 7) {
+ sr_dbg("Failed to configure device: %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ /* The configure command ack takes about 32ms to come in. */
+ ret = libusb_bulk_transfer(usb->devhdl, EP_IN, buf, 1, &len, 40);
+ if (ret != 0 || len != 1) {
+ sr_dbg("Failed to configure device (no ack): %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if (buf[0] != (CMD_CONFIGURE | 0x80)) {
+ sr_dbg("Failed to configure device: invalid response 0x%2.x", buf[0]);
+ return SR_ERR;
+ }
+
+ devc->config_dirty = FALSE;
+
+ return SR_OK;
+}
+
+SR_PRIV int kecheng_kc_330b_set_date_time(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ GDateTime *dt;
+ int len, ret;
+ unsigned char buf[7];
+
+ sr_dbg("Setting device date/time.");
+
+ usb = sdi->conn;
+
+ dt = g_date_time_new_now_local();
+ buf[0] = CMD_SET_DATE_TIME;
+ buf[1] = g_date_time_get_year(dt) - 2000;
+ buf[2] = g_date_time_get_month(dt);
+ buf[3] = g_date_time_get_day_of_month(dt);
+ buf[4] = g_date_time_get_hour(dt);
+ buf[5] = g_date_time_get_minute(dt);
+ buf[6] = g_date_time_get_second(dt);
+ g_date_time_unref(dt);
+ ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 7, &len, 5);
+ if (ret != 0 || len != 7) {
+ sr_dbg("Failed to set date/time: %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ ret = libusb_bulk_transfer(usb->devhdl, EP_IN, buf, 1, &len, 10);
+ if (ret != 0 || len != 1) {
+ sr_dbg("Failed to set date/time (no ack): %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if (buf[0] != (CMD_SET_DATE_TIME | 0x80)) {
+ sr_dbg("Failed to set date/time: invalid response 0x%2.x", buf[0]);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int kecheng_kc_330b_status_get(const struct sr_dev_inst *sdi,
+ int *status)
+{
+ struct sr_usb_dev_inst *usb;
+ int len, ret;
+ unsigned char buf;
+
+ sr_dbg("Getting device status.");
+
+ usb = sdi->conn;
+ buf = CMD_GET_STATUS;
+ ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, &buf, 1, &len, 5);
+ if (ret != 0 || len != 1) {
+ sr_dbg("Failed to get status: %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ ret = libusb_bulk_transfer(usb->devhdl, EP_IN, &buf, 1, &len, 10);
+ if (ret != 0 || len != 1) {
+ sr_dbg("Failed to get status (no ack): %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ /* Need either 0x84 or 0xa4. */
+ if (buf != (CMD_GET_STATUS | 0x80) && buf != (CMD_GET_STATUS | 0xa0)) {
+ sr_dbg("Failed to get status: invalid response 0x%2.x", buf);
+ return SR_ERR;
+ }
+
+ if (buf & 0x20)
+ *status = DEVICE_INACTIVE;
+ else
+ *status = DEVICE_ACTIVE;
+
+ return SR_OK;
+}
+
+SR_PRIV int kecheng_kc_330b_log_info_get(const struct sr_dev_inst *sdi,
+ unsigned char *buf)
+{
+ struct sr_usb_dev_inst *usb;
+ int len, ret;
+
+ sr_dbg("Getting logging info.");
+
+ usb = sdi->conn;
+ buf[0] = CMD_GET_LOG_INFO;
+ ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 1, &len, 5);
+ if (ret != 0 || len != 1) {
+ sr_dbg("Failed to get status: %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ ret = libusb_bulk_transfer(usb->devhdl, EP_IN, buf, 9, &len, 10);
+ if (ret != 0 || len != 9) {
+ sr_dbg("Failed to get status (no ack): %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if (buf[0] != (CMD_GET_LOG_INFO | 0x80) || buf[1] > 6) {
+ sr_dbg("Failed to get log info: invalid response 0x%2.x", buf[0]);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_KECHENG_KC_330B_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_KECHENG_KC_330B_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "kecheng-kc-330b"
+
+#define EP_IN 0x80 | 1
+#define EP_OUT 2
+
+/* 500ms */
+#define DEFAULT_SAMPLE_INTERVAL 0
+#define DEFAULT_ALARM_LOW 40
+#define DEFAULT_ALARM_HIGH 120
+#define DEFAULT_WEIGHT_TIME SR_MQFLAG_SPL_TIME_WEIGHT_F
+#define DEFAULT_WEIGHT_FREQ SR_MQFLAG_SPL_FREQ_WEIGHT_A
+/* Live */
+#define DEFAULT_DATA_SOURCE DATA_SOURCE_LIVE
+
+enum {
+ LIVE_SPL_IDLE,
+ LIVE_SPL_WAIT,
+ LOG_DATA_IDLE,
+ LOG_DATA_WAIT,
+};
+
+enum {
+ CMD_CONFIGURE = 0x01,
+ CMD_IDENTIFY = 0x02,
+ CMD_SET_DATE_TIME = 0x03,
+ CMD_GET_STATUS = 0x04,
+ CMD_GET_LOG_INFO = 0x05,
+ CMD_GET_LOG_DATA = 0x07,
+ CMD_GET_LIVE_SPL = 0x08,
+};
+
+enum {
+ DATA_SOURCE_LIVE,
+ DATA_SOURCE_MEMORY,
+};
+
+enum {
+ DEVICE_ACTIVE,
+ DEVICE_INACTIVE,
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Acquisition settings */
+ uint64_t limit_samples;
+ int sample_interval;
+ int alarm_low;
+ int alarm_high;
+ uint64_t mqflags;
+ int data_source;
+
+ /* Operational state */
+ int state;
+ gboolean config_dirty;
+ uint64_t num_samples;
+ uint64_t stored_samples;
+ void *cb_data;
+ struct libusb_transfer *xfer;
+ unsigned char buf[128];
+
+ /* Temporary state across callbacks */
+ gint64 last_live_request;
+
+};
+
+SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data);
+SR_PRIV void kecheng_kc_330b_receive_transfer(struct libusb_transfer *transfer);
+SR_PRIV int kecheng_kc_330b_configure(const struct sr_dev_inst *sdi);
+SR_PRIV int kecheng_kc_330b_set_date_time(struct sr_dev_inst *sdi);
+SR_PRIV int kecheng_kc_330b_recording_get(const struct sr_dev_inst *sdi,
+ gboolean *tmp);
+SR_PRIV int kecheng_kc_330b_status_get(const struct sr_dev_inst *sdi,
+ int *status);
+SR_PRIV int kecheng_kc_330b_log_info_get(const struct sr_dev_inst *sdi,
+ unsigned char *buf);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <glib.h>
+#include <libusb.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info;
+static struct sr_dev_driver *di = &lascar_el_usb_driver_info;
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_THERMOMETER,
+ SR_CONF_HYGROMETER,
+ SR_CONF_DATALOG,
+ SR_CONF_LIMIT_SAMPLES,
+};
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct sr_dev_inst *sdi;
+ struct sr_usb_dev_inst *usb;
+ struct sr_config *src;
+ GSList *usb_devices, *devices, *l;
+ const char *conn;
+
+ drvc = di->priv;
+
+ 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;
+
+ devices = NULL;
+ if ((usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn))) {
+ /* We have a list of sr_usb_dev_inst matching the connection
+ * string. Wrap them in sr_dev_inst and we're done. */
+ for (l = usb_devices; l; l = l->next) {
+ usb = l->data;
+ if (!(sdi = lascar_scan(usb->bus, usb->address))) {
+ /* Not a Lascar EL-USB. */
+ g_free(usb);
+ continue;
+ }
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = usb;
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ }
+ g_slist_free(usb_devices);
+ } else
+ g_slist_free_full(usb_devices, g_free);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ int ret;
+
+ if (!(drvc = di->priv)) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+
+ if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_claim_interface(usb->devhdl, LASCAR_INTERFACE))) {
+ sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sdi->status = SR_ST_ACTIVE;
+
+ return ret;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+
+ if (!usb->devhdl)
+ /* Nothing to do. */
+ return SR_OK;
+
+ libusb_release_interface(usb->devhdl, LASCAR_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ int ret;
+ struct drv_context *drvc;
+
+ if (!(drvc = di->priv))
+ /* Can get called on an unused driver, doesn't matter. */
+ return SR_OK;
+
+
+ ret = std_dev_clear(di, NULL);
+ g_free(drvc);
+ di->priv = NULL;
+
+ return ret;
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int ret;
+ char str[128];
+
+ (void)cg;
+
+ devc = sdi->priv;
+ switch (id) {
+ case SR_CONF_CONN:
+ if (!sdi || !sdi->conn)
+ return SR_ERR_ARG;
+ usb = sdi->conn;
+ snprintf(str, 128, "%d.%d", usb->bus, usb->address);
+ *data = g_variant_new_string(str);
+ break;
+ case SR_CONF_DATALOG:
+ if (!sdi)
+ return SR_ERR_ARG;
+ if ((ret = lascar_is_logging(sdi)) == -1)
+ return SR_ERR;
+ *data = g_variant_new_boolean(ret ? TRUE : FALSE);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ int ret;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ devc = sdi->priv;
+ ret = SR_OK;
+ switch (id) {
+ case SR_CONF_DATALOG:
+ if (g_variant_get_boolean(data)) {
+ /* Start logging. */
+ ret = lascar_start_logging(sdi);
+ } else {
+ /* Stop logging. */
+ ret = lascar_stop_logging(sdi);
+ }
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static void mark_xfer(struct libusb_transfer *xfer)
+{
+
+ if (xfer->status == LIBUSB_TRANSFER_COMPLETED)
+ xfer->user_data = GINT_TO_POINTER(1);
+ else
+ xfer->user_data = GINT_TO_POINTER(-1);
+
+}
+
+/* The Lascar software, in its infinite ignorance, reads a set of four
+ * bytes from the device config struct and interprets it as a float.
+ * That only works because they only use windows, and only on x86. However
+ * we may be running on any architecture, any operating system. So we have
+ * to convert these four bytes as the Lascar software would on windows/x86,
+ * to the local representation of a float.
+ * The source format is little-endian, with IEEE 754-2008 BINARY32 encoding. */
+static float binary32_le_to_float(unsigned char *buf)
+{
+ GFloatIEEE754 f;
+
+ f.v_float = 0;
+ f.mpn.sign = (buf[3] & 0x80) ? 1 : 0;
+ f.mpn.biased_exponent = (buf[3] << 1) | (buf[2] >> 7);
+ f.mpn.mantissa = buf[0] | (buf[1] << 8) | ((buf[2] & 0x7f) << 16);
+
+ return f.v_float;
+}
+
+static int lascar_proc_config(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int dummy, ret;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ if (lascar_get_config(usb->devhdl, devc->config, &dummy) != SR_OK)
+ return SR_ERR;
+
+ ret = SR_OK;
+ switch (devc->profile->logformat) {
+ case LOG_TEMP_RH:
+ devc->sample_size = 2;
+ devc->temp_unit = devc->config[0x2e] | (devc->config[0x2f] << 8);
+ if (devc->temp_unit != 0 && devc->temp_unit != 1) {
+ sr_dbg("invalid temperature unit %d", devc->temp_unit);
+ /* Default to Celcius, we're all adults here. */
+ devc->temp_unit = 0;
+ } else
+ sr_dbg("temperature unit is %s", devc->temp_unit
+ ? "Fahrenheit" : "Celcius");
+ break;
+ case LOG_CO:
+ devc->sample_size = 2;
+ devc->co_high = binary32_le_to_float(devc->config + 0x24);
+ devc->co_low = binary32_le_to_float(devc->config + 0x28);
+ sr_dbg("EL-USB-CO calibration high %f low %f", devc->co_high,
+ devc->co_low);
+ break;
+ default:
+ ret = SR_ERR_ARG;
+ }
+ devc->logged_samples = devc->config[0x1e] | (devc->config[0x1f] << 8);
+ sr_dbg("device log contains %d samples.", devc->logged_samples);
+
+ return ret;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_meta meta;
+ struct sr_config *src;
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_transfer *xfer_in, *xfer_out;
+ struct timeval tv;
+ uint64_t interval;
+ int ret;
+ unsigned char cmd[3], resp[4], *buf;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ drvc = di->priv;
+ devc = sdi->priv;
+ usb = sdi->conn;
+ devc->cb_data = cb_data;
+
+ if (lascar_proc_config(sdi) != SR_OK)
+ return SR_ERR;
+
+ sr_dbg("Starting log retrieval.");
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ interval = (devc->config[0x1c] | (devc->config[0x1d] << 8)) * 1000;
+ packet.type = SR_DF_META;
+ packet.payload = &meta;
+ src = sr_config_new(SR_CONF_SAMPLE_INTERVAL, g_variant_new_uint64(interval));
+ meta.config = g_slist_append(NULL, src);
+ sr_session_send(devc->cb_data, &packet);
+ g_free(src);
+
+ if (devc->logged_samples == 0) {
+ /* This ensures the frontend knows the session is done. */
+ packet.type = SR_DF_END;
+ sr_session_send(devc->cb_data, &packet);
+ return SR_OK;
+ }
+
+ if (!(xfer_in = libusb_alloc_transfer(0)) ||
+ !(xfer_out = libusb_alloc_transfer(0)))
+ return SR_ERR;
+
+ libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR,
+ 0x00, 0xffff, 0x00, NULL, 0, 50);
+ libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR,
+ 0x02, 0x0002, 0x00, NULL, 0, 50);
+ libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR,
+ 0x02, 0x0001, 0x00, NULL, 0, 50);
+
+
+ /* Flush input. The F321 requires this. */
+ while (libusb_bulk_transfer(usb->devhdl, LASCAR_EP_IN, resp,
+ 256, &ret, 5) == 0 && ret > 0)
+ ;
+
+ libusb_fill_bulk_transfer(xfer_in, usb->devhdl, LASCAR_EP_IN,
+ resp, sizeof(resp), mark_xfer, 0, 10000);
+ if (libusb_submit_transfer(xfer_in) != 0) {
+ libusb_free_transfer(xfer_in);
+ libusb_free_transfer(xfer_out);
+ return SR_ERR;
+ }
+
+ cmd[0] = 0x03;
+ cmd[1] = 0xff;
+ cmd[2] = 0xff;
+ libusb_fill_bulk_transfer(xfer_out, usb->devhdl, LASCAR_EP_OUT,
+ cmd, 3, mark_xfer, 0, 100);
+ if (libusb_submit_transfer(xfer_out) != 0) {
+ libusb_free_transfer(xfer_in);
+ libusb_free_transfer(xfer_out);
+ return SR_ERR;
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ while (!xfer_in->user_data || !xfer_out->user_data) {
+ g_usleep(5000);
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+ }
+ if (xfer_in->user_data != GINT_TO_POINTER(1) ||
+ xfer_in->user_data != GINT_TO_POINTER(1)) {
+ sr_dbg("no response to log transfer request");
+ libusb_free_transfer(xfer_in);
+ libusb_free_transfer(xfer_out);
+ return SR_ERR;
+ }
+ if (xfer_in->actual_length != 3 || xfer_in->buffer[0] != 2) {
+ sr_dbg("invalid response to log transfer request");
+ libusb_free_transfer(xfer_in);
+ libusb_free_transfer(xfer_out);
+ return SR_ERR;
+ }
+ devc->log_size = xfer_in->buffer[1] + (xfer_in->buffer[2] << 8);
+ libusb_free_transfer(xfer_out);
+
+ usb_source_add(sdi->session, drvc->sr_ctx, 100,
+ lascar_el_usb_handle_events, (void *)sdi);
+
+ buf = g_try_malloc(4096);
+ libusb_fill_bulk_transfer(xfer_in, usb->devhdl, LASCAR_EP_IN,
+ buf, 4096, lascar_el_usb_receive_transfer, cb_data, 100);
+ if ((ret = libusb_submit_transfer(xfer_in) != 0)) {
+ sr_err("Unable to submit transfer: %s.", libusb_error_name(ret));
+ libusb_free_transfer(xfer_in);
+ g_free(buf);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ (void)cb_data;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ if (sdi->status != SR_ST_ACTIVE) {
+ sr_err("Device inactive, can't stop acquisition.");
+ return SR_ERR;
+ }
+
+ sdi->status = SR_ST_STOPPING;
+ /* TODO: free ongoing transfers? */
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info = {
+ .name = "lascar-el-usb",
+ .longname = "Lascar EL-USB",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdlib.h>
+#include <sys/time.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+extern struct sr_dev_driver lascar_el_usb_driver_info;
+static struct sr_dev_driver *di = &lascar_el_usb_driver_info;
+
+static const struct elusb_profile profiles[] = {
+ { 1, "EL-USB-1", LOG_UNSUPPORTED },
+ { 2, "EL-USB-1", LOG_UNSUPPORTED },
+ { 3, "EL-USB-2", LOG_TEMP_RH },
+ { 4, "EL-USB-3", LOG_UNSUPPORTED },
+ { 5, "EL-USB-4", LOG_UNSUPPORTED },
+ { 6, "EL-USB-3", LOG_UNSUPPORTED },
+ { 7, "EL-USB-4", LOG_UNSUPPORTED },
+ { 8, "EL-USB-LITE", LOG_UNSUPPORTED },
+ { 9, "EL-USB-CO", LOG_CO },
+ { 10, "EL-USB-TC", LOG_UNSUPPORTED },
+ { 11, "EL-USB-CO300", LOG_CO },
+ { 12, "EL-USB-2-LCD", LOG_TEMP_RH },
+ { 13, "EL-USB-2+", LOG_TEMP_RH },
+ { 14, "EL-USB-1-PRO", LOG_UNSUPPORTED },
+ { 15, "EL-USB-TC-LCD", LOG_UNSUPPORTED },
+ { 16, "EL-USB-2-LCD+", LOG_TEMP_RH },
+ { 17, "EL-USB-5", LOG_UNSUPPORTED },
+ { 18, "EL-USB-1-RCG", LOG_UNSUPPORTED },
+ { 19, "EL-USB-1-LCD", LOG_UNSUPPORTED },
+ { 20, "EL-OEM-3", LOG_UNSUPPORTED },
+ { 21, "EL-USB-1-LCD", LOG_UNSUPPORTED },
+ { 0, NULL, 0 }
+};
+
+
+static libusb_device_handle *lascar_open(struct libusb_device *dev)
+{
+ libusb_device_handle *dev_hdl;
+ int ret;
+
+ if ((ret = libusb_open(dev, &dev_hdl)) != 0) {
+ sr_dbg("failed to open device for scan: %s",
+ libusb_error_name(ret));
+ return NULL;
+ }
+
+ /* Some of these fail, but it needs doing -- some sort of mode
+ * setup for the SILabs F32x. */
+ libusb_control_transfer(dev_hdl, LIBUSB_REQUEST_TYPE_VENDOR,
+ 0x00, 0xffff, 0x00, NULL, 0, 50);
+ libusb_control_transfer(dev_hdl, LIBUSB_REQUEST_TYPE_VENDOR,
+ 0x02, 0x0002, 0x00, NULL, 0, 50);
+ libusb_control_transfer(dev_hdl, LIBUSB_REQUEST_TYPE_VENDOR,
+ 0x02, 0x0001, 0x00, NULL, 0, 50);
+
+ return dev_hdl;
+}
+
+static void mark_xfer(struct libusb_transfer *xfer)
+{
+
+ xfer->user_data = GINT_TO_POINTER(1);
+
+}
+
+SR_PRIV int lascar_get_config(libusb_device_handle *dev_hdl,
+ unsigned char *configblock, int *configlen)
+{
+ struct drv_context *drvc;
+ struct libusb_transfer *xfer_in, *xfer_out;
+ struct timeval tv;
+ int64_t start;
+ int buflen;
+ unsigned char cmd[3], buf[MAX_CONFIGBLOCK_SIZE];
+
+ sr_spew("Reading config block.");
+
+ drvc = di->priv;
+ *configlen = 0;
+
+ if (!(xfer_in = libusb_alloc_transfer(0)) ||
+ !(xfer_out = libusb_alloc_transfer(0)))
+ return SR_ERR;
+
+ /* Flush anything the F321 still has queued. */
+ while (libusb_bulk_transfer(dev_hdl, LASCAR_EP_IN, buf, 256, &buflen,
+ 5) == 0 && buflen > 0)
+ ;
+
+ /* Keep a read request waiting in the wings, ready to pounce
+ * the moment the device sends something. */
+ libusb_fill_bulk_transfer(xfer_in, dev_hdl, LASCAR_EP_IN,
+ buf, 256, mark_xfer, 0, 10000);
+ if (libusb_submit_transfer(xfer_in) != 0)
+ goto cleanup;
+
+ /* Request device configuration structure. */
+ cmd[0] = 0x00;
+ cmd[1] = 0xff;
+ cmd[2] = 0xff;
+ libusb_fill_bulk_transfer(xfer_out, dev_hdl, LASCAR_EP_OUT,
+ cmd, 3, mark_xfer, 0, 100);
+ if (libusb_submit_transfer(xfer_out) != 0)
+ goto cleanup;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ start = g_get_monotonic_time();
+ while (!xfer_in->user_data || !xfer_out->user_data) {
+ if (g_get_monotonic_time() - start > SCAN_TIMEOUT) {
+ start = 0;
+ break;
+ }
+ g_usleep(5000);
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+ }
+ if (!start) {
+ sr_dbg("no response");
+ goto cleanup;
+ }
+ if (xfer_in->actual_length != 3) {
+ sr_dbg("expected 3-byte header, got %d bytes", xfer_in->actual_length);
+ goto cleanup;
+ }
+
+ /* Got configuration structure header. */
+ sr_spew("Response to config request: 0x%.2x 0x%.2x 0x%.2x ",
+ buf[0], buf[1], buf[2]);
+ buflen = buf[1] | (buf[2] << 8);
+ if (buf[0] != 0x02 || buflen > MAX_CONFIGBLOCK_SIZE) {
+ sr_dbg("Invalid response to config request: "
+ "0x%.2x 0x%.2x 0x%.2x ", buf[0], buf[1], buf[2]);
+ libusb_close(dev_hdl);
+ goto cleanup;
+ }
+
+ /* Get configuration structure. */
+ xfer_in->length = buflen;
+ xfer_in->user_data = 0;
+ if (libusb_submit_transfer(xfer_in) != 0)
+ goto cleanup;
+ while (!xfer_in->user_data) {
+ if (g_get_monotonic_time() - start > SCAN_TIMEOUT) {
+ start = 0;
+ break;
+ }
+ g_usleep(5000);
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+ }
+ if (!start) {
+ sr_dbg("Timeout waiting for configuration structure.");
+ goto cleanup;
+ }
+ if (xfer_in->actual_length != buflen) {
+ sr_dbg("expected %d-byte structure, got %d bytes", buflen,
+ xfer_in->actual_length);
+ goto cleanup;
+ }
+
+ memcpy(configblock, buf, buflen);
+ *configlen = buflen;
+
+cleanup:
+ if (!xfer_in->user_data || !xfer_in->user_data) {
+ if (!xfer_in->user_data)
+ libusb_cancel_transfer(xfer_in);
+ if (!xfer_out->user_data)
+ libusb_cancel_transfer(xfer_out);
+ start = g_get_monotonic_time();
+ while (!xfer_in->user_data || !xfer_out->user_data) {
+ if (g_get_monotonic_time() - start > 10000)
+ break;
+ g_usleep(1000);
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+ }
+ }
+ libusb_free_transfer(xfer_in);
+ libusb_free_transfer(xfer_out);
+
+ return *configlen ? SR_OK : SR_ERR;
+}
+
+static int lascar_save_config(libusb_device_handle *dev_hdl,
+ unsigned char *config, int configlen)
+{
+ struct drv_context *drvc;
+ struct libusb_transfer *xfer_in, *xfer_out;
+ struct timeval tv;
+ int64_t start;
+ int buflen, ret;
+ unsigned char cmd[3], buf[256];
+
+ sr_spew("Writing config block.");
+
+ drvc = di->priv;
+
+ if (!(xfer_in = libusb_alloc_transfer(0)) ||
+ !(xfer_out = libusb_alloc_transfer(0)))
+ return SR_ERR;
+
+ /* Flush anything the F321 still has queued. */
+ while (libusb_bulk_transfer(dev_hdl, LASCAR_EP_IN, buf, 256, &buflen,
+ 5) == 0 && buflen > 0)
+ ;
+ ret = SR_OK;
+
+ /* Keep a read request waiting in the wings, ready to pounce
+ * the moment the device sends something. */
+ libusb_fill_bulk_transfer(xfer_in, dev_hdl, LASCAR_EP_IN,
+ buf, 256, mark_xfer, 0, 10000);
+ if (libusb_submit_transfer(xfer_in) != 0) {
+ ret = SR_ERR;
+ goto cleanup;
+ }
+
+ /* Request device configuration structure. */
+ cmd[0] = 0x01;
+ cmd[1] = configlen & 0xff;
+ cmd[2] = (configlen >> 8) & 0xff;
+ libusb_fill_bulk_transfer(xfer_out, dev_hdl, LASCAR_EP_OUT,
+ cmd, 3, mark_xfer, 0, 100);
+ if (libusb_submit_transfer(xfer_out) != 0) {
+ ret = SR_ERR;
+ goto cleanup;
+ }
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ while (!xfer_out->user_data) {
+ g_usleep(5000);
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+ }
+
+ libusb_fill_bulk_transfer(xfer_out, dev_hdl, LASCAR_EP_OUT,
+ config, configlen, mark_xfer, 0, 100);
+ if (libusb_submit_transfer(xfer_out) != 0) {
+ ret = SR_ERR;
+ goto cleanup;
+ }
+ while (!xfer_in->user_data || !xfer_out->user_data) {
+ g_usleep(5000);
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+ }
+
+ if (xfer_in->actual_length != 1 || buf[0] != 0xff) {
+ sr_dbg("unexpected response after transfer");
+ ret = SR_ERR;
+ }
+
+cleanup:
+ if (!xfer_in->user_data || !xfer_in->user_data) {
+ if (!xfer_in->user_data)
+ libusb_cancel_transfer(xfer_in);
+ if (!xfer_out->user_data)
+ libusb_cancel_transfer(xfer_out);
+ start = g_get_monotonic_time();
+ while (!xfer_in->user_data || !xfer_out->user_data) {
+ if (g_get_monotonic_time() - start > 10000)
+ break;
+ g_usleep(1000);
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+ }
+ }
+ libusb_free_transfer(xfer_in);
+ libusb_free_transfer(xfer_out);
+
+ return ret;
+}
+
+static struct sr_dev_inst *lascar_identify(unsigned char *config)
+{
+ struct dev_context *devc;
+ const struct elusb_profile *profile;
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ int modelid, i;
+ char firmware[5];
+
+ modelid = config[0];
+ sdi = NULL;
+ if (modelid) {
+ profile = NULL;
+ for (i = 0; profiles[i].modelid; i++) {
+ if (profiles[i].modelid == modelid) {
+ profile = &profiles[i];
+ break;
+ }
+ }
+ if (!profile) {
+ sr_dbg("unknown EL-USB modelid %d", modelid);
+ return NULL;
+ }
+
+ i = config[52] | (config[53] << 8);
+ memcpy(firmware, config + 0x30, 4);
+ firmware[4] = '\0';
+ sr_dbg("found %s with firmware version %s serial %d",
+ profile->modelname, firmware, i);
+
+ if (profile->logformat == LOG_UNSUPPORTED) {
+ sr_dbg("unsupported EL-USB logformat for %s", profile->modelname);
+ return NULL;
+ }
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, LASCAR_VENDOR,
+ profile->modelname, firmware)))
+ return NULL;
+ sdi->driver = di;
+
+ if (profile->logformat == LOG_TEMP_RH) {
+ /* Model this as two channels: temperature and humidity. */
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "Temp")))
+ return NULL;
+ sdi->channels = g_slist_append(NULL, ch);
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "Hum")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ } else if (profile->logformat == LOG_CO) {
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "CO")))
+ return NULL;
+ sdi->channels = g_slist_append(NULL, ch);
+ } else {
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
+ return NULL;
+ sdi->channels = g_slist_append(NULL, ch);
+ }
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
+ return NULL;
+ sdi->priv = devc;
+ devc->profile = profile;
+ }
+
+ return sdi;
+}
+
+SR_PRIV struct sr_dev_inst *lascar_scan(int bus, int address)
+{
+ struct drv_context *drvc;
+ struct sr_dev_inst *sdi;
+ struct libusb_device **devlist;
+ struct libusb_device_descriptor des;
+ libusb_device_handle *dev_hdl;
+ int dummy, ret, i;
+ unsigned char config[MAX_CONFIGBLOCK_SIZE];
+
+ drvc = di->priv;
+ sdi = NULL;
+
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
+ sr_err("Failed to get device descriptor: %d.", ret);
+ continue;
+ }
+
+ if (libusb_get_bus_number(devlist[i]) != bus ||
+ libusb_get_device_address(devlist[i]) != address)
+ continue;
+
+ if (!(dev_hdl = lascar_open(devlist[i])))
+ continue;
+
+ if (lascar_get_config(dev_hdl, config, &dummy) != SR_OK)
+ continue;
+
+ libusb_close(dev_hdl);
+ sdi = lascar_identify(config);
+ }
+
+ return sdi;
+}
+
+static void lascar_el_usb_dispatch(struct sr_dev_inst *sdi, unsigned char *buf,
+ int buflen)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct sr_channel *ch;
+ float *temp, *rh;
+ uint16_t s;
+ int samples, samples_left, i, j;
+
+ devc = sdi->priv;
+
+ samples = buflen / devc->sample_size;
+ samples_left = devc->logged_samples - devc->rcvd_samples;
+ if (samples_left < samples)
+ samples = samples_left;
+ switch (devc->profile->logformat) {
+ case LOG_TEMP_RH:
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ analog.mqflags = 0;
+ if (!(temp = g_try_malloc(sizeof(float) * samples)))
+ break;
+ if (!(rh = g_try_malloc(sizeof(float) * samples)))
+ break;
+ for (i = 0, j = 0; i < samples; i++) {
+ /* Both Celcius and Fahrenheit stored at base -40. */
+ if (devc->temp_unit == 0)
+ /* Celcius is stored in half-degree increments. */
+ temp[j] = buf[i * 2] / 2 - 40;
+ else
+ temp[j] = buf[i * 2] - 40;
+
+ rh[j] = buf[i * 2 + 1] / 2;
+
+ if (temp[j] == 0.0 && rh[j] == 0.0)
+ /* Skip invalid measurement. */
+ continue;
+ j++;
+ }
+ analog.num_samples = j;
+
+ ch = sdi->channels->data;
+ if (ch->enabled) {
+ analog.channels = g_slist_append(NULL, ch);
+ analog.mq = SR_MQ_TEMPERATURE;
+ if (devc->temp_unit == 1)
+ analog.unit = SR_UNIT_FAHRENHEIT;
+ else
+ analog.unit = SR_UNIT_CELSIUS;
+ analog.data = temp;
+ sr_session_send(devc->cb_data, &packet);
+ }
+
+ ch = sdi->channels->next->data;
+ if (ch->enabled) {
+ analog.channels = g_slist_append(NULL, ch);
+ analog.mq = SR_MQ_RELATIVE_HUMIDITY;
+ analog.unit = SR_UNIT_PERCENTAGE;
+ analog.data = rh;
+ sr_session_send(devc->cb_data, &packet);
+ }
+
+ g_free(temp);
+ g_free(rh);
+ break;
+ case LOG_CO:
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ analog.channels = sdi->channels;
+ analog.num_samples = samples;
+ analog.mq = SR_MQ_CARBON_MONOXIDE;
+ analog.unit = SR_UNIT_CONCENTRATION;
+ analog.mqflags = 0;
+ if (!(analog.data = g_try_malloc(sizeof(float) * samples)))
+ break;
+ for (i = 0; i < samples; i++) {
+ s = (buf[i * 2] << 8) | buf[i * 2 + 1];
+ analog.data[i] = (s * devc->co_high + devc->co_low) / 1000000;
+ if (analog.data[i] < 0.0)
+ analog.data[i] = 0.0;
+ }
+ sr_session_send(devc->cb_data, &packet);
+ g_free(analog.data);
+ break;
+ default:
+ /* How did we even get this far? */
+ break;
+ }
+ devc->rcvd_samples += samples;
+
+}
+
+SR_PRIV int lascar_el_usb_handle_events(int fd, int revents, void *cb_data)
+{
+ struct drv_context *drvc = di->priv;
+ struct sr_datafeed_packet packet;
+ struct sr_dev_inst *sdi;
+ struct timeval tv;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+
+ if (sdi->status == SR_ST_STOPPING) {
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+
+ packet.type = SR_DF_END;
+ sr_session_send(cb_data, &packet);
+ }
+
+ memset(&tv, 0, sizeof(struct timeval));
+ libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
+ NULL);
+
+ return TRUE;
+}
+
+SR_PRIV void lascar_el_usb_receive_transfer(struct libusb_transfer *transfer)
+{
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ int ret;
+ gboolean packet_has_error;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+
+ packet_has_error = FALSE;
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ /* USB device was unplugged. */
+ dev_acquisition_stop(sdi, sdi);
+ return;
+ case LIBUSB_TRANSFER_COMPLETED:
+ case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though */
+ break;
+ default:
+ packet_has_error = TRUE;
+ break;
+ }
+
+ if (!packet_has_error) {
+ if (devc->rcvd_samples < devc->logged_samples)
+ lascar_el_usb_dispatch(sdi, transfer->buffer,
+ transfer->actual_length);
+ devc->rcvd_bytes += transfer->actual_length;
+ sr_spew("received %d/%d bytes (%d/%d samples)",
+ devc->rcvd_bytes, devc->log_size,
+ devc->rcvd_samples, devc->logged_samples);
+ if (devc->rcvd_bytes >= devc->log_size)
+ dev_acquisition_stop(sdi, sdi);
+ }
+
+ if (sdi->status == SR_ST_ACTIVE) {
+ /* Send the same request again. */
+ if ((ret = libusb_submit_transfer(transfer) != 0)) {
+ sr_err("Unable to resubmit transfer: %s.",
+ libusb_error_name(ret));
+ g_free(transfer->buffer);
+ libusb_free_transfer(transfer);
+ dev_acquisition_stop(sdi, sdi);
+ }
+ } else {
+ /* This was the last transfer we're going to receive, so
+ * clean up now. */
+ g_free(transfer->buffer);
+ libusb_free_transfer(transfer);
+ }
+
+}
+
+static int get_flags(unsigned char *configblock)
+{
+ int flags;
+
+ flags = (configblock[32] | (configblock[33] << 8)) & 0x1fff;
+ sr_spew("Read flags (0x%.4x).", flags);
+
+ return flags;
+}
+
+static int set_flags(unsigned char *configblock, int flags)
+{
+
+ sr_spew("Setting flags to 0x%.4x.", flags);
+ configblock[32] = flags & 0xff;
+ configblock[33] = (flags >> 8) & 0x1f;
+
+ return flags;
+}
+
+SR_PRIV int lascar_is_logging(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int dummy, flags, ret;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ if (lascar_get_config(usb->devhdl, devc->config, &dummy) != SR_OK)
+ return -1;
+
+ flags = get_flags(devc->config);
+ if (flags & 0x0100)
+ ret = 1;
+ else
+ ret = 0;
+
+ return ret;
+}
+
+SR_PRIV int lascar_start_logging(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int len, flags, ret;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ if (lascar_get_config(usb->devhdl, devc->config, &len) != SR_OK)
+ return SR_ERR;
+
+ /* Turn on logging. */
+ flags = get_flags(devc->config);
+ flags |= 0x0100;
+ set_flags(devc->config, flags);
+
+ /* Start logging in 0 seconds. */
+ memset(devc->config + 24, 0, 4);
+
+ ret = lascar_save_config(usb->devhdl, devc->config, len);
+ sr_info("Started internal logging.");
+
+ return ret;
+}
+
+SR_PRIV int lascar_stop_logging(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int len, flags, ret;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ if (lascar_get_config(usb->devhdl, devc->config, &len) != SR_OK)
+ return SR_ERR;
+
+ flags = get_flags(devc->config);
+ flags &= ~0x0100;
+ set_flags(devc->config, flags);
+
+ ret = lascar_save_config(usb->devhdl, devc->config, len);
+ sr_info("Stopped internal logging.");
+
+ return ret;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_LASCAR_EL_USB_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_LASCAR_EL_USB_PROTOCOL_H
+
+#include <stdint.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "lascar-el-usb"
+
+#define LASCAR_VENDOR "Lascar"
+#define LASCAR_INTERFACE 0
+#define LASCAR_EP_IN 0x82
+#define LASCAR_EP_OUT 2
+/* Max 100ms for a device to positively identify. */
+#define SCAN_TIMEOUT 100000
+#define MAX_CONFIGBLOCK_SIZE 256
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ void *cb_data;
+ const struct elusb_profile *profile;
+ /* Generic EL-USB */
+ unsigned char config[MAX_CONFIGBLOCK_SIZE];
+ unsigned int log_size;
+ unsigned int rcvd_bytes;
+ unsigned int sample_size;
+ unsigned int logged_samples;
+ unsigned int rcvd_samples;
+ uint64_t limit_samples;
+ /* Model-specific */
+ /* EL-USB-CO: these are something like scaling and calibration values
+ * fixed per device, used to convert the sample values to CO ppm. */
+ float co_high;
+ float co_low;
+ /* Temperature units as stored in the device config. */
+ int temp_unit;
+};
+
+enum {
+ LOG_UNSUPPORTED,
+ LOG_TEMP_RH,
+ LOG_CO,
+};
+
+struct elusb_profile {
+ int modelid;
+ char *modelname;
+ int logformat;
+};
+
+SR_PRIV int lascar_get_config(libusb_device_handle *dev_hdl,
+ unsigned char *configblock, int *configlen);
+SR_PRIV struct sr_dev_inst *lascar_scan(int bus, int address);
+SR_PRIV int lascar_el_usb_handle_events(int fd, int revents, void *cb_data);
+SR_PRIV void lascar_el_usb_receive_transfer(struct libusb_transfer *transfer);
+SR_PRIV int lascar_start_logging(const struct sr_dev_inst *sdi);
+SR_PRIV int lascar_stop_logging(const struct sr_dev_inst *sdi);
+SR_PRIV int lascar_is_logging(const struct sr_dev_inst *sdi);
+SR_PRIV int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011 Daniel Ribeiro <drwyrm@gmail.com>
+ * Copyright (C) 2012 Renato Caldas <rmsc@fe.up.pt>
+ * Copyright (C) 2013 Lior Elazary <lelazary@yahoo.com>
+ *
+ * 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 "protocol.h"
+
+static const int32_t hwcaps[] = {
+ SR_CONF_OSCILLOSCOPE,
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_TRIGGER_TYPE,
+ SR_CONF_TRIGGER_SLOPE,
+ SR_CONF_HORIZ_TRIGGERPOS,
+// SR_CONF_CAPTURE_RATIO,
+ SR_CONF_LIMIT_SAMPLES,
+// SR_CONF_RLE,
+};
+
+/*
+ * Channels are numbered 0 to 7.
+ *
+ * See also: http://www.linkinstruments.com/images/mso19_1113.gif
+ */
+SR_PRIV const char *mso19_channel_names[NUM_CHANNELS + 1] = {
+ /* Note: DSO needs to be first. */
+ "DSO", "0", "1", "2", "3", "4", "5", "6", "7", NULL,
+};
+
+static const uint64_t samplerates[] = {
+ SR_HZ(100),
+ SR_MHZ(200),
+ SR_HZ(100),
+};
+
+SR_PRIV struct sr_dev_driver link_mso19_driver_info;
+static struct sr_dev_driver *di = &link_mso19_driver_info;
+
+/* TODO: Use sr_dev_inst to store connection handle & use std_dev_clear(). */
+static int dev_clear(void)
+{
+ GSList *l;
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ int ret = SR_OK;
+
+ if (!(drvc = di->priv))
+ return SR_OK;
+
+ /* Properly close and free all devices. */
+ for (l = drvc->instances; l; l = l->next) {
+ if (!(sdi = l->data)) {
+ /* Log error, but continue cleaning up the rest. */
+ sr_err("%s: sdi was NULL, continuing", __func__);
+ ret = SR_ERR_BUG;
+ continue;
+ }
+ if (!(devc = sdi->priv)) {
+ /* Log error, but continue cleaning up the rest. */
+ sr_err("%s: sdi->priv was NULL, continuing", __func__);
+ ret = SR_ERR_BUG;
+ continue;
+ }
+ std_serial_dev_close(sdi);
+ sr_serial_dev_inst_free(devc->serial);
+ sr_dev_inst_free(sdi);
+ }
+ g_slist_free(drvc->instances);
+ drvc->instances = NULL;
+
+ return ret;
+}
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ int i;
+ GSList *devices = NULL;
+ const char *conn = NULL;
+ const char *serialcomm = NULL;
+ GSList *l;
+ struct sr_config *src;
+ struct udev *udev;
+ int chtype;
+
+ 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)
+ conn = SERIALCONN;
+ if (serialcomm == NULL)
+ serialcomm = SERIALCOMM;
+
+ udev = udev_new();
+ if (!udev) {
+ sr_err("Failed to initialize udev.");
+ }
+
+ struct udev_enumerate *enumerate = udev_enumerate_new(udev);
+ udev_enumerate_add_match_subsystem(enumerate, "usb-serial");
+ udev_enumerate_scan_devices(enumerate);
+ struct udev_list_entry *devs = udev_enumerate_get_list_entry(enumerate);
+ struct udev_list_entry *dev_list_entry;
+ for (dev_list_entry = devs;
+ dev_list_entry != NULL;
+ dev_list_entry = udev_list_entry_get_next(dev_list_entry)) {
+ const char *syspath = udev_list_entry_get_name(dev_list_entry);
+ struct udev_device *dev =
+ udev_device_new_from_syspath(udev, syspath);
+ const char *sysname = udev_device_get_sysname(dev);
+ struct udev_device *parent =
+ udev_device_get_parent_with_subsystem_devtype(dev, "usb",
+ "usb_device");
+
+ if (!parent) {
+ sr_err("Unable to find parent usb device for %s",
+ sysname);
+ continue;
+ }
+
+ const char *idVendor =
+ udev_device_get_sysattr_value(parent, "idVendor");
+ const char *idProduct =
+ udev_device_get_sysattr_value(parent, "idProduct");
+ if (strcmp(USB_VENDOR, idVendor)
+ || strcmp(USB_PRODUCT, idProduct))
+ continue;
+
+ const char *iSerial =
+ udev_device_get_sysattr_value(parent, "serial");
+ const char *iProduct =
+ udev_device_get_sysattr_value(parent, "product");
+
+ char path[32];
+ snprintf(path, sizeof(path), "/dev/%s", sysname);
+ conn = path;
+
+ size_t s = strcspn(iProduct, " ");
+ char product[32];
+ char manufacturer[32];
+ if (s > sizeof(product) ||
+ strlen(iProduct) - s > sizeof(manufacturer)) {
+ sr_err("Could not parse iProduct: %s.", iProduct);
+ continue;
+ }
+ strncpy(product, iProduct, s);
+ product[s] = 0;
+ strcpy(manufacturer, iProduct + s + 1);
+
+ //Create the device context and set its params
+ struct dev_context *devc;
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return devices;
+ }
+
+ if (mso_parse_serial(iSerial, iProduct, devc) != SR_OK) {
+ sr_err("Invalid iSerial: %s.", iSerial);
+ g_free(devc);
+ return devices;
+ }
+
+ char hwrev[32];
+ sprintf(hwrev, "r%d", devc->hwrev);
+ devc->ctlbase1 = 0;
+ devc->protocol_trigger.spimode = 0;
+ for (i = 0; i < 4; i++) {
+ devc->protocol_trigger.word[i] = 0;
+ devc->protocol_trigger.mask[i] = 0xff;
+ }
+
+ if (!(devc->serial = sr_serial_dev_inst_new(conn, serialcomm))) {
+ g_free(devc);
+ return devices;
+ }
+
+ struct sr_dev_inst *sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
+ manufacturer, product, hwrev);
+
+ if (!sdi) {
+ sr_err("Unable to create device instance for %s",
+ sysname);
+ sr_dev_inst_free(sdi);
+ g_free(devc);
+ return devices;
+ }
+
+ sdi->driver = di;
+ sdi->priv = devc;
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ struct sr_channel *ch;
+ chtype = (i == 0) ? SR_CHANNEL_ANALOG : SR_CHANNEL_LOGIC;
+ if (!(ch = sr_channel_new(i, chtype, TRUE,
+ mso19_channel_names[i])))
+ return 0;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ //Add the driver
+ struct drv_context *drvc = di->priv;
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ }
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ int ret;
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ if (serial_open(devc->serial, SERIAL_RDWR) != SR_OK)
+ return SR_ERR;
+
+ sdi->status = SR_ST_ACTIVE;
+
+ /* FIXME: discard serial buffer */
+ mso_check_trigger(devc->serial, &devc->trigger_state);
+ sr_dbg("Trigger state: 0x%x.", devc->trigger_state);
+
+ ret = mso_reset_adc(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ mso_check_trigger(devc->serial, &devc->trigger_state);
+ sr_dbg("Trigger state: 0x%x.", devc->trigger_state);
+
+ // ret = mso_reset_fsm(sdi);
+ // if (ret != SR_OK)
+ // return ret;
+ // return SR_ERR;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return dev_clear();
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ if (sdi) {
+ devc = sdi->priv;
+ *data = g_variant_new_uint64(devc->cur_rate);
+ } else
+ return SR_ERR;
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ int ret;
+ struct dev_context *devc;
+ uint64_t num_samples;
+ const char *slope;
+ int trigger_pos;
+ double pos;
+
+ (void)cg;
+ devc = sdi->priv;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ // FIXME
+ return mso_configure_rate(sdi, g_variant_get_uint64(data));
+ ret = SR_OK;
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ num_samples = g_variant_get_uint64(data);
+ if (num_samples != 1024) {
+ sr_err("Only 1024 samples are supported.");
+ ret = SR_ERR_ARG;
+ } else {
+ devc->limit_samples = num_samples;
+ sr_dbg("setting limit_samples to %i\n",
+ num_samples);
+ ret = SR_OK;
+ }
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ ret = SR_OK;
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ slope = g_variant_get_string(data, NULL);
+
+ if (!slope || !(slope[0] == 'f' || slope[0] == 'r'))
+ sr_err("Invalid trigger slope");
+ ret = SR_ERR_ARG;
+ } else {
+ devc->trigger_slope = (slope[0] == 'r')
+ ? SLOPE_POSITIVE : SLOPE_NEGATIVE;
+ ret = SR_OK;
+ }
+ break;
+ case SR_CONF_HORIZ_TRIGGERPOS:
+ pos = g_variant_get_double(data);
+ if (pos < 0 || pos > 255) {
+ sr_err("Trigger position (%f) should be between 0 and 255.", pos);
+ ret = SR_ERR_ARG;
+ } else {
+ trigger_pos = (int)pos;
+ devc->trigger_holdoff[0] = trigger_pos & 0xff;
+ ret = SR_OK;
+ }
+ break;
+ case SR_CONF_RLE:
+ ret = SR_OK;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ break;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ GVariant *gvar;
+ GVariantBuilder gvb;
+
+ (void)cg;
+ (void)sdi;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
+ ARRAY_SIZE(samplerates), sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_TYPE:
+ *data = g_variant_new_string(TRIGGER_TYPE);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ int ret = SR_ERR;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ if (mso_configure_channels(sdi) != SR_OK) {
+ sr_err("Failed to configure channels.");
+ return SR_ERR;
+ }
+
+ /* FIXME: No need to do full reconfigure every time */
+// ret = mso_reset_fsm(sdi);
+// if (ret != SR_OK)
+// return ret;
+
+ /* FIXME: ACDC Mode */
+ devc->ctlbase1 &= 0x7f;
+// devc->ctlbase1 |= devc->acdcmode;
+
+ ret = mso_configure_rate(sdi, devc->cur_rate);
+ if (ret != SR_OK)
+ return ret;
+
+ /* set dac offset */
+ ret = mso_dac_out(sdi, devc->dac_offset);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = mso_configure_threshold_level(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = mso_configure_trigger(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ /* END of config hardware part */
+ ret = mso_arm(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ /* Start acquisition on the device. */
+ mso_check_trigger(devc->serial, &devc->trigger_state);
+ ret = mso_check_trigger(devc->serial, NULL);
+ if (ret != SR_OK)
+ return ret;
+
+ /* Reset trigger state. */
+ devc->trigger_state = 0x00;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Our first channel is analog, the other 8 are of type 'logic'. */
+ /* TODO. */
+
+ serial_source_add(sdi->session, devc->serial, G_IO_IN, -1,
+ mso_receive_data, cb_data);
+
+ return SR_OK;
+}
+
+/* This stops acquisition on ALL devices, ignoring dev_index. */
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ (void)cb_data;
+
+ stop_acquisition(sdi);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver link_mso19_driver_info = {
+ .name = "link-mso19",
+ .longname = "Link Instruments MSO-19",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = std_serial_dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011 Daniel Ribeiro <drwyrm@gmail.com>
+ * Copyright (C) 2012 Renato Caldas <rmsc@fe.up.pt>
+ * Copyright (C) 2013 Lior Elazary <lelazary@yahoo.com>
+ *
+ * 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 "protocol.h"
+
+/* serial protocol */
+#define mso_trans(a, v) \
+ (((v) & 0x3f) | (((v) & 0xc0) << 6) | (((a) & 0xf) << 8) | \
+ ((~(v) & 0x20) << 1) | ((~(v) & 0x80) << 7))
+
+static const char mso_head[] = { 0x40, 0x4c, 0x44, 0x53, 0x7e };
+static const char mso_foot[] = { 0x7e };
+
+extern SR_PRIV struct sr_dev_driver link_mso19_driver_info;
+static struct sr_dev_driver *di = &link_mso19_driver_info;
+
+SR_PRIV int mso_send_control_message(struct sr_serial_dev_inst *serial,
+ uint16_t payload[], int n)
+{
+ int i, w, ret, s = n * 2 + sizeof(mso_head) + sizeof(mso_foot);
+ char *p, *buf;
+
+ ret = SR_ERR;
+
+ if (serial->fd < 0)
+ goto ret;
+
+ if (!(buf = g_try_malloc(s))) {
+ sr_err("Failed to malloc message buffer.");
+ ret = SR_ERR_MALLOC;
+ goto ret;
+ }
+
+ p = buf;
+ memcpy(p, mso_head, sizeof(mso_head));
+ p += sizeof(mso_head);
+
+ for (i = 0; i < n; i++) {
+ *(uint16_t *) p = g_htons(payload[i]);
+ p += 2;
+ }
+ memcpy(p, mso_foot, sizeof(mso_foot));
+
+ w = 0;
+ while (w < s) {
+ ret = serial_write(serial, buf + w, s - w);
+ if (ret < 0) {
+ ret = SR_ERR;
+ goto free;
+ }
+ w += ret;
+ }
+ ret = SR_OK;
+free:
+ g_free(buf);
+ret:
+ return ret;
+}
+
+SR_PRIV int mso_configure_trigger(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ uint16_t threshold_value = mso_calc_raw_from_mv(devc);
+
+ threshold_value = 0x153C;
+ uint8_t trigger_config = 0;
+
+ if (devc->trigger_slope)
+ trigger_config |= 0x04; //Trigger on falling edge
+
+ switch (devc->trigger_outsrc) {
+ case 1:
+ trigger_config |= 0x00; //Trigger pulse output
+ break;
+ case 2:
+ trigger_config |= 0x08; //PWM DAC from the pattern generator buffer
+ break;
+ case 3:
+ trigger_config |= 0x18; //White noise
+ break;
+ }
+
+ switch (devc->trigger_chan) {
+ case 0:
+ trigger_config |= 0x00; //DSO level trigger //b00000000
+ break;
+ case 1:
+ trigger_config |= 0x20; //DSO level trigger & width < trigger_width
+ break;
+ case 2:
+ trigger_config |= 0x40; //DSO level trigger & width >= trigger_width
+ break;
+ case 3:
+ trigger_config |= 0x60; //LA combination trigger
+ break;
+ }
+
+ //Last bit of trigger config reg 4 needs to be 1 for trigger enable,
+ //otherwise the trigger is not enabled
+ if (devc->use_trigger)
+ trigger_config |= 0x80;
+
+ uint16_t ops[18];
+ ops[0] = mso_trans(3, threshold_value & 0xff);
+ //The trigger_config also holds the 2 MSB bits from the threshold value
+ ops[1] = mso_trans(4, trigger_config | ((threshold_value >> 8) & 0x03));
+ ops[2] = mso_trans(5, devc->la_trigger);
+ ops[3] = mso_trans(6, devc->la_trigger_mask);
+ ops[4] = mso_trans(7, devc->trigger_holdoff[0]);
+ ops[5] = mso_trans(8, devc->trigger_holdoff[1]);
+
+ ops[6] = mso_trans(11,
+ devc->dso_trigger_width /
+ SR_HZ_TO_NS(devc->cur_rate));
+
+ /* Select the SPI/I2C trigger config bank */
+ ops[7] = mso_trans(REG_CTL2, (devc->ctlbase2 | BITS_CTL2_BANK(2)));
+ /* Configure the SPI/I2C protocol trigger */
+ ops[8] = mso_trans(REG_PT_WORD(0), devc->protocol_trigger.word[0]);
+ ops[9] = mso_trans(REG_PT_WORD(1), devc->protocol_trigger.word[1]);
+ ops[10] = mso_trans(REG_PT_WORD(2), devc->protocol_trigger.word[2]);
+ ops[11] = mso_trans(REG_PT_WORD(3), devc->protocol_trigger.word[3]);
+ ops[12] = mso_trans(REG_PT_MASK(0), devc->protocol_trigger.mask[0]);
+ ops[13] = mso_trans(REG_PT_MASK(1), devc->protocol_trigger.mask[1]);
+ ops[14] = mso_trans(REG_PT_MASK(2), devc->protocol_trigger.mask[2]);
+ ops[15] = mso_trans(REG_PT_MASK(3), devc->protocol_trigger.mask[3]);
+ ops[16] = mso_trans(REG_PT_SPIMODE, devc->protocol_trigger.spimode);
+ /* Select the default config bank */
+ ops[17] = mso_trans(REG_CTL2, devc->ctlbase2);
+
+ return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
+}
+
+SR_PRIV int mso_configure_threshold_level(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+
+ return mso_dac_out(sdi, la_threshold_map[devc->la_threshold]);
+}
+
+SR_PRIV int mso_read_buffer(struct sr_dev_inst *sdi)
+{
+ uint16_t ops[] = { mso_trans(REG_BUFFER, 0) };
+ struct dev_context *devc = sdi->priv;
+
+ sr_dbg("Requesting buffer dump.");
+ return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
+}
+
+SR_PRIV int mso_arm(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ uint16_t ops[] = {
+ mso_trans(REG_CTL1, devc->ctlbase1 | BIT_CTL1_RESETFSM),
+ mso_trans(REG_CTL1, devc->ctlbase1 | BIT_CTL1_ARM),
+ mso_trans(REG_CTL1, devc->ctlbase1),
+ };
+
+ sr_dbg("Requesting trigger arm.");
+ return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
+}
+
+SR_PRIV int mso_force_capture(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ uint16_t ops[] = {
+ mso_trans(REG_CTL1, devc->ctlbase1 | 8),
+ mso_trans(REG_CTL1, devc->ctlbase1),
+ };
+
+ sr_dbg("Requesting forced capture.");
+ return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
+}
+
+SR_PRIV int mso_dac_out(const struct sr_dev_inst *sdi, uint16_t val)
+{
+ struct dev_context *devc = sdi->priv;
+ uint16_t ops[] = {
+ mso_trans(REG_DAC1, (val >> 8) & 0xff),
+ mso_trans(REG_DAC2, val & 0xff),
+ mso_trans(REG_CTL1, devc->ctlbase1 | BIT_CTL1_RESETADC),
+ };
+
+ sr_dbg("Setting dac word to 0x%x.", val);
+ return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
+}
+
+SR_PRIV inline uint16_t mso_calc_raw_from_mv(struct dev_context * devc)
+{
+ return (uint16_t) (0x200 -
+ ((devc->dso_trigger_voltage / devc->dso_probe_attn) /
+ devc->vbit));
+}
+
+SR_PRIV int mso_parse_serial(const char *iSerial, const char *iProduct,
+ struct dev_context *devc)
+{
+ unsigned int u1, u2, u3, u4, u5, u6;
+
+ (void)iProduct;
+
+ /* FIXME: This code is in the original app, but I think its
+ * used only for the GUI */
+ /* if (strstr(iProduct, "REV_02") || strstr(iProduct, "REV_03"))
+ devc->num_sample_rates = 0x16;
+ else
+ devc->num_sample_rates = 0x10; */
+
+ /* parse iSerial */
+ if (iSerial[0] != '4' || sscanf(iSerial, "%5u%3u%3u%1u%1u%6u",
+ &u1, &u2, &u3, &u4, &u5, &u6) != 6)
+ return SR_ERR;
+ devc->hwmodel = u4;
+ devc->hwrev = u5;
+ devc->vbit = u1 / 10000;
+ if (devc->vbit == 0)
+ devc->vbit = 4.19195;
+ devc->dac_offset = u2;
+ if (devc->dac_offset == 0)
+ devc->dac_offset = 0x1ff;
+ devc->offset_range = u3;
+ if (devc->offset_range == 0)
+ devc->offset_range = 0x17d;
+
+ /*
+ * FIXME: There is more code on the original software to handle
+ * bigger iSerial strings, but as I can't test on my device
+ * I will not implement it yet
+ */
+
+ return SR_OK;
+}
+
+SR_PRIV int mso_reset_adc(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ uint16_t ops[2];
+
+ ops[0] = mso_trans(REG_CTL1, (devc->ctlbase1 | BIT_CTL1_RESETADC));
+ ops[1] = mso_trans(REG_CTL1, devc->ctlbase1);
+ devc->ctlbase1 |= BIT_CTL1_ADC_UNKNOWN4;
+
+ sr_dbg("Requesting ADC reset.");
+ return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
+}
+
+SR_PRIV int mso_reset_fsm(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ uint16_t ops[1];
+
+ devc->ctlbase1 |= BIT_CTL1_RESETFSM;
+ ops[0] = mso_trans(REG_CTL1, devc->ctlbase1);
+
+ sr_dbg("Requesting ADC reset.");
+ return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
+}
+
+SR_PRIV int mso_toggle_led(struct sr_dev_inst *sdi, int state)
+{
+ struct dev_context *devc = sdi->priv;
+ uint16_t ops[1];
+
+ devc->ctlbase1 &= ~BIT_CTL1_LED;
+ if (state)
+ devc->ctlbase1 |= BIT_CTL1_LED;
+ ops[0] = mso_trans(REG_CTL1, devc->ctlbase1);
+
+ sr_dbg("Requesting LED toggle.");
+ return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops));
+}
+
+SR_PRIV void stop_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct sr_datafeed_packet packet;
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+ serial_source_remove(sdi->session, devc->serial);
+
+ /* Terminate session */
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+}
+
+SR_PRIV int mso_clkrate_out(struct sr_serial_dev_inst *serial, uint16_t val)
+{
+ uint16_t ops[] = {
+ mso_trans(REG_CLKRATE1, (val >> 8) & 0xff),
+ mso_trans(REG_CLKRATE2, val & 0xff),
+ };
+
+ sr_dbg("Setting clkrate word to 0x%x.", val);
+ return mso_send_control_message(serial, ARRAY_AND_SIZE(ops));
+}
+
+SR_PRIV int mso_configure_rate(const struct sr_dev_inst *sdi, uint32_t rate)
+{
+ struct dev_context *devc = sdi->priv;
+ unsigned int i;
+ int ret = SR_ERR;
+
+ for (i = 0; i < ARRAY_SIZE(rate_map); i++) {
+ if (rate_map[i].rate == rate) {
+ devc->ctlbase2 = rate_map[i].slowmode;
+ ret = mso_clkrate_out(devc->serial, rate_map[i].val);
+ if (ret == SR_OK)
+ devc->cur_rate = rate;
+ return ret;
+ }
+ }
+
+ if (ret != SR_OK)
+ sr_err("Unsupported rate.");
+
+ return ret;
+}
+
+SR_PRIV int mso_check_trigger(struct sr_serial_dev_inst *serial, uint8_t *info)
+{
+ uint16_t ops[] = { mso_trans(REG_TRIGGER, 0) };
+ int ret;
+
+ sr_dbg("Requesting trigger state.");
+ ret = mso_send_control_message(serial, ARRAY_AND_SIZE(ops));
+ if (info == NULL || ret != SR_OK)
+ return ret;
+
+ uint8_t buf = 0;
+ if (serial_read(serial, &buf, 1) != 1) /* FIXME: Need timeout */
+ ret = SR_ERR;
+ if (!info)
+ *info = buf;
+
+ sr_dbg("Trigger state is: 0x%x.", *info);
+ return ret;
+}
+
+SR_PRIV int mso_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ struct sr_dev_inst *sdi;
+ GSList *l;
+ int i;
+
+ struct drv_context *drvc = di->priv;
+
+ /* Find this device's devc struct by its fd. */
+ struct dev_context *devc = NULL;
+ for (l = drvc->instances; l; l = l->next) {
+ sdi = l->data;
+ devc = sdi->priv;
+ if (devc->serial->fd == fd)
+ break;
+ devc = NULL;
+ }
+ if (!devc)
+ /* Shouldn't happen. */
+ return TRUE;
+
+ (void)revents;
+
+ uint8_t in[1024];
+ size_t s = serial_read(devc->serial, in, sizeof(in));
+
+ if (s <= 0)
+ return FALSE;
+
+ /* Check if we triggered, then send a command that we are ready
+ * to read the data */
+ if (devc->trigger_state != MSO_TRIGGER_DATAREADY) {
+ devc->trigger_state = in[0];
+ if (devc->trigger_state == MSO_TRIGGER_DATAREADY) {
+ mso_read_buffer(sdi);
+ devc->buffer_n = 0;
+ } else {
+ mso_check_trigger(devc->serial, NULL);
+ }
+ return TRUE;
+ }
+
+ /* the hardware always dumps 1024 samples, 24bits each */
+ if (devc->buffer_n < 3072) {
+ memcpy(devc->buffer + devc->buffer_n, in, s);
+ devc->buffer_n += s;
+ }
+ if (devc->buffer_n < 3072)
+ return TRUE;
+
+ /* do the conversion */
+ uint8_t logic_out[1024];
+ double analog_out[1024];
+ for (i = 0; i < 1024; i++) {
+ /* FIXME: Need to do conversion to mV */
+ analog_out[i] = (devc->buffer[i * 3] & 0x3f) |
+ ((devc->buffer[i * 3 + 1] & 0xf) << 6);
+ (void)analog_out;
+ logic_out[i] = ((devc->buffer[i * 3 + 1] & 0x30) >> 4) |
+ ((devc->buffer[i * 3 + 2] & 0x3f) << 2);
+ }
+
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = 1024;
+ logic.unitsize = 1;
+ logic.data = logic_out;
+ sr_session_send(cb_data, &packet);
+
+ devc->num_samples += 1024;
+
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sr_info("Requested number of samples reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ }
+
+ return TRUE;
+}
+
+SR_PRIV int mso_configure_channels(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ GSList *l;
+ char *tc;
+
+ devc = sdi->priv;
+
+ devc->la_trigger_mask = 0xFF; //the mask for the LA_TRIGGER (bits set to 0 matter, those set to 1 are ignored).
+ devc->la_trigger = 0x00; //The value of the LA byte that generates a trigger event (in that mode).
+ devc->dso_trigger_voltage = 3;
+ devc->dso_probe_attn = 1;
+ devc->trigger_outsrc = 0;
+ devc->trigger_chan = 3; //LA combination trigger
+ devc->use_trigger = FALSE;
+
+ for (l = sdi->channels; l; l = l->next) {
+ ch = (struct sr_channel *)l->data;
+ if (ch->enabled == FALSE)
+ continue;
+
+ int channel_bit = 1 << (ch->index);
+ if (!(ch->trigger))
+ continue;
+
+ devc->use_trigger = TRUE;
+ //Configure trigger mask and value.
+ for (tc = ch->trigger; *tc; tc++) {
+ devc->la_trigger_mask &= ~channel_bit;
+ if (*tc == '1')
+ devc->la_trigger |= channel_bit;
+ }
+ }
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011 Daniel Ribeiro <drwyrm@gmail.com>
+ * Copyright (C) 2012 Renato Caldas <rmsc@fe.up.pt>
+ * Copyright (C) 2013 Lior Elazary <lelazary@yahoo.com>
+ *
+ * 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_LINK_MSO19_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_LINK_MSO19_PROTOCOL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include <libudev.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "link-mso19"
+
+#define USB_VENDOR "3195"
+#define USB_PRODUCT "f190"
+
+#define NUM_CHANNELS (1 + 8)
+#define NUM_TRIGGER_STAGES 4
+#define TRIGGER_TYPE "01" //the first r/f is used for the whole group
+#define SERIALCOMM "460800/8n1/flow=2"
+#define SERIALCONN "/dev/ttyUSB0"
+#define CLOCK_RATE SR_MHZ(100)
+#define MIN_NUM_SAMPLES 4
+
+#define MSO_TRIGGER_UNKNOWN '!'
+#define MSO_TRIGGER_UNKNOWN1 '1'
+#define MSO_TRIGGER_UNKNOWN2 '2'
+#define MSO_TRIGGER_UNKNOWN3 '3'
+#define MSO_TRIGGER_WAIT '4'
+#define MSO_TRIGGER_FIRED '5'
+#define MSO_TRIGGER_DATAREADY '6'
+
+enum trigger_slopes {
+ SLOPE_POSITIVE = 0,
+ SLOPE_NEGATIVE,
+};
+
+/* Structure for the pattern generator state */
+struct mso_patgen {
+ /* Pattern generator clock config */
+ uint16_t clock;
+ /* Buffer start address */
+ uint16_t start;
+ /* Buffer end address */
+ uint16_t end;
+ /* Pattern generator config */
+ uint8_t config;
+ /* Samples buffer */
+ uint8_t buffer[1024];
+ /* Input/output configuration for the samples buffer (?) */
+ uint8_t io[1024];
+ /* Number of loops for the pattern generator */
+ uint8_t loops;
+ /* Bit enable mask for the I/O lines */
+ uint8_t mask;
+};
+
+/* Data structure for the protocol trigger state */
+struct mso_prototrig {
+ /* Word match buffer */
+ uint8_t word[4];
+ /* Masks for the wordmatch buffer */
+ uint8_t mask[4];
+ /* SPI mode 0, 1, 2, 3. Set to 0 for I2C */
+ uint8_t spimode;
+};
+
+/* Private, per-device-instance driver context. */
+struct dev_context {
+ /* info */
+ uint8_t hwmodel;
+ uint8_t hwrev;
+ struct sr_serial_dev_inst *serial;
+// uint8_t num_sample_rates;
+ /* calibration */
+ double vbit;
+ uint16_t dac_offset;
+ uint16_t offset_range;
+ uint64_t limit_samples;
+ uint64_t num_samples;
+ /* register cache */
+ uint8_t ctlbase1;
+ uint8_t ctlbase2;
+ /* state */
+ uint8_t la_threshold;
+ uint64_t cur_rate;
+ uint8_t dso_probe_attn;
+ int8_t use_trigger;
+ uint8_t trigger_chan;
+ uint8_t trigger_slope;
+ uint8_t trigger_outsrc;
+ uint8_t trigger_state;
+ uint8_t trigger_holdoff[2];
+ uint8_t la_trigger;
+ uint8_t la_trigger_mask;
+ double dso_trigger_voltage;
+ uint16_t dso_trigger_width;
+ struct mso_prototrig protocol_trigger;
+ void *cb_data;
+ uint16_t buffer_n;
+ char buffer[4096];
+};
+
+SR_PRIV int mso_parse_serial(const char *iSerial, const char *iProduct,
+ struct dev_context *ctx);
+SR_PRIV int mso_check_trigger(struct sr_serial_dev_inst *serial,
+ uint8_t * info);
+SR_PRIV int mso_reset_adc(struct sr_dev_inst *sdi);
+SR_PRIV int mso_clkrate_out(struct sr_serial_dev_inst *serial, uint16_t val);
+SR_PRIV int mso_configure_rate(const struct sr_dev_inst *sdi, uint32_t rate);
+SR_PRIV int mso_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int mso_configure_trigger(const struct sr_dev_inst *sdi);
+SR_PRIV int mso_configure_threshold_level(const struct sr_dev_inst *sdi);
+SR_PRIV int mso_read_buffer(struct sr_dev_inst *sdi);
+SR_PRIV int mso_arm(const struct sr_dev_inst *sdi);
+SR_PRIV int mso_force_capture(struct sr_dev_inst *sdi);
+SR_PRIV int mso_dac_out(const struct sr_dev_inst *sdi, uint16_t val);
+SR_PRIV inline uint16_t mso_calc_raw_from_mv(struct dev_context *devc);
+SR_PRIV int mso_reset_fsm(struct sr_dev_inst *sdi);
+SR_PRIV int mso_toggle_led(struct sr_dev_inst *sdi, int state);
+
+SR_PRIV int mso_configure_channels(const struct sr_dev_inst *sdi);
+SR_PRIV void stop_acquisition(const struct sr_dev_inst *sdi);
+
+/* bank agnostic registers */
+#define REG_CTL2 15
+
+/* bank 0 registers */
+#define REG_BUFFER 1
+#define REG_TRIGGER 2
+#define REG_CLKRATE1 9
+#define REG_CLKRATE2 10
+#define REG_DAC1 12
+#define REG_DAC2 13
+/* possibly bank agnostic: */
+#define REG_CTL1 14
+
+/* bank 2 registers (SPI/I2C protocol trigger) */
+#define REG_PT_WORD(x) (x)
+#define REG_PT_MASK(x) (x + 4)
+#define REG_PT_SPIMODE 8
+
+/* bits - REG_CTL1 */
+#define BIT_CTL1_RESETFSM (1 << 0)
+#define BIT_CTL1_ARM (1 << 1)
+#define BIT_CTL1_ADC_UNKNOWN4 (1 << 4) /* adc enable? */
+#define BIT_CTL1_RESETADC (1 << 6)
+#define BIT_CTL1_LED (1 << 7)
+
+/* bits - REG_CTL2 */
+#define BITS_CTL2_BANK(x) (x & 0x3)
+#define BIT_CTL2_SLOWMODE (1 << 5)
+
+struct rate_map {
+ uint32_t rate;
+ uint16_t val;
+ uint8_t slowmode;
+};
+
+static const struct rate_map rate_map[] = {
+ { SR_MHZ(200), 0x0205, 0 },
+ { SR_MHZ(100), 0x0105, 0 },
+ { SR_MHZ(50), 0x0005, 0 },
+ { SR_MHZ(20), 0x0303, 0 },
+ { SR_MHZ(10), 0x0308, 0 },
+ { SR_MHZ(5), 0x030c, 0 },
+ { SR_MHZ(2), 0x0330, 0 },
+ { SR_MHZ(1), 0x0362, 0 },
+ { SR_KHZ(500), 0x03c6, 0 },
+ { SR_KHZ(200), 0x07f2, 0 },
+ { SR_KHZ(100), 0x0fe6, 0 },
+ { SR_KHZ(50), 0x1fce, 0 },
+ { SR_KHZ(20), 0x4f86, 0 },
+ { SR_KHZ(10), 0x9f0e, 0 },
+ { SR_KHZ(5), 0x03c7, 0x20 },
+ { SR_KHZ(2), 0x07f3, 0x20 },
+ { SR_KHZ(1), 0x0fe7, 0x20 },
+ { SR_HZ(500), 0x1fcf, 0x20 },
+ { SR_HZ(200), 0x4f87, 0x20 },
+ { SR_HZ(100), 0x9f0f, 0x20 },
+};
+
+/* FIXME: Determine corresponding voltages */
+static const uint16_t la_threshold_map[] = {
+ 0x8600,
+ 0x8770,
+ 0x88ff,
+ 0x8c70,
+ 0x8eff,
+ 0x8fff,
+};
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/** @file
+ * <em>Manson HCS-3xxx Series</em> power supply driver
+ * @internal
+ */
+
+#include "protocol.h"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t devopts[] = {
+ /* Device class */
+ SR_CONF_POWER_SUPPLY,
+ /* Aquisition modes. */
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+ /* Device configuration */
+ SR_CONF_OUTPUT_CURRENT,
+ SR_CONF_OUTPUT_CURRENT_MAX,
+ SR_CONF_OUTPUT_ENABLED,
+ SR_CONF_OUTPUT_VOLTAGE,
+ SR_CONF_OUTPUT_VOLTAGE_MAX,
+};
+
+/* Note: All models have one power supply output only. */
+static struct hcs_model models[] = {
+ { MANSON_HCS_3100, "HCS-3100", "3100", { 1, 18, 0.1 }, { 0, 10, 0.10 } },
+ { MANSON_HCS_3102, "HCS-3102", "3102", { 1, 36, 0.1 }, { 0, 5, 0.01 } },
+ { MANSON_HCS_3104, "HCS-3104", "3104", { 1, 60, 0.1 }, { 0, 2.5, 0.01 } },
+ { MANSON_HCS_3150, "HCS-3150", "3150", { 1, 18, 0.1 }, { 0, 15, 0.10 } },
+ { MANSON_HCS_3200, "HCS-3200", "3200", { 1, 18, 0.1 }, { 0, 20, 0.10 } },
+ { MANSON_HCS_3202, "HCS-3202", "3202", { 1, 36, 0.1 }, { 0, 10, 0.10 } },
+ { MANSON_HCS_3204, "HCS-3204", "3204", { 1, 60, 0.1 }, { 0, 5, 0.01 } },
+ { MANSON_HCS_3300, "HCS-3300-USB", "3300", { 1, 16, 0.1 }, { 0, 30, 0.10 } },
+ { MANSON_HCS_3302, "HCS-3302-USB", "3302", { 1, 32, 0.1 }, { 0, 15, 0.10 } },
+ { MANSON_HCS_3304, "HCS-3304-USB", "3304", { 1, 60, 0.1 }, { 0, 8, 0.10 } },
+ { MANSON_HCS_3400, "HCS-3400-USB", "3400", { 1, 16, 0.1 }, { 0, 40, 0.10 } },
+ { MANSON_HCS_3402, "HCS-3402-USB", "3402", { 1, 32, 0.1 }, { 0, 20, 0.10 } },
+ { MANSON_HCS_3404, "HCS-3404-USB", "3404", { 1, 60, 0.1 }, { 0, 10, 0.10 } },
+ { MANSON_HCS_3600, "HCS-3600-USB", "3600", { 1, 16, 0.1 }, { 0, 60, 0.10 } },
+ { MANSON_HCS_3602, "HCS-3602-USB", "3602", { 1, 32, 0.1 }, { 0, 30, 0.10 } },
+ { MANSON_HCS_3604, "HCS-3604-USB", "3604", { 1, 60, 0.1 }, { 0, 15, 0.10 } },
+ { 0, NULL, NULL, { 0, 0, 0 }, { 0, 0, 0 }, },
+};
+
+SR_PRIV struct sr_dev_driver manson_hcs_3xxx_driver_info;
+static struct sr_dev_driver *di = &manson_hcs_3xxx_driver_info;
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ int i, model_id;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_config *src;
+ struct sr_channel *ch;
+ GSList *devices, *l;
+ const char *conn, *serialcomm;
+ struct sr_serial_dev_inst *serial;
+ char reply[50], **tokens, *dummy;
+
+ drvc = di->priv;
+ drvc->instances = NULL;
+ devices = NULL;
+ conn = NULL;
+ serialcomm = NULL;
+ devc = 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;
+ default:
+ sr_err("Unknown option %d, skipping.", src->key);
+ break;
+ }
+ }
+
+ if (!conn)
+ return NULL;
+ if (!serialcomm)
+ serialcomm = "9600/8n1";
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+ return NULL;
+
+ serial_flush(serial);
+
+ sr_info("Probing serial port %s.", conn);
+
+ /* Get the device model. */
+ memset(&reply, 0, sizeof(reply));
+ if ((hcs_send_cmd(serial, "GMOD\r") < 0) ||
+ (hcs_read_reply(serial, 2, reply, sizeof(reply)) < 0))
+ return NULL;
+ tokens = g_strsplit((const gchar *)&reply, "\r", 2);
+
+ model_id = -1;
+ for (i = 0; models[i].id != NULL; i++) {
+ if (!strcmp(models[i].id, tokens[0]))
+ model_id = i;
+ }
+ g_strfreev(tokens);
+
+ if (model_id < 0) {
+ sr_err("Unknown model id '%s' detected, aborting.", tokens[0]);
+ return NULL;
+ }
+
+ /* Init device instance, etc. */
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Manson",
+ models[model_id].name, NULL))) {
+ sr_err("Failed to create device instance.");
+ return NULL;
+ }
+
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+ sdi->driver = di;
+
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "CH1"))) {
+ sr_err("Failed to create channel.");
+ goto exit_err;
+ }
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ devc = g_malloc0(sizeof(struct dev_context));
+ devc->model = &models[model_id];
+
+ sdi->priv = devc;
+
+ /* Get current voltage, current, status. */
+ if ((hcs_send_cmd(serial, "GETD\r") < 0) ||
+ (hcs_read_reply(serial, 2, reply, sizeof(reply)) < 0))
+ goto exit_err;
+ tokens = g_strsplit((const gchar *)&reply, "\r", 2);
+ if (hcs_parse_volt_curr_mode(sdi, tokens) < 0)
+ goto exit_err;
+ g_strfreev(tokens);
+
+ /* Get max. voltage and current. */
+ if ((hcs_send_cmd(serial, "GMAX\r") < 0) ||
+ (hcs_read_reply(serial, 2, reply, sizeof(reply)) < 0))
+ goto exit_err;
+ tokens = g_strsplit((const gchar *)&reply, "\r", 2);
+ devc->current_max_device = g_strtod(&tokens[0][3], &dummy) * devc->model->current[2];
+ tokens[0][3] = '\0';
+ devc->voltage_max_device = g_strtod(tokens[0], &dummy) * devc->model->voltage[2];
+ g_strfreev(tokens);
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ serial_close(serial);
+ if (!devices)
+ sr_serial_dev_inst_free(serial);
+
+ return devices;
+
+exit_err:
+ sr_dev_inst_free(sdi);
+ if (devc)
+ g_free(devc);
+ return NULL;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int cleanup(void)
+{
+ return dev_clear();
+}
+
+static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ *data = g_variant_new_uint64(devc->limit_msec);
+ break;
+ case SR_CONF_OUTPUT_CURRENT:
+ *data = g_variant_new_double(devc->current);
+ break;
+ case SR_CONF_OUTPUT_CURRENT_MAX:
+ *data = g_variant_new_double(devc->current_max);
+ break;
+ case SR_CONF_OUTPUT_ENABLED:
+ *data = g_variant_new_boolean(devc->output_enabled);
+ break;
+ case SR_CONF_OUTPUT_VOLTAGE:
+ *data = g_variant_new_double(devc->voltage);
+ break;
+ case SR_CONF_OUTPUT_VOLTAGE_MAX:
+ *data = g_variant_new_double(devc->voltage_max);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ gboolean bval;
+ gdouble dval;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_LIMIT_MSEC:
+ if (g_variant_get_uint64(data) == 0)
+ return SR_ERR_ARG;
+ devc->limit_msec = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ if (g_variant_get_uint64(data) == 0)
+ return SR_ERR_ARG;
+ devc->limit_samples = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_OUTPUT_CURRENT_MAX:
+ dval = g_variant_get_double(data);
+ if (dval < devc->model->current[0] || dval > devc->current_max_device)
+ return SR_ERR_ARG;
+
+ if ((hcs_send_cmd(sdi->conn, "CURR%03.0f\r",
+ (dval / devc->model->current[2])) < 0) ||
+ (hcs_read_reply(sdi->conn, 1, devc->buf, sizeof(devc->buf)) < 0))
+ return SR_ERR;
+ devc->current_max = dval;
+ break;
+ case SR_CONF_OUTPUT_ENABLED:
+ bval = g_variant_get_boolean(data);
+ if (bval == devc->output_enabled) /* Nothing to do. */
+ break;
+ if ((hcs_send_cmd(sdi->conn, "SOUT%1d\r", !bval) < 0) ||
+ (hcs_read_reply(sdi->conn, 1, devc->buf, sizeof(devc->buf)) < 0))
+ return SR_ERR;
+ devc->output_enabled = bval;
+ break;
+ case SR_CONF_OUTPUT_VOLTAGE_MAX:
+ dval = g_variant_get_double(data);
+ if (dval < devc->model->voltage[0] || dval > devc->voltage_max_device)
+ return SR_ERR_ARG;
+
+ if ((hcs_send_cmd(sdi->conn, "VOLT%03.0f\r",
+ (dval / devc->model->voltage[2])) < 0) ||
+ (hcs_read_reply(sdi->conn, 1, devc->buf, sizeof(devc->buf)) < 0))
+ return SR_ERR;
+ devc->voltage_max = dval;
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ GVariant *gvar;
+ GVariantBuilder gvb;
+ double dval;
+ int idx;
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
+ break;
+ case SR_CONF_OUTPUT_CURRENT_MAX:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ /* Min, max, step. */
+ for (idx = 0; idx < 3; idx++) {
+ if (idx == 1)
+ dval = devc->current_max_device;
+ else
+ dval = devc->model->current[idx];
+ gvar = g_variant_new_double(dval);
+ g_variant_builder_add_value(&gvb, gvar);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_OUTPUT_VOLTAGE_MAX:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ /* Min, max, step. */
+ for (idx = 0; idx < 3; idx++) {
+ if (idx == 1)
+ dval = devc->voltage_max_device;
+ else
+ dval = devc->model->voltage[idx];
+ gvar = g_variant_new_double(dval);
+ g_variant_builder_add_value(&gvb, gvar);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ devc->cb_data = cb_data;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ devc->starttime = g_get_monotonic_time();
+ devc->num_samples = 0;
+ devc->reply_pending = FALSE;
+ devc->req_sent_at = 0;
+
+ /* Poll every 10ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 10,
+ hcs_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ return std_serial_dev_acquisition_stop(sdi, cb_data,
+ std_serial_dev_close, sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver manson_hcs_3xxx_driver_info = {
+ .name = "manson-hcs-3xxx",
+ .longname = "Manson HCS-3xxx",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = 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 = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/** @file
+ * <em>Manson HCS-3xxx Series</em> power supply driver
+ * @internal
+ */
+
+#include "protocol.h"
+
+#define REQ_TIMEOUT_MS 500
+
+SR_PRIV int hcs_send_cmd(struct sr_serial_dev_inst *serial, const char *cmd, ...)
+{
+ int ret;
+ char cmdbuf[50];
+ char *cmd_esc;
+ va_list args;
+
+ va_start(args, cmd);
+ vsnprintf(cmdbuf, sizeof(cmdbuf), cmd, args);
+ va_end(args);
+
+ cmd_esc = g_strescape(cmdbuf, NULL);
+ sr_dbg("Sending '%s'.", cmd_esc);
+ g_free(cmd_esc);
+
+ if ((ret = serial_write_blocking(serial, cmdbuf, strlen(cmdbuf))) < 0) {
+ sr_err("Error sending command: %d.", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * Read data from interface into buffer blocking until @a lines number of \\r chars
+ * received.
+ * @param serial Previously initialized serial port structure.
+ * @param[in] lines Number of \\r-terminated lines to read (1-n).
+ * @param buf Buffer for result. Contents is NUL-terminated on success.
+ * @param[in] buflen Buffer length (>0).
+ * @retval SR_OK Lines received and ending with "OK\r" (success).
+ * @retval SR_ERR Error.
+ * @retval SR_ERR_ARG Invalid argument.
+ */
+SR_PRIV int hcs_read_reply(struct sr_serial_dev_inst *serial, int lines, char* buf, int buflen)
+{
+ int l_recv = 0;
+ int bufpos = 0;
+ int retc;
+
+ if (!serial || (lines <= 0) || !buf || (buflen <= 0))
+ return SR_ERR_ARG;
+
+ while ((l_recv < lines) && (bufpos < (buflen + 1))) {
+ retc = serial_read_blocking(serial, &buf[bufpos], 1);
+ if (retc != 1)
+ return SR_ERR;
+ if (buf[bufpos] == '\r')
+ l_recv++;
+ bufpos++;
+ }
+ buf[bufpos] = '\0';
+
+ if ((l_recv == lines) && (g_str_has_suffix(buf, "OK\r")))
+ return SR_OK;
+ else
+ return SR_ERR;
+}
+
+/** Interpret result of GETD command. */
+SR_PRIV int hcs_parse_volt_curr_mode(struct sr_dev_inst *sdi, char **tokens)
+{
+ char *str;
+ double val;
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ /* Bytes 0-3: Voltage. */
+ str = g_strndup(tokens[0], 4);
+ val = g_ascii_strtod(str, NULL) / 100;
+ devc->voltage = val;
+ g_free(str);
+
+ /* Bytes 4-7: Current. */
+ str = g_strndup((tokens[0] + 4), 4);
+ val = g_ascii_strtod(str, NULL) / 100;
+ devc->current = val;
+ g_free(str);
+
+ /* Byte 8: Mode ('0' means CV, '1' means CC). */
+ devc->cc_mode = (tokens[0][8] == '1');
+
+ /* Output enabled? Works because voltage cannot be set to 0.0 directly. */
+ devc->output_enabled = devc->voltage != 0.0;
+
+ return SR_OK;
+}
+
+static void send_sample(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+
+ devc = sdi->priv;
+
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+
+ analog.mq = SR_MQ_VOLTAGE;
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags = SR_MQFLAG_DC;
+ analog.data = &devc->voltage;
+ sr_session_send(sdi, &packet);
+
+ analog.mq = SR_MQ_CURRENT;
+ analog.unit = SR_UNIT_AMPERE;
+ analog.mqflags = 0;
+ analog.data = &devc->current;
+ sr_session_send(sdi, &packet);
+
+ devc->num_samples++;
+}
+
+static int parse_reply(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ char *reply_esc, **tokens;
+ int retc;
+
+ devc = sdi->priv;
+
+ reply_esc = g_strescape(devc->buf, NULL);
+ sr_dbg("Received '%s'.", reply_esc);
+ g_free(reply_esc);
+
+ tokens = g_strsplit(devc->buf, "\r", 0);
+ retc = hcs_parse_volt_curr_mode(sdi, tokens);
+ g_strfreev(tokens);
+ if (retc < 0)
+ return SR_ERR;
+
+ send_sample(sdi);
+
+ return SR_OK;
+}
+
+static int handle_new_data(struct sr_dev_inst *sdi)
+{
+ int len;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+
+ len = serial_read(serial, devc->buf + devc->buflen, 1);
+ if (len < 1)
+ return SR_ERR;
+
+ devc->buflen += len;
+ devc->buf[devc->buflen] = '\0';
+
+ /* Wait until we received an "OK\r" (among other bytes). */
+ if (!g_str_has_suffix(devc->buf, "OK\r"))
+ return SR_OK;
+
+ parse_reply(sdi);
+
+ devc->buf[0] = '\0';
+ devc->buflen = 0;
+
+ devc->reply_pending = FALSE;
+
+ return SR_OK;
+}
+
+/** Driver/serial data reception function. */
+SR_PRIV int hcs_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int64_t t, elapsed_us;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+
+ if (revents == G_IO_IN) {
+ /* New data arrived. */
+ handle_new_data(sdi);
+ } else {
+ /* Timeout. */
+ }
+
+ if (devc->limit_samples && (devc->num_samples >= devc->limit_samples)) {
+ sr_info("Requested number of samples reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+
+ if (devc->limit_msec) {
+ t = (g_get_monotonic_time() - devc->starttime) / 1000;
+ if (t > (int64_t)devc->limit_msec) {
+ sr_info("Requested time limit reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+ }
+
+ /* Request next packet, if required. */
+ if (sdi->status == SR_ST_ACTIVE) {
+ if (devc->reply_pending) {
+ elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
+ if (elapsed_us > (REQ_TIMEOUT_MS * 1000))
+ devc->reply_pending = FALSE;
+ return TRUE;
+ }
+
+ /* Send command to get voltage, current, and mode (CC or CV). */
+ if (hcs_send_cmd(serial, "GETD\r") < 0)
+ return TRUE;
+
+ devc->req_sent_at = g_get_monotonic_time();
+ devc->reply_pending = TRUE;
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/** @file
+ * <em>Manson HCS-3xxx Series</em> power supply driver
+ * @internal
+ */
+
+#ifndef LIBSIGROK_HARDWARE_MANSON_HCS_3XXX_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_MANSON_HCS_3XXX_PROTOCOL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "manson-hcs-3xxx"
+
+enum {
+ MANSON_HCS_3100,
+ MANSON_HCS_3102,
+ MANSON_HCS_3104,
+ MANSON_HCS_3150,
+ MANSON_HCS_3200,
+ MANSON_HCS_3202,
+ MANSON_HCS_3204,
+ MANSON_HCS_3300,
+ MANSON_HCS_3302,
+ MANSON_HCS_3304,
+ MANSON_HCS_3400,
+ MANSON_HCS_3402,
+ MANSON_HCS_3404,
+ MANSON_HCS_3600,
+ MANSON_HCS_3602,
+ MANSON_HCS_3604,
+};
+
+/** Information on a single model. */
+struct hcs_model {
+ int model_id; /**< Model info */
+ char *name; /**< Model name */
+ char *id; /**< Model ID, like delivered by interface */
+ double voltage[3]; /**< Min, max, step */
+ double current[3]; /**< Min, max, step */
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ struct hcs_model *model; /**< Model informaion. */
+
+ uint64_t limit_samples;
+ uint64_t limit_msec;
+ uint64_t num_samples;
+ int64_t starttime;
+ int64_t req_sent_at;
+ gboolean reply_pending;
+
+ void *cb_data;
+
+ float current; /**< Last current value [A] read from device. */
+ float current_max; /**< Output current set. */
+ float current_max_device;/**< Device-provided maximum output current. */
+ float voltage; /**< Last voltage value [V] read from device. */
+ float voltage_max; /**< Output voltage set. */
+ float voltage_max_device;/**< Device-provided maximum output voltage. */
+ gboolean cc_mode; /**< Device is in constant current mode (otherwise constant voltage). */
+
+ gboolean output_enabled; /**< Is the output enabled? */
+
+ char buf[50];
+ int buflen;
+};
+
+SR_PRIV int hcs_parse_volt_curr_mode(struct sr_dev_inst *sdi, char **tokens);
+SR_PRIV int hcs_read_reply(struct sr_serial_dev_inst *serial, int lines, char* buf, int buflen);
+SR_PRIV int hcs_send_cmd(struct sr_serial_dev_inst *serial, const char *cmd, ...);
+SR_PRIV int hcs_receive_data(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "protocol.h"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_THERMOMETER,
+ SR_CONF_HYGROMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+SR_PRIV struct sr_dev_driver mic_98581_driver_info;
+SR_PRIV struct sr_dev_driver mic_98583_driver_info;
+
+SR_PRIV const struct mic_dev_info mic_devs[] = {
+ {
+ "MIC", "98581", "38400/8n2", 32000, TRUE, FALSE, 6,
+ packet_valid_temp,
+ &mic_98581_driver_info, receive_data_MIC_98581,
+ },
+ {
+ "MIC", "98583", "38400/8n2", 32000, TRUE, TRUE, 10,
+ packet_valid_temp_hum,
+ &mic_98583_driver_info, receive_data_MIC_98583,
+ },
+};
+
+static int dev_clear(int idx)
+{
+ return std_dev_clear(mic_devs[idx].di, NULL);
+}
+
+static int init(struct sr_context *sr_ctx, int idx)
+{
+ sr_dbg("Selected '%s' subdriver.", mic_devs[idx].di->name);
+
+ return std_init(sr_ctx, mic_devs[idx].di, LOG_PREFIX);
+}
+
+static GSList *mic_scan(const char *conn, const char *serialcomm, int idx)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ struct sr_serial_dev_inst *serial;
+ GSList *devices;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ drvc = mic_devs[idx].di->priv;
+ devices = NULL;
+ serial_flush(serial);
+
+ /* TODO: Query device type. */
+ // ret = mic_cmd_get_device_info(serial);
+
+ sr_info("Found device on port %s.", conn);
+
+ /* TODO: Fill in version from protocol response. */
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, mic_devs[idx].vendor,
+ mic_devs[idx].device, NULL)))
+ goto scan_cleanup;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ goto scan_cleanup;
+ }
+
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+
+ sdi->priv = devc;
+ sdi->driver = mic_devs[idx].di;
+
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "Temperature")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ if (mic_devs[idx].has_humidity) {
+ if (!(ch = sr_channel_new(1, SR_CHANNEL_ANALOG, TRUE, "Humidity")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+scan_cleanup:
+ serial_close(serial);
+
+ return devices;
+}
+
+static GSList *scan(GSList *options, int idx)
+{
+ struct sr_config *src;
+ GSList *l, *devices;
+ const char *conn, *serialcomm;
+
+ 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 (!conn)
+ return NULL;
+
+ if (serialcomm) {
+ /* Use the provided comm specs. */
+ devices = mic_scan(conn, serialcomm, idx);
+ } else {
+ /* Try the default. */
+ devices = mic_scan(conn, mic_devs[idx].conn, idx);
+ }
+
+ return devices;
+}
+
+static GSList *dev_list(int idx)
+{
+ return ((struct drv_context *)(mic_devs[idx].di->priv))->instances;
+}
+
+static int cleanup(int idx)
+{
+ return dev_clear(idx);
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ switch (id) {
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data, int idx)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ devc->cb_data = cb_data;
+ devc->num_samples = 0;
+ devc->starttime = g_get_monotonic_time();
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Poll every 100ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 100,
+ mic_devs[idx].receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+/* Driver-specific API function wrappers */
+#define HW_INIT(X) \
+static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
+#define HW_CLEANUP(X) \
+static int cleanup_##X(void) { return cleanup(X); }
+#define HW_SCAN(X) \
+static GSList *scan_##X(GSList *options) { return scan(options, X); }
+#define HW_DEV_LIST(X) \
+static GSList *dev_list_##X(void) { return dev_list(X); }
+#define HW_DEV_CLEAR(X) \
+static int dev_clear_##X(void) { return dev_clear(X); }
+#define HW_DEV_ACQUISITION_START(X) \
+static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
+void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
+
+/* Driver structs and API function wrappers */
+#define DRV(ID, ID_UPPER, NAME, LONGNAME) \
+HW_INIT(ID_UPPER) \
+HW_CLEANUP(ID_UPPER) \
+HW_SCAN(ID_UPPER) \
+HW_DEV_LIST(ID_UPPER) \
+HW_DEV_CLEAR(ID_UPPER) \
+HW_DEV_ACQUISITION_START(ID_UPPER) \
+SR_PRIV struct sr_dev_driver ID##_driver_info = { \
+ .name = NAME, \
+ .longname = LONGNAME, \
+ .api_version = 1, \
+ .init = init_##ID_UPPER, \
+ .cleanup = cleanup_##ID_UPPER, \
+ .scan = scan_##ID_UPPER, \
+ .dev_list = dev_list_##ID_UPPER, \
+ .dev_clear = dev_clear_##ID_UPPER, \
+ .config_get = NULL, \
+ .config_set = config_set, \
+ .config_list = config_list, \
+ .dev_open = std_serial_dev_open, \
+ .dev_close = std_serial_dev_close, \
+ .dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
+ .dev_acquisition_stop = dev_acquisition_stop, \
+ .priv = NULL, \
+};
+
+DRV(mic_98581, MIC_98581, "mic-98581", "MIC 98581")
+DRV(mic_98583, MIC_98583, "mic-98583", "MIC 98583")
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "protocol.h"
+
+static int mic_send(struct sr_serial_dev_inst *serial, const char *cmd)
+{
+ int ret;
+
+ if ((ret = serial_write(serial, cmd, strlen(cmd))) < 0) {
+ sr_err("Error sending '%s' command: %d.", cmd, ret);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int mic_cmd_get_device_info(struct sr_serial_dev_inst *serial)
+{
+ return mic_send(serial, "I\r");
+}
+
+static int mic_cmd_set_realtime_mode(struct sr_serial_dev_inst *serial)
+{
+ return mic_send(serial, "S 1 M 2 32 3\r");
+}
+
+SR_PRIV gboolean packet_valid_temp(const uint8_t *buf)
+{
+ if (buf[0] != 'v' || buf[1] != ' ' || buf[5] != '\r')
+ return FALSE;
+
+ if (!isdigit(buf[2]) || !isdigit(buf[3]) || !isdigit(buf[4]))
+ return FALSE;
+
+ return TRUE;
+}
+
+SR_PRIV gboolean packet_valid_temp_hum(const uint8_t *buf)
+{
+ if (buf[0] != 'v' || buf[1] != ' ' || buf[5] != ' ' || buf[9] != '\r')
+ return FALSE;
+
+ if (!isdigit(buf[2]) || !isdigit(buf[3]) || !isdigit(buf[4]))
+ return FALSE;
+
+ if (!isdigit(buf[6]) || !isdigit(buf[7]) || !isdigit(buf[8]))
+ return FALSE;
+
+ return TRUE;
+}
+
+static int packet_parse(const char *buf, int idx, float *temp, float *humidity)
+{
+ char tmp[4];
+
+ /* Packet format MIC98581: "v ttt\r". */
+ /* Packet format MIC98583: "v ttt hhh\r". */
+
+ /* TODO: Sanity check on buf. For now we assume well-formed ASCII. */
+
+ tmp[3] = '\0';
+
+ strncpy((char *)&tmp, &buf[2], 3);
+ *temp = g_ascii_strtoull((const char *)&tmp, NULL, 10) / 10;
+
+ if (mic_devs[idx].has_humidity) {
+ strncpy((char *)&tmp, &buf[6], 3);
+ *humidity = g_ascii_strtoull((const char *)&tmp, NULL, 10) / 10;
+ }
+
+ return SR_OK;
+}
+
+static int handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi, int idx)
+{
+ float temperature, humidity;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct dev_context *devc;
+ GSList *l;
+ int ret;
+
+ (void)idx;
+
+ devc = sdi->priv;
+
+ ret = packet_parse((const char *)buf, idx, &temperature, &humidity);
+ if (ret < 0) {
+ sr_err("Failed to parse packet.");
+ return SR_ERR;
+ }
+
+ /* Clear 'analog', otherwise it'll contain random garbage. */
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+
+ /* Common values for both channels. */
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ analog.num_samples = 1;
+
+ /* Temperature. */
+ l = g_slist_copy(sdi->channels);
+ l = g_slist_remove_link(l, g_slist_nth(l, 1));
+ analog.channels = l;
+ analog.mq = SR_MQ_TEMPERATURE;
+ analog.unit = SR_UNIT_CELSIUS; /* TODO: Use C/F correctly. */
+ analog.data = &temperature;
+ sr_session_send(devc->cb_data, &packet);
+ g_slist_free(l);
+
+ /* Humidity. */
+ if (mic_devs[idx].has_humidity) {
+ l = g_slist_copy(sdi->channels);
+ l = g_slist_remove_link(l, g_slist_nth(l, 0));
+ analog.channels = l;
+ analog.mq = SR_MQ_RELATIVE_HUMIDITY;
+ analog.unit = SR_UNIT_PERCENTAGE;
+ analog.data = &humidity;
+ sr_session_send(devc->cb_data, &packet);
+ g_slist_free(l);
+ }
+
+ devc->num_samples++;
+
+ return SR_OK;
+}
+
+static void handle_new_data(struct sr_dev_inst *sdi, int idx)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int len, i, offset = 0;
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+
+ /* Try to get as much data as the buffer can hold. */
+ len = SERIAL_BUFSIZE - devc->buflen;
+ len = serial_read(serial, devc->buf + devc->buflen, len);
+ if (len < 1) {
+ sr_err("Serial port read error: %d.", len);
+ return;
+ }
+
+ devc->buflen += len;
+
+ /* Now look for packets in that data. */
+ while ((devc->buflen - offset) >= mic_devs[idx].packet_size) {
+ if (mic_devs[idx].packet_valid(devc->buf + offset)) {
+ handle_packet(devc->buf + offset, sdi, idx);
+ offset += mic_devs[idx].packet_size;
+ } else {
+ offset++;
+ }
+ }
+
+ /* If we have any data left, move it to the beginning of our buffer. */
+ for (i = 0; i < devc->buflen - offset; i++)
+ devc->buf[i] = devc->buf[offset + i];
+ devc->buflen -= offset;
+}
+
+static int receive_data(int fd, int revents, int idx, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ int64_t t;
+ static gboolean first_time = TRUE;
+ struct sr_serial_dev_inst *serial;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+
+ if (revents == G_IO_IN) {
+ /* New data arrived. */
+ handle_new_data(sdi, idx);
+ } else {
+ /* Timeout. */
+ if (first_time) {
+ mic_cmd_set_realtime_mode(serial);
+ first_time = FALSE;
+ }
+ }
+
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sr_info("Requested number of samples reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+
+ if (devc->limit_msec) {
+ t = (g_get_monotonic_time() - devc->starttime) / 1000;
+ if (t > (int64_t)devc->limit_msec) {
+ sr_info("Requested time limit reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#define RECEIVE_DATA(ID_UPPER) \
+SR_PRIV int receive_data_##ID_UPPER(int fd, int revents, void *cb_data) { \
+ return receive_data(fd, revents, ID_UPPER, cb_data); }
+
+/* Driver-specific receive_data() wrappers */
+RECEIVE_DATA(MIC_98581)
+RECEIVE_DATA(MIC_98583)
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBSIGROK_HARDWARE_MIC_985XX_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_MIC_985XX_PROTOCOL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "mic-985xx"
+
+/* Note: When adding entries here, don't forget to update MIC_DEV_COUNT. */
+enum {
+ MIC_98581,
+ MIC_98583,
+};
+
+#define MIC_DEV_COUNT 2
+
+struct mic_dev_info {
+ char *vendor;
+ char *device;
+ char *conn;
+ uint32_t max_sample_points;
+ gboolean has_temperature;
+ gboolean has_humidity;
+ uint8_t packet_size;
+ gboolean (*packet_valid)(const uint8_t *);
+ struct sr_dev_driver *di;
+ int (*receive_data)(int, int, void *);
+};
+
+extern SR_PRIV const struct mic_dev_info mic_devs[MIC_DEV_COUNT];
+
+#define SERIAL_BUFSIZE 256
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /** The current sampling limit (in number of samples). */
+ uint64_t limit_samples;
+
+ /** The current sampling limit (in ms). */
+ uint64_t limit_msec;
+
+ /** Opaque pointer passed in by the frontend. */
+ void *cb_data;
+
+ /** The current number of already received samples. */
+ uint64_t num_samples;
+
+ int64_t starttime;
+
+ uint8_t buf[SERIAL_BUFSIZE];
+ int bufoffset;
+ int buflen;
+};
+
+SR_PRIV gboolean packet_valid_temp(const uint8_t *buf);
+SR_PRIV gboolean packet_valid_temp_hum(const uint8_t *buf);
+
+SR_PRIV int receive_data_MIC_98581(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_MIC_98583(int fd, int revents, void *cb_data);
+
+SR_PRIV int mic_cmd_get_device_info(struct sr_serial_dev_inst *serial);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com> (code from atten-pps3xxx)
+ *
+ * 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/>.
+ */
+
+/** @file
+ * <em>Motech LPS-30x series</em> power supply driver
+ * @internal
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <string.h>
+
+#include "protocol.h"
+
+/* Forward declarations */
+SR_PRIV struct sr_dev_driver motech_lps_301_driver_info;
+SR_PRIV int lps_read_reply(struct sr_serial_dev_inst *serial, char **buf, int *buflen);
+SR_PRIV int lps_send_va(struct sr_serial_dev_inst *serial, const char* fmt, va_list args);
+SR_PRIV int lps_cmd_ok(struct sr_serial_dev_inst *serial, const char* fmt, ...);
+SR_PRIV int lps_cmd_reply(char* reply, struct sr_serial_dev_inst *serial, const char* fmt, ...);
+SR_PRIV int lps_query_status(struct sr_dev_inst* sdi);
+
+/* Serial communication parameters */
+#define SERIALCOMM "2400/8n1/dtr=1/rts=1/flow=0"
+
+#define VENDOR_MOTECH "Motech"
+
+/** Driver scanning options. */
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+/** Hardware capabilities generic. */
+static const int32_t hwcaps[] = {
+ /* Device class */
+ SR_CONF_POWER_SUPPLY,
+ /* Aquisition modes. */
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+ /* Device configuration */
+ SR_CONF_OUTPUT_CHANNEL,
+};
+
+/** Hardware capabilities channel 1, 2. */
+static const int32_t hwcaps_ch12[] = {
+ SR_CONF_OUTPUT_VOLTAGE,
+ SR_CONF_OUTPUT_VOLTAGE_MAX,
+ SR_CONF_OUTPUT_CURRENT,
+ SR_CONF_OUTPUT_CURRENT_MAX,
+ SR_CONF_OUTPUT_ENABLED,
+};
+
+/** Hardware capabilities channel 3. (LPS-304/305 only). */
+static const int32_t hwcaps_ch3[] = {
+ SR_CONF_OUTPUT_VOLTAGE,
+ SR_CONF_OUTPUT_ENABLED,
+};
+
+static const char *channel_modes[] = {
+ "Independent",
+ "Track1",
+ "Track2",
+};
+
+static struct lps_modelspec models[] = {
+ { LPS_UNKNOWN, "Dummy", 0,
+ {
+
+ }
+ },
+ { LPS_301, "LPS-301", 1,
+ {
+ /* Channel 1 */
+ { { 0, 32, 0.01 }, { 0.005, 2, 0.001 } },
+ },
+ },
+ { LPS_302, "LPS-302", 1,
+ {
+ /* Channel 1 */
+ { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+ },
+ },
+ { LPS_303, "LPS-303", 1,
+ {
+ /* Channel 1 */
+ { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+ },
+ },
+ { LPS_304, "LPS-304", 3,
+ {
+ /* Channel 1 */
+ { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+ /* Channel 2 */
+ { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+ /* Channel 3 */
+ { { 5, 5, 0.0 }, { 0.005, 3, 0.001 } },
+ },
+ },
+ { LPS_305, "LPS-305", 3,
+ {
+ /* Channel 1 */
+ { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+ /* Channel 2 */
+ { { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+ /* Channel 3 */
+ { { 3.3, 5, 1.7 }, { 0.005, 3, 0.001 } },
+ },
+ },
+};
+
+static int init_lps301(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, &motech_lps_301_driver_info, LOG_PREFIX);
+}
+
+/** Send command to device with va_list.
+ */
+SR_PRIV int lps_send_va(struct sr_serial_dev_inst *serial, const char* fmt, va_list args)
+{
+ int retc;
+ char auxfmt[LINELEN_MAX];
+ char buf[LINELEN_MAX];
+
+ snprintf(auxfmt, sizeof(auxfmt), "%s\r\n", fmt);
+ vsnprintf(buf, sizeof(buf), auxfmt, args);
+
+ sr_spew("lps_send_va: \"%s\"", buf);
+
+ retc = serial_write_nonblocking(serial, buf, strlen(buf));
+
+ if (retc < 0)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+/** Send command to device.
+ */
+SR_PRIV int lps_send_req(struct sr_serial_dev_inst *serial, const char* fmt, ...)
+{
+ int retc;
+ va_list args;
+
+ va_start(args, fmt);
+ retc = lps_send_va(serial, fmt, args);
+ va_end(args);
+
+ return retc;
+}
+
+/** Send command and consume simple OK reply. */
+SR_PRIV int lps_cmd_ok(struct sr_serial_dev_inst *serial, const char* fmt, ...)
+{
+ int retc;
+ va_list args;
+ char buf[LINELEN_MAX];
+ char* bufptr;
+ int buflen;
+
+ /* Send command */
+ va_start(args, fmt);
+ retc = lps_send_va(serial, fmt, args);
+ va_end(args);
+
+ if (retc != SR_OK)
+ return SR_ERR;
+
+ /* Read reply */
+ buf[0] = '\0';
+ bufptr = buf;
+ buflen = sizeof(buf);
+ retc = lps_read_reply(serial, &bufptr, &buflen);
+ if ((retc == SR_OK) && (buflen == 0))
+ return SR_OK;
+
+ return SR_ERR;
+}
+
+/** Send command and read reply string.
+ * \param reply Pointer to buffer of size LINELEN_MAX. Will be NUL-terminated.
+ */
+SR_PRIV int lps_cmd_reply(char* reply, struct sr_serial_dev_inst *serial, const char* fmt, ...)
+{
+ int retc;
+ va_list args;
+ char buf[LINELEN_MAX];
+ char* bufptr;
+ int buflen;
+
+ reply[0] = '\0';
+
+ /* Send command */
+ va_start(args, fmt);
+ retc = lps_send_va(serial, fmt, args);
+ va_end(args);
+
+ if (retc != SR_OK)
+ return SR_ERR;
+
+ /* Read reply */
+ buf[0] = '\0';
+ bufptr = buf;
+ buflen = sizeof(buf);
+ retc = lps_read_reply(serial, &bufptr, &buflen);
+ if ((retc == SR_OK) && (buflen > 0)) {
+ strcpy(reply, buf);
+ return SR_OK;
+ }
+
+ return SR_ERR;
+}
+
+/** Process integer value returned by STATUS command. */
+SR_PRIV int lps_process_status(struct sr_dev_inst* sdi, int stat)
+{
+ struct dev_context* devc;
+ int tracking_mode;
+
+ devc = (struct dev_context*)sdi->priv;
+
+ sr_spew("Status: %d", stat);
+ devc->channel_status[0].cc_mode = (stat & 0x01) != 0;
+ sr_spew("Channel 1 %s mode", devc->channel_status[0].cc_mode?"CC":"CV");
+ if (devc->model->num_channels > 1) {
+ devc->channel_status[1].cc_mode = (stat & 0x02) != 0;
+ sr_spew("Channel 2 %s mode", devc->channel_status[1].cc_mode?"CC":"CV");
+
+ tracking_mode = (stat & 0x0c) >> 2;
+ switch (tracking_mode) {
+ case 0: devc->tracking_mode = 0;
+ break;
+ case 2: devc->tracking_mode = 1;
+ break;
+ case 3: devc->tracking_mode = 2;
+ break;
+ default:
+ sr_err("Illegal channel tracking mode %d!", tracking_mode);
+ devc->tracking_mode = 0;
+ break;
+ }
+
+ sr_spew("Channel tracking: %d", devc->tracking_mode);
+ }
+ devc->channel_status[0].output_enabled = devc->channel_status[1].output_enabled = stat&0x040?TRUE:FALSE;
+ sr_spew("Channel 1%s output: %s", devc->model->num_channels > 1?"+2":"", devc->channel_status[0].output_enabled?"ON":"OFF");
+ if (devc->model->num_channels > 2) {
+ devc->channel_status[2].output_enabled = stat&0x010?TRUE:FALSE;
+ devc->channel_status[2].output_voltage_last = stat&0x020?3.3:5;
+ sr_spew("Channel 3 output: %s, U=%02f V, overload=%d",
+ devc->channel_status[2].output_enabled?"ON":"OFF",
+ devc->channel_status[2].output_voltage_last,
+ stat&0x080?1:0);
+ }
+ sr_spew("Fan=%d, beep=%d, CC output compensated=%d", stat&0x0100?1:0, stat&0x0200?1:0, stat&0x0400?1:0);
+
+ return SR_OK;
+}
+
+/** Send STATUS commend and process status string. */
+SR_PRIV int lps_query_status(struct sr_dev_inst* sdi)
+{
+ char buf[LINELEN_MAX];
+ int stat;
+ struct dev_context* devc;
+
+ devc = (struct dev_context*)sdi->priv;
+
+ devc->req_sent_at = g_get_real_time();
+
+ if (lps_cmd_reply(buf, sdi->conn, "STATUS") < 0) {
+ sr_err("%s: Failed to read status: %d %s", __func__, errno, strerror(errno));
+ return SR_ERR;
+ }
+
+ if (sr_atoi(buf, &stat) != SR_OK)
+ return SR_ERR;
+
+ return lps_process_status(sdi, stat);
+}
+
+static gint64 calc_timeout_ms(gint64 start_us)
+{
+ gint64 result = REQ_TIMEOUT_MS - ((g_get_real_time() - start_us) / 1000);
+
+ if (result < 0)
+ return 0;
+
+ return result;
+}
+
+/** Read message into buf until "OK" received.
+ * \retval SR_OK Msg received; buf and buflen contain result, if any except OK.
+ * \retval SR_ERR Error, including timeout.
+*/
+SR_PRIV int lps_read_reply(struct sr_serial_dev_inst *serial, char **buf, int *buflen)
+{
+ int retries;
+ char buf2[LINELEN_MAX];
+ char *buf2ptr;
+ int buf2len;
+ gint64 timeout_start;
+
+ *buf[0] = '\0';
+
+ /* Read one line. It is either a data message or "OK". */
+ timeout_start = g_get_real_time();
+ buf2len = *buflen;
+ /* Up to 5 tries because serial_readline() will consume only one CR or LF per
+ * call, but device sends up to 4 in a row. */
+ for (retries = 0; retries < 5; retries++) {
+ *buflen = buf2len;
+ if (serial_readline(serial, buf, buflen, calc_timeout_ms(timeout_start)) != SR_OK)
+ return SR_ERR;
+ if (!strcmp(*buf, "OK")) { /* We got an OK! */
+ *buf[0] = '\0';
+ *buflen = 0;
+ return SR_OK;
+ }
+ if (*buflen > 0) /* We got a msg! */
+ break;
+ }
+
+ /* A data msg is in buf (possibly ERROR), need to consume "OK". */
+ buf2[0] = '\0';
+ buf2ptr = buf2;
+ for (retries = 0; retries < 5; retries++) {
+ buf2len = sizeof(buf2);
+ if (serial_readline(serial, &buf2ptr, &buf2len, calc_timeout_ms(timeout_start)) != SR_OK)
+ return SR_ERR;
+
+ if (!strcmp(buf2ptr, "OK")) { /* We got an OK! */
+ if (!strcmp(*buf, "ERROR")) { /* OK came after msg ERROR! */
+ sr_spew("ERROR found!");
+ *buf[0] = '\0';
+ *buflen = 0;
+ return SR_ERR;
+ }
+ return SR_OK;
+ }
+ }
+
+ return SR_ERR; /* Timeout! */
+}
+
+/** Scan for LPS-300 series device.
+ */
+static GSList *do_scan(lps_modelid modelid, struct sr_dev_driver *drv, GSList *options)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ struct sr_channel *ch;
+ struct sr_channel_group *cg;
+ GSList *devices;
+ const char *conn, *serialcomm;
+ int cnt;
+ gchar buf[LINELEN_MAX];
+ gchar channel[10];
+ char* verstr;
+
+ sdi = NULL;
+ devc = NULL;
+ conn = serialcomm = NULL;
+ devices = NULL;
+
+ drvc = drv->priv;
+ drvc->instances = NULL;
+
+ sr_spew("scan() called!");
+
+ /* Process and check options. */
+ if (sr_serial_extract_options(options, &conn, &serialcomm) != SR_OK)
+ return NULL;
+ if (!serialcomm)
+ serialcomm = SERIALCOMM;
+
+ /* Init serial port. */
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ goto exit_err;
+
+ /* Query and verify model string. */
+ serial_flush(serial);
+ if (lps_cmd_reply(buf, serial, "MODEL") != SR_OK)
+ return NULL;
+
+ /* Check model string. */
+ if (strncmp(buf, "LPS-", 4)) {
+ sr_spew("Unknown model code \"%s\"!", buf);
+ return NULL;
+ }
+
+ /* Bug in device FW 1.17, model number is empty, so this can't work with this FW! */
+ if (modelid == LPS_UNKNOWN) {
+ g_strstrip(buf);
+ for (cnt = LPS_301; cnt <= LPS_305; cnt++) {
+ if (!strcmp(buf, models[cnt].modelstr)) {
+ modelid = cnt;
+ break;
+ }
+ }
+ if (modelid == LPS_UNKNOWN) {
+ sr_err("Unable to detect model from model string '%s'!", buf);
+ return NULL;
+ }
+ }
+
+ /* Query version */
+ verstr = NULL;
+ if (lps_cmd_reply(buf, serial, "VERSION") == SR_OK) {
+ if (strncmp(buf, "Ver-", 4)) {
+ sr_spew("Version string %s not recognized.", buf);
+ goto exit_err;
+ }
+
+
+ g_strstrip(buf);
+ verstr = buf + 4;
+ }
+ else /* Bug in device FW 1.17: Quering version string fails while output is active.
+ Therefore just print an error message, but do not exit with error. */
+ sr_err("Failed to query for hardware version: %d %s", errno, strerror(errno));
+
+ sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR_MOTECH, models[modelid].modelstr, verstr);
+ sdi->driver = drv;
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+
+ devc = g_malloc0(sizeof(struct dev_context));
+ devc->model = &models[modelid];
+ devc->limit_samples = 0;
+ devc->limit_msec = 0;
+ devc->num_samples = 0;
+ devc->elapsed_msec = g_timer_new();
+
+ sdi->priv = devc;
+
+ /* Setup channels and channel groups. */
+ for (cnt = 0; cnt < models[modelid].num_channels; cnt++) {
+ snprintf(channel, sizeof(channel), "CH%d", cnt + 1);
+ ch = sr_channel_new(cnt, SR_CHANNEL_ANALOG, TRUE, channel);
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ devc->channel_status[cnt].info = g_slist_append(NULL, ch);
+
+ cg = g_malloc(sizeof(struct sr_channel_group));
+ snprintf(channel, sizeof(channel), "CG%d", cnt+1);
+ cg->name = g_strdup(channel);
+ cg->priv = NULL;
+ cg->channels = g_slist_append(NULL, ch);
+
+ sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+ }
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ /* Query status */
+ if (lps_query_status(sdi) != SR_OK)
+ goto exit_err;
+
+ serial_close(serial);
+ if (!devices)
+ sr_serial_dev_inst_free(serial);
+
+ return devices;
+
+exit_err:
+ sr_info("%s: Error!", __func__);
+
+ if (serial) {
+ serial_close(serial);
+ sr_serial_dev_inst_free(serial);
+ }
+ if (devc)
+ g_free(devc);
+ if (sdi)
+ sr_dev_inst_free(sdi);
+
+ return NULL;
+}
+
+/** Scan for LPS-301 device. */
+static GSList *scan_lps301(GSList *options)
+{
+ return do_scan(LPS_301, &motech_lps_301_driver_info, options);
+}
+
+static GSList *doDevList(struct sr_dev_driver *drv)
+{
+ return ((struct drv_context *)(drv->priv))->instances;
+}
+
+static GSList *dev_list_lps301(void)
+{
+ return doDevList(&motech_lps_301_driver_info);
+}
+
+static void dev_clear_private(struct dev_context* devc)
+{
+ int ch_idx;
+
+ /* Free channel_status.info (list only, data owned by sdi). */
+ for (ch_idx = 0; ch_idx < devc->model->num_channels; ch_idx++)
+ g_slist_free(devc->channel_status[ch_idx].info);
+
+ g_timer_destroy(devc->elapsed_msec);
+}
+
+static int dev_clear_lps301(void)
+{
+ return std_dev_clear(&motech_lps_301_driver_info, (std_dev_clear_callback)dev_clear_private);
+}
+
+static int cleanup(void)
+{
+ return dev_clear_lps301();
+}
+
+static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ int ch_idx;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ if (!cg) {
+ /* No channel group: global options. */
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ *data = g_variant_new_uint64(devc->limit_msec);
+ break;
+ case SR_CONF_OUTPUT_CHANNEL:
+ *data = g_variant_new_string(channel_modes[devc->tracking_mode]);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ } else {
+ /* We only ever have one channel per channel group in this driver. */
+ ch = cg->channels->data;
+ ch_idx = ch->index;
+ switch (key) {
+ case SR_CONF_OUTPUT_VOLTAGE:
+ *data = g_variant_new_double(devc->channel_status[ch_idx].output_voltage_last);
+ break;
+ case SR_CONF_OUTPUT_VOLTAGE_MAX:
+ *data = g_variant_new_double(devc->channel_status[ch_idx].output_voltage_max);
+ break;
+ case SR_CONF_OUTPUT_CURRENT:
+ *data = g_variant_new_double(devc->channel_status[ch_idx].output_current_last);
+ break;
+ case SR_CONF_OUTPUT_CURRENT_MAX:
+ *data = g_variant_new_double(devc->channel_status[ch_idx].output_current_max);
+ break;
+ case SR_CONF_OUTPUT_ENABLED:
+ *data = g_variant_new_boolean(devc->channel_status[ch_idx].output_enabled);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ gdouble dval;
+ int ch_idx;
+ const char *sval;
+ gboolean bval;
+ int idx;
+ gboolean found;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ /* Cannot change settings while acquisition active, would cause a mess with commands.
+ * Changing this would be possible, but tricky. */
+ if (devc->acq_running)
+ return SR_ERR_NA;
+
+ if (!cg) {
+ /* No channel group: global options. */
+ switch (key) {
+ case SR_CONF_LIMIT_MSEC:
+ if (g_variant_get_uint64(data) == 0) {
+ sr_err("LIMIT_MSEC can't be 0.");
+ return SR_ERR;
+ }
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ case SR_CONF_OUTPUT_CHANNEL:
+ sval = g_variant_get_string(data, NULL);
+ found = FALSE;
+ for (idx = 0; idx < (int)ARRAY_SIZE(channel_modes); idx++)
+ {
+ if (!strcmp(sval, channel_modes[idx])) {
+ found = TRUE;
+ if (devc->tracking_mode == idx)
+ break; /* Nothing to do! */
+ devc->tracking_mode = idx;
+ if (devc->model->modelid >= LPS_304) /* No use to set anything in the smaller models. */
+ return lps_cmd_ok(sdi->conn, "TRACK%1d", devc->tracking_mode);
+ }
+ if (devc->model->modelid <= LPS_303) /* Only first setting possible for smaller models. */
+ break;
+ }
+ if (!found) {
+ return SR_ERR_ARG;
+ }
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ } else {
+ /* Channel group specified: per-channel options. */
+ /* We only ever have one channel per channel group in this driver. */
+ ch = cg->channels->data;
+ ch_idx = ch->index;
+
+ switch (key) {
+ case SR_CONF_OUTPUT_VOLTAGE_MAX:
+ dval = g_variant_get_double(data);
+ if (dval < 0 || dval > devc->model->channels[ch_idx].voltage[1])
+ return SR_ERR_ARG;
+ if (ch_idx == 2) {
+ if (devc->model->modelid < LPS_304)
+ return SR_ERR_ARG;
+
+ if (fabs(dval - 5.000) <= 0.001)
+ dval = 5.0;
+ else if ((devc->model->modelid >= LPS_305) && (fabs(dval - 3.300) <= 0.001))
+ dval = 3.3;
+ else return SR_ERR_ARG;
+ }
+
+ devc->channel_status[ch_idx].output_voltage_max = dval;
+ if (ch_idx == 2)
+ return lps_cmd_ok(sdi->conn, "VDD%1.0f", trunc(dval));
+ else
+ return lps_cmd_ok(sdi->conn, "VSET%d %05.3f", ch_idx+1, dval);
+ break;
+ case SR_CONF_OUTPUT_CURRENT_MAX:
+ dval = g_variant_get_double(data);
+ if (dval < 0 || dval > devc->model->channels[ch_idx].current[1])
+ return SR_ERR_ARG;
+ if (ch_idx == 2) /* No current setting for CH3. */
+ return SR_ERR_NA;
+ devc->channel_status[ch_idx].output_current_max = dval;
+ return lps_cmd_ok(sdi->conn, "ISET%d %05.4f", ch_idx+1, dval);
+ break;
+ case SR_CONF_OUTPUT_ENABLED:
+ bval = g_variant_get_boolean(data);
+ if (bval == devc->channel_status[ch_idx].output_enabled) /* Nothing to do. */
+ break;
+ devc->channel_status[ch_idx].output_enabled = bval;
+ if (ch_idx != 2) { /* Channels 1,2 can be set only together. */
+ devc->channel_status[ch_idx^1].output_enabled = bval;
+ return lps_cmd_ok(sdi->conn, "OUT%1d", (int)bval);
+ } else { /* Channel 3: No command to disable output, set voltage to 0 instead. */
+ if (bval)
+ return lps_cmd_ok(sdi->conn, "VDD%1.0f", devc->channel_status[ch_idx].output_voltage_max);
+ else
+ return lps_cmd_ok(sdi->conn, "VDD0");
+ }
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ int ch_idx, i;
+ GVariant *gvar;
+ GVariantBuilder gvb;
+
+ (void)data;
+
+ /* Driver options, no device instance necessary. */
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ return SR_OK;
+ default:
+ if (sdi == NULL)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+ }
+
+ /* Device options, independant from channel groups. */
+ if (cg == NULL) {
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ return SR_OK;
+ case SR_CONF_OUTPUT_CHANNEL:
+ if (devc->model->modelid <= LPS_303) {
+ /* The 1-channel models. */
+ *data = g_variant_new_strv(channel_modes, 1);
+ } else {
+ /* The other models support all modes. */
+ *data = g_variant_new_strv(channel_modes, ARRAY_SIZE(channel_modes));
+ }
+ return SR_OK;
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ }
+
+ /* Device options, depending on channel groups. */
+ ch = cg->channels->data;
+ ch_idx = ch->index;
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ if ((ch_idx == 0) || (ch_idx == 1)) /* CH1, CH2 */
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps_ch12, ARRAY_SIZE(hwcaps_ch12), sizeof(int32_t));
+ else /* Must be CH3 */
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps_ch3, ARRAY_SIZE(hwcaps_ch3), sizeof(int32_t));
+ break;
+ case SR_CONF_OUTPUT_VOLTAGE_MAX:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ /* Min, max, step. */
+ for (i = 0; i < 3; i++) {
+ gvar = g_variant_new_double(devc->model->channels[ch_idx].voltage[i]);
+ g_variant_builder_add_value(&gvb, gvar);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_OUTPUT_CURRENT_MAX:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ /* Min, max, step. */
+ for (i = 0; i < 3; i++) {
+ gvar = g_variant_new_double(devc->model->channels[ch_idx].current[i]);
+ g_variant_builder_add_value(&gvb, gvar);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ devc->acq_running = TRUE;
+
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 50,
+ motech_lps_30x_receive_data, (void *)sdi);
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Start timer, if required. */
+ if (devc->limit_msec)
+ g_timer_start(devc->elapsed_msec);
+
+ devc->acq_req = AQ_NONE;
+ /* Do not start polling device here, the read function will do it in 50 ms. */
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+
+ /* Stop timer, if required. */
+ if (sdi && (devc = sdi->priv) && devc->limit_msec)
+ g_timer_stop(devc->elapsed_msec);
+
+ return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver motech_lps_301_driver_info = {
+ .name = "motech-lps-301",
+ .longname = "Motech LPS-301",
+ .api_version = 1,
+ .init = init_lps301,
+ .cleanup = cleanup,
+ .scan = scan_lps301,
+ .dev_list = dev_list_lps301,
+ .dev_clear = dev_clear_lps301,
+ .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 = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com> (code from atten-pps3xxx)
+ *
+ * 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/>.
+ */
+
+/** @file
+ * <em>Motech LPS-30x series</em> power supply driver
+ * @internal
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "protocol.h"
+
+/** Send data packets for current measurements. */
+static void send_data(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ int i;
+ float data[MAX_CHANNELS];
+
+ devc = sdi->priv;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+
+ analog.mq = SR_MQ_VOLTAGE;
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags = SR_MQFLAG_DC;
+ analog.data = data;
+ for (i = 0; i < devc->model->num_channels; i++)
+ analog.data[i] = devc->channel_status[i].output_voltage_last; /* Value always 3.3 or 5 for channel 3, if present! */
+ sr_session_send(sdi, &packet);
+
+ analog.mq = SR_MQ_CURRENT;
+ analog.unit = SR_UNIT_AMPERE;
+ analog.mqflags = 0;
+ analog.data = data;
+ for (i = 0; i < devc->model->num_channels; i++)
+ analog.data[i] = devc->channel_status[i].output_current_last; /* Value always 0 for channel 3, if present! */
+ sr_session_send(sdi, &packet);
+
+ devc->num_samples++;
+}
+
+/** Process a complete line (without CR/LF) in buf. */
+static void process_line(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ double dbl;
+ int auxint;
+
+ devc = sdi->priv;
+
+ switch (devc->acq_req_pending) {
+ case 0: /* Should not happen... */
+ break;
+ case 1: /* Waiting for data reply to request */
+ /* Convert numbers */
+ switch (devc->acq_req) {
+ case AQ_U1: case AQ_U2: case AQ_I1: case AQ_I2:
+ if (sr_atod(devc->buf, &dbl) != SR_OK) {
+ sr_err("Failed to convert '%s' to double, errno=%d %s",
+ devc->buf, errno, strerror(errno));
+ dbl = 0.0;
+ }
+ break;
+ case AQ_STATUS:
+ if (sr_atoi(devc->buf, &auxint) != SR_OK) {
+ sr_err("Failed to convert '%s' to int, errno=%d %s",
+ devc->buf, errno, strerror(errno));
+ auxint = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (devc->acq_req) {
+ case AQ_U1:
+ devc->channel_status[0].output_voltage_last = dbl;
+ break;
+ case AQ_I1:
+ devc->channel_status[0].output_current_last = dbl;
+ break;
+ case AQ_U2:
+ devc->channel_status[1].output_voltage_last = dbl;
+ break;
+ case AQ_I2:
+ devc->channel_status[1].output_current_last = dbl;
+ break;
+ case AQ_STATUS: /* Process status and generate data. */
+ if (lps_process_status(sdi, auxint) == SR_OK) {
+ send_data(sdi);
+ }
+ break;
+ default:
+ break;
+ }
+
+ devc->acq_req_pending = 2;
+ break;
+ case 2: /* Waiting for OK after request */
+ if (strcmp(devc->buf, "OK")) {
+ sr_err("Unexpected reply while waiting for OK: '%s'", devc->buf);
+ }
+ devc->acq_req_pending = 0;
+ break;
+ }
+
+ devc->buf[0] = '\0';
+ devc->buflen = 0;
+}
+
+
+SR_PRIV int motech_lps_30x_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int len;
+ gdouble elapsed_s;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+
+ if (revents == G_IO_IN) { /* Serial data arrived. */
+ while (LINELEN_MAX - devc->buflen - 2 > 0) {
+ len = serial_read(serial, devc->buf + devc->buflen, 1);
+ if (len < 1)
+ break;
+
+ /* Eliminate whitespace at beginning of line. */
+ if (g_ascii_isspace(devc->buf[0])) {
+ devc->buf[0] = '\0';
+ devc->buflen = 0;
+ continue;
+ }
+
+ devc->buflen += len;
+ devc->buf[devc->buflen] = '\0';
+
+ /* If line complete, process msg. */
+ if ((devc->buflen > 0) && ((devc->buf[devc->buflen-1] == '\r') || devc->buf[devc->buflen-1] == '\n')) {
+ devc->buflen--;
+ devc->buf[devc->buflen] = '\0';
+
+ sr_spew("Line complete: \"%s\"", devc->buf);
+ process_line(sdi);
+ }
+ }
+ }
+
+ /* If number of samples or time limit reached, stop acquisition. */
+ if (devc->limit_samples && (devc->num_samples >= devc->limit_samples))
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+
+ if (devc->limit_msec) {
+ elapsed_s = g_timer_elapsed(devc->elapsed_msec, NULL);
+ if ((elapsed_s * 1000) >= devc->limit_msec)
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ }
+
+ /* Request next packet, if required. */
+ if ((sdi->status == SR_ST_ACTIVE) && (devc->acq_running)){
+ if (devc->acq_req_pending) {
+ gint64 elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
+ if (elapsed_us > (REQ_TIMEOUT_MS * 1000)) {
+ sr_spew("Request timeout: req=%d t=%lldus", (int)devc->acq_req, elapsed_us);
+ devc->acq_req_pending = 0;
+ }
+ }
+ if (devc->acq_req_pending == 0) {
+ switch(devc->acq_req)
+ {
+ case AQ_NONE: /* Fall through */
+ case AQ_STATUS:
+ devc->acq_req = AQ_U1;
+ lps_send_req(serial, "VOUT1");
+ break;
+ case AQ_U1:
+ devc->acq_req = AQ_I1;
+ lps_send_req(serial, "IOUT1");
+ break;
+ case AQ_I1:
+ if (devc->model->num_channels == 1) {
+ devc->acq_req = AQ_STATUS;
+ lps_send_req(serial, "STATUS");
+ } else {
+ devc->acq_req = AQ_U2;
+ lps_send_req(serial, "VOUT2");
+ }
+ break;
+ case AQ_U2:
+ devc->acq_req = AQ_I2;
+ lps_send_req(serial, "IOUT2");
+ break;
+ case AQ_I2:
+ devc->acq_req = AQ_STATUS;
+ lps_send_req(serial, "STATUS");
+ break;
+ default:
+ sr_err("Illegal devc->acq_req=%d", devc->acq_req);
+ return SR_ERR;
+ }
+ devc->req_sent_at = g_get_real_time();
+ devc->acq_req_pending = 1;
+ }
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com> (code from atten-pps3xxx)
+ *
+ * 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/>.
+ */
+
+/** @file
+ * <em>Motech LPS-30x series</em> power supply driver
+ * @internal
+ */
+
+
+#ifndef LIBSIGROK_HARDWARE_MOTECH_LPS_30X_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_MOTECH_LPS_30X_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+
+SR_PRIV int lps_process_status(struct sr_dev_inst* sdi, int stat);
+SR_PRIV int lps_send_req(struct sr_serial_dev_inst *serial, const char* fmt, ...);
+
+#define LOG_PREFIX "motech-lps-30x"
+
+#define LINELEN_MAX 50 /**< Max. line length for requests */
+
+#define REQ_TIMEOUT_MS 250 /**< Timeout [ms] for single request. */
+
+#define MAX_CHANNELS 3
+
+typedef enum {
+ LPS_UNKNOWN = 0,/**< Unknown model (used during detection process) */
+ LPS_301, /**< Motech/Amrel LPS-301, 1 output */
+ LPS_302, /**< Motech/Amrel LPS-302, 1 output */
+ LPS_303, /**< Motech/Amrel LPS-303, 1 output */
+ LPS_304, /**< Motech/Amrel LPS-304, 3 outputs */
+ LPS_305, /**< Motech/Amrel LPS-305, 3 outputs */
+} lps_modelid;
+
+/** Channel specification */
+struct channel_spec {
+ /* Min, max, step. */
+ gdouble voltage[3];
+ gdouble current[3];
+};
+
+/** Model properties specification */
+struct lps_modelspec {
+ lps_modelid modelid;
+ const char* modelstr;
+ uint8_t num_channels;
+ struct channel_spec channels[3];
+};
+
+/** Used to implement a little state machine to query all required values in a row. */
+typedef enum {
+ AQ_NONE,
+ AQ_U1,
+ AQ_I1,
+ AQ_I2,
+ AQ_U2,
+ AQ_STATUS,
+} acquisition_req;
+
+/** Status of a single channel. */
+struct channel_status {
+ /* Channel information (struct channel_info*). data (struct) owned by sdi, just a reference to address a single channel. */
+ GSList* info;
+ /* Received from device. */
+ gdouble output_voltage_last;
+ gdouble output_current_last;
+ gboolean output_enabled; /**< Also used when set. */
+ gboolean cc_mode; /**< Constant current mode. If false, constant voltage mode. */
+ /* Set by frontend. */
+ gdouble output_voltage_max;
+ gdouble output_current_max;
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Model-specific information */
+ struct lps_modelspec* model;
+
+ /* Acquisition status */
+ gboolean acq_running; /**< Aquisition is running. */
+ uint64_t limit_samples; /**< Target number of samples */
+ uint64_t limit_msec; /**< Target sampling time */
+ acquisition_req acq_req; /**< Current request. */
+ uint8_t acq_req_pending; /**< Request pending. 0=none, 1=reply, 2=OK */
+
+ /* Operational state */
+ struct channel_status channel_status[MAX_CHANNELS];
+ guint8 tracking_mode; /**< 0=off, 1=Tracking from CH1, 2=Tracking from CH2. */
+
+ /* Temporary state across callbacks */
+ int64_t req_sent_at; /**< Request sent. */
+ uint64_t num_samples; /**< Current #samples for limit_samples */
+ GTimer *elapsed_msec; /**< Used for sampling with limit_msec */
+ gchar buf[LINELEN_MAX]; /**< Buffer for read callback */
+ int buflen; /**< Data len in buf */
+};
+
+SR_PRIV int motech_lps_30x_receive_data(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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/>.
+ */
+
+/** @file
+ * Norma DM9x0/Siemens B102x DMMs driver.
+ * @internal
+ */
+
+#include "protocol.h"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+#define BUF_MAX 50
+
+#define SERIALCOMM "4800/8n1/dtr=1/rts=0/flow=1"
+
+SR_PRIV struct sr_dev_driver norma_dmm_driver_info;
+SR_PRIV struct sr_dev_driver siemens_b102x_driver_info;
+
+static const char* get_brandstr(struct sr_dev_driver* drv)
+{
+ if (drv == &norma_dmm_driver_info)
+ return "Norma";
+ else
+ return "Siemens";
+}
+
+static const char* get_typestr(int type, struct sr_dev_driver* drv)
+{
+ static const char* nameref[5][2] = {
+ {"DM910", "B1024"},
+ {"DM920", "B1025"},
+ {"DM930", "B1026"},
+ {"DM940", "B1027"},
+ {"DM950", "B1028"}};
+
+ if ((type < 1) || (type > 5))
+ return "Unknown type!";
+
+ return nameref[type-1][(drv == &siemens_b102x_driver_info)];
+}
+
+static int init_norma_dmm(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, &norma_dmm_driver_info, LOG_PREFIX);
+}
+
+static int init_siemens_b102x(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, &siemens_b102x_driver_info, LOG_PREFIX);
+}
+
+static GSList *do_scan(struct sr_dev_driver* drv, GSList *options)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_config *src;
+ struct sr_channel *ch;
+ struct sr_serial_dev_inst *serial;
+ GSList *l, *devices;
+ int len, cnt;
+ const char *conn, *serialcomm;
+ char *buf;
+ char req[10];
+ int auxtype;
+
+ devices = NULL;
+ drvc = drv->priv;
+ drvc->instances = NULL;
+ 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 (!conn)
+ return NULL;
+ if (!serialcomm)
+ serialcomm = SERIALCOMM;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ serial_flush(serial);
+
+ if (!(buf = g_try_malloc(BUF_MAX))) {
+ sr_err("Serial buffer malloc failed.");
+ return NULL;
+ }
+
+ snprintf(req, sizeof(req), "%s\r\n",
+ nmadmm_requests[NMADMM_REQ_IDN].req_str);
+ g_usleep(150 * 1000); /* Wait a little to allow serial port to settle. */
+ for (cnt = 0; cnt < 7; cnt++) {
+ if (serial_write(serial, req, strlen(req)) == -1) {
+ sr_err("Unable to send identification request: %d %s.",
+ errno, strerror(errno));
+ return NULL;
+ }
+ len = BUF_MAX;
+ serial_readline(serial, &buf, &len, NMADMM_TIMEOUT_MS);
+ if (!len)
+ continue;
+ buf[BUF_MAX - 1] = '\0';
+
+ /* Match ID string, e.g. "1834 065 V1.06,IF V1.02" (DM950) */
+ if (g_regex_match_simple("^1834 [^,]*,IF V*", (char *)buf, 0, 0)) {
+ auxtype = xgittoint(buf[7]);
+ sr_spew("%s %s DMM %s detected!", get_brandstr(drv), get_typestr(auxtype, drv), buf + 9);
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
+ get_brandstr(drv), get_typestr(auxtype, drv), buf + 9)))
+ return NULL;
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+ devc->type = auxtype;
+ devc->version = g_strdup(&buf[9]);
+ devc->elapsed_msec = g_timer_new();
+
+ sdi->conn = serial;
+ sdi->priv = devc;
+ sdi->driver = drv;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE,
+ "P1")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ break;
+ }
+
+ /*
+ * The interface of the DM9x0 contains a cap that needs to
+ * charge for up to 10s before the interface works, if not
+ * powered externally. Therefore wait a little to improve
+ * chances.
+ */
+ if (cnt == 3) {
+ sr_info("Waiting 5s to allow interface to settle.");
+ g_usleep(5 * 1000 * 1000);
+ }
+ }
+
+ g_free(buf);
+
+ serial_close(serial);
+ if (!devices)
+ sr_serial_dev_inst_free(serial);
+
+ return devices;
+}
+
+static GSList *scan_norma_dmm(GSList *options)
+{
+ return do_scan(&norma_dmm_driver_info, options);
+}
+
+static GSList *scan_siemens_b102x(GSList *options)
+{
+ return do_scan(&siemens_b102x_driver_info, options);
+}
+
+static GSList *dev_list_norma_dmm(void)
+{
+ return ((struct drv_context *)(norma_dmm_driver_info.priv))->instances;
+}
+
+static GSList *dev_list_siemens_b102x(void)
+{
+ return ((struct drv_context *)(siemens_b102x_driver_info.priv))->instances;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ std_serial_dev_close(sdi);
+
+ /* Free dynamically allocated resources. */
+ if ((devc = sdi->priv) && devc->version) {
+ g_free(devc->version);
+ devc->version = NULL;
+ g_timer_destroy(devc->elapsed_msec);
+ }
+
+ return SR_OK;
+}
+
+static int cleanup_norma_dmm(void)
+{
+ return std_dev_clear(&norma_dmm_driver_info, NULL);
+}
+
+static int cleanup_siemens_b102x(void)
+{
+ return std_dev_clear(&siemens_b102x_driver_info, NULL);
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ switch (key) {
+ case SR_CONF_LIMIT_MSEC:
+ if (g_variant_get_uint64(data) == 0) {
+ sr_err("LIMIT_MSEC can't be 0.");
+ return SR_ERR;
+ }
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (!sdi || !cb_data || !(devc = sdi->priv))
+ return SR_ERR_BUG;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc->cb_data = cb_data;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Start timer, if required. */
+ if (devc->limit_msec)
+ g_timer_start(devc->elapsed_msec);
+
+ /* Poll every 100ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 100,
+ norma_dmm_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+
+ /* Stop timer, if required. */
+ if (sdi && (devc = sdi->priv) && devc->limit_msec)
+ g_timer_stop(devc->elapsed_msec);
+
+ return std_serial_dev_acquisition_stop(sdi, cb_data, dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver norma_dmm_driver_info = {
+ .name = "norma-dmm",
+ .longname = "Norma DM9x0 DMMs",
+ .api_version = 1,
+ .init = init_norma_dmm,
+ .cleanup = cleanup_norma_dmm,
+ .scan = scan_norma_dmm,
+ .dev_list = dev_list_norma_dmm,
+ .dev_clear = NULL,
+ .config_get = NULL,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = std_serial_dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
+
+
+SR_PRIV struct sr_dev_driver siemens_b102x_driver_info = {
+ .name = "siemens-b102x",
+ .longname = "Siemens B102x DMMs",
+ .api_version = 1,
+ .init = init_siemens_b102x,
+ .cleanup = cleanup_siemens_b102x,
+ .scan = scan_siemens_b102x,
+ .dev_list = dev_list_siemens_b102x,
+ .dev_clear = NULL,
+ .config_get = NULL,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = std_serial_dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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/>.
+ */
+
+/** @file
+ * Norma DM9x0/Siemens B102x DMMs driver.
+ * @internal
+ */
+
+#include "protocol.h"
+
+SR_PRIV const struct nmadmm_req nmadmm_requests[] = {
+ { NMADMM_REQ_IDN, "IDN?" },
+ { NMADMM_REQ_IDN, "STATUS?" },
+ { 0, NULL },
+};
+
+static int nma_send_req(const struct sr_dev_inst *sdi, int req, char *params)
+{
+ struct sr_serial_dev_inst *serial;
+ struct dev_context *devc;
+ char buf[NMADMM_BUFSIZE];
+ int len;
+
+ if (!sdi || !(serial = sdi->conn) || !(devc = sdi->priv))
+ return SR_ERR_BUG;
+
+ len = snprintf(buf, sizeof(buf), "%s%s\r\n",
+ nmadmm_requests[req].req_str, params ? params : "");
+
+ sr_spew("Sending request: '%s'.", buf);
+
+ devc->last_req = req;
+ devc->last_req_pending = TRUE;
+
+ if (serial_write(serial, buf, len) == -1) {
+ sr_err("Unable to send request: %d %s.",
+ errno, strerror(errno));
+ devc->last_req_pending = FALSE;
+ return SR_ERR;
+ }
+
+ devc->req_sent_at = g_get_monotonic_time();
+
+ return SR_OK;
+}
+
+/**
+ * Convert hexadecimal digit to int.
+ *
+ * @param[in] xgit Hexadecimal digit to convert.
+ * @return Int value of xgit (0 on invalid xgit).
+ */
+SR_PRIV int xgittoint(char xgit)
+{
+ if ((xgit >= '0') && (xgit <= '9'))
+ return xgit - '0';
+ xgit = tolower(xgit);
+ if ((xgit >= 'a') && (xgit <= 'f'))
+ return xgit - 'a';
+ return 0;
+}
+
+/**
+ * Process received line. It consists of 20 hex digits + \\r\\n,
+ * e.g. '08100400018100400000'.
+ */
+static void nma_process_line(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int pos, flags;
+ int vt, range; /* Measurement value type, range in device format */
+ int mmode, devstat; /* Measuring mode, device status */
+ float value; /* Measured value */
+ float scale; /* Scaling factor depending on range and function */
+ struct sr_datafeed_analog analog;
+ struct sr_datafeed_packet packet;
+
+ devc = sdi->priv;
+
+ devc->buf[20] = '\0';
+
+ sr_spew("Received line '%s'.", devc->buf);
+
+ /* Check line. */
+ if (strlen((const char *)devc->buf) != 20) {
+ sr_err("line: Invalid status '%s', must be 20 hex digits.",
+ devc->buf);
+ devc->buflen = 0;
+ return;
+ }
+
+ for (pos = 0; pos < 20; pos++) {
+ if (!isxdigit(devc->buf[pos])) {
+ sr_err("line: Expected hex digit in '%s' at pos %d!",
+ devc->buf, pos);
+ devc->buflen = 0;
+ return;
+ }
+ }
+
+ /* Start decoding. */
+ value = 0.0;
+ scale = 1.0;
+ memset(&analog, 0, sizeof(analog));
+
+ /*
+ * The numbers are hex digits, starting from 0.
+ * 0: Keyboard status, currently not interesting.
+ * 1: Central switch status, currently not interesting.
+ * 2: Type of measured value.
+ */
+ vt = xgittoint(devc->buf[2]);
+ switch (vt) {
+ case 0:
+ analog.mq = SR_MQ_VOLTAGE;
+ break;
+ case 1:
+ analog.mq = SR_MQ_CURRENT; /* 2A */
+ break;
+ case 2:
+ analog.mq = SR_MQ_RESISTANCE;
+ break;
+ case 3:
+ analog.mq = SR_MQ_CAPACITANCE;
+ break;
+ case 4:
+ analog.mq = SR_MQ_TEMPERATURE;
+ break;
+ case 5:
+ analog.mq = SR_MQ_FREQUENCY;
+ break;
+ case 6:
+ analog.mq = SR_MQ_CURRENT; /* 10A */
+ break;
+ case 7:
+ analog.mq = SR_MQ_GAIN; /* TODO: Scale factor */
+ break;
+ case 8:
+ analog.mq = SR_MQ_GAIN; /* Percentage */
+ scale /= 100.0;
+ break;
+ case 9:
+ analog.mq = SR_MQ_GAIN; /* dB */
+ scale /= 100.0;
+ break;
+ default:
+ sr_err("Unknown value type: 0x%02x.", vt);
+ break;
+ }
+
+ /* 3: Measurement range for measured value */
+ range = xgittoint(devc->buf[3]);
+ switch (vt) {
+ case 0: /* V */
+ scale *= pow(10.0, range - 5);
+ break;
+ case 1: /* A */
+ scale *= pow(10.0, range - 7);
+ break;
+ case 2: /* Ω */
+ scale *= pow(10.0, range - 2);
+ break;
+ case 3: /* F */
+ scale *= pow(10.0, range - 12);
+ break;
+ case 4: /* °C */
+ scale *= pow(10.0, range - 1);
+ break;
+ case 5: /* Hz */
+ scale *= pow(10.0, range - 2);
+ break;
+ // No default, other value types have fixed display format.
+ }
+
+ /* 5: Sign and 1st digit */
+ flags = xgittoint(devc->buf[5]);
+ value = (flags & 0x03);
+ if (flags & 0x04)
+ scale *= -1;
+
+ /* 6-9: 2nd-4th digit */
+ for (pos = 6; pos < 10; pos++)
+ value = value * 10 + xgittoint(devc->buf[pos]);
+ value *= scale;
+
+ /* 10: Display counter */
+ mmode = xgittoint(devc->buf[10]);
+ switch (mmode) {
+ case 0: /* Frequency */
+ analog.unit = SR_UNIT_HERTZ;
+ break;
+ case 1: /* V TRMS, only type 5 */
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS);
+ break;
+ case 2: /* V AC */
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags |= SR_MQFLAG_AC;
+ if (devc->type >= 3)
+ analog.mqflags |= SR_MQFLAG_RMS;
+ break;
+ case 3: /* V DC */
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags |= SR_MQFLAG_DC;
+ break;
+ case 4: /* Ohm */
+ analog.unit = SR_UNIT_OHM;
+ break;
+ case 5: /* Continuity */
+ analog.unit = SR_UNIT_BOOLEAN;
+ analog.mq = SR_MQ_CONTINUITY;
+ /* TODO: Continuity handling is a bit odd in libsigrok. */
+ break;
+ case 6: /* Degree Celsius */
+ analog.unit = SR_UNIT_CELSIUS;
+ break;
+ case 7: /* Capacity */
+ analog.unit = SR_UNIT_FARAD;
+ break;
+ case 8: /* Current DC */
+ analog.unit = SR_UNIT_AMPERE;
+ analog.mqflags |= SR_MQFLAG_DC;
+ break;
+ case 9: /* Current AC */
+ analog.unit = SR_UNIT_AMPERE;
+ analog.mqflags |= SR_MQFLAG_AC;
+ if (devc->type >= 3)
+ analog.mqflags |= SR_MQFLAG_RMS;
+ break;
+ case 0xa: /* Current TRMS, only type 5 */
+ analog.unit = SR_UNIT_AMPERE;
+ analog.mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS);
+ break;
+ case 0xb: /* Diode */
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags |= (SR_MQFLAG_DIODE | SR_MQFLAG_DC);
+ break;
+ default:
+ sr_err("Unknown mmode: 0x%02x.", mmode);
+ break;
+ }
+
+ /* 11: Device status */
+ devstat = xgittoint(devc->buf[11]);
+
+ switch (devstat) {
+ case 1: /* Normal measurement */
+ break;
+ case 2: /* Input loop (limit, reference values) */
+ break;
+ case 3: /* TRANS/SENS */
+ break;
+ case 4: /* Error */
+ sr_err("Device error. Fuse?"); /* TODO: Really abort? */
+ devc->buflen = 0;
+ return; /* Cannot continue. */
+ default:
+ sr_err("Unknown device status: 0x%02x", devstat);
+ break;
+ }
+
+ /* 12-19: Flags and display symbols */
+ /* 12, 13 */
+ flags = (xgittoint(devc->buf[12]) << 8) | xgittoint(devc->buf[13]);
+ /* 0x80: PRINT TODO: Stop polling when discovered? */
+ /* 0x40: EXTR */
+ if (analog.mq == SR_MQ_CONTINUITY) {
+ if (flags & 0x20)
+ value = 1.0; /* Beep */
+ else
+ value = 0.0;
+ }
+ /* 0x10: AVG */
+ /* 0x08: Diode */
+ if (flags & 0x04) /* REL */
+ analog.mqflags |= SR_MQFLAG_RELATIVE;
+ /* 0x02: SHIFT */
+ if (flags & 0x01) /* % */
+ analog.unit = SR_UNIT_PERCENTAGE;
+
+ /* 14, 15 */
+ flags = (xgittoint(devc->buf[14]) << 8) | xgittoint(devc->buf[15]);
+ if (!(flags & 0x80)) /* MAN: Manual range */
+ analog.mqflags |= SR_MQFLAG_AUTORANGE;
+ if (flags & 0x40) /* LOBATT1: Low battery, measurement still within specs */
+ devc->lowbatt = 1;
+ /* 0x20: PEAK */
+ /* 0x10: COUNT */
+ if (flags & 0x08) /* HOLD */
+ analog.mqflags |= SR_MQFLAG_HOLD;
+ /* 0x04: LIMIT */
+ if (flags & 0x02) /* MAX */
+ analog.mqflags |= SR_MQFLAG_MAX;
+ if (flags & 0x01) /* MIN */
+ analog.mqflags |= SR_MQFLAG_MIN;
+
+ /* 16, 17 */
+ flags = (xgittoint(devc->buf[16]) << 8) | xgittoint(devc->buf[17]);
+ /* 0xe0: undefined */
+ if (flags & 0x10) { /* LOBATT2: Low battery, measurement inaccurate */
+ devc->lowbatt = 2;
+ sr_warn("Low battery, measurement quality degraded!");
+ }
+ /* 0x08: SCALED */
+ /* 0x04: RATE (=lower resolution, allows higher rata rate up to 10/s. */
+ /* 0x02: Current clamp */
+ if (flags & 0x01) { /* dB */
+ /*
+ * TODO: The Norma has an adjustable dB reference value. If
+ * changed from default, this is not correct.
+ */
+ if (analog.unit == SR_UNIT_VOLT)
+ analog.unit = SR_UNIT_DECIBEL_VOLT;
+ else
+ analog.unit = SR_UNIT_UNITLESS;
+ }
+
+ /* 18, 19 */
+ /* flags = (xgittoint(devc->buf[18]) << 8) | xgittoint(devc->buf[19]); */
+ /* 0x80: Undefined. */
+ /* 0x40: Remote mode, keyboard locked */
+ /* 0x38: Undefined. */
+ /* 0x04: MIN > MAX */
+ /* 0x02: Measured value < Min */
+ /* 0x01: Measured value > Max */
+
+ /* 4: Flags. Evaluating this after setting value! */
+ flags = xgittoint(devc->buf[4]);
+ if (flags & 0x04) /* Invalid value */
+ value = NAN;
+ else if (flags & 0x01) /* Overload */
+ value = INFINITY;
+ if (flags & 0x02) { /* Duplicate value, has been sent before. */
+ sr_spew("Duplicate value, dismissing!");
+ devc->buflen = 0;
+ return;
+ }
+
+ sr_spew("range=%d/scale=%f/value=%f", range,
+ (double)scale, (double)value);
+
+ /* Finish and send packet. */
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+ analog.data = &value;
+
+ memset(&packet, 0, sizeof(packet));
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+
+ /* Finish processing. */
+ devc->num_samples++;
+ devc->buflen = 0;
+}
+
+SR_PRIV int norma_dmm_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int len;
+ gboolean terminating;
+ gdouble elapsed_s;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+ if (revents == G_IO_IN) {
+ /* Serial data arrived. */
+ while (NMADMM_BUFSIZE - devc->buflen - 1 > 0) {
+ len = serial_read(serial, devc->buf + devc->buflen, 1);
+ if (len < 1)
+ break;
+ devc->buflen += len;
+ *(devc->buf + devc->buflen) = '\0';
+ if (*(devc->buf + devc->buflen - 1) == '\n') {
+ /*
+ * TODO: According to specs, should be \r, but
+ * then we'd have to get rid of the \n.
+ */
+ devc->last_req_pending = FALSE;
+ nma_process_line(sdi);
+ break;
+ }
+ }
+ }
+
+ /* If number of samples or time limit reached, stop acquisition. */
+ terminating = FALSE;
+ if (devc->limit_samples && (devc->num_samples >= devc->limit_samples)) {
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ terminating = TRUE;
+ }
+
+ if (devc->limit_msec) {
+ elapsed_s = g_timer_elapsed(devc->elapsed_msec, NULL);
+ if ((elapsed_s * 1000) >= devc->limit_msec) {
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ terminating = TRUE;
+ }
+ }
+
+ /* Request next package. */
+ if (!terminating) {
+ if (devc->last_req_pending) {
+ gint64 elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
+ if (elapsed_us > NMADMM_TIMEOUT_MS * 1000) {/* Timeout! */
+ sr_spew("Request timeout!");
+ devc->last_req_pending = FALSE;
+ }
+ }
+ if (!devc->last_req_pending) {
+ if (nma_send_req(sdi, NMADMM_REQ_STATUS, NULL) != SR_OK)
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Matthias Heidbrink <m-sigrok@heidbrink.biz>
+ *
+ * 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_NORMA_DMM_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_NORMA_DMM_PROTOCOL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/** @file
+ * Norma DM9x0/Siemens B102x DMMs driver.
+ * @internal
+ */
+
+#define LOG_PREFIX "norma-dmm"
+
+#define NMADMM_BUFSIZE 256
+
+#define NMADMM_TIMEOUT_MS 2000 /**< Request timeout. */
+
+/** Norma DMM request types (used ones only, the DMMs support about 50). */
+enum {
+ NMADMM_REQ_IDN = 0, /**< Request identity */
+ NMADMM_REQ_STATUS, /**< Request device status (value + ...) */
+};
+
+/** Defines requests used to communicate with device. */
+struct nmadmm_req {
+ int req_type; /**< Request type. */
+ const char *req_str; /**< Request string. */
+};
+
+/** Strings for requests. */
+extern const struct nmadmm_req nmadmm_requests[];
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Model-specific information */
+ char *version; /**< Version string */
+ int type; /**< DM9x0, e.g. 5 = DM950 */
+
+ /* Acquisition settings */
+ uint64_t limit_samples; /**< Target number of samples */
+ uint64_t limit_msec; /**< Target sampling time */
+
+ /* Opaque pointer passed in by frontend. */
+ void *cb_data;
+
+ /* Operational state */
+ int last_req; /**< Last request. */
+ int64_t req_sent_at; /**< Request sent. */
+ gboolean last_req_pending; /**< Last request not answered yet. */
+ int lowbatt; /**< Low battery. 1=low, 2=critical. */
+
+ /* Temporary state across callbacks */
+ uint64_t num_samples; /**< Current #samples. */
+ GTimer *elapsed_msec; /**< Used for limit_msec */
+ uint8_t buf[NMADMM_BUFSIZE]; /**< Buffer for read callback */
+ int buflen; /**< Data len in buf */
+};
+
+SR_PRIV int norma_dmm_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int xgittoint(char xgit);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 "protocol.h"
+#include <libserialport.h>
+
+#define SERIALCOMM "115200/8n1"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_TRIGGER_MATCH,
+ SR_CONF_CAPTURE_RATIO,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_EXTERNAL_CLOCK,
+ SR_CONF_PATTERN_MODE,
+ SR_CONF_SWAP,
+ SR_CONF_RLE,
+};
+
+static const int32_t trigger_matches[] = {
+ SR_TRIGGER_ZERO,
+ SR_TRIGGER_ONE,
+};
+
+#define STR_PATTERN_NONE "None"
+#define STR_PATTERN_EXTERNAL "External"
+#define STR_PATTERN_INTERNAL "Internal"
+
+/* Supported methods of test pattern outputs */
+enum {
+ /**
+ * Capture pins 31:16 (unbuffered wing) output a test pattern
+ * that can captured on pins 0:15.
+ */
+ PATTERN_EXTERNAL,
+
+ /** Route test pattern internally to capture buffer. */
+ PATTERN_INTERNAL,
+};
+
+static const char *patterns[] = {
+ STR_PATTERN_NONE,
+ STR_PATTERN_EXTERNAL,
+ STR_PATTERN_INTERNAL,
+};
+
+/* Channels are numbered 0-31 (on the PCB silkscreen). */
+SR_PRIV const char *ols_channel_names[NUM_CHANNELS + 1] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+ "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23",
+ "24", "25", "26", "27", "28", "29", "30", "31",
+ NULL,
+};
+
+/* Default supported samplerates, can be overridden by device metadata. */
+static const uint64_t samplerates[] = {
+ SR_HZ(10),
+ SR_MHZ(200),
+ SR_HZ(1),
+};
+
+SR_PRIV struct sr_dev_driver ols_driver_info;
+static struct sr_dev_driver *di = &ols_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct sr_config *src;
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ struct sr_serial_dev_inst *serial;
+ GPollFD probefd;
+ GSList *l, *devices;
+ int ret, i;
+ const char *conn, *serialcomm;
+ char buf[8];
+
+ drvc = di->priv;
+
+ devices = NULL;
+
+ 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 (!conn)
+ return NULL;
+
+ if (serialcomm == NULL)
+ serialcomm = SERIALCOMM;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ /* The discovery procedure is like this: first send the Reset
+ * command (0x00) 5 times, since the device could be anywhere
+ * in a 5-byte command. Then send the ID command (0x02).
+ * If the device responds with 4 bytes ("OLS1" or "SLA1"), we
+ * have a match.
+ */
+ sr_info("Probing %s.", conn);
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ ret = SR_OK;
+ for (i = 0; i < 5; i++) {
+ if ((ret = send_shortcommand(serial, CMD_RESET)) != SR_OK) {
+ sr_err("Port %s is not writable.", conn);
+ break;
+ }
+ }
+ if (ret != SR_OK) {
+ serial_close(serial);
+ sr_err("Could not use port %s. Quitting.", conn);
+ return NULL;
+ }
+ send_shortcommand(serial, CMD_ID);
+
+ /* Wait 10ms for a response. */
+ g_usleep(10000);
+
+ sp_get_port_handle(serial->data, &probefd.fd);
+ probefd.events = G_IO_IN;
+ g_poll(&probefd, 1, 1);
+
+ if (probefd.revents != G_IO_IN)
+ return NULL;
+ if (serial_read_blocking(serial, buf, 4) != 4)
+ return NULL;
+ if (strncmp(buf, "1SLO", 4) && strncmp(buf, "1ALS", 4))
+ return NULL;
+
+ /* Definitely using the OLS protocol, check if it supports
+ * the metadata command.
+ */
+ send_shortcommand(serial, CMD_METADATA);
+ if (g_poll(&probefd, 1, 10) > 0) {
+ /* Got metadata. */
+ sdi = get_metadata(serial);
+ sdi->index = 0;
+ devc = sdi->priv;
+ } else {
+ /* Not an OLS -- some other board that uses the sump protocol. */
+ sr_info("Device does not support metadata.");
+ sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
+ "Sump", "Logic Analyzer", "v1.0");
+ sdi->driver = di;
+ for (i = 0; i < 32; i++) {
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
+ ols_channel_names[i])))
+ return 0;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+ devc = ols_dev_new();
+ sdi->priv = devc;
+ }
+ /* Configure samplerate and divider. */
+ if (ols_set_samplerate(sdi, DEFAULT_SAMPLERATE) != SR_OK)
+ sr_dbg("Failed to set default samplerate (%"PRIu64").",
+ DEFAULT_SAMPLERATE);
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ serial_close(serial);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ *data = g_variant_new_uint64(devc->cur_samplerate);
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ *data = g_variant_new_uint64(devc->capture_ratio);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_PATTERN_MODE:
+ if (devc->flag_reg & FLAG_EXTERNAL_TEST_MODE)
+ *data = g_variant_new_string(STR_PATTERN_EXTERNAL);
+ else if (devc->flag_reg & FLAG_INTERNAL_TEST_MODE)
+ *data = g_variant_new_string(STR_PATTERN_INTERNAL);
+ else
+ *data = g_variant_new_string(STR_PATTERN_NONE);
+ break;
+ case SR_CONF_RLE:
+ *data = g_variant_new_boolean(devc->flag_reg & FLAG_RLE ? TRUE : FALSE);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ uint16_t flag;
+ uint64_t tmp_u64;
+ int ret;
+ const char *stropt;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ tmp_u64 = g_variant_get_uint64(data);
+ if (tmp_u64 < samplerates[0] || tmp_u64 > samplerates[1])
+ return SR_ERR_SAMPLERATE;
+ ret = ols_set_samplerate(sdi, g_variant_get_uint64(data));
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ tmp_u64 = g_variant_get_uint64(data);
+ if (tmp_u64 < MIN_NUM_SAMPLES)
+ return SR_ERR;
+ devc->limit_samples = tmp_u64;
+ ret = SR_OK;
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ devc->capture_ratio = g_variant_get_uint64(data);
+ if (devc->capture_ratio < 0 || devc->capture_ratio > 100) {
+ devc->capture_ratio = 0;
+ ret = SR_ERR;
+ } else
+ ret = SR_OK;
+ break;
+ case SR_CONF_EXTERNAL_CLOCK:
+ if (g_variant_get_boolean(data)) {
+ sr_info("Enabling external clock.");
+ devc->flag_reg |= FLAG_CLOCK_EXTERNAL;
+ } else {
+ sr_info("Disabled external clock.");
+ devc->flag_reg &= ~FLAG_CLOCK_EXTERNAL;
+ }
+ ret = SR_OK;
+ break;
+ case SR_CONF_PATTERN_MODE:
+ stropt = g_variant_get_string(data, NULL);
+ ret = SR_OK;
+ flag = 0xffff;
+ if (!strcmp(stropt, STR_PATTERN_NONE)) {
+ sr_info("Disabling test modes.");
+ flag = 0x0000;
+ }else if (!strcmp(stropt, STR_PATTERN_INTERNAL)) {
+ sr_info("Enabling internal test mode.");
+ flag = FLAG_INTERNAL_TEST_MODE;
+ } else if (!strcmp(stropt, STR_PATTERN_EXTERNAL)) {
+ sr_info("Enabling external test mode.");
+ flag = FLAG_EXTERNAL_TEST_MODE;
+ } else {
+ ret = SR_ERR;
+ }
+ if (flag != 0xffff) {
+ devc->flag_reg &= ~(FLAG_INTERNAL_TEST_MODE | FLAG_EXTERNAL_TEST_MODE);
+ devc->flag_reg |= flag;
+ }
+ break;
+ case SR_CONF_SWAP:
+ if (g_variant_get_boolean(data)) {
+ sr_info("Enabling channel swapping.");
+ devc->flag_reg |= FLAG_SWAP_CHANNELS;
+ } else {
+ sr_info("Disabling channel swapping.");
+ devc->flag_reg &= ~FLAG_SWAP_CHANNELS;
+ }
+ ret = SR_OK;
+ break;
+
+ case SR_CONF_RLE:
+ if (g_variant_get_boolean(data)) {
+ sr_info("Enabling RLE.");
+ devc->flag_reg |= FLAG_RLE;
+ } else {
+ sr_info("Disabling RLE.");
+ devc->flag_reg &= ~FLAG_RLE;
+ }
+ ret = SR_OK;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ GVariant *gvar, *grange[2];
+ GVariantBuilder gvb;
+ int num_ols_changrp, i;
+
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
+ ARRAY_SIZE(samplerates), sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ trigger_matches, ARRAY_SIZE(trigger_matches),
+ sizeof(int32_t));
+ break;
+ case SR_CONF_PATTERN_MODE:
+ *data = g_variant_new_strv(patterns, ARRAY_SIZE(patterns));
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ if (devc->flag_reg & FLAG_RLE)
+ return SR_ERR_NA;
+ if (devc->max_samples == 0)
+ /* Device didn't specify sample memory size in metadata. */
+ return SR_ERR_NA;
+ /*
+ * Channel groups are turned off if no channels in that group are
+ * enabled, making more room for samples for the enabled group.
+ */
+ ols_channel_mask(sdi);
+ num_ols_changrp = 0;
+ for (i = 0; i < 4; i++) {
+ if (devc->channel_mask & (0xff << (i * 8)))
+ num_ols_changrp++;
+ }
+ grange[0] = g_variant_new_uint64(MIN_NUM_SAMPLES);
+ if (num_ols_changrp)
+ grange[1] = g_variant_new_uint64(devc->max_samples / num_ols_changrp);
+ else
+ grange[1] = g_variant_new_uint64(MIN_NUM_SAMPLES);
+ *data = g_variant_new_tuple(grange, 2);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int set_trigger(const struct sr_dev_inst *sdi, int stage)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ uint8_t cmd, arg[4];
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+
+ cmd = CMD_SET_TRIGGER_MASK + stage * 4;
+ arg[0] = devc->trigger_mask[stage] & 0xff;
+ arg[1] = (devc->trigger_mask[stage] >> 8) & 0xff;
+ arg[2] = (devc->trigger_mask[stage] >> 16) & 0xff;
+ arg[3] = (devc->trigger_mask[stage] >> 24) & 0xff;
+ if (send_longcommand(serial, cmd, arg) != SR_OK)
+ return SR_ERR;
+
+ cmd = CMD_SET_TRIGGER_VALUE + stage * 4;
+ arg[0] = devc->trigger_value[stage] & 0xff;
+ arg[1] = (devc->trigger_value[stage] >> 8) & 0xff;
+ arg[2] = (devc->trigger_value[stage] >> 16) & 0xff;
+ arg[3] = (devc->trigger_value[stage] >> 24) & 0xff;
+ if (send_longcommand(serial, cmd, arg) != SR_OK)
+ return SR_ERR;
+
+ cmd = CMD_SET_TRIGGER_CONFIG + stage * 4;
+ arg[0] = arg[1] = arg[3] = 0x00;
+ arg[2] = stage;
+ if (stage == devc->num_stages)
+ /* Last stage, fire when this one matches. */
+ arg[3] |= TRIGGER_START;
+ if (send_longcommand(serial, cmd, arg) != SR_OK)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ uint16_t samplecount, readcount, delaycount;
+ uint8_t ols_changrp_mask, arg[4];
+ int num_ols_changrp;
+ int ret, i;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+
+ ols_channel_mask(sdi);
+
+ num_ols_changrp = 0;
+ ols_changrp_mask = 0;
+ for (i = 0; i < 4; i++) {
+ if (devc->channel_mask & (0xff << (i * 8))) {
+ ols_changrp_mask |= (1 << i);
+ num_ols_changrp++;
+ }
+ }
+
+ /*
+ * Limit readcount to prevent reading past the end of the hardware
+ * buffer.
+ */
+ samplecount = MIN(devc->max_samples / num_ols_changrp, devc->limit_samples);
+ readcount = samplecount / 4;
+
+ /* Rather read too many samples than too few. */
+ if (samplecount % 4 != 0)
+ readcount++;
+
+ /* Basic triggers. */
+ if (ols_convert_trigger(sdi) != SR_OK) {
+ sr_err("Failed to configure channels.");
+ return SR_ERR;
+ }
+ if (devc->num_stages > 0) {
+ delaycount = readcount * (1 - devc->capture_ratio / 100.0);
+ devc->trigger_at = (readcount - delaycount) * 4 - devc->num_stages;
+ for (i = 0; i <= devc->num_stages; i++) {
+ sr_dbg("Setting OLS stage %d trigger.", i);
+ if ((ret = set_trigger(sdi, i)) != SR_OK)
+ return ret;
+ }
+ } else {
+ /* No triggers configured, force trigger on first stage. */
+ sr_dbg("Forcing trigger at stage 0.");
+ if ((ret = set_trigger(sdi, 0)) != SR_OK)
+ return ret;
+ delaycount = readcount;
+ }
+
+ /* Samplerate. */
+ sr_dbg("Setting samplerate to %" PRIu64 "Hz (divider %u)",
+ devc->cur_samplerate, devc->cur_samplerate_divider);
+ arg[0] = devc->cur_samplerate_divider & 0xff;
+ arg[1] = (devc->cur_samplerate_divider & 0xff00) >> 8;
+ arg[2] = (devc->cur_samplerate_divider & 0xff0000) >> 16;
+ arg[3] = 0x00;
+ if (send_longcommand(serial, CMD_SET_DIVIDER, arg) != SR_OK)
+ return SR_ERR;
+
+ /* Send sample limit and pre/post-trigger capture ratio. */
+ sr_dbg("Setting sample limit %d, trigger point at %d",
+ (readcount - 1) * 4, (delaycount - 1) * 4);
+ arg[0] = ((readcount - 1) & 0xff);
+ arg[1] = ((readcount - 1) & 0xff00) >> 8;
+ arg[2] = ((delaycount - 1) & 0xff);
+ arg[3] = ((delaycount - 1) & 0xff00) >> 8;
+ if (send_longcommand(serial, CMD_CAPTURE_SIZE, arg) != SR_OK)
+ return SR_ERR;
+
+ /* Flag register. */
+ sr_dbg("Setting intpat %s, extpat %s, RLE %s, noise_filter %s, demux %s",
+ devc->flag_reg & FLAG_INTERNAL_TEST_MODE ? "on": "off",
+ devc->flag_reg & FLAG_EXTERNAL_TEST_MODE ? "on": "off",
+ devc->flag_reg & FLAG_RLE ? "on" : "off",
+ devc->flag_reg & FLAG_FILTER ? "on": "off",
+ devc->flag_reg & FLAG_DEMUX ? "on" : "off");
+ /*
+ * Enable/disable OLS channel groups in the flag register according
+ * to the channel mask. 1 means "disable channel".
+ */
+ devc->flag_reg |= ~(ols_changrp_mask << 2) & 0x3c;
+ arg[0] = devc->flag_reg & 0xff;
+ arg[1] = devc->flag_reg >> 8;
+ arg[2] = arg[3] = 0x00;
+ if (send_longcommand(serial, CMD_SET_FLAGS, arg) != SR_OK)
+ return SR_ERR;
+
+ /* Start acquisition on the device. */
+ if (send_shortcommand(serial, CMD_RUN) != SR_OK)
+ return SR_ERR;
+
+ /* Reset all operational states. */
+ devc->rle_count = devc->num_transfers = 0;
+ devc->num_samples = devc->num_bytes = 0;
+ devc->cnt_bytes = devc->cnt_samples = devc->cnt_samples_rle = 0;
+ memset(devc->sample, 0, 4);
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ serial_source_add(sdi->session, serial, G_IO_IN, -1,
+ ols_receive_data, cb_data);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ (void)cb_data;
+
+ abort_acquisition(sdi);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver ols_driver_info = {
+ .name = "ols",
+ .longname = "Openbench Logic Sniffer",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .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 = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 "protocol.h"
+#include <libserialport.h>
+
+extern SR_PRIV struct sr_dev_driver ols_driver_info;
+static struct sr_dev_driver *di = &ols_driver_info;
+
+SR_PRIV int send_shortcommand(struct sr_serial_dev_inst *serial,
+ uint8_t command)
+{
+ char buf[1];
+
+ sr_dbg("Sending cmd 0x%.2x.", command);
+ buf[0] = command;
+ if (serial_write_blocking(serial, buf, 1) != 1)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+SR_PRIV int send_longcommand(struct sr_serial_dev_inst *serial,
+ uint8_t command, uint8_t *data)
+{
+ char buf[5];
+
+ sr_dbg("Sending cmd 0x%.2x data 0x%.2x%.2x%.2x%.2x.", command,
+ data[0], data[1], data[2], data[3]);
+ buf[0] = command;
+ buf[1] = data[0];
+ buf[2] = data[1];
+ buf[3] = data[2];
+ buf[4] = data[3];
+ if (serial_write_blocking(serial, buf, 5) != 5)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+/* Configures the channel mask based on which channels are enabled. */
+SR_PRIV void ols_channel_mask(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_channel *channel;
+ const GSList *l;
+
+ devc = sdi->priv;
+
+ devc->channel_mask = 0;
+ for (l = sdi->channels; l; l = l->next) {
+ channel = l->data;
+ if (channel->enabled)
+ devc->channel_mask |= 1 << channel->index;
+ }
+}
+
+SR_PRIV int ols_convert_trigger(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_trigger *trigger;
+ struct sr_trigger_stage *stage;
+ struct sr_trigger_match *match;
+ const GSList *l, *m;
+ int i;
+
+ devc = sdi->priv;
+
+ devc->num_stages = 0;
+ for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
+ devc->trigger_mask[i] = 0;
+ devc->trigger_value[i] = 0;
+ }
+
+ if (!(trigger = sr_session_trigger_get(sdi->session)))
+ return SR_OK;
+
+ devc->num_stages = g_slist_length(trigger->stages);
+ if (devc->num_stages > NUM_TRIGGER_STAGES) {
+ sr_err("This device only supports %d trigger stages.",
+ NUM_TRIGGER_STAGES);
+ return SR_ERR;
+ }
+
+ for (l = trigger->stages; l; l = l->next) {
+ stage = l->data;
+ for (m = stage->matches; m; m = m->next) {
+ match = m->data;
+ if (!match->channel->enabled)
+ /* Ignore disabled channels with a trigger. */
+ continue;
+ devc->trigger_mask[stage->stage] |= 1 << match->channel->index;
+ if (match->match == SR_TRIGGER_ONE)
+ devc->trigger_value[stage->stage] |= 1 << match->channel->index;
+ }
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV struct dev_context *ols_dev_new(void)
+{
+ struct dev_context *devc;
+
+ if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+
+ /* Device-specific settings */
+ devc->max_samples = devc->max_samplerate = devc->protocol_version = 0;
+
+ /* Acquisition settings */
+ devc->limit_samples = devc->capture_ratio = 0;
+ devc->trigger_at = -1;
+ devc->channel_mask = 0xffffffff;
+ devc->flag_reg = 0;
+
+ return devc;
+}
+
+SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ uint32_t tmp_int, ui;
+ uint8_t key, type, token;
+ GString *tmp_str, *devname, *version;
+ guchar tmp_c;
+
+ sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, NULL, NULL, NULL);
+ sdi->driver = di;
+ devc = ols_dev_new();
+ sdi->priv = devc;
+
+ devname = g_string_new("");
+ version = g_string_new("");
+
+ key = 0xff;
+ while (key) {
+ if (serial_read_blocking(serial, &key, 1) != 1)
+ break;
+ if (key == 0x00) {
+ sr_dbg("Got metadata key 0x00, metadata ends.");
+ break;
+ }
+ type = key >> 5;
+ token = key & 0x1f;
+ switch (type) {
+ case 0:
+ /* NULL-terminated string */
+ tmp_str = g_string_new("");
+ while (serial_read_blocking(serial, &tmp_c, 1) == 1 && tmp_c != '\0')
+ g_string_append_c(tmp_str, tmp_c);
+ sr_dbg("Got metadata key 0x%.2x value '%s'.",
+ key, tmp_str->str);
+ switch (token) {
+ case 0x01:
+ /* Device name */
+ devname = g_string_append(devname, tmp_str->str);
+ break;
+ case 0x02:
+ /* FPGA firmware version */
+ if (version->len)
+ g_string_append(version, ", ");
+ g_string_append(version, "FPGA version ");
+ g_string_append(version, tmp_str->str);
+ break;
+ case 0x03:
+ /* Ancillary version */
+ if (version->len)
+ g_string_append(version, ", ");
+ g_string_append(version, "Ancillary version ");
+ g_string_append(version, tmp_str->str);
+ break;
+ default:
+ sr_info("ols: unknown token 0x%.2x: '%s'",
+ token, tmp_str->str);
+ break;
+ }
+ g_string_free(tmp_str, TRUE);
+ break;
+ case 1:
+ /* 32-bit unsigned integer */
+ if (serial_read_blocking(serial, &tmp_int, 4) != 4)
+ break;
+ tmp_int = RB32(&tmp_int);
+ sr_dbg("Got metadata key 0x%.2x value 0x%.8x.",
+ key, tmp_int);
+ switch (token) {
+ case 0x00:
+ /* Number of usable channels */
+ for (ui = 0; ui < tmp_int; ui++) {
+ if (!(ch = sr_channel_new(ui, SR_CHANNEL_LOGIC, TRUE,
+ ols_channel_names[ui])))
+ return 0;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+ break;
+ case 0x01:
+ /* Amount of sample memory available (bytes) */
+ devc->max_samples = tmp_int;
+ break;
+ case 0x02:
+ /* Amount of dynamic memory available (bytes) */
+ /* what is this for? */
+ break;
+ case 0x03:
+ /* Maximum sample rate (hz) */
+ devc->max_samplerate = tmp_int;
+ break;
+ case 0x04:
+ /* protocol version */
+ devc->protocol_version = tmp_int;
+ break;
+ default:
+ sr_info("Unknown token 0x%.2x: 0x%.8x.",
+ token, tmp_int);
+ break;
+ }
+ break;
+ case 2:
+ /* 8-bit unsigned integer */
+ if (serial_read_blocking(serial, &tmp_c, 1) != 1)
+ break;
+ sr_dbg("Got metadata key 0x%.2x value 0x%.2x.",
+ key, tmp_c);
+ switch (token) {
+ case 0x00:
+ /* Number of usable channels */
+ for (ui = 0; ui < tmp_c; ui++) {
+ if (!(ch = sr_channel_new(ui, SR_CHANNEL_LOGIC, TRUE,
+ ols_channel_names[ui])))
+ return 0;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+ break;
+ case 0x01:
+ /* protocol version */
+ devc->protocol_version = tmp_c;
+ break;
+ default:
+ sr_info("Unknown token 0x%.2x: 0x%.2x.",
+ token, tmp_c);
+ break;
+ }
+ break;
+ default:
+ /* unknown type */
+ break;
+ }
+ }
+
+ sdi->model = devname->str;
+ sdi->version = version->str;
+ g_string_free(devname, FALSE);
+ g_string_free(version, FALSE);
+
+ return sdi;
+}
+
+SR_PRIV int ols_set_samplerate(const struct sr_dev_inst *sdi,
+ const uint64_t samplerate)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+ if (devc->max_samplerate && samplerate > devc->max_samplerate)
+ return SR_ERR_SAMPLERATE;
+
+ if (samplerate > CLOCK_RATE) {
+ sr_info("Enabling demux mode.");
+ devc->flag_reg |= FLAG_DEMUX;
+ devc->flag_reg &= ~FLAG_FILTER;
+ devc->max_channels = NUM_CHANNELS / 2;
+ devc->cur_samplerate_divider = (CLOCK_RATE * 2 / samplerate) - 1;
+ } else {
+ sr_info("Disabling demux mode.");
+ devc->flag_reg &= ~FLAG_DEMUX;
+ devc->flag_reg |= FLAG_FILTER;
+ devc->max_channels = NUM_CHANNELS;
+ devc->cur_samplerate_divider = (CLOCK_RATE / samplerate) - 1;
+ }
+
+ /* Calculate actual samplerate used and complain if it is different
+ * from the requested.
+ */
+ devc->cur_samplerate = CLOCK_RATE / (devc->cur_samplerate_divider + 1);
+ if (devc->flag_reg & FLAG_DEMUX)
+ devc->cur_samplerate *= 2;
+ if (devc->cur_samplerate != samplerate)
+ sr_info("Can't match samplerate %" PRIu64 ", using %"
+ PRIu64 ".", samplerate, devc->cur_samplerate);
+
+ return SR_OK;
+}
+
+SR_PRIV void abort_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_serial_dev_inst *serial;
+
+ serial = sdi->conn;
+ serial_source_remove(sdi->session, serial);
+
+ /* Terminate session */
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+}
+
+SR_PRIV int ols_receive_data(int fd, int revents, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_serial_dev_inst *serial;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ uint32_t sample;
+ int num_ols_changrp, offset, j;
+ unsigned int i;
+ unsigned char byte;
+
+ (void)fd;
+
+ sdi = cb_data;
+ serial = sdi->conn;
+ devc = sdi->priv;
+
+ if (devc->num_transfers++ == 0) {
+ /*
+ * First time round, means the device started sending data,
+ * and will not stop until done. If it stops sending for
+ * longer than it takes to send a byte, that means it's
+ * finished. We'll double that to 30ms to be sure...
+ */
+ serial_source_remove(sdi->session, serial);
+ serial_source_add(sdi->session, serial, G_IO_IN, 30,
+ ols_receive_data, cb_data);
+ devc->raw_sample_buf = g_try_malloc(devc->limit_samples * 4);
+ if (!devc->raw_sample_buf) {
+ sr_err("Sample buffer malloc failed.");
+ return FALSE;
+ }
+ /* fill with 1010... for debugging */
+ memset(devc->raw_sample_buf, 0x82, devc->limit_samples * 4);
+ }
+
+ num_ols_changrp = 0;
+ for (i = NUM_CHANNELS; i > 0x02; i /= 2) {
+ if ((devc->flag_reg & i) == 0) {
+ num_ols_changrp++;
+ }
+ }
+
+ if (revents == G_IO_IN && devc->num_samples < devc->limit_samples) {
+ if (serial_read_nonblocking(serial, &byte, 1) != 1)
+ return FALSE;
+ devc->cnt_bytes++;
+
+ /* Ignore it if we've read enough. */
+ if (devc->num_samples >= devc->limit_samples)
+ return TRUE;
+
+ devc->sample[devc->num_bytes++] = byte;
+ sr_spew("Received byte 0x%.2x.", byte);
+ if (devc->num_bytes == num_ols_changrp) {
+ devc->cnt_samples++;
+ devc->cnt_samples_rle++;
+ /*
+ * Got a full sample. Convert from the OLS's little-endian
+ * sample to the local format.
+ */
+ sample = devc->sample[0] | (devc->sample[1] << 8) \
+ | (devc->sample[2] << 16) | (devc->sample[3] << 24);
+ sr_dbg("Received sample 0x%.*x.", devc->num_bytes * 2, sample);
+ if (devc->flag_reg & FLAG_RLE) {
+ /*
+ * In RLE mode the high bit of the sample is the
+ * "count" flag, meaning this sample is the number
+ * of times the previous sample occurred.
+ */
+ if (devc->sample[devc->num_bytes - 1] & 0x80) {
+ /* Clear the high bit. */
+ sample &= ~(0x80 << (devc->num_bytes - 1) * 8);
+ devc->rle_count = sample;
+ devc->cnt_samples_rle += devc->rle_count;
+ sr_dbg("RLE count: %u.", devc->rle_count);
+ devc->num_bytes = 0;
+ return TRUE;
+ }
+ }
+ devc->num_samples += devc->rle_count + 1;
+ if (devc->num_samples > devc->limit_samples) {
+ /* Save us from overrunning the buffer. */
+ devc->rle_count -= devc->num_samples - devc->limit_samples;
+ devc->num_samples = devc->limit_samples;
+ }
+
+ if (num_ols_changrp < 4) {
+ /*
+ * Some channel groups may have been turned
+ * off, to speed up transfer between the
+ * hardware and the PC. Expand that here before
+ * submitting it over the session bus --
+ * whatever is listening on the bus will be
+ * expecting a full 32-bit sample, based on
+ * the number of channels.
+ */
+ j = 0;
+ memset(devc->tmp_sample, 0, 4);
+ for (i = 0; i < 4; i++) {
+ if (((devc->flag_reg >> 2) & (1 << i)) == 0) {
+ /*
+ * This channel group was
+ * enabled, copy from received
+ * sample.
+ */
+ devc->tmp_sample[i] = devc->sample[j++];
+ } else if (devc->flag_reg & FLAG_DEMUX && (i > 2)) {
+ /* group 2 & 3 get added to 0 & 1 */
+ devc->tmp_sample[i - 2] = devc->sample[j++];
+ }
+ }
+ memcpy(devc->sample, devc->tmp_sample, 4);
+ sr_spew("Expanded sample: 0x%.8x.", sample);
+ }
+
+ /*
+ * the OLS sends its sample buffer backwards.
+ * store it in reverse order here, so we can dump
+ * this on the session bus later.
+ */
+ offset = (devc->limit_samples - devc->num_samples) * 4;
+ for (i = 0; i <= devc->rle_count; i++) {
+ memcpy(devc->raw_sample_buf + offset + (i * 4),
+ devc->sample, 4);
+ }
+ memset(devc->sample, 0, 4);
+ devc->num_bytes = 0;
+ devc->rle_count = 0;
+ }
+ } else {
+ /*
+ * This is the main loop telling us a timeout was reached, or
+ * we've acquired all the samples we asked for -- we're done.
+ * Send the (properly-ordered) buffer to the frontend.
+ */
+ sr_dbg("Received %d bytes, %d samples, %d decompressed samples.",
+ devc->cnt_bytes, devc->cnt_samples,
+ devc->cnt_samples_rle);
+ if (devc->trigger_at != -1) {
+ /*
+ * A trigger was set up, so we need to tell the frontend
+ * about it.
+ */
+ if (devc->trigger_at > 0) {
+ /* There are pre-trigger samples, send those first. */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = devc->trigger_at * 4;
+ logic.unitsize = 4;
+ logic.data = devc->raw_sample_buf +
+ (devc->limit_samples - devc->num_samples) * 4;
+ sr_session_send(cb_data, &packet);
+ }
+
+ /* Send the trigger. */
+ packet.type = SR_DF_TRIGGER;
+ sr_session_send(cb_data, &packet);
+
+ /* Send post-trigger samples. */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = (devc->num_samples * 4) - (devc->trigger_at * 4);
+ logic.unitsize = 4;
+ logic.data = devc->raw_sample_buf + devc->trigger_at * 4 +
+ (devc->limit_samples - devc->num_samples) * 4;
+ sr_session_send(cb_data, &packet);
+ } else {
+ /* no trigger was used */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = devc->num_samples * 4;
+ logic.unitsize = 4;
+ logic.data = devc->raw_sample_buf +
+ (devc->limit_samples - devc->num_samples) * 4;
+ sr_session_send(cb_data, &packet);
+ }
+ g_free(devc->raw_sample_buf);
+
+ serial_flush(serial);
+ abort_acquisition(sdi);
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_OPENBENCH_LOGIC_SNIFFER_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_OPENBENCH_LOGIC_SNIFFER_PROTOCOL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "ols"
+
+#define NUM_CHANNELS 32
+#define NUM_TRIGGER_STAGES 4
+#define SERIAL_SPEED B115200
+#define CLOCK_RATE SR_MHZ(100)
+#define MIN_NUM_SAMPLES 4
+#define DEFAULT_SAMPLERATE SR_KHZ(200)
+
+/* Command opcodes */
+#define CMD_RESET 0x00
+#define CMD_RUN 0x01
+#define CMD_TESTMODE 0x03
+#define CMD_ID 0x02
+#define CMD_METADATA 0x04
+#define CMD_SET_FLAGS 0x82
+#define CMD_SET_DIVIDER 0x80
+#define CMD_CAPTURE_SIZE 0x81
+#define CMD_SET_TRIGGER_MASK 0xc0
+#define CMD_SET_TRIGGER_VALUE 0xc1
+#define CMD_SET_TRIGGER_CONFIG 0xc2
+
+/* Trigger config */
+#define TRIGGER_START (1 << 3)
+
+/* Bitmasks for CMD_FLAGS */
+/* 12-13 unused, 14-15 RLE mode (we hardcode mode 0). */
+#define FLAG_INTERNAL_TEST_MODE (1 << 11)
+#define FLAG_EXTERNAL_TEST_MODE (1 << 10)
+#define FLAG_SWAP_CHANNELS (1 << 9)
+#define FLAG_RLE (1 << 8)
+#define FLAG_SLOPE_FALLING (1 << 7)
+#define FLAG_CLOCK_EXTERNAL (1 << 6)
+#define FLAG_CHANNELGROUP_4 (1 << 5)
+#define FLAG_CHANNELGROUP_3 (1 << 4)
+#define FLAG_CHANNELGROUP_2 (1 << 3)
+#define FLAG_CHANNELGROUP_1 (1 << 2)
+#define FLAG_FILTER (1 << 1)
+#define FLAG_DEMUX (1 << 0)
+
+/* Private, per-device-instance driver context. */
+struct dev_context {
+ /* Fixed device settings */
+ int max_channels;
+ uint32_t max_samples;
+ uint32_t max_samplerate;
+ uint32_t protocol_version;
+
+ /* Acquisition settings */
+ uint64_t cur_samplerate;
+ uint32_t cur_samplerate_divider;
+ uint64_t limit_samples;
+ int capture_ratio;
+ int trigger_at;
+ uint32_t channel_mask;
+ uint32_t trigger_mask[NUM_TRIGGER_STAGES];
+ uint32_t trigger_value[NUM_TRIGGER_STAGES];
+ int num_stages;
+ uint16_t flag_reg;
+
+ /* Operational states */
+ unsigned int num_transfers;
+ unsigned int num_samples;
+ int num_bytes;
+ int cnt_bytes;
+ int cnt_samples;
+ int cnt_samples_rle;
+
+ /* Temporary variables */
+ unsigned int rle_count;
+ unsigned char sample[4];
+ unsigned char tmp_sample[4];
+ unsigned char *raw_sample_buf;
+};
+
+
+SR_PRIV extern const char *ols_channel_names[NUM_CHANNELS + 1];
+
+SR_PRIV int send_shortcommand(struct sr_serial_dev_inst *serial,
+ uint8_t command);
+SR_PRIV int send_longcommand(struct sr_serial_dev_inst *serial,
+ uint8_t command, uint8_t *data);
+SR_PRIV void ols_channel_mask(const struct sr_dev_inst *sdi);
+SR_PRIV int ols_convert_trigger(const struct sr_dev_inst *sdi);
+SR_PRIV struct dev_context *ols_dev_new(void);
+SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial);
+SR_PRIV int ols_set_samplerate(const struct sr_dev_inst *sdi,
+ uint64_t samplerate);
+SR_PRIV void abort_acquisition(const struct sr_dev_inst *sdi);
+SR_PRIV int ols_receive_data(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Martin Ling <martin-git@earth.li>
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2013 Mathias Grimmberger <mgri@zaphod.sax.de>
+ *
+ * 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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_OSCILLOSCOPE,
+ SR_CONF_TIMEBASE,
+ SR_CONF_TRIGGER_SOURCE,
+ SR_CONF_TRIGGER_SLOPE,
+ SR_CONF_HORIZ_TRIGGERPOS,
+ SR_CONF_NUM_TIMEBASE,
+ SR_CONF_LIMIT_FRAMES,
+ SR_CONF_SAMPLERATE,
+};
+
+static const int32_t analog_hwcaps[] = {
+ SR_CONF_NUM_VDIV,
+ SR_CONF_VDIV,
+ SR_CONF_COUPLING,
+ SR_CONF_DATA_SOURCE,
+};
+
+static const uint64_t timebases[][2] = {
+ /* nanoseconds */
+ { 1, 1000000000 },
+ { 2, 1000000000 },
+ { 5, 1000000000 },
+ { 10, 1000000000 },
+ { 20, 1000000000 },
+ { 50, 1000000000 },
+ { 100, 1000000000 },
+ { 500, 1000000000 },
+ /* microseconds */
+ { 1, 1000000 },
+ { 2, 1000000 },
+ { 5, 1000000 },
+ { 10, 1000000 },
+ { 20, 1000000 },
+ { 50, 1000000 },
+ { 100, 1000000 },
+ { 200, 1000000 },
+ { 500, 1000000 },
+ /* milliseconds */
+ { 1, 1000 },
+ { 2, 1000 },
+ { 5, 1000 },
+ { 10, 1000 },
+ { 20, 1000 },
+ { 50, 1000 },
+ { 100, 1000 },
+ { 200, 1000 },
+ { 500, 1000 },
+ /* seconds */
+ { 1, 1 },
+ { 2, 1 },
+ { 5, 1 },
+ { 10, 1 },
+ { 20, 1 },
+ { 50, 1 },
+ { 100, 1 },
+ { 200, 1 },
+ { 500, 1 },
+ { 1000, 1 },
+};
+
+static const uint64_t vdivs[][2] = {
+ /* microvolts */
+ { 500, 1000000 },
+ /* millivolts */
+ { 1, 1000 },
+ { 2, 1000 },
+ { 5, 1000 },
+ { 10, 1000 },
+ { 20, 1000 },
+ { 50, 1000 },
+ { 100, 1000 },
+ { 200, 1000 },
+ { 500, 1000 },
+ /* volts */
+ { 1, 1 },
+ { 2, 1 },
+ { 5, 1 },
+ { 10, 1 },
+};
+
+#define NUM_TIMEBASE ARRAY_SIZE(timebases)
+#define NUM_VDIV ARRAY_SIZE(vdivs)
+
+static const char *trigger_sources[] = {
+ "CH1",
+ "CH2",
+ "CH3",
+ "CH4",
+ "EXT",
+ "AC Line",
+ "D0",
+ "D1",
+ "D2",
+ "D3",
+ "D4",
+ "D5",
+ "D6",
+ "D7",
+ "D8",
+ "D9",
+ "D10",
+ "D11",
+ "D12",
+ "D13",
+ "D14",
+ "D15",
+};
+
+static const char *trigger_slopes[] = {
+ "r",
+ "f",
+};
+
+static const char *coupling[] = {
+ "AC",
+ "DC",
+ "GND",
+};
+
+/* Do not change the order of entries */
+static const char *data_sources[] = {
+ "Live",
+ "Memory",
+ "Segmented",
+};
+
+enum vendor {
+ RIGOL,
+ AGILENT,
+};
+
+enum series {
+ VS5000,
+ DS1000,
+ DS2000,
+ DS2000A,
+ DSO1000,
+};
+
+/* short name, full name */
+static const struct rigol_ds_vendor supported_vendors[] = {
+ [RIGOL] = {"Rigol", "Rigol Technologies"},
+ [AGILENT] = {"Agilent", "Rigol Technologies"},
+};
+
+#define VENDOR(x) &supported_vendors[x]
+/* vendor, series, protocol, max timebase, min vdiv, number of horizontal divs,
+ * live waveform samples, memory buffer samples */
+static const struct rigol_ds_series supported_series[] = {
+ [VS5000] = {VENDOR(RIGOL), "VS5000", PROTOCOL_V1, FORMAT_RAW,
+ {50, 1}, {2, 1000}, 14, 2048, 0},
+ [DS1000] = {VENDOR(RIGOL), "DS1000", PROTOCOL_V2, FORMAT_IEEE488_2,
+ {50, 1}, {2, 1000}, 12, 600, 1048576},
+ [DS2000] = {VENDOR(RIGOL), "DS2000", PROTOCOL_V3, FORMAT_IEEE488_2,
+ {500, 1}, {2, 1000}, 14, 1400, 14000},
+ [DS2000A] = {VENDOR(RIGOL), "DS2000A", PROTOCOL_V3, FORMAT_IEEE488_2,
+ {1000, 1}, {500, 1000000}, 14, 1400, 14000},
+ [DSO1000] = {VENDOR(AGILENT), "DSO1000", PROTOCOL_V3, FORMAT_IEEE488_2,
+ {50, 1}, {2, 1000}, 12, 600, 20480},
+};
+
+#define SERIES(x) &supported_series[x]
+/* series, model, min timebase, analog channels, digital */
+static const struct rigol_ds_model supported_models[] = {
+ {SERIES(VS5000), "VS5022", {20, 1000000000}, 2, false},
+ {SERIES(VS5000), "VS5042", {10, 1000000000}, 2, false},
+ {SERIES(VS5000), "VS5062", {5, 1000000000}, 2, false},
+ {SERIES(VS5000), "VS5102", {2, 1000000000}, 2, false},
+ {SERIES(VS5000), "VS5202", {2, 1000000000}, 2, false},
+ {SERIES(VS5000), "VS5022D", {20, 1000000000}, 2, true},
+ {SERIES(VS5000), "VS5042D", {10, 1000000000}, 2, true},
+ {SERIES(VS5000), "VS5062D", {5, 1000000000}, 2, true},
+ {SERIES(VS5000), "VS5102D", {2, 1000000000}, 2, true},
+ {SERIES(VS5000), "VS5202D", {2, 1000000000}, 2, true},
+ {SERIES(DS1000), "DS1052E", {5, 1000000000}, 2, false},
+ {SERIES(DS1000), "DS1102E", {2, 1000000000}, 2, false},
+ {SERIES(DS1000), "DS1152E", {2, 1000000000}, 2, false},
+ {SERIES(DS1000), "DS1052D", {5, 1000000000}, 2, true},
+ {SERIES(DS1000), "DS1102D", {2, 1000000000}, 2, true},
+ {SERIES(DS1000), "DS1152D", {2, 1000000000}, 2, true},
+ {SERIES(DS2000), "DS2072", {5, 1000000000}, 2, false},
+ {SERIES(DS2000), "DS2102", {5, 1000000000}, 2, false},
+ {SERIES(DS2000), "DS2202", {2, 1000000000}, 2, false},
+ {SERIES(DS2000), "DS2302", {1, 1000000000}, 2, false},
+ {SERIES(DS2000A), "DS2072A", {5, 1000000000}, 2, false},
+ {SERIES(DS2000A), "DS2102A", {5, 1000000000}, 2, false},
+ {SERIES(DS2000A), "DS2202A", {2, 1000000000}, 2, false},
+ {SERIES(DS2000A), "DS2302A", {1, 1000000000}, 2, false},
+ {SERIES(DSO1000), "DSO1002A", {5, 1000000000}, 2, false},
+ {SERIES(DSO1000), "DSO1004A", {5, 1000000000}, 4, false},
+ {SERIES(DSO1000), "DSO1012A", {2, 1000000000}, 2, false},
+ {SERIES(DSO1000), "DSO1014A", {2, 1000000000}, 4, false},
+ {SERIES(DSO1000), "DSO1022A", {2, 1000000000}, 2, false},
+ {SERIES(DSO1000), "DSO1024A", {2, 1000000000}, 4, false},
+};
+
+SR_PRIV struct sr_dev_driver rigol_ds_driver_info;
+static struct sr_dev_driver *di = &rigol_ds_driver_info;
+
+static void clear_helper(void *priv)
+{
+ struct dev_context *devc;
+
+ devc = priv;
+ g_free(devc->data);
+ g_free(devc->buffer);
+ g_free(devc->coupling[0]);
+ g_free(devc->coupling[1]);
+ g_free(devc->trigger_source);
+ g_free(devc->trigger_slope);
+ g_slist_free(devc->analog_groups[0].channels);
+ g_slist_free(devc->analog_groups[1].channels);
+ g_slist_free(devc->digital_group.channels);
+}
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, clear_helper);
+}
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
+{
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_scpi_hw_info *hw_info;
+ struct sr_channel *ch;
+ long n[3];
+ unsigned int i;
+ const struct rigol_ds_model *model = NULL;
+ gchar *channel_name, **version;
+
+ if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+ sr_info("Couldn't get IDN response, retrying.");
+ sr_scpi_close(scpi);
+ sr_scpi_open(scpi);
+ if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+ sr_info("Couldn't get IDN response.");
+ return NULL;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
+ if (!strcasecmp(hw_info->manufacturer,
+ supported_models[i].series->vendor->full_name) &&
+ !strcmp(hw_info->model, supported_models[i].name)) {
+ model = &supported_models[i];
+ break;
+ }
+ }
+
+ if (!model || !(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE,
+ model->series->vendor->name,
+ model->name,
+ hw_info->firmware_version))) {
+ sr_scpi_hw_info_free(hw_info);
+ return NULL;
+ }
+
+ sdi->conn = scpi;
+
+ sdi->driver = di;
+ sdi->inst_type = SR_INST_SCPI;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
+ return NULL;
+
+ devc->limit_frames = 0;
+ devc->model = model;
+ devc->format = model->series->format;
+
+ /* DS1000 models with firmware before 0.2.4 used the old data format. */
+ if (model->series == SERIES(DS1000)) {
+ version = g_strsplit(hw_info->firmware_version, ".", 0);
+ do {
+ if (!version[0] || !version[1] || !version[2])
+ break;
+ if (version[0][0] == 0 || version[1][0] == 0 || version[2][0] == 0)
+ break;
+ for (i = 0; i < 3; i++) {
+ if (sr_atol(version[i], &n[i]) != SR_OK)
+ break;
+ }
+ if (i != 3)
+ break;
+ if (n[0] != 0 || n[1] > 2)
+ break;
+ if (n[1] == 2 && n[2] > 3)
+ break;
+ sr_dbg("Found DS1000 firmware < 0.2.4, using raw data format.");
+ devc->format = FORMAT_RAW;
+ } while(0);
+ g_strfreev(version);
+ }
+
+ sr_scpi_hw_info_free(hw_info);
+
+ for (i = 0; i < model->analog_channels; i++) {
+ if (!(channel_name = g_strdup_printf("CH%d", i + 1)))
+ return NULL;
+ ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, channel_name);
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ devc->analog_groups[i].name = channel_name;
+ devc->analog_groups[i].channels = g_slist_append(NULL, ch);
+ sdi->channel_groups = g_slist_append(sdi->channel_groups,
+ &devc->analog_groups[i]);
+ }
+
+ if (devc->model->has_digital) {
+ for (i = 0; i < 16; i++) {
+ if (!(channel_name = g_strdup_printf("D%d", i)))
+ return NULL;
+ ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, channel_name);
+ g_free(channel_name);
+ if (!ch)
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ devc->digital_group.channels = g_slist_append(
+ devc->digital_group.channels, ch);
+ }
+ devc->digital_group.name = "LA";
+ sdi->channel_groups = g_slist_append(sdi->channel_groups,
+ &devc->digital_group);
+ }
+
+ for (i = 0; i < NUM_TIMEBASE; i++) {
+ if (!memcmp(&devc->model->min_timebase, &timebases[i], sizeof(uint64_t[2])))
+ devc->timebases = &timebases[i];
+ if (!memcmp(&devc->model->series->max_timebase, &timebases[i], sizeof(uint64_t[2])))
+ devc->num_timebases = &timebases[i] - devc->timebases + 1;
+ }
+
+ for (i = 0; i < NUM_VDIV; i++)
+ if (!memcmp(&devc->model->series->min_vdiv, &vdivs[i], sizeof(uint64_t[2])))
+ devc->vdivs = &vdivs[i];
+
+ if (!(devc->buffer = g_try_malloc(ACQ_BUFFER_SIZE)))
+ return NULL;
+ if (!(devc->data = g_try_malloc(ACQ_BUFFER_SIZE * sizeof(float))))
+ return NULL;
+
+ devc->data_source = DATA_SOURCE_LIVE;
+
+ sdi->priv = devc;
+
+ return sdi;
+}
+
+static GSList *scan(GSList *options)
+{
+ return sr_scpi_scan(di->priv, options, probe_device);
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct sr_scpi_dev_inst *scpi = sdi->conn;
+
+ if (sr_scpi_open(scpi) < 0)
+ return SR_ERR;
+
+ if (rigol_ds_get_dev_cfg(sdi) != SR_OK)
+ return SR_ERR;
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_scpi_dev_inst *scpi;
+ struct dev_context *devc;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ scpi = sdi->conn;
+ devc = sdi->priv;
+
+ if (devc->model->series->protocol == PROTOCOL_V2)
+ rigol_ds_config_set(sdi, ":KEY:LOCK DISABLE");
+
+ if (scpi) {
+ if (sr_scpi_close(scpi) < 0)
+ return SR_ERR;
+ sdi->status = SR_ST_INACTIVE;
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return dev_clear();
+}
+
+static int analog_frame_size(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ struct sr_channel *ch;
+ int analog_channels = 0;
+ GSList *l;
+
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type == SR_CHANNEL_ANALOG && ch->enabled)
+ analog_channels++;
+ }
+
+ if (analog_channels == 0)
+ return 0;
+
+ switch (devc->data_source) {
+ case DATA_SOURCE_LIVE:
+ return devc->model->series->live_samples;
+ case DATA_SOURCE_MEMORY:
+ return devc->model->series->buffer_samples / analog_channels;
+ default:
+ return 0;
+ }
+}
+
+static int digital_frame_size(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+
+ switch (devc->data_source) {
+ case DATA_SOURCE_LIVE:
+ return devc->model->series->live_samples * 2;
+ case DATA_SOURCE_MEMORY:
+ return devc->model->series->buffer_samples * 2;
+ default:
+ return 0;
+ }
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ const char *tmp_str;
+ uint64_t samplerate;
+ int analog_channel = -1;
+ float smallest_diff = 0.0000000001;
+ int idx = -1;
+ unsigned i;
+
+ if (!sdi || !(devc = sdi->priv))
+ return SR_ERR_ARG;
+
+ /* If a channel group is specified, it must be a valid one. */
+ if (cg && !g_slist_find(sdi->channel_groups, cg)) {
+ sr_err("Invalid channel group specified.");
+ return SR_ERR;
+ }
+
+ if (cg) {
+ ch = g_slist_nth_data(cg->channels, 0);
+ if (!ch)
+ return SR_ERR;
+ if (ch->type == SR_CHANNEL_ANALOG) {
+ if (ch->name[2] < '1' || ch->name[2] > '4')
+ return SR_ERR;
+ analog_channel = ch->name[2] - '1';
+ }
+ }
+
+ switch (id) {
+ case SR_CONF_NUM_TIMEBASE:
+ *data = g_variant_new_int32(devc->model->series->num_horizontal_divs);
+ break;
+ case SR_CONF_NUM_VDIV:
+ *data = g_variant_new_int32(NUM_VDIV);
+ case SR_CONF_DATA_SOURCE:
+ if (devc->data_source == DATA_SOURCE_LIVE)
+ *data = g_variant_new_string("Live");
+ else if (devc->data_source == DATA_SOURCE_MEMORY)
+ *data = g_variant_new_string("Memory");
+ else
+ *data = g_variant_new_string("Segmented");
+ break;
+ case SR_CONF_SAMPLERATE:
+ if (devc->data_source == DATA_SOURCE_LIVE) {
+ samplerate = analog_frame_size(sdi) /
+ (devc->timebase * devc->model->series->num_horizontal_divs);
+ *data = g_variant_new_uint64(samplerate);
+ } else {
+ return SR_ERR_NA;
+ }
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ if (!strcmp(devc->trigger_source, "ACL"))
+ tmp_str = "AC Line";
+ else if (!strcmp(devc->trigger_source, "CHAN1"))
+ tmp_str = "CH1";
+ else if (!strcmp(devc->trigger_source, "CHAN2"))
+ tmp_str = "CH2";
+ else if (!strcmp(devc->trigger_source, "CHAN3"))
+ tmp_str = "CH3";
+ else if (!strcmp(devc->trigger_source, "CHAN4"))
+ tmp_str = "CH4";
+ else
+ tmp_str = devc->trigger_source;
+ *data = g_variant_new_string(tmp_str);
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ if (!strcmp(devc->trigger_slope, "POS"))
+ tmp_str = "r";
+ else if (!strcmp(devc->trigger_slope, "NEG"))
+ tmp_str = "f";
+ else
+ return SR_ERR_NA;
+ *data = g_variant_new_string(tmp_str);
+ break;
+ case SR_CONF_TIMEBASE:
+ for (i = 0; i < devc->num_timebases; i++) {
+ float tb = (float)devc->timebases[i][0] / devc->timebases[i][1];
+ float diff = fabs(devc->timebase - tb);
+ if (diff < smallest_diff) {
+ smallest_diff = diff;
+ idx = i;
+ }
+ }
+ if (idx < 0)
+ return SR_ERR_NA;
+ *data = g_variant_new("(tt)", devc->timebases[idx][0],
+ devc->timebases[idx][1]);
+ break;
+ case SR_CONF_VDIV:
+ if (analog_channel < 0)
+ return SR_ERR_NA;
+ for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+ float vdiv = (float)vdivs[i][0] / vdivs[i][1];
+ float diff = fabs(devc->vdiv[analog_channel] - vdiv);
+ if (diff < smallest_diff) {
+ smallest_diff = diff;
+ idx = i;
+ }
+ }
+ if (idx < 0)
+ return SR_ERR_NA;
+ *data = g_variant_new("(tt)", vdivs[idx][0], vdivs[idx][1]);
+ break;
+ case SR_CONF_COUPLING:
+ if (analog_channel < 0)
+ return SR_ERR_NA;
+ *data = g_variant_new_string(devc->coupling[analog_channel]);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ uint64_t p, q;
+ double t_dbl;
+ unsigned int i, j;
+ int ret;
+ const char *tmp_str;
+ char buffer[16];
+
+ if (!(devc = sdi->priv))
+ return SR_ERR_ARG;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ /* If a channel group is specified, it must be a valid one. */
+ if (cg && !g_slist_find(sdi->channel_groups, cg)) {
+ sr_err("Invalid channel group specified.");
+ return SR_ERR;
+ }
+
+ ret = SR_OK;
+ switch (id) {
+ case SR_CONF_LIMIT_FRAMES:
+ devc->limit_frames = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ tmp_str = g_variant_get_string(data, NULL);
+
+ if (!tmp_str || !(tmp_str[0] == 'f' || tmp_str[0] == 'r'))
+ return SR_ERR_ARG;
+
+ g_free(devc->trigger_slope);
+ devc->trigger_slope = g_strdup((tmp_str[0] == 'r') ? "POS" : "NEG");
+ ret = rigol_ds_config_set(sdi, ":TRIG:EDGE:SLOP %s", devc->trigger_slope);
+ break;
+ case SR_CONF_HORIZ_TRIGGERPOS:
+ t_dbl = g_variant_get_double(data);
+ if (t_dbl < 0.0 || t_dbl > 1.0)
+ return SR_ERR;
+ devc->horiz_triggerpos = t_dbl;
+ /* We have the trigger offset as a percentage of the frame, but
+ * need to express this in seconds. */
+ t_dbl = -(devc->horiz_triggerpos - 0.5) * devc->timebase * devc->num_timebases;
+ g_ascii_formatd(buffer, sizeof(buffer), "%.6f", t_dbl);
+ ret = rigol_ds_config_set(sdi, ":TIM:OFFS %s", buffer);
+ break;
+ case SR_CONF_TIMEBASE:
+ g_variant_get(data, "(tt)", &p, &q);
+ for (i = 0; i < devc->num_timebases; i++) {
+ if (devc->timebases[i][0] == p && devc->timebases[i][1] == q) {
+ devc->timebase = (float)p / q;
+ g_ascii_formatd(buffer, sizeof(buffer), "%.9f",
+ devc->timebase);
+ ret = rigol_ds_config_set(sdi, ":TIM:SCAL %s", buffer);
+ break;
+ }
+ }
+ if (i == devc->num_timebases)
+ ret = SR_ERR_ARG;
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ tmp_str = g_variant_get_string(data, NULL);
+ for (i = 0; i < ARRAY_SIZE(trigger_sources); i++) {
+ if (!strcmp(trigger_sources[i], tmp_str)) {
+ g_free(devc->trigger_source);
+ devc->trigger_source = g_strdup(trigger_sources[i]);
+ if (!strcmp(devc->trigger_source, "AC Line"))
+ tmp_str = "ACL";
+ else if (!strcmp(devc->trigger_source, "CH1"))
+ tmp_str = "CHAN1";
+ else if (!strcmp(devc->trigger_source, "CH2"))
+ tmp_str = "CHAN2";
+ else if (!strcmp(devc->trigger_source, "CH3"))
+ tmp_str = "CHAN3";
+ else if (!strcmp(devc->trigger_source, "CH4"))
+ tmp_str = "CHAN4";
+ else
+ tmp_str = (char *)devc->trigger_source;
+ ret = rigol_ds_config_set(sdi, ":TRIG:EDGE:SOUR %s", tmp_str);
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(trigger_sources))
+ ret = SR_ERR_ARG;
+ break;
+ case SR_CONF_VDIV:
+ if (!cg) {
+ sr_err("No channel group specified.");
+ return SR_ERR_CHANNEL_GROUP;
+ }
+ g_variant_get(data, "(tt)", &p, &q);
+ for (i = 0; i < 2; i++) {
+ if (cg == &devc->analog_groups[i]) {
+ for (j = 0; j < ARRAY_SIZE(vdivs); j++) {
+ if (vdivs[j][0] != p || vdivs[j][1] != q)
+ continue;
+ devc->vdiv[i] = (float)p / q;
+ g_ascii_formatd(buffer, sizeof(buffer), "%.3f",
+ devc->vdiv[i]);
+ return rigol_ds_config_set(sdi, ":CHAN%d:SCAL %s", i + 1,
+ buffer);
+ }
+ return SR_ERR_ARG;
+ }
+ }
+ return SR_ERR_NA;
+ case SR_CONF_COUPLING:
+ if (!cg) {
+ sr_err("No channel group specified.");
+ return SR_ERR_CHANNEL_GROUP;
+ }
+ tmp_str = g_variant_get_string(data, NULL);
+ for (i = 0; i < 2; i++) {
+ if (cg == &devc->analog_groups[i]) {
+ for (j = 0; j < ARRAY_SIZE(coupling); j++) {
+ if (!strcmp(tmp_str, coupling[j])) {
+ g_free(devc->coupling[i]);
+ devc->coupling[i] = g_strdup(coupling[j]);
+ return rigol_ds_config_set(sdi, ":CHAN%d:COUP %s", i + 1,
+ devc->coupling[i]);
+ }
+ }
+ return SR_ERR_ARG;
+ }
+ }
+ return SR_ERR_NA;
+ case SR_CONF_DATA_SOURCE:
+ tmp_str = g_variant_get_string(data, NULL);
+ if (!strcmp(tmp_str, "Live"))
+ devc->data_source = DATA_SOURCE_LIVE;
+ else if (devc->model->series->protocol >= PROTOCOL_V2
+ && !strcmp(tmp_str, "Memory"))
+ devc->data_source = DATA_SOURCE_MEMORY;
+ else if (devc->model->series->protocol >= PROTOCOL_V3
+ && !strcmp(tmp_str, "Segmented"))
+ devc->data_source = DATA_SOURCE_SEGMENTED;
+ else
+ return SR_ERR;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ break;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ GVariant *tuple, *rational[2];
+ GVariantBuilder gvb;
+ unsigned int i;
+ struct dev_context *devc = NULL;
+
+ if (sdi)
+ devc = sdi->priv;
+
+ if (key == SR_CONF_SCAN_OPTIONS) {
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ return SR_OK;
+ } else if (key == SR_CONF_DEVICE_OPTIONS && cg == NULL) {
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ return SR_OK;
+ }
+
+ /* Every other option requires a valid device instance. */
+ if (!sdi || !(devc = sdi->priv))
+ return SR_ERR_ARG;
+
+ /* If a channel group is specified, it must be a valid one. */
+ if (cg) {
+ if (cg != &devc->analog_groups[0]
+ && cg != &devc->analog_groups[1]) {
+ sr_err("Invalid channel group specified.");
+ return SR_ERR;
+ }
+ }
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ if (!cg) {
+ sr_err("No channel group specified.");
+ return SR_ERR_CHANNEL_GROUP;
+ }
+ if (cg == &devc->digital_group) {
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ NULL, 0, sizeof(int32_t));
+ return SR_OK;
+ } else {
+ for (i = 0; i < 2; i++) {
+ if (cg == &devc->analog_groups[i]) {
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ analog_hwcaps, ARRAY_SIZE(analog_hwcaps), sizeof(int32_t));
+ return SR_OK;
+ }
+ }
+ return SR_ERR_NA;
+ }
+ break;
+ case SR_CONF_COUPLING:
+ if (!cg) {
+ sr_err("No channel group specified.");
+ return SR_ERR_CHANNEL_GROUP;
+ }
+ *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+ break;
+ case SR_CONF_VDIV:
+ if (!devc)
+ /* Can't know this until we have the exact model. */
+ return SR_ERR_ARG;
+ if (!cg) {
+ sr_err("No channel group specified.");
+ return SR_ERR_CHANNEL_GROUP;
+ }
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < NUM_VDIV; i++) {
+ rational[0] = g_variant_new_uint64(devc->vdivs[i][0]);
+ rational[1] = g_variant_new_uint64(devc->vdivs[i][1]);
+ tuple = g_variant_new_tuple(rational, 2);
+ g_variant_builder_add_value(&gvb, tuple);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TIMEBASE:
+ if (!devc)
+ /* Can't know this until we have the exact model. */
+ return SR_ERR_ARG;
+ if (devc->num_timebases <= 0)
+ return SR_ERR_NA;
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < devc->num_timebases; i++) {
+ rational[0] = g_variant_new_uint64(devc->timebases[i][0]);
+ rational[1] = g_variant_new_uint64(devc->timebases[i][1]);
+ tuple = g_variant_new_tuple(rational, 2);
+ g_variant_builder_add_value(&gvb, tuple);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ if (!devc)
+ /* Can't know this until we have the exact model. */
+ return SR_ERR_ARG;
+ *data = g_variant_new_strv(trigger_sources,
+ devc->model->has_digital ? ARRAY_SIZE(trigger_sources) : 4);
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ *data = g_variant_new_strv(trigger_slopes, ARRAY_SIZE(trigger_slopes));
+ break;
+ case SR_CONF_DATA_SOURCE:
+ if (!devc)
+ /* Can't know this until we have the exact model. */
+ return SR_ERR_ARG;
+ switch (devc->model->series->protocol) {
+ case PROTOCOL_V1:
+ *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources) - 2);
+ break;
+ case PROTOCOL_V2:
+ *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources) - 1);
+ break;
+ default:
+ *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+ break;
+ }
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct sr_scpi_dev_inst *scpi;
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ struct sr_datafeed_packet packet;
+ GSList *l;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ scpi = sdi->conn;
+ devc = sdi->priv;
+
+ devc->num_frames = 0;
+
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ sr_dbg("handling channel %s", ch->name);
+ if (ch->type == SR_CHANNEL_ANALOG) {
+ if (ch->enabled)
+ devc->enabled_analog_channels = g_slist_append(
+ devc->enabled_analog_channels, ch);
+ if (ch->enabled != devc->analog_channels[ch->index]) {
+ /* Enabled channel is currently disabled, or vice versa. */
+ if (rigol_ds_config_set(sdi, ":CHAN%d:DISP %s", ch->index + 1,
+ ch->enabled ? "ON" : "OFF") != SR_OK)
+ return SR_ERR;
+ devc->analog_channels[ch->index] = ch->enabled;
+ }
+ } else if (ch->type == SR_CHANNEL_LOGIC) {
+ if (ch->enabled) {
+ devc->enabled_digital_channels = g_slist_append(
+ devc->enabled_digital_channels, ch);
+ /* Turn on LA module if currently off. */
+ if (!devc->la_enabled) {
+ if (rigol_ds_config_set(sdi, ":LA:DISP ON") != SR_OK)
+ return SR_ERR;
+ devc->la_enabled = TRUE;
+ }
+ }
+ if (ch->enabled != devc->digital_channels[ch->index]) {
+ /* Enabled channel is currently disabled, or vice versa. */
+ if (rigol_ds_config_set(sdi, ":DIG%d:TURN %s", ch->index,
+ ch->enabled ? "ON" : "OFF") != SR_OK)
+ return SR_ERR;
+ devc->digital_channels[ch->index] = ch->enabled;
+ }
+ }
+ }
+
+ if (!devc->enabled_analog_channels && !devc->enabled_digital_channels)
+ return SR_ERR;
+
+ /* Turn off LA module if on and no digital channels selected. */
+ if (devc->la_enabled && !devc->enabled_digital_channels)
+ if (rigol_ds_config_set(sdi, ":LA:DISP OFF") != SR_OK)
+ return SR_ERR;
+
+ /* Set memory mode. */
+ if (devc->data_source == DATA_SOURCE_SEGMENTED) {
+ sr_err("Data source 'Segmented' not yet supported");
+ return SR_ERR;
+ }
+
+ devc->analog_frame_size = analog_frame_size(sdi);
+ devc->digital_frame_size = digital_frame_size(sdi);
+
+ switch (devc->model->series->protocol) {
+ case PROTOCOL_V2:
+ if (rigol_ds_config_set(sdi, ":ACQ:MEMD LONG") != SR_OK)
+ return SR_ERR;
+ break;
+ case PROTOCOL_V3:
+ /* Apparently for the DS2000 the memory
+ * depth can only be set in Running state -
+ * this matches the behaviour of the UI. */
+ if (rigol_ds_config_set(sdi, ":RUN") != SR_OK)
+ return SR_ERR;
+ if (rigol_ds_config_set(sdi, ":ACQ:MDEP %d",
+ devc->analog_frame_size) != SR_OK)
+ return SR_ERR;
+ if (rigol_ds_config_set(sdi, ":STOP") != SR_OK)
+ return SR_ERR;
+ break;
+ default:
+ break;
+ }
+
+ if (devc->data_source == DATA_SOURCE_LIVE)
+ if (rigol_ds_config_set(sdi, ":RUN") != SR_OK)
+ return SR_ERR;
+
+ sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 50,
+ rigol_ds_receive, (void *)sdi);
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ if (devc->enabled_analog_channels)
+ devc->channel_entry = devc->enabled_analog_channels;
+ else
+ devc->channel_entry = devc->enabled_digital_channels;
+
+ if (rigol_ds_capture_start(sdi) != SR_OK)
+ return SR_ERR;
+
+ /* Start of first frame. */
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(cb_data, &packet);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_scpi_dev_inst *scpi;
+ struct sr_datafeed_packet packet;
+
+ (void)cb_data;
+
+ devc = sdi->priv;
+
+ if (sdi->status != SR_ST_ACTIVE) {
+ sr_err("Device inactive, can't stop acquisition.");
+ return SR_ERR;
+ }
+
+ /* End of last frame. */
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+
+ g_slist_free(devc->enabled_analog_channels);
+ g_slist_free(devc->enabled_digital_channels);
+ devc->enabled_analog_channels = NULL;
+ devc->enabled_digital_channels = NULL;
+ scpi = sdi->conn;
+ sr_scpi_source_remove(sdi->session, scpi);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver rigol_ds_driver_info = {
+ .name = "rigol-ds",
+ .longname = "Rigol DS",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = 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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Martin Ling <martin-git@earth.li>
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2013 Mathias Grimmberger <mgri@zaphod.sax.de>
+ *
+ * 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 <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+#include <time.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+/*
+ * This is a unified protocol driver for the DS1000 and DS2000 series.
+ *
+ * DS1000 support tested with a Rigol DS1102D.
+ *
+ * DS2000 support tested with a Rigol DS2072 using firmware version 01.01.00.02.
+ *
+ * The Rigol DS2000 series scopes try to adhere to the IEEE 488.2 (I think)
+ * standard. If you want to read it - it costs real money...
+ *
+ * Every response from the scope has a linefeed appended because the
+ * standard says so. In principle this could be ignored because sending the
+ * next command clears the output queue of the scope. This driver tries to
+ * avoid doing that because it may cause an error being generated inside the
+ * scope and who knows what bugs the firmware has WRT this.
+ *
+ * Waveform data is transferred in a format called "arbitrary block program
+ * data" specified in IEEE 488.2. See Agilents programming manuals for their
+ * 2000/3000 series scopes for a nice description.
+ *
+ * Each data block from the scope has a header, e.g. "#900000001400".
+ * The '#' marks the start of a block.
+ * Next is one ASCII decimal digit between 1 and 9, this gives the number of
+ * ASCII decimal digits following.
+ * Last are the ASCII decimal digits giving the number of bytes (not
+ * samples!) in the block.
+ *
+ * After this header as many data bytes as indicated follow.
+ *
+ * Each data block has a trailing linefeed too.
+ */
+
+static int parse_int(const char *str, int *ret)
+{
+ char *e;
+ long tmp;
+
+ errno = 0;
+ tmp = strtol(str, &e, 10);
+ if (e == str || *e != '\0') {
+ sr_dbg("Failed to parse integer: '%s'", str);
+ return SR_ERR;
+ }
+ if (errno) {
+ sr_dbg("Failed to parse integer: '%s', numerical overflow", str);
+ return SR_ERR;
+ }
+ if (tmp > INT_MAX || tmp < INT_MIN) {
+ sr_dbg("Failed to parse integer: '%s', value to large/small", str);
+ return SR_ERR;
+ }
+
+ *ret = (int)tmp;
+ return SR_OK;
+}
+
+/* Set the next event to wait for in rigol_ds_receive */
+static void rigol_ds_set_wait_event(struct dev_context *devc, enum wait_events event)
+{
+ if (event == WAIT_STOP)
+ devc->wait_status = 2;
+ else
+ devc->wait_status = 1;
+ devc->wait_event = event;
+}
+
+/*
+ * Waiting for a event will return a timeout after 2 to 3 seconds in order
+ * to not block the application.
+ */
+static int rigol_ds_event_wait(const struct sr_dev_inst *sdi, char status1, char status2)
+{
+ char *buf;
+ struct dev_context *devc;
+ time_t start;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+
+ start = time(NULL);
+
+ /*
+ * Trigger status may return:
+ * "TD" or "T'D" - triggered
+ * "AUTO" - autotriggered
+ * "RUN" - running
+ * "WAIT" - waiting for trigger
+ * "STOP" - stopped
+ */
+
+ if (devc->wait_status == 1) {
+ do {
+ if (time(NULL) - start >= 3) {
+ sr_dbg("Timeout waiting for trigger");
+ return SR_ERR_TIMEOUT;
+ }
+
+ if (sr_scpi_get_string(sdi->conn, ":TRIG:STAT?", &buf) != SR_OK)
+ return SR_ERR;
+ } while (buf[0] == status1 || buf[0] == status2);
+
+ devc->wait_status = 2;
+ }
+ if (devc->wait_status == 2) {
+ do {
+ if (time(NULL) - start >= 3) {
+ sr_dbg("Timeout waiting for trigger");
+ return SR_ERR_TIMEOUT;
+ }
+
+ if (sr_scpi_get_string(sdi->conn, ":TRIG:STAT?", &buf) != SR_OK)
+ return SR_ERR;
+ } while (buf[0] != status1 && buf[0] != status2);
+
+ rigol_ds_set_wait_event(devc, WAIT_NONE);
+ }
+
+ return SR_OK;
+}
+
+/*
+ * For live capture we need to wait for a new trigger event to ensure that
+ * sample data is not returned twice.
+ *
+ * Unfortunately this will never really work because for sufficiently fast
+ * timebases and trigger rates it just can't catch the status changes.
+ *
+ * What would be needed is a trigger event register with autoreset like the
+ * Agilents have. The Rigols don't seem to have anything like this.
+ *
+ * The workaround is to only wait for the trigger when the timebase is slow
+ * enough. Of course this means that for faster timebases sample data can be
+ * returned multiple times, this effect is mitigated somewhat by sleeping
+ * for about one sweep time in that case.
+ */
+static int rigol_ds_trigger_wait(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ long s;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+
+ /*
+ * If timebase < 50 msecs/DIV just sleep about one sweep time except
+ * for really fast sweeps.
+ */
+ if (devc->timebase < 0.0499) {
+ if (devc->timebase > 0.99e-6) {
+ /*
+ * Timebase * num hor. divs * 85(%) * 1e6(usecs) / 100
+ * -> 85 percent of sweep time
+ */
+ s = (devc->timebase * devc->model->series->num_horizontal_divs
+ * 85e6) / 100L;
+ sr_spew("Sleeping for %ld usecs instead of trigger-wait", s);
+ g_usleep(s);
+ }
+ rigol_ds_set_wait_event(devc, WAIT_NONE);
+ return SR_OK;
+ } else {
+ return rigol_ds_event_wait(sdi, 'T', 'A');
+ }
+}
+
+/* Wait for scope to got to "Stop" in single shot mode */
+static int rigol_ds_stop_wait(const struct sr_dev_inst *sdi)
+{
+ return rigol_ds_event_wait(sdi, 'S', 'S');
+}
+
+/* Check that a single shot acquisition actually succeeded on the DS2000 */
+static int rigol_ds_check_stop(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ int tmp;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+
+ ch = devc->channel_entry->data;
+
+ if (devc->model->series->protocol <= PROTOCOL_V2)
+ return SR_OK;
+
+ if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d",
+ ch->index + 1) != SR_OK)
+ return SR_ERR;
+ /* Check that the number of samples will be accepted */
+ if (rigol_ds_config_set(sdi, ":WAV:POIN %d", devc->analog_frame_size) != SR_OK)
+ return SR_ERR;
+ if (sr_scpi_get_int(sdi->conn, "*ESR?", &tmp) != SR_OK)
+ return SR_ERR;
+ /*
+ * If we get an "Execution error" the scope went from "Single" to
+ * "Stop" without actually triggering. There is no waveform
+ * displayed and trying to download one will fail - the scope thinks
+ * it has 1400 samples (like display memory) and the driver thinks
+ * it has a different number of samples.
+ *
+ * In that case just try to capture something again. Might still
+ * fail in interesting ways.
+ *
+ * Ain't firmware fun?
+ */
+ if (tmp & 0x10) {
+ sr_warn("Single shot acquisition failed, retrying...");
+ /* Sleep a bit, otherwise the single shot will often fail */
+ g_usleep(500000);
+ rigol_ds_config_set(sdi, ":SING");
+ rigol_ds_set_wait_event(devc, WAIT_STOP);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+/* Wait for enough data becoming available in scope output buffer */
+static int rigol_ds_block_wait(const struct sr_dev_inst *sdi)
+{
+ char *buf;
+ struct dev_context *devc;
+ time_t start;
+ int len;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+
+ if (devc->model->series->protocol >= PROTOCOL_V3) {
+
+ start = time(NULL);
+
+ do {
+ if (time(NULL) - start >= 3) {
+ sr_dbg("Timeout waiting for data block");
+ return SR_ERR_TIMEOUT;
+ }
+
+ /*
+ * The scope copies data really slowly from sample
+ * memory to its output buffer, so try not to bother
+ * it too much with SCPI requests but don't wait too
+ * long for short sample frame sizes.
+ */
+ g_usleep(devc->analog_frame_size < 15000 ? 100000 : 1000000);
+
+ /* "READ,nnnn" (still working) or "IDLE,nnnn" (finished) */
+ if (sr_scpi_get_string(sdi->conn, ":WAV:STAT?", &buf) != SR_OK)
+ return SR_ERR;
+
+ if (parse_int(buf + 5, &len) != SR_OK)
+ return SR_ERR;
+ } while (buf[0] == 'R' && len < 1000000);
+ }
+
+ rigol_ds_set_wait_event(devc, WAIT_NONE);
+
+ return SR_OK;
+}
+
+/* Send a configuration setting. */
+SR_PRIV int rigol_ds_config_set(const struct sr_dev_inst *sdi, const char *format, ...)
+{
+ struct dev_context *devc = sdi->priv;
+ va_list args;
+ int ret;
+
+ va_start(args, format);
+ ret = sr_scpi_send_variadic(sdi->conn, format, args);
+ va_end(args);
+
+ if (ret != SR_OK)
+ return SR_ERR;
+
+ if (devc->model->series->protocol == PROTOCOL_V2) {
+ /* The DS1000 series needs this stupid delay, *OPC? doesn't work. */
+ sr_spew("delay %dms", 100);
+ g_usleep(100000);
+ return SR_OK;
+ } else {
+ return sr_scpi_get_opc(sdi->conn);
+ }
+}
+
+/* Start capturing a new frameset */
+SR_PRIV int rigol_ds_capture_start(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ gchar *trig_mode;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+
+ sr_dbg("Starting data capture for frameset %lu of %lu",
+ devc->num_frames + 1, devc->limit_frames);
+
+ switch (devc->model->series->protocol) {
+ case PROTOCOL_V1:
+ rigol_ds_set_wait_event(devc, WAIT_TRIGGER);
+ break;
+ case PROTOCOL_V2:
+ if (devc->data_source == DATA_SOURCE_LIVE) {
+ if (rigol_ds_config_set(sdi, ":WAV:POIN:MODE NORMAL") != SR_OK)
+ return SR_ERR;
+ rigol_ds_set_wait_event(devc, WAIT_TRIGGER);
+ } else {
+ if (rigol_ds_config_set(sdi, ":STOP") != SR_OK)
+ return SR_ERR;
+ if (rigol_ds_config_set(sdi, ":WAV:POIN:MODE RAW") != SR_OK)
+ return SR_ERR;
+ if (sr_scpi_get_string(sdi->conn, ":TRIG:MODE?", &trig_mode) != SR_OK)
+ return SR_ERR;
+ if (rigol_ds_config_set(sdi, ":TRIG:%s:SWE SING", trig_mode) != SR_OK)
+ return SR_ERR;
+ if (rigol_ds_config_set(sdi, ":RUN") != SR_OK)
+ return SR_ERR;
+ rigol_ds_set_wait_event(devc, WAIT_STOP);
+ }
+ break;
+ case PROTOCOL_V3:
+ if (rigol_ds_config_set(sdi, ":WAV:FORM BYTE") != SR_OK)
+ return SR_ERR;
+ if (devc->data_source == DATA_SOURCE_LIVE) {
+ if (rigol_ds_config_set(sdi, ":WAV:MODE NORM") != SR_OK)
+ return SR_ERR;
+ rigol_ds_set_wait_event(devc, WAIT_TRIGGER);
+ } else {
+ if (rigol_ds_config_set(sdi, ":WAV:MODE RAW") != SR_OK)
+ return SR_ERR;
+ if (rigol_ds_config_set(sdi, ":SING") != SR_OK)
+ return SR_ERR;
+ rigol_ds_set_wait_event(devc, WAIT_STOP);
+ }
+ break;
+ }
+
+ return SR_OK;
+}
+
+/* Start reading data from the current channel */
+SR_PRIV int rigol_ds_channel_start(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+
+ ch = devc->channel_entry->data;
+
+ sr_dbg("Starting reading data from channel %d", ch->index + 1);
+
+ if (devc->model->series->protocol <= PROTOCOL_V2) {
+ if (ch->type == SR_CHANNEL_LOGIC) {
+ if (sr_scpi_send(sdi->conn, ":WAV:DATA? DIG") != SR_OK)
+ return SR_ERR;
+ } else {
+ if (sr_scpi_send(sdi->conn, ":WAV:DATA? CHAN%d",
+ ch->index + 1) != SR_OK)
+ return SR_ERR;
+ }
+ rigol_ds_set_wait_event(devc, WAIT_NONE);
+ } else {
+ if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d",
+ ch->index + 1) != SR_OK)
+ return SR_ERR;
+ if (devc->data_source != DATA_SOURCE_LIVE) {
+ if (rigol_ds_config_set(sdi, ":WAV:RES") != SR_OK)
+ return SR_ERR;
+ if (rigol_ds_config_set(sdi, ":WAV:BEG") != SR_OK)
+ return SR_ERR;
+ }
+ }
+
+ rigol_ds_set_wait_event(devc, WAIT_BLOCK);
+
+ devc->num_channel_bytes = 0;
+ devc->num_header_bytes = 0;
+ devc->num_block_bytes = 0;
+
+ return SR_OK;
+}
+
+/* Read the header of a data block */
+static int rigol_ds_read_header(struct sr_dev_inst *sdi)
+{
+ struct sr_scpi_dev_inst *scpi = sdi->conn;
+ struct dev_context *devc = sdi->priv;
+ char *buf = (char *) devc->buffer;
+ size_t header_length;
+ int ret;
+
+ /* Try to read the hashsign and length digit. */
+ if (devc->num_header_bytes < 2) {
+ ret = sr_scpi_read_data(scpi, buf + devc->num_header_bytes,
+ 2 - devc->num_header_bytes);
+ if (ret < 0) {
+ sr_err("Read error while reading data header.");
+ return SR_ERR;
+ }
+ devc->num_header_bytes += ret;
+ }
+
+ if (devc->num_header_bytes < 2)
+ return 0;
+
+ if (buf[0] != '#' || !isdigit(buf[1]) || buf[1] == '0') {
+ sr_err("Received invalid data block header '%c%c'.", buf[0], buf[1]);
+ return SR_ERR;
+ }
+
+ header_length = 2 + buf[1] - '0';
+
+ /* Try to read the length. */
+ if (devc->num_header_bytes < header_length) {
+ ret = sr_scpi_read_data(scpi, buf + devc->num_header_bytes,
+ header_length - devc->num_header_bytes);
+ if (ret < 0) {
+ sr_err("Read error while reading data header.");
+ return SR_ERR;
+ }
+ devc->num_header_bytes += ret;
+ }
+
+ if (devc->num_header_bytes < header_length)
+ return 0;
+
+ /* Read the data length. */
+ buf[header_length] = '\0';
+
+ if (parse_int(buf + 2, &ret) != SR_OK) {
+ sr_err("Received invalid data block length '%s'.", buf + 2);
+ return -1;
+ }
+
+ sr_dbg("Received data block header: '%s' -> block length %d", buf, ret);
+
+ return ret;
+}
+
+SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct sr_scpi_dev_inst *scpi;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct sr_datafeed_logic logic;
+ double vdiv, offset;
+ int len, i, vref;
+ struct sr_channel *ch;
+ gsize expected_data_bytes;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ scpi = sdi->conn;
+
+ if (revents == G_IO_IN || revents == 0) {
+ switch(devc->wait_event) {
+ case WAIT_NONE:
+ break;
+ case WAIT_TRIGGER:
+ if (rigol_ds_trigger_wait(sdi) != SR_OK)
+ return TRUE;
+ if (rigol_ds_channel_start(sdi) != SR_OK)
+ return TRUE;
+ return TRUE;
+ case WAIT_BLOCK:
+ if (rigol_ds_block_wait(sdi) != SR_OK)
+ return TRUE;
+ break;
+ case WAIT_STOP:
+ if (rigol_ds_stop_wait(sdi) != SR_OK)
+ return TRUE;
+ if (rigol_ds_check_stop(sdi) != SR_OK)
+ return TRUE;
+ if (rigol_ds_channel_start(sdi) != SR_OK)
+ return TRUE;
+ return TRUE;
+ default:
+ sr_err("BUG: Unknown event target encountered");
+ }
+
+ ch = devc->channel_entry->data;
+
+ expected_data_bytes = ch->type == SR_CHANNEL_ANALOG ?
+ devc->analog_frame_size : devc->digital_frame_size;
+
+ if (devc->num_block_bytes == 0) {
+ if (devc->model->series->protocol >= PROTOCOL_V3)
+ if (sr_scpi_send(sdi->conn, ":WAV:DATA?") != SR_OK)
+ return TRUE;
+
+ if (sr_scpi_read_begin(scpi) != SR_OK)
+ return TRUE;
+
+ if (devc->format == FORMAT_IEEE488_2) {
+ sr_dbg("New block header expected");
+ len = rigol_ds_read_header(sdi);
+ if (len == 0)
+ /* Still reading the header. */
+ return TRUE;
+ if (len == -1) {
+ sr_err("Read error, aborting capture.");
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(cb_data, &packet);
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+ /* At slow timebases in live capture the DS2072
+ * sometimes returns "short" data blocks, with
+ * apparently no way to get the rest of the data.
+ * Discard these, the complete data block will
+ * appear eventually.
+ */
+ if (devc->data_source == DATA_SOURCE_LIVE
+ && (unsigned)len < expected_data_bytes) {
+ sr_dbg("Discarding short data block");
+ sr_scpi_read_data(scpi, (char *)devc->buffer, len + 1);
+ return TRUE;
+ }
+ devc->num_block_bytes = len;
+ } else {
+ devc->num_block_bytes = expected_data_bytes;
+ }
+ devc->num_block_read = 0;
+ }
+
+ len = devc->num_block_bytes - devc->num_block_read;
+ if (len > ACQ_BUFFER_SIZE)
+ len = ACQ_BUFFER_SIZE;
+ sr_dbg("Requesting read of %d bytes", len);
+
+ len = sr_scpi_read_data(scpi, (char *)devc->buffer, len);
+
+ if (len == -1) {
+ sr_err("Read error, aborting capture.");
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(cb_data, &packet);
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+
+ sr_dbg("Received %d bytes.", len);
+
+ devc->num_block_read += len;
+
+ if (ch->type == SR_CHANNEL_ANALOG) {
+ vref = devc->vert_reference[ch->index];
+ vdiv = devc->vdiv[ch->index] / 25.6;
+ offset = devc->vert_offset[ch->index];
+ if (devc->model->series->protocol >= PROTOCOL_V3)
+ for (i = 0; i < len; i++)
+ devc->data[i] = ((int)devc->buffer[i] - vref) * vdiv - offset;
+ else
+ for (i = 0; i < len; i++)
+ devc->data[i] = (128 - devc->buffer[i]) * vdiv - offset;
+ analog.channels = g_slist_append(NULL, ch);
+ analog.num_samples = len;
+ analog.data = devc->data;
+ analog.mq = SR_MQ_VOLTAGE;
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags = 0;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(cb_data, &packet);
+ g_slist_free(analog.channels);
+ } else {
+ logic.length = len;
+ logic.unitsize = 2;
+ logic.data = devc->buffer;
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ sr_session_send(cb_data, &packet);
+ }
+
+ if (devc->num_block_read == devc->num_block_bytes) {
+ sr_dbg("Block has been completed");
+ if (devc->model->series->protocol >= PROTOCOL_V3) {
+ /* Discard the terminating linefeed */
+ sr_scpi_read_data(scpi, (char *)devc->buffer, 1);
+ }
+ if (devc->format == FORMAT_IEEE488_2) {
+ /* Prepare for possible next block */
+ devc->num_header_bytes = 0;
+ devc->num_block_bytes = 0;
+ if (devc->data_source != DATA_SOURCE_LIVE)
+ rigol_ds_set_wait_event(devc, WAIT_BLOCK);
+ }
+ if (!sr_scpi_read_complete(scpi)) {
+ sr_err("Read should have been completed");
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(cb_data, &packet);
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+ devc->num_block_read = 0;
+ } else {
+ sr_dbg("%d of %d block bytes read", devc->num_block_read, devc->num_block_bytes);
+ }
+
+ devc->num_channel_bytes += len;
+
+ if (devc->num_channel_bytes < expected_data_bytes)
+ /* Don't have the full data for this channel yet, re-run. */
+ return TRUE;
+
+ /* End of data for this channel. */
+ if (devc->model->series->protocol >= PROTOCOL_V3) {
+ /* Signal end of data download to scope */
+ if (devc->data_source != DATA_SOURCE_LIVE)
+ /*
+ * This causes a query error, without it switching
+ * to the next channel causes an error. Fun with
+ * firmware...
+ */
+ rigol_ds_config_set(sdi, ":WAV:END");
+ }
+
+ if (ch->type == SR_CHANNEL_ANALOG
+ && devc->channel_entry->next != NULL) {
+ /* We got the frame for this analog channel, but
+ * there's another analog channel. */
+ devc->channel_entry = devc->channel_entry->next;
+ rigol_ds_channel_start(sdi);
+ } else {
+ /* Done with all analog channels in this frame. */
+ if (devc->enabled_digital_channels
+ && devc->channel_entry != devc->enabled_digital_channels) {
+ /* Now we need to get the digital data. */
+ devc->channel_entry = devc->enabled_digital_channels;
+ rigol_ds_channel_start(sdi);
+ } else {
+ /* Done with this frame. */
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(cb_data, &packet);
+
+ if (++devc->num_frames == devc->limit_frames) {
+ /* Last frame, stop capture. */
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ } else {
+ /* Get the next frame, starting with the first analog channel. */
+ if (devc->enabled_analog_channels)
+ devc->channel_entry = devc->enabled_analog_channels;
+ else
+ devc->channel_entry = devc->enabled_digital_channels;
+
+ rigol_ds_capture_start(sdi);
+
+ /* Start of next frame. */
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(cb_data, &packet);
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+SR_PRIV int rigol_ds_get_dev_cfg(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ char *t_s, *cmd;
+ unsigned int i;
+ int res;
+
+ devc = sdi->priv;
+
+ /* Analog channel state. */
+ for (i = 0; i < devc->model->analog_channels; i++) {
+ cmd = g_strdup_printf(":CHAN%d:DISP?", i + 1);
+ res = sr_scpi_get_string(sdi->conn, cmd, &t_s);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ devc->analog_channels[i] = !strcmp(t_s, "ON") || !strcmp(t_s, "1");
+ }
+ sr_dbg("Current analog channel state:");
+ for (i = 0; i < devc->model->analog_channels; i++)
+ sr_dbg("CH%d %s", i + 1, devc->analog_channels[i] ? "on" : "off");
+
+ /* Digital channel state. */
+ if (devc->model->has_digital) {
+ if (sr_scpi_get_string(sdi->conn, ":LA:DISP?", &t_s) != SR_OK)
+ return SR_ERR;
+ devc->la_enabled = !strcmp(t_s, "ON") ? TRUE : FALSE;
+ sr_dbg("Logic analyzer %s, current digital channel state:",
+ devc->la_enabled ? "enabled" : "disabled");
+ for (i = 0; i < 16; i++) {
+ cmd = g_strdup_printf(":DIG%d:TURN?", i);
+ res = sr_scpi_get_string(sdi->conn, cmd, &t_s);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ devc->digital_channels[i] = !strcmp(t_s, "ON") ? TRUE : FALSE;
+ g_free(t_s);
+ sr_dbg("D%d: %s", i, devc->digital_channels[i] ? "on" : "off");
+ }
+ }
+
+ /* Timebase. */
+ if (sr_scpi_get_float(sdi->conn, ":TIM:SCAL?", &devc->timebase) != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current timebase %g", devc->timebase);
+
+ /* Vertical gain. */
+ for (i = 0; i < devc->model->analog_channels; i++) {
+ cmd = g_strdup_printf(":CHAN%d:SCAL?", i + 1);
+ res = sr_scpi_get_float(sdi->conn, cmd, &devc->vdiv[i]);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ }
+ sr_dbg("Current vertical gain:");
+ for (i = 0; i < devc->model->analog_channels; i++)
+ sr_dbg("CH%d %g", i + 1, devc->vdiv[i]);
+
+ sr_dbg("Current vertical reference:");
+ if (devc->model->series->protocol >= PROTOCOL_V3) {
+ /* Vertical reference - not certain if this is the place to read it. */
+ for (i = 0; i < devc->model->analog_channels; i++) {
+ if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d", i + 1) != SR_OK)
+ return SR_ERR;
+ if (sr_scpi_get_int(sdi->conn, ":WAV:YREF?", &devc->vert_reference[i]) != SR_OK)
+ return SR_ERR;
+ sr_dbg("CH%d %d", i + 1, devc->vert_reference[i]);
+ }
+ }
+
+ /* Vertical offset. */
+ for (i = 0; i < devc->model->analog_channels; i++) {
+ cmd = g_strdup_printf(":CHAN%d:OFFS?", i + 1);
+ res = sr_scpi_get_float(sdi->conn, cmd, &devc->vert_offset[i]);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ }
+ sr_dbg("Current vertical offset:");
+ for (i = 0; i < devc->model->analog_channels; i++)
+ sr_dbg("CH%d %g", i + 1, devc->vert_offset[i]);
+
+ /* Coupling. */
+ for (i = 0; i < devc->model->analog_channels; i++) {
+ cmd = g_strdup_printf(":CHAN%d:COUP?", i + 1);
+ res = sr_scpi_get_string(sdi->conn, cmd, &devc->coupling[i]);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ }
+ sr_dbg("Current coupling:");
+ for (i = 0; i < devc->model->analog_channels; i++)
+ sr_dbg("CH%d %s", i + 1, devc->coupling[i]);
+
+ /* Trigger source. */
+ if (sr_scpi_get_string(sdi->conn, ":TRIG:EDGE:SOUR?", &devc->trigger_source) != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current trigger source %s", devc->trigger_source);
+
+ /* Horizontal trigger position. */
+ if (sr_scpi_get_float(sdi->conn, ":TIM:OFFS?", &devc->horiz_triggerpos) != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current horizontal trigger position %g", devc->horiz_triggerpos);
+
+ /* Trigger slope. */
+ if (sr_scpi_get_string(sdi->conn, ":TRIG:EDGE:SLOP?", &devc->trigger_slope) != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current trigger slope %s", devc->trigger_slope);
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Martin Ling <martin-git@earth.li>
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_RIGOL_DS_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_RIGOL_DS_PROTOCOL_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "rigol-ds"
+
+/* Size of acquisition buffers */
+#define ACQ_BUFFER_SIZE 32768
+
+#define MAX_ANALOG_CHANNELS 4
+#define MAX_DIGITAL_CHANNELS 16
+
+enum protocol_version {
+ PROTOCOL_V1, /* VS5000 */
+ PROTOCOL_V2, /* DS1000 */
+ PROTOCOL_V3, /* DS2000, DSO1000 */
+};
+
+enum data_format {
+ /* Used by DS1000 versions up to 2.02, and VS5000 series */
+ FORMAT_RAW,
+ /* Used by DS1000 versions from 2.04 onwards and all later series */
+ FORMAT_IEEE488_2,
+};
+
+enum data_source {
+ DATA_SOURCE_LIVE,
+ DATA_SOURCE_MEMORY,
+ DATA_SOURCE_SEGMENTED,
+};
+
+struct rigol_ds_vendor {
+ const char *name;
+ const char *full_name;
+};
+
+struct rigol_ds_series {
+ const struct rigol_ds_vendor *vendor;
+ const char *name;
+ enum protocol_version protocol;
+ enum data_format format;
+ uint64_t max_timebase[2];
+ uint64_t min_vdiv[2];
+ int num_horizontal_divs;
+ int live_samples;
+ int buffer_samples;
+};
+
+struct rigol_ds_model {
+ const struct rigol_ds_series *series;
+ const char *name;
+ uint64_t min_timebase[2];
+ unsigned int analog_channels;
+ bool has_digital;
+};
+
+enum wait_events {
+ WAIT_NONE, /* Don't wait */
+ WAIT_TRIGGER, /* Wait for trigger (only live capture) */
+ WAIT_BLOCK, /* Wait for block data (only when reading sample mem) */
+ WAIT_STOP, /* Wait for scope stopping (only single shots) */
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Device model */
+ const struct rigol_ds_model *model;
+ enum data_format format;
+
+ /* Device properties */
+ const uint64_t (*timebases)[2];
+ uint64_t num_timebases;
+ const uint64_t (*vdivs)[2];
+ uint64_t num_vdivs;
+
+ /* Channel groups */
+ struct sr_channel_group analog_groups[MAX_ANALOG_CHANNELS];
+ struct sr_channel_group digital_group;
+
+ /* Acquisition settings */
+ GSList *enabled_analog_channels;
+ GSList *enabled_digital_channels;
+ uint64_t limit_frames;
+ void *cb_data;
+ enum data_source data_source;
+ uint64_t analog_frame_size;
+ uint64_t digital_frame_size;
+
+ /* Device settings */
+ gboolean analog_channels[MAX_ANALOG_CHANNELS];
+ gboolean digital_channels[MAX_DIGITAL_CHANNELS];
+ gboolean la_enabled;
+ float timebase;
+ float vdiv[MAX_ANALOG_CHANNELS];
+ int vert_reference[MAX_ANALOG_CHANNELS];
+ float vert_offset[MAX_ANALOG_CHANNELS];
+ char *trigger_source;
+ float horiz_triggerpos;
+ char *trigger_slope;
+ char *coupling[MAX_ANALOG_CHANNELS];
+
+ /* Operational state */
+
+ /* Number of frames received in total. */
+ uint64_t num_frames;
+ /* GSList entry for the current channel. */
+ GSList *channel_entry;
+ /* Number of bytes received for current channel. */
+ uint64_t num_channel_bytes;
+ /* Number of bytes of block header read */
+ uint64_t num_header_bytes;
+ /* Number of bytes in current data block, if 0 block header expected */
+ uint64_t num_block_bytes;
+ /* Number of data block bytes already read */
+ uint64_t num_block_read;
+ /* What to wait for in *_receive */
+ enum wait_events wait_event;
+ /* Trigger/block copying/stop waiting status */
+ int wait_status;
+ /* Acq buffers used for reading from the scope and sending data to app */
+ unsigned char *buffer;
+ float *data;
+};
+
+SR_PRIV int rigol_ds_config_set(const struct sr_dev_inst *sdi, const char *format, ...);
+SR_PRIV int rigol_ds_capture_start(const struct sr_dev_inst *sdi);
+SR_PRIV int rigol_ds_channel_start(const struct sr_dev_inst *sdi);
+SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data);
+SR_PRIV int rigol_ds_get_dev_cfg(const struct sr_dev_inst *sdi);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 <glib.h>
+#include <libusb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+#define LOGIC16_VID 0x21a9
+#define LOGIC16_PID 0x1001
+
+#define USB_INTERFACE 0
+#define USB_CONFIGURATION 1
+#define FX2_FIRMWARE FIRMWARE_DIR "/saleae-logic16-fx2.fw"
+
+#define MAX_RENUM_DELAY_MS 3000
+#define NUM_SIMUL_TRANSFERS 32
+
+SR_PRIV struct sr_dev_driver saleae_logic16_driver_info;
+static struct sr_dev_driver *di = &saleae_logic16_driver_info;
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_VOLTAGE_THRESHOLD,
+ SR_CONF_TRIGGER_MATCH,
+
+ /* These are really implemented in the driver, not the hardware. */
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_CONTINUOUS,
+};
+
+static const int32_t soft_trigger_matches[] = {
+ SR_TRIGGER_ZERO,
+ SR_TRIGGER_ONE,
+ SR_TRIGGER_RISING,
+ SR_TRIGGER_FALLING,
+ SR_TRIGGER_EDGE,
+};
+
+static const char *channel_names[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8",
+ "9", "10", "11", "12", "13", "14", "15",
+ NULL,
+};
+
+static const struct {
+ enum voltage_range range;
+ gdouble low;
+ gdouble high;
+} volt_thresholds[] = {
+ { VOLTAGE_RANGE_18_33_V, 0.7, 1.4 },
+ { VOLTAGE_RANGE_5_V, 1.4, 3.6 },
+};
+
+static const uint64_t samplerates[] = {
+ SR_KHZ(500),
+ SR_MHZ(1),
+ SR_MHZ(2),
+ SR_MHZ(4),
+ SR_MHZ(5),
+ SR_MHZ(8),
+ SR_MHZ(10),
+ SR_KHZ(12500),
+ SR_MHZ(16),
+ SR_MHZ(25),
+ SR_MHZ(32),
+ SR_MHZ(40),
+ SR_MHZ(80),
+ SR_MHZ(100),
+};
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static gboolean check_conf_profile(libusb_device *dev)
+{
+ struct libusb_device_descriptor des;
+ struct libusb_device_handle *hdl;
+ gboolean ret;
+ unsigned char strdesc[64];
+
+ hdl = NULL;
+ ret = FALSE;
+ while (!ret) {
+ /* Assume the FW has not been loaded, unless proven wrong. */
+ if (libusb_get_device_descriptor(dev, &des) != 0)
+ break;
+
+ if (libusb_open(dev, &hdl) != 0)
+ break;
+
+ if (libusb_get_string_descriptor_ascii(hdl,
+ des.iManufacturer, strdesc, sizeof(strdesc)) < 0)
+ break;
+ if (strcmp((const char *)strdesc, "Saleae LLC"))
+ break;
+
+ if (libusb_get_string_descriptor_ascii(hdl,
+ des.iProduct, strdesc, sizeof(strdesc)) < 0)
+ break;
+ if (strcmp((const char *)strdesc, "Logic S/16"))
+ break;
+
+ /* If we made it here, it must be a configured Logic16. */
+ ret = TRUE;
+ }
+ if (hdl)
+ libusb_close(hdl);
+
+ return ret;
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_usb_dev_inst *usb;
+ struct sr_channel *ch;
+ struct sr_config *src;
+ GSList *l, *devices, *conn_devices;
+ struct libusb_device_descriptor des;
+ libusb_device **devlist;
+ int devcnt, ret, i, j;
+ const char *conn;
+
+ drvc = di->priv;
+
+ 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)
+ conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
+ else
+ conn_devices = NULL;
+
+ /* Find all Logic16 devices and upload firmware to them. */
+ devices = NULL;
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if (conn) {
+ usb = NULL;
+ for (l = conn_devices; l; l = l->next) {
+ usb = l->data;
+ if (usb->bus == libusb_get_bus_number(devlist[i])
+ && usb->address == libusb_get_device_address(devlist[i]))
+ break;
+ }
+ if (!l)
+ /* This device matched none of the ones that
+ * matched the conn specification. */
+ continue;
+ }
+
+ if ((ret = libusb_get_device_descriptor(devlist[i], &des)) != 0) {
+ sr_warn("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ if (des.idVendor != LOGIC16_VID || des.idProduct != LOGIC16_PID)
+ continue;
+
+ devcnt = g_slist_length(drvc->instances);
+ sdi = sr_dev_inst_new(devcnt, SR_ST_INITIALIZING,
+ "Saleae", "Logic16", NULL);
+ if (!sdi)
+ return NULL;
+ sdi->driver = di;
+
+ for (j = 0; channel_names[j]; j++) {
+ if (!(ch = sr_channel_new(j, SR_CHANNEL_LOGIC, TRUE,
+ channel_names[j])))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
+ return NULL;
+ devc->selected_voltage_range = VOLTAGE_RANGE_18_33_V;
+ sdi->priv = devc;
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ if (check_conf_profile(devlist[i])) {
+ /* Already has the firmware, so fix the new address. */
+ sr_dbg("Found a Logic16 device.");
+ sdi->status = SR_ST_INACTIVE;
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = sr_usb_dev_inst_new(
+ libusb_get_bus_number(devlist[i]),
+ libusb_get_device_address(devlist[i]), NULL);
+ } else {
+ if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION,
+ FX2_FIRMWARE) == SR_OK)
+ /* Store when this device's FW was updated. */
+ devc->fw_updated = g_get_monotonic_time();
+ else
+ sr_err("Firmware upload failed for "
+ "device %d.", devcnt);
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = sr_usb_dev_inst_new(
+ libusb_get_bus_number(devlist[i]), 0xff, NULL);
+ }
+ }
+ libusb_free_device_list(devlist, 1);
+ g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int logic16_dev_open(struct sr_dev_inst *sdi)
+{
+ libusb_device **devlist;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_device_descriptor des;
+ struct drv_context *drvc;
+ int ret, skip, i, device_count;
+
+ drvc = di->priv;
+ usb = sdi->conn;
+
+ if (sdi->status == SR_ST_ACTIVE)
+ /* Device is already in use. */
+ return SR_ERR;
+
+ skip = 0;
+ device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ if (device_count < 0) {
+ sr_err("Failed to get device list: %s.",
+ libusb_error_name(device_count));
+ return SR_ERR;
+ }
+
+ for (i = 0; i < device_count; i++) {
+ if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
+ sr_err("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ if (des.idVendor != LOGIC16_VID || des.idProduct != LOGIC16_PID)
+ continue;
+
+ if (sdi->status == SR_ST_INITIALIZING) {
+ if (skip != sdi->index) {
+ /* Skip devices of this type that aren't the one we want. */
+ skip += 1;
+ continue;
+ }
+ } else if (sdi->status == SR_ST_INACTIVE) {
+ /*
+ * This device is fully enumerated, so we need to find
+ * this device by vendor, product, bus and address.
+ */
+ if (libusb_get_bus_number(devlist[i]) != usb->bus
+ || libusb_get_device_address(devlist[i]) != usb->address)
+ /* This is not the one. */
+ continue;
+ }
+
+ if (!(ret = libusb_open(devlist[i], &usb->devhdl))) {
+ if (usb->address == 0xff)
+ /*
+ * First time we touch this device after FW
+ * upload, so we don't know the address yet.
+ */
+ usb->address = libusb_get_device_address(devlist[i]);
+ } else {
+ sr_err("Failed to open device: %s.",
+ libusb_error_name(ret));
+ break;
+ }
+
+ ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
+ if (ret == LIBUSB_ERROR_BUSY) {
+ sr_err("Unable to claim USB interface. Another "
+ "program or driver has already claimed it.");
+ break;
+ } else if (ret == LIBUSB_ERROR_NO_DEVICE) {
+ sr_err("Device has been disconnected.");
+ break;
+ } else if (ret != 0) {
+ sr_err("Unable to claim interface: %s.",
+ libusb_error_name(ret));
+ break;
+ }
+
+ if ((ret = logic16_init_device(sdi)) != SR_OK) {
+ sr_err("Failed to init device.");
+ break;
+ }
+
+ sdi->status = SR_ST_ACTIVE;
+ sr_info("Opened device %d on %d.%d, interface %d.",
+ sdi->index, usb->bus, usb->address, USB_INTERFACE);
+
+ break;
+ }
+ libusb_free_device_list(devlist, 1);
+
+ if (sdi->status != SR_ST_ACTIVE) {
+ if (usb->devhdl) {
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+ }
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int ret;
+ int64_t timediff_us, timediff_ms;
+
+ devc = sdi->priv;
+
+ /*
+ * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS
+ * milliseconds for the FX2 to renumerate.
+ */
+ ret = SR_ERR;
+ if (devc->fw_updated > 0) {
+ sr_info("Waiting for device to reset.");
+ /* Takes >= 300ms for the FX2 to be gone from the USB bus. */
+ g_usleep(300 * 1000);
+ timediff_ms = 0;
+ while (timediff_ms < MAX_RENUM_DELAY_MS) {
+ if ((ret = logic16_dev_open(sdi)) == SR_OK)
+ break;
+ g_usleep(100 * 1000);
+
+ timediff_us = g_get_monotonic_time() - devc->fw_updated;
+ timediff_ms = timediff_us / 1000;
+ sr_spew("Waited %" PRIi64 "ms.", timediff_ms);
+ }
+ if (ret != SR_OK) {
+ sr_err("Device failed to renumerate.");
+ return SR_ERR;
+ }
+ sr_info("Device came back after %" PRIi64 "ms.", timediff_ms);
+ } else {
+ sr_info("Firmware upload was not needed.");
+ ret = logic16_dev_open(sdi);
+ }
+
+ if (ret != SR_OK) {
+ sr_err("Unable to open device.");
+ return SR_ERR;
+ }
+
+ if (devc->cur_samplerate == 0) {
+ /* Samplerate hasn't been set; default to the slowest one. */
+ devc->cur_samplerate = samplerates[0];
+ }
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ usb = sdi->conn;
+ if (usb->devhdl == NULL)
+ return SR_ERR;
+
+ sr_info("Closing device %d on %d.%d interface %d.",
+ sdi->index, usb->bus, usb->address, USB_INTERFACE);
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ int ret;
+ struct drv_context *drvc;
+
+ if (!(drvc = di->priv))
+ /* Can get called on an unused driver, doesn't matter. */
+ return SR_OK;
+
+
+ ret = std_dev_clear(di, NULL);
+ g_free(drvc);
+ di->priv = NULL;
+
+ return ret;
+}
+
+static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ GVariant *range[2];
+ char str[128];
+ int ret;
+ unsigned int i;
+
+ (void)cg;
+
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_CONN:
+ if (!sdi || !sdi->conn)
+ return SR_ERR_ARG;
+ usb = sdi->conn;
+ if (usb->address == 255)
+ /* Device still needs to re-enumerate after firmware
+ * upload, so we don't know its (future) address. */
+ return SR_ERR;
+ snprintf(str, 128, "%d.%d", usb->bus, usb->address);
+ *data = g_variant_new_string(str);
+ break;
+ case SR_CONF_SAMPLERATE:
+ if (!sdi)
+ return SR_ERR;
+ devc = sdi->priv;
+ *data = g_variant_new_uint64(devc->cur_samplerate);
+ break;
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ if (!sdi)
+ return SR_ERR;
+ devc = sdi->priv;
+ ret = SR_ERR;
+ for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
+ if (devc->selected_voltage_range !=
+ volt_thresholds[i].range)
+ continue;
+ range[0] = g_variant_new_double(volt_thresholds[i].low);
+ range[1] = g_variant_new_double(volt_thresholds[i].high);
+ *data = g_variant_new_tuple(range, 2);
+ ret = SR_OK;
+ break;
+ }
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ gdouble low, high;
+ int ret;
+ unsigned int i;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_SAMPLERATE:
+ devc->cur_samplerate = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ g_variant_get(data, "(dd)", &low, &high);
+ ret = SR_ERR_ARG;
+ for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
+ if (fabs(volt_thresholds[i].low - low) < 0.1 &&
+ fabs(volt_thresholds[i].high - high) < 0.1) {
+ devc->selected_voltage_range =
+ volt_thresholds[i].range;
+ ret = SR_OK;
+ break;
+ }
+ }
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ GVariant *gvar, *range[2];
+ GVariantBuilder gvb;
+ int ret;
+ unsigned int i;
+
+ (void)sdi;
+ (void)cg;
+
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
+ samplerates, ARRAY_SIZE(samplerates), sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
+ range[0] = g_variant_new_double(volt_thresholds[i].low);
+ range[1] = g_variant_new_double(volt_thresholds[i].high);
+ gvar = g_variant_new_tuple(range, 2);
+ g_variant_builder_add_value(&gvb, gvar);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ soft_trigger_matches, ARRAY_SIZE(soft_trigger_matches),
+ sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static void abort_acquisition(struct dev_context *devc)
+{
+ int i;
+
+ devc->sent_samples = -1;
+
+ for (i = devc->num_transfers - 1; i >= 0; i--) {
+ if (devc->transfers[i])
+ libusb_cancel_transfer(devc->transfers[i]);
+ }
+}
+
+static unsigned int bytes_per_ms(struct dev_context *devc)
+{
+ return devc->cur_samplerate * devc->num_channels / 8000;
+}
+
+static size_t get_buffer_size(struct dev_context *devc)
+{
+ size_t s;
+
+ /*
+ * The buffer should be large enough to hold 10ms of data and
+ * a multiple of 512.
+ */
+ s = 10 * bytes_per_ms(devc);
+ return (s + 511) & ~511;
+}
+
+static unsigned int get_number_of_transfers(struct dev_context *devc)
+{
+ unsigned int n;
+
+ /* Total buffer size should be able to hold about 500ms of data. */
+ n = 500 * bytes_per_ms(devc) / get_buffer_size(devc);
+
+ if (n > NUM_SIMUL_TRANSFERS)
+ return NUM_SIMUL_TRANSFERS;
+
+ return n;
+}
+
+static unsigned int get_timeout(struct dev_context *devc)
+{
+ size_t total_size;
+ unsigned int timeout;
+
+ total_size = get_buffer_size(devc) * get_number_of_transfers(devc);
+ timeout = total_size / bytes_per_ms(devc);
+ return timeout + timeout / 4; /* Leave a headroom of 25% percent. */
+}
+
+static int configure_channels(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ GSList *l;
+ uint16_t channel_bit;
+
+ devc = sdi->priv;
+
+ devc->cur_channels = 0;
+ devc->num_channels = 0;
+ for (l = sdi->channels; l; l = l->next) {
+ ch = (struct sr_channel *)l->data;
+ if (ch->enabled == FALSE)
+ continue;
+
+ channel_bit = 1 << (ch->index);
+
+ devc->cur_channels |= channel_bit;
+
+#ifdef WORDS_BIGENDIAN
+ /*
+ * Output logic data should be stored in little endian format.
+ * To speed things up during conversion, do the switcharoo
+ * here instead.
+ */
+ channel_bit = 1 << (ch->index ^ 8);
+#endif
+
+ devc->channel_masks[devc->num_channels++] = channel_bit;
+ }
+
+ return SR_OK;
+}
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+ struct timeval tv;
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ const struct sr_dev_inst *sdi;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+ drvc = di->priv;
+ devc = sdi->priv;
+
+ tv.tv_sec = tv.tv_usec = 0;
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+ if (devc->sent_samples == -2) {
+ logic16_abort_acquisition(sdi);
+ abort_acquisition(devc);
+ }
+
+ return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ struct sr_trigger *trigger;
+ struct libusb_transfer *transfer;
+ unsigned int i, timeout, num_transfers;
+ int ret;
+ unsigned char *buf;
+ size_t size, convsize;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ drvc = di->priv;
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ /* Configures devc->cur_channels. */
+ if (configure_channels(sdi) != SR_OK) {
+ sr_err("Failed to configure channels.");
+ return SR_ERR;
+ }
+
+ devc->cb_data = cb_data;
+ devc->sent_samples = 0;
+ devc->empty_transfer_count = 0;
+ devc->cur_channel = 0;
+ memset(devc->channel_data, 0, sizeof(devc->channel_data));
+
+ if ((trigger = sr_session_trigger_get(sdi->session))) {
+ devc->stl = soft_trigger_logic_new(sdi, trigger);
+ devc->trigger_fired = FALSE;
+ } else
+ devc->trigger_fired = TRUE;
+
+ timeout = get_timeout(devc);
+ num_transfers = get_number_of_transfers(devc);
+ size = get_buffer_size(devc);
+ convsize = (size / devc->num_channels + 2) * 16;
+ devc->submitted_transfers = 0;
+
+ devc->convbuffer_size = convsize;
+ if (!(devc->convbuffer = g_try_malloc(convsize))) {
+ sr_err("Conversion buffer malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+
+ devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers);
+ if (!devc->transfers) {
+ sr_err("USB transfers malloc failed.");
+ g_free(devc->convbuffer);
+ return SR_ERR_MALLOC;
+ }
+
+ if ((ret = logic16_setup_acquisition(sdi, devc->cur_samplerate,
+ devc->cur_channels)) != SR_OK) {
+ g_free(devc->transfers);
+ g_free(devc->convbuffer);
+ return ret;
+ }
+
+ devc->num_transfers = num_transfers;
+ for (i = 0; i < num_transfers; i++) {
+ if (!(buf = g_try_malloc(size))) {
+ sr_err("USB transfer buffer malloc failed.");
+ if (devc->submitted_transfers)
+ abort_acquisition(devc);
+ else {
+ g_free(devc->transfers);
+ g_free(devc->convbuffer);
+ }
+ return SR_ERR_MALLOC;
+ }
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, usb->devhdl,
+ 2 | LIBUSB_ENDPOINT_IN, buf, size,
+ logic16_receive_transfer, (void *)sdi, timeout);
+ if ((ret = libusb_submit_transfer(transfer)) != 0) {
+ sr_err("Failed to submit transfer: %s.",
+ libusb_error_name(ret));
+ libusb_free_transfer(transfer);
+ g_free(buf);
+ abort_acquisition(devc);
+ return SR_ERR;
+ }
+ devc->transfers[i] = transfer;
+ devc->submitted_transfers++;
+ }
+
+ devc->ctx = drvc->sr_ctx;
+
+ usb_source_add(sdi->session, devc->ctx, timeout, receive_data, (void *)sdi);
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ if ((ret = logic16_start_acquisition(sdi)) != SR_OK) {
+ abort_acquisition(devc);
+ return ret;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ int ret;
+
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ ret = logic16_abort_acquisition(sdi);
+
+ abort_acquisition(sdi->priv);
+
+ return ret;
+}
+
+SR_PRIV struct sr_dev_driver saleae_logic16_driver_info = {
+ .name = "saleae-logic16",
+ .longname = "Saleae Logic16",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 "protocol.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <stdio.h>
+#include <errno.h>
+#include <math.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define FPGA_FIRMWARE_18 FIRMWARE_DIR"/saleae-logic16-fpga-18.bitstream"
+#define FPGA_FIRMWARE_33 FIRMWARE_DIR"/saleae-logic16-fpga-33.bitstream"
+
+#define MAX_SAMPLE_RATE SR_MHZ(100)
+#define MAX_4CH_SAMPLE_RATE SR_MHZ(50)
+#define MAX_7CH_SAMPLE_RATE SR_MHZ(40)
+#define MAX_8CH_SAMPLE_RATE SR_MHZ(32)
+#define MAX_10CH_SAMPLE_RATE SR_MHZ(25)
+#define MAX_13CH_SAMPLE_RATE SR_MHZ(16)
+
+#define BASE_CLOCK_0_FREQ SR_MHZ(100)
+#define BASE_CLOCK_1_FREQ SR_MHZ(160)
+
+#define COMMAND_START_ACQUISITION 1
+#define COMMAND_ABORT_ACQUISITION_ASYNC 2
+#define COMMAND_WRITE_EEPROM 6
+#define COMMAND_READ_EEPROM 7
+#define COMMAND_WRITE_LED_TABLE 0x7a
+#define COMMAND_SET_LED_MODE 0x7b
+#define COMMAND_RETURN_TO_BOOTLOADER 0x7c
+#define COMMAND_ABORT_ACQUISITION_SYNC 0x7d
+#define COMMAND_FPGA_UPLOAD_INIT 0x7e
+#define COMMAND_FPGA_UPLOAD_SEND_DATA 0x7f
+#define COMMAND_FPGA_WRITE_REGISTER 0x80
+#define COMMAND_FPGA_READ_REGISTER 0x81
+#define COMMAND_GET_REVID 0x82
+
+#define WRITE_EEPROM_COOKIE1 0x42
+#define WRITE_EEPROM_COOKIE2 0x55
+#define READ_EEPROM_COOKIE1 0x33
+#define READ_EEPROM_COOKIE2 0x81
+#define ABORT_ACQUISITION_SYNC_PATTERN 0x55
+
+#define MAX_EMPTY_TRANSFERS 64
+
+static void encrypt(uint8_t *dest, const uint8_t *src, uint8_t cnt)
+{
+ uint8_t state1 = 0x9b, state2 = 0x54;
+ uint8_t t, v;
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ v = src[i];
+ t = (((v ^ state2 ^ 0x2b) - 0x05) ^ 0x35) - 0x39;
+ t = (((t ^ state1 ^ 0x5a) - 0xb0) ^ 0x38) - 0x45;
+ dest[i] = state2 = t;
+ state1 = v;
+ }
+}
+
+static void decrypt(uint8_t *dest, const uint8_t *src, uint8_t cnt)
+{
+ uint8_t state1 = 0x9b, state2 = 0x54;
+ uint8_t t, v;
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ v = src[i];
+ t = (((v + 0x45) ^ 0x38) + 0xb0) ^ 0x5a ^ state1;
+ t = (((t + 0x39) ^ 0x35) + 0x05) ^ 0x2b ^ state2;
+ dest[i] = state1 = t;
+ state2 = v;
+ }
+}
+
+static int do_ep1_command(const struct sr_dev_inst *sdi,
+ const uint8_t *command, uint8_t cmd_len,
+ uint8_t *reply, uint8_t reply_len)
+{
+ uint8_t buf[64];
+ struct sr_usb_dev_inst *usb;
+ int ret, xfer;
+
+ usb = sdi->conn;
+
+ if (cmd_len < 1 || cmd_len > 64 || reply_len > 64 ||
+ command == NULL || (reply_len > 0 && reply == NULL))
+ return SR_ERR_ARG;
+
+ encrypt(buf, command, cmd_len);
+
+ ret = libusb_bulk_transfer(usb->devhdl, 1, buf, cmd_len, &xfer, 1000);
+ if (ret != 0) {
+ sr_dbg("Failed to send EP1 command 0x%02x: %s.",
+ command[0], libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if (xfer != cmd_len) {
+ sr_dbg("Failed to send EP1 command 0x%02x: incorrect length "
+ "%d != %d.", xfer, cmd_len);
+ return SR_ERR;
+ }
+
+ if (reply_len == 0)
+ return SR_OK;
+
+ ret = libusb_bulk_transfer(usb->devhdl, 0x80 | 1, buf, reply_len,
+ &xfer, 1000);
+ if (ret != 0) {
+ sr_dbg("Failed to receive reply to EP1 command 0x%02x: %s.",
+ command[0], libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if (xfer != reply_len) {
+ sr_dbg("Failed to receive reply to EP1 command 0x%02x: "
+ "incorrect length %d != %d.", xfer, reply_len);
+ return SR_ERR;
+ }
+
+ decrypt(reply, buf, reply_len);
+
+ return SR_OK;
+}
+
+static int read_eeprom(const struct sr_dev_inst *sdi,
+ uint8_t address, uint8_t length, uint8_t *buf)
+{
+ uint8_t command[5] = {
+ COMMAND_READ_EEPROM,
+ READ_EEPROM_COOKIE1,
+ READ_EEPROM_COOKIE2,
+ address,
+ length,
+ };
+
+ return do_ep1_command(sdi, command, 5, buf, length);
+}
+
+static int upload_led_table(const struct sr_dev_inst *sdi,
+ const uint8_t *table, uint8_t offset, uint8_t cnt)
+{
+ uint8_t chunk, command[64];
+ int ret;
+
+ if (cnt < 1 || cnt + offset > 64 || table == NULL)
+ return SR_ERR_ARG;
+
+ while (cnt > 0) {
+ chunk = (cnt > 32 ? 32 : cnt);
+
+ command[0] = COMMAND_WRITE_LED_TABLE;
+ command[1] = offset;
+ command[2] = chunk;
+ memcpy(command + 3, table, chunk);
+
+ ret = do_ep1_command(sdi, command, 3 + chunk, NULL, 0);
+ if (ret != SR_OK)
+ return ret;
+
+ table += chunk;
+ offset += chunk;
+ cnt -= chunk;
+ }
+
+ return SR_OK;
+}
+
+static int set_led_mode(const struct sr_dev_inst *sdi,
+ uint8_t animate, uint16_t t2reload, uint8_t div,
+ uint8_t repeat)
+{
+ uint8_t command[6] = {
+ COMMAND_SET_LED_MODE,
+ animate,
+ t2reload & 0xff,
+ t2reload >> 8,
+ div,
+ repeat,
+ };
+
+ return do_ep1_command(sdi, command, 6, NULL, 0);
+}
+
+static int read_fpga_register(const struct sr_dev_inst *sdi,
+ uint8_t address, uint8_t *value)
+{
+ uint8_t command[3] = {
+ COMMAND_FPGA_READ_REGISTER,
+ 1,
+ address,
+ };
+
+ return do_ep1_command(sdi, command, 3, value, 1);
+}
+
+static int write_fpga_registers(const struct sr_dev_inst *sdi,
+ uint8_t (*regs)[2], uint8_t cnt)
+{
+ uint8_t command[64];
+ int i;
+
+ if (cnt < 1 || cnt > 31)
+ return SR_ERR_ARG;
+
+ command[0] = COMMAND_FPGA_WRITE_REGISTER;
+ command[1] = cnt;
+ for (i = 0; i < cnt; i++) {
+ command[2 + 2 * i] = regs[i][0];
+ command[3 + 2 * i] = regs[i][1];
+ }
+
+ return do_ep1_command(sdi, command, 2 * (cnt + 1), NULL, 0);
+}
+
+static int write_fpga_register(const struct sr_dev_inst *sdi,
+ uint8_t address, uint8_t value)
+{
+ uint8_t regs[2] = { address, value };
+
+ return write_fpga_registers(sdi, ®s, 1);
+}
+
+static uint8_t map_eeprom_data(uint8_t v)
+{
+ return (((v ^ 0x80) + 0x44) ^ 0xd5) + 0x69;
+}
+
+static int prime_fpga(const struct sr_dev_inst *sdi)
+{
+ uint8_t eeprom_data[16];
+ uint8_t old_reg_10, version;
+ uint8_t regs[8][2] = {
+ {10, 0x00},
+ {10, 0x40},
+ {12, 0},
+ {10, 0xc0},
+ {10, 0x40},
+ {6, 0},
+ {7, 1},
+ {7, 0}
+ };
+ int i, ret;
+
+ if ((ret = read_eeprom(sdi, 16, 16, eeprom_data)) != SR_OK)
+ return ret;
+
+ if ((ret = read_fpga_register(sdi, 10, &old_reg_10)) != SR_OK)
+ return ret;
+
+ regs[0][1] = (old_reg_10 &= 0x7f);
+ regs[1][1] |= old_reg_10;
+ regs[3][1] |= old_reg_10;
+ regs[4][1] |= old_reg_10;
+
+ for (i = 0; i < 16; i++) {
+ regs[2][1] = eeprom_data[i];
+ regs[5][1] = map_eeprom_data(eeprom_data[i]);
+ if (i)
+ ret = write_fpga_registers(sdi, ®s[2], 6);
+ else
+ ret = write_fpga_registers(sdi, ®s[0], 8);
+ if (ret != SR_OK)
+ return ret;
+ }
+
+ if ((ret = write_fpga_register(sdi, 10, old_reg_10)) != SR_OK)
+ return ret;
+
+ if ((ret = read_fpga_register(sdi, 0, &version)) != SR_OK)
+ return ret;
+
+ if (version != 0x10) {
+ sr_err("Invalid FPGA bitstream version: 0x%02x != 0x10.", version);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static void make_heartbeat(uint8_t *table, int len)
+{
+ int i, j;
+
+ memset(table, 0, len);
+ len >>= 3;
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < len; j++)
+ *table++ = sin(j * M_PI / len) * 255;
+}
+
+static int configure_led(const struct sr_dev_inst *sdi)
+{
+ uint8_t table[64];
+ int ret;
+
+ make_heartbeat(table, 64);
+ if ((ret = upload_led_table(sdi, table, 0, 64)) != SR_OK)
+ return ret;
+
+ return set_led_mode(sdi, 1, 6250, 0, 1);
+}
+
+static int upload_fpga_bitstream(const struct sr_dev_inst *sdi,
+ enum voltage_range vrange)
+{
+ struct dev_context *devc;
+ int offset, chunksize, ret;
+ const char *filename;
+ uint8_t len, buf[256 * 62], command[64];
+ FILE *fw;
+
+ devc = sdi->priv;
+
+ if (devc->cur_voltage_range == vrange)
+ return SR_OK;
+
+ switch (vrange) {
+ case VOLTAGE_RANGE_18_33_V:
+ filename = FPGA_FIRMWARE_18;
+ break;
+ case VOLTAGE_RANGE_5_V:
+ filename = FPGA_FIRMWARE_33;
+ break;
+ default:
+ sr_err("Unsupported voltage range.");
+ return SR_ERR;
+ }
+
+ sr_info("Uploading FPGA bitstream at %s.", filename);
+ if ((fw = g_fopen(filename, "rb")) == NULL) {
+ sr_err("Unable to open bitstream file %s for reading: %s.",
+ filename, strerror(errno));
+ return SR_ERR;
+ }
+
+ buf[0] = COMMAND_FPGA_UPLOAD_INIT;
+ if ((ret = do_ep1_command(sdi, buf, 1, NULL, 0)) != SR_OK) {
+ fclose(fw);
+ return ret;
+ }
+
+ while (1) {
+ chunksize = fread(buf, 1, sizeof(buf), fw);
+ if (chunksize == 0)
+ break;
+
+ for (offset = 0; offset < chunksize; offset += 62) {
+ len = (offset + 62 > chunksize ?
+ chunksize - offset : 62);
+ command[0] = COMMAND_FPGA_UPLOAD_SEND_DATA;
+ command[1] = len;
+ memcpy(command + 2, buf + offset, len);
+ ret = do_ep1_command(sdi, command, len + 2, NULL, 0);
+ if (ret != SR_OK) {
+ fclose(fw);
+ return ret;
+ }
+ }
+
+ sr_info("Uploaded %d bytes.", chunksize);
+ }
+ fclose(fw);
+ sr_info("FPGA bitstream upload done.");
+
+ if ((ret = prime_fpga(sdi)) != SR_OK)
+ return ret;
+
+ if ((ret = configure_led(sdi)) != SR_OK)
+ return ret;
+
+ devc->cur_voltage_range = vrange;
+ return SR_OK;
+}
+
+static int abort_acquisition_sync(const struct sr_dev_inst *sdi)
+{
+ static const uint8_t command[2] = {
+ COMMAND_ABORT_ACQUISITION_SYNC,
+ ABORT_ACQUISITION_SYNC_PATTERN,
+ };
+ uint8_t reply, expected_reply;
+ int ret;
+
+ if ((ret = do_ep1_command(sdi, command, 2, &reply, 1)) != SR_OK)
+ return ret;
+
+ expected_reply = ~command[1];
+ if (reply != expected_reply) {
+ sr_err("Invalid response for abort acquisition command: "
+ "0x%02x != 0x%02x.", reply, expected_reply);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int logic16_setup_acquisition(const struct sr_dev_inst *sdi,
+ uint64_t samplerate, uint16_t channels)
+{
+ uint8_t clock_select, reg1, reg10;
+ uint64_t div;
+ int i, ret, nchan = 0;
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ if (samplerate == 0 || samplerate > MAX_SAMPLE_RATE) {
+ sr_err("Unable to sample at %" PRIu64 "Hz.", samplerate);
+ return SR_ERR;
+ }
+
+ if (BASE_CLOCK_0_FREQ % samplerate == 0 &&
+ (div = BASE_CLOCK_0_FREQ / samplerate) <= 256) {
+ clock_select = 0;
+ } else if (BASE_CLOCK_1_FREQ % samplerate == 0 &&
+ (div = BASE_CLOCK_1_FREQ / samplerate) <= 256) {
+ clock_select = 1;
+ } else {
+ sr_err("Unable to sample at %" PRIu64 "Hz.", samplerate);
+ return SR_ERR;
+ }
+
+ for (i = 0; i < 16; i++)
+ if (channels & (1U << i))
+ nchan++;
+
+ if ((nchan >= 13 && samplerate > MAX_13CH_SAMPLE_RATE) ||
+ (nchan >= 10 && samplerate > MAX_10CH_SAMPLE_RATE) ||
+ (nchan >= 8 && samplerate > MAX_8CH_SAMPLE_RATE) ||
+ (nchan >= 7 && samplerate > MAX_7CH_SAMPLE_RATE) ||
+ (nchan >= 4 && samplerate > MAX_4CH_SAMPLE_RATE)) {
+ sr_err("Unable to sample at %" PRIu64 "Hz "
+ "with this many channels.", samplerate);
+ return SR_ERR;
+ }
+
+ ret = upload_fpga_bitstream(sdi, devc->selected_voltage_range);
+ if (ret != SR_OK)
+ return ret;
+
+ if ((ret = read_fpga_register(sdi, 1, ®1)) != SR_OK)
+ return ret;
+
+ if (reg1 != 0x08) {
+ sr_dbg("Invalid state at acquisition setup: 0x%02x != 0x08.", reg1);
+ return SR_ERR;
+ }
+
+ if ((ret = write_fpga_register(sdi, 1, 0x40)) != SR_OK)
+ return ret;
+
+ if ((ret = write_fpga_register(sdi, 10, clock_select)) != SR_OK)
+ return ret;
+
+ if ((ret = write_fpga_register(sdi, 4, (uint8_t)(div - 1))) != SR_OK)
+ return ret;
+
+ if ((ret = write_fpga_register(sdi, 2, (uint8_t)(channels & 0xff))) != SR_OK)
+ return ret;
+
+ if ((ret = write_fpga_register(sdi, 3, (uint8_t)(channels >> 8))) != SR_OK)
+ return ret;
+
+ if ((ret = write_fpga_register(sdi, 1, 0x42)) != SR_OK)
+ return ret;
+
+ if ((ret = write_fpga_register(sdi, 1, 0x40)) != SR_OK)
+ return ret;
+
+ if ((ret = read_fpga_register(sdi, 1, ®1)) != SR_OK)
+ return ret;
+
+ if (reg1 != 0x48) {
+ sr_dbg("Invalid state at acquisition setup: 0x%02x != 0x48.", reg1);
+ return SR_ERR;
+ }
+
+ if ((ret = read_fpga_register(sdi, 10, ®10)) != SR_OK)
+ return ret;
+
+ if (reg10 != clock_select) {
+ sr_dbg("Invalid state at acquisition setup: 0x%02x != 0x%02x.",
+ reg10, clock_select);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int logic16_start_acquisition(const struct sr_dev_inst *sdi)
+{
+ static const uint8_t command[1] = {
+ COMMAND_START_ACQUISITION,
+ };
+ int ret;
+
+ if ((ret = do_ep1_command(sdi, command, 1, NULL, 0)) != SR_OK)
+ return ret;
+
+ return write_fpga_register(sdi, 1, 0x41);
+}
+
+SR_PRIV int logic16_abort_acquisition(const struct sr_dev_inst *sdi)
+{
+ static const uint8_t command[1] = {
+ COMMAND_ABORT_ACQUISITION_ASYNC,
+ };
+ int ret;
+ uint8_t reg1, reg8, reg9;
+
+ if ((ret = do_ep1_command(sdi, command, 1, NULL, 0)) != SR_OK)
+ return ret;
+
+ if ((ret = write_fpga_register(sdi, 1, 0x00)) != SR_OK)
+ return ret;
+
+ if ((ret = read_fpga_register(sdi, 1, ®1)) != SR_OK)
+ return ret;
+
+ if (reg1 != 0x08) {
+ sr_dbg("Invalid state at acquisition stop: 0x%02x != 0x08.", reg1);
+ return SR_ERR;
+ }
+
+ if ((ret = read_fpga_register(sdi, 8, ®8)) != SR_OK)
+ return ret;
+
+ if ((ret = read_fpga_register(sdi, 9, ®9)) != SR_OK)
+ return ret;
+
+ return SR_OK;
+}
+
+SR_PRIV int logic16_init_device(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int ret;
+
+ devc = sdi->priv;
+
+ devc->cur_voltage_range = VOLTAGE_RANGE_UNKNOWN;
+
+ if ((ret = abort_acquisition_sync(sdi)) != SR_OK)
+ return ret;
+
+ if ((ret = read_eeprom(sdi, 8, 8, devc->eeprom_data)) != SR_OK)
+ return ret;
+
+ ret = upload_fpga_bitstream(sdi, devc->selected_voltage_range);
+ if (ret != SR_OK)
+ return ret;
+
+ return SR_OK;
+}
+
+static void finish_acquisition(struct sr_dev_inst *sdi)
+{
+ struct sr_datafeed_packet packet;
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ /* Terminate session. */
+ packet.type = SR_DF_END;
+ sr_session_send(devc->cb_data, &packet);
+
+ /* Remove fds from polling. */
+ usb_source_remove(sdi->session, devc->ctx);
+
+ devc->num_transfers = 0;
+ g_free(devc->transfers);
+ g_free(devc->convbuffer);
+ if (devc->stl) {
+ soft_trigger_logic_free(devc->stl);
+ devc->stl = NULL;
+ }
+}
+
+static void free_transfer(struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ unsigned int i;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+
+ g_free(transfer->buffer);
+ transfer->buffer = NULL;
+ libusb_free_transfer(transfer);
+
+ for (i = 0; i < devc->num_transfers; i++) {
+ if (devc->transfers[i] == transfer) {
+ devc->transfers[i] = NULL;
+ break;
+ }
+ }
+
+ devc->submitted_transfers--;
+ if (devc->submitted_transfers == 0)
+ finish_acquisition(sdi);
+}
+
+static void resubmit_transfer(struct libusb_transfer *transfer)
+{
+ int ret;
+
+ if ((ret = libusb_submit_transfer(transfer)) == LIBUSB_SUCCESS)
+ return;
+
+ free_transfer(transfer);
+ /* TODO: Stop session? */
+
+ sr_err("%s: %s", __func__, libusb_error_name(ret));
+}
+
+static size_t convert_sample_data(struct dev_context *devc,
+ uint8_t *dest, size_t destcnt, const uint8_t *src, size_t srccnt)
+{
+ uint16_t *channel_data;
+ int i, cur_channel;
+ size_t ret = 0;
+ uint16_t sample, channel_mask;
+
+ srccnt /= 2;
+
+ channel_data = devc->channel_data;
+ cur_channel = devc->cur_channel;
+
+ while (srccnt--) {
+ sample = src[0] | (src[1] << 8);
+ src += 2;
+
+ channel_mask = devc->channel_masks[cur_channel];
+
+ for (i = 15; i >= 0; --i, sample >>= 1)
+ if (sample & 1)
+ channel_data[i] |= channel_mask;
+
+ if (++cur_channel == devc->num_channels) {
+ cur_channel = 0;
+ if (destcnt < 16 * 2) {
+ sr_err("Conversion buffer too small!");
+ break;
+ }
+ memcpy(dest, channel_data, 16 * 2);
+ memset(channel_data, 0, 16 * 2);
+ dest += 16 * 2;
+ ret += 16;
+ destcnt -= 16 * 2;
+ }
+ }
+
+ devc->cur_channel = cur_channel;
+
+ return ret;
+}
+
+SR_PRIV void logic16_receive_transfer(struct libusb_transfer *transfer)
+{
+ gboolean packet_has_error = FALSE;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ size_t new_samples, num_samples;
+ int trigger_offset;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+
+ /*
+ * If acquisition has already ended, just free any queued up
+ * transfer that come in.
+ */
+ if (devc->sent_samples < 0) {
+ free_transfer(transfer);
+ return;
+ }
+
+ sr_info("receive_transfer(): status %d received %d bytes.",
+ transfer->status, transfer->actual_length);
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ devc->sent_samples = -2;
+ free_transfer(transfer);
+ return;
+ case LIBUSB_TRANSFER_COMPLETED:
+ case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */
+ break;
+ default:
+ packet_has_error = TRUE;
+ break;
+ }
+
+ if (transfer->actual_length & 1) {
+ sr_err("Got an odd number of bytes from the device. "
+ "This should not happen.");
+ /* Bail out right away. */
+ packet_has_error = TRUE;
+ devc->empty_transfer_count = MAX_EMPTY_TRANSFERS;
+ }
+
+ if (transfer->actual_length == 0 || packet_has_error) {
+ devc->empty_transfer_count++;
+ if (devc->empty_transfer_count > MAX_EMPTY_TRANSFERS) {
+ /*
+ * The FX2 gave up. End the acquisition, the frontend
+ * will work out that the samplecount is short.
+ */
+ devc->sent_samples = -2;
+ free_transfer(transfer);
+ } else {
+ resubmit_transfer(transfer);
+ }
+ return;
+ } else {
+ devc->empty_transfer_count = 0;
+ }
+
+ new_samples = convert_sample_data(devc, devc->convbuffer,
+ devc->convbuffer_size, transfer->buffer, transfer->actual_length);
+
+ if (new_samples > 0) {
+ if (devc->trigger_fired) {
+ /* Send the incoming transfer to the session bus. */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ if (devc->limit_samples &&
+ new_samples > devc->limit_samples - devc->sent_samples)
+ new_samples = devc->limit_samples - devc->sent_samples;
+ logic.length = new_samples * 2;
+ logic.unitsize = 2;
+ logic.data = devc->convbuffer;
+ sr_session_send(devc->cb_data, &packet);
+ devc->sent_samples += new_samples;
+ } else {
+ trigger_offset = soft_trigger_logic_check(devc->stl,
+ devc->convbuffer, new_samples * 2);
+ if (trigger_offset > -1) {
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ num_samples = new_samples - trigger_offset;
+ if (devc->limit_samples &&
+ num_samples > devc->limit_samples - devc->sent_samples)
+ num_samples = devc->limit_samples - devc->sent_samples;
+ logic.length = num_samples * 2;
+ logic.unitsize = 2;
+ logic.data = devc->convbuffer + trigger_offset * 2;
+ sr_session_send(devc->cb_data, &packet);
+ devc->sent_samples += num_samples;
+
+ devc->trigger_fired = TRUE;
+ }
+ }
+
+ if (devc->limit_samples &&
+ (uint64_t)devc->sent_samples >= devc->limit_samples) {
+ devc->sent_samples = -2;
+ free_transfer(transfer);
+ return;
+ }
+ }
+
+ resubmit_transfer(transfer);
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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_SALEAE_LOGIC16_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SALEAE_LOGIC16_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "saleae-logic16"
+
+enum voltage_range {
+ VOLTAGE_RANGE_UNKNOWN,
+ VOLTAGE_RANGE_18_33_V, /* 1.8V and 3.3V logic */
+ VOLTAGE_RANGE_5_V, /* 5V logic */
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /*
+ * Since we can't keep track of a Logic16 device after upgrading
+ * the firmware (it renumerates into a different device address
+ * after the upgrade) this is like a global lock. No device will open
+ * until a proper delay after the last device was upgraded.
+ */
+ int64_t fw_updated;
+
+ /** The currently configured samplerate of the device. */
+ uint64_t cur_samplerate;
+
+ /** Maximum number of samples to capture, if nonzero. */
+ uint64_t limit_samples;
+
+ /** The currently configured input voltage of the device. */
+ enum voltage_range cur_voltage_range;
+
+ /** The input voltage selected by the user. */
+ enum voltage_range selected_voltage_range;
+
+ /** Channels to use. */
+ uint16_t cur_channels;
+
+ /* EEPROM data from address 8. */
+ uint8_t eeprom_data[8];
+
+ int64_t sent_samples;
+ int submitted_transfers;
+ int empty_transfer_count;
+ int num_channels;
+ int cur_channel;
+ uint16_t channel_masks[16];
+ uint16_t channel_data[16];
+ uint8_t *convbuffer;
+ size_t convbuffer_size;
+ struct soft_trigger_logic *stl;
+ gboolean trigger_fired;
+
+ void *cb_data;
+ unsigned int num_transfers;
+ struct libusb_transfer **transfers;
+ struct sr_context *ctx;
+};
+
+SR_PRIV int logic16_setup_acquisition(const struct sr_dev_inst *sdi,
+ uint64_t samplerate, uint16_t channels);
+SR_PRIV int logic16_start_acquisition(const struct sr_dev_inst *sdi);
+SR_PRIV int logic16_abort_acquisition(const struct sr_dev_inst *sdi);
+SR_PRIV int logic16_init_device(const struct sr_dev_inst *sdi);
+SR_PRIV void logic16_receive_transfer(struct libusb_transfer *transfer);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+SR_PRIV struct sr_dev_driver bbcgm_m2110_driver_info;
+SR_PRIV struct sr_dev_driver digitek_dt4000zc_driver_info;
+SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info;
+SR_PRIV struct sr_dev_driver metex_me31_driver_info;
+SR_PRIV struct sr_dev_driver peaktech_3410_driver_info;
+SR_PRIV struct sr_dev_driver mastech_mas345_driver_info;
+SR_PRIV struct sr_dev_driver va_va18b_driver_info;
+SR_PRIV struct sr_dev_driver va_va40b_driver_info;
+SR_PRIV struct sr_dev_driver metex_m3640d_driver_info;
+SR_PRIV struct sr_dev_driver metex_m4650cr_driver_info;
+SR_PRIV struct sr_dev_driver peaktech_4370_driver_info;
+SR_PRIV struct sr_dev_driver pce_pce_dm32_driver_info;
+SR_PRIV struct sr_dev_driver radioshack_22_168_driver_info;
+SR_PRIV struct sr_dev_driver radioshack_22_805_driver_info;
+SR_PRIV struct sr_dev_driver radioshack_22_812_driver_info;
+SR_PRIV struct sr_dev_driver tecpel_dmm_8061_ser_driver_info;
+SR_PRIV struct sr_dev_driver voltcraft_m3650cr_driver_info;
+SR_PRIV struct sr_dev_driver voltcraft_m3650d_driver_info;
+SR_PRIV struct sr_dev_driver voltcraft_m4650cr_driver_info;
+SR_PRIV struct sr_dev_driver voltcraft_me42_driver_info;
+SR_PRIV struct sr_dev_driver voltcraft_vc820_ser_driver_info;
+SR_PRIV struct sr_dev_driver voltcraft_vc830_ser_driver_info;
+SR_PRIV struct sr_dev_driver voltcraft_vc840_ser_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut60a_ser_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut60e_ser_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut60g_ser_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut61b_ser_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut61c_ser_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut61d_ser_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut61e_ser_driver_info;
+SR_PRIV struct sr_dev_driver iso_tech_idm103n_driver_info;
+SR_PRIV struct sr_dev_driver tenma_72_7745_ser_driver_info;
+SR_PRIV struct sr_dev_driver tenma_72_7750_ser_driver_info;
+
+SR_PRIV struct dmm_info dmms[] = {
+ {
+ "BBC Goertz Metrawatt", "M2110", "1200/7n2", 1200,
+ BBCGM_M2110_PACKET_SIZE, 0, 0, NULL,
+ sr_m2110_packet_valid, sr_m2110_parse,
+ NULL,
+ &bbcgm_m2110_driver_info, receive_data_BBCGM_M2110,
+ },
+ {
+ "Digitek", "DT4000ZC", "2400/8n1/dtr=1", 2400,
+ FS9721_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_10_temp_c,
+ &digitek_dt4000zc_driver_info, receive_data_DIGITEK_DT4000ZC,
+ },
+ {
+ "TekPower", "TP4000ZC", "2400/8n1/dtr=1", 2400,
+ FS9721_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_10_temp_c,
+ &tekpower_tp4000zc_driver_info, receive_data_TEKPOWER_TP4000ZC,
+ },
+ {
+ "Metex", "ME-31", "600/7n2/rts=0/dtr=1", 600,
+ METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &metex_me31_driver_info, receive_data_METEX_ME31,
+ },
+ {
+ "Peaktech", "3410", "600/7n2/rts=0/dtr=1", 600,
+ METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &peaktech_3410_driver_info, receive_data_PEAKTECH_3410,
+ },
+ {
+ "MASTECH", "MAS345", "600/7n2/rts=0/dtr=1", 600,
+ METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &mastech_mas345_driver_info, receive_data_MASTECH_MAS345,
+ },
+ {
+ "V&A", "VA18B", "2400/8n1", 2400,
+ FS9721_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_01_temp_c,
+ &va_va18b_driver_info, receive_data_VA_VA18B,
+ },
+ {
+ "V&A", "VA40B", "2400/8n1", 2400,
+ FS9721_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_max_c_min,
+ &va_va40b_driver_info, receive_data_VA_VA40B,
+ },
+ {
+ "Metex", "M-3640D", "1200/7n2/rts=0/dtr=1", 1200,
+ METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &metex_m3640d_driver_info, receive_data_METEX_M3640D,
+ },
+ {
+ "Metex", "M-4650CR", "1200/7n2/rts=0/dtr=1", 1200,
+ METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &metex_m4650cr_driver_info, receive_data_METEX_M4650CR,
+ },
+ {
+ "PeakTech", "4370", "1200/7n2/rts=0/dtr=1", 1200,
+ METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &peaktech_4370_driver_info, receive_data_PEAKTECH_4370,
+ },
+ {
+ "PCE", "PCE-DM32", "2400/8n1", 2400,
+ FS9721_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_01_10_temp_f_c,
+ &pce_pce_dm32_driver_info, receive_data_PCE_PCE_DM32,
+ },
+ {
+ "RadioShack", "22-168", "1200/7n2/rts=0/dtr=1", 1200,
+ METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &radioshack_22_168_driver_info, receive_data_RADIOSHACK_22_168,
+ },
+ {
+ "RadioShack", "22-805", "600/7n2/rts=0/dtr=1", 600,
+ METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &radioshack_22_805_driver_info, receive_data_RADIOSHACK_22_805,
+ },
+ {
+ "RadioShack", "22-812", "4800/8n1/rts=0/dtr=1", 4800,
+ RS9LCD_PACKET_SIZE, 0, 0, NULL,
+ sr_rs9lcd_packet_valid, sr_rs9lcd_parse,
+ NULL,
+ &radioshack_22_812_driver_info, receive_data_RADIOSHACK_22_812,
+ },
+ {
+ "Tecpel", "DMM-8061 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+ 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_00_temp_c,
+ &tecpel_dmm_8061_ser_driver_info,
+ receive_data_TECPEL_DMM_8061_SER,
+ },
+ {
+ "Voltcraft", "M-3650CR", "1200/7n2/rts=0/dtr=1", 1200,
+ METEX14_PACKET_SIZE, 150, 20, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &voltcraft_m3650cr_driver_info, receive_data_VOLTCRAFT_M3650CR,
+ },
+ {
+ "Voltcraft", "M-3650D", "1200/7n2/rts=0/dtr=1", 1200,
+ METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &voltcraft_m3650d_driver_info, receive_data_VOLTCRAFT_M3650D,
+ },
+ {
+ "Voltcraft", "M-4650CR", "1200/7n2/rts=0/dtr=1", 1200,
+ METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &voltcraft_m4650cr_driver_info, receive_data_VOLTCRAFT_M4650CR,
+ },
+ {
+ "Voltcraft", "ME-42", "600/7n2/rts=0/dtr=1", 600,
+ METEX14_PACKET_SIZE, 250, 60, sr_metex14_packet_request,
+ sr_metex14_packet_valid, sr_metex14_parse,
+ NULL,
+ &voltcraft_me42_driver_info, receive_data_VOLTCRAFT_ME42,
+ },
+ {
+ "Voltcraft", "VC-820 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+ 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ NULL,
+ &voltcraft_vc820_ser_driver_info,
+ receive_data_VOLTCRAFT_VC820_SER,
+ },
+ {
+ /*
+ * Note: The VC830 doesn't set the 'volt' and 'diode' bits of
+ * the FS9922 protocol. Instead, it only sets the user-defined
+ * bit "z1" to indicate "diode mode" and "voltage".
+ */
+ "Voltcraft", "VC-830 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+ 2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9922_packet_valid, sr_fs9922_parse,
+ &sr_fs9922_z1_diode,
+ &voltcraft_vc830_ser_driver_info,
+ receive_data_VOLTCRAFT_VC830_SER,
+ },
+ {
+ "Voltcraft", "VC-840 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+ 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_00_temp_c,
+ &voltcraft_vc840_ser_driver_info,
+ receive_data_VOLTCRAFT_VC840_SER,
+ },
+ {
+ "UNI-T", "UT60A (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+ 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ NULL,
+ &uni_t_ut60a_ser_driver_info,
+ receive_data_UNI_T_UT60A_SER,
+ },
+ {
+ "UNI-T", "UT60E (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+ 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_00_temp_c,
+ &uni_t_ut60e_ser_driver_info,
+ receive_data_UNI_T_UT60E_SER,
+ },
+ {
+ /* Note: ES51986 baudrate is actually 19230! */
+ "UNI-T", "UT60G (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
+ 19200, ES519XX_11B_PACKET_SIZE, 0, 0, NULL,
+ sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
+ NULL,
+ &uni_t_ut60g_ser_driver_info, receive_data_UNI_T_UT60G_SER,
+ },
+ {
+ "UNI-T", "UT61B (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+ 2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9922_packet_valid, sr_fs9922_parse, NULL,
+ &uni_t_ut61b_ser_driver_info, receive_data_UNI_T_UT61B_SER,
+ },
+ {
+ "UNI-T", "UT61C (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+ 2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9922_packet_valid, sr_fs9922_parse, NULL,
+ &uni_t_ut61c_ser_driver_info, receive_data_UNI_T_UT61C_SER,
+ },
+ {
+ "UNI-T", "UT61D (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+ 2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9922_packet_valid, sr_fs9922_parse, NULL,
+ &uni_t_ut61d_ser_driver_info, receive_data_UNI_T_UT61D_SER,
+ },
+ {
+ /* Note: ES51922 baudrate is actually 19230! */
+ "UNI-T", "UT61E (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
+ 19200, ES519XX_14B_PACKET_SIZE, 0, 0, NULL,
+ sr_es519xx_19200_14b_packet_valid, sr_es519xx_19200_14b_parse,
+ NULL,
+ &uni_t_ut61e_ser_driver_info, receive_data_UNI_T_UT61E_SER,
+ },
+ {
+ "ISO-TECH", "IDM103N", "2400/7o1/rts=0/dtr=1",
+ 2400, ES519XX_11B_PACKET_SIZE, 0, 0, NULL,
+ sr_es519xx_2400_11b_packet_valid, sr_es519xx_2400_11b_parse,
+ NULL,
+ &iso_tech_idm103n_driver_info, receive_data_ISO_TECH_IDM103N,
+ },
+ {
+ "Tenma", "72-7745 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+ 2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_00_temp_c,
+ &tenma_72_7745_ser_driver_info, receive_data_TENMA_72_7745_SER,
+ },
+ {
+ /* Note: ES51986 baudrate is actually 19230! */
+ "Tenma", "72-7750 (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
+ 19200, ES519XX_11B_PACKET_SIZE, 0, 0, NULL,
+ sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
+ NULL,
+ &tenma_72_7750_ser_driver_info, receive_data_TENMA_72_7750_SER,
+ },
+};
+
+static int dev_clear(int dmm)
+{
+ return std_dev_clear(dmms[dmm].di, NULL);
+}
+
+static int init(struct sr_context *sr_ctx, int dmm)
+{
+ sr_dbg("Selected '%s' subdriver.", dmms[dmm].di->name);
+
+ return std_init(sr_ctx, dmms[dmm].di, LOG_PREFIX);
+}
+
+static GSList *sdmm_scan(const char *conn, const char *serialcomm, int dmm)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ struct sr_serial_dev_inst *serial;
+ GSList *devices;
+ int dropped, ret;
+ size_t len;
+ uint8_t buf[128];
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ sr_info("Probing serial port %s.", conn);
+
+ drvc = dmms[dmm].di->priv;
+ devices = NULL;
+ serial_flush(serial);
+
+ /* Request a packet if the DMM requires this. */
+ if (dmms[dmm].packet_request) {
+ if ((ret = dmms[dmm].packet_request(serial)) < 0) {
+ sr_err("Failed to request packet: %d.", ret);
+ return FALSE;
+ }
+ }
+
+ /*
+ * There's no way to get an ID from the multimeter. It just sends data
+ * periodically (or upon request), so the best we can do is check if
+ * the packets match the expected format.
+ */
+
+ /* Let's get a bit of data and see if we can find a packet. */
+ len = sizeof(buf);
+ ret = serial_stream_detect(serial, buf, &len, dmms[dmm].packet_size,
+ dmms[dmm].packet_valid, 3000,
+ dmms[dmm].baudrate);
+ if (ret != SR_OK)
+ goto scan_cleanup;
+
+ /*
+ * 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 - dmms[dmm].packet_size;
+ if (dropped > 2 * dmms[dmm].packet_size)
+ sr_warn("Had to drop too much data.");
+
+ sr_info("Found device on port %s.", conn);
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, dmms[dmm].vendor,
+ dmms[dmm].device, NULL)))
+ goto scan_cleanup;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ goto scan_cleanup;
+ }
+
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+
+ sdi->priv = devc;
+ sdi->driver = dmms[dmm].di;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+scan_cleanup:
+ serial_close(serial);
+
+ return devices;
+}
+
+static GSList *scan(GSList *options, int dmm)
+{
+ struct sr_config *src;
+ GSList *l, *devices;
+ const char *conn, *serialcomm;
+
+ 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 (!conn)
+ return NULL;
+
+ if (serialcomm) {
+ /* Use the provided comm specs. */
+ devices = sdmm_scan(conn, serialcomm, dmm);
+ } else {
+ /* Try the default. */
+ devices = sdmm_scan(conn, dmms[dmm].conn, dmm);
+ }
+
+ return devices;
+}
+
+static GSList *dev_list(int dmm)
+{
+ return ((struct drv_context *)(dmms[dmm].di->priv))->instances;
+}
+
+static int cleanup(int dmm)
+{
+ return dev_clear(dmm);
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ switch (id) {
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data, int dmm)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ devc->cb_data = cb_data;
+
+ /*
+ * Reset the number of samples to take. If we've already collected our
+ * quota, but we start a new session, and don't reset this, we'll just
+ * quit without acquiring any new samples.
+ */
+ devc->num_samples = 0;
+ devc->starttime = g_get_monotonic_time();
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Poll every 50ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 50,
+ dmms[dmm].receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+/* Driver-specific API function wrappers */
+#define HW_INIT(X) \
+static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
+#define HW_CLEANUP(X) \
+static int cleanup_##X(void) { return cleanup(X); }
+#define HW_SCAN(X) \
+static GSList *scan_##X(GSList *options) { return scan(options, X); }
+#define HW_DEV_LIST(X) \
+static GSList *dev_list_##X(void) { return dev_list(X); }
+#define HW_DEV_CLEAR(X) \
+static int dev_clear_##X(void) { return dev_clear(X); }
+#define HW_DEV_ACQUISITION_START(X) \
+static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
+void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
+
+/* Driver structs and API function wrappers */
+#define DRV(ID, ID_UPPER, NAME, LONGNAME) \
+HW_INIT(ID_UPPER) \
+HW_CLEANUP(ID_UPPER) \
+HW_SCAN(ID_UPPER) \
+HW_DEV_LIST(ID_UPPER) \
+HW_DEV_CLEAR(ID_UPPER) \
+HW_DEV_ACQUISITION_START(ID_UPPER) \
+SR_PRIV struct sr_dev_driver ID##_driver_info = { \
+ .name = NAME, \
+ .longname = LONGNAME, \
+ .api_version = 1, \
+ .init = init_##ID_UPPER, \
+ .cleanup = cleanup_##ID_UPPER, \
+ .scan = scan_##ID_UPPER, \
+ .dev_list = dev_list_##ID_UPPER, \
+ .dev_clear = dev_clear_##ID_UPPER, \
+ .config_get = NULL, \
+ .config_set = config_set, \
+ .config_list = config_list, \
+ .dev_open = std_serial_dev_open, \
+ .dev_close = std_serial_dev_close, \
+ .dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
+ .dev_acquisition_stop = dev_acquisition_stop, \
+ .priv = NULL, \
+};
+
+DRV(bbcgm_m2110, BBCGM_M2110, "bbcgm-m2110", "BBC Goertz Metrawatt M2110")
+DRV(digitek_dt4000zc, DIGITEK_DT4000ZC, "digitek-dt4000zc", "Digitek DT4000ZC")
+DRV(tekpower_tp4000zc, TEKPOWER_TP4000ZC, "tekpower-tp4000zc", "TekPower TP4000ZC")
+DRV(metex_me31, METEX_ME31, "metex-me31", "Metex ME-31")
+DRV(peaktech_3410, PEAKTECH_3410, "peaktech-3410", "PeakTech 3410")
+DRV(mastech_mas345, MASTECH_MAS345, "mastech-mas345", "MASTECH MAS345")
+DRV(va_va18b, VA_VA18B, "va-va18b", "V&A VA18B")
+DRV(va_va40b, VA_VA40B, "va-va40b", "V&A VA40B")
+DRV(metex_m3640d, METEX_M3640D, "metex-m3640d", "Metex M-3640D")
+DRV(metex_m4650cr, METEX_M4650CR, "metex-m4650cr", "Metex M-4650CR")
+DRV(peaktech_4370, PEAKTECH_4370, "peaktech-4370", "PeakTech 4370")
+DRV(pce_pce_dm32, PCE_PCE_DM32, "pce-pce-dm32", "PCE PCE-DM32")
+DRV(radioshack_22_168, RADIOSHACK_22_168, "radioshack-22-168", "RadioShack 22-168")
+DRV(radioshack_22_805, RADIOSHACK_22_805, "radioshack-22-805", "RadioShack 22-805")
+DRV(radioshack_22_812, RADIOSHACK_22_812, "radioshack-22-812", "RadioShack 22-812")
+DRV(tecpel_dmm_8061_ser, TECPEL_DMM_8061_SER, "tecpel-dmm-8061-ser", "Tecpel DMM-8061 (UT-D02 cable)")
+DRV(voltcraft_m3650cr, VOLTCRAFT_M3650CR, "voltcraft-m3650cr", "Voltcraft M-3650CR")
+DRV(voltcraft_m3650d, VOLTCRAFT_M3650D, "voltcraft-m3650d", "Voltcraft M-3650D")
+DRV(voltcraft_m4650cr, VOLTCRAFT_M4650CR, "voltcraft-m4650cr", "Voltcraft M-4650CR")
+DRV(voltcraft_me42, VOLTCRAFT_ME42, "voltcraft-me42", "Voltcraft ME-42")
+DRV(voltcraft_vc820_ser, VOLTCRAFT_VC820_SER, "voltcraft-vc820-ser", "Voltcraft VC-820 (UT-D02 cable)")
+DRV(voltcraft_vc830_ser, VOLTCRAFT_VC830_SER, "voltcraft-vc830-ser", "Voltcraft VC-830 (UT-D02 cable)")
+DRV(voltcraft_vc840_ser, VOLTCRAFT_VC840_SER, "voltcraft-vc840-ser", "Voltcraft VC-840 (UT-D02 cable)")
+DRV(uni_t_ut60a_ser, UNI_T_UT60A_SER, "uni-t-ut60a-ser", "UNI-T UT60A (UT-D02 cable)")
+DRV(uni_t_ut60e_ser, UNI_T_UT60E_SER, "uni-t-ut60e-ser", "UNI-T UT60E (UT-D02 cable)")
+DRV(uni_t_ut60g_ser, UNI_T_UT60G_SER, "uni-t-ut60g-ser", "UNI-T UT60G (UT-D02 cable)")
+DRV(uni_t_ut61b_ser, UNI_T_UT61B_SER, "uni-t-ut61b-ser", "UNI-T UT61B (UT-D02 cable)")
+DRV(uni_t_ut61c_ser, UNI_T_UT61C_SER, "uni-t-ut61c-ser", "UNI-T UT61C (UT-D02 cable)")
+DRV(uni_t_ut61d_ser, UNI_T_UT61D_SER, "uni-t-ut61d-ser", "UNI-T UT61D (UT-D02 cable)")
+DRV(uni_t_ut61e_ser, UNI_T_UT61E_SER, "uni-t-ut61e-ser", "UNI-T UT61E (UT-D02 cable)")
+DRV(iso_tech_idm103n, ISO_TECH_IDM103N, "iso-tech-idm103n", "ISO-TECH IDM103N")
+DRV(tenma_72_7745_ser, TENMA_72_7745_SER, "tenma-72-7745-ser", "Tenma 72-7745 (UT-D02 cable)")
+DRV(tenma_72_7750_ser, TENMA_72_7750_SER, "tenma-72-7750-ser", "Tenma 72-7750 (UT-D02 cable)")
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+static void log_dmm_packet(const uint8_t *buf)
+{
+ sr_dbg("DMM packet: %02x %02x %02x %02x %02x %02x %02x"
+ " %02x %02x %02x %02x %02x %02x %02x",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
+ buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]);
+}
+
+static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi,
+ int dmm, void *info)
+{
+ float floatval;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct dev_context *devc;
+
+ log_dmm_packet(buf);
+ devc = sdi->priv;
+
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+ analog.mq = -1;
+
+ dmms[dmm].packet_parse(buf, &floatval, &analog, info);
+ analog.data = &floatval;
+
+ /* If this DMM needs additional handling, call the resp. function. */
+ if (dmms[dmm].dmm_details)
+ dmms[dmm].dmm_details(&analog, info);
+
+ if (analog.mq != -1) {
+ /* Got a measurement. */
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+ devc->num_samples++;
+ }
+}
+
+/** Request packet, if required. */
+SR_PRIV int req_packet(struct sr_dev_inst *sdi, int dmm)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int ret;
+
+ if (!dmms[dmm].packet_request)
+ return SR_OK;
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+
+ if (devc->req_next_at && (devc->req_next_at > g_get_monotonic_time())) {
+ sr_spew("Not requesting new packet yet, %" PRIi64 " ms left.",
+ ((devc->req_next_at - g_get_monotonic_time()) / 1000));
+ return SR_OK;
+ }
+
+ ret = dmms[dmm].packet_request(serial);
+ if (ret < 0) {
+ sr_err("Failed to request packet: %d.", ret);
+ return ret;
+ }
+
+ if (dmms[dmm].req_timeout_ms)
+ devc->req_next_at = g_get_monotonic_time() + (dmms[dmm].req_timeout_ms * 1000);
+
+ return SR_OK;
+}
+
+static void handle_new_data(struct sr_dev_inst *sdi, int dmm, void *info)
+{
+ struct dev_context *devc;
+ int len, i, offset = 0;
+ struct sr_serial_dev_inst *serial;
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+
+ /* Try to get as much data as the buffer can hold. */
+ len = DMM_BUFSIZE - devc->buflen;
+ len = serial_read(serial, devc->buf + devc->buflen, len);
+ if (len == 0)
+ return; /* No new bytes, nothing to do. */
+ if (len < 0) {
+ sr_err("Serial port read error: %d.", len);
+ return;
+ }
+ devc->buflen += len;
+
+ /* Now look for packets in that data. */
+ while ((devc->buflen - offset) >= dmms[dmm].packet_size) {
+ if (dmms[dmm].packet_valid(devc->buf + offset)) {
+ handle_packet(devc->buf + offset, sdi, dmm, info);
+ offset += dmms[dmm].packet_size;
+
+ /* Request next packet, if required. */
+ if (!dmms[dmm].packet_request)
+ break;
+ if (dmms[dmm].req_timeout_ms || dmms[dmm].req_delay_ms)
+ devc->req_next_at = g_get_monotonic_time() +
+ dmms[dmm].req_delay_ms * 1000;
+ req_packet(sdi, dmm);
+ } else {
+ offset++;
+ }
+ }
+
+ /* If we have any data left, move it to the beginning of our buffer. */
+ for (i = 0; i < devc->buflen - offset; i++)
+ devc->buf[i] = devc->buf[offset + i];
+ devc->buflen -= offset;
+}
+
+static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ int64_t time;
+
+ (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, dmm, info);
+ } else {
+ /* Timeout; send another packet request if DMM needs it. */
+ if (dmms[dmm].packet_request && (req_packet(sdi, dmm) < 0))
+ return FALSE;
+ }
+
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sr_info("Requested number of samples reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+
+ if (devc->limit_msec) {
+ time = (g_get_monotonic_time() - devc->starttime) / 1000;
+ if (time > (int64_t)devc->limit_msec) {
+ sr_info("Requested time limit reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#define RECEIVE_DATA(ID_UPPER, DMM_DRIVER) \
+SR_PRIV int receive_data_##ID_UPPER(int fd, int revents, void *cb_data) { \
+ struct DMM_DRIVER##_info info; \
+ return receive_data(fd, revents, ID_UPPER, &info, cb_data); }
+
+/* Driver-specific receive_data() wrappers */
+RECEIVE_DATA(BBCGM_M2110, metex14) /* metex14_info used as a dummy. */
+RECEIVE_DATA(DIGITEK_DT4000ZC, fs9721)
+RECEIVE_DATA(TEKPOWER_TP4000ZC, fs9721)
+RECEIVE_DATA(METEX_ME31, metex14)
+RECEIVE_DATA(PEAKTECH_3410, metex14)
+RECEIVE_DATA(MASTECH_MAS345, metex14)
+RECEIVE_DATA(VA_VA18B, fs9721)
+RECEIVE_DATA(VA_VA40B, fs9721)
+RECEIVE_DATA(METEX_M3640D, metex14)
+RECEIVE_DATA(METEX_M4650CR, metex14)
+RECEIVE_DATA(PEAKTECH_4370, metex14)
+RECEIVE_DATA(PCE_PCE_DM32, fs9721)
+RECEIVE_DATA(RADIOSHACK_22_168, metex14)
+RECEIVE_DATA(RADIOSHACK_22_805, metex14)
+RECEIVE_DATA(RADIOSHACK_22_812, rs9lcd)
+RECEIVE_DATA(TECPEL_DMM_8061_SER, fs9721)
+RECEIVE_DATA(VOLTCRAFT_M3650CR, metex14)
+RECEIVE_DATA(VOLTCRAFT_M3650D, metex14)
+RECEIVE_DATA(VOLTCRAFT_M4650CR, metex14)
+RECEIVE_DATA(VOLTCRAFT_ME42, metex14)
+RECEIVE_DATA(VOLTCRAFT_VC820_SER, fs9721)
+RECEIVE_DATA(VOLTCRAFT_VC830_SER, fs9922)
+RECEIVE_DATA(VOLTCRAFT_VC840_SER, fs9721)
+RECEIVE_DATA(UNI_T_UT60A_SER, fs9721)
+RECEIVE_DATA(UNI_T_UT60E_SER, fs9721)
+RECEIVE_DATA(UNI_T_UT60G_SER, es519xx)
+RECEIVE_DATA(UNI_T_UT61B_SER, fs9922)
+RECEIVE_DATA(UNI_T_UT61C_SER, fs9922)
+RECEIVE_DATA(UNI_T_UT61D_SER, fs9922)
+RECEIVE_DATA(UNI_T_UT61E_SER, es519xx)
+RECEIVE_DATA(ISO_TECH_IDM103N, es519xx)
+RECEIVE_DATA(TENMA_72_7745_SER, fs9721)
+RECEIVE_DATA(TENMA_72_7750_SER, es519xx)
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * 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_DMM_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SERIAL_DMM_PROTOCOL_H
+
+#define LOG_PREFIX "serial-dmm"
+
+enum {
+ BBCGM_M2110,
+ DIGITEK_DT4000ZC,
+ TEKPOWER_TP4000ZC,
+ METEX_ME31,
+ PEAKTECH_3410,
+ MASTECH_MAS345,
+ VA_VA18B,
+ VA_VA40B,
+ METEX_M3640D,
+ METEX_M4650CR,
+ PEAKTECH_4370,
+ PCE_PCE_DM32,
+ RADIOSHACK_22_168,
+ RADIOSHACK_22_805,
+ RADIOSHACK_22_812,
+ TECPEL_DMM_8061_SER,
+ VOLTCRAFT_M3650CR,
+ VOLTCRAFT_M3650D,
+ VOLTCRAFT_M4650CR,
+ VOLTCRAFT_ME42,
+ VOLTCRAFT_VC820_SER,
+ VOLTCRAFT_VC830_SER,
+ VOLTCRAFT_VC840_SER,
+ UNI_T_UT60A_SER,
+ UNI_T_UT60E_SER,
+ UNI_T_UT60G_SER,
+ UNI_T_UT61B_SER,
+ UNI_T_UT61C_SER,
+ UNI_T_UT61D_SER,
+ UNI_T_UT61E_SER,
+ ISO_TECH_IDM103N,
+ TENMA_72_7745_SER,
+ TENMA_72_7750_SER,
+};
+
+struct dmm_info {
+ /** Manufacturer/brand. */
+ char *vendor;
+ /** Model. */
+ char *device;
+ /** serialconn string. */
+ char *conn;
+ /** Baud rate. */
+ uint32_t baudrate;
+ /** Packet size in bytes. */
+ int packet_size;
+ /** Request timeout [ms] before request is considered lost and a new
+ * one is sent. Used only if device needs polling. */
+ int64_t req_timeout_ms;
+ /** Delay between reception of packet and next request. Some DMMs
+ * need this. Used only if device needs polling. */
+ int64_t req_delay_ms;
+ /** Packet request function. */
+ int (*packet_request)(struct sr_serial_dev_inst *);
+ /** Packet validation function. */
+ gboolean (*packet_valid)(const uint8_t *);
+ /** Packet parsing function. */
+ int (*packet_parse)(const uint8_t *, float *,
+ struct sr_datafeed_analog *, void *);
+ /** */
+ void (*dmm_details)(struct sr_datafeed_analog *, void *);
+ /** libsigrok driver info struct. */
+ struct sr_dev_driver *di;
+ /** Data reception function. */
+ int (*receive_data)(int, int, void *);
+};
+
+extern SR_PRIV struct dmm_info dmms[];
+
+#define DMM_BUFSIZE 256
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /** The current sampling limit (in number of samples). */
+ uint64_t limit_samples;
+
+ /** The time limit (in milliseconds). */
+ uint64_t limit_msec;
+
+ /** Opaque pointer passed in by the frontend. */
+ void *cb_data;
+
+ /** The current number of already received samples. */
+ uint64_t num_samples;
+
+ /** The starting time of current sampling run. */
+ int64_t starttime;
+
+ uint8_t buf[DMM_BUFSIZE];
+ int bufoffset;
+ int buflen;
+
+ /** The timestamp [µs] to send the next request.
+ * Used only if device needs polling. */
+ int64_t req_next_at;
+};
+
+SR_PRIV int req_packet(struct sr_dev_inst *sdi, int dmm);
+
+SR_PRIV int receive_data_BBCGM_M2110(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_DIGITEK_DT4000ZC(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_TEKPOWER_TP4000ZC(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_METEX_ME31(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_PEAKTECH_3410(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_MASTECH_MAS345(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VA_VA18B(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VA_VA40B(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_METEX_M3640D(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_METEX_M4650CR(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_PEAKTECH_4370(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_PCE_PCE_DM32(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_RADIOSHACK_22_168(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_RADIOSHACK_22_805(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_RADIOSHACK_22_812(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_TECPEL_DMM_8061_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VOLTCRAFT_M3650CR(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VOLTCRAFT_M3650D(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VOLTCRAFT_M4650CR(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VOLTCRAFT_ME42(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VOLTCRAFT_VC820_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VOLTCRAFT_VC830_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VOLTCRAFT_VC840_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT60A_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT60E_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT60G_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT61B_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT61C_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT61D_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT61E_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_ISO_TECH_IDM103N(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_TENMA_72_7745_SER(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_TENMA_72_7750_SER(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
+ *
+ * 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 "protocol.h"
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include <glib.h>
+#include <libusb.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_EXTERNAL_CLOCK,
+ SR_CONF_CLOCK_EDGE,
+ SR_CONF_TRIGGER_MATCH,
+ SR_CONF_TRIGGER_SOURCE,
+ SR_CONF_TRIGGER_SLOPE,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_LIMIT_SAMPLES,
+};
+
+static const int32_t trigger_matches[] = {
+ SR_TRIGGER_ZERO,
+ SR_TRIGGER_ONE,
+ SR_TRIGGER_RISING,
+ SR_TRIGGER_FALLING,
+};
+
+/* The hardware supports more samplerates than these, but these are the
+ * options hardcoded into the vendor's Windows GUI.
+ */
+static const uint64_t samplerates[] = {
+ SR_MHZ(125), SR_MHZ(100),
+ SR_MHZ(50), SR_MHZ(20), SR_MHZ(10),
+ SR_MHZ(5), SR_MHZ(2), SR_MHZ(1),
+ SR_KHZ(500), SR_KHZ(200), SR_KHZ(100),
+ SR_KHZ(50), SR_KHZ(20), SR_KHZ(10),
+ SR_KHZ(5), SR_KHZ(2), SR_KHZ(1),
+ SR_HZ(500), SR_HZ(200), SR_HZ(100),
+};
+
+/* Names assigned to available trigger sources. Indices must match
+ * trigger_source enum values.
+ */
+static const char *const trigger_source_names[] = { "CH", "TRG" };
+
+/* Names assigned to available trigger slope choices. Indices must
+ * match the signal_edge enum values.
+ */
+static const char *const signal_edge_names[] = { "r", "f" };
+
+SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info;
+static struct sr_dev_driver *const di = &sysclk_lwla_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *gen_channel_list(int num_channels)
+{
+ GSList *list;
+ struct sr_channel *ch;
+ int i;
+ char name[8];
+
+ list = NULL;
+
+ for (i = num_channels; i > 0; --i) {
+ /* The LWLA series simply number channels from CH1 to CHxx. */
+ g_snprintf(name, sizeof(name), "CH%d", i);
+
+ ch = sr_channel_new(i - 1, SR_CHANNEL_LOGIC, TRUE, name);
+ list = g_slist_prepend(list, ch);
+ }
+
+ return list;
+}
+
+static struct sr_dev_inst *dev_inst_new(int device_index)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+
+ /* Allocate memory for our private driver context. */
+ devc = g_try_new0(struct dev_context, 1);
+ if (!devc) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+
+ /* Register the device with libsigrok. */
+ sdi = sr_dev_inst_new(device_index, SR_ST_INACTIVE,
+ VENDOR_NAME, MODEL_NAME, NULL);
+ if (!sdi) {
+ sr_err("Failed to instantiate device.");
+ g_free(devc);
+ return NULL;
+ }
+
+ /* Enable all channels to match the default channel configuration. */
+ devc->channel_mask = ALL_CHANNELS_MASK;
+ devc->samplerate = DEFAULT_SAMPLERATE;
+
+ sdi->priv = devc;
+ sdi->channels = gen_channel_list(NUM_CHANNELS);
+
+ return sdi;
+}
+
+static GSList *scan(GSList *options)
+{
+ GSList *usb_devices, *devices, *node;
+ struct drv_context *drvc;
+ struct sr_dev_inst *sdi;
+ struct sr_usb_dev_inst *usb;
+ struct sr_config *src;
+ const char *conn;
+ int device_index;
+
+ drvc = di->priv;
+ conn = USB_VID_PID;
+
+ for (node = options; node != NULL; node = node->next) {
+ src = node->data;
+ if (src->key == SR_CONF_CONN) {
+ conn = g_variant_get_string(src->data, NULL);
+ break;
+ }
+ }
+ usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
+ devices = NULL;
+ device_index = g_slist_length(drvc->instances);
+
+ for (node = usb_devices; node != NULL; node = node->next) {
+ usb = node->data;
+
+ /* Create sigrok device instance. */
+ sdi = dev_inst_new(device_index);
+ if (!sdi) {
+ sr_usb_dev_inst_free(usb);
+ continue;
+ }
+ sdi->driver = di;
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = usb;
+
+ /* Register device instance with driver. */
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ }
+
+ g_slist_free(usb_devices);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ struct drv_context *drvc;
+
+ drvc = di->priv;
+
+ return drvc->instances;
+}
+
+static void clear_dev_context(void *priv)
+{
+ struct dev_context *devc;
+
+ devc = priv;
+
+ sr_dbg("Device context cleared.");
+
+ lwla_free_acquisition_state(devc->acquisition);
+ g_free(devc);
+}
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, &clear_dev_context);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ int ret;
+
+ drvc = di->priv;
+
+ if (!drvc) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+
+ ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
+ if (ret < 0) {
+ sr_err("Failed to claim interface: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ sdi->status = SR_ST_INITIALIZING;
+
+ ret = lwla_init_device(sdi);
+
+ if (ret == SR_OK)
+ sdi->status = SR_ST_ACTIVE;
+
+ return ret;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+ if (!usb->devhdl)
+ return SR_OK;
+
+ sdi->status = SR_ST_INACTIVE;
+
+ /* Trigger download of the shutdown bitstream. */
+ if (lwla_set_clock_config(sdi) != SR_OK)
+ sr_err("Unable to shut down device.");
+
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+
+ usb->devhdl = NULL;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return dev_clear();
+}
+
+static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ size_t idx;
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_SAMPLERATE:
+ *data = g_variant_new_uint64(devc->samplerate);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ *data = g_variant_new_uint64(devc->limit_msec);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_EXTERNAL_CLOCK:
+ *data = g_variant_new_boolean(devc->cfg_clock_source
+ == CLOCK_EXT_CLK);
+ break;
+ case SR_CONF_CLOCK_EDGE:
+ idx = devc->cfg_clock_edge;
+ if (idx >= G_N_ELEMENTS(signal_edge_names))
+ return SR_ERR_BUG;
+ *data = g_variant_new_string(signal_edge_names[idx]);
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ idx = devc->cfg_trigger_source;
+ if (idx >= G_N_ELEMENTS(trigger_source_names))
+ return SR_ERR_BUG;
+ *data = g_variant_new_string(trigger_source_names[idx]);
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ idx = devc->cfg_trigger_slope;
+ if (idx >= G_N_ELEMENTS(signal_edge_names))
+ return SR_ERR_BUG;
+ *data = g_variant_new_string(signal_edge_names[idx]);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+/* Helper for mapping a string-typed configuration value to an index
+ * within a table of possible values.
+ */
+static int lookup_index(GVariant *value, const char *const *table, int len)
+{
+ const char *entry;
+ int i;
+
+ entry = g_variant_get_string(value, NULL);
+ if (!entry)
+ return -1;
+
+ /* Linear search is fine for very small tables. */
+ for (i = 0; i < len; ++i) {
+ if (strcmp(entry, table[i]) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ uint64_t value;
+ struct dev_context *devc;
+ int idx;
+
+ (void)cg;
+
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_DEV_CLOSED;
+
+ switch (key) {
+ case SR_CONF_SAMPLERATE:
+ value = g_variant_get_uint64(data);
+ if (value < samplerates[G_N_ELEMENTS(samplerates) - 1]
+ || value > samplerates[0])
+ return SR_ERR_SAMPLERATE;
+ devc->samplerate = value;
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ value = g_variant_get_uint64(data);
+ if (value > MAX_LIMIT_MSEC)
+ return SR_ERR_ARG;
+ devc->limit_msec = value;
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ value = g_variant_get_uint64(data);
+ if (value > MAX_LIMIT_SAMPLES)
+ return SR_ERR_ARG;
+ devc->limit_samples = value;
+ break;
+ case SR_CONF_EXTERNAL_CLOCK:
+ devc->cfg_clock_source = (g_variant_get_boolean(data))
+ ? CLOCK_EXT_CLK : CLOCK_INTERNAL;
+ break;
+ case SR_CONF_CLOCK_EDGE:
+ idx = lookup_index(data, signal_edge_names,
+ G_N_ELEMENTS(signal_edge_names));
+ if (idx < 0)
+ return SR_ERR_ARG;
+ devc->cfg_clock_edge = idx;
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ idx = lookup_index(data, trigger_source_names,
+ G_N_ELEMENTS(trigger_source_names));
+ if (idx < 0)
+ return SR_ERR_ARG;
+ devc->cfg_trigger_source = idx;
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ idx = lookup_index(data, signal_edge_names,
+ G_N_ELEMENTS(signal_edge_names));
+ if (idx < 0)
+ return SR_ERR_ARG;
+ devc->cfg_trigger_slope = idx;
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_channel_set(const struct sr_dev_inst *sdi,
+ struct sr_channel *ch, unsigned int changes)
+{
+ uint64_t channel_bit;
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_DEV_CLOSED;
+
+ if (ch->index < 0 || ch->index >= NUM_CHANNELS) {
+ sr_err("Channel index %d out of range.", ch->index);
+ return SR_ERR_BUG;
+ }
+ channel_bit = (uint64_t)1 << ch->index;
+
+ if ((changes & SR_CHANNEL_SET_ENABLED) != 0) {
+ /* Enable or disable input channel for this channel. */
+ if (ch->enabled)
+ devc->channel_mask |= channel_bit;
+ else
+ devc->channel_mask &= ~channel_bit;
+ }
+
+ return SR_OK;
+}
+
+static int config_commit(const struct sr_dev_inst *sdi)
+{
+ if (sdi->status != SR_ST_ACTIVE) {
+ sr_err("Device not ready (status %d).", (int)sdi->status);
+ return SR_ERR;
+ }
+
+ return lwla_set_clock_config(sdi);
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ GVariant *gvar;
+ GVariantBuilder gvb;
+
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, G_N_ELEMENTS(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, G_N_ELEMENTS(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
+ samplerates, G_N_ELEMENTS(samplerates),
+ sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ trigger_matches, ARRAY_SIZE(trigger_matches),
+ sizeof(int32_t));
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ *data = g_variant_new_strv(trigger_source_names,
+ G_N_ELEMENTS(trigger_source_names));
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ case SR_CONF_CLOCK_EDGE:
+ *data = g_variant_new_strv(signal_edge_names,
+ G_N_ELEMENTS(signal_edge_names));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct acquisition_state *acq;
+ int ret;
+
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ drvc = di->priv;
+
+ if (devc->acquisition) {
+ sr_err("Acquisition still in progress?");
+ return SR_ERR;
+ }
+ acq = lwla_alloc_acquisition_state();
+ if (!acq)
+ return SR_ERR_MALLOC;
+
+ devc->stopping_in_progress = FALSE;
+ devc->transfer_error = FALSE;
+
+ sr_info("Starting acquisition.");
+
+ devc->acquisition = acq;
+ lwla_convert_trigger(sdi);
+ ret = lwla_setup_acquisition(sdi);
+ if (ret != SR_OK) {
+ sr_err("Failed to set up acquisition.");
+ devc->acquisition = NULL;
+ lwla_free_acquisition_state(acq);
+ return ret;
+ }
+
+ ret = lwla_start_acquisition(sdi);
+ if (ret != SR_OK) {
+ sr_err("Failed to start acquisition.");
+ devc->acquisition = NULL;
+ lwla_free_acquisition_state(acq);
+ return ret;
+ }
+ usb_source_add(sdi->session, drvc->sr_ctx, 100, &lwla_receive_data,
+ (struct sr_dev_inst *)sdi);
+
+ sr_info("Waiting for data.");
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(sdi, LOG_PREFIX);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ sr_dbg("Stopping acquisition.");
+
+ sdi->status = SR_ST_STOPPING;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info = {
+ .name = "sysclk-lwla",
+ .longname = "SysClk LWLA series",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_channel_set = config_channel_set,
+ .config_commit = config_commit,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
+ *
+ * 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 "lwla.h"
+#include "protocol.h"
+#include "libsigrok-internal.h"
+#include <errno.h>
+#include <glib/gstdio.h>
+
+#define BITSTREAM_MAX_SIZE 262144 /* bitstream size limit for safety */
+#define BITSTREAM_HEADER_SIZE 4 /* transfer header size in bytes */
+
+/* Load a bitstream file into memory. Returns a newly allocated array
+ * consisting of a 32-bit length field followed by the bitstream data.
+ */
+static unsigned char *load_bitstream_file(const char *filename, int *length_p)
+{
+ GStatBuf statbuf;
+ FILE *file;
+ unsigned char *stream;
+ size_t length, count;
+
+ /* Retrieve and validate the file size. */
+ if (g_stat(filename, &statbuf) < 0) {
+ sr_err("Failed to access bitstream file: %s.",
+ g_strerror(errno));
+ return NULL;
+ }
+ if (!S_ISREG(statbuf.st_mode)) {
+ sr_err("Bitstream is not a regular file.");
+ return NULL;
+ }
+ if (statbuf.st_size <= 0 || statbuf.st_size > BITSTREAM_MAX_SIZE) {
+ sr_err("Refusing to load bitstream of unreasonable size "
+ "(%" PRIu64 " bytes).", (uint64_t)statbuf.st_size);
+ return NULL;
+ }
+
+ /* The message length includes the 4-byte header. */
+ length = BITSTREAM_HEADER_SIZE + statbuf.st_size;
+ stream = g_try_malloc(length);
+ if (!stream) {
+ sr_err("Failed to allocate bitstream buffer.");
+ return NULL;
+ }
+
+ file = g_fopen(filename, "rb");
+ if (!file) {
+ sr_err("Failed to open bitstream file: %s.", g_strerror(errno));
+ g_free(stream);
+ return NULL;
+ }
+
+ /* Write the message length header. */
+ *(uint32_t *)stream = GUINT32_TO_BE(length);
+
+ count = fread(stream + BITSTREAM_HEADER_SIZE,
+ length - BITSTREAM_HEADER_SIZE, 1, file);
+ if (count != 1) {
+ sr_err("Failed to read bitstream file: %s.", g_strerror(errno));
+ fclose(file);
+ g_free(stream);
+ return NULL;
+ }
+ fclose(file);
+
+ *length_p = length;
+ return stream;
+}
+
+/* Load a Raw Binary File (.rbf) from the firmware directory and transfer
+ * it to the device.
+ */
+SR_PRIV int lwla_send_bitstream(const struct sr_usb_dev_inst *usb,
+ const char *basename)
+{
+ char *filename;
+ unsigned char *stream;
+ int ret;
+ int length;
+ int xfer_len;
+
+ if (!usb || !basename)
+ return SR_ERR_BUG;
+
+ filename = g_build_filename(FIRMWARE_DIR, basename, NULL);
+ sr_info("Downloading FPGA bitstream at '%s'.", filename);
+
+ stream = load_bitstream_file(filename, &length);
+ g_free(filename);
+
+ if (!stream)
+ return SR_ERR;
+
+ /* Transfer the entire bitstream in one URB. */
+ ret = libusb_bulk_transfer(usb->devhdl, EP_BITSTREAM,
+ stream, length, &xfer_len, USB_TIMEOUT);
+ g_free(stream);
+
+ if (ret != 0) {
+ sr_err("Failed to transfer bitstream: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if (xfer_len != length) {
+ sr_err("Failed to transfer bitstream: incorrect length "
+ "%d != %d.", xfer_len, length);
+ return SR_ERR;
+ }
+ sr_info("FPGA bitstream download of %d bytes done.", xfer_len);
+
+ /* This delay appears to be necessary for reliable operation. */
+ g_usleep(30000);
+
+ return SR_OK;
+}
+
+SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb,
+ const uint16_t *command, int cmd_len)
+{
+ int ret;
+ int xfer_len;
+
+ if (!usb || !command || cmd_len <= 0)
+ return SR_ERR_BUG;
+
+ xfer_len = 0;
+ ret = libusb_bulk_transfer(usb->devhdl, EP_COMMAND,
+ (unsigned char *)command, cmd_len * 2,
+ &xfer_len, USB_TIMEOUT);
+ if (ret != 0) {
+ sr_dbg("Failed to send command %d: %s.",
+ LWLA_TO_UINT16(command[0]), libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if (xfer_len != cmd_len * 2) {
+ sr_dbg("Failed to send command %d: incorrect length %d != %d.",
+ LWLA_TO_UINT16(command[0]), xfer_len, cmd_len * 2);
+ return SR_ERR;
+ }
+ return SR_OK;
+}
+
+SR_PRIV int lwla_receive_reply(const struct sr_usb_dev_inst *usb,
+ uint32_t *reply, int reply_len, int expect_len)
+{
+ int ret;
+ int xfer_len;
+
+ if (!usb || !reply || reply_len <= 0)
+ return SR_ERR_BUG;
+
+ xfer_len = 0;
+ ret = libusb_bulk_transfer(usb->devhdl, EP_REPLY,
+ (unsigned char *)reply, reply_len * 4,
+ &xfer_len, USB_TIMEOUT);
+ if (ret != 0) {
+ sr_dbg("Failed to receive reply: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if (xfer_len != expect_len * 4) {
+ sr_dbg("Failed to receive reply: incorrect length %d != %d.",
+ xfer_len, expect_len * 4);
+ return SR_ERR;
+ }
+ return SR_OK;
+}
+
+SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb,
+ uint16_t reg, uint32_t *value)
+{
+ int ret;
+ uint16_t command[2];
+ uint32_t reply[128]; /* full EP buffer to avoid overflows */
+
+ command[0] = LWLA_WORD(CMD_READ_REG);
+ command[1] = LWLA_WORD(reg);
+
+ ret = lwla_send_command(usb, command, G_N_ELEMENTS(command));
+
+ if (ret != SR_OK)
+ return ret;
+
+ ret = lwla_receive_reply(usb, reply, G_N_ELEMENTS(reply), 1);
+
+ if (ret == SR_OK)
+ *value = LWLA_TO_UINT32(reply[0]);
+
+ return ret;
+}
+
+SR_PRIV int lwla_write_reg(const struct sr_usb_dev_inst *usb,
+ uint16_t reg, uint32_t value)
+{
+ uint16_t command[4];
+
+ command[0] = LWLA_WORD(CMD_WRITE_REG);
+ command[1] = LWLA_WORD(reg);
+ command[2] = LWLA_WORD_0(value);
+ command[3] = LWLA_WORD_1(value);
+
+ return lwla_send_command(usb, command, G_N_ELEMENTS(command));
+}
+
+SR_PRIV int lwla_write_regs(const struct sr_usb_dev_inst *usb,
+ const struct regval_pair *regvals, int count)
+{
+ int i;
+ int ret;
+
+ ret = SR_OK;
+
+ for (i = 0; i < count; ++i) {
+ ret = lwla_write_reg(usb, regvals[i].reg, regvals[i].val);
+
+ if (ret != SR_OK)
+ break;
+ }
+
+ return ret;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
+ *
+ * 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_SYSCLK_LWLA_LWLA_H
+#define LIBSIGROK_HARDWARE_SYSCLK_LWLA_LWLA_H
+
+#include "libsigrok.h"
+#include <stdint.h>
+#include <libusb.h>
+#include <glib.h>
+
+struct sr_usb_dev_inst;
+
+/* Rotate argument n bits to the left.
+ * This construct is an idiom recognized by GCC as bit rotation.
+ */
+#define LROTATE(a, n) (((a) << (n)) | ((a) >> (CHAR_BIT * sizeof(a) - (n))))
+
+/* Convert 16-bit little endian LWLA protocol word to machine word order. */
+#define LWLA_TO_UINT16(val) GUINT16_FROM_LE(val)
+
+/* Convert 32-bit mixed endian LWLA protocol word to machine word order. */
+#define LWLA_TO_UINT32(val) LROTATE(GUINT32_FROM_LE(val), 16)
+
+/* Convert 16-bit argument to LWLA protocol word. */
+#define LWLA_WORD(val) GUINT16_TO_LE(val)
+
+/* Extract 16-bit units in mixed endian order from 32/64-bit value. */
+#define LWLA_WORD_0(val) GUINT16_TO_LE(((val) >> 16) & 0xFFFF)
+#define LWLA_WORD_1(val) GUINT16_TO_LE((val) & 0xFFFF)
+#define LWLA_WORD_2(val) GUINT16_TO_LE(((val) >> 48) & 0xFFFF)
+#define LWLA_WORD_3(val) GUINT16_TO_LE(((val) >> 32) & 0xFFFF)
+
+/** USB device end points.
+ */
+enum {
+ EP_COMMAND = 2,
+ EP_BITSTREAM = 4,
+ EP_REPLY = 6 | LIBUSB_ENDPOINT_IN
+};
+
+/** LWLA protocol command ID codes.
+ */
+enum {
+ CMD_READ_REG = 1,
+ CMD_WRITE_REG = 2,
+ CMD_READ_MEM = 6,
+ CMD_CAP_SETUP = 7,
+ CMD_CAP_STATUS = 8,
+};
+
+/** LWLA capture state flags.
+ */
+enum {
+ STATUS_CAPTURING = 1 << 1,
+ STATUS_TRIGGERED = 1 << 4,
+ STATUS_MEM_AVAIL = 1 << 5,
+ STATUS_FLAG_MASK = 0x3F
+};
+
+/** LWLA register addresses.
+ */
+enum {
+ REG_MEM_CTRL2 = 0x1074, /* capture buffer control ??? */
+ REG_MEM_FILL = 0x1078, /* capture buffer fill level */
+ REG_MEM_CTRL4 = 0x107C, /* capture buffer control ??? */
+
+ REG_DIV_BYPASS = 0x1094, /* bypass clock divider flag */
+
+ REG_CMD_CTRL1 = 0x10B0, /* command control ??? */
+ REG_CMD_CTRL2 = 0x10B4, /* command control ??? */
+ REG_CMD_CTRL3 = 0x10B8, /* command control ??? */
+ REG_CMD_CTRL4 = 0x10BC, /* command control ??? */
+
+ REG_FREQ_CH1 = 0x10C0, /* channel 1 live frequency */
+ REG_FREQ_CH2 = 0x10C4, /* channel 2 live frequency */
+ REG_FREQ_CH3 = 0x10C8, /* channel 3 live frequency */
+ REG_FREQ_CH4 = 0x10CC, /* channel 4 live frequency */
+};
+
+/** Register/value pair.
+ */
+struct regval_pair {
+ unsigned int reg;
+ unsigned int val;
+};
+
+SR_PRIV int lwla_send_bitstream(const struct sr_usb_dev_inst *usb,
+ const char *basename);
+
+SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb,
+ const uint16_t *command, int cmd_len);
+
+SR_PRIV int lwla_receive_reply(const struct sr_usb_dev_inst *usb,
+ uint32_t *reply, int reply_len, int expect_len);
+
+SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb,
+ uint16_t reg, uint32_t *value);
+
+SR_PRIV int lwla_write_reg(const struct sr_usb_dev_inst *usb,
+ uint16_t reg, uint32_t value);
+
+SR_PRIV int lwla_write_regs(const struct sr_usb_dev_inst *usb,
+ const struct regval_pair *regvals, int count);
+
+#endif /* !LIBSIGROK_HARDWARE_SYSCLK_LWLA_LWLA_H */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
+ *
+ * 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 "protocol.h"
+#include <string.h>
+
+/* Bit mask for the RLE repeat-count-follows flag. */
+#define RLE_FLAG_LEN_FOLLOWS ((uint64_t)1 << 35)
+
+/* Start address of capture status memory area to read. */
+#define CAP_STAT_ADDR 5
+
+/* Number of 64-bit words read from the capture status memory. */
+#define CAP_STAT_LEN 5
+
+/* The bitstream filenames are indexed by the clock_config enumeration.
+ */
+static const char bitstream_map[][32] = {
+ "sysclk-lwla1034-off.rbf",
+ "sysclk-lwla1034-int.rbf",
+ "sysclk-lwla1034-extpos.rbf",
+ "sysclk-lwla1034-extneg.rbf",
+};
+
+/* Submit an already filled-in USB transfer.
+ */
+static int submit_transfer(struct dev_context *devc,
+ struct libusb_transfer *xfer)
+{
+ int ret;
+
+ ret = libusb_submit_transfer(xfer);
+
+ if (ret != 0) {
+ sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
+ devc->transfer_error = TRUE;
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+/* Set up the LWLA in preparation for an acquisition session.
+ */
+static int capture_setup(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct acquisition_state *acq;
+ uint64_t divider_count;
+ uint64_t trigger_mask;
+ uint64_t memory_limit;
+ uint16_t command[3 + 10*4];
+
+ devc = sdi->priv;
+ acq = devc->acquisition;
+
+ command[0] = LWLA_WORD(CMD_CAP_SETUP);
+ command[1] = LWLA_WORD(0); /* address */
+ command[2] = LWLA_WORD(10); /* length */
+
+ command[3] = LWLA_WORD_0(devc->channel_mask);
+ command[4] = LWLA_WORD_1(devc->channel_mask);
+ command[5] = LWLA_WORD_2(devc->channel_mask);
+ command[6] = LWLA_WORD_3(devc->channel_mask);
+
+ /* Set the clock divide counter maximum for samplerates of up to
+ * 100 MHz. At the highest samplerate of 125 MHz the clock divider
+ * is bypassed.
+ */
+ if (!acq->bypass_clockdiv && devc->samplerate > 0)
+ divider_count = SR_MHZ(100) / devc->samplerate - 1;
+ else
+ divider_count = 0;
+
+ command[7] = LWLA_WORD_0(divider_count);
+ command[8] = LWLA_WORD_1(divider_count);
+ command[9] = LWLA_WORD_2(divider_count);
+ command[10] = LWLA_WORD_3(divider_count);
+
+ command[11] = LWLA_WORD_0(devc->trigger_values);
+ command[12] = LWLA_WORD_1(devc->trigger_values);
+ command[13] = LWLA_WORD_2(devc->trigger_values);
+ command[14] = LWLA_WORD_3(devc->trigger_values);
+
+ command[15] = LWLA_WORD_0(devc->trigger_edge_mask);
+ command[16] = LWLA_WORD_1(devc->trigger_edge_mask);
+ command[17] = LWLA_WORD_2(devc->trigger_edge_mask);
+ command[18] = LWLA_WORD_3(devc->trigger_edge_mask);
+
+ trigger_mask = devc->trigger_mask;
+ /* Set bits to select external TRG input edge. */
+ if (devc->cfg_trigger_source == TRIGGER_EXT_TRG)
+ switch (devc->cfg_trigger_slope) {
+ case EDGE_POSITIVE: trigger_mask |= (uint64_t)1 << 35; break;
+ case EDGE_NEGATIVE: trigger_mask |= (uint64_t)1 << 34; break;
+ }
+
+ command[19] = LWLA_WORD_0(trigger_mask);
+ command[20] = LWLA_WORD_1(trigger_mask);
+ command[21] = LWLA_WORD_2(trigger_mask);
+ command[22] = LWLA_WORD_3(trigger_mask);
+
+ /* Set the capture memory full threshold. This is slightly less
+ * than the actual maximum, most likely in order to compensate for
+ * pipeline latency.
+ */
+ memory_limit = MEMORY_DEPTH - 16;
+
+ command[23] = LWLA_WORD_0(memory_limit);
+ command[24] = LWLA_WORD_1(memory_limit);
+ command[25] = LWLA_WORD_2(memory_limit);
+ command[26] = LWLA_WORD_3(memory_limit);
+
+ /* Fill remaining 64-bit words with zeroes. */
+ memset(&command[27], 0, 16 * sizeof(uint16_t));
+
+ return lwla_send_command(sdi->conn, command, G_N_ELEMENTS(command));
+}
+
+/* Issue a register write command as an asynchronous USB transfer.
+ */
+static int issue_write_reg(const struct sr_dev_inst *sdi,
+ unsigned int reg, unsigned int value)
+{
+ struct dev_context *devc;
+ struct acquisition_state *acq;
+
+ devc = sdi->priv;
+ acq = devc->acquisition;
+
+ acq->xfer_buf_out[0] = LWLA_WORD(CMD_WRITE_REG);
+ acq->xfer_buf_out[1] = LWLA_WORD(reg);
+ acq->xfer_buf_out[2] = LWLA_WORD_0(value);
+ acq->xfer_buf_out[3] = LWLA_WORD_1(value);
+
+ acq->xfer_out->length = 4 * sizeof(uint16_t);
+
+ return submit_transfer(devc, acq->xfer_out);
+}
+
+/* Issue a register write command as an asynchronous USB transfer for the
+ * next register/value pair of the currently active register write sequence.
+ */
+static int issue_next_write_reg(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct regval_pair *regval;
+ int ret;
+
+ devc = sdi->priv;
+
+ if (devc->reg_write_pos >= devc->reg_write_len) {
+ sr_err("Already written all registers in sequence.");
+ return SR_ERR_BUG;
+ }
+ regval = &devc->reg_write_seq[devc->reg_write_pos];
+
+ ret = issue_write_reg(sdi, regval->reg, regval->val);
+ if (ret != SR_OK)
+ return ret;
+
+ ++devc->reg_write_pos;
+ return SR_OK;
+}
+
+/* Issue a capture status request as an asynchronous USB transfer.
+ */
+static void request_capture_status(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct acquisition_state *acq;
+
+ devc = sdi->priv;
+ acq = devc->acquisition;
+
+ acq->xfer_buf_out[0] = LWLA_WORD(CMD_CAP_STATUS);
+ acq->xfer_buf_out[1] = LWLA_WORD(CAP_STAT_ADDR);
+ acq->xfer_buf_out[2] = LWLA_WORD(CAP_STAT_LEN);
+
+ acq->xfer_out->length = 3 * sizeof(uint16_t);
+
+ if (submit_transfer(devc, acq->xfer_out) == SR_OK)
+ devc->state = STATE_STATUS_REQUEST;
+}
+
+/* Issue a request for the capture buffer fill level as
+ * an asynchronous USB transfer.
+ */
+static void request_capture_length(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct acquisition_state *acq;
+
+ devc = sdi->priv;
+ acq = devc->acquisition;
+
+ acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_REG);
+ acq->xfer_buf_out[1] = LWLA_WORD(REG_MEM_FILL);
+
+ acq->xfer_out->length = 2 * sizeof(uint16_t);
+
+ if (submit_transfer(devc, acq->xfer_out) == SR_OK)
+ devc->state = STATE_LENGTH_REQUEST;
+}
+
+/* Initiate the capture memory read operation: Reset the acquisition state
+ * and start a sequence of register writes in order to set up the device for
+ * reading from the capture buffer.
+ */
+static void issue_read_start(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct acquisition_state *acq;
+ struct regval_pair *regvals;
+
+ devc = sdi->priv;
+ acq = devc->acquisition;
+
+ /* Reset RLE state. */
+ acq->rle = RLE_STATE_DATA;
+ acq->sample = 0;
+ acq->run_len = 0;
+
+ acq->samples_done = 0;
+
+ /* For some reason, the start address is 4 rather than 0. */
+ acq->mem_addr_done = 4;
+ acq->mem_addr_next = 4;
+ acq->mem_addr_stop = acq->mem_addr_fill;
+
+ /* Sample position in the packet output buffer. */
+ acq->out_index = 0;
+
+ regvals = devc->reg_write_seq;
+
+ regvals[0].reg = REG_DIV_BYPASS;
+ regvals[0].val = 1;
+
+ regvals[1].reg = REG_MEM_CTRL2;
+ regvals[1].val = 2;
+
+ regvals[2].reg = REG_MEM_CTRL4;
+ regvals[2].val = 4;
+
+ devc->reg_write_pos = 0;
+ devc->reg_write_len = 3;
+
+ if (issue_next_write_reg(sdi) == SR_OK)
+ devc->state = STATE_READ_PREPARE;
+}
+
+/* Issue a command as an asynchronous USB transfer which returns the device
+ * to normal state after a read operation. Sets a new device context state
+ * on success.
+ */
+static void issue_read_end(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ if (issue_write_reg(sdi, REG_DIV_BYPASS, 0) == SR_OK)
+ devc->state = STATE_READ_END;
+}
+
+/* Decode an incoming reponse to a buffer fill level request and act on it
+ * as appropriate. Note that this function changes the device context state.
+ */
+static void process_capture_length(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct acquisition_state *acq;
+
+ devc = sdi->priv;
+ acq = devc->acquisition;
+
+ if (acq->xfer_in->actual_length != 4) {
+ sr_err("Received size %d doesn't match expected size 4.",
+ acq->xfer_in->actual_length);
+ devc->transfer_error = TRUE;
+ return;
+ }
+ acq->mem_addr_fill = LWLA_TO_UINT32(acq->xfer_buf_in[0]);
+
+ sr_dbg("%zu words in capture buffer.", acq->mem_addr_fill);
+
+ if (acq->mem_addr_fill > 0 && sdi->status == SR_ST_ACTIVE)
+ issue_read_start(sdi);
+ else
+ issue_read_end(sdi);
+}
+
+/* Initiate a sequence of register write commands with the effect of
+ * cancelling a running capture operation. This sets a new device state
+ * if issuing the first command succeeds.
+ */
+static void issue_stop_capture(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct regval_pair *regvals;
+
+ devc = sdi->priv;
+
+ if (devc->stopping_in_progress)
+ return;
+
+ regvals = devc->reg_write_seq;
+
+ regvals[0].reg = REG_CMD_CTRL2;
+ regvals[0].val = 10;
+
+ regvals[1].reg = REG_CMD_CTRL3;
+ regvals[1].val = 0;
+
+ regvals[2].reg = REG_CMD_CTRL4;
+ regvals[2].val = 0;
+
+ regvals[3].reg = REG_CMD_CTRL1;
+ regvals[3].val = 0;
+
+ regvals[4].reg = REG_DIV_BYPASS;
+ regvals[4].val = 0;
+
+ devc->reg_write_pos = 0;
+ devc->reg_write_len = 5;
+
+ if (issue_next_write_reg(sdi) == SR_OK) {
+ devc->stopping_in_progress = TRUE;
+ devc->state = STATE_STOP_CAPTURE;
+ }
+}
+
+/* Decode an incoming capture status reponse and act on it as appropriate.
+ * Note that this function changes the device state.
+ */
+static void process_capture_status(const struct sr_dev_inst *sdi)
+{
+ uint64_t duration;
+ struct dev_context *devc;
+ struct acquisition_state *acq;
+ unsigned int mem_fill;
+ unsigned int flags;
+
+ devc = sdi->priv;
+ acq = devc->acquisition;
+
+ if (acq->xfer_in->actual_length != CAP_STAT_LEN * 8) {
+ sr_err("Received size %d doesn't match expected size %d.",
+ acq->xfer_in->actual_length, CAP_STAT_LEN * 8);
+ devc->transfer_error = TRUE;
+ return;
+ }
+
+ /* TODO: Find out the actual bit width of these fields as stored
+ * in the FPGA. These fields are definitely less than 64 bit wide
+ * internally, and the unused bits occasionally even contain garbage.
+ */
+ mem_fill = LWLA_TO_UINT32(acq->xfer_buf_in[0]);
+ duration = LWLA_TO_UINT32(acq->xfer_buf_in[4]);
+ flags = LWLA_TO_UINT32(acq->xfer_buf_in[8]) & STATUS_FLAG_MASK;
+
+ /* The LWLA1034 runs at 125 MHz if the clock divider is bypassed.
+ * However, the time base used for the duration is apparently not
+ * adjusted for this "boost" mode. Whereas normally the duration
+ * unit is 1 ms, it is 0.8 ms when the clock divider is bypassed.
+ * As 0.8 = 100 MHz / 125 MHz, it seems that the internal cycle
+ * counter period is the same as at the 100 MHz setting.
+ */
+ if (acq->bypass_clockdiv)
+ acq->duration_now = duration * 4 / 5;
+ else
+ acq->duration_now = duration;
+
+ sr_spew("Captured %u words, %" PRIu64 " ms, flags 0x%02X.",
+ mem_fill, acq->duration_now, flags);
+
+ if ((flags & STATUS_TRIGGERED) > (acq->capture_flags & STATUS_TRIGGERED))
+ sr_info("Capture triggered.");
+
+ acq->capture_flags = flags;
+
+ if (acq->duration_now >= acq->duration_max) {
+ sr_dbg("Time limit reached, stopping capture.");
+ issue_stop_capture(sdi);
+ return;
+ }
+ devc->state = STATE_STATUS_WAIT;
+
+ if ((acq->capture_flags & STATUS_TRIGGERED) == 0) {
+ sr_spew("Waiting for trigger.");
+ } else if ((acq->capture_flags & STATUS_MEM_AVAIL) == 0) {
+ sr_dbg("Capture memory filled.");
+ request_capture_length(sdi);
+ } else if ((acq->capture_flags & STATUS_CAPTURING) != 0) {
+ sr_spew("Sampling in progress.");
+ }
+}
+
+/* Issue a capture buffer read request as an asynchronous USB transfer.
+ * The address and size of the memory area to read are derived from the
+ * current acquisition state.
+ */
+static void request_read_mem(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct acquisition_state *acq;
+ size_t count;
+
+ devc = sdi->priv;
+ acq = devc->acquisition;
+
+ if (acq->mem_addr_next >= acq->mem_addr_stop)
+ return;
+
+ /* Always read a multiple of 8 device words. */
+ count = (acq->mem_addr_stop - acq->mem_addr_next + 7) / 8 * 8;
+ count = MIN(count, READ_CHUNK_LEN);
+
+ acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_MEM);
+ acq->xfer_buf_out[1] = LWLA_WORD_0(acq->mem_addr_next);
+ acq->xfer_buf_out[2] = LWLA_WORD_1(acq->mem_addr_next);
+ acq->xfer_buf_out[3] = LWLA_WORD_0(count);
+ acq->xfer_buf_out[4] = LWLA_WORD_1(count);
+
+ acq->xfer_out->length = 5 * sizeof(uint16_t);
+
+ if (submit_transfer(devc, acq->xfer_out) == SR_OK) {
+ acq->mem_addr_next += count;
+ devc->state = STATE_READ_REQUEST;
+ }
+}
+
+/* Demangle and decompress incoming sample data from the capture buffer.
+ * The data chunk is taken from the acquisition state, and is expected to
+ * contain a multiple of 8 device words.
+ * All data currently in the acquisition buffer will be processed. Packets
+ * of decoded samples are sent off to the session bus whenever the output
+ * buffer becomes full while decoding.
+ */
+static int process_sample_data(const struct sr_dev_inst *sdi)
+{
+ uint64_t sample;
+ uint64_t high_nibbles;
+ uint64_t word;
+ struct dev_context *devc;
+ struct acquisition_state *acq;
+ uint8_t *out_p;
+ uint32_t *slice;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ size_t expect_len;
+ size_t actual_len;
+ size_t out_max_samples;
+ size_t out_run_samples;
+ size_t ri;
+ size_t in_words_left;
+ size_t si;
+
+ devc = sdi->priv;
+ acq = devc->acquisition;
+
+ if (acq->mem_addr_done >= acq->mem_addr_stop
+ || acq->samples_done >= acq->samples_max)
+ return SR_OK;
+
+ in_words_left = MIN(acq->mem_addr_stop - acq->mem_addr_done,
+ READ_CHUNK_LEN);
+ expect_len = LWLA1034_MEMBUF_LEN(in_words_left) * sizeof(uint32_t);
+ actual_len = acq->xfer_in->actual_length;
+
+ if (actual_len != expect_len) {
+ sr_err("Received size %zu does not match expected size %zu.",
+ actual_len, expect_len);
+ devc->transfer_error = TRUE;
+ return SR_ERR;
+ }
+ acq->mem_addr_done += in_words_left;
+
+ /* Prepare session packet. */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.unitsize = UNIT_SIZE;
+ logic.data = acq->out_packet;
+
+ slice = acq->xfer_buf_in;
+ si = 0; /* word index within slice */
+
+ for (;;) {
+ /* Calculate number of samples to write into packet. */
+ out_max_samples = MIN(acq->samples_max - acq->samples_done,
+ PACKET_LENGTH - acq->out_index);
+ out_run_samples = MIN(acq->run_len, out_max_samples);
+
+ /* Expand run-length samples into session packet. */
+ sample = acq->sample;
+ out_p = &acq->out_packet[acq->out_index * UNIT_SIZE];
+
+ for (ri = 0; ri < out_run_samples; ++ri) {
+ out_p[0] = sample & 0xFF;
+ out_p[1] = (sample >> 8) & 0xFF;
+ out_p[2] = (sample >> 16) & 0xFF;
+ out_p[3] = (sample >> 24) & 0xFF;
+ out_p[4] = (sample >> 32) & 0xFF;
+ out_p += UNIT_SIZE;
+ }
+ acq->run_len -= out_run_samples;
+ acq->out_index += out_run_samples;
+ acq->samples_done += out_run_samples;
+
+ /* Packet full or sample count limit reached? */
+ if (out_run_samples == out_max_samples) {
+ logic.length = acq->out_index * UNIT_SIZE;
+ sr_session_send(sdi, &packet);
+ acq->out_index = 0;
+
+ if (acq->samples_done >= acq->samples_max)
+ return SR_OK; /* sample limit reached */
+ if (acq->run_len > 0)
+ continue; /* need another packet */
+ }
+
+ if (in_words_left == 0)
+ break; /* done with current chunk */
+
+ /* Now work on the current slice. */
+ high_nibbles = LWLA_TO_UINT32(slice[8]);
+ word = LWLA_TO_UINT32(slice[si]);
+ word |= (high_nibbles << (4 * si + 4)) & ((uint64_t)0xF << 32);
+
+ if (acq->rle == RLE_STATE_DATA) {
+ acq->sample = word & ALL_CHANNELS_MASK;
+ acq->run_len = ((word >> NUM_CHANNELS) & 1) + 1;
+ if (word & RLE_FLAG_LEN_FOLLOWS)
+ acq->rle = RLE_STATE_LEN;
+ } else {
+ acq->run_len += word << 1;
+ acq->rle = RLE_STATE_DATA;
+ }
+
+ /* Move to next word. */
+ si = (si + 1) % 8;
+ if (si == 0)
+ slice += 9;
+ --in_words_left;
+ }
+
+ /* Send out partially filled packet if this was the last chunk. */
+ if (acq->mem_addr_done >= acq->mem_addr_stop && acq->out_index > 0) {
+ logic.length = acq->out_index * UNIT_SIZE;
+ sr_session_send(sdi, &packet);
+ acq->out_index = 0;
+ }
+ return SR_OK;
+}
+
+/* Finish an acquisition session. This sends the end packet to the session
+ * bus and removes the listener for asynchronous USB transfers.
+ */
+static void end_acquisition(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+
+ drvc = sdi->driver->priv;
+ devc = sdi->priv;
+
+ if (devc->state == STATE_IDLE)
+ return;
+
+ devc->state = STATE_IDLE;
+
+ /* Remove USB file descriptors from polling. */
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+
+ lwla_free_acquisition_state(devc->acquisition);
+ devc->acquisition = NULL;
+
+ sdi->status = SR_ST_ACTIVE;
+}
+
+/* USB output transfer completion callback.
+ */
+static void receive_transfer_out(struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ sr_err("Transfer to device failed: %d.", transfer->status);
+ devc->transfer_error = TRUE;
+ return;
+ }
+
+ if (devc->reg_write_pos < devc->reg_write_len) {
+ issue_next_write_reg(sdi);
+ } else {
+ switch (devc->state) {
+ case STATE_START_CAPTURE:
+ devc->state = STATE_STATUS_WAIT;
+ break;
+ case STATE_STATUS_REQUEST:
+ devc->state = STATE_STATUS_RESPONSE;
+ submit_transfer(devc, devc->acquisition->xfer_in);
+ break;
+ case STATE_STOP_CAPTURE:
+ if (sdi->status == SR_ST_ACTIVE)
+ request_capture_length(sdi);
+ else
+ end_acquisition(sdi);
+ break;
+ case STATE_LENGTH_REQUEST:
+ devc->state = STATE_LENGTH_RESPONSE;
+ submit_transfer(devc, devc->acquisition->xfer_in);
+ break;
+ case STATE_READ_PREPARE:
+ request_read_mem(sdi);
+ break;
+ case STATE_READ_REQUEST:
+ devc->state = STATE_READ_RESPONSE;
+ submit_transfer(devc, devc->acquisition->xfer_in);
+ break;
+ case STATE_READ_END:
+ end_acquisition(sdi);
+ break;
+ default:
+ sr_err("Unexpected device state %d.", devc->state);
+ break;
+ }
+ }
+}
+
+/* USB input transfer completion callback.
+ */
+static void receive_transfer_in(struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct acquisition_state *acq;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+ acq = devc->acquisition;
+
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ sr_err("Transfer from device failed: %d.", transfer->status);
+ devc->transfer_error = TRUE;
+ return;
+ }
+
+ switch (devc->state) {
+ case STATE_STATUS_RESPONSE:
+ process_capture_status(sdi);
+ break;
+ case STATE_LENGTH_RESPONSE:
+ process_capture_length(sdi);
+ break;
+ case STATE_READ_RESPONSE:
+ if (process_sample_data(sdi) == SR_OK
+ && acq->mem_addr_next < acq->mem_addr_stop
+ && acq->samples_done < acq->samples_max)
+ request_read_mem(sdi);
+ else
+ issue_read_end(sdi);
+ break;
+ default:
+ sr_err("Unexpected device state %d.", devc->state);
+ break;
+ }
+}
+
+/* Initialize the LWLA. This downloads a bitstream into the FPGA
+ * and executes a simple device test sequence.
+ */
+SR_PRIV int lwla_init_device(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int ret;
+ uint32_t value;
+
+ devc = sdi->priv;
+
+ /* Force reload of bitstream */
+ devc->cur_clock_config = CONF_CLOCK_NONE;
+
+ ret = lwla_set_clock_config(sdi);
+
+ if (ret != SR_OK)
+ return ret;
+
+ ret = lwla_write_reg(sdi->conn, REG_CMD_CTRL2, 100);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL1, &value);
+ if (ret != SR_OK)
+ return ret;
+ sr_dbg("Received test word 0x%08X back.", value);
+ if (value != 0x12345678)
+ return SR_ERR;
+
+ ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL4, &value);
+ if (ret != SR_OK)
+ return ret;
+ sr_dbg("Received test word 0x%08X back.", value);
+ if (value != 0x12345678)
+ return SR_ERR;
+
+ ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL3, &value);
+ if (ret != SR_OK)
+ return ret;
+ sr_dbg("Received test word 0x%08X back.", value);
+ if (value != 0x87654321)
+ return SR_ERR;
+
+ return ret;
+}
+
+SR_PRIV int lwla_convert_trigger(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_trigger *trigger;
+ struct sr_trigger_stage *stage;
+ struct sr_trigger_match *match;
+ const GSList *l, *m;
+ uint64_t channel_index;
+
+ devc = sdi->priv;
+
+ devc->trigger_mask = 0;
+ devc->trigger_values = 0;
+ devc->trigger_edge_mask = 0;
+
+ if (!(trigger = sr_session_trigger_get(sdi->session)))
+ return SR_OK;
+
+ if (g_slist_length(trigger->stages) > 1) {
+ sr_err("This device only supports 1 trigger stage.");
+ return SR_ERR;
+ }
+
+ for (l = trigger->stages; l; l = l->next) {
+ stage = l->data;
+ for (m = stage->matches; m; m = m->next) {
+ match = m->data;
+ if (!match->channel->enabled)
+ /* Ignore disabled channels with a trigger. */
+ continue;
+ channel_index = 1 << match->channel->index;
+ devc->trigger_mask |= channel_index;
+ switch (match->match) {
+ case SR_TRIGGER_ONE:
+ devc->trigger_values |= channel_index;
+ break;
+ case SR_TRIGGER_RISING:
+ devc->trigger_values |= channel_index;
+ /* Fall through for edge mask. */
+ case SR_TRIGGER_FALLING:
+ devc->trigger_edge_mask |= channel_index;
+ break;
+ }
+ }
+ }
+
+ return SR_OK;
+}
+
+/* Select the LWLA clock configuration. If the clock source changed from
+ * the previous setting, this will download a new bitstream to the FPGA.
+ */
+SR_PRIV int lwla_set_clock_config(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int ret;
+ enum clock_config choice;
+
+ devc = sdi->priv;
+
+ if (sdi->status == SR_ST_INACTIVE)
+ choice = CONF_CLOCK_NONE;
+ else if (devc->cfg_clock_source == CLOCK_INTERNAL)
+ choice = CONF_CLOCK_INT;
+ else if (devc->cfg_clock_edge == EDGE_POSITIVE)
+ choice = CONF_CLOCK_EXT_RISE;
+ else
+ choice = CONF_CLOCK_EXT_FALL;
+
+ if (choice != devc->cur_clock_config) {
+ devc->cur_clock_config = CONF_CLOCK_NONE;
+ ret = lwla_send_bitstream(sdi->conn, bitstream_map[choice]);
+ if (ret == SR_OK)
+ devc->cur_clock_config = choice;
+ return ret;
+ }
+ return SR_OK;
+}
+
+/* Configure the LWLA in preparation for an acquisition session.
+ */
+SR_PRIV int lwla_setup_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ struct acquisition_state *acq;
+ struct regval_pair regvals[7];
+ int ret;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+ acq = devc->acquisition;
+
+ if (devc->limit_msec > 0) {
+ acq->duration_max = devc->limit_msec;
+ sr_info("Acquisition time limit %" PRIu64 " ms.",
+ devc->limit_msec);
+ } else
+ acq->duration_max = MAX_LIMIT_MSEC;
+
+ if (devc->limit_samples > 0) {
+ acq->samples_max = devc->limit_samples;
+ sr_info("Acquisition sample count limit %" PRIu64 ".",
+ devc->limit_samples);
+ } else
+ acq->samples_max = MAX_LIMIT_SAMPLES;
+
+ if (devc->cfg_clock_source == CLOCK_INTERNAL) {
+ sr_info("Internal clock, samplerate %" PRIu64 ".",
+ devc->samplerate);
+ if (devc->samplerate == 0)
+ return SR_ERR_BUG;
+ /* At 125 MHz, the clock divider is bypassed. */
+ acq->bypass_clockdiv = (devc->samplerate > SR_MHZ(100));
+
+ /* If only one of the limits is set, derive the other one. */
+ if (devc->limit_msec == 0 && devc->limit_samples > 0)
+ acq->duration_max = devc->limit_samples
+ * 1000 / devc->samplerate + 1;
+ else if (devc->limit_samples == 0 && devc->limit_msec > 0)
+ acq->samples_max = devc->limit_msec
+ * devc->samplerate / 1000;
+ } else {
+ acq->bypass_clockdiv = TRUE;
+
+ if (devc->cfg_clock_edge == EDGE_NEGATIVE)
+ sr_info("External clock, falling edge.");
+ else
+ sr_info("External clock, rising edge.");
+ }
+
+ regvals[0].reg = REG_MEM_CTRL2;
+ regvals[0].val = 2;
+
+ regvals[1].reg = REG_MEM_CTRL2;
+ regvals[1].val = 1;
+
+ regvals[2].reg = REG_CMD_CTRL2;
+ regvals[2].val = 10;
+
+ regvals[3].reg = REG_CMD_CTRL3;
+ regvals[3].val = 0x74;
+
+ regvals[4].reg = REG_CMD_CTRL4;
+ regvals[4].val = 0;
+
+ regvals[5].reg = REG_CMD_CTRL1;
+ regvals[5].val = 0;
+
+ regvals[6].reg = REG_DIV_BYPASS;
+ regvals[6].val = acq->bypass_clockdiv;
+
+ ret = lwla_write_regs(usb, regvals, G_N_ELEMENTS(regvals));
+ if (ret != SR_OK)
+ return ret;
+
+ return capture_setup(sdi);
+}
+
+/* Start the capture operation on the LWLA device. Beginning with this
+ * function, all USB transfers will be asynchronous until the end of the
+ * acquisition session.
+ */
+SR_PRIV int lwla_start_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ struct acquisition_state *acq;
+ struct regval_pair *regvals;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+ acq = devc->acquisition;
+
+ acq->duration_now = 0;
+ acq->mem_addr_fill = 0;
+ acq->capture_flags = 0;
+
+ libusb_fill_bulk_transfer(acq->xfer_out, usb->devhdl, EP_COMMAND,
+ (unsigned char *)acq->xfer_buf_out, 0,
+ &receive_transfer_out,
+ (struct sr_dev_inst *)sdi, USB_TIMEOUT);
+
+ libusb_fill_bulk_transfer(acq->xfer_in, usb->devhdl, EP_REPLY,
+ (unsigned char *)acq->xfer_buf_in,
+ sizeof acq->xfer_buf_in,
+ &receive_transfer_in,
+ (struct sr_dev_inst *)sdi, USB_TIMEOUT);
+
+ regvals = devc->reg_write_seq;
+
+ regvals[0].reg = REG_CMD_CTRL2;
+ regvals[0].val = 10;
+
+ regvals[1].reg = REG_CMD_CTRL3;
+ regvals[1].val = 1;
+
+ regvals[2].reg = REG_CMD_CTRL4;
+ regvals[2].val = 0;
+
+ regvals[3].reg = REG_CMD_CTRL1;
+ regvals[3].val = 0;
+
+ devc->reg_write_pos = 0;
+ devc->reg_write_len = 4;
+
+ devc->state = STATE_START_CAPTURE;
+
+ return issue_next_write_reg(sdi);
+}
+
+/* Allocate an acquisition state object.
+ */
+SR_PRIV struct acquisition_state *lwla_alloc_acquisition_state(void)
+{
+ struct acquisition_state *acq;
+
+ acq = g_try_new0(struct acquisition_state, 1);
+ if (!acq) {
+ sr_err("Acquisition state malloc failed.");
+ return NULL;
+ }
+
+ acq->xfer_in = libusb_alloc_transfer(0);
+ if (!acq->xfer_in) {
+ sr_err("Transfer malloc failed.");
+ g_free(acq);
+ return NULL;
+ }
+
+ acq->xfer_out = libusb_alloc_transfer(0);
+ if (!acq->xfer_out) {
+ sr_err("Transfer malloc failed.");
+ libusb_free_transfer(acq->xfer_in);
+ g_free(acq);
+ return NULL;
+ }
+
+ return acq;
+}
+
+/* Deallocate an acquisition state object.
+ */
+SR_PRIV void lwla_free_acquisition_state(struct acquisition_state *acq)
+{
+ if (acq) {
+ libusb_free_transfer(acq->xfer_out);
+ libusb_free_transfer(acq->xfer_in);
+ g_free(acq);
+ }
+}
+
+/* USB I/O source callback.
+ */
+SR_PRIV int lwla_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ struct timeval tv;
+ int ret;
+
+ (void)fd;
+
+ sdi = cb_data;
+ devc = sdi->priv;
+ drvc = sdi->driver->priv;
+
+ if (!devc || !drvc)
+ return FALSE;
+
+ /* No timeout: return immediately. */
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ ret = libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx,
+ &tv, NULL);
+ if (ret != 0)
+ sr_err("Event handling failed: %s.", libusb_error_name(ret));
+
+ /* If no event flags are set the timeout must have expired. */
+ if (revents == 0 && devc->state == STATE_STATUS_WAIT) {
+ if (sdi->status == SR_ST_STOPPING)
+ issue_stop_capture(sdi);
+ else
+ request_capture_status(sdi);
+ }
+
+ /* Check if an error occurred on a transfer. */
+ if (devc->transfer_error)
+ end_acquisition(sdi);
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
+ *
+ * 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_SYSCLK_LWLA_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SYSCLK_LWLA_PROTOCOL_H
+
+#define LOG_PREFIX "sysclk-lwla"
+
+#include "lwla.h"
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include <stdint.h>
+#include <glib.h>
+
+/* For now, only the LWLA1034 is supported.
+ */
+#define VENDOR_NAME "SysClk"
+#define MODEL_NAME "LWLA1034"
+
+#define USB_VID_PID "2961.6689"
+#define USB_INTERFACE 0
+#define USB_TIMEOUT 3000 /* ms */
+
+#define NUM_CHANNELS 34
+
+/* Bit mask covering all 34 channels.
+ */
+#define ALL_CHANNELS_MASK (((uint64_t)1 << NUM_CHANNELS) - 1)
+
+/** Unit and packet size for the sigrok logic datafeed.
+ */
+#define UNIT_SIZE ((NUM_CHANNELS + 7) / 8)
+#define PACKET_LENGTH 10000 /* units */
+
+/** Size of the acquisition buffer in device memory units.
+ */
+#define MEMORY_DEPTH (256 * 1024) /* 256k x 36 bit */
+
+/** Number of device memory units (36 bit) to read at a time. Slices of 8
+ * consecutive 36-bit words are mapped to 9 32-bit words each, so the chunk
+ * length should be a multiple of 8 to ensure alignment to slice boundaries.
+ *
+ * Experimentation has shown that reading chunks larger than about 1024 bytes
+ * is unreliable. The threshold seems to relate to the buffer size on the FX2
+ * USB chip: The configured endpoint buffer size is 512, and with double or
+ * triple buffering enabled a multiple of 512 bytes can be kept in fly.
+ *
+ * The vendor software limits reads to 120 words (15 slices, 540 bytes) at
+ * a time. So far, it appears safe to increase this to 224 words (28 slices,
+ * 1008 bytes), thus making the most of two 512 byte buffers.
+ */
+#define READ_CHUNK_LEN (28 * 8)
+
+/** Calculate the required buffer size in 32-bit units for reading a given
+ * number of device memory words. Rounded to a multiple of 8 device words.
+ */
+#define LWLA1034_MEMBUF_LEN(count) (((count) + 7) / 8 * 9)
+
+/** Maximum number of 16-bit words sent at a time during acquisition.
+ * Used for allocating the libusb transfer buffer.
+ */
+#define MAX_ACQ_SEND_WORDS 8 /* 5 for memory read request plus stuffing */
+
+/** Maximum number of 32-bit words received at a time during acquisition.
+ * Round to the next multiple of the endpoint buffer size to avoid nasty
+ * transfer overflow conditions on hiccups.
+ */
+#define MAX_ACQ_RECV_LEN ((READ_CHUNK_LEN / 8 * 9 + 127) / 128 * 128)
+
+/** Maximum length of a register write sequence.
+ */
+#define MAX_REG_WRITE_SEQ_LEN 5
+
+/** Default configured samplerate.
+ */
+#define DEFAULT_SAMPLERATE SR_MHZ(125)
+
+/** Maximum configurable sample count limit.
+ */
+#define MAX_LIMIT_SAMPLES (UINT64_C(1) << 48)
+
+/** Maximum configurable capture duration in milliseconds.
+ */
+#define MAX_LIMIT_MSEC (UINT64_C(1) << 32)
+
+/** LWLA1034 FPGA clock configurations.
+ */
+enum clock_config {
+ CONF_CLOCK_NONE,
+ CONF_CLOCK_INT,
+ CONF_CLOCK_EXT_RISE,
+ CONF_CLOCK_EXT_FALL,
+};
+
+/** Available clock sources.
+ */
+enum clock_source {
+ CLOCK_INTERNAL,
+ CLOCK_EXT_CLK,
+};
+
+/** Available trigger sources.
+ */
+enum trigger_source {
+ TRIGGER_CHANNELS = 0,
+ TRIGGER_EXT_TRG,
+};
+
+/** Available edge choices for the external clock and trigger inputs.
+ */
+enum signal_edge {
+ EDGE_POSITIVE = 0,
+ EDGE_NEGATIVE,
+};
+
+/** LWLA device states.
+ */
+enum device_state {
+ STATE_IDLE = 0,
+
+ STATE_START_CAPTURE,
+
+ STATE_STATUS_WAIT,
+ STATE_STATUS_REQUEST,
+ STATE_STATUS_RESPONSE,
+
+ STATE_STOP_CAPTURE,
+
+ STATE_LENGTH_REQUEST,
+ STATE_LENGTH_RESPONSE,
+
+ STATE_READ_PREPARE,
+ STATE_READ_REQUEST,
+ STATE_READ_RESPONSE,
+ STATE_READ_END,
+};
+
+/** LWLA run-length encoding states.
+ */
+enum rle_state {
+ RLE_STATE_DATA,
+ RLE_STATE_LEN
+};
+
+/** LWLA sample acquisition and decompression state.
+ */
+struct acquisition_state {
+ uint64_t sample;
+ uint64_t run_len;
+
+ /** Maximum number of samples to process. */
+ uint64_t samples_max;
+ /** Number of samples sent to the session bus. */
+ uint64_t samples_done;
+
+ /** Maximum duration of capture, in milliseconds. */
+ uint64_t duration_max;
+ /** Running capture duration since trigger event. */
+ uint64_t duration_now;
+
+ /** Capture memory fill level. */
+ size_t mem_addr_fill;
+
+ size_t mem_addr_done;
+ size_t mem_addr_next;
+ size_t mem_addr_stop;
+
+ size_t out_index;
+
+ struct libusb_transfer *xfer_in;
+ struct libusb_transfer *xfer_out;
+
+ unsigned int capture_flags;
+
+ enum rle_state rle;
+
+ /** Whether to bypass the clock divider. */
+ gboolean bypass_clockdiv;
+
+ /* Payload data buffers for incoming and outgoing transfers. */
+ uint32_t xfer_buf_in[MAX_ACQ_RECV_LEN];
+ uint16_t xfer_buf_out[MAX_ACQ_SEND_WORDS];
+
+ /* Payload buffer for sigrok logic packets. */
+ uint8_t out_packet[PACKET_LENGTH * UNIT_SIZE];
+};
+
+/** Private, per-device-instance driver context.
+ */
+struct dev_context {
+ /** The samplerate selected by the user. */
+ uint64_t samplerate;
+
+ /** The maximimum sampling duration, in milliseconds. */
+ uint64_t limit_msec;
+
+ /** The maximimum number of samples to acquire. */
+ uint64_t limit_samples;
+
+ /** Channels to use. */
+ uint64_t channel_mask;
+
+ uint64_t trigger_mask;
+ uint64_t trigger_edge_mask;
+ uint64_t trigger_values;
+
+ struct acquisition_state *acquisition;
+
+ struct regval_pair reg_write_seq[MAX_REG_WRITE_SEQ_LEN];
+ int reg_write_pos;
+ int reg_write_len;
+
+ enum device_state state;
+
+ /** The currently active clock configuration of the device. */
+ enum clock_config cur_clock_config;
+
+ /** Clock source configuration setting. */
+ enum clock_source cfg_clock_source;
+ /** Clock edge configuration setting. */
+ enum signal_edge cfg_clock_edge;
+
+ /** Trigger source configuration setting. */
+ enum trigger_source cfg_trigger_source;
+ /** Trigger slope configuration setting. */
+ enum signal_edge cfg_trigger_slope;
+
+ /* Indicates that stopping the acquisition is currently in progress. */
+ gboolean stopping_in_progress;
+
+ /* Indicates whether a transfer failed. */
+ gboolean transfer_error;
+};
+
+SR_PRIV struct acquisition_state *lwla_alloc_acquisition_state(void);
+SR_PRIV void lwla_free_acquisition_state(struct acquisition_state *acq);
+
+SR_PRIV int lwla_init_device(const struct sr_dev_inst *sdi);
+SR_PRIV int lwla_convert_trigger(const struct sr_dev_inst *sdi);
+SR_PRIV int lwla_set_clock_config(const struct sr_dev_inst *sdi);
+SR_PRIV int lwla_setup_acquisition(const struct sr_dev_inst *sdi);
+SR_PRIV int lwla_start_acquisition(const struct sr_dev_inst *sdi);
+SR_PRIV int lwla_abort_acquisition(const struct sr_dev_inst *sdi);
+
+SR_PRIV int lwla_receive_data(int fd, int revents, void *cb_data);
+
+#endif /* !LIBSIGROK_HARDWARE_SYSCLK_LWLA_PROTOCOL_H */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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 <stdlib.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_ENERGYMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+SR_PRIV struct sr_dev_driver teleinfo_driver_info;
+static struct sr_dev_driver *di = &teleinfo_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ GSList *devices = NULL, *l;
+ const char *conn = NULL, *serialcomm = NULL;
+ uint8_t buf[292];
+ size_t len;
+ struct sr_config *src;
+
+ len = sizeof(buf);
+
+ 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;
+ if (!serialcomm)
+ serialcomm = "1200/7e1";
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+ if (serial_open(serial, SERIAL_RDONLY | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ sr_info("Probing serial port %s.", conn);
+
+ drvc = di->priv;
+ drvc->instances = NULL;
+ serial_flush(serial);
+
+ /* Let's get a bit of data and see if we can find a packet. */
+ if (serial_stream_detect(serial, buf, &len, len,
+ teleinfo_packet_valid, 3000, 1200) != SR_OK)
+ goto scan_cleanup;
+
+ sr_info("Found device on port %s.", conn);
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "EDF", "Teleinfo", NULL)))
+ goto scan_cleanup;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ goto scan_cleanup;
+ }
+
+ devc->optarif = teleinfo_get_optarif(buf);
+
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+ sdi->priv = devc;
+ sdi->driver = di;
+
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ if (devc->optarif == OPTARIF_BASE) {
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "BASE")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ } else if (devc->optarif == OPTARIF_HC) {
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HP")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HC")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ } else if (devc->optarif == OPTARIF_EJP) {
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HN")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPM")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ } else if (devc->optarif == OPTARIF_BBR) {
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPJB")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPJW")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPJR")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HCJB")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HCJW")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HCJR")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "IINST")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "PAPP")))
+ goto scan_cleanup;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+scan_cleanup:
+ serial_close(serial);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".", devc->limit_samples);
+ break;
+ case SR_CONF_LIMIT_MSEC:
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.", devc->limit_msec);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct sr_serial_dev_inst *serial = sdi->conn;
+ struct dev_context *devc;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ devc->session_cb_data = cb_data;
+
+ /*
+ * Reset the number of samples to take. If we've already collected our
+ * quota, but we start a new session, and don't reset this, we'll just
+ * quit without acquiring any new samples.
+ */
+ devc->num_samples = 0;
+ devc->start_time = g_get_monotonic_time();
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Poll every 50ms, or whenever some data comes in. */
+ serial_source_add(sdi->session, serial, G_IO_IN, 50,
+ teleinfo_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ return std_serial_dev_acquisition_stop(sdi, cb_data,
+ std_serial_dev_close, sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver teleinfo_driver_info = {
+ .name = "teleinfo",
+ .longname = "Teleinfo",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .config_get = NULL,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = std_serial_dev_open,
+ .dev_close = std_serial_dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "protocol.h"
+
+#define STX 0x02
+#define ETX 0x03
+#define EOT 0x04
+#define LF 0x0A
+#define CR 0x0D
+
+static gboolean teleinfo_control_check(char *label, char *data, char control)
+{
+ int sum = 0;
+ while (*label)
+ sum += *label++;
+ sum += ' ';
+ while (*data)
+ sum += *data++;
+ return ((sum & 0x3F) + ' ') == control;
+}
+
+static gint teleinfo_channel_compare(gconstpointer a, gconstpointer b)
+{
+ const struct sr_channel *ch = a;
+ const char *name = b;
+ return strcmp(ch->name, name);
+}
+
+static struct sr_channel *teleinfo_find_channel(struct sr_dev_inst *sdi,
+ const char *name)
+{
+ GSList *elem = g_slist_find_custom(sdi->channels, name,
+ teleinfo_channel_compare);
+ return elem ? elem->data : NULL;
+}
+
+static void teleinfo_send_value(struct sr_dev_inst *sdi, const char *channel_name,
+ float value, int mq, int unit)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct sr_channel *ch;
+
+ devc = sdi->priv;
+ ch = teleinfo_find_channel(sdi, channel_name);
+
+ if (!ch || !ch->enabled)
+ return;
+
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+ analog.channels = g_slist_append(analog.channels, ch);
+ analog.num_samples = 1;
+ analog.mq = mq;
+ analog.unit = unit;
+ analog.data = &value;
+
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->session_cb_data, &packet);
+ g_slist_free(analog.channels);
+}
+
+static void teleinfo_handle_mesurement(struct sr_dev_inst *sdi,
+ const char *label, const char *data,
+ char *optarif)
+{
+ struct dev_context *devc;
+ int v = atoi(data);
+
+ if (!sdi || !(devc = sdi->priv)) {
+ if (optarif && !strcmp(label, "OPTARIF"))
+ strcpy(optarif, data);
+ return;
+ }
+
+ if (!strcmp(label, "ADCO")) {
+ devc->num_samples++;
+ } else if (!strcmp(label, "BASE")) {
+ teleinfo_send_value(sdi, "BASE", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
+ } else if (!strcmp(label, "HCHP")) {
+ teleinfo_send_value(sdi, "HP" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
+ } else if (!strcmp(label, "HCHC")) {
+ teleinfo_send_value(sdi, "HC" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
+ } else if (!strcmp(label, "EJPHN")) {
+ teleinfo_send_value(sdi, "HN" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
+ } else if (!strcmp(label, "EJPHPM")) {
+ teleinfo_send_value(sdi, "HPM" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
+ } else if (!strcmp(label, "BBRHPJB")) {
+ teleinfo_send_value(sdi, "HPJB", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
+ } else if (!strcmp(label, "BBRHPJW")) {
+ teleinfo_send_value(sdi, "HPJW", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
+ } else if (!strcmp(label, "BBRHPJR")) {
+ teleinfo_send_value(sdi, "HPJR", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
+ } else if (!strcmp(label, "BBRHCJB")) {
+ teleinfo_send_value(sdi, "HCJB", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
+ } else if (!strcmp(label, "BBRHCJW")) {
+ teleinfo_send_value(sdi, "HCJW", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
+ } else if (!strcmp(label, "BBRHCJR")) {
+ teleinfo_send_value(sdi, "HCJR", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
+ } else if (!strcmp(label, "IINST")) {
+ teleinfo_send_value(sdi, "IINST", v, SR_MQ_CURRENT, SR_UNIT_AMPERE);
+ } else if (!strcmp(label, "PAPP")) {
+ teleinfo_send_value(sdi, "PAPP", v, SR_MQ_POWER, SR_UNIT_VOLT_AMPERE);
+ }
+}
+
+static gboolean teleinfo_parse_group(struct sr_dev_inst *sdi,
+ const uint8_t *group, char *optarif)
+{
+ char label[9], data[13], control, cr;
+ const char *str = (const char *)group;
+ if (sscanf(str, "\x0A%8s %13s %c%c", label, data, &control, &cr) != 4
+ || cr != CR)
+ return FALSE;
+ if (!teleinfo_control_check(label, data, control))
+ return FALSE;
+ teleinfo_handle_mesurement(sdi, label, data, optarif);
+ return TRUE;
+}
+
+static const uint8_t *teleinfo_parse_data(struct sr_dev_inst *sdi,
+ const uint8_t *buf, int len,
+ char *optarif)
+{
+ const uint8_t *group_start, *group_end;
+
+ group_start = memchr(buf, LF, len);
+ if (!group_start)
+ return NULL;
+
+ group_end = memchr(group_start, CR, len - (group_start - buf));
+ if (!group_end)
+ return NULL;
+
+ teleinfo_parse_group(sdi, group_start, optarif);
+ return group_end + 1;
+}
+
+SR_PRIV int teleinfo_get_optarif(const uint8_t *buf)
+{
+ const uint8_t *ptr = buf;
+ char optarif[5] = { 0 };
+
+ while ((ptr = teleinfo_parse_data(NULL, ptr, 292-(ptr-buf), optarif)));
+ if (!strcmp(optarif, "BASE"))
+ return OPTARIF_BASE;
+ else if (!strcmp(optarif, "HC.."))
+ return OPTARIF_HC;
+ else if (!strcmp(optarif, "EJP."))
+ return OPTARIF_EJP;
+ else if (!strncmp(optarif, "BBR", 3))
+ return OPTARIF_BBR;
+ return OPTARIF_NONE;
+}
+
+SR_PRIV gboolean teleinfo_packet_valid(const uint8_t *buf)
+{
+ return !!teleinfo_get_optarif(buf);
+}
+
+SR_PRIV int teleinfo_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ const uint8_t *ptr, *next_ptr, *end_ptr;
+ int len;
+ int64_t time;
+
+ (void)fd;
+
+ if (!(sdi = cb_data) || !(devc = sdi->priv) || revents != G_IO_IN)
+ return TRUE;
+ serial = sdi->conn;
+
+ /* Try to get as much data as the buffer can hold. */
+ len = TELEINFO_BUF_SIZE - devc->buf_len;
+ len = serial_read(serial, devc->buf + devc->buf_len, len);
+ if (len < 1) {
+ sr_err("Serial port read error: %d.", len);
+ return FALSE;
+ }
+ devc->buf_len += len;
+
+ /* Now look for packets in that data. */
+ ptr = devc->buf;
+ end_ptr = ptr + devc->buf_len;
+ while ((next_ptr = teleinfo_parse_data(sdi, ptr, end_ptr - ptr, NULL)))
+ ptr = next_ptr;
+
+ /* If we have any data left, move it to the beginning of our buffer. */
+ memmove(devc->buf, ptr, end_ptr - ptr);
+ devc->buf_len -= ptr - devc->buf;
+
+ /* If buffer is full and no valid packet was found, wipe buffer. */
+ if (devc->buf_len >= TELEINFO_BUF_SIZE) {
+ devc->buf_len = 0;
+ return FALSE;
+ }
+
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sr_info("Requested number of samples reached.");
+ sdi->driver->dev_acquisition_stop(sdi, devc->session_cb_data);
+ return TRUE;
+ }
+
+ if (devc->limit_msec) {
+ time = (g_get_monotonic_time() - devc->start_time) / 1000;
+ if (time > (int64_t)devc->limit_msec) {
+ sr_info("Requested time limit reached.");
+ sdi->driver->dev_acquisition_stop(sdi, devc->session_cb_data);
+ return TRUE;
+ }
+ }
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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_TELEINFO_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_TELEINFO_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "teleinfo"
+
+enum optarif {
+ OPTARIF_NONE,
+ OPTARIF_BASE,
+ OPTARIF_HC,
+ OPTARIF_EJP,
+ OPTARIF_BBR,
+};
+
+#define TELEINFO_BUF_SIZE 256
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Acquisition settings */
+ uint64_t limit_samples; /**< The sampling limit (in number of samples). */
+ uint64_t limit_msec; /**< The time limit (in milliseconds). */
+ void *session_cb_data; /**< Opaque pointer passed in by the frontend. */
+
+ /* Operational state */
+ enum optarif optarif; /**< The device mode (which mesures are reported) */
+ uint64_t num_samples; /**< The number of already received samples. */
+ int64_t start_time; /**< The time at which sampling started. */
+
+ /* Temporary state across callbacks */
+ uint8_t buf[TELEINFO_BUF_SIZE];
+ int buf_len;
+};
+
+SR_PRIV gboolean teleinfo_packet_valid(const uint8_t *buf);
+SR_PRIV int teleinfo_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int teleinfo_get_optarif(const uint8_t *buf);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <string.h>
+#include "protocol.h"
+
+#define SERIALCOMM "115200/8n1"
+
+SR_PRIV struct sr_dev_driver testo_driver_info;
+static struct sr_dev_driver *di = &testo_driver_info;
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
+
+static const int32_t scanopts[] = {
+ SR_CONF_CONN,
+};
+
+static const int32_t devopts[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_CONTINUOUS,
+};
+
+unsigned char TESTO_x35_REQUEST[] = { 0x12, 0, 0, 0, 1, 1, 0x55, 0xd1, 0xb7 };
+struct testo_model models[] = {
+ { "435", 9, TESTO_x35_REQUEST },
+};
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_config *src;
+ struct sr_dev_inst *sdi;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_device_descriptor des;
+ libusb_device **devlist;
+ struct libusb_device_handle *hdl;
+ GSList *conn_devices, *devices, *l;
+ int devcnt, ret, i;
+ const char *str;
+ char manufacturer[64], product[64];
+
+ devices = NULL;
+ drvc = di->priv;
+ drvc->instances = NULL;
+
+ conn_devices = NULL;
+ for (l = options; l; l = l->next) {
+ src = l->data;
+ if (src->key != SR_CONF_CONN)
+ continue;
+ str = g_variant_get_string(src->data, NULL);
+ conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, str);
+ }
+
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if (conn_devices) {
+ usb = NULL;
+ for (l = conn_devices; l; l = l->next) {
+ usb = l->data;
+ if (usb->bus == libusb_get_bus_number(devlist[i])
+ && usb->address == libusb_get_device_address(devlist[i]))
+ break;
+ }
+ if (!l)
+ /* This device matched none of the ones that
+ * matched the conn specification. */
+ continue;
+ }
+
+ if ((ret = libusb_get_device_descriptor( devlist[i], &des)) != 0) {
+ sr_warn("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ if ((ret = libusb_open(devlist[i], &hdl)) < 0)
+ continue;
+
+ manufacturer[0] = product[0] = '\0';
+ if (des.iManufacturer && (ret = libusb_get_string_descriptor_ascii(
+ hdl, des.iManufacturer, (unsigned char *) manufacturer,
+ sizeof(manufacturer))) < 0) {
+ sr_warn("Failed to get manufacturer string descriptor: %s.",
+ libusb_error_name(ret));
+ }
+ if (des.iProduct && (ret = libusb_get_string_descriptor_ascii(
+ hdl, des.iProduct, (unsigned char *) product,
+ sizeof(product))) < 0) {
+ sr_warn("Failed to get product string descriptor: %s.",
+ libusb_error_name(ret));
+ }
+ libusb_close(hdl);
+
+ if (strncmp(manufacturer, "testo", 5))
+ continue;
+
+ /* Hardcode the 435 for now.*/
+ if (strcmp(product, "testo 435/635/735"))
+ continue;
+
+ devcnt = g_slist_length(drvc->instances);
+ sdi = sr_dev_inst_new(devcnt, SR_ST_INACTIVE, "Testo",
+ "435/635/735", NULL);
+ sdi->driver = di;
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+ libusb_get_device_address(devlist[i]), NULL);
+ devc = g_malloc(sizeof(struct dev_context));
+ devc->model = &models[0];
+ devc->limit_msec = 0;
+ devc->limit_samples = 0;
+ sdi->priv = devc;
+ if (testo_probe_channels(sdi) != SR_OK)
+ continue;
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ }
+ libusb_free_device_list(devlist, 1);
+ g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc = di->priv;
+ struct sr_usb_dev_inst *usb;
+ libusb_device **devlist;
+ int ret, i;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if (libusb_get_bus_number(devlist[i]) != usb->bus
+ || libusb_get_device_address(devlist[i]) != usb->address)
+ continue;
+ if ((ret = libusb_open(devlist[i], &usb->devhdl))) {
+ sr_err("Failed to open device: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ break;
+ }
+ libusb_free_device_list(devlist, 1);
+ if (!devlist[i]) {
+ sr_err("Device not found.");
+ return SR_ERR;
+ }
+
+ if (libusb_has_capability(LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER)) {
+ if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
+ if ((ret = libusb_detach_kernel_driver(usb->devhdl, 0)) < 0) {
+ sr_err("Failed to detach kernel driver: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ }
+ }
+
+ if ((ret = libusb_claim_interface(usb->devhdl, 0))) {
+ sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+ if (!usb->devhdl)
+ /* Nothing to do. */
+ return SR_OK;
+
+ libusb_release_interface(usb->devhdl, 0);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ int ret;
+ struct drv_context *drvc;
+
+ if (!(drvc = di->priv))
+ return SR_OK;
+
+ ret = dev_clear();
+ g_free(drvc);
+ di->priv = NULL;
+
+ return ret;
+}
+
+static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct sr_usb_dev_inst *usb;
+ char str[128];
+
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_CONN:
+ if (!sdi || !sdi->conn)
+ return SR_ERR_ARG;
+ usb = sdi->conn;
+ snprintf(str, 128, "%d.%d", usb->bus, usb->address);
+ *data = g_variant_new_string(str);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ gint64 now;
+ int ret;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ devc = sdi->priv;
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_LIMIT_MSEC:
+ devc->limit_msec = g_variant_get_uint64(data);
+ now = g_get_monotonic_time() / 1000;
+ devc->end_time = now + devc->limit_msec;
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ scanopts, ARRAY_SIZE(scanopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static void receive_data(struct sr_dev_inst *sdi, unsigned char *data, int len)
+{
+ struct dev_context *devc;
+ int packet_size;
+ uint16_t crc;
+
+ devc = sdi->priv;
+
+ if (devc->reply_size + len > MAX_REPLY_SIZE) {
+ /* Something went very wrong. */
+ sr_dbg("Receive buffer overrun.");
+ devc->reply_size = 0;
+ return;
+ }
+
+ memcpy(devc->reply + devc->reply_size, data, len);
+ devc->reply_size += len;
+ /* Sixth byte contains the length of the packet. */
+ if (devc->reply_size < 7)
+ return;
+
+ packet_size = 7 + devc->reply[6] * 7 + 2;
+ if (devc->reply_size < packet_size)
+ return;
+
+ if (!testo_check_packet_prefix(devc->reply, devc->reply_size))
+ return;
+
+ crc = crc16_mcrf4xx(0xffff, devc->reply, devc->reply_size - 2);
+ if (crc == RL16(&devc->reply[devc->reply_size - 2])) {
+ testo_receive_packet(sdi);
+ devc->num_samples++;
+ } else {
+ sr_dbg("Packet has invalid CRC.");
+ }
+
+ devc->reply_size = 0;
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
+ dev_acquisition_stop(sdi, devc->cb_data);
+ else
+ testo_request_packet(sdi);
+
+}
+
+SR_PRIV void receive_transfer(struct libusb_transfer *transfer)
+{
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ int ret;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+ if (transfer == devc->out_transfer)
+ /* Just the command acknowledgement. */
+ return;
+
+ if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
+ /* USB device was unplugged. */
+ dev_acquisition_stop(sdi, devc->cb_data);
+ } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
+ /* First two bytes in any transfer are FTDI status bytes. */
+ if (transfer->actual_length > 2)
+ receive_data(sdi, transfer->buffer + 2, transfer->actual_length - 2);
+ }
+ /* Anything else is either an error or a timeout, which is fine:
+ * we were just going to send another transfer request anyway. */
+
+ if (sdi->status == SR_ST_ACTIVE) {
+ if ((ret = libusb_submit_transfer(transfer) != 0)) {
+ sr_err("Unable to resubmit transfer: %s.",
+ libusb_error_name(ret));
+ g_free(transfer->buffer);
+ libusb_free_transfer(transfer);
+ dev_acquisition_stop(sdi, devc->cb_data);
+ }
+ } else {
+ /* This was the last transfer we're going to receive, so
+ * clean up now. */
+ g_free(transfer->buffer);
+ libusb_free_transfer(transfer);
+ }
+}
+
+static int handle_events(int fd, int revents, void *cb_data)
+{
+ struct dev_context *devc;
+ struct drv_context *drvc = di->priv;
+ struct sr_datafeed_packet packet;
+ struct sr_dev_inst *sdi;
+ struct timeval tv;
+ gint64 now;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+ devc = sdi->priv;
+
+ if (devc->limit_msec) {
+ now = g_get_monotonic_time() / 1000;
+ if (now > devc->end_time)
+ dev_acquisition_stop(sdi, devc->cb_data);
+ }
+
+ if (sdi->status == SR_ST_STOPPING) {
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+
+ dev_close(sdi);
+
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+ }
+
+ memset(&tv, 0, sizeof(struct timeval));
+ libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
+ NULL);
+
+ return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_transfer *transfer;
+ int ret;
+ unsigned char *buf;
+
+ drvc = di->priv;
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+ devc->cb_data = cb_data;
+ devc->end_time = 0;
+ devc->num_samples = 0;
+ devc->reply_size = 0;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ usb_source_add(sdi->session, drvc->sr_ctx, 100,
+ handle_events, (void *)sdi);
+
+ if (testo_set_serial_params(usb) != SR_OK)
+ return SR_ERR;
+
+ devc->out_transfer = libusb_alloc_transfer(0);
+ if (testo_request_packet(sdi) != SR_OK)
+ return SR_ERR;
+
+ buf = g_try_malloc(MAX_REPLY_SIZE);
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, usb->devhdl, EP_IN, buf,
+ MAX_REPLY_SIZE, receive_transfer, (void *)sdi, 100);
+ if ((ret = libusb_submit_transfer(transfer) != 0)) {
+ sr_err("Unable to submit transfer: %s.", libusb_error_name(ret));
+ libusb_free_transfer(transfer);
+ g_free(buf);
+ return SR_ERR;
+ }
+ devc->reply_size = 0;
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ (void)cb_data;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ sdi->status = SR_ST_STOPPING;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver testo_driver_info = {
+ .name = "testo",
+ .longname = "Testo",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = 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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <string.h>
+#include "protocol.h"
+
+SR_PRIV int testo_set_serial_params(struct sr_usb_dev_inst *usb)
+{
+ int ret;
+
+ if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_BAUDRATE,
+ FTDI_BAUDRATE_115200, FTDI_INDEX, NULL, 0, 10)) < 0) {
+ sr_err("Failed to set baudrate: %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_PARAMS,
+ FTDI_PARAMS_8N1, FTDI_INDEX, NULL, 0, 10)) < 0) {
+ sr_err("Failed to set comm parameters: %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_FLOWCTRL,
+ FTDI_FLOW_NONE, FTDI_INDEX, NULL, 0, 10)) < 0) {
+ sr_err("Failed to set flow control: %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_MODEMCTRL,
+ FTDI_MODEM_ALLHIGH, FTDI_INDEX, NULL, 0, 10)) < 0) {
+ sr_err("Failed to set modem control: %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+
+/* Due to the modular nature of the Testo hardware, you can't assume
+ * which measurements the device will supply. Fetch a single result
+ * set synchronously to see which measurements it has. */
+SR_PRIV int testo_probe_channels(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ struct sr_channel *ch;
+ int unit, packet_len, len, i;
+ unsigned char packet[MAX_REPLY_SIZE], buf[MAX_REPLY_SIZE];
+ char *probe_name;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ sr_dbg("Probing for channels.");
+ if (sdi->driver->dev_open(sdi) != SR_OK)
+ return SR_ERR;
+ if (testo_set_serial_params(usb) != SR_OK)
+ return SR_ERR;
+
+ /* Flush anything buffered from a previous run. */
+ do {
+ libusb_bulk_transfer(usb->devhdl, EP_IN, buf, MAX_REPLY_SIZE, &len, 10);
+ } while (len > 2);
+
+ if (libusb_bulk_transfer(usb->devhdl, EP_OUT, devc->model->request,
+ devc->model->request_size, &devc->reply_size, 10) < 0)
+ return SR_ERR;
+
+ packet_len = 0;
+ while(TRUE) {
+ if (libusb_bulk_transfer(usb->devhdl, EP_IN, buf, MAX_REPLY_SIZE,
+ &len, 250) < 0)
+ return SR_ERR;
+ if (len == 2)
+ /* FTDI cruft */
+ continue;
+ if (packet_len + len - 2 > MAX_REPLY_SIZE)
+ return SR_ERR;
+
+ memcpy(packet + packet_len, buf + 2, len - 2);
+ packet_len += len - 2;
+ if (packet_len < 5)
+ /* Not even enough to check prefix yet. */
+ continue;
+
+ if (!testo_check_packet_prefix(packet, packet_len)) {
+ /* Tail end of some previous data, drop it. */
+ packet_len = 0;
+ continue;
+ }
+
+ if (packet_len >= 7 + packet[6] * 7 + 2)
+ /* Got a complete packet. */
+ break;
+ }
+ sdi->driver->dev_close(sdi);
+
+ if (packet[6] > MAX_CHANNELS) {
+ sr_err("Device says it has %d channels!", packet[6]);
+ return SR_ERR;
+ }
+
+ for (i = 0; i < packet[6]; i++) {
+ unit = packet[7 + i * 7 + 4];
+ devc->channel_units[i] = unit;
+ switch (unit) {
+ case 1:
+ probe_name = "Temperature";
+ break;
+ case 3:
+ probe_name = "Humidity";
+ break;
+ case 5:
+ probe_name = "Windspeed";
+ break;
+ case 24:
+ probe_name = "Pressure";
+ break;
+ default:
+ sr_dbg("Unsupported measurement unit %d", unit);
+ return SR_ERR;
+ }
+ ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, probe_name);
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+ devc->num_channels = packet[6];
+ sr_dbg("Found %d channel%s.", devc->num_channels,
+ devc->num_channels > 1 ? "s" : "");
+
+ return SR_OK;
+}
+
+SR_PRIV int testo_request_packet(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int ret;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ libusb_fill_bulk_transfer(devc->out_transfer, usb->devhdl, EP_OUT,
+ devc->model->request, devc->model->request_size,
+ receive_transfer, (void *)sdi, 100);
+ if ((ret = libusb_submit_transfer(devc->out_transfer) != 0)) {
+ sr_err("Failed to request packet: %s.", libusb_error_name(ret));
+ sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+ devc->cb_data);
+ return SR_ERR;
+ }
+ sr_dbg("Requested new packet.");
+
+ return SR_OK;
+}
+
+/* Check if the packet is well-formed. This matches packets for the
+ * Testo 175/177/400/650/950/435/635/735/445/645/945/946/545. */
+SR_PRIV gboolean testo_check_packet_prefix(unsigned char *buf, int len)
+{
+ int i;
+ unsigned char check[] = { 0x21, 0, 0, 0, 1 };
+
+ if (len < 5)
+ return FALSE;
+
+ for (i = 0; i < 5; i++) {
+ if (buf[i] != check[i]) {
+ sr_dbg("Packet has invalid prefix.");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+SR_PRIV uint16_t crc16_mcrf4xx(uint16_t crc, uint8_t *data, size_t len)
+{
+ int i;
+
+ if (!data || !len)
+ return crc;
+
+ while (len--) {
+ crc ^= *data++;
+ for (i = 0; i < 8; i++) {
+ if (crc & 1)
+ crc = (crc >> 1) ^ 0x8408;
+ else
+ crc = (crc >> 1);
+ }
+ }
+
+ return crc;
+}
+
+static float binary32_le_to_float(unsigned char *buf)
+{
+ GFloatIEEE754 f;
+
+ f.v_float = 0;
+ f.mpn.sign = (buf[3] & 0x80) ? 1 : 0;
+ f.mpn.biased_exponent = (buf[3] << 1) | (buf[2] >> 7);
+ f.mpn.mantissa = buf[0] | (buf[1] << 8) | ((buf[2] & 0x7f) << 16);
+
+ return f.v_float;
+}
+
+SR_PRIV void testo_receive_packet(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct sr_channel *ch;
+ GString *dbg;
+ float value;
+ int i;
+ unsigned char *buf;
+
+ devc = sdi->priv;
+ sr_dbg("Got %d-byte packet.", devc->reply_size);
+
+ if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+ dbg = g_string_sized_new(128);
+ g_string_printf(dbg, "Packet:");
+ for (i = 0; i < devc->reply_size; i++)
+ g_string_append_printf(dbg, " %.2x", devc->reply[i]);
+ sr_spew("%s", dbg->str);
+ g_string_free(dbg, TRUE);
+ }
+
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ analog.num_samples = 1;
+ analog.mqflags = 0;
+ analog.data = &value;
+ /* Decode 7-byte values */
+ for (i = 0; i < devc->reply[6]; i++) {
+ buf = devc->reply + 7 + i * 7;
+ value = binary32_le_to_float(buf);
+ switch (buf[4]) {
+ case 1:
+ analog.mq = SR_MQ_TEMPERATURE;
+ analog.unit = SR_UNIT_CELSIUS;
+ break;
+ case 3:
+ analog.mq = SR_MQ_RELATIVE_HUMIDITY;
+ analog.unit = SR_UNIT_HUMIDITY_293K;
+ break;
+ case 5:
+ analog.mq = SR_MQ_WIND_SPEED;
+ analog.unit = SR_UNIT_METER_SECOND;
+ break;
+ case 24:
+ analog.mq = SR_MQ_PRESSURE;
+ analog.unit = SR_UNIT_HECTOPASCAL;
+ break;
+ default:
+ sr_dbg("Unsupported measurement unit %d.", buf[4]);
+ return;
+ }
+
+ /* Match this measurement with its channel. */
+ for (i = 0; i < devc->num_channels; i++) {
+ if (devc->channel_units[i] == buf[4])
+ break;
+ }
+ if (i == devc->num_channels) {
+ /* Shouldn't happen. */
+ sr_err("Some channel hotswapped in!");
+ return;
+ }
+ ch = g_slist_nth_data(sdi->channels, i);
+ analog.channels = g_slist_append(NULL, ch);
+ sr_session_send(sdi, &packet);
+ g_slist_free(analog.channels);
+ }
+}
+
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_TESTO_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_TESTO_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "testo"
+
+#define MAX_REPLY_SIZE 128
+#define MAX_CHANNELS 16
+
+/* FTDI commands */
+#define FTDI_SET_MODEMCTRL 0x01
+#define FTDI_SET_FLOWCTRL 0x02
+#define FTDI_SET_BAUDRATE 0x03
+#define FTDI_SET_PARAMS 0x04
+/* FTDI command values */
+#define FTDI_BAUDRATE_115200 0x001a
+#define FTDI_PARAMS_8N1 0x0008
+#define FTDI_FLOW_NONE 0x0008
+#define FTDI_MODEM_ALLHIGH 0x0303
+#define FTDI_INDEX 0x0000
+/* FTDI USB stuff */
+#define EP_IN 1 | LIBUSB_ENDPOINT_IN
+#define EP_OUT 2 | LIBUSB_ENDPOINT_OUT
+
+struct testo_model {
+ char *name;
+ int request_size;
+ unsigned char *request;
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Model-specific information */
+ struct testo_model *model;
+
+ /* Acquisition settings */
+ uint64_t limit_msec;
+ uint64_t limit_samples;
+ void *cb_data;
+
+ /* Operational state */
+ gint64 end_time;
+ uint64_t num_samples;
+ uint8_t channel_units[MAX_CHANNELS];
+ int num_channels;
+
+ /* Temporary state across callbacks */
+ struct libusb_transfer *out_transfer;
+ unsigned char reply[MAX_REPLY_SIZE];
+ int reply_size;
+};
+
+SR_PRIV int testo_set_serial_params(struct sr_usb_dev_inst *usb);
+SR_PRIV int testo_probe_channels(struct sr_dev_inst *sdi);
+SR_PRIV void receive_transfer(struct libusb_transfer *transfer);
+SR_PRIV int testo_request_packet(const struct sr_dev_inst *sdi);
+SR_PRIV gboolean testo_check_packet_prefix(unsigned char *buf, int len);
+SR_PRIV uint16_t crc16_mcrf4xx(uint16_t crc, uint8_t *data, size_t len);
+SR_PRIV void testo_receive_packet(const struct sr_dev_inst *sdi);
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <fcntl.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+#define SERIALCOMM "9600/8e1"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_SOUNDLEVELMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_CONTINUOUS,
+};
+
+SR_PRIV struct sr_dev_driver tondaj_sl_814_driver_info;
+static struct sr_dev_driver *di = &tondaj_sl_814_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_config *src;
+ struct sr_channel *ch;
+ GSList *devices, *l;
+ const char *conn, *serialcomm;
+ struct sr_serial_dev_inst *serial;
+
+ drvc = di->priv;
+ drvc->instances = NULL;
+
+ devices = NULL;
+
+ conn = serialcomm = NULL;
+ for (l = options; l; l = l->next) {
+ if (!(src = l->data)) {
+ sr_err("Invalid option data, skipping.");
+ continue;
+ }
+ 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;
+ default:
+ sr_err("Unknown option %d, skipping.", src->key);
+ break;
+ }
+ }
+ if (!conn)
+ return NULL;
+ if (!serialcomm)
+ serialcomm = SERIALCOMM;
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Tondaj",
+ "SL-814", NULL))) {
+ sr_err("Failed to create device instance.");
+ return NULL;
+ }
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+
+ sdi->priv = devc;
+ sdi->driver = di;
+ ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1");
+ if (!ch) {
+ sr_err("Failed to create channel.");
+ return NULL;
+ }
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+
+ switch (id) {
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc = sdi->priv;
+ devc->cb_data = cb_data;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Poll every 500ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 500,
+ tondaj_sl_814_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver tondaj_sl_814_driver_info = {
+ .name = "tondaj-sl-814",
+ .longname = "Tondaj SL-814",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .config_get = NULL,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = std_serial_dev_open,
+ .dev_close = std_serial_dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+/* States */
+enum {
+ SEND_INIT,
+ GET_INIT_REPLY,
+ SEND_PACKET_REQUEST,
+ GET_PACKET,
+};
+
+static void parse_packet(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog)
+{
+ gboolean is_a, is_fast;
+ uint16_t intval;
+ uint8_t level = 0, level_bits;
+
+ /* Byte 0 [7:7]: 0 = A, 1 = C */
+ is_a = ((buf[0] & (1 << 7)) == 0);
+
+ /* Byte 0 [6:6]: Unknown/unused? */
+
+ /* Byte 0 [5:4]: Level (00 = 40, 01 = 60, 10 = 80, 11 = 100) */
+ level_bits = (buf[0] >> 4) & 0x03;
+ if (level_bits == 0)
+ level = 40;
+ else if (level_bits == 1)
+ level = 60;
+ else if (level_bits == 2)
+ level = 80;
+ else if (level_bits == 3)
+ level = 100;
+
+ /* Byte 0 [3:3]: 0 = fast, 1 = slow */
+ is_fast = ((buf[0] & (1 << 3)) == 0);
+
+ /* Byte 0 [2:0]: value[10..8] */
+ /* Byte 1 [7:0]: value[7..0] */
+ intval = (buf[0] & 0x7) << 8;
+ intval |= buf[1];
+
+ *floatval = (float)intval;
+
+ /* The value on the display always has one digit after the comma. */
+ *floatval /= 10;
+
+ analog->mq = SR_MQ_SOUND_PRESSURE_LEVEL;
+ analog->unit = SR_UNIT_DECIBEL_SPL;
+
+ if (is_a)
+ analog->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A;
+ else
+ analog->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C;
+
+ if (is_fast)
+ analog->mqflags |= SR_MQFLAG_SPL_TIME_WEIGHT_F;
+ else
+ analog->mqflags |= SR_MQFLAG_SPL_TIME_WEIGHT_S;
+
+ /* TODO: How to handle level? */
+ (void)level;
+}
+
+static void decode_packet(struct sr_dev_inst *sdi)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct dev_context *devc;
+ float floatval;
+
+ devc = sdi->priv;
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+
+ parse_packet(devc->buf, &floatval, &analog);
+
+ /* Send a sample packet with one analog value. */
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+ analog.data = &floatval;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+
+ devc->num_samples++;
+}
+
+int tondaj_sl_814_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ uint8_t buf[3];
+ int ret;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+ serial = sdi->conn;
+ devc = sdi->priv;
+
+ /* TODO: Parts of this code need to be improved later. */
+
+ /* State machine. */
+ if (devc->state == SEND_INIT) {
+ /* On the first run, send the "init" command. */
+ buf[0] = 0x10;
+ buf[1] = 0x04;
+ buf[2] = 0x0d;
+ sr_spew("Sending init command: %02x %02x %02x.",
+ buf[0], buf[1], buf[2]);
+ if ((ret = serial_write(serial, buf, 3)) < 0) {
+ sr_err("Error sending init command: %d.", ret);
+ return FALSE;
+ }
+ devc->state = GET_INIT_REPLY;
+ } else if (devc->state == GET_INIT_REPLY) {
+ /* If we just sent the "init" command, get its reply. */
+ if ((ret = serial_read(serial, buf, 2)) < 0) {
+ sr_err("Error reading init reply: %d.", ret);
+ return FALSE;
+ }
+ sr_spew("Received init reply: %02x %02x.", buf[0], buf[1]);
+ /* Expected reply: 0x05 0x0d */
+ if (buf[0] != 0x05 || buf[1] != 0x0d) {
+ sr_err("Received incorrect init reply, retrying.");
+ devc->state = SEND_INIT;
+ return TRUE;
+ }
+ devc->state = SEND_PACKET_REQUEST;
+ } else if (devc->state == SEND_PACKET_REQUEST) {
+ /* Request a packet (send 0x30 ZZ 0x0d). */
+ buf[0] = 0x30;
+ buf[1] = 0x00; /* ZZ */
+ buf[2] = 0x0d;
+ sr_spew("Sending data request command: %02x %02x %02x.",
+ buf[0], buf[1], buf[2]);
+ if ((ret = serial_write(serial, buf, 3)) < 0) {
+ sr_err("Error sending request command: %d.", ret);
+ return FALSE;
+ }
+ devc->buflen = 0;
+ devc->state = GET_PACKET;
+ } else if (devc->state == GET_PACKET) {
+ /* Read a packet from the device. */
+ ret = serial_read(serial, devc->buf + devc->buflen,
+ 4 - devc->buflen);
+ if (ret < 0) {
+ sr_err("Error reading packet: %d.", ret);
+ return TRUE;
+ }
+
+ devc->buflen += ret;
+
+ /* Didn't receive all 4 bytes, yet. */
+ if (devc->buflen != 4)
+ return TRUE;
+
+ sr_spew("Received packet: %02x %02x %02x %02x.", devc->buf[0],
+ devc->buf[1], devc->buf[2], devc->buf[3]);
+
+ /* Expected reply: AA BB ZZ+1 0x0d */
+ if (devc->buf[2] != 0x01 || devc->buf[3] != 0x0d) {
+ sr_err("Received incorrect request reply, retrying.");
+ devc->state = SEND_PACKET_REQUEST;
+ return TRUE;
+ }
+
+ decode_packet(sdi);
+
+ devc->state = SEND_PACKET_REQUEST;
+ } else {
+ sr_err("Invalid state: %d.", devc->state);
+ return FALSE;
+ }
+
+ /* Stop acquisition if we acquired enough samples. */
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sr_info("Requested number of samples reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBSIGROK_HARDWARE_TONDAJ_SL_814_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_TONDAJ_SL_814_PROTOCOL_H
+
+#include <stdint.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "tondaj-sl-814"
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /** The current sampling limit (in number of samples). */
+ uint64_t limit_samples;
+
+ /** The current sampling limit (in ms). */
+ uint64_t limit_msec;
+
+ /** Opaque pointer passed in by the frontend. */
+ void *cb_data;
+
+ /** The current number of already received samples. */
+ uint64_t num_samples;
+
+ int state;
+
+ uint8_t buf[4];
+ uint8_t buflen;
+};
+
+SR_PRIV int tondaj_sl_814_receive_data(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+#define UNI_T_UT_D04_NEW "1a86.e008"
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+SR_PRIV struct sr_dev_driver tecpel_dmm_8061_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut60a_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut60e_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut60g_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut61b_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut61c_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut61d_driver_info;
+SR_PRIV struct sr_dev_driver uni_t_ut61e_driver_info;
+SR_PRIV struct sr_dev_driver voltcraft_vc820_driver_info;
+SR_PRIV struct sr_dev_driver voltcraft_vc830_driver_info;
+SR_PRIV struct sr_dev_driver voltcraft_vc840_driver_info;
+SR_PRIV struct sr_dev_driver tenma_72_7745_driver_info;
+SR_PRIV struct sr_dev_driver tenma_72_7750_driver_info;
+
+SR_PRIV struct dmm_info udmms[] = {
+ {
+ "Tecpel", "DMM-8061", 2400,
+ FS9721_PACKET_SIZE,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_00_temp_c,
+ &tecpel_dmm_8061_driver_info, receive_data_TECPEL_DMM_8061,
+ },
+ {
+ "UNI-T", "UT60A", 2400,
+ FS9721_PACKET_SIZE,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ NULL,
+ &uni_t_ut60a_driver_info, receive_data_UNI_T_UT60A,
+ },
+ {
+ "UNI-T", "UT60E", 2400,
+ FS9721_PACKET_SIZE,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_00_temp_c,
+ &uni_t_ut60e_driver_info, receive_data_UNI_T_UT60E,
+ },
+ {
+ /* The baudrate is actually 19230, see "Note 1" below. */
+ "UNI-T", "UT60G", 19200,
+ ES519XX_11B_PACKET_SIZE,
+ sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
+ NULL,
+ &uni_t_ut60g_driver_info, receive_data_UNI_T_UT60G,
+ },
+ {
+ "UNI-T", "UT61B", 2400,
+ FS9922_PACKET_SIZE,
+ sr_fs9922_packet_valid, sr_fs9922_parse,
+ NULL,
+ &uni_t_ut61b_driver_info, receive_data_UNI_T_UT61B,
+ },
+ {
+ "UNI-T", "UT61C", 2400,
+ FS9922_PACKET_SIZE,
+ sr_fs9922_packet_valid, sr_fs9922_parse,
+ NULL,
+ &uni_t_ut61c_driver_info, receive_data_UNI_T_UT61C,
+ },
+ {
+ "UNI-T", "UT61D", 2400,
+ FS9922_PACKET_SIZE,
+ sr_fs9922_packet_valid, sr_fs9922_parse,
+ NULL,
+ &uni_t_ut61d_driver_info, receive_data_UNI_T_UT61D,
+ },
+ {
+ /* The baudrate is actually 19230, see "Note 1" below. */
+ "UNI-T", "UT61E", 19200,
+ ES519XX_14B_PACKET_SIZE,
+ sr_es519xx_19200_14b_packet_valid, sr_es519xx_19200_14b_parse,
+ NULL,
+ &uni_t_ut61e_driver_info, receive_data_UNI_T_UT61E,
+ },
+ {
+ "Voltcraft", "VC-820", 2400,
+ FS9721_PACKET_SIZE,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ NULL,
+ &voltcraft_vc820_driver_info, receive_data_VOLTCRAFT_VC820,
+ },
+ {
+ /*
+ * Note: The VC830 doesn't set the 'volt' and 'diode' bits of
+ * the FS9922 protocol. Instead, it only sets the user-defined
+ * bit "z1" to indicate "diode mode" and "voltage".
+ */
+ "Voltcraft", "VC-830", 2400,
+ FS9922_PACKET_SIZE,
+ sr_fs9922_packet_valid, sr_fs9922_parse,
+ &sr_fs9922_z1_diode,
+ &voltcraft_vc830_driver_info, receive_data_VOLTCRAFT_VC830,
+ },
+ {
+ "Voltcraft", "VC-840", 2400,
+ FS9721_PACKET_SIZE,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_00_temp_c,
+ &voltcraft_vc840_driver_info, receive_data_VOLTCRAFT_VC840,
+ },
+ {
+ "Tenma", "72-7745", 2400,
+ FS9721_PACKET_SIZE,
+ sr_fs9721_packet_valid, sr_fs9721_parse,
+ sr_fs9721_00_temp_c,
+ &tenma_72_7745_driver_info, receive_data_TENMA_72_7745,
+ },
+ {
+ /* The baudrate is actually 19230, see "Note 1" below. */
+ "Tenma", "72-7750", 19200,
+ ES519XX_11B_PACKET_SIZE,
+ sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
+ NULL,
+ &tenma_72_7750_driver_info, receive_data_TENMA_72_7750,
+ },
+};
+
+/*
+ * Note 1: The actual baudrate of the Cyrustek ES519xx chip used in this DMM
+ * is 19230. However, the WCH CH9325 chip (UART to USB/HID) used in (some
+ * versions of) the UNI-T UT-D04 cable doesn't support 19230 baud. It only
+ * supports 19200, and setting an unsupported baudrate will result in the
+ * default of 2400 being used (which will not work with this DMM, of course).
+ */
+
+static int dev_clear(int dmm)
+{
+ return std_dev_clear(udmms[dmm].di, NULL);
+}
+
+static int init(struct sr_context *sr_ctx, int dmm)
+{
+ sr_dbg("Selected '%s' subdriver.", udmms[dmm].di->name);
+
+ return std_init(sr_ctx, udmms[dmm].di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options, int dmm)
+{
+ GSList *usb_devices, *devices, *l;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ struct sr_config *src;
+ struct sr_channel *ch;
+ const char *conn;
+
+ drvc = udmms[dmm].di->priv;
+
+ 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;
+
+ devices = NULL;
+ if (!(usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn))) {
+ g_slist_free_full(usb_devices, g_free);
+ return NULL;
+ }
+
+ for (l = usb_devices; l; l = l->next) {
+ usb = l->data;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+
+ devc->first_run = TRUE;
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
+ udmms[dmm].vendor, udmms[dmm].device, NULL))) {
+ sr_err("sr_dev_inst_new returned NULL.");
+ return NULL;
+ }
+ sdi->priv = devc;
+ sdi->driver = udmms[dmm].di;
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = usb;
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ }
+
+ return devices;
+}
+
+static GSList *dev_list(int dmm)
+{
+ return ((struct drv_context *)(udmms[dmm].di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi, int dmm)
+{
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ int ret;
+
+ drvc = udmms[dmm].di->priv;
+ usb = sdi->conn;
+
+ if ((ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb)) == SR_OK)
+ sdi->status = SR_ST_ACTIVE;
+
+ return ret;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ /* TODO */
+
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(int dmm)
+{
+ return dev_clear(dmm);
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ devc = sdi->priv;
+
+ switch (id) {
+ case SR_CONF_LIMIT_MSEC:
+ if (g_variant_get_uint64(data) == 0) {
+ sr_err("Time limit cannot be 0.");
+ return SR_ERR;
+ }
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ if (g_variant_get_uint64(data) == 0) {
+ sr_err("Sample limit cannot be 0.");
+ return SR_ERR;
+ }
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data, int dmm)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ devc->cb_data = cb_data;
+
+ devc->starttime = g_get_monotonic_time();
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(sdi, LOG_PREFIX);
+
+ sr_session_source_add(sdi->session, 0, 0, 10 /* poll_timeout */,
+ udmms[dmm].receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct sr_datafeed_packet packet;
+
+ (void)cb_data;
+
+ sr_dbg("Stopping acquisition.");
+
+ /* Send end packet to the session bus. */
+ sr_dbg("Sending SR_DF_END.");
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+
+ sr_session_source_remove(sdi->session, 0);
+
+ return SR_OK;
+}
+
+/* Driver-specific API function wrappers */
+#define HW_INIT(X) \
+static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
+#define HW_CLEANUP(X) \
+static int cleanup_##X(void) { return cleanup(X); }
+#define HW_SCAN(X) \
+static GSList *scan_##X(GSList *options) { return scan(options, X); }
+#define HW_DEV_LIST(X) \
+static GSList *dev_list_##X(void) { return dev_list(X); }
+#define HW_DEV_CLEAR(X) \
+static int dev_clear_##X(void) { return dev_clear(X); }
+#define HW_DEV_ACQUISITION_START(X) \
+static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
+void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
+#define HW_DEV_OPEN(X) \
+static int dev_open_##X(struct sr_dev_inst *sdi) { return dev_open(sdi, X); }
+
+/* Driver structs and API function wrappers */
+#define DRV(ID, ID_UPPER, NAME, LONGNAME) \
+HW_INIT(ID_UPPER) \
+HW_CLEANUP(ID_UPPER) \
+HW_SCAN(ID_UPPER) \
+HW_DEV_LIST(ID_UPPER) \
+HW_DEV_CLEAR(ID_UPPER) \
+HW_DEV_ACQUISITION_START(ID_UPPER) \
+HW_DEV_OPEN(ID_UPPER) \
+SR_PRIV struct sr_dev_driver ID##_driver_info = { \
+ .name = NAME, \
+ .longname = LONGNAME, \
+ .api_version = 1, \
+ .init = init_##ID_UPPER, \
+ .cleanup = cleanup_##ID_UPPER, \
+ .scan = scan_##ID_UPPER, \
+ .dev_list = dev_list_##ID_UPPER, \
+ .dev_clear = dev_clear_##ID_UPPER, \
+ .config_get = NULL, \
+ .config_set = config_set, \
+ .config_list = config_list, \
+ .dev_open = dev_open_##ID_UPPER, \
+ .dev_close = dev_close, \
+ .dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
+ .dev_acquisition_stop = dev_acquisition_stop, \
+ .priv = NULL, \
+};
+
+DRV(tecpel_dmm_8061, TECPEL_DMM_8061, "tecpel-dmm-8061", "Tecpel DMM-8061")
+DRV(uni_t_ut60a, UNI_T_UT60A, "uni-t-ut60a", "UNI-T UT60A")
+DRV(uni_t_ut60e, UNI_T_UT60E, "uni-t-ut60e", "UNI-T UT60E")
+DRV(uni_t_ut60g, UNI_T_UT60G, "uni-t-ut60g", "UNI-T UT60G")
+DRV(uni_t_ut61b, UNI_T_UT61B, "uni-t-ut61b", "UNI-T UT61B")
+DRV(uni_t_ut61c, UNI_T_UT61C, "uni-t-ut61c", "UNI-T UT61C")
+DRV(uni_t_ut61d, UNI_T_UT61D, "uni-t-ut61d", "UNI-T UT61D")
+DRV(uni_t_ut61e, UNI_T_UT61E, "uni-t-ut61e", "UNI-T UT61E")
+DRV(voltcraft_vc820, VOLTCRAFT_VC820, "voltcraft-vc820", "Voltcraft VC-820")
+DRV(voltcraft_vc830, VOLTCRAFT_VC830, "voltcraft-vc830", "Voltcraft VC-830")
+DRV(voltcraft_vc840, VOLTCRAFT_VC840, "voltcraft-vc840", "Voltcraft VC-840")
+DRV(tenma_72_7745, TENMA_72_7745, "tenma-72-7745", "Tenma 72-7745")
+DRV(tenma_72_7750, TENMA_72_7750, "tenma-72-7750", "Tenma 72-7750")
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+extern struct dmm_info udmms[];
+
+/*
+ * Driver for various UNI-T multimeters (and rebranded ones).
+ *
+ * Most UNI-T DMMs can be used with two (three) different PC interface cables:
+ * - The UT-D04 USB/HID cable, old version with Hoitek HE2325U chip.
+ * - The UT-D04 USB/HID cable, new version with WCH CH9325 chip.
+ * - The UT-D02 RS232 cable.
+ *
+ * This driver is meant to support all USB/HID cables, and various DMMs that
+ * can be attached to a PC via these cables. Currently only the UT-D04 cable
+ * (new version) is supported/tested.
+ * The UT-D02 RS232 cable is handled by the 'serial-dmm' driver.
+ *
+ * The data for one DMM packet (e.g. 14 bytes if the respective DMM uses a
+ * Fortune Semiconductor FS9922-DMM4 chip) is spread across multiple
+ * 8-byte chunks.
+ *
+ * An 8-byte chunk looks like this:
+ * - Byte 0: 0xfz, where z is the number of actual data bytes in this chunk.
+ * - Bytes 1-7: z data bytes, the rest of the bytes should be ignored.
+ *
+ * Example:
+ * f0 00 00 00 00 00 00 00 (no data bytes)
+ * f2 55 77 00 00 00 00 00 (2 data bytes, 0x55 and 0x77)
+ * f1 d1 00 00 00 00 00 00 (1 data byte, 0xd1)
+ */
+
+static void decode_packet(struct sr_dev_inst *sdi, int dmm, const uint8_t *buf,
+ void *info)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ float floatval;
+ int ret;
+
+ devc = sdi->priv;
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+
+ /* Parse the protocol packet. */
+ ret = udmms[dmm].packet_parse(buf, &floatval, &analog, info);
+ if (ret != SR_OK) {
+ sr_dbg("Invalid DMM packet, ignoring.");
+ return;
+ }
+
+ /* If this DMM needs additional handling, call the resp. function. */
+ if (udmms[dmm].dmm_details)
+ udmms[dmm].dmm_details(&analog, info);
+
+ /* Send a sample packet with one analog value. */
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+ analog.data = &floatval;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+
+ /* Increase sample count. */
+ devc->num_samples++;
+}
+
+static int hid_chip_init(struct sr_dev_inst *sdi, uint16_t baudrate)
+{
+ int ret;
+ uint8_t buf[5];
+ struct sr_usb_dev_inst *usb;
+
+ usb = sdi->conn;
+
+ /* Detach kernel drivers which grabbed this device (if any). */
+ if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
+ ret = libusb_detach_kernel_driver(usb->devhdl, 0);
+ if (ret < 0) {
+ sr_err("Failed to detach kernel driver: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sr_dbg("Successfully detached kernel driver.");
+ } else {
+ sr_dbg("No need to detach a kernel driver.");
+ }
+
+ /* Claim interface 0. */
+ if ((ret = libusb_claim_interface(usb->devhdl, 0)) < 0) {
+ sr_err("Failed to claim interface 0: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sr_dbg("Successfully claimed interface 0.");
+
+ /* Set data for the HID feature report (e.g. baudrate). */
+ buf[0] = baudrate & 0xff; /* Baudrate, LSB */
+ buf[1] = (baudrate >> 8) & 0xff; /* Baudrate, MSB */
+ buf[2] = 0x00; /* Unknown/unused (?) */
+ buf[3] = 0x00; /* Unknown/unused (?) */
+ buf[4] = 0x03; /* Unknown, always 0x03. */
+
+ /* Send HID feature report to setup the baudrate/chip. */
+ sr_dbg("Sending initial HID feature report.");
+ sr_spew("HID init = 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x (%d baud)",
+ buf[0], buf[1], buf[2], buf[3], buf[4], baudrate);
+ ret = libusb_control_transfer(
+ usb->devhdl, /* libusb device handle */
+ LIBUSB_REQUEST_TYPE_CLASS |
+ LIBUSB_RECIPIENT_INTERFACE |
+ LIBUSB_ENDPOINT_OUT,
+ 9, /* bRequest: HID set_report */
+ 0x300, /* wValue: HID feature, report number 0 */
+ 0, /* wIndex: interface 0 */
+ (unsigned char *)&buf, /* payload buffer */
+ 5, /* wLength: 5 bytes payload */
+ 1000 /* timeout (ms) */);
+
+ if (ret < 0) {
+ sr_err("HID feature report error: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if (ret != 5) {
+ /* TODO: Handle better by also sending the remaining bytes. */
+ sr_err("Short packet: sent %d/5 bytes.", ret);
+ return SR_ERR;
+ }
+
+ sr_dbg("Successfully sent initial HID feature report.");
+
+ return SR_OK;
+}
+
+static void log_8byte_chunk(const uint8_t *buf)
+{
+ sr_spew("8-byte chunk: %02x %02x %02x %02x %02x %02x %02x %02x "
+ "(%d data bytes)", buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7], (buf[0] & 0x0f));
+}
+
+static void log_dmm_packet(const uint8_t *buf)
+{
+ sr_dbg("DMM packet: %02x %02x %02x %02x %02x %02x %02x"
+ " %02x %02x %02x %02x %02x %02x %02x",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
+ buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]);
+}
+
+static int get_and_handle_data(struct sr_dev_inst *sdi, int dmm, void *info)
+{
+ struct dev_context *devc;
+ uint8_t buf[CHUNK_SIZE], *pbuf;
+ int i, ret, len, num_databytes_in_chunk;
+ struct sr_usb_dev_inst *usb;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+ pbuf = devc->protocol_buf;
+
+ /* On the first run, we need to init the HID chip. */
+ if (devc->first_run) {
+ if ((ret = hid_chip_init(sdi, udmms[dmm].baudrate)) != SR_OK) {
+ sr_err("HID chip init failed: %d.", ret);
+ return SR_ERR;
+ }
+ memset(pbuf, 0x00, DMM_BUFSIZE);
+ devc->first_run = FALSE;
+ }
+
+ memset(&buf, 0x00, CHUNK_SIZE);
+
+ /* Get data from EP2 using an interrupt transfer. */
+ ret = libusb_interrupt_transfer(
+ usb->devhdl, /* libusb device handle */
+ LIBUSB_ENDPOINT_IN | 2, /* EP2, IN */
+ (unsigned char *)&buf, /* receive buffer */
+ CHUNK_SIZE, /* wLength */
+ &len, /* actually received byte count */
+ 1000 /* timeout (ms) */);
+
+ if (ret < 0) {
+ sr_err("USB receive error: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if (len != CHUNK_SIZE) {
+ sr_err("Short packet: received %d/%d bytes.", len, CHUNK_SIZE);
+ /* TODO: Print the bytes? */
+ return SR_ERR;
+ }
+
+ log_8byte_chunk((const uint8_t *)&buf);
+
+ /* If there are no data bytes just return (without error). */
+ if (buf[0] == 0xf0)
+ return SR_OK;
+
+ devc->bufoffset = 0;
+
+ /*
+ * Append the 1-7 data bytes of this chunk to pbuf.
+ *
+ * Special case:
+ * DMMs with Cyrustek ES51922 chip need serial settings of
+ * 19230/7o1. The WCH CH9325 UART to USB/HID chip used in (some
+ * versions of) the UNI-T UT-D04 cable however, will also send
+ * the parity bit to the host in the 8-byte data chunks. This bit
+ * is encoded in bit 7 of each of the 1-7 data bytes and must thus
+ * be removed in order for the actual ES51922 protocol parser to
+ * work properly.
+ */
+ num_databytes_in_chunk = buf[0] & 0x0f;
+ for (i = 0; i < num_databytes_in_chunk; i++, devc->buflen++) {
+ pbuf[devc->buflen] = buf[1 + i];
+ if (udmms[dmm].packet_parse == sr_es519xx_19200_14b_parse)
+ pbuf[devc->buflen] &= ~(1 << 7);
+ }
+
+ /* Now look for packets in that data. */
+ while ((devc->buflen - devc->bufoffset) >= udmms[dmm].packet_size) {
+ if (udmms[dmm].packet_valid(pbuf + devc->bufoffset)) {
+ log_dmm_packet(pbuf + devc->bufoffset);
+ decode_packet(sdi, dmm, pbuf + devc->bufoffset, info);
+ devc->bufoffset += udmms[dmm].packet_size;
+ } else {
+ devc->bufoffset++;
+ }
+ }
+
+ /* Move remaining bytes to beginning of buffer. */
+ for (i = 0; i < devc->buflen - devc->bufoffset; i++)
+ pbuf[i] = pbuf[devc->bufoffset + i];
+ devc->buflen -= devc->bufoffset;
+
+ return SR_OK;
+}
+
+static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data)
+{
+ int ret;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ int64_t time_ms;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+ devc = sdi->priv;
+
+ if ((ret = get_and_handle_data(sdi, dmm, info)) != SR_OK)
+ return FALSE;
+
+ /* Abort acquisition if we acquired enough samples. */
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sr_info("Requested number of samples reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ }
+
+ if (devc->limit_msec) {
+ time_ms = (g_get_monotonic_time() - devc->starttime) / 1000;
+ if (time_ms > (int64_t)devc->limit_msec) {
+ sr_info("Requested time limit reached.");
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#define RECEIVE_DATA(ID_UPPER, DMM_DRIVER) \
+SR_PRIV int receive_data_##ID_UPPER(int fd, int revents, void *cb_data) { \
+ struct DMM_DRIVER##_info info; \
+ return receive_data(fd, revents, ID_UPPER, &info, cb_data); }
+
+/* Driver-specific receive_data() wrappers */
+RECEIVE_DATA(TECPEL_DMM_8061, fs9721)
+RECEIVE_DATA(UNI_T_UT60A, fs9721)
+RECEIVE_DATA(UNI_T_UT60E, fs9721)
+RECEIVE_DATA(UNI_T_UT60G, es519xx)
+RECEIVE_DATA(UNI_T_UT61B, fs9922)
+RECEIVE_DATA(UNI_T_UT61C, fs9922)
+RECEIVE_DATA(UNI_T_UT61D, fs9922)
+RECEIVE_DATA(UNI_T_UT61E, es519xx)
+RECEIVE_DATA(VOLTCRAFT_VC820, fs9721)
+RECEIVE_DATA(VOLTCRAFT_VC830, fs9922)
+RECEIVE_DATA(VOLTCRAFT_VC840, fs9721)
+RECEIVE_DATA(TENMA_72_7745, es519xx)
+RECEIVE_DATA(TENMA_72_7750, es519xx)
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBSIGROK_HARDWARE_UNI_T_DMM_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_UNI_T_DMM_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libusb.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "uni-t-dmm"
+
+enum {
+ TECPEL_DMM_8061,
+ UNI_T_UT60A,
+ UNI_T_UT60E,
+ UNI_T_UT60G,
+ UNI_T_UT61B,
+ UNI_T_UT61C,
+ UNI_T_UT61D,
+ UNI_T_UT61E,
+ VOLTCRAFT_VC820,
+ VOLTCRAFT_VC830,
+ VOLTCRAFT_VC840,
+ TENMA_72_7745,
+ TENMA_72_7750,
+};
+
+struct dmm_info {
+ char *vendor;
+ char *device;
+ uint32_t baudrate;
+ int packet_size;
+ gboolean (*packet_valid)(const uint8_t *);
+ int (*packet_parse)(const uint8_t *, float *,
+ struct sr_datafeed_analog *, void *);
+ void (*dmm_details)(struct sr_datafeed_analog *, void *);
+ struct sr_dev_driver *di;
+ int (*receive_data)(int, int, void *);
+};
+
+#define CHUNK_SIZE 8
+
+#define DMM_BUFSIZE 256
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /** The current sampling limit (in number of samples). */
+ uint64_t limit_samples;
+
+ /** The current sampling limit (in ms). */
+ uint64_t limit_msec;
+
+ /** Opaque pointer passed in by the frontend. */
+ void *cb_data;
+
+ /** The current number of already received samples. */
+ uint64_t num_samples;
+
+ int64_t starttime;
+
+ gboolean first_run;
+
+ uint8_t protocol_buf[DMM_BUFSIZE];
+ uint8_t bufoffset;
+ uint8_t buflen;
+};
+
+SR_PRIV int receive_data_TECPEL_DMM_8061(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT60A(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT60E(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT60G(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT61B(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT61C(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT61D(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_UNI_T_UT61E(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VOLTCRAFT_VC820(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VOLTCRAFT_VC830(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_VOLTCRAFT_VC840(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_TENMA_72_7745(int fd, int revents, void *cb_data);
+SR_PRIV int receive_data_TENMA_72_7750(int fd, int revents, void *cb_data);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 "protocol.h"
+
+#include <string.h>
+
+static const int32_t hwcaps[] = {
+ SR_CONF_THERMOMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_CONTINUOUS,
+ SR_CONF_DATA_SOURCE,
+};
+
+static char *channels[] = {
+ "T1",
+ "T2",
+ "T1-T2",
+};
+
+static const char *data_sources[] = {
+ "Live",
+ "Memory",
+};
+
+SR_PRIV struct sr_dev_driver uni_t_ut32x_driver_info;
+static struct sr_dev_driver *di = &uni_t_ut32x_driver_info;
+
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ struct sr_config *src;
+ GSList *usb_devices, *devices, *l;
+ int i;
+ const char *conn;
+
+ drvc = di->priv;
+ drvc->instances = NULL;
+
+ 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;
+
+ devices = NULL;
+ if ((usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn))) {
+ /* We have a list of sr_usb_dev_inst matching the connection
+ * string. Wrap them in sr_dev_inst and we're done. */
+ for (l = usb_devices; l; l = l->next) {
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR,
+ MODEL, NULL)))
+ return NULL;
+ sdi->driver = di;
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = l->data;
+ for (i = 0; i < 3; i++) {
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE,
+ channels[i]))) {
+ sr_dbg("Channel malloc failed.");
+ return NULL;
+ }
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
+ sr_dbg("Device context malloc failed.");
+ return NULL;
+ }
+ sdi->priv = devc;
+ devc->limit_samples = 0;
+ devc->data_source = DEFAULT_DATA_SOURCE;
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ }
+ g_slist_free(usb_devices);
+ } else
+ g_slist_free_full(usb_devices, g_free);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ int ret;
+
+ if (!(drvc = di->priv)) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+
+ if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
+ return SR_ERR;
+
+/*
+ * The libusbx 1.0.9 darwin backend is broken: it can report a kernel
+ * driver being active, but detaching it always returns an error.
+ */
+#if !defined(__APPLE__)
+ if (libusb_kernel_driver_active(usb->devhdl, USB_INTERFACE) == 1) {
+ if ((ret = libusb_detach_kernel_driver(usb->devhdl, USB_INTERFACE)) < 0) {
+ sr_err("failed to detach kernel driver: %s",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ }
+#endif
+
+ if ((ret = libusb_set_configuration(usb->devhdl, USB_CONFIGURATION))) {
+ sr_err("Failed to set configuration: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if ((ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE))) {
+ sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sdi->status = SR_ST_ACTIVE;
+
+ return ret;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+ if (!usb->devhdl)
+ /* Nothing to do. */
+ return SR_OK;
+
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ int ret;
+ struct drv_context *drvc;
+
+ if (!(drvc = di->priv))
+ /* Can get called on an unused driver, doesn't matter. */
+ return SR_OK;
+
+
+ ret = std_dev_clear(di, NULL);
+ g_free(drvc);
+ di->priv = NULL;
+
+ return ret;
+}
+
+static int config_get(int 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_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ break;
+ case SR_CONF_DATA_SOURCE:
+ if (devc->data_source == DATA_SOURCE_LIVE)
+ *data = g_variant_new_string("Live");
+ else
+ *data = g_variant_new_string("Memory");
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ int ret;
+ const char *tmp_str;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ devc = sdi->priv;
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ case SR_CONF_DATA_SOURCE:
+ tmp_str = g_variant_get_string(data, NULL);
+ if (!strcmp(tmp_str, "Live"))
+ devc->data_source = DATA_SOURCE_LIVE;
+ else if (!strcmp(tmp_str, "Memory"))
+ devc->data_source = DATA_SOURCE_MEMORY;
+ else
+ return SR_ERR;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_DATA_SOURCE:
+ *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int len, ret;
+ unsigned char cmd[2];
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ drvc = di->priv;
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ devc->cb_data = cb_data;
+ devc->num_samples = 0;
+ devc->packet_len = 0;
+
+ /* Configure serial port parameters on USB-UART interface
+ * chip inside the device (just baudrate 2400 actually). */
+ cmd[0] = 0x09;
+ cmd[1] = 0x60;
+ ret = libusb_control_transfer(usb->devhdl, 0x21, 0x09, 0x0300, 0x00,
+ cmd, 2, 5);
+ if (ret != 2) {
+ sr_dbg("Failed to configure CH9325: %s", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ if (!(devc->xfer = libusb_alloc_transfer(0)))
+ return SR_ERR;
+
+ /* Length of payload to follow. */
+ cmd[0] = 0x01;
+ if (devc->data_source == DATA_SOURCE_LIVE)
+ cmd[1] = CMD_GET_LIVE;
+ else
+ cmd[1] = CMD_GET_STORED;
+
+ ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, cmd, 2, &len, 5);
+ if (ret != 0 || len != 2) {
+ sr_dbg("Failed to start acquisition: %s", libusb_error_name(ret));
+ libusb_free_transfer(devc->xfer);
+ return SR_ERR;
+ }
+
+ libusb_fill_bulk_transfer(devc->xfer, usb->devhdl, EP_IN, devc->buf,
+ 8, uni_t_ut32x_receive_transfer, (void *)sdi, 15);
+ if (libusb_submit_transfer(devc->xfer) != 0) {
+ libusb_free_transfer(devc->xfer);
+ return SR_ERR;
+ }
+
+ usb_source_add(sdi->session, drvc->sr_ctx, 10,
+ uni_t_ut32x_handle_events, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ /* Signal USB transfer handler to clean up and stop. */
+ sdi->status = SR_ST_STOPPING;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver uni_t_ut32x_driver_info = {
+ .name = "uni-t-ut32x",
+ .longname = "UNI-T UT32x",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 "protocol.h"
+
+#include <string.h>
+#include <math.h>
+
+extern struct sr_dev_driver uni_t_ut32x_driver_info;
+static struct sr_dev_driver *di = &uni_t_ut32x_driver_info;
+
+static float parse_temperature(unsigned char *buf)
+{
+ float temp;
+ int i;
+ gboolean negative;
+
+ negative = FALSE;
+ temp = 0.0;
+ for (i = 0; i < 4; i++) {
+ if (buf[i] == 0x3a)
+ continue;
+ if (buf[i] == 0x3b) {
+ if (negative) {
+ sr_dbg("Double negative sign!");
+ return NAN;
+ } else {
+ negative = TRUE;
+ continue;
+ }
+ }
+ if (buf[i] < 0x30 || buf[i] > 0x39) {
+ sr_dbg("Invalid digit '%.2x'!", buf[i]);
+ return NAN;
+ }
+ temp *= 10;
+ temp += (buf[i] - 0x30);
+ }
+ temp /= 10;
+ if (negative)
+ temp = -temp;
+
+ return temp;
+}
+
+static void process_packet(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ GString *spew;
+ float temp;
+ int i;
+ gboolean is_valid;
+
+ devc = sdi->priv;
+ sr_dbg("Received full 19-byte packet.");
+ if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+ spew = g_string_sized_new(60);
+ for (i = 0; i < devc->packet_len; i++)
+ g_string_append_printf(spew, "%.2x ", devc->packet[i]);
+ sr_spew("%s", spew->str);
+ g_string_free(spew, TRUE);
+ }
+
+ is_valid = TRUE;
+ if (devc->packet[1] == 0x3b && devc->packet[2] == 0x3b
+ && devc->packet[3] == 0x3b && devc->packet[4] == 0x3b)
+ /* No measurement: missing channel, empty storage location, ... */
+ is_valid = FALSE;
+
+ temp = parse_temperature(devc->packet + 1);
+ if (isnan(temp))
+ is_valid = FALSE;
+
+ if (is_valid) {
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+ analog.mq = SR_MQ_TEMPERATURE;
+ analog.mqflags = 0;
+ switch (devc->packet[5] - 0x30) {
+ case 1:
+ analog.unit = SR_UNIT_CELSIUS;
+ break;
+ case 2:
+ analog.unit = SR_UNIT_FAHRENHEIT;
+ break;
+ case 3:
+ analog.unit = SR_UNIT_KELVIN;
+ break;
+ default:
+ /* We can still pass on the measurement, whatever it is. */
+ sr_dbg("Unknown unit 0x%.2x.", devc->packet[5]);
+ }
+ switch (devc->packet[13] - 0x30) {
+ case 0:
+ /* Channel T1. */
+ analog.channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, 0));
+ break;
+ case 1:
+ /* Channel T2. */
+ analog.channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, 1));
+ break;
+ case 2:
+ case 3:
+ /* Channel T1-T2. */
+ analog.channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, 2));
+ analog.mqflags |= SR_MQFLAG_RELATIVE;
+ break;
+ default:
+ sr_err("Unknown channel 0x%.2x.", devc->packet[13]);
+ is_valid = FALSE;
+ }
+ if (is_valid) {
+ analog.num_samples = 1;
+ analog.data = &temp;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+ g_slist_free(analog.channels);
+ }
+ }
+
+ /* We count packets even if the temperature was invalid. This way
+ * a sample limit on "Memory" data source still works: unused
+ * memory slots come through as "----" measurements. */
+ devc->num_samples++;
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
+ sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+ devc->cb_data);
+ }
+
+}
+
+SR_PRIV void uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer)
+{
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ int hid_payload_len, ret;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+ if (transfer->actual_length == 8) {
+ /* CH9325 encodes length in low nibble of first byte, with
+ * bytes 1-7 being the (padded) payload. */
+ hid_payload_len = transfer->buffer[0] & 0x0f;
+ memcpy(devc->packet + devc->packet_len, transfer->buffer + 1,
+ hid_payload_len);
+ devc->packet_len += hid_payload_len;
+ if (devc->packet_len >= 2
+ && devc->packet[devc->packet_len - 2] == 0x0d
+ && devc->packet[devc->packet_len - 1] == 0x0a) {
+ /* Got end of packet, but do we have a complete packet? */
+ if (devc->packet_len == 19)
+ process_packet(sdi);
+ /* Either way, done with it. */
+ devc->packet_len = 0;
+ } else if (devc->packet_len > 19) {
+ /* Guard against garbage from the device overrunning
+ * our packet buffer. */
+ sr_dbg("Buffer overrun!");
+ devc->packet_len = 0;
+ }
+ }
+
+ /* Get the next transfer (unless we're shutting down). */
+ if (sdi->status != SR_ST_STOPPING) {
+ if ((ret = libusb_submit_transfer(devc->xfer)) != 0) {
+ sr_dbg("Failed to resubmit transfer: %s", libusb_error_name(ret));
+ sdi->status = SR_ST_STOPPING;
+ libusb_free_transfer(devc->xfer);
+ }
+ } else
+ libusb_free_transfer(devc->xfer);
+
+}
+
+SR_PRIV int uni_t_ut32x_handle_events(int fd, int revents, void *cb_data)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_datafeed_packet packet;
+ struct sr_usb_dev_inst *usb;
+ struct timeval tv;
+ int len, ret;
+ unsigned char cmd[2];
+
+ (void)fd;
+ (void)revents;
+
+ drvc = di->priv;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ memset(&tv, 0, sizeof(struct timeval));
+ libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
+ NULL);
+
+ if (sdi->status == SR_ST_STOPPING) {
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+ packet.type = SR_DF_END;
+ sr_session_send(cb_data, &packet);
+
+ /* Tell the device to stop sending USB packets. */
+ usb = sdi->conn;
+ cmd[0] = 0x01;
+ cmd[1] = CMD_STOP;
+ ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, cmd, 2, &len, 5);
+ if (ret != 0 || len != 2) {
+ /* Warning only, doesn't matter. */
+ sr_dbg("Failed to send stop command: %s", libusb_error_name(ret));
+ }
+
+ sdi->status = SR_ST_ACTIVE;
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_UNI_T_UT32X_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_UNI_T_UT32X_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "uni-t-ut32x"
+
+#define DEFAULT_DATA_SOURCE DATA_SOURCE_LIVE
+#define USB_CONN "1a86.e008"
+#define VENDOR "UNI-T"
+#define MODEL "UT32x"
+#define USB_INTERFACE 0
+#define USB_CONFIGURATION 1
+
+#define EP_IN 0x80 | 2
+#define EP_OUT 2
+
+enum {
+ DATA_SOURCE_LIVE,
+ DATA_SOURCE_MEMORY,
+};
+
+enum {
+ CMD_GET_LIVE = 1,
+ CMD_STOP = 2,
+ CMD_GET_STORED = 7,
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Acquisition settings */
+ uint64_t limit_samples;
+ gboolean data_source;
+
+ /* Operational state */
+ uint64_t num_samples;
+ unsigned char buf[8];
+ struct libusb_transfer *xfer;
+ void *cb_data;
+
+ /* Temporary state across callbacks */
+ unsigned char packet[32];
+ int packet_len;
+};
+
+SR_PRIV int uni_t_ut32x_handle_events(int fd, int revents, void *cb_data);
+SR_PRIV void uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <glib.h>
+#include <libusb.h>
+#include <stdlib.h>
+#include <string.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+#define VICTOR_VID 0x1244
+#define VICTOR_PID 0xd237
+#define VICTOR_VENDOR "Victor"
+#define VICTOR_INTERFACE 0
+#define VICTOR_ENDPOINT LIBUSB_ENDPOINT_IN | 1
+
+SR_PRIV struct sr_dev_driver victor_dmm_driver_info;
+static struct sr_dev_driver *di = &victor_dmm_driver_info;
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_CONTINUOUS,
+};
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ struct libusb_device_descriptor des;
+ libusb_device **devlist;
+ GSList *devices;
+ int ret, devcnt, i;
+
+ (void)options;
+
+ drvc = di->priv;
+
+ devices = NULL;
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if ((ret = libusb_get_device_descriptor(devlist[i], &des)) != 0) {
+ sr_warn("Failed to get device descriptor: %s",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ if (des.idVendor != VICTOR_VID || des.idProduct != VICTOR_PID)
+ continue;
+
+ devcnt = g_slist_length(drvc->instances);
+ if (!(sdi = sr_dev_inst_new(devcnt, SR_ST_INACTIVE,
+ VICTOR_VENDOR, NULL, NULL)))
+ return NULL;
+ sdi->driver = di;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
+ return NULL;
+ sdi->priv = devc;
+
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
+ return NULL;
+ sdi->channels = g_slist_append(NULL, ch);
+
+ if (!(sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+ libusb_get_device_address(devlist[i]), NULL)))
+ return NULL;
+ sdi->inst_type = SR_INST_USB;
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ }
+ libusb_free_device_list(devlist, 1);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc = di->priv;
+ struct sr_usb_dev_inst *usb;
+ libusb_device **devlist;
+ int ret, i;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if (libusb_get_bus_number(devlist[i]) != usb->bus
+ || libusb_get_device_address(devlist[i]) != usb->address)
+ continue;
+ if ((ret = libusb_open(devlist[i], &usb->devhdl))) {
+ sr_err("Failed to open device: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ break;
+ }
+ libusb_free_device_list(devlist, 1);
+ if (!devlist[i]) {
+ sr_err("Device not found.");
+ return SR_ERR;
+ }
+
+ /* The device reports as HID class, so the kernel would have
+ * claimed it. */
+ if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
+ if ((ret = libusb_detach_kernel_driver(usb->devhdl, 0)) < 0) {
+ sr_err("Failed to detach kernel driver: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ }
+
+ if ((ret = libusb_claim_interface(usb->devhdl,
+ VICTOR_INTERFACE))) {
+ sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+
+ if (!usb->devhdl)
+ /* Nothing to do. */
+ return SR_OK;
+
+ libusb_release_interface(usb->devhdl, VICTOR_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ int ret;
+ struct drv_context *drvc;
+
+ if (!(drvc = di->priv))
+ /* Can get called on an unused driver, doesn't matter. */
+ return SR_OK;
+
+
+ ret = std_dev_clear(di, NULL);
+ g_free(drvc);
+ di->priv = NULL;
+
+ return ret;
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct sr_usb_dev_inst *usb;
+ char str[128];
+
+ (void)cg;
+
+ switch (id) {
+ case SR_CONF_CONN:
+ if (!sdi || !sdi->conn)
+ return SR_ERR_ARG;
+ usb = sdi->conn;
+ snprintf(str, 128, "%d.%d", usb->bus, usb->address);
+ *data = g_variant_new_string(str);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ gint64 now;
+ int ret;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ devc = sdi->priv;
+ ret = SR_OK;
+ switch (id) {
+ case SR_CONF_LIMIT_MSEC:
+ devc->limit_msec = g_variant_get_uint64(data);
+ now = g_get_monotonic_time() / 1000;
+ devc->end_time = now + devc->limit_msec;
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ default:
+ ret = SR_ERR_NA;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static void receive_transfer(struct libusb_transfer *transfer)
+{
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ int ret;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+ if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
+ /* USB device was unplugged. */
+ dev_acquisition_stop(sdi, sdi);
+ } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
+ sr_dbg("Got %d-byte packet.", transfer->actual_length);
+ if (transfer->actual_length == DMM_DATA_SIZE) {
+ victor_dmm_receive_data(sdi, transfer->buffer);
+ if (devc->limit_samples) {
+ if (devc->num_samples >= devc->limit_samples)
+ dev_acquisition_stop(sdi, sdi);
+ }
+ }
+ }
+ /* Anything else is either an error or a timeout, which is fine:
+ * we were just going to send another transfer request anyway. */
+
+ if (sdi->status == SR_ST_ACTIVE) {
+ /* Send the same request again. */
+ if ((ret = libusb_submit_transfer(transfer) != 0)) {
+ sr_err("Unable to resubmit transfer: %s.",
+ libusb_error_name(ret));
+ g_free(transfer->buffer);
+ libusb_free_transfer(transfer);
+ dev_acquisition_stop(sdi, sdi);
+ }
+ } else {
+ /* This was the last transfer we're going to receive, so
+ * clean up now. */
+ g_free(transfer->buffer);
+ libusb_free_transfer(transfer);
+ }
+}
+
+static int handle_events(int fd, int revents, void *cb_data)
+{
+ struct dev_context *devc;
+ struct drv_context *drvc = di->priv;
+ struct sr_datafeed_packet packet;
+ struct sr_dev_inst *sdi;
+ struct timeval tv;
+ gint64 now;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+ devc = sdi->priv;
+
+ if (devc->limit_msec) {
+ now = g_get_monotonic_time() / 1000;
+ if (now > devc->end_time)
+ dev_acquisition_stop(sdi, sdi);
+ }
+
+ if (sdi->status == SR_ST_STOPPING) {
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+
+ dev_close(sdi);
+
+ packet.type = SR_DF_END;
+ sr_session_send(cb_data, &packet);
+ }
+
+ memset(&tv, 0, sizeof(struct timeval));
+ libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
+ NULL);
+
+ return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct drv_context *drvc = di->priv;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_transfer *transfer;
+ int ret;
+ unsigned char *buf;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+ devc->cb_data = cb_data;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ usb_source_add(sdi->session, drvc->sr_ctx, 100,
+ handle_events, (void *)sdi);
+
+ buf = g_try_malloc(DMM_DATA_SIZE);
+ transfer = libusb_alloc_transfer(0);
+ /* Each transfer request gets 100ms to arrive before it's restarted.
+ * The device only sends 1 transfer/second no matter how many
+ * times you ask, but we want to keep step with the USB events
+ * handling above. */
+ libusb_fill_interrupt_transfer(transfer, usb->devhdl,
+ VICTOR_ENDPOINT, buf, DMM_DATA_SIZE, receive_transfer,
+ cb_data, 100);
+ if ((ret = libusb_submit_transfer(transfer) != 0)) {
+ sr_err("Unable to submit transfer: %s.", libusb_error_name(ret));
+ libusb_free_transfer(transfer);
+ g_free(buf);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ (void)cb_data;
+
+ if (!di->priv) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ if (sdi->status != SR_ST_ACTIVE) {
+ sr_err("Device not active, can't stop acquisition.");
+ return SR_ERR;
+ }
+
+ sdi->status = SR_ST_STOPPING;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver victor_dmm_driver_info = {
+ .name = "victor-dmm",
+ .longname = "Victor DMMs",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <glib.h>
+#include <string.h>
+#include <math.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+/* Reverse the high nibble into the low nibble */
+static uint8_t decode_digit(uint8_t in)
+{
+ uint8_t out, i;
+
+ out = 0;
+ in >>= 4;
+ for (i = 0x08; i; i >>= 1) {
+ out >>= 1;
+ if (in & i)
+ out |= 0x08;
+ }
+
+ return out;
+}
+
+static void decode_buf(struct sr_dev_inst *sdi, unsigned char *data)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct dev_context *devc;
+ long factor, ivalue;
+ uint8_t digits[4];
+ gboolean is_duty, is_continuity, is_diode, is_ac, is_dc, is_auto;
+ gboolean is_hold, is_max, is_min, is_relative, minus;
+ float fvalue;
+
+ devc = sdi->priv;
+
+ digits[0] = decode_digit(data[12]);
+ digits[1] = decode_digit(data[11]);
+ digits[2] = decode_digit(data[10]);
+ digits[3] = decode_digit(data[9]);
+
+ if (digits[0] == 0x0f && digits[1] == 0x00 && digits[2] == 0x0a &&
+ digits[3] == 0x0f)
+ /* The "over limit" (OL) display comes through like this */
+ ivalue = -1;
+ else if (digits[0] > 9 || digits[1] > 9 || digits[2] > 9 || digits[3] > 9)
+ /* An invalid digit in any position denotes no value. */
+ ivalue = -2;
+ else {
+ ivalue = digits[0] * 1000;
+ ivalue += digits[1] * 100;
+ ivalue += digits[2] * 10;
+ ivalue += digits[3];
+ }
+
+ /* Decimal point position */
+ factor = 0;
+ switch (data[7] >> 4) {
+ case 0x00:
+ factor = 0;
+ break;
+ case 0x02:
+ factor = 1;
+ break;
+ case 0x04:
+ factor = 2;
+ break;
+ case 0x08:
+ factor = 3;
+ break;
+ default:
+ sr_err("Unknown decimal point byte: 0x%.2x.", data[7]);
+ break;
+ }
+
+ /* Minus flag */
+ minus = data[2] & 0x01;
+
+ /* Mode detail symbols on the right side of the digits */
+ is_duty = is_continuity = is_diode = FALSE;
+ switch (data[4]) {
+ case 0x00:
+ /* None. */
+ break;
+ case 0x01:
+ /* Micro */
+ factor += 6;
+ break;
+ case 0x02:
+ /* Milli */
+ factor += 3;
+ break;
+ case 0x04:
+ /* Kilo */
+ ivalue *= 1000;
+ break;
+ case 0x08:
+ /* Mega */
+ ivalue *= 1000000;
+ break;
+ case 0x10:
+ /* Continuity shows up as Ohm + this bit */
+ is_continuity = TRUE;
+ break;
+ case 0x20:
+ /* Diode tester is Volt + this bit */
+ is_diode = TRUE;
+ break;
+ case 0x40:
+ is_duty = TRUE;
+ break;
+ case 0x80:
+ /* Never seen */
+ sr_dbg("Unknown mode right detail: 0x%.2x.", data[4]);
+ break;
+ default:
+ sr_dbg("Unknown/invalid mode right detail: 0x%.2x.", data[4]);
+ break;
+ }
+
+ /* Scale flags on the right, continued */
+ is_max = is_min = FALSE;
+ if (data[5] & 0x04)
+ is_max = TRUE;
+ if (data[5] & 0x08)
+ is_min = TRUE;
+ if (data[5] & 0x40)
+ /* Nano */
+ factor += 9;
+
+ /* Mode detail symbols on the left side of the digits */
+ is_auto = is_dc = is_ac = is_hold = is_relative = FALSE;
+ if (data[6] & 0x04)
+ is_auto = TRUE;
+ if (data[6] & 0x08)
+ is_dc = TRUE;
+ if (data[6] & 0x10)
+ is_ac = TRUE;
+ if (data[6] & 0x20)
+ is_relative = TRUE;
+ if (data[6] & 0x40)
+ is_hold = TRUE;
+
+ fvalue = (float)ivalue / pow(10, factor);
+ if (minus)
+ fvalue = -fvalue;
+
+ memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+
+ /* Measurement mode */
+ analog.mq = -1;
+ switch (data[3]) {
+ case 0x00:
+ if (is_duty) {
+ analog.mq = SR_MQ_DUTY_CYCLE;
+ analog.unit = SR_UNIT_PERCENTAGE;
+ } else
+ sr_dbg("Unknown measurement mode: %.2x.", data[3]);
+ break;
+ case 0x01:
+ if (is_diode) {
+ analog.mq = SR_MQ_VOLTAGE;
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags |= SR_MQFLAG_DIODE;
+ if (ivalue < 0)
+ fvalue = NAN;
+ } else {
+ if (ivalue < 0)
+ break;
+ analog.mq = SR_MQ_VOLTAGE;
+ analog.unit = SR_UNIT_VOLT;
+ if (is_ac)
+ analog.mqflags |= SR_MQFLAG_AC;
+ if (is_dc)
+ analog.mqflags |= SR_MQFLAG_DC;
+ }
+ break;
+ case 0x02:
+ analog.mq = SR_MQ_CURRENT;
+ analog.unit = SR_UNIT_AMPERE;
+ if (is_ac)
+ analog.mqflags |= SR_MQFLAG_AC;
+ if (is_dc)
+ analog.mqflags |= SR_MQFLAG_DC;
+ break;
+ case 0x04:
+ if (is_continuity) {
+ analog.mq = SR_MQ_CONTINUITY;
+ analog.unit = SR_UNIT_BOOLEAN;
+ fvalue = ivalue < 0 ? 0.0 : 1.0;
+ } else {
+ analog.mq = SR_MQ_RESISTANCE;
+ analog.unit = SR_UNIT_OHM;
+ if (ivalue < 0)
+ fvalue = INFINITY;
+ }
+ break;
+ case 0x08:
+ /* Never seen */
+ sr_dbg("Unknown measurement mode: 0x%.2x.", data[3]);
+ break;
+ case 0x10:
+ analog.mq = SR_MQ_FREQUENCY;
+ analog.unit = SR_UNIT_HERTZ;
+ break;
+ case 0x20:
+ analog.mq = SR_MQ_CAPACITANCE;
+ analog.unit = SR_UNIT_FARAD;
+ break;
+ case 0x40:
+ analog.mq = SR_MQ_TEMPERATURE;
+ analog.unit = SR_UNIT_CELSIUS;
+ break;
+ case 0x80:
+ analog.mq = SR_MQ_TEMPERATURE;
+ analog.unit = SR_UNIT_FAHRENHEIT;
+ break;
+ default:
+ sr_dbg("Unknown/invalid measurement mode: 0x%.2x.", data[3]);
+ break;
+ }
+ if (analog.mq == -1)
+ return;
+
+ if (is_auto)
+ analog.mqflags |= SR_MQFLAG_AUTORANGE;
+ if (is_hold)
+ analog.mqflags |= SR_MQFLAG_HOLD;
+ if (is_max)
+ analog.mqflags |= SR_MQFLAG_MAX;
+ if (is_min)
+ analog.mqflags |= SR_MQFLAG_MIN;
+ if (is_relative)
+ analog.mqflags |= SR_MQFLAG_RELATIVE;
+
+ analog.channels = sdi->channels;
+ analog.num_samples = 1;
+ analog.data = &fvalue;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+
+ devc->num_samples++;
+}
+
+SR_PRIV int victor_dmm_receive_data(struct sr_dev_inst *sdi, unsigned char *buf)
+{
+ GString *dbg;
+ int i;
+ unsigned char data[DMM_DATA_SIZE];
+ unsigned char obfuscation[DMM_DATA_SIZE] = "jodenxunickxia";
+ unsigned char shuffle[DMM_DATA_SIZE] = {
+ 6, 13, 5, 11, 2, 7, 9, 8, 3, 10, 12, 0, 4, 1
+ };
+
+ for (i = 0; i < DMM_DATA_SIZE && buf[i] == 0; i++);
+ if (i == DMM_DATA_SIZE) {
+ /* This DMM outputs all zeroes from time to time, just ignore it. */
+ sr_dbg("Received all zeroes.");
+ return SR_OK;
+ }
+
+ /* Deobfuscate and reorder data. */
+ for (i = 0; i < DMM_DATA_SIZE; i++)
+ data[shuffle[i]] = (buf[i] - obfuscation[i]) & 0xff;
+
+ if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+ dbg = g_string_sized_new(128);
+ g_string_printf(dbg, "Deobfuscated.");
+ for (i = 0; i < DMM_DATA_SIZE; i++)
+ g_string_append_printf(dbg, " %.2x", data[i]);
+ sr_spew("%s", dbg->str);
+ g_string_free(dbg, TRUE);
+ }
+
+ decode_buf(sdi, data);
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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_VICTOR_DMM_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_VICTOR_DMM_PROTOCOL_H
+
+#include <stdint.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "victor-dmm"
+
+#define DMM_DATA_SIZE 14
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /** The current sampling limit (in number of samples). */
+ uint64_t limit_samples;
+
+ /** The current sampling limit (in ms). */
+ uint64_t limit_msec;
+
+ /** Opaque pointer passed in by the frontend. */
+ void *cb_data;
+
+ /** The current number of already received samples. */
+ uint64_t num_samples;
+ gint64 end_time;
+};
+
+SR_PRIV int victor_dmm_receive_data(struct sr_dev_inst *sdi, unsigned char *buf);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010 Sven Peter <sven@fail0verflow.com>
+ * Copyright (C) 2010 Haxx Enterprises <bushing@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "analyzer.h"
+#include "gl_usb.h"
+#include "protocol.h"
+
+enum {
+ HARD_DATA_CHECK_SUM = 0x00,
+ PASS_WORD,
+
+ DEV_ID0 = 0x10,
+ DEV_ID1,
+
+ START_STATUS = 0x20,
+ DEV_STATUS = 0x21,
+ FREQUENCY_REG0 = 0x30,
+ FREQUENCY_REG1,
+ FREQUENCY_REG2,
+ FREQUENCY_REG3,
+ FREQUENCY_REG4,
+ MEMORY_LENGTH,
+ CLOCK_SOURCE,
+
+ TRIGGER_STATUS0 = 0x40,
+ TRIGGER_STATUS1,
+ TRIGGER_STATUS2,
+ TRIGGER_STATUS3,
+ TRIGGER_STATUS4,
+ TRIGGER_STATUS5,
+ TRIGGER_STATUS6,
+ TRIGGER_STATUS7,
+ TRIGGER_STATUS8,
+
+ TRIGGER_COUNT0 = 0x50,
+ TRIGGER_COUNT1,
+
+ TRIGGER_LEVEL0 = 0x55,
+ TRIGGER_LEVEL1,
+ TRIGGER_LEVEL2,
+ TRIGGER_LEVEL3,
+
+ RAMSIZE_TRIGGERBAR_ADDRESS0 = 0x60,
+ RAMSIZE_TRIGGERBAR_ADDRESS1,
+ RAMSIZE_TRIGGERBAR_ADDRESS2,
+ TRIGGERBAR_ADDRESS0,
+ TRIGGERBAR_ADDRESS1,
+ TRIGGERBAR_ADDRESS2,
+ DONT_CARE_TRIGGERBAR,
+
+ FILTER_ENABLE = 0x70,
+ FILTER_STATUS,
+
+ ENABLE_DELAY_TIME0 = 0x7a,
+ ENABLE_DELAY_TIME1,
+
+ ENABLE_INSERT_DATA0 = 0x80,
+ ENABLE_INSERT_DATA1,
+ ENABLE_INSERT_DATA2,
+ ENABLE_INSERT_DATA3,
+ COMPRESSION_TYPE0,
+ COMPRESSION_TYPE1,
+
+ TRIGGER_ADDRESS0 = 0x90,
+ TRIGGER_ADDRESS1,
+ TRIGGER_ADDRESS2,
+
+ NOW_ADDRESS0 = 0x96,
+ NOW_ADDRESS1,
+ NOW_ADDRESS2,
+
+ STOP_ADDRESS0 = 0x9b,
+ STOP_ADDRESS1,
+ STOP_ADDRESS2,
+
+ READ_RAM_STATUS = 0xa0,
+};
+
+static int g_trigger_status[9] = { 0 };
+static int g_trigger_count = 1;
+static int g_filter_status[8] = { 0 };
+static int g_filter_enable = 0;
+
+static int g_freq_value = 1;
+static int g_freq_scale = FREQ_SCALE_MHZ;
+static int g_memory_size = MEMORY_SIZE_8K;
+static int g_ramsize_triggerbar_addr = 2 * 1024;
+static int g_triggerbar_addr = 0;
+static int g_compression = COMPRESSION_NONE;
+static int g_thresh = 0x31; /* 1.5V */
+
+/* Maybe unk specifies an "endpoint" or "register" of sorts. */
+static int analyzer_write_status(libusb_device_handle *devh, unsigned char unk,
+ unsigned char flags)
+{
+ assert(unk <= 3);
+ return gl_reg_write(devh, START_STATUS, unk << 6 | flags);
+}
+
+#if 0
+static int __analyzer_set_freq(libusb_device_handle *devh, int freq, int scale)
+{
+ int reg0 = 0, divisor = 0, reg2 = 0;
+
+ switch (scale) {
+ case FREQ_SCALE_MHZ: /* MHz */
+ if (freq >= 100 && freq <= 200) {
+ reg0 = freq * 0.1;
+ divisor = 1;
+ reg2 = 0;
+ break;
+ }
+ if (freq >= 50 && freq < 100) {
+ reg0 = freq * 0.2;
+ divisor = 2;
+ reg2 = 0;
+ break;
+ }
+ if (freq >= 10 && freq < 50) {
+ if (freq == 25) {
+ reg0 = 25;
+ divisor = 5;
+ reg2 = 1;
+ break;
+ } else {
+ reg0 = freq * 0.5;
+ divisor = 5;
+ reg2 = 1;
+ break;
+ }
+ }
+ if (freq >= 2 && freq < 10) {
+ divisor = 5;
+ reg0 = freq * 2;
+ reg2 = 2;
+ break;
+ }
+ if (freq == 1) {
+ divisor = 5;
+ reg2 = 16;
+ reg0 = 5;
+ break;
+ }
+ divisor = 5;
+ reg0 = 5;
+ reg2 = 64;
+ break;
+ case FREQ_SCALE_HZ: /* Hz */
+ if (freq >= 500 && freq < 1000) {
+ reg0 = freq * 0.01;
+ divisor = 10;
+ reg2 = 64;
+ break;
+ }
+ if (freq >= 300 && freq < 500) {
+ reg0 = freq * 0.005 * 8;
+ divisor = 5;
+ reg2 = 67;
+ break;
+ }
+ if (freq >= 100 && freq < 300) {
+ reg0 = freq * 0.005 * 16;
+ divisor = 5;
+ reg2 = 68;
+ break;
+ }
+ divisor = 5;
+ reg0 = 5;
+ reg2 = 64;
+ break;
+ case FREQ_SCALE_KHZ: /* kHz */
+ if (freq >= 500 && freq < 1000) {
+ reg0 = freq * 0.01;
+ divisor = 5;
+ reg2 = 17;
+ break;
+ }
+ if (freq >= 100 && freq < 500) {
+ reg0 = freq * 0.05;
+ divisor = 5;
+ reg2 = 32;
+ break;
+ }
+ if (freq >= 50 && freq < 100) {
+ reg0 = freq * 0.1;
+ divisor = 5;
+ reg2 = 33;
+ break;
+ }
+ if (freq >= 10 && freq < 50) {
+ if (freq == 25) {
+ reg0 = 25;
+ divisor = 5;
+ reg2 = 49;
+ break;
+ }
+ reg0 = freq * 0.5;
+ divisor = 5;
+ reg2 = 48;
+ break;
+ }
+ if (freq >= 2 && freq < 10) {
+ divisor = 5;
+ reg0 = freq * 2;
+ reg2 = 50;
+ break;
+ }
+ divisor = 5;
+ reg0 = 5;
+ reg2 = 64;
+ break;
+ default:
+ divisor = 5;
+ reg0 = 5;
+ reg2 = 64;
+ break;
+ }
+
+ sr_dbg("Setting samplerate regs (freq=%d, scale=%d): "
+ "reg0: %d, reg1: %d, reg2: %d, reg3: %d.",
+ freq, scale, divisor, reg0, 0x02, reg2);
+
+ if (gl_reg_write(devh, FREQUENCY_REG0, divisor) < 0)
+ return -1; /* Divisor maybe? */
+
+ if (gl_reg_write(devh, FREQUENCY_REG1, reg0) < 0)
+ return -1; /* 10 / 0.2 */
+
+ if (gl_reg_write(devh, FREQUENCY_REG2, 0x02) < 0)
+ return -1; /* Always 2 */
+
+ if (gl_reg_write(devh, FREQUENCY_REG4, reg2) < 0)
+ return -1;
+
+ return 0;
+}
+#endif
+
+/*
+ * It seems that ...
+ * FREQUENCT_REG0 - division factor (?)
+ * FREQUENCT_REG1 - multiplication factor (?)
+ * FREQUENCT_REG4 - clock selection (?)
+ *
+ * clock selection
+ * 0 10MHz 16 1MHz 32 100kHz 48 10kHz 64 1kHz
+ * 1 5MHz 17 500kHz 33 50kHz 49 5kHz 65 500Hz
+ * 2 2.5MHz . . 50 2.5kHz 66 250Hz
+ * . . . . 67 125Hz
+ * . . . . 68 62.5Hz
+ */
+static int __analyzer_set_freq(libusb_device_handle *devh, int freq, int scale)
+{
+ struct freq_factor {
+ int freq;
+ int scale;
+ int sel;
+ int div;
+ int mul;
+ };
+
+ static const struct freq_factor f[] = {
+ { 200, FREQ_SCALE_MHZ, 0, 1, 20 },
+ { 150, FREQ_SCALE_MHZ, 0, 1, 15 },
+ { 100, FREQ_SCALE_MHZ, 0, 1, 10 },
+ { 80, FREQ_SCALE_MHZ, 0, 2, 16 },
+ { 50, FREQ_SCALE_MHZ, 0, 2, 10 },
+ { 25, FREQ_SCALE_MHZ, 1, 5, 25 },
+ { 10, FREQ_SCALE_MHZ, 1, 5, 10 },
+ { 1, FREQ_SCALE_MHZ, 16, 5, 5 },
+ { 800, FREQ_SCALE_KHZ, 17, 5, 8 },
+ { 400, FREQ_SCALE_KHZ, 32, 5, 20 },
+ { 200, FREQ_SCALE_KHZ, 32, 5, 10 },
+ { 100, FREQ_SCALE_KHZ, 32, 5, 5 },
+ { 50, FREQ_SCALE_KHZ, 33, 5, 5 },
+ { 25, FREQ_SCALE_KHZ, 49, 5, 25 },
+ { 5, FREQ_SCALE_KHZ, 50, 5, 10 },
+ { 1, FREQ_SCALE_KHZ, 64, 5, 5 },
+ { 500, FREQ_SCALE_HZ, 64, 10, 5 },
+ { 100, FREQ_SCALE_HZ, 68, 5, 8 },
+ { 0, 0, 0, 0, 0 }
+ };
+
+ int i;
+
+ for (i = 0; f[i].freq; i++) {
+ if (scale == f[i].scale && freq == f[i].freq)
+ break;
+ }
+ if (!f[i].freq)
+ return -1;
+
+ sr_dbg("Setting samplerate regs (freq=%d, scale=%d): "
+ "reg0: %d, reg1: %d, reg2: %d, reg3: %d.",
+ freq, scale, f[i].div, f[i].mul, 0x02, f[i].sel);
+
+ if (gl_reg_write(devh, FREQUENCY_REG0, f[i].div) < 0)
+ return -1;
+
+ if (gl_reg_write(devh, FREQUENCY_REG1, f[i].mul) < 0)
+ return -1;
+
+ if (gl_reg_write(devh, FREQUENCY_REG2, 0x02) < 0)
+ return -1;
+
+ if (gl_reg_write(devh, FREQUENCY_REG4, f[i].sel) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void __analyzer_set_ramsize_trigger_address(libusb_device_handle *devh,
+ unsigned int address)
+{
+ gl_reg_write(devh, RAMSIZE_TRIGGERBAR_ADDRESS0, (address >> 0) & 0xFF);
+ gl_reg_write(devh, RAMSIZE_TRIGGERBAR_ADDRESS1, (address >> 8) & 0xFF);
+ gl_reg_write(devh, RAMSIZE_TRIGGERBAR_ADDRESS2, (address >> 16) & 0xFF);
+}
+
+static void __analyzer_set_triggerbar_address(libusb_device_handle *devh,
+ unsigned int address)
+{
+ gl_reg_write(devh, TRIGGERBAR_ADDRESS0, (address >> 0) & 0xFF);
+ gl_reg_write(devh, TRIGGERBAR_ADDRESS1, (address >> 8) & 0xFF);
+ gl_reg_write(devh, TRIGGERBAR_ADDRESS2, (address >> 16) & 0xFF);
+}
+
+static void __analyzer_set_compression(libusb_device_handle *devh,
+ unsigned int type)
+{
+ gl_reg_write(devh, COMPRESSION_TYPE0, (type >> 0) & 0xFF);
+ gl_reg_write(devh, COMPRESSION_TYPE1, (type >> 8) & 0xFF);
+}
+
+static void __analyzer_set_trigger_count(libusb_device_handle *devh,
+ unsigned int count)
+{
+ gl_reg_write(devh, TRIGGER_COUNT0, (count >> 0) & 0xFF);
+ gl_reg_write(devh, TRIGGER_COUNT1, (count >> 8) & 0xFF);
+}
+
+static void analyzer_write_enable_insert_data(libusb_device_handle *devh)
+{
+ gl_reg_write(devh, ENABLE_INSERT_DATA0, 0x12);
+ gl_reg_write(devh, ENABLE_INSERT_DATA1, 0x34);
+ gl_reg_write(devh, ENABLE_INSERT_DATA2, 0x56);
+ gl_reg_write(devh, ENABLE_INSERT_DATA3, 0x78);
+}
+
+static void analyzer_set_filter(libusb_device_handle *devh)
+{
+ int i;
+ gl_reg_write(devh, FILTER_ENABLE, g_filter_enable);
+ for (i = 0; i < 8; i++)
+ gl_reg_write(devh, FILTER_STATUS + i, g_filter_status[i]);
+}
+
+SR_PRIV void analyzer_reset(libusb_device_handle *devh)
+{
+ analyzer_write_status(devh, 3, STATUS_FLAG_NONE); // reset device
+ analyzer_write_status(devh, 3, STATUS_FLAG_RESET); // reset device
+}
+
+SR_PRIV void analyzer_initialize(libusb_device_handle *devh)
+{
+ analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
+ analyzer_write_status(devh, 1, STATUS_FLAG_INIT);
+ analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
+}
+
+SR_PRIV void analyzer_wait(libusb_device_handle *devh, int set, int unset)
+{
+ int status;
+
+ while (1) {
+ status = gl_reg_read(devh, DEV_STATUS);
+ if ((!set || (status & set)) && ((status & unset) == 0))
+ return;
+ }
+}
+
+SR_PRIV void analyzer_read_start(libusb_device_handle *devh)
+{
+ analyzer_write_status(devh, 3, STATUS_FLAG_20 | STATUS_FLAG_READ);
+
+ /* Prep for bulk reads */
+ gl_reg_read_buf(devh, READ_RAM_STATUS, NULL, 0);
+}
+
+SR_PRIV int analyzer_read_data(libusb_device_handle *devh, void *buffer,
+ unsigned int size)
+{
+ return gl_read_bulk(devh, buffer, size);
+}
+
+SR_PRIV void analyzer_read_stop(libusb_device_handle *devh)
+{
+ analyzer_write_status(devh, 3, STATUS_FLAG_20);
+ analyzer_write_status(devh, 3, STATUS_FLAG_NONE);
+}
+
+SR_PRIV void analyzer_start(libusb_device_handle *devh)
+{
+ analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
+ analyzer_write_status(devh, 1, STATUS_FLAG_INIT);
+ analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
+ analyzer_write_status(devh, 1, STATUS_FLAG_GO);
+}
+
+SR_PRIV void analyzer_configure(libusb_device_handle *devh)
+{
+ int i;
+
+ /* Write_Start_Status */
+ analyzer_write_status(devh, 1, STATUS_FLAG_RESET);
+ analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
+
+ /* Start_Config_Outside_Device ? */
+ analyzer_write_status(devh, 1, STATUS_FLAG_INIT);
+ analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
+
+ /* SetData_To_Frequence_Reg */
+ __analyzer_set_freq(devh, g_freq_value, g_freq_scale);
+
+ /* SetMemory_Length */
+ gl_reg_write(devh, MEMORY_LENGTH, g_memory_size);
+
+ /* Sele_Inside_Outside_Clock */
+ gl_reg_write(devh, CLOCK_SOURCE, 0x03);
+
+ /* Set_Trigger_Status */
+ for (i = 0; i < 9; i++)
+ gl_reg_write(devh, TRIGGER_STATUS0 + i, g_trigger_status[i]);
+
+ __analyzer_set_trigger_count(devh, g_trigger_count);
+
+ /* Set_Trigger_Level */
+ gl_reg_write(devh, TRIGGER_LEVEL0, g_thresh);
+ gl_reg_write(devh, TRIGGER_LEVEL1, g_thresh);
+ gl_reg_write(devh, TRIGGER_LEVEL2, g_thresh);
+ gl_reg_write(devh, TRIGGER_LEVEL3, g_thresh);
+
+ /* Size of actual memory >> 2 */
+ __analyzer_set_ramsize_trigger_address(devh, g_ramsize_triggerbar_addr);
+ __analyzer_set_triggerbar_address(devh, g_triggerbar_addr);
+
+ /* Set_Dont_Care_TriggerBar */
+ gl_reg_write(devh, DONT_CARE_TRIGGERBAR, 0x01);
+
+ /* Enable_Status */
+ analyzer_set_filter(devh);
+
+ /* Set_Enable_Delay_Time */
+ gl_reg_write(devh, 0x7a, 0x00);
+ gl_reg_write(devh, 0x7b, 0x00);
+ analyzer_write_enable_insert_data(devh);
+ __analyzer_set_compression(devh, g_compression);
+}
+
+SR_PRIV int analyzer_add_triggers(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_trigger *trigger;
+ struct sr_trigger_stage *stage;
+ struct sr_trigger_match *match;
+ GSList *l, *m;
+ int channel;
+
+ devc = sdi->priv;
+
+ if (!(trigger = sr_session_trigger_get(sdi->session)))
+ return SR_OK;
+
+ for (l = trigger->stages; l; l = l->next) {
+ stage = l->data;
+ for (m = stage->matches; m; m = m->next) {
+ match = m->data;
+ devc->trigger = 1;
+ if (!match->channel->enabled)
+ /* Ignore disabled channels with a trigger. */
+ continue;
+ channel = match->channel->index;
+ switch (match->match) {
+ case SR_TRIGGER_ZERO:
+ g_trigger_status[channel / 4] |= 2 << (channel % 4 * 2);
+ case SR_TRIGGER_ONE:
+ g_trigger_status[channel / 4] |= 1 << (channel % 4 * 2);
+ break;
+ default:
+ sr_err("Unsupported match %d", match->match);
+ return SR_ERR;
+ }
+ }
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV void analyzer_add_filter(int channel, int type)
+{
+ int i;
+
+ if (type != FILTER_HIGH && type != FILTER_LOW)
+ return;
+ if ((channel & 0xf) >= 8)
+ return;
+
+ if (channel & CHANNEL_A)
+ i = 0;
+ else if (channel & CHANNEL_B)
+ i = 2;
+ else if (channel & CHANNEL_C)
+ i = 4;
+ else if (channel & CHANNEL_D)
+ i = 6;
+ else
+ return;
+
+ if ((channel & 0xf) >= 4) {
+ i++;
+ channel -= 4;
+ }
+
+ g_filter_status[i] |=
+ 1 << ((2 * channel) + (type == FILTER_LOW ? 1 : 0));
+
+ g_filter_enable = 1;
+}
+
+SR_PRIV void analyzer_set_trigger_count(int count)
+{
+ g_trigger_count = count;
+}
+
+SR_PRIV void analyzer_set_freq(int freq, int scale)
+{
+ g_freq_value = freq;
+ g_freq_scale = scale;
+}
+
+SR_PRIV void analyzer_set_memory_size(unsigned int size)
+{
+ g_memory_size = size;
+}
+
+SR_PRIV void analyzer_set_ramsize_trigger_address(unsigned int address)
+{
+ g_ramsize_triggerbar_addr = address;
+}
+
+SR_PRIV unsigned int analyzer_get_ramsize_trigger_address(void)
+{
+ return g_ramsize_triggerbar_addr;
+}
+
+SR_PRIV void analyzer_set_triggerbar_address(unsigned int address)
+{
+ g_triggerbar_addr = address;
+}
+
+SR_PRIV unsigned int analyzer_get_triggerbar_address(void)
+{
+ return g_triggerbar_addr;
+}
+
+SR_PRIV unsigned int analyzer_read_status(libusb_device_handle *devh)
+{
+ return gl_reg_read(devh, DEV_STATUS);
+}
+
+SR_PRIV unsigned int analyzer_read_id(libusb_device_handle *devh)
+{
+ return gl_reg_read(devh, DEV_ID1) << 8 | gl_reg_read(devh, DEV_ID0);
+}
+
+SR_PRIV unsigned int analyzer_get_stop_address(libusb_device_handle *devh)
+{
+ return gl_reg_read(devh, STOP_ADDRESS2) << 16 | gl_reg_read(devh,
+ STOP_ADDRESS1) << 8 | gl_reg_read(devh, STOP_ADDRESS0);
+}
+
+SR_PRIV unsigned int analyzer_get_now_address(libusb_device_handle *devh)
+{
+ return gl_reg_read(devh, NOW_ADDRESS2) << 16 | gl_reg_read(devh,
+ NOW_ADDRESS1) << 8 | gl_reg_read(devh, NOW_ADDRESS0);
+}
+
+SR_PRIV unsigned int analyzer_get_trigger_address(libusb_device_handle *devh)
+{
+ return gl_reg_read(devh, TRIGGER_ADDRESS2) << 16 | gl_reg_read(devh,
+ TRIGGER_ADDRESS1) << 8 | gl_reg_read(devh, TRIGGER_ADDRESS0);
+}
+
+SR_PRIV void analyzer_set_compression(unsigned int type)
+{
+ g_compression = type;
+}
+
+SR_PRIV void analyzer_set_voltage_threshold(int thresh)
+{
+ g_thresh = thresh;
+}
+
+SR_PRIV void analyzer_wait_button(libusb_device_handle *devh)
+{
+ analyzer_wait(devh, STATUS_BUTTON_PRESSED, 0);
+}
+
+SR_PRIV void analyzer_wait_data(libusb_device_handle *devh)
+{
+ analyzer_wait(devh, 0, STATUS_BUSY);
+}
+
+SR_PRIV int analyzer_decompress(void *input, unsigned int input_len,
+ void *output, unsigned int output_len)
+{
+ unsigned char *in = input;
+ unsigned char *out = output;
+ unsigned int A, B, C, count;
+ unsigned int written = 0;
+
+ while (input_len > 0) {
+ A = *in++;
+ B = *in++;
+ C = *in++;
+ count = (*in++) + 1;
+
+ if (count > output_len)
+ count = output_len;
+ output_len -= count;
+ written += count;
+
+ while (count--) {
+ *out++ = A;
+ *out++ = B;
+ *out++ = C;
+ *out++ = 0; /* Channel D */
+ }
+
+ input_len -= 4;
+ if (output_len == 0)
+ break;
+ }
+
+ return written;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010 Sven Peter <sven@fail0verflow.com>
+ * Copyright (C) 2010 Haxx Enterprises <bushing@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_ANALYZER_H
+#define LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_ANALYZER_H
+
+#include <libusb.h>
+#include "libsigrok.h"
+
+#define STATUS_FLAG_NONE 0x00
+#define STATUS_FLAG_RESET 0x01
+#define STATUS_FLAG_INIT 0x02
+#define STATUS_FLAG_GO 0x04
+#define STATUS_FLAG_PAUSE 0x08
+#define STATUS_FLAG_READ 0x10
+#define STATUS_FLAG_20 0x20
+
+/* In bytes */
+#define MEMORY_SIZE_8K 0x00
+#define MEMORY_SIZE_64K 0x01
+#define MEMORY_SIZE_128K 0x02
+#define MEMORY_SIZE_256K 0x03
+#define MEMORY_SIZE_512K 0x04
+#define MEMORY_SIZE_1M 0x05
+#define MEMORY_SIZE_2M 0x06
+#define MEMORY_SIZE_4M 0x07
+#define MEMORY_SIZE_8M 0x08
+
+#define STATUS_BUSY 0x01 /* WTF / ??? */
+#define STATUS_READY 0x02
+#define STATUS_BUTTON_PRESSED 0x04
+
+#define CHANNEL_A 0x1000
+#define CHANNEL_B 0x2000
+#define CHANNEL_C 0x3000
+#define CHANNEL_D 0x4000
+
+#define FREQ_SCALE_HZ 0
+#define FREQ_SCALE_KHZ 1
+#define FREQ_SCALE_MHZ 2
+
+#define FILTER_HIGH 0
+#define FILTER_LOW 1
+
+#define COMPRESSION_NONE 0x0001
+#define COMPRESSION_ENABLE 0x8001
+#define COMPRESSION_DOUBLE 0x8002
+
+SR_PRIV void analyzer_set_freq(int freq, int scale);
+SR_PRIV void analyzer_set_ramsize_trigger_address(unsigned int address);
+SR_PRIV void analyzer_set_triggerbar_address(unsigned int address);
+SR_PRIV unsigned int analyzer_get_ramsize_trigger_address(void );
+SR_PRIV unsigned int analyzer_get_triggerbar_address(void);
+SR_PRIV void analyzer_set_compression(unsigned int type);
+SR_PRIV void analyzer_set_memory_size(unsigned int size);
+SR_PRIV int analyzer_add_triggers(const struct sr_dev_inst *sdi);
+SR_PRIV void analyzer_set_trigger_count(int count);
+SR_PRIV void analyzer_add_filter(int channel, int type);
+SR_PRIV void analyzer_set_voltage_threshold(int thresh);
+
+SR_PRIV unsigned int analyzer_read_status(libusb_device_handle *devh);
+SR_PRIV unsigned int analyzer_read_id(libusb_device_handle *devh);
+SR_PRIV unsigned int analyzer_get_stop_address(libusb_device_handle *devh);
+SR_PRIV unsigned int analyzer_get_now_address(libusb_device_handle *devh);
+SR_PRIV unsigned int analyzer_get_trigger_address(libusb_device_handle *devh);
+SR_PRIV int analyzer_decompress(void *input, unsigned int input_len,
+ void *output, unsigned int output_len);
+
+SR_PRIV void analyzer_reset(libusb_device_handle *devh);
+SR_PRIV void analyzer_initialize(libusb_device_handle *devh);
+SR_PRIV void analyzer_wait(libusb_device_handle *devh, int set, int unset);
+SR_PRIV void analyzer_read_start(libusb_device_handle *devh);
+SR_PRIV int analyzer_read_data(libusb_device_handle *devh, void *buffer,
+ unsigned int size);
+SR_PRIV void analyzer_read_stop(libusb_device_handle *devh);
+SR_PRIV void analyzer_start(libusb_device_handle *devh);
+SR_PRIV void analyzer_configure(libusb_device_handle *devh);
+
+SR_PRIV void analyzer_wait_button(libusb_device_handle *devh);
+SR_PRIV void analyzer_wait_data(libusb_device_handle *devh);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 "protocol.h"
+
+#define VENDOR_NAME "ZEROPLUS"
+#define USB_INTERFACE 0
+#define USB_CONFIGURATION 1
+#define NUM_TRIGGER_STAGES 4
+#define PACKET_SIZE 2048 /* ?? */
+
+//#define ZP_EXPERIMENTAL
+
+struct zp_model {
+ uint16_t vid;
+ uint16_t pid;
+ char *model_name;
+ unsigned int channels;
+ unsigned int sample_depth; /* In Ksamples/channel */
+ unsigned int max_sampling_freq;
+};
+
+/*
+ * Note -- 16032, 16064 and 16128 *usually* -- but not always -- have the
+ * same 128K sample depth.
+ */
+static const struct zp_model zeroplus_models[] = {
+ {0x0c12, 0x7002, "LAP-16128U", 16, 128, 200},
+ {0x0c12, 0x7009, "LAP-C(16064)", 16, 64, 100},
+ {0x0c12, 0x700a, "LAP-C(16128)", 16, 128, 200},
+ {0x0c12, 0x700b, "LAP-C(32128)", 32, 128, 200},
+ {0x0c12, 0x700c, "LAP-C(321000)", 32, 1024, 200},
+ {0x0c12, 0x700d, "LAP-C(322000)", 32, 2048, 200},
+ {0x0c12, 0x700e, "LAP-C(16032)", 16, 32, 100},
+ {0x0c12, 0x7016, "LAP-C(162000)", 16, 2048, 200},
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_SAMPLERATE,
+ SR_CONF_TRIGGER_MATCH,
+ SR_CONF_CAPTURE_RATIO,
+ SR_CONF_VOLTAGE_THRESHOLD,
+ SR_CONF_LIMIT_SAMPLES,
+};
+
+static const int32_t trigger_matches[] = {
+ SR_TRIGGER_ZERO,
+ SR_TRIGGER_ONE,
+};
+
+/*
+ * ZEROPLUS LAP-C (16032) numbers the 16 channels A0-A7 and B0-B7.
+ * We currently ignore other untested/unsupported devices here.
+ */
+static const char *channel_names[] = {
+ "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
+ "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7",
+ "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7",
+ "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
+ NULL,
+};
+
+SR_PRIV struct sr_dev_driver zeroplus_logic_cube_driver_info;
+static struct sr_dev_driver *di = &zeroplus_logic_cube_driver_info;
+
+/*
+ * The hardware supports more samplerates than these, but these are the
+ * options hardcoded into the vendor's Windows GUI.
+ */
+
+static const uint64_t samplerates_100[] = {
+ SR_HZ(100),
+ SR_HZ(500),
+ SR_KHZ(1),
+ SR_KHZ(5),
+ SR_KHZ(25),
+ SR_KHZ(50),
+ SR_KHZ(100),
+ SR_KHZ(200),
+ SR_KHZ(400),
+ SR_KHZ(800),
+ SR_MHZ(1),
+ SR_MHZ(10),
+ SR_MHZ(25),
+ SR_MHZ(50),
+ SR_MHZ(80),
+ SR_MHZ(100),
+};
+
+const uint64_t samplerates_200[] = {
+ SR_HZ(100),
+ SR_HZ(500),
+ SR_KHZ(1),
+ SR_KHZ(5),
+ SR_KHZ(25),
+ SR_KHZ(50),
+ SR_KHZ(100),
+ SR_KHZ(200),
+ SR_KHZ(400),
+ SR_KHZ(800),
+ SR_MHZ(1),
+ SR_MHZ(10),
+ SR_MHZ(25),
+ SR_MHZ(50),
+ SR_MHZ(80),
+ SR_MHZ(100),
+ SR_MHZ(150),
+ SR_MHZ(200),
+};
+
+static int dev_close(struct sr_dev_inst *sdi);
+
+SR_PRIV int zp_set_samplerate(struct dev_context *devc, uint64_t samplerate)
+{
+ int i;
+
+ for (i = 0; ARRAY_SIZE(samplerates_200); i++)
+ if (samplerate == samplerates_200[i])
+ break;
+
+ if (i == ARRAY_SIZE(samplerates_200) || samplerate > devc->max_samplerate) {
+ sr_err("Unsupported samplerate: %" PRIu64 "Hz.", samplerate);
+ return SR_ERR_ARG;
+ }
+
+ sr_info("Setting samplerate to %" PRIu64 "Hz.", samplerate);
+
+ if (samplerate >= SR_MHZ(1))
+ analyzer_set_freq(samplerate / SR_MHZ(1), FREQ_SCALE_MHZ);
+ else if (samplerate >= SR_KHZ(1))
+ analyzer_set_freq(samplerate / SR_KHZ(1), FREQ_SCALE_KHZ);
+ else
+ analyzer_set_freq(samplerate, FREQ_SCALE_HZ);
+
+ devc->cur_samplerate = samplerate;
+
+ return SR_OK;
+}
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ const struct zp_model *prof;
+ struct libusb_device_descriptor des;
+ libusb_device **devlist;
+ GSList *devices;
+ int ret, devcnt, i, j;
+
+ (void)options;
+
+ drvc = di->priv;
+
+ devices = NULL;
+
+ /* Find all ZEROPLUS analyzers and add them to device list. */
+ devcnt = 0;
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); /* TODO: Errors. */
+
+ for (i = 0; devlist[i]; i++) {
+ ret = libusb_get_device_descriptor(devlist[i], &des);
+ if (ret != 0) {
+ sr_err("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ prof = NULL;
+ for (j = 0; j < zeroplus_models[j].vid; j++) {
+ if (des.idVendor == zeroplus_models[j].vid &&
+ des.idProduct == zeroplus_models[j].pid) {
+ prof = &zeroplus_models[j];
+ }
+ }
+ /* Skip if the device was not found. */
+ if (!prof)
+ continue;
+ sr_info("Found ZEROPLUS %s.", prof->model_name);
+
+ /* Register the device with libsigrok. */
+ if (!(sdi = sr_dev_inst_new(devcnt, SR_ST_INACTIVE,
+ VENDOR_NAME, prof->model_name, NULL))) {
+ sr_err("%s: sr_dev_inst_new failed", __func__);
+ return NULL;
+ }
+ sdi->driver = di;
+
+ /* Allocate memory for our private driver context. */
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+
+ sdi->priv = devc;
+ devc->prof = prof;
+ devc->num_channels = prof->channels;
+#ifdef ZP_EXPERIMENTAL
+ devc->max_sample_depth = 128 * 1024;
+ devc->max_samplerate = 200;
+#else
+ devc->max_sample_depth = prof->sample_depth * 1024;
+ devc->max_samplerate = prof->max_sampling_freq;
+#endif
+ devc->max_samplerate *= SR_MHZ(1);
+ devc->memory_size = MEMORY_SIZE_8K;
+ // memset(devc->trigger_buffer, 0, NUM_TRIGGER_STAGES);
+
+ /* Fill in channellist according to this device's profile. */
+ for (j = 0; j < devc->num_channels; j++) {
+ if (!(ch = sr_channel_new(j, SR_CHANNEL_LOGIC, TRUE,
+ channel_names[j])))
+ return NULL;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+
+ devices = g_slist_append(devices, sdi);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = sr_usb_dev_inst_new(
+ libusb_get_bus_number(devlist[i]),
+ libusb_get_device_address(devlist[i]), NULL);
+ devcnt++;
+
+ }
+ libusb_free_device_list(devlist, 1);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ libusb_device **devlist, *dev;
+ struct libusb_device_descriptor des;
+ int device_count, ret, i;
+
+ drvc = di->priv;
+ usb = sdi->conn;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("%s: sdi->priv was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx,
+ &devlist);
+ if (device_count < 0) {
+ sr_err("Failed to retrieve device list.");
+ return SR_ERR;
+ }
+
+ dev = NULL;
+ for (i = 0; i < device_count; i++) {
+ if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
+ sr_err("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+ if (libusb_get_bus_number(devlist[i]) == usb->bus
+ && libusb_get_device_address(devlist[i]) == usb->address) {
+ dev = devlist[i];
+ break;
+ }
+ }
+ if (!dev) {
+ sr_err("Device on bus %d address %d disappeared!",
+ usb->bus, usb->address);
+ return SR_ERR;
+ }
+
+ if (!(ret = libusb_open(dev, &(usb->devhdl)))) {
+ sdi->status = SR_ST_ACTIVE;
+ sr_info("Opened device %d on %d.%d interface %d.",
+ sdi->index, usb->bus, usb->address, USB_INTERFACE);
+ } else {
+ sr_err("Failed to open device: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ ret = libusb_set_configuration(usb->devhdl, USB_CONFIGURATION);
+ if (ret < 0) {
+ sr_err("Unable to set USB configuration %d: %s.",
+ USB_CONFIGURATION, libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
+ if (ret != 0) {
+ sr_err("Unable to claim interface: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ /* Set default configuration after power on. */
+ if (analyzer_read_status(usb->devhdl) == 0)
+ analyzer_configure(usb->devhdl);
+
+ analyzer_reset(usb->devhdl);
+ analyzer_initialize(usb->devhdl);
+
+ //analyzer_set_memory_size(MEMORY_SIZE_512K);
+ // analyzer_set_freq(g_freq, g_freq_scale);
+ analyzer_set_trigger_count(1);
+ // analyzer_set_ramsize_trigger_address((((100 - g_pre_trigger)
+ // * get_memory_size(g_memory_size)) / 100) >> 2);
+
+#if 0
+ if (g_double_mode == 1)
+ analyzer_set_compression(COMPRESSION_DOUBLE);
+ else if (g_compression == 1)
+ analyzer_set_compression(COMPRESSION_ENABLE);
+ else
+#endif
+ analyzer_set_compression(COMPRESSION_NONE);
+
+ if (devc->cur_samplerate == 0) {
+ /* Samplerate hasn't been set. Default to 1MHz. */
+ analyzer_set_freq(1, FREQ_SCALE_MHZ);
+ devc->cur_samplerate = SR_MHZ(1);
+ }
+
+ if (devc->cur_threshold == 0)
+ set_voltage_threshold(devc, 1.5);
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ usb = sdi->conn;
+
+ if (!usb->devhdl)
+ return SR_ERR;
+
+ sr_info("Closing device %d on %d.%d interface %d.", sdi->index,
+ usb->bus, usb->address, USB_INTERFACE);
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_reset_device(usb->devhdl);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ if (sdi) {
+ devc = sdi->priv;
+ *data = g_variant_new_uint64(devc->cur_samplerate);
+ sr_spew("Returning samplerate: %" PRIu64 "Hz.",
+ devc->cur_samplerate);
+ } else
+ return SR_ERR_ARG;
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ if (sdi) {
+ devc = sdi->priv;
+ *data = g_variant_new_uint64(devc->capture_ratio);
+ } else
+ return SR_ERR_ARG;
+ break;
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ if (sdi) {
+ GVariant *range[2];
+ devc = sdi->priv;
+ range[0] = g_variant_new_double(devc->cur_threshold);
+ range[1] = g_variant_new_double(devc->cur_threshold);
+ *data = g_variant_new_tuple(range, 2);
+ } else
+ return SR_ERR_ARG;
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ gdouble low, high;
+
+ (void)cg;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("%s: sdi->priv was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ return zp_set_samplerate(devc, g_variant_get_uint64(data));
+ case SR_CONF_LIMIT_SAMPLES:
+ return set_limit_samples(devc, g_variant_get_uint64(data));
+ case SR_CONF_CAPTURE_RATIO:
+ return set_capture_ratio(devc, g_variant_get_uint64(data));
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ g_variant_get(data, "(dd)", &low, &high);
+ return set_voltage_threshold(devc, (low + high) / 2.0);
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ GVariant *gvar, *grange[2];
+ GVariantBuilder gvb;
+ double v;
+ GVariant *range[2];
+
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ devc = sdi->priv;
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ if (devc->prof->max_sampling_freq == 100) {
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
+ samplerates_100, ARRAY_SIZE(samplerates_100),
+ sizeof(uint64_t));
+ } else if (devc->prof->max_sampling_freq == 200) {
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
+ samplerates_200, ARRAY_SIZE(samplerates_200),
+ sizeof(uint64_t));
+ } else {
+ sr_err("Internal error: Unknown max. samplerate: %d.",
+ devc->prof->max_sampling_freq);
+ return SR_ERR_ARG;
+ }
+ g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ trigger_matches, ARRAY_SIZE(trigger_matches),
+ sizeof(int32_t));
+ break;
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (v = -6.0; v <= 6.0; v += 0.1) {
+ range[0] = g_variant_new_double(v);
+ range[1] = g_variant_new_double(v);
+ gvar = g_variant_new_tuple(range, 2);
+ g_variant_builder_add_value(&gvb, gvar);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ grange[0] = g_variant_new_uint64(0);
+ grange[1] = g_variant_new_uint64(devc->max_sample_depth);
+ *data = g_variant_new_tuple(grange, 2);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+ void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ unsigned int samples_read;
+ int res;
+ unsigned int packet_num, n;
+ unsigned char *buf;
+ unsigned int status;
+ unsigned int stop_address;
+ unsigned int now_address;
+ unsigned int trigger_address;
+ unsigned int trigger_offset;
+ unsigned int triggerbar;
+ unsigned int ramsize_trigger;
+ unsigned int memory_size;
+ unsigned int valid_samples;
+ unsigned int discard;
+ int trigger_now;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("%s: sdi->priv was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ if (analyzer_add_triggers(sdi) != SR_OK) {
+ sr_err("Failed to configure triggers.");
+ return SR_ERR;
+ }
+
+ usb = sdi->conn;
+
+ set_triggerbar(devc);
+
+ /* Push configured settings to device. */
+ analyzer_configure(usb->devhdl);
+
+ analyzer_start(usb->devhdl);
+ sr_info("Waiting for data.");
+ analyzer_wait_data(usb->devhdl);
+
+ status = analyzer_read_status(usb->devhdl);
+ stop_address = analyzer_get_stop_address(usb->devhdl);
+ now_address = analyzer_get_now_address(usb->devhdl);
+ trigger_address = analyzer_get_trigger_address(usb->devhdl);
+
+ triggerbar = analyzer_get_triggerbar_address();
+ ramsize_trigger = analyzer_get_ramsize_trigger_address();
+
+ n = get_memory_size(devc->memory_size);
+ memory_size = n / 4;
+
+ sr_info("Status = 0x%x.", status);
+ sr_info("Stop address = 0x%x.", stop_address);
+ sr_info("Now address = 0x%x.", now_address);
+ sr_info("Trigger address = 0x%x.", trigger_address);
+ sr_info("Triggerbar address = 0x%x.", triggerbar);
+ sr_info("Ramsize trigger = 0x%x.", ramsize_trigger);
+ sr_info("Memory size = 0x%x.", memory_size);
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Check for empty capture */
+ if ((status & STATUS_READY) && !stop_address) {
+ packet.type = SR_DF_END;
+ sr_session_send(cb_data, &packet);
+ return SR_OK;
+ }
+
+ if (!(buf = g_try_malloc(PACKET_SIZE))) {
+ sr_err("Packet buffer malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+
+ /* Check if the trigger is in the samples we are throwing away */
+ trigger_now = now_address == trigger_address ||
+ ((now_address + 1) % memory_size) == trigger_address;
+
+ /*
+ * STATUS_READY doesn't clear until now_address advances past
+ * addr 0, but for our logic, clear it in that case
+ */
+ if (!now_address)
+ status &= ~STATUS_READY;
+
+ analyzer_read_start(usb->devhdl);
+
+ /* Calculate how much data to discard */
+ discard = 0;
+ if (status & STATUS_READY) {
+ /*
+ * We haven't wrapped around, we need to throw away data from
+ * our current position to the end of the buffer.
+ * Additionally, the first two samples captured are always
+ * bogus.
+ */
+ discard += memory_size - now_address + 2;
+ now_address = 2;
+ }
+
+ /* If we have more samples than we need, discard them */
+ valid_samples = (stop_address - now_address) % memory_size;
+ if (valid_samples > ramsize_trigger + triggerbar) {
+ discard += valid_samples - (ramsize_trigger + triggerbar);
+ now_address += valid_samples - (ramsize_trigger + triggerbar);
+ }
+
+ sr_info("Need to discard %d samples.", discard);
+
+ /* Calculate how far in the trigger is */
+ if (trigger_now)
+ trigger_offset = 0;
+ else
+ trigger_offset = (trigger_address - now_address) % memory_size;
+
+ /* Recalculate the number of samples available */
+ valid_samples = (stop_address - now_address) % memory_size;
+
+ /* Send the incoming transfer to the session bus. */
+ samples_read = 0;
+ for (packet_num = 0; packet_num < n / PACKET_SIZE; packet_num++) {
+ unsigned int len;
+ unsigned int buf_offset;
+
+ res = analyzer_read_data(usb->devhdl, buf, PACKET_SIZE);
+ sr_info("Tried to read %d bytes, actually read %d bytes.",
+ PACKET_SIZE, res);
+
+ if (discard >= PACKET_SIZE / 4) {
+ discard -= PACKET_SIZE / 4;
+ continue;
+ }
+
+ len = PACKET_SIZE - discard * 4;
+ buf_offset = discard * 4;
+ discard = 0;
+
+ /* Check if we've read all the samples */
+ if (samples_read + len / 4 >= valid_samples)
+ len = (valid_samples - samples_read) * 4;
+ if (!len)
+ break;
+
+ if (samples_read < trigger_offset &&
+ samples_read + len / 4 > trigger_offset) {
+ /* Send out samples remaining before trigger */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = (trigger_offset - samples_read) * 4;
+ logic.unitsize = 4;
+ logic.data = buf + buf_offset;
+ sr_session_send(cb_data, &packet);
+ len -= logic.length;
+ samples_read += logic.length / 4;
+ buf_offset += logic.length;
+ }
+
+ if (samples_read == trigger_offset) {
+ /* Send out trigger */
+ packet.type = SR_DF_TRIGGER;
+ packet.payload = NULL;
+ sr_session_send(cb_data, &packet);
+ }
+
+ /* Send out data (or data after trigger) */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = len;
+ logic.unitsize = 4;
+ logic.data = buf + buf_offset;
+ sr_session_send(cb_data, &packet);
+ samples_read += len / 4;
+ }
+ analyzer_read_stop(usb->devhdl);
+ g_free(buf);
+
+ packet.type = SR_DF_END;
+ sr_session_send(cb_data, &packet);
+
+ return SR_OK;
+}
+
+/* TODO: This stops acquisition on ALL devices, ignoring dev_index. */
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ struct sr_datafeed_packet packet;
+
+ packet.type = SR_DF_END;
+ sr_session_send(cb_data, &packet);
+
+ if (!(devc = sdi->priv)) {
+ sr_err("%s: sdi->priv was NULL", __func__);
+ return SR_ERR_BUG;
+ }
+
+ usb = sdi->conn;
+ analyzer_reset(usb->devhdl);
+ /* TODO: Need to cancel and free any queued up transfers. */
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver zeroplus_logic_cube_driver_info = {
+ .name = "zeroplus-logic-cube",
+ .longname = "ZEROPLUS Logic Cube LAP-C series",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = NULL,
+ .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,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010 Sven Peter <sven@fail0verflow.com>
+ * Copyright (C) 2010 Haxx Enterprises <bushing@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <libusb.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "gl_usb.h"
+#include "protocol.h"
+
+#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN | \
+ LIBUSB_RECIPIENT_INTERFACE)
+#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT | \
+ LIBUSB_RECIPIENT_INTERFACE)
+#define EP1_BULK_IN (LIBUSB_ENDPOINT_IN | 1)
+
+#define TIMEOUT 5000 /* Timeout in ms */
+
+enum {
+ REQ_READBULK = 0x82,
+ REQ_WRITEADDR,
+ REQ_READDATA,
+ REQ_WRITEDATA,
+};
+
+static int gl_write_address(libusb_device_handle *devh, unsigned int address)
+{
+ unsigned char packet[8] = { address & 0xFF };
+ int ret;
+
+ ret = libusb_control_transfer(devh, CTRL_OUT, 0xc, REQ_WRITEADDR,
+ 0, packet, 1, TIMEOUT);
+ if (ret != 1)
+ sr_err("%s: %s.", __func__, libusb_error_name(ret));
+ return ret;
+}
+
+static int gl_write_data(libusb_device_handle *devh, unsigned int val)
+{
+ unsigned char packet[8] = { val & 0xFF };
+ int ret;
+
+ ret = libusb_control_transfer(devh, CTRL_OUT, 0xc, REQ_WRITEDATA,
+ 0, packet, 1, TIMEOUT);
+ if (ret != 1)
+ sr_err("%s: %s.", __func__, libusb_error_name(ret));
+ return ret;
+}
+
+static int gl_read_data(libusb_device_handle *devh)
+{
+ unsigned char packet[8] = { 0 };
+ int ret;
+
+ ret = libusb_control_transfer(devh, CTRL_IN, 0xc, REQ_READDATA,
+ 0, packet, 1, TIMEOUT);
+ if (ret != 1)
+ sr_err("%s: %s, val=%hhx.", __func__,
+ libusb_error_name(ret), packet[0]);
+ return (ret == 1) ? packet[0] : ret;
+}
+
+SR_PRIV int gl_read_bulk(libusb_device_handle *devh, void *buffer,
+ unsigned int size)
+{
+ unsigned char packet[8] =
+ { 0, 0, 0, 0, size & 0xff, (size & 0xff00) >> 8,
+ (size & 0xff0000) >> 16, (size & 0xff000000) >> 24 };
+ int ret, transferred = 0;
+
+ ret = libusb_control_transfer(devh, CTRL_OUT, 0x4, REQ_READBULK,
+ 0, packet, 8, TIMEOUT);
+ if (ret != 8)
+ sr_err("%s: libusb_control_transfer: %s.", __func__,
+ libusb_error_name(ret));
+
+ ret = libusb_bulk_transfer(devh, EP1_BULK_IN, buffer, size,
+ &transferred, TIMEOUT);
+ if (ret < 0)
+ sr_err("%s: libusb_bulk_transfer: %s.", __func__,
+ libusb_error_name(ret));
+ return transferred;
+}
+
+SR_PRIV int gl_reg_write(libusb_device_handle *devh, unsigned int reg,
+ unsigned int val)
+{
+ int ret;
+
+ ret = gl_write_address(devh, reg);
+ if (ret < 0)
+ return ret;
+ ret = gl_write_data(devh, val);
+ return ret;
+}
+
+SR_PRIV int gl_reg_read(libusb_device_handle *devh, unsigned int reg)
+{
+ int ret;
+
+ ret = gl_write_address(devh, reg);
+ if (ret < 0)
+ return ret;
+ ret = gl_read_data(devh);
+ return ret;
+}
+
+SR_PRIV int gl_reg_read_buf(libusb_device_handle *devh, unsigned int reg,
+ unsigned char *buf, unsigned int len)
+{
+ int ret;
+ unsigned int i;
+
+ ret = gl_write_address(devh, reg);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < len; i++) {
+ ret = gl_read_data(devh);
+ if (ret < 0)
+ return ret;
+ buf[i] = ret;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010 Sven Peter <sven@fail0verflow.com>
+ * Copyright (C) 2010 Haxx Enterprises <bushing@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_GL_USB_H
+#define LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_GL_USB_H
+
+#include <libusb.h>
+#include "libsigrok.h"
+
+SR_PRIV int gl_read_bulk(libusb_device_handle *devh, void *buffer,
+ unsigned int size);
+SR_PRIV int gl_reg_write(libusb_device_handle *devh, unsigned int reg,
+ unsigned int val);
+SR_PRIV int gl_reg_read(libusb_device_handle *devh, unsigned int reg);
+SR_PRIV int gl_reg_read_buf(libusb_device_handle *devh, unsigned int reg,
+ unsigned char *buf, unsigned int len);
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <math.h>
+#include "protocol.h"
+
+SR_PRIV unsigned int get_memory_size(int type)
+{
+ if (type == MEMORY_SIZE_8K)
+ return 8 * 1024;
+ else if (type <= MEMORY_SIZE_8M)
+ return (32 * 1024) << type;
+ else
+ return 0;
+}
+
+static int clz(unsigned int x)
+{
+ int n = 0;
+ if (x == 0)
+ return 32;
+ if (!(x & 0xFFFF0000)) {
+ n = n + 16;
+ x = x << 16;
+ }
+ if (!(x & 0xFF000000)) {
+ n = n + 8;
+ x = x << 8;
+ }
+ if (!(x & 0xF0000000)) {
+ n = n + 4;
+ x = x << 4;
+ }
+ if (!(x & 0xC0000000)) {
+ n = n + 2;
+ x = x << 2;
+ }
+ if (!(x & 0x80000000))
+ n = n + 1;
+ return n;
+}
+
+SR_PRIV int set_limit_samples(struct dev_context *devc, uint64_t samples)
+{
+ if (samples > devc->max_sample_depth)
+ samples = devc->max_sample_depth;
+
+ devc->limit_samples = samples;
+
+ if (samples <= 2 * 1024)
+ devc->memory_size = MEMORY_SIZE_8K;
+ else if (samples <= 16 * 1024)
+ devc->memory_size = MEMORY_SIZE_64K;
+ else
+ devc->memory_size = 19 - clz(samples - 1);
+
+ sr_info("Setting memory size to %dK.",
+ get_memory_size(devc->memory_size) / 1024);
+
+ analyzer_set_memory_size(devc->memory_size);
+
+ return SR_OK;
+}
+
+SR_PRIV int set_capture_ratio(struct dev_context *devc, uint64_t ratio)
+{
+ if (ratio > 100) {
+ sr_err("Invalid capture ratio: %" PRIu64 ".", ratio);
+ return SR_ERR_ARG;
+ }
+
+ devc->capture_ratio = ratio;
+
+ sr_info("Setting capture ratio to %d%%.", devc->capture_ratio);
+
+ return SR_OK;
+}
+
+SR_PRIV int set_voltage_threshold(struct dev_context *devc, double thresh)
+{
+ if (thresh > 6.0)
+ thresh = 6.0;
+ if (thresh < -6.0)
+ thresh = -6.0;
+
+ devc->cur_threshold = thresh;
+
+ analyzer_set_voltage_threshold((int) round(-9.1*thresh + 62.6));
+
+ sr_info("Setting voltage threshold to %fV.", devc->cur_threshold);
+
+ return SR_OK;
+}
+
+SR_PRIV void set_triggerbar(struct dev_context *devc)
+{
+ unsigned int trigger_depth, triggerbar, ramsize_trigger;
+
+ trigger_depth = get_memory_size(devc->memory_size) / 4;
+ if (devc->limit_samples < trigger_depth)
+ trigger_depth = devc->limit_samples;
+
+ if (devc->trigger)
+ triggerbar = trigger_depth * devc->capture_ratio / 100;
+ else
+ triggerbar = 0;
+
+ ramsize_trigger = trigger_depth - triggerbar;
+ /* Matches USB packet captures from official app/driver */
+ if (triggerbar > 2)
+ triggerbar -= 2;
+ else {
+ ramsize_trigger -= 1;
+ triggerbar = 0;
+ }
+
+ analyzer_set_triggerbar_address(triggerbar);
+ analyzer_set_ramsize_trigger_address(ramsize_trigger);
+
+ sr_dbg("triggerbar_address = %d(0x%x)", triggerbar, triggerbar);
+ sr_dbg("ramsize_triggerbar_address = %d(0x%x)",
+ ramsize_trigger, ramsize_trigger);
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libusb.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "analyzer.h"
+
+#define LOG_PREFIX "zeroplus"
+
+/* Private, per-device-instance driver context. */
+struct dev_context {
+ uint64_t cur_samplerate;
+ uint64_t max_samplerate;
+ uint64_t limit_samples;
+ int num_channels;
+ int memory_size;
+ unsigned int max_sample_depth;
+ //uint8_t channel_mask;
+ //uint8_t trigger_mask[NUM_TRIGGER_STAGES];
+ //uint8_t trigger_value[NUM_TRIGGER_STAGES];
+ // uint8_t trigger_buffer[NUM_TRIGGER_STAGES];
+ int trigger;
+ unsigned int capture_ratio;
+ double cur_threshold;
+ const struct zp_model *prof;
+};
+
+SR_PRIV unsigned int get_memory_size(int type);
+SR_PRIV int zp_set_samplerate(struct dev_context *devc, uint64_t samplerate);
+SR_PRIV int set_limit_samples(struct dev_context *devc, uint64_t samples);
+SR_PRIV int set_capture_ratio(struct dev_context *devc, uint64_t ratio);
+SR_PRIV int set_voltage_threshold(struct dev_context *devc, double thresh);
+SR_PRIV void set_triggerbar(struct dev_context *devc);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <glib.h>
+#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "hwdriver"
+/** @endcond */
+
+extern SR_PRIV struct sr_dev_driver *drivers_list[];
+
+/**
+ * @file
+ *
+ * Hardware driver handling in libsigrok.
+ */
+
+/**
+ * @defgroup grp_driver Hardware drivers
+ *
+ * Hardware driver handling in libsigrok.
+ *
+ * @{
+ */
+
+static struct sr_config_info sr_config_info_data[] = {
+ {SR_CONF_CONN, SR_T_STRING, "conn",
+ "Connection", NULL},
+ {SR_CONF_SERIALCOMM, SR_T_STRING, "serialcomm",
+ "Serial communication", NULL},
+ {SR_CONF_SAMPLERATE, SR_T_UINT64, "samplerate",
+ "Sample rate", NULL},
+ {SR_CONF_CAPTURE_RATIO, SR_T_UINT64, "captureratio",
+ "Pre-trigger capture ratio", NULL},
+ {SR_CONF_PATTERN_MODE, SR_T_STRING, "pattern",
+ "Pattern", NULL},
+ {SR_CONF_TRIGGER_MATCH, SR_T_INT32, "triggermatch",
+ "Trigger matches", NULL},
+ {SR_CONF_EXTERNAL_CLOCK, SR_T_BOOL, "external_clock",
+ "External clock mode", NULL},
+ {SR_CONF_SWAP, SR_T_BOOL, "swap",
+ "Swap channel order", NULL},
+ {SR_CONF_RLE, SR_T_BOOL, "rle",
+ "Run Length Encoding", NULL},
+ {SR_CONF_TRIGGER_SLOPE, SR_T_STRING, "triggerslope",
+ "Trigger slope", NULL},
+ {SR_CONF_TRIGGER_SOURCE, SR_T_STRING, "triggersource",
+ "Trigger source", NULL},
+ {SR_CONF_HORIZ_TRIGGERPOS, SR_T_FLOAT, "horiz_triggerpos",
+ "Horizontal trigger position", NULL},
+ {SR_CONF_BUFFERSIZE, SR_T_UINT64, "buffersize",
+ "Buffer size", NULL},
+ {SR_CONF_TIMEBASE, SR_T_RATIONAL_PERIOD, "timebase",
+ "Time base", NULL},
+ {SR_CONF_FILTER, SR_T_STRING, "filter",
+ "Filter targets", NULL},
+ {SR_CONF_VDIV, SR_T_RATIONAL_VOLT, "vdiv",
+ "Volts/div", NULL},
+ {SR_CONF_COUPLING, SR_T_STRING, "coupling",
+ "Coupling", NULL},
+ {SR_CONF_DATALOG, SR_T_BOOL, "datalog",
+ "Datalog", NULL},
+ {SR_CONF_SPL_WEIGHT_FREQ, SR_T_STRING, "spl_weight_freq",
+ "Sound pressure level frequency weighting", NULL},
+ {SR_CONF_SPL_WEIGHT_TIME, SR_T_STRING, "spl_weight_time",
+ "Sound pressure level time weighting", NULL},
+ {SR_CONF_HOLD_MAX, SR_T_BOOL, "hold_max",
+ "Hold max", NULL},
+ {SR_CONF_HOLD_MIN, SR_T_BOOL, "hold_min",
+ "Hold min", NULL},
+ {SR_CONF_SPL_MEASUREMENT_RANGE, SR_T_UINT64_RANGE, "spl_meas_range",
+ "Sound pressure level measurement range", NULL},
+ {SR_CONF_VOLTAGE_THRESHOLD, SR_T_DOUBLE_RANGE, "voltage_threshold",
+ "Voltage threshold", NULL },
+ {SR_CONF_POWER_OFF, SR_T_BOOL, "power_off",
+ "Power off", NULL},
+ {SR_CONF_DATA_SOURCE, SR_T_STRING, "data_source",
+ "Data source", NULL},
+ {SR_CONF_NUM_LOGIC_CHANNELS, SR_T_INT32, "logic_channels",
+ "Number of logic channels", NULL},
+ {SR_CONF_NUM_ANALOG_CHANNELS, SR_T_INT32, "analog_channels",
+ "Number of analog channels", NULL},
+ {SR_CONF_OUTPUT_VOLTAGE, SR_T_FLOAT, "output_voltage",
+ "Current output voltage", NULL},
+ {SR_CONF_OUTPUT_VOLTAGE_MAX, SR_T_FLOAT, "output_voltage_max",
+ "Maximum output voltage", NULL},
+ {SR_CONF_OUTPUT_CURRENT, SR_T_FLOAT, "output_current",
+ "Current output current", NULL},
+ {SR_CONF_OUTPUT_CURRENT_MAX, SR_T_FLOAT, "output_current_max",
+ "Maximum output current", NULL},
+ {SR_CONF_OUTPUT_ENABLED, SR_T_BOOL, "output_enabled",
+ "Output enabled", NULL},
+ {SR_CONF_OUTPUT_CHANNEL, SR_T_STRING, "output_channel",
+ "Output channel modes", NULL},
+ {SR_CONF_OVER_VOLTAGE_PROTECTION, SR_T_BOOL, "ovp",
+ "Over-voltage protection", NULL},
+ {SR_CONF_OVER_CURRENT_PROTECTION, SR_T_BOOL, "ocp",
+ "Over-current protection", NULL},
+ {SR_CONF_LIMIT_SAMPLES, SR_T_UINT64, "limit_samples",
+ "Sample limit", NULL},
+ {SR_CONF_CLOCK_EDGE, SR_T_STRING, "clock_edge",
+ "Clock edge", NULL},
+ {0, 0, NULL, NULL, NULL},
+};
+
+/**
+ * Return the list of supported hardware drivers.
+ *
+ * @return Pointer to the NULL-terminated list of hardware driver pointers.
+ *
+ * @since 0.1.0
+ */
+SR_API struct sr_dev_driver **sr_driver_list(void)
+{
+
+ return drivers_list;
+}
+
+/**
+ * Initialize a hardware driver.
+ *
+ * This usually involves memory allocations and variable initializations
+ * within the driver, but _not_ scanning for attached devices.
+ * The API call sr_driver_scan() is used for that.
+ *
+ * @param ctx A libsigrok context object allocated by a previous call to
+ * sr_init(). Must not be NULL.
+ * @param driver The driver to initialize. This must be a pointer to one of
+ * the entries returned by sr_driver_list(). Must not be NULL.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_ARG Invalid parameter(s).
+ * @retval SR_ERR_BUG Internal errors.
+ * @retval other Another negative error code upon other errors.
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_driver_init(struct sr_context *ctx, struct sr_dev_driver *driver)
+{
+ int ret;
+
+ if (!ctx) {
+ sr_err("Invalid libsigrok context, can't initialize.");
+ return SR_ERR_ARG;
+ }
+
+ if (!driver) {
+ sr_err("Invalid driver, can't initialize.");
+ return SR_ERR_ARG;
+ }
+
+ sr_spew("Initializing driver '%s'.", driver->name);
+ if ((ret = driver->init(ctx)) < 0)
+ sr_err("Failed to initialize the driver: %d.", ret);
+
+ return ret;
+}
+
+/**
+ * Tell a hardware driver to scan for devices.
+ *
+ * In addition to the detection, the devices that are found are also
+ * initialized automatically. On some devices, this involves a firmware upload,
+ * or other such measures.
+ *
+ * The order in which the system is scanned for devices is not specified. The
+ * caller should not assume or rely on any specific order.
+ *
+ * Before calling sr_driver_scan(), the user must have previously initialized
+ * the driver by calling sr_driver_init().
+ *
+ * @param driver The driver that should scan. This must be a pointer to one of
+ * the entries returned by sr_driver_list(). Must not be NULL.
+ * @param options A list of 'struct sr_hwopt' options to pass to the driver's
+ * scanner. Can be NULL/empty.
+ *
+ * @return A GSList * of 'struct sr_dev_inst', or NULL if no devices were
+ * found (or errors were encountered). This list must be freed by the
+ * caller using g_slist_free(), but without freeing the data pointed
+ * to in the list.
+ *
+ * @since 0.2.0
+ */
+SR_API GSList *sr_driver_scan(struct sr_dev_driver *driver, GSList *options)
+{
+ GSList *l;
+
+ if (!driver) {
+ sr_err("Invalid driver, can't scan for devices.");
+ return NULL;
+ }
+
+ if (!driver->priv) {
+ sr_err("Driver not initialized, can't scan for devices.");
+ return NULL;
+ }
+
+ l = driver->scan(options);
+
+ sr_spew("Scan of '%s' found %d devices.", driver->name,
+ g_slist_length(l));
+
+ return l;
+}
+
+/** Call driver cleanup function for all drivers.
+ * @private */
+SR_PRIV void sr_hw_cleanup_all(void)
+{
+ int i;
+ struct sr_dev_driver **drivers;
+
+ drivers = sr_driver_list();
+ for (i = 0; drivers[i]; i++) {
+ if (drivers[i]->cleanup)
+ drivers[i]->cleanup();
+ }
+}
+
+/** Allocate struct sr_config.
+ * A floating reference can be passed in for data.
+ * @private
+ */
+SR_PRIV struct sr_config *sr_config_new(int key, GVariant *data)
+{
+ struct sr_config *src;
+
+ if (!(src = g_try_malloc(sizeof(struct sr_config))))
+ return NULL;
+ src->key = key;
+ src->data = g_variant_ref_sink(data);
+
+ return src;
+}
+
+/** Free struct sr_config.
+ * @private
+ */
+SR_PRIV void sr_config_free(struct sr_config *src)
+{
+
+ if (!src || !src->data) {
+ sr_err("%s: invalid data!", __func__);
+ return;
+ }
+
+ g_variant_unref(src->data);
+ g_free(src);
+
+}
+
+/**
+ * Query value of a configuration key at the given driver or device instance.
+ *
+ * @param[in] driver The sr_dev_driver struct to query.
+ * @param[in] sdi (optional) If the key is specific to a device, this must
+ * contain a pointer to the struct sr_dev_inst to be checked.
+ * Otherwise it must be NULL.
+ * @param[in] cg The channel group on the device for which to list the
+ * values, or NULL.
+ * @param[in] key The configuration key (SR_CONF_*).
+ * @param[in,out] data Pointer to a GVariant where the value will be stored.
+ * Must not be NULL. The caller is given ownership of the GVariant
+ * and must thus decrease the refcount after use. However if
+ * this function returns an error code, the field should be
+ * considered unused, and should not be unreferenced.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Error.
+ * @retval SR_ERR_ARG The driver doesn't know that key, but this is not to be
+ * interpreted as an error by the caller; merely as an indication
+ * that it's not applicable.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_config_get(const struct sr_dev_driver *driver,
+ const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg,
+ int key, GVariant **data)
+{
+ int ret;
+
+ if (!driver || !data)
+ return SR_ERR;
+
+ if (!driver->config_get)
+ return SR_ERR_ARG;
+
+ if ((ret = driver->config_get(key, data, sdi, cg)) == SR_OK) {
+ /* Got a floating reference from the driver. Sink it here,
+ * caller will need to unref when done with it. */
+ g_variant_ref_sink(*data);
+ }
+
+ return ret;
+}
+
+/**
+ * Set value of a configuration key in a device instance.
+ *
+ * @param[in] sdi The device instance.
+ * @param[in] cg The channel group on the device for which to list the
+ * values, or NULL.
+ * @param[in] key The configuration key (SR_CONF_*).
+ * @param data The new value for the key, as a GVariant with GVariantType
+ * appropriate to that key. A floating reference can be passed
+ * in; its refcount will be sunk and unreferenced after use.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Error.
+ * @retval SR_ERR_ARG The driver doesn't know that key, but this is not to be
+ * interpreted as an error by the caller; merely as an indication
+ * that it's not applicable.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_config_set(const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg,
+ int key, GVariant *data)
+{
+ int ret;
+
+ g_variant_ref_sink(data);
+
+ if (!sdi || !sdi->driver || !data)
+ ret = SR_ERR;
+ else if (!sdi->driver->config_set)
+ ret = SR_ERR_ARG;
+ else
+ ret = sdi->driver->config_set(key, data, sdi, cg);
+
+ g_variant_unref(data);
+
+ return ret;
+}
+
+/**
+ * Apply configuration settings to the device hardware.
+ *
+ * @param sdi The device instance.
+ *
+ * @return SR_OK upon success or SR_ERR in case of error.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_config_commit(const struct sr_dev_inst *sdi)
+{
+ int ret;
+
+ if (!sdi || !sdi->driver)
+ ret = SR_ERR;
+ else if (!sdi->driver->config_commit)
+ ret = SR_OK;
+ else
+ ret = sdi->driver->config_commit(sdi);
+
+ return ret;
+}
+
+/**
+ * List all possible values for a configuration key.
+ *
+ * @param[in] driver The sr_dev_driver struct to query.
+ * @param[in] sdi (optional) If the key is specific to a device, this must
+ * contain a pointer to the struct sr_dev_inst to be checked.
+ * @param[in] cg The channel group on the device for which to list the
+ * values, or NULL.
+ * @param[in] key The configuration key (SR_CONF_*).
+ * @param[in,out] data A pointer to a GVariant where the list will be stored.
+ * The caller is given ownership of the GVariant and must thus
+ * unref the GVariant after use. However if this function
+ * returns an error code, the field should be considered
+ * unused, and should not be unreferenced.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Error.
+ * @retval SR_ERR_ARG The driver doesn't know that key, but this is not to be
+ * interpreted as an error by the caller; merely as an indication
+ * that it's not applicable.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_config_list(const struct sr_dev_driver *driver,
+ const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg,
+ int key, GVariant **data)
+{
+ int ret;
+
+ if (!driver || !data)
+ ret = SR_ERR;
+ else if (!driver->config_list)
+ ret = SR_ERR_ARG;
+ else if ((ret = driver->config_list(key, data, sdi, cg)) == SR_OK)
+ g_variant_ref_sink(*data);
+
+ return ret;
+}
+
+/**
+ * Get information about a configuration key, by key.
+ *
+ * @param[in] key The configuration key.
+ *
+ * @return A pointer to a struct sr_config_info, or NULL if the key
+ * was not found.
+ *
+ * @since 0.2.0
+ */
+SR_API const struct sr_config_info *sr_config_info_get(int key)
+{
+ int i;
+
+ for (i = 0; sr_config_info_data[i].key; i++) {
+ if (sr_config_info_data[i].key == key)
+ return &sr_config_info_data[i];
+ }
+
+ return NULL;
+}
+
+/**
+ * Get information about a configuration key, by name.
+ *
+ * @param[in] optname The configuration key.
+ *
+ * @return A pointer to a struct sr_config_info, or NULL if the key
+ * was not found.
+ *
+ * @since 0.2.0
+ */
+SR_API const struct sr_config_info *sr_config_info_name_get(const char *optname)
+{
+ int i;
+
+ for (i = 0; sr_config_info_data[i].key; i++) {
+ if (!strcmp(sr_config_info_data[i].id, optname))
+ return &sr_config_info_data[i];
+ }
+
+ return NULL;
+}
+
+/** @} */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/binary"
+
+#define CHUNKSIZE (512 * 1024)
+#define DEFAULT_NUM_CHANNELS 8
+
+struct context {
+ uint64_t samplerate;
+};
+
+static int format_match(const char *filename)
+{
+ (void)filename;
+
+ /* This module will handle anything you throw at it. */
+ return TRUE;
+}
+
+static int init(struct sr_input *in, const char *filename)
+{
+ struct sr_channel *ch;
+ int num_channels, i;
+ char name[SR_MAX_CHANNELNAME_LEN + 1];
+ char *param;
+ struct context *ctx;
+
+ (void)filename;
+
+ if (!(ctx = g_try_malloc0(sizeof(*ctx)))) {
+ sr_err("Input format context malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+
+ num_channels = DEFAULT_NUM_CHANNELS;
+ ctx->samplerate = 0;
+
+ if (in->param) {
+ param = g_hash_table_lookup(in->param, "numchannels");
+ if (param) {
+ num_channels = strtoul(param, NULL, 10);
+ if (num_channels < 1)
+ return SR_ERR;
+ }
+
+ param = g_hash_table_lookup(in->param, "samplerate");
+ if (param) {
+ if (sr_parse_sizestring(param, &ctx->samplerate) != SR_OK)
+ return SR_ERR;
+ }
+ }
+
+ /* Create a virtual device. */
+ in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
+ in->internal = ctx;
+
+ for (i = 0; i < num_channels; i++) {
+ snprintf(name, SR_MAX_CHANNELNAME_LEN, "%d", i);
+ /* TODO: Check return value. */
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, name)))
+ return SR_ERR;
+ in->sdi->channels = g_slist_append(in->sdi->channels, ch);
+ }
+
+ return SR_OK;
+}
+
+static int loadfile(struct sr_input *in, const char *filename)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_meta meta;
+ struct sr_datafeed_logic logic;
+ struct sr_config *src;
+ unsigned char buffer[CHUNKSIZE];
+ int fd, size, num_channels;
+ struct context *ctx;
+
+ ctx = in->internal;
+
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return SR_ERR;
+
+ num_channels = g_slist_length(in->sdi->channels);
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(in->sdi, LOG_PREFIX);
+
+ if (ctx->samplerate) {
+ packet.type = SR_DF_META;
+ packet.payload = &meta;
+ src = sr_config_new(SR_CONF_SAMPLERATE,
+ g_variant_new_uint64(ctx->samplerate));
+ meta.config = g_slist_append(NULL, src);
+ sr_session_send(in->sdi, &packet);
+ sr_config_free(src);
+ }
+
+ /* Chop up the input file into chunks & send it to the session bus. */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.unitsize = (num_channels + 7) / 8;
+ logic.data = buffer;
+ while ((size = read(fd, buffer, CHUNKSIZE)) > 0) {
+ logic.length = size;
+ sr_session_send(in->sdi, &packet);
+ }
+ close(fd);
+
+ /* Send end packet to the session bus. */
+ packet.type = SR_DF_END;
+ sr_session_send(in->sdi, &packet);
+
+ g_free(ctx);
+ in->internal = NULL;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_input_format input_binary = {
+ .id = "binary",
+ .description = "Raw binary",
+ .format_match = format_match,
+ .init = init,
+ .loadfile = loadfile,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/chronovu-la8"
+
+#define NUM_PACKETS 2048
+#define PACKET_SIZE 4096
+#define DEFAULT_NUM_CHANNELS 8
+
+/**
+ * Convert the LA8 'divcount' value to the respective samplerate (in Hz).
+ *
+ * LA8 hardware: sample period = (divcount + 1) * 10ns.
+ * Min. value for divcount: 0x00 (10ns sample period, 100MHz samplerate).
+ * Max. value for divcount: 0xfe (2550ns sample period, 392.15kHz samplerate).
+ *
+ * @param divcount The divcount value as needed by the hardware.
+ *
+ * @return The samplerate in Hz, or 0xffffffffffffffff upon errors.
+ */
+static uint64_t divcount_to_samplerate(uint8_t divcount)
+{
+ if (divcount == 0xff)
+ return 0xffffffffffffffffULL;
+
+ return SR_MHZ(100) / (divcount + 1);
+}
+
+static int format_match(const char *filename)
+{
+ struct stat stat_buf;
+ int ret;
+
+ if (!filename) {
+ sr_err("%s: filename was NULL", __func__);
+ // return SR_ERR; /* FIXME */
+ return FALSE;
+ }
+
+ if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
+ sr_err("%s: input file '%s' does not exist",
+ __func__, filename);
+ // return SR_ERR; /* FIXME */
+ return FALSE;
+ }
+
+ if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
+ sr_err("%s: input file '%s' not a regular file",
+ __func__, filename);
+ // return SR_ERR; /* FIXME */
+ return FALSE;
+ }
+
+ /* Only accept files of length 8MB + 5 bytes. */
+ ret = stat(filename, &stat_buf);
+ if (ret != 0) {
+ sr_err("%s: Error getting file size of '%s'",
+ __func__, filename);
+ return FALSE;
+ }
+ if (stat_buf.st_size != (8 * 1024 * 1024 + 5)) {
+ sr_dbg("%s: File size must be exactly 8388613 bytes ("
+ "it actually is %d bytes in size), so this is not a "
+ "ChronoVu LA8 file.", __func__, stat_buf.st_size);
+ return FALSE;
+ }
+
+ /* TODO: Check for divcount != 0xff. */
+
+ return TRUE;
+}
+
+static int init(struct sr_input *in, const char *filename)
+{
+ struct sr_channel *ch;
+ int num_channels, i;
+ char name[SR_MAX_CHANNELNAME_LEN + 1];
+ char *param;
+
+ (void)filename;
+
+ num_channels = DEFAULT_NUM_CHANNELS;
+
+ if (in->param) {
+ param = g_hash_table_lookup(in->param, "numchannels");
+ if (param) {
+ num_channels = strtoul(param, NULL, 10);
+ if (num_channels < 1) {
+ sr_err("%s: strtoul failed", __func__);
+ return SR_ERR;
+ }
+ }
+ }
+
+ /* Create a virtual device. */
+ in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
+
+ for (i = 0; i < num_channels; i++) {
+ snprintf(name, SR_MAX_CHANNELNAME_LEN, "%d", i);
+ /* TODO: Check return value. */
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, name)))
+ return SR_ERR;
+ in->sdi->channels = g_slist_append(in->sdi->channels, ch);
+ }
+
+ return SR_OK;
+}
+
+static int loadfile(struct sr_input *in, const char *filename)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_meta meta;
+ struct sr_datafeed_logic logic;
+ struct sr_config *src;
+ uint8_t buf[PACKET_SIZE], divcount;
+ int i, fd, size, num_channels;
+ uint64_t samplerate;
+
+ /* TODO: Use glib functions! GIOChannel, g_fopen, etc. */
+ if ((fd = open(filename, O_RDONLY)) == -1) {
+ sr_err("%s: file open failed", __func__);
+ return SR_ERR;
+ }
+
+ num_channels = g_slist_length(in->sdi->channels);
+
+ /* Seek to the end of the file, and read the divcount byte. */
+ divcount = 0x00; /* TODO: Don't hardcode! */
+
+ /* Convert the divcount value to a samplerate. */
+ samplerate = divcount_to_samplerate(divcount);
+ if (samplerate == 0xffffffffffffffffULL) {
+ close(fd); /* FIXME */
+ return SR_ERR;
+ }
+ sr_dbg("%s: samplerate is %" PRIu64, __func__, samplerate);
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(in->sdi, LOG_PREFIX);
+
+ /* Send metadata about the SR_DF_LOGIC packets to come. */
+ packet.type = SR_DF_META;
+ packet.payload = &meta;
+ src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate));
+ meta.config = g_slist_append(NULL, src);
+ sr_session_send(in->sdi, &packet);
+ sr_config_free(src);
+
+ /* TODO: Handle trigger point. */
+
+ /* Send data packets to the session bus. */
+ sr_dbg("%s: sending SR_DF_LOGIC data packets", __func__);
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.unitsize = (num_channels + 7) / 8;
+ logic.data = buf;
+
+ /* Send 8MB of total data to the session bus in small chunks. */
+ for (i = 0; i < NUM_PACKETS; i++) {
+ /* TODO: Handle errors, handle incomplete reads. */
+ size = read(fd, buf, PACKET_SIZE);
+ logic.length = size;
+ sr_session_send(in->sdi, &packet);
+ }
+ close(fd); /* FIXME */
+
+ /* Send end packet to the session bus. */
+ sr_dbg("%s: sending SR_DF_END", __func__);
+ packet.type = SR_DF_END;
+ packet.payload = NULL;
+ sr_session_send(in->sdi, &packet);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_input_format input_chronovu_la8 = {
+ .id = "chronovu-la8",
+ .description = "ChronoVu LA8",
+ .format_match = format_match,
+ .init = init,
+ .loadfile = loadfile,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Marc Schink <sigrok-dev@marcschink.de>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/csv"
+
+/*
+ * The CSV input module has the following options:
+ *
+ * single-column: Specifies the column number which stores the sample data for
+ * single column mode and enables single column mode. Multi
+ * column mode is used if this parameter is omitted.
+ *
+ * numchannels: Specifies the number of channels to use. In multi column mode
+ * the number of channels are the number of columns and in single
+ * column mode the number of bits (LSB first) beginning at
+ * 'first-channel'.
+ *
+ * delimiter: Specifies the delimiter for columns. Must be at least one
+ * character. Comma is used as default delimiter.
+ *
+ * format: Specifies the format of the sample data in single column mode.
+ * Available formats are: 'bin', 'hex' and 'oct'. The binary
+ * format is used by default. This option has no effect in multi
+ * column mode.
+ *
+ * comment: Specifies the prefix character(s) for comments. No prefix
+ * characters are used by default which disables removing of
+ * comments.
+ *
+ * samplerate: Samplerate which the sample data was captured with. Default
+ * value is 0.
+ *
+ * first-channel: Column number of the first channel in multi column mode and
+ * position of the bit for the first channel in single column mode.
+ * Default value is 0.
+ *
+ * header: Determines if the first line should be treated as header
+ * and used for channel names in multi column mode. Empty header
+ * names will be replaced by the channel number. If enabled in
+ * single column mode the first line will be skipped. Usage of
+ * header is disabled by default.
+ *
+ * startline: Line number to start processing sample data. Must be greater
+ * than 0. The default line number to start processing is 1.
+ */
+
+/* Single column formats. */
+enum {
+ FORMAT_BIN,
+ FORMAT_HEX,
+ FORMAT_OCT
+};
+
+struct context {
+ /* Current selected samplerate. */
+ uint64_t samplerate;
+
+ /* Number of channels. */
+ gsize num_channels;
+
+ /* Column delimiter character(s). */
+ GString *delimiter;
+
+ /* Comment prefix character(s). */
+ GString *comment;
+
+ /* Determines if sample data is stored in multiple columns. */
+ gboolean multi_column_mode;
+
+ /* Column number of the sample data in single column mode. */
+ gsize single_column;
+
+ /*
+ * Number of the first column to parse. Equivalent to the number of the
+ * first channel in multi column mode and the single column number in
+ * single column mode.
+ */
+ gsize first_column;
+
+ /*
+ * Column number of the first channel in multi column mode and position of
+ * the bit for the first channel in single column mode.
+ */
+ gsize first_channel;
+
+ /* Line number to start processing. */
+ gsize start_line;
+
+ /*
+ * Determines if the first line should be treated as header and used for
+ * channel names in multi column mode.
+ */
+ gboolean header;
+
+ /* Format sample data is stored in single column mode. */
+ int format;
+
+ /* Size of the sample buffer. */
+ gsize sample_buffer_size;
+
+ /* Buffer to store sample data. */
+ uint8_t *sample_buffer;
+
+ GIOChannel *channel;
+
+ /* Buffer for the current line. */
+ GString *buffer;
+
+ /* Current line number. */
+ gsize line_number;
+};
+
+static int format_match(const char *filename)
+{
+ if (!filename) {
+ sr_err("%s: filename was NULL.", __func__);
+ return FALSE;
+ }
+
+ if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
+ sr_err("Input file '%s' does not exist.", filename);
+ return FALSE;
+ }
+
+ if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
+ sr_err("Input file '%s' not a regular file.", filename);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void free_context(struct context *ctx)
+{
+ if (!ctx)
+ return;
+
+ if (ctx->delimiter)
+ g_string_free(ctx->delimiter, TRUE);
+
+ if (ctx->comment)
+ g_string_free(ctx->comment, TRUE);
+
+ if (ctx->channel) {
+ g_io_channel_shutdown(ctx->channel, FALSE, NULL);
+ g_io_channel_unref(ctx->channel);
+ }
+
+ if (ctx->sample_buffer)
+ g_free(ctx->sample_buffer);
+
+ if (ctx->buffer)
+ g_string_free(ctx->buffer, TRUE);
+
+ g_free(ctx);
+}
+
+static void strip_comment(GString *string, const GString *prefix)
+{
+ char *ptr;
+
+ if (!prefix->len)
+ return;
+
+ if (!(ptr = strstr(string->str, prefix->str)))
+ return;
+
+ g_string_truncate(string, ptr - string->str);
+}
+
+static int parse_binstr(const char *str, struct context *ctx)
+{
+ gsize i, j, length;
+
+ length = strlen(str);
+
+ if (!length) {
+ sr_err("Column %zu in line %zu is empty.", ctx->single_column,
+ ctx->line_number);
+ return SR_ERR;
+ }
+
+ /* Clear buffer in order to set bits only. */
+ memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
+
+ i = ctx->first_channel;
+
+ for (j = 0; i < length && j < ctx->num_channels; i++, j++) {
+ if (str[length - i - 1] == '1') {
+ ctx->sample_buffer[j / 8] |= (1 << (j % 8));
+ } else if (str[length - i - 1] != '0') {
+ sr_err("Invalid value '%s' in column %zu in line %zu.",
+ str, ctx->single_column, ctx->line_number);
+ return SR_ERR;
+ }
+ }
+
+ return SR_OK;
+}
+
+static int parse_hexstr(const char *str, struct context *ctx)
+{
+ gsize i, j, k, length;
+ uint8_t value;
+ char c;
+
+ length = strlen(str);
+
+ if (!length) {
+ sr_err("Column %zu in line %zu is empty.", ctx->single_column,
+ ctx->line_number);
+ return SR_ERR;
+ }
+
+ /* Clear buffer in order to set bits only. */
+ memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
+
+ /* Calculate the position of the first hexadecimal digit. */
+ i = ctx->first_channel / 4;
+
+ for (j = 0; i < length && j < ctx->num_channels; i++) {
+ c = str[length - i - 1];
+
+ if (!g_ascii_isxdigit(c)) {
+ sr_err("Invalid value '%s' in column %zu in line %zu.",
+ str, ctx->single_column, ctx->line_number);
+ return SR_ERR;
+ }
+
+ value = g_ascii_xdigit_value(c);
+
+ k = (ctx->first_channel + j) % 4;
+
+ for (; j < ctx->num_channels && k < 4; k++) {
+ if (value & (1 << k))
+ ctx->sample_buffer[j / 8] |= (1 << (j % 8));
+
+ j++;
+ }
+ }
+
+ return SR_OK;
+}
+
+static int parse_octstr(const char *str, struct context *ctx)
+{
+ gsize i, j, k, length;
+ uint8_t value;
+ char c;
+
+ length = strlen(str);
+
+ if (!length) {
+ sr_err("Column %zu in line %zu is empty.", ctx->single_column,
+ ctx->line_number);
+ return SR_ERR;
+ }
+
+ /* Clear buffer in order to set bits only. */
+ memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
+
+ /* Calculate the position of the first octal digit. */
+ i = ctx->first_channel / 3;
+
+ for (j = 0; i < length && j < ctx->num_channels; i++) {
+ c = str[length - i - 1];
+
+ if (c < '0' || c > '7') {
+ sr_err("Invalid value '%s' in column %zu in line %zu.",
+ str, ctx->single_column, ctx->line_number);
+ return SR_ERR;
+ }
+
+ value = g_ascii_xdigit_value(c);
+
+ k = (ctx->first_channel + j) % 3;
+
+ for (; j < ctx->num_channels && k < 3; k++) {
+ if (value & (1 << k))
+ ctx->sample_buffer[j / 8] |= (1 << (j % 8));
+
+ j++;
+ }
+ }
+
+ return SR_OK;
+}
+
+static char **parse_line(const struct context *ctx, int max_columns)
+{
+ const char *str, *remainder;
+ GSList *list, *l;
+ char **columns;
+ char *column;
+ gsize n, k;
+
+ n = 0;
+ k = 0;
+ list = NULL;
+
+ remainder = ctx->buffer->str;
+ str = strstr(remainder, ctx->delimiter->str);
+
+ while (str && max_columns) {
+ if (n >= ctx->first_column) {
+ column = g_strndup(remainder, str - remainder);
+ list = g_slist_prepend(list, g_strstrip(column));
+
+ max_columns--;
+ k++;
+ }
+
+ remainder = str + ctx->delimiter->len;
+ str = strstr(remainder, ctx->delimiter->str);
+ n++;
+ }
+
+ if (ctx->buffer->len && max_columns && n >= ctx->first_column) {
+ column = g_strdup(remainder);
+ list = g_slist_prepend(list, g_strstrip(column));
+ k++;
+ }
+
+ if (!(columns = g_try_new(char *, k + 1)))
+ return NULL;
+
+ columns[k--] = NULL;
+
+ for (l = list; l; l = l->next)
+ columns[k--] = l->data;
+
+ g_slist_free(list);
+
+ return columns;
+}
+
+static int parse_multi_columns(char **columns, struct context *ctx)
+{
+ gsize i;
+
+ /* Clear buffer in order to set bits only. */
+ memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
+
+ for (i = 0; i < ctx->num_channels; i++) {
+ if (columns[i][0] == '1') {
+ ctx->sample_buffer[i / 8] |= (1 << (i % 8));
+ } else if (!strlen(columns[i])) {
+ sr_err("Column %zu in line %zu is empty.",
+ ctx->first_channel + i, ctx->line_number);
+ return SR_ERR;
+ } else if (columns[i][0] != '0') {
+ sr_err("Invalid value '%s' in column %zu in line %zu.",
+ columns[i], ctx->first_channel + i,
+ ctx->line_number);
+ return SR_ERR;
+ }
+ }
+
+ return SR_OK;
+}
+
+static int parse_single_column(const char *column, struct context *ctx)
+{
+ int res;
+
+ res = SR_ERR;
+
+ switch(ctx->format) {
+ case FORMAT_BIN:
+ res = parse_binstr(column, ctx);
+ break;
+ case FORMAT_HEX:
+ res = parse_hexstr(column, ctx);
+ break;
+ case FORMAT_OCT:
+ res = parse_octstr(column, ctx);
+ break;
+ }
+
+ return res;
+}
+
+static int send_samples(const struct sr_dev_inst *sdi, uint8_t *buffer,
+ gsize buffer_size, gsize count)
+{
+ int res;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ gsize i;
+
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.unitsize = buffer_size;
+ logic.length = buffer_size;
+ logic.data = buffer;
+
+ for (i = 0; i < count; i++) {
+ if ((res = sr_session_send(sdi, &packet)) != SR_OK)
+ return res;
+ }
+
+ return SR_OK;
+}
+
+static int init(struct sr_input *in, const char *filename)
+{
+ int res;
+ struct context *ctx;
+ const char *param;
+ GIOStatus status;
+ gsize i, term_pos;
+ char channel_name[SR_MAX_CHANNELNAME_LEN + 1];
+ struct sr_channel *ch;
+ char **columns;
+ gsize num_columns;
+ char *ptr;
+
+ if (!(ctx = g_try_malloc0(sizeof(struct context)))) {
+ sr_err("Context malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+
+ /* Create a virtual device. */
+ in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
+ in->internal = ctx;
+
+ /* Set default samplerate. */
+ ctx->samplerate = 0;
+
+ /*
+ * Enable auto-detection of the number of channels in multi column mode
+ * and enforce the specification of the number of channels in single
+ * column mode.
+ */
+ ctx->num_channels = 0;
+
+ /* Set default delimiter. */
+ if (!(ctx->delimiter = g_string_new(","))) {
+ sr_err("Delimiter malloc failed.");
+ free_context(ctx);
+ return SR_ERR_MALLOC;
+ }
+
+ /*
+ * Set default comment prefix. Note that an empty comment prefix
+ * disables removing of comments.
+ */
+ if (!(ctx->comment = g_string_new(""))) {
+ sr_err("Comment malloc failed.");
+ free_context(ctx);
+ return SR_ERR_MALLOC;
+ }
+
+ /* Enable multi column mode by default. */
+ ctx->multi_column_mode = TRUE;
+
+ /* Use first column as default single column number. */
+ ctx->single_column = 0;
+
+ /*
+ * In multi column mode start parsing sample data at the first column
+ * and in single column mode at the first bit.
+ */
+ ctx->first_channel = 0;
+
+ /* Start at the beginning of the file. */
+ ctx->start_line = 1;
+
+ /* Disable the usage of the first line as header by default. */
+ ctx->header = FALSE;
+
+ /* Set default format for single column mode. */
+ ctx->format = FORMAT_BIN;
+
+ if (!(ctx->buffer = g_string_new(""))) {
+ sr_err("Line buffer malloc failed.");
+ free_context(ctx);
+ return SR_ERR_MALLOC;
+ }
+
+ if (in->param) {
+ if ((param = g_hash_table_lookup(in->param, "samplerate"))) {
+ res = sr_parse_sizestring(param, &ctx->samplerate);
+
+ if (res != SR_OK) {
+ sr_err("Invalid samplerate: %s.", param);
+ free_context(ctx);
+ return SR_ERR_ARG;
+ }
+ }
+
+ if ((param = g_hash_table_lookup(in->param, "numchannels")))
+ ctx->num_channels = g_ascii_strtoull(param, NULL, 10);
+
+ if ((param = g_hash_table_lookup(in->param, "delimiter"))) {
+ if (!strlen(param)) {
+ sr_err("Delimiter must be at least one character.");
+ free_context(ctx);
+ return SR_ERR_ARG;
+ }
+
+ if (!g_ascii_strcasecmp(param, "\\t"))
+ g_string_assign(ctx->delimiter, "\t");
+ else
+ g_string_assign(ctx->delimiter, param);
+ }
+
+ if ((param = g_hash_table_lookup(in->param, "comment")))
+ g_string_assign(ctx->comment, param);
+
+ if ((param = g_hash_table_lookup(in->param, "single-column"))) {
+ ctx->single_column = g_ascii_strtoull(param, &ptr, 10);
+ ctx->multi_column_mode = FALSE;
+
+ if (param == ptr) {
+ sr_err("Invalid single-colum number: %s.",
+ param);
+ free_context(ctx);
+ return SR_ERR_ARG;
+ }
+ }
+
+ if ((param = g_hash_table_lookup(in->param, "first-channel")))
+ ctx->first_channel = g_ascii_strtoull(param, NULL, 10);
+
+ if ((param = g_hash_table_lookup(in->param, "startline"))) {
+ ctx->start_line = g_ascii_strtoull(param, NULL, 10);
+
+ if (ctx->start_line < 1) {
+ sr_err("Invalid start line: %s.", param);
+ free_context(ctx);
+ return SR_ERR_ARG;
+ }
+ }
+
+ if ((param = g_hash_table_lookup(in->param, "header")))
+ ctx->header = sr_parse_boolstring(param);
+
+ if ((param = g_hash_table_lookup(in->param, "format"))) {
+ if (!g_ascii_strncasecmp(param, "bin", 3)) {
+ ctx->format = FORMAT_BIN;
+ } else if (!g_ascii_strncasecmp(param, "hex", 3)) {
+ ctx->format = FORMAT_HEX;
+ } else if (!g_ascii_strncasecmp(param, "oct", 3)) {
+ ctx->format = FORMAT_OCT;
+ } else {
+ sr_err("Invalid format: %s.", param);
+ free_context(ctx);
+ return SR_ERR;
+ }
+ }
+ }
+
+ if (ctx->multi_column_mode)
+ ctx->first_column = ctx->first_channel;
+ else
+ ctx->first_column = ctx->single_column;
+
+ if (!ctx->multi_column_mode && !ctx->num_channels) {
+ sr_err("Number of channels needs to be specified in single column mode.");
+ free_context(ctx);
+ return SR_ERR;
+ }
+
+ if (!(ctx->channel = g_io_channel_new_file(filename, "r", NULL))) {
+ sr_err("Input file '%s' could not be opened.", filename);
+ free_context(ctx);
+ return SR_ERR;
+ }
+
+ while (TRUE) {
+ ctx->line_number++;
+ status = g_io_channel_read_line_string(ctx->channel,
+ ctx->buffer, &term_pos, NULL);
+
+ if (status == G_IO_STATUS_EOF) {
+ sr_err("Input file is empty.");
+ free_context(ctx);
+ return SR_ERR;
+ }
+
+ if (status != G_IO_STATUS_NORMAL) {
+ sr_err("Error while reading line %zu.",
+ ctx->line_number);
+ free_context(ctx);
+ return SR_ERR;
+ }
+
+ if (ctx->start_line > ctx->line_number) {
+ sr_spew("Line %zu skipped.", ctx->line_number);
+ continue;
+ }
+
+ /* Remove line termination character(s). */
+ g_string_truncate(ctx->buffer, term_pos);
+
+ if (!ctx->buffer->len) {
+ sr_spew("Blank line %zu skipped.", ctx->line_number);
+ continue;
+ }
+
+ /* Remove trailing comment. */
+ strip_comment(ctx->buffer, ctx->comment);
+
+ if (ctx->buffer->len)
+ break;
+
+ sr_spew("Comment-only line %zu skipped.", ctx->line_number);
+ }
+
+ /*
+ * In order to determine the number of columns parse the current line
+ * without limiting the number of columns.
+ */
+ if (!(columns = parse_line(ctx, -1))) {
+ sr_err("Error while parsing line %zu.", ctx->line_number);
+ free_context(ctx);
+ return SR_ERR;
+ }
+
+ num_columns = g_strv_length(columns);
+
+ /* Ensure that the first column is not out of bounds. */
+ if (!num_columns) {
+ sr_err("Column %zu in line %zu is out of bounds.",
+ ctx->first_column, ctx->line_number);
+ g_strfreev(columns);
+ free_context(ctx);
+ return SR_ERR;
+ }
+
+ if (ctx->multi_column_mode) {
+ /*
+ * Detect the number of channels in multi column mode
+ * automatically if not specified.
+ */
+ if (!ctx->num_channels) {
+ ctx->num_channels = num_columns;
+ sr_info("Number of auto-detected channels: %zu.",
+ ctx->num_channels);
+ }
+
+ /*
+ * Ensure that the number of channels does not exceed the number
+ * of columns in multi column mode.
+ */
+ if (num_columns < ctx->num_channels) {
+ sr_err("Not enough columns for desired number of channels in line %zu.",
+ ctx->line_number);
+ g_strfreev(columns);
+ free_context(ctx);
+ return SR_ERR;
+ }
+ }
+
+ for (i = 0; i < ctx->num_channels; i++) {
+ if (ctx->header && ctx->multi_column_mode && strlen(columns[i]))
+ snprintf(channel_name, sizeof(channel_name), "%s",
+ columns[i]);
+ else
+ snprintf(channel_name, sizeof(channel_name), "%zu", i);
+
+ ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, channel_name);
+
+ if (!ch) {
+ sr_err("Channel creation failed.");
+ free_context(ctx);
+ g_strfreev(columns);
+ return SR_ERR;
+ }
+
+ in->sdi->channels = g_slist_append(in->sdi->channels, ch);
+ }
+
+ g_strfreev(columns);
+
+ /*
+ * Calculate the minimum buffer size to store the sample data of the
+ * channels.
+ */
+ ctx->sample_buffer_size = (ctx->num_channels + 7) >> 3;
+
+ if (!(ctx->sample_buffer = g_try_malloc(ctx->sample_buffer_size))) {
+ sr_err("Sample buffer malloc failed.");
+ free_context(ctx);
+ return SR_ERR_MALLOC;
+ }
+
+ return SR_OK;
+}
+
+static int loadfile(struct sr_input *in, const char *filename)
+{
+ int res;
+ struct context *ctx;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_meta meta;
+ struct sr_config *cfg;
+ GIOStatus status;
+ gboolean read_new_line;
+ gsize term_pos;
+ char **columns;
+ gsize num_columns;
+ int max_columns;
+
+ (void)filename;
+
+ ctx = in->internal;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(in->sdi, LOG_PREFIX);
+
+ if (ctx->samplerate) {
+ packet.type = SR_DF_META;
+ packet.payload = &meta;
+ cfg = sr_config_new(SR_CONF_SAMPLERATE,
+ g_variant_new_uint64(ctx->samplerate));
+ meta.config = g_slist_append(NULL, cfg);
+ sr_session_send(in->sdi, &packet);
+ sr_config_free(cfg);
+ }
+
+ read_new_line = FALSE;
+
+ /* Limit the number of columns to parse. */
+ if (ctx->multi_column_mode)
+ max_columns = ctx->num_channels;
+ else
+ max_columns = 1;
+
+ while (TRUE) {
+ /*
+ * Skip reading a new line for the first time if the last read
+ * line was not a header because the sample data is not parsed
+ * yet.
+ */
+ if (read_new_line || ctx->header) {
+ ctx->line_number++;
+ status = g_io_channel_read_line_string(ctx->channel,
+ ctx->buffer, &term_pos, NULL);
+
+ if (status == G_IO_STATUS_EOF)
+ break;
+
+ if (status != G_IO_STATUS_NORMAL) {
+ sr_err("Error while reading line %zu.",
+ ctx->line_number);
+ free_context(ctx);
+ return SR_ERR;
+ }
+
+ /* Remove line termination character(s). */
+ g_string_truncate(ctx->buffer, term_pos);
+ }
+
+ read_new_line = TRUE;
+
+ if (!ctx->buffer->len) {
+ sr_spew("Blank line %zu skipped.", ctx->line_number);
+ continue;
+ }
+
+ /* Remove trailing comment. */
+ strip_comment(ctx->buffer, ctx->comment);
+
+ if (!ctx->buffer->len) {
+ sr_spew("Comment-only line %zu skipped.",
+ ctx->line_number);
+ continue;
+ }
+
+ if (!(columns = parse_line(ctx, max_columns))) {
+ sr_err("Error while parsing line %zu.",
+ ctx->line_number);
+ free_context(ctx);
+ return SR_ERR;
+ }
+
+ num_columns = g_strv_length(columns);
+
+ /* Ensure that the first column is not out of bounds. */
+ if (!num_columns) {
+ sr_err("Column %zu in line %zu is out of bounds.",
+ ctx->first_column, ctx->line_number);
+ g_strfreev(columns);
+ free_context(ctx);
+ return SR_ERR;
+ }
+
+ /*
+ * Ensure that the number of channels does not exceed the number
+ * of columns in multi column mode.
+ */
+ if (ctx->multi_column_mode && num_columns < ctx->num_channels) {
+ sr_err("Not enough columns for desired number of channels in line %zu.",
+ ctx->line_number);
+ g_strfreev(columns);
+ free_context(ctx);
+ return SR_ERR;
+ }
+
+ if (ctx->multi_column_mode)
+ res = parse_multi_columns(columns, ctx);
+ else
+ res = parse_single_column(columns[0], ctx);
+
+ if (res != SR_OK) {
+ g_strfreev(columns);
+ free_context(ctx);
+ return SR_ERR;
+ }
+
+ g_strfreev(columns);
+
+ /*
+ * TODO: Parse sample numbers / timestamps and use it for
+ * decompression.
+ */
+
+ /* Send sample data to the session bus. */
+ res = send_samples(in->sdi, ctx->sample_buffer,
+ ctx->sample_buffer_size, 1);
+
+ if (res != SR_OK) {
+ sr_err("Sending samples failed.");
+ free_context(ctx);
+ return SR_ERR;
+ }
+ }
+
+ /* Send end packet to the session bus. */
+ packet.type = SR_DF_END;
+ sr_session_send(in->sdi, &packet);
+
+ free_context(ctx);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_input_format input_csv = {
+ .id = "csv",
+ .description = "Comma-separated values (CSV)",
+ .format_match = format_match,
+ .init = init,
+ .loadfile = loadfile,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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.h"
+#include "libsigrok-internal.h"
+
+/**
+ * @file
+ *
+ * Input file/data format handling.
+ */
+
+/**
+ * @defgroup grp_input Input formats
+ *
+ * Input file/data format handling.
+ *
+ * libsigrok can process acquisition data in several different ways.
+ * Aside from acquiring data from a hardware device, it can also take it from
+ * a file in various formats (binary, CSV, VCD, and so on).
+ *
+ * Like everything in libsigrok that handles data, processing is done in a
+ * streaming manner -- input should be supplied to libsigrok a chunk at a time.
+ * This way anything that processes data can do so in real time, without the
+ * user having to wait for the whole thing to be finished.
+ *
+ * Every input module is "pluggable", meaning it's handled as being separate
+ * from the main libsigrok, but linked in to it statically. To keep things
+ * modular and separate like this, functions within an input module should be
+ * declared static, with only the respective 'struct sr_input_format' being
+ * exported for use into the wider libsigrok namespace.
+ *
+ * @{
+ */
+
+/** @cond PRIVATE */
+extern SR_PRIV struct sr_input_format input_chronovu_la8;
+extern SR_PRIV struct sr_input_format input_csv;
+extern SR_PRIV struct sr_input_format input_binary;
+extern SR_PRIV struct sr_input_format input_vcd;
+extern SR_PRIV struct sr_input_format input_wav;
+/* @endcond */
+
+static struct sr_input_format *input_module_list[] = {
+ &input_vcd,
+ &input_chronovu_la8,
+ &input_wav,
+ &input_csv,
+ /* This one has to be last, because it will take any input. */
+ &input_binary,
+ NULL,
+};
+
+/** @since 0.1.0 */
+SR_API struct sr_input_format **sr_input_list(void)
+{
+ return input_module_list;
+}
+
+/** @} */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Petteri Aimonen <jpa@sr.mail.kapsi.fi>
+ *
+ * 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/>.
+ */
+
+/* The VCD input module has the following options:
+ *
+ * numchannels: Maximum number of channels to use. The channels are
+ * detected in the same order as they are listed
+ * in the $var sections of the VCD file.
+ *
+ * skip: Allows skipping until given timestamp in the file.
+ * This can speed up analyzing of long captures.
+ *
+ * Value < 0: Skip until first timestamp listed in
+ * the file. (default)
+ *
+ * Value = 0: Do not skip, instead generate samples
+ * beginning from timestamp 0.
+ *
+ * Value > 0: Start at the given timestamp.
+ *
+ * downsample: Divide the samplerate by the given factor.
+ * This can speed up analyzing of long captures.
+ *
+ * compress: Compress idle periods longer than this value.
+ * This can speed up analyzing of long captures.
+ * Default 0 = don't compress.
+ *
+ * Based on Verilog standard IEEE Std 1364-2001 Version C
+ *
+ * Supported features:
+ * - $var with 'wire' and 'reg' types of scalar variables
+ * - $timescale definition for samplerate
+ * - multiple character variable identifiers
+ *
+ * Most important unsupported features:
+ * - vector variables (bit vectors etc.)
+ * - analog, integer and real number variables
+ * - $dumpvars initial value declaration
+ * - $scope namespaces
+ * - more than 64 channels
+ */
+
+#include <stdlib.h>
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/vcd"
+
+#define DEFAULT_NUM_CHANNELS 8
+#define CHUNKSIZE 1024
+
+struct context {
+ uint64_t samplerate;
+ int maxchannels;
+ int channelcount;
+ int downsample;
+ unsigned compress;
+ int64_t skip;
+ GSList *channels;
+};
+
+struct vcd_channel {
+ gchar *name;
+ gchar *identifier;
+};
+
+
+/* Read until specific type of character occurs in file.
+ * Skip input if dest is NULL.
+ * Modes:
+ * 'W' read until whitespace
+ * 'N' read until non-whitespace, and ungetc() the character
+ * '$' read until $end
+ */
+static gboolean read_until(FILE *file, GString *dest, char mode)
+{
+ int c;
+ char prev[4] = "";
+
+ for(;;) {
+ c = fgetc(file);
+
+ if (c == EOF) {
+ if (mode == '$')
+ sr_err("Unexpected EOF.");
+ return FALSE;
+ }
+
+ if (mode == 'W' && g_ascii_isspace(c))
+ return TRUE;
+
+ if (mode == 'N' && !g_ascii_isspace(c)) {
+ ungetc(c, file);
+ return TRUE;
+ }
+
+ if (mode == '$') {
+ prev[0] = prev[1]; prev[1] = prev[2]; prev[2] = prev[3]; prev[3] = c;
+ if (prev[0] == '$' && prev[1] == 'e' && prev[2] == 'n' && prev[3] == 'd') {
+ if (dest != NULL)
+ g_string_truncate(dest, dest->len - 3);
+
+ return TRUE;
+ }
+ }
+
+ if (dest != NULL)
+ g_string_append_c(dest, c);
+ }
+}
+
+/*
+ * Reads a single VCD section from input file and parses it to structure.
+ * e.g. $timescale 1ps $end => "timescale" "1ps"
+ */
+static gboolean parse_section(FILE *file, gchar **name, gchar **contents)
+{
+ gboolean status;
+ GString *sname, *scontents;
+
+ /* Skip any initial white-space */
+ if (!read_until(file, NULL, 'N')) return FALSE;
+
+ /* Section tag should start with $. */
+ if (fgetc(file) != '$') {
+ sr_err("Expected $ at beginning of section.");
+ return FALSE;
+ }
+
+ /* Read the section tag */
+ sname = g_string_sized_new(32);
+ status = read_until(file, sname, 'W');
+
+ /* Skip whitespace before content */
+ status = status && read_until(file, NULL, 'N');
+
+ /* Read the content */
+ scontents = g_string_sized_new(128);
+ status = status && read_until(file, scontents, '$');
+ g_strchomp(scontents->str);
+
+ /* Release strings if status is FALSE, return them if status is TRUE */
+ *name = g_string_free(sname, !status);
+ *contents = g_string_free(scontents, !status);
+ return status;
+}
+
+static void free_channel(void *data)
+{
+ struct vcd_channel *vcd_ch = data;
+ g_free(vcd_ch->name);
+ g_free(vcd_ch->identifier);
+ g_free(vcd_ch);
+}
+
+static void release_context(struct context *ctx)
+{
+ g_slist_free_full(ctx->channels, free_channel);
+ g_free(ctx);
+}
+
+/* Remove empty parts from an array returned by g_strsplit. */
+static void remove_empty_parts(gchar **parts)
+{
+ gchar **src = parts;
+ gchar **dest = parts;
+ while (*src != NULL) {
+ if (**src != '\0')
+ *dest++ = *src;
+ src++;
+ }
+
+ *dest = NULL;
+}
+
+/*
+ * Parse VCD header to get values for context structure.
+ * The context structure should be zeroed before calling this.
+ */
+static gboolean parse_header(FILE *file, struct context *ctx)
+{
+ uint64_t p, q;
+ gchar *name = NULL, *contents = NULL;
+ gboolean status = FALSE;
+ struct vcd_channel *vcd_ch;
+
+ while (parse_section(file, &name, &contents)) {
+ sr_dbg("Section '%s', contents '%s'.", name, contents);
+
+ if (g_strcmp0(name, "enddefinitions") == 0) {
+ status = TRUE;
+ break;
+ } else if (g_strcmp0(name, "timescale") == 0) {
+ /*
+ * The standard allows for values 1, 10 or 100
+ * and units s, ms, us, ns, ps and fs.
+ * */
+ if (sr_parse_period(contents, &p, &q) == SR_OK) {
+ ctx->samplerate = q / p;
+ if (q % p != 0) {
+ /* Does not happen unless time value is non-standard */
+ sr_warn("Inexact rounding of samplerate, %" PRIu64 " / %" PRIu64 " to %" PRIu64 " Hz.",
+ q, p, ctx->samplerate);
+ }
+
+ sr_dbg("Samplerate: %" PRIu64, ctx->samplerate);
+ } else {
+ sr_err("Parsing timescale failed.");
+ }
+ } else if (g_strcmp0(name, "var") == 0) {
+ /* Format: $var type size identifier reference $end */
+ gchar **parts = g_strsplit_set(contents, " \r\n\t", 0);
+ remove_empty_parts(parts);
+
+ if (g_strv_length(parts) != 4)
+ sr_warn("$var section should have 4 items");
+ else if (g_strcmp0(parts[0], "reg") != 0 && g_strcmp0(parts[0], "wire") != 0)
+ sr_info("Unsupported signal type: '%s'", parts[0]);
+ else if (strtol(parts[1], NULL, 10) != 1)
+ sr_info("Unsupported signal size: '%s'", parts[1]);
+ else if (ctx->channelcount >= ctx->maxchannels)
+ sr_warn("Skipping '%s' because only %d channels requested.", parts[3], ctx->maxchannels);
+ else {
+ sr_info("Channel %d is '%s' identified by '%s'.", ctx->channelcount, parts[3], parts[2]);
+ vcd_ch = g_malloc(sizeof(struct vcd_channel));
+ vcd_ch->identifier = g_strdup(parts[2]);
+ vcd_ch->name = g_strdup(parts[3]);
+ ctx->channels = g_slist_append(ctx->channels, vcd_ch);
+ ctx->channelcount++;
+ }
+
+ g_strfreev(parts);
+ }
+
+ g_free(name); name = NULL;
+ g_free(contents); contents = NULL;
+ }
+
+ g_free(name);
+ g_free(contents);
+
+ return status;
+}
+
+static int format_match(const char *filename)
+{
+ FILE *file;
+ gchar *name = NULL, *contents = NULL;
+ gboolean status;
+
+ file = fopen(filename, "r");
+ if (file == NULL)
+ return FALSE;
+
+ /*
+ * If we can parse the first section correctly,
+ * then it is assumed to be a VCD file.
+ */
+ status = parse_section(file, &name, &contents);
+ status = status && (*name != '\0');
+
+ g_free(name);
+ g_free(contents);
+ fclose(file);
+
+ return status;
+}
+
+static int init(struct sr_input *in, const char *filename)
+{
+ struct sr_channel *ch;
+ int num_channels, i;
+ char name[SR_MAX_CHANNELNAME_LEN + 1];
+ char *param;
+ struct context *ctx;
+
+ (void)filename;
+
+ if (!(ctx = g_try_malloc0(sizeof(*ctx)))) {
+ sr_err("Input format context malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+
+ num_channels = DEFAULT_NUM_CHANNELS;
+ ctx->samplerate = 0;
+ ctx->downsample = 1;
+ ctx->skip = -1;
+
+ if (in->param) {
+ param = g_hash_table_lookup(in->param, "numchannels");
+ if (param) {
+ num_channels = strtoul(param, NULL, 10);
+ if (num_channels < 1) {
+ release_context(ctx);
+ return SR_ERR;
+ } else if (num_channels > 64) {
+ sr_err("No more than 64 channels supported.");
+ return SR_ERR;
+ }
+ }
+
+ param = g_hash_table_lookup(in->param, "downsample");
+ if (param) {
+ ctx->downsample = strtoul(param, NULL, 10);
+ if (ctx->downsample < 1)
+ ctx->downsample = 1;
+ }
+
+ param = g_hash_table_lookup(in->param, "compress");
+ if (param)
+ ctx->compress = strtoul(param, NULL, 10);
+
+ param = g_hash_table_lookup(in->param, "skip");
+ if (param)
+ ctx->skip = strtoul(param, NULL, 10) / ctx->downsample;
+ }
+
+ /* Maximum number of channels to parse from the VCD */
+ ctx->maxchannels = num_channels;
+
+ /* Create a virtual device. */
+ in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
+ in->internal = ctx;
+
+ for (i = 0; i < num_channels; i++) {
+ snprintf(name, SR_MAX_CHANNELNAME_LEN, "%d", i);
+
+ if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, name))) {
+ release_context(ctx);
+ return SR_ERR;
+ }
+
+ in->sdi->channels = g_slist_append(in->sdi->channels, ch);
+ }
+
+ return SR_OK;
+}
+
+/* Send N samples of the given value. */
+static void send_samples(const struct sr_dev_inst *sdi, uint64_t sample, uint64_t count)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ uint64_t buffer[CHUNKSIZE];
+ uint64_t i;
+ unsigned chunksize = CHUNKSIZE;
+
+ if (count < chunksize)
+ chunksize = count;
+
+ for (i = 0; i < chunksize; i++)
+ buffer[i] = sample;
+
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.unitsize = sizeof(uint64_t);
+ logic.data = buffer;
+
+ while (count) {
+ if (count < chunksize)
+ chunksize = count;
+
+ logic.length = sizeof(uint64_t) * chunksize;
+
+ sr_session_send(sdi, &packet);
+ count -= chunksize;
+ }
+}
+
+/* Parse the data section of VCD */
+static void parse_contents(FILE *file, const struct sr_dev_inst *sdi, struct context *ctx)
+{
+ GString *token = g_string_sized_new(32);
+
+ uint64_t prev_timestamp = 0;
+ uint64_t prev_values = 0;
+
+ /* Read one space-delimited token at a time. */
+ while (read_until(file, NULL, 'N') && read_until(file, token, 'W')) {
+ if (token->str[0] == '#' && g_ascii_isdigit(token->str[1])) {
+ /* Numeric value beginning with # is a new timestamp value */
+ uint64_t timestamp;
+ timestamp = strtoull(token->str + 1, NULL, 10);
+
+ if (ctx->downsample > 1)
+ timestamp /= ctx->downsample;
+
+ /*
+ * Skip < 0 => skip until first timestamp.
+ * Skip = 0 => don't skip
+ * Skip > 0 => skip until timestamp >= skip.
+ */
+ if (ctx->skip < 0) {
+ ctx->skip = timestamp;
+ prev_timestamp = timestamp;
+ } else if (ctx->skip > 0 && timestamp < (uint64_t)ctx->skip) {
+ prev_timestamp = ctx->skip;
+ }
+ else if (timestamp == prev_timestamp) {
+ /* Ignore repeated timestamps (e.g. sigrok outputs these) */
+ }
+ else {
+ if (ctx->compress != 0 && timestamp - prev_timestamp > ctx->compress)
+ {
+ /* Compress long idle periods */
+ prev_timestamp = timestamp - ctx->compress;
+ }
+
+ sr_dbg("New timestamp: %" PRIu64, timestamp);
+
+ /* Generate samples from prev_timestamp up to timestamp - 1. */
+ send_samples(sdi, prev_values, timestamp - prev_timestamp);
+ prev_timestamp = timestamp;
+ }
+ } else if (token->str[0] == '$' && token->len > 1) {
+ /* This is probably a $dumpvars, $comment or similar.
+ * $dump* contain useful data, but other tags will be skipped until $end. */
+ if (g_strcmp0(token->str, "$dumpvars") == 0
+ || g_strcmp0(token->str, "$dumpon") == 0
+ || g_strcmp0(token->str, "$dumpoff") == 0
+ || g_strcmp0(token->str, "$end") == 0) {
+ /* Ignore, parse contents as normally. */
+ } else {
+ /* Skip until $end */
+ read_until(file, NULL, '$');
+ }
+ }
+ else if (strchr("bBrR", token->str[0]) != NULL) {
+ /* A vector value. Skip it and also the following identifier. */
+ read_until(file, NULL, 'N');
+ read_until(file, NULL, 'W');
+ } else if (strchr("01xXzZ", token->str[0]) != NULL) {
+ /* A new 1-bit sample value */
+ int i, bit;
+ GSList *l;
+ struct vcd_channel *vcd_ch;
+
+ bit = (token->str[0] == '1');
+
+ g_string_erase(token, 0, 1);
+ if (token->len == 0) {
+ /* There was a space between value and identifier.
+ * Read in the rest.
+ */
+ read_until(file, NULL, 'N');
+ read_until(file, token, 'W');
+ }
+
+ for (i = 0, l = ctx->channels; i < ctx->channelcount && l; i++, l = l->next) {
+ vcd_ch = l->data;
+
+ if (g_strcmp0(token->str, vcd_ch->identifier) == 0) {
+ /* Found our channel */
+ if (bit)
+ prev_values |= (uint64_t)1 << i;
+ else
+ prev_values &= ~((uint64_t)1 << i);
+
+ break;
+ }
+ }
+
+ if (i == ctx->channelcount)
+ sr_dbg("Did not find channel for identifier '%s'.", token->str);
+ } else {
+ sr_warn("Skipping unknown token '%s'.", token->str);
+ }
+
+ g_string_truncate(token, 0);
+ }
+
+ g_string_free(token, TRUE);
+}
+
+static int loadfile(struct sr_input *in, const char *filename)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_meta meta;
+ struct sr_config *src;
+ FILE *file;
+ struct context *ctx;
+ uint64_t samplerate;
+
+ ctx = in->internal;
+
+ if ((file = fopen(filename, "r")) == NULL)
+ return SR_ERR;
+
+ if (!parse_header(file, ctx)) {
+ sr_err("VCD parsing failed");
+ fclose(file);
+ return SR_ERR;
+ }
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(in->sdi, LOG_PREFIX);
+
+ /* Send metadata about the SR_DF_LOGIC packets to come. */
+ packet.type = SR_DF_META;
+ packet.payload = &meta;
+ samplerate = ctx->samplerate / ctx->downsample;
+ src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate));
+ meta.config = g_slist_append(NULL, src);
+ sr_session_send(in->sdi, &packet);
+ sr_config_free(src);
+
+ /* Parse the contents of the VCD file */
+ parse_contents(file, in->sdi, ctx);
+
+ /* Send end packet to the session bus. */
+ packet.type = SR_DF_END;
+ sr_session_send(in->sdi, &packet);
+
+ fclose(file);
+ release_context(ctx);
+ in->internal = NULL;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_input_format input_vcd = {
+ .id = "vcd",
+ .description = "Value Change Dump",
+ .format_match = format_match,
+ .init = init,
+ .loadfile = loadfile,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/wav"
+
+#define CHUNK_SIZE 4096
+
+struct context {
+ uint64_t samplerate;
+ int samplesize;
+ int num_channels;
+};
+
+static int get_wav_header(const char *filename, char *buf)
+{
+ struct stat st;
+ int fd, l;
+
+ l = strlen(filename);
+ if (l <= 4 || strcasecmp(filename + l - 4, ".wav"))
+ return SR_ERR;
+
+ if (stat(filename, &st) == -1)
+ return SR_ERR;
+ if (st.st_size <= 45)
+ /* Minimum size of header + 1 8-bit mono PCM sample. */
+ return SR_ERR;
+
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return SR_ERR;
+
+ l = read(fd, buf, 40);
+ close(fd);
+ if (l != 40)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static int format_match(const char *filename)
+{
+ char buf[40];
+
+ if (get_wav_header(filename, buf) != SR_OK)
+ return FALSE;
+
+ if (strncmp(buf, "RIFF", 4))
+ return FALSE;
+ if (strncmp(buf + 8, "WAVE", 4))
+ return FALSE;
+ if (strncmp(buf + 12, "fmt ", 4))
+ return FALSE;
+ if (GUINT16_FROM_LE(*(uint16_t *)(buf + 20)) != 1)
+ /* Not PCM. */
+ return FALSE;
+ if (strncmp(buf + 36, "data", 4))
+ return FALSE;
+
+ return TRUE;
+}
+
+static int init(struct sr_input *in, const char *filename)
+{
+ struct sr_channel *ch;
+ struct context *ctx;
+ char buf[40], channelname[8];
+ int i;
+
+ if (get_wav_header(filename, buf) != SR_OK)
+ return SR_ERR;
+
+ if (!(ctx = g_try_malloc0(sizeof(struct context))))
+ return SR_ERR_MALLOC;
+
+ /* Create a virtual device. */
+ in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
+ in->sdi->priv = ctx;
+
+ ctx->samplerate = GUINT32_FROM_LE(*(uint32_t *)(buf + 24));
+ ctx->samplesize = GUINT16_FROM_LE(*(uint16_t *)(buf + 34)) / 8;
+ if (ctx->samplesize != 1 && ctx->samplesize != 2 && ctx->samplesize != 4) {
+ sr_err("only 8, 16 or 32 bits per sample supported.");
+ return SR_ERR;
+ }
+
+ if ((ctx->num_channels = GUINT16_FROM_LE(*(uint16_t *)(buf + 22))) > 20) {
+ sr_err("%d channels seems crazy.", ctx->num_channels);
+ return SR_ERR;
+ }
+
+ for (i = 0; i < ctx->num_channels; i++) {
+ snprintf(channelname, 8, "CH%d", i + 1);
+ if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, channelname)))
+ return SR_ERR;
+ in->sdi->channels = g_slist_append(in->sdi->channels, ch);
+ }
+
+ return SR_OK;
+}
+
+static int loadfile(struct sr_input *in, const char *filename)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_meta meta;
+ struct sr_datafeed_analog analog;
+ struct sr_config *src;
+ struct context *ctx;
+ float fdata[CHUNK_SIZE];
+ uint64_t sample;
+ int num_samples, chunk_samples, s, c, fd, l;
+ char buf[CHUNK_SIZE];
+
+ ctx = in->sdi->priv;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(in->sdi, LOG_PREFIX);
+
+ packet.type = SR_DF_META;
+ packet.payload = &meta;
+ src = sr_config_new(SR_CONF_SAMPLERATE,
+ g_variant_new_uint64(ctx->samplerate));
+ meta.config = g_slist_append(NULL, src);
+ sr_session_send(in->sdi, &packet);
+ sr_config_free(src);
+
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return SR_ERR;
+
+ lseek(fd, 40, SEEK_SET);
+ l = read(fd, buf, 4);
+ num_samples = GUINT32_FROM_LE((uint32_t)*(buf));
+ num_samples /= ctx->samplesize / ctx->num_channels;
+ while (TRUE) {
+ if ((l = read(fd, buf, CHUNK_SIZE)) < 1)
+ break;
+ chunk_samples = l / ctx->samplesize / ctx->num_channels;
+ for (s = 0; s < chunk_samples; s++) {
+ for (c = 0; c < ctx->num_channels; c++) {
+ sample = 0;
+ memcpy(&sample, buf + s * ctx->samplesize + c, ctx->samplesize);
+ switch (ctx->samplesize) {
+ case 1:
+ /* 8-bit PCM samples are unsigned. */
+ fdata[s + c] = (uint8_t)sample / 255.0;
+ break;
+ case 2:
+ fdata[s + c] = GINT16_FROM_LE(sample) / 32767.0;
+ break;
+ case 4:
+ fdata[s + c] = GINT32_FROM_LE(sample) / 65535.0;
+ break;
+ }
+ }
+ }
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ analog.channels = in->sdi->channels;
+ analog.num_samples = chunk_samples;
+ analog.mq = 0;
+ analog.unit = 0;
+ analog.data = fdata;
+ sr_session_send(in->sdi, &packet);
+ }
+
+ close(fd);
+ packet.type = SR_DF_END;
+ sr_session_send(in->sdi, &packet);
+
+ return SR_OK;
+}
+
+
+SR_PRIV struct sr_input_format input_wav = {
+ .id = "wav",
+ .description = "WAV file",
+ .format_match = format_match,
+ .init = init,
+ .loadfile = loadfile,
+};
+
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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/>.
+ */
+
+/** @file
+ * @internal
+ */
+
+#ifndef LIBSIGROK_LIBSIGROK_INTERNAL_H
+#define LIBSIGROK_LIBSIGROK_INTERNAL_H
+
+#include <stdarg.h>
+#include <glib.h>
+#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
+#ifdef HAVE_LIBUSB_1_0
+#include <libusb.h>
+#endif
+#ifdef HAVE_LIBSERIALPORT
+#include <libserialport.h>
+#endif
+
+/**
+ * @file
+ *
+ * libsigrok private header file, only to be used internally.
+ */
+
+/*--- Macros ----------------------------------------------------------------*/
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef ARRAY_AND_SIZE
+#define ARRAY_AND_SIZE(a) (a), ARRAY_SIZE(a)
+#endif
+
+/**
+ * Read a 8 bits integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding integer
+ */
+#define R8(x) ((unsigned)((const uint8_t*)(x))[0])
+
+/**
+ * Read a 16 bits big endian integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding integer
+ */
+#define RB16(x) (((unsigned)((const uint8_t*)(x))[0] << 8) | \
+ (unsigned)((const uint8_t*)(x))[1])
+
+/**
+ * Read a 16 bits little endian integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding integer
+ */
+#define RL16(x) (((unsigned)((const uint8_t*)(x))[1] << 8) | \
+ (unsigned)((const uint8_t*)(x))[0])
+
+/**
+ * Read a 32 bits big endian integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding integer
+ */
+#define RB32(x) (((unsigned)((const uint8_t*)(x))[0] << 24) | \
+ ((unsigned)((const uint8_t*)(x))[1] << 16) | \
+ ((unsigned)((const uint8_t*)(x))[2] << 8) | \
+ (unsigned)((const uint8_t*)(x))[3])
+
+/**
+ * Read a 32 bits little endian integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding integer
+ */
+#define RL32(x) (((unsigned)((const uint8_t*)(x))[3] << 24) | \
+ ((unsigned)((const uint8_t*)(x))[2] << 16) | \
+ ((unsigned)((const uint8_t*)(x))[1] << 8) | \
+ (unsigned)((const uint8_t*)(x))[0])
+
+/**
+ * Write a 8 bits integer to memory.
+ * @param p a pointer to the output memory
+ * @param x the input integer
+ */
+#define W8(p, x) do { ((uint8_t*)(p))[0] = (uint8_t) (x); } while(0)
+
+/**
+ * Write a 16 bits integer to memory stored as big endian.
+ * @param p a pointer to the output memory
+ * @param x the input integer
+ */
+#define WB16(p, x) do { ((uint8_t*)(p))[1] = (uint8_t) (x); \
+ ((uint8_t*)(p))[0] = (uint8_t)((x)>>8); } while(0)
+
+/**
+ * Write a 16 bits integer to memory stored as little endian.
+ * @param p a pointer to the output memory
+ * @param x the input integer
+ */
+#define WL16(p, x) do { ((uint8_t*)(p))[0] = (uint8_t) (x); \
+ ((uint8_t*)(p))[1] = (uint8_t)((x)>>8); } while(0)
+
+/**
+ * Write a 32 bits integer to memory stored as big endian.
+ * @param p a pointer to the output memory
+ * @param x the input integer
+ */
+#define WB32(p, x) do { ((uint8_t*)(p))[3] = (uint8_t) (x); \
+ ((uint8_t*)(p))[2] = (uint8_t)((x)>>8); \
+ ((uint8_t*)(p))[1] = (uint8_t)((x)>>16); \
+ ((uint8_t*)(p))[0] = (uint8_t)((x)>>24); } while(0)
+
+/**
+ * Write a 32 bits integer to memory stored as little endian.
+ * @param p a pointer to the output memory
+ * @param x the input integer
+ */
+#define WL32(p, x) do { ((uint8_t*)(p))[0] = (uint8_t) (x); \
+ ((uint8_t*)(p))[1] = (uint8_t)((x)>>8); \
+ ((uint8_t*)(p))[2] = (uint8_t)((x)>>16); \
+ ((uint8_t*)(p))[3] = (uint8_t)((x)>>24); } while(0)
+
+/* Portability fixes for FreeBSD. */
+#ifdef __FreeBSD__
+#define LIBUSB_CLASS_APPLICATION 0xfe
+#define libusb_handle_events_timeout_completed(ctx, tv, c) \
+ libusb_handle_events_timeout(ctx, tv)
+#endif
+
+struct sr_context {
+#ifdef HAVE_LIBUSB_1_0
+ libusb_context *libusb_ctx;
+ gboolean usb_source_present;
+#ifdef _WIN32
+ GThread *usb_thread;
+ gboolean usb_thread_running;
+ GMutex usb_mutex;
+ HANDLE usb_event;
+ GPollFD usb_pollfd;
+ sr_receive_data_callback usb_cb;
+ void *usb_cb_data;
+#endif
+#endif
+};
+
+#ifdef HAVE_LIBUSB_1_0
+/** USB device instance */
+struct sr_usb_dev_inst {
+ /** USB bus */
+ uint8_t bus;
+ /** Device address on USB bus */
+ uint8_t address;
+ /** libusb device handle */
+ struct libusb_device_handle *devhdl;
+};
+#endif
+
+#ifdef HAVE_LIBSERIALPORT
+#define SERIAL_PARITY_NONE SP_PARITY_NONE
+#define SERIAL_PARITY_EVEN SP_PARITY_EVEN
+#define SERIAL_PARITY_ODD SP_PARITY_ODD
+struct sr_serial_dev_inst {
+ /** Port name, e.g. '/dev/tty42'. */
+ char *port;
+ /** Comm params for serial_set_paramstr(). */
+ char *serialcomm;
+ /** Port is non-blocking. */
+ int nonblocking;
+ /** libserialport port handle */
+ struct sp_port *data;
+ /** libserialport event set */
+ struct sp_event_set *event_set;
+ /** GPollFDs for event polling */
+ GPollFD *pollfds;
+};
+#endif
+
+struct sr_usbtmc_dev_inst {
+ char *device;
+ int fd;
+};
+
+/* Private driver context. */
+struct drv_context {
+ /** sigrok context */
+ struct sr_context *sr_ctx;
+ GSList *instances;
+};
+
+/*--- log.c -----------------------------------------------------------------*/
+
+SR_PRIV int sr_log(int loglevel, const char *format, ...);
+SR_PRIV int sr_spew(const char *format, ...);
+SR_PRIV int sr_dbg(const char *format, ...);
+SR_PRIV int sr_info(const char *format, ...);
+SR_PRIV int sr_warn(const char *format, ...);
+SR_PRIV int sr_err(const char *format, ...);
+
+/* Message logging helpers with subsystem-specific prefix string. */
+#ifndef NO_LOG_WRAPPERS
+#define sr_log(l, s, args...) sr_log(l, "%s: " s, LOG_PREFIX, ## args)
+#define sr_spew(s, args...) sr_spew("%s: " s, LOG_PREFIX, ## args)
+#define sr_dbg(s, args...) sr_dbg("%s: " s, LOG_PREFIX, ## args)
+#define sr_info(s, args...) sr_info("%s: " s, LOG_PREFIX, ## args)
+#define sr_warn(s, args...) sr_warn("%s: " s, LOG_PREFIX, ## args)
+#define sr_err(s, args...) sr_err("%s: " s, LOG_PREFIX, ## args)
+#endif
+
+/*--- device.c --------------------------------------------------------------*/
+
+/** Values for the changes argument of sr_dev_driver.config_channel_set. */
+enum {
+ /** The enabled state of the channel has been changed. */
+ SR_CHANNEL_SET_ENABLED = 1 << 0,
+};
+
+SR_PRIV struct sr_channel *sr_channel_new(int index, int type,
+ gboolean enabled, const char *name);
+
+/* Generic device instances */
+SR_PRIV struct sr_dev_inst *sr_dev_inst_new(int index, int status,
+ const char *vendor, const char *model, const char *version);
+SR_PRIV void sr_dev_inst_free(struct sr_dev_inst *sdi);
+
+#ifdef HAVE_LIBUSB_1_0
+/* USB-specific instances */
+SR_PRIV struct sr_usb_dev_inst *sr_usb_dev_inst_new(uint8_t bus,
+ uint8_t address, struct libusb_device_handle *hdl);
+SR_PRIV void sr_usb_dev_inst_free(struct sr_usb_dev_inst *usb);
+#endif
+
+#ifdef HAVE_LIBSERIALPORT
+/* Serial-specific instances */
+SR_PRIV struct sr_serial_dev_inst *sr_serial_dev_inst_new(const char *port,
+ const char *serialcomm);
+SR_PRIV void sr_serial_dev_inst_free(struct sr_serial_dev_inst *serial);
+#endif
+
+/* USBTMC-specific instances */
+SR_PRIV struct sr_usbtmc_dev_inst *sr_usbtmc_dev_inst_new(const char *device);
+SR_PRIV void sr_usbtmc_dev_inst_free(struct sr_usbtmc_dev_inst *usbtmc);
+
+/*--- hwdriver.c ------------------------------------------------------------*/
+
+SR_PRIV void sr_hw_cleanup_all(void);
+SR_PRIV struct sr_config *sr_config_new(int key, GVariant *data);
+SR_PRIV void sr_config_free(struct sr_config *src);
+SR_PRIV int sr_source_remove(int fd);
+SR_PRIV int sr_source_remove_pollfd(GPollFD *pollfd);
+SR_PRIV int sr_source_remove_channel(GIOChannel *channel);
+SR_PRIV int sr_source_add(int fd, int events, int timeout,
+ sr_receive_data_callback cb, void *cb_data);
+SR_PRIV int sr_source_add_pollfd(GPollFD *pollfd, int timeout,
+ sr_receive_data_callback cb, void *cb_data);
+SR_PRIV int sr_source_add_channel(GIOChannel *channel, int events, int timeout,
+ sr_receive_data_callback cb, void *cb_data);
+
+/*--- session.c -------------------------------------------------------------*/
+
+struct sr_session {
+ /** List of struct sr_dev pointers. */
+ GSList *devs;
+ /** List of struct datafeed_callback pointers. */
+ GSList *datafeed_callbacks;
+ struct sr_trigger *trigger;
+ GTimeVal starttime;
+ gboolean running;
+
+ unsigned int num_sources;
+
+ /*
+ * Both "sources" and "pollfds" are of the same size and contain pairs
+ * of descriptor and callback function. We can not embed the GPollFD
+ * into the source struct since we want to be able to pass the array
+ * of all poll descriptors to g_poll().
+ */
+ struct source *sources;
+ GPollFD *pollfds;
+ int source_timeout;
+
+ /*
+ * These are our synchronization primitives for stopping the session in
+ * an async fashion. We need to make sure the session is stopped from
+ * within the session thread itself.
+ */
+ /** Mutex protecting access to abort_session. */
+ GMutex stop_mutex;
+ /** Abort current session. See sr_session_stop(). */
+ gboolean abort_session;
+};
+
+SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
+ const struct sr_datafeed_packet *packet);
+SR_PRIV int sr_session_stop_sync(struct sr_session *session);
+SR_PRIV int sr_sessionfile_check(const char *filename);
+
+/*--- std.c -----------------------------------------------------------------*/
+
+typedef int (*dev_close_callback)(struct sr_dev_inst *sdi);
+typedef void (*std_dev_clear_callback)(void *priv);
+
+SR_PRIV int std_init(struct sr_context *sr_ctx, struct sr_dev_driver *di,
+ const char *prefix);
+#ifdef HAVE_LIBSERIALPORT
+SR_PRIV int std_serial_dev_open(struct sr_dev_inst *sdi);
+SR_PRIV int std_serial_dev_acquisition_stop(struct sr_dev_inst *sdi,
+ void *cb_data, dev_close_callback dev_close_fn,
+ struct sr_serial_dev_inst *serial, const char *prefix);
+#endif
+SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi,
+ const char *prefix);
+SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
+ std_dev_clear_callback clear_private);
+SR_PRIV int std_serial_dev_close(struct sr_dev_inst *sdi);
+
+/*--- strutil.c -------------------------------------------------------------*/
+
+SR_PRIV int sr_atol(const char *str, long *ret);
+SR_PRIV int sr_atoi(const char *str, int *ret);
+SR_PRIV int sr_atod(const char *str, double *ret);
+SR_PRIV int sr_atof(const char *str, float *ret);
+SR_PRIV int sr_atof_ascii(const char *str, float *ret);
+
+/*--- soft-trigger.c --------------------------------------------------------*/
+
+struct soft_trigger_logic {
+ const struct sr_dev_inst *sdi;
+ const struct sr_trigger *trigger;
+ int count;
+ int unitsize;
+ int cur_stage;
+ uint8_t *prev_sample;
+};
+
+SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
+ const struct sr_dev_inst *sdi, struct sr_trigger *trigger);
+SR_PRIV void soft_trigger_logic_free(struct soft_trigger_logic *st);
+SR_PRIV int soft_trigger_logic_check(struct soft_trigger_logic *st, uint8_t *buf,
+ int len);
+
+/*--- hardware/common/serial.c ----------------------------------------------*/
+
+#ifdef HAVE_LIBSERIALPORT
+enum {
+ SERIAL_RDWR = 1,
+ SERIAL_RDONLY = 2,
+ SERIAL_NONBLOCK = 4,
+};
+
+typedef gboolean (*packet_valid_callback)(const uint8_t *buf);
+
+SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags);
+SR_PRIV int serial_close(struct sr_serial_dev_inst *serial);
+SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial);
+SR_PRIV int serial_write(struct sr_serial_dev_inst *serial,
+ const void *buf, size_t count);
+SR_PRIV int serial_write_blocking(struct sr_serial_dev_inst *serial,
+ const void *buf, size_t count);
+SR_PRIV int serial_write_nonblocking(struct sr_serial_dev_inst *serial,
+ const void *buf, size_t count);
+SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf,
+ size_t count);
+SR_PRIV int serial_read_blocking(struct sr_serial_dev_inst *serial, void *buf,
+ size_t count);
+SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf,
+ size_t count);
+SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate,
+ int bits, int parity, int stopbits, int flowcontrol, int rts, int dtr);
+SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
+ const char *paramstr);
+SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
+ int *buflen, gint64 timeout_ms);
+SR_PRIV int serial_stream_detect(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, int baudrate);
+SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_device,
+ const char **serial_options);
+SR_PRIV int serial_source_add(struct sr_session *session,
+ struct sr_serial_dev_inst *serial, int events, int timeout,
+ sr_receive_data_callback cb, void *cb_data);
+SR_PRIV int serial_source_remove(struct sr_session *session,
+ struct sr_serial_dev_inst *serial);
+SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id);
+#endif
+
+/*--- hardware/common/ezusb.c -----------------------------------------------*/
+
+#ifdef HAVE_LIBUSB_1_0
+SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear);
+SR_PRIV int ezusb_install_firmware(libusb_device_handle *hdl,
+ const char *filename);
+SR_PRIV int ezusb_upload_firmware(libusb_device *dev, int configuration,
+ const char *filename);
+#endif
+
+/*--- hardware/common/usb.c -------------------------------------------------*/
+
+#ifdef HAVE_LIBUSB_1_0
+SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn);
+SR_PRIV int sr_usb_open(libusb_context *usb_ctx, struct sr_usb_dev_inst *usb);
+SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx,
+ int timeout, sr_receive_data_callback cb, void *cb_data);
+SR_PRIV int usb_source_remove(struct sr_session *session, struct sr_context *ctx);
+#endif
+
+/*--- hardware/common/scpi.c ------------------------------------------------*/
+
+#define SCPI_CMD_IDN "*IDN?"
+#define SCPI_CMD_OPC "*OPC?"
+
+enum {
+ SCPI_CMD_SET_TRIGGER_SOURCE,
+ SCPI_CMD_SET_TIMEBASE,
+ SCPI_CMD_SET_VERTICAL_DIV,
+ SCPI_CMD_SET_TRIGGER_SLOPE,
+ SCPI_CMD_SET_COUPLING,
+ SCPI_CMD_SET_HORIZ_TRIGGERPOS,
+ SCPI_CMD_GET_ANALOG_CHAN_STATE,
+ SCPI_CMD_GET_DIG_CHAN_STATE,
+ SCPI_CMD_GET_TIMEBASE,
+ SCPI_CMD_GET_VERTICAL_DIV,
+ SCPI_CMD_GET_VERTICAL_OFFSET,
+ SCPI_CMD_GET_TRIGGER_SOURCE,
+ SCPI_CMD_GET_HORIZ_TRIGGERPOS,
+ SCPI_CMD_GET_TRIGGER_SLOPE,
+ SCPI_CMD_GET_COUPLING,
+ SCPI_CMD_SET_ANALOG_CHAN_STATE,
+ SCPI_CMD_SET_DIG_CHAN_STATE,
+ SCPI_CMD_GET_DIG_POD_STATE,
+ SCPI_CMD_SET_DIG_POD_STATE,
+ SCPI_CMD_GET_ANALOG_DATA,
+ SCPI_CMD_GET_DIG_DATA,
+ SCPI_CMD_GET_SAMPLE_RATE,
+ SCPI_CMD_GET_SAMPLE_RATE_LIVE,
+};
+
+struct sr_scpi_hw_info {
+ char *manufacturer;
+ char *model;
+ char *serial_number;
+ char *firmware_version;
+};
+
+struct sr_scpi_dev_inst {
+ const char *name;
+ const char *prefix;
+ int priv_size;
+ GSList *(*scan)(struct drv_context *drvc);
+ int (*dev_inst_new)(void *priv, struct drv_context *drvc,
+ const char *resource, char **params, const char *serialcomm);
+ int (*open)(void *priv);
+ int (*source_add)(struct sr_session *session, void *priv, int events,
+ int timeout, sr_receive_data_callback cb, void *cb_data);
+ int (*source_remove)(struct sr_session *session, void *priv);
+ int (*send)(void *priv, const char *command);
+ int (*read_begin)(void *priv);
+ int (*read_data)(void *priv, char *buf, int maxlen);
+ int (*read_complete)(void *priv);
+ int (*close)(void *priv);
+ void (*free)(void *priv);
+ void *priv;
+};
+
+SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
+ struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi));
+SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc,
+ const char *resource, const char *serialcomm);
+SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi);
+SR_PRIV int sr_scpi_source_add(struct sr_session *session,
+ struct sr_scpi_dev_inst *scpi, int events, int timeout,
+ sr_receive_data_callback cb, void *cb_data);
+SR_PRIV int sr_scpi_source_remove(struct sr_session *session,
+ struct sr_scpi_dev_inst *scpi);
+SR_PRIV int sr_scpi_send(struct sr_scpi_dev_inst *scpi,
+ const char *format, ...);
+SR_PRIV int sr_scpi_send_variadic(struct sr_scpi_dev_inst *scpi,
+ const char *format, va_list args);
+SR_PRIV int sr_scpi_read_begin(struct sr_scpi_dev_inst *scpi);
+SR_PRIV int sr_scpi_read_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen);
+SR_PRIV int sr_scpi_read_complete(struct sr_scpi_dev_inst *scpi);
+SR_PRIV int sr_scpi_close(struct sr_scpi_dev_inst *scpi);
+SR_PRIV void sr_scpi_free(struct sr_scpi_dev_inst *scpi);
+
+SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
+ const char *command, char **scpi_response);
+SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi,
+ const char *command, gboolean *scpi_response);
+SR_PRIV int sr_scpi_get_int(struct sr_scpi_dev_inst *scpi,
+ const char *command, int *scpi_response);
+SR_PRIV int sr_scpi_get_float(struct sr_scpi_dev_inst *scpi,
+ const char *command, float *scpi_response);
+SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi,
+ const char *command, double *scpi_response);
+SR_PRIV int sr_scpi_get_opc(struct sr_scpi_dev_inst *scpi);
+SR_PRIV int sr_scpi_get_floatv(struct sr_scpi_dev_inst *scpi,
+ const char *command, GArray **scpi_response);
+SR_PRIV int sr_scpi_get_uint8v(struct sr_scpi_dev_inst *scpi,
+ const char *command, GArray **scpi_response);
+SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
+ struct sr_scpi_hw_info **scpi_response);
+SR_PRIV void sr_scpi_hw_info_free(struct sr_scpi_hw_info *hw_info);
+
+/*--- hardware/common/dmm/es519xx.c -----------------------------------------*/
+
+/**
+ * All 11-byte es519xx chips repeat each block twice for each conversion cycle
+ * so always read 2 blocks at a time.
+ */
+#define ES519XX_11B_PACKET_SIZE (11 * 2)
+#define ES519XX_14B_PACKET_SIZE 14
+
+struct es519xx_info {
+ gboolean is_judge, is_voltage, is_auto, is_micro, is_current;
+ gboolean is_milli, is_resistance, is_continuity, is_diode;
+ gboolean is_frequency, is_rpm, is_capacitance, is_duty_cycle;
+ gboolean is_temperature, is_celsius, is_fahrenheit;
+ gboolean is_adp0, is_adp1, is_adp2, is_adp3;
+ gboolean is_sign, is_batt, is_ol, is_pmax, is_pmin, is_apo;
+ gboolean is_dc, is_ac, is_vahz, is_min, is_max, is_rel, is_hold;
+ gboolean is_digit4, is_ul, is_vasel, is_vbar, is_lpf1, is_lpf0, is_rmr;
+ uint32_t baudrate;
+ int packet_size;
+ gboolean alt_functions, fivedigits, clampmeter, selectable_lpf;
+};
+
+SR_PRIV gboolean sr_es519xx_2400_11b_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_2400_11b_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+SR_PRIV gboolean sr_es519xx_2400_11b_altfn_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_2400_11b_altfn_parse(const uint8_t *buf,
+ float *floatval, struct sr_datafeed_analog *analog, void *info);
+SR_PRIV gboolean sr_es519xx_19200_11b_5digits_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_19200_11b_5digits_parse(const uint8_t *buf,
+ float *floatval, struct sr_datafeed_analog *analog, void *info);
+SR_PRIV gboolean sr_es519xx_19200_11b_clamp_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_19200_11b_clamp_parse(const uint8_t *buf,
+ float *floatval, struct sr_datafeed_analog *analog, void *info);
+SR_PRIV gboolean sr_es519xx_19200_11b_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_19200_11b_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+SR_PRIV gboolean sr_es519xx_19200_14b_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_19200_14b_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+SR_PRIV gboolean sr_es519xx_19200_14b_sel_lpf_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_19200_14b_sel_lpf_parse(const uint8_t *buf,
+ float *floatval, struct sr_datafeed_analog *analog, void *info);
+
+/*--- hardware/common/dmm/fs9922.c ------------------------------------------*/
+
+#define FS9922_PACKET_SIZE 14
+
+struct fs9922_info {
+ gboolean is_auto, is_dc, is_ac, is_rel, is_hold, is_bpn, is_z1, is_z2;
+ gboolean is_max, is_min, is_apo, is_bat, is_nano, is_z3, is_micro;
+ gboolean is_milli, is_kilo, is_mega, is_beep, is_diode, is_percent;
+ gboolean is_z4, is_volt, is_ampere, is_ohm, is_hfe, is_hertz, is_farad;
+ gboolean is_celsius, is_fahrenheit;
+ int bargraph_sign, bargraph_value;
+};
+
+SR_PRIV gboolean sr_fs9922_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_fs9922_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+SR_PRIV void sr_fs9922_z1_diode(struct sr_datafeed_analog *analog, void *info);
+
+/*--- hardware/common/dmm/fs9721.c ------------------------------------------*/
+
+#define FS9721_PACKET_SIZE 14
+
+struct fs9721_info {
+ gboolean is_ac, is_dc, is_auto, is_rs232, is_micro, is_nano, is_kilo;
+ gboolean is_diode, is_milli, is_percent, is_mega, is_beep, is_farad;
+ gboolean is_ohm, is_rel, is_hold, is_ampere, is_volt, is_hz, is_bat;
+ gboolean is_c2c1_11, is_c2c1_10, is_c2c1_01, is_c2c1_00, is_sign;
+};
+
+SR_PRIV gboolean sr_fs9721_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+SR_PRIV void sr_fs9721_00_temp_c(struct sr_datafeed_analog *analog, void *info);
+SR_PRIV void sr_fs9721_01_temp_c(struct sr_datafeed_analog *analog, void *info);
+SR_PRIV void sr_fs9721_10_temp_c(struct sr_datafeed_analog *analog, void *info);
+SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog *analog, void *info);
+SR_PRIV void sr_fs9721_max_c_min(struct sr_datafeed_analog *analog, void *info);
+
+/*--- hardware/common/dmm/m2110.c -----------------------------------------*/
+
+#define BBCGM_M2110_PACKET_SIZE 9
+
+SR_PRIV gboolean sr_m2110_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_m2110_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+
+/*--- hardware/common/dmm/metex14.c -----------------------------------------*/
+
+#define METEX14_PACKET_SIZE 14
+
+struct metex14_info {
+ gboolean is_ac, is_dc, is_resistance, is_capacity, is_temperature;
+ gboolean is_diode, is_frequency, is_ampere, is_volt, is_farad;
+ gboolean is_hertz, is_ohm, is_celsius, is_pico, is_nano, is_micro;
+ gboolean is_milli, is_kilo, is_mega, is_gain, is_decibel, is_hfe;
+ gboolean is_unitless, is_logic;
+};
+
+#ifdef HAVE_LIBSERIALPORT
+SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial);
+#endif
+SR_PRIV gboolean sr_metex14_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_metex14_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+
+/*--- hardware/common/dmm/rs9lcd.c ------------------------------------------*/
+
+#define RS9LCD_PACKET_SIZE 9
+
+/* Dummy info struct. The parser does not use it. */
+struct rs9lcd_info { int dummy; };
+
+SR_PRIV gboolean sr_rs9lcd_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_rs9lcd_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011-2012 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "libsigrok.h"
+/** @cond PRIVATE */
+#define NO_LOG_WRAPPERS
+/** @endcond */
+#include "libsigrok-internal.h"
+
+/**
+ * @file
+ *
+ * Controlling the libsigrok message logging functionality.
+ */
+
+/**
+ * @defgroup grp_logging Logging
+ *
+ * Controlling the libsigrok message logging functionality.
+ *
+ * @{
+ */
+
+/* Currently selected libsigrok loglevel. Default: SR_LOG_WARN. */
+static int cur_loglevel = SR_LOG_WARN; /* Show errors+warnings per default. */
+
+/* Function prototype. */
+static int sr_logv(void *cb_data, int loglevel, const char *format,
+ va_list args);
+
+/* Pointer to the currently selected log callback. Default: sr_logv(). */
+static sr_log_callback sr_log_cb = sr_logv;
+
+/*
+ * Pointer to private data that can be passed to the log callback.
+ * This can be used (for example) by C++ GUIs to pass a "this" pointer.
+ */
+static void *sr_log_cb_data = NULL;
+
+/* Log domain (a short string that is used as prefix for all messages). */
+/** @cond PRIVATE */
+#define LOGDOMAIN_MAXLEN 30
+#define LOGDOMAIN_DEFAULT "sr: "
+/** @endcond */
+static char sr_log_domain[LOGDOMAIN_MAXLEN + 1] = LOGDOMAIN_DEFAULT;
+
+/**
+ * Set the libsigrok loglevel.
+ *
+ * This influences the amount of log messages (debug messages, error messages,
+ * and so on) libsigrok will output. Using SR_LOG_NONE disables all messages.
+ *
+ * Note that this function itself will also output log messages. After the
+ * loglevel has changed, it will output a debug message with SR_LOG_DBG for
+ * example. Whether this message is shown depends on the (new) loglevel.
+ *
+ * @param loglevel The loglevel to set (SR_LOG_NONE, SR_LOG_ERR, SR_LOG_WARN,
+ * SR_LOG_INFO, SR_LOG_DBG, or SR_LOG_SPEW).
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid loglevel.
+ *
+ * @since 0.1.0
+ */
+SR_API int sr_log_loglevel_set(int loglevel)
+{
+ if (loglevel < SR_LOG_NONE || loglevel > SR_LOG_SPEW) {
+ sr_err("Invalid loglevel %d.", loglevel);
+ return SR_ERR_ARG;
+ }
+
+ cur_loglevel = loglevel;
+
+ sr_dbg("libsigrok loglevel set to %d.", loglevel);
+
+ return SR_OK;
+}
+
+/**
+ * Get the libsigrok loglevel.
+ *
+ * @return The currently configured libsigrok loglevel.
+ *
+ * @since 0.1.0
+ */
+SR_API int sr_log_loglevel_get(void)
+{
+ return cur_loglevel;
+}
+
+/**
+ * Set the libsigrok logdomain string.
+ *
+ * @param logdomain The string to use as logdomain for libsigrok log
+ * messages from now on. Must not be NULL. The maximum
+ * length of the string is 30 characters (this does not
+ * include the trailing NUL-byte). Longer strings are
+ * silently truncated.
+ * In order to not use a logdomain, pass an empty string.
+ * The function makes its own copy of the input string, i.e.
+ * the caller does not need to keep it around.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid logdomain.
+ *
+ * @since 0.1.0
+ */
+SR_API int sr_log_logdomain_set(const char *logdomain)
+{
+ if (!logdomain) {
+ sr_err("log: %s: logdomain was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ /* TODO: Error handling. */
+ snprintf((char *)&sr_log_domain, LOGDOMAIN_MAXLEN, "%s", logdomain);
+
+ sr_dbg("Log domain set to '%s'.", (const char *)&sr_log_domain);
+
+ return SR_OK;
+}
+
+/**
+ * Get the currently configured libsigrok logdomain.
+ *
+ * @return A copy of the currently configured libsigrok logdomain
+ * string. The caller is responsible for g_free()ing the string when
+ * it is no longer needed.
+ *
+ * @since 0.1.0
+ */
+SR_API char *sr_log_logdomain_get(void)
+{
+ return g_strdup((const char *)&sr_log_domain);
+}
+
+/**
+ * Set the libsigrok log callback to the specified function.
+ *
+ * @param cb Function pointer to the log callback function to use.
+ * Must not be NULL.
+ * @param cb_data Pointer to private data to be passed on. This can be used by
+ * the caller to pass arbitrary data to the log functions. This
+ * pointer is only stored or passed on by libsigrok, and is
+ * never used or interpreted in any way. The pointer is allowed
+ * to be NULL if the caller doesn't need/want to pass any data.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_log_callback_set(sr_log_callback cb, void *cb_data)
+{
+ if (!cb) {
+ sr_err("log: %s: cb was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ /* Note: 'cb_data' is allowed to be NULL. */
+
+ sr_log_cb = cb;
+ sr_log_cb_data = cb_data;
+
+ return SR_OK;
+}
+
+/**
+ * Set the libsigrok log callback to the default built-in one.
+ *
+ * Additionally, the internal 'sr_log_cb_data' pointer is set to NULL.
+ *
+ * @return SR_OK upon success, a negative error code otherwise.
+ *
+ * @since 0.1.0
+ */
+SR_API int sr_log_callback_set_default(void)
+{
+ /*
+ * Note: No log output in this function, as it should safely work
+ * even if the currently set log callback is buggy/broken.
+ */
+ sr_log_cb = sr_logv;
+ sr_log_cb_data = NULL;
+
+ return SR_OK;
+}
+
+static int sr_logv(void *cb_data, int loglevel, const char *format, va_list args)
+{
+ int ret;
+
+ /* This specific log callback doesn't need the void pointer data. */
+ (void)cb_data;
+
+ /* Only output messages of at least the selected loglevel(s). */
+ if (loglevel > cur_loglevel)
+ return SR_OK; /* TODO? */
+
+ if (sr_log_domain[0] != '\0')
+ fprintf(stderr, "%s", sr_log_domain);
+ ret = vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+
+ return ret;
+}
+
+/** @private */
+SR_PRIV int sr_log(int loglevel, const char *format, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = sr_log_cb(sr_log_cb_data, loglevel, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/** @private */
+SR_PRIV int sr_spew(const char *format, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = sr_log_cb(sr_log_cb_data, SR_LOG_SPEW, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/** @private */
+SR_PRIV int sr_dbg(const char *format, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = sr_log_cb(sr_log_cb_data, SR_LOG_DBG, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/** @private */
+SR_PRIV int sr_info(const char *format, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = sr_log_cb(sr_log_cb_data, SR_LOG_INFO, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/** @private */
+SR_PRIV int sr_warn(const char *format, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = sr_log_cb(sr_log_cb_data, SR_LOG_WARN, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/** @private */
+SR_PRIV int sr_err(const char *format, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = sr_log_cb(sr_log_cb_data, SR_LOG_ERR, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/** @} */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/analog"
+
+struct context {
+ int num_enabled_channels;
+ GPtrArray *channellist;
+};
+
+static int init(struct sr_output *o)
+{
+ struct context *ctx;
+ struct sr_channel *ch;
+ GSList *l;
+
+ sr_spew("Initializing output module.");
+
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+
+ if (!(ctx = g_try_malloc0(sizeof(struct context)))) {
+ sr_err("Output module context malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+ o->internal = ctx;
+
+ /* Get the number of channels and their names. */
+ ctx->channellist = g_ptr_array_new();
+ for (l = o->sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (!ch || !ch->enabled)
+ continue;
+ g_ptr_array_add(ctx->channellist, ch->name);
+ ctx->num_enabled_channels++;
+ }
+
+ return SR_OK;
+}
+
+static void si_printf(float value, GString *out, char *unitstr)
+{
+ float v;
+
+ if (signbit(value))
+ v = -(value);
+ else
+ v = value;
+
+ if (v < 1e-12 || v > 1e+12)
+ g_string_append_printf(out, "%f %s", value, unitstr);
+ else if (v > 1e+9)
+ g_string_append_printf(out, "%f G%s", value / 1e+9, unitstr);
+ else if (v > 1e+6)
+ g_string_append_printf(out, "%f M%s", value / 1e+6, unitstr);
+ else if (v > 1e+3)
+ g_string_append_printf(out, "%f k%s", value / 1e+3, unitstr);
+ else if (v < 1e-9)
+ g_string_append_printf(out, "%f n%s", value * 1e+9, unitstr);
+ else if (v < 1e-6)
+ g_string_append_printf(out, "%f u%s", value * 1e+6, unitstr);
+ else if (v < 1e-3)
+ g_string_append_printf(out, "%f m%s", value * 1e+3, unitstr);
+ else
+ g_string_append_printf(out, "%f %s", value, unitstr);
+
+}
+
+static void fancyprint(int unit, int mqflags, float value, GString *out)
+{
+ switch (unit) {
+ case SR_UNIT_VOLT:
+ si_printf(value, out, "V");
+ break;
+ case SR_UNIT_AMPERE:
+ si_printf(value, out, "A");
+ break;
+ case SR_UNIT_OHM:
+ si_printf(value, out, "");
+ g_string_append_unichar(out, 0x2126);
+ break;
+ case SR_UNIT_FARAD:
+ si_printf(value, out, "F");
+ break;
+ case SR_UNIT_KELVIN:
+ si_printf(value, out, "K");
+ break;
+ case SR_UNIT_CELSIUS:
+ si_printf(value, out, "");
+ g_string_append_unichar(out, 0x00b0);
+ g_string_append_c(out, 'C');
+ break;
+ case SR_UNIT_FAHRENHEIT:
+ si_printf(value, out, "");
+ g_string_append_unichar(out, 0x00b0);
+ g_string_append_c(out, 'F');
+ break;
+ case SR_UNIT_HERTZ:
+ si_printf(value, out, "Hz");
+ break;
+ case SR_UNIT_PERCENTAGE:
+ g_string_append_printf(out, "%f %%", value);
+ break;
+ case SR_UNIT_BOOLEAN:
+ if (value > 0)
+ g_string_append_printf(out, "TRUE");
+ else
+ g_string_append_printf(out, "FALSE");
+ break;
+ case SR_UNIT_SECOND:
+ si_printf(value, out, "s");
+ break;
+ case SR_UNIT_SIEMENS:
+ si_printf(value, out, "S");
+ break;
+ case SR_UNIT_DECIBEL_MW:
+ si_printf(value, out, "dBu");
+ break;
+ case SR_UNIT_DECIBEL_VOLT:
+ si_printf(value, out, "dBV");
+ break;
+ case SR_UNIT_DECIBEL_SPL:
+ if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A)
+ si_printf(value, out, "dB(A)");
+ else if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_C)
+ si_printf(value, out, "dB(C)");
+ else if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_Z)
+ si_printf(value, out, "dB(Z)");
+ else
+ /* No frequency weighting, or non-standard "flat" */
+ si_printf(value, out, "dB(SPL)");
+ if (mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_S)
+ g_string_append(out, " S");
+ else if (mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_F)
+ g_string_append(out, " F");
+ if (mqflags & SR_MQFLAG_SPL_LAT)
+ g_string_append(out, " LAT");
+ else if (mqflags & SR_MQFLAG_SPL_PCT_OVER_ALARM)
+ /* Not a standard function for SLMs, so this is
+ * a made-up notation. */
+ g_string_append(out, " %oA");
+ break;
+ case SR_UNIT_CONCENTRATION:
+ g_string_append_printf(out, "%f ppm", value * 1000000);
+ break;
+ case SR_UNIT_REVOLUTIONS_PER_MINUTE:
+ si_printf(value, out, "RPM");
+ break;
+ case SR_UNIT_VOLT_AMPERE:
+ si_printf(value, out, "VA");
+ break;
+ case SR_UNIT_WATT:
+ si_printf(value, out, "W");
+ break;
+ case SR_UNIT_WATT_HOUR:
+ si_printf(value, out, "Wh");
+ break;
+ case SR_UNIT_METER_SECOND:
+ si_printf(value, out, "m/s");
+ break;
+ case SR_UNIT_HECTOPASCAL:
+ si_printf(value, out, "hPa");
+ break;
+ case SR_UNIT_HUMIDITY_293K:
+ si_printf(value, out, "%rF");
+ break;
+ default:
+ si_printf(value, out, "");
+ break;
+ }
+
+ if (mqflags & SR_MQFLAG_AC)
+ g_string_append_printf(out, " AC");
+ if (mqflags & SR_MQFLAG_DC)
+ g_string_append_printf(out, " DC");
+ if (mqflags & SR_MQFLAG_RMS)
+ g_string_append_printf(out, " RMS");
+ if (mqflags & SR_MQFLAG_DIODE)
+ g_string_append_printf(out, " DIODE");
+ if (mqflags & SR_MQFLAG_HOLD)
+ g_string_append_printf(out, " HOLD");
+ if (mqflags & SR_MQFLAG_MAX)
+ g_string_append_printf(out, " MAX");
+ if (mqflags & SR_MQFLAG_MIN)
+ g_string_append_printf(out, " MIN");
+ if (mqflags & SR_MQFLAG_AUTORANGE)
+ g_string_append_printf(out, " AUTO");
+ if (mqflags & SR_MQFLAG_RELATIVE)
+ g_string_append_printf(out, " REL");
+ if (mqflags & SR_MQFLAG_AVG)
+ g_string_append_printf(out, " AVG");
+ g_string_append_c(out, '\n');
+}
+
+static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+ GString **out)
+{
+ const struct sr_datafeed_analog *analog;
+ struct sr_channel *ch;
+ GSList *l;
+ const float *fdata;
+ int i, p;
+
+ *out = NULL;
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+
+ switch (packet->type) {
+ case SR_DF_FRAME_BEGIN:
+ *out = g_string_new("FRAME-BEGIN\n");
+ break;
+ case SR_DF_FRAME_END:
+ *out = g_string_new("FRAME-END\n");
+ break;
+ case SR_DF_ANALOG:
+ analog = packet->payload;
+ fdata = (const float *)analog->data;
+ *out = g_string_sized_new(512);
+ for (i = 0; i < analog->num_samples; i++) {
+ for (l = analog->channels, p = 0; l; l = l->next, p++) {
+ ch = l->data;
+ g_string_append_printf(*out, "%s: ", ch->name);
+ fancyprint(analog->unit, analog->mqflags,
+ fdata[i + p], *out);
+ }
+ }
+ break;
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(struct sr_output *o)
+{
+ struct context *ctx;
+
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+ ctx = o->internal;
+
+ g_ptr_array_free(ctx->channellist, 1);
+ g_free(ctx);
+ o->internal = NULL;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_output_format output_analog = {
+ .id = "analog",
+ .description = "Analog data",
+ .init = init,
+ .receive = receive,
+ .cleanup = cleanup
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011 HÃ¥vard Espeland <gus@ping.uio.no>
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/hex"
+
+#define DEFAULT_SAMPLES_PER_LINE 74
+
+struct context {
+ unsigned int num_enabled_channels;
+ int samples_per_line;
+ int bit_cnt;
+ int spl_cnt;
+ int trigger;
+ uint64_t samplerate;
+ int *channel_index;
+ char **channel_names;
+ char **line_values;
+ uint8_t *prev_sample;
+ gboolean header_done;
+ GString **lines;
+ GString *header;
+};
+
+static int init(struct sr_output *o)
+{
+ struct context *ctx;
+ struct sr_channel *ch;
+ GSList *l;
+ GHashTableIter iter;
+ gpointer key, value;
+ unsigned int i, j;
+ int spl;
+
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+
+ spl = DEFAULT_SAMPLES_PER_LINE;
+ g_hash_table_iter_init(&iter, o->params);
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ if (!strcmp(key, "width")) {
+ if ((spl = strtoul(value, NULL, 10)) < 1) {
+ sr_err("Invalid width.");
+ return SR_ERR_ARG;
+ }
+ } else {
+ sr_err("Unknown parameter '%s'.", key);
+ return SR_ERR_ARG;
+ }
+ }
+
+ ctx = g_malloc0(sizeof(struct context));
+ o->internal = ctx;
+ ctx->trigger = -1;
+ ctx->samples_per_line = spl;
+
+ for (l = o->sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->num_enabled_channels++;
+ }
+ ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
+ ctx->channel_names = g_malloc(sizeof(char *) * ctx->num_enabled_channels);
+ ctx->lines = g_malloc(sizeof(GString *) * ctx->num_enabled_channels);
+ ctx->prev_sample = g_malloc(g_slist_length(o->sdi->channels));
+
+ j = 0;
+ for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->channel_index[j] = ch->index;
+ ctx->channel_names[j] = ch->name;
+ ctx->lines[j] = g_string_sized_new(80);
+ g_string_printf(ctx->lines[j], "%s:", ch->name);
+ j++;
+ }
+
+ return SR_OK;
+}
+
+static GString *gen_header(struct sr_output *o)
+{
+ struct context *ctx;
+ GVariant *gvar;
+ GString *header;
+ int num_channels;
+ char *samplerate_s;
+
+ ctx = o->internal;
+ if (ctx->samplerate == 0) {
+ if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
+ &gvar) == SR_OK) {
+ ctx->samplerate = g_variant_get_uint64(gvar);
+ g_variant_unref(gvar);
+ }
+ }
+
+ header = g_string_sized_new(512);
+ g_string_printf(header, "%s\n", PACKAGE_STRING);
+ num_channels = g_slist_length(o->sdi->channels);
+ g_string_append_printf(header, "Acquisition with %d/%d channels",
+ ctx->num_enabled_channels, num_channels);
+ if (ctx->samplerate != 0) {
+ samplerate_s = sr_samplerate_string(ctx->samplerate);
+ g_string_append_printf(header, " at %s", samplerate_s);
+ g_free(samplerate_s);
+ }
+ g_string_append_printf(header, "\n");
+
+ return header;
+}
+
+static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+ GString **out)
+{
+ const struct sr_datafeed_meta *meta;
+ const struct sr_datafeed_logic *logic;
+ const struct sr_config *src;
+ GSList *l;
+ struct context *ctx;
+ int idx, offset, curbit, prevbit;
+ uint64_t i, j;
+ gchar *p, c;
+
+ *out = NULL;
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+ if (!(ctx = o->internal))
+ return SR_ERR_ARG;
+
+ switch (packet->type) {
+ case SR_DF_META:
+ meta = packet->payload;
+ for (l = meta->config; l; l = l->next) {
+ src = l->data;
+ if (src->key != SR_CONF_SAMPLERATE)
+ continue;
+ ctx->samplerate = g_variant_get_uint64(src->data);
+ }
+ break;
+ case SR_DF_TRIGGER:
+ ctx->trigger = ctx->spl_cnt;
+ break;
+ case SR_DF_LOGIC:
+ if (!ctx->header_done) {
+ *out = gen_header(o);
+ ctx->header_done = TRUE;
+ } else
+ *out = g_string_sized_new(512);
+
+ logic = packet->payload;
+ for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
+ ctx->spl_cnt++;
+ for (j = 0; j < ctx->num_enabled_channels; j++) {
+ idx = ctx->channel_index[j];
+ p = logic->data + i + idx / 8;
+ curbit = *p & (1 << (idx % 8));
+ prevbit = (ctx->prev_sample[idx / 8] & ((uint8_t) 1 << (idx % 8)));
+
+ c = curbit ? '"' : '.';
+ if (ctx->spl_cnt > 1) {
+ if (curbit < prevbit)
+ c = '\\';
+ else if (curbit > prevbit)
+ c = '/';
+ }
+ g_string_append_c(ctx->lines[j], c);
+
+ if (ctx->spl_cnt == ctx->samples_per_line) {
+ /* Flush line buffers. */
+ g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
+ g_string_append_c(*out, '\n');
+ if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
+ offset = ctx->trigger + ctx->trigger / 8;
+ g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
+ ctx->trigger = -1;
+ }
+ g_string_printf(ctx->lines[j], "%s:", ctx->channel_names[j]);
+ }
+ }
+ if (ctx->spl_cnt == ctx->samples_per_line)
+ /* Line buffers were already flushed. */
+ ctx->spl_cnt = 0;
+ memcpy(ctx->prev_sample, logic->data + i, logic->unitsize);
+ }
+ break;
+ case SR_DF_END:
+ if (ctx->spl_cnt) {
+ /* Line buffers need flushing. */
+ *out = g_string_sized_new(512);
+ for (i = 0; i < ctx->num_enabled_channels; i++) {
+ g_string_append_len(*out, ctx->lines[i]->str, ctx->lines[i]->len);
+ g_string_append_c(*out, '\n');
+ }
+ }
+ break;
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(struct sr_output *o)
+{
+ struct context *ctx;
+ unsigned int i;
+
+ if (!o)
+ return SR_ERR_ARG;
+
+ if (!(ctx = o->internal))
+ return SR_OK;
+
+ g_free(ctx->channel_index);
+ g_free(ctx->prev_sample);
+ g_free(ctx->channel_names);
+ for (i = 0; i < ctx->num_enabled_channels; i++)
+ g_string_free(ctx->lines[i], TRUE);
+ g_free(ctx->lines);
+ g_free(ctx);
+ o->internal = NULL;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_output_format output_ascii = {
+ .id = "ascii",
+ .description = "ASCII",
+ .init = init,
+ .receive = receive,
+ .cleanup = cleanup,
+};
+
+
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/binary"
+
+static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+ GString **out)
+{
+ const struct sr_datafeed_logic *logic;
+
+ (void)o;
+
+ *out = NULL;
+ if (packet->type != SR_DF_LOGIC)
+ return SR_OK;
+ logic = packet->payload;
+ *out = g_string_new_len(logic->data, logic->length);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_output_format output_binary = {
+ .id = "binary",
+ .description = "Raw binary",
+ .receive = receive,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/bits"
+
+#define DEFAULT_SAMPLES_PER_LINE 64
+
+struct context {
+ unsigned int num_enabled_channels;
+ int samples_per_line;
+ int spl_cnt;
+ int trigger;
+ uint64_t samplerate;
+ int *channel_index;
+ char **channel_names;
+ gboolean header_done;
+ GString **lines;
+};
+
+static int init(struct sr_output *o)
+{
+ struct context *ctx;
+ struct sr_channel *ch;
+ GSList *l;
+ GHashTableIter iter;
+ gpointer key, value;
+ unsigned int i, j;
+ int spl;
+
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+
+ spl = DEFAULT_SAMPLES_PER_LINE;
+ g_hash_table_iter_init(&iter, o->params);
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ if (!strcmp(key, "width")) {
+ if ((spl = strtoul(value, NULL, 10)) < 1) {
+ sr_err("Invalid width.");
+ return SR_ERR_ARG;
+ }
+ } else {
+ sr_err("Unknown parameter '%s'.", key);
+ return SR_ERR_ARG;
+ }
+ }
+
+ ctx = g_malloc0(sizeof(struct context));
+ o->internal = ctx;
+ ctx->trigger = -1;
+ ctx->samples_per_line = spl;
+
+ for (l = o->sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->num_enabled_channels++;
+ }
+ ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
+ ctx->channel_names = g_malloc(sizeof(char *) * ctx->num_enabled_channels);
+ ctx->lines = g_malloc(sizeof(GString *) * ctx->num_enabled_channels);
+
+ j = 0;
+ for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->channel_index[j] = ch->index;
+ ctx->channel_names[j] = ch->name;
+ ctx->lines[j] = g_string_sized_new(80);
+ g_string_printf(ctx->lines[j], "%s:", ch->name);
+ j++;
+ }
+
+ return SR_OK;
+}
+
+static GString *gen_header(struct sr_output *o)
+{
+ struct context *ctx;
+ GVariant *gvar;
+ GString *header;
+ int num_channels;
+ char *samplerate_s;
+
+ ctx = o->internal;
+ if (ctx->samplerate == 0) {
+ if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
+ &gvar) == SR_OK) {
+ ctx->samplerate = g_variant_get_uint64(gvar);
+ g_variant_unref(gvar);
+ }
+ }
+
+ header = g_string_sized_new(512);
+ g_string_printf(header, "%s\n", PACKAGE_STRING);
+ num_channels = g_slist_length(o->sdi->channels);
+ g_string_append_printf(header, "Acquisition with %d/%d channels",
+ ctx->num_enabled_channels, num_channels);
+ if (ctx->samplerate != 0) {
+ samplerate_s = sr_samplerate_string(ctx->samplerate);
+ g_string_append_printf(header, " at %s", samplerate_s);
+ g_free(samplerate_s);
+ }
+ g_string_append_printf(header, "\n");
+
+ return header;
+}
+
+static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+ GString **out)
+{
+ const struct sr_datafeed_meta *meta;
+ const struct sr_datafeed_logic *logic;
+ const struct sr_config *src;
+ struct context *ctx;
+ GSList *l;
+ int idx, offset;
+ uint64_t i, j;
+ gchar *p, c;
+
+ *out = NULL;
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+ if (!(ctx = o->internal))
+ return SR_ERR_ARG;
+
+ switch (packet->type) {
+ case SR_DF_META:
+ meta = packet->payload;
+ for (l = meta->config; l; l = l->next) {
+ src = l->data;
+ if (src->key != SR_CONF_SAMPLERATE)
+ continue;
+ ctx->samplerate = g_variant_get_uint64(src->data);
+ }
+ break;
+ case SR_DF_TRIGGER:
+ ctx->trigger = ctx->spl_cnt;
+ break;
+ case SR_DF_LOGIC:
+ if (!ctx->header_done) {
+ *out = gen_header(o);
+ ctx->header_done = TRUE;
+ } else
+ *out = g_string_sized_new(512);
+
+ logic = packet->payload;
+ for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
+ ctx->spl_cnt++;
+ for (j = 0; j < ctx->num_enabled_channels; j++) {
+ idx = ctx->channel_index[j];
+ p = logic->data + i + idx / 8;
+ c = (*p & (1 << (idx % 8))) ? '1' : '0';
+ g_string_append_c(ctx->lines[j], c);
+
+ if (ctx->spl_cnt == ctx->samples_per_line) {
+ /* Flush line buffers. */
+ g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
+ g_string_append_c(*out, '\n');
+ if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
+ offset = ctx->trigger + ctx->trigger / 8;
+ g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
+ ctx->trigger = -1;
+ }
+ g_string_printf(ctx->lines[j], "%s:", ctx->channel_names[j]);
+ } else if ((ctx->spl_cnt & 7) == 0) {
+ /* Add a space every 8th bit. */
+ g_string_append_c(ctx->lines[j], ' ');
+ }
+ }
+ if (ctx->spl_cnt == ctx->samples_per_line)
+ /* Line buffers were already flushed. */
+ ctx->spl_cnt = 0;
+ }
+ break;
+ case SR_DF_END:
+ if (ctx->spl_cnt) {
+ /* Line buffers need flushing. */
+ *out = g_string_sized_new(512);
+ for (i = 0; i < ctx->num_enabled_channels; i++) {
+ g_string_append_len(*out, ctx->lines[i]->str, ctx->lines[i]->len);
+ g_string_append_c(*out, '\n');
+ }
+ }
+ break;
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(struct sr_output *o)
+{
+ struct context *ctx;
+ unsigned int i;
+
+ if (!o)
+ return SR_ERR_ARG;
+
+ if (!(ctx = o->internal))
+ return SR_OK;
+
+ g_free(ctx->channel_index);
+ g_free(ctx->channel_names);
+ for (i = 0; i < ctx->num_enabled_channels; i++)
+ g_string_free(ctx->lines[i], TRUE);
+ g_free(ctx->lines);
+ g_free(ctx);
+ o->internal = NULL;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_output_format output_bits = {
+ .id = "bits",
+ .description = "Bits",
+ .init = init,
+ .receive = receive,
+ .cleanup = cleanup,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 2 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 <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/chronovu-la8"
+
+struct context {
+ unsigned int num_enabled_channels;
+ gboolean triggered;
+ uint64_t samplerate;
+ uint64_t samplecount;
+ int *channel_index;
+ GString *pretrig_buf;
+};
+
+/**
+ * Check if the given samplerate is supported by the LA8 hardware.
+ *
+ * @param samplerate The samplerate (in Hz) to check.
+ *
+ * @return 1 if the samplerate is supported/valid, 0 otherwise.
+ */
+static gboolean is_valid_samplerate(uint64_t samplerate)
+{
+ unsigned int i;
+
+ for (i = 0; i < 255; i++) {
+ if (samplerate == (SR_MHZ(100) / (i + 1)))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Convert a samplerate (in Hz) to the 'divcount' value the LA8 wants.
+ *
+ * LA8 hardware: sample period = (divcount + 1) * 10ns.
+ * Min. value for divcount: 0x00 (10ns sample period, 100MHz samplerate).
+ * Max. value for divcount: 0xfe (2550ns sample period, 392.15kHz samplerate).
+ *
+ * @param samplerate The samplerate in Hz.
+ *
+ * @return The divcount value as needed by the hardware, or 0xff upon errors.
+ */
+static uint8_t samplerate_to_divcount(uint64_t samplerate)
+{
+ if (samplerate == 0 || !is_valid_samplerate(samplerate)) {
+ sr_warn("Invalid samplerate (%" PRIu64 "Hz)", samplerate);
+ return 0xff;
+ }
+
+ return (SR_MHZ(100) / samplerate) - 1;
+}
+
+static int init(struct sr_output *o)
+{
+ struct context *ctx;
+ struct sr_channel *ch;
+ GSList *l;
+
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+
+ ctx = g_malloc0(sizeof(struct context));
+ o->internal = ctx;
+
+ for (l = o->sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->num_enabled_channels++;
+ }
+ ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
+ ctx->pretrig_buf = g_string_sized_new(1024);
+
+ return SR_OK;
+}
+
+static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+ GString **out)
+{
+ const struct sr_datafeed_logic *logic;
+ struct context *ctx;
+ GVariant *gvar;
+ uint64_t samplerate;
+ gchar c[4];
+
+ *out = NULL;
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+ if (!(ctx = o->internal))
+ return SR_ERR_ARG;
+
+ switch (packet->type) {
+ case SR_DF_HEADER:
+ /* One byte for the 'divcount' value. */
+ if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
+ &gvar) == SR_OK) {
+ samplerate = g_variant_get_uint64(gvar);
+ g_variant_unref(gvar);
+ } else
+ samplerate = 0;
+ c[0] = samplerate_to_divcount(samplerate);
+ *out = g_string_new_len(c, 1);
+ ctx->triggered = FALSE;
+ break;
+ case SR_DF_TRIGGER:
+ /* Four bytes (little endian) for the trigger point. */
+ c[0] = ctx->samplecount & 0xff;
+ c[1] = (ctx->samplecount >> 8) & 0xff;
+ c[2] = (ctx->samplecount >> 16) & 0xff;
+ c[3] = (ctx->samplecount >> 24) & 0xff;
+ *out = g_string_new_len(c, 4);
+ /* Flush the pre-trigger buffer. */
+ if (ctx->pretrig_buf->len)
+ g_string_append_len(*out, ctx->pretrig_buf->str,
+ ctx->pretrig_buf->len);
+ ctx->triggered = TRUE;
+ break;
+ case SR_DF_LOGIC:
+ logic = packet->payload;
+ if (!ctx->triggered)
+ g_string_append_len(ctx->pretrig_buf, logic->data, logic->length);
+ else
+ *out = g_string_new_len(logic->data, logic->length);
+ ctx->samplecount += logic->length / logic->unitsize;
+ break;
+ case SR_DF_END:
+ if (!ctx->triggered && ctx->pretrig_buf->len) {
+ /* We never got a trigger, submit an empty one. */
+ *out = g_string_sized_new(ctx->pretrig_buf->len + 4);
+ g_string_append_len(*out, "\x00\x00\x00\x00", 4);
+ g_string_append_len(*out, ctx->pretrig_buf->str, ctx->pretrig_buf->len);
+ }
+ break;
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(struct sr_output *o)
+{
+ struct context *ctx;
+
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+
+ if (o->internal) {
+ ctx = o->internal;
+ g_string_free(ctx->pretrig_buf, TRUE);
+ g_free(ctx->channel_index);
+ g_free(o->internal);
+ o->internal = NULL;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_output_format output_chronovu_la8 = {
+ .id = "chronovu-la8",
+ .description = "ChronoVu LA8",
+ .init = init,
+ .receive = receive,
+ .cleanup = cleanup,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "config.h" /* Needed for PACKAGE_STRING and others. */
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/csv"
+
+struct context {
+ unsigned int num_enabled_channels;
+ uint64_t samplerate;
+ char separator;
+ gboolean header_done;
+ int *channel_index;
+};
+
+/*
+ * TODO:
+ * - Option to specify delimiter character and/or string.
+ * - Option to (not) print metadata as comments.
+ * - Option to specify the comment character(s), e.g. # or ; or C/C++-style.
+ * - Option to (not) print samplenumber / time as extra column.
+ * - Option to "compress" output (only print changed samples, VCD-like).
+ * - Option to print comma-separated bits, or whole bytes/words (for 8/16
+ * channel LAs) as ASCII/hex etc. etc.
+ * - Trigger support.
+ */
+
+static int init(struct sr_output *o)
+{
+ struct context *ctx;
+ struct sr_channel *ch;
+ GSList *l;
+ int i;
+
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+
+ ctx = g_malloc0(sizeof(struct context));
+ o->internal = ctx;
+ ctx->separator = ',';
+
+ /* Get the number of channels, and the unitsize. */
+ for (l = o->sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->num_enabled_channels++;
+ }
+ ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
+
+ /* Once more to map the enabled channels. */
+ for (i = 0, l = o->sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->channel_index[i++] = ch->index;
+ }
+
+ return SR_OK;
+}
+
+static GString *gen_header(struct sr_output *o)
+{
+ struct context *ctx;
+ struct sr_channel *ch;
+ GVariant *gvar;
+ GString *header;
+ GSList *l;
+ time_t t;
+ int num_channels, i;
+ char *samplerate_s;
+
+ ctx = o->internal;
+ header = g_string_sized_new(512);
+
+ /* Some metadata */
+ t = time(NULL);
+ g_string_append_printf(header, "; CSV, generated by %s on %s",
+ PACKAGE_STRING, ctime(&t));
+
+ /* Columns / channels */
+ num_channels = g_slist_length(o->sdi->channels);
+ g_string_append_printf(header, "; Channels (%d/%d):",
+ ctx->num_enabled_channels, num_channels);
+ for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ g_string_append_printf(header, " %s,", ch->name);
+ }
+ if (o->sdi->channels)
+ /* Drop last separator. */
+ g_string_truncate(header, header->len - 1);
+ g_string_append_printf(header, "\n");
+
+ if (ctx->samplerate == 0) {
+ if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
+ &gvar) == SR_OK) {
+ ctx->samplerate = g_variant_get_uint64(gvar);
+ g_variant_unref(gvar);
+ }
+ }
+ if (ctx->samplerate != 0) {
+ samplerate_s = sr_samplerate_string(ctx->samplerate);
+ g_string_append_printf(header, "; Samplerate: %s\n", samplerate_s);
+ g_free(samplerate_s);
+ }
+
+ return header;
+}
+
+static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+ GString **out)
+{
+ const struct sr_datafeed_meta *meta;
+ const struct sr_datafeed_logic *logic;
+ const struct sr_config *src;
+ GSList *l;
+ struct context *ctx;
+ int idx;
+ uint64_t i, j;
+ gchar *p, c;
+
+ *out = NULL;
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+ if (!(ctx = o->internal))
+ return SR_ERR_ARG;
+
+ switch (packet->type) {
+ case SR_DF_META:
+ meta = packet->payload;
+ for (l = meta->config; l; l = l->next) {
+ src = l->data;
+ if (src->key != SR_CONF_SAMPLERATE)
+ continue;
+ ctx->samplerate = g_variant_get_uint64(src->data);
+ }
+ break;
+ case SR_DF_LOGIC:
+ logic = packet->payload;
+ if (!ctx->header_done) {
+ *out = gen_header(o);
+ ctx->header_done = TRUE;
+ } else {
+ *out = g_string_sized_new(512);
+ }
+
+ for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
+ for (j = 0; j < ctx->num_enabled_channels; j++) {
+ idx = ctx->channel_index[j];
+ p = logic->data + i + idx / 8;
+ c = *p & (1 << (idx % 8));
+ g_string_append_c(*out, c ? '1' : '0');
+ g_string_append_c(*out, ctx->separator);
+ }
+ if (j) {
+ /* Drop last separator. */
+ g_string_truncate(*out, (*out)->len - 1);
+ }
+ g_string_append_printf(*out, "\n");
+ }
+ break;
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(struct sr_output *o)
+{
+ struct context *ctx;
+
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+
+ if (o->internal) {
+ ctx = o->internal;
+ g_free(ctx->channel_index);
+ g_free(o->internal);
+ o->internal = NULL;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_output_format output_csv = {
+ .id = "csv",
+ .description = "Comma-separated values (CSV)",
+ .init = init,
+ .receive = receive,
+ .cleanup = cleanup,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "config.h" /* Needed for PACKAGE_STRING and others. */
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/gnuplot"
+
+struct context {
+ unsigned int num_enabled_channels;
+ uint64_t samplerate;
+ uint64_t samplecount;
+ gboolean header_done;
+ uint8_t *prevsample;
+ int *channel_index;
+};
+
+static const char *gnuplot_header = "\
+# Sample data in space-separated columns format usable by gnuplot.\n";
+static const char *gnuplot_header2 = "\
+#\n# Column\tChannel\n\
+# -----------------------------------------------------------------------------\n\
+# 0\t\tSample counter (for internal gnuplot purposes)\n";
+
+
+static int init(struct sr_output *o)
+{
+ struct context *ctx;
+ struct sr_channel *ch;
+ GSList *l;
+ unsigned int i;
+
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+
+ ctx = g_malloc0(sizeof(struct context));
+ o->internal = ctx;
+ ctx->num_enabled_channels = 0;
+ for (l = o->sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->num_enabled_channels++;
+ }
+ if (ctx->num_enabled_channels <= 0) {
+ sr_err("No logic channel enabled.");
+ return SR_ERR;
+ }
+ ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
+
+ /* Once more to map the enabled channels. */
+ for (i = 0, l = o->sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->channel_index[i++] = ch->index;
+ }
+
+ return SR_OK;
+}
+
+static GString *gen_header(struct sr_output *o)
+{
+ struct context *ctx;
+ struct sr_channel *ch;
+ GVariant *gvar;
+ GString *header;
+ time_t t;
+ unsigned int num_channels, i;
+ char *samplerate_s;
+
+ ctx = o->internal;
+ if (ctx->samplerate == 0) {
+ if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
+ &gvar) == SR_OK) {
+ ctx->samplerate = g_variant_get_uint64(gvar);
+ g_variant_unref(gvar);
+ }
+ }
+
+ t = time(NULL);
+ header = g_string_sized_new(512);
+ g_string_printf(header, "%s", gnuplot_header);
+ g_string_append_printf(header, "# Generated by %s on %s",
+ PACKAGE_STRING, ctime(&t));
+
+ num_channels = g_slist_length(o->sdi->channels);
+ g_string_append_printf(header, "# Acquisition with %d/%d channels",
+ ctx->num_enabled_channels, num_channels);
+ if (ctx->samplerate != 0) {
+ samplerate_s = sr_samplerate_string(ctx->samplerate);
+ g_string_append_printf(header, " at %s", samplerate_s);
+ g_free(samplerate_s);
+ }
+ g_string_append_printf(header, "\n");
+
+ g_string_append_printf(header, "%s", gnuplot_header2);
+
+ /* Columns / channels */
+ for (i = 0; i < ctx->num_enabled_channels; i++) {
+ ch = g_slist_nth_data(o->sdi->channels, ctx->channel_index[i]);
+ g_string_append_printf(header, "# %d\t\t%s\n", i + 1, ch->name);
+ }
+
+ return header;
+}
+
+static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+ GString **out)
+{
+ const struct sr_datafeed_meta *meta;
+ const struct sr_datafeed_logic *logic;
+ const struct sr_config *src;
+ GSList *l;
+ struct context *ctx;
+ const uint8_t *sample;
+ unsigned int curbit, p, idx, i;
+
+ *out = NULL;
+ if (!o || !o->internal)
+ return SR_ERR_BUG;
+ ctx = o->internal;
+
+ if (packet->type == SR_DF_META) {
+ meta = packet->payload;
+ for (l = meta->config; l; l = l->next) {
+ src = l->data;
+ if (src->key != SR_CONF_SAMPLERATE)
+ continue;
+ ctx->samplerate = g_variant_get_uint64(src->data);
+ }
+ }
+
+ if (packet->type != SR_DF_LOGIC)
+ return SR_OK;
+ logic = packet->payload;
+
+ if (!ctx->prevsample) {
+ /* Can't allocate this until we know the stream's unitsize. */
+ ctx->prevsample = g_malloc0(logic->unitsize);
+ }
+
+ if (!ctx->header_done) {
+ *out = gen_header(o);
+ ctx->header_done = TRUE;
+ } else {
+ *out = g_string_sized_new(512);
+ }
+
+ for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
+ sample = logic->data + i;
+ ctx->samplecount++;
+
+ /*
+ * Don't output the same sample multiple times, but make
+ * sure to output at least the first and last sample.
+ */
+ if (i > 0 && i < logic->length - logic->unitsize) {
+ if (!memcmp(sample, ctx->prevsample, logic->unitsize))
+ continue;
+ }
+ memcpy(ctx->prevsample, sample, logic->unitsize);
+
+ /* The first column is a counter (needed for gnuplot). */
+ g_string_append_printf(*out, "%" PRIu64 "\t", ctx->samplecount);
+
+ /* The next columns are the values of all channels. */
+ for (p = 0; p < ctx->num_enabled_channels; p++) {
+ idx = ctx->channel_index[p];
+ curbit = (sample[idx / 8] & ((uint8_t) (1 << (idx % 8)))) >> (idx % 8);
+ g_string_append_printf(*out, "%d ", curbit);
+ }
+ g_string_append_printf(*out, "\n");
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(struct sr_output *o)
+{
+ struct context *ctx;
+
+ if (!o || !o->internal)
+ return SR_ERR_BUG;
+ ctx = o->internal;
+ g_free(ctx->channel_index);
+ g_free(ctx->prevsample);
+ g_free(ctx);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_output_format output_gnuplot = {
+ .id = "gnuplot",
+ .description = "Gnuplot",
+ .init = init,
+ .receive = receive,
+ .cleanup = cleanup,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/hex"
+
+#define DEFAULT_SAMPLES_PER_LINE 192
+
+struct context {
+ unsigned int num_enabled_channels;
+ int samples_per_line;
+ int bit_cnt;
+ int spl_cnt;
+ int trigger;
+ uint64_t samplerate;
+ int *channel_index;
+ char **channel_names;
+ char **line_values;
+ uint8_t *sample_buf;
+ gboolean header_done;
+ GString **lines;
+};
+
+static int init(struct sr_output *o)
+{
+ struct context *ctx;
+ struct sr_channel *ch;
+ GSList *l;
+ GHashTableIter iter;
+ gpointer key, value;
+ unsigned int i, j;
+ int spl;
+
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+
+ spl = DEFAULT_SAMPLES_PER_LINE;
+ g_hash_table_iter_init(&iter, o->params);
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ if (!strcmp(key, "width")) {
+ if ((spl = strtoul(value, NULL, 10)) < 1) {
+ sr_err("Invalid width.");
+ return SR_ERR_ARG;
+ }
+ } else {
+ sr_err("Unknown parameter '%s'.", key);
+ return SR_ERR_ARG;
+ }
+ }
+
+ ctx = g_malloc0(sizeof(struct context));
+ o->internal = ctx;
+ ctx->trigger = -1;
+ ctx->samples_per_line = spl;
+
+ for (l = o->sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->num_enabled_channels++;
+ }
+ ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
+ ctx->channel_names = g_malloc(sizeof(char *) * ctx->num_enabled_channels);
+ ctx->lines = g_malloc(sizeof(GString *) * ctx->num_enabled_channels);
+ ctx->sample_buf = g_malloc(ctx->num_enabled_channels);
+
+ j = 0;
+ for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->channel_index[j] = ch->index;
+ ctx->channel_names[j] = ch->name;
+ ctx->lines[j] = g_string_sized_new(80);
+ ctx->sample_buf[j] = 0;
+ g_string_printf(ctx->lines[j], "%s:", ch->name);
+ j++;
+ }
+
+ return SR_OK;
+}
+
+static GString *gen_header(struct sr_output *o)
+{
+ struct context *ctx;
+ GVariant *gvar;
+ GString *header;
+ int num_channels;
+ char *samplerate_s;
+
+ ctx = o->internal;
+ if (ctx->samplerate == 0) {
+ if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
+ &gvar) == SR_OK) {
+ ctx->samplerate = g_variant_get_uint64(gvar);
+ g_variant_unref(gvar);
+ }
+ }
+
+ header = g_string_sized_new(512);
+ g_string_printf(header, "%s\n", PACKAGE_STRING);
+ num_channels = g_slist_length(o->sdi->channels);
+ g_string_append_printf(header, "Acquisition with %d/%d channels",
+ ctx->num_enabled_channels, num_channels);
+ if (ctx->samplerate != 0) {
+ samplerate_s = sr_samplerate_string(ctx->samplerate);
+ g_string_append_printf(header, " at %s", samplerate_s);
+ g_free(samplerate_s);
+ }
+ g_string_append_printf(header, "\n");
+
+ return header;
+}
+
+static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+ GString **out)
+{
+ const struct sr_datafeed_meta *meta;
+ const struct sr_datafeed_logic *logic;
+ const struct sr_config *src;
+ GSList *l;
+ struct context *ctx;
+ int idx, pos, offset;
+ uint64_t i, j;
+ gchar *p;
+
+ *out = NULL;
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+ if (!(ctx = o->internal))
+ return SR_ERR_ARG;
+
+ switch (packet->type) {
+ case SR_DF_META:
+ meta = packet->payload;
+ for (l = meta->config; l; l = l->next) {
+ src = l->data;
+ if (src->key != SR_CONF_SAMPLERATE)
+ continue;
+ ctx->samplerate = g_variant_get_uint64(src->data);
+ }
+ break;
+ case SR_DF_TRIGGER:
+ ctx->trigger = ctx->spl_cnt;
+ break;
+ case SR_DF_LOGIC:
+ if (!ctx->header_done) {
+ *out = gen_header(o);
+ ctx->header_done = TRUE;
+ } else
+ *out = g_string_sized_new(512);
+
+ logic = packet->payload;
+ for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
+ ctx->spl_cnt++;
+ pos = ctx->spl_cnt & 7;
+ for (j = 0; j < ctx->num_enabled_channels; j++) {
+ idx = ctx->channel_index[j];
+ p = logic->data + i + idx / 8;
+ ctx->sample_buf[j] <<= 1;
+ if (*p & (1 << (idx % 8)))
+ ctx->sample_buf[j] |= 1;
+ if (ctx->spl_cnt && pos == 0) {
+ /* Buffered a byte's worth, output hex. */
+ g_string_append_printf(ctx->lines[j], "%.2x ",
+ ctx->sample_buf[j]);
+ ctx->sample_buf[j] = 0;
+ }
+
+ if (ctx->spl_cnt == ctx->samples_per_line) {
+ /* Flush line buffers. */
+ g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
+ g_string_append_c(*out, '\n');
+ if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
+ offset = ctx->trigger + ctx->trigger / 8;
+ g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
+ ctx->trigger = -1;
+ }
+ g_string_printf(ctx->lines[j], "%s:", ctx->channel_names[j]);
+ }
+ }
+ if (ctx->spl_cnt == ctx->samples_per_line)
+ /* Line buffers were already flushed. */
+ ctx->spl_cnt = 0;
+ }
+ break;
+ case SR_DF_END:
+ if (ctx->spl_cnt) {
+ /* Line buffers need flushing. */
+ *out = g_string_sized_new(512);
+ for (i = 0; i < ctx->num_enabled_channels; i++) {
+ if (ctx->spl_cnt & 7)
+ g_string_append_printf(ctx->lines[i], "%.2x ",
+ ctx->sample_buf[i] << (8 - (ctx->spl_cnt & 7)));
+ g_string_append_len(*out, ctx->lines[i]->str, ctx->lines[i]->len);
+ g_string_append_c(*out, '\n');
+ }
+ }
+ break;
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(struct sr_output *o)
+{
+ struct context *ctx;
+ unsigned int i;
+
+ if (!o)
+ return SR_ERR_ARG;
+
+ if (!(ctx = o->internal))
+ return SR_OK;
+
+ g_free(ctx->channel_index);
+ g_free(ctx->sample_buf);
+ g_free(ctx->channel_names);
+ for (i = 0; i < ctx->num_enabled_channels; i++)
+ g_string_free(ctx->lines[i], TRUE);
+ g_free(ctx->lines);
+ g_free(ctx);
+ o->internal = NULL;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_output_format output_hex = {
+ .id = "hex",
+ .description = "Hexadecimal",
+ .init = init,
+ .receive = receive,
+ .cleanup = cleanup,
+};
+
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * This implements version 1.3 of the output format for the OpenBench Logic
+ * Sniffer "Alternative" Java client. Details:
+ * https://github.com/jawi/ols/wiki/OLS-data-file-format
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/ols"
+
+struct context {
+ uint64_t samplerate;
+ uint64_t num_samples;
+};
+
+static int init(struct sr_output *o)
+{
+ struct context *ctx;
+
+ if (!(ctx = g_try_malloc(sizeof(struct context)))) {
+ sr_err("%s: ctx malloc failed", __func__);
+ return SR_ERR_MALLOC;
+ }
+ o->internal = ctx;
+
+ ctx->samplerate = 0;
+ ctx->num_samples = 0;
+
+ return SR_OK;
+}
+
+static GString *gen_header(const struct sr_dev_inst *sdi, struct context *ctx)
+{
+ struct sr_channel *ch;
+ GSList *l;
+ GString *s;
+ GVariant *gvar;
+ int num_enabled_channels;
+
+ if (!ctx->samplerate && sr_config_get(sdi->driver, sdi, NULL,
+ SR_CONF_SAMPLERATE, &gvar) == SR_OK) {
+ ctx->samplerate = g_variant_get_uint64(gvar);
+ g_variant_unref(gvar);
+ }
+
+ num_enabled_channels = 0;
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ num_enabled_channels++;
+ }
+
+ s = g_string_sized_new(512);
+ g_string_append_printf(s, ";Rate: %"PRIu64"\n", ctx->samplerate);
+ g_string_append_printf(s, ";Channels: %d\n", num_enabled_channels);
+ g_string_append_printf(s, ";EnabledChannels: -1\n");
+ g_string_append_printf(s, ";Compressed: true\n");
+ g_string_append_printf(s, ";CursorEnabled: false\n");
+
+ return s;
+}
+
+static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+ GString **out)
+{
+ struct context *ctx;
+ const struct sr_datafeed_meta *meta;
+ const struct sr_datafeed_logic *logic;
+ const struct sr_config *src;
+ GSList *l;
+ unsigned int i, j;
+ uint8_t c;
+
+ *out = NULL;
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+ ctx = o->internal;
+
+ switch (packet->type) {
+ case SR_DF_META:
+ meta = packet->payload;
+ for (l = meta->config; l; l = l->next) {
+ src = l->data;
+ if (src->key == SR_CONF_SAMPLERATE)
+ ctx->samplerate = g_variant_get_uint64(src->data);
+ }
+ break;
+ case SR_DF_LOGIC:
+ logic = packet->payload;
+ if (ctx->num_samples == 0) {
+ /* First logic packet in the feed. */
+ *out = gen_header(o->sdi, ctx);
+ } else
+ *out = g_string_sized_new(512);
+ for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
+ for (j = 0; j < logic->unitsize; j++) {
+ /* The OLS format wants the samples presented MSB first. */
+ c = *((uint8_t *)logic->data + i + logic->unitsize - 1 - j);
+ g_string_append_printf(*out, "%02x", c);
+ }
+ g_string_append_printf(*out, "@%"PRIu64"\n", ctx->num_samples++);
+ }
+ break;
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(struct sr_output *o)
+{
+ struct context *ctx;
+
+ if (!o || !o->sdi)
+ return SR_ERR_ARG;
+
+ ctx = o->internal;
+ g_free(ctx);
+ o->internal = NULL;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_output_format output_ols = {
+ .id = "ols",
+ .description = "OpenBench Logic Sniffer",
+ .init = init,
+ .receive = receive,
+ .cleanup = cleanup
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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.h"
+#include "libsigrok-internal.h"
+
+/**
+ * @file
+ *
+ * Output file/data format handling.
+ */
+
+/**
+ * @defgroup grp_output Output formats
+ *
+ * Output file/data format handling.
+ *
+ * libsigrok supports several output (file) formats, e.g. binary, VCD,
+ * gnuplot, and so on. It provides an output API that frontends can use.
+ * New output formats can be added/implemented in libsigrok without having
+ * to change the frontends at all.
+ *
+ * All output modules are fed data in a stream. Devices that can stream data
+ * into libsigrok, instead of storing and then transferring the whole buffer,
+ * can thus generate output live.
+ *
+ * Output modules generate a newly allocated GString. The caller is then
+ * expected to free this with g_string_free() when finished with it.
+ *
+ * @{
+ */
+
+/** @cond PRIVATE */
+extern SR_PRIV struct sr_output_format output_bits;
+extern SR_PRIV struct sr_output_format output_hex;
+extern SR_PRIV struct sr_output_format output_ascii;
+extern SR_PRIV struct sr_output_format output_binary;
+extern SR_PRIV struct sr_output_format output_vcd;
+extern SR_PRIV struct sr_output_format output_ols;
+extern SR_PRIV struct sr_output_format output_gnuplot;
+extern SR_PRIV struct sr_output_format output_chronovu_la8;
+extern SR_PRIV struct sr_output_format output_csv;
+extern SR_PRIV struct sr_output_format output_analog;
+/* @endcond */
+
+static struct sr_output_format *output_module_list[] = {
+ &output_ascii,
+ &output_binary,
+ &output_bits,
+ &output_csv,
+ &output_gnuplot,
+ &output_hex,
+ &output_ols,
+ &output_vcd,
+ &output_chronovu_la8,
+ &output_analog,
+ NULL,
+};
+
+/** @since 0.1.0 */
+SR_API struct sr_output_format **sr_output_list(void)
+{
+ return output_module_list;
+}
+
+/** @since 0.3.0 */
+SR_API struct sr_output *sr_output_new(struct sr_output_format *of,
+ GHashTable *params, const struct sr_dev_inst *sdi)
+{
+ struct sr_output *o;
+
+ o = g_malloc(sizeof(struct sr_output));
+ o->format = of;
+ o->sdi = sdi;
+ o->params = params;
+ if (o->format->init && o->format->init(o) != SR_OK) {
+ g_free(o);
+ o = NULL;
+ }
+
+ return o;
+}
+
+/** @since 0.3.0 */
+SR_API int sr_output_send(struct sr_output *o,
+ const struct sr_datafeed_packet *packet, GString **out)
+{
+ return o->format->receive(o, packet, out);
+}
+
+/** @since 0.3.0 */
+SR_API int sr_output_free(struct sr_output *o)
+{
+ int ret;
+
+ ret = SR_OK;
+ if (o->format->cleanup)
+ ret = o->format->cleanup(o);
+ g_free(o);
+
+ return ret;
+}
+
+/** @} */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "config.h" /* Needed for PACKAGE and others. */
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/vcd"
+
+struct context {
+ int num_enabled_channels;
+ GArray *channelindices;
+ uint8_t *prevsample;
+ gboolean header_done;
+ int period;
+ int *channel_index;
+ uint64_t samplerate;
+ uint64_t samplecount;
+};
+
+static const char *const vcd_header_comment =
+ "$comment\n Acquisition with %d/%d channels at %s\n$end\n";
+
+static int init(struct sr_output *o)
+{
+ struct context *ctx;
+ struct sr_channel *ch;
+ GSList *l;
+ int num_enabled_channels, i;
+
+ num_enabled_channels = 0;
+ for (l = o->sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ num_enabled_channels++;
+ }
+ if (num_enabled_channels > 94) {
+ sr_err("VCD only supports 94 channels.");
+ return SR_ERR;
+ }
+
+ ctx = g_malloc0(sizeof(struct context));
+ o->internal = ctx;
+ ctx->num_enabled_channels = num_enabled_channels;
+ ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
+
+ /* Once more to map the enabled channels. */
+ for (i = 0, l = o->sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ ctx->channel_index[i++] = ch->index;
+ }
+
+ return SR_OK;
+}
+
+static GString *gen_header(struct sr_output *o)
+{
+ struct context *ctx;
+ struct sr_channel *ch;
+ GVariant *gvar;
+ GString *header;
+ GSList *l;
+ time_t t;
+ int num_channels, i;
+ char *samplerate_s, *frequency_s, *timestamp;
+
+ ctx = o->internal;
+ header = g_string_sized_new(512);
+ num_channels = g_slist_length(o->sdi->channels);
+
+ /* timestamp */
+ t = time(NULL);
+ timestamp = g_strdup(ctime(&t));
+ timestamp[strlen(timestamp)-1] = 0;
+ g_string_printf(header, "$date %s $end\n", timestamp);
+ g_free(timestamp);
+
+ /* generator */
+ g_string_append_printf(header, "$version %s %s $end\n",
+ PACKAGE, PACKAGE_VERSION);
+ g_string_append_printf(header, "$comment\n Acquisition with "
+ "%d/%d channels", ctx->num_enabled_channels, num_channels);
+
+ if (ctx->samplerate == 0) {
+ if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
+ &gvar) == SR_OK) {
+ ctx->samplerate = g_variant_get_uint64(gvar);
+ g_variant_unref(gvar);
+ }
+ }
+ if (ctx->samplerate != 0) {
+ samplerate_s = sr_samplerate_string(ctx->samplerate);
+ g_string_append_printf(header, " at %s", samplerate_s);
+ g_free(samplerate_s);
+ }
+ g_string_append_printf(header, "\n$end\n");
+
+ /* timescale */
+ /* VCD can only handle 1/10/100 (s - fs), so scale up first */
+ if (ctx->samplerate > SR_MHZ(1))
+ ctx->period = SR_GHZ(1);
+ else if (ctx->samplerate > SR_KHZ(1))
+ ctx->period = SR_MHZ(1);
+ else
+ ctx->period = SR_KHZ(1);
+ frequency_s = sr_period_string(ctx->period);
+ g_string_append_printf(header, "$timescale %s $end\n", frequency_s);
+ g_free(frequency_s);
+
+ /* scope */
+ g_string_append_printf(header, "$scope module %s $end\n", PACKAGE);
+
+ /* Wires / channels */
+ for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (!ch->enabled)
+ continue;
+ g_string_append_printf(header, "$var wire 1 %c %s $end\n",
+ (char)('!' + i), ch->name);
+ }
+
+ g_string_append(header, "$upscope $end\n$enddefinitions $end\n");
+
+ return header;
+}
+
+static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+ GString **out)
+{
+ const struct sr_datafeed_meta *meta;
+ const struct sr_datafeed_logic *logic;
+ const struct sr_config *src;
+ GSList *l;
+ struct context *ctx;
+ unsigned int i;
+ int p, curbit, prevbit, index;
+ uint8_t *sample;
+ gboolean timestamp_written;
+
+ *out = NULL;
+ if (!o || !o->internal)
+ return SR_ERR_BUG;
+ ctx = o->internal;
+
+ switch (packet->type) {
+ case SR_DF_META:
+ meta = packet->payload;
+ for (l = meta->config; l; l = l->next) {
+ src = l->data;
+ if (src->key != SR_CONF_SAMPLERATE)
+ continue;
+ ctx->samplerate = g_variant_get_uint64(src->data);
+ }
+ break;
+ case SR_DF_LOGIC:
+ logic = packet->payload;
+
+ if (!ctx->header_done) {
+ *out = gen_header(o);
+ ctx->header_done = TRUE;
+ } else {
+ *out = g_string_sized_new(512);
+ }
+
+ if (!ctx->prevsample) {
+ /* Can't allocate this until we know the stream's unitsize. */
+ ctx->prevsample = g_malloc0(logic->unitsize);
+ }
+
+ for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
+ sample = logic->data + i;
+ timestamp_written = FALSE;
+
+ for (p = 0; p < ctx->num_enabled_channels; p++) {
+ index = ctx->channel_index[p];
+
+ curbit = ((unsigned)sample[index / 8]
+ >> (index % 8)) & 1;
+ prevbit = ((unsigned)ctx->prevsample[index / 8]
+ >> (index % 8)) & 1;
+
+ /* VCD only contains deltas/changes of signals. */
+ if (prevbit == curbit && ctx->samplecount > 0)
+ continue;
+
+ /* Output timestamp of subsequent signal changes. */
+ if (!timestamp_written)
+ g_string_append_printf(*out, "#%.0f",
+ (double)ctx->samplecount /
+ ctx->samplerate * ctx->period);
+
+ /* Output which signal changed to which value. */
+ g_string_append_c(*out, ' ');
+ g_string_append_c(*out, '0' + curbit);
+ g_string_append_c(*out, '!' + p);
+
+ timestamp_written = TRUE;
+ }
+
+ if (timestamp_written)
+ g_string_append_c(*out, '\n');
+
+ ctx->samplecount++;
+ memcpy(ctx->prevsample, sample, logic->unitsize);
+ }
+ break;
+ case SR_DF_END:
+ /* Write final timestamp as length indicator. */
+ *out = g_string_sized_new(512);
+ g_string_printf(*out, "#%.0f\n",
+ (double)ctx->samplecount / ctx->samplerate * ctx->period);
+ break;
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(struct sr_output *o)
+{
+ struct context *ctx;
+
+ if (!o || !o->internal)
+ return SR_ERR_ARG;
+
+ ctx = o->internal;
+ g_free(ctx->prevsample);
+ g_free(ctx->channel_index);
+ g_free(ctx);
+
+ return SR_OK;
+}
+
+struct sr_output_format output_vcd = {
+ .id = "vcd",
+ .description = "Value Change Dump (VCD)",
+ .init = init,
+ .receive = receive,
+ .cleanup = cleanup,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
+ *
+ * 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.h"
+#include "libsigrok-internal.h"
+
+#include <glib.h>
+#include <string.h>
+
+#define LOG_PREFIX "scpi"
+
+#define SCPI_READ_RETRIES 100
+#define SCPI_READ_RETRY_TIMEOUT 10000
+
+/**
+ * Parse a string representation of a boolean-like value into a gboolean.
+ * Similar to sr_parse_boolstring but rejects strings which do not represent
+ * a boolean-like value.
+ *
+ * @param str String to convert.
+ * @param ret Pointer to a gboolean where the result of the conversion will be
+ * stored.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+static int parse_strict_bool(const char *str, gboolean *ret)
+{
+ if (!str)
+ return SR_ERR_ARG;
+
+ if (!g_strcmp0(str, "1") ||
+ !g_ascii_strncasecmp(str, "y", 1) ||
+ !g_ascii_strncasecmp(str, "t", 1) ||
+ !g_ascii_strncasecmp(str, "yes", 3) ||
+ !g_ascii_strncasecmp(str, "true", 4) ||
+ !g_ascii_strncasecmp(str, "on", 2)) {
+ *ret = TRUE;
+ return SR_OK;
+ } else if (!g_strcmp0(str, "0") ||
+ !g_ascii_strncasecmp(str, "n", 1) ||
+ !g_ascii_strncasecmp(str, "f", 1) ||
+ !g_ascii_strncasecmp(str, "no", 2) ||
+ !g_ascii_strncasecmp(str, "false", 5) ||
+ !g_ascii_strncasecmp(str, "off", 3)) {
+ *ret = FALSE;
+ return SR_OK;
+ }
+
+ return SR_ERR;
+}
+
+SR_PRIV extern const struct sr_scpi_dev_inst scpi_serial_dev;
+SR_PRIV extern const struct sr_scpi_dev_inst scpi_tcp_raw_dev;
+SR_PRIV extern const struct sr_scpi_dev_inst scpi_tcp_rigol_dev;
+SR_PRIV extern const struct sr_scpi_dev_inst scpi_usbtmc_libusb_dev;
+SR_PRIV extern const struct sr_scpi_dev_inst scpi_vxi_dev;
+SR_PRIV extern const struct sr_scpi_dev_inst scpi_visa_dev;
+
+static const struct sr_scpi_dev_inst *scpi_devs[] = {
+ &scpi_tcp_raw_dev,
+ &scpi_tcp_rigol_dev,
+#ifdef HAVE_LIBUSB_1_0
+ &scpi_usbtmc_libusb_dev,
+#endif
+#if HAVE_RPC
+ &scpi_vxi_dev,
+#endif
+#ifdef HAVE_LIBREVISA
+ &scpi_visa_dev,
+#endif
+#ifdef HAVE_LIBSERIALPORT
+ &scpi_serial_dev, /* must be last as it matches any resource */
+#endif
+};
+
+static struct sr_dev_inst *sr_scpi_scan_resource(struct drv_context *drvc,
+ const char *resource, const char *serialcomm,
+ struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi))
+{
+ struct sr_scpi_dev_inst *scpi;
+ struct sr_dev_inst *sdi;
+
+ if (!(scpi = scpi_dev_inst_new(drvc, resource, serialcomm)))
+ return NULL;
+
+ if (sr_scpi_open(scpi) != SR_OK) {
+ sr_info("Couldn't open SCPI device.");
+ sr_scpi_free(scpi);
+ return NULL;
+ };
+
+ if ((sdi = probe_device(scpi)))
+ return sdi;
+
+ sr_scpi_close(scpi);
+ sr_scpi_free(scpi);
+ return NULL;
+}
+
+SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
+ struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi))
+{
+ GSList *resources, *l, *devices;
+ struct sr_dev_inst *sdi;
+ const char *resource = NULL;
+ const char *serialcomm = NULL;
+ gchar **res;
+ unsigned i;
+
+ for (l = options; l; l = l->next) {
+ struct sr_config *src = l->data;
+ switch (src->key) {
+ case SR_CONF_CONN:
+ resource = g_variant_get_string(src->data, NULL);
+ break;
+ case SR_CONF_SERIALCOMM:
+ serialcomm = g_variant_get_string(src->data, NULL);
+ break;
+ }
+ }
+
+ devices = NULL;
+ for (i = 0; i < ARRAY_SIZE(scpi_devs); i++) {
+ if ((resource && strcmp(resource, scpi_devs[i]->prefix))
+ || !scpi_devs[i]->scan)
+ continue;
+ resources = scpi_devs[i]->scan(drvc);
+ for (l = resources; l; l = l->next) {
+ res = g_strsplit(l->data, ":", 2);
+ if (res[0] && (sdi = sr_scpi_scan_resource(drvc, res[0],
+ serialcomm ? serialcomm : res[1], probe_device)))
+ devices = g_slist_append(devices, sdi);
+ g_strfreev(res);
+ }
+ g_slist_free_full(resources, g_free);
+ }
+
+ if (!devices && resource) {
+ sdi = sr_scpi_scan_resource(drvc, resource, serialcomm, probe_device);
+ devices = g_slist_append(NULL, sdi);
+ }
+
+ /* Tack a copy of the newly found devices onto the driver list. */
+ if (devices)
+ drvc->instances = g_slist_concat(drvc->instances, g_slist_copy(devices));
+
+ return devices;
+}
+
+SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc,
+ const char *resource, const char *serialcomm)
+{
+ struct sr_scpi_dev_inst *scpi = NULL;
+ const struct sr_scpi_dev_inst *scpi_dev;
+ gchar **params;
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(scpi_devs); i++) {
+ scpi_dev = scpi_devs[i];
+ if (!strncmp(resource, scpi_dev->prefix, strlen(scpi_dev->prefix))) {
+ sr_dbg("Opening %s device %s.", scpi_dev->name, resource);
+ scpi = g_malloc(sizeof(*scpi));
+ *scpi = *scpi_dev;
+ scpi->priv = g_malloc0(scpi->priv_size);
+ params = g_strsplit(resource, "/", 0);
+ if (scpi->dev_inst_new(scpi->priv, drvc, resource,
+ params, serialcomm) != SR_OK) {
+ sr_scpi_free(scpi);
+ scpi = NULL;
+ }
+ g_strfreev(params);
+ break;
+ }
+ }
+
+ return scpi;
+}
+
+/**
+ * Open SCPI device.
+ *
+ * @param scpi Previously initialized SCPI device structure.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi)
+{
+ return scpi->open(scpi->priv);
+}
+
+/**
+ * Add an event source for an SCPI device.
+ *
+ * @param scpi Previously initialized SCPI device structure.
+ * @param events Events to check for.
+ * @param timeout Max time to wait before the callback is called, ignored if 0.
+ * @param cb Callback function to add. Must not be NULL.
+ * @param cb_data Data for the callback function. Can be NULL.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
+ * SR_ERR_MALLOC upon memory allocation errors.
+ */
+SR_PRIV int sr_scpi_source_add(struct sr_session *session,
+ struct sr_scpi_dev_inst *scpi, int events, int timeout,
+ sr_receive_data_callback cb, void *cb_data)
+{
+ return scpi->source_add(session, scpi->priv, events, timeout, cb, cb_data);
+}
+
+/**
+ * Remove event source for an SCPI device.
+ *
+ * @param scpi Previously initialized SCPI device structure.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
+ * SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
+ * internal errors.
+ */
+SR_PRIV int sr_scpi_source_remove(struct sr_session *session,
+ struct sr_scpi_dev_inst *scpi)
+{
+ return scpi->source_remove(session, scpi->priv);
+}
+
+/**
+ * Send a SCPI command.
+ *
+ * @param scpi Previously initialized SCPI device structure.
+ * @param format Format string, to be followed by any necessary arguments.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_send(struct sr_scpi_dev_inst *scpi,
+ const char *format, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, format);
+ ret = sr_scpi_send_variadic(scpi, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/**
+ * Send a SCPI command with a variadic argument list.
+ *
+ * @param scpi Previously initialized SCPI device structure.
+ * @param format Format string.
+ * @param args Argument list.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_send_variadic(struct sr_scpi_dev_inst *scpi,
+ const char *format, va_list args)
+{
+ va_list args_copy;
+ char *buf;
+ int len, ret;
+
+ /* Get length of buffer required. */
+ va_copy(args_copy, args);
+ len = vsnprintf(NULL, 0, format, args_copy);
+ va_end(args_copy);
+
+ /* Allocate buffer and write out command. */
+ buf = g_malloc(len + 1);
+ vsprintf(buf, format, args);
+
+ /* Send command. */
+ ret = scpi->send(scpi->priv, buf);
+
+ /* Free command buffer. */
+ g_free(buf);
+
+ return ret;
+}
+
+/**
+ * Begin receiving an SCPI reply.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_read_begin(struct sr_scpi_dev_inst *scpi)
+{
+ return scpi->read_begin(scpi->priv);
+}
+
+/**
+ * Read part of a response from SCPI device.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param buf Buffer to store result.
+ * @param maxlen Maximum number of bytes to read.
+ *
+ * @return Number of bytes read, or SR_ERR upon failure.
+ */
+SR_PRIV int sr_scpi_read_data(struct sr_scpi_dev_inst *scpi,
+ char *buf, int maxlen)
+{
+ return scpi->read_data(scpi->priv, buf, maxlen);
+}
+
+/**
+ * Check whether a complete SCPI response has been received.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ *
+ * @return 1 if complete, 0 otherwise.
+ */
+SR_PRIV int sr_scpi_read_complete(struct sr_scpi_dev_inst *scpi)
+{
+ return scpi->read_complete(scpi->priv);
+}
+
+/**
+ * Close SCPI device.
+ *
+ * @param scpi Previously initialized SCPI device structure.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_close(struct sr_scpi_dev_inst *scpi)
+{
+ return scpi->close(scpi->priv);
+}
+
+/**
+ * Free SCPI device.
+ *
+ * @param scpi Previously initialized SCPI device structure.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV void sr_scpi_free(struct sr_scpi_dev_inst *scpi)
+{
+ scpi->free(scpi->priv);
+ g_free(scpi->priv);
+ g_free(scpi);
+}
+
+/**
+ * Send a SCPI command, receive the reply and store the reply in scpi_response.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param command The SCPI command to send to the device (can be NULL).
+ * @param scpi_response Pointer where to store the SCPI response.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
+ const char *command, char **scpi_response)
+{
+ char buf[256];
+ int len;
+ GString *response;
+
+ if (command)
+ if (sr_scpi_send(scpi, command) != SR_OK)
+ return SR_ERR;
+
+ if (sr_scpi_read_begin(scpi) != SR_OK)
+ return SR_ERR;
+
+ response = g_string_new("");
+
+ *scpi_response = NULL;
+
+ while (!sr_scpi_read_complete(scpi)) {
+ len = sr_scpi_read_data(scpi, buf, sizeof(buf));
+ if (len < 0) {
+ g_string_free(response, TRUE);
+ return SR_ERR;
+ }
+ g_string_append_len(response, buf, len);
+ }
+
+ /* Get rid of trailing linefeed if present */
+ if (response->len >= 1 && response->str[response->len - 1] == '\n')
+ g_string_truncate(response, response->len - 1);
+
+ *scpi_response = response->str;
+ g_string_free(response, FALSE);
+
+ sr_spew("Got response: '%.70s'.", *scpi_response);
+
+ return SR_OK;
+}
+
+/**
+ * Send a SCPI command, read the reply, parse it as a bool value and store the
+ * result in scpi_response.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param command The SCPI command to send to the device (can be NULL).
+ * @param scpi_response Pointer where to store the parsed result.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi,
+ const char *command, gboolean *scpi_response)
+{
+ int ret;
+ char *response;
+
+ response = NULL;
+
+ if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
+ if (!response)
+ return SR_ERR;
+
+ if (parse_strict_bool(response, scpi_response) == SR_OK)
+ ret = SR_OK;
+ else
+ ret = SR_ERR;
+
+ g_free(response);
+
+ return ret;
+}
+
+/**
+ * Send a SCPI command, read the reply, parse it as an integer and store the
+ * result in scpi_response.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param command The SCPI command to send to the device (can be NULL).
+ * @param scpi_response Pointer where to store the parsed result.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_get_int(struct sr_scpi_dev_inst *scpi,
+ const char *command, int *scpi_response)
+{
+ int ret;
+ char *response;
+
+ response = NULL;
+
+ if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
+ if (!response)
+ return SR_ERR;
+
+ if (sr_atoi(response, scpi_response) == SR_OK)
+ ret = SR_OK;
+ else
+ ret = SR_ERR;
+
+ g_free(response);
+
+ return ret;
+}
+
+/**
+ * Send a SCPI command, read the reply, parse it as a float and store the
+ * result in scpi_response.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param command The SCPI command to send to the device (can be NULL).
+ * @param scpi_response Pointer where to store the parsed result.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_get_float(struct sr_scpi_dev_inst *scpi,
+ const char *command, float *scpi_response)
+{
+ int ret;
+ char *response;
+
+ response = NULL;
+
+ if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
+ if (!response)
+ return SR_ERR;
+
+ if (sr_atof_ascii(response, scpi_response) == SR_OK)
+ ret = SR_OK;
+ else
+ ret = SR_ERR;
+
+ g_free(response);
+
+ return ret;
+}
+
+/**
+ * Send a SCPI command, read the reply, parse it as a double and store the
+ * result in scpi_response.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param command The SCPI command to send to the device (can be NULL).
+ * @param scpi_response Pointer where to store the parsed result.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi,
+ const char *command, double *scpi_response)
+{
+ int ret;
+ char *response;
+
+ response = NULL;
+
+ if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
+ if (!response)
+ return SR_ERR;
+
+ if (sr_atod(response, scpi_response) == SR_OK)
+ ret = SR_OK;
+ else
+ ret = SR_ERR;
+
+ g_free(response);
+
+ return ret;
+}
+
+/**
+ * Send a SCPI *OPC? command, read the reply and return the result of the
+ * command.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_get_opc(struct sr_scpi_dev_inst *scpi)
+{
+ unsigned int i;
+ gboolean opc;
+
+ for (i = 0; i < SCPI_READ_RETRIES; ++i) {
+ sr_scpi_get_bool(scpi, SCPI_CMD_OPC, &opc);
+ if (opc)
+ return SR_OK;
+ g_usleep(SCPI_READ_RETRY_TIMEOUT);
+ }
+
+ return SR_ERR;
+}
+
+/**
+ * Send a SCPI command, read the reply, parse it as comma separated list of
+ * floats and store the as an result in scpi_response.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param command The SCPI command to send to the device (can be NULL).
+ * @param scpi_response Pointer where to store the parsed result.
+ *
+ * @return SR_OK upon successfully parsing all values, SR_ERR upon a parsing
+ * error or upon no response. The allocated response must be freed by
+ * the caller in the case of an SR_OK as well as in the case of
+ * parsing error.
+ */
+SR_PRIV int sr_scpi_get_floatv(struct sr_scpi_dev_inst *scpi,
+ const char *command, GArray **scpi_response)
+{
+ int ret;
+ float tmp;
+ char *response;
+ gchar **ptr, **tokens;
+ GArray *response_array;
+
+ ret = SR_OK;
+ response = NULL;
+ tokens = NULL;
+
+ if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
+ if (!response)
+ return SR_ERR;
+
+ tokens = g_strsplit(response, ",", 0);
+ ptr = tokens;
+
+ response_array = g_array_sized_new(TRUE, FALSE, sizeof(float), 256);
+
+ while (*ptr) {
+ if (sr_atof_ascii(*ptr, &tmp) == SR_OK)
+ response_array = g_array_append_val(response_array,
+ tmp);
+ else
+ ret = SR_ERR;
+
+ ptr++;
+ }
+ g_strfreev(tokens);
+ g_free(response);
+
+ if (ret == SR_ERR && response_array->len == 0) {
+ g_array_free(response_array, TRUE);
+ *scpi_response = NULL;
+ return SR_ERR;
+ }
+
+ *scpi_response = response_array;
+
+ return ret;
+}
+
+/**
+ * Send a SCPI command, read the reply, parse it as comma separated list of
+ * unsigned 8 bit integers and store the as an result in scpi_response.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param command The SCPI command to send to the device (can be NULL).
+ * @param scpi_response Pointer where to store the parsed result.
+ *
+ * @return SR_OK upon successfully parsing all values, SR_ERR upon a parsing
+ * error or upon no response. The allocated response must be freed by
+ * the caller in the case of an SR_OK as well as in the case of
+ * parsing error.
+ */
+SR_PRIV int sr_scpi_get_uint8v(struct sr_scpi_dev_inst *scpi,
+ const char *command, GArray **scpi_response)
+{
+ int tmp, ret;
+ char *response;
+ gchar **ptr, **tokens;
+ GArray *response_array;
+
+ ret = SR_OK;
+ response = NULL;
+ tokens = NULL;
+
+ if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
+ if (!response)
+ return SR_ERR;
+
+ tokens = g_strsplit(response, ",", 0);
+ ptr = tokens;
+
+ response_array = g_array_sized_new(TRUE, FALSE, sizeof(uint8_t), 256);
+
+ while (*ptr) {
+ if (sr_atoi(*ptr, &tmp) == SR_OK)
+ response_array = g_array_append_val(response_array,
+ tmp);
+ else
+ ret = SR_ERR;
+
+ ptr++;
+ }
+ g_strfreev(tokens);
+ g_free(response);
+
+ if (response_array->len == 0) {
+ g_array_free(response_array, TRUE);
+ *scpi_response = NULL;
+ return SR_ERR;
+ }
+
+ *scpi_response = response_array;
+
+ return ret;
+}
+
+/**
+ * Send the *IDN? SCPI command, receive the reply, parse it and store the
+ * reply as a sr_scpi_hw_info structure in the supplied scpi_response pointer.
+ *
+ * The hw_info structure must be freed by the caller via sr_scpi_hw_info_free().
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param scpi_response Pointer where to store the hw_info structure.
+ *
+ * @return SR_OK upon success, SR_ERR on failure.
+ */
+SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
+ struct sr_scpi_hw_info **scpi_response)
+{
+ int num_tokens;
+ char *response;
+ gchar **tokens;
+ struct sr_scpi_hw_info *hw_info;
+
+ response = NULL;
+ tokens = NULL;
+
+ if (sr_scpi_get_string(scpi, SCPI_CMD_IDN, &response) != SR_OK)
+ if (!response)
+ return SR_ERR;
+
+ sr_info("Got IDN string: '%s'", response);
+
+ /*
+ * The response to a '*IDN?' is specified by the SCPI spec. It contains
+ * a comma-separated list containing the manufacturer name, instrument
+ * model, serial number of the instrument and the firmware version.
+ */
+ tokens = g_strsplit(response, ",", 0);
+
+ for (num_tokens = 0; tokens[num_tokens] != NULL; num_tokens++);
+
+ if (num_tokens != 4) {
+ sr_dbg("IDN response not according to spec: %80.s.", response);
+ g_strfreev(tokens);
+ g_free(response);
+ return SR_ERR;
+ }
+ g_free(response);
+
+ hw_info = g_try_malloc(sizeof(struct sr_scpi_hw_info));
+ if (!hw_info) {
+ g_strfreev(tokens);
+ return SR_ERR_MALLOC;
+ }
+
+ hw_info->manufacturer = g_strdup(tokens[0]);
+ hw_info->model = g_strdup(tokens[1]);
+ hw_info->serial_number = g_strdup(tokens[2]);
+ hw_info->firmware_version = g_strdup(tokens[3]);
+
+ g_strfreev(tokens);
+
+ *scpi_response = hw_info;
+
+ return SR_OK;
+}
+
+/**
+ * Free a sr_scpi_hw_info struct.
+ *
+ * @param hw_info Pointer to the struct to free.
+ *
+ * This function is safe to call with a NULL pointer.
+ */
+SR_PRIV void sr_scpi_hw_info_free(struct sr_scpi_hw_info *hw_info)
+{
+ if (hw_info) {
+ g_free(hw_info->manufacturer);
+ g_free(hw_info->model);
+ g_free(hw_info->serial_number);
+ g_free(hw_info->firmware_version);
+ g_free(hw_info);
+ }
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
+ * Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
+ *
+ * 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.h"
+#include "libsigrok-internal.h"
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_PREFIX "scpi_serial"
+
+#define BUFFER_SIZE 1024
+
+struct scpi_serial {
+ struct sr_serial_dev_inst *serial;
+ char buffer[BUFFER_SIZE];
+ size_t count;
+ size_t read;
+};
+
+static struct {
+ uint16_t vendor_id;
+ uint16_t product_id;
+ const char *serialcomm;
+} scpi_serial_usb_ids[] = {
+ { 0x0403, 0xed72, "115200/8n1/flow=1" }, /* Hameg HO720 */
+ { 0x0403, 0xed73, "115200/8n1/flow=1" }, /* Hameg HO730 */
+};
+
+static GSList *scpi_serial_scan(struct drv_context *drvc)
+{
+ GSList *l, *r, *resources = NULL;
+ gchar *res;
+ unsigned i;
+
+ (void)drvc;
+
+ for (i = 0; i < ARRAY_SIZE(scpi_serial_usb_ids); i++) {
+ if ((l = sr_serial_find_usb(scpi_serial_usb_ids[i].vendor_id,
+ scpi_serial_usb_ids[i].product_id)) == NULL)
+ continue;
+ for (r = l; r; r = r->next) {
+ if (scpi_serial_usb_ids[i].serialcomm)
+ res = g_strdup_printf("%s:%s", (char *) r->data,
+ scpi_serial_usb_ids[i].serialcomm);
+ else
+ res = g_strdup(r->data);
+ resources = g_slist_append(resources, res);
+ }
+ g_slist_free_full(l, g_free);
+ }
+
+ return resources;
+}
+
+static int scpi_serial_dev_inst_new(void *priv, struct drv_context *drvc,
+ const char *resource, char **params, const char *serialcomm)
+{
+ struct scpi_serial *sscpi = priv;
+
+ (void)drvc;
+ (void)params;
+
+ if (!(sscpi->serial = sr_serial_dev_inst_new(resource, serialcomm)))
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static int scpi_serial_open(void *priv)
+{
+ struct scpi_serial *sscpi = priv;
+ struct sr_serial_dev_inst *serial = sscpi->serial;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return SR_ERR;
+
+ if (serial_flush(serial) != SR_OK)
+ return SR_ERR;
+
+ sscpi->count = 0;
+ sscpi->read = 0;
+
+ return SR_OK;
+}
+
+static int scpi_serial_source_add(struct sr_session *session, void *priv,
+ int events, int timeout, sr_receive_data_callback cb, void *cb_data)
+{
+ struct scpi_serial *sscpi = priv;
+ struct sr_serial_dev_inst *serial = sscpi->serial;
+
+ return serial_source_add(session, serial, events, timeout, cb, cb_data);
+}
+
+static int scpi_serial_source_remove(struct sr_session *session, void *priv)
+{
+ struct scpi_serial *sscpi = priv;
+ struct sr_serial_dev_inst *serial = sscpi->serial;
+
+ return serial_source_remove(session, serial);
+}
+
+static int scpi_serial_send(void *priv, const char *command)
+{
+ int len, result, written;
+ gchar *terminated_command;
+ struct scpi_serial *sscpi = priv;
+ struct sr_serial_dev_inst *serial = sscpi->serial;
+
+ terminated_command = g_strconcat(command, "\n", NULL);
+ len = strlen(terminated_command);
+ written = 0;
+ while (written < len) {
+ result = serial_write(serial, terminated_command + written, len - written);
+ if (result < 0) {
+ sr_err("Error while sending SCPI command: '%s'.", command);
+ g_free(terminated_command);
+ return SR_ERR;
+ }
+ written += result;
+ }
+
+ g_free(terminated_command);
+
+ sr_spew("Successfully sent SCPI command: '%s'.", command);
+
+ return SR_OK;
+}
+
+static int scpi_serial_read_begin(void *priv)
+{
+ (void) priv;
+
+ return SR_OK;
+}
+
+static int scpi_serial_read_data(void *priv, char *buf, int maxlen)
+{
+ struct scpi_serial *sscpi = priv;
+ int len, ret;
+
+ len = BUFFER_SIZE - sscpi->count;
+
+ /* Try to read new data into the buffer if there is space. */
+ if (len > 0) {
+ ret = serial_read(sscpi->serial, sscpi->buffer + sscpi->read,
+ BUFFER_SIZE - sscpi->count);
+
+ if (ret < 0)
+ return ret;
+
+ sscpi->count += ret;
+
+ if (ret > 0)
+ sr_spew("Read %d bytes into buffer.", ret);
+ }
+
+ /* Return as many bytes as possible from buffer, excluding any trailing newline. */
+ if (sscpi->read < sscpi->count) {
+ len = sscpi->count - sscpi->read;
+ if (len > maxlen)
+ len = maxlen;
+ if (sscpi->buffer[sscpi->read + len - 1] == '\n')
+ len--;
+ sr_spew("Returning %d bytes from buffer.", len);
+ memcpy(buf, sscpi->buffer + sscpi->read, len);
+ sscpi->read += len;
+ if (sscpi->read == BUFFER_SIZE) {
+ sr_spew("Resetting buffer.");
+ sscpi->count = 0;
+ sscpi->read = 0;
+ }
+ return len;
+ }
+
+ return 0;
+}
+
+static int scpi_serial_read_complete(void *priv)
+{
+ struct scpi_serial *sscpi = priv;
+
+ /* If the next character is a newline, discard it and report complete. */
+ if (sscpi->read < sscpi->count && sscpi->buffer[sscpi->read] == '\n') {
+ sscpi->read++;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int scpi_serial_close(void *priv)
+{
+ struct scpi_serial *sscpi = priv;
+
+ return serial_close(sscpi->serial);
+}
+
+static void scpi_serial_free(void *priv)
+{
+ struct scpi_serial *sscpi = priv;
+
+ sr_serial_dev_inst_free(sscpi->serial);
+}
+
+SR_PRIV const struct sr_scpi_dev_inst scpi_serial_dev = {
+ .name = "serial",
+ .prefix = "",
+ .priv_size = sizeof(struct scpi_serial),
+ .scan = scpi_serial_scan,
+ .dev_inst_new = scpi_serial_dev_inst_new,
+ .open = scpi_serial_open,
+ .source_add = scpi_serial_source_add,
+ .source_remove = scpi_serial_source_remove,
+ .send = scpi_serial_send,
+ .read_begin = scpi_serial_read_begin,
+ .read_data = scpi_serial_read_data,
+ .read_complete = scpi_serial_read_complete,
+ .close = scpi_serial_close,
+ .free = scpi_serial_free,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
+ *
+ * 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/>.
+ */
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0501
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#include <glib.h>
+#include <string.h>
+#include <unistd.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+#include <errno.h>
+
+#define LOG_PREFIX "scpi_tcp"
+
+#define LENGTH_BYTES 4
+
+struct scpi_tcp {
+ char *address;
+ char *port;
+ int socket;
+ char length_buf[LENGTH_BYTES];
+ int length_bytes_read;
+ int response_length;
+ int response_bytes_read;
+};
+
+static int scpi_tcp_dev_inst_new(void *priv, struct drv_context *drvc,
+ const char *resource, char **params, const char *serialcomm)
+{
+ struct scpi_tcp *tcp = priv;
+
+ (void)drvc;
+ (void)resource;
+ (void)serialcomm;
+
+ if (!params || !params[1] || !params[2]) {
+ sr_err("Invalid parameters.");
+ return SR_ERR;
+ }
+
+ tcp->address = g_strdup(params[1]);
+ tcp->port = g_strdup(params[2]);
+ tcp->socket = -1;
+
+ return SR_OK;
+}
+
+static int scpi_tcp_open(void *priv)
+{
+ struct scpi_tcp *tcp = priv;
+ struct addrinfo hints;
+ struct addrinfo *results, *res;
+ int err;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ err = getaddrinfo(tcp->address, tcp->port, &hints, &results);
+
+ if (err) {
+ sr_err("Address lookup failed: %s:%d: %s", tcp->address, tcp->port,
+ gai_strerror(err));
+ return SR_ERR;
+ }
+
+ for (res = results; res; res = res->ai_next) {
+ if ((tcp->socket = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) < 0)
+ continue;
+ if (connect(tcp->socket, res->ai_addr, res->ai_addrlen) != 0) {
+ close(tcp->socket);
+ tcp->socket = -1;
+ continue;
+ }
+ break;
+ }
+
+ freeaddrinfo(results);
+
+ if (tcp->socket < 0) {
+ sr_err("Failed to connect to %s:%s: %s", tcp->address, tcp->port,
+ strerror(errno));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int scpi_tcp_source_add(struct sr_session *session, void *priv,
+ int events, int timeout, sr_receive_data_callback cb, void *cb_data)
+{
+ struct scpi_tcp *tcp = priv;
+
+ return sr_session_source_add(session, tcp->socket, events, timeout,
+ cb, cb_data);
+}
+
+static int scpi_tcp_source_remove(struct sr_session *session, void *priv)
+{
+ struct scpi_tcp *tcp = priv;
+
+ return sr_session_source_remove(session, tcp->socket);
+}
+
+static int scpi_tcp_send(void *priv, const char *command)
+{
+ struct scpi_tcp *tcp = priv;
+ int len, out;
+ char *terminated_command;
+
+ terminated_command = g_strdup_printf("%s\r\n", command);
+ len = strlen(terminated_command);
+ out = send(tcp->socket, terminated_command, len, 0);
+ g_free(terminated_command);
+
+ if (out < 0) {
+ sr_err("Send error: %s", strerror(errno));
+ return SR_ERR;
+ }
+
+ if (out < len) {
+ sr_dbg("Only sent %d/%d bytes of SCPI command: '%s'.", out,
+ len, command);
+ }
+
+ sr_spew("Successfully sent SCPI command: '%s'.", command);
+
+ return SR_OK;
+}
+
+static int scpi_tcp_read_begin(void *priv)
+{
+ struct scpi_tcp *tcp = priv;
+
+ tcp->response_bytes_read = 0;
+ tcp->length_bytes_read = 0;
+
+ return SR_OK;
+}
+
+static int scpi_tcp_raw_read_data(void *priv, char *buf, int maxlen)
+{
+ struct scpi_tcp *tcp = priv;
+ int len;
+
+ len = recv(tcp->socket, buf, maxlen, 0);
+
+ if (len < 0) {
+ sr_err("Receive error: %s", strerror(errno));
+ return SR_ERR;
+ }
+
+ tcp->length_bytes_read = LENGTH_BYTES;
+ tcp->response_length = len < maxlen ? len : maxlen + 1;
+ tcp->response_bytes_read = len;
+
+ return len;
+}
+
+static int scpi_tcp_rigol_read_data(void *priv, char *buf, int maxlen)
+{
+ struct scpi_tcp *tcp = priv;
+ int len;
+
+ if (tcp->length_bytes_read < LENGTH_BYTES) {
+ len = recv(tcp->socket, tcp->length_buf + tcp->length_bytes_read,
+ LENGTH_BYTES - tcp->length_bytes_read, 0);
+ if (len < 0) {
+ sr_err("Receive error: %s", strerror(errno));
+ return SR_ERR;
+ }
+
+ tcp->length_bytes_read += len;
+
+ if (tcp->length_bytes_read < LENGTH_BYTES)
+ return 0;
+ else
+ tcp->response_length = RL32(tcp->length_buf);
+ }
+
+ if (tcp->response_bytes_read >= tcp->response_length)
+ return SR_ERR;
+
+ len = recv(tcp->socket, buf, maxlen, 0);
+
+ if (len < 0) {
+ sr_err("Receive error: %s", strerror(errno));
+ return SR_ERR;
+ }
+
+ tcp->response_bytes_read += len;
+
+ return len;
+}
+
+static int scpi_tcp_read_complete(void *priv)
+{
+ struct scpi_tcp *tcp = priv;
+
+ return (tcp->length_bytes_read == LENGTH_BYTES &&
+ tcp->response_bytes_read >= tcp->response_length);
+}
+
+static int scpi_tcp_close(void *priv)
+{
+ struct scpi_tcp *tcp = priv;
+
+ if (close(tcp->socket) < 0)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static void scpi_tcp_free(void *priv)
+{
+ struct scpi_tcp *tcp = priv;
+
+ g_free(tcp->address);
+ g_free(tcp->port);
+}
+
+SR_PRIV const struct sr_scpi_dev_inst scpi_tcp_raw_dev = {
+ .name = "RAW TCP",
+ .prefix = "tcp-raw",
+ .priv_size = sizeof(struct scpi_tcp),
+ .dev_inst_new = scpi_tcp_dev_inst_new,
+ .open = scpi_tcp_open,
+ .source_add = scpi_tcp_source_add,
+ .source_remove = scpi_tcp_source_remove,
+ .send = scpi_tcp_send,
+ .read_begin = scpi_tcp_read_begin,
+ .read_data = scpi_tcp_raw_read_data,
+ .read_complete = scpi_tcp_read_complete,
+ .close = scpi_tcp_close,
+ .free = scpi_tcp_free,
+};
+
+SR_PRIV const struct sr_scpi_dev_inst scpi_tcp_rigol_dev = {
+ .name = "RIGOL TCP",
+ .prefix = "tcp-rigol",
+ .priv_size = sizeof(struct scpi_tcp),
+ .dev_inst_new = scpi_tcp_dev_inst_new,
+ .open = scpi_tcp_open,
+ .source_add = scpi_tcp_source_add,
+ .source_remove = scpi_tcp_source_remove,
+ .send = scpi_tcp_send,
+ .read_begin = scpi_tcp_read_begin,
+ .read_data = scpi_tcp_rigol_read_data,
+ .read_complete = scpi_tcp_read_complete,
+ .close = scpi_tcp_close,
+ .free = scpi_tcp_free,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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 <string.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "scpi_usbtmc"
+
+#define MAX_TRANSFER_LENGTH 2048
+#define TRANSFER_TIMEOUT 1000
+
+struct scpi_usbtmc_libusb {
+ struct sr_context *ctx;
+ struct sr_usb_dev_inst *usb;
+ int detached_kernel_driver;
+ uint8_t interface;
+ uint8_t bulk_in_ep;
+ uint8_t bulk_out_ep;
+ uint8_t interrupt_ep;
+ uint8_t usbtmc_int_cap;
+ uint8_t usbtmc_dev_cap;
+ uint8_t usb488_dev_cap;
+ uint8_t bTag;
+ uint8_t bulkin_attributes;
+ uint8_t buffer[MAX_TRANSFER_LENGTH];
+ int response_length;
+ int response_bytes_read;
+ int remaining_length;
+ int rigol_ds1000;
+};
+
+/* Some USBTMC-specific enums, as defined in the USBTMC standard. */
+#define SUBCLASS_USBTMC 0x03
+#define USBTMC_USB488 0x01
+
+enum {
+ /* USBTMC control requests */
+ INITIATE_ABORT_BULK_OUT = 1,
+ CHECK_ABORT_BULK_OUT_STATUS = 2,
+ INITIATE_ABORT_BULK_IN = 3,
+ CHECK_ABORT_BULK_IN_STATUS = 4,
+ INITIATE_CLEAR = 5,
+ CHECK_CLEAR_STATUS = 6,
+ GET_CAPABILITIES = 7,
+ INDICATOR_PULSE = 64,
+
+ /* USB488 control requests */
+ READ_STATUS_BYTE = 128,
+ REN_CONTROL = 160,
+ GO_TO_LOCAL = 161,
+ LOCAL_LOCKOUT = 162,
+};
+
+/* USBTMC capabilities */
+#define USBTMC_INT_CAP_LISTEN_ONLY 0x01
+#define USBTMC_INT_CAP_TALK_ONLY 0x02
+#define USBTMC_INT_CAP_INDICATOR 0x04
+
+#define USBTMC_DEV_CAP_TERMCHAR 0x01
+
+#define USB488_DEV_CAP_DT1 0x01
+#define USB488_DEV_CAP_RL1 0x02
+#define USB488_DEV_CAP_SR1 0x04
+#define USB488_DEV_CAP_SCPI 0x08
+
+/* Bulk messages constants */
+#define USBTMC_BULK_HEADER_SIZE 12
+
+/* Bulk MsgID values */
+#define DEV_DEP_MSG_OUT 1
+#define REQUEST_DEV_DEP_MSG_IN 2
+#define DEV_DEP_MSG_IN 2
+
+/* bmTransferAttributes */
+#define EOM 0x01
+#define TERM_CHAR_ENABLED 0x02
+
+
+static GSList *scpi_usbtmc_libusb_scan(struct drv_context *drvc)
+{
+ struct libusb_device **devlist;
+ struct libusb_device_descriptor des;
+ struct libusb_config_descriptor *confdes;
+ const struct libusb_interface_descriptor *intfdes;
+ GSList *resources = NULL;
+ int confidx, intfidx, ret, i;
+ char *res;
+
+ ret = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ if (ret < 0) {
+ sr_err("Failed to get device list: %s.",
+ libusb_error_name(ret));
+ return NULL;
+ }
+ for (i = 0; devlist[i]; i++) {
+ if ((ret = libusb_get_device_descriptor(devlist[i], &des)) < 0) {
+ sr_err("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ for (confidx = 0; confidx < des.bNumConfigurations; confidx++) {
+ if ((ret = libusb_get_config_descriptor(devlist[i], confidx, &confdes)) < 0) {
+ sr_dbg("Failed to get configuration descriptor: %s, "
+ "ignoring device.", libusb_error_name(ret));
+ break;
+ }
+ for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) {
+ intfdes = confdes->interface[intfidx].altsetting;
+ if (intfdes->bInterfaceClass != LIBUSB_CLASS_APPLICATION ||
+ intfdes->bInterfaceSubClass != SUBCLASS_USBTMC ||
+ intfdes->bInterfaceProtocol != USBTMC_USB488)
+ continue;
+ sr_dbg("Found USBTMC device (VID:PID = %04x:%04x, "
+ "bus.address = %d.%d).", des.idVendor, des.idProduct,
+ libusb_get_bus_number(devlist[i]),
+ libusb_get_device_address(devlist[i]));
+ res = g_strdup_printf("usbtmc/%d.%d",
+ libusb_get_bus_number(devlist[i]),
+ libusb_get_device_address(devlist[i]));
+ resources = g_slist_append(resources, res);
+ }
+ libusb_free_config_descriptor(confdes);
+ }
+ }
+ libusb_free_device_list(devlist, 1);
+
+ sr_dbg("Found %d device(s).", g_slist_length(resources));
+
+ return resources;
+}
+
+static int scpi_usbtmc_libusb_dev_inst_new(void *priv, struct drv_context *drvc,
+ const char *resource, char **params, const char *serialcomm)
+{
+ struct scpi_usbtmc_libusb *uscpi = priv;
+ GSList *devices;
+
+ (void)resource;
+ (void)serialcomm;
+
+ if (!params || !params[1]) {
+ sr_err("Invalid parameters.");
+ return SR_ERR;
+ }
+
+ uscpi->ctx = drvc->sr_ctx;
+ devices = sr_usb_find(uscpi->ctx->libusb_ctx, params[1]);
+ if (g_slist_length(devices) != 1) {
+ sr_err("Failed to find USB device '%s'.", params[1]);
+ g_slist_free_full(devices, (GDestroyNotify)sr_usb_dev_inst_free);
+ return SR_ERR;
+ }
+ uscpi->usb = devices->data;
+ g_slist_free(devices);
+
+ return SR_OK;
+}
+
+static int scpi_usbtmc_libusb_open(void *priv)
+{
+ struct scpi_usbtmc_libusb *uscpi = priv;
+ struct sr_usb_dev_inst *usb = uscpi->usb;
+ struct libusb_device *dev;
+ struct libusb_device_descriptor des;
+ struct libusb_config_descriptor *confdes;
+ const struct libusb_interface_descriptor *intfdes;
+ const struct libusb_endpoint_descriptor *ep;
+ int confidx, intfidx, epidx, config = 0;
+ uint8_t capabilities[24];
+ int ret, found = 0;
+
+ if (usb->devhdl)
+ return SR_OK;
+
+ if (sr_usb_open(uscpi->ctx->libusb_ctx, usb) != SR_OK)
+ return SR_ERR;
+
+ dev = libusb_get_device(usb->devhdl);
+ if ((ret = libusb_get_device_descriptor(dev, &des)) < 0) {
+ sr_err("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ for (confidx = 0; confidx < des.bNumConfigurations; confidx++) {
+ if ((ret = libusb_get_config_descriptor(dev, confidx, &confdes)) < 0) {
+ sr_dbg("Failed to get configuration descriptor: %s, "
+ "ignoring device.", libusb_error_name(ret));
+ continue;
+ }
+ for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) {
+ intfdes = confdes->interface[intfidx].altsetting;
+ if (intfdes->bInterfaceClass != LIBUSB_CLASS_APPLICATION ||
+ intfdes->bInterfaceSubClass != SUBCLASS_USBTMC ||
+ intfdes->bInterfaceProtocol != USBTMC_USB488)
+ continue;
+ uscpi->interface = intfdes->bInterfaceNumber;
+ sr_dbg("Interface %d", uscpi->interface);
+ config = confdes->bConfigurationValue;
+ sr_dbg("Configuration %d", config);
+ for (epidx = 0; epidx < intfdes->bNumEndpoints; epidx++) {
+ ep = &intfdes->endpoint[epidx];
+ if (ep->bmAttributes == LIBUSB_TRANSFER_TYPE_BULK &&
+ !(ep->bEndpointAddress & (LIBUSB_ENDPOINT_DIR_MASK))) {
+ uscpi->bulk_out_ep = ep->bEndpointAddress;
+ sr_dbg("Bulk OUT EP %d", uscpi->bulk_out_ep);
+ }
+ if (ep->bmAttributes == LIBUSB_TRANSFER_TYPE_BULK &&
+ ep->bEndpointAddress & (LIBUSB_ENDPOINT_DIR_MASK)) {
+ uscpi->bulk_in_ep = ep->bEndpointAddress;
+ sr_dbg("Bulk IN EP %d", uscpi->bulk_in_ep);
+ }
+ if (ep->bmAttributes == LIBUSB_TRANSFER_TYPE_INTERRUPT &&
+ ep->bEndpointAddress & (LIBUSB_ENDPOINT_DIR_MASK)) {
+ uscpi->interrupt_ep = ep->bEndpointAddress;
+ sr_dbg("Interrupt EP %d", uscpi->interrupt_ep);
+ }
+ }
+ found = 1;
+ uscpi->rigol_ds1000 = des.idVendor == 0x1ab1 &&
+ des.idProduct == 0x0588;
+ }
+ libusb_free_config_descriptor(confdes);
+ if (found)
+ break;
+ }
+
+ if (!found) {
+ sr_err("Failed to find USBTMC interface.");
+ return SR_ERR;
+ }
+
+ if (libusb_kernel_driver_active(usb->devhdl, uscpi->interface) == 1) {
+ if ((ret = libusb_detach_kernel_driver(usb->devhdl,
+ uscpi->interface)) < 0) {
+ sr_err("Failed to detach kernel driver: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ uscpi->detached_kernel_driver = 1;
+ }
+
+ if ((ret = libusb_set_configuration(usb->devhdl, config)) < 0) {
+ sr_err("Failed to set configuration: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if ((ret = libusb_claim_interface(usb->devhdl, uscpi->interface)) < 0) {
+ sr_err("Failed to claim interface: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if (!uscpi->rigol_ds1000) {
+ if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_in_ep)) < 0) {
+ sr_err("Failed to clear halt/stall condition for EP %d: %s.",
+ uscpi->bulk_in_ep, libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_out_ep)) < 0) {
+ sr_err("Failed to clear halt/stall condition for EP %d: %s.",
+ uscpi->bulk_out_ep, libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if ((ret = libusb_clear_halt(usb->devhdl, uscpi->interrupt_ep)) < 0) {
+ sr_err("Failed to clear halt/stall condition for EP %d: %s.",
+ uscpi->interrupt_ep, libusb_error_name(ret));
+ return SR_ERR;
+ }
+ }
+
+ /* Get capabilities. */
+ ret = libusb_control_transfer(usb->devhdl,
+ LIBUSB_ENDPOINT_IN |
+ LIBUSB_REQUEST_TYPE_CLASS |
+ LIBUSB_RECIPIENT_INTERFACE,
+ GET_CAPABILITIES, 0,
+ uscpi->interface,
+ capabilities, sizeof(capabilities),
+ TRANSFER_TIMEOUT);
+ if (ret == sizeof(capabilities)) {
+ uscpi->usbtmc_int_cap = capabilities[ 4];
+ uscpi->usbtmc_dev_cap = capabilities[ 5];
+ uscpi->usb488_dev_cap = capabilities[15];
+ }
+ sr_dbg("Device capabilities: %s%s%s%s%s, %s, %s",
+ uscpi->usb488_dev_cap & USB488_DEV_CAP_SCPI ? "SCPI, " : "",
+ uscpi->usbtmc_dev_cap & USBTMC_DEV_CAP_TERMCHAR ? "TermChar, ": "",
+ uscpi->usbtmc_int_cap & USBTMC_INT_CAP_LISTEN_ONLY? "L3, " :
+ uscpi->usbtmc_int_cap & USBTMC_INT_CAP_TALK_ONLY ? "" : "L4, ",
+ uscpi->usbtmc_int_cap & USBTMC_INT_CAP_TALK_ONLY ? "T5, " :
+ uscpi->usbtmc_int_cap & USBTMC_INT_CAP_LISTEN_ONLY? "" : "T6, ",
+ uscpi->usb488_dev_cap & USB488_DEV_CAP_SR1 ? "SR1" : "SR0",
+ uscpi->usb488_dev_cap & USB488_DEV_CAP_RL1 ? "RL1" : "RL0",
+ uscpi->usb488_dev_cap & USB488_DEV_CAP_DT1 ? "DT1" : "DT0");
+
+ return SR_OK;
+}
+
+static int scpi_usbtmc_libusb_source_add(struct sr_session *session,
+ void *priv, int events, int timeout, sr_receive_data_callback cb,
+ void *cb_data)
+{
+ struct scpi_usbtmc_libusb *uscpi = priv;
+ (void)events;
+ return usb_source_add(session, uscpi->ctx, timeout, cb, cb_data);
+}
+
+static int scpi_usbtmc_libusb_source_remove(struct sr_session *session,
+ void *priv)
+{
+ struct scpi_usbtmc_libusb *uscpi = priv;
+ return usb_source_remove(session, uscpi->ctx);
+}
+
+static void usbtmc_bulk_out_header_write(void *header, uint8_t MsgID,
+ uint8_t bTag,
+ uint32_t TransferSize,
+ uint8_t bmTransferAttributes,
+ char TermChar)
+{
+ W8(header+ 0, MsgID);
+ W8(header+ 1, bTag);
+ W8(header+ 2, ~bTag);
+ W8(header+ 3, 0);
+ WL32(header+ 4, TransferSize);
+ W8(header+ 8, bmTransferAttributes);
+ W8(header+ 9, TermChar);
+ WL16(header+10, 0);
+}
+
+static int usbtmc_bulk_in_header_read(void *header, uint8_t MsgID,
+ unsigned char bTag,
+ int32_t *TransferSize,
+ uint8_t *bmTransferAttributes)
+{
+ if (R8(header+0) != MsgID ||
+ R8(header+1) != bTag ||
+ R8(header+2) != (unsigned char)~bTag)
+ return SR_ERR;
+ if (TransferSize)
+ *TransferSize = RL32(header+4);
+ if (bmTransferAttributes)
+ *bmTransferAttributes = R8(header+8);
+ return SR_OK;
+}
+
+static int scpi_usbtmc_bulkout(struct scpi_usbtmc_libusb *uscpi,
+ uint8_t msg_id, const void *data, int32_t size,
+ uint8_t transfer_attributes)
+{
+ struct sr_usb_dev_inst *usb = uscpi->usb;
+ int padded_size, ret, transferred;
+
+ if (data && size+USBTMC_BULK_HEADER_SIZE+3 > (int)sizeof(uscpi->buffer)) {
+ sr_err("USBTMC bulk out transfer is too big.");
+ return SR_ERR;
+ }
+
+ uscpi->bTag++;
+ uscpi->bTag += !uscpi->bTag; /* bTag == 0 is invalid so avoid it. */
+
+ usbtmc_bulk_out_header_write(uscpi->buffer, msg_id, uscpi->bTag,
+ size, transfer_attributes, 0);
+ if (data)
+ memcpy(uscpi->buffer+USBTMC_BULK_HEADER_SIZE, data, size);
+ else
+ size = 0;
+ size += USBTMC_BULK_HEADER_SIZE;
+ padded_size = (size + 3) & ~0x3;
+ memset(uscpi->buffer+size, 0, padded_size - size);
+
+ ret = libusb_bulk_transfer(usb->devhdl, uscpi->bulk_out_ep,
+ uscpi->buffer, padded_size, &transferred,
+ TRANSFER_TIMEOUT);
+ if (ret < 0) {
+ sr_err("USBTMC bulk out transfer error: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if (transferred < padded_size) {
+ sr_dbg("USBTMC bulk out partial transfer (%d/%d bytes).",
+ transferred, padded_size);
+ return SR_ERR;
+ }
+
+ return transferred - USBTMC_BULK_HEADER_SIZE;
+}
+
+static int scpi_usbtmc_bulkin_start(struct scpi_usbtmc_libusb *uscpi,
+ uint8_t msg_id, void *data, int32_t size,
+ uint8_t *transfer_attributes)
+{
+ struct sr_usb_dev_inst *usb = uscpi->usb;
+ int ret, transferred, message_size;
+
+ ret = libusb_bulk_transfer(usb->devhdl, uscpi->bulk_in_ep, data, size,
+ &transferred, TRANSFER_TIMEOUT);
+ if (ret < 0) {
+ sr_err("USBTMC bulk in transfer error: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if (usbtmc_bulk_in_header_read(data, msg_id, uscpi->bTag, &message_size,
+ transfer_attributes) != SR_OK) {
+ sr_err("USBTMC invalid bulk in header.");
+ return SR_ERR;
+ }
+
+ message_size += USBTMC_BULK_HEADER_SIZE;
+ uscpi->response_length = MIN(transferred, message_size);
+ uscpi->response_bytes_read = USBTMC_BULK_HEADER_SIZE;
+ uscpi->remaining_length = message_size - uscpi->response_length;
+
+ return transferred - USBTMC_BULK_HEADER_SIZE;
+}
+
+static int scpi_usbtmc_bulkin_continue(struct scpi_usbtmc_libusb *uscpi,
+ void *data, int size)
+{
+ struct sr_usb_dev_inst *usb = uscpi->usb;
+ int ret, transferred;
+
+ ret = libusb_bulk_transfer(usb->devhdl, uscpi->bulk_in_ep, data, size,
+ &transferred, TRANSFER_TIMEOUT);
+ if (ret < 0) {
+ sr_err("USBTMC bulk in transfer error: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ uscpi->response_length = MIN(transferred, uscpi->remaining_length);
+ uscpi->response_bytes_read = 0;
+ uscpi->remaining_length -= uscpi->response_length;
+
+ return transferred;
+}
+
+static int scpi_usbtmc_libusb_send(void *priv, const char *command)
+{
+ struct scpi_usbtmc_libusb *uscpi = priv;
+
+ if (scpi_usbtmc_bulkout(uscpi, DEV_DEP_MSG_OUT,
+ command, strlen(command), EOM) <= 0)
+ return SR_ERR;
+
+ sr_spew("Successfully sent SCPI command: '%s'.", command);
+
+ return SR_OK;
+}
+
+static int scpi_usbtmc_libusb_read_begin(void *priv)
+{
+ struct scpi_usbtmc_libusb *uscpi = priv;
+
+ uscpi->remaining_length = 0;
+
+ if (scpi_usbtmc_bulkout(uscpi, REQUEST_DEV_DEP_MSG_IN,
+ NULL, INT32_MAX, 0) < 0)
+ return SR_ERR;
+ if (scpi_usbtmc_bulkin_start(uscpi, DEV_DEP_MSG_IN,
+ uscpi->buffer, sizeof(uscpi->buffer),
+ &uscpi->bulkin_attributes) < 0)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static int scpi_usbtmc_libusb_read_data(void *priv, char *buf, int maxlen)
+{
+ struct scpi_usbtmc_libusb *uscpi = priv;
+ int read_length;
+
+ if (uscpi->response_bytes_read >= uscpi->response_length) {
+ if (uscpi->remaining_length > 0) {
+ if (scpi_usbtmc_bulkin_continue(uscpi, uscpi->buffer,
+ sizeof(uscpi->buffer)) <= 0)
+ return SR_ERR;
+ } else {
+ if (uscpi->bulkin_attributes & EOM)
+ return SR_ERR;
+ if (scpi_usbtmc_libusb_read_begin(uscpi) < 0)
+ return SR_ERR;
+ }
+ }
+
+ read_length = MIN(uscpi->response_length - uscpi->response_bytes_read, maxlen);
+
+ memcpy(buf, uscpi->buffer + uscpi->response_bytes_read, read_length);
+
+ uscpi->response_bytes_read += read_length;
+
+ return read_length;
+}
+
+static int scpi_usbtmc_libusb_read_complete(void *priv)
+{
+ struct scpi_usbtmc_libusb *uscpi = priv;
+ return uscpi->response_bytes_read >= uscpi->response_length &&
+ uscpi->remaining_length <= 0 &&
+ uscpi->bulkin_attributes & EOM;
+}
+
+static int scpi_usbtmc_libusb_close(void *priv)
+{
+ int ret;
+ struct scpi_usbtmc_libusb *uscpi = priv;
+ struct sr_usb_dev_inst *usb = uscpi->usb;
+
+ if (!usb->devhdl)
+ return SR_ERR;
+
+ if (!uscpi->rigol_ds1000) {
+ if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_in_ep)) < 0)
+ sr_err("Failed to clear halt/stall condition for EP %d: %s.",
+ uscpi->bulk_in_ep, libusb_error_name(ret));
+ if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_out_ep)) < 0)
+ sr_err("Failed to clear halt/stall condition for EP %d: %s.",
+ uscpi->bulk_out_ep, libusb_error_name(ret));
+ if ((ret = libusb_clear_halt(usb->devhdl, uscpi->interrupt_ep)) < 0)
+ sr_err("Failed to clear halt/stall condition for EP %d: %s.",
+ uscpi->interrupt_ep, libusb_error_name(ret));
+ }
+
+ if ((ret = libusb_release_interface(usb->devhdl, uscpi->interface)) < 0)
+ sr_err("Failed to release interface: %s.",
+ libusb_error_name(ret));
+
+ if (uscpi->detached_kernel_driver) {
+ if ((ret = libusb_attach_kernel_driver(usb->devhdl,
+ uscpi->interface)) < 0)
+ sr_err("Failed to re-attach kernel driver: %s.",
+ libusb_error_name(ret));
+
+ uscpi->detached_kernel_driver = 0;
+ }
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+
+ return SR_OK;
+}
+
+static void scpi_usbtmc_libusb_free(void *priv)
+{
+ struct scpi_usbtmc_libusb *uscpi = priv;
+ sr_usb_dev_inst_free(uscpi->usb);
+}
+
+SR_PRIV const struct sr_scpi_dev_inst scpi_usbtmc_libusb_dev = {
+ .name = "USBTMC",
+ .prefix = "usbtmc",
+ .priv_size = sizeof(struct scpi_usbtmc_libusb),
+ .scan = scpi_usbtmc_libusb_scan,
+ .dev_inst_new = scpi_usbtmc_libusb_dev_inst_new,
+ .open = scpi_usbtmc_libusb_open,
+ .source_add = scpi_usbtmc_libusb_source_add,
+ .source_remove = scpi_usbtmc_libusb_source_remove,
+ .send = scpi_usbtmc_libusb_send,
+ .read_begin = scpi_usbtmc_libusb_read_begin,
+ .read_data = scpi_usbtmc_libusb_read_data,
+ .read_complete = scpi_usbtmc_libusb_read_complete,
+ .close = scpi_usbtmc_libusb_close,
+ .free = scpi_usbtmc_libusb_free,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
+ *
+ * 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.h"
+#include "libsigrok-internal.h"
+
+#include <visa.h>
+#include <string.h>
+
+#define LOG_PREFIX "scpi_visa"
+
+struct scpi_visa {
+ char *resource;
+ ViSession rmgr;
+ ViSession vi;
+};
+
+static int scpi_visa_dev_inst_new(void *priv, struct drv_context *drvc,
+ const char *resource, char **params, const char *serialcomm)
+{
+ struct scpi_visa *vscpi = priv;
+
+ (void)drvc;
+ (void)resource;
+ (void)serialcomm;
+
+ if (!params || !params[1]) {
+ sr_err("Invalid parameters.");
+ return SR_ERR_BUG;
+ }
+
+ vscpi->resource = g_strdup(params[1]);
+
+ return SR_OK;
+}
+
+static int scpi_visa_open(void *priv)
+{
+ struct scpi_visa *vscpi = priv;
+
+ if (viOpenDefaultRM(&vscpi->rmgr) != VI_SUCCESS) {
+ sr_err("Cannot open default resource manager.");
+ return SR_ERR;
+ }
+
+ if (viOpen(vscpi->rmgr, vscpi->resource, VI_NO_LOCK, 0, &vscpi->vi) != VI_SUCCESS) {
+ sr_err("Cannot open resource.");
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int scpi_visa_source_add(struct sr_session *session, void *priv,
+ int events, int timeout, sr_receive_data_callback cb, void *cb_data)
+{
+ (void) priv;
+
+ /* Hook up a dummy handler to receive data from the device. */
+ return sr_session_source_add(session, -1, events, timeout, cb, cb_data);
+}
+
+static int scpi_visa_source_remove(struct sr_session *session, void *priv)
+{
+ (void) priv;
+
+ return sr_session_source_remove(session, -1);
+}
+
+static int scpi_visa_send(void *priv, const char *command)
+{
+ struct scpi_visa *vscpi = priv;
+ gchar *terminated_command;
+ ViUInt32 written = 0;
+ int len;
+
+ terminated_command = g_strconcat(command, "\n", NULL);
+ len = strlen(terminated_command);
+ if (viWrite(vscpi->vi, (ViBuf) (terminated_command + written), len,
+ &written) != VI_SUCCESS) {
+ sr_err("Error while sending SCPI command: '%s'.", command);
+ g_free(terminated_command);
+ return SR_ERR;
+ }
+
+ g_free(terminated_command);
+
+ sr_spew("Successfully sent SCPI command: '%s'.", command);
+
+ return SR_OK;
+}
+
+static int scpi_visa_read_begin(void *priv)
+{
+ (void) priv;
+
+ return SR_OK;
+}
+
+static int scpi_visa_read_data(void *priv, char *buf, int maxlen)
+{
+ struct scpi_visa *vscpi = priv;
+ ViUInt32 count;
+
+ if (viRead(vscpi->vi, (ViBuf) buf, maxlen, &count) != VI_SUCCESS) {
+ sr_err("Read failed.");
+ return SR_ERR;
+ }
+
+ return count;
+}
+
+static int scpi_visa_read_complete(void *priv)
+{
+ struct scpi_visa *vscpi = priv;
+ ViUInt16 status;
+
+ if (viReadSTB(vscpi->vi, &status) != VI_SUCCESS) {
+ sr_err("Failed to read status.");
+ return SR_ERR;
+ }
+
+ return !(status & 16);
+}
+
+static int scpi_visa_close(void *priv)
+{
+ struct scpi_visa *vscpi = priv;
+
+ viClose(vscpi->vi);
+ viClose(vscpi->rmgr);
+
+ return SR_OK;
+}
+
+static void scpi_visa_free(void *priv)
+{
+ struct scpi_visa *vscpi = priv;
+
+ g_free(vscpi->resource);
+ g_free(vscpi);
+}
+
+SR_PRIV const struct sr_scpi_dev_inst scpi_visa_dev = {
+ .name = "VISA",
+ .prefix = "visa",
+ .priv_size = sizeof(struct scpi_visa),
+ .dev_inst_new = scpi_visa_dev_inst_new,
+ .open = scpi_visa_open,
+ .source_add = scpi_visa_source_add,
+ .source_remove = scpi_visa_source_remove,
+ .send = scpi_visa_send,
+ .read_begin = scpi_visa_read_begin,
+ .read_data = scpi_visa_read_data,
+ .read_complete = scpi_visa_read_complete,
+ .close = scpi_visa_close,
+ .free = scpi_visa_free,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * Inspired by the VXI11 Ethernet Protocol for Linux:
+ * http://optics.eee.nottingham.ac.uk/vxi11/
+ *
+ * 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 <rpc/rpc.h>
+#include <string.h>
+
+#include "vxi.h"
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "scpi_vxi"
+#define VXI_DEFAULT_TIMEOUT 2000 /* in ms */
+
+struct scpi_vxi {
+ char *address;
+ char *instrument;
+ CLIENT *client;
+ Device_Link link;
+ unsigned int max_send_size;
+ unsigned int read_complete;
+};
+
+static int scpi_vxi_dev_inst_new(void *priv, struct drv_context *drvc,
+ const char *resource, char **params, const char *serialcomm)
+{
+ struct scpi_vxi *vxi = priv;
+
+ (void)drvc;
+ (void)resource;
+ (void)serialcomm;
+
+ if (!params || !params[1]) {
+ sr_err("Invalid parameters.");
+ return SR_ERR;
+ }
+
+ vxi->address = g_strdup(params[1]);
+ vxi->instrument = g_strdup(params[2] ? params[2] : "inst0");
+
+ return SR_OK;
+}
+
+static int scpi_vxi_open(void *priv)
+{
+ struct scpi_vxi *vxi = priv;
+ Create_LinkParms link_parms;
+ Create_LinkResp *link_resp;
+
+ vxi->client = clnt_create(vxi->address, DEVICE_CORE, DEVICE_CORE_VERSION, "tcp");
+ if (vxi->client == NULL) {
+ sr_err("Client creation failed for %s", vxi->address);
+ return SR_ERR;
+ }
+
+ /* Set link parameters */
+ link_parms.clientId = (long) vxi->client;
+ link_parms.lockDevice = 0;
+ link_parms.lock_timeout = VXI_DEFAULT_TIMEOUT;
+ link_parms.device = "inst0";
+
+ if (!(link_resp = create_link_1(&link_parms, vxi->client))) {
+ sr_err("Link creation failed for %s", vxi->address);
+ return SR_ERR;
+ }
+ vxi->link = link_resp->lid;
+ vxi->max_send_size = link_resp->maxRecvSize;
+
+ /* Set a default maxRecvSize for devices which do not specify it */
+ if (vxi->max_send_size <= 0)
+ vxi->max_send_size = 4096;
+
+ return SR_OK;
+}
+
+static int scpi_vxi_source_add(struct sr_session *session, void *priv,
+ int events, int timeout, sr_receive_data_callback cb, void *cb_data)
+{
+ (void)priv;
+
+ /* Hook up a dummy handler to receive data from the device. */
+ return sr_session_source_add(session, -1, events, timeout, cb, cb_data);
+}
+
+static int scpi_vxi_source_remove(struct sr_session *session, void *priv)
+{
+ (void)priv;
+
+ return sr_session_source_remove(session, -1);
+}
+
+/* Operation Flags */
+#define DF_WAITLOCK 0x01 /* wait if the operation is locked by another link */
+#define DF_END 0x08 /* an END indicator is sent with last byte of buffer */
+#define DF_TERM 0x80 /* a termination char is set during a read */
+
+static int scpi_vxi_send(void *priv, const char *command)
+{
+ struct scpi_vxi *vxi = priv;
+ Device_WriteResp *write_resp;
+ Device_WriteParms write_parms;
+ char *terminated_command;
+ unsigned int len;
+
+ terminated_command = g_strdup_printf("%s\r\n", command);
+ len = strlen(terminated_command);
+
+ write_parms.lid = vxi->link;
+ write_parms.io_timeout = VXI_DEFAULT_TIMEOUT;
+ write_parms.lock_timeout = VXI_DEFAULT_TIMEOUT;
+ write_parms.flags = DF_END;
+ write_parms.data.data_len = MIN(len, vxi->max_send_size);
+ write_parms.data.data_val = terminated_command;
+
+ if (!(write_resp = device_write_1(&write_parms, vxi->client))
+ || write_resp->error) {
+ sr_err("Device write failed for %s with error %d",
+ vxi->address, write_resp->error);
+ return SR_ERR;
+ }
+
+ g_free(terminated_command);
+
+ if (write_resp->size < len)
+ sr_dbg("Only sent %d/%d bytes of SCPI command: '%s'.",
+ write_resp->size, len, command);
+ else
+ sr_spew("Successfully sent SCPI command: '%s'.", command);
+
+ return SR_OK;
+}
+
+static int scpi_vxi_read_begin(void *priv)
+{
+ struct scpi_vxi *vxi = priv;
+
+ vxi->read_complete = 0;
+
+ return SR_OK;
+}
+
+/* Read Response Reason Flags */
+#define RRR_SIZE 0x01 /* requestSize bytes have been transferred */
+#define RRR_TERM 0x02 /* a termination char has been read */
+#define RRR_END 0x04 /* an END indicator has been read */
+
+static int scpi_vxi_read_data(void *priv, char *buf, int maxlen)
+{
+ struct scpi_vxi *vxi = priv;
+ Device_ReadParms read_parms;
+ Device_ReadResp *read_resp;
+
+ read_parms.lid = vxi->link;
+ read_parms.io_timeout = VXI_DEFAULT_TIMEOUT;
+ read_parms.lock_timeout = VXI_DEFAULT_TIMEOUT;
+ read_parms.flags = 0;
+ read_parms.termChar = 0;
+ read_parms.requestSize = maxlen;
+
+ if (!(read_resp = device_read_1(&read_parms, vxi->client))
+ || read_resp->error) {
+ sr_err("Device read failed for %s with error %d",
+ vxi->address, read_resp->error);
+ return SR_ERR;
+ }
+
+ memcpy(buf, read_resp->data.data_val, read_resp->data.data_len);
+ vxi->read_complete = read_resp->reason & (RRR_SIZE | RRR_TERM | RRR_END);
+ return read_resp->data.data_len; /* actual number of bytes received */
+}
+
+static int scpi_vxi_read_complete(void *priv)
+{
+ struct scpi_vxi *vxi = priv;
+
+ return vxi->read_complete;
+}
+
+static int scpi_vxi_close(void *priv)
+{
+ struct scpi_vxi *vxi = priv;
+ Device_Error *dev_error;
+
+ if (!vxi->client)
+ return SR_ERR;
+
+ if (!(dev_error = destroy_link_1(&vxi->link, vxi->client))) {
+ sr_err("Link destruction failed for %s", vxi->address);
+ return SR_ERR;
+ }
+
+ clnt_destroy(vxi->client);
+ vxi->client = NULL;
+
+ return SR_OK;
+}
+
+static void scpi_vxi_free(void *priv)
+{
+ struct scpi_vxi *vxi = priv;
+
+ g_free(vxi->address);
+ g_free(vxi->instrument);
+}
+
+SR_PRIV const struct sr_scpi_dev_inst scpi_vxi_dev = {
+ .name = "VXI",
+ .prefix = "vxi",
+ .priv_size = sizeof(struct scpi_vxi),
+ .dev_inst_new = scpi_vxi_dev_inst_new,
+ .open = scpi_vxi_open,
+ .source_add = scpi_vxi_source_add,
+ .source_remove = scpi_vxi_source_remove,
+ .send = scpi_vxi_send,
+ .read_begin = scpi_vxi_read_begin,
+ .read_data = scpi_vxi_read_data,
+ .read_complete = scpi_vxi_read_complete,
+ .close = scpi_vxi_close,
+ .free = scpi_vxi_free,
+};
--- /dev/null
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _VXI_H_RPCGEN
+#define _VXI_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef long Device_Link;
+
+enum Device_AddrFamily {
+ DEVICE_TCP = 0,
+ DEVICE_UDP = 1,
+};
+typedef enum Device_AddrFamily Device_AddrFamily;
+
+typedef long Device_Flags;
+
+typedef long Device_ErrorCode;
+
+struct Device_Error {
+ Device_ErrorCode error;
+};
+typedef struct Device_Error Device_Error;
+
+struct Create_LinkParms {
+ long clientId;
+ bool_t lockDevice;
+ u_long lock_timeout;
+ char *device;
+};
+typedef struct Create_LinkParms Create_LinkParms;
+
+struct Create_LinkResp {
+ Device_ErrorCode error;
+ Device_Link lid;
+ u_short abortPort;
+ u_long maxRecvSize;
+};
+typedef struct Create_LinkResp Create_LinkResp;
+
+struct Device_WriteParms {
+ Device_Link lid;
+ u_long io_timeout;
+ u_long lock_timeout;
+ Device_Flags flags;
+ struct {
+ u_int data_len;
+ char *data_val;
+ } data;
+};
+typedef struct Device_WriteParms Device_WriteParms;
+
+struct Device_WriteResp {
+ Device_ErrorCode error;
+ u_long size;
+};
+typedef struct Device_WriteResp Device_WriteResp;
+
+struct Device_ReadParms {
+ Device_Link lid;
+ u_long requestSize;
+ u_long io_timeout;
+ u_long lock_timeout;
+ Device_Flags flags;
+ char termChar;
+};
+typedef struct Device_ReadParms Device_ReadParms;
+
+struct Device_ReadResp {
+ Device_ErrorCode error;
+ long reason;
+ struct {
+ u_int data_len;
+ char *data_val;
+ } data;
+};
+typedef struct Device_ReadResp Device_ReadResp;
+
+struct Device_ReadStbResp {
+ Device_ErrorCode error;
+ u_char stb;
+};
+typedef struct Device_ReadStbResp Device_ReadStbResp;
+
+struct Device_GenericParms {
+ Device_Link lid;
+ Device_Flags flags;
+ u_long lock_timeout;
+ u_long io_timeout;
+};
+typedef struct Device_GenericParms Device_GenericParms;
+
+struct Device_RemoteFunc {
+ u_long hostAddr;
+ u_short hostPort;
+ u_long progNum;
+ u_long progVers;
+ Device_AddrFamily progFamily;
+};
+typedef struct Device_RemoteFunc Device_RemoteFunc;
+
+struct Device_EnableSrqParms {
+ Device_Link lid;
+ bool_t enable;
+ struct {
+ u_int handle_len;
+ char *handle_val;
+ } handle;
+};
+typedef struct Device_EnableSrqParms Device_EnableSrqParms;
+
+struct Device_LockParms {
+ Device_Link lid;
+ Device_Flags flags;
+ u_long lock_timeout;
+};
+typedef struct Device_LockParms Device_LockParms;
+
+struct Device_DocmdParms {
+ Device_Link lid;
+ Device_Flags flags;
+ u_long io_timeout;
+ u_long lock_timeout;
+ long cmd;
+ bool_t network_order;
+ long datasize;
+ struct {
+ u_int data_in_len;
+ char *data_in_val;
+ } data_in;
+};
+typedef struct Device_DocmdParms Device_DocmdParms;
+
+struct Device_DocmdResp {
+ Device_ErrorCode error;
+ struct {
+ u_int data_out_len;
+ char *data_out_val;
+ } data_out;
+};
+typedef struct Device_DocmdResp Device_DocmdResp;
+
+struct Device_SrqParms {
+ struct {
+ u_int handle_len;
+ char *handle_val;
+ } handle;
+};
+typedef struct Device_SrqParms Device_SrqParms;
+
+#define DEVICE_ASYNC 0x0607B0
+#define DEVICE_ASYNC_VERSION 1
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define device_abort 1
+extern Device_Error * device_abort_1(Device_Link *, CLIENT *);
+extern Device_Error * device_abort_1_svc(Device_Link *, struct svc_req *);
+extern int device_async_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#else /* K&R C */
+#define device_abort 1
+extern Device_Error * device_abort_1();
+extern Device_Error * device_abort_1_svc();
+extern int device_async_1_freeresult ();
+#endif /* K&R C */
+
+#define DEVICE_CORE 0x0607AF
+#define DEVICE_CORE_VERSION 1
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define create_link 10
+extern Create_LinkResp * create_link_1(Create_LinkParms *, CLIENT *);
+extern Create_LinkResp * create_link_1_svc(Create_LinkParms *, struct svc_req *);
+#define device_write 11
+extern Device_WriteResp * device_write_1(Device_WriteParms *, CLIENT *);
+extern Device_WriteResp * device_write_1_svc(Device_WriteParms *, struct svc_req *);
+#define device_read 12
+extern Device_ReadResp * device_read_1(Device_ReadParms *, CLIENT *);
+extern Device_ReadResp * device_read_1_svc(Device_ReadParms *, struct svc_req *);
+#define device_readstb 13
+extern Device_ReadStbResp * device_readstb_1(Device_GenericParms *, CLIENT *);
+extern Device_ReadStbResp * device_readstb_1_svc(Device_GenericParms *, struct svc_req *);
+#define device_trigger 14
+extern Device_Error * device_trigger_1(Device_GenericParms *, CLIENT *);
+extern Device_Error * device_trigger_1_svc(Device_GenericParms *, struct svc_req *);
+#define device_clear 15
+extern Device_Error * device_clear_1(Device_GenericParms *, CLIENT *);
+extern Device_Error * device_clear_1_svc(Device_GenericParms *, struct svc_req *);
+#define device_remote 16
+extern Device_Error * device_remote_1(Device_GenericParms *, CLIENT *);
+extern Device_Error * device_remote_1_svc(Device_GenericParms *, struct svc_req *);
+#define device_local 17
+extern Device_Error * device_local_1(Device_GenericParms *, CLIENT *);
+extern Device_Error * device_local_1_svc(Device_GenericParms *, struct svc_req *);
+#define device_lock 18
+extern Device_Error * device_lock_1(Device_LockParms *, CLIENT *);
+extern Device_Error * device_lock_1_svc(Device_LockParms *, struct svc_req *);
+#define device_unlock 19
+extern Device_Error * device_unlock_1(Device_Link *, CLIENT *);
+extern Device_Error * device_unlock_1_svc(Device_Link *, struct svc_req *);
+#define device_enable_srq 20
+extern Device_Error * device_enable_srq_1(Device_EnableSrqParms *, CLIENT *);
+extern Device_Error * device_enable_srq_1_svc(Device_EnableSrqParms *, struct svc_req *);
+#define device_docmd 22
+extern Device_DocmdResp * device_docmd_1(Device_DocmdParms *, CLIENT *);
+extern Device_DocmdResp * device_docmd_1_svc(Device_DocmdParms *, struct svc_req *);
+#define destroy_link 23
+extern Device_Error * destroy_link_1(Device_Link *, CLIENT *);
+extern Device_Error * destroy_link_1_svc(Device_Link *, struct svc_req *);
+#define create_intr_chan 25
+extern Device_Error * create_intr_chan_1(Device_RemoteFunc *, CLIENT *);
+extern Device_Error * create_intr_chan_1_svc(Device_RemoteFunc *, struct svc_req *);
+#define destroy_intr_chan 26
+extern Device_Error * destroy_intr_chan_1(void *, CLIENT *);
+extern Device_Error * destroy_intr_chan_1_svc(void *, struct svc_req *);
+extern int device_core_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#else /* K&R C */
+#define create_link 10
+extern Create_LinkResp * create_link_1();
+extern Create_LinkResp * create_link_1_svc();
+#define device_write 11
+extern Device_WriteResp * device_write_1();
+extern Device_WriteResp * device_write_1_svc();
+#define device_read 12
+extern Device_ReadResp * device_read_1();
+extern Device_ReadResp * device_read_1_svc();
+#define device_readstb 13
+extern Device_ReadStbResp * device_readstb_1();
+extern Device_ReadStbResp * device_readstb_1_svc();
+#define device_trigger 14
+extern Device_Error * device_trigger_1();
+extern Device_Error * device_trigger_1_svc();
+#define device_clear 15
+extern Device_Error * device_clear_1();
+extern Device_Error * device_clear_1_svc();
+#define device_remote 16
+extern Device_Error * device_remote_1();
+extern Device_Error * device_remote_1_svc();
+#define device_local 17
+extern Device_Error * device_local_1();
+extern Device_Error * device_local_1_svc();
+#define device_lock 18
+extern Device_Error * device_lock_1();
+extern Device_Error * device_lock_1_svc();
+#define device_unlock 19
+extern Device_Error * device_unlock_1();
+extern Device_Error * device_unlock_1_svc();
+#define device_enable_srq 20
+extern Device_Error * device_enable_srq_1();
+extern Device_Error * device_enable_srq_1_svc();
+#define device_docmd 22
+extern Device_DocmdResp * device_docmd_1();
+extern Device_DocmdResp * device_docmd_1_svc();
+#define destroy_link 23
+extern Device_Error * destroy_link_1();
+extern Device_Error * destroy_link_1_svc();
+#define create_intr_chan 25
+extern Device_Error * create_intr_chan_1();
+extern Device_Error * create_intr_chan_1_svc();
+#define destroy_intr_chan 26
+extern Device_Error * destroy_intr_chan_1();
+extern Device_Error * destroy_intr_chan_1_svc();
+extern int device_core_1_freeresult ();
+#endif /* K&R C */
+
+#define DEVICE_INTR 0x0607B1
+#define DEVICE_INTR_VERSION 1
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define device_intr_srq 30
+extern void * device_intr_srq_1(Device_SrqParms *, CLIENT *);
+extern void * device_intr_srq_1_svc(Device_SrqParms *, struct svc_req *);
+extern int device_intr_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#else /* K&R C */
+#define device_intr_srq 30
+extern void * device_intr_srq_1();
+extern void * device_intr_srq_1_svc();
+extern int device_intr_1_freeresult ();
+#endif /* K&R C */
+
+/* the xdr functions */
+
+#if defined(__STDC__) || defined(__cplusplus)
+extern bool_t xdr_Device_Link (XDR *, Device_Link*);
+extern bool_t xdr_Device_AddrFamily (XDR *, Device_AddrFamily*);
+extern bool_t xdr_Device_Flags (XDR *, Device_Flags*);
+extern bool_t xdr_Device_ErrorCode (XDR *, Device_ErrorCode*);
+extern bool_t xdr_Device_Error (XDR *, Device_Error*);
+extern bool_t xdr_Create_LinkParms (XDR *, Create_LinkParms*);
+extern bool_t xdr_Create_LinkResp (XDR *, Create_LinkResp*);
+extern bool_t xdr_Device_WriteParms (XDR *, Device_WriteParms*);
+extern bool_t xdr_Device_WriteResp (XDR *, Device_WriteResp*);
+extern bool_t xdr_Device_ReadParms (XDR *, Device_ReadParms*);
+extern bool_t xdr_Device_ReadResp (XDR *, Device_ReadResp*);
+extern bool_t xdr_Device_ReadStbResp (XDR *, Device_ReadStbResp*);
+extern bool_t xdr_Device_GenericParms (XDR *, Device_GenericParms*);
+extern bool_t xdr_Device_RemoteFunc (XDR *, Device_RemoteFunc*);
+extern bool_t xdr_Device_EnableSrqParms (XDR *, Device_EnableSrqParms*);
+extern bool_t xdr_Device_LockParms (XDR *, Device_LockParms*);
+extern bool_t xdr_Device_DocmdParms (XDR *, Device_DocmdParms*);
+extern bool_t xdr_Device_DocmdResp (XDR *, Device_DocmdResp*);
+extern bool_t xdr_Device_SrqParms (XDR *, Device_SrqParms*);
+
+#else /* K&R C */
+extern bool_t xdr_Device_Link ();
+extern bool_t xdr_Device_AddrFamily ();
+extern bool_t xdr_Device_Flags ();
+extern bool_t xdr_Device_ErrorCode ();
+extern bool_t xdr_Device_Error ();
+extern bool_t xdr_Create_LinkParms ();
+extern bool_t xdr_Create_LinkResp ();
+extern bool_t xdr_Device_WriteParms ();
+extern bool_t xdr_Device_WriteResp ();
+extern bool_t xdr_Device_ReadParms ();
+extern bool_t xdr_Device_ReadResp ();
+extern bool_t xdr_Device_ReadStbResp ();
+extern bool_t xdr_Device_GenericParms ();
+extern bool_t xdr_Device_RemoteFunc ();
+extern bool_t xdr_Device_EnableSrqParms ();
+extern bool_t xdr_Device_LockParms ();
+extern bool_t xdr_Device_DocmdParms ();
+extern bool_t xdr_Device_DocmdResp ();
+extern bool_t xdr_Device_SrqParms ();
+
+#endif /* K&R C */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_VXI_H_RPCGEN */
--- /dev/null
+/* This is taken straight from Appendix C of the specification */
+
+typedef long Device_Link;
+
+enum Device_AddrFamily {
+ DEVICE_TCP,
+ DEVICE_UDP
+};
+
+typedef long Device_Flags;
+
+typedef long Device_ErrorCode;
+
+struct Device_Error {
+ Device_ErrorCode error;
+};
+
+struct Create_LinkParms {
+ long clientId; /* implementation specific value */
+ bool lockDevice; /* attempt to lock the device */
+ unsigned long lock_timeout; /* time to wait on a lock */
+ string device<>; /* name of device */
+};
+
+struct Create_LinkResp {
+ Device_ErrorCode error;
+ Device_Link lid;
+ unsigned short abortPort; /* for the abort RPC */
+ unsigned long maxRecvSize; /* specifies max data size in bytes
+ device will accept on a write */
+};
+
+struct Device_WriteParms {
+ Device_Link lid; /* link id from create_link */
+ unsigned long io_timeout; /* time to wait for I/O */
+ unsigned long lock_timeout; /* time to wait for lock */
+ Device_Flags flags;
+ opaque data<>; /* data length and the data itself */
+};
+
+struct Device_WriteResp {
+ Device_ErrorCode error;
+ unsigned long size; /* number of bytes written */
+};
+
+struct Device_ReadParms {
+ Device_Link lid; /* link id from create_link */
+ unsigned long requestSize; /* bytes requested */
+ unsigned long io_timeout; /* time to wait for I/O */
+ unsigned long lock_timeout; /* time to wait for lock */
+ Device_Flags flags;
+ char termChar; /* valid if flags & termchrset */
+};
+
+struct Device_ReadResp {
+ Device_ErrorCode error;
+ long reason; /* reason(s) read completed */
+ opaque data<>; /* data.len and data.val */
+};
+
+struct Device_ReadStbResp {
+ Device_ErrorCode error; /* error code */
+ unsigned char stb; /* the returned status byte */
+};
+
+struct Device_GenericParms {
+ Device_Link lid; /* Device_Link id from connect call */
+ Device_Flags flags; /* flags with options */
+ unsigned long lock_timeout; /* time to wait for lock */
+ unsigned long io_timeout; /* time to wait for I/O */
+};
+
+struct Device_RemoteFunc {
+ unsigned long hostAddr; /* host servicing interrupt */
+ unsigned short hostPort; /* valid port # on client */
+ unsigned long progNum; /* DEVICE_INTR */
+ unsigned long progVers; /* DEVICE_INTR_VERSION */
+ Device_AddrFamily progFamily; /* DEVICE_UDP | DEVICE_TCP */
+};
+
+struct Device_EnableSrqParms {
+ Device_Link lid;
+ bool enable; /* enable or disable interrupts */
+ opaque handle<40>; /* host specific data */
+};
+
+struct Device_LockParms {
+ Device_Link lid; /* link id from create_link */
+ Device_Flags flags; /* contains the waitlock flag */
+ unsigned long lock_timeout; /* time to wait to acquire lock */
+};
+
+struct Device_DocmdParms {
+ Device_Link lid; /* link id from create_link */
+ Device_Flags flags; /* flags specifying various options */
+ unsigned long io_timeout; /* time to wait for I/O to complete */
+ unsigned long lock_timeout; /* time to wait on a lock */
+ long cmd; /* which command to execute */
+ bool network_order; /* client's byte order */
+ long datasize; /* size of individual data elements */
+ opaque data_in<>; /* docmd data parameters */
+};
+
+struct Device_DocmdResp {
+ Device_ErrorCode error; /* returned status */
+ opaque data_out<>; /* returned data parameter */
+};
+
+struct Device_SrqParms {
+ opaque handle<>;
+};
+
+program DEVICE_ASYNC{
+ version DEVICE_ASYNC_VERSION {
+ Device_Error device_abort(Device_Link) = 1;
+ } = 1;
+} = 0x0607B0;
+
+program DEVICE_CORE {
+ version DEVICE_CORE_VERSION {
+ Create_LinkResp create_link(Create_LinkParms) = 10;
+ Device_WriteResp device_write(Device_WriteParms) = 11;
+ Device_ReadResp device_read(Device_ReadParms) = 12;
+ Device_ReadStbResp device_readstb(Device_GenericParms) = 13;
+ Device_Error device_trigger(Device_GenericParms) = 14;
+ Device_Error device_clear(Device_GenericParms) = 15;
+ Device_Error device_remote(Device_GenericParms) = 16;
+ Device_Error device_local(Device_GenericParms) = 17;
+ Device_Error device_lock(Device_LockParms) = 18;
+ Device_Error device_unlock(Device_Link) = 19;
+ Device_Error device_enable_srq(Device_EnableSrqParms)= 20;
+ Device_DocmdResp device_docmd(Device_DocmdParms) = 22;
+ Device_Error destroy_link(Device_Link) = 23;
+ Device_Error create_intr_chan(Device_RemoteFunc) = 25;
+ Device_Error destroy_intr_chan(void) = 26;
+ } = 1;
+} = 0x0607AF;
+
+program DEVICE_INTR {
+ version DEVICE_INTR_VERSION {
+ void device_intr_srq(Device_SrqParms) = 30;
+ } = 1;
+} = 0x0607B1;
--- /dev/null
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include <memory.h> /* for memset */
+#include "vxi.h"
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 25, 0 };
+
+Device_Error *
+device_abort_1(Device_Link *argp, CLIENT *clnt)
+{
+ static Device_Error clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_abort,
+ (xdrproc_t) xdr_Device_Link, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Create_LinkResp *
+create_link_1(Create_LinkParms *argp, CLIENT *clnt)
+{
+ static Create_LinkResp clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, create_link,
+ (xdrproc_t) xdr_Create_LinkParms, (caddr_t) argp,
+ (xdrproc_t) xdr_Create_LinkResp, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_WriteResp *
+device_write_1(Device_WriteParms *argp, CLIENT *clnt)
+{
+ static Device_WriteResp clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_write,
+ (xdrproc_t) xdr_Device_WriteParms, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_WriteResp, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_ReadResp *
+device_read_1(Device_ReadParms *argp, CLIENT *clnt)
+{
+ static Device_ReadResp clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_read,
+ (xdrproc_t) xdr_Device_ReadParms, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_ReadResp, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_ReadStbResp *
+device_readstb_1(Device_GenericParms *argp, CLIENT *clnt)
+{
+ static Device_ReadStbResp clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_readstb,
+ (xdrproc_t) xdr_Device_GenericParms, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_ReadStbResp, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_Error *
+device_trigger_1(Device_GenericParms *argp, CLIENT *clnt)
+{
+ static Device_Error clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_trigger,
+ (xdrproc_t) xdr_Device_GenericParms, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_Error *
+device_clear_1(Device_GenericParms *argp, CLIENT *clnt)
+{
+ static Device_Error clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_clear,
+ (xdrproc_t) xdr_Device_GenericParms, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_Error *
+device_remote_1(Device_GenericParms *argp, CLIENT *clnt)
+{
+ static Device_Error clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_remote,
+ (xdrproc_t) xdr_Device_GenericParms, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_Error *
+device_local_1(Device_GenericParms *argp, CLIENT *clnt)
+{
+ static Device_Error clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_local,
+ (xdrproc_t) xdr_Device_GenericParms, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_Error *
+device_lock_1(Device_LockParms *argp, CLIENT *clnt)
+{
+ static Device_Error clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_lock,
+ (xdrproc_t) xdr_Device_LockParms, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_Error *
+device_unlock_1(Device_Link *argp, CLIENT *clnt)
+{
+ static Device_Error clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_unlock,
+ (xdrproc_t) xdr_Device_Link, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_Error *
+device_enable_srq_1(Device_EnableSrqParms *argp, CLIENT *clnt)
+{
+ static Device_Error clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_enable_srq,
+ (xdrproc_t) xdr_Device_EnableSrqParms, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_DocmdResp *
+device_docmd_1(Device_DocmdParms *argp, CLIENT *clnt)
+{
+ static Device_DocmdResp clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_docmd,
+ (xdrproc_t) xdr_Device_DocmdParms, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_DocmdResp, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_Error *
+destroy_link_1(Device_Link *argp, CLIENT *clnt)
+{
+ static Device_Error clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, destroy_link,
+ (xdrproc_t) xdr_Device_Link, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_Error *
+create_intr_chan_1(Device_RemoteFunc *argp, CLIENT *clnt)
+{
+ static Device_Error clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, create_intr_chan,
+ (xdrproc_t) xdr_Device_RemoteFunc, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+Device_Error *
+destroy_intr_chan_1(void *argp, CLIENT *clnt)
+{
+ static Device_Error clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, destroy_intr_chan,
+ (xdrproc_t) xdr_void, (caddr_t) argp,
+ (xdrproc_t) xdr_Device_Error, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+void *
+device_intr_srq_1(Device_SrqParms *argp, CLIENT *clnt)
+{
+ static char clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, device_intr_srq,
+ (xdrproc_t) xdr_Device_SrqParms, (caddr_t) argp,
+ (xdrproc_t) xdr_void, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&clnt_res);
+}
--- /dev/null
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "vxi.h"
+
+bool_t
+xdr_Device_Link (XDR *xdrs, Device_Link *objp)
+{
+ if (!xdr_long (xdrs, objp))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_AddrFamily (XDR *xdrs, Device_AddrFamily *objp)
+{
+ if (!xdr_enum (xdrs, (enum_t *) objp))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_Flags (XDR *xdrs, Device_Flags *objp)
+{
+ if (!xdr_long (xdrs, objp))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_ErrorCode (XDR *xdrs, Device_ErrorCode *objp)
+{
+ if (!xdr_long (xdrs, objp))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_Error (XDR *xdrs, Device_Error *objp)
+{
+ if (!xdr_Device_ErrorCode (xdrs, &objp->error))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Create_LinkParms (XDR *xdrs, Create_LinkParms *objp)
+{
+ register int32_t *buf;
+
+ if (xdrs->x_op == XDR_ENCODE) {
+ buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
+ if (buf == NULL) {
+ if (!xdr_long (xdrs, &objp->clientId))
+ return FALSE;
+ if (!xdr_bool (xdrs, &objp->lockDevice))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+
+ } else {
+ IXDR_PUT_LONG(buf, objp->clientId);
+ IXDR_PUT_BOOL(buf, objp->lockDevice);
+ IXDR_PUT_U_LONG(buf, objp->lock_timeout);
+ }
+ if (!xdr_string (xdrs, &objp->device, ~0))
+ return FALSE;
+ return TRUE;
+ } else if (xdrs->x_op == XDR_DECODE) {
+ buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
+ if (buf == NULL) {
+ if (!xdr_long (xdrs, &objp->clientId))
+ return FALSE;
+ if (!xdr_bool (xdrs, &objp->lockDevice))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+
+ } else {
+ objp->clientId = IXDR_GET_LONG(buf);
+ objp->lockDevice = IXDR_GET_BOOL(buf);
+ objp->lock_timeout = IXDR_GET_U_LONG(buf);
+ }
+ if (!xdr_string (xdrs, &objp->device, ~0))
+ return FALSE;
+ return TRUE;
+ }
+
+ if (!xdr_long (xdrs, &objp->clientId))
+ return FALSE;
+ if (!xdr_bool (xdrs, &objp->lockDevice))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+ if (!xdr_string (xdrs, &objp->device, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Create_LinkResp (XDR *xdrs, Create_LinkResp *objp)
+{
+ if (!xdr_Device_ErrorCode (xdrs, &objp->error))
+ return FALSE;
+ if (!xdr_Device_Link (xdrs, &objp->lid))
+ return FALSE;
+ if (!xdr_u_short (xdrs, &objp->abortPort))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->maxRecvSize))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_WriteParms (XDR *xdrs, Device_WriteParms *objp)
+{
+ if (!xdr_Device_Link (xdrs, &objp->lid))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->io_timeout))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+ if (!xdr_Device_Flags (xdrs, &objp->flags))
+ return FALSE;
+ if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_WriteResp (XDR *xdrs, Device_WriteResp *objp)
+{
+ if (!xdr_Device_ErrorCode (xdrs, &objp->error))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->size))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_ReadParms (XDR *xdrs, Device_ReadParms *objp)
+{
+ register int32_t *buf;
+
+ if (xdrs->x_op == XDR_ENCODE) {
+ if (!xdr_Device_Link (xdrs, &objp->lid))
+ return FALSE;
+ buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
+ if (buf == NULL) {
+ if (!xdr_u_long (xdrs, &objp->requestSize))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->io_timeout))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+
+ } else {
+ IXDR_PUT_U_LONG(buf, objp->requestSize);
+ IXDR_PUT_U_LONG(buf, objp->io_timeout);
+ IXDR_PUT_U_LONG(buf, objp->lock_timeout);
+ }
+ if (!xdr_Device_Flags (xdrs, &objp->flags))
+ return FALSE;
+ if (!xdr_char (xdrs, &objp->termChar))
+ return FALSE;
+ return TRUE;
+ } else if (xdrs->x_op == XDR_DECODE) {
+ if (!xdr_Device_Link (xdrs, &objp->lid))
+ return FALSE;
+ buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
+ if (buf == NULL) {
+ if (!xdr_u_long (xdrs, &objp->requestSize))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->io_timeout))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+
+ } else {
+ objp->requestSize = IXDR_GET_U_LONG(buf);
+ objp->io_timeout = IXDR_GET_U_LONG(buf);
+ objp->lock_timeout = IXDR_GET_U_LONG(buf);
+ }
+ if (!xdr_Device_Flags (xdrs, &objp->flags))
+ return FALSE;
+ if (!xdr_char (xdrs, &objp->termChar))
+ return FALSE;
+ return TRUE;
+ }
+
+ if (!xdr_Device_Link (xdrs, &objp->lid))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->requestSize))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->io_timeout))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+ if (!xdr_Device_Flags (xdrs, &objp->flags))
+ return FALSE;
+ if (!xdr_char (xdrs, &objp->termChar))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_ReadResp (XDR *xdrs, Device_ReadResp *objp)
+{
+ if (!xdr_Device_ErrorCode (xdrs, &objp->error))
+ return FALSE;
+ if (!xdr_long (xdrs, &objp->reason))
+ return FALSE;
+ if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_ReadStbResp (XDR *xdrs, Device_ReadStbResp *objp)
+{
+ if (!xdr_Device_ErrorCode (xdrs, &objp->error))
+ return FALSE;
+ if (!xdr_u_char (xdrs, &objp->stb))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_GenericParms (XDR *xdrs, Device_GenericParms *objp)
+{
+ if (!xdr_Device_Link (xdrs, &objp->lid))
+ return FALSE;
+ if (!xdr_Device_Flags (xdrs, &objp->flags))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->io_timeout))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_RemoteFunc (XDR *xdrs, Device_RemoteFunc *objp)
+{
+ register int32_t *buf;
+
+ if (xdrs->x_op == XDR_ENCODE) {
+ buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
+ if (buf == NULL) {
+ if (!xdr_u_long (xdrs, &objp->hostAddr))
+ return FALSE;
+ if (!xdr_u_short (xdrs, &objp->hostPort))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->progNum))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->progVers))
+ return FALSE;
+
+ } else {
+ IXDR_PUT_U_LONG(buf, objp->hostAddr);
+ IXDR_PUT_U_SHORT(buf, objp->hostPort);
+ IXDR_PUT_U_LONG(buf, objp->progNum);
+ IXDR_PUT_U_LONG(buf, objp->progVers);
+ }
+ if (!xdr_Device_AddrFamily (xdrs, &objp->progFamily))
+ return FALSE;
+ return TRUE;
+ } else if (xdrs->x_op == XDR_DECODE) {
+ buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
+ if (buf == NULL) {
+ if (!xdr_u_long (xdrs, &objp->hostAddr))
+ return FALSE;
+ if (!xdr_u_short (xdrs, &objp->hostPort))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->progNum))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->progVers))
+ return FALSE;
+
+ } else {
+ objp->hostAddr = IXDR_GET_U_LONG(buf);
+ objp->hostPort = IXDR_GET_U_SHORT(buf);
+ objp->progNum = IXDR_GET_U_LONG(buf);
+ objp->progVers = IXDR_GET_U_LONG(buf);
+ }
+ if (!xdr_Device_AddrFamily (xdrs, &objp->progFamily))
+ return FALSE;
+ return TRUE;
+ }
+
+ if (!xdr_u_long (xdrs, &objp->hostAddr))
+ return FALSE;
+ if (!xdr_u_short (xdrs, &objp->hostPort))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->progNum))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->progVers))
+ return FALSE;
+ if (!xdr_Device_AddrFamily (xdrs, &objp->progFamily))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_EnableSrqParms (XDR *xdrs, Device_EnableSrqParms *objp)
+{
+ if (!xdr_Device_Link (xdrs, &objp->lid))
+ return FALSE;
+ if (!xdr_bool (xdrs, &objp->enable))
+ return FALSE;
+ if (!xdr_bytes (xdrs, (char **)&objp->handle.handle_val, (u_int *) &objp->handle.handle_len, 40))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_LockParms (XDR *xdrs, Device_LockParms *objp)
+{
+ if (!xdr_Device_Link (xdrs, &objp->lid))
+ return FALSE;
+ if (!xdr_Device_Flags (xdrs, &objp->flags))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_DocmdParms (XDR *xdrs, Device_DocmdParms *objp)
+{
+ register int32_t *buf;
+
+ if (xdrs->x_op == XDR_ENCODE) {
+ if (!xdr_Device_Link (xdrs, &objp->lid))
+ return FALSE;
+ if (!xdr_Device_Flags (xdrs, &objp->flags))
+ return FALSE;
+ buf = XDR_INLINE (xdrs, 5 * BYTES_PER_XDR_UNIT);
+ if (buf == NULL) {
+ if (!xdr_u_long (xdrs, &objp->io_timeout))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+ if (!xdr_long (xdrs, &objp->cmd))
+ return FALSE;
+ if (!xdr_bool (xdrs, &objp->network_order))
+ return FALSE;
+ if (!xdr_long (xdrs, &objp->datasize))
+ return FALSE;
+
+ } else {
+ IXDR_PUT_U_LONG(buf, objp->io_timeout);
+ IXDR_PUT_U_LONG(buf, objp->lock_timeout);
+ IXDR_PUT_LONG(buf, objp->cmd);
+ IXDR_PUT_BOOL(buf, objp->network_order);
+ IXDR_PUT_LONG(buf, objp->datasize);
+ }
+ if (!xdr_bytes (xdrs, (char **)&objp->data_in.data_in_val, (u_int *) &objp->data_in.data_in_len, ~0))
+ return FALSE;
+ return TRUE;
+ } else if (xdrs->x_op == XDR_DECODE) {
+ if (!xdr_Device_Link (xdrs, &objp->lid))
+ return FALSE;
+ if (!xdr_Device_Flags (xdrs, &objp->flags))
+ return FALSE;
+ buf = XDR_INLINE (xdrs, 5 * BYTES_PER_XDR_UNIT);
+ if (buf == NULL) {
+ if (!xdr_u_long (xdrs, &objp->io_timeout))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+ if (!xdr_long (xdrs, &objp->cmd))
+ return FALSE;
+ if (!xdr_bool (xdrs, &objp->network_order))
+ return FALSE;
+ if (!xdr_long (xdrs, &objp->datasize))
+ return FALSE;
+
+ } else {
+ objp->io_timeout = IXDR_GET_U_LONG(buf);
+ objp->lock_timeout = IXDR_GET_U_LONG(buf);
+ objp->cmd = IXDR_GET_LONG(buf);
+ objp->network_order = IXDR_GET_BOOL(buf);
+ objp->datasize = IXDR_GET_LONG(buf);
+ }
+ if (!xdr_bytes (xdrs, (char **)&objp->data_in.data_in_val, (u_int *) &objp->data_in.data_in_len, ~0))
+ return FALSE;
+ return TRUE;
+ }
+
+ if (!xdr_Device_Link (xdrs, &objp->lid))
+ return FALSE;
+ if (!xdr_Device_Flags (xdrs, &objp->flags))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->io_timeout))
+ return FALSE;
+ if (!xdr_u_long (xdrs, &objp->lock_timeout))
+ return FALSE;
+ if (!xdr_long (xdrs, &objp->cmd))
+ return FALSE;
+ if (!xdr_bool (xdrs, &objp->network_order))
+ return FALSE;
+ if (!xdr_long (xdrs, &objp->datasize))
+ return FALSE;
+ if (!xdr_bytes (xdrs, (char **)&objp->data_in.data_in_val, (u_int *) &objp->data_in.data_in_len, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_DocmdResp (XDR *xdrs, Device_DocmdResp *objp)
+{
+ if (!xdr_Device_ErrorCode (xdrs, &objp->error))
+ return FALSE;
+ if (!xdr_bytes (xdrs, (char **)&objp->data_out.data_out_val, (u_int *) &objp->data_out.data_out_len, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_Device_SrqParms (XDR *xdrs, Device_SrqParms *objp)
+{
+ if (!xdr_bytes (xdrs, (char **)&objp->handle.handle_val, (u_int *) &objp->handle.handle_len, ~0))
+ return FALSE;
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2010-2012 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <libserialport.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "serial"
+
+/**
+ * Open the specified serial port.
+ *
+ * @param serial Previously initialized serial port structure.
+ * @param[in] flags Flags to use when opening the serial port. Possible flags
+ * include SERIAL_RDWR, SERIAL_RDONLY, SERIAL_NONBLOCK.
+ *
+ * If the serial structure contains a serialcomm string, it will be
+ * passed to serial_set_paramstr() after the port is opened.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Failure.
+ */
+SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags)
+{
+ int ret;
+ char *error;
+ int sp_flags = 0;
+
+ if (!serial) {
+ sr_dbg("Invalid serial port.");
+ return SR_ERR;
+ }
+
+ sr_spew("Opening serial port '%s' (flags %d).", serial->port, flags);
+
+ sp_get_port_by_name(serial->port, &serial->data);
+
+ if (flags & SERIAL_RDWR)
+ sp_flags = (SP_MODE_READ | SP_MODE_WRITE);
+ else if (flags & SERIAL_RDONLY)
+ sp_flags = SP_MODE_READ;
+
+ serial->nonblocking = (flags & SERIAL_NONBLOCK) ? 1 : 0;
+
+ ret = sp_open(serial->data, sp_flags);
+
+ switch (ret) {
+ case SP_ERR_ARG:
+ sr_err("Attempt to open serial port with invalid parameters.");
+ return SR_ERR_ARG;
+ case SP_ERR_FAIL:
+ error = sp_last_error_message();
+ sr_err("Error opening port (%d): %s.",
+ sp_last_error_code(), error);
+ sp_free_error_message(error);
+ return SR_ERR;
+ }
+
+ if (serial->serialcomm)
+ return serial_set_paramstr(serial, serial->serialcomm);
+ else
+ return SR_OK;
+}
+
+/**
+ * Close the specified serial port.
+ *
+ * @param serial Previously initialized serial port structure.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Failure.
+ */
+SR_PRIV int serial_close(struct sr_serial_dev_inst *serial)
+{
+ int ret;
+ char *error;
+
+ if (!serial) {
+ sr_dbg("Invalid serial port.");
+ return SR_ERR;
+ }
+
+ if (!serial->data) {
+ sr_dbg("Cannot close unopened serial port %s.", serial->port);
+ return SR_ERR;
+ }
+
+ sr_spew("Closing serial port %s.", serial->port);
+
+ ret = sp_close(serial->data);
+
+ switch (ret) {
+ case SP_ERR_ARG:
+ sr_err("Attempt to close an invalid serial port.");
+ return SR_ERR_ARG;
+ case SP_ERR_FAIL:
+ error = sp_last_error_message();
+ sr_err("Error closing port (%d): %s.",
+ sp_last_error_code(), error);
+ sp_free_error_message(error);
+ return SR_ERR;
+ }
+
+ sp_free_port(serial->data);
+ serial->data = NULL;
+
+ return SR_OK;
+}
+
+/**
+ * Flush serial port buffers.
+ *
+ * @param serial Previously initialized serial port structure.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Failure.
+ */
+SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial)
+{
+ int ret;
+ char *error;
+
+ if (!serial) {
+ sr_dbg("Invalid serial port.");
+ return SR_ERR;
+ }
+
+ if (!serial->data) {
+ sr_dbg("Cannot flush unopened serial port %s.", serial->port);
+ return SR_ERR;
+ }
+
+ sr_spew("Flushing serial port %s.", serial->port);
+
+ ret = sp_flush(serial->data, SP_BUF_BOTH);
+
+ switch (ret) {
+ case SP_ERR_ARG:
+ sr_err("Attempt to flush an invalid serial port.");
+ return SR_ERR_ARG;
+ case SP_ERR_FAIL:
+ error = sp_last_error_message();
+ sr_err("Error flushing port (%d): %s.",
+ sp_last_error_code(), error);
+ sp_free_error_message(error);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int _serial_write(struct sr_serial_dev_inst *serial,
+ const void *buf, size_t count, int nonblocking)
+{
+ ssize_t ret;
+ char *error;
+
+ if (!serial) {
+ sr_dbg("Invalid serial port.");
+ return SR_ERR;
+ }
+
+ if (!serial->data) {
+ sr_dbg("Cannot use unopened serial port %s.", serial->port);
+ return SR_ERR;
+ }
+
+ if (nonblocking)
+ ret = sp_nonblocking_write(serial->data, buf, count);
+ else
+ ret = sp_blocking_write(serial->data, buf, count, 0);
+
+ switch (ret) {
+ case SP_ERR_ARG:
+ sr_err("Attempted serial port write with invalid arguments.");
+ return SR_ERR_ARG;
+ case SP_ERR_FAIL:
+ error = sp_last_error_message();
+ sr_err("Write error (%d): %s.", sp_last_error_code(), error);
+ sp_free_error_message(error);
+ return SR_ERR;
+ }
+
+ sr_spew("Wrote %d/%d bytes.", ret, count);
+
+ return ret;
+}
+
+/**
+ * Write a number of bytes to the specified serial port.
+ *
+ * @param serial Previously initialized serial port structure.
+ * @param[in] buf Buffer containing the bytes to write.
+ * @param[in] count Number of bytes to write.
+ *
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR Other error.
+ * @retval other The number of bytes written.
+ */
+SR_PRIV int serial_write(struct sr_serial_dev_inst *serial,
+ const void *buf, size_t count)
+{
+ return _serial_write(serial, buf, count, serial->nonblocking);
+}
+
+/**
+ * Write a number of bytes to the specified serial port, blocking until finished.
+ * @copydetails serial_write()
+ */
+SR_PRIV int serial_write_blocking(struct sr_serial_dev_inst *serial,
+ const void *buf, size_t count)
+{
+ return _serial_write(serial, buf, count, 0);
+}
+
+/**
+ * Write a number of bytes to the specified serial port, return immediately.
+ * @copydetails serial_write()
+*/
+SR_PRIV int serial_write_nonblocking(struct sr_serial_dev_inst *serial,
+ const void *buf, size_t count)
+{
+ return _serial_write(serial, buf, count, 1);
+}
+
+static int _serial_read(struct sr_serial_dev_inst *serial, void *buf,
+ size_t count, int nonblocking)
+{
+ ssize_t ret;
+ char *error;
+
+ if (!serial) {
+ sr_dbg("Invalid serial port.");
+ return SR_ERR;
+ }
+
+ if (!serial->data) {
+ sr_dbg("Cannot use unopened serial port %s.", serial->port);
+ return SR_ERR;
+ }
+
+ if (nonblocking)
+ ret = sp_nonblocking_read(serial->data, buf, count);
+ else
+ ret = sp_blocking_read(serial->data, buf, count, 0);
+
+ switch (ret) {
+ case SP_ERR_ARG:
+ sr_err("Attempted serial port read with invalid arguments.");
+ return SR_ERR_ARG;
+ case SP_ERR_FAIL:
+ error = sp_last_error_message();
+ sr_err("Read error (%d): %s.", sp_last_error_code(), error);
+ sp_free_error_message(error);
+ return SR_ERR;
+ }
+
+ if (ret > 0)
+ sr_spew("Read %d/%d bytes.", ret, count);
+
+ return ret;
+}
+
+/**
+ * Read a number of bytes from the specified serial port.
+ *
+ * @param serial Previously initialized serial port structure.
+ * @param buf Buffer where to store the bytes that are read.
+ * @param[in] count The number of bytes to read.
+ *
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR Other error.
+ * @retval other The number of bytes read.
+ */
+SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf,
+ size_t count)
+{
+ return _serial_read(serial, buf, count, serial->nonblocking);
+}
+
+/**
+ * Read a number of bytes from the specified serial port, block until finished.
+ * @copydetails serial_read()
+ */
+SR_PRIV int serial_read_blocking(struct sr_serial_dev_inst *serial, void *buf,
+ size_t count)
+{
+ return _serial_read(serial, buf, count, 0);
+}
+
+/**
+ * Try to read up to @a count bytes from the specified serial port, return
+ * immediately with what's available.
+ * @copydetails serial_read()
+ */
+SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf,
+ size_t count)
+{
+ return _serial_read(serial, buf, count, 1);
+}
+
+/**
+ * Set serial parameters for the specified serial port.
+ *
+ * @param serial Previously initialized serial port structure.
+ * @param[in] baudrate The baudrate to set.
+ * @param[in] bits The number of data bits to use (5, 6, 7 or 8).
+ * @param[in] parity The parity setting to use (0 = none, 1 = even, 2 = odd).
+ * @param[in] stopbits The number of stop bits to use (1 or 2).
+ * @param[in] flowcontrol The flow control settings to use (0 = none,
+ * 1 = RTS/CTS, 2 = XON/XOFF).
+ * @param[in] rts Status of RTS line (0 or 1; required by some interfaces).
+ * @param[in] dtr Status of DTR line (0 or 1; required by some interfaces).
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Failure.
+ */
+SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate,
+ int bits, int parity, int stopbits,
+ int flowcontrol, int rts, int dtr)
+{
+ int ret;
+ char *error;
+ struct sp_port_config *config;
+
+ if (!serial) {
+ sr_dbg("Invalid serial port.");
+ return SR_ERR;
+ }
+
+ if (!serial->data) {
+ sr_dbg("Cannot configure unopened serial port %s.", serial->port);
+ return SR_ERR;
+ }
+
+ sr_spew("Setting serial parameters on port %s.", serial->port);
+
+ sp_new_config(&config);
+ sp_set_config_baudrate(config, baudrate);
+ sp_set_config_bits(config, bits);
+ switch (parity) {
+ case 0:
+ sp_set_config_parity(config, SP_PARITY_NONE);
+ break;
+ case 1:
+ sp_set_config_parity(config, SP_PARITY_EVEN);
+ break;
+ case 2:
+ sp_set_config_parity(config, SP_PARITY_ODD);
+ break;
+ default:
+ return SR_ERR_ARG;
+ }
+ sp_set_config_stopbits(config, stopbits);
+ sp_set_config_rts(config, flowcontrol == 1 ? SP_RTS_FLOW_CONTROL : rts);
+ sp_set_config_cts(config, flowcontrol == 1 ? SP_CTS_FLOW_CONTROL : SP_CTS_IGNORE);
+ sp_set_config_dtr(config, dtr);
+ sp_set_config_dsr(config, SP_DSR_IGNORE);
+ sp_set_config_xon_xoff(config, flowcontrol == 2 ? SP_XONXOFF_INOUT : SP_XONXOFF_DISABLED);
+
+ ret = sp_set_config(serial->data, config);
+ sp_free_config(config);
+
+ switch (ret) {
+ case SP_ERR_ARG:
+ sr_err("Invalid arguments for setting serial port parameters.");
+ return SR_ERR_ARG;
+ case SP_ERR_FAIL:
+ error = sp_last_error_message();
+ sr_err("Error setting serial port parameters (%d): %s.",
+ sp_last_error_code(), error);
+ sp_free_error_message(error);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+/**
+ * Set serial parameters for the specified serial port from parameter string.
+ *
+ * @param serial Previously initialized serial port structure.
+ * @param[in] paramstr A serial communication parameters string of the form
+ * "<baudrate>/<bits><parity><stopbits>{/<option>}".\n
+ * Examples: "9600/8n1", "600/7o2/dtr=1/rts=0" or "460800/8n1/flow=2".\n
+ * \<baudrate\>=integer Baud rate.\n
+ * \<bits\>=5|6|7|8 Number of data bits.\n
+ * \<parity\>=n|e|o None, even, odd.\n
+ * \<stopbits\>=1|2 One or two stop bits.\n
+ * Options:\n
+ * dtr=0|1 Set DTR off resp. on.\n
+ * flow=0|1|2 Flow control. 0 for none, 1 for RTS/CTS, 2 for XON/XOFF.\n
+ * rts=0|1 Set RTS off resp. on.\n
+ * Please note that values and combinations of these parameters must be
+ * supported by the concrete serial interface hardware and the drivers for it.
+ * @retval SR_OK Success.
+ * @retval SR_ERR Failure.
+ */
+SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
+ const char *paramstr)
+{
+#define SERIAL_COMM_SPEC "^(\\d+)/([5678])([neo])([12])(.*)$"
+
+ GRegex *reg;
+ GMatchInfo *match;
+ int speed, databits, parity, stopbits, flow, rts, dtr, i;
+ char *mstr, **opts, **kv;
+
+ speed = databits = parity = stopbits = flow = 0;
+ rts = dtr = -1;
+ sr_spew("Parsing parameters from \"%s\".", paramstr);
+ reg = g_regex_new(SERIAL_COMM_SPEC, 0, 0, NULL);
+ if (g_regex_match(reg, paramstr, 0, &match)) {
+ if ((mstr = g_match_info_fetch(match, 1)))
+ speed = strtoul(mstr, NULL, 10);
+ g_free(mstr);
+ if ((mstr = g_match_info_fetch(match, 2)))
+ databits = strtoul(mstr, NULL, 10);
+ g_free(mstr);
+ if ((mstr = g_match_info_fetch(match, 3))) {
+ switch (mstr[0]) {
+ case 'n':
+ parity = SERIAL_PARITY_NONE;
+ break;
+ case 'e':
+ parity = SERIAL_PARITY_EVEN;
+ break;
+ case 'o':
+ parity = SERIAL_PARITY_ODD;
+ break;
+ }
+ }
+ g_free(mstr);
+ if ((mstr = g_match_info_fetch(match, 4)))
+ stopbits = strtoul(mstr, NULL, 10);
+ g_free(mstr);
+ if ((mstr = g_match_info_fetch(match, 5)) && mstr[0] != '\0') {
+ if (mstr[0] != '/') {
+ sr_dbg("missing separator before extra options");
+ speed = 0;
+ } else {
+ /* A set of "key=value" options separated by / */
+ opts = g_strsplit(mstr + 1, "/", 0);
+ for (i = 0; opts[i]; i++) {
+ kv = g_strsplit(opts[i], "=", 2);
+ if (!strncmp(kv[0], "rts", 3)) {
+ if (kv[1][0] == '1')
+ rts = 1;
+ else if (kv[1][0] == '0')
+ rts = 0;
+ else {
+ sr_dbg("invalid value for rts: %c", kv[1][0]);
+ speed = 0;
+ }
+ } else if (!strncmp(kv[0], "dtr", 3)) {
+ if (kv[1][0] == '1')
+ dtr = 1;
+ else if (kv[1][0] == '0')
+ dtr = 0;
+ else {
+ sr_dbg("invalid value for dtr: %c", kv[1][0]);
+ speed = 0;
+ }
+ } else if (!strncmp(kv[0], "flow", 4)) {
+ if (kv[1][0] == '0')
+ flow = 0;
+ else if (kv[1][0] == '1')
+ flow = 1;
+ else if (kv[1][0] == '2')
+ flow = 2;
+ else {
+ sr_dbg("invalid value for flow: %c", kv[1][0]);
+ speed = 0;
+ }
+ }
+ g_strfreev(kv);
+ }
+ g_strfreev(opts);
+ }
+ }
+ g_free(mstr);
+ }
+ g_match_info_unref(match);
+ g_regex_unref(reg);
+
+ if (speed) {
+ return serial_set_params(serial, speed, databits, parity,
+ stopbits, flow, rts, dtr);
+ } else {
+ sr_dbg("Could not infer speed from parameter string.");
+ return SR_ERR_ARG;
+ }
+}
+
+/**
+ * Read a line from the specified serial port.
+ *
+ * @param serial Previously initialized serial port structure.
+ * @param buf Buffer where to store the bytes that are read.
+ * @param buflen Size of the buffer.
+ * @param[in] timeout_ms How long to wait for a line to come in.
+ *
+ * Reading stops when CR of LR is found, which is stripped from the buffer.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Failure.
+ */
+SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
+ int *buflen, gint64 timeout_ms)
+{
+ gint64 start;
+ int maxlen, len;
+
+ if (!serial) {
+ sr_dbg("Invalid serial port.");
+ return SR_ERR;
+ }
+
+ if (!serial->data) {
+ sr_dbg("Cannot use unopened serial port %s.", serial->port);
+ return -1;
+ }
+
+ timeout_ms *= 1000;
+ start = g_get_monotonic_time();
+
+ maxlen = *buflen;
+ *buflen = len = 0;
+ while(1) {
+ len = maxlen - *buflen - 1;
+ if (len < 1)
+ break;
+ len = serial_read(serial, *buf + *buflen, 1);
+ if (len > 0) {
+ *buflen += len;
+ *(*buf + *buflen) = '\0';
+ if (*buflen > 0 && (*(*buf + *buflen - 1) == '\r'
+ || *(*buf + *buflen - 1) == '\n')) {
+ /* Strip CR/LF and terminate. */
+ *(*buf + --*buflen) = '\0';
+ break;
+ }
+ }
+ if (g_get_monotonic_time() - start > timeout_ms)
+ /* Timeout */
+ break;
+ if (len < 1)
+ g_usleep(2000);
+ }
+ if (*buflen)
+ sr_dbg("Received %d: '%s'.", *buflen, *buf);
+
+ return SR_OK;
+}
+
+/**
+ * Try to find a valid packet in a serial data stream.
+ *
+ * @param serial Previously initialized serial port structure.
+ * @param buf Buffer containing the bytes to write.
+ * @param buflen Size of the buffer.
+ * @param[in] packet_size Size, in bytes, of a valid packet.
+ * @param is_valid Callback that assesses whether the packet is valid or not.
+ * @param[in] timeout_ms The timeout after which, if no packet is detected, to
+ * abort scanning.
+ * @param[in] baudrate The baudrate of the serial port. This parameter is not
+ * critical, but it helps fine tune the serial port polling
+ * delay.
+ *
+ * @retval SR_OK Valid packet was found within the given timeout.
+ * @retval SR_ERR Failure.
+ */
+SR_PRIV int serial_stream_detect(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, int baudrate)
+{
+ uint64_t start, time, byte_delay_us;
+ size_t ibuf, i, maxlen;
+ int len;
+
+ maxlen = *buflen;
+
+ sr_dbg("Detecting packets on %s (timeout = %" PRIu64
+ "ms, baudrate = %d).", serial->port, timeout_ms, baudrate);
+
+ if (maxlen < (packet_size / 2) ) {
+ sr_err("Buffer size must be at least twice the packet size.");
+ return SR_ERR;
+ }
+
+ /* Assume 8n1 transmission. That is 10 bits for every byte. */
+ byte_delay_us = 10 * (1000000 / baudrate);
+ start = g_get_monotonic_time();
+
+ i = ibuf = len = 0;
+ while (ibuf < maxlen) {
+ len = serial_read(serial, &buf[ibuf], 1);
+ if (len > 0) {
+ ibuf += len;
+ } else if (len == 0) {
+ /* No logging, already done in serial_read(). */
+ } else {
+ /* Error reading byte, but continuing anyway. */
+ }
+
+ time = g_get_monotonic_time() - start;
+ time /= 1000;
+
+ if ((ibuf - i) >= packet_size) {
+ /* We have at least a packet's worth of data. */
+ if (is_valid(&buf[i])) {
+ sr_spew("Found valid %d-byte packet after "
+ "%" PRIu64 "ms.", (ibuf - i), time);
+ *buflen = ibuf;
+ return SR_OK;
+ } else {
+ sr_spew("Got %d bytes, but not a valid "
+ "packet.", (ibuf - i));
+ }
+ /* Not a valid packet. Continue searching. */
+ i++;
+ }
+ if (time >= timeout_ms) {
+ /* Timeout */
+ sr_dbg("Detection timed out after %dms.", time);
+ break;
+ }
+ if (len < 1)
+ g_usleep(byte_delay_us);
+ }
+
+ *buflen = ibuf;
+
+ sr_err("Didn't find a valid packet (read %d bytes).", *buflen);
+
+ return SR_ERR;
+}
+
+/**
+ * Extract the serial device and options from the options linked list.
+ *
+ * @param options List of options passed from the command line.
+ * @param serial_device Pointer where to store the exctracted serial device.
+ * @param serial_options Pointer where to store the optional extracted serial
+ * options.
+ *
+ * @return SR_OK if a serial_device is found, SR_ERR if no device is found. The
+ * returned string should not be freed by the caller.
+ */
+SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_device,
+ const char **serial_options)
+{
+ GSList *l;
+ struct sr_config *src;
+
+ *serial_device = NULL;
+
+ for (l = options; l; l = l->next) {
+ src = l->data;
+ switch (src->key) {
+ case SR_CONF_CONN:
+ *serial_device = g_variant_get_string(src->data, NULL);
+ sr_dbg("Parsed serial device: %s", *serial_device);
+ break;
+
+ case SR_CONF_SERIALCOMM:
+ *serial_options = g_variant_get_string(src->data, NULL);
+ sr_dbg("Parsed serial options: %s", *serial_options);
+ break;
+ }
+ }
+
+ if (!*serial_device) {
+ sr_dbg("No serial device specified");
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+#ifdef _WIN32
+typedef HANDLE event_handle;
+#else
+typedef int event_handle;
+#endif
+
+SR_PRIV int serial_source_add(struct sr_session *session,
+ struct sr_serial_dev_inst *serial, int events, int timeout,
+ sr_receive_data_callback cb, void *cb_data)
+{
+ enum sp_event mask = 0;
+ unsigned int i;
+
+ if (sp_new_event_set(&serial->event_set) != SP_OK)
+ return SR_ERR;
+
+ if (events & G_IO_IN)
+ mask |= SP_EVENT_RX_READY;
+ if (events & G_IO_OUT)
+ mask |= SP_EVENT_TX_READY;
+ if (events & G_IO_ERR)
+ mask |= SP_EVENT_ERROR;
+
+ if (sp_add_port_events(serial->event_set, serial->data, mask) != SP_OK) {
+ sp_free_event_set(serial->event_set);
+ return SR_ERR;
+ }
+
+ serial->pollfds = (GPollFD *) g_malloc0(sizeof(GPollFD) * serial->event_set->count);
+
+ for (i = 0; i < serial->event_set->count; i++) {
+
+ serial->pollfds[i].fd = ((event_handle *) serial->event_set->handles)[i];
+
+ mask = serial->event_set->masks[i];
+
+ if (mask & SP_EVENT_RX_READY)
+ serial->pollfds[i].events |= G_IO_IN;
+ if (mask & SP_EVENT_TX_READY)
+ serial->pollfds[i].events |= G_IO_OUT;
+ if (mask & SP_EVENT_ERROR)
+ serial->pollfds[i].events |= G_IO_ERR;
+
+ if (sr_session_source_add_pollfd(session, &serial->pollfds[i],
+ timeout, cb, cb_data) != SR_OK)
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int serial_source_remove(struct sr_session *session,
+ struct sr_serial_dev_inst *serial)
+{
+ unsigned int i;
+
+ for (i = 0; i < serial->event_set->count; i++)
+ if (sr_session_source_remove_pollfd(session, &serial->pollfds[i]) != SR_OK)
+ return SR_ERR;
+
+ g_free(serial->pollfds);
+ sp_free_event_set(serial->event_set);
+
+ serial->pollfds = NULL;
+ serial->event_set = NULL;
+
+ return SR_OK;
+}
+
+/**
+ * Find USB serial devices via the USB vendor ID and product ID.
+ *
+ * @param[in] vendor_id Vendor ID of the USB device.
+ * @param[in] product_id Product ID of the USB device.
+ *
+ * @return A GSList of strings containing the path of the serial device or
+ * NULL if no serial device is found. The returned list must be freed
+ * by the caller.
+ */
+SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id)
+{
+#ifdef __linux__
+ const gchar *usb_dev;
+ const char device_tree[] = "/sys/bus/usb/devices/";
+ GDir *devices_dir, *device_dir;
+ GSList *l = NULL;
+ GSList *tty_devs;
+ GSList *matched_paths;
+ FILE *fd;
+ char tmp[5];
+ gchar *vendor_path, *product_path, *path_copy;
+ gchar *prefix, *subdir_path, *device_path, *tty_path;
+ unsigned long read_vendor_id, read_product_id;
+ const char *file;
+
+ l = NULL;
+ tty_devs = NULL;
+ matched_paths = NULL;
+
+ if (!(devices_dir = g_dir_open(device_tree, 0, NULL)))
+ return NULL;
+
+ /*
+ * Find potential candidates using the vendor ID and product ID
+ * and store them in matched_paths.
+ */
+ while ((usb_dev = g_dir_read_name(devices_dir))) {
+ vendor_path = g_strconcat(device_tree,
+ usb_dev, "/idVendor", NULL);
+ product_path = g_strconcat(device_tree,
+ usb_dev, "/idProduct", NULL);
+
+ if (!g_file_test(vendor_path, G_FILE_TEST_EXISTS) ||
+ !g_file_test(product_path, G_FILE_TEST_EXISTS))
+ goto skip_device;
+
+ if ((fd = g_fopen(vendor_path, "r")) == NULL)
+ goto skip_device;
+
+ if (fgets(tmp, sizeof(tmp), fd) == NULL) {
+ fclose(fd);
+ goto skip_device;
+ }
+ read_vendor_id = strtoul(tmp, NULL, 16);
+
+ fclose(fd);
+
+ if ((fd = g_fopen(product_path, "r")) == NULL)
+ goto skip_device;
+
+ if (fgets(tmp, sizeof(tmp), fd) == NULL) {
+ fclose(fd);
+ goto skip_device;
+ }
+ read_product_id = strtoul(tmp, NULL, 16);
+
+ fclose(fd);
+
+ if (vendor_id == read_vendor_id &&
+ product_id == read_product_id) {
+ path_copy = g_strdup(usb_dev);
+ matched_paths = g_slist_prepend(matched_paths,
+ path_copy);
+ }
+
+skip_device:
+ g_free(vendor_path);
+ g_free(product_path);
+ }
+ g_dir_close(devices_dir);
+
+ /* For every matched device try to find a ttyUSBX subfolder. */
+ for (l = matched_paths; l; l = l->next) {
+ subdir_path = NULL;
+
+ device_path = g_strconcat(device_tree, l->data, NULL);
+
+ if (!(device_dir = g_dir_open(device_path, 0, NULL))) {
+ g_free(device_path);
+ continue;
+ }
+
+ prefix = g_strconcat(l->data, ":", NULL);
+
+ while ((file = g_dir_read_name(device_dir))) {
+ if (g_str_has_prefix(file, prefix)) {
+ subdir_path = g_strconcat(device_path,
+ "/", file, NULL);
+ break;
+ }
+ }
+ g_dir_close(device_dir);
+
+ g_free(prefix);
+ g_free(device_path);
+
+ if (subdir_path) {
+ if (!(device_dir = g_dir_open(subdir_path, 0, NULL))) {
+ g_free(subdir_path);
+ continue;
+ }
+ g_free(subdir_path);
+
+ while ((file = g_dir_read_name(device_dir))) {
+ if (g_str_has_prefix(file, "ttyUSB")) {
+ tty_path = g_strconcat("/dev/",
+ file, NULL);
+ sr_dbg("Found USB device %04x:%04x attached to %s.",
+ vendor_id, product_id, tty_path);
+ tty_devs = g_slist_prepend(tty_devs,
+ tty_path);
+ break;
+ }
+ }
+ g_dir_close(device_dir);
+ }
+ }
+ g_slist_free_full(matched_paths, g_free);
+
+ return tty_devs;
+#else
+ (void)vendor_id;
+ (void)product_id;
+
+ return NULL;
+#endif
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "session"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Creating, using, or destroying libsigrok sessions.
+ */
+
+/**
+ * @defgroup grp_session Session handling
+ *
+ * Creating, using, or destroying libsigrok sessions.
+ *
+ * @{
+ */
+
+struct source {
+ int timeout;
+ sr_receive_data_callback cb;
+ void *cb_data;
+
+ /* This is used to keep track of the object (fd, pollfd or channel) which is
+ * being polled and will be used to match the source when removing it again.
+ */
+ gintptr poll_object;
+};
+
+struct datafeed_callback {
+ sr_datafeed_callback cb;
+ void *cb_data;
+};
+
+/**
+ * Create a new session.
+ * Currently, there can be only one session at a time within the same process.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_BUG A session exists already.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_new(struct sr_session **new_session)
+{
+ struct sr_session *session;
+
+ session = g_malloc0(sizeof(struct sr_session));
+
+ session->source_timeout = -1;
+ session->running = FALSE;
+ session->abort_session = FALSE;
+ g_mutex_init(&session->stop_mutex);
+
+ *new_session = session;
+
+ return SR_OK;
+}
+
+/**
+ * Destroy a session.
+ * This frees up all memory used by the session.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_destroy(struct sr_session *session)
+{
+ if (!session) {
+ sr_err("%s: session was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ sr_session_dev_remove_all(session);
+ g_mutex_clear(&session->stop_mutex);
+ if (session->trigger)
+ sr_trigger_free(session->trigger);
+
+ g_free(session);
+
+ return SR_OK;
+}
+
+/**
+ * Remove all the devices from a session.
+ *
+ * The session itself (i.e., the struct sr_session) is not free'd and still
+ * exists after this function returns.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_BUG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_dev_remove_all(struct sr_session *session)
+{
+ struct sr_dev_inst *sdi;
+ GSList *l;
+
+ if (!session) {
+ sr_err("%s: session was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ for (l = session->devs; l; l = l->next) {
+ sdi = (struct sr_dev_inst *) l->data;
+ sdi->session = NULL;
+ }
+
+ g_slist_free(session->devs);
+ session->devs = NULL;
+
+ return SR_OK;
+}
+
+/**
+ * Add a device instance to a session.
+ *
+ * @param sdi The device instance to add to a session. Must not
+ * be NULL. Also, sdi->driver and sdi->driver->dev_open must
+ * not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_dev_add(struct sr_session *session,
+ struct sr_dev_inst *sdi)
+{
+ int ret;
+
+ if (!sdi) {
+ sr_err("%s: sdi was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ if (!session) {
+ sr_err("%s: session was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ /* If sdi->session is not NULL, the device is already in this or
+ * another session. */
+ if (sdi->session) {
+ sr_err("%s: already assigned to session", __func__);
+ return SR_ERR_ARG;
+ }
+
+ /* If sdi->driver is NULL, this is a virtual device. */
+ if (!sdi->driver) {
+ sr_dbg("%s: sdi->driver was NULL, this seems to be "
+ "a virtual device; continuing", __func__);
+ /* Just add the device, don't run dev_open(). */
+ session->devs = g_slist_append(session->devs, (gpointer)sdi);
+ sdi->session = session;
+ return SR_OK;
+ }
+
+ /* sdi->driver is non-NULL (i.e. we have a real device). */
+ if (!sdi->driver->dev_open) {
+ sr_err("%s: sdi->driver->dev_open was NULL", __func__);
+ return SR_ERR_BUG;
+ }
+
+ session->devs = g_slist_append(session->devs, (gpointer)sdi);
+ sdi->session = session;
+
+ if (session->running) {
+ /* Adding a device to a running session. Commit settings
+ * and start acquisition on that device now. */
+ if ((ret = sr_config_commit(sdi)) != SR_OK) {
+ sr_err("Failed to commit device settings before "
+ "starting acquisition in running session (%s)",
+ sr_strerror(ret));
+ return ret;
+ }
+ if ((ret = sdi->driver->dev_acquisition_start(sdi,
+ (void *)sdi)) != SR_OK) {
+ sr_err("Failed to start acquisition of device in "
+ "running session (%s)", sr_strerror(ret));
+ return ret;
+ }
+ }
+
+ return SR_OK;
+}
+
+/**
+ * List all device instances attached to a session.
+ *
+ * @param devlist A pointer where the device instance list will be
+ * stored on return. If no devices are in the session,
+ * this will be NULL. Each element in the list points
+ * to a struct sr_dev_inst *.
+ * The list must be freed by the caller, but not the
+ * elements pointed to.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_dev_list(struct sr_session *session, GSList **devlist)
+{
+ if (!session)
+ return SR_ERR_ARG;
+
+ if (!devlist)
+ return SR_ERR_ARG;
+
+ *devlist = g_slist_copy(session->devs);
+
+ return SR_OK;
+}
+
+/**
+ * Remove all datafeed callbacks in a session.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_datafeed_callback_remove_all(struct sr_session *session)
+{
+ if (!session) {
+ sr_err("%s: session was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ g_slist_free_full(session->datafeed_callbacks, g_free);
+ session->datafeed_callbacks = NULL;
+
+ return SR_OK;
+}
+
+/**
+ * Add a datafeed callback to a session.
+ *
+ * @param cb Function to call when a chunk of data is received.
+ * Must not be NULL.
+ * @param cb_data Opaque pointer passed in by the caller.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_BUG No session exists.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_session_datafeed_callback_add(struct sr_session *session,
+ sr_datafeed_callback cb, void *cb_data)
+{
+ struct datafeed_callback *cb_struct;
+
+ if (!session) {
+ sr_err("%s: session was NULL", __func__);
+ return SR_ERR_BUG;
+ }
+
+ if (!cb) {
+ sr_err("%s: cb was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ if (!(cb_struct = g_try_malloc0(sizeof(struct datafeed_callback))))
+ return SR_ERR_MALLOC;
+
+ cb_struct->cb = cb;
+ cb_struct->cb_data = cb_data;
+
+ session->datafeed_callbacks =
+ g_slist_append(session->datafeed_callbacks, cb_struct);
+
+ return SR_OK;
+}
+
+SR_API struct sr_trigger *sr_session_trigger_get(struct sr_session *session)
+{
+ return session->trigger;
+}
+
+SR_API int sr_session_trigger_set(struct sr_session *session, struct sr_trigger *trig)
+{
+ session->trigger = trig;
+
+ return SR_OK;
+}
+
+/**
+ * Call every device in the current session's callback.
+ *
+ * For sessions not driven by select loops such as sr_session_run(),
+ * but driven by another scheduler, this can be used to poll the devices
+ * from within that scheduler.
+ *
+ * @param block If TRUE, this call will wait for any of the session's
+ * sources to fire an event on the file descriptors, or
+ * any of their timeouts to activate. In other words, this
+ * can be used as a select loop.
+ * If FALSE, all sources have their callback run, regardless
+ * of file descriptor or timeout status.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Error occured.
+ */
+static int sr_session_iteration(struct sr_session *session, gboolean block)
+{
+ unsigned int i;
+ int ret;
+
+ ret = g_poll(session->pollfds, session->num_sources,
+ block ? session->source_timeout : 0);
+ for (i = 0; i < session->num_sources; i++) {
+ if (session->pollfds[i].revents > 0 || (ret == 0
+ && session->source_timeout == session->sources[i].timeout)) {
+ /*
+ * Invoke the source's callback on an event,
+ * or if the poll timed out and this source
+ * asked for that timeout.
+ */
+ if (!session->sources[i].cb(session->pollfds[i].fd,
+ session->pollfds[i].revents,
+ session->sources[i].cb_data))
+ sr_session_source_remove(session,
+ session->sources[i].poll_object);
+ }
+ /*
+ * We want to take as little time as possible to stop
+ * the session if we have been told to do so. Therefore,
+ * we check the flag after processing every source, not
+ * just once per main event loop.
+ */
+ g_mutex_lock(&session->stop_mutex);
+ if (session->abort_session) {
+ sr_session_stop_sync(session);
+ /* But once is enough. */
+ session->abort_session = FALSE;
+ }
+ g_mutex_unlock(&session->stop_mutex);
+ }
+
+ return SR_OK;
+}
+
+
+static int verify_trigger(struct sr_trigger *trigger)
+{
+ struct sr_trigger_stage *stage;
+ struct sr_trigger_match *match;
+ GSList *l, *m;
+
+ if (!trigger->stages) {
+ sr_err("No trigger stages defined.");
+ return SR_ERR;
+ }
+
+ sr_spew("Checking trigger:");
+ for (l = trigger->stages; l; l = l->next) {
+ stage = l->data;
+ if (!stage->matches) {
+ sr_err("Stage %d has no matches defined.", stage->stage);
+ return SR_ERR;
+ }
+ for (m = stage->matches; m; m = m->next) {
+ match = m->data;
+ if (!match->channel) {
+ sr_err("Stage %d match has no channel.", stage->stage);
+ return SR_ERR;
+ }
+ if (!match->match) {
+ sr_err("Stage %d match is not defined.", stage->stage);
+ return SR_ERR;
+ }
+ sr_spew("Stage %d match on channel %s, match %d", stage->stage,
+ match->channel->name, match->match);
+ }
+ }
+
+ return SR_OK;
+}
+/**
+ * Start a session.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_start(struct sr_session *session)
+{
+ struct sr_dev_inst *sdi;
+ GSList *l;
+ int ret;
+
+ if (!session) {
+ sr_err("%s: session was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ if (!session->devs) {
+ sr_err("%s: session->devs was NULL; a session "
+ "cannot be started without devices.", __func__);
+ return SR_ERR_ARG;
+ }
+
+ if (session->trigger && verify_trigger(session->trigger) != SR_OK)
+ return SR_ERR;
+
+ sr_info("Starting.");
+
+ ret = SR_OK;
+ for (l = session->devs; l; l = l->next) {
+ sdi = l->data;
+ if ((ret = sr_config_commit(sdi)) != SR_OK) {
+ sr_err("Failed to commit device settings before "
+ "starting acquisition (%s)", sr_strerror(ret));
+ break;
+ }
+ if ((ret = sdi->driver->dev_acquisition_start(sdi, sdi)) != SR_OK) {
+ sr_err("%s: could not start an acquisition "
+ "(%s)", __func__, sr_strerror(ret));
+ break;
+ }
+ }
+
+ /* TODO: What if there are multiple devices? Which return code? */
+
+ return ret;
+}
+
+/**
+ * Run a session.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_run(struct sr_session *session)
+{
+ if (!session) {
+ sr_err("%s: session was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ if (!session->devs) {
+ /* TODO: Actually the case? */
+ sr_err("%s: session->devs was NULL; a session "
+ "cannot be run without devices.", __func__);
+ return SR_ERR_ARG;
+ }
+ session->running = TRUE;
+
+ sr_info("Running.");
+
+ /* Do we have real sources? */
+ if (session->num_sources == 1 && session->pollfds[0].fd == -1) {
+ /* Dummy source, freewheel over it. */
+ while (session->num_sources)
+ session->sources[0].cb(-1, 0, session->sources[0].cb_data);
+ } else {
+ /* Real sources, use g_poll() main loop. */
+ while (session->num_sources)
+ sr_session_iteration(session, TRUE);
+ }
+
+ return SR_OK;
+}
+
+/**
+ * Stop a session.
+ *
+ * The session is stopped immediately, with all acquisition sessions stopped
+ * and hardware drivers cleaned up.
+ *
+ * This must be called from within the session thread, to prevent freeing
+ * resources that the session thread will try to use.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ *
+ * @private
+ */
+SR_PRIV int sr_session_stop_sync(struct sr_session *session)
+{
+ struct sr_dev_inst *sdi;
+ GSList *l;
+
+ if (!session) {
+ sr_err("%s: session was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ sr_info("Stopping.");
+
+ for (l = session->devs; l; l = l->next) {
+ sdi = l->data;
+ if (sdi->driver) {
+ if (sdi->driver->dev_acquisition_stop)
+ sdi->driver->dev_acquisition_stop(sdi, sdi);
+ }
+ }
+ session->running = FALSE;
+
+ return SR_OK;
+}
+
+/**
+ * Stop a session.
+ *
+ * The session is stopped immediately, with all acquisition sessions being
+ * stopped and hardware drivers cleaned up.
+ *
+ * If the session is run in a separate thread, this function will not block
+ * until the session is finished executing. It is the caller's responsibility
+ * to wait for the session thread to return before assuming that the session is
+ * completely decommissioned.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_stop(struct sr_session *session)
+{
+ if (!session) {
+ sr_err("%s: session was NULL", __func__);
+ return SR_ERR_BUG;
+ }
+
+ g_mutex_lock(&session->stop_mutex);
+ session->abort_session = TRUE;
+ g_mutex_unlock(&session->stop_mutex);
+
+ return SR_OK;
+}
+
+/**
+ * Debug helper.
+ *
+ * @param packet The packet to show debugging information for.
+ */
+static void datafeed_dump(const struct sr_datafeed_packet *packet)
+{
+ const struct sr_datafeed_logic *logic;
+ const struct sr_datafeed_analog *analog;
+
+ switch (packet->type) {
+ case SR_DF_HEADER:
+ sr_dbg("bus: Received SR_DF_HEADER packet.");
+ break;
+ case SR_DF_TRIGGER:
+ sr_dbg("bus: Received SR_DF_TRIGGER packet.");
+ break;
+ case SR_DF_META:
+ sr_dbg("bus: Received SR_DF_META packet.");
+ break;
+ case SR_DF_LOGIC:
+ logic = packet->payload;
+ sr_dbg("bus: Received SR_DF_LOGIC packet (%" PRIu64 " bytes, "
+ "unitsize = %d).", logic->length, logic->unitsize);
+ break;
+ case SR_DF_ANALOG:
+ analog = packet->payload;
+ sr_dbg("bus: Received SR_DF_ANALOG packet (%d samples).",
+ analog->num_samples);
+ break;
+ case SR_DF_END:
+ sr_dbg("bus: Received SR_DF_END packet.");
+ break;
+ case SR_DF_FRAME_BEGIN:
+ sr_dbg("bus: Received SR_DF_FRAME_BEGIN packet.");
+ break;
+ case SR_DF_FRAME_END:
+ sr_dbg("bus: Received SR_DF_FRAME_END packet.");
+ break;
+ default:
+ sr_dbg("bus: Received unknown packet type: %d.", packet->type);
+ break;
+ }
+}
+
+/**
+ * Send a packet to whatever is listening on the datafeed bus.
+ *
+ * Hardware drivers use this to send a data packet to the frontend.
+ *
+ * @param sdi TODO.
+ * @param packet The datafeed packet to send to the session bus.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @private
+ */
+SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
+ const struct sr_datafeed_packet *packet)
+{
+ GSList *l;
+ struct datafeed_callback *cb_struct;
+
+ if (!sdi) {
+ sr_err("%s: sdi was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ if (!packet) {
+ sr_err("%s: packet was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ for (l = sdi->session->datafeed_callbacks; l; l = l->next) {
+ if (sr_log_loglevel_get() >= SR_LOG_DBG)
+ datafeed_dump(packet);
+ cb_struct = l->data;
+ cb_struct->cb(sdi, packet, cb_struct->cb_data);
+ }
+
+ return SR_OK;
+}
+
+/**
+ * Add an event source for a file descriptor.
+ *
+ * @param pollfd The GPollFD.
+ * @param[in] timeout Max time to wait before the callback is called,
+ * ignored if 0.
+ * @param cb Callback function to add. Must not be NULL.
+ * @param cb_data Data for the callback function. Can be NULL.
+ * @param poll_object TODO.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR_MALLOC Memory allocation error.
+ */
+static int _sr_session_source_add(struct sr_session *session, GPollFD *pollfd,
+ int timeout, sr_receive_data_callback cb, void *cb_data, gintptr poll_object)
+{
+ struct source *new_sources, *s;
+ GPollFD *new_pollfds;
+
+ if (!cb) {
+ sr_err("%s: cb was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ /* Note: cb_data can be NULL, that's not a bug. */
+
+ new_pollfds = g_try_realloc(session->pollfds,
+ sizeof(GPollFD) * (session->num_sources + 1));
+ if (!new_pollfds) {
+ sr_err("%s: new_pollfds malloc failed", __func__);
+ return SR_ERR_MALLOC;
+ }
+
+ new_sources = g_try_realloc(session->sources, sizeof(struct source) *
+ (session->num_sources + 1));
+ if (!new_sources) {
+ sr_err("%s: new_sources malloc failed", __func__);
+ return SR_ERR_MALLOC;
+ }
+
+ new_pollfds[session->num_sources] = *pollfd;
+ s = &new_sources[session->num_sources++];
+ s->timeout = timeout;
+ s->cb = cb;
+ s->cb_data = cb_data;
+ s->poll_object = poll_object;
+ session->pollfds = new_pollfds;
+ session->sources = new_sources;
+
+ if (timeout != session->source_timeout && timeout > 0
+ && (session->source_timeout == -1 || timeout < session->source_timeout))
+ session->source_timeout = timeout;
+
+ return SR_OK;
+}
+
+/**
+ * Add an event source for a file descriptor.
+ *
+ * @param fd The file descriptor.
+ * @param events Events to check for.
+ * @param timeout Max time to wait before the callback is called, ignored if 0.
+ * @param cb Callback function to add. Must not be NULL.
+ * @param cb_data Data for the callback function. Can be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR_MALLOC Memory allocation error.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_session_source_add(struct sr_session *session, int fd,
+ int events, int timeout, sr_receive_data_callback cb, void *cb_data)
+{
+ GPollFD p;
+
+ (void) session;
+
+ p.fd = fd;
+ p.events = events;
+
+ return _sr_session_source_add(session, &p, timeout, cb, cb_data, (gintptr)fd);
+}
+
+/**
+ * Add an event source for a GPollFD.
+ *
+ * @param pollfd The GPollFD.
+ * @param timeout Max time to wait before the callback is called, ignored if 0.
+ * @param cb Callback function to add. Must not be NULL.
+ * @param cb_data Data for the callback function. Can be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR_MALLOC Memory allocation error.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_session_source_add_pollfd(struct sr_session *session,
+ GPollFD *pollfd, int timeout, sr_receive_data_callback cb,
+ void *cb_data)
+{
+ (void) session;
+
+ return _sr_session_source_add(session, pollfd, timeout, cb,
+ cb_data, (gintptr)pollfd);
+}
+
+/**
+ * Add an event source for a GIOChannel.
+ *
+ * @param channel The GIOChannel.
+ * @param events Events to poll on.
+ * @param timeout Max time to wait before the callback is called, ignored if 0.
+ * @param cb Callback function to add. Must not be NULL.
+ * @param cb_data Data for the callback function. Can be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR_MALLOC Memory allocation error.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_session_source_add_channel(struct sr_session *session,
+ GIOChannel *channel, int events, int timeout,
+ sr_receive_data_callback cb, void *cb_data)
+{
+ GPollFD p;
+
+ (void) session;
+
+#ifdef _WIN32
+ g_io_channel_win32_make_pollfd(channel, events, &p);
+#else
+ p.fd = g_io_channel_unix_get_fd(channel);
+ p.events = events;
+#endif
+
+ return _sr_session_source_add(session, &p, timeout, cb, cb_data, (gintptr)channel);
+}
+
+/**
+ * Remove the source belonging to the specified channel.
+ *
+ * @todo Add more error checks and logging.
+ *
+ * @param poll_object The channel for which the source should be removed.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_ARG Invalid arguments
+ * @retval SR_ERR_MALLOC Memory allocation error
+ * @retval SR_ERR_BUG Internal error
+ */
+static int _sr_session_source_remove(struct sr_session *session, gintptr poll_object)
+{
+ struct source *new_sources;
+ GPollFD *new_pollfds;
+ unsigned int old;
+
+ if (!session->sources || !session->num_sources) {
+ sr_err("%s: sources was NULL", __func__);
+ return SR_ERR_BUG;
+ }
+
+ for (old = 0; old < session->num_sources; old++) {
+ if (session->sources[old].poll_object == poll_object)
+ break;
+ }
+
+ /* fd not found, nothing to do */
+ if (old == session->num_sources)
+ return SR_OK;
+
+ session->num_sources -= 1;
+
+ if (old != session->num_sources) {
+ memmove(&session->pollfds[old], &session->pollfds[old+1],
+ (session->num_sources - old) * sizeof(GPollFD));
+ memmove(&session->sources[old], &session->sources[old+1],
+ (session->num_sources - old) * sizeof(struct source));
+ }
+
+ new_pollfds = g_try_realloc(session->pollfds, sizeof(GPollFD) * session->num_sources);
+ if (!new_pollfds && session->num_sources > 0) {
+ sr_err("%s: new_pollfds malloc failed", __func__);
+ return SR_ERR_MALLOC;
+ }
+
+ new_sources = g_try_realloc(session->sources, sizeof(struct source) * session->num_sources);
+ if (!new_sources && session->num_sources > 0) {
+ sr_err("%s: new_sources malloc failed", __func__);
+ return SR_ERR_MALLOC;
+ }
+
+ session->pollfds = new_pollfds;
+ session->sources = new_sources;
+
+ return SR_OK;
+}
+
+/**
+ * Remove the source belonging to the specified file descriptor.
+ *
+ * @param fd The file descriptor for which the source should be removed.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_ARG Invalid argument
+ * @retval SR_ERR_MALLOC Memory allocation error.
+ * @retval SR_ERR_BUG Internal error.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_session_source_remove(struct sr_session *session, int fd)
+{
+ (void) session;
+
+ return _sr_session_source_remove(session, (gintptr)fd);
+}
+
+/**
+ * Remove the source belonging to the specified poll descriptor.
+ *
+ * @param pollfd The poll descriptor for which the source should be removed.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
+ * SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
+ * internal errors.
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_session_source_remove_pollfd(struct sr_session *session,
+ GPollFD *pollfd)
+{
+ (void) session;
+
+ return _sr_session_source_remove(session, (gintptr)pollfd);
+}
+
+/**
+ * Remove the source belonging to the specified channel.
+ *
+ * @param channel The channel for which the source should be removed.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR_MALLOC Memory allocation error.
+ * @return SR_ERR_BUG Internal error.
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_session_source_remove_channel(struct sr_session *session,
+ GIOChannel *channel)
+{
+ (void) session;
+
+ return _sr_session_source_remove(session, (gintptr)channel);
+}
+
+/** @} */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <zip.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "virtual-session"
+
+/* size of payloads sent across the session bus */
+/** @cond PRIVATE */
+#define CHUNKSIZE (512 * 1024)
+/** @endcond */
+
+SR_PRIV struct sr_dev_driver session_driver_info;
+static struct sr_dev_driver *di = &session_driver_info;
+
+struct session_vdev {
+ char *sessionfile;
+ char *capturefile;
+ struct zip *archive;
+ struct zip_file *capfile;
+ int bytes_read;
+ uint64_t samplerate;
+ int unitsize;
+ int num_channels;
+ int cur_chunk;
+ gboolean finished;
+};
+
+static const int hwcaps[] = {
+ SR_CONF_CAPTUREFILE,
+ SR_CONF_CAPTURE_UNITSIZE,
+ SR_CONF_SAMPLERATE,
+};
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct session_vdev *vdev;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ struct zip_stat zs;
+ int ret, got_data;
+ char capturefile[16];
+ void *buf;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+ got_data = FALSE;
+ vdev = sdi->priv;
+ if (!vdev->finished) {
+ if (!vdev->capfile) {
+ /* No capture file opened yet, or finished with the last
+ * chunked one. */
+ if (vdev->cur_chunk == 0) {
+ /* capturefile is always the unchunked base name. */
+ if (zip_stat(vdev->archive, vdev->capturefile, 0, &zs) != -1) {
+ /* No chunks, just a single capture file. */
+ vdev->cur_chunk = 0;
+ if (!(vdev->capfile = zip_fopen(vdev->archive,
+ vdev->capturefile, 0)))
+ return FALSE;
+ sr_dbg("Opened %s.", vdev->capturefile);
+ } else {
+ /* Try as first chunk filename. */
+ snprintf(capturefile, 15, "%s-1", vdev->capturefile);
+ if (zip_stat(vdev->archive, capturefile, 0, &zs) != -1) {
+ vdev->cur_chunk = 1;
+ if (!(vdev->capfile = zip_fopen(vdev->archive,
+ capturefile, 0)))
+ return FALSE;
+ sr_dbg("Opened %s.", capturefile);
+ } else {
+ sr_err("No capture file '%s' in " "session file '%s'.",
+ vdev->capturefile, vdev->sessionfile);
+ return FALSE;
+ }
+ }
+ } else {
+ /* Capture data is chunked, advance to the next chunk. */
+ vdev->cur_chunk++;
+ snprintf(capturefile, 15, "%s-%d", vdev->capturefile,
+ vdev->cur_chunk);
+ if (zip_stat(vdev->archive, capturefile, 0, &zs) != -1) {
+ if (!(vdev->capfile = zip_fopen(vdev->archive,
+ capturefile, 0)))
+ return FALSE;
+ sr_dbg("Opened %s.", capturefile);
+ } else {
+ /* We got all the chunks, finish up. */
+ vdev->finished = TRUE;
+ return TRUE;
+ }
+ }
+ }
+
+ if (!(buf = g_try_malloc(CHUNKSIZE))) {
+ sr_err("%s: buf malloc failed", __func__);
+ return FALSE;
+ }
+
+ ret = zip_fread(vdev->capfile, buf,
+ CHUNKSIZE / vdev->unitsize * vdev->unitsize);
+ if (ret > 0) {
+ if (ret % vdev->unitsize != 0)
+ sr_warn("Read size %d not a multiple of the"
+ " unit size %d.", ret, vdev->unitsize);
+ got_data = TRUE;
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = ret;
+ logic.unitsize = vdev->unitsize;
+ logic.data = buf;
+ vdev->bytes_read += ret;
+ sr_session_send(sdi, &packet);
+ } else {
+ /* done with this capture file */
+ zip_fclose(vdev->capfile);
+ vdev->capfile = NULL;
+ if (vdev->cur_chunk == 0) {
+ /* It was the only file. */
+ vdev->finished = TRUE;
+ } else {
+ /* There might be more chunks, so don't fall through
+ * to the SR_DF_END here. */
+ g_free(buf);
+ return TRUE;
+ }
+ }
+ g_free(buf);
+ }
+
+ if (!got_data) {
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+ sr_session_source_remove(sdi->session, -1);
+ }
+
+ return TRUE;
+}
+
+/* driver callbacks */
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static int dev_clear(void)
+{
+ struct drv_context *drvc;
+ GSList *l;
+
+ drvc = di->priv;
+ for (l = drvc->instances; l; l = l->next)
+ sr_dev_inst_free(l->data);
+ g_slist_free(drvc->instances);
+ drvc->instances = NULL;
+
+ return SR_OK;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc;
+ struct session_vdev *vdev;
+
+ drvc = di->priv;
+ vdev = g_malloc0(sizeof(struct session_vdev));
+ sdi->priv = vdev;
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ const struct session_vdev *const vdev = sdi->priv;
+ g_free(vdev->sessionfile);
+ g_free(vdev->capturefile);
+
+ g_free(sdi->priv);
+ sdi->priv = NULL;
+
+ return SR_OK;
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct session_vdev *vdev;
+
+ (void)cg;
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ if (sdi) {
+ vdev = sdi->priv;
+ *data = g_variant_new_uint64(vdev->samplerate);
+ } else
+ return SR_ERR;
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ struct session_vdev *vdev;
+
+ (void)cg;
+
+ vdev = sdi->priv;
+
+ switch (id) {
+ case SR_CONF_SAMPLERATE:
+ vdev->samplerate = g_variant_get_uint64(data);
+ sr_info("Setting samplerate to %" PRIu64 ".", vdev->samplerate);
+ break;
+ case SR_CONF_SESSIONFILE:
+ g_free(vdev->sessionfile);
+ vdev->sessionfile = g_strdup(g_variant_get_string(data, NULL));
+ sr_info("Setting sessionfile to '%s'.", vdev->sessionfile);
+ break;
+ case SR_CONF_CAPTUREFILE:
+ g_free(vdev->capturefile);
+ vdev->capturefile = g_strdup(g_variant_get_string(data, NULL));
+ sr_info("Setting capturefile to '%s'.", vdev->capturefile);
+ break;
+ case SR_CONF_CAPTURE_UNITSIZE:
+ vdev->unitsize = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_NUM_LOGIC_CHANNELS:
+ vdev->num_channels = g_variant_get_uint64(data);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg)
+{
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct session_vdev *vdev;
+ int ret;
+
+ (void)cb_data;
+
+ vdev = sdi->priv;
+ vdev->bytes_read = 0;
+ vdev->cur_chunk = 0;
+ vdev->finished = FALSE;
+
+ sr_info("Opening archive %s file %s", vdev->sessionfile,
+ vdev->capturefile);
+
+ if (!(vdev->archive = zip_open(vdev->sessionfile, 0, &ret))) {
+ sr_err("Failed to open session file '%s': "
+ "zip error %d.", vdev->sessionfile, ret);
+ return SR_ERR;
+ }
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(sdi, LOG_PREFIX);
+
+ /* freewheeling source */
+ sr_session_source_add(sdi->session, -1, 0, 0, receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+/** @private */
+SR_PRIV struct sr_dev_driver session_driver = {
+ .name = "virtual-session",
+ .longname = "Session-emulating driver",
+ .api_version = 1,
+ .init = init,
+ .cleanup = dev_clear,
+ .scan = NULL,
+ .dev_list = NULL,
+ .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 = NULL,
+ .priv = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <zip.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "config.h" /* Needed for PACKAGE_VERSION and others. */
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "session-file"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Loading and saving libsigrok session files.
+ */
+
+/**
+ * @addtogroup grp_session
+ *
+ * @{
+ */
+
+extern struct sr_session *session;
+extern SR_PRIV struct sr_dev_driver session_driver;
+
+/** @private */
+SR_PRIV int sr_sessionfile_check(const char *filename)
+{
+ struct stat st;
+ struct zip *archive;
+ struct zip_file *zf;
+ struct zip_stat zs;
+ int version, ret;
+ char s[11];
+
+ if (!filename)
+ return SR_ERR_ARG;
+
+ if (stat(filename, &st) == -1) {
+ sr_err("Couldn't stat %s: %s", filename, strerror(errno));
+ return SR_ERR;
+ }
+
+ if (!(archive = zip_open(filename, 0, &ret)))
+ /* No logging: this can be used just to check if it's
+ * a sigrok session file or not. */
+ return SR_ERR;
+
+ /* check "version" */
+ version = 0;
+ if (!(zf = zip_fopen(archive, "version", 0))) {
+ sr_dbg("Not a sigrok session file: no version found.");
+ return SR_ERR;
+ }
+ if ((ret = zip_fread(zf, s, 10)) == -1)
+ return SR_ERR;
+ zip_fclose(zf);
+ s[ret] = 0;
+ version = strtoull(s, NULL, 10);
+ if (version > 2) {
+ sr_dbg("Cannot handle sigrok session file version %d.", version);
+ return SR_ERR;
+ }
+ sr_spew("Detected sigrok session file version %d.", version);
+
+ /* read "metadata" */
+ if (zip_stat(archive, "metadata", 0, &zs) == -1) {
+ sr_dbg("Not a valid sigrok session file.");
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+/**
+ * Load the session from the specified filename.
+ *
+ * @param filename The name of the session file to load. Must not be NULL.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments,
+ * SR_ERR_MALLOC upon memory allocation errors, or SR_ERR upon
+ * other errors.
+ */
+SR_API int sr_session_load(const char *filename, struct sr_session **session)
+{
+ GKeyFile *kf;
+ GPtrArray *capturefiles;
+ struct zip *archive;
+ struct zip_file *zf;
+ struct zip_stat zs;
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+ int devcnt, ret, i, j;
+ uint64_t tmp_u64, total_channels, enabled_channels, p;
+ char **sections, **keys, *metafile, *val;
+ char channelname[SR_MAX_CHANNELNAME_LEN + 1];
+
+ if ((ret = sr_sessionfile_check(filename)) != SR_OK)
+ return ret;
+
+ if (!(archive = zip_open(filename, 0, &ret)))
+ return SR_ERR;
+
+ if (zip_stat(archive, "metadata", 0, &zs) == -1)
+ return SR_ERR;
+
+ if (!(metafile = g_try_malloc(zs.size))) {
+ sr_err("%s: metafile malloc failed", __func__);
+ return SR_ERR_MALLOC;
+ }
+
+ zf = zip_fopen_index(archive, zs.index, 0);
+ zip_fread(zf, metafile, zs.size);
+ zip_fclose(zf);
+
+ kf = g_key_file_new();
+ if (!g_key_file_load_from_data(kf, metafile, zs.size, 0, NULL)) {
+ sr_dbg("Failed to parse metadata.");
+ return SR_ERR;
+ }
+
+ if ((ret = sr_session_new(session)) != SR_OK)
+ return ret;
+
+ devcnt = 0;
+ capturefiles = g_ptr_array_new_with_free_func(g_free);
+ sections = g_key_file_get_groups(kf, NULL);
+ for (i = 0; sections[i]; i++) {
+ if (!strcmp(sections[i], "global"))
+ /* nothing really interesting in here yet */
+ continue;
+ if (!strncmp(sections[i], "device ", 7)) {
+ /* device section */
+ sdi = NULL;
+ enabled_channels = total_channels = 0;
+ keys = g_key_file_get_keys(kf, sections[i], NULL, NULL);
+ for (j = 0; keys[j]; j++) {
+ val = g_key_file_get_string(kf, sections[i], keys[j], NULL);
+ if (!strcmp(keys[j], "capturefile")) {
+ sdi = sr_dev_inst_new(devcnt, SR_ST_ACTIVE, NULL, NULL, NULL);
+ sdi->driver = &session_driver;
+ if (devcnt == 0)
+ /* first device, init the driver */
+ sdi->driver->init(NULL);
+ sr_dev_open(sdi);
+ sr_session_dev_add(*session, sdi);
+ sdi->driver->config_set(SR_CONF_SESSIONFILE,
+ g_variant_new_string(filename), sdi, NULL);
+ sdi->driver->config_set(SR_CONF_CAPTUREFILE,
+ g_variant_new_string(val), sdi, NULL);
+ g_ptr_array_add(capturefiles, val);
+ } else if (!strcmp(keys[j], "samplerate")) {
+ sr_parse_sizestring(val, &tmp_u64);
+ sdi->driver->config_set(SR_CONF_SAMPLERATE,
+ g_variant_new_uint64(tmp_u64), sdi, NULL);
+ } else if (!strcmp(keys[j], "unitsize")) {
+ tmp_u64 = strtoull(val, NULL, 10);
+ sdi->driver->config_set(SR_CONF_CAPTURE_UNITSIZE,
+ g_variant_new_uint64(tmp_u64), sdi, NULL);
+ } else if (!strcmp(keys[j], "total probes")) {
+ total_channels = strtoull(val, NULL, 10);
+ sdi->driver->config_set(SR_CONF_NUM_LOGIC_CHANNELS,
+ g_variant_new_uint64(total_channels), sdi, NULL);
+ for (p = 0; p < total_channels; p++) {
+ snprintf(channelname, SR_MAX_CHANNELNAME_LEN, "%" PRIu64, p);
+ if (!(ch = sr_channel_new(p, SR_CHANNEL_LOGIC, TRUE,
+ channelname)))
+ return SR_ERR;
+ sdi->channels = g_slist_append(sdi->channels, ch);
+ }
+ } else if (!strncmp(keys[j], "probe", 5)) {
+ if (!sdi)
+ continue;
+ enabled_channels++;
+ tmp_u64 = strtoul(keys[j]+5, NULL, 10);
+ /* sr_session_save() */
+ sr_dev_channel_name_set(sdi, tmp_u64 - 1, val);
+ }
+ }
+ g_strfreev(keys);
+ /* Disable channels not specifically listed. */
+ if (total_channels)
+ for (p = enabled_channels; p < total_channels; p++)
+ sr_dev_channel_enable(sdi, p, FALSE);
+ }
+ devcnt++;
+ }
+ g_strfreev(sections);
+ g_key_file_free(kf);
+
+ return SR_OK;
+}
+
+/**
+ * Save a session to the specified file.
+ *
+ * @param filename The name of the filename to save the session as.
+ * Must not be NULL.
+ * @param sdi The device instance from which the data was captured.
+ * @param buf The data to be saved.
+ * @param unitsize The number of bytes per sample.
+ * @param units The number of samples.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_ARG Invalid arguments
+ * @retval SR_ERR Other errors
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_session_save(struct sr_session *session, const char *filename,
+ const struct sr_dev_inst *sdi, unsigned char *buf, int unitsize,
+ int units)
+{
+ struct sr_channel *ch;
+ GSList *l;
+ GVariant *gvar;
+ uint64_t samplerate;
+ int cnt, ret;
+ char **channel_names;
+
+ samplerate = 0;
+ if (sr_dev_has_option(sdi, SR_CONF_SAMPLERATE)) {
+ if (sr_config_get(sdi->driver, sdi, NULL,
+ SR_CONF_SAMPLERATE, &gvar) == SR_OK) {
+ samplerate = g_variant_get_uint64(gvar);
+ g_variant_unref(gvar);
+ }
+ }
+
+ channel_names = g_malloc0(sizeof(char *) * (g_slist_length(sdi->channels) + 1));
+ cnt = 0;
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ if (ch->enabled != TRUE)
+ continue;
+ if (!ch->name)
+ continue;
+ /* Just borrowing the ptr. */
+ channel_names[cnt++] = ch->name;
+ }
+
+ if ((ret = sr_session_save_init(session, filename, samplerate,
+ channel_names)) != SR_OK)
+ return ret;
+
+ ret = sr_session_append(session, filename, buf, unitsize, units);
+
+ return ret;
+}
+
+/**
+ * Initialize a saved session file.
+ *
+ * @param filename The name of the filename to save the session as.
+ * Must not be NULL.
+ * @param samplerate The samplerate to store for this session.
+ * @param channels A NULL-terminated array of strings containing the names
+ * of all the channels active in this session.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_ARG Invalid arguments
+ * @retval SR_ERR Other errors
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_session_save_init(struct sr_session *session,
+ const char *filename, uint64_t samplerate, char **channels)
+{
+ FILE *meta;
+ struct zip *zipfile;
+ struct zip_source *versrc, *metasrc;
+ int tmpfile, cnt, ret, i;
+ char version[1], metafile[32], *s;
+
+ (void) session;
+
+ if (!filename) {
+ sr_err("%s: filename was NULL", __func__);
+ return SR_ERR_ARG;
+ }
+
+ /* Quietly delete it first, libzip wants replace ops otherwise. */
+ unlink(filename);
+ if (!(zipfile = zip_open(filename, ZIP_CREATE, &ret)))
+ return SR_ERR;
+
+ /* "version" */
+ version[0] = '2';
+ if (!(versrc = zip_source_buffer(zipfile, version, 1, 0)))
+ return SR_ERR;
+ if (zip_add(zipfile, "version", versrc) == -1) {
+ sr_info("error saving version into zipfile: %s",
+ zip_strerror(zipfile));
+ return SR_ERR;
+ }
+
+ /* init "metadata" */
+ strcpy(metafile, "sigrok-meta-XXXXXX");
+ if ((tmpfile = g_mkstemp(metafile)) == -1)
+ return SR_ERR;
+ close(tmpfile);
+ meta = g_fopen(metafile, "wb");
+ fprintf(meta, "[global]\n");
+ fprintf(meta, "sigrok version = %s\n", PACKAGE_VERSION);
+
+ /* metadata */
+ fprintf(meta, "[device 1]\n");
+
+ /* metadata */
+ fprintf(meta, "capturefile = logic-1\n");
+ cnt = 0;
+ for (i = 0; channels[i]; i++)
+ cnt++;
+ fprintf(meta, "total probes = %d\n", cnt);
+ s = sr_samplerate_string(samplerate);
+ fprintf(meta, "samplerate = %s\n", s);
+ g_free(s);
+
+ for (i = 0; channels[i]; i++)
+ fprintf(meta, "probe%d = %s\n", i + 1, channels[i]);
+
+ fclose(meta);
+
+ if (!(metasrc = zip_source_file(zipfile, metafile, 0, -1))) {
+ unlink(metafile);
+ return SR_ERR;
+ }
+ if (zip_add(zipfile, "metadata", metasrc) == -1) {
+ unlink(metafile);
+ return SR_ERR;
+ }
+
+ if ((ret = zip_close(zipfile)) == -1) {
+ sr_info("error saving zipfile: %s", zip_strerror(zipfile));
+ unlink(metafile);
+ return SR_ERR;
+ }
+
+ unlink(metafile);
+
+ return SR_OK;
+}
+
+/**
+ * Append data to an existing session file.
+ *
+ * The session file must have been created with sr_session_save_init()
+ * or sr_session_save() beforehand.
+ *
+ * @param filename The name of the filename to append to. Must not be NULL.
+ * @param buf The data to be appended.
+ * @param unitsize The number of bytes per sample.
+ * @param units The number of samples.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_ARG Invalid arguments
+ * @retval SR_ERR Other errors
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_session_append(struct sr_session *session, const char *filename,
+ unsigned char *buf, int unitsize, int units)
+{
+ struct zip *archive;
+ struct zip_source *logicsrc;
+ zip_int64_t num_files;
+ struct zip_file *zf;
+ struct zip_stat zs;
+ struct zip_source *metasrc;
+ GKeyFile *kf;
+ GError *error;
+ gsize len;
+ int chunk_num, next_chunk_num, tmpfile, ret, i;
+ const char *entry_name;
+ char *metafile, tmpname[32], chunkname[16];
+
+ (void) session;
+
+ if ((ret = sr_sessionfile_check(filename)) != SR_OK)
+ return ret;
+
+ if (!(archive = zip_open(filename, 0, &ret)))
+ return SR_ERR;
+
+ if (zip_stat(archive, "metadata", 0, &zs) == -1)
+ return SR_ERR;
+
+ metafile = g_malloc(zs.size);
+ zf = zip_fopen_index(archive, zs.index, 0);
+ zip_fread(zf, metafile, zs.size);
+ zip_fclose(zf);
+
+ /*
+ * If the file was only initialized but doesn't yet have any
+ * data it in, it won't have a unitsize field in metadata yet.
+ */
+ error = NULL;
+ kf = g_key_file_new();
+ if (!g_key_file_load_from_data(kf, metafile, zs.size, 0, &error)) {
+ sr_err("Failed to parse metadata: %s.", error->message);
+ return SR_ERR;
+ }
+ g_free(metafile);
+ tmpname[0] = '\0';
+ if (!g_key_file_has_key(kf, "device 1", "unitsize", &error)) {
+ if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
+ sr_err("Failed to check unitsize key: %s", error ? error->message : "?");
+ return SR_ERR;
+ }
+ /* Add unitsize field. */
+ g_key_file_set_integer(kf, "device 1", "unitsize", unitsize);
+ metafile = g_key_file_to_data(kf, &len, &error);
+ strcpy(tmpname, "sigrok-meta-XXXXXX");
+ if ((tmpfile = g_mkstemp(tmpname)) == -1)
+ return SR_ERR;
+ if (write(tmpfile, metafile, len) < 0) {
+ sr_dbg("Failed to create new metadata: %s", strerror(errno));
+ g_free(metafile);
+ unlink(tmpname);
+ return SR_ERR;
+ }
+ close(tmpfile);
+ if (!(metasrc = zip_source_file(archive, tmpname, 0, -1))) {
+ sr_err("Failed to create zip source for metadata.");
+ g_free(metafile);
+ unlink(tmpname);
+ return SR_ERR;
+ }
+ if (zip_replace(archive, zs.index, metasrc) == -1) {
+ sr_err("Failed to replace metadata file.");
+ g_free(metafile);
+ unlink(tmpname);
+ return SR_ERR;
+ }
+ g_free(metafile);
+ }
+ g_key_file_free(kf);
+
+ next_chunk_num = 1;
+ num_files = zip_get_num_entries(archive, 0);
+ for (i = 0; i < num_files; i++) {
+ entry_name = zip_get_name(archive, i, 0);
+ if (strncmp(entry_name, "logic-1", 7))
+ continue;
+ if (strlen(entry_name) == 7) {
+ /* This file has no extra chunks, just a single "logic-1".
+ * Rename it to "logic-1-1" * and continue with chunk 2. */
+ if (zip_rename(archive, i, "logic-1-1") == -1) {
+ sr_err("Failed to rename 'logic-1' to 'logic-1-1'.");
+ unlink(tmpname);
+ return SR_ERR;
+ }
+ next_chunk_num = 2;
+ break;
+ } else if (strlen(entry_name) > 8 && entry_name[7] == '-') {
+ chunk_num = strtoull(entry_name + 8, NULL, 10);
+ if (chunk_num >= next_chunk_num)
+ next_chunk_num = chunk_num + 1;
+ }
+ }
+ snprintf(chunkname, 15, "logic-1-%d", next_chunk_num);
+ if (!(logicsrc = zip_source_buffer(archive, buf, units * unitsize, FALSE))) {
+ unlink(tmpname);
+ return SR_ERR;
+ }
+ if (zip_add(archive, chunkname, logicsrc) == -1) {
+ unlink(tmpname);
+ return SR_ERR;
+ }
+ if ((ret = zip_close(archive)) == -1) {
+ sr_info("error saving session file: %s", zip_strerror(archive));
+ unlink(tmpname);
+ return SR_ERR;
+ }
+ unlink(tmpname);
+
+ return SR_OK;
+}
+
+/** @} */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 <string.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/* @cond PRIVATE */
+#define LOG_PREFIX "soft-trigger"
+/* @endcond */
+
+SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
+ const struct sr_dev_inst *sdi, struct sr_trigger *trigger)
+{
+ struct soft_trigger_logic *stl;
+
+ stl = g_malloc0(sizeof(struct soft_trigger_logic));
+ stl->sdi = sdi;
+ stl->trigger = trigger;
+ stl->unitsize = (g_slist_length(sdi->channels) + 7) / 8;
+ stl->prev_sample = g_malloc0(stl->unitsize);
+
+ return stl;
+}
+
+SR_PRIV void soft_trigger_logic_free(struct soft_trigger_logic *stl)
+{
+ g_free(stl->prev_sample);
+ g_free(stl);
+}
+
+static gboolean logic_check_match(struct soft_trigger_logic *stl,
+ uint8_t *sample, struct sr_trigger_match *match)
+{
+ int bit, prev_bit;
+ gboolean result;
+
+ stl->count++;
+ result = FALSE;
+ bit = *(sample + match->channel->index / 8)
+ & (1 << (match->channel->index % 8));
+ if (match->match == SR_TRIGGER_ZERO)
+ result = bit == 0;
+ else if (match->match == SR_TRIGGER_ONE)
+ result = bit != 0;
+ else {
+ /* Edge matches. */
+ if (stl->count == 1)
+ /* First sample, don't have enough for an edge match yet. */
+ return FALSE;
+ prev_bit = *(stl->prev_sample + match->channel->index / 8)
+ & (1 << (match->channel->index % 8));
+ if (match->match == SR_TRIGGER_RISING)
+ result = prev_bit == 0 && bit != 0;
+ else if (match->match == SR_TRIGGER_FALLING)
+ result = prev_bit != 0 && bit == 0;
+ else if (match->match == SR_TRIGGER_EDGE)
+ result = prev_bit != bit;
+ }
+
+ return result;
+}
+
+/* Returns the offset (in samples) within buf of where the trigger
+ * occurred, or -1 if not triggered. */
+SR_PRIV int soft_trigger_logic_check(struct soft_trigger_logic *stl,
+ uint8_t *buf, int len)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_trigger_stage *stage;
+ struct sr_trigger_match *match;
+ GSList *l, *l_stage;
+ int offset;
+ int i;
+ gboolean match_found;
+
+ offset = -1;
+ for (i = 0; i < len; i += stl->unitsize) {
+ l_stage = g_slist_nth(stl->trigger->stages, stl->cur_stage);
+ stage = l_stage->data;
+ if (!stage->matches)
+ /* No matches supplied, client error. */
+ return SR_ERR_ARG;
+
+ match_found = TRUE;
+ for (l = stage->matches; l; l = l->next) {
+ match = l->data;
+ if (!match->channel->enabled)
+ /* Ignore disabled channels with a trigger. */
+ continue;
+ if (!logic_check_match(stl, buf + i, match)) {
+ match_found = FALSE;
+ break;
+ }
+ }
+ memcpy(stl->prev_sample, buf + i, stl->unitsize);
+ if (match_found) {
+ /* Matched on the current stage. */
+ if (l_stage->next) {
+ /* Advance to next stage. */
+ stl->cur_stage++;
+ } else {
+ /* Matched on last stage, fire trigger. */
+ offset = i / stl->unitsize;
+
+ packet.type = SR_DF_TRIGGER;
+ packet.payload = NULL;
+ sr_session_send(stl->sdi, &packet);
+ break;
+ }
+ } else if (stl->cur_stage > 0) {
+ /*
+ * We had a match at an earlier stage, but failed on the
+ * current stage. However, we may have a match on this
+ * stage in the next bit -- trigger on 0001 will fail on
+ * seeing 00001, so we need to go back to stage 0 -- but
+ * at the next sample from the one that matched originally,
+ * which the counter increment at the end of the loop
+ * takes care of.
+ */
+ i -= stl->cur_stage * stl->unitsize;
+ if (i < -1)
+ i = -1; /* Oops, went back past this buffer. */
+ /* Reset trigger stage. */
+ stl->cur_stage = 0;
+ }
+ }
+
+ return offset;
+}
+
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/** @file
+ * Standard API helper functions.
+ * @internal
+ */
+
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "std"
+
+/**
+ * Standard sr_driver_init() API helper.
+ *
+ * This function can be used to simplify most driver's init() API callback.
+ *
+ * It creates a new 'struct drv_context' (drvc), assigns sr_ctx to it, and
+ * then 'drvc' is assigned to the 'struct sr_dev_driver' (di) that is passed.
+ *
+ * @param sr_ctx The libsigrok context to assign.
+ * @param di The driver instance to use.
+ * @param[in] prefix A driver-specific prefix string used for log messages.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
+ * SR_ERR_MALLOC upon memory allocation errors.
+ */
+SR_PRIV int std_init(struct sr_context *sr_ctx, struct sr_dev_driver *di,
+ const char *prefix)
+{
+ struct drv_context *drvc;
+
+ if (!di) {
+ sr_err("%s: Invalid driver, cannot initialize.", prefix);
+ return SR_ERR_ARG;
+ }
+
+ if (!(drvc = g_try_malloc(sizeof(struct drv_context)))) {
+ sr_err("%s: Driver context malloc failed.", prefix);
+ return SR_ERR_MALLOC;
+ }
+
+ drvc->sr_ctx = sr_ctx;
+ drvc->instances = NULL;
+ di->priv = drvc;
+
+ return SR_OK;
+}
+
+/**
+ * Standard API helper for sending an SR_DF_HEADER packet.
+ *
+ * This function can be used to simplify most driver's
+ * dev_acquisition_start() API callback.
+ *
+ * @param sdi The device instance to use.
+ * @param prefix A driver-specific prefix string used for log messages.
+ * Must not be NULL. An empty string is allowed.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
+ * SR_ERR upon other errors.
+ */
+SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi,
+ const char *prefix)
+{
+ int ret;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_header header;
+
+ if (!prefix) {
+ sr_err("Invalid prefix.");
+ return SR_ERR_ARG;
+ }
+
+ sr_dbg("%s: Starting acquisition.", prefix);
+
+ /* Send header packet to the session bus. */
+ sr_dbg("%s: Sending SR_DF_HEADER packet.", prefix);
+ packet.type = SR_DF_HEADER;
+ packet.payload = (uint8_t *)&header;
+ header.feed_version = 1;
+ gettimeofday(&header.starttime, NULL);
+
+ if ((ret = sr_session_send(sdi, &packet)) < 0) {
+ sr_err("%s: Failed to send header packet: %d.", prefix, ret);
+ return ret;
+ }
+
+ return SR_OK;
+}
+
+#ifdef HAVE_LIBSERIALPORT
+
+/**
+ * Standard serial driver dev_open() helper.
+ *
+ * This function can be used to implement the dev_open() driver API
+ * callback in drivers that use a serial port. The port is opened
+ * with the SERIAL_RDWR and SERIAL_NONBLOCK flags.
+ *
+ * If the open succeeded, the status field of the given sdi is set
+ * to SR_ST_ACTIVE.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Serial port open failed.
+ */
+SR_PRIV int std_serial_dev_open(struct sr_dev_inst *sdi)
+{
+ struct sr_serial_dev_inst *serial;
+
+ serial = sdi->conn;
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return SR_ERR;
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+/**
+ * Standard serial driver dev_close() helper.
+ *
+ * This function can be used to implement the dev_close() driver API
+ * callback in drivers that use a serial port.
+ *
+ * After closing the port, the status field of the given sdi is set
+ * to SR_ST_INACTIVE.
+ *
+ * @retval SR_OK Success.
+ */
+SR_PRIV int std_serial_dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_serial_dev_inst *serial;
+
+ serial = sdi->conn;
+ if (serial && sdi->status == SR_ST_ACTIVE) {
+ serial_close(serial);
+ sdi->status = SR_ST_INACTIVE;
+ }
+
+ return SR_OK;
+}
+
+/**
+ * Standard sr_session_stop() API helper.
+ *
+ * This function can be used to simplify most (serial port based) driver's
+ * dev_acquisition_stop() API callback.
+ *
+ * @param sdi The device instance for which acquisition should stop.
+ * Must not be NULL.
+ * @param cb_data Opaque 'cb_data' pointer. Must not be NULL.
+ * @param dev_close_fn Function pointer to the driver's dev_close().
+ * Must not be NULL.
+ * @param serial The serial device instance (struct serial_dev_inst *).
+ * Must not be NULL.
+ * @param[in] prefix A driver-specific prefix string used for log messages.
+ * Must not be NULL. An empty string is allowed.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid arguments.
+ * @retval SR_ERR_DEV_CLOSED Device is closed.
+ * @retval SR_ERR Other errors.
+ */
+SR_PRIV int std_serial_dev_acquisition_stop(struct sr_dev_inst *sdi,
+ void *cb_data, dev_close_callback dev_close_fn,
+ struct sr_serial_dev_inst *serial, const char *prefix)
+{
+ int ret;
+ struct sr_datafeed_packet packet;
+
+ if (!prefix) {
+ sr_err("Invalid prefix.");
+ return SR_ERR_ARG;
+ }
+
+ if (sdi->status != SR_ST_ACTIVE) {
+ sr_err("%s: Device inactive, can't stop acquisition.", prefix);
+ return SR_ERR_DEV_CLOSED;
+ }
+
+ sr_dbg("%s: Stopping acquisition.", prefix);
+
+ if ((ret = serial_source_remove(sdi->session, serial)) < 0) {
+ sr_err("%s: Failed to remove source: %d.", prefix, ret);
+ return ret;
+ }
+
+ if ((ret = dev_close_fn(sdi)) < 0) {
+ sr_err("%s: Failed to close device: %d.", prefix, ret);
+ return ret;
+ }
+
+ /* Send SR_DF_END packet to the session bus. */
+ sr_dbg("%s: Sending SR_DF_END packet.", prefix);
+ packet.type = SR_DF_END;
+ packet.payload = NULL;
+ if ((ret = sr_session_send(cb_data, &packet)) < 0) {
+ sr_err("%s: Failed to send SR_DF_END packet: %d.", prefix, ret);
+ return ret;
+ }
+
+ return SR_OK;
+}
+
+#endif
+
+/**
+ * Standard driver dev_clear() helper.
+ *
+ * Clear driver, this means, close all instances.
+ *
+ * This function can be used to implement the dev_clear() driver API
+ * callback. dev_close() is called before every sr_dev_inst is cleared.
+ *
+ * The only limitation is driver-specific device contexts (sdi->priv).
+ * These are freed, but any dynamic allocation within structs stored
+ * there cannot be freed.
+ *
+ * @param driver The driver which will have its instances released.
+ * @param clear_private If not NULL, this points to a function called
+ * with sdi->priv as argument. The function can then clear any device
+ * instance-specific resources kept there. It must also clear the struct
+ * pointed to by sdi->priv.
+ *
+ * @return SR_OK on success.
+ */
+SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
+ std_dev_clear_callback clear_private)
+{
+ struct drv_context *drvc;
+ struct sr_dev_inst *sdi;
+ GSList *l;
+ int ret;
+
+ if (!(drvc = driver->priv))
+ /* Driver was never initialized, nothing to do. */
+ return SR_OK;
+
+ ret = SR_OK;
+ for (l = drvc->instances; l; l = l->next) {
+ if (!(sdi = l->data)) {
+ ret = SR_ERR_BUG;
+ continue;
+ }
+ if (driver->dev_close)
+ driver->dev_close(sdi);
+
+ if (sdi->conn) {
+#ifdef HAVE_LIBSERIALPORT
+ if (sdi->inst_type == SR_INST_SERIAL)
+ sr_serial_dev_inst_free(sdi->conn);
+#endif
+#ifdef HAVE_LIBUSB_1_0
+ if (sdi->inst_type == SR_INST_USB)
+ sr_usb_dev_inst_free(sdi->conn);
+#endif
+ if (sdi->inst_type == SR_INST_SCPI)
+ sr_scpi_free(sdi->conn);
+ }
+ if (clear_private)
+ clear_private(sdi->priv);
+ else
+ g_free(sdi->priv);
+ sr_dev_inst_free(sdi);
+ }
+
+ g_slist_free(drvc->instances);
+ drvc->instances = NULL;
+
+ return ret;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "strutil"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Helper functions for handling or converting libsigrok-related strings.
+ */
+
+/**
+ * @defgroup grp_strutil String utilities
+ *
+ * Helper functions for handling or converting libsigrok-related strings.
+ *
+ * @{
+ */
+
+/**
+ * @private
+ *
+ * Convert a string representation of a numeric value (base 10) to a long integer. The
+ * conversion is strict and will fail if the complete string does not represent
+ * a valid long integer. The function sets errno according to the details of the
+ * failure.
+ *
+ * @param str The string representation to convert.
+ * @param ret Pointer to long where the result of the conversion will be stored.
+ *
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
+ *
+ * @since 0.3.0
+ */
+SR_PRIV int sr_atol(const char *str, long *ret)
+{
+ long tmp;
+ char *endptr = NULL;
+
+ errno = 0;
+ tmp = strtol(str, &endptr, 10);
+
+ if (!endptr || *endptr || errno) {
+ if (!errno)
+ errno = EINVAL;
+ return SR_ERR;
+ }
+
+ *ret = tmp;
+ return SR_OK;
+}
+
+/**
+ * @private
+ *
+ * Convert a string representation of a numeric value (base 10) to an integer. The
+ * conversion is strict and will fail if the complete string does not represent
+ * a valid integer. The function sets errno according to the details of the
+ * failure.
+ *
+ * @param str The string representation to convert.
+ * @param ret Pointer to int where the result of the conversion will be stored.
+ *
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
+ *
+ * @since 0.3.0
+ */
+SR_PRIV int sr_atoi(const char *str, int *ret)
+{
+ long tmp;
+
+ if (sr_atol(str, &tmp) != SR_OK)
+ return SR_ERR;
+
+ if ((int) tmp != tmp) {
+ errno = ERANGE;
+ return SR_ERR;
+ }
+
+ *ret = (int) tmp;
+ return SR_OK;
+}
+
+/**
+ * @private
+ *
+ * Convert a string representation of a numeric value to a double. The
+ * conversion is strict and will fail if the complete string does not represent
+ * a valid double. The function sets errno according to the details of the
+ * failure.
+ *
+ * @param str The string representation to convert.
+ * @param ret Pointer to double where the result of the conversion will be stored.
+ *
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
+ *
+ * @since 0.3.0
+ */
+SR_PRIV int sr_atod(const char *str, double *ret)
+{
+ double tmp;
+ char *endptr = NULL;
+
+ errno = 0;
+ tmp = strtof(str, &endptr);
+
+ if (!endptr || *endptr || errno) {
+ if (!errno)
+ errno = EINVAL;
+ return SR_ERR;
+ }
+
+ *ret = tmp;
+ return SR_OK;
+}
+
+/**
+ * @private
+ *
+ * Convert a string representation of a numeric value to a float. The
+ * conversion is strict and will fail if the complete string does not represent
+ * a valid float. The function sets errno according to the details of the
+ * failure.
+ *
+ * @param str The string representation to convert.
+ * @param ret Pointer to float where the result of the conversion will be stored.
+ *
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
+ *
+ * @since 0.3.0
+ */
+SR_PRIV int sr_atof(const char *str, float *ret)
+{
+ double tmp;
+
+ if (sr_atod(str, &tmp) != SR_OK)
+ return SR_ERR;
+
+ if ((float) tmp != tmp) {
+ errno = ERANGE;
+ return SR_ERR;
+ }
+
+ *ret = (float) tmp;
+ return SR_OK;
+}
+
+/**
+ * @private
+ *
+ * Convert a string representation of a numeric value to a float. The
+ * conversion is strict and will fail if the complete string does not represent
+ * a valid float. The function sets errno according to the details of the
+ * failure. This version ignores the locale.
+ *
+ * @param str The string representation to convert.
+ * @param ret Pointer to float where the result of the conversion will be stored.
+ *
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
+ *
+ * @since 0.3.0
+ */
+SR_PRIV int sr_atof_ascii(const char *str, float *ret)
+{
+ double tmp;
+ char *endptr = NULL;
+
+ errno = 0;
+ tmp = g_ascii_strtod(str, &endptr);
+
+ if (!endptr || *endptr || errno) {
+ if (!errno)
+ errno = EINVAL;
+ return SR_ERR;
+ }
+
+ /* FIXME This fails unexpectedly. Some other method to safel downcast
+ * needs to be found. Checking against FLT_MAX doesn't work as well. */
+ /*
+ if ((float) tmp != tmp) {
+ errno = ERANGE;
+ sr_dbg("ERANGEEEE %e != %e", (float) tmp, tmp);
+ return SR_ERR;
+ }
+ */
+
+ *ret = (float) tmp;
+ return SR_OK;
+}
+
+/**
+ * Convert a numeric value value to its "natural" string representation
+ * in SI units.
+ *
+ * E.g. a value of 3000000, with units set to "W", would be converted
+ * to "3 MW", 20000 to "20 kW", 31500 would become "31.5 kW".
+ *
+ * @param x The value to convert.
+ * @param unit The unit to append to the string, or NULL if the string
+ * has no units.
+ *
+ * @return A g_try_malloc()ed string representation of the samplerate value,
+ * or NULL upon errors. The caller is responsible to g_free() the
+ * memory.
+ *
+ * @since 0.2.0
+ */
+SR_API char *sr_si_string_u64(uint64_t x, const char *unit)
+{
+ uint8_t i;
+ uint64_t quot, divisor[] = {
+ SR_HZ(1), SR_KHZ(1), SR_MHZ(1), SR_GHZ(1),
+ SR_GHZ(1000), SR_GHZ(1000 * 1000), SR_GHZ(1000 * 1000 * 1000),
+ };
+ const char *p, prefix[] = "\0kMGTPE";
+ char fmt[16], fract[20] = "", *f;
+
+ if (unit == NULL)
+ unit = "";
+
+ for (i = 0; (quot = x / divisor[i]) >= 1000; i++);
+
+ if (i) {
+ sprintf(fmt, ".%%0%d"PRIu64, i * 3);
+ f = fract + sprintf(fract, fmt, x % divisor[i]) - 1;
+
+ while (f >= fract && strchr("0.", *f))
+ *f-- = 0;
+ }
+
+ p = prefix + i;
+
+ return g_strdup_printf("%" PRIu64 "%s %.1s%s", quot, fract, p, unit);
+}
+
+/**
+ * Convert a numeric samplerate value to its "natural" string representation.
+ *
+ * E.g. a value of 3000000 would be converted to "3 MHz", 20000 to "20 kHz",
+ * 31500 would become "31.5 kHz".
+ *
+ * @param samplerate The samplerate in Hz.
+ *
+ * @return A g_try_malloc()ed string representation of the samplerate value,
+ * or NULL upon errors. The caller is responsible to g_free() the
+ * memory.
+ *
+ * @since 0.1.0
+ */
+SR_API char *sr_samplerate_string(uint64_t samplerate)
+{
+ return sr_si_string_u64(samplerate, "Hz");
+}
+
+/**
+ * Convert a numeric frequency value to the "natural" string representation
+ * of its period.
+ *
+ * E.g. a value of 3000000 would be converted to "3 us", 20000 to "50 ms".
+ *
+ * @param frequency The frequency in Hz.
+ *
+ * @return A g_try_malloc()ed string representation of the frequency value,
+ * or NULL upon errors. The caller is responsible to g_free() the
+ * memory.
+ *
+ * @since 0.1.0
+ */
+SR_API char *sr_period_string(uint64_t frequency)
+{
+ char *o;
+ int r;
+
+ /* Allocate enough for a uint64_t as string + " ms". */
+ if (!(o = g_try_malloc0(30 + 1))) {
+ sr_err("%s: o malloc failed", __func__);
+ return NULL;
+ }
+
+ if (frequency >= SR_GHZ(1))
+ r = snprintf(o, 30, "%" PRIu64 " ns", frequency / 1000000000);
+ else if (frequency >= SR_MHZ(1))
+ r = snprintf(o, 30, "%" PRIu64 " us", frequency / 1000000);
+ else if (frequency >= SR_KHZ(1))
+ r = snprintf(o, 30, "%" PRIu64 " ms", frequency / 1000);
+ else
+ r = snprintf(o, 30, "%" PRIu64 " s", frequency);
+
+ if (r < 0) {
+ /* Something went wrong... */
+ g_free(o);
+ return NULL;
+ }
+
+ return o;
+}
+
+/**
+ * Convert a numeric voltage value to the "natural" string representation
+ * of its voltage value. The voltage is specified as a rational number's
+ * numerator and denominator.
+ *
+ * E.g. a value of 300000 would be converted to "300mV", 2 to "2V".
+ *
+ * @param v_p The voltage numerator.
+ * @param v_q The voltage denominator.
+ *
+ * @return A g_try_malloc()ed string representation of the voltage value,
+ * or NULL upon errors. The caller is responsible to g_free() the
+ * memory.
+ *
+ * @since 0.2.0
+ */
+SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
+{
+ int r;
+ char *o;
+
+ if (!(o = g_try_malloc0(30 + 1))) {
+ sr_err("%s: o malloc failed", __func__);
+ return NULL;
+ }
+
+ if (v_q == 1000)
+ r = snprintf(o, 30, "%" PRIu64 "mV", v_p);
+ else if (v_q == 1)
+ r = snprintf(o, 30, "%" PRIu64 "V", v_p);
+ else
+ r = snprintf(o, 30, "%gV", (float)v_p / (float)v_q);
+
+ if (r < 0) {
+ /* Something went wrong... */
+ g_free(o);
+ return NULL;
+ }
+
+ return o;
+}
+
+/**
+ * Convert a "natural" string representation of a size value to uint64_t.
+ *
+ * E.g. a value of "3k" or "3 K" would be converted to 3000, a value
+ * of "15M" would be converted to 15000000.
+ *
+ * Value representations other than decimal (such as hex or octal) are not
+ * supported. Only 'k' (kilo), 'm' (mega), 'g' (giga) suffixes are supported.
+ * Spaces (but not other whitespace) between value and suffix are allowed.
+ *
+ * @param sizestring A string containing a (decimal) size value.
+ * @param size Pointer to uint64_t which will contain the string's size value.
+ *
+ * @return SR_OK upon success, SR_ERR upon errors.
+ *
+ * @since 0.1.0
+ */
+SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
+{
+ int multiplier, done;
+ double frac_part;
+ char *s;
+
+ *size = strtoull(sizestring, &s, 10);
+ multiplier = 0;
+ frac_part = 0;
+ done = FALSE;
+ while (s && *s && multiplier == 0 && !done) {
+ switch (*s) {
+ case ' ':
+ break;
+ case '.':
+ frac_part = g_ascii_strtod(s, &s);
+ break;
+ case 'k':
+ case 'K':
+ multiplier = SR_KHZ(1);
+ break;
+ case 'm':
+ case 'M':
+ multiplier = SR_MHZ(1);
+ break;
+ case 'g':
+ case 'G':
+ multiplier = SR_GHZ(1);
+ break;
+ default:
+ done = TRUE;
+ s--;
+ }
+ s++;
+ }
+ if (multiplier > 0) {
+ *size *= multiplier;
+ *size += frac_part * multiplier;
+ } else
+ *size += frac_part;
+
+ if (*s && strcasecmp(s, "Hz"))
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+/**
+ * Convert a "natural" string representation of a time value to an
+ * uint64_t value in milliseconds.
+ *
+ * E.g. a value of "3s" or "3 s" would be converted to 3000, a value
+ * of "15ms" would be converted to 15.
+ *
+ * Value representations other than decimal (such as hex or octal) are not
+ * supported. Only lower-case "s" and "ms" time suffixes are supported.
+ * Spaces (but not other whitespace) between value and suffix are allowed.
+ *
+ * @param timestring A string containing a (decimal) time value.
+ * @return The string's time value as uint64_t, in milliseconds.
+ *
+ * @todo Add support for "m" (minutes) and others.
+ * @todo Add support for picoseconds?
+ * @todo Allow both lower-case and upper-case? If no, document it.
+ *
+ * @since 0.1.0
+ */
+SR_API uint64_t sr_parse_timestring(const char *timestring)
+{
+ uint64_t time_msec;
+ char *s;
+
+ /* TODO: Error handling, logging. */
+
+ time_msec = strtoull(timestring, &s, 10);
+ if (time_msec == 0 && s == timestring)
+ return 0;
+
+ if (s && *s) {
+ while (*s == ' ')
+ s++;
+ if (!strcmp(s, "s"))
+ time_msec *= 1000;
+ else if (!strcmp(s, "ms"))
+ ; /* redundant */
+ else
+ return 0;
+ }
+
+ return time_msec;
+}
+
+/** @since 0.1.0 */
+SR_API gboolean sr_parse_boolstring(const char *boolstr)
+{
+ if (!boolstr)
+ return FALSE;
+
+ if (!g_ascii_strncasecmp(boolstr, "true", 4) ||
+ !g_ascii_strncasecmp(boolstr, "yes", 3) ||
+ !g_ascii_strncasecmp(boolstr, "on", 2) ||
+ !g_ascii_strncasecmp(boolstr, "1", 1))
+ return TRUE;
+
+ return FALSE;
+}
+
+/** @since 0.2.0 */
+SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q)
+{
+ char *s;
+
+ *p = strtoull(periodstr, &s, 10);
+ if (*p == 0 && s == periodstr)
+ /* No digits found. */
+ return SR_ERR_ARG;
+
+ if (s && *s) {
+ while (*s == ' ')
+ s++;
+ if (!strcmp(s, "fs"))
+ *q = 1000000000000000ULL;
+ else if (!strcmp(s, "ps"))
+ *q = 1000000000000ULL;
+ else if (!strcmp(s, "ns"))
+ *q = 1000000000ULL;
+ else if (!strcmp(s, "us"))
+ *q = 1000000;
+ else if (!strcmp(s, "ms"))
+ *q = 1000;
+ else if (!strcmp(s, "s"))
+ *q = 1;
+ else
+ /* Must have a time suffix. */
+ return SR_ERR_ARG;
+ }
+
+ return SR_OK;
+}
+
+/** @since 0.2.0 */
+SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q)
+{
+ char *s;
+
+ *p = strtoull(voltstr, &s, 10);
+ if (*p == 0 && s == voltstr)
+ /* No digits found. */
+ return SR_ERR_ARG;
+
+ if (s && *s) {
+ while (*s == ' ')
+ s++;
+ if (!strcasecmp(s, "mv"))
+ *q = 1000L;
+ else if (!strcasecmp(s, "v"))
+ *q = 1;
+ else
+ /* Must have a base suffix. */
+ return SR_ERR_ARG;
+ }
+
+ return SR_OK;
+}
+
+/** @} */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ *
+ * 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.h"
+#include "libsigrok-internal.h"
+
+/* * @cond PRIVATE */
+#define LOG_PREFIX "trigger"
+/* * @endcond */
+
+SR_API struct sr_trigger *sr_trigger_new(const char *name)
+{
+ struct sr_trigger *trig;
+
+ trig = g_malloc0(sizeof(struct sr_trigger));
+ if (name)
+ trig->name = g_strdup(name);
+
+ return trig;
+}
+
+SR_API void sr_trigger_free(struct sr_trigger *trig)
+{
+ struct sr_trigger_stage *stage;
+ GSList *l;
+
+ for (l = trig->stages; l; l = l->next) {
+ stage = l->data;
+ g_slist_free_full(stage->matches, g_free);
+ }
+ g_slist_free_full(trig->stages, g_free);
+
+ g_free(trig->name);
+ g_free(trig);
+}
+
+SR_API struct sr_trigger_stage *sr_trigger_stage_add(struct sr_trigger *trig)
+{
+ struct sr_trigger_stage *stage;
+
+ stage = g_malloc0(sizeof(struct sr_trigger_stage));
+ stage->stage = g_slist_length(trig->stages);
+ trig->stages = g_slist_append(trig->stages, stage);
+
+ return stage;
+}
+
+SR_API int sr_trigger_match_add(struct sr_trigger_stage *stage,
+ struct sr_channel *ch, int trigger_match, float value)
+{
+ struct sr_trigger_match *match;
+
+ if (ch->type == SR_CHANNEL_LOGIC) {
+ if (trigger_match != SR_TRIGGER_ZERO &&
+ trigger_match != SR_TRIGGER_ONE &&
+ trigger_match != SR_TRIGGER_RISING &&
+ trigger_match != SR_TRIGGER_FALLING &&
+ trigger_match != SR_TRIGGER_EDGE) {
+ sr_err("Invalid trigger match for a logic channel.");
+ return SR_ERR_ARG;
+ }
+
+
+ } else if (ch->type == SR_CHANNEL_ANALOG) {
+ if (trigger_match != SR_TRIGGER_FALLING &&
+ trigger_match != SR_TRIGGER_OVER &&
+ trigger_match != SR_TRIGGER_UNDER) {
+ sr_err("Invalid trigger match for an analog channel.");
+ return SR_ERR_ARG;
+ }
+ }
+
+ match = g_malloc0(sizeof(struct sr_trigger_match));
+ match->channel = ch;
+ match->match = trigger_match;
+ match->value = value;
+ stage->matches = g_slist_append(stage->matches, match);
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <glib.h>
+#include <libusb.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/* SR_CONF_CONN takes one of these: */
+#define CONN_USB_VIDPID "^([0-9a-z]{4})\\.([0-9a-z]{4})$"
+#define CONN_USB_BUSADDR "^(\\d+)\\.(\\d+)$"
+
+#define LOG_PREFIX "usb"
+
+/**
+ * Find USB devices according to a connection string.
+ *
+ * @param usb_ctx libusb context to use while scanning.
+ * @param conn Connection string specifying the device(s) to match. This
+ * can be of the form "<bus>.<address>", or "<vendorid>.<productid>".
+ *
+ * @return A GSList of struct sr_usb_dev_inst, with bus and address fields
+ * matching the device that matched the connection string. The GSList and
+ * its contents must be freed by the caller.
+ */
+SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn)
+{
+ struct sr_usb_dev_inst *usb;
+ struct libusb_device **devlist;
+ struct libusb_device_descriptor des;
+ GSList *devices;
+ GRegex *reg;
+ GMatchInfo *match;
+ int vid, pid, bus, addr, b, a, ret, i;
+ char *mstr;
+
+ vid = pid = bus = addr = 0;
+ reg = g_regex_new(CONN_USB_VIDPID, 0, 0, NULL);
+ if (g_regex_match(reg, conn, 0, &match)) {
+ if ((mstr = g_match_info_fetch(match, 1)))
+ vid = strtoul(mstr, NULL, 16);
+ g_free(mstr);
+
+ if ((mstr = g_match_info_fetch(match, 2)))
+ pid = strtoul(mstr, NULL, 16);
+ g_free(mstr);
+ sr_dbg("Trying to find USB device with VID:PID = %04x:%04x.",
+ vid, pid);
+ } else {
+ g_match_info_unref(match);
+ g_regex_unref(reg);
+ reg = g_regex_new(CONN_USB_BUSADDR, 0, 0, NULL);
+ if (g_regex_match(reg, conn, 0, &match)) {
+ if ((mstr = g_match_info_fetch(match, 1)))
+ bus = strtoul(mstr, NULL, 10);
+ g_free(mstr);
+
+ if ((mstr = g_match_info_fetch(match, 2)))
+ addr = strtoul(mstr, NULL, 10);
+ g_free(mstr);
+ sr_dbg("Trying to find USB device with bus.address = "
+ "%d.%d.", bus, addr);
+ }
+ }
+ g_match_info_unref(match);
+ g_regex_unref(reg);
+
+ if (vid + pid + bus + addr == 0) {
+ sr_err("Neither VID:PID nor bus.address was specified.");
+ return NULL;
+ }
+
+ if (bus > 64) {
+ sr_err("Invalid bus specified: %d.", bus);
+ return NULL;
+ }
+
+ if (addr > 127) {
+ sr_err("Invalid address specified: %d.", addr);
+ return NULL;
+ }
+
+ /* Looks like a valid USB device specification, but is it connected? */
+ devices = NULL;
+ libusb_get_device_list(usb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
+ sr_err("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ if (vid + pid && (des.idVendor != vid || des.idProduct != pid))
+ continue;
+
+ b = libusb_get_bus_number(devlist[i]);
+ a = libusb_get_device_address(devlist[i]);
+ if (bus + addr && (b != bus || a != addr))
+ continue;
+
+ sr_dbg("Found USB device (VID:PID = %04x:%04x, bus.address = "
+ "%d.%d).", des.idVendor, des.idProduct, b, a);
+
+ usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+ libusb_get_device_address(devlist[i]), NULL);
+ devices = g_slist_append(devices, usb);
+ }
+ libusb_free_device_list(devlist, 1);
+
+ sr_dbg("Found %d device(s).", g_slist_length(devices));
+
+ return devices;
+}
+
+SR_PRIV int sr_usb_open(libusb_context *usb_ctx, struct sr_usb_dev_inst *usb)
+{
+ struct libusb_device **devlist;
+ struct libusb_device_descriptor des;
+ int ret, r, cnt, i, a, b;
+
+ sr_dbg("Trying to open USB device %d.%d.", usb->bus, usb->address);
+
+ if ((cnt = libusb_get_device_list(usb_ctx, &devlist)) < 0) {
+ sr_err("Failed to retrieve device list: %s.",
+ libusb_error_name(cnt));
+ return SR_ERR;
+ }
+
+ ret = SR_ERR;
+ for (i = 0; i < cnt; i++) {
+ if ((r = libusb_get_device_descriptor(devlist[i], &des)) < 0) {
+ sr_err("Failed to get device descriptor: %s.",
+ libusb_error_name(r));
+ continue;
+ }
+
+ b = libusb_get_bus_number(devlist[i]);
+ a = libusb_get_device_address(devlist[i]);
+ if (b != usb->bus || a != usb->address)
+ continue;
+
+ if ((r = libusb_open(devlist[i], &usb->devhdl)) < 0) {
+ sr_err("Failed to open device: %s.",
+ libusb_error_name(r));
+ break;
+ }
+
+ sr_dbg("Opened USB device (VID:PID = %04x:%04x, bus.address = "
+ "%d.%d).", des.idVendor, des.idProduct, b, a);
+
+ ret = SR_OK;
+ break;
+ }
+
+ libusb_free_device_list(devlist, 1);
+
+ return ret;
+}
+
+#ifdef _WIN32
+static gpointer usb_thread(gpointer data)
+{
+ struct sr_context *ctx = data;
+
+ while (ctx->usb_thread_running) {
+ g_mutex_lock(&ctx->usb_mutex);
+ libusb_wait_for_event(ctx->libusb_ctx, NULL);
+ SetEvent(ctx->usb_event);
+ g_mutex_unlock(&ctx->usb_mutex);
+ g_thread_yield();
+ }
+
+ return NULL;
+}
+
+static int usb_callback(int fd, int revents, void *cb_data)
+{
+ struct sr_context *ctx = cb_data;
+ int ret;
+
+ g_mutex_lock(&ctx->usb_mutex);
+ ret = ctx->usb_cb(fd, revents, ctx->usb_cb_data);
+
+ if (ctx->usb_thread_running) {
+ ResetEvent(ctx->usb_event);
+ g_mutex_unlock(&ctx->usb_mutex);
+ }
+
+ return ret;
+}
+#endif
+
+SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx,
+ int timeout, sr_receive_data_callback cb, void *cb_data)
+{
+ if (ctx->usb_source_present) {
+ sr_err("A USB event source is already present.");
+ return SR_ERR;
+ }
+
+#ifdef _WIN32
+ ctx->usb_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ g_mutex_init(&ctx->usb_mutex);
+ ctx->usb_thread_running = TRUE;
+ ctx->usb_thread = g_thread_new("usb", usb_thread, ctx);
+ ctx->usb_pollfd.fd = ctx->usb_event;
+ ctx->usb_pollfd.events = G_IO_IN;
+ ctx->usb_cb = cb;
+ ctx->usb_cb_data = cb_data;
+ sr_session_source_add_pollfd(session, &ctx->usb_pollfd, timeout,
+ usb_callback, ctx);
+#else
+ const struct libusb_pollfd **lupfd;
+ unsigned int i;
+
+ lupfd = libusb_get_pollfds(ctx->libusb_ctx);
+ for (i = 0; lupfd[i]; i++)
+ sr_session_source_add(session, lupfd[i]->fd, lupfd[i]->events,
+ timeout, cb, cb_data);
+ free(lupfd);
+#endif
+ ctx->usb_source_present = TRUE;
+
+ return SR_OK;
+}
+
+SR_PRIV int usb_source_remove(struct sr_session *session, struct sr_context *ctx)
+{
+ if (!ctx->usb_source_present)
+ return SR_OK;
+
+#ifdef _WIN32
+ ctx->usb_thread_running = FALSE;
+ g_mutex_unlock(&ctx->usb_mutex);
+ libusb_unlock_events(ctx->libusb_ctx);
+ g_thread_join(ctx->usb_thread);
+ g_mutex_clear(&ctx->usb_mutex);
+ sr_session_source_remove_pollfd(session, &ctx->usb_pollfd);
+ CloseHandle(ctx->usb_event);
+#else
+ const struct libusb_pollfd **lupfd;
+ unsigned int i;
+
+ lupfd = libusb_get_pollfds(ctx->libusb_ctx);
+ for (i = 0; lupfd[i]; i++)
+ sr_session_source_remove(session, lupfd[i]->fd);
+ free(lupfd);
+#endif
+ ctx->usb_source_present = FALSE;
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libsigrok.h"
+
+/**
+ * @file
+ *
+ * Version number querying functions, definitions, and macros.
+ */
+
+/**
+ * @defgroup grp_versions Versions
+ *
+ * Version number querying functions, definitions, and macros.
+ *
+ * This set of API calls returns two different version numbers related
+ * to libsigrok. The "package version" is the release version number of the
+ * libsigrok tarball in the usual "major.minor.micro" format, e.g. "0.1.0".
+ *
+ * The "library version" is independent of that; it is the libtool version
+ * number in the "current:revision:age" format, e.g. "2:0:0".
+ * See http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning for details.
+ *
+ * Both version numbers (and/or individual components of them) can be
+ * retrieved via the API calls at runtime, and/or they can be checked at
+ * compile/preprocessor time using the respective macros.
+ *
+ * @{
+ */
+
+/**
+ * Get the major libsigrok package version number.
+ *
+ * @return The major package version number.
+ *
+ * @since 0.1.0
+ */
+SR_API int sr_package_version_major_get(void)
+{
+ return SR_PACKAGE_VERSION_MAJOR;
+}
+
+/**
+ * Get the minor libsigrok package version number.
+ *
+ * @return The minor package version number.
+ *
+ * @since 0.1.0
+ */
+SR_API int sr_package_version_minor_get(void)
+{
+ return SR_PACKAGE_VERSION_MINOR;
+}
+
+/**
+ * Get the micro libsigrok package version number.
+ *
+ * @return The micro package version number.
+ *
+ * @since 0.1.0
+ */
+SR_API int sr_package_version_micro_get(void)
+{
+ return SR_PACKAGE_VERSION_MICRO;
+}
+
+/**
+ * Get the libsigrok package version number as a string.
+ *
+ * @return The package version number string. The returned string is
+ * static and thus should NOT be free'd by the caller.
+ *
+ * @since 0.1.0
+ */
+SR_API const char *sr_package_version_string_get(void)
+{
+ return SR_PACKAGE_VERSION_STRING;
+}
+
+/**
+ * Get the "current" part of the libsigrok library version number.
+ *
+ * @return The "current" library version number.
+ *
+ * @since 0.1.0
+ */
+SR_API int sr_lib_version_current_get(void)
+{
+ return SR_LIB_VERSION_CURRENT;
+}
+
+/**
+ * Get the "revision" part of the libsigrok library version number.
+ *
+ * @return The "revision" library version number.
+ *
+ * @since 0.1.0
+ */
+SR_API int sr_lib_version_revision_get(void)
+{
+ return SR_LIB_VERSION_REVISION;
+}
+
+/**
+ * Get the "age" part of the libsigrok library version number.
+ *
+ * @return The "age" library version number.
+ *
+ * @since 0.1.0
+ */
+SR_API int sr_lib_version_age_get(void)
+{
+ return SR_LIB_VERSION_AGE;
+}
+
+/**
+ * Get the libsigrok library version number as a string.
+ *
+ * @return The library version number string. The returned string is
+ * static and thus should NOT be free'd by the caller.
+ *
+ * @since 0.1.0
+ */
+SR_API const char *sr_lib_version_string_get(void)
+{
+ return SR_LIB_VERSION_STRING;
+}
+
+/** @} */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/** @file
- * Standard API helper functions.
- * @internal
- */
-
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "std"
-
-/**
- * Standard sr_driver_init() API helper.
- *
- * This function can be used to simplify most driver's init() API callback.
- *
- * It creates a new 'struct drv_context' (drvc), assigns sr_ctx to it, and
- * then 'drvc' is assigned to the 'struct sr_dev_driver' (di) that is passed.
- *
- * @param sr_ctx The libsigrok context to assign.
- * @param di The driver instance to use.
- * @param[in] prefix A driver-specific prefix string used for log messages.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- * SR_ERR_MALLOC upon memory allocation errors.
- */
-SR_PRIV int std_init(struct sr_context *sr_ctx, struct sr_dev_driver *di,
- const char *prefix)
-{
- struct drv_context *drvc;
-
- if (!di) {
- sr_err("%s: Invalid driver, cannot initialize.", prefix);
- return SR_ERR_ARG;
- }
-
- if (!(drvc = g_try_malloc(sizeof(struct drv_context)))) {
- sr_err("%s: Driver context malloc failed.", prefix);
- return SR_ERR_MALLOC;
- }
-
- drvc->sr_ctx = sr_ctx;
- drvc->instances = NULL;
- di->priv = drvc;
-
- return SR_OK;
-}
-
-/**
- * Standard API helper for sending an SR_DF_HEADER packet.
- *
- * This function can be used to simplify most driver's
- * dev_acquisition_start() API callback.
- *
- * @param sdi The device instance to use.
- * @param prefix A driver-specific prefix string used for log messages.
- * Must not be NULL. An empty string is allowed.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- * SR_ERR upon other errors.
- */
-SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi,
- const char *prefix)
-{
- int ret;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_header header;
-
- if (!prefix) {
- sr_err("Invalid prefix.");
- return SR_ERR_ARG;
- }
-
- sr_dbg("%s: Starting acquisition.", prefix);
-
- /* Send header packet to the session bus. */
- sr_dbg("%s: Sending SR_DF_HEADER packet.", prefix);
- packet.type = SR_DF_HEADER;
- packet.payload = (uint8_t *)&header;
- header.feed_version = 1;
- gettimeofday(&header.starttime, NULL);
-
- if ((ret = sr_session_send(sdi, &packet)) < 0) {
- sr_err("%s: Failed to send header packet: %d.", prefix, ret);
- return ret;
- }
-
- return SR_OK;
-}
-
-#ifdef HAVE_LIBSERIALPORT
-
-/**
- * Standard serial driver dev_open() helper.
- *
- * This function can be used to implement the dev_open() driver API
- * callback in drivers that use a serial port. The port is opened
- * with the SERIAL_RDWR and SERIAL_NONBLOCK flags.
- *
- * If the open succeeded, the status field of the given sdi is set
- * to SR_ST_ACTIVE.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Serial port open failed.
- */
-SR_PRIV int std_serial_dev_open(struct sr_dev_inst *sdi)
-{
- struct sr_serial_dev_inst *serial;
-
- serial = sdi->conn;
- if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
- return SR_ERR;
-
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-/**
- * Standard serial driver dev_close() helper.
- *
- * This function can be used to implement the dev_close() driver API
- * callback in drivers that use a serial port.
- *
- * After closing the port, the status field of the given sdi is set
- * to SR_ST_INACTIVE.
- *
- * @retval SR_OK Success.
- */
-SR_PRIV int std_serial_dev_close(struct sr_dev_inst *sdi)
-{
- struct sr_serial_dev_inst *serial;
-
- serial = sdi->conn;
- if (serial && sdi->status == SR_ST_ACTIVE) {
- serial_close(serial);
- sdi->status = SR_ST_INACTIVE;
- }
-
- return SR_OK;
-}
-
-/**
- * Standard sr_session_stop() API helper.
- *
- * This function can be used to simplify most (serial port based) driver's
- * dev_acquisition_stop() API callback.
- *
- * @param sdi The device instance for which acquisition should stop.
- * Must not be NULL.
- * @param cb_data Opaque 'cb_data' pointer. Must not be NULL.
- * @param dev_close_fn Function pointer to the driver's dev_close().
- * Must not be NULL.
- * @param serial The serial device instance (struct serial_dev_inst *).
- * Must not be NULL.
- * @param[in] prefix A driver-specific prefix string used for log messages.
- * Must not be NULL. An empty string is allowed.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid arguments.
- * @retval SR_ERR_DEV_CLOSED Device is closed.
- * @retval SR_ERR Other errors.
- */
-SR_PRIV int std_serial_dev_acquisition_stop(struct sr_dev_inst *sdi,
- void *cb_data, dev_close_callback dev_close_fn,
- struct sr_serial_dev_inst *serial, const char *prefix)
-{
- int ret;
- struct sr_datafeed_packet packet;
-
- if (!prefix) {
- sr_err("Invalid prefix.");
- return SR_ERR_ARG;
- }
-
- if (sdi->status != SR_ST_ACTIVE) {
- sr_err("%s: Device inactive, can't stop acquisition.", prefix);
- return SR_ERR_DEV_CLOSED;
- }
-
- sr_dbg("%s: Stopping acquisition.", prefix);
-
- if ((ret = serial_source_remove(sdi->session, serial)) < 0) {
- sr_err("%s: Failed to remove source: %d.", prefix, ret);
- return ret;
- }
-
- if ((ret = dev_close_fn(sdi)) < 0) {
- sr_err("%s: Failed to close device: %d.", prefix, ret);
- return ret;
- }
-
- /* Send SR_DF_END packet to the session bus. */
- sr_dbg("%s: Sending SR_DF_END packet.", prefix);
- packet.type = SR_DF_END;
- packet.payload = NULL;
- if ((ret = sr_session_send(cb_data, &packet)) < 0) {
- sr_err("%s: Failed to send SR_DF_END packet: %d.", prefix, ret);
- return ret;
- }
-
- return SR_OK;
-}
-
-#endif
-
-/**
- * Standard driver dev_clear() helper.
- *
- * Clear driver, this means, close all instances.
- *
- * This function can be used to implement the dev_clear() driver API
- * callback. dev_close() is called before every sr_dev_inst is cleared.
- *
- * The only limitation is driver-specific device contexts (sdi->priv).
- * These are freed, but any dynamic allocation within structs stored
- * there cannot be freed.
- *
- * @param driver The driver which will have its instances released.
- * @param clear_private If not NULL, this points to a function called
- * with sdi->priv as argument. The function can then clear any device
- * instance-specific resources kept there. It must also clear the struct
- * pointed to by sdi->priv.
- *
- * @return SR_OK on success.
- */
-SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
- std_dev_clear_callback clear_private)
-{
- struct drv_context *drvc;
- struct sr_dev_inst *sdi;
- GSList *l;
- int ret;
-
- if (!(drvc = driver->priv))
- /* Driver was never initialized, nothing to do. */
- return SR_OK;
-
- ret = SR_OK;
- for (l = drvc->instances; l; l = l->next) {
- if (!(sdi = l->data)) {
- ret = SR_ERR_BUG;
- continue;
- }
- if (driver->dev_close)
- driver->dev_close(sdi);
-
- if (sdi->conn) {
-#ifdef HAVE_LIBSERIALPORT
- if (sdi->inst_type == SR_INST_SERIAL)
- sr_serial_dev_inst_free(sdi->conn);
-#endif
-#ifdef HAVE_LIBUSB_1_0
- if (sdi->inst_type == SR_INST_USB)
- sr_usb_dev_inst_free(sdi->conn);
-#endif
- if (sdi->inst_type == SR_INST_SCPI)
- sr_scpi_free(sdi->conn);
- }
- if (clear_private)
- clear_private(sdi->priv);
- else
- g_free(sdi->priv);
- sr_dev_inst_free(sdi);
- }
-
- g_slist_free(drvc->instances);
- drvc->instances = NULL;
-
- return ret;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/** @cond PRIVATE */
-#define LOG_PREFIX "strutil"
-/** @endcond */
-
-/**
- * @file
- *
- * Helper functions for handling or converting libsigrok-related strings.
- */
-
-/**
- * @defgroup grp_strutil String utilities
- *
- * Helper functions for handling or converting libsigrok-related strings.
- *
- * @{
- */
-
-/**
- * @private
- *
- * Convert a string representation of a numeric value (base 10) to a long integer. The
- * conversion is strict and will fail if the complete string does not represent
- * a valid long integer. The function sets errno according to the details of the
- * failure.
- *
- * @param str The string representation to convert.
- * @param ret Pointer to long where the result of the conversion will be stored.
- *
- * @retval SR_OK Conversion successful.
- * @retval SR_ERR Failure.
- *
- * @since 0.3.0
- */
-SR_PRIV int sr_atol(const char *str, long *ret)
-{
- long tmp;
- char *endptr = NULL;
-
- errno = 0;
- tmp = strtol(str, &endptr, 10);
-
- if (!endptr || *endptr || errno) {
- if (!errno)
- errno = EINVAL;
- return SR_ERR;
- }
-
- *ret = tmp;
- return SR_OK;
-}
-
-/**
- * @private
- *
- * Convert a string representation of a numeric value (base 10) to an integer. The
- * conversion is strict and will fail if the complete string does not represent
- * a valid integer. The function sets errno according to the details of the
- * failure.
- *
- * @param str The string representation to convert.
- * @param ret Pointer to int where the result of the conversion will be stored.
- *
- * @retval SR_OK Conversion successful.
- * @retval SR_ERR Failure.
- *
- * @since 0.3.0
- */
-SR_PRIV int sr_atoi(const char *str, int *ret)
-{
- long tmp;
-
- if (sr_atol(str, &tmp) != SR_OK)
- return SR_ERR;
-
- if ((int) tmp != tmp) {
- errno = ERANGE;
- return SR_ERR;
- }
-
- *ret = (int) tmp;
- return SR_OK;
-}
-
-/**
- * @private
- *
- * Convert a string representation of a numeric value to a double. The
- * conversion is strict and will fail if the complete string does not represent
- * a valid double. The function sets errno according to the details of the
- * failure.
- *
- * @param str The string representation to convert.
- * @param ret Pointer to double where the result of the conversion will be stored.
- *
- * @retval SR_OK Conversion successful.
- * @retval SR_ERR Failure.
- *
- * @since 0.3.0
- */
-SR_PRIV int sr_atod(const char *str, double *ret)
-{
- double tmp;
- char *endptr = NULL;
-
- errno = 0;
- tmp = strtof(str, &endptr);
-
- if (!endptr || *endptr || errno) {
- if (!errno)
- errno = EINVAL;
- return SR_ERR;
- }
-
- *ret = tmp;
- return SR_OK;
-}
-
-/**
- * @private
- *
- * Convert a string representation of a numeric value to a float. The
- * conversion is strict and will fail if the complete string does not represent
- * a valid float. The function sets errno according to the details of the
- * failure.
- *
- * @param str The string representation to convert.
- * @param ret Pointer to float where the result of the conversion will be stored.
- *
- * @retval SR_OK Conversion successful.
- * @retval SR_ERR Failure.
- *
- * @since 0.3.0
- */
-SR_PRIV int sr_atof(const char *str, float *ret)
-{
- double tmp;
-
- if (sr_atod(str, &tmp) != SR_OK)
- return SR_ERR;
-
- if ((float) tmp != tmp) {
- errno = ERANGE;
- return SR_ERR;
- }
-
- *ret = (float) tmp;
- return SR_OK;
-}
-
-/**
- * @private
- *
- * Convert a string representation of a numeric value to a float. The
- * conversion is strict and will fail if the complete string does not represent
- * a valid float. The function sets errno according to the details of the
- * failure. This version ignores the locale.
- *
- * @param str The string representation to convert.
- * @param ret Pointer to float where the result of the conversion will be stored.
- *
- * @retval SR_OK Conversion successful.
- * @retval SR_ERR Failure.
- *
- * @since 0.3.0
- */
-SR_PRIV int sr_atof_ascii(const char *str, float *ret)
-{
- double tmp;
- char *endptr = NULL;
-
- errno = 0;
- tmp = g_ascii_strtod(str, &endptr);
-
- if (!endptr || *endptr || errno) {
- if (!errno)
- errno = EINVAL;
- return SR_ERR;
- }
-
- /* FIXME This fails unexpectedly. Some other method to safel downcast
- * needs to be found. Checking against FLT_MAX doesn't work as well. */
- /*
- if ((float) tmp != tmp) {
- errno = ERANGE;
- sr_dbg("ERANGEEEE %e != %e", (float) tmp, tmp);
- return SR_ERR;
- }
- */
-
- *ret = (float) tmp;
- return SR_OK;
-}
-
-/**
- * Convert a numeric value value to its "natural" string representation
- * in SI units.
- *
- * E.g. a value of 3000000, with units set to "W", would be converted
- * to "3 MW", 20000 to "20 kW", 31500 would become "31.5 kW".
- *
- * @param x The value to convert.
- * @param unit The unit to append to the string, or NULL if the string
- * has no units.
- *
- * @return A g_try_malloc()ed string representation of the samplerate value,
- * or NULL upon errors. The caller is responsible to g_free() the
- * memory.
- *
- * @since 0.2.0
- */
-SR_API char *sr_si_string_u64(uint64_t x, const char *unit)
-{
- uint8_t i;
- uint64_t quot, divisor[] = {
- SR_HZ(1), SR_KHZ(1), SR_MHZ(1), SR_GHZ(1),
- SR_GHZ(1000), SR_GHZ(1000 * 1000), SR_GHZ(1000 * 1000 * 1000),
- };
- const char *p, prefix[] = "\0kMGTPE";
- char fmt[16], fract[20] = "", *f;
-
- if (unit == NULL)
- unit = "";
-
- for (i = 0; (quot = x / divisor[i]) >= 1000; i++);
-
- if (i) {
- sprintf(fmt, ".%%0%d"PRIu64, i * 3);
- f = fract + sprintf(fract, fmt, x % divisor[i]) - 1;
-
- while (f >= fract && strchr("0.", *f))
- *f-- = 0;
- }
-
- p = prefix + i;
-
- return g_strdup_printf("%" PRIu64 "%s %.1s%s", quot, fract, p, unit);
-}
-
-/**
- * Convert a numeric samplerate value to its "natural" string representation.
- *
- * E.g. a value of 3000000 would be converted to "3 MHz", 20000 to "20 kHz",
- * 31500 would become "31.5 kHz".
- *
- * @param samplerate The samplerate in Hz.
- *
- * @return A g_try_malloc()ed string representation of the samplerate value,
- * or NULL upon errors. The caller is responsible to g_free() the
- * memory.
- *
- * @since 0.1.0
- */
-SR_API char *sr_samplerate_string(uint64_t samplerate)
-{
- return sr_si_string_u64(samplerate, "Hz");
-}
-
-/**
- * Convert a numeric frequency value to the "natural" string representation
- * of its period.
- *
- * E.g. a value of 3000000 would be converted to "3 us", 20000 to "50 ms".
- *
- * @param frequency The frequency in Hz.
- *
- * @return A g_try_malloc()ed string representation of the frequency value,
- * or NULL upon errors. The caller is responsible to g_free() the
- * memory.
- *
- * @since 0.1.0
- */
-SR_API char *sr_period_string(uint64_t frequency)
-{
- char *o;
- int r;
-
- /* Allocate enough for a uint64_t as string + " ms". */
- if (!(o = g_try_malloc0(30 + 1))) {
- sr_err("%s: o malloc failed", __func__);
- return NULL;
- }
-
- if (frequency >= SR_GHZ(1))
- r = snprintf(o, 30, "%" PRIu64 " ns", frequency / 1000000000);
- else if (frequency >= SR_MHZ(1))
- r = snprintf(o, 30, "%" PRIu64 " us", frequency / 1000000);
- else if (frequency >= SR_KHZ(1))
- r = snprintf(o, 30, "%" PRIu64 " ms", frequency / 1000);
- else
- r = snprintf(o, 30, "%" PRIu64 " s", frequency);
-
- if (r < 0) {
- /* Something went wrong... */
- g_free(o);
- return NULL;
- }
-
- return o;
-}
-
-/**
- * Convert a numeric voltage value to the "natural" string representation
- * of its voltage value. The voltage is specified as a rational number's
- * numerator and denominator.
- *
- * E.g. a value of 300000 would be converted to "300mV", 2 to "2V".
- *
- * @param v_p The voltage numerator.
- * @param v_q The voltage denominator.
- *
- * @return A g_try_malloc()ed string representation of the voltage value,
- * or NULL upon errors. The caller is responsible to g_free() the
- * memory.
- *
- * @since 0.2.0
- */
-SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
-{
- int r;
- char *o;
-
- if (!(o = g_try_malloc0(30 + 1))) {
- sr_err("%s: o malloc failed", __func__);
- return NULL;
- }
-
- if (v_q == 1000)
- r = snprintf(o, 30, "%" PRIu64 "mV", v_p);
- else if (v_q == 1)
- r = snprintf(o, 30, "%" PRIu64 "V", v_p);
- else
- r = snprintf(o, 30, "%gV", (float)v_p / (float)v_q);
-
- if (r < 0) {
- /* Something went wrong... */
- g_free(o);
- return NULL;
- }
-
- return o;
-}
-
-/**
- * Convert a "natural" string representation of a size value to uint64_t.
- *
- * E.g. a value of "3k" or "3 K" would be converted to 3000, a value
- * of "15M" would be converted to 15000000.
- *
- * Value representations other than decimal (such as hex or octal) are not
- * supported. Only 'k' (kilo), 'm' (mega), 'g' (giga) suffixes are supported.
- * Spaces (but not other whitespace) between value and suffix are allowed.
- *
- * @param sizestring A string containing a (decimal) size value.
- * @param size Pointer to uint64_t which will contain the string's size value.
- *
- * @return SR_OK upon success, SR_ERR upon errors.
- *
- * @since 0.1.0
- */
-SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
-{
- int multiplier, done;
- double frac_part;
- char *s;
-
- *size = strtoull(sizestring, &s, 10);
- multiplier = 0;
- frac_part = 0;
- done = FALSE;
- while (s && *s && multiplier == 0 && !done) {
- switch (*s) {
- case ' ':
- break;
- case '.':
- frac_part = g_ascii_strtod(s, &s);
- break;
- case 'k':
- case 'K':
- multiplier = SR_KHZ(1);
- break;
- case 'm':
- case 'M':
- multiplier = SR_MHZ(1);
- break;
- case 'g':
- case 'G':
- multiplier = SR_GHZ(1);
- break;
- default:
- done = TRUE;
- s--;
- }
- s++;
- }
- if (multiplier > 0) {
- *size *= multiplier;
- *size += frac_part * multiplier;
- } else
- *size += frac_part;
-
- if (*s && strcasecmp(s, "Hz"))
- return SR_ERR;
-
- return SR_OK;
-}
-
-/**
- * Convert a "natural" string representation of a time value to an
- * uint64_t value in milliseconds.
- *
- * E.g. a value of "3s" or "3 s" would be converted to 3000, a value
- * of "15ms" would be converted to 15.
- *
- * Value representations other than decimal (such as hex or octal) are not
- * supported. Only lower-case "s" and "ms" time suffixes are supported.
- * Spaces (but not other whitespace) between value and suffix are allowed.
- *
- * @param timestring A string containing a (decimal) time value.
- * @return The string's time value as uint64_t, in milliseconds.
- *
- * @todo Add support for "m" (minutes) and others.
- * @todo Add support for picoseconds?
- * @todo Allow both lower-case and upper-case? If no, document it.
- *
- * @since 0.1.0
- */
-SR_API uint64_t sr_parse_timestring(const char *timestring)
-{
- uint64_t time_msec;
- char *s;
-
- /* TODO: Error handling, logging. */
-
- time_msec = strtoull(timestring, &s, 10);
- if (time_msec == 0 && s == timestring)
- return 0;
-
- if (s && *s) {
- while (*s == ' ')
- s++;
- if (!strcmp(s, "s"))
- time_msec *= 1000;
- else if (!strcmp(s, "ms"))
- ; /* redundant */
- else
- return 0;
- }
-
- return time_msec;
-}
-
-/** @since 0.1.0 */
-SR_API gboolean sr_parse_boolstring(const char *boolstr)
-{
- if (!boolstr)
- return FALSE;
-
- if (!g_ascii_strncasecmp(boolstr, "true", 4) ||
- !g_ascii_strncasecmp(boolstr, "yes", 3) ||
- !g_ascii_strncasecmp(boolstr, "on", 2) ||
- !g_ascii_strncasecmp(boolstr, "1", 1))
- return TRUE;
-
- return FALSE;
-}
-
-/** @since 0.2.0 */
-SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q)
-{
- char *s;
-
- *p = strtoull(periodstr, &s, 10);
- if (*p == 0 && s == periodstr)
- /* No digits found. */
- return SR_ERR_ARG;
-
- if (s && *s) {
- while (*s == ' ')
- s++;
- if (!strcmp(s, "fs"))
- *q = 1000000000000000ULL;
- else if (!strcmp(s, "ps"))
- *q = 1000000000000ULL;
- else if (!strcmp(s, "ns"))
- *q = 1000000000ULL;
- else if (!strcmp(s, "us"))
- *q = 1000000;
- else if (!strcmp(s, "ms"))
- *q = 1000;
- else if (!strcmp(s, "s"))
- *q = 1;
- else
- /* Must have a time suffix. */
- return SR_ERR_ARG;
- }
-
- return SR_OK;
-}
-
-/** @since 0.2.0 */
-SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q)
-{
- char *s;
-
- *p = strtoull(voltstr, &s, 10);
- if (*p == 0 && s == voltstr)
- /* No digits found. */
- return SR_ERR_ARG;
-
- if (s && *s) {
- while (*s == ' ')
- s++;
- if (!strcasecmp(s, "mv"))
- *q = 1000L;
- else if (!strcasecmp(s, "v"))
- *q = 1;
- else
- /* Must have a base suffix. */
- return SR_ERR_ARG;
- }
-
- return SR_OK;
-}
-
-/** @} */
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
- *
- * 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.h"
-#include "libsigrok-internal.h"
-
-/* * @cond PRIVATE */
-#define LOG_PREFIX "trigger"
-/* * @endcond */
-
-SR_API struct sr_trigger *sr_trigger_new(const char *name)
-{
- struct sr_trigger *trig;
-
- trig = g_malloc0(sizeof(struct sr_trigger));
- if (name)
- trig->name = g_strdup(name);
-
- return trig;
-}
-
-SR_API void sr_trigger_free(struct sr_trigger *trig)
-{
- struct sr_trigger_stage *stage;
- GSList *l;
-
- for (l = trig->stages; l; l = l->next) {
- stage = l->data;
- g_slist_free_full(stage->matches, g_free);
- }
- g_slist_free_full(trig->stages, g_free);
-
- g_free(trig->name);
- g_free(trig);
-}
-
-SR_API struct sr_trigger_stage *sr_trigger_stage_add(struct sr_trigger *trig)
-{
- struct sr_trigger_stage *stage;
-
- stage = g_malloc0(sizeof(struct sr_trigger_stage));
- stage->stage = g_slist_length(trig->stages);
- trig->stages = g_slist_append(trig->stages, stage);
-
- return stage;
-}
-
-SR_API int sr_trigger_match_add(struct sr_trigger_stage *stage,
- struct sr_channel *ch, int trigger_match, float value)
-{
- struct sr_trigger_match *match;
-
- if (ch->type == SR_CHANNEL_LOGIC) {
- if (trigger_match != SR_TRIGGER_ZERO &&
- trigger_match != SR_TRIGGER_ONE &&
- trigger_match != SR_TRIGGER_RISING &&
- trigger_match != SR_TRIGGER_FALLING &&
- trigger_match != SR_TRIGGER_EDGE) {
- sr_err("Invalid trigger match for a logic channel.");
- return SR_ERR_ARG;
- }
-
-
- } else if (ch->type == SR_CHANNEL_ANALOG) {
- if (trigger_match != SR_TRIGGER_FALLING &&
- trigger_match != SR_TRIGGER_OVER &&
- trigger_match != SR_TRIGGER_UNDER) {
- sr_err("Invalid trigger match for an analog channel.");
- return SR_ERR_ARG;
- }
- }
-
- match = g_malloc0(sizeof(struct sr_trigger_match));
- match->channel = ch;
- match->match = trigger_match;
- match->value = value;
- stage->matches = g_slist_append(stage->matches, match);
-
- return SR_OK;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "libsigrok.h"
-
-/**
- * @file
- *
- * Version number querying functions, definitions, and macros.
- */
-
-/**
- * @defgroup grp_versions Versions
- *
- * Version number querying functions, definitions, and macros.
- *
- * This set of API calls returns two different version numbers related
- * to libsigrok. The "package version" is the release version number of the
- * libsigrok tarball in the usual "major.minor.micro" format, e.g. "0.1.0".
- *
- * The "library version" is independent of that; it is the libtool version
- * number in the "current:revision:age" format, e.g. "2:0:0".
- * See http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning for details.
- *
- * Both version numbers (and/or individual components of them) can be
- * retrieved via the API calls at runtime, and/or they can be checked at
- * compile/preprocessor time using the respective macros.
- *
- * @{
- */
-
-/**
- * Get the major libsigrok package version number.
- *
- * @return The major package version number.
- *
- * @since 0.1.0
- */
-SR_API int sr_package_version_major_get(void)
-{
- return SR_PACKAGE_VERSION_MAJOR;
-}
-
-/**
- * Get the minor libsigrok package version number.
- *
- * @return The minor package version number.
- *
- * @since 0.1.0
- */
-SR_API int sr_package_version_minor_get(void)
-{
- return SR_PACKAGE_VERSION_MINOR;
-}
-
-/**
- * Get the micro libsigrok package version number.
- *
- * @return The micro package version number.
- *
- * @since 0.1.0
- */
-SR_API int sr_package_version_micro_get(void)
-{
- return SR_PACKAGE_VERSION_MICRO;
-}
-
-/**
- * Get the libsigrok package version number as a string.
- *
- * @return The package version number string. The returned string is
- * static and thus should NOT be free'd by the caller.
- *
- * @since 0.1.0
- */
-SR_API const char *sr_package_version_string_get(void)
-{
- return SR_PACKAGE_VERSION_STRING;
-}
-
-/**
- * Get the "current" part of the libsigrok library version number.
- *
- * @return The "current" library version number.
- *
- * @since 0.1.0
- */
-SR_API int sr_lib_version_current_get(void)
-{
- return SR_LIB_VERSION_CURRENT;
-}
-
-/**
- * Get the "revision" part of the libsigrok library version number.
- *
- * @return The "revision" library version number.
- *
- * @since 0.1.0
- */
-SR_API int sr_lib_version_revision_get(void)
-{
- return SR_LIB_VERSION_REVISION;
-}
-
-/**
- * Get the "age" part of the libsigrok library version number.
- *
- * @return The "age" library version number.
- *
- * @since 0.1.0
- */
-SR_API int sr_lib_version_age_get(void)
-{
- return SR_LIB_VERSION_AGE;
-}
-
-/**
- * Get the libsigrok library version number as a string.
- *
- * @return The library version number string. The returned string is
- * static and thus should NOT be free'd by the caller.
- *
- * @since 0.1.0
- */
-SR_API const char *sr_lib_version_string_get(void)
-{
- return SR_LIB_VERSION_STRING;
-}
-
-/** @} */